From 0bc77f3f06fcf2ca7b7fad782d70926cd4d235f1 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 9 May 2012 09:55:57 -0300 Subject: [media] mt9t001: Implement V4L2_CID_PIXEL_RATE control The pixel rate control is required by the OMAP3 ISP driver and should be implemented by all media controller-compatible sensor drivers. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- include/media/mt9t001.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/media/mt9t001.h b/include/media/mt9t001.h index e839a78bb9c5..03fd63edd133 100644 --- a/include/media/mt9t001.h +++ b/include/media/mt9t001.h @@ -3,6 +3,7 @@ struct mt9t001_platform_data { unsigned int clk_pol:1; + unsigned int ext_clk; }; #endif -- cgit v1.2.3 From 23ba4fd0a42a46551041e379712e808ad496ba45 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 24 May 2012 15:03:10 -0700 Subject: drm/i915: wait render timeout ioctl This helps implement GL_ARB_sync but stops short of allowing full blown sync objects. Finally we can use the new timed seqno waiting function to allow userspace to wait on a buffer object with a timeout. This implements that interface. The IOCTL will take as input a buffer object handle, and a timeout in nanoseconds (flags is currently optional but will likely be used for permutations of flush operations). Users may specify 0 nanoseconds to instantly check. The wait ioctl with a timeout of 0 reimplements the busy ioctl. With any non-zero timeout parameter the wait ioctl will wait for the given number of nanoseconds on an object becoming unbusy. Since the wait itself does so holding struct_mutex the object may become re-busied before this completes. A similar but shorter race condition exists in the busy ioctl. v2: ETIME/ERESTARTSYS instead of changing to EBUSY, and EGAIN (Chris) Flush the object from the gpu write domain (Chris + Daniel) Fix leaked refcount in good case (Chris) Naturally align ioctl struct (Chris) v3: Drop lock after getting seqno to avoid ugly dance (Chris) v4: check for 0 timeout after olr check to allow polling (Chris) v5: Updated the comment. (Chris) v6: Return -ETIME instead of -EBUSY when timeout_ns is 0 (Daniel) Fix the commit message comment to be less ugly (Ben) Add a warning to check the return timespec (Ben) v7: Use DRM_AUTH for the ioctl. (Eugeni) Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 1 + drivers/gpu/drm/i915/i915_drv.h | 2 + drivers/gpu/drm/i915/i915_gem.c | 86 +++++++++++++++++++++++++++++++++++++++++ include/drm/i915_drm.h | 10 +++++ 4 files changed, 99 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index f94792626b94..262a74d1f852 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1803,6 +1803,7 @@ struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF_DRV(I915_OVERLAY_ATTRS, intel_overlay_attrs, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(I915_SET_SPRITE_COLORKEY, intel_sprite_set_colorkey, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(I915_GET_SPRITE_COLORKEY, intel_sprite_get_colorkey, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(I915_GEM_WAIT, i915_gem_wait_ioctl, DRM_AUTH|DRM_UNLOCKED), }; int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 11c7a6a330c1..8ac10e8c0cdb 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1229,6 +1229,8 @@ int i915_gem_get_tiling(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +int i915_gem_wait_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); void i915_gem_load(struct drm_device *dev); int i915_gem_init_object(struct drm_gem_object *obj); int __must_check i915_gem_flush_ring(struct intel_ring_buffer *ring, diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index c8a5b04bc8d5..a0d740fac240 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2000,6 +2000,92 @@ i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj) return 0; } +/** + * i915_gem_wait_ioctl - implements DRM_IOCTL_I915_GEM_WAIT + * @DRM_IOCTL_ARGS: standard ioctl arguments + * + * Returns 0 if successful, else an error is returned with the remaining time in + * the timeout parameter. + * -ETIME: object is still busy after timeout + * -ERESTARTSYS: signal interrupted the wait + * -ENONENT: object doesn't exist + * Also possible, but rare: + * -EAGAIN: GPU wedged + * -ENOMEM: damn + * -ENODEV: Internal IRQ fail + * -E?: The add request failed + * + * The wait ioctl with a timeout of 0 reimplements the busy ioctl. With any + * non-zero timeout parameter the wait ioctl will wait for the given number of + * nanoseconds on an object becoming unbusy. Since the wait itself does so + * without holding struct_mutex the object may become re-busied before this + * function completes. A similar but shorter * race condition exists in the busy + * ioctl + */ +int +i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file) +{ + struct drm_i915_gem_wait *args = data; + struct drm_i915_gem_object *obj; + struct intel_ring_buffer *ring = NULL; + struct timespec timeout; + u32 seqno = 0; + int ret = 0; + + timeout = ns_to_timespec(args->timeout_ns); + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return ret; + + obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->bo_handle)); + if (&obj->base == NULL) { + mutex_unlock(&dev->struct_mutex); + return -ENOENT; + } + + /* Need to make sure the object is flushed first. This non-obvious + * flush is required to enforce that (active && !olr) == no wait + * necessary. + */ + ret = i915_gem_object_flush_gpu_write_domain(obj); + if (ret) + goto out; + + if (obj->active) { + seqno = obj->last_rendering_seqno; + ring = obj->ring; + } + + if (seqno == 0) + goto out; + + ret = i915_gem_check_olr(ring, seqno); + if (ret) + goto out; + + /* Do this after OLR check to make sure we make forward progress polling + * on this IOCTL with a 0 timeout (like busy ioctl) + */ + if (!args->timeout_ns) { + ret = -ETIME; + goto out; + } + + drm_gem_object_unreference(&obj->base); + mutex_unlock(&dev->struct_mutex); + + ret = __wait_seqno(ring, seqno, true, &timeout); + WARN_ON(!timespec_valid(&timeout)); + args->timeout_ns = timespec_to_ns(&timeout); + return ret; + +out: + drm_gem_object_unreference(&obj->base); + mutex_unlock(&dev->struct_mutex); + return ret; +} + /** * i915_gem_object_sync - sync an object to a ring. * diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index f3f82242bf1d..bab174334da5 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/i915_drm.h @@ -200,6 +200,7 @@ typedef struct _drm_i915_sarea { #define DRM_I915_GEM_EXECBUFFER2 0x29 #define DRM_I915_GET_SPRITE_COLORKEY 0x2a #define DRM_I915_SET_SPRITE_COLORKEY 0x2b +#define DRM_I915_GEM_WAIT 0x2c #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) #define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) @@ -243,6 +244,7 @@ typedef struct _drm_i915_sarea { #define DRM_IOCTL_I915_OVERLAY_ATTRS DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_OVERLAY_ATTRS, struct drm_intel_overlay_attrs) #define DRM_IOCTL_I915_SET_SPRITE_COLORKEY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_SET_SPRITE_COLORKEY, struct drm_intel_sprite_colorkey) #define DRM_IOCTL_I915_GET_SPRITE_COLORKEY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_SET_SPRITE_COLORKEY, struct drm_intel_sprite_colorkey) +#define DRM_IOCTL_I915_GEM_WAIT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_WAIT, struct drm_i915_gem_wait) /* Allow drivers to submit batchbuffers directly to hardware, relying * on the security mechanisms provided by hardware. @@ -886,4 +888,12 @@ struct drm_intel_sprite_colorkey { __u32 flags; }; +struct drm_i915_gem_wait { + /** Handle of BO we shall wait on */ + __u32 bo_handle; + __u32 flags; + /** Number of nanoseconds to wait, Returns time remaining. */ + __u64 timeout_ns; +}; + #endif /* _I915_DRM_H_ */ -- cgit v1.2.3 From 188726ecb66f022e92ec110ca85c62a937184636 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 24 May 2012 19:28:17 +0200 Subject: firewire: core: make address handler length 64 bits The type of the length field of the fw_address_handler structure was size_t, which restricted it to 32 bits on 32-bit architectures. While making it u32 would match the userspace API, all calculations on this field use 64 bits anyway, and the ability to use 4 GB or larger address ranges is useful in the kernel. Signed-off-by: Clemens Ladisch Signed-off-by: Stefan Richter --- include/linux/firewire.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 584826ba2eb7..d77f60c6d1ed 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -307,7 +307,7 @@ struct fw_transaction { struct fw_address_handler { u64 offset; - size_t length; + u64 length; fw_address_callback_t address_callback; void *callback_data; struct list_head link; -- cgit v1.2.3 From 32fad281c0680ed0ccade7dda85a2121cf9b1d06 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Fri, 4 May 2012 02:32:53 +0000 Subject: KVM: PPC: Book3S HV: Make the guest hash table size configurable This adds a new ioctl to enable userspace to control the size of the guest hashed page table (HPT) and to clear it out when resetting the guest. The KVM_PPC_ALLOCATE_HTAB ioctl is a VM ioctl and takes as its parameter a pointer to a u32 containing the desired order of the HPT (log base 2 of the size in bytes), which is updated on successful return to the actual order of the HPT which was allocated. There must be no vcpus running at the time of this ioctl. To enforce this, we now keep a count of the number of vcpus running in kvm->arch.vcpus_running. If the ioctl is called when a HPT has already been allocated, we don't reallocate the HPT but just clear it out. We first clear the kvm->arch.rma_setup_done flag, which has two effects: (a) since we hold the kvm->lock mutex, it will prevent any vcpus from starting to run until we're done, and (b) it means that the first vcpu to run after we're done will re-establish the VRMA if necessary. If userspace doesn't call this ioctl before running the first vcpu, the kernel will allocate a default-sized HPT at that point. We do it then rather than when creating the VM, as the code did previously, so that userspace has a chance to do the ioctl if it wants. When allocating the HPT, we can allocate either from the kernel page allocator, or from the preallocated pool. If userspace is asking for a different size from the preallocated HPTs, we first try to allocate using the kernel page allocator. Then we try to allocate from the preallocated pool, and then if that fails, we try allocating decreasing sizes from the kernel page allocator, down to the minimum size allowed (256kB). Note that the kernel page allocator limits allocations to 1 << CONFIG_FORCE_MAX_ZONEORDER pages, which by default corresponds to 16MB (on 64-bit powerpc, at least). Signed-off-by: Paul Mackerras [agraf: fix module compilation] Signed-off-by: Alexander Graf --- Documentation/virtual/kvm/api.txt | 36 +++++++++ arch/powerpc/include/asm/kvm_book3s_64.h | 7 +- arch/powerpc/include/asm/kvm_host.h | 4 + arch/powerpc/include/asm/kvm_ppc.h | 3 +- arch/powerpc/kvm/book3s_64_mmu_hv.c | 123 ++++++++++++++++++++++++------- arch/powerpc/kvm/book3s_hv.c | 40 +++++++--- arch/powerpc/kvm/book3s_hv_builtin.c | 5 +- arch/powerpc/kvm/book3s_hv_rm_mmu.c | 15 ++-- arch/powerpc/kvm/powerpc.c | 18 +++++ include/linux/kvm.h | 3 + 10 files changed, 200 insertions(+), 54 deletions(-) (limited to 'include') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 930126698a0f..310fe508d9cd 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -1930,6 +1930,42 @@ The "pte_enc" field provides a value that can OR'ed into the hash PTE's RPN field (ie, it needs to be shifted left by 12 to OR it into the hash PTE second double word). + +4.75 KVM_PPC_ALLOCATE_HTAB + +Capability: KVM_CAP_PPC_ALLOC_HTAB +Architectures: powerpc +Type: vm ioctl +Parameters: Pointer to u32 containing hash table order (in/out) +Returns: 0 on success, -1 on error + +This requests the host kernel to allocate an MMU hash table for a +guest using the PAPR paravirtualization interface. This only does +anything if the kernel is configured to use the Book 3S HV style of +virtualization. Otherwise the capability doesn't exist and the ioctl +returns an ENOTTY error. The rest of this description assumes Book 3S +HV. + +There must be no vcpus running when this ioctl is called; if there +are, it will do nothing and return an EBUSY error. + +The parameter is a pointer to a 32-bit unsigned integer variable +containing the order (log base 2) of the desired size of the hash +table, which must be between 18 and 46. On successful return from the +ioctl, it will have been updated with the order of the hash table that +was allocated. + +If no hash table has been allocated when any vcpu is asked to run +(with the KVM_RUN ioctl), the host kernel will allocate a +default-sized hash table (16 MB). + +If this ioctl is called when a hash table has already been allocated, +the kernel will clear out the existing hash table (zero all HPTEs) and +return the hash table order in the parameter. (If the guest is using +the virtualized real-mode area (VRMA) facility, the kernel will +re-create the VMRA HPTEs on the next KVM_RUN of any vcpu.) + + 5. The kvm_run structure ------------------------ diff --git a/arch/powerpc/include/asm/kvm_book3s_64.h b/arch/powerpc/include/asm/kvm_book3s_64.h index b0c08b142770..0dd1d86d3e31 100644 --- a/arch/powerpc/include/asm/kvm_book3s_64.h +++ b/arch/powerpc/include/asm/kvm_book3s_64.h @@ -36,11 +36,8 @@ static inline void svcpu_put(struct kvmppc_book3s_shadow_vcpu *svcpu) #define SPAPR_TCE_SHIFT 12 #ifdef CONFIG_KVM_BOOK3S_64_HV -/* For now use fixed-size 16MB page table */ -#define HPT_ORDER 24 -#define HPT_NPTEG (1ul << (HPT_ORDER - 7)) /* 128B per pteg */ -#define HPT_NPTE (HPT_NPTEG << 3) /* 8 PTEs per PTEG */ -#define HPT_HASH_MASK (HPT_NPTEG - 1) +#define KVM_DEFAULT_HPT_ORDER 24 /* 16MB HPT by default */ +extern int kvm_hpt_order; /* order of preallocated HPTs */ #endif #define VRMA_VSID 0x1ffffffUL /* 1TB VSID reserved for VRMA */ diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index d848cdc49715..dd783beb88b3 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -237,6 +237,10 @@ struct kvm_arch { unsigned long vrma_slb_v; int rma_setup_done; int using_mmu_notifiers; + u32 hpt_order; + atomic_t vcpus_running; + unsigned long hpt_npte; + unsigned long hpt_mask; spinlock_t slot_phys_lock; unsigned long *slot_phys[KVM_MEM_SLOTS_NUM]; int slot_npages[KVM_MEM_SLOTS_NUM]; diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h index f68c22fa2fce..0124937a23b9 100644 --- a/arch/powerpc/include/asm/kvm_ppc.h +++ b/arch/powerpc/include/asm/kvm_ppc.h @@ -119,7 +119,8 @@ extern void kvmppc_core_destroy_mmu(struct kvm_vcpu *vcpu); extern int kvmppc_kvm_pv(struct kvm_vcpu *vcpu); extern void kvmppc_map_magic(struct kvm_vcpu *vcpu); -extern long kvmppc_alloc_hpt(struct kvm *kvm); +extern long kvmppc_alloc_hpt(struct kvm *kvm, u32 *htab_orderp); +extern long kvmppc_alloc_reset_hpt(struct kvm *kvm, u32 *htab_orderp); extern void kvmppc_free_hpt(struct kvm *kvm); extern long kvmppc_prepare_vrma(struct kvm *kvm, struct kvm_userspace_memory_region *mem); diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c index 80a577517584..d03eb6f7b058 100644 --- a/arch/powerpc/kvm/book3s_64_mmu_hv.c +++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c @@ -37,56 +37,121 @@ /* POWER7 has 10-bit LPIDs, PPC970 has 6-bit LPIDs */ #define MAX_LPID_970 63 -long kvmppc_alloc_hpt(struct kvm *kvm) +/* Power architecture requires HPT is at least 256kB */ +#define PPC_MIN_HPT_ORDER 18 + +long kvmppc_alloc_hpt(struct kvm *kvm, u32 *htab_orderp) { unsigned long hpt; - long lpid; struct revmap_entry *rev; struct kvmppc_linear_info *li; + long order = kvm_hpt_order; - /* Allocate guest's hashed page table */ - li = kvm_alloc_hpt(); - if (li) { - /* using preallocated memory */ - hpt = (ulong)li->base_virt; - kvm->arch.hpt_li = li; - } else { - /* using dynamic memory */ + if (htab_orderp) { + order = *htab_orderp; + if (order < PPC_MIN_HPT_ORDER) + order = PPC_MIN_HPT_ORDER; + } + + /* + * If the user wants a different size from default, + * try first to allocate it from the kernel page allocator. + */ + hpt = 0; + if (order != kvm_hpt_order) { hpt = __get_free_pages(GFP_KERNEL|__GFP_ZERO|__GFP_REPEAT| - __GFP_NOWARN, HPT_ORDER - PAGE_SHIFT); + __GFP_NOWARN, order - PAGE_SHIFT); + if (!hpt) + --order; } + /* Next try to allocate from the preallocated pool */ if (!hpt) { - pr_err("kvm_alloc_hpt: Couldn't alloc HPT\n"); - return -ENOMEM; + li = kvm_alloc_hpt(); + if (li) { + hpt = (ulong)li->base_virt; + kvm->arch.hpt_li = li; + order = kvm_hpt_order; + } } + + /* Lastly try successively smaller sizes from the page allocator */ + while (!hpt && order > PPC_MIN_HPT_ORDER) { + hpt = __get_free_pages(GFP_KERNEL|__GFP_ZERO|__GFP_REPEAT| + __GFP_NOWARN, order - PAGE_SHIFT); + if (!hpt) + --order; + } + + if (!hpt) + return -ENOMEM; + kvm->arch.hpt_virt = hpt; + kvm->arch.hpt_order = order; + /* HPTEs are 2**4 bytes long */ + kvm->arch.hpt_npte = 1ul << (order - 4); + /* 128 (2**7) bytes in each HPTEG */ + kvm->arch.hpt_mask = (1ul << (order - 7)) - 1; /* Allocate reverse map array */ - rev = vmalloc(sizeof(struct revmap_entry) * HPT_NPTE); + rev = vmalloc(sizeof(struct revmap_entry) * kvm->arch.hpt_npte); if (!rev) { pr_err("kvmppc_alloc_hpt: Couldn't alloc reverse map array\n"); goto out_freehpt; } kvm->arch.revmap = rev; + kvm->arch.sdr1 = __pa(hpt) | (order - 18); - lpid = kvmppc_alloc_lpid(); - if (lpid < 0) - goto out_freeboth; + pr_info("KVM guest htab at %lx (order %ld), LPID %x\n", + hpt, order, kvm->arch.lpid); - kvm->arch.sdr1 = __pa(hpt) | (HPT_ORDER - 18); - kvm->arch.lpid = lpid; - - pr_info("KVM guest htab at %lx, LPID %lx\n", hpt, lpid); + if (htab_orderp) + *htab_orderp = order; return 0; - out_freeboth: - vfree(rev); out_freehpt: - free_pages(hpt, HPT_ORDER - PAGE_SHIFT); + if (kvm->arch.hpt_li) + kvm_release_hpt(kvm->arch.hpt_li); + else + free_pages(hpt, order - PAGE_SHIFT); return -ENOMEM; } +long kvmppc_alloc_reset_hpt(struct kvm *kvm, u32 *htab_orderp) +{ + long err = -EBUSY; + long order; + + mutex_lock(&kvm->lock); + if (kvm->arch.rma_setup_done) { + kvm->arch.rma_setup_done = 0; + /* order rma_setup_done vs. vcpus_running */ + smp_mb(); + if (atomic_read(&kvm->arch.vcpus_running)) { + kvm->arch.rma_setup_done = 1; + goto out; + } + } + if (kvm->arch.hpt_virt) { + order = kvm->arch.hpt_order; + /* Set the entire HPT to 0, i.e. invalid HPTEs */ + memset((void *)kvm->arch.hpt_virt, 0, 1ul << order); + /* + * Set the whole last_vcpu array to an invalid vcpu number. + * This ensures that each vcpu will flush its TLB on next entry. + */ + memset(kvm->arch.last_vcpu, 0xff, sizeof(kvm->arch.last_vcpu)); + *htab_orderp = order; + err = 0; + } else { + err = kvmppc_alloc_hpt(kvm, htab_orderp); + order = *htab_orderp; + } + out: + mutex_unlock(&kvm->lock); + return err; +} + void kvmppc_free_hpt(struct kvm *kvm) { kvmppc_free_lpid(kvm->arch.lpid); @@ -94,7 +159,8 @@ void kvmppc_free_hpt(struct kvm *kvm) if (kvm->arch.hpt_li) kvm_release_hpt(kvm->arch.hpt_li); else - free_pages(kvm->arch.hpt_virt, HPT_ORDER - PAGE_SHIFT); + free_pages(kvm->arch.hpt_virt, + kvm->arch.hpt_order - PAGE_SHIFT); } /* Bits in first HPTE dword for pagesize 4k, 64k or 16M */ @@ -119,6 +185,7 @@ void kvmppc_map_vrma(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot, unsigned long psize; unsigned long hp0, hp1; long ret; + struct kvm *kvm = vcpu->kvm; psize = 1ul << porder; npages = memslot->npages >> (porder - PAGE_SHIFT); @@ -127,8 +194,8 @@ void kvmppc_map_vrma(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot, if (npages > 1ul << (40 - porder)) npages = 1ul << (40 - porder); /* Can't use more than 1 HPTE per HPTEG */ - if (npages > HPT_NPTEG) - npages = HPT_NPTEG; + if (npages > kvm->arch.hpt_mask + 1) + npages = kvm->arch.hpt_mask + 1; hp0 = HPTE_V_1TB_SEG | (VRMA_VSID << (40 - 16)) | HPTE_V_BOLTED | hpte0_pgsize_encoding(psize); @@ -138,7 +205,7 @@ void kvmppc_map_vrma(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot, for (i = 0; i < npages; ++i) { addr = i << porder; /* can't use hpt_hash since va > 64 bits */ - hash = (i ^ (VRMA_VSID ^ (VRMA_VSID << 25))) & HPT_HASH_MASK; + hash = (i ^ (VRMA_VSID ^ (VRMA_VSID << 25))) & kvm->arch.hpt_mask; /* * We assume that the hash table is empty and no * vcpus are using it at this stage. Since we create diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index c6af1d623839..d084e412b3c5 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -56,7 +56,7 @@ /* #define EXIT_DEBUG_INT */ static void kvmppc_end_cede(struct kvm_vcpu *vcpu); -static int kvmppc_hv_setup_rma(struct kvm_vcpu *vcpu); +static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu); void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu) { @@ -1068,11 +1068,15 @@ int kvmppc_vcpu_run(struct kvm_run *run, struct kvm_vcpu *vcpu) return -EINTR; } - /* On the first time here, set up VRMA or RMA */ + atomic_inc(&vcpu->kvm->arch.vcpus_running); + /* Order vcpus_running vs. rma_setup_done, see kvmppc_alloc_reset_hpt */ + smp_mb(); + + /* On the first time here, set up HTAB and VRMA or RMA */ if (!vcpu->kvm->arch.rma_setup_done) { - r = kvmppc_hv_setup_rma(vcpu); + r = kvmppc_hv_setup_htab_rma(vcpu); if (r) - return r; + goto out; } flush_fp_to_thread(current); @@ -1090,6 +1094,9 @@ int kvmppc_vcpu_run(struct kvm_run *run, struct kvm_vcpu *vcpu) kvmppc_core_prepare_to_enter(vcpu); } } while (r == RESUME_GUEST); + + out: + atomic_dec(&vcpu->kvm->arch.vcpus_running); return r; } @@ -1305,7 +1312,7 @@ void kvmppc_core_commit_memory_region(struct kvm *kvm, { } -static int kvmppc_hv_setup_rma(struct kvm_vcpu *vcpu) +static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu) { int err = 0; struct kvm *kvm = vcpu->kvm; @@ -1324,6 +1331,15 @@ static int kvmppc_hv_setup_rma(struct kvm_vcpu *vcpu) if (kvm->arch.rma_setup_done) goto out; /* another vcpu beat us to it */ + /* Allocate hashed page table (if not done already) and reset it */ + if (!kvm->arch.hpt_virt) { + err = kvmppc_alloc_hpt(kvm, NULL); + if (err) { + pr_err("KVM: Couldn't alloc HPT\n"); + goto out; + } + } + /* Look up the memslot for guest physical address 0 */ memslot = gfn_to_memslot(kvm, 0); @@ -1435,13 +1451,14 @@ static int kvmppc_hv_setup_rma(struct kvm_vcpu *vcpu) int kvmppc_core_init_vm(struct kvm *kvm) { - long r; - unsigned long lpcr; + unsigned long lpcr, lpid; - /* Allocate hashed page table */ - r = kvmppc_alloc_hpt(kvm); - if (r) - return r; + /* Allocate the guest's logical partition ID */ + + lpid = kvmppc_alloc_lpid(); + if (lpid < 0) + return -ENOMEM; + kvm->arch.lpid = lpid; INIT_LIST_HEAD(&kvm->arch.spapr_tce_tables); @@ -1451,7 +1468,6 @@ int kvmppc_core_init_vm(struct kvm *kvm) if (cpu_has_feature(CPU_FTR_ARCH_201)) { /* PPC970; HID4 is effectively the LPCR */ - unsigned long lpid = kvm->arch.lpid; kvm->arch.host_lpid = 0; kvm->arch.host_lpcr = lpcr = mfspr(SPRN_HID4); lpcr &= ~((3 << HID4_LPID1_SH) | (0xful << HID4_LPID5_SH)); diff --git a/arch/powerpc/kvm/book3s_hv_builtin.c b/arch/powerpc/kvm/book3s_hv_builtin.c index e1b60f56f2a1..fb4eac290fef 100644 --- a/arch/powerpc/kvm/book3s_hv_builtin.c +++ b/arch/powerpc/kvm/book3s_hv_builtin.c @@ -25,6 +25,9 @@ static void __init kvm_linear_init_one(ulong size, int count, int type); static struct kvmppc_linear_info *kvm_alloc_linear(int type); static void kvm_release_linear(struct kvmppc_linear_info *ri); +int kvm_hpt_order = KVM_DEFAULT_HPT_ORDER; +EXPORT_SYMBOL_GPL(kvm_hpt_order); + /*************** RMA *************/ /* @@ -209,7 +212,7 @@ static void kvm_release_linear(struct kvmppc_linear_info *ri) void __init kvm_linear_init(void) { /* HPT */ - kvm_linear_init_one(1 << HPT_ORDER, kvm_hpt_count, KVM_LINEAR_HPT); + kvm_linear_init_one(1 << kvm_hpt_order, kvm_hpt_count, KVM_LINEAR_HPT); /* RMA */ /* Only do this on PPC970 in HV mode */ diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c b/arch/powerpc/kvm/book3s_hv_rm_mmu.c index cec4daddbf31..5c70d19494f9 100644 --- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c +++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c @@ -237,7 +237,7 @@ long kvmppc_h_enter(struct kvm_vcpu *vcpu, unsigned long flags, /* Find and lock the HPTEG slot to use */ do_insert: - if (pte_index >= HPT_NPTE) + if (pte_index >= kvm->arch.hpt_npte) return H_PARAMETER; if (likely((flags & H_EXACT) == 0)) { pte_index &= ~7UL; @@ -352,7 +352,7 @@ long kvmppc_h_remove(struct kvm_vcpu *vcpu, unsigned long flags, unsigned long v, r, rb; struct revmap_entry *rev; - if (pte_index >= HPT_NPTE) + if (pte_index >= kvm->arch.hpt_npte) return H_PARAMETER; hpte = (unsigned long *)(kvm->arch.hpt_virt + (pte_index << 4)); while (!try_lock_hpte(hpte, HPTE_V_HVLOCK)) @@ -419,7 +419,8 @@ long kvmppc_h_bulk_remove(struct kvm_vcpu *vcpu) i = 4; break; } - if (req != 1 || flags == 3 || pte_index >= HPT_NPTE) { + if (req != 1 || flags == 3 || + pte_index >= kvm->arch.hpt_npte) { /* parameter error */ args[j] = ((0xa0 | flags) << 56) + pte_index; ret = H_PARAMETER; @@ -521,7 +522,7 @@ long kvmppc_h_protect(struct kvm_vcpu *vcpu, unsigned long flags, struct revmap_entry *rev; unsigned long v, r, rb, mask, bits; - if (pte_index >= HPT_NPTE) + if (pte_index >= kvm->arch.hpt_npte) return H_PARAMETER; hpte = (unsigned long *)(kvm->arch.hpt_virt + (pte_index << 4)); @@ -583,7 +584,7 @@ long kvmppc_h_read(struct kvm_vcpu *vcpu, unsigned long flags, int i, n = 1; struct revmap_entry *rev = NULL; - if (pte_index >= HPT_NPTE) + if (pte_index >= kvm->arch.hpt_npte) return H_PARAMETER; if (flags & H_READ_4) { pte_index &= ~3; @@ -678,7 +679,7 @@ long kvmppc_hv_find_lock_hpte(struct kvm *kvm, gva_t eaddr, unsigned long slb_v, somask = (1UL << 28) - 1; vsid = (slb_v & ~SLB_VSID_B) >> SLB_VSID_SHIFT; } - hash = (vsid ^ ((eaddr & somask) >> pshift)) & HPT_HASH_MASK; + hash = (vsid ^ ((eaddr & somask) >> pshift)) & kvm->arch.hpt_mask; avpn = slb_v & ~(somask >> 16); /* also includes B */ avpn |= (eaddr & somask) >> 16; @@ -723,7 +724,7 @@ long kvmppc_hv_find_lock_hpte(struct kvm *kvm, gva_t eaddr, unsigned long slb_v, if (val & HPTE_V_SECONDARY) break; val |= HPTE_V_SECONDARY; - hash = hash ^ HPT_HASH_MASK; + hash = hash ^ kvm->arch.hpt_mask; } return -1; } diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 1493c8de947b..87f4dc886076 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -246,6 +246,7 @@ int kvm_dev_ioctl_check_extension(long ext) #endif #ifdef CONFIG_PPC_BOOK3S_64 case KVM_CAP_SPAPR_TCE: + case KVM_CAP_PPC_ALLOC_HTAB: r = 1; break; #endif /* CONFIG_PPC_BOOK3S_64 */ @@ -802,6 +803,23 @@ long kvm_arch_vm_ioctl(struct file *filp, r = -EFAULT; break; } + + case KVM_PPC_ALLOCATE_HTAB: { + struct kvm *kvm = filp->private_data; + u32 htab_order; + + r = -EFAULT; + if (get_user(htab_order, (u32 __user *)argp)) + break; + r = kvmppc_alloc_reset_hpt(kvm, &htab_order); + if (r) + break; + r = -EFAULT; + if (put_user(htab_order, (u32 __user *)argp)) + break; + r = 0; + break; + } #endif /* CONFIG_KVM_BOOK3S_64_HV */ #ifdef CONFIG_PPC_BOOK3S_64 diff --git a/include/linux/kvm.h b/include/linux/kvm.h index 09f2b3aa2da7..2ce09aa7d3b3 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -617,6 +617,7 @@ struct kvm_ppc_smmu_info { #define KVM_CAP_SIGNAL_MSI 77 #define KVM_CAP_PPC_GET_SMMU_INFO 78 #define KVM_CAP_S390_COW 79 +#define KVM_CAP_PPC_ALLOC_HTAB 80 #ifdef KVM_CAP_IRQ_ROUTING @@ -828,6 +829,8 @@ struct kvm_s390_ucas_mapping { #define KVM_SIGNAL_MSI _IOW(KVMIO, 0xa5, struct kvm_msi) /* Available with KVM_CAP_PPC_GET_SMMU_INFO */ #define KVM_PPC_GET_SMMU_INFO _IOR(KVMIO, 0xa6, struct kvm_ppc_smmu_info) +/* Available with KVM_CAP_PPC_ALLOC_HTAB */ +#define KVM_PPC_ALLOCATE_HTAB _IOWR(KVMIO, 0xa7, __u32) /* * ioctls for vcpu fds -- cgit v1.2.3 From ec3ab083a7a004282ee374bdaeb0aa603521b8eb Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 9 May 2012 10:09:56 -0500 Subject: slub: Get rid of the node field The node field is always page_to_nid(c->page). So its rather easy to replace. Note that there maybe slightly more overhead in various hot paths due to the need to shift the bits from page->flags. However, that is mostly compensated for by a smaller footprint of the kmem_cache_cpu structure (this patch reduces that to 3 words per cache) which allows better caching. Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/slub_def.h | 1 - mm/slub.c | 35 ++++++++++++++++------------------- 2 files changed, 16 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index c2f8c8bc56ed..ebdcf4ba42ee 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -48,7 +48,6 @@ struct kmem_cache_cpu { unsigned long tid; /* Globally unique transaction id */ struct page *page; /* The slab from which we are allocating */ struct page *partial; /* Partially allocated frozen slabs */ - int node; /* The node of the page (or -1 for debug) */ #ifdef CONFIG_SLUB_STATS unsigned stat[NR_SLUB_STAT_ITEMS]; #endif diff --git a/mm/slub.c b/mm/slub.c index b29246bc7392..aed879276410 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1561,7 +1561,6 @@ static void *get_partial_node(struct kmem_cache *s, if (!object) { c->page = page; - c->node = page_to_nid(page); stat(s, ALLOC_FROM_PARTIAL); object = t; available = page->objects - page->inuse; @@ -2057,7 +2056,7 @@ static void flush_all(struct kmem_cache *s) static inline int node_match(struct kmem_cache_cpu *c, int node) { #ifdef CONFIG_NUMA - if (node != NUMA_NO_NODE && c->node != node) + if (node != NUMA_NO_NODE && page_to_nid(c->page) != node) return 0; #endif return 1; @@ -2152,7 +2151,6 @@ static inline void *new_slab_objects(struct kmem_cache *s, gfp_t flags, page->freelist = NULL; stat(s, ALLOC_SLAB); - c->node = page_to_nid(page); c->page = page; *pc = c; } else @@ -2269,7 +2267,6 @@ new_slab: if (c->partial) { c->page = c->partial; c->partial = c->page->next; - c->node = page_to_nid(c->page); stat(s, CPU_PARTIAL_ALLOC); c->freelist = NULL; goto redo; @@ -2294,7 +2291,6 @@ new_slab: c->freelist = get_freepointer(s, freelist); deactivate_slab(s, c); - c->node = NUMA_NO_NODE; local_irq_restore(flags); return freelist; } @@ -4507,30 +4503,31 @@ static ssize_t show_slab_objects(struct kmem_cache *s, for_each_possible_cpu(cpu) { struct kmem_cache_cpu *c = per_cpu_ptr(s->cpu_slab, cpu); - int node = ACCESS_ONCE(c->node); + int node; struct page *page; - if (node < 0) - continue; page = ACCESS_ONCE(c->page); - if (page) { - if (flags & SO_TOTAL) - x = page->objects; - else if (flags & SO_OBJECTS) - x = page->inuse; - else - x = 1; + if (!page) + continue; - total += x; - nodes[node] += x; - } - page = c->partial; + node = page_to_nid(page); + if (flags & SO_TOTAL) + x = page->objects; + else if (flags & SO_OBJECTS) + x = page->inuse; + else + x = 1; + + total += x; + nodes[node] += x; + page = ACCESS_ONCE(c->partial); if (page) { x = page->pobjects; total += x; nodes[node] += x; } + per_cpu[node]++; } } -- cgit v1.2.3 From e5e372da9a469dfe3ece40277090a7056c566838 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 22 May 2012 11:41:43 -0500 Subject: libceph: eliminate connection state "DEAD" The ceph connection state "DEAD" is never set and is therefore not needed. Eliminate it. Signed-off-by: Alex Elder Reviewed-by: Yehuda Sadeh --- include/linux/ceph/messenger.h | 1 - net/ceph/messenger.c | 6 ------ 2 files changed, 7 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index 2521a95fa6d9..aa506cadea67 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -119,7 +119,6 @@ struct ceph_msg_pos { #define CLOSED 10 /* we've closed the connection */ #define SOCK_CLOSED 11 /* socket state changed to closed */ #define OPENING 13 /* open connection w/ (possibly new) peer */ -#define DEAD 14 /* dead, about to kfree */ #define BACKOFF 15 /* diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 1a80907282cc..42ca8aab6dcf 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -2087,12 +2087,6 @@ bad_tag: */ static void queue_con(struct ceph_connection *con) { - if (test_bit(DEAD, &con->state)) { - dout("queue_con %p ignoring: DEAD\n", - con); - return; - } - if (!con->ops->get(con)) { dout("queue_con %p ref count 0\n", con); return; -- cgit v1.2.3 From 6384bb8b8e88a9c6bf2ae0d9517c2c0199177c34 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 29 May 2012 21:47:38 -0500 Subject: libceph: kill bad_proto ceph connection op No code sets a bad_proto method in its ceph connection operations vector, so just get rid of it. Signed-off-by: Alex Elder Reviewed-by: Yehuda Sadeh --- include/linux/ceph/messenger.h | 3 --- net/ceph/messenger.c | 5 ----- 2 files changed, 8 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index aa506cadea67..74f6c9bd8074 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -31,9 +31,6 @@ struct ceph_connection_operations { int (*verify_authorizer_reply) (struct ceph_connection *con, int len); int (*invalidate_authorizer)(struct ceph_connection *con); - /* protocol version mismatch */ - void (*bad_proto) (struct ceph_connection *con); - /* there was some error on the socket (disconnect, whatever) */ void (*fault) (struct ceph_connection *con); diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 42ca8aab6dcf..07af9948e3f7 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -1356,11 +1356,6 @@ static void fail_protocol(struct ceph_connection *con) { reset_connection(con); set_bit(CLOSED, &con->state); /* in case there's queued work */ - - mutex_unlock(&con->mutex); - if (con->ops->bad_proto) - con->ops->bad_proto(con); - mutex_lock(&con->mutex); } static int process_connect(struct ceph_connection *con) -- cgit v1.2.3 From 15d9882c336db2db73ccf9871ae2398e452f694c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sat, 26 May 2012 23:26:43 -0500 Subject: libceph: embed ceph messenger structure in ceph_client A ceph client has a pointer to a ceph messenger structure in it. There is always exactly one ceph messenger for a ceph client, so there is no need to allocate it separate from the ceph client structure. Switch the ceph_client structure to embed its ceph_messenger structure. Signed-off-by: Alex Elder Reviewed-by: Yehuda Sadeh Reviewed-by: Sage Weil --- fs/ceph/mds_client.c | 2 +- include/linux/ceph/libceph.h | 2 +- include/linux/ceph/messenger.h | 9 +++++---- net/ceph/ceph_common.c | 18 +++++------------- net/ceph/messenger.c | 30 +++++++++--------------------- net/ceph/mon_client.c | 6 +++--- net/ceph/osd_client.c | 4 ++-- 7 files changed, 26 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 200bc87eceb1..ad30261cd4c0 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -394,7 +394,7 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc, s->s_seq = 0; mutex_init(&s->s_mutex); - ceph_con_init(mdsc->fsc->client->msgr, &s->s_con); + ceph_con_init(&mdsc->fsc->client->msgr, &s->s_con); s->s_con.private = s; s->s_con.ops = &mds_con_ops; s->s_con.peer_name.type = CEPH_ENTITY_TYPE_MDS; diff --git a/include/linux/ceph/libceph.h b/include/linux/ceph/libceph.h index 92eef7c3d3c5..927361c4b0a8 100644 --- a/include/linux/ceph/libceph.h +++ b/include/linux/ceph/libceph.h @@ -131,7 +131,7 @@ struct ceph_client { u32 supported_features; u32 required_features; - struct ceph_messenger *msgr; /* messenger instance */ + struct ceph_messenger msgr; /* messenger instance */ struct ceph_mon_client monc; struct ceph_osd_client osdc; diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index 74f6c9bd8074..3fbd4be804ed 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -211,10 +211,11 @@ extern int ceph_msgr_init(void); extern void ceph_msgr_exit(void); extern void ceph_msgr_flush(void); -extern struct ceph_messenger *ceph_messenger_create( - struct ceph_entity_addr *myaddr, - u32 features, u32 required); -extern void ceph_messenger_destroy(struct ceph_messenger *); +extern void ceph_messenger_init(struct ceph_messenger *msgr, + struct ceph_entity_addr *myaddr, + u32 supported_features, + u32 required_features, + bool nocrc); extern void ceph_con_init(struct ceph_messenger *msgr, struct ceph_connection *con); diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c index cc913193d992..2de3ea1bbd64 100644 --- a/net/ceph/ceph_common.c +++ b/net/ceph/ceph_common.c @@ -468,19 +468,15 @@ struct ceph_client *ceph_create_client(struct ceph_options *opt, void *private, /* msgr */ if (ceph_test_opt(client, MYIP)) myaddr = &client->options->my_addr; - client->msgr = ceph_messenger_create(myaddr, - client->supported_features, - client->required_features); - if (IS_ERR(client->msgr)) { - err = PTR_ERR(client->msgr); - goto fail; - } - client->msgr->nocrc = ceph_test_opt(client, NOCRC); + ceph_messenger_init(&client->msgr, myaddr, + client->supported_features, + client->required_features, + ceph_test_opt(client, NOCRC)); /* subsystems */ err = ceph_monc_init(&client->monc, client); if (err < 0) - goto fail_msgr; + goto fail; err = ceph_osdc_init(&client->osdc, client); if (err < 0) goto fail_monc; @@ -489,8 +485,6 @@ struct ceph_client *ceph_create_client(struct ceph_options *opt, void *private, fail_monc: ceph_monc_stop(&client->monc); -fail_msgr: - ceph_messenger_destroy(client->msgr); fail: kfree(client); return ERR_PTR(err); @@ -515,8 +509,6 @@ void ceph_destroy_client(struct ceph_client *client) ceph_debugfs_client_cleanup(client); - ceph_messenger_destroy(client->msgr); - ceph_destroy_options(client->options); kfree(client); diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 2ca491fc50e2..d8423a3f6698 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -2245,18 +2245,14 @@ out: /* - * create a new messenger instance + * initialize a new messenger instance */ -struct ceph_messenger *ceph_messenger_create(struct ceph_entity_addr *myaddr, - u32 supported_features, - u32 required_features) +void ceph_messenger_init(struct ceph_messenger *msgr, + struct ceph_entity_addr *myaddr, + u32 supported_features, + u32 required_features, + bool nocrc) { - struct ceph_messenger *msgr; - - msgr = kzalloc(sizeof(*msgr), GFP_KERNEL); - if (msgr == NULL) - return ERR_PTR(-ENOMEM); - msgr->supported_features = supported_features; msgr->required_features = required_features; @@ -2269,19 +2265,11 @@ struct ceph_messenger *ceph_messenger_create(struct ceph_entity_addr *myaddr, msgr->inst.addr.type = 0; get_random_bytes(&msgr->inst.addr.nonce, sizeof(msgr->inst.addr.nonce)); encode_my_addr(msgr); + msgr->nocrc = nocrc; - dout("messenger_create %p\n", msgr); - return msgr; -} -EXPORT_SYMBOL(ceph_messenger_create); - -void ceph_messenger_destroy(struct ceph_messenger *msgr) -{ - dout("destroy %p\n", msgr); - kfree(msgr); - dout("destroyed messenger %p\n", msgr); + dout("%s %p\n", __func__, msgr); } -EXPORT_SYMBOL(ceph_messenger_destroy); +EXPORT_SYMBOL(ceph_messenger_init); static void clear_standby(struct ceph_connection *con) { diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c index 1845cde26227..704dc95dc620 100644 --- a/net/ceph/mon_client.c +++ b/net/ceph/mon_client.c @@ -763,7 +763,7 @@ int ceph_monc_init(struct ceph_mon_client *monc, struct ceph_client *cl) monc->con = kmalloc(sizeof(*monc->con), GFP_KERNEL); if (!monc->con) goto out_monmap; - ceph_con_init(monc->client->msgr, monc->con); + ceph_con_init(&monc->client->msgr, monc->con); monc->con->private = monc; monc->con->ops = &mon_con_ops; @@ -880,8 +880,8 @@ static void handle_auth_reply(struct ceph_mon_client *monc, } else if (!was_auth && monc->auth->ops->is_authenticated(monc->auth)) { dout("authenticated, starting session\n"); - monc->client->msgr->inst.name.type = CEPH_ENTITY_TYPE_CLIENT; - monc->client->msgr->inst.name.num = + monc->client->msgr.inst.name.type = CEPH_ENTITY_TYPE_CLIENT; + monc->client->msgr.inst.name.num = cpu_to_le64(monc->auth->global_id); __send_subscribe(monc); diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index b098e7b591f0..cca4c7f1c780 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -639,7 +639,7 @@ static struct ceph_osd *create_osd(struct ceph_osd_client *osdc) INIT_LIST_HEAD(&osd->o_osd_lru); osd->o_incarnation = 1; - ceph_con_init(osdc->client->msgr, &osd->o_con); + ceph_con_init(&osdc->client->msgr, &osd->o_con); osd->o_con.private = osd; osd->o_con.ops = &osd_con_ops; osd->o_con.peer_name.type = CEPH_ENTITY_TYPE_OSD; @@ -1391,7 +1391,7 @@ void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg) epoch, maplen); newmap = osdmap_apply_incremental(&p, next, osdc->osdmap, - osdc->client->msgr); + &osdc->client->msgr); if (IS_ERR(newmap)) { err = PTR_ERR(newmap); goto bad; -- cgit v1.2.3 From 928443cd9644e7cfd46f687dbeffda2d1a357ff9 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 22 May 2012 11:41:43 -0500 Subject: libceph: start separating connection flags from state A ceph_connection holds a mixture of connection state (as in "state machine" state) and connection flags in a single "state" field. To make the distinction more clear, define a new "flags" field and use it rather than the "state" field to hold Boolean flag values. Signed-off-by: Alex Elder Reviewed-by: Sage Weil --- include/linux/ceph/messenger.h | 18 ++++++++++----- net/ceph/messenger.c | 50 +++++++++++++++++++++--------------------- 2 files changed, 37 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index 3fbd4be804ed..920235e114ad 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -103,20 +103,25 @@ struct ceph_msg_pos { #define MAX_DELAY_INTERVAL (5 * 60 * HZ) /* - * ceph_connection state bit flags + * ceph_connection flag bits */ + #define LOSSYTX 0 /* we can close channel or drop messages on errors */ -#define CONNECTING 1 -#define NEGOTIATING 2 #define KEEPALIVE_PENDING 3 #define WRITE_PENDING 4 /* we have data ready to send */ +#define SOCK_CLOSED 11 /* socket state changed to closed */ +#define BACKOFF 15 + +/* + * ceph_connection states + */ +#define CONNECTING 1 +#define NEGOTIATING 2 #define STANDBY 8 /* no outgoing messages, socket closed. we keep * the ceph_connection around to maintain shared * state with the peer. */ #define CLOSED 10 /* we've closed the connection */ -#define SOCK_CLOSED 11 /* socket state changed to closed */ #define OPENING 13 /* open connection w/ (possibly new) peer */ -#define BACKOFF 15 /* * A single connection with another host. @@ -133,7 +138,8 @@ struct ceph_connection { struct ceph_messenger *msgr; struct socket *sock; - unsigned long state; /* connection state (see flags above) */ + unsigned long flags; + unsigned long state; const char *error_msg; /* error message, if any */ struct ceph_entity_addr peer_addr; /* peer address */ diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index d8423a3f6698..e84e4fd86bb7 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -176,7 +176,7 @@ static void ceph_sock_write_space(struct sock *sk) * buffer. See net/ipv4/tcp_input.c:tcp_check_space() * and net/core/stream.c:sk_stream_write_space(). */ - if (test_bit(WRITE_PENDING, &con->state)) { + if (test_bit(WRITE_PENDING, &con->flags)) { if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) { dout("%s %p queueing write work\n", __func__, con); clear_bit(SOCK_NOSPACE, &sk->sk_socket->flags); @@ -203,7 +203,7 @@ static void ceph_sock_state_change(struct sock *sk) dout("%s TCP_CLOSE\n", __func__); case TCP_CLOSE_WAIT: dout("%s TCP_CLOSE_WAIT\n", __func__); - if (test_and_set_bit(SOCK_CLOSED, &con->state) == 0) { + if (test_and_set_bit(SOCK_CLOSED, &con->flags) == 0) { if (test_bit(CONNECTING, &con->state)) con->error_msg = "connection failed"; else @@ -395,9 +395,9 @@ void ceph_con_close(struct ceph_connection *con) ceph_pr_addr(&con->peer_addr.in_addr)); set_bit(CLOSED, &con->state); /* in case there's queued work */ clear_bit(STANDBY, &con->state); /* avoid connect_seq bump */ - clear_bit(LOSSYTX, &con->state); /* so we retry next connect */ - clear_bit(KEEPALIVE_PENDING, &con->state); - clear_bit(WRITE_PENDING, &con->state); + clear_bit(LOSSYTX, &con->flags); /* so we retry next connect */ + clear_bit(KEEPALIVE_PENDING, &con->flags); + clear_bit(WRITE_PENDING, &con->flags); mutex_lock(&con->mutex); reset_connection(con); con->peer_global_seq = 0; @@ -614,7 +614,7 @@ static void prepare_write_message(struct ceph_connection *con) prepare_write_message_footer(con); } - set_bit(WRITE_PENDING, &con->state); + set_bit(WRITE_PENDING, &con->flags); } /* @@ -635,7 +635,7 @@ static void prepare_write_ack(struct ceph_connection *con) &con->out_temp_ack); con->out_more = 1; /* more will follow.. eventually.. */ - set_bit(WRITE_PENDING, &con->state); + set_bit(WRITE_PENDING, &con->flags); } /* @@ -646,7 +646,7 @@ static void prepare_write_keepalive(struct ceph_connection *con) dout("prepare_write_keepalive %p\n", con); con_out_kvec_reset(con); con_out_kvec_add(con, sizeof (tag_keepalive), &tag_keepalive); - set_bit(WRITE_PENDING, &con->state); + set_bit(WRITE_PENDING, &con->flags); } /* @@ -675,7 +675,7 @@ static struct ceph_auth_handshake *get_connect_authorizer(struct ceph_connection if (IS_ERR(auth)) return auth; - if (test_bit(CLOSED, &con->state) || test_bit(OPENING, &con->state)) + if (test_bit(CLOSED, &con->state) || test_bit(OPENING, &con->flags)) return ERR_PTR(-EAGAIN); con->auth_reply_buf = auth->authorizer_reply_buf; @@ -695,7 +695,7 @@ static void prepare_write_banner(struct ceph_connection *con) &con->msgr->my_enc_addr); con->out_more = 0; - set_bit(WRITE_PENDING, &con->state); + set_bit(WRITE_PENDING, &con->flags); } static int prepare_write_connect(struct ceph_connection *con) @@ -745,7 +745,7 @@ static int prepare_write_connect(struct ceph_connection *con) auth->authorizer_buf); con->out_more = 0; - set_bit(WRITE_PENDING, &con->state); + set_bit(WRITE_PENDING, &con->flags); return 0; } @@ -1492,7 +1492,7 @@ static int process_connect(struct ceph_connection *con) le32_to_cpu(con->in_reply.connect_seq)); if (con->in_reply.flags & CEPH_MSG_CONNECT_LOSSY) - set_bit(LOSSYTX, &con->state); + set_bit(LOSSYTX, &con->flags); prepare_read_tag(con); break; @@ -1933,14 +1933,14 @@ do_next: prepare_write_ack(con); goto more; } - if (test_and_clear_bit(KEEPALIVE_PENDING, &con->state)) { + if (test_and_clear_bit(KEEPALIVE_PENDING, &con->flags)) { prepare_write_keepalive(con); goto more; } } /* Nothing to do! */ - clear_bit(WRITE_PENDING, &con->state); + clear_bit(WRITE_PENDING, &con->flags); dout("try_write nothing else to write.\n"); ret = 0; out: @@ -2106,7 +2106,7 @@ static void con_work(struct work_struct *work) mutex_lock(&con->mutex); restart: - if (test_and_clear_bit(BACKOFF, &con->state)) { + if (test_and_clear_bit(BACKOFF, &con->flags)) { dout("con_work %p backing off\n", con); if (queue_delayed_work(ceph_msgr_wq, &con->work, round_jiffies_relative(con->delay))) { @@ -2135,7 +2135,7 @@ restart: con_close_socket(con); } - if (test_and_clear_bit(SOCK_CLOSED, &con->state)) + if (test_and_clear_bit(SOCK_CLOSED, &con->flags)) goto fault; ret = try_read(con); @@ -2174,7 +2174,7 @@ static void ceph_fault(struct ceph_connection *con) dout("fault %p state %lu to peer %s\n", con, con->state, ceph_pr_addr(&con->peer_addr.in_addr)); - if (test_bit(LOSSYTX, &con->state)) { + if (test_bit(LOSSYTX, &con->flags)) { dout("fault on LOSSYTX channel\n"); goto out; } @@ -2196,9 +2196,9 @@ static void ceph_fault(struct ceph_connection *con) /* If there are no messages queued or keepalive pending, place * the connection in a STANDBY state */ if (list_empty(&con->out_queue) && - !test_bit(KEEPALIVE_PENDING, &con->state)) { + !test_bit(KEEPALIVE_PENDING, &con->flags)) { dout("fault %p setting STANDBY clearing WRITE_PENDING\n", con); - clear_bit(WRITE_PENDING, &con->state); + clear_bit(WRITE_PENDING, &con->flags); set_bit(STANDBY, &con->state); } else { /* retry after a delay. */ @@ -2222,7 +2222,7 @@ static void ceph_fault(struct ceph_connection *con) * that when con_work restarts we schedule the * delay then. */ - set_bit(BACKOFF, &con->state); + set_bit(BACKOFF, &con->flags); } } @@ -2278,8 +2278,8 @@ static void clear_standby(struct ceph_connection *con) mutex_lock(&con->mutex); dout("clear_standby %p and ++connect_seq\n", con); con->connect_seq++; - WARN_ON(test_bit(WRITE_PENDING, &con->state)); - WARN_ON(test_bit(KEEPALIVE_PENDING, &con->state)); + WARN_ON(test_bit(WRITE_PENDING, &con->flags)); + WARN_ON(test_bit(KEEPALIVE_PENDING, &con->flags)); mutex_unlock(&con->mutex); } } @@ -2317,7 +2317,7 @@ void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg) /* if there wasn't anything waiting to send before, queue * new work */ clear_standby(con); - if (test_and_set_bit(WRITE_PENDING, &con->state) == 0) + if (test_and_set_bit(WRITE_PENDING, &con->flags) == 0) queue_con(con); } EXPORT_SYMBOL(ceph_con_send); @@ -2384,8 +2384,8 @@ void ceph_con_keepalive(struct ceph_connection *con) { dout("con_keepalive %p\n", con); clear_standby(con); - if (test_and_set_bit(KEEPALIVE_PENDING, &con->state) == 0 && - test_and_set_bit(WRITE_PENDING, &con->state) == 0) + if (test_and_set_bit(KEEPALIVE_PENDING, &con->flags) == 0 && + test_and_set_bit(WRITE_PENDING, &con->flags) == 0) queue_con(con); } EXPORT_SYMBOL(ceph_con_keepalive); -- cgit v1.2.3 From ce2c8903e76e690846a00a0284e4bd9ee954d680 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 22 May 2012 22:15:49 -0500 Subject: libceph: start tracking connection socket state Start explicitly keeping track of the state of a ceph connection's socket, separate from the state of the connection itself. Create placeholder functions to encapsulate the state transitions. -------- | NEW* | transient initial state -------- | con_sock_state_init() v ---------- | CLOSED | initialized, but no socket (and no ---------- TCP connection) ^ \ | \ con_sock_state_connecting() | ---------------------- | \ + con_sock_state_closed() \ |\ \ | \ \ | ----------- \ | | CLOSING | socket event; \ | ----------- await close \ | ^ | | | | | + con_sock_state_closing() | | / \ | | / --------------- | | / \ v | / -------------- | / -----------------| CONNECTING | socket created, TCP | | / -------------- connect initiated | | | con_sock_state_connected() | | v ------------- | CONNECTED | TCP connection established ------------- Make the socket state an atomic variable, reinforcing that it's a distinct transtion with no possible "intermediate/both" states. This is almost certainly overkill at this point, though the transitions into CONNECTED and CLOSING state do get called via socket callback (the rest of the transitions occur with the connection mutex held). We can back out the atomicity later. Signed-off-by: Alex Elder Reviewed-by: Sage Weil --- include/linux/ceph/messenger.h | 8 ++++-- net/ceph/messenger.c | 64 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index 920235e114ad..5e852f444f68 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -137,14 +137,18 @@ struct ceph_connection { const struct ceph_connection_operations *ops; struct ceph_messenger *msgr; + + atomic_t sock_state; struct socket *sock; + struct ceph_entity_addr peer_addr; /* peer address */ + struct ceph_entity_addr peer_addr_for_me; + unsigned long flags; unsigned long state; const char *error_msg; /* error message, if any */ - struct ceph_entity_addr peer_addr; /* peer address */ struct ceph_entity_name peer_name; /* peer name */ - struct ceph_entity_addr peer_addr_for_me; + unsigned peer_features; u32 connect_seq; /* identify the most recent connection attempt for this connection, client */ diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index e84e4fd86bb7..a4ac3deec161 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -29,6 +29,14 @@ * the sender. */ +/* State values for ceph_connection->sock_state; NEW is assumed to be 0 */ + +#define CON_SOCK_STATE_NEW 0 /* -> CLOSED */ +#define CON_SOCK_STATE_CLOSED 1 /* -> CONNECTING */ +#define CON_SOCK_STATE_CONNECTING 2 /* -> CONNECTED or -> CLOSING */ +#define CON_SOCK_STATE_CONNECTED 3 /* -> CLOSING or -> CLOSED */ +#define CON_SOCK_STATE_CLOSING 4 /* -> CLOSED */ + /* static tag bytes (protocol control messages) */ static char tag_msg = CEPH_MSGR_TAG_MSG; static char tag_ack = CEPH_MSGR_TAG_ACK; @@ -147,6 +155,55 @@ void ceph_msgr_flush(void) } EXPORT_SYMBOL(ceph_msgr_flush); +/* Connection socket state transition functions */ + +static void con_sock_state_init(struct ceph_connection *con) +{ + int old_state; + + old_state = atomic_xchg(&con->sock_state, CON_SOCK_STATE_CLOSED); + if (WARN_ON(old_state != CON_SOCK_STATE_NEW)) + printk("%s: unexpected old state %d\n", __func__, old_state); +} + +static void con_sock_state_connecting(struct ceph_connection *con) +{ + int old_state; + + old_state = atomic_xchg(&con->sock_state, CON_SOCK_STATE_CONNECTING); + if (WARN_ON(old_state != CON_SOCK_STATE_CLOSED)) + printk("%s: unexpected old state %d\n", __func__, old_state); +} + +static void con_sock_state_connected(struct ceph_connection *con) +{ + int old_state; + + old_state = atomic_xchg(&con->sock_state, CON_SOCK_STATE_CONNECTED); + if (WARN_ON(old_state != CON_SOCK_STATE_CONNECTING)) + printk("%s: unexpected old state %d\n", __func__, old_state); +} + +static void con_sock_state_closing(struct ceph_connection *con) +{ + int old_state; + + old_state = atomic_xchg(&con->sock_state, CON_SOCK_STATE_CLOSING); + if (WARN_ON(old_state != CON_SOCK_STATE_CONNECTING && + old_state != CON_SOCK_STATE_CONNECTED && + old_state != CON_SOCK_STATE_CLOSING)) + printk("%s: unexpected old state %d\n", __func__, old_state); +} + +static void con_sock_state_closed(struct ceph_connection *con) +{ + int old_state; + + old_state = atomic_xchg(&con->sock_state, CON_SOCK_STATE_CLOSED); + if (WARN_ON(old_state != CON_SOCK_STATE_CONNECTED && + old_state != CON_SOCK_STATE_CLOSING)) + printk("%s: unexpected old state %d\n", __func__, old_state); +} /* * socket callback functions @@ -203,6 +260,7 @@ static void ceph_sock_state_change(struct sock *sk) dout("%s TCP_CLOSE\n", __func__); case TCP_CLOSE_WAIT: dout("%s TCP_CLOSE_WAIT\n", __func__); + con_sock_state_closing(con); if (test_and_set_bit(SOCK_CLOSED, &con->flags) == 0) { if (test_bit(CONNECTING, &con->state)) con->error_msg = "connection failed"; @@ -213,6 +271,7 @@ static void ceph_sock_state_change(struct sock *sk) break; case TCP_ESTABLISHED: dout("%s TCP_ESTABLISHED\n", __func__); + con_sock_state_connected(con); queue_con(con); break; default: /* Everything else is uninteresting */ @@ -277,6 +336,7 @@ static int ceph_tcp_connect(struct ceph_connection *con) return ret; } con->sock = sock; + con_sock_state_connecting(con); return 0; } @@ -343,6 +403,7 @@ static int con_close_socket(struct ceph_connection *con) sock_release(con->sock); con->sock = NULL; clear_bit(SOCK_CLOSED, &con->state); + con_sock_state_closed(con); return rc; } @@ -462,6 +523,9 @@ void ceph_con_init(struct ceph_messenger *msgr, struct ceph_connection *con) memset(con, 0, sizeof(*con)); atomic_set(&con->nref, 1); con->msgr = msgr; + + con_sock_state_init(con); + mutex_init(&con->mutex); INIT_LIST_HEAD(&con->out_queue); INIT_LIST_HEAD(&con->out_sent); -- cgit v1.2.3 From 86ed4bc83abf530cf2019044b74f89a39dfd6425 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 3 May 2012 11:08:19 +0800 Subject: ACPICA: Add support for multiple notify handlers This change adds support to allow multiple notify handlers on Device, ThermalZone, and Processor objects. Also re-worked and restructured the entire notify support code for handler installation, handler removal, notify event queuing, and notify dispatch to handler. Extends and updates original commit 3f0be67("ACPI / ACPICA: Multiple system notify handlers per device") by Rafael Wysocki. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/acglobal.h | 3 +- drivers/acpi/acpica/aclocal.h | 13 +- drivers/acpi/acpica/acobject.h | 9 +- drivers/acpi/acpica/evmisc.c | 185 +++++++---------- drivers/acpi/acpica/evxface.c | 440 ++++++++++++++--------------------------- drivers/acpi/acpica/exdump.c | 25 ++- drivers/acpi/acpica/utdelete.c | 24 ++- drivers/acpi/acpica/utglobal.c | 4 +- include/acpi/actypes.h | 4 + 9 files changed, 266 insertions(+), 441 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index 4f7d3f57d05c..dec7994d405c 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -278,8 +278,7 @@ ACPI_EXTERN acpi_cache_t *acpi_gbl_operand_cache; /* Global handlers */ -ACPI_EXTERN struct acpi_object_notify_handler acpi_gbl_device_notify; -ACPI_EXTERN struct acpi_object_notify_handler acpi_gbl_system_notify; +ACPI_EXTERN struct acpi_global_notify_handler acpi_gbl_global_notify[2]; ACPI_EXTERN acpi_exception_handler acpi_gbl_exception_handler; ACPI_EXTERN acpi_init_handler acpi_gbl_init_handler; ACPI_EXTERN acpi_tbl_handler acpi_gbl_table_handler; diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index e3922ca20e7f..28f677834bfd 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -600,13 +600,22 @@ acpi_status(*acpi_parse_downwards) (struct acpi_walk_state * walk_state, typedef acpi_status(*acpi_parse_upwards) (struct acpi_walk_state * walk_state); +/* Global handlers for AML Notifies */ + +struct acpi_global_notify_handler { + acpi_notify_handler handler; + void *context; +}; + /* * Notify info - used to pass info to the deferred notify * handler/dispatcher. */ struct acpi_notify_info { - ACPI_STATE_COMMON struct acpi_namespace_node *node; - union acpi_operand_object *handler_obj; + ACPI_STATE_COMMON u8 handler_list_id; + struct acpi_namespace_node *node; + union acpi_operand_object *handler_list_head; + struct acpi_global_notify_handler *global; }; /* Generic state is union of structs above */ diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h index c065078ca83b..39a2b84120be 100644 --- a/drivers/acpi/acpica/acobject.h +++ b/drivers/acpi/acpica/acobject.h @@ -206,8 +206,7 @@ struct acpi_object_method { * Common fields for objects that support ASL notifications */ #define ACPI_COMMON_NOTIFY_INFO \ - union acpi_operand_object *system_notify; /* Handler for system notifies */\ - union acpi_operand_object *device_notify; /* Handler for driver notifies */\ + union acpi_operand_object *notify_list[2]; /* Handlers for system/device notifies */\ union acpi_operand_object *handler; /* Handler for Address space */ struct acpi_object_notify_common { /* COMMON NOTIFY for POWER, PROCESSOR, DEVICE, and THERMAL */ @@ -296,10 +295,10 @@ struct acpi_object_buffer_field { struct acpi_object_notify_handler { ACPI_OBJECT_COMMON_HEADER struct acpi_namespace_node *node; /* Parent device */ - u32 handler_type; - acpi_notify_handler handler; + u32 handler_type; /* Type: Device/System/Both */ + acpi_notify_handler handler; /* Handler address */ void *context; - struct acpi_object_notify_handler *next; + union acpi_operand_object *next[2]; /* Device and System handler lists */ }; struct acpi_object_addr_handler { diff --git a/drivers/acpi/acpica/evmisc.c b/drivers/acpi/acpica/evmisc.c index 51ef9f5e002d..381fce93a561 100644 --- a/drivers/acpi/acpica/evmisc.c +++ b/drivers/acpi/acpica/evmisc.c @@ -101,102 +101,77 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node * node, u32 notify_value) { union acpi_operand_object *obj_desc; - union acpi_operand_object *handler_obj = NULL; - union acpi_generic_state *notify_info; + union acpi_operand_object *handler_list_head = NULL; + union acpi_generic_state *info; + u8 handler_list_id = 0; acpi_status status = AE_OK; ACPI_FUNCTION_NAME(ev_queue_notify_request); - /* - * For value 0x03 (Ejection Request), may need to run a device method. - * For value 0x02 (Device Wake), if _PRW exists, may need to run - * the _PS0 method. - * For value 0x80 (Status Change) on the power button or sleep button, - * initiate soft-off or sleep operation. - * - * For all cases, simply dispatch the notify to the handler. - */ - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Dispatching Notify on [%4.4s] (%s) Value 0x%2.2X (%s) Node %p\n", - acpi_ut_get_node_name(node), - acpi_ut_get_type_name(node->type), notify_value, - acpi_ut_get_notify_name(notify_value), node)); + /* Are Notifies allowed on this object? */ - /* Get the notify object attached to the NS Node */ - - obj_desc = acpi_ns_get_attached_object(node); - if (obj_desc) { - - /* We have the notify object, Get the correct handler */ - - switch (node->type) { + if (!acpi_ev_is_notify_object(node)) { + return (AE_TYPE); + } - /* Notify is allowed only on these types */ + /* Get the correct notify list type (System or Device) */ - case ACPI_TYPE_DEVICE: - case ACPI_TYPE_THERMAL: - case ACPI_TYPE_PROCESSOR: + if (notify_value <= ACPI_MAX_SYS_NOTIFY) { + handler_list_id = ACPI_SYSTEM_HANDLER_LIST; + } else { + handler_list_id = ACPI_DEVICE_HANDLER_LIST; + } - if (notify_value <= ACPI_MAX_SYS_NOTIFY) { - handler_obj = - obj_desc->common_notify.system_notify; - } else { - handler_obj = - obj_desc->common_notify.device_notify; - } - break; + /* Get the notify object attached to the namespace Node */ - default: + obj_desc = acpi_ns_get_attached_object(node); + if (obj_desc) { - /* All other types are not supported */ + /* We have an attached object, Get the correct handler list */ - return (AE_TYPE); - } + handler_list_head = + obj_desc->common_notify.notify_list[handler_list_id]; } /* - * If there is a handler to run, schedule the dispatcher. - * Check for: - * 1) Global system notify handler - * 2) Global device notify handler - * 3) Per-device notify handler + * If there is no notify handler (Global or Local) + * for this object, just ignore the notify */ - if ((acpi_gbl_system_notify.handler && - (notify_value <= ACPI_MAX_SYS_NOTIFY)) || - (acpi_gbl_device_notify.handler && - (notify_value > ACPI_MAX_SYS_NOTIFY)) || handler_obj) { - notify_info = acpi_ut_create_generic_state(); - if (!notify_info) { - return (AE_NO_MEMORY); - } + if (!acpi_gbl_global_notify[handler_list_id].handler + && !handler_list_head) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "No notify handler for Notify, ignoring (%4.4s, %X) node %p\n", + acpi_ut_get_node_name(node), notify_value, + node)); - if (!handler_obj) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Executing system notify handler for Notify (%4.4s, %X) " - "node %p\n", - acpi_ut_get_node_name(node), - notify_value, node)); - } + return (AE_OK); + } - notify_info->common.descriptor_type = - ACPI_DESC_TYPE_STATE_NOTIFY; - notify_info->notify.node = node; - notify_info->notify.value = (u16) notify_value; - notify_info->notify.handler_obj = handler_obj; + /* Setup notify info and schedule the notify dispatcher */ - status = - acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_ev_notify_dispatch, - notify_info); - if (ACPI_FAILURE(status)) { - acpi_ut_delete_generic_state(notify_info); - } - } else { - /* There is no notify handler (per-device or system) for this device */ + info = acpi_ut_create_generic_state(); + if (!info) { + return (AE_NO_MEMORY); + } - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "No notify handler for Notify (%4.4s, %X) node %p\n", - acpi_ut_get_node_name(node), notify_value, - node)); + info->common.descriptor_type = ACPI_DESC_TYPE_STATE_NOTIFY; + + info->notify.node = node; + info->notify.value = (u16)notify_value; + info->notify.handler_list_id = handler_list_id; + info->notify.handler_list_head = handler_list_head; + info->notify.global = &acpi_gbl_global_notify[handler_list_id]; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Dispatching Notify on [%4.4s] (%s) Value 0x%2.2X (%s) Node %p\n", + acpi_ut_get_node_name(node), + acpi_ut_get_type_name(node->type), notify_value, + acpi_ut_get_notify_name(notify_value), node)); + + status = acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_ev_notify_dispatch, + info); + if (ACPI_FAILURE(status)) { + acpi_ut_delete_generic_state(info); } return (status); @@ -217,60 +192,34 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node * node, static void ACPI_SYSTEM_XFACE acpi_ev_notify_dispatch(void *context) { - union acpi_generic_state *notify_info = - (union acpi_generic_state *)context; - acpi_notify_handler global_handler = NULL; - void *global_context = NULL; + union acpi_generic_state *info = (union acpi_generic_state *)context; union acpi_operand_object *handler_obj; ACPI_FUNCTION_ENTRY(); - /* - * We will invoke a global notify handler if installed. This is done - * _before_ we invoke the per-device handler attached to the device. - */ - if (notify_info->notify.value <= ACPI_MAX_SYS_NOTIFY) { - - /* Global system notification handler */ - - if (acpi_gbl_system_notify.handler) { - global_handler = acpi_gbl_system_notify.handler; - global_context = acpi_gbl_system_notify.context; - } - } else { - /* Global driver notification handler */ - - if (acpi_gbl_device_notify.handler) { - global_handler = acpi_gbl_device_notify.handler; - global_context = acpi_gbl_device_notify.context; - } - } - - /* Invoke the system handler first, if present */ + /* Invoke a global notify handler if installed */ - if (global_handler) { - global_handler(notify_info->notify.node, - notify_info->notify.value, global_context); + if (info->notify.global->handler) { + info->notify.global->handler(info->notify.node, + info->notify.value, + info->notify.global->context); } - /* Now invoke the per-device handler, if present */ + /* Now invoke the local notify handler(s) if any are installed */ - handler_obj = notify_info->notify.handler_obj; - if (handler_obj) { - struct acpi_object_notify_handler *notifier; + handler_obj = info->notify.handler_list_head; + while (handler_obj) { + handler_obj->notify.handler(info->notify.node, + info->notify.value, + handler_obj->notify.context); - notifier = &handler_obj->notify; - while (notifier) { - notifier->handler(notify_info->notify.node, - notify_info->notify.value, - notifier->context); - notifier = notifier->next; - } + handler_obj = + handler_obj->notify.next[info->notify.handler_list_id]; } /* All done with the info object */ - acpi_ut_delete_generic_state(notify_info); + acpi_ut_delete_generic_state(info); } #if (!ACPI_REDUCED_HARDWARE) diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index 44bef5744ebb..90ae6d193645 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -52,88 +52,27 @@ ACPI_MODULE_NAME("evxface") -/******************************************************************************* - * - * FUNCTION: acpi_populate_handler_object - * - * PARAMETERS: handler_obj - Handler object to populate - * handler_type - The type of handler: - * ACPI_SYSTEM_NOTIFY: system_handler (00-7f) - * ACPI_DEVICE_NOTIFY: driver_handler (80-ff) - * ACPI_ALL_NOTIFY: both system and device - * handler - Address of the handler - * context - Value passed to the handler on each GPE - * next - Address of a handler object to link to - * - * RETURN: None - * - * DESCRIPTION: Populate a handler object. - * - ******************************************************************************/ -static void -acpi_populate_handler_object(struct acpi_object_notify_handler *handler_obj, - u32 handler_type, - acpi_notify_handler handler, void *context, - struct acpi_object_notify_handler *next) -{ - handler_obj->handler_type = handler_type; - handler_obj->handler = handler; - handler_obj->context = context; - handler_obj->next = next; -} - -/******************************************************************************* - * - * FUNCTION: acpi_add_handler_object - * - * PARAMETERS: parent_obj - Parent of the new object - * handler - Address of the handler - * context - Value passed to the handler on each GPE - * - * RETURN: Status - * - * DESCRIPTION: Create a new handler object and populate it. - * - ******************************************************************************/ -static acpi_status -acpi_add_handler_object(struct acpi_object_notify_handler *parent_obj, - acpi_notify_handler handler, void *context) -{ - struct acpi_object_notify_handler *handler_obj; - - /* The parent must not be a defice notify handler object. */ - if (parent_obj->handler_type & ACPI_DEVICE_NOTIFY) - return AE_BAD_PARAMETER; - - handler_obj = ACPI_ALLOCATE_ZEROED(sizeof(*handler_obj)); - if (!handler_obj) - return AE_NO_MEMORY; - - acpi_populate_handler_object(handler_obj, - ACPI_SYSTEM_NOTIFY, - handler, context, - parent_obj->next); - parent_obj->next = handler_obj; - - return AE_OK; -} - - /******************************************************************************* * * FUNCTION: acpi_install_notify_handler * * PARAMETERS: Device - The device for which notifies will be handled * handler_type - The type of handler: - * ACPI_SYSTEM_NOTIFY: system_handler (00-7f) - * ACPI_DEVICE_NOTIFY: driver_handler (80-ff) - * ACPI_ALL_NOTIFY: both system and device + * ACPI_SYSTEM_NOTIFY: System Handler (00-7F) + * ACPI_DEVICE_NOTIFY: Device Handler (80-FF) + * ACPI_ALL_NOTIFY: Both System and Device * Handler - Address of the handler * Context - Value passed to the handler on each GPE * * RETURN: Status * - * DESCRIPTION: Install a handler for notifies on an ACPI device + * DESCRIPTION: Install a handler for notifications on an ACPI Device, + * thermal_zone, or Processor object. + * + * NOTES: The Root namespace object may have only one handler for each + * type of notify (System/Device). Device/Thermal/Processor objects + * may have one device notify handler, and multiple system notify + * handlers. * ******************************************************************************/ acpi_status @@ -141,17 +80,19 @@ acpi_install_notify_handler(acpi_handle device, u32 handler_type, acpi_notify_handler handler, void *context) { + struct acpi_namespace_node *node = + ACPI_CAST_PTR(struct acpi_namespace_node, device); union acpi_operand_object *obj_desc; - union acpi_operand_object *notify_obj; - struct acpi_namespace_node *node; + union acpi_operand_object *handler_obj; acpi_status status; + u32 i; ACPI_FUNCTION_TRACE(acpi_install_notify_handler); /* Parameter validation */ - if ((!device) || - (!handler) || (handler_type > ACPI_MAX_NOTIFY_HANDLER_TYPE)) { + if ((!device) || (!handler) || (!handler_type) || + (handler_type > ACPI_MAX_NOTIFY_HANDLER_TYPE)) { return_ACPI_STATUS(AE_BAD_PARAMETER); } @@ -160,144 +101,112 @@ acpi_install_notify_handler(acpi_handle device, return_ACPI_STATUS(status); } - /* Convert and validate the device handle */ - - node = acpi_ns_validate_handle(device); - if (!node) { - status = AE_BAD_PARAMETER; - goto unlock_and_exit; - } - /* * Root Object: * Registering a notify handler on the root object indicates that the * caller wishes to receive notifications for all objects. Note that - * only one global handler can be regsitered (per notify type). + * only one global handler can be registered per notify type. + * Ensure that a handler is not already installed. */ if (device == ACPI_ROOT_OBJECT) { + for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) { + if (handler_type & (i + 1)) { + if (acpi_gbl_global_notify[i].handler) { + status = AE_ALREADY_EXISTS; + goto unlock_and_exit; + } - /* Make sure the handler is not already installed */ - - if (((handler_type & ACPI_SYSTEM_NOTIFY) && - acpi_gbl_system_notify.handler) || - ((handler_type & ACPI_DEVICE_NOTIFY) && - acpi_gbl_device_notify.handler)) { - status = AE_ALREADY_EXISTS; - goto unlock_and_exit; - } - - if (handler_type & ACPI_SYSTEM_NOTIFY) { - acpi_gbl_system_notify.node = node; - acpi_gbl_system_notify.handler = handler; - acpi_gbl_system_notify.context = context; - } - - if (handler_type & ACPI_DEVICE_NOTIFY) { - acpi_gbl_device_notify.node = node; - acpi_gbl_device_notify.handler = handler; - acpi_gbl_device_notify.context = context; + acpi_gbl_global_notify[i].handler = handler; + acpi_gbl_global_notify[i].context = context; + } } - /* Global notify handler installed */ + goto unlock_and_exit; /* Global notify handler installed, all done */ } /* * All Other Objects: - * Caller will only receive notifications specific to the target object. - * Note that only certain object types can receive notifications. + * Caller will only receive notifications specific to the target + * object. Note that only certain object types are allowed to + * receive notifications. */ - else { - /* Notifies allowed on this object? */ - if (!acpi_ev_is_notify_object(node)) { - status = AE_TYPE; - goto unlock_and_exit; - } + /* Are Notifies allowed on this object? */ - /* Check for an existing internal object */ + if (!acpi_ev_is_notify_object(node)) { + status = AE_TYPE; + goto unlock_and_exit; + } - obj_desc = acpi_ns_get_attached_object(node); - if (obj_desc) { + /* Check for an existing internal object, might not exist */ - /* Object exists. */ + obj_desc = acpi_ns_get_attached_object(node); + if (!obj_desc) { - /* For a device notify, make sure there's no handler. */ - if ((handler_type & ACPI_DEVICE_NOTIFY) && - obj_desc->common_notify.device_notify) { - status = AE_ALREADY_EXISTS; - goto unlock_and_exit; - } + /* Create a new object */ - /* System notifies may have more handlers installed. */ - notify_obj = obj_desc->common_notify.system_notify; + obj_desc = acpi_ut_create_internal_object(node->type); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } - if ((handler_type & ACPI_SYSTEM_NOTIFY) && notify_obj) { - struct acpi_object_notify_handler *parent_obj; + /* Attach new object to the Node, remove local reference */ - if (handler_type & ACPI_DEVICE_NOTIFY) { + status = acpi_ns_attach_object(device, obj_desc, node->type); + acpi_ut_remove_reference(obj_desc); + if (ACPI_FAILURE(status)) { + goto unlock_and_exit; + } + } + + /* Ensure that the handler is not already installed in the lists */ + + for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) { + if (handler_type & (i + 1)) { + handler_obj = obj_desc->common_notify.notify_list[i]; + while (handler_obj) { + if (handler_obj->notify.handler == handler) { status = AE_ALREADY_EXISTS; goto unlock_and_exit; } - parent_obj = ¬ify_obj->notify; - status = acpi_add_handler_object(parent_obj, - handler, - context); - goto unlock_and_exit; - } - } else { - /* Create a new object */ - - obj_desc = acpi_ut_create_internal_object(node->type); - if (!obj_desc) { - status = AE_NO_MEMORY; - goto unlock_and_exit; - } - - /* Attach new object to the Node */ - - status = - acpi_ns_attach_object(device, obj_desc, node->type); - - /* Remove local reference to the object */ - - acpi_ut_remove_reference(obj_desc); - if (ACPI_FAILURE(status)) { - goto unlock_and_exit; + handler_obj = handler_obj->notify.next[i]; } } + } - /* Install the handler */ + /* Create and populate a new notify handler object */ - notify_obj = - acpi_ut_create_internal_object(ACPI_TYPE_LOCAL_NOTIFY); - if (!notify_obj) { - status = AE_NO_MEMORY; - goto unlock_and_exit; - } + handler_obj = acpi_ut_create_internal_object(ACPI_TYPE_LOCAL_NOTIFY); + if (!handler_obj) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } - acpi_populate_handler_object(¬ify_obj->notify, - handler_type, - handler, context, - NULL); + handler_obj->notify.node = node; + handler_obj->notify.handler_type = handler_type; + handler_obj->notify.handler = handler; + handler_obj->notify.context = context; - if (handler_type & ACPI_SYSTEM_NOTIFY) { - obj_desc->common_notify.system_notify = notify_obj; - } + /* Install the handler at the list head(s) */ - if (handler_type & ACPI_DEVICE_NOTIFY) { - obj_desc->common_notify.device_notify = notify_obj; - } + for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) { + if (handler_type & (i + 1)) { + handler_obj->notify.next[i] = + obj_desc->common_notify.notify_list[i]; - if (handler_type == ACPI_ALL_NOTIFY) { + obj_desc->common_notify.notify_list[i] = handler_obj; + } + } - /* Extra ref if installed in both */ + /* Add an extra reference if handler was installed in both lists */ - acpi_ut_add_reference(notify_obj); - } + if (handler_type == ACPI_ALL_NOTIFY) { + acpi_ut_add_reference(handler_obj); } - unlock_and_exit: +unlock_and_exit: (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); return_ACPI_STATUS(status); } @@ -308,11 +217,11 @@ ACPI_EXPORT_SYMBOL(acpi_install_notify_handler) * * FUNCTION: acpi_remove_notify_handler * - * PARAMETERS: Device - The device for which notifies will be handled + * PARAMETERS: Device - The device for which the handler is installed * handler_type - The type of handler: - * ACPI_SYSTEM_NOTIFY: system_handler (00-7f) - * ACPI_DEVICE_NOTIFY: driver_handler (80-ff) - * ACPI_ALL_NOTIFY: both system and device + * ACPI_SYSTEM_NOTIFY: System Handler (00-7F) + * ACPI_DEVICE_NOTIFY: Device Handler (80-FF) + * ACPI_ALL_NOTIFY: Both System and Device * Handler - Address of the handler * * RETURN: Status @@ -324,165 +233,106 @@ acpi_status acpi_remove_notify_handler(acpi_handle device, u32 handler_type, acpi_notify_handler handler) { - union acpi_operand_object *notify_obj; + struct acpi_namespace_node *node = + ACPI_CAST_PTR(struct acpi_namespace_node, device); union acpi_operand_object *obj_desc; - struct acpi_namespace_node *node; + union acpi_operand_object *handler_obj; + union acpi_operand_object *previous_handler_obj; acpi_status status; + u32 i; ACPI_FUNCTION_TRACE(acpi_remove_notify_handler); /* Parameter validation */ - if ((!device) || - (!handler) || (handler_type > ACPI_MAX_NOTIFY_HANDLER_TYPE)) { - status = AE_BAD_PARAMETER; - goto exit; + if ((!device) || (!handler) || (!handler_type) || + (handler_type > ACPI_MAX_NOTIFY_HANDLER_TYPE)) { + return_ACPI_STATUS(AE_BAD_PARAMETER); } - - /* Make sure all deferred tasks are completed */ + acpi_os_wait_events_complete(NULL); status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); if (ACPI_FAILURE(status)) { - goto exit; - } - - /* Convert and validate the device handle */ - - node = acpi_ns_validate_handle(device); - if (!node) { - status = AE_BAD_PARAMETER; - goto unlock_and_exit; + return_ACPI_STATUS(status); } - /* Root Object */ + /* Root Object. Global handlers are removed here */ if (device == ACPI_ROOT_OBJECT) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Removing notify handler for namespace root object\n")); + for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) { + if (handler_type & (i + 1)) { + if (!acpi_gbl_global_notify[i].handler || + (acpi_gbl_global_notify[i].handler != + handler)) { + status = AE_NOT_EXIST; + goto unlock_and_exit; + } - if (((handler_type & ACPI_SYSTEM_NOTIFY) && - !acpi_gbl_system_notify.handler) || - ((handler_type & ACPI_DEVICE_NOTIFY) && - !acpi_gbl_device_notify.handler)) { - status = AE_NOT_EXIST; - goto unlock_and_exit; - } + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Removing global notify handler\n")); - if (handler_type & ACPI_SYSTEM_NOTIFY) { - acpi_gbl_system_notify.node = NULL; - acpi_gbl_system_notify.handler = NULL; - acpi_gbl_system_notify.context = NULL; + acpi_gbl_global_notify[i].handler = NULL; + acpi_gbl_global_notify[i].context = NULL; + } } - if (handler_type & ACPI_DEVICE_NOTIFY) { - acpi_gbl_device_notify.node = NULL; - acpi_gbl_device_notify.handler = NULL; - acpi_gbl_device_notify.context = NULL; - } + goto unlock_and_exit; } - /* All Other Objects */ - - else { - /* Notifies allowed on this object? */ + /* All other objects: Are Notifies allowed on this object? */ - if (!acpi_ev_is_notify_object(node)) { - status = AE_TYPE; - goto unlock_and_exit; - } + if (!acpi_ev_is_notify_object(node)) { + status = AE_TYPE; + goto unlock_and_exit; + } - /* Check for an existing internal object */ + /* Must have an existing internal object */ - obj_desc = acpi_ns_get_attached_object(node); - if (!obj_desc) { - status = AE_NOT_EXIST; - goto unlock_and_exit; - } + obj_desc = acpi_ns_get_attached_object(node); + if (!obj_desc) { + status = AE_NOT_EXIST; + goto unlock_and_exit; + } - /* Object exists - make sure there's an existing handler */ + /* Internal object exists. Find the handler and remove it */ - if (handler_type & ACPI_SYSTEM_NOTIFY) { - struct acpi_object_notify_handler *handler_obj; - struct acpi_object_notify_handler *parent_obj; + for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) { + if (handler_type & (i + 1)) { + handler_obj = obj_desc->common_notify.notify_list[i]; + previous_handler_obj = NULL; - notify_obj = obj_desc->common_notify.system_notify; - if (!notify_obj) { - status = AE_NOT_EXIST; - goto unlock_and_exit; - } + /* Attempt to find the handler in the handler list */ - handler_obj = ¬ify_obj->notify; - parent_obj = NULL; - while (handler_obj->handler != handler) { - if (handler_obj->next) { - parent_obj = handler_obj; - handler_obj = handler_obj->next; - } else { - break; - } + while (handler_obj && + (handler_obj->notify.handler != handler)) { + previous_handler_obj = handler_obj; + handler_obj = handler_obj->notify.next[i]; } - if (handler_obj->handler != handler) { - status = AE_BAD_PARAMETER; + if (!handler_obj) { + status = AE_NOT_EXIST; goto unlock_and_exit; } - /* - * Remove the handler. There are three possible cases. - * First, we may need to remove a non-embedded object. - * Second, we may need to remove the embedded object's - * handler data, while non-embedded objects exist. - * Finally, we may need to remove the embedded object - * entirely along with its container. - */ - if (parent_obj) { - /* Non-embedded object is being removed. */ - parent_obj->next = handler_obj->next; - ACPI_FREE(handler_obj); - } else if (notify_obj->notify.next) { - /* - * The handler matches the embedded object, but - * there are more handler objects in the list. - * Replace the embedded object's data with the - * first next object's data and remove that - * object. - */ - parent_obj = ¬ify_obj->notify; - handler_obj = notify_obj->notify.next; - *parent_obj = *handler_obj; - ACPI_FREE(handler_obj); - } else { - /* No more handler objects in the list. */ - obj_desc->common_notify.system_notify = NULL; - acpi_ut_remove_reference(notify_obj); - } - } + /* Remove the handler object from the list */ - if (handler_type & ACPI_DEVICE_NOTIFY) { - notify_obj = obj_desc->common_notify.device_notify; - if (!notify_obj) { - status = AE_NOT_EXIST; - goto unlock_and_exit; - } + if (previous_handler_obj) { /* Handler is not at the list head */ + previous_handler_obj->notify.next[i] = + handler_obj->notify.next[i]; + } else { /* Handler is at the list head */ - if (notify_obj->notify.handler != handler) { - status = AE_BAD_PARAMETER; - goto unlock_and_exit; + obj_desc->common_notify.notify_list[i] = + handler_obj->notify.next[i]; } - /* Remove the handler */ - obj_desc->common_notify.device_notify = NULL; - acpi_ut_remove_reference(notify_obj); + acpi_ut_remove_reference(handler_obj); } } - unlock_and_exit: +unlock_and_exit: (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); - exit: - if (ACPI_FAILURE(status)) - ACPI_EXCEPTION((AE_INFO, status, "Removing notify handler")); return_ACPI_STATUS(status); } diff --git a/drivers/acpi/acpica/exdump.c b/drivers/acpi/acpica/exdump.c index 836fe76e65d0..26c56545804d 100644 --- a/drivers/acpi/acpica/exdump.c +++ b/drivers/acpi/acpica/exdump.c @@ -109,9 +109,9 @@ static struct acpi_exdump_info acpi_ex_dump_package[5] = { static struct acpi_exdump_info acpi_ex_dump_device[4] = { {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_device), NULL}, {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(device.handler), "Handler"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(device.system_notify), + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(device.notify_list[0]), "System Notify"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(device.device_notify), + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(device.notify_list[1]), "Device Notify"} }; @@ -158,9 +158,9 @@ static struct acpi_exdump_info acpi_ex_dump_power[5] = { "System Level"}, {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(power_resource.resource_order), "Resource Order"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(power_resource.system_notify), + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(power_resource.notify_list[0]), "System Notify"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(power_resource.device_notify), + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(power_resource.notify_list[1]), "Device Notify"} }; @@ -169,18 +169,18 @@ static struct acpi_exdump_info acpi_ex_dump_processor[7] = { {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(processor.proc_id), "Processor ID"}, {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(processor.length), "Length"}, {ACPI_EXD_ADDRESS, ACPI_EXD_OFFSET(processor.address), "Address"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(processor.system_notify), + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(processor.notify_list[0]), "System Notify"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(processor.device_notify), + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(processor.notify_list[1]), "Device Notify"}, {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(processor.handler), "Handler"} }; static struct acpi_exdump_info acpi_ex_dump_thermal[4] = { {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_thermal), NULL}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(thermal_zone.system_notify), + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(thermal_zone.notify_list[0]), "System Notify"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(thermal_zone.device_notify), + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(thermal_zone.notify_list[1]), "Device Notify"}, {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(thermal_zone.handler), "Handler"} }; @@ -241,10 +241,15 @@ static struct acpi_exdump_info acpi_ex_dump_address_handler[6] = { {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(address_space.context), "Context"} }; -static struct acpi_exdump_info acpi_ex_dump_notify[3] = { +static struct acpi_exdump_info acpi_ex_dump_notify[7] = { {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_notify), NULL}, {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(notify.node), "Node"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(notify.context), "Context"} + {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(notify.handler_type), "Handler Type"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(notify.handler), "Handler"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(notify.context), "Context"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(notify.next[0]), + "Next System Notify"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(notify.next[1]), "Next Device Notify"} }; /* Miscellaneous tables */ diff --git a/drivers/acpi/acpica/utdelete.c b/drivers/acpi/acpica/utdelete.c index 2a6c3e183697..0d50f2c6bac2 100644 --- a/drivers/acpi/acpica/utdelete.c +++ b/drivers/acpi/acpica/utdelete.c @@ -152,7 +152,7 @@ static void acpi_ut_delete_internal_obj(union acpi_operand_object *object) case ACPI_TYPE_PROCESSOR: case ACPI_TYPE_THERMAL: - /* Walk the notify handler list for this object */ + /* Walk the address handler list for this object */ handler_desc = object->common_notify.handler; while (handler_desc) { @@ -480,6 +480,7 @@ acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action) acpi_status status = AE_OK; union acpi_generic_state *state_list = NULL; union acpi_operand_object *next_object = NULL; + union acpi_operand_object *prev_object; union acpi_generic_state *state; u32 i; @@ -505,12 +506,21 @@ acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action) case ACPI_TYPE_POWER: case ACPI_TYPE_THERMAL: - /* Update the notify objects for these types (if present) */ - - acpi_ut_update_ref_count(object->common_notify. - system_notify, action); - acpi_ut_update_ref_count(object->common_notify. - device_notify, action); + /* + * Update the notify objects for these types (if present) + * Two lists, system and device notify handlers. + */ + for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) { + prev_object = + object->common_notify.notify_list[i]; + while (prev_object) { + next_object = + prev_object->notify.next[i]; + acpi_ut_update_ref_count(prev_object, + action); + prev_object = next_object; + } + } break; case ACPI_TYPE_PACKAGE: diff --git a/drivers/acpi/acpica/utglobal.c b/drivers/acpi/acpica/utglobal.c index 90f53b42eca9..78cf1fe390b3 100644 --- a/drivers/acpi/acpica/utglobal.c +++ b/drivers/acpi/acpica/utglobal.c @@ -304,8 +304,8 @@ acpi_status acpi_ut_init_globals(void) /* Global handlers */ - acpi_gbl_system_notify.handler = NULL; - acpi_gbl_device_notify.handler = NULL; + acpi_gbl_global_notify[0].handler = NULL; + acpi_gbl_global_notify[1].handler = NULL; acpi_gbl_exception_handler = NULL; acpi_gbl_init_handler = NULL; acpi_gbl_table_handler = NULL; diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index e8bcc4742e0e..0339a2d93f1d 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -706,10 +706,14 @@ typedef u32 acpi_event_status; #define ACPI_DEVICE_NOTIFY 0x2 #define ACPI_ALL_NOTIFY (ACPI_SYSTEM_NOTIFY | ACPI_DEVICE_NOTIFY) #define ACPI_MAX_NOTIFY_HANDLER_TYPE 0x3 +#define ACPI_NUM_NOTIFY_TYPES 2 #define ACPI_MAX_SYS_NOTIFY 0x7F #define ACPI_MAX_DEVICE_SPECIFIC_NOTIFY 0xBF +#define ACPI_SYSTEM_HANDLER_LIST 0 /* Used as index, must be SYSTEM_NOTIFY -1 */ +#define ACPI_DEVICE_HANDLER_LIST 1 /* Used as index, must be DEVICE_NOTIFY -1 */ + /* Address Space (Operation Region) Types */ typedef u8 acpi_adr_space_type; -- cgit v1.2.3 From 9fd7522570449bd10f6a6377cf5cb542ef527dd5 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 3 May 2012 10:50:54 +0800 Subject: ACPICA: Update to version 20120420 Version 20120420. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- 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 982110134672..a323a7cc1224 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -47,7 +47,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20120320 +#define ACPI_CA_VERSION 0x20120420 #include "acconfig.h" #include "actypes.h" -- cgit v1.2.3 From 43e1c6892c88ae0730cb0eb7bd5a2812b960c32a Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Tue, 22 May 2012 16:23:21 +0800 Subject: ACPICA: ACPI 5/iASL: Add support for PCC keyword Adds support for the PCC keyword for the Register() resource descriptor macro. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- include/acpi/actypes.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 0339a2d93f1d..376c9ed739d4 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -728,8 +728,9 @@ typedef u8 acpi_adr_space_type; #define ACPI_ADR_SPACE_IPMI (acpi_adr_space_type) 7 #define ACPI_ADR_SPACE_GPIO (acpi_adr_space_type) 8 #define ACPI_ADR_SPACE_GSBUS (acpi_adr_space_type) 9 +#define ACPI_ADR_SPACE_PLATFORM_COMM (acpi_adr_space_type) 10 -#define ACPI_NUM_PREDEFINED_REGIONS 10 +#define ACPI_NUM_PREDEFINED_REGIONS 11 /* * Special Address Spaces -- cgit v1.2.3 From bd6f10a5f984e48cb56a39f2698cd58e7a33d56b Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Tue, 22 May 2012 16:43:49 +0800 Subject: ACPICA: Remove argument of acpi_os_wait_events_complete Remove the unused argument of acpi_os_wait_events_complete. Signed-off-by: Lin Ming Signed-off-by: Bob Moore Signed-off-by: Len Brown --- drivers/acpi/acpica/evxface.c | 4 ++-- drivers/acpi/osl.c | 4 ++-- drivers/acpi/sleep.c | 2 +- include/acpi/acpiosxf.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index 90ae6d193645..6a8b53789be4 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -251,7 +251,7 @@ acpi_remove_notify_handler(acpi_handle device, } /* Make sure all deferred tasks are completed */ - acpi_os_wait_events_complete(NULL); + acpi_os_wait_events_complete(); status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); if (ACPI_FAILURE(status)) { @@ -699,7 +699,7 @@ acpi_remove_gpe_handler(acpi_handle gpe_device, /* Make sure all deferred tasks are completed */ - acpi_os_wait_events_complete(NULL); + acpi_os_wait_events_complete(); status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); if (ACPI_FAILURE(status)) { diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index c3881b2eb8b2..9eaf708f5885 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -891,7 +891,7 @@ static void acpi_os_execute_deferred(struct work_struct *work) struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work); if (dpc->wait) - acpi_os_wait_events_complete(NULL); + acpi_os_wait_events_complete(); dpc->function(dpc->context); kfree(dpc); @@ -987,7 +987,7 @@ acpi_status acpi_os_hotplug_execute(acpi_osd_exec_callback function, return __acpi_os_execute(0, function, context, 1); } -void acpi_os_wait_events_complete(void *context) +void acpi_os_wait_events_complete(void) { flush_workqueue(kacpid_wq); flush_workqueue(kacpi_notify_wq); diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index eb6fd233764b..9a14f90d98ec 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -144,7 +144,7 @@ void __init acpi_old_suspend_ordering(void) static int acpi_pm_freeze(void) { acpi_disable_all_gpes(); - acpi_os_wait_events_complete(NULL); + acpi_os_wait_events_complete(); acpi_ec_block_transactions(); return 0; } diff --git a/include/acpi/acpiosxf.h b/include/acpi/acpiosxf.h index 21a5548c6686..f3746f7e0f40 100644 --- a/include/acpi/acpiosxf.h +++ b/include/acpi/acpiosxf.h @@ -205,7 +205,7 @@ acpi_os_execute(acpi_execute_type type, acpi_status acpi_os_hotplug_execute(acpi_osd_exec_callback function, void *context); -void acpi_os_wait_events_complete(void *context); +void acpi_os_wait_events_complete(void); void acpi_os_sleep(u64 milliseconds); -- cgit v1.2.3 From 9748f313101c95dcc65f5dddd1c2ea99b7ce3e9b Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Tue, 22 May 2012 16:44:42 +0800 Subject: ACPICA: Update to version 20120518 Version string 20120518. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- 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 a323a7cc1224..381c94008536 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -47,7 +47,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20120420 +#define ACPI_CA_VERSION 0x20120518 #include "acconfig.h" #include "actypes.h" -- cgit v1.2.3 From 4126c0197bc8c58a0bb7fcda07b01b596b6fb4c5 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 7 May 2012 17:57:41 -0700 Subject: cpuidle: add support for states that affect multiple cpus On some ARM SMP SoCs (OMAP4460, Tegra 2, and probably more), the cpus cannot be independently powered down, either due to sequencing restrictions (on Tegra 2, cpu 0 must be the last to power down), or due to HW bugs (on OMAP4460, a cpu powering up will corrupt the gic state unless the other cpu runs a work around). Each cpu has a power state that it can enter without coordinating with the other cpu (usually Wait For Interrupt, or WFI), and one or more "coupled" power states that affect blocks shared between the cpus (L2 cache, interrupt controller, and sometimes the whole SoC). Entering a coupled power state must be tightly controlled on both cpus. The easiest solution to implementing coupled cpu power states is to hotplug all but one cpu whenever possible, usually using a cpufreq governor that looks at cpu load to determine when to enable the secondary cpus. This causes problems, as hotplug is an expensive operation, so the number of hotplug transitions must be minimized, leading to very slow response to loads, often on the order of seconds. This file implements an alternative solution, where each cpu will wait in the WFI state until all cpus are ready to enter a coupled state, at which point the coupled state function will be called on all cpus at approximately the same time. Once all cpus are ready to enter idle, they are woken by an smp cross call. At this point, there is a chance that one of the cpus will find work to do, and choose not to enter idle. A final pass is needed to guarantee that all cpus will call the power state enter function at the same time. During this pass, each cpu will increment the ready counter, and continue once the ready counter matches the number of online coupled cpus. If any cpu exits idle, the other cpus will decrement their counter and retry. To use coupled cpuidle states, a cpuidle driver must: Set struct cpuidle_device.coupled_cpus to the mask of all coupled cpus, usually the same as cpu_possible_mask if all cpus are part of the same cluster. The coupled_cpus mask must be set in the struct cpuidle_device for each cpu. Set struct cpuidle_device.safe_state to a state that is not a coupled state. This is usually WFI. Set CPUIDLE_FLAG_COUPLED in struct cpuidle_state.flags for each state that affects multiple cpus. Provide a struct cpuidle_state.enter function for each state that affects multiple cpus. This function is guaranteed to be called on all cpus at approximately the same time. The driver should ensure that the cpus all abort together if any cpu tries to abort once the function is called. update1: cpuidle: coupled: fix count of online cpus online_count was never incremented on boot, and was also counting cpus that were not part of the coupled set. Fix both issues by introducting a new function that counts online coupled cpus, and call it from register as well as the hotplug notifier. update2: cpuidle: coupled: fix decrementing ready count cpuidle_coupled_set_not_ready sometimes refuses to decrement the ready count in order to prevent a race condition. This makes it unsuitable for use when finished with idle. Add a new function cpuidle_coupled_set_done that decrements both the ready count and waiting count, and call it after idle is complete. Cc: Amit Kucheria Cc: Arjan van de Ven Cc: Trinabh Gupta Cc: Deepthi Dharwar Reviewed-by: Santosh Shilimkar Tested-by: Santosh Shilimkar Reviewed-by: Kevin Hilman Tested-by: Kevin Hilman Signed-off-by: Colin Cross Acked-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/cpuidle/Kconfig | 3 + drivers/cpuidle/Makefile | 1 + drivers/cpuidle/coupled.c | 678 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/cpuidle/cpuidle.c | 15 +- drivers/cpuidle/cpuidle.h | 30 ++ include/linux/cpuidle.h | 7 + 6 files changed, 733 insertions(+), 1 deletion(-) create mode 100644 drivers/cpuidle/coupled.c (limited to 'include') diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig index 78a666d1e5f5..a76b689e553b 100644 --- a/drivers/cpuidle/Kconfig +++ b/drivers/cpuidle/Kconfig @@ -18,3 +18,6 @@ config CPU_IDLE_GOV_MENU bool depends on CPU_IDLE && NO_HZ default y + +config ARCH_NEEDS_CPU_IDLE_COUPLED + def_bool n diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index 5634f88379df..38c8f69f30cf 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -3,3 +3,4 @@ # obj-y += cpuidle.o driver.o governor.o sysfs.o governors/ +obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o diff --git a/drivers/cpuidle/coupled.c b/drivers/cpuidle/coupled.c new file mode 100644 index 000000000000..aab6bba8daec --- /dev/null +++ b/drivers/cpuidle/coupled.c @@ -0,0 +1,678 @@ +/* + * coupled.c - helper functions to enter the same idle state on multiple cpus + * + * Copyright (c) 2011 Google, Inc. + * + * Author: Colin Cross + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "cpuidle.h" + +/** + * DOC: Coupled cpuidle states + * + * On some ARM SMP SoCs (OMAP4460, Tegra 2, and probably more), the + * cpus cannot be independently powered down, either due to + * sequencing restrictions (on Tegra 2, cpu 0 must be the last to + * power down), or due to HW bugs (on OMAP4460, a cpu powering up + * will corrupt the gic state unless the other cpu runs a work + * around). Each cpu has a power state that it can enter without + * coordinating with the other cpu (usually Wait For Interrupt, or + * WFI), and one or more "coupled" power states that affect blocks + * shared between the cpus (L2 cache, interrupt controller, and + * sometimes the whole SoC). Entering a coupled power state must + * be tightly controlled on both cpus. + * + * This file implements a solution, where each cpu will wait in the + * WFI state until all cpus are ready to enter a coupled state, at + * which point the coupled state function will be called on all + * cpus at approximately the same time. + * + * Once all cpus are ready to enter idle, they are woken by an smp + * cross call. At this point, there is a chance that one of the + * cpus will find work to do, and choose not to enter idle. A + * final pass is needed to guarantee that all cpus will call the + * power state enter function at the same time. During this pass, + * each cpu will increment the ready counter, and continue once the + * ready counter matches the number of online coupled cpus. If any + * cpu exits idle, the other cpus will decrement their counter and + * retry. + * + * requested_state stores the deepest coupled idle state each cpu + * is ready for. It is assumed that the states are indexed from + * shallowest (highest power, lowest exit latency) to deepest + * (lowest power, highest exit latency). The requested_state + * variable is not locked. It is only written from the cpu that + * it stores (or by the on/offlining cpu if that cpu is offline), + * and only read after all the cpus are ready for the coupled idle + * state are are no longer updating it. + * + * Three atomic counters are used. alive_count tracks the number + * of cpus in the coupled set that are currently or soon will be + * online. waiting_count tracks the number of cpus that are in + * the waiting loop, in the ready loop, or in the coupled idle state. + * ready_count tracks the number of cpus that are in the ready loop + * or in the coupled idle state. + * + * To use coupled cpuidle states, a cpuidle driver must: + * + * Set struct cpuidle_device.coupled_cpus to the mask of all + * coupled cpus, usually the same as cpu_possible_mask if all cpus + * are part of the same cluster. The coupled_cpus mask must be + * set in the struct cpuidle_device for each cpu. + * + * Set struct cpuidle_device.safe_state to a state that is not a + * coupled state. This is usually WFI. + * + * Set CPUIDLE_FLAG_COUPLED in struct cpuidle_state.flags for each + * state that affects multiple cpus. + * + * Provide a struct cpuidle_state.enter function for each state + * that affects multiple cpus. This function is guaranteed to be + * called on all cpus at approximately the same time. The driver + * should ensure that the cpus all abort together if any cpu tries + * to abort once the function is called. The function should return + * with interrupts still disabled. + */ + +/** + * struct cpuidle_coupled - data for set of cpus that share a coupled idle state + * @coupled_cpus: mask of cpus that are part of the coupled set + * @requested_state: array of requested states for cpus in the coupled set + * @ready_waiting_counts: combined count of cpus in ready or waiting loops + * @online_count: count of cpus that are online + * @refcnt: reference count of cpuidle devices that are using this struct + * @prevent: flag to prevent coupled idle while a cpu is hotplugging + */ +struct cpuidle_coupled { + cpumask_t coupled_cpus; + int requested_state[NR_CPUS]; + atomic_t ready_waiting_counts; + int online_count; + int refcnt; + int prevent; +}; + +#define WAITING_BITS 16 +#define MAX_WAITING_CPUS (1 << WAITING_BITS) +#define WAITING_MASK (MAX_WAITING_CPUS - 1) +#define READY_MASK (~WAITING_MASK) + +#define CPUIDLE_COUPLED_NOT_IDLE (-1) + +static DEFINE_MUTEX(cpuidle_coupled_lock); +static DEFINE_PER_CPU(struct call_single_data, cpuidle_coupled_poke_cb); + +/* + * The cpuidle_coupled_poked_mask mask is used to avoid calling + * __smp_call_function_single with the per cpu call_single_data struct already + * in use. This prevents a deadlock where two cpus are waiting for each others + * call_single_data struct to be available + */ +static cpumask_t cpuidle_coupled_poked_mask; + +/** + * cpuidle_state_is_coupled - check if a state is part of a coupled set + * @dev: struct cpuidle_device for the current cpu + * @drv: struct cpuidle_driver for the platform + * @state: index of the target state in drv->states + * + * Returns true if the target state is coupled with cpus besides this one + */ +bool cpuidle_state_is_coupled(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int state) +{ + return drv->states[state].flags & CPUIDLE_FLAG_COUPLED; +} + +/** + * cpuidle_coupled_set_ready - mark a cpu as ready + * @coupled: the struct coupled that contains the current cpu + */ +static inline void cpuidle_coupled_set_ready(struct cpuidle_coupled *coupled) +{ + atomic_add(MAX_WAITING_CPUS, &coupled->ready_waiting_counts); +} + +/** + * cpuidle_coupled_set_not_ready - mark a cpu as not ready + * @coupled: the struct coupled that contains the current cpu + * + * Decrements the ready counter, unless the ready (and thus the waiting) counter + * is equal to the number of online cpus. Prevents a race where one cpu + * decrements the waiting counter and then re-increments it just before another + * cpu has decremented its ready counter, leading to the ready counter going + * down from the number of online cpus without going through the coupled idle + * state. + * + * Returns 0 if the counter was decremented successfully, -EINVAL if the ready + * counter was equal to the number of online cpus. + */ +static +inline int cpuidle_coupled_set_not_ready(struct cpuidle_coupled *coupled) +{ + int all; + int ret; + + all = coupled->online_count || (coupled->online_count << WAITING_BITS); + ret = atomic_add_unless(&coupled->ready_waiting_counts, + -MAX_WAITING_CPUS, all); + + return ret ? 0 : -EINVAL; +} + +/** + * cpuidle_coupled_no_cpus_ready - check if no cpus in a coupled set are ready + * @coupled: the struct coupled that contains the current cpu + * + * Returns true if all of the cpus in a coupled set are out of the ready loop. + */ +static inline int cpuidle_coupled_no_cpus_ready(struct cpuidle_coupled *coupled) +{ + int r = atomic_read(&coupled->ready_waiting_counts) >> WAITING_BITS; + return r == 0; +} + +/** + * cpuidle_coupled_cpus_ready - check if all cpus in a coupled set are ready + * @coupled: the struct coupled that contains the current cpu + * + * Returns true if all cpus coupled to this target state are in the ready loop + */ +static inline bool cpuidle_coupled_cpus_ready(struct cpuidle_coupled *coupled) +{ + int r = atomic_read(&coupled->ready_waiting_counts) >> WAITING_BITS; + return r == coupled->online_count; +} + +/** + * cpuidle_coupled_cpus_waiting - check if all cpus in a coupled set are waiting + * @coupled: the struct coupled that contains the current cpu + * + * Returns true if all cpus coupled to this target state are in the wait loop + */ +static inline bool cpuidle_coupled_cpus_waiting(struct cpuidle_coupled *coupled) +{ + int w = atomic_read(&coupled->ready_waiting_counts) & WAITING_MASK; + return w == coupled->online_count; +} + +/** + * cpuidle_coupled_no_cpus_waiting - check if no cpus in coupled set are waiting + * @coupled: the struct coupled that contains the current cpu + * + * Returns true if all of the cpus in a coupled set are out of the waiting loop. + */ +static inline int cpuidle_coupled_no_cpus_waiting(struct cpuidle_coupled *coupled) +{ + int w = atomic_read(&coupled->ready_waiting_counts) & WAITING_MASK; + return w == 0; +} + +/** + * cpuidle_coupled_get_state - determine the deepest idle state + * @dev: struct cpuidle_device for this cpu + * @coupled: the struct coupled that contains the current cpu + * + * Returns the deepest idle state that all coupled cpus can enter + */ +static inline int cpuidle_coupled_get_state(struct cpuidle_device *dev, + struct cpuidle_coupled *coupled) +{ + int i; + int state = INT_MAX; + + /* + * Read barrier ensures that read of requested_state is ordered after + * reads of ready_count. Matches the write barriers + * cpuidle_set_state_waiting. + */ + smp_rmb(); + + for_each_cpu_mask(i, coupled->coupled_cpus) + if (cpu_online(i) && coupled->requested_state[i] < state) + state = coupled->requested_state[i]; + + return state; +} + +static void cpuidle_coupled_poked(void *info) +{ + int cpu = (unsigned long)info; + cpumask_clear_cpu(cpu, &cpuidle_coupled_poked_mask); +} + +/** + * cpuidle_coupled_poke - wake up a cpu that may be waiting + * @cpu: target cpu + * + * Ensures that the target cpu exits it's waiting idle state (if it is in it) + * and will see updates to waiting_count before it re-enters it's waiting idle + * state. + * + * If cpuidle_coupled_poked_mask is already set for the target cpu, that cpu + * either has or will soon have a pending IPI that will wake it out of idle, + * or it is currently processing the IPI and is not in idle. + */ +static void cpuidle_coupled_poke(int cpu) +{ + struct call_single_data *csd = &per_cpu(cpuidle_coupled_poke_cb, cpu); + + if (!cpumask_test_and_set_cpu(cpu, &cpuidle_coupled_poked_mask)) + __smp_call_function_single(cpu, csd, 0); +} + +/** + * cpuidle_coupled_poke_others - wake up all other cpus that may be waiting + * @dev: struct cpuidle_device for this cpu + * @coupled: the struct coupled that contains the current cpu + * + * Calls cpuidle_coupled_poke on all other online cpus. + */ +static void cpuidle_coupled_poke_others(int this_cpu, + struct cpuidle_coupled *coupled) +{ + int cpu; + + for_each_cpu_mask(cpu, coupled->coupled_cpus) + if (cpu != this_cpu && cpu_online(cpu)) + cpuidle_coupled_poke(cpu); +} + +/** + * cpuidle_coupled_set_waiting - mark this cpu as in the wait loop + * @dev: struct cpuidle_device for this cpu + * @coupled: the struct coupled that contains the current cpu + * @next_state: the index in drv->states of the requested state for this cpu + * + * Updates the requested idle state for the specified cpuidle device, + * poking all coupled cpus out of idle if necessary to let them see the new + * state. + */ +static void cpuidle_coupled_set_waiting(int cpu, + struct cpuidle_coupled *coupled, int next_state) +{ + int w; + + coupled->requested_state[cpu] = next_state; + + /* + * If this is the last cpu to enter the waiting state, poke + * all the other cpus out of their waiting state so they can + * enter a deeper state. This can race with one of the cpus + * exiting the waiting state due to an interrupt and + * decrementing waiting_count, see comment below. + * + * The atomic_inc_return provides a write barrier to order the write + * to requested_state with the later write that increments ready_count. + */ + w = atomic_inc_return(&coupled->ready_waiting_counts) & WAITING_MASK; + if (w == coupled->online_count) + cpuidle_coupled_poke_others(cpu, coupled); +} + +/** + * cpuidle_coupled_set_not_waiting - mark this cpu as leaving the wait loop + * @dev: struct cpuidle_device for this cpu + * @coupled: the struct coupled that contains the current cpu + * + * Removes the requested idle state for the specified cpuidle device. + */ +static void cpuidle_coupled_set_not_waiting(int cpu, + struct cpuidle_coupled *coupled) +{ + /* + * Decrementing waiting count can race with incrementing it in + * cpuidle_coupled_set_waiting, but that's OK. Worst case, some + * cpus will increment ready_count and then spin until they + * notice that this cpu has cleared it's requested_state. + */ + atomic_dec(&coupled->ready_waiting_counts); + + coupled->requested_state[cpu] = CPUIDLE_COUPLED_NOT_IDLE; +} + +/** + * cpuidle_coupled_set_done - mark this cpu as leaving the ready loop + * @cpu: the current cpu + * @coupled: the struct coupled that contains the current cpu + * + * Marks this cpu as no longer in the ready and waiting loops. Decrements + * the waiting count first to prevent another cpu looping back in and seeing + * this cpu as waiting just before it exits idle. + */ +static void cpuidle_coupled_set_done(int cpu, struct cpuidle_coupled *coupled) +{ + cpuidle_coupled_set_not_waiting(cpu, coupled); + atomic_sub(MAX_WAITING_CPUS, &coupled->ready_waiting_counts); +} + +/** + * cpuidle_coupled_clear_pokes - spin until the poke interrupt is processed + * @cpu - this cpu + * + * Turns on interrupts and spins until any outstanding poke interrupts have + * been processed and the poke bit has been cleared. + * + * Other interrupts may also be processed while interrupts are enabled, so + * need_resched() must be tested after turning interrupts off again to make sure + * the interrupt didn't schedule work that should take the cpu out of idle. + * + * Returns 0 if need_resched was false, -EINTR if need_resched was true. + */ +static int cpuidle_coupled_clear_pokes(int cpu) +{ + local_irq_enable(); + while (cpumask_test_cpu(cpu, &cpuidle_coupled_poked_mask)) + cpu_relax(); + local_irq_disable(); + + return need_resched() ? -EINTR : 0; +} + +/** + * cpuidle_enter_state_coupled - attempt to enter a state with coupled cpus + * @dev: struct cpuidle_device for the current cpu + * @drv: struct cpuidle_driver for the platform + * @next_state: index of the requested state in drv->states + * + * Coordinate with coupled cpus to enter the target state. This is a two + * stage process. In the first stage, the cpus are operating independently, + * and may call into cpuidle_enter_state_coupled at completely different times. + * To save as much power as possible, the first cpus to call this function will + * go to an intermediate state (the cpuidle_device's safe state), and wait for + * all the other cpus to call this function. Once all coupled cpus are idle, + * the second stage will start. Each coupled cpu will spin until all cpus have + * guaranteed that they will call the target_state. + * + * This function must be called with interrupts disabled. It may enable + * interrupts while preparing for idle, and it will always return with + * interrupts enabled. + */ +int cpuidle_enter_state_coupled(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int next_state) +{ + int entered_state = -1; + struct cpuidle_coupled *coupled = dev->coupled; + + if (!coupled) + return -EINVAL; + + while (coupled->prevent) { + if (cpuidle_coupled_clear_pokes(dev->cpu)) { + local_irq_enable(); + return entered_state; + } + entered_state = cpuidle_enter_state(dev, drv, + dev->safe_state_index); + } + + /* Read barrier ensures online_count is read after prevent is cleared */ + smp_rmb(); + + cpuidle_coupled_set_waiting(dev->cpu, coupled, next_state); + +retry: + /* + * Wait for all coupled cpus to be idle, using the deepest state + * allowed for a single cpu. + */ + while (!cpuidle_coupled_cpus_waiting(coupled)) { + if (cpuidle_coupled_clear_pokes(dev->cpu)) { + cpuidle_coupled_set_not_waiting(dev->cpu, coupled); + goto out; + } + + if (coupled->prevent) { + cpuidle_coupled_set_not_waiting(dev->cpu, coupled); + goto out; + } + + entered_state = cpuidle_enter_state(dev, drv, + dev->safe_state_index); + } + + if (cpuidle_coupled_clear_pokes(dev->cpu)) { + cpuidle_coupled_set_not_waiting(dev->cpu, coupled); + goto out; + } + + /* + * All coupled cpus are probably idle. There is a small chance that + * one of the other cpus just became active. Increment the ready count, + * and spin until all coupled cpus have incremented the counter. Once a + * cpu has incremented the ready counter, it cannot abort idle and must + * spin until either all cpus have incremented the ready counter, or + * another cpu leaves idle and decrements the waiting counter. + */ + + cpuidle_coupled_set_ready(coupled); + while (!cpuidle_coupled_cpus_ready(coupled)) { + /* Check if any other cpus bailed out of idle. */ + if (!cpuidle_coupled_cpus_waiting(coupled)) + if (!cpuidle_coupled_set_not_ready(coupled)) + goto retry; + + cpu_relax(); + } + + /* all cpus have acked the coupled state */ + next_state = cpuidle_coupled_get_state(dev, coupled); + + entered_state = cpuidle_enter_state(dev, drv, next_state); + + cpuidle_coupled_set_done(dev->cpu, coupled); + +out: + /* + * Normal cpuidle states are expected to return with irqs enabled. + * That leads to an inefficiency where a cpu receiving an interrupt + * that brings it out of idle will process that interrupt before + * exiting the idle enter function and decrementing ready_count. All + * other cpus will need to spin waiting for the cpu that is processing + * the interrupt. If the driver returns with interrupts disabled, + * all other cpus will loop back into the safe idle state instead of + * spinning, saving power. + * + * Calling local_irq_enable here allows coupled states to return with + * interrupts disabled, but won't cause problems for drivers that + * exit with interrupts enabled. + */ + local_irq_enable(); + + /* + * Wait until all coupled cpus have exited idle. There is no risk that + * a cpu exits and re-enters the ready state because this cpu has + * already decremented its waiting_count. + */ + while (!cpuidle_coupled_no_cpus_ready(coupled)) + cpu_relax(); + + return entered_state; +} + +static void cpuidle_coupled_update_online_cpus(struct cpuidle_coupled *coupled) +{ + cpumask_t cpus; + cpumask_and(&cpus, cpu_online_mask, &coupled->coupled_cpus); + coupled->online_count = cpumask_weight(&cpus); +} + +/** + * cpuidle_coupled_register_device - register a coupled cpuidle device + * @dev: struct cpuidle_device for the current cpu + * + * Called from cpuidle_register_device to handle coupled idle init. Finds the + * cpuidle_coupled struct for this set of coupled cpus, or creates one if none + * exists yet. + */ +int cpuidle_coupled_register_device(struct cpuidle_device *dev) +{ + int cpu; + struct cpuidle_device *other_dev; + struct call_single_data *csd; + struct cpuidle_coupled *coupled; + + if (cpumask_empty(&dev->coupled_cpus)) + return 0; + + for_each_cpu_mask(cpu, dev->coupled_cpus) { + other_dev = per_cpu(cpuidle_devices, cpu); + if (other_dev && other_dev->coupled) { + coupled = other_dev->coupled; + goto have_coupled; + } + } + + /* No existing coupled info found, create a new one */ + coupled = kzalloc(sizeof(struct cpuidle_coupled), GFP_KERNEL); + if (!coupled) + return -ENOMEM; + + coupled->coupled_cpus = dev->coupled_cpus; + +have_coupled: + dev->coupled = coupled; + if (WARN_ON(!cpumask_equal(&dev->coupled_cpus, &coupled->coupled_cpus))) + coupled->prevent++; + + cpuidle_coupled_update_online_cpus(coupled); + + coupled->refcnt++; + + csd = &per_cpu(cpuidle_coupled_poke_cb, dev->cpu); + csd->func = cpuidle_coupled_poked; + csd->info = (void *)(unsigned long)dev->cpu; + + return 0; +} + +/** + * cpuidle_coupled_unregister_device - unregister a coupled cpuidle device + * @dev: struct cpuidle_device for the current cpu + * + * Called from cpuidle_unregister_device to tear down coupled idle. Removes the + * cpu from the coupled idle set, and frees the cpuidle_coupled_info struct if + * this was the last cpu in the set. + */ +void cpuidle_coupled_unregister_device(struct cpuidle_device *dev) +{ + struct cpuidle_coupled *coupled = dev->coupled; + + if (cpumask_empty(&dev->coupled_cpus)) + return; + + if (--coupled->refcnt) + kfree(coupled); + dev->coupled = NULL; +} + +/** + * cpuidle_coupled_prevent_idle - prevent cpus from entering a coupled state + * @coupled: the struct coupled that contains the cpu that is changing state + * + * Disables coupled cpuidle on a coupled set of cpus. Used to ensure that + * cpu_online_mask doesn't change while cpus are coordinating coupled idle. + */ +static void cpuidle_coupled_prevent_idle(struct cpuidle_coupled *coupled) +{ + int cpu = get_cpu(); + + /* Force all cpus out of the waiting loop. */ + coupled->prevent++; + cpuidle_coupled_poke_others(cpu, coupled); + put_cpu(); + while (!cpuidle_coupled_no_cpus_waiting(coupled)) + cpu_relax(); +} + +/** + * cpuidle_coupled_allow_idle - allows cpus to enter a coupled state + * @coupled: the struct coupled that contains the cpu that is changing state + * + * Enables coupled cpuidle on a coupled set of cpus. Used to ensure that + * cpu_online_mask doesn't change while cpus are coordinating coupled idle. + */ +static void cpuidle_coupled_allow_idle(struct cpuidle_coupled *coupled) +{ + int cpu = get_cpu(); + + /* + * Write barrier ensures readers see the new online_count when they + * see prevent == 0. + */ + smp_wmb(); + coupled->prevent--; + /* Force cpus out of the prevent loop. */ + cpuidle_coupled_poke_others(cpu, coupled); + put_cpu(); +} + +/** + * cpuidle_coupled_cpu_notify - notifier called during hotplug transitions + * @nb: notifier block + * @action: hotplug transition + * @hcpu: target cpu number + * + * Called when a cpu is brought on or offline using hotplug. Updates the + * coupled cpu set appropriately + */ +static int cpuidle_coupled_cpu_notify(struct notifier_block *nb, + unsigned long action, void *hcpu) +{ + int cpu = (unsigned long)hcpu; + struct cpuidle_device *dev; + + mutex_lock(&cpuidle_lock); + + dev = per_cpu(cpuidle_devices, cpu); + if (!dev->coupled) + goto out; + + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_UP_PREPARE: + case CPU_DOWN_PREPARE: + cpuidle_coupled_prevent_idle(dev->coupled); + break; + case CPU_ONLINE: + case CPU_DEAD: + cpuidle_coupled_update_online_cpus(dev->coupled); + /* Fall through */ + case CPU_UP_CANCELED: + case CPU_DOWN_FAILED: + cpuidle_coupled_allow_idle(dev->coupled); + break; + } + +out: + mutex_unlock(&cpuidle_lock); + return NOTIFY_OK; +} + +static struct notifier_block cpuidle_coupled_cpu_notifier = { + .notifier_call = cpuidle_coupled_cpu_notify, +}; + +static int __init cpuidle_coupled_init(void) +{ + return register_cpu_notifier(&cpuidle_coupled_cpu_notifier); +} +core_initcall(cpuidle_coupled_init); diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 4540672a2e1c..e81cfda295a5 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -171,7 +171,11 @@ int cpuidle_idle_call(void) trace_power_start_rcuidle(POWER_CSTATE, next_state, dev->cpu); trace_cpu_idle_rcuidle(next_state, dev->cpu); - entered_state = cpuidle_enter_state(dev, drv, next_state); + if (cpuidle_state_is_coupled(dev, drv, next_state)) + entered_state = cpuidle_enter_state_coupled(dev, drv, + next_state); + else + entered_state = cpuidle_enter_state(dev, drv, next_state); trace_power_end_rcuidle(dev->cpu); trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); @@ -407,9 +411,16 @@ static int __cpuidle_register_device(struct cpuidle_device *dev) if (ret) goto err_sysfs; + ret = cpuidle_coupled_register_device(dev); + if (ret) + goto err_coupled; + dev->registered = 1; return 0; +err_coupled: + cpuidle_remove_sysfs(cpu_dev); + wait_for_completion(&dev->kobj_unregister); err_sysfs: list_del(&dev->device_list); per_cpu(cpuidle_devices, dev->cpu) = NULL; @@ -464,6 +475,8 @@ void cpuidle_unregister_device(struct cpuidle_device *dev) wait_for_completion(&dev->kobj_unregister); per_cpu(cpuidle_devices, dev->cpu) = NULL; + cpuidle_coupled_unregister_device(dev); + cpuidle_resume_and_unlock(); module_put(cpuidle_driver->owner); diff --git a/drivers/cpuidle/cpuidle.h b/drivers/cpuidle/cpuidle.h index d8a3ccce8281..76e7f696ad8c 100644 --- a/drivers/cpuidle/cpuidle.h +++ b/drivers/cpuidle/cpuidle.h @@ -32,4 +32,34 @@ extern void cpuidle_remove_state_sysfs(struct cpuidle_device *device); extern int cpuidle_add_sysfs(struct device *dev); extern void cpuidle_remove_sysfs(struct device *dev); +#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED +bool cpuidle_state_is_coupled(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int state); +int cpuidle_enter_state_coupled(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int next_state); +int cpuidle_coupled_register_device(struct cpuidle_device *dev); +void cpuidle_coupled_unregister_device(struct cpuidle_device *dev); +#else +static inline bool cpuidle_state_is_coupled(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int state) +{ + return false; +} + +static inline int cpuidle_enter_state_coupled(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int next_state) +{ + return -1; +} + +static inline int cpuidle_coupled_register_device(struct cpuidle_device *dev) +{ + return 0; +} + +static inline void cpuidle_coupled_unregister_device(struct cpuidle_device *dev) +{ +} +#endif + #endif /* __DRIVER_CPUIDLE_H */ diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 6c26a3da0e03..603844835bc7 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -57,6 +57,7 @@ struct cpuidle_state { /* Idle State Flags */ #define CPUIDLE_FLAG_TIME_VALID (0x01) /* is residency time measurable? */ +#define CPUIDLE_FLAG_COUPLED (0x02) /* state applies to multiple cpus */ #define CPUIDLE_DRIVER_FLAGS_MASK (0xFFFF0000) @@ -100,6 +101,12 @@ struct cpuidle_device { struct list_head device_list; struct kobject kobj; struct completion kobj_unregister; + +#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED + int safe_state_index; + cpumask_t coupled_cpus; + struct cpuidle_coupled *coupled; +#endif }; DECLARE_PER_CPU(struct cpuidle_device *, cpuidle_devices); -- cgit v1.2.3 From 20ff51a36b2cd25ee7eb3216b6d02b68935435ba Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 7 May 2012 17:57:42 -0700 Subject: cpuidle: coupled: add parallel barrier function Adds cpuidle_coupled_parallel_barrier, which can be used by coupled cpuidle state enter functions to handle resynchronization after determining if any cpu needs to abort. The normal use case will be: static bool abort_flag; static atomic_t abort_barrier; int arch_cpuidle_enter(struct cpuidle_device *dev, ...) { if (arch_turn_off_irq_controller()) { /* returns an error if an irq is pending and would be lost if idle continued and turned off power */ abort_flag = true; } cpuidle_coupled_parallel_barrier(dev, &abort_barrier); if (abort_flag) { /* One of the cpus didn't turn off it's irq controller */ arch_turn_on_irq_controller(); return -EINTR; } /* continue with idle */ ... } This will cause all cpus to abort idle together if one of them needs to abort. Reviewed-by: Santosh Shilimkar Tested-by: Santosh Shilimkar Reviewed-by: Kevin Hilman Tested-by: Kevin Hilman Signed-off-by: Colin Cross Signed-off-by: Len Brown --- drivers/cpuidle/coupled.c | 37 +++++++++++++++++++++++++++++++++++++ include/linux/cpuidle.h | 4 ++++ 2 files changed, 41 insertions(+) (limited to 'include') diff --git a/drivers/cpuidle/coupled.c b/drivers/cpuidle/coupled.c index aab6bba8daec..2c9bf2692232 100644 --- a/drivers/cpuidle/coupled.c +++ b/drivers/cpuidle/coupled.c @@ -129,6 +129,43 @@ static DEFINE_PER_CPU(struct call_single_data, cpuidle_coupled_poke_cb); */ static cpumask_t cpuidle_coupled_poked_mask; +/** + * cpuidle_coupled_parallel_barrier - synchronize all online coupled cpus + * @dev: cpuidle_device of the calling cpu + * @a: atomic variable to hold the barrier + * + * No caller to this function will return from this function until all online + * cpus in the same coupled group have called this function. Once any caller + * has returned from this function, the barrier is immediately available for + * reuse. + * + * The atomic variable a must be initialized to 0 before any cpu calls + * this function, will be reset to 0 before any cpu returns from this function. + * + * Must only be called from within a coupled idle state handler + * (state.enter when state.flags has CPUIDLE_FLAG_COUPLED set). + * + * Provides full smp barrier semantics before and after calling. + */ +void cpuidle_coupled_parallel_barrier(struct cpuidle_device *dev, atomic_t *a) +{ + int n = dev->coupled->online_count; + + smp_mb__before_atomic_inc(); + atomic_inc(a); + + while (atomic_read(a) < n) + cpu_relax(); + + if (atomic_inc_return(a) == n * 2) { + atomic_set(a, 0); + return; + } + + while (atomic_read(a) > n) + cpu_relax(); +} + /** * cpuidle_state_is_coupled - check if a state is part of a coupled set * @dev: struct cpuidle_device for the current cpu diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 603844835bc7..5ab7183313ce 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -183,6 +183,10 @@ static inline int cpuidle_play_dead(void) {return -ENODEV; } #endif +#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED +void cpuidle_coupled_parallel_barrier(struct cpuidle_device *dev, atomic_t *a); +#endif + /****************************** * CPUIDLE GOVERNOR INTERFACE * ******************************/ -- cgit v1.2.3 From b9c7aff481f19dd655ae3ce6513817d625e2d47c Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 29 May 2012 11:18:51 -0700 Subject: drivers/thermal/spear_thermal.c: add Device Tree probing capability SPEAr platforms now support DT and so must convert all drivers to support DT. This patch adds DT probing support for SPEAr thermal sensor driver and updates its documentation too. Also, as SPEAr is the only user of this driver and is only available with DT, make this an only DT driver. So, platform_data is completely removed and passed via DT now. Signed-off-by: Viresh Kumar Cc: Dan Carpenter Reviewed-by: Vincenzo Frascino Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- .../devicetree/bindings/thermal/spear-thermal.txt | 14 ++++++++++++ drivers/thermal/Kconfig | 1 + drivers/thermal/spear_thermal.c | 26 +++++++++++++--------- include/linux/platform_data/spear_thermal.h | 26 ---------------------- 4 files changed, 31 insertions(+), 36 deletions(-) create mode 100644 Documentation/devicetree/bindings/thermal/spear-thermal.txt delete mode 100644 include/linux/platform_data/spear_thermal.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/thermal/spear-thermal.txt b/Documentation/devicetree/bindings/thermal/spear-thermal.txt new file mode 100644 index 000000000000..93e3b67c102d --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/spear-thermal.txt @@ -0,0 +1,14 @@ +* SPEAr Thermal + +Required properties: +- compatible : "st,thermal-spear1340" +- reg : Address range of the thermal registers +- st,thermal-flags: flags used to enable thermal sensor + +Example: + + thermal@fc000000 { + compatible = "st,thermal-spear1340"; + reg = <0xfc000000 0x1000>; + st,thermal-flags = <0x7000>; + }; diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 514a691abea0..3ab2bd540b54 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -23,6 +23,7 @@ config SPEAR_THERMAL bool "SPEAr thermal sensor driver" depends on THERMAL depends on PLAT_SPEAR + depends on OF help Enable this to plug the SPEAr thermal sensor driver into the Linux thermal framework diff --git a/drivers/thermal/spear_thermal.c b/drivers/thermal/spear_thermal.c index c2e32df3b164..ca40d36d0ba8 100644 --- a/drivers/thermal/spear_thermal.c +++ b/drivers/thermal/spear_thermal.c @@ -20,9 +20,9 @@ #include #include #include +#include #include #include -#include #include #define MD_FACTOR 1000 @@ -103,21 +103,20 @@ static int spear_thermal_probe(struct platform_device *pdev) { struct thermal_zone_device *spear_thermal = NULL; struct spear_thermal_dev *stdev; - struct spear_thermal_pdata *pdata; - int ret = 0; + struct device_node *np = pdev->dev.of_node; struct resource *stres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + int ret = 0, val; + + if (!np || !of_property_read_u32(np, "st,thermal-flags", &val)) { + dev_err(&pdev->dev, "Failed: DT Pdata not passed\n"); + return -EINVAL; + } if (!stres) { dev_err(&pdev->dev, "memory resource missing\n"); return -ENODEV; } - pdata = dev_get_platdata(&pdev->dev); - if (!pdata) { - dev_err(&pdev->dev, "platform data is NULL\n"); - return -EINVAL; - } - stdev = devm_kzalloc(&pdev->dev, sizeof(*stdev), GFP_KERNEL); if (!stdev) { dev_err(&pdev->dev, "kzalloc fail\n"); @@ -144,7 +143,7 @@ static int spear_thermal_probe(struct platform_device *pdev) goto put_clk; } - stdev->flags = pdata->thermal_flags; + stdev->flags = val; writel_relaxed(stdev->flags, stdev->thermal_base); spear_thermal = thermal_zone_device_register("spear_thermal", 0, @@ -189,6 +188,12 @@ static int spear_thermal_exit(struct platform_device *pdev) return 0; } +static const struct of_device_id spear_thermal_id_table[] = { + { .compatible = "st,thermal-spear1340" }, + {} +}; +MODULE_DEVICE_TABLE(of, spear_thermal_id_table); + static struct platform_driver spear_thermal_driver = { .probe = spear_thermal_probe, .remove = spear_thermal_exit, @@ -196,6 +201,7 @@ static struct platform_driver spear_thermal_driver = { .name = "spear_thermal", .owner = THIS_MODULE, .pm = &spear_thermal_pm_ops, + .of_match_table = of_match_ptr(spear_thermal_id_table), }, }; diff --git a/include/linux/platform_data/spear_thermal.h b/include/linux/platform_data/spear_thermal.h deleted file mode 100644 index 724f2e1cbbcb..000000000000 --- a/include/linux/platform_data/spear_thermal.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * SPEAr thermal driver platform data. - * - * Copyright (C) 2011-2012 ST Microelectronics - * Author: Vincenzo Frascino - * - * 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 SPEAR_THERMAL_H -#define SPEAR_THERMAL_H - -/* SPEAr Thermal Sensor Platform Data */ -struct spear_thermal_pdata { - /* flags used to enable thermal sensor */ - unsigned int thermal_flags; -}; - -#endif /* SPEAR_THERMAL_H */ -- cgit v1.2.3 From 1aad779fccdbb4d79af7b9de93dfd2bfe807e052 Mon Sep 17 00:00:00 2001 From: Ola Lilja Date: Thu, 24 May 2012 15:26:03 +0200 Subject: ALSA: pcm: Add debug-print helper function Adds a function getting the stream-name as a string for a specific stream. Signed-off-by: Ola Lilja Reviewed-by: Takashi Iwai Signed-off-by: Mark Brown --- include/sound/pcm.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 0d1112815be3..a55d5db7eb5a 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -1073,4 +1073,15 @@ static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max) const char *snd_pcm_format_name(snd_pcm_format_t format); +/** + * Get a string naming the direction of a stream + */ +static inline const char *snd_pcm_stream_str(struct snd_pcm_substream *substream) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + return "Playback"; + else + return "Capture"; +} + #endif /* __SOUND_PCM_H */ -- cgit v1.2.3 From d7e7eb91551ad99244b989d71d092cb0375648fa Mon Sep 17 00:00:00 2001 From: Ola Lilja Date: Thu, 24 May 2012 15:26:25 +0200 Subject: ASoC: core: Add widget SND_SOC_DAPM_CLOCK_SUPPLY Adds a supply-widget variant for connection to the clock-framework. This widget-type corresponds to the variant for regulators. Signed-off-by: Ola Lilja Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 10 ++++++++++ sound/soc/soc-dapm.c | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index e3833d9f1914..05559e571d44 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -229,6 +229,10 @@ struct device; { .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \ .shift = wshift, .invert = winvert, \ .event = wevent, .event_flags = wflags} +#define SND_SOC_DAPM_CLOCK_SUPPLY(wname) \ +{ .id = snd_soc_dapm_clock_supply, .name = wname, \ + .reg = SND_SOC_NOPM, .event = dapm_clock_event, \ + .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD } /* generic widgets */ #define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \ @@ -245,6 +249,7 @@ struct device; .reg = SND_SOC_NOPM, .shift = wdelay, .event = dapm_regulator_event, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD } + /* dapm kcontrol types */ #define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ @@ -327,6 +332,8 @@ int dapm_reg_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); int dapm_regulator_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); +int dapm_clock_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); /* dapm controls */ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, @@ -432,6 +439,7 @@ enum snd_soc_dapm_type { snd_soc_dapm_post, /* machine specific post widget - exec last */ snd_soc_dapm_supply, /* power/clock supply */ snd_soc_dapm_regulator_supply, /* external regulator */ + snd_soc_dapm_clock_supply, /* external clock */ snd_soc_dapm_aif_in, /* audio interface input */ snd_soc_dapm_aif_out, /* audio interface output */ snd_soc_dapm_siggen, /* signal generator */ @@ -537,6 +545,8 @@ struct snd_soc_dapm_widget { struct list_head dirty; int inputs; int outputs; + + struct clk *clk; }; struct snd_soc_dapm_update { diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 90ee77d2409d..3bb7a6f058d0 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,7 @@ static int dapm_up_seq[] = { [snd_soc_dapm_pre] = 0, [snd_soc_dapm_supply] = 1, [snd_soc_dapm_regulator_supply] = 1, + [snd_soc_dapm_clock_supply] = 1, [snd_soc_dapm_micbias] = 2, [snd_soc_dapm_dai_link] = 2, [snd_soc_dapm_dai] = 3, @@ -92,6 +94,7 @@ static int dapm_down_seq[] = { [snd_soc_dapm_aif_out] = 10, [snd_soc_dapm_dai] = 10, [snd_soc_dapm_dai_link] = 11, + [snd_soc_dapm_clock_supply] = 12, [snd_soc_dapm_regulator_supply] = 12, [snd_soc_dapm_supply] = 12, [snd_soc_dapm_post] = 13, @@ -391,6 +394,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, case snd_soc_dapm_vmid: case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: + case snd_soc_dapm_clock_supply: case snd_soc_dapm_aif_in: case snd_soc_dapm_aif_out: case snd_soc_dapm_dai: @@ -764,6 +768,7 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, switch (widget->id) { case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: + case snd_soc_dapm_clock_supply: return 0; default: break; @@ -850,6 +855,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, switch (widget->id) { case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: + case snd_soc_dapm_clock_supply: return 0; default: break; @@ -996,6 +1002,24 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w, } EXPORT_SYMBOL_GPL(dapm_regulator_event); +/* + * Handler for clock supply widget. + */ +int dapm_clock_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + if (!w->clk) + return -EIO; + + if (SND_SOC_DAPM_EVENT_ON(event)) { + return clk_enable(w->clk); + } else { + clk_disable(w->clk); + return 0; + } +} +EXPORT_SYMBOL_GPL(dapm_clock_event); + static int dapm_widget_power_check(struct snd_soc_dapm_widget *w) { if (w->power_checked) @@ -1487,6 +1511,7 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, switch (w->id) { case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: + case snd_soc_dapm_clock_supply: /* Supplies can't affect their outputs, only their inputs */ break; default: @@ -1587,6 +1612,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) break; case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: + case snd_soc_dapm_clock_supply: case snd_soc_dapm_micbias: if (d->target_bias_level < SND_SOC_BIAS_STANDBY) d->target_bias_level = SND_SOC_BIAS_STANDBY; @@ -1941,6 +1967,7 @@ static ssize_t dapm_widget_show(struct device *dev, case snd_soc_dapm_mixer_named_ctl: case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: + case snd_soc_dapm_clock_supply: if (w->name) count += sprintf(buf + count, "%s: %s\n", w->name, w->power ? "On":"Off"); @@ -2187,6 +2214,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_post: case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: + case snd_soc_dapm_clock_supply: case snd_soc_dapm_aif_in: case snd_soc_dapm_aif_out: case snd_soc_dapm_dai: @@ -2873,6 +2901,15 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, return NULL; } break; + case snd_soc_dapm_clock_supply: + w->clk = clk_get(dapm->dev, w->name); + if (IS_ERR(w->clk)) { + ret = PTR_ERR(w->clk); + dev_err(dapm->dev, "Failed to request %s: %d\n", + w->name, ret); + return NULL; + } + break; default: break; } @@ -2924,6 +2961,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, break; case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: + case snd_soc_dapm_clock_supply: w->power_check = dapm_supply_check_power; break; case snd_soc_dapm_dai: -- cgit v1.2.3 From bc92657a11c0982783979bbb84ceaf58ba222124 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 25 May 2012 18:22:11 -0600 Subject: ASoC: make snd_soc_dai_link more symmetrical Prior to this patch, the CPU side of a DAI link was specified using a single name. Often, this was the result of calling dev_name() on the device providing the DAI, but in the case of a CPU DAI driver that provided multiple DAIs, it needed to mix together both the device name and some device-relative name, in order to form a single globally unique name. However, the CODEC side of the DAI link was specified using separate fields for device (name or OF node) and device-relative DAI name. This patch allows the CPU side of a DAI link to be specified in the same way as the CODEC side, separating concepts of device and device-relative DAI name. I believe this will be important in multi-codec and/or dynamic PCM scenarios, where a single CPU driver provides multiple DAIs, while also booting using device tree, with accompanying desire not to hard-code the CPU side device's name into the original .cpu_dai_name field. Ideally, both the CPU DAI and CODEC DAI loops in soc_bind_dai_link() would now be identical. However, two things prevent that at present: 1) The need to save rtd->codec for the CODEC side, which means we have to search for the CODEC explicitly, and not just the CODEC side DAI. 2) Since we know the CODEC side DAI is part of a codec, and not just a standalone DAI, it's slightly more efficient to convert .codec_name/ .codec_of_node into a codec first, and then compare each DAI's .codec field, since this avoids strcmp() on each DAI's CODEC's name within the loop. However, the two loops are essentially semantically equivalent. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- include/sound/soc.h | 33 +++++++++++++++++++++++++++----- sound/soc/mxs/mxs-sgtl5000.c | 2 +- sound/soc/soc-core.c | 42 ++++++++++++++++++++++++++++++----------- sound/soc/tegra/tegra_alc5632.c | 6 +++--- sound/soc/tegra/tegra_wm8753.c | 6 +++--- sound/soc/tegra/tegra_wm8903.c | 6 +++--- sound/soc/tegra/trimslice.c | 6 +++--- 7 files changed, 72 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index c703871f5f65..23c4efbe13a6 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -785,13 +785,36 @@ struct snd_soc_dai_link { /* config - must be set by machine driver */ const char *name; /* Codec name */ const char *stream_name; /* Stream name */ - const char *codec_name; /* for multi-codec */ - const struct device_node *codec_of_node; - const char *platform_name; /* for multi-platform */ - const struct device_node *platform_of_node; + /* + * You MAY specify the link's CPU-side device, either by device name, + * or by DT/OF node, but not both. If this information is omitted, + * the CPU-side DAI is matched using .cpu_dai_name only, which hence + * must be globally unique. These fields are currently typically used + * only for codec to codec links, or systems using device tree. + */ + const char *cpu_name; + const struct device_node *cpu_of_node; + /* + * You MAY specify the DAI name of the CPU DAI. If this information is + * omitted, the CPU-side DAI is matched using .cpu_name/.cpu_of_node + * only, which only works well when that device exposes a single DAI. + */ const char *cpu_dai_name; - const struct device_node *cpu_dai_of_node; + /* + * You MUST specify the link's codec, either by device name, or by + * DT/OF node, but not both. + */ + const char *codec_name; + const struct device_node *codec_of_node; + /* You MUST specify the DAI name within the codec */ const char *codec_dai_name; + /* + * You MAY specify the link's platform/PCM/DMA driver, either by + * device name, or by DT/OF node, but not both. Some forms of link + * do not need a platform. + */ + const char *platform_name; + const struct device_node *platform_of_node; int be_id; /* optional ID for machine driver BE identification */ const struct snd_soc_pcm_stream *params; diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index 3e6e8764b2e6..215113b05f7d 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -133,7 +133,7 @@ static int __devinit mxs_sgtl5000_probe_dt(struct platform_device *pdev) mxs_sgtl5000_dai[i].codec_name = NULL; mxs_sgtl5000_dai[i].codec_of_node = codec_np; mxs_sgtl5000_dai[i].cpu_dai_name = NULL; - mxs_sgtl5000_dai[i].cpu_dai_of_node = saif_np[i]; + mxs_sgtl5000_dai[i].cpu_of_node = saif_np[i]; mxs_sgtl5000_dai[i].platform_name = NULL; mxs_sgtl5000_dai[i].platform_of_node = saif_np[i]; } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index b37ee8077ed1..ec8350570346 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -812,13 +812,15 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num) /* Find CPU DAI from registered DAIs*/ list_for_each_entry(cpu_dai, &dai_list, list) { - if (dai_link->cpu_dai_of_node) { - if (cpu_dai->dev->of_node != dai_link->cpu_dai_of_node) - continue; - } else { - if (strcmp(cpu_dai->name, dai_link->cpu_dai_name)) - continue; - } + if (dai_link->cpu_of_node && + (cpu_dai->dev->of_node != dai_link->cpu_of_node)) + continue; + if (dai_link->cpu_name && + strcmp(dev_name(cpu_dai->dev), dai_link->cpu_name)) + continue; + if (dai_link->cpu_dai_name && + strcmp(cpu_dai->name, dai_link->cpu_dai_name)) + continue; rtd->cpu_dai = cpu_dai; } @@ -3346,6 +3348,12 @@ int snd_soc_register_card(struct snd_soc_card *card) link->name); return -EINVAL; } + /* Codec DAI name must be specified */ + if (!link->codec_dai_name) { + dev_err(card->dev, "codec_dai_name not set for %s\n", + link->name); + return -EINVAL; + } /* * Platform may be specified by either name or OF node, but @@ -3358,12 +3366,24 @@ int snd_soc_register_card(struct snd_soc_card *card) } /* - * CPU DAI must be specified by 1 of name or OF node, - * not both or neither. + * CPU device may be specified by either name or OF node, but + * can be left unspecified, and will be matched based on DAI + * name alone.. + */ + if (link->cpu_name && link->cpu_of_node) { + dev_err(card->dev, + "Neither/both cpu name/of_node are set for %s\n", + link->name); + return -EINVAL; + } + /* + * At least one of CPU DAI name or CPU device name/node must be + * specified */ - if (!!link->cpu_dai_name == !!link->cpu_dai_of_node) { + if (!link->cpu_dai_name && + !(link->cpu_name || link->cpu_of_node)) { dev_err(card->dev, - "Neither/both cpu_dai name/of_node are set for %s\n", + "Neither cpu_dai_name nor cpu_name/of_node are set for %s\n", link->name); return -EINVAL; } diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c index 15669570ae31..417b09b83fdf 100644 --- a/sound/soc/tegra/tegra_alc5632.c +++ b/sound/soc/tegra/tegra_alc5632.c @@ -197,16 +197,16 @@ static __devinit int tegra_alc5632_probe(struct platform_device *pdev) goto err; } - tegra_alc5632_dai.cpu_dai_of_node = of_parse_phandle( + tegra_alc5632_dai.cpu_of_node = of_parse_phandle( pdev->dev.of_node, "nvidia,i2s-controller", 0); - if (!tegra_alc5632_dai.cpu_dai_of_node) { + if (!tegra_alc5632_dai.cpu_of_node) { dev_err(&pdev->dev, "Property 'nvidia,i2s-controller' missing or invalid\n"); ret = -EINVAL; goto err; } - tegra_alc5632_dai.platform_of_node = tegra_alc5632_dai.cpu_dai_of_node; + tegra_alc5632_dai.platform_of_node = tegra_alc5632_dai.cpu_of_node; ret = tegra_asoc_utils_init(&alc5632->util_data, &pdev->dev); if (ret) diff --git a/sound/soc/tegra/tegra_wm8753.c b/sound/soc/tegra/tegra_wm8753.c index 4e77026807a2..02bd5a8e8544 100644 --- a/sound/soc/tegra/tegra_wm8753.c +++ b/sound/soc/tegra/tegra_wm8753.c @@ -157,9 +157,9 @@ static __devinit int tegra_wm8753_driver_probe(struct platform_device *pdev) goto err; } - tegra_wm8753_dai.cpu_dai_of_node = of_parse_phandle( + tegra_wm8753_dai.cpu_of_node = of_parse_phandle( pdev->dev.of_node, "nvidia,i2s-controller", 0); - if (!tegra_wm8753_dai.cpu_dai_of_node) { + if (!tegra_wm8753_dai.cpu_of_node) { dev_err(&pdev->dev, "Property 'nvidia,i2s-controller' missing or invalid\n"); ret = -EINVAL; @@ -167,7 +167,7 @@ static __devinit int tegra_wm8753_driver_probe(struct platform_device *pdev) } tegra_wm8753_dai.platform_of_node = - tegra_wm8753_dai.cpu_dai_of_node; + tegra_wm8753_dai.cpu_of_node; ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev); if (ret) diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c index b75e0e8db1d0..1fd71e5a9eb9 100644 --- a/sound/soc/tegra/tegra_wm8903.c +++ b/sound/soc/tegra/tegra_wm8903.c @@ -331,9 +331,9 @@ static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev) } tegra_wm8903_dai.cpu_dai_name = NULL; - tegra_wm8903_dai.cpu_dai_of_node = of_parse_phandle(np, + tegra_wm8903_dai.cpu_of_node = of_parse_phandle(np, "nvidia,i2s-controller", 0); - if (!tegra_wm8903_dai.cpu_dai_of_node) { + if (!tegra_wm8903_dai.cpu_of_node) { dev_err(&pdev->dev, "Property 'nvidia,i2s-controller' missing or invalid\n"); ret = -EINVAL; @@ -342,7 +342,7 @@ static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev) tegra_wm8903_dai.platform_name = NULL; tegra_wm8903_dai.platform_of_node = - tegra_wm8903_dai.cpu_dai_of_node; + tegra_wm8903_dai.cpu_of_node; } else { card->dapm_routes = harmony_audio_map; card->num_dapm_routes = ARRAY_SIZE(harmony_audio_map); diff --git a/sound/soc/tegra/trimslice.c b/sound/soc/tegra/trimslice.c index 4a8d5b672c9f..5815430e8521 100644 --- a/sound/soc/tegra/trimslice.c +++ b/sound/soc/tegra/trimslice.c @@ -162,9 +162,9 @@ static __devinit int tegra_snd_trimslice_probe(struct platform_device *pdev) } trimslice_tlv320aic23_dai.cpu_dai_name = NULL; - trimslice_tlv320aic23_dai.cpu_dai_of_node = of_parse_phandle( + trimslice_tlv320aic23_dai.cpu_of_node = of_parse_phandle( pdev->dev.of_node, "nvidia,i2s-controller", 0); - if (!trimslice_tlv320aic23_dai.cpu_dai_of_node) { + if (!trimslice_tlv320aic23_dai.cpu_of_node) { dev_err(&pdev->dev, "Property 'nvidia,i2s-controller' missing or invalid\n"); ret = -EINVAL; @@ -173,7 +173,7 @@ static __devinit int tegra_snd_trimslice_probe(struct platform_device *pdev) trimslice_tlv320aic23_dai.platform_name = NULL; trimslice_tlv320aic23_dai.platform_of_node = - trimslice_tlv320aic23_dai.cpu_dai_of_node; + trimslice_tlv320aic23_dai.cpu_of_node; } ret = tegra_asoc_utils_init(&trimslice->util_data, &pdev->dev); -- cgit v1.2.3 From 6c9d8cf6372ed2995a3d982f5c1f966e842101cc Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Thu, 31 May 2012 15:18:01 +0100 Subject: ASoC: core: Add single controls with specified range of values Control type added for cases where a specific range of values within a register are required for control. Added convenience macros: SOC_SINGLE_RANGE SOC_SINGLE_RANGE_TLV Added accessor implementations: snd_soc_info_volsw_range snd_soc_put_volsw_range snd_soc_get_volsw_range Signed-off-by: Michal Hajduk Signed-off-by: Adam Thomson Signed-off-by: Mark Brown --- include/sound/soc.h | 23 ++++++++++++ sound/soc/soc-core.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 23c4efbe13a6..e4348d25fca3 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -47,6 +47,13 @@ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ .put = snd_soc_put_volsw, \ .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } +#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, \ + .put = snd_soc_put_volsw_range, \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ + {.reg = xreg, .shift = xshift, .min = xmin,\ + .max = xmax, .platform_max = xmax, .invert = xinvert} } #define SOC_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ @@ -67,6 +74,16 @@ {.reg = xreg, .rreg = xreg, \ .shift = xshift, .rshift = xshift, \ .max = xmax, .min = xmin} } +#define SOC_SINGLE_RANGE_TLV(xname, xreg, xshift, xmin, xmax, xinvert, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ + SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw_range, \ + .get = snd_soc_get_volsw_range, .put = snd_soc_put_volsw_range, \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ + {.reg = xreg, .shift = xshift, .min = xmin,\ + .max = xmax, .platform_max = xmax, .invert = xinvert} } #define SOC_DOUBLE(xname, reg, shift_left, shift_right, max, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \ @@ -460,6 +477,12 @@ int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); int snd_soc_limit_volume(struct snd_soc_codec *codec, const char *name, int max); int snd_soc_bytes_info(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index ec8350570346..3d803f3cd272 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2791,6 +2791,104 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8); +/** + * snd_soc_info_volsw_range - single mixer info callback with range. + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to provide information, within a range, about a single + * mixer control. + * + * returns 0 for success. + */ +int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int platform_max; + int min = mc->min; + + if (!mc->platform_max) + mc->platform_max = mc->max; + platform_max = mc->platform_max; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = platform_max - min; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_info_volsw_range); + +/** + * snd_soc_put_volsw_range - single mixer put value callback with range. + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to set the value, within a range, for a single mixer control. + * + * Returns 0 for success. + */ +int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int reg = mc->reg; + unsigned int shift = mc->shift; + int min = mc->min; + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + unsigned int val, val_mask; + + val = ((ucontrol->value.integer.value[0] + min) & mask); + if (invert) + val = max - val; + val_mask = mask << shift; + val = val << shift; + + return snd_soc_update_bits_locked(codec, reg, val_mask, val); +} +EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range); + +/** + * snd_soc_get_volsw_range - single mixer get callback with range + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to get the value, within a range, of a single mixer control. + * + * Returns 0 for success. + */ +int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int reg = mc->reg; + unsigned int shift = mc->shift; + int min = mc->min; + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + + ucontrol->value.integer.value[0] = + (snd_soc_read(codec, reg) >> shift) & mask; + if (invert) + ucontrol->value.integer.value[0] = + max - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[0] = + ucontrol->value.integer.value[0] - min; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range); + /** * snd_soc_limit_volume - Set new limit to an existing volume control. * -- cgit v1.2.3 From 275c58d77062bbb85dbeb3843ba04f34aa50cf8e Mon Sep 17 00:00:00 2001 From: Toshi Kani Date: Wed, 23 May 2012 20:25:19 -0600 Subject: ACPI: Add an interface to evaluate _OST Added acpi_evaluate_hotplug_opt(). All ACPI hotplug handlers must call this function when evaluating _OST for hotplug operations. If the platform does not support _OST, this function returns AE_NOT_FOUND and has no effect on the platform. ACPI_HOTPLUG_OST is defined when all relevant ACPI hotplug operations, such as CPU, memory and container hotplug, are enabled. This assures consistent behavior among the hotplug operations with regarding the _OST support. When ACPI_HOTPLUG_OST is not defined, this function is a no-op. ACPI PCI hotplug is not enhanced to support _OST at this time since it is a legacy method being replaced by PCIe native hotplug. _OST support for ACPI PCI hotplug may be added in future if necessary. Some platforms may require the OS to support _OST in order to support ACPI hotplug operations. For example, if a platform has the management console where user can request a hotplug operation from, this _OST support would be required for the management console to show the result of the hotplug request to user. Added macro definitions of _OST source events and status codes. Also renamed OSC_SB_CPUHP_OST_SUPPORT to OSC_SB_HOTPLUG_OST_SUPPORT since this _OSC bit is not specific to CPU hotplug. This bit is defined in Table 6-147 of ACPI 5.0 as follows. Bits: 3 Field Name: Insertion / Ejection _OST Processing Support Definition: This bit is set if OSPM will evaluate the _OST object defined under a device when processing insertion and ejection source event codes. Signed-off-by: Toshi Kani Signed-off-by: Len Brown --- drivers/acpi/utils.c | 42 ++++++++++++++++++++++++++++++++++++++++++ include/acpi/acpi_bus.h | 3 +++ include/linux/acpi.h | 40 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 84 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index adbbc1c80a26..3e87c9c538aa 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -412,3 +412,45 @@ out: return status; } EXPORT_SYMBOL(acpi_get_physical_device_location); + +/** + * acpi_evaluate_hotplug_ost: Evaluate _OST for hotplug operations + * @handle: ACPI device handle + * @source_event: source event code + * @status_code: status code + * @status_buf: optional detailed information (NULL if none) + * + * Evaluate _OST for hotplug operations. All ACPI hotplug handlers + * must call this function when evaluating _OST for hotplug operations. + * When the platform does not support _OST, this function has no effect. + */ +acpi_status +acpi_evaluate_hotplug_ost(acpi_handle handle, u32 source_event, + u32 status_code, struct acpi_buffer *status_buf) +{ +#ifdef ACPI_HOTPLUG_OST + union acpi_object params[3] = { + {.type = ACPI_TYPE_INTEGER,}, + {.type = ACPI_TYPE_INTEGER,}, + {.type = ACPI_TYPE_BUFFER,} + }; + struct acpi_object_list arg_list = {3, params}; + acpi_status status; + + params[0].integer.value = source_event; + params[1].integer.value = status_code; + if (status_buf != NULL) { + params[2].buffer.pointer = status_buf->pointer; + params[2].buffer.length = status_buf->length; + } else { + params[2].buffer.pointer = NULL; + params[2].buffer.length = 0; + } + + status = acpi_evaluate_object(handle, "_OST", &arg_list, NULL); + return status; +#else + return AE_OK; +#endif +} +EXPORT_SYMBOL(acpi_evaluate_hotplug_ost); diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index b0d62820ada1..1139f3a01209 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -50,6 +50,9 @@ acpi_evaluate_reference(acpi_handle handle, acpi_string pathname, struct acpi_object_list *arguments, struct acpi_handle_list *list); +acpi_status +acpi_evaluate_hotplug_ost(acpi_handle handle, u32 source_event, + u32 status_code, struct acpi_buffer *status_buf); struct acpi_pld { unsigned int revision:7; /* 0 */ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index f421dd84f29d..b2b4d2ad7103 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -277,7 +277,7 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context); #define OSC_SB_PAD_SUPPORT 1 #define OSC_SB_PPC_OST_SUPPORT 2 #define OSC_SB_PR3_SUPPORT 4 -#define OSC_SB_CPUHP_OST_SUPPORT 8 +#define OSC_SB_HOTPLUG_OST_SUPPORT 8 #define OSC_SB_APEI_SUPPORT 16 extern bool osc_sb_apei_support_acked; @@ -309,6 +309,44 @@ extern bool osc_sb_apei_support_acked; extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req); + +/* Enable _OST when all relevant hotplug operations are enabled */ +#if defined(CONFIG_ACPI_HOTPLUG_CPU) && \ + (defined(CONFIG_ACPI_HOTPLUG_MEMORY) || \ + defined(CONFIG_ACPI_HOTPLUG_MEMORY_MODULE)) && \ + (defined(CONFIG_ACPI_CONTAINER) || \ + defined(CONFIG_ACPI_CONTAINER_MODULE)) +#define ACPI_HOTPLUG_OST +#endif + +/* _OST Source Event Code (OSPM Action) */ +#define ACPI_OST_EC_OSPM_SHUTDOWN 0x100 +#define ACPI_OST_EC_OSPM_EJECT 0x103 +#define ACPI_OST_EC_OSPM_INSERTION 0x200 + +/* _OST General Processing Status Code */ +#define ACPI_OST_SC_SUCCESS 0x0 +#define ACPI_OST_SC_NON_SPECIFIC_FAILURE 0x1 +#define ACPI_OST_SC_UNRECOGNIZED_NOTIFY 0x2 + +/* _OST OS Shutdown Processing (0x100) Status Code */ +#define ACPI_OST_SC_OS_SHUTDOWN_DENIED 0x80 +#define ACPI_OST_SC_OS_SHUTDOWN_IN_PROGRESS 0x81 +#define ACPI_OST_SC_OS_SHUTDOWN_COMPLETED 0x82 +#define ACPI_OST_SC_OS_SHUTDOWN_NOT_SUPPORTED 0x83 + +/* _OST Ejection Request (0x3, 0x103) Status Code */ +#define ACPI_OST_SC_EJECT_NOT_SUPPORTED 0x80 +#define ACPI_OST_SC_DEVICE_IN_USE 0x81 +#define ACPI_OST_SC_DEVICE_BUSY 0x82 +#define ACPI_OST_SC_EJECT_DEPENDENCY_BUSY 0x83 +#define ACPI_OST_SC_EJECT_IN_PROGRESS 0x84 + +/* _OST Insertion Request (0x200) Status Code */ +#define ACPI_OST_SC_INSERT_IN_PROGRESS 0x80 +#define ACPI_OST_SC_DRIVER_LOAD_FAILURE 0x81 +#define ACPI_OST_SC_INSERT_NOT_SUPPORTED 0x82 + extern void acpi_early_init(void); extern int acpi_nvs_register(__u64 start, __u64 size); -- cgit v1.2.3 From c4753e57b78b213f2384fa0dbafa348b087114fa Mon Sep 17 00:00:00 2001 From: Toshi Kani Date: Wed, 23 May 2012 20:25:20 -0600 Subject: ACPI: Add _OST support for sysfs eject Changed acpi_bus_hot_remove_device() to support _OST. This function is also changed to global so that it can be called from hotplug notify handlers to perform hot-remove operation. Changed acpi_eject_store(), which is the sysfs eject handler. It checks eject_pending to see if the request was originated from ACPI eject notification. If not, it calls _OST(0x103,84,) per Figure 6-37 in ACPI 5.0 spec. Added eject_pending bit to acpi_device_flags. This bit is set when the kernel has received an ACPI eject notification, but does not initiate its hot-remove operation by itself. Added struct acpi_eject_event. This structure is used to pass extended information to acpi_bus_hot_remove_device(), which has a single argument to support asynchronous call Signed-off-by: Toshi Kani Signed-off-by: Len Brown --- drivers/acpi/scan.c | 58 +++++++++++++++++++++++++++++++++++++++++-------- include/acpi/acpi_bus.h | 9 +++++++- 2 files changed, 57 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 85cbfdccc97c..bea3ab6b524f 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -83,19 +83,29 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha } static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); -static void acpi_bus_hot_remove_device(void *context) +/** + * acpi_bus_hot_remove_device: hot-remove a device and its children + * @context: struct acpi_eject_event pointer (freed in this func) + * + * Hot-remove a device and its children. This function frees up the + * memory space passed by arg context, so that the caller may call + * this function asynchronously through acpi_os_hotplug_execute(). + */ +void acpi_bus_hot_remove_device(void *context) { + struct acpi_eject_event *ej_event = (struct acpi_eject_event *) context; struct acpi_device *device; - acpi_handle handle = context; + acpi_handle handle = ej_event->handle; struct acpi_object_list arg_list; union acpi_object arg; acpi_status status = AE_OK; + u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */ if (acpi_bus_get_device(handle, &device)) - return; + goto err_out; if (!device) - return; + goto err_out; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Hot-removing device %s...\n", dev_name(&device->dev))); @@ -103,7 +113,7 @@ static void acpi_bus_hot_remove_device(void *context) if (acpi_bus_trim(device, 1)) { printk(KERN_ERR PREFIX "Removing device failed\n"); - return; + goto err_out; } /* power off device */ @@ -129,10 +139,21 @@ static void acpi_bus_hot_remove_device(void *context) * TBD: _EJD support. */ status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL); - if (ACPI_FAILURE(status)) - printk(KERN_WARNING PREFIX - "Eject device failed\n"); + if (ACPI_FAILURE(status)) { + if (status != AE_NOT_FOUND) + printk(KERN_WARNING PREFIX + "Eject device failed\n"); + goto err_out; + } + + kfree(context); + return; +err_out: + /* Inform firmware the hot-remove operation has completed w/ error */ + (void) acpi_evaluate_hotplug_ost(handle, + ej_event->event, ost_code, NULL); + kfree(context); return; } @@ -144,6 +165,7 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, acpi_status status; acpi_object_type type = 0; struct acpi_device *acpi_device = to_acpi_device(d); + struct acpi_eject_event *ej_event; if ((!count) || (buf[0] != '1')) { return -EINVAL; @@ -160,7 +182,25 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, goto err; } - acpi_os_hotplug_execute(acpi_bus_hot_remove_device, acpi_device->handle); + ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL); + if (!ej_event) { + ret = -ENOMEM; + goto err; + } + + ej_event->handle = acpi_device->handle; + if (acpi_device->flags.eject_pending) { + /* event originated from ACPI eject notification */ + ej_event->event = ACPI_NOTIFY_EJECT_REQUEST; + acpi_device->flags.eject_pending = 0; + } else { + /* event originated from user */ + ej_event->event = ACPI_OST_EC_OSPM_EJECT; + (void) acpi_evaluate_hotplug_ost(ej_event->handle, + ej_event->event, ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); + } + + acpi_os_hotplug_execute(acpi_bus_hot_remove_device, (void *)ej_event); err: return ret; } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 1139f3a01209..62eb514f8e3a 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -182,7 +182,8 @@ struct acpi_device_flags { u32 suprise_removal_ok:1; u32 power_manageable:1; u32 performance_manageable:1; - u32 reserved:24; + u32 eject_pending:1; + u32 reserved:23; }; /* File System */ @@ -334,6 +335,11 @@ struct acpi_bus_event { u32 data; }; +struct acpi_eject_event { + acpi_handle handle; + u32 event; +}; + extern struct kobject *acpi_kobj; extern int acpi_bus_generate_netlink_event(const char*, const char*, u8, int); void acpi_bus_private_data_handler(acpi_handle, void *); @@ -371,6 +377,7 @@ int acpi_bus_register_driver(struct acpi_driver *driver); void acpi_bus_unregister_driver(struct acpi_driver *driver); int acpi_bus_add(struct acpi_device **child, struct acpi_device *parent, acpi_handle handle, int type); +void acpi_bus_hot_remove_device(void *context); int acpi_bus_trim(struct acpi_device *start, int rmdevice); int acpi_bus_start(struct acpi_device *device); acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle * ejd); -- cgit v1.2.3 From cd1678f963298a9e777f3edb72d28bc18a3a32c2 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Tue, 29 May 2012 12:41:19 +0200 Subject: iio: frequency: New driver for AD9523 SPI Low Jitter Clock Generator Changes since V1: Apply Jonathan's review feedback: Revise device status attribute names, and split documentation into two sections. Add additional comments, and fix indention issues. Remove pointless zero initializations. Revise return value handling. Simplify some code sections. Split store_eeprom and sync handling into separate functions. Use strtobool where applicable. Document platform data structures using kernel-doc style. Use dev_to_iio_dev write_raw IIO_CHAN_INFO_FREQUENCY: Reject values <= 0 Make patch target drivers/iio Changes since V2: Use for_each_clear_bit() and __set_bit() where applicable. Add descriptive comment. Avoid temporary for struct regulator. spi_device_id name use ad9523-1, ad9523 will be added later. Signed-off-by: Michael Hennerich Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- .../ABI/testing/sysfs-bus-iio-frequency-ad9523 | 37 + drivers/iio/Kconfig | 1 + drivers/iio/Makefile | 1 + drivers/iio/frequency/Kconfig | 23 + drivers/iio/frequency/Makefile | 5 + drivers/iio/frequency/ad9523.c | 1057 ++++++++++++++++++++ include/linux/iio/frequency/ad9523.h | 195 ++++ 7 files changed, 1319 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523 create mode 100644 drivers/iio/frequency/Kconfig create mode 100644 drivers/iio/frequency/Makefile create mode 100644 drivers/iio/frequency/ad9523.c create mode 100644 include/linux/iio/frequency/ad9523.h (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523 b/Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523 new file mode 100644 index 000000000000..2ce9c3f68eee --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523 @@ -0,0 +1,37 @@ +What: /sys/bus/iio/devices/iio:deviceX/pll2_feedback_clk_present +What: /sys/bus/iio/devices/iio:deviceX/pll2_reference_clk_present +What: /sys/bus/iio/devices/iio:deviceX/pll1_reference_clk_a_present +What: /sys/bus/iio/devices/iio:deviceX/pll1_reference_clk_b_present +What: /sys/bus/iio/devices/iio:deviceX/pll1_reference_clk_test_present +What: /sys/bus/iio/devices/iio:deviceX/vcxo_clk_present +KernelVersion: 3.4.0 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns either '1' or '0'. + '1' means that the clock in question is present. + '0' means that the clock is missing. + +What: /sys/bus/iio/devices/iio:deviceX/pllY_locked +KernelVersion: 3.4.0 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns either '1' or '0'. '1' means that the + pllY is locked. + +What: /sys/bus/iio/devices/iio:deviceX/store_eeprom +KernelVersion: 3.4.0 +Contact: linux-iio@vger.kernel.org +Description: + Writing '1' stores the current device configuration into + on-chip EEPROM. After power-up or chip reset the device will + automatically load the saved configuration. + +What: /sys/bus/iio/devices/iio:deviceX/sync_dividers +KernelVersion: 3.4.0 +Contact: linux-iio@vger.kernel.org +Description: + Writing '1' triggers the clock distribution synchronization + functionality. All dividers are reset and the channels start + with their predefined phase offsets (out_altvoltageY_phase). + Writing this file has the effect as driving the external + /SYNC pin low. diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index cacc74d70241..64c88e5cda4d 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -51,5 +51,6 @@ config IIO_CONSUMERS_PER_TRIGGER source "drivers/iio/adc/Kconfig" source "drivers/iio/amplifiers/Kconfig" source "drivers/iio/light/Kconfig" +source "drivers/iio/frequency/Kconfig" endif # IIO diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 060b674d278c..bd801c0bbc2f 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o obj-y += adc/ obj-y += amplifiers/ obj-y += light/ +obj-y += frequency/ diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig new file mode 100644 index 000000000000..0458c92464a3 --- /dev/null +++ b/drivers/iio/frequency/Kconfig @@ -0,0 +1,23 @@ +# +# Frequency +# Direct Digital Synthesis drivers (DDS) +# Clock Distribution device drivers +# Phase-Locked Loop (PLL) frequency synthesizers +# + +menu "Frequency Synthesizers DDS/PLL" + +menu "Clock Generator/Distribution" + +config AD9523 + tristate "Analog Devices AD9523 Low Jitter Clock Generator" + depends on SPI + help + Say yes here to build support for Analog Devices AD9523 Low Jitter + Clock Generator. The driver provides direct access via sysfs. + + To compile this driver as a module, choose M here: the + module will be called ad9523. + +endmenu +endmenu diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile new file mode 100644 index 000000000000..1b5b22417da1 --- /dev/null +++ b/drivers/iio/frequency/Makefile @@ -0,0 +1,5 @@ +# +# Makefile iio/frequency +# + +obj-$(CONFIG_AD9523) += ad9523.o diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c new file mode 100644 index 000000000000..7272924484c1 --- /dev/null +++ b/drivers/iio/frequency/ad9523.c @@ -0,0 +1,1057 @@ +/* + * AD9523 SPI Low Jitter Clock Generator + * + * Copyright 2012 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define AD9523_READ (1 << 15) +#define AD9523_WRITE (0 << 15) +#define AD9523_CNT(x) (((x) - 1) << 13) +#define AD9523_ADDR(x) ((x) & 0xFFF) + +#define AD9523_R1B (1 << 16) +#define AD9523_R2B (2 << 16) +#define AD9523_R3B (3 << 16) +#define AD9523_TRANSF_LEN(x) ((x) >> 16) + +#define AD9523_SERIAL_PORT_CONFIG (AD9523_R1B | 0x0) +#define AD9523_VERSION_REGISTER (AD9523_R1B | 0x2) +#define AD9523_PART_REGISTER (AD9523_R1B | 0x3) +#define AD9523_READBACK_CTRL (AD9523_R1B | 0x4) + +#define AD9523_EEPROM_CUSTOMER_VERSION_ID (AD9523_R2B | 0x6) + +#define AD9523_PLL1_REF_A_DIVIDER (AD9523_R2B | 0x11) +#define AD9523_PLL1_REF_B_DIVIDER (AD9523_R2B | 0x13) +#define AD9523_PLL1_REF_TEST_DIVIDER (AD9523_R1B | 0x14) +#define AD9523_PLL1_FEEDBACK_DIVIDER (AD9523_R2B | 0x17) +#define AD9523_PLL1_CHARGE_PUMP_CTRL (AD9523_R2B | 0x19) +#define AD9523_PLL1_INPUT_RECEIVERS_CTRL (AD9523_R1B | 0x1A) +#define AD9523_PLL1_REF_CTRL (AD9523_R1B | 0x1B) +#define AD9523_PLL1_MISC_CTRL (AD9523_R1B | 0x1C) +#define AD9523_PLL1_LOOP_FILTER_CTRL (AD9523_R1B | 0x1D) + +#define AD9523_PLL2_CHARGE_PUMP (AD9523_R1B | 0xF0) +#define AD9523_PLL2_FEEDBACK_DIVIDER_AB (AD9523_R1B | 0xF1) +#define AD9523_PLL2_CTRL (AD9523_R1B | 0xF2) +#define AD9523_PLL2_VCO_CTRL (AD9523_R1B | 0xF3) +#define AD9523_PLL2_VCO_DIVIDER (AD9523_R1B | 0xF4) +#define AD9523_PLL2_LOOP_FILTER_CTRL (AD9523_R2B | 0xF6) +#define AD9523_PLL2_R2_DIVIDER (AD9523_R1B | 0xF7) + +#define AD9523_CHANNEL_CLOCK_DIST(ch) (AD9523_R3B | (0x192 + 3 * ch)) + +#define AD9523_PLL1_OUTPUT_CTRL (AD9523_R1B | 0x1BA) +#define AD9523_PLL1_OUTPUT_CHANNEL_CTRL (AD9523_R1B | 0x1BB) + +#define AD9523_READBACK_0 (AD9523_R1B | 0x22C) +#define AD9523_READBACK_1 (AD9523_R1B | 0x22D) + +#define AD9523_STATUS_SIGNALS (AD9523_R3B | 0x232) +#define AD9523_POWER_DOWN_CTRL (AD9523_R1B | 0x233) +#define AD9523_IO_UPDATE (AD9523_R1B | 0x234) + +#define AD9523_EEPROM_DATA_XFER_STATUS (AD9523_R1B | 0xB00) +#define AD9523_EEPROM_ERROR_READBACK (AD9523_R1B | 0xB01) +#define AD9523_EEPROM_CTRL1 (AD9523_R1B | 0xB02) +#define AD9523_EEPROM_CTRL2 (AD9523_R1B | 0xB03) + +/* AD9523_SERIAL_PORT_CONFIG */ + +#define AD9523_SER_CONF_SDO_ACTIVE (1 << 7) +#define AD9523_SER_CONF_SOFT_RESET (1 << 5) + +/* AD9523_READBACK_CTRL */ +#define AD9523_READBACK_CTRL_READ_BUFFERED (1 << 0) + +/* AD9523_PLL1_CHARGE_PUMP_CTRL */ +#define AD9523_PLL1_CHARGE_PUMP_CURRENT_nA(x) (((x) / 500) & 0x7F) +#define AD9523_PLL1_CHARGE_PUMP_TRISTATE (1 << 7) +#define AD9523_PLL1_CHARGE_PUMP_MODE_NORMAL (3 << 8) +#define AD9523_PLL1_CHARGE_PUMP_MODE_PUMP_DOWN (2 << 8) +#define AD9523_PLL1_CHARGE_PUMP_MODE_PUMP_UP (1 << 8) +#define AD9523_PLL1_CHARGE_PUMP_MODE_TRISTATE (0 << 8) +#define AD9523_PLL1_BACKLASH_PW_MIN (0 << 10) +#define AD9523_PLL1_BACKLASH_PW_LOW (1 << 10) +#define AD9523_PLL1_BACKLASH_PW_HIGH (2 << 10) +#define AD9523_PLL1_BACKLASH_PW_MAX (3 << 10) + +/* AD9523_PLL1_INPUT_RECEIVERS_CTRL */ +#define AD9523_PLL1_REF_TEST_RCV_EN (1 << 7) +#define AD9523_PLL1_REFB_DIFF_RCV_EN (1 << 6) +#define AD9523_PLL1_REFA_DIFF_RCV_EN (1 << 5) +#define AD9523_PLL1_REFB_RCV_EN (1 << 4) +#define AD9523_PLL1_REFA_RCV_EN (1 << 3) +#define AD9523_PLL1_REFA_REFB_PWR_CTRL_EN (1 << 2) +#define AD9523_PLL1_OSC_IN_CMOS_NEG_INP_EN (1 << 1) +#define AD9523_PLL1_OSC_IN_DIFF_EN (1 << 0) + +/* AD9523_PLL1_REF_CTRL */ +#define AD9523_PLL1_BYPASS_REF_TEST_DIV_EN (1 << 7) +#define AD9523_PLL1_BYPASS_FEEDBACK_DIV_EN (1 << 6) +#define AD9523_PLL1_ZERO_DELAY_MODE_INT (1 << 5) +#define AD9523_PLL1_ZERO_DELAY_MODE_EXT (0 << 5) +#define AD9523_PLL1_OSC_IN_PLL_FEEDBACK_EN (1 << 4) +#define AD9523_PLL1_ZD_IN_CMOS_NEG_INP_EN (1 << 3) +#define AD9523_PLL1_ZD_IN_DIFF_EN (1 << 2) +#define AD9523_PLL1_REFB_CMOS_NEG_INP_EN (1 << 1) +#define AD9523_PLL1_REFA_CMOS_NEG_INP_EN (1 << 0) + +/* AD9523_PLL1_MISC_CTRL */ +#define AD9523_PLL1_REFB_INDEP_DIV_CTRL_EN (1 << 7) +#define AD9523_PLL1_OSC_CTRL_FAIL_VCC_BY2_EN (1 << 6) +#define AD9523_PLL1_REF_MODE(x) ((x) << 2) +#define AD9523_PLL1_BYPASS_REFB_DIV (1 << 1) +#define AD9523_PLL1_BYPASS_REFA_DIV (1 << 0) + +/* AD9523_PLL1_LOOP_FILTER_CTRL */ +#define AD9523_PLL1_LOOP_FILTER_RZERO(x) ((x) & 0xF) + +/* AD9523_PLL2_CHARGE_PUMP */ +#define AD9523_PLL2_CHARGE_PUMP_CURRENT_nA(x) ((x) / 3500) + +/* AD9523_PLL2_FEEDBACK_DIVIDER_AB */ +#define AD9523_PLL2_FB_NDIV_A_CNT(x) (((x) & 0x3) << 6) +#define AD9523_PLL2_FB_NDIV_B_CNT(x) (((x) & 0x3F) << 0) +#define AD9523_PLL2_FB_NDIV(a, b) (4 * (b) + (a)) + +/* AD9523_PLL2_CTRL */ +#define AD9523_PLL2_CHARGE_PUMP_MODE_NORMAL (3 << 0) +#define AD9523_PLL2_CHARGE_PUMP_MODE_PUMP_DOWN (2 << 0) +#define AD9523_PLL2_CHARGE_PUMP_MODE_PUMP_UP (1 << 0) +#define AD9523_PLL2_CHARGE_PUMP_MODE_TRISTATE (0 << 0) +#define AD9523_PLL2_BACKLASH_PW_MIN (0 << 2) +#define AD9523_PLL2_BACKLASH_PW_LOW (1 << 2) +#define AD9523_PLL2_BACKLASH_PW_HIGH (2 << 2) +#define AD9523_PLL2_BACKLASH_PW_MAX (3 << 1) +#define AD9523_PLL2_BACKLASH_CTRL_EN (1 << 4) +#define AD9523_PLL2_FREQ_DOUBLER_EN (1 << 5) +#define AD9523_PLL2_LOCK_DETECT_PWR_DOWN_EN (1 << 7) + +/* AD9523_PLL2_VCO_CTRL */ +#define AD9523_PLL2_VCO_CALIBRATE (1 << 1) +#define AD9523_PLL2_FORCE_VCO_MIDSCALE (1 << 2) +#define AD9523_PLL2_FORCE_REFERENCE_VALID (1 << 3) +#define AD9523_PLL2_FORCE_RELEASE_SYNC (1 << 4) + +/* AD9523_PLL2_VCO_DIVIDER */ +#define AD9523_PLL2_VCO_DIV_M1(x) ((((x) - 3) & 0x3) << 0) +#define AD9523_PLL2_VCO_DIV_M2(x) ((((x) - 3) & 0x3) << 4) +#define AD9523_PLL2_VCO_DIV_M1_PWR_DOWN_EN (1 << 2) +#define AD9523_PLL2_VCO_DIV_M2_PWR_DOWN_EN (1 << 6) + +/* AD9523_PLL2_LOOP_FILTER_CTRL */ +#define AD9523_PLL2_LOOP_FILTER_CPOLE1(x) (((x) & 0x7) << 0) +#define AD9523_PLL2_LOOP_FILTER_RZERO(x) (((x) & 0x7) << 3) +#define AD9523_PLL2_LOOP_FILTER_RPOLE2(x) (((x) & 0x7) << 6) +#define AD9523_PLL2_LOOP_FILTER_RZERO_BYPASS_EN (1 << 8) + +/* AD9523_PLL2_R2_DIVIDER */ +#define AD9523_PLL2_R2_DIVIDER_VAL(x) (((x) & 0x1F) << 0) + +/* AD9523_CHANNEL_CLOCK_DIST */ +#define AD9523_CLK_DIST_DIV_PHASE(x) (((x) & 0x3F) << 18) +#define AD9523_CLK_DIST_DIV_PHASE_REV(x) ((ret >> 18) & 0x3F) +#define AD9523_CLK_DIST_DIV(x) ((((x) - 1) & 0x3FF) << 8) +#define AD9523_CLK_DIST_DIV_REV(x) (((ret >> 8) & 0x3FF) + 1) +#define AD9523_CLK_DIST_INV_DIV_OUTPUT_EN (1 << 7) +#define AD9523_CLK_DIST_IGNORE_SYNC_EN (1 << 6) +#define AD9523_CLK_DIST_PWR_DOWN_EN (1 << 5) +#define AD9523_CLK_DIST_LOW_PWR_MODE_EN (1 << 4) +#define AD9523_CLK_DIST_DRIVER_MODE(x) (((x) & 0xF) << 0) + +/* AD9523_PLL1_OUTPUT_CTRL */ +#define AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH6_M2 (1 << 7) +#define AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH5_M2 (1 << 6) +#define AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH4_M2 (1 << 5) +#define AD9523_PLL1_OUTP_CTRL_CMOS_DRV_WEAK (1 << 4) +#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_1 (0 << 0) +#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_2 (1 << 0) +#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_4 (2 << 0) +#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_8 (4 << 0) +#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_16 (8 << 0) + +/* AD9523_PLL1_OUTPUT_CHANNEL_CTRL */ +#define AD9523_PLL1_OUTP_CH_CTRL_OUTPUT_PWR_DOWN_EN (1 << 7) +#define AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH9_M2 (1 << 6) +#define AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH8_M2 (1 << 5) +#define AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH7_M2 (1 << 4) +#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH3 (1 << 3) +#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH2 (1 << 2) +#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH1 (1 << 1) +#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH0 (1 << 0) + +/* AD9523_READBACK_0 */ +#define AD9523_READBACK_0_STAT_PLL2_REF_CLK (1 << 7) +#define AD9523_READBACK_0_STAT_PLL2_FB_CLK (1 << 6) +#define AD9523_READBACK_0_STAT_VCXO (1 << 5) +#define AD9523_READBACK_0_STAT_REF_TEST (1 << 4) +#define AD9523_READBACK_0_STAT_REFB (1 << 3) +#define AD9523_READBACK_0_STAT_REFA (1 << 2) +#define AD9523_READBACK_0_STAT_PLL2_LD (1 << 1) +#define AD9523_READBACK_0_STAT_PLL1_LD (1 << 0) + +/* AD9523_READBACK_1 */ +#define AD9523_READBACK_1_HOLDOVER_ACTIVE (1 << 3) +#define AD9523_READBACK_1_AUTOMODE_SEL_REFB (1 << 2) +#define AD9523_READBACK_1_VCO_CALIB_IN_PROGRESS (1 << 0) + +/* AD9523_STATUS_SIGNALS */ +#define AD9523_STATUS_SIGNALS_SYNC_MAN_CTRL (1 << 16) +#define AD9523_STATUS_MONITOR_01_PLL12_LOCKED (0x302) +/* AD9523_POWER_DOWN_CTRL */ +#define AD9523_POWER_DOWN_CTRL_PLL1_PWR_DOWN (1 << 2) +#define AD9523_POWER_DOWN_CTRL_PLL2_PWR_DOWN (1 << 1) +#define AD9523_POWER_DOWN_CTRL_DIST_PWR_DOWN (1 << 0) + +/* AD9523_IO_UPDATE */ +#define AD9523_IO_UPDATE_EN (1 << 0) + +/* AD9523_EEPROM_DATA_XFER_STATUS */ +#define AD9523_EEPROM_DATA_XFER_IN_PROGRESS (1 << 0) + +/* AD9523_EEPROM_ERROR_READBACK */ +#define AD9523_EEPROM_ERROR_READBACK_FAIL (1 << 0) + +/* AD9523_EEPROM_CTRL1 */ +#define AD9523_EEPROM_CTRL1_SOFT_EEPROM (1 << 1) +#define AD9523_EEPROM_CTRL1_EEPROM_WRITE_PROT_DIS (1 << 0) + +/* AD9523_EEPROM_CTRL2 */ +#define AD9523_EEPROM_CTRL2_REG2EEPROM (1 << 0) + +#define AD9523_NUM_CHAN 14 +#define AD9523_NUM_CHAN_ALT_CLK_SRC 10 + +/* Helpers to avoid excess line breaks */ +#define AD_IFE(_pde, _a, _b) ((pdata->_pde) ? _a : _b) +#define AD_IF(_pde, _a) AD_IFE(_pde, _a, 0) + +enum { + AD9523_STAT_PLL1_LD, + AD9523_STAT_PLL2_LD, + AD9523_STAT_REFA, + AD9523_STAT_REFB, + AD9523_STAT_REF_TEST, + AD9523_STAT_VCXO, + AD9523_STAT_PLL2_FB_CLK, + AD9523_STAT_PLL2_REF_CLK, + AD9523_SYNC, + AD9523_EEPROM, +}; + +enum { + AD9523_VCO1, + AD9523_VCO2, + AD9523_VCXO, + AD9523_NUM_CLK_SRC, +}; + +struct ad9523_state { + struct spi_device *spi; + struct regulator *reg; + struct ad9523_platform_data *pdata; + struct iio_chan_spec ad9523_channels[AD9523_NUM_CHAN]; + + unsigned long vcxo_freq; + unsigned long vco_freq; + unsigned long vco_out_freq[AD9523_NUM_CLK_SRC]; + unsigned char vco_out_map[AD9523_NUM_CHAN_ALT_CLK_SRC]; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + union { + __be32 d32; + u8 d8[4]; + } data[2] ____cacheline_aligned; +}; + +static int ad9523_read(struct iio_dev *indio_dev, unsigned addr) +{ + struct ad9523_state *st = iio_priv(indio_dev); + struct spi_message m; + int ret; + + /* We encode the register size 1..3 bytes into the register address. + * On transfer we get the size from the register datum, and make sure + * the result is properly aligned. + */ + + struct spi_transfer t[] = { + { + .tx_buf = &st->data[0].d8[2], + .len = 2, + }, { + .rx_buf = &st->data[1].d8[4 - AD9523_TRANSF_LEN(addr)], + .len = AD9523_TRANSF_LEN(addr), + }, + }; + + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + st->data[0].d32 = cpu_to_be32(AD9523_READ | + AD9523_CNT(AD9523_TRANSF_LEN(addr)) | + AD9523_ADDR(addr)); + + ret = spi_sync(st->spi, &m); + if (ret < 0) + dev_err(&indio_dev->dev, "read failed (%d)", ret); + else + ret = be32_to_cpu(st->data[1].d32) & (0xFFFFFF >> + (8 * (3 - AD9523_TRANSF_LEN(addr)))); + + return ret; +}; + +static int ad9523_write(struct iio_dev *indio_dev, unsigned addr, unsigned val) +{ + struct ad9523_state *st = iio_priv(indio_dev); + struct spi_message m; + int ret; + struct spi_transfer t[] = { + { + .tx_buf = &st->data[0].d8[2], + .len = 2, + }, { + .tx_buf = &st->data[1].d8[4 - AD9523_TRANSF_LEN(addr)], + .len = AD9523_TRANSF_LEN(addr), + }, + }; + + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + st->data[0].d32 = cpu_to_be32(AD9523_WRITE | + AD9523_CNT(AD9523_TRANSF_LEN(addr)) | + AD9523_ADDR(addr)); + st->data[1].d32 = cpu_to_be32(val); + + ret = spi_sync(st->spi, &m); + + if (ret < 0) + dev_err(&indio_dev->dev, "write failed (%d)", ret); + + return ret; +} + +static int ad9523_io_update(struct iio_dev *indio_dev) +{ + return ad9523_write(indio_dev, AD9523_IO_UPDATE, AD9523_IO_UPDATE_EN); +} + +static int ad9523_vco_out_map(struct iio_dev *indio_dev, + unsigned ch, bool out) +{ + struct ad9523_state *st = iio_priv(indio_dev); + int ret; + unsigned mask; + + switch (ch) { + case 0 ... 3: + ret = ad9523_read(indio_dev, AD9523_PLL1_OUTPUT_CHANNEL_CTRL); + if (ret < 0) + break; + mask = AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH0 << ch; + if (out) { + ret |= mask; + out = 2; + } else { + ret &= ~mask; + } + ret = ad9523_write(indio_dev, + AD9523_PLL1_OUTPUT_CHANNEL_CTRL, ret); + break; + case 4 ... 6: + ret = ad9523_read(indio_dev, AD9523_PLL1_OUTPUT_CTRL); + if (ret < 0) + break; + mask = AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH4_M2 << (ch - 4); + if (out) + ret |= mask; + else + ret &= ~mask; + ret = ad9523_write(indio_dev, AD9523_PLL1_OUTPUT_CTRL, ret); + break; + case 7 ... 9: + ret = ad9523_read(indio_dev, AD9523_PLL1_OUTPUT_CHANNEL_CTRL); + if (ret < 0) + break; + mask = AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH7_M2 << (ch - 7); + if (out) + ret |= mask; + else + ret &= ~mask; + ret = ad9523_write(indio_dev, + AD9523_PLL1_OUTPUT_CHANNEL_CTRL, ret); + break; + default: + return 0; + } + + st->vco_out_map[ch] = out; + + return ret; +} + +static int ad9523_set_clock_provider(struct iio_dev *indio_dev, + unsigned ch, unsigned long freq) +{ + struct ad9523_state *st = iio_priv(indio_dev); + long tmp1, tmp2; + bool use_alt_clk_src; + + switch (ch) { + case 0 ... 3: + use_alt_clk_src = (freq == st->vco_out_freq[AD9523_VCXO]); + break; + case 4 ... 9: + tmp1 = st->vco_out_freq[AD9523_VCO1] / freq; + tmp2 = st->vco_out_freq[AD9523_VCO2] / freq; + tmp1 *= freq; + tmp2 *= freq; + use_alt_clk_src = (abs(tmp1 - freq) > abs(tmp2 - freq)); + break; + default: + /* Ch 10..14: No action required, return success */ + return 0; + } + + return ad9523_vco_out_map(indio_dev, ch, use_alt_clk_src); +} + +static int ad9523_store_eeprom(struct iio_dev *indio_dev) +{ + int ret, tmp; + + ret = ad9523_write(indio_dev, AD9523_EEPROM_CTRL1, + AD9523_EEPROM_CTRL1_EEPROM_WRITE_PROT_DIS); + if (ret < 0) + return ret; + ret = ad9523_write(indio_dev, AD9523_EEPROM_CTRL2, + AD9523_EEPROM_CTRL2_REG2EEPROM); + if (ret < 0) + return ret; + + tmp = 4; + do { + msleep(16); + ret = ad9523_read(indio_dev, + AD9523_EEPROM_DATA_XFER_STATUS); + if (ret < 0) + return ret; + } while ((ret & AD9523_EEPROM_DATA_XFER_IN_PROGRESS) && tmp--); + + ret = ad9523_write(indio_dev, AD9523_EEPROM_CTRL1, 0); + if (ret < 0) + return ret; + + ret = ad9523_read(indio_dev, AD9523_EEPROM_ERROR_READBACK); + if (ret < 0) + return ret; + + if (ret & AD9523_EEPROM_ERROR_READBACK_FAIL) { + dev_err(&indio_dev->dev, "Verify EEPROM failed"); + ret = -EIO; + } + + return ret; +} + +static int ad9523_sync(struct iio_dev *indio_dev) +{ + int ret, tmp; + + ret = ad9523_read(indio_dev, AD9523_STATUS_SIGNALS); + if (ret < 0) + return ret; + + tmp = ret; + tmp |= AD9523_STATUS_SIGNALS_SYNC_MAN_CTRL; + + ret = ad9523_write(indio_dev, AD9523_STATUS_SIGNALS, tmp); + if (ret < 0) + return ret; + + ad9523_io_update(indio_dev); + tmp &= ~AD9523_STATUS_SIGNALS_SYNC_MAN_CTRL; + + ret = ad9523_write(indio_dev, AD9523_STATUS_SIGNALS, tmp); + if (ret < 0) + return ret; + + return ad9523_io_update(indio_dev); +} + +static ssize_t ad9523_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + bool state; + int ret; + + ret = strtobool(buf, &state); + if (ret < 0) + return ret; + + if (!state) + return 0; + + mutex_lock(&indio_dev->mlock); + switch ((u32)this_attr->address) { + case AD9523_SYNC: + ret = ad9523_sync(indio_dev); + break; + case AD9523_EEPROM: + ret = ad9523_store_eeprom(indio_dev); + break; + default: + ret = -ENODEV; + } + mutex_unlock(&indio_dev->mlock); + + return ret ? ret : len; +} + +static ssize_t ad9523_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret; + + mutex_lock(&indio_dev->mlock); + ret = ad9523_read(indio_dev, AD9523_READBACK_0); + if (ret >= 0) { + ret = sprintf(buf, "%d\n", !!(ret & (1 << + (u32)this_attr->address))); + } + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static IIO_DEVICE_ATTR(pll1_locked, S_IRUGO, + ad9523_show, + NULL, + AD9523_STAT_PLL1_LD); + +static IIO_DEVICE_ATTR(pll2_locked, S_IRUGO, + ad9523_show, + NULL, + AD9523_STAT_PLL2_LD); + +static IIO_DEVICE_ATTR(pll1_reference_clk_a_present, S_IRUGO, + ad9523_show, + NULL, + AD9523_STAT_REFA); + +static IIO_DEVICE_ATTR(pll1_reference_clk_b_present, S_IRUGO, + ad9523_show, + NULL, + AD9523_STAT_REFB); + +static IIO_DEVICE_ATTR(pll1_reference_clk_test_present, S_IRUGO, + ad9523_show, + NULL, + AD9523_STAT_REF_TEST); + +static IIO_DEVICE_ATTR(vcxo_clk_present, S_IRUGO, + ad9523_show, + NULL, + AD9523_STAT_VCXO); + +static IIO_DEVICE_ATTR(pll2_feedback_clk_present, S_IRUGO, + ad9523_show, + NULL, + AD9523_STAT_PLL2_FB_CLK); + +static IIO_DEVICE_ATTR(pll2_reference_clk_present, S_IRUGO, + ad9523_show, + NULL, + AD9523_STAT_PLL2_REF_CLK); + +static IIO_DEVICE_ATTR(sync_dividers, S_IWUSR, + NULL, + ad9523_store, + AD9523_SYNC); + +static IIO_DEVICE_ATTR(store_eeprom, S_IWUSR, + NULL, + ad9523_store, + AD9523_EEPROM); + +static struct attribute *ad9523_attributes[] = { + &iio_dev_attr_sync_dividers.dev_attr.attr, + &iio_dev_attr_store_eeprom.dev_attr.attr, + &iio_dev_attr_pll2_feedback_clk_present.dev_attr.attr, + &iio_dev_attr_pll2_reference_clk_present.dev_attr.attr, + &iio_dev_attr_pll1_reference_clk_a_present.dev_attr.attr, + &iio_dev_attr_pll1_reference_clk_b_present.dev_attr.attr, + &iio_dev_attr_pll1_reference_clk_test_present.dev_attr.attr, + &iio_dev_attr_vcxo_clk_present.dev_attr.attr, + &iio_dev_attr_pll1_locked.dev_attr.attr, + &iio_dev_attr_pll2_locked.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ad9523_attribute_group = { + .attrs = ad9523_attributes, +}; + +static int ad9523_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad9523_state *st = iio_priv(indio_dev); + unsigned code; + int ret; + + mutex_lock(&indio_dev->mlock); + ret = ad9523_read(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel)); + mutex_unlock(&indio_dev->mlock); + + if (ret < 0) + return ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + *val = !(ret & AD9523_CLK_DIST_PWR_DOWN_EN); + return IIO_VAL_INT; + case IIO_CHAN_INFO_FREQUENCY: + *val = st->vco_out_freq[st->vco_out_map[chan->channel]] / + AD9523_CLK_DIST_DIV_REV(ret); + return IIO_VAL_INT; + case IIO_CHAN_INFO_PHASE: + code = (AD9523_CLK_DIST_DIV_PHASE_REV(ret) * 3141592) / + AD9523_CLK_DIST_DIV_REV(ret); + *val = code / 1000000; + *val2 = (code % 1000000) * 10; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +}; + +static int ad9523_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad9523_state *st = iio_priv(indio_dev); + unsigned reg; + int ret, tmp, code; + + mutex_lock(&indio_dev->mlock); + ret = ad9523_read(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel)); + if (ret < 0) + goto out; + + reg = ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val) + reg &= ~AD9523_CLK_DIST_PWR_DOWN_EN; + else + reg |= AD9523_CLK_DIST_PWR_DOWN_EN; + break; + case IIO_CHAN_INFO_FREQUENCY: + if (val <= 0) { + ret = -EINVAL; + goto out; + } + ret = ad9523_set_clock_provider(indio_dev, chan->channel, val); + if (ret < 0) + goto out; + tmp = st->vco_out_freq[st->vco_out_map[chan->channel]] / val; + tmp = clamp(tmp, 1, 1024); + reg &= ~(0x3FF << 8); + reg |= AD9523_CLK_DIST_DIV(tmp); + break; + case IIO_CHAN_INFO_PHASE: + code = val * 1000000 + val2 % 1000000; + tmp = (code * AD9523_CLK_DIST_DIV_REV(ret)) / 3141592; + tmp = clamp(tmp, 0, 63); + reg &= ~AD9523_CLK_DIST_DIV_PHASE(~0); + reg |= AD9523_CLK_DIST_DIV_PHASE(tmp); + break; + default: + ret = -EINVAL; + goto out; + } + + ret = ad9523_write(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel), + reg); + if (ret < 0) + goto out; + + ad9523_io_update(indio_dev); +out: + mutex_unlock(&indio_dev->mlock); + return ret; +} + +static int ad9523_reg_access(struct iio_dev *indio_dev, + unsigned reg, unsigned writeval, + unsigned *readval) +{ + int ret; + + mutex_lock(&indio_dev->mlock); + if (readval == NULL) { + ret = ad9523_write(indio_dev, reg | AD9523_R1B, writeval); + ad9523_io_update(indio_dev); + } else { + ret = ad9523_read(indio_dev, reg | AD9523_R1B); + if (ret < 0) + return ret; + *readval = ret; + ret = 0; + } + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static const struct iio_info ad9523_info = { + .read_raw = &ad9523_read_raw, + .write_raw = &ad9523_write_raw, + .debugfs_reg_access = &ad9523_reg_access, + .attrs = &ad9523_attribute_group, + .driver_module = THIS_MODULE, +}; + +static int ad9523_setup(struct iio_dev *indio_dev) +{ + struct ad9523_state *st = iio_priv(indio_dev); + struct ad9523_platform_data *pdata = st->pdata; + struct ad9523_channel_spec *chan; + unsigned long active_mask = 0; + int ret, i; + + ret = ad9523_write(indio_dev, AD9523_SERIAL_PORT_CONFIG, + AD9523_SER_CONF_SOFT_RESET | + (st->spi->mode & SPI_3WIRE ? 0 : + AD9523_SER_CONF_SDO_ACTIVE)); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_READBACK_CTRL, + AD9523_READBACK_CTRL_READ_BUFFERED); + if (ret < 0) + return ret; + + ret = ad9523_io_update(indio_dev); + if (ret < 0) + return ret; + + /* + * PLL1 Setup + */ + ret = ad9523_write(indio_dev, AD9523_PLL1_REF_A_DIVIDER, + pdata->refa_r_div); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_PLL1_REF_B_DIVIDER, + pdata->refb_r_div); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_PLL1_FEEDBACK_DIVIDER, + pdata->pll1_feedback_div); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_PLL1_CHARGE_PUMP_CTRL, + AD9523_PLL1_CHARGE_PUMP_CURRENT_nA(pdata-> + pll1_charge_pump_current_nA) | + AD9523_PLL1_CHARGE_PUMP_MODE_NORMAL | + AD9523_PLL1_BACKLASH_PW_MIN); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_PLL1_INPUT_RECEIVERS_CTRL, + AD_IF(refa_diff_rcv_en, AD9523_PLL1_REFA_RCV_EN) | + AD_IF(refb_diff_rcv_en, AD9523_PLL1_REFB_RCV_EN) | + AD_IF(osc_in_diff_en, AD9523_PLL1_OSC_IN_DIFF_EN) | + AD_IF(osc_in_cmos_neg_inp_en, + AD9523_PLL1_OSC_IN_CMOS_NEG_INP_EN) | + AD_IF(refa_diff_rcv_en, AD9523_PLL1_REFA_DIFF_RCV_EN) | + AD_IF(refb_diff_rcv_en, AD9523_PLL1_REFB_DIFF_RCV_EN)); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_PLL1_REF_CTRL, + AD_IF(zd_in_diff_en, AD9523_PLL1_ZD_IN_DIFF_EN) | + AD_IF(zd_in_cmos_neg_inp_en, + AD9523_PLL1_ZD_IN_CMOS_NEG_INP_EN) | + AD_IF(zero_delay_mode_internal_en, + AD9523_PLL1_ZERO_DELAY_MODE_INT) | + AD_IF(osc_in_feedback_en, AD9523_PLL1_OSC_IN_PLL_FEEDBACK_EN) | + AD_IF(refa_cmos_neg_inp_en, AD9523_PLL1_REFA_CMOS_NEG_INP_EN) | + AD_IF(refb_cmos_neg_inp_en, AD9523_PLL1_REFB_CMOS_NEG_INP_EN)); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_PLL1_MISC_CTRL, + AD9523_PLL1_REFB_INDEP_DIV_CTRL_EN | + AD9523_PLL1_REF_MODE(pdata->ref_mode)); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_PLL1_LOOP_FILTER_CTRL, + AD9523_PLL1_LOOP_FILTER_RZERO(pdata->pll1_loop_filter_rzero)); + if (ret < 0) + return ret; + /* + * PLL2 Setup + */ + + ret = ad9523_write(indio_dev, AD9523_PLL2_CHARGE_PUMP, + AD9523_PLL2_CHARGE_PUMP_CURRENT_nA(pdata-> + pll2_charge_pump_current_nA)); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_PLL2_FEEDBACK_DIVIDER_AB, + AD9523_PLL2_FB_NDIV_A_CNT(pdata->pll2_ndiv_a_cnt) | + AD9523_PLL2_FB_NDIV_B_CNT(pdata->pll2_ndiv_b_cnt)); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_PLL2_CTRL, + AD9523_PLL2_CHARGE_PUMP_MODE_NORMAL | + AD9523_PLL2_BACKLASH_CTRL_EN | + AD_IF(pll2_freq_doubler_en, AD9523_PLL2_FREQ_DOUBLER_EN)); + if (ret < 0) + return ret; + + st->vco_freq = (pdata->vcxo_freq * (pdata->pll2_freq_doubler_en ? 2 : 1) + / pdata->pll2_r2_div) * AD9523_PLL2_FB_NDIV(pdata-> + pll2_ndiv_a_cnt, pdata->pll2_ndiv_b_cnt); + + ret = ad9523_write(indio_dev, AD9523_PLL2_VCO_CTRL, + AD9523_PLL2_VCO_CALIBRATE); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_PLL2_VCO_DIVIDER, + AD9523_PLL2_VCO_DIV_M1(pdata->pll2_vco_diff_m1) | + AD9523_PLL2_VCO_DIV_M2(pdata->pll2_vco_diff_m2) | + AD_IFE(pll2_vco_diff_m1, 0, + AD9523_PLL2_VCO_DIV_M1_PWR_DOWN_EN) | + AD_IFE(pll2_vco_diff_m2, 0, + AD9523_PLL2_VCO_DIV_M2_PWR_DOWN_EN)); + if (ret < 0) + return ret; + + if (pdata->pll2_vco_diff_m1) + st->vco_out_freq[AD9523_VCO1] = + st->vco_freq / pdata->pll2_vco_diff_m1; + + if (pdata->pll2_vco_diff_m2) + st->vco_out_freq[AD9523_VCO2] = + st->vco_freq / pdata->pll2_vco_diff_m2; + + st->vco_out_freq[AD9523_VCXO] = pdata->vcxo_freq; + + ret = ad9523_write(indio_dev, AD9523_PLL2_R2_DIVIDER, + AD9523_PLL2_R2_DIVIDER_VAL(pdata->pll2_r2_div)); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_PLL2_LOOP_FILTER_CTRL, + AD9523_PLL2_LOOP_FILTER_CPOLE1(pdata->cpole1) | + AD9523_PLL2_LOOP_FILTER_RZERO(pdata->rzero) | + AD9523_PLL2_LOOP_FILTER_RPOLE2(pdata->rpole2) | + AD_IF(rzero_bypass_en, + AD9523_PLL2_LOOP_FILTER_RZERO_BYPASS_EN)); + if (ret < 0) + return ret; + + for (i = 0; i < pdata->num_channels; i++) { + chan = &pdata->channels[i]; + if (chan->channel_num < AD9523_NUM_CHAN) { + __set_bit(chan->channel_num, &active_mask); + ret = ad9523_write(indio_dev, + AD9523_CHANNEL_CLOCK_DIST(chan->channel_num), + AD9523_CLK_DIST_DRIVER_MODE(chan->driver_mode) | + AD9523_CLK_DIST_DIV(chan->channel_divider) | + AD9523_CLK_DIST_DIV_PHASE(chan->divider_phase) | + (chan->sync_ignore_en ? + AD9523_CLK_DIST_IGNORE_SYNC_EN : 0) | + (chan->divider_output_invert_en ? + AD9523_CLK_DIST_INV_DIV_OUTPUT_EN : 0) | + (chan->low_power_mode_en ? + AD9523_CLK_DIST_LOW_PWR_MODE_EN : 0) | + (chan->output_dis ? + AD9523_CLK_DIST_PWR_DOWN_EN : 0)); + if (ret < 0) + return ret; + + ret = ad9523_vco_out_map(indio_dev, chan->channel_num, + chan->use_alt_clock_src); + if (ret < 0) + return ret; + + st->ad9523_channels[i].type = IIO_ALTVOLTAGE; + st->ad9523_channels[i].output = 1; + st->ad9523_channels[i].indexed = 1; + st->ad9523_channels[i].channel = chan->channel_num; + st->ad9523_channels[i].extend_name = + chan->extended_name; + st->ad9523_channels[i].info_mask = + IIO_CHAN_INFO_RAW_SEPARATE_BIT | + IIO_CHAN_INFO_PHASE_SEPARATE_BIT | + IIO_CHAN_INFO_FREQUENCY_SEPARATE_BIT; + } + } + + for_each_clear_bit(i, &active_mask, AD9523_NUM_CHAN) + ad9523_write(indio_dev, + AD9523_CHANNEL_CLOCK_DIST(i), + AD9523_CLK_DIST_DRIVER_MODE(TRISTATE) | + AD9523_CLK_DIST_PWR_DOWN_EN); + + ret = ad9523_write(indio_dev, AD9523_POWER_DOWN_CTRL, 0); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_STATUS_SIGNALS, + AD9523_STATUS_MONITOR_01_PLL12_LOCKED); + if (ret < 0) + return ret; + + ret = ad9523_io_update(indio_dev); + if (ret < 0) + return ret; + + return 0; +} + +static int __devinit ad9523_probe(struct spi_device *spi) +{ + struct ad9523_platform_data *pdata = spi->dev.platform_data; + struct iio_dev *indio_dev; + struct ad9523_state *st; + int ret; + + if (!pdata) { + dev_err(&spi->dev, "no platform data?\n"); + return -EINVAL; + } + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->reg = regulator_get(&spi->dev, "vcc"); + if (!IS_ERR(st->reg)) { + ret = regulator_enable(st->reg); + if (ret) + goto error_put_reg; + } + + spi_set_drvdata(spi, indio_dev); + st->spi = spi; + st->pdata = pdata; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = (pdata->name[0] != 0) ? pdata->name : + spi_get_device_id(spi)->name; + indio_dev->info = &ad9523_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->ad9523_channels; + indio_dev->num_channels = pdata->num_channels; + + ret = ad9523_setup(indio_dev); + if (ret < 0) + goto error_disable_reg; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_disable_reg; + + dev_info(&spi->dev, "probed %s\n", indio_dev->name); + + return 0; + +error_disable_reg: + if (!IS_ERR(st->reg)) + regulator_disable(st->reg); +error_put_reg: + if (!IS_ERR(st->reg)) + regulator_put(st->reg); + + iio_device_free(indio_dev); + + return ret; +} + +static int __devexit ad9523_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad9523_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + if (!IS_ERR(st->reg)) { + regulator_disable(st->reg); + regulator_put(st->reg); + } + + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad9523_id[] = { + {"ad9523-1", 9523}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad9523_id); + +static struct spi_driver ad9523_driver = { + .driver = { + .name = "ad9523", + .owner = THIS_MODULE, + }, + .probe = ad9523_probe, + .remove = __devexit_p(ad9523_remove), + .id_table = ad9523_id, +}; +module_spi_driver(ad9523_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD9523 CLOCKDIST/PLL"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/iio/frequency/ad9523.h b/include/linux/iio/frequency/ad9523.h new file mode 100644 index 000000000000..12ce3ee427fd --- /dev/null +++ b/include/linux/iio/frequency/ad9523.h @@ -0,0 +1,195 @@ +/* + * AD9523 SPI Low Jitter Clock Generator + * + * Copyright 2012 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#ifndef IIO_FREQUENCY_AD9523_H_ +#define IIO_FREQUENCY_AD9523_H_ + +enum outp_drv_mode { + TRISTATE, + LVPECL_8mA, + LVDS_4mA, + LVDS_7mA, + HSTL0_16mA, + HSTL1_8mA, + CMOS_CONF1, + CMOS_CONF2, + CMOS_CONF3, + CMOS_CONF4, + CMOS_CONF5, + CMOS_CONF6, + CMOS_CONF7, + CMOS_CONF8, + CMOS_CONF9 +}; + +enum ref_sel_mode { + NONEREVERTIVE_STAY_ON_REFB, + REVERT_TO_REFA, + SELECT_REFA, + SELECT_REFB, + EXT_REF_SEL +}; + +/** + * struct ad9523_channel_spec - Output channel configuration + * + * @channel_num: Output channel number. + * @divider_output_invert_en: Invert the polarity of the output clock. + * @sync_ignore_en: Ignore chip-level SYNC signal. + * @low_power_mode_en: Reduce power used in the differential output modes. + * @use_alt_clock_src: Channel divider uses alternative clk source. + * @output_dis: Disables, powers down the entire channel. + * @driver_mode: Output driver mode (logic level family). + * @divider_phase: Divider initial phase after a SYNC. Range 0..63 + LSB = 1/2 of a period of the divider input clock. + * @channel_divider: 10-bit channel divider. + * @extended_name: Optional descriptive channel name. + */ + +struct ad9523_channel_spec { + unsigned channel_num; + bool divider_output_invert_en; + bool sync_ignore_en; + bool low_power_mode_en; + /* CH0..CH3 VCXO, CH4..CH9 VCO2 */ + bool use_alt_clock_src; + bool output_dis; + enum outp_drv_mode driver_mode; + unsigned char divider_phase; + unsigned short channel_divider; + char extended_name[16]; +}; + +enum pll1_rzero_resistor { + RZERO_883_OHM, + RZERO_677_OHM, + RZERO_341_OHM, + RZERO_135_OHM, + RZERO_10_OHM, + RZERO_USE_EXT_RES = 8, +}; + +enum rpole2_resistor { + RPOLE2_900_OHM, + RPOLE2_450_OHM, + RPOLE2_300_OHM, + RPOLE2_225_OHM, +}; + +enum rzero_resistor { + RZERO_3250_OHM, + RZERO_2750_OHM, + RZERO_2250_OHM, + RZERO_2100_OHM, + RZERO_3000_OHM, + RZERO_2500_OHM, + RZERO_2000_OHM, + RZERO_1850_OHM, +}; + +enum cpole1_capacitor { + CPOLE1_0_PF, + CPOLE1_8_PF, + CPOLE1_16_PF, + CPOLE1_24_PF, + _CPOLE1_24_PF, /* place holder */ + CPOLE1_32_PF, + CPOLE1_40_PF, + CPOLE1_48_PF, +}; + +/** + * struct ad9523_platform_data - platform specific information + * + * @vcxo_freq: External VCXO frequency in Hz + * @refa_diff_rcv_en: REFA differential/single-ended input selection. + * @refb_diff_rcv_en: REFB differential/single-ended input selection. + * @zd_in_diff_en: Zero Delay differential/single-ended input selection. + * @osc_in_diff_en: OSC differential/ single-ended input selection. + * @refa_cmos_neg_inp_en: REFA single-ended neg./pos. input enable. + * @refb_cmos_neg_inp_en: REFB single-ended neg./pos. input enable. + * @zd_in_cmos_neg_inp_en: Zero Delay single-ended neg./pos. input enable. + * @osc_in_cmos_neg_inp_en: OSC single-ended neg./pos. input enable. + * @refa_r_div: PLL1 10-bit REFA R divider. + * @refb_r_div: PLL1 10-bit REFB R divider. + * @pll1_feedback_div: PLL1 10-bit Feedback N divider. + * @pll1_charge_pump_current_nA: Magnitude of PLL1 charge pump current (nA). + * @zero_delay_mode_internal_en: Internal, external Zero Delay mode selection. + * @osc_in_feedback_en: PLL1 feedback path, local feedback from + * the OSC_IN receiver or zero delay mode + * @pll1_loop_filter_rzero: PLL1 Loop Filter Zero Resistor selection. + * @ref_mode: Reference selection mode. + * @pll2_charge_pump_current_nA: Magnitude of PLL2 charge pump current (nA). + * @pll2_ndiv_a_cnt: PLL2 Feedback N-divider, A Counter, range 0..4. + * @pll2_ndiv_b_cnt: PLL2 Feedback N-divider, B Counter, range 0..63. + * @pll2_freq_doubler_en: PLL2 frequency doubler enable. + * @pll2_r2_div: PLL2 R2 divider, range 0..31. + * @pll2_vco_diff_m1: VCO1 divider, range 3..5. + * @pll2_vco_diff_m2: VCO2 divider, range 3..5. + * @rpole2: PLL2 loop filter Rpole resistor value. + * @rzero: PLL2 loop filter Rzero resistor value. + * @cpole1: PLL2 loop filter Cpole capacitor value. + * @rzero_bypass_en: PLL2 loop filter Rzero bypass enable. + * @num_channels: Array size of struct ad9523_channel_spec. + * @channels: Pointer to channel array. + * @name: Optional alternative iio device name. + */ + +struct ad9523_platform_data { + unsigned long vcxo_freq; + + /* Differential/ Single-Ended Input Configuration */ + bool refa_diff_rcv_en; + bool refb_diff_rcv_en; + bool zd_in_diff_en; + bool osc_in_diff_en; + + /* + * Valid if differential input disabled + * if false defaults to pos input + */ + bool refa_cmos_neg_inp_en; + bool refb_cmos_neg_inp_en; + bool zd_in_cmos_neg_inp_en; + bool osc_in_cmos_neg_inp_en; + + /* PLL1 Setting */ + unsigned short refa_r_div; + unsigned short refb_r_div; + unsigned short pll1_feedback_div; + unsigned short pll1_charge_pump_current_nA; + bool zero_delay_mode_internal_en; + bool osc_in_feedback_en; + enum pll1_rzero_resistor pll1_loop_filter_rzero; + + /* Reference */ + enum ref_sel_mode ref_mode; + + /* PLL2 Setting */ + unsigned int pll2_charge_pump_current_nA; + unsigned char pll2_ndiv_a_cnt; + unsigned char pll2_ndiv_b_cnt; + bool pll2_freq_doubler_en; + unsigned char pll2_r2_div; + unsigned char pll2_vco_diff_m1; /* 3..5 */ + unsigned char pll2_vco_diff_m2; /* 3..5 */ + + /* Loop Filter PLL2 */ + enum rpole2_resistor rpole2; + enum rzero_resistor rzero; + enum cpole1_capacitor cpole1; + bool rzero_bypass_en; + + /* Output Channel Configuration */ + int num_channels; + struct ad9523_channel_spec *channels; + + char name[SPI_NAME_SIZE]; +}; + +#endif /* IIO_FREQUENCY_AD9523_H_ */ -- cgit v1.2.3 From e31166f0fd48478866ee9661c36789126435ebe8 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Tue, 29 May 2012 12:41:20 +0200 Subject: iio: frequency: New driver for Analog Devices ADF4350/ADF4351 Wideband Synthesizers Changes since V1: Apply Jonathan's review feedback: Introduce and use IIO_ALTVOLTAGE. Fix up comments and documentation. Remove dead code. Reorder some code fragments. Add missing iio_device_free. Convert to new API. Fix-up out of staging includes. Removed pll_locked attribute. Changes since V2: Use module_spi_driver. adf4350_remove: move gpio_free after regulator. target patch to drivers/iio Signed-off-by: Michael Hennerich Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- .../ABI/testing/sysfs-bus-iio-frequency-adf4350 | 21 + drivers/iio/frequency/Kconfig | 18 + drivers/iio/frequency/Makefile | 1 + drivers/iio/frequency/adf4350.c | 478 +++++++++++++++++++++ include/linux/iio/frequency/adf4350.h | 126 ++++++ 5 files changed, 644 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 create mode 100644 drivers/iio/frequency/adf4350.c create mode 100644 include/linux/iio/frequency/adf4350.h (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 b/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 new file mode 100644 index 000000000000..d89aded01c5a --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 @@ -0,0 +1,21 @@ +What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_frequency_resolution +KernelVersion: 3.4.0 +Contact: linux-iio@vger.kernel.org +Description: + Stores channel Y frequency resolution/channel spacing in Hz. + The value given directly influences the MODULUS used by + the fractional-N PLL. It is assumed that the algorithm + that is used to compute the various dividers, is able to + generate proper values for multiples of channel spacing. + +What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_refin_frequency +KernelVersion: 3.4.0 +Contact: linux-iio@vger.kernel.org +Description: + Sets channel Y REFin frequency in Hz. In some clock chained + applications, the reference frequency used by the PLL may + change during runtime. This attribute allows the user to + adjust the reference frequency accordingly. + The value written has no effect until out_altvoltageY_frequency + is updated. Consider to use out_altvoltageY_powerdown to power + down the PLL and it's RFOut buffers during REFin changes. diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig index 0458c92464a3..6aaa33ef4544 100644 --- a/drivers/iio/frequency/Kconfig +++ b/drivers/iio/frequency/Kconfig @@ -19,5 +19,23 @@ config AD9523 To compile this driver as a module, choose M here: the module will be called ad9523. +endmenu + +# +# Phase-Locked Loop (PLL) frequency synthesizers +# + +menu "Phase-Locked Loop (PLL) frequency synthesizers" + +config ADF4350 + tristate "Analog Devices ADF4350/ADF4351 Wideband Synthesizers" + depends on SPI + help + Say yes here to build support for Analog Devices ADF4350/ADF4351 + Wideband Synthesizers. The driver provides direct access via sysfs. + + To compile this driver as a module, choose M here: the + module will be called adf4350. + endmenu endmenu diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile index 1b5b22417da1..00d26e5d1dc2 100644 --- a/drivers/iio/frequency/Makefile +++ b/drivers/iio/frequency/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_AD9523) += ad9523.o +obj-$(CONFIG_ADF4350) += adf4350.o diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c new file mode 100644 index 000000000000..fd4c8501aba9 --- /dev/null +++ b/drivers/iio/frequency/adf4350.c @@ -0,0 +1,478 @@ +/* + * ADF4350/ADF4351 SPI Wideband Synthesizer driver + * + * Copyright 2012 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +enum { + ADF4350_FREQ, + ADF4350_FREQ_REFIN, + ADF4350_FREQ_RESOLUTION, + ADF4350_PWRDOWN, +}; + +struct adf4350_state { + struct spi_device *spi; + struct regulator *reg; + struct adf4350_platform_data *pdata; + unsigned long clkin; + unsigned long chspc; /* Channel Spacing */ + unsigned long fpfd; /* Phase Frequency Detector */ + unsigned long min_out_freq; + unsigned r0_fract; + unsigned r0_int; + unsigned r1_mod; + unsigned r4_rf_div_sel; + unsigned long regs[6]; + unsigned long regs_hw[6]; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + __be32 val ____cacheline_aligned; +}; + +static struct adf4350_platform_data default_pdata = { + .clkin = 122880000, + .channel_spacing = 10000, + .r2_user_settings = ADF4350_REG2_PD_POLARITY_POS, + ADF4350_REG2_CHARGE_PUMP_CURR_uA(2500), + .r3_user_settings = ADF4350_REG3_12BIT_CLKDIV_MODE(0), + .r4_user_settings = ADF4350_REG4_OUTPUT_PWR(3) | + ADF4350_REG4_MUTE_TILL_LOCK_EN, + .gpio_lock_detect = -1, +}; + +static int adf4350_sync_config(struct adf4350_state *st) +{ + int ret, i, doublebuf = 0; + + for (i = ADF4350_REG5; i >= ADF4350_REG0; i--) { + if ((st->regs_hw[i] != st->regs[i]) || + ((i == ADF4350_REG0) && doublebuf)) { + + switch (i) { + case ADF4350_REG1: + case ADF4350_REG4: + doublebuf = 1; + break; + } + + st->val = cpu_to_be32(st->regs[i] | i); + ret = spi_write(st->spi, &st->val, 4); + if (ret < 0) + return ret; + st->regs_hw[i] = st->regs[i]; + dev_dbg(&st->spi->dev, "[%d] 0x%X\n", + i, (u32)st->regs[i] | i); + } + } + return 0; +} + +static int adf4350_reg_access(struct iio_dev *indio_dev, + unsigned reg, unsigned writeval, + unsigned *readval) +{ + struct adf4350_state *st = iio_priv(indio_dev); + int ret; + + if (reg > ADF4350_REG5) + return -EINVAL; + + mutex_lock(&indio_dev->mlock); + if (readval == NULL) { + st->regs[reg] = writeval & ~(BIT(0) | BIT(1) | BIT(2)); + ret = adf4350_sync_config(st); + } else { + *readval = st->regs_hw[reg]; + ret = 0; + } + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int adf4350_tune_r_cnt(struct adf4350_state *st, unsigned short r_cnt) +{ + struct adf4350_platform_data *pdata = st->pdata; + + do { + r_cnt++; + st->fpfd = (st->clkin * (pdata->ref_doubler_en ? 2 : 1)) / + (r_cnt * (pdata->ref_div2_en ? 2 : 1)); + } while (st->fpfd > ADF4350_MAX_FREQ_PFD); + + return r_cnt; +} + +static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq) +{ + struct adf4350_platform_data *pdata = st->pdata; + u64 tmp; + u32 div_gcd, prescaler; + u16 mdiv, r_cnt = 0; + u8 band_sel_div; + + if (freq > ADF4350_MAX_OUT_FREQ || freq < st->min_out_freq) + return -EINVAL; + + if (freq > ADF4350_MAX_FREQ_45_PRESC) { + prescaler = ADF4350_REG1_PRESCALER; + mdiv = 75; + } else { + prescaler = 0; + mdiv = 23; + } + + st->r4_rf_div_sel = 0; + + while (freq < ADF4350_MIN_VCO_FREQ) { + freq <<= 1; + st->r4_rf_div_sel++; + } + + /* + * Allow a predefined reference division factor + * if not set, compute our own + */ + if (pdata->ref_div_factor) + r_cnt = pdata->ref_div_factor - 1; + + do { + r_cnt = adf4350_tune_r_cnt(st, r_cnt); + + st->r1_mod = st->fpfd / st->chspc; + while (st->r1_mod > ADF4350_MAX_MODULUS) { + r_cnt = adf4350_tune_r_cnt(st, r_cnt); + st->r1_mod = st->fpfd / st->chspc; + } + + tmp = freq * (u64)st->r1_mod + (st->fpfd > 1); + do_div(tmp, st->fpfd); /* Div round closest (n + d/2)/d */ + st->r0_fract = do_div(tmp, st->r1_mod); + st->r0_int = tmp; + } while (mdiv > st->r0_int); + + band_sel_div = DIV_ROUND_UP(st->fpfd, ADF4350_MAX_BANDSEL_CLK); + + if (st->r0_fract && st->r1_mod) { + div_gcd = gcd(st->r1_mod, st->r0_fract); + st->r1_mod /= div_gcd; + st->r0_fract /= div_gcd; + } else { + st->r0_fract = 0; + st->r1_mod = 1; + } + + dev_dbg(&st->spi->dev, "VCO: %llu Hz, PFD %lu Hz\n" + "REF_DIV %d, R0_INT %d, R0_FRACT %d\n" + "R1_MOD %d, RF_DIV %d\nPRESCALER %s, BAND_SEL_DIV %d\n", + freq, st->fpfd, r_cnt, st->r0_int, st->r0_fract, st->r1_mod, + 1 << st->r4_rf_div_sel, prescaler ? "8/9" : "4/5", + band_sel_div); + + st->regs[ADF4350_REG0] = ADF4350_REG0_INT(st->r0_int) | + ADF4350_REG0_FRACT(st->r0_fract); + + st->regs[ADF4350_REG1] = ADF4350_REG1_PHASE(0) | + ADF4350_REG1_MOD(st->r1_mod) | + prescaler; + + st->regs[ADF4350_REG2] = + ADF4350_REG2_10BIT_R_CNT(r_cnt) | + ADF4350_REG2_DOUBLE_BUFF_EN | + (pdata->ref_doubler_en ? ADF4350_REG2_RMULT2_EN : 0) | + (pdata->ref_div2_en ? ADF4350_REG2_RDIV2_EN : 0) | + (pdata->r2_user_settings & (ADF4350_REG2_PD_POLARITY_POS | + ADF4350_REG2_LDP_6ns | ADF4350_REG2_LDF_INT_N | + ADF4350_REG2_CHARGE_PUMP_CURR_uA(5000) | + ADF4350_REG2_MUXOUT(0x7) | ADF4350_REG2_NOISE_MODE(0x9))); + + st->regs[ADF4350_REG3] = pdata->r3_user_settings & + (ADF4350_REG3_12BIT_CLKDIV(0xFFF) | + ADF4350_REG3_12BIT_CLKDIV_MODE(0x3) | + ADF4350_REG3_12BIT_CSR_EN | + ADF4351_REG3_CHARGE_CANCELLATION_EN | + ADF4351_REG3_ANTI_BACKLASH_3ns_EN | + ADF4351_REG3_BAND_SEL_CLOCK_MODE_HIGH); + + st->regs[ADF4350_REG4] = + ADF4350_REG4_FEEDBACK_FUND | + ADF4350_REG4_RF_DIV_SEL(st->r4_rf_div_sel) | + ADF4350_REG4_8BIT_BAND_SEL_CLKDIV(band_sel_div) | + ADF4350_REG4_RF_OUT_EN | + (pdata->r4_user_settings & + (ADF4350_REG4_OUTPUT_PWR(0x3) | + ADF4350_REG4_AUX_OUTPUT_PWR(0x3) | + ADF4350_REG4_AUX_OUTPUT_EN | + ADF4350_REG4_AUX_OUTPUT_FUND | + ADF4350_REG4_MUTE_TILL_LOCK_EN)); + + st->regs[ADF4350_REG5] = ADF4350_REG5_LD_PIN_MODE_DIGITAL; + + return adf4350_sync_config(st); +} + +static ssize_t adf4350_write(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct adf4350_state *st = iio_priv(indio_dev); + unsigned long long readin; + int ret; + + ret = kstrtoull(buf, 10, &readin); + if (ret) + return ret; + + mutex_lock(&indio_dev->mlock); + switch ((u32)private) { + case ADF4350_FREQ: + ret = adf4350_set_freq(st, readin); + break; + case ADF4350_FREQ_REFIN: + if (readin > ADF4350_MAX_FREQ_REFIN) + ret = -EINVAL; + else + st->clkin = readin; + break; + case ADF4350_FREQ_RESOLUTION: + if (readin == 0) + ret = -EINVAL; + else + st->chspc = readin; + break; + case ADF4350_PWRDOWN: + if (readin) + st->regs[ADF4350_REG2] |= ADF4350_REG2_POWER_DOWN_EN; + else + st->regs[ADF4350_REG2] &= ~ADF4350_REG2_POWER_DOWN_EN; + + adf4350_sync_config(st); + break; + default: + ret = -ENODEV; + } + mutex_unlock(&indio_dev->mlock); + + return ret ? ret : len; +} + +static ssize_t adf4350_read(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct adf4350_state *st = iio_priv(indio_dev); + unsigned long long val; + int ret = 0; + + mutex_lock(&indio_dev->mlock); + switch ((u32)private) { + case ADF4350_FREQ: + val = (u64)((st->r0_int * st->r1_mod) + st->r0_fract) * + (u64)st->fpfd; + do_div(val, st->r1_mod * (1 << st->r4_rf_div_sel)); + /* PLL unlocked? return error */ + if (gpio_is_valid(st->pdata->gpio_lock_detect)) + if (!gpio_get_value(st->pdata->gpio_lock_detect)) { + dev_dbg(&st->spi->dev, "PLL un-locked\n"); + ret = -EBUSY; + } + break; + case ADF4350_FREQ_REFIN: + val = st->clkin; + break; + case ADF4350_FREQ_RESOLUTION: + val = st->chspc; + break; + case ADF4350_PWRDOWN: + val = !!(st->regs[ADF4350_REG2] & ADF4350_REG2_POWER_DOWN_EN); + break; + } + mutex_unlock(&indio_dev->mlock); + + return ret < 0 ? ret : sprintf(buf, "%llu\n", val); +} + +#define _ADF4350_EXT_INFO(_name, _ident) { \ + .name = _name, \ + .read = adf4350_read, \ + .write = adf4350_write, \ + .private = _ident, \ +} + +static const struct iio_chan_spec_ext_info adf4350_ext_info[] = { + /* Ideally we use IIO_CHAN_INFO_FREQUENCY, but there are + * values > 2^32 in order to support the entire frequency range + * in Hz. Using scale is a bit ugly. + */ + _ADF4350_EXT_INFO("frequency", ADF4350_FREQ), + _ADF4350_EXT_INFO("frequency_resolution", ADF4350_FREQ_RESOLUTION), + _ADF4350_EXT_INFO("refin_frequency", ADF4350_FREQ_REFIN), + _ADF4350_EXT_INFO("powerdown", ADF4350_PWRDOWN), + { }, +}; + +static const struct iio_chan_spec adf4350_chan = { + .type = IIO_ALTVOLTAGE, + .indexed = 1, + .output = 1, + .ext_info = adf4350_ext_info, +}; + +static const struct iio_info adf4350_info = { + .debugfs_reg_access = &adf4350_reg_access, + .driver_module = THIS_MODULE, +}; + +static int __devinit adf4350_probe(struct spi_device *spi) +{ + struct adf4350_platform_data *pdata = spi->dev.platform_data; + struct iio_dev *indio_dev; + struct adf4350_state *st; + int ret; + + if (!pdata) { + dev_warn(&spi->dev, "no platform data? using default\n"); + + pdata = &default_pdata; + } + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->reg = regulator_get(&spi->dev, "vcc"); + if (!IS_ERR(st->reg)) { + ret = regulator_enable(st->reg); + if (ret) + goto error_put_reg; + } + + spi_set_drvdata(spi, indio_dev); + st->spi = spi; + st->pdata = pdata; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = (pdata->name[0] != 0) ? pdata->name : + spi_get_device_id(spi)->name; + + indio_dev->info = &adf4350_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = &adf4350_chan; + indio_dev->num_channels = 1; + + st->chspc = pdata->channel_spacing; + st->clkin = pdata->clkin; + + st->min_out_freq = spi_get_device_id(spi)->driver_data == 4351 ? + ADF4351_MIN_OUT_FREQ : ADF4350_MIN_OUT_FREQ; + + memset(st->regs_hw, 0xFF, sizeof(st->regs_hw)); + + if (gpio_is_valid(pdata->gpio_lock_detect)) { + ret = gpio_request(pdata->gpio_lock_detect, indio_dev->name); + if (ret) { + dev_err(&spi->dev, "fail to request lock detect GPIO-%d", + pdata->gpio_lock_detect); + goto error_disable_reg; + } + gpio_direction_input(pdata->gpio_lock_detect); + } + + if (pdata->power_up_frequency) { + ret = adf4350_set_freq(st, pdata->power_up_frequency); + if (ret) + goto error_free_gpio; + } + + ret = iio_device_register(indio_dev); + if (ret) + goto error_free_gpio; + + return 0; + +error_free_gpio: + if (gpio_is_valid(pdata->gpio_lock_detect)) + gpio_free(pdata->gpio_lock_detect); + +error_disable_reg: + if (!IS_ERR(st->reg)) + regulator_disable(st->reg); +error_put_reg: + if (!IS_ERR(st->reg)) + regulator_put(st->reg); + + iio_device_free(indio_dev); + + return ret; +} + +static int __devexit adf4350_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct adf4350_state *st = iio_priv(indio_dev); + struct regulator *reg = st->reg; + + st->regs[ADF4350_REG2] |= ADF4350_REG2_POWER_DOWN_EN; + adf4350_sync_config(st); + + iio_device_unregister(indio_dev); + + if (!IS_ERR(reg)) { + regulator_disable(reg); + regulator_put(reg); + } + + if (gpio_is_valid(st->pdata->gpio_lock_detect)) + gpio_free(st->pdata->gpio_lock_detect); + + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id adf4350_id[] = { + {"adf4350", 4350}, + {"adf4351", 4351}, + {} +}; + +static struct spi_driver adf4350_driver = { + .driver = { + .name = "adf4350", + .owner = THIS_MODULE, + }, + .probe = adf4350_probe, + .remove = __devexit_p(adf4350_remove), + .id_table = adf4350_id, +}; +module_spi_driver(adf4350_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices ADF4350/ADF4351 PLL"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/iio/frequency/adf4350.h b/include/linux/iio/frequency/adf4350.h new file mode 100644 index 000000000000..b76b4a87065e --- /dev/null +++ b/include/linux/iio/frequency/adf4350.h @@ -0,0 +1,126 @@ +/* + * ADF4350/ADF4351 SPI PLL driver + * + * Copyright 2012 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#ifndef IIO_PLL_ADF4350_H_ +#define IIO_PLL_ADF4350_H_ + +/* Registers */ +#define ADF4350_REG0 0 +#define ADF4350_REG1 1 +#define ADF4350_REG2 2 +#define ADF4350_REG3 3 +#define ADF4350_REG4 4 +#define ADF4350_REG5 5 + +/* REG0 Bit Definitions */ +#define ADF4350_REG0_FRACT(x) (((x) & 0xFFF) << 3) +#define ADF4350_REG0_INT(x) (((x) & 0xFFFF) << 15) + +/* REG1 Bit Definitions */ +#define ADF4350_REG1_MOD(x) (((x) & 0xFFF) << 3) +#define ADF4350_REG1_PHASE(x) (((x) & 0xFFF) << 15) +#define ADF4350_REG1_PRESCALER (1 << 27) + +/* REG2 Bit Definitions */ +#define ADF4350_REG2_COUNTER_RESET_EN (1 << 3) +#define ADF4350_REG2_CP_THREESTATE_EN (1 << 4) +#define ADF4350_REG2_POWER_DOWN_EN (1 << 5) +#define ADF4350_REG2_PD_POLARITY_POS (1 << 6) +#define ADF4350_REG2_LDP_6ns (1 << 7) +#define ADF4350_REG2_LDP_10ns (0 << 7) +#define ADF4350_REG2_LDF_FRACT_N (0 << 8) +#define ADF4350_REG2_LDF_INT_N (1 << 8) +#define ADF4350_REG2_CHARGE_PUMP_CURR_uA(x) (((((x)-312) / 312) & 0xF) << 9) +#define ADF4350_REG2_DOUBLE_BUFF_EN (1 << 13) +#define ADF4350_REG2_10BIT_R_CNT(x) ((x) << 14) +#define ADF4350_REG2_RDIV2_EN (1 << 24) +#define ADF4350_REG2_RMULT2_EN (1 << 25) +#define ADF4350_REG2_MUXOUT(x) ((x) << 26) +#define ADF4350_REG2_NOISE_MODE(x) ((x) << 29) +#define ADF4350_MUXOUT_THREESTATE 0 +#define ADF4350_MUXOUT_DVDD 1 +#define ADF4350_MUXOUT_GND 2 +#define ADF4350_MUXOUT_R_DIV_OUT 3 +#define ADF4350_MUXOUT_N_DIV_OUT 4 +#define ADF4350_MUXOUT_ANALOG_LOCK_DETECT 5 +#define ADF4350_MUXOUT_DIGITAL_LOCK_DETECT 6 + +/* REG3 Bit Definitions */ +#define ADF4350_REG3_12BIT_CLKDIV(x) ((x) << 3) +#define ADF4350_REG3_12BIT_CLKDIV_MODE(x) ((x) << 16) +#define ADF4350_REG3_12BIT_CSR_EN (1 << 18) +#define ADF4351_REG3_CHARGE_CANCELLATION_EN (1 << 21) +#define ADF4351_REG3_ANTI_BACKLASH_3ns_EN (1 << 22) +#define ADF4351_REG3_BAND_SEL_CLOCK_MODE_HIGH (1 << 23) + +/* REG4 Bit Definitions */ +#define ADF4350_REG4_OUTPUT_PWR(x) ((x) << 3) +#define ADF4350_REG4_RF_OUT_EN (1 << 5) +#define ADF4350_REG4_AUX_OUTPUT_PWR(x) ((x) << 6) +#define ADF4350_REG4_AUX_OUTPUT_EN (1 << 8) +#define ADF4350_REG4_AUX_OUTPUT_FUND (1 << 9) +#define ADF4350_REG4_AUX_OUTPUT_DIV (0 << 9) +#define ADF4350_REG4_MUTE_TILL_LOCK_EN (1 << 10) +#define ADF4350_REG4_VCO_PWRDOWN_EN (1 << 11) +#define ADF4350_REG4_8BIT_BAND_SEL_CLKDIV(x) ((x) << 12) +#define ADF4350_REG4_RF_DIV_SEL(x) ((x) << 20) +#define ADF4350_REG4_FEEDBACK_DIVIDED (0 << 23) +#define ADF4350_REG4_FEEDBACK_FUND (1 << 23) + +/* REG5 Bit Definitions */ +#define ADF4350_REG5_LD_PIN_MODE_LOW (0 << 22) +#define ADF4350_REG5_LD_PIN_MODE_DIGITAL (1 << 22) +#define ADF4350_REG5_LD_PIN_MODE_HIGH (3 << 22) + +/* Specifications */ +#define ADF4350_MAX_OUT_FREQ 4400000000ULL /* Hz */ +#define ADF4350_MIN_OUT_FREQ 137500000 /* Hz */ +#define ADF4351_MIN_OUT_FREQ 34375000 /* Hz */ +#define ADF4350_MIN_VCO_FREQ 2200000000ULL /* Hz */ +#define ADF4350_MAX_FREQ_45_PRESC 3000000000ULL /* Hz */ +#define ADF4350_MAX_FREQ_PFD 32000000 /* Hz */ +#define ADF4350_MAX_BANDSEL_CLK 125000 /* Hz */ +#define ADF4350_MAX_FREQ_REFIN 250000000 /* Hz */ +#define ADF4350_MAX_MODULUS 4095 + +/** + * struct adf4350_platform_data - platform specific information + * @name: Optional device name. + * @clkin: REFin frequency in Hz. + * @channel_spacing: Channel spacing in Hz (influences MODULUS). + * @power_up_frequency: Optional, If set in Hz the PLL tunes to the desired + * frequency on probe. + * @ref_div_factor: Optional, if set the driver skips dynamic calculation + * and uses this default value instead. + * @ref_doubler_en: Enables reference doubler. + * @ref_div2_en: Enables reference divider. + * @r2_user_settings: User defined settings for ADF4350/1 REGISTER_2. + * @r3_user_settings: User defined settings for ADF4350/1 REGISTER_3. + * @r4_user_settings: User defined settings for ADF4350/1 REGISTER_4. + * @gpio_lock_detect: Optional, if set with a valid GPIO number, + * pll lock state is tested upon read. + * If not used - set to -1. + */ + +struct adf4350_platform_data { + char name[32]; + unsigned long clkin; + unsigned long channel_spacing; + unsigned long long power_up_frequency; + + unsigned short ref_div_factor; /* 10-bit R counter */ + bool ref_doubler_en; + bool ref_div2_en; + + unsigned r2_user_settings; + unsigned r3_user_settings; + unsigned r4_user_settings; + int gpio_lock_detect; +}; + +#endif /* IIO_PLL_ADF4350_H_ */ -- cgit v1.2.3 From e4e8b7765867e8f4705bcc18b8930edbe0e4ef3c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 4 Jun 2012 10:50:02 +0200 Subject: iio: Add iio_device_get() This patch add the iio_device_get() function, which increases the reference count of a iio device. The matching function to decrease the reference count - iio_device_put() - already exists. Signed-off-by: Lars-Peter Clausen Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- include/linux/iio/iio.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include') diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 3a4f6a3ab80d..3238fa3374f7 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -438,6 +438,17 @@ static inline struct iio_dev *dev_to_iio_dev(struct device *dev) return container_of(dev, struct iio_dev, dev); } +/** + * iio_device_get() - increment reference count for the device + * @indio_dev: IIO device structure + * + * Returns: The passed IIO device + **/ +static inline struct iio_dev *iio_device_get(struct iio_dev *indio_dev) +{ + return indio_dev ? dev_to_iio_dev(get_device(&indio_dev->dev)) : NULL; +} + /* Can we make this smaller? */ #define IIO_ALIGN L1_CACHE_BYTES /** -- cgit v1.2.3 From 5212cc8a9d833791a7aec566db136e78951f203d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 4 Jun 2012 11:36:11 +0200 Subject: iio: Add helper functions for enum style channel attributes We often have the case were we do have a enum style channel attribute. These attributes have in common that they are a list of string values which usually map in a 1-to-1 fashion to integer values. This patch implements some common helper code for implementing enum style channel attributes using extended channel attributes. The helper functions take care of converting between the string and integer values, as well providing a function for "_available" attributes which list all available enum items. Signed-off-by: Lars-Peter Clausen Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/industrialio-core.c | 63 ++++++++++++++++++++++++++++++++++++++++ include/linux/iio/iio.h | 64 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) (limited to 'include') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 1ddd8861c71b..56a3c0bc996c 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -289,6 +289,69 @@ static ssize_t iio_write_channel_ext_info(struct device *dev, this_attr->c, buf, len); } +ssize_t iio_enum_available_read(struct iio_dev *indio_dev, + uintptr_t priv, const struct iio_chan_spec *chan, char *buf) +{ + const struct iio_enum *e = (const struct iio_enum *)priv; + unsigned int i; + size_t len = 0; + + if (!e->num_items) + return 0; + + for (i = 0; i < e->num_items; ++i) + len += snprintf(buf + len, PAGE_SIZE - len, "%s ", e->items[i]); + + /* replace last space with a newline */ + buf[len - 1] = '\n'; + + return len; +} +EXPORT_SYMBOL_GPL(iio_enum_available_read); + +ssize_t iio_enum_read(struct iio_dev *indio_dev, + uintptr_t priv, const struct iio_chan_spec *chan, char *buf) +{ + const struct iio_enum *e = (const struct iio_enum *)priv; + int i; + + if (!e->get) + return -EINVAL; + + i = e->get(indio_dev, chan); + if (i < 0) + return i; + else if (i >= e->num_items) + return -EINVAL; + + return sprintf(buf, "%s\n", e->items[i]); +} +EXPORT_SYMBOL_GPL(iio_enum_read); + +ssize_t iio_enum_write(struct iio_dev *indio_dev, + uintptr_t priv, const struct iio_chan_spec *chan, const char *buf, + size_t len) +{ + const struct iio_enum *e = (const struct iio_enum *)priv; + unsigned int i; + int ret; + + if (!e->set) + return -EINVAL; + + for (i = 0; i < e->num_items; i++) { + if (sysfs_streq(buf, e->items[i])) + break; + } + + if (i == e->num_items) + return -EINVAL; + + ret = e->set(indio_dev, chan, i); + return ret ? ret : len; +} +EXPORT_SYMBOL_GPL(iio_enum_write); + static ssize_t iio_read_channel_info(struct device *dev, struct device_attribute *attr, char *buf) diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 3238fa3374f7..944c63511731 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -129,6 +129,70 @@ struct iio_chan_spec_ext_info { uintptr_t private; }; +/** + * struct iio_enum - Enum channel info attribute + * items: A array of strings. + * num_items: Length of the item array. + * set: Set callback function, may be NULL. + * get: Get callback function, may be NULL. + * + * The iio_enum struct can be used to implement enum style channel attributes. + * Enum style attributes are those which have a set of strings which map to + * unsigned integer values. The IIO enum helper code takes care of mapping + * between value and string as well as generating a "_available" file which + * contains a list of all available items. The set callback will be called when + * the attribute is updated. The last parameter is the index to the newly + * activated item. The get callback will be used to query the currently active + * item and is supposed to return the index for it. + */ +struct iio_enum { + const char * const *items; + unsigned int num_items; + int (*set)(struct iio_dev *, const struct iio_chan_spec *, unsigned int); + int (*get)(struct iio_dev *, const struct iio_chan_spec *); +}; + +ssize_t iio_enum_available_read(struct iio_dev *indio_dev, + uintptr_t priv, const struct iio_chan_spec *chan, char *buf); +ssize_t iio_enum_read(struct iio_dev *indio_dev, + uintptr_t priv, const struct iio_chan_spec *chan, char *buf); +ssize_t iio_enum_write(struct iio_dev *indio_dev, + uintptr_t priv, const struct iio_chan_spec *chan, const char *buf, + size_t len); + +/** + * IIO_ENUM() - Initialize enum extended channel attribute + * @_name: Attribute name + * @_shared: Whether the attribute is shared between all channels + * @_e: Pointer to a iio_enum struct + * + * This should usually be used together with IIO_ENUM_AVAILABLE() + */ +#define IIO_ENUM(_name, _shared, _e) \ +{ \ + .name = (_name), \ + .shared = (_shared), \ + .read = iio_enum_read, \ + .write = iio_enum_write, \ + .private = (uintptr_t)(_e), \ +} + +/** + * IIO_ENUM_AVAILABLE() - Initialize enum available extended channel attribute + * @_name: Attribute name ("_available" will be appended to the name) + * @_e: Pointer to a iio_enum struct + * + * Creates a read only attribute which list all the available enum items in a + * space separated list. This should usually be used together with IIO_ENUM() + */ +#define IIO_ENUM_AVAILABLE(_name, _e) \ +{ \ + .name = (_name "_available"), \ + .shared = true, \ + .read = iio_enum_available_read, \ + .private = (uintptr_t)(_e), \ +} + /** * struct iio_chan_spec - specification of a single channel * @type: What type of measurement is the channel making. -- cgit v1.2.3 From dbdc025bb239ce62c9b4d28c459a98f22ce9ec0a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 4 Jun 2012 11:36:28 +0200 Subject: staging:iio: Move DAC drivers out of staging The IIO DAC drivers are in a reasonably good shape. They all make use of channel spec and non of them provides non-documented sysfs attributes. Code style should be OK as well, both checkpatch and coccicheck only report trivial issues. So lets move the whole folder out of staging. Signed-off-by: Lars-Peter Clausen Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/Kconfig | 1 + drivers/iio/Makefile | 1 + drivers/iio/dac/Kconfig | 121 +++++++ drivers/iio/dac/Makefile | 15 + drivers/iio/dac/ad5064.c | 538 ++++++++++++++++++++++++++++ drivers/iio/dac/ad5360.c | 570 +++++++++++++++++++++++++++++ drivers/iio/dac/ad5380.c | 657 ++++++++++++++++++++++++++++++++++ drivers/iio/dac/ad5421.c | 544 ++++++++++++++++++++++++++++ drivers/iio/dac/ad5446.c | 381 ++++++++++++++++++++ drivers/iio/dac/ad5446.h | 89 +++++ drivers/iio/dac/ad5504.c | 393 ++++++++++++++++++++ drivers/iio/dac/ad5624r.h | 79 ++++ drivers/iio/dac/ad5624r_spi.c | 324 +++++++++++++++++ drivers/iio/dac/ad5686.c | 418 +++++++++++++++++++++ drivers/iio/dac/ad5764.c | 382 ++++++++++++++++++++ drivers/iio/dac/ad5791.c | 485 +++++++++++++++++++++++++ drivers/iio/dac/max517.c | 243 +++++++++++++ drivers/staging/iio/Kconfig | 1 - drivers/staging/iio/Makefile | 1 - drivers/staging/iio/dac/Kconfig | 121 ------- drivers/staging/iio/dac/Makefile | 15 - drivers/staging/iio/dac/ad5064.c | 538 ---------------------------- drivers/staging/iio/dac/ad5360.c | 570 ----------------------------- drivers/staging/iio/dac/ad5380.c | 657 ---------------------------------- drivers/staging/iio/dac/ad5421.c | 544 ---------------------------- drivers/staging/iio/dac/ad5421.h | 32 -- drivers/staging/iio/dac/ad5446.c | 381 -------------------- drivers/staging/iio/dac/ad5446.h | 89 ----- drivers/staging/iio/dac/ad5504.c | 394 -------------------- drivers/staging/iio/dac/ad5504.h | 20 -- drivers/staging/iio/dac/ad5624r.h | 79 ---- drivers/staging/iio/dac/ad5624r_spi.c | 324 ----------------- drivers/staging/iio/dac/ad5686.c | 418 --------------------- drivers/staging/iio/dac/ad5764.c | 382 -------------------- drivers/staging/iio/dac/ad5791.c | 486 ------------------------- drivers/staging/iio/dac/ad5791.h | 29 -- drivers/staging/iio/dac/max517.c | 244 ------------- drivers/staging/iio/dac/max517.h | 19 - include/linux/iio/dac/ad5421.h | 28 ++ include/linux/iio/dac/ad5504.h | 16 + include/linux/iio/dac/ad5791.h | 25 ++ include/linux/iio/dac/max517.h | 15 + 42 files changed, 5325 insertions(+), 5344 deletions(-) create mode 100644 drivers/iio/dac/Kconfig create mode 100644 drivers/iio/dac/Makefile create mode 100644 drivers/iio/dac/ad5064.c create mode 100644 drivers/iio/dac/ad5360.c create mode 100644 drivers/iio/dac/ad5380.c create mode 100644 drivers/iio/dac/ad5421.c create mode 100644 drivers/iio/dac/ad5446.c create mode 100644 drivers/iio/dac/ad5446.h create mode 100644 drivers/iio/dac/ad5504.c create mode 100644 drivers/iio/dac/ad5624r.h create mode 100644 drivers/iio/dac/ad5624r_spi.c create mode 100644 drivers/iio/dac/ad5686.c create mode 100644 drivers/iio/dac/ad5764.c create mode 100644 drivers/iio/dac/ad5791.c create mode 100644 drivers/iio/dac/max517.c delete mode 100644 drivers/staging/iio/dac/Kconfig delete mode 100644 drivers/staging/iio/dac/Makefile delete mode 100644 drivers/staging/iio/dac/ad5064.c delete mode 100644 drivers/staging/iio/dac/ad5360.c delete mode 100644 drivers/staging/iio/dac/ad5380.c delete mode 100644 drivers/staging/iio/dac/ad5421.c delete mode 100644 drivers/staging/iio/dac/ad5421.h delete mode 100644 drivers/staging/iio/dac/ad5446.c delete mode 100644 drivers/staging/iio/dac/ad5446.h delete mode 100644 drivers/staging/iio/dac/ad5504.c delete mode 100644 drivers/staging/iio/dac/ad5504.h delete mode 100644 drivers/staging/iio/dac/ad5624r.h delete mode 100644 drivers/staging/iio/dac/ad5624r_spi.c delete mode 100644 drivers/staging/iio/dac/ad5686.c delete mode 100644 drivers/staging/iio/dac/ad5764.c delete mode 100644 drivers/staging/iio/dac/ad5791.c delete mode 100644 drivers/staging/iio/dac/ad5791.h delete mode 100644 drivers/staging/iio/dac/max517.c delete mode 100644 drivers/staging/iio/dac/max517.h create mode 100644 include/linux/iio/dac/ad5421.h create mode 100644 include/linux/iio/dac/ad5504.h create mode 100644 include/linux/iio/dac/ad5791.h create mode 100644 include/linux/iio/dac/max517.h (limited to 'include') diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 64c88e5cda4d..103349f2b3b5 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -52,5 +52,6 @@ source "drivers/iio/adc/Kconfig" source "drivers/iio/amplifiers/Kconfig" source "drivers/iio/light/Kconfig" source "drivers/iio/frequency/Kconfig" +source "drivers/iio/dac/Kconfig" endif # IIO diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index bd801c0bbc2f..c38fa2a40af2 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -13,3 +13,4 @@ obj-y += adc/ obj-y += amplifiers/ obj-y += light/ obj-y += frequency/ +obj-y += dac/ diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig new file mode 100644 index 000000000000..a626f03871ec --- /dev/null +++ b/drivers/iio/dac/Kconfig @@ -0,0 +1,121 @@ +# +# DAC drivers +# +menu "Digital to analog converters" + +config AD5064 + tristate "Analog Devices AD5064/64-1/65/44/45/24/25, AD5628/48/66/68 DAC driver" + depends on SPI + help + Say yes here to build support for Analog Devices AD5024, AD5025, AD5044, + AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5648, AD5666, AD5668 Digital + to Analog Converter. + + To compile this driver as a module, choose M here: the + module will be called ad5064. + +config AD5360 + tristate "Analog Devices Analog Devices AD5360/61/62/63/70/71/73 DAC driver" + depends on SPI + help + Say yes here to build support for Analog Devices AD5360, AD5361, + AD5362, AD5363, AD5370, AD5371, AD5373 multi-channel + Digital to Analog Converters (DAC). + + To compile this driver as module choose M here: the module will be called + ad5360. + +config AD5380 + tristate "Analog Devices AD5380/81/82/83/84/90/91/92 DAC driver" + depends on (SPI_MASTER || I2C) + select REGMAP_I2C if I2C + select REGMAP_SPI if SPI_MASTER + help + Say yes here to build support for Analog Devices AD5380, AD5381, + AD5382, AD5383, AD5384, AD5390, AD5391, AD5392 multi-channel + Digital to Analog Converters (DAC). + + To compile this driver as module choose M here: the module will be called + ad5380. + +config AD5421 + tristate "Analog Devices AD5421 DAC driver" + depends on SPI + help + Say yes here to build support for Analog Devices AD5421 loop-powered + digital-to-analog convertors (DAC). + + To compile this driver as module choose M here: the module will be called + ad5421. + +config AD5624R_SPI + tristate "Analog Devices AD5624/44/64R DAC spi driver" + depends on SPI + help + Say yes here to build support for Analog Devices AD5624R, AD5644R and + AD5664R converters (DAC). This driver uses the common SPI interface. + +config AD5446 + tristate "Analog Devices AD5446 and similar single channel DACs driver" + depends on SPI + help + Say yes here to build support for Analog Devices AD5444, AD5446, + AD5512A, AD5541A, AD5542A, AD5543, AD5553, AD5601, AD5611, AD5620, + AD5621, AD5640, AD5660, AD5662 DACs. + + To compile this driver as a module, choose M here: the + module will be called ad5446. + +config AD5504 + tristate "Analog Devices AD5504/AD5501 DAC SPI driver" + depends on SPI + help + Say yes here to build support for Analog Devices AD5504, AD5501, + High Voltage Digital to Analog Converter. + + To compile this driver as a module, choose M here: the + module will be called ad5504. + +config AD5764 + tristate "Analog Devices AD5764/64R/44/44R DAC driver" + depends on SPI_MASTER + help + Say yes here to build support for Analog Devices AD5764, AD5764R, AD5744, + AD5744R Digital to Analog Converter. + + To compile this driver as a module, choose M here: the + module will be called ad5764. + +config AD5791 + tristate "Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC SPI driver" + depends on SPI + help + Say yes here to build support for Analog Devices AD5760, AD5780, + AD5781, AD5790, AD5791 High Resolution Voltage Output Digital to + Analog Converter. + + To compile this driver as a module, choose M here: the + module will be called ad5791. + +config AD5686 + tristate "Analog Devices AD5686R/AD5685R/AD5684R DAC SPI driver" + depends on SPI + help + Say yes here to build support for Analog Devices AD5686R, AD5685R, + AD5684R, AD5791 Voltage Output Digital to + Analog Converter. + + To compile this driver as a module, choose M here: the + module will be called ad5686. + +config MAX517 + tristate "Maxim MAX517/518/519 DAC driver" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for the Maxim chips MAX517, + MAX518 and MAX519 (I2C 8-Bit DACs with rail-to-rail outputs). + + This driver can also be built as a module. If so, the module + will be called max517. + +endmenu diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile new file mode 100644 index 000000000000..8ab1d264aab7 --- /dev/null +++ b/drivers/iio/dac/Makefile @@ -0,0 +1,15 @@ +# +# Makefile for industrial I/O DAC drivers +# + +obj-$(CONFIG_AD5360) += ad5360.o +obj-$(CONFIG_AD5380) += ad5380.o +obj-$(CONFIG_AD5421) += ad5421.o +obj-$(CONFIG_AD5624R_SPI) += ad5624r_spi.o +obj-$(CONFIG_AD5064) += ad5064.o +obj-$(CONFIG_AD5504) += ad5504.o +obj-$(CONFIG_AD5446) += ad5446.o +obj-$(CONFIG_AD5764) += ad5764.o +obj-$(CONFIG_AD5791) += ad5791.o +obj-$(CONFIG_AD5686) += ad5686.o +obj-$(CONFIG_MAX517) += max517.o diff --git a/drivers/iio/dac/ad5064.c b/drivers/iio/dac/ad5064.c new file mode 100644 index 000000000000..276af02520af --- /dev/null +++ b/drivers/iio/dac/ad5064.c @@ -0,0 +1,538 @@ +/* + * AD5024, AD5025, AD5044, AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5648, + * AD5666, AD5668 Digital to analog converters driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define AD5064_MAX_DAC_CHANNELS 8 +#define AD5064_MAX_VREFS 4 + +#define AD5064_ADDR(x) ((x) << 20) +#define AD5064_CMD(x) ((x) << 24) + +#define AD5064_ADDR_DAC(chan) (chan) +#define AD5064_ADDR_ALL_DAC 0xF + +#define AD5064_CMD_WRITE_INPUT_N 0x0 +#define AD5064_CMD_UPDATE_DAC_N 0x1 +#define AD5064_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2 +#define AD5064_CMD_WRITE_INPUT_N_UPDATE_N 0x3 +#define AD5064_CMD_POWERDOWN_DAC 0x4 +#define AD5064_CMD_CLEAR 0x5 +#define AD5064_CMD_LDAC_MASK 0x6 +#define AD5064_CMD_RESET 0x7 +#define AD5064_CMD_CONFIG 0x8 + +#define AD5064_CONFIG_DAISY_CHAIN_ENABLE BIT(1) +#define AD5064_CONFIG_INT_VREF_ENABLE BIT(0) + +#define AD5064_LDAC_PWRDN_NONE 0x0 +#define AD5064_LDAC_PWRDN_1K 0x1 +#define AD5064_LDAC_PWRDN_100K 0x2 +#define AD5064_LDAC_PWRDN_3STATE 0x3 + +/** + * struct ad5064_chip_info - chip specific information + * @shared_vref: whether the vref supply is shared between channels + * @internal_vref: internal reference voltage. 0 if the chip has no internal + * vref. + * @channel: channel specification + * @num_channels: number of channels + */ + +struct ad5064_chip_info { + bool shared_vref; + unsigned long internal_vref; + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +/** + * struct ad5064_state - driver instance specific data + * @spi: spi_device + * @chip_info: chip model specific constants, available modes etc + * @vref_reg: vref supply regulators + * @pwr_down: whether channel is powered down + * @pwr_down_mode: channel's current power down mode + * @dac_cache: current DAC raw value (chip does not support readback) + * @use_internal_vref: set to true if the internal reference voltage should be + * used. + * @data: spi transfer buffers + */ + +struct ad5064_state { + struct spi_device *spi; + const struct ad5064_chip_info *chip_info; + struct regulator_bulk_data vref_reg[AD5064_MAX_VREFS]; + bool pwr_down[AD5064_MAX_DAC_CHANNELS]; + u8 pwr_down_mode[AD5064_MAX_DAC_CHANNELS]; + unsigned int dac_cache[AD5064_MAX_DAC_CHANNELS]; + bool use_internal_vref; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + __be32 data ____cacheline_aligned; +}; + +enum ad5064_type { + ID_AD5024, + ID_AD5025, + ID_AD5044, + ID_AD5045, + ID_AD5064, + ID_AD5064_1, + ID_AD5065, + ID_AD5628_1, + ID_AD5628_2, + ID_AD5648_1, + ID_AD5648_2, + ID_AD5666_1, + ID_AD5666_2, + ID_AD5668_1, + ID_AD5668_2, +}; + +static int ad5064_spi_write(struct ad5064_state *st, unsigned int cmd, + unsigned int addr, unsigned int val, unsigned int shift) +{ + val <<= shift; + + st->data = cpu_to_be32(AD5064_CMD(cmd) | AD5064_ADDR(addr) | val); + + return spi_write(st->spi, &st->data, sizeof(st->data)); +} + +static int ad5064_sync_powerdown_mode(struct ad5064_state *st, + unsigned int channel) +{ + unsigned int val; + int ret; + + val = (0x1 << channel); + + if (st->pwr_down[channel]) + val |= st->pwr_down_mode[channel] << 8; + + ret = ad5064_spi_write(st, AD5064_CMD_POWERDOWN_DAC, 0, val, 0); + + return ret; +} + +static const char * const ad5064_powerdown_modes[] = { + "1kohm_to_gnd", + "100kohm_to_gnd", + "three_state", +}; + +static int ad5064_get_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5064_state *st = iio_priv(indio_dev); + + return st->pwr_down_mode[chan->channel] - 1; +} + +static int ad5064_set_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int mode) +{ + struct ad5064_state *st = iio_priv(indio_dev); + int ret; + + mutex_lock(&indio_dev->mlock); + st->pwr_down_mode[chan->channel] = mode + 1; + + ret = ad5064_sync_powerdown_mode(st, chan->channel); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static const struct iio_enum ad5064_powerdown_mode_enum = { + .items = ad5064_powerdown_modes, + .num_items = ARRAY_SIZE(ad5064_powerdown_modes), + .get = ad5064_get_powerdown_mode, + .set = ad5064_set_powerdown_mode, +}; + +static ssize_t ad5064_read_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, char *buf) +{ + struct ad5064_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", st->pwr_down[chan->channel]); +} + +static ssize_t ad5064_write_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, const char *buf, + size_t len) +{ + struct ad5064_state *st = iio_priv(indio_dev); + bool pwr_down; + int ret; + + ret = strtobool(buf, &pwr_down); + if (ret) + return ret; + + mutex_lock(&indio_dev->mlock); + st->pwr_down[chan->channel] = pwr_down; + + ret = ad5064_sync_powerdown_mode(st, chan->channel); + mutex_unlock(&indio_dev->mlock); + return ret ? ret : len; +} + +static int ad5064_get_vref(struct ad5064_state *st, + struct iio_chan_spec const *chan) +{ + unsigned int i; + + if (st->use_internal_vref) + return st->chip_info->internal_vref; + + i = st->chip_info->shared_vref ? 0 : chan->channel; + return regulator_get_voltage(st->vref_reg[i].consumer); +} + +static int ad5064_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad5064_state *st = iio_priv(indio_dev); + int scale_uv; + + switch (m) { + case IIO_CHAN_INFO_RAW: + *val = st->dac_cache[chan->channel]; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + scale_uv = ad5064_get_vref(st, chan); + if (scale_uv < 0) + return scale_uv; + + scale_uv = (scale_uv * 100) >> chan->scan_type.realbits; + *val = scale_uv / 100000; + *val2 = (scale_uv % 100000) * 10; + return IIO_VAL_INT_PLUS_MICRO; + default: + break; + } + return -EINVAL; +} + +static int ad5064_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long mask) +{ + struct ad5064_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val > (1 << chan->scan_type.realbits) || val < 0) + return -EINVAL; + + mutex_lock(&indio_dev->mlock); + ret = ad5064_spi_write(st, AD5064_CMD_WRITE_INPUT_N_UPDATE_N, + chan->address, val, chan->scan_type.shift); + if (ret == 0) + st->dac_cache[chan->channel] = val; + mutex_unlock(&indio_dev->mlock); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct iio_info ad5064_info = { + .read_raw = ad5064_read_raw, + .write_raw = ad5064_write_raw, + .driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec_ext_info ad5064_ext_info[] = { + { + .name = "powerdown", + .read = ad5064_read_dac_powerdown, + .write = ad5064_write_dac_powerdown, + }, + IIO_ENUM("powerdown_mode", false, &ad5064_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", &ad5064_powerdown_mode_enum), + { }, +}; + +#define AD5064_CHANNEL(chan, bits) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = (chan), \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \ + .address = AD5064_ADDR_DAC(chan), \ + .scan_type = IIO_ST('u', (bits), 16, 20 - (bits)), \ + .ext_info = ad5064_ext_info, \ +} + +#define DECLARE_AD5064_CHANNELS(name, bits) \ +const struct iio_chan_spec name[] = { \ + AD5064_CHANNEL(0, bits), \ + AD5064_CHANNEL(1, bits), \ + AD5064_CHANNEL(2, bits), \ + AD5064_CHANNEL(3, bits), \ + AD5064_CHANNEL(4, bits), \ + AD5064_CHANNEL(5, bits), \ + AD5064_CHANNEL(6, bits), \ + AD5064_CHANNEL(7, bits), \ +} + +static DECLARE_AD5064_CHANNELS(ad5024_channels, 12); +static DECLARE_AD5064_CHANNELS(ad5044_channels, 14); +static DECLARE_AD5064_CHANNELS(ad5064_channels, 16); + +static const struct ad5064_chip_info ad5064_chip_info_tbl[] = { + [ID_AD5024] = { + .shared_vref = false, + .channels = ad5024_channels, + .num_channels = 4, + }, + [ID_AD5025] = { + .shared_vref = false, + .channels = ad5024_channels, + .num_channels = 2, + }, + [ID_AD5044] = { + .shared_vref = false, + .channels = ad5044_channels, + .num_channels = 4, + }, + [ID_AD5045] = { + .shared_vref = false, + .channels = ad5044_channels, + .num_channels = 2, + }, + [ID_AD5064] = { + .shared_vref = false, + .channels = ad5064_channels, + .num_channels = 4, + }, + [ID_AD5064_1] = { + .shared_vref = true, + .channels = ad5064_channels, + .num_channels = 4, + }, + [ID_AD5065] = { + .shared_vref = false, + .channels = ad5064_channels, + .num_channels = 2, + }, + [ID_AD5628_1] = { + .shared_vref = true, + .internal_vref = 2500000, + .channels = ad5024_channels, + .num_channels = 8, + }, + [ID_AD5628_2] = { + .shared_vref = true, + .internal_vref = 5000000, + .channels = ad5024_channels, + .num_channels = 8, + }, + [ID_AD5648_1] = { + .shared_vref = true, + .internal_vref = 2500000, + .channels = ad5044_channels, + .num_channels = 8, + }, + [ID_AD5648_2] = { + .shared_vref = true, + .internal_vref = 5000000, + .channels = ad5044_channels, + .num_channels = 8, + }, + [ID_AD5666_1] = { + .shared_vref = true, + .internal_vref = 2500000, + .channels = ad5064_channels, + .num_channels = 4, + }, + [ID_AD5666_2] = { + .shared_vref = true, + .internal_vref = 5000000, + .channels = ad5064_channels, + .num_channels = 4, + }, + [ID_AD5668_1] = { + .shared_vref = true, + .internal_vref = 2500000, + .channels = ad5064_channels, + .num_channels = 8, + }, + [ID_AD5668_2] = { + .shared_vref = true, + .internal_vref = 5000000, + .channels = ad5064_channels, + .num_channels = 8, + }, +}; + +static inline unsigned int ad5064_num_vref(struct ad5064_state *st) +{ + return st->chip_info->shared_vref ? 1 : st->chip_info->num_channels; +} + +static const char * const ad5064_vref_names[] = { + "vrefA", + "vrefB", + "vrefC", + "vrefD", +}; + +static const char * const ad5064_vref_name(struct ad5064_state *st, + unsigned int vref) +{ + return st->chip_info->shared_vref ? "vref" : ad5064_vref_names[vref]; +} + +static int __devinit ad5064_probe(struct spi_device *spi) +{ + enum ad5064_type type = spi_get_device_id(spi)->driver_data; + struct iio_dev *indio_dev; + struct ad5064_state *st; + unsigned int i; + int ret; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + + st->chip_info = &ad5064_chip_info_tbl[type]; + st->spi = spi; + + for (i = 0; i < ad5064_num_vref(st); ++i) + st->vref_reg[i].supply = ad5064_vref_name(st, i); + + ret = regulator_bulk_get(&st->spi->dev, ad5064_num_vref(st), + st->vref_reg); + if (ret) { + if (!st->chip_info->internal_vref) + goto error_free; + st->use_internal_vref = true; + ret = ad5064_spi_write(st, AD5064_CMD_CONFIG, 0, + AD5064_CONFIG_INT_VREF_ENABLE, 0); + if (ret) { + dev_err(&spi->dev, "Failed to enable internal vref: %d\n", + ret); + goto error_free; + } + } else { + ret = regulator_bulk_enable(ad5064_num_vref(st), st->vref_reg); + if (ret) + goto error_free_reg; + } + + for (i = 0; i < st->chip_info->num_channels; ++i) { + st->pwr_down_mode[i] = AD5064_LDAC_PWRDN_1K; + st->dac_cache[i] = 0x8000; + } + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &ad5064_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_disable_reg; + + return 0; + +error_disable_reg: + if (!st->use_internal_vref) + regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg); +error_free_reg: + if (!st->use_internal_vref) + regulator_bulk_free(ad5064_num_vref(st), st->vref_reg); +error_free: + iio_device_free(indio_dev); + + return ret; +} + + +static int __devexit ad5064_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad5064_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + if (!st->use_internal_vref) { + regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg); + regulator_bulk_free(ad5064_num_vref(st), st->vref_reg); + } + + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad5064_id[] = { + {"ad5024", ID_AD5024}, + {"ad5025", ID_AD5025}, + {"ad5044", ID_AD5044}, + {"ad5045", ID_AD5045}, + {"ad5064", ID_AD5064}, + {"ad5064-1", ID_AD5064_1}, + {"ad5065", ID_AD5065}, + {"ad5628-1", ID_AD5628_1}, + {"ad5628-2", ID_AD5628_2}, + {"ad5648-1", ID_AD5648_1}, + {"ad5648-2", ID_AD5648_2}, + {"ad5666-1", ID_AD5666_1}, + {"ad5666-2", ID_AD5666_2}, + {"ad5668-1", ID_AD5668_1}, + {"ad5668-2", ID_AD5668_2}, + {"ad5668-3", ID_AD5668_2}, /* similar enough to ad5668-2 */ + {} +}; +MODULE_DEVICE_TABLE(spi, ad5064_id); + +static struct spi_driver ad5064_driver = { + .driver = { + .name = "ad5064", + .owner = THIS_MODULE, + }, + .probe = ad5064_probe, + .remove = __devexit_p(ad5064_remove), + .id_table = ad5064_id, +}; +module_spi_driver(ad5064_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Analog Devices AD5024/25/44/45/64/64-1/65, AD5628/48/66/68 DAC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5360.c b/drivers/iio/dac/ad5360.c new file mode 100644 index 000000000000..8fce84fe70b1 --- /dev/null +++ b/drivers/iio/dac/ad5360.c @@ -0,0 +1,570 @@ +/* + * Analog devices AD5360, AD5361, AD5362, AD5363, AD5370, AD5371, AD5373 + * multi-channel Digital to Analog Converters driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define AD5360_CMD(x) ((x) << 22) +#define AD5360_ADDR(x) ((x) << 16) + +#define AD5360_READBACK_TYPE(x) ((x) << 13) +#define AD5360_READBACK_ADDR(x) ((x) << 7) + +#define AD5360_CHAN_ADDR(chan) ((chan) + 0x8) + +#define AD5360_CMD_WRITE_DATA 0x3 +#define AD5360_CMD_WRITE_OFFSET 0x2 +#define AD5360_CMD_WRITE_GAIN 0x1 +#define AD5360_CMD_SPECIAL_FUNCTION 0x0 + +/* Special function register addresses */ +#define AD5360_REG_SF_NOP 0x0 +#define AD5360_REG_SF_CTRL 0x1 +#define AD5360_REG_SF_OFS(x) (0x2 + (x)) +#define AD5360_REG_SF_READBACK 0x5 + +#define AD5360_SF_CTRL_PWR_DOWN BIT(0) + +#define AD5360_READBACK_X1A 0x0 +#define AD5360_READBACK_X1B 0x1 +#define AD5360_READBACK_OFFSET 0x2 +#define AD5360_READBACK_GAIN 0x3 +#define AD5360_READBACK_SF 0x4 + + +/** + * struct ad5360_chip_info - chip specific information + * @channel_template: channel specification template + * @num_channels: number of channels + * @channels_per_group: number of channels per group + * @num_vrefs: number of vref supplies for the chip +*/ + +struct ad5360_chip_info { + struct iio_chan_spec channel_template; + unsigned int num_channels; + unsigned int channels_per_group; + unsigned int num_vrefs; +}; + +/** + * struct ad5360_state - driver instance specific data + * @spi: spi_device + * @chip_info: chip model specific constants, available modes etc + * @vref_reg: vref supply regulators + * @ctrl: control register cache + * @data: spi transfer buffers + */ + +struct ad5360_state { + struct spi_device *spi; + const struct ad5360_chip_info *chip_info; + struct regulator_bulk_data vref_reg[3]; + unsigned int ctrl; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + union { + __be32 d32; + u8 d8[4]; + } data[2] ____cacheline_aligned; +}; + +enum ad5360_type { + ID_AD5360, + ID_AD5361, + ID_AD5362, + ID_AD5363, + ID_AD5370, + ID_AD5371, + ID_AD5372, + ID_AD5373, +}; + +#define AD5360_CHANNEL(bits) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \ + IIO_CHAN_INFO_OFFSET_SEPARATE_BIT | \ + IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | \ + IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, \ + .scan_type = IIO_ST('u', (bits), 16, 16 - (bits)) \ +} + +static const struct ad5360_chip_info ad5360_chip_info_tbl[] = { + [ID_AD5360] = { + .channel_template = AD5360_CHANNEL(16), + .num_channels = 16, + .channels_per_group = 8, + .num_vrefs = 2, + }, + [ID_AD5361] = { + .channel_template = AD5360_CHANNEL(14), + .num_channels = 16, + .channels_per_group = 8, + .num_vrefs = 2, + }, + [ID_AD5362] = { + .channel_template = AD5360_CHANNEL(16), + .num_channels = 8, + .channels_per_group = 4, + .num_vrefs = 2, + }, + [ID_AD5363] = { + .channel_template = AD5360_CHANNEL(14), + .num_channels = 8, + .channels_per_group = 4, + .num_vrefs = 2, + }, + [ID_AD5370] = { + .channel_template = AD5360_CHANNEL(16), + .num_channels = 40, + .channels_per_group = 8, + .num_vrefs = 2, + }, + [ID_AD5371] = { + .channel_template = AD5360_CHANNEL(14), + .num_channels = 40, + .channels_per_group = 8, + .num_vrefs = 3, + }, + [ID_AD5372] = { + .channel_template = AD5360_CHANNEL(16), + .num_channels = 32, + .channels_per_group = 8, + .num_vrefs = 2, + }, + [ID_AD5373] = { + .channel_template = AD5360_CHANNEL(14), + .num_channels = 32, + .channels_per_group = 8, + .num_vrefs = 2, + }, +}; + +static unsigned int ad5360_get_channel_vref_index(struct ad5360_state *st, + unsigned int channel) +{ + unsigned int i; + + /* The first groups have their own vref, while the remaining groups + * share the last vref */ + i = channel / st->chip_info->channels_per_group; + if (i >= st->chip_info->num_vrefs) + i = st->chip_info->num_vrefs - 1; + + return i; +} + +static int ad5360_get_channel_vref(struct ad5360_state *st, + unsigned int channel) +{ + unsigned int i = ad5360_get_channel_vref_index(st, channel); + + return regulator_get_voltage(st->vref_reg[i].consumer); +} + + +static int ad5360_write_unlocked(struct iio_dev *indio_dev, + unsigned int cmd, unsigned int addr, unsigned int val, + unsigned int shift) +{ + struct ad5360_state *st = iio_priv(indio_dev); + + val <<= shift; + val |= AD5360_CMD(cmd) | AD5360_ADDR(addr); + st->data[0].d32 = cpu_to_be32(val); + + return spi_write(st->spi, &st->data[0].d8[1], 3); +} + +static int ad5360_write(struct iio_dev *indio_dev, unsigned int cmd, + unsigned int addr, unsigned int val, unsigned int shift) +{ + int ret; + + mutex_lock(&indio_dev->mlock); + ret = ad5360_write_unlocked(indio_dev, cmd, addr, val, shift); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int ad5360_read(struct iio_dev *indio_dev, unsigned int type, + unsigned int addr) +{ + struct ad5360_state *st = iio_priv(indio_dev); + struct spi_message m; + int ret; + struct spi_transfer t[] = { + { + .tx_buf = &st->data[0].d8[1], + .len = 3, + .cs_change = 1, + }, { + .rx_buf = &st->data[1].d8[1], + .len = 3, + }, + }; + + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + mutex_lock(&indio_dev->mlock); + + st->data[0].d32 = cpu_to_be32(AD5360_CMD(AD5360_CMD_SPECIAL_FUNCTION) | + AD5360_ADDR(AD5360_REG_SF_READBACK) | + AD5360_READBACK_TYPE(type) | + AD5360_READBACK_ADDR(addr)); + + ret = spi_sync(st->spi, &m); + if (ret >= 0) + ret = be32_to_cpu(st->data[1].d32) & 0xffff; + + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t ad5360_read_dac_powerdown(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad5360_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", (bool)(st->ctrl & AD5360_SF_CTRL_PWR_DOWN)); +} + +static int ad5360_update_ctrl(struct iio_dev *indio_dev, unsigned int set, + unsigned int clr) +{ + struct ad5360_state *st = iio_priv(indio_dev); + unsigned int ret; + + mutex_lock(&indio_dev->mlock); + + st->ctrl |= set; + st->ctrl &= ~clr; + + ret = ad5360_write_unlocked(indio_dev, AD5360_CMD_SPECIAL_FUNCTION, + AD5360_REG_SF_CTRL, st->ctrl, 0); + + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t ad5360_write_dac_powerdown(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + bool pwr_down; + int ret; + + ret = strtobool(buf, &pwr_down); + if (ret) + return ret; + + if (pwr_down) + ret = ad5360_update_ctrl(indio_dev, AD5360_SF_CTRL_PWR_DOWN, 0); + else + ret = ad5360_update_ctrl(indio_dev, 0, AD5360_SF_CTRL_PWR_DOWN); + + return ret ? ret : len; +} + +static IIO_DEVICE_ATTR(out_voltage_powerdown, + S_IRUGO | S_IWUSR, + ad5360_read_dac_powerdown, + ad5360_write_dac_powerdown, 0); + +static struct attribute *ad5360_attributes[] = { + &iio_dev_attr_out_voltage_powerdown.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ad5360_attribute_group = { + .attrs = ad5360_attributes, +}; + +static int ad5360_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad5360_state *st = iio_priv(indio_dev); + int max_val = (1 << chan->scan_type.realbits); + unsigned int ofs_index; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val >= max_val || val < 0) + return -EINVAL; + + return ad5360_write(indio_dev, AD5360_CMD_WRITE_DATA, + chan->address, val, chan->scan_type.shift); + + case IIO_CHAN_INFO_CALIBBIAS: + if (val >= max_val || val < 0) + return -EINVAL; + + return ad5360_write(indio_dev, AD5360_CMD_WRITE_OFFSET, + chan->address, val, chan->scan_type.shift); + + case IIO_CHAN_INFO_CALIBSCALE: + if (val >= max_val || val < 0) + return -EINVAL; + + return ad5360_write(indio_dev, AD5360_CMD_WRITE_GAIN, + chan->address, val, chan->scan_type.shift); + + case IIO_CHAN_INFO_OFFSET: + if (val <= -max_val || val > 0) + return -EINVAL; + + val = -val; + + /* offset is supposed to have the same scale as raw, but it + * is always 14bits wide, so on a chip where the raw value has + * more bits, we need to shift offset. */ + val >>= (chan->scan_type.realbits - 14); + + /* There is one DAC offset register per vref. Changing one + * channels offset will also change the offset for all other + * channels which share the same vref supply. */ + ofs_index = ad5360_get_channel_vref_index(st, chan->channel); + return ad5360_write(indio_dev, AD5360_CMD_SPECIAL_FUNCTION, + AD5360_REG_SF_OFS(ofs_index), val, 0); + default: + break; + } + + return -EINVAL; +} + +static int ad5360_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad5360_state *st = iio_priv(indio_dev); + unsigned int ofs_index; + int scale_uv; + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = ad5360_read(indio_dev, AD5360_READBACK_X1A, + chan->address); + if (ret < 0) + return ret; + *val = ret >> chan->scan_type.shift; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* vout = 4 * vref * dac_code */ + scale_uv = ad5360_get_channel_vref(st, chan->channel) * 4 * 100; + if (scale_uv < 0) + return scale_uv; + + scale_uv >>= (chan->scan_type.realbits); + *val = scale_uv / 100000; + *val2 = (scale_uv % 100000) * 10; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_CALIBBIAS: + ret = ad5360_read(indio_dev, AD5360_READBACK_OFFSET, + chan->address); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBSCALE: + ret = ad5360_read(indio_dev, AD5360_READBACK_GAIN, + chan->address); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + ofs_index = ad5360_get_channel_vref_index(st, chan->channel); + ret = ad5360_read(indio_dev, AD5360_READBACK_SF, + AD5360_REG_SF_OFS(ofs_index)); + if (ret < 0) + return ret; + + ret <<= (chan->scan_type.realbits - 14); + *val = -ret; + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static const struct iio_info ad5360_info = { + .read_raw = ad5360_read_raw, + .write_raw = ad5360_write_raw, + .attrs = &ad5360_attribute_group, + .driver_module = THIS_MODULE, +}; + +static const char * const ad5360_vref_name[] = { + "vref0", "vref1", "vref2" +}; + +static int __devinit ad5360_alloc_channels(struct iio_dev *indio_dev) +{ + struct ad5360_state *st = iio_priv(indio_dev); + struct iio_chan_spec *channels; + unsigned int i; + + channels = kcalloc(st->chip_info->num_channels, + sizeof(struct iio_chan_spec), GFP_KERNEL); + + if (!channels) + return -ENOMEM; + + for (i = 0; i < st->chip_info->num_channels; ++i) { + channels[i] = st->chip_info->channel_template; + channels[i].channel = i; + channels[i].address = AD5360_CHAN_ADDR(i); + } + + indio_dev->channels = channels; + + return 0; +} + +static int __devinit ad5360_probe(struct spi_device *spi) +{ + enum ad5360_type type = spi_get_device_id(spi)->driver_data; + struct iio_dev *indio_dev; + struct ad5360_state *st; + unsigned int i; + int ret; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) { + dev_err(&spi->dev, "Failed to allocate iio device\n"); + return -ENOMEM; + } + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + + st->chip_info = &ad5360_chip_info_tbl[type]; + st->spi = spi; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &ad5360_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->num_channels = st->chip_info->num_channels; + + ret = ad5360_alloc_channels(indio_dev); + if (ret) { + dev_err(&spi->dev, "Failed to allocate channel spec: %d\n", ret); + goto error_free; + } + + for (i = 0; i < st->chip_info->num_vrefs; ++i) + st->vref_reg[i].supply = ad5360_vref_name[i]; + + ret = regulator_bulk_get(&st->spi->dev, st->chip_info->num_vrefs, + st->vref_reg); + if (ret) { + dev_err(&spi->dev, "Failed to request vref regulators: %d\n", ret); + goto error_free_channels; + } + + ret = regulator_bulk_enable(st->chip_info->num_vrefs, st->vref_reg); + if (ret) { + dev_err(&spi->dev, "Failed to enable vref regulators: %d\n", ret); + goto error_free_reg; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&spi->dev, "Failed to register iio device: %d\n", ret); + goto error_disable_reg; + } + + return 0; + +error_disable_reg: + regulator_bulk_disable(st->chip_info->num_vrefs, st->vref_reg); +error_free_reg: + regulator_bulk_free(st->chip_info->num_vrefs, st->vref_reg); +error_free_channels: + kfree(indio_dev->channels); +error_free: + iio_device_free(indio_dev); + + return ret; +} + +static int __devexit ad5360_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad5360_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + kfree(indio_dev->channels); + + regulator_bulk_disable(st->chip_info->num_vrefs, st->vref_reg); + regulator_bulk_free(st->chip_info->num_vrefs, st->vref_reg); + + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad5360_ids[] = { + { "ad5360", ID_AD5360 }, + { "ad5361", ID_AD5361 }, + { "ad5362", ID_AD5362 }, + { "ad5363", ID_AD5363 }, + { "ad5370", ID_AD5370 }, + { "ad5371", ID_AD5371 }, + { "ad5372", ID_AD5372 }, + { "ad5373", ID_AD5373 }, + {} +}; +MODULE_DEVICE_TABLE(spi, ad5360_ids); + +static struct spi_driver ad5360_driver = { + .driver = { + .name = "ad5360", + .owner = THIS_MODULE, + }, + .probe = ad5360_probe, + .remove = __devexit_p(ad5360_remove), + .id_table = ad5360_ids, +}; +module_spi_driver(ad5360_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Analog Devices AD5360/61/62/63/70/71/72/73 DAC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c new file mode 100644 index 000000000000..5dfb4451728f --- /dev/null +++ b/drivers/iio/dac/ad5380.c @@ -0,0 +1,657 @@ +/* + * Analog devices AD5380, AD5381, AD5382, AD5383, AD5390, AD5391, AD5392 + * multi-channel Digital to Analog Converters driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define AD5380_REG_DATA(x) (((x) << 2) | 3) +#define AD5380_REG_OFFSET(x) (((x) << 2) | 2) +#define AD5380_REG_GAIN(x) (((x) << 2) | 1) +#define AD5380_REG_SF_PWR_DOWN (8 << 2) +#define AD5380_REG_SF_PWR_UP (9 << 2) +#define AD5380_REG_SF_CTRL (12 << 2) + +#define AD5380_CTRL_PWR_DOWN_MODE_OFFSET 13 +#define AD5380_CTRL_INT_VREF_2V5 BIT(12) +#define AD5380_CTRL_INT_VREF_EN BIT(10) + +/** + * struct ad5380_chip_info - chip specific information + * @channel_template: channel specification template + * @num_channels: number of channels + * @int_vref: internal vref in uV +*/ + +struct ad5380_chip_info { + struct iio_chan_spec channel_template; + unsigned int num_channels; + unsigned int int_vref; +}; + +/** + * struct ad5380_state - driver instance specific data + * @regmap: regmap instance used by the device + * @chip_info: chip model specific constants, available modes etc + * @vref_reg: vref supply regulator + * @vref: actual reference voltage used in uA + * @pwr_down: whether the chip is currently in power down mode + */ + +struct ad5380_state { + struct regmap *regmap; + const struct ad5380_chip_info *chip_info; + struct regulator *vref_reg; + int vref; + bool pwr_down; +}; + +enum ad5380_type { + ID_AD5380_3, + ID_AD5380_5, + ID_AD5381_3, + ID_AD5381_5, + ID_AD5382_3, + ID_AD5382_5, + ID_AD5383_3, + ID_AD5383_5, + ID_AD5390_3, + ID_AD5390_5, + ID_AD5391_3, + ID_AD5391_5, + ID_AD5392_3, + ID_AD5392_5, +}; + +static ssize_t ad5380_read_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, char *buf) +{ + struct ad5380_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", st->pwr_down); +} + +static ssize_t ad5380_write_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, const char *buf, + size_t len) +{ + struct ad5380_state *st = iio_priv(indio_dev); + bool pwr_down; + int ret; + + ret = strtobool(buf, &pwr_down); + if (ret) + return ret; + + mutex_lock(&indio_dev->mlock); + + if (pwr_down) + ret = regmap_write(st->regmap, AD5380_REG_SF_PWR_DOWN, 0); + else + ret = regmap_write(st->regmap, AD5380_REG_SF_PWR_UP, 0); + + st->pwr_down = pwr_down; + + mutex_unlock(&indio_dev->mlock); + + return ret ? ret : len; +} + +static const char * const ad5380_powerdown_modes[] = { + "100kohm_to_gnd", + "three_state", +}; + +static int ad5380_get_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5380_state *st = iio_priv(indio_dev); + unsigned int mode; + int ret; + + ret = regmap_read(st->regmap, AD5380_REG_SF_CTRL, &mode); + if (ret) + return ret; + + mode = (mode >> AD5380_CTRL_PWR_DOWN_MODE_OFFSET) & 1; + + return mode; +} + +static int ad5380_set_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int mode) +{ + struct ad5380_state *st = iio_priv(indio_dev); + int ret; + + ret = regmap_update_bits(st->regmap, AD5380_REG_SF_CTRL, + 1 << AD5380_CTRL_PWR_DOWN_MODE_OFFSET, + mode << AD5380_CTRL_PWR_DOWN_MODE_OFFSET); + + return ret; +} + +static const struct iio_enum ad5380_powerdown_mode_enum = { + .items = ad5380_powerdown_modes, + .num_items = ARRAY_SIZE(ad5380_powerdown_modes), + .get = ad5380_get_powerdown_mode, + .set = ad5380_set_powerdown_mode, +}; + +static unsigned int ad5380_info_to_reg(struct iio_chan_spec const *chan, + long info) +{ + switch (info) { + case 0: + return AD5380_REG_DATA(chan->address); + case IIO_CHAN_INFO_CALIBBIAS: + return AD5380_REG_OFFSET(chan->address); + case IIO_CHAN_INFO_CALIBSCALE: + return AD5380_REG_GAIN(chan->address); + default: + break; + } + + return 0; +} + +static int ad5380_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long info) +{ + const unsigned int max_val = (1 << chan->scan_type.realbits); + struct ad5380_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_CALIBSCALE: + if (val >= max_val || val < 0) + return -EINVAL; + + return regmap_write(st->regmap, + ad5380_info_to_reg(chan, info), + val << chan->scan_type.shift); + case IIO_CHAN_INFO_CALIBBIAS: + val += (1 << chan->scan_type.realbits) / 2; + if (val >= max_val || val < 0) + return -EINVAL; + + return regmap_write(st->regmap, + AD5380_REG_OFFSET(chan->address), + val << chan->scan_type.shift); + default: + break; + } + return -EINVAL; +} + +static int ad5380_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long info) +{ + struct ad5380_state *st = iio_priv(indio_dev); + unsigned long scale_uv; + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_CALIBSCALE: + ret = regmap_read(st->regmap, ad5380_info_to_reg(chan, info), + val); + if (ret) + return ret; + *val >>= chan->scan_type.shift; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + ret = regmap_read(st->regmap, AD5380_REG_OFFSET(chan->address), + val); + if (ret) + return ret; + *val >>= chan->scan_type.shift; + val -= (1 << chan->scan_type.realbits) / 2; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + scale_uv = ((2 * st->vref) >> chan->scan_type.realbits) * 100; + *val = scale_uv / 100000; + *val2 = (scale_uv % 100000) * 10; + return IIO_VAL_INT_PLUS_MICRO; + default: + break; + } + + return -EINVAL; +} + +static const struct iio_info ad5380_info = { + .read_raw = ad5380_read_raw, + .write_raw = ad5380_write_raw, + .driver_module = THIS_MODULE, +}; + +static struct iio_chan_spec_ext_info ad5380_ext_info[] = { + { + .name = "powerdown", + .read = ad5380_read_dac_powerdown, + .write = ad5380_write_dac_powerdown, + }, + IIO_ENUM("powerdown_mode", true, &ad5380_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", &ad5380_powerdown_mode_enum), + { }, +}; + +#define AD5380_CHANNEL(_bits) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SHARED_BIT | \ + IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | \ + IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, \ + .scan_type = IIO_ST('u', (_bits), 16, 14 - (_bits)), \ + .ext_info = ad5380_ext_info, \ +} + +static const struct ad5380_chip_info ad5380_chip_info_tbl[] = { + [ID_AD5380_3] = { + .channel_template = AD5380_CHANNEL(14), + .num_channels = 40, + .int_vref = 1250000, + }, + [ID_AD5380_5] = { + .channel_template = AD5380_CHANNEL(14), + .num_channels = 40, + .int_vref = 2500000, + }, + [ID_AD5381_3] = { + .channel_template = AD5380_CHANNEL(12), + .num_channels = 16, + .int_vref = 1250000, + }, + [ID_AD5381_5] = { + .channel_template = AD5380_CHANNEL(12), + .num_channels = 16, + .int_vref = 2500000, + }, + [ID_AD5382_3] = { + .channel_template = AD5380_CHANNEL(14), + .num_channels = 32, + .int_vref = 1250000, + }, + [ID_AD5382_5] = { + .channel_template = AD5380_CHANNEL(14), + .num_channels = 32, + .int_vref = 2500000, + }, + [ID_AD5383_3] = { + .channel_template = AD5380_CHANNEL(12), + .num_channels = 32, + .int_vref = 1250000, + }, + [ID_AD5383_5] = { + .channel_template = AD5380_CHANNEL(12), + .num_channels = 32, + .int_vref = 2500000, + }, + [ID_AD5390_3] = { + .channel_template = AD5380_CHANNEL(14), + .num_channels = 16, + .int_vref = 1250000, + }, + [ID_AD5390_5] = { + .channel_template = AD5380_CHANNEL(14), + .num_channels = 16, + .int_vref = 2500000, + }, + [ID_AD5391_3] = { + .channel_template = AD5380_CHANNEL(12), + .num_channels = 16, + .int_vref = 1250000, + }, + [ID_AD5391_5] = { + .channel_template = AD5380_CHANNEL(12), + .num_channels = 16, + .int_vref = 2500000, + }, + [ID_AD5392_3] = { + .channel_template = AD5380_CHANNEL(14), + .num_channels = 8, + .int_vref = 1250000, + }, + [ID_AD5392_5] = { + .channel_template = AD5380_CHANNEL(14), + .num_channels = 8, + .int_vref = 2500000, + }, +}; + +static int __devinit ad5380_alloc_channels(struct iio_dev *indio_dev) +{ + struct ad5380_state *st = iio_priv(indio_dev); + struct iio_chan_spec *channels; + unsigned int i; + + channels = kcalloc(st->chip_info->num_channels, + sizeof(struct iio_chan_spec), GFP_KERNEL); + + if (!channels) + return -ENOMEM; + + for (i = 0; i < st->chip_info->num_channels; ++i) { + channels[i] = st->chip_info->channel_template; + channels[i].channel = i; + channels[i].address = i; + } + + indio_dev->channels = channels; + + return 0; +} + +static int __devinit ad5380_probe(struct device *dev, struct regmap *regmap, + enum ad5380_type type, const char *name) +{ + struct iio_dev *indio_dev; + struct ad5380_state *st; + unsigned int ctrl = 0; + int ret; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) { + dev_err(dev, "Failed to allocate iio device\n"); + ret = -ENOMEM; + goto error_regmap_exit; + } + + st = iio_priv(indio_dev); + dev_set_drvdata(dev, indio_dev); + + st->chip_info = &ad5380_chip_info_tbl[type]; + st->regmap = regmap; + + indio_dev->dev.parent = dev; + indio_dev->name = name; + indio_dev->info = &ad5380_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->num_channels = st->chip_info->num_channels; + + ret = ad5380_alloc_channels(indio_dev); + if (ret) { + dev_err(dev, "Failed to allocate channel spec: %d\n", ret); + goto error_free; + } + + if (st->chip_info->int_vref == 2500000) + ctrl |= AD5380_CTRL_INT_VREF_2V5; + + st->vref_reg = regulator_get(dev, "vref"); + if (!IS_ERR(st->vref_reg)) { + ret = regulator_enable(st->vref_reg); + if (ret) { + dev_err(dev, "Failed to enable vref regulators: %d\n", + ret); + goto error_free_reg; + } + + st->vref = regulator_get_voltage(st->vref_reg); + } else { + st->vref = st->chip_info->int_vref; + ctrl |= AD5380_CTRL_INT_VREF_EN; + } + + ret = regmap_write(st->regmap, AD5380_REG_SF_CTRL, ctrl); + if (ret) { + dev_err(dev, "Failed to write to device: %d\n", ret); + goto error_disable_reg; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(dev, "Failed to register iio device: %d\n", ret); + goto error_disable_reg; + } + + return 0; + +error_disable_reg: + if (!IS_ERR(st->vref_reg)) + regulator_disable(st->vref_reg); +error_free_reg: + if (!IS_ERR(st->vref_reg)) + regulator_put(st->vref_reg); + + kfree(indio_dev->channels); +error_free: + iio_device_free(indio_dev); +error_regmap_exit: + regmap_exit(regmap); + + return ret; +} + +static int __devexit ad5380_remove(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ad5380_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + kfree(indio_dev->channels); + + if (!IS_ERR(st->vref_reg)) { + regulator_disable(st->vref_reg); + regulator_put(st->vref_reg); + } + + regmap_exit(st->regmap); + iio_device_free(indio_dev); + + return 0; +} + +static bool ad5380_reg_false(struct device *dev, unsigned int reg) +{ + return false; +} + +static const struct regmap_config ad5380_regmap_config = { + .reg_bits = 10, + .val_bits = 14, + + .max_register = AD5380_REG_DATA(40), + .cache_type = REGCACHE_RBTREE, + + .volatile_reg = ad5380_reg_false, + .readable_reg = ad5380_reg_false, +}; + +#if IS_ENABLED(CONFIG_SPI_MASTER) + +static int __devinit ad5380_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct regmap *regmap; + + regmap = regmap_init_spi(spi, &ad5380_regmap_config); + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return ad5380_probe(&spi->dev, regmap, id->driver_data, id->name); +} + +static int __devexit ad5380_spi_remove(struct spi_device *spi) +{ + return ad5380_remove(&spi->dev); +} + +static const struct spi_device_id ad5380_spi_ids[] = { + { "ad5380-3", ID_AD5380_3 }, + { "ad5380-5", ID_AD5380_5 }, + { "ad5381-3", ID_AD5381_3 }, + { "ad5381-5", ID_AD5381_5 }, + { "ad5382-3", ID_AD5382_3 }, + { "ad5382-5", ID_AD5382_5 }, + { "ad5383-3", ID_AD5383_3 }, + { "ad5383-5", ID_AD5383_5 }, + { "ad5384-3", ID_AD5380_3 }, + { "ad5384-5", ID_AD5380_5 }, + { "ad5390-3", ID_AD5390_3 }, + { "ad5390-5", ID_AD5390_5 }, + { "ad5391-3", ID_AD5391_3 }, + { "ad5391-5", ID_AD5391_5 }, + { "ad5392-3", ID_AD5392_3 }, + { "ad5392-5", ID_AD5392_5 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad5380_spi_ids); + +static struct spi_driver ad5380_spi_driver = { + .driver = { + .name = "ad5380", + .owner = THIS_MODULE, + }, + .probe = ad5380_spi_probe, + .remove = __devexit_p(ad5380_spi_remove), + .id_table = ad5380_spi_ids, +}; + +static inline int ad5380_spi_register_driver(void) +{ + return spi_register_driver(&ad5380_spi_driver); +} + +static inline void ad5380_spi_unregister_driver(void) +{ + spi_unregister_driver(&ad5380_spi_driver); +} + +#else + +static inline int ad5380_spi_register_driver(void) +{ + return 0; +} + +static inline void ad5380_spi_unregister_driver(void) +{ +} + +#endif + +#if IS_ENABLED(CONFIG_I2C) + +static int __devinit ad5380_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct regmap *regmap; + + regmap = regmap_init_i2c(i2c, &ad5380_regmap_config); + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return ad5380_probe(&i2c->dev, regmap, id->driver_data, id->name); +} + +static int __devexit ad5380_i2c_remove(struct i2c_client *i2c) +{ + return ad5380_remove(&i2c->dev); +} + +static const struct i2c_device_id ad5380_i2c_ids[] = { + { "ad5380-3", ID_AD5380_3 }, + { "ad5380-5", ID_AD5380_5 }, + { "ad5381-3", ID_AD5381_3 }, + { "ad5381-5", ID_AD5381_5 }, + { "ad5382-3", ID_AD5382_3 }, + { "ad5382-5", ID_AD5382_5 }, + { "ad5383-3", ID_AD5383_3 }, + { "ad5383-5", ID_AD5383_5 }, + { "ad5384-3", ID_AD5380_3 }, + { "ad5384-5", ID_AD5380_5 }, + { "ad5390-3", ID_AD5390_3 }, + { "ad5390-5", ID_AD5390_5 }, + { "ad5391-3", ID_AD5391_3 }, + { "ad5391-5", ID_AD5391_5 }, + { "ad5392-3", ID_AD5392_3 }, + { "ad5392-5", ID_AD5392_5 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ad5380_i2c_ids); + +static struct i2c_driver ad5380_i2c_driver = { + .driver = { + .name = "ad5380", + .owner = THIS_MODULE, + }, + .probe = ad5380_i2c_probe, + .remove = __devexit_p(ad5380_i2c_remove), + .id_table = ad5380_i2c_ids, +}; + +static inline int ad5380_i2c_register_driver(void) +{ + return i2c_add_driver(&ad5380_i2c_driver); +} + +static inline void ad5380_i2c_unregister_driver(void) +{ + i2c_del_driver(&ad5380_i2c_driver); +} + +#else + +static inline int ad5380_i2c_register_driver(void) +{ + return 0; +} + +static inline void ad5380_i2c_unregister_driver(void) +{ +} + +#endif + +static int __init ad5380_spi_init(void) +{ + int ret; + + ret = ad5380_spi_register_driver(); + if (ret) + return ret; + + ret = ad5380_i2c_register_driver(); + if (ret) { + ad5380_spi_unregister_driver(); + return ret; + } + + return 0; +} +module_init(ad5380_spi_init); + +static void __exit ad5380_spi_exit(void) +{ + ad5380_i2c_unregister_driver(); + ad5380_spi_unregister_driver(); + +} +module_exit(ad5380_spi_exit); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Analog Devices AD5380/81/82/83/84/90/91/92 DAC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5421.c b/drivers/iio/dac/ad5421.c new file mode 100644 index 000000000000..cdbc5bf25c31 --- /dev/null +++ b/drivers/iio/dac/ad5421.c @@ -0,0 +1,544 @@ +/* + * AD5421 Digital to analog converters driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +#define AD5421_REG_DAC_DATA 0x1 +#define AD5421_REG_CTRL 0x2 +#define AD5421_REG_OFFSET 0x3 +#define AD5421_REG_GAIN 0x4 +/* load dac and fault shared the same register number. Writing to it will cause + * a dac load command, reading from it will return the fault status register */ +#define AD5421_REG_LOAD_DAC 0x5 +#define AD5421_REG_FAULT 0x5 +#define AD5421_REG_FORCE_ALARM_CURRENT 0x6 +#define AD5421_REG_RESET 0x7 +#define AD5421_REG_START_CONVERSION 0x8 +#define AD5421_REG_NOOP 0x9 + +#define AD5421_CTRL_WATCHDOG_DISABLE BIT(12) +#define AD5421_CTRL_AUTO_FAULT_READBACK BIT(11) +#define AD5421_CTRL_MIN_CURRENT BIT(9) +#define AD5421_CTRL_ADC_SOURCE_TEMP BIT(8) +#define AD5421_CTRL_ADC_ENABLE BIT(7) +#define AD5421_CTRL_PWR_DOWN_INT_VREF BIT(6) + +#define AD5421_FAULT_SPI BIT(15) +#define AD5421_FAULT_PEC BIT(14) +#define AD5421_FAULT_OVER_CURRENT BIT(13) +#define AD5421_FAULT_UNDER_CURRENT BIT(12) +#define AD5421_FAULT_TEMP_OVER_140 BIT(11) +#define AD5421_FAULT_TEMP_OVER_100 BIT(10) +#define AD5421_FAULT_UNDER_VOLTAGE_6V BIT(9) +#define AD5421_FAULT_UNDER_VOLTAGE_12V BIT(8) + +/* These bits will cause the fault pin to go high */ +#define AD5421_FAULT_TRIGGER_IRQ \ + (AD5421_FAULT_SPI | AD5421_FAULT_PEC | AD5421_FAULT_OVER_CURRENT | \ + AD5421_FAULT_UNDER_CURRENT | AD5421_FAULT_TEMP_OVER_140) + +/** + * struct ad5421_state - driver instance specific data + * @spi: spi_device + * @ctrl: control register cache + * @current_range: current range which the device is configured for + * @data: spi transfer buffers + * @fault_mask: software masking of events + */ +struct ad5421_state { + struct spi_device *spi; + unsigned int ctrl; + enum ad5421_current_range current_range; + unsigned int fault_mask; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + union { + u32 d32; + u8 d8[4]; + } data[2] ____cacheline_aligned; +}; + +static const struct iio_chan_spec ad5421_channels[] = { + { + .type = IIO_CURRENT, + .indexed = 1, + .output = 1, + .channel = 0, + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT | + IIO_CHAN_INFO_OFFSET_SHARED_BIT | + IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | + IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, + .scan_type = IIO_ST('u', 16, 16, 0), + .event_mask = IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) | + IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + }, + { + .type = IIO_TEMP, + .channel = -1, + .event_mask = IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + }, +}; + +static int ad5421_write_unlocked(struct iio_dev *indio_dev, + unsigned int reg, unsigned int val) +{ + struct ad5421_state *st = iio_priv(indio_dev); + + st->data[0].d32 = cpu_to_be32((reg << 16) | val); + + return spi_write(st->spi, &st->data[0].d8[1], 3); +} + +static int ad5421_write(struct iio_dev *indio_dev, unsigned int reg, + unsigned int val) +{ + int ret; + + mutex_lock(&indio_dev->mlock); + ret = ad5421_write_unlocked(indio_dev, reg, val); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int ad5421_read(struct iio_dev *indio_dev, unsigned int reg) +{ + struct ad5421_state *st = iio_priv(indio_dev); + struct spi_message m; + int ret; + struct spi_transfer t[] = { + { + .tx_buf = &st->data[0].d8[1], + .len = 3, + .cs_change = 1, + }, { + .rx_buf = &st->data[1].d8[1], + .len = 3, + }, + }; + + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + mutex_lock(&indio_dev->mlock); + + st->data[0].d32 = cpu_to_be32((1 << 23) | (reg << 16)); + + ret = spi_sync(st->spi, &m); + if (ret >= 0) + ret = be32_to_cpu(st->data[1].d32) & 0xffff; + + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int ad5421_update_ctrl(struct iio_dev *indio_dev, unsigned int set, + unsigned int clr) +{ + struct ad5421_state *st = iio_priv(indio_dev); + unsigned int ret; + + mutex_lock(&indio_dev->mlock); + + st->ctrl &= ~clr; + st->ctrl |= set; + + ret = ad5421_write_unlocked(indio_dev, AD5421_REG_CTRL, st->ctrl); + + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static irqreturn_t ad5421_fault_handler(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct ad5421_state *st = iio_priv(indio_dev); + unsigned int fault; + unsigned int old_fault = 0; + unsigned int events; + + fault = ad5421_read(indio_dev, AD5421_REG_FAULT); + if (!fault) + return IRQ_NONE; + + /* If we had a fault, this might mean that the DAC has lost its state + * and has been reset. Make sure that the control register actually + * contains what we expect it to contain. Otherwise the watchdog might + * be enabled and we get watchdog timeout faults, which will render the + * DAC unusable. */ + ad5421_update_ctrl(indio_dev, 0, 0); + + + /* The fault pin stays high as long as a fault condition is present and + * it is not possible to mask fault conditions. For certain fault + * conditions for example like over-temperature it takes some time + * until the fault condition disappears. If we would exit the interrupt + * handler immediately after handling the event it would be entered + * again instantly. Thus we fall back to polling in case we detect that + * a interrupt condition is still present. + */ + do { + /* 0xffff is a invalid value for the register and will only be + * read if there has been a communication error */ + if (fault == 0xffff) + fault = 0; + + /* we are only interested in new events */ + events = (old_fault ^ fault) & fault; + events &= st->fault_mask; + + if (events & AD5421_FAULT_OVER_CURRENT) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_CURRENT, + 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + iio_get_time_ns()); + } + + if (events & AD5421_FAULT_UNDER_CURRENT) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_CURRENT, + 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + iio_get_time_ns()); + } + + if (events & AD5421_FAULT_TEMP_OVER_140) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_TEMP, + 0, + IIO_EV_TYPE_MAG, + IIO_EV_DIR_RISING), + iio_get_time_ns()); + } + + old_fault = fault; + fault = ad5421_read(indio_dev, AD5421_REG_FAULT); + + /* still active? go to sleep for some time */ + if (fault & AD5421_FAULT_TRIGGER_IRQ) + msleep(1000); + + } while (fault & AD5421_FAULT_TRIGGER_IRQ); + + + return IRQ_HANDLED; +} + +static void ad5421_get_current_min_max(struct ad5421_state *st, + unsigned int *min, unsigned int *max) +{ + /* The current range is configured using external pins, which are + * usually hard-wired and not run-time switchable. */ + switch (st->current_range) { + case AD5421_CURRENT_RANGE_4mA_20mA: + *min = 4000; + *max = 20000; + break; + case AD5421_CURRENT_RANGE_3mA8_21mA: + *min = 3800; + *max = 21000; + break; + case AD5421_CURRENT_RANGE_3mA2_24mA: + *min = 3200; + *max = 24000; + break; + default: + *min = 0; + *max = 1; + break; + } +} + +static inline unsigned int ad5421_get_offset(struct ad5421_state *st) +{ + unsigned int min, max; + + ad5421_get_current_min_max(st, &min, &max); + return (min * (1 << 16)) / (max - min); +} + +static inline unsigned int ad5421_get_scale(struct ad5421_state *st) +{ + unsigned int min, max; + + ad5421_get_current_min_max(st, &min, &max); + return ((max - min) * 1000) / (1 << 16); +} + +static int ad5421_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long m) +{ + struct ad5421_state *st = iio_priv(indio_dev); + int ret; + + if (chan->type != IIO_CURRENT) + return -EINVAL; + + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = ad5421_read(indio_dev, AD5421_REG_DAC_DATA); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = ad5421_get_scale(st); + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OFFSET: + *val = ad5421_get_offset(st); + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + ret = ad5421_read(indio_dev, AD5421_REG_OFFSET); + if (ret < 0) + return ret; + *val = ret - 32768; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBSCALE: + ret = ad5421_read(indio_dev, AD5421_REG_GAIN); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int ad5421_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long mask) +{ + const unsigned int max_val = 1 << 16; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val >= max_val || val < 0) + return -EINVAL; + + return ad5421_write(indio_dev, AD5421_REG_DAC_DATA, val); + case IIO_CHAN_INFO_CALIBBIAS: + val += 32768; + if (val >= max_val || val < 0) + return -EINVAL; + + return ad5421_write(indio_dev, AD5421_REG_OFFSET, val); + case IIO_CHAN_INFO_CALIBSCALE: + if (val >= max_val || val < 0) + return -EINVAL; + + return ad5421_write(indio_dev, AD5421_REG_GAIN, val); + default: + break; + } + + return -EINVAL; +} + +static int ad5421_write_event_config(struct iio_dev *indio_dev, + u64 event_code, int state) +{ + struct ad5421_state *st = iio_priv(indio_dev); + unsigned int mask; + + switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) { + case IIO_CURRENT: + if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == + IIO_EV_DIR_RISING) + mask = AD5421_FAULT_OVER_CURRENT; + else + mask = AD5421_FAULT_UNDER_CURRENT; + break; + case IIO_TEMP: + mask = AD5421_FAULT_TEMP_OVER_140; + break; + default: + return -EINVAL; + } + + mutex_lock(&indio_dev->mlock); + if (state) + st->fault_mask |= mask; + else + st->fault_mask &= ~mask; + mutex_unlock(&indio_dev->mlock); + + return 0; +} + +static int ad5421_read_event_config(struct iio_dev *indio_dev, + u64 event_code) +{ + struct ad5421_state *st = iio_priv(indio_dev); + unsigned int mask; + + switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) { + case IIO_CURRENT: + if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == + IIO_EV_DIR_RISING) + mask = AD5421_FAULT_OVER_CURRENT; + else + mask = AD5421_FAULT_UNDER_CURRENT; + break; + case IIO_TEMP: + mask = AD5421_FAULT_TEMP_OVER_140; + break; + default: + return -EINVAL; + } + + return (bool)(st->fault_mask & mask); +} + +static int ad5421_read_event_value(struct iio_dev *indio_dev, u64 event_code, + int *val) +{ + int ret; + + switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) { + case IIO_CURRENT: + ret = ad5421_read(indio_dev, AD5421_REG_DAC_DATA); + if (ret < 0) + return ret; + *val = ret; + break; + case IIO_TEMP: + *val = 140000; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct iio_info ad5421_info = { + .read_raw = ad5421_read_raw, + .write_raw = ad5421_write_raw, + .read_event_config = ad5421_read_event_config, + .write_event_config = ad5421_write_event_config, + .read_event_value = ad5421_read_event_value, + .driver_module = THIS_MODULE, +}; + +static int __devinit ad5421_probe(struct spi_device *spi) +{ + struct ad5421_platform_data *pdata = dev_get_platdata(&spi->dev); + struct iio_dev *indio_dev; + struct ad5421_state *st; + int ret; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) { + dev_err(&spi->dev, "Failed to allocate iio device\n"); + return -ENOMEM; + } + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + + st->spi = spi; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = "ad5421"; + indio_dev->info = &ad5421_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad5421_channels; + indio_dev->num_channels = ARRAY_SIZE(ad5421_channels); + + st->ctrl = AD5421_CTRL_WATCHDOG_DISABLE | + AD5421_CTRL_AUTO_FAULT_READBACK; + + if (pdata) { + st->current_range = pdata->current_range; + if (pdata->external_vref) + st->ctrl |= AD5421_CTRL_PWR_DOWN_INT_VREF; + } else { + st->current_range = AD5421_CURRENT_RANGE_4mA_20mA; + } + + /* write initial ctrl register value */ + ad5421_update_ctrl(indio_dev, 0, 0); + + if (spi->irq) { + ret = request_threaded_irq(spi->irq, + NULL, + ad5421_fault_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "ad5421 fault", + indio_dev); + if (ret) + goto error_free; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&spi->dev, "Failed to register iio device: %d\n", ret); + goto error_free_irq; + } + + return 0; + +error_free_irq: + if (spi->irq) + free_irq(spi->irq, indio_dev); +error_free: + iio_device_free(indio_dev); + + return ret; +} + +static int __devexit ad5421_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + + iio_device_unregister(indio_dev); + if (spi->irq) + free_irq(spi->irq, indio_dev); + iio_device_free(indio_dev); + + return 0; +} + +static struct spi_driver ad5421_driver = { + .driver = { + .name = "ad5421", + .owner = THIS_MODULE, + }, + .probe = ad5421_probe, + .remove = __devexit_p(ad5421_remove), +}; +module_spi_driver(ad5421_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Analog Devices AD5421 DAC"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:ad5421"); diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c new file mode 100644 index 000000000000..49f557fc673b --- /dev/null +++ b/drivers/iio/dac/ad5446.c @@ -0,0 +1,381 @@ +/* + * AD5446 SPI DAC driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ad5446.h" + +static int ad5446_write(struct ad5446_state *st, unsigned val) +{ + __be16 data = cpu_to_be16(val); + return spi_write(st->spi, &data, sizeof(data)); +} + +static int ad5660_write(struct ad5446_state *st, unsigned val) +{ + uint8_t data[3]; + + data[0] = (val >> 16) & 0xFF; + data[1] = (val >> 8) & 0xFF; + data[2] = val & 0xFF; + + return spi_write(st->spi, data, sizeof(data)); +} + +static const char * const ad5446_powerdown_modes[] = { + "1kohm_to_gnd", "100kohm_to_gnd", "three_state" +}; + +static int ad5446_set_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int mode) +{ + struct ad5446_state *st = iio_priv(indio_dev); + + st->pwr_down_mode = mode + 1; + + return 0; +} + +static int ad5446_get_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5446_state *st = iio_priv(indio_dev); + + return st->pwr_down_mode - 1; +} + +static const struct iio_enum ad5446_powerdown_mode_enum = { + .items = ad5446_powerdown_modes, + .num_items = ARRAY_SIZE(ad5446_powerdown_modes), + .get = ad5446_get_powerdown_mode, + .set = ad5446_set_powerdown_mode, +}; + +static ssize_t ad5446_read_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ad5446_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", st->pwr_down); +} + +static ssize_t ad5446_write_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad5446_state *st = iio_priv(indio_dev); + unsigned int shift; + unsigned int val; + bool powerdown; + int ret; + + ret = strtobool(buf, &powerdown); + if (ret) + return ret; + + mutex_lock(&indio_dev->mlock); + st->pwr_down = powerdown; + + if (st->pwr_down) { + shift = chan->scan_type.realbits + chan->scan_type.shift; + val = st->pwr_down_mode << shift; + } else { + val = st->cached_val; + } + + ret = st->chip_info->write(st, val); + mutex_unlock(&indio_dev->mlock); + + return ret ? ret : len; +} + +static const struct iio_chan_spec_ext_info ad5064_ext_info_powerdown[] = { + { + .name = "powerdown", + .read = ad5446_read_dac_powerdown, + .write = ad5446_write_dac_powerdown, + }, + IIO_ENUM("powerdown_mode", false, &ad5446_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", &ad5446_powerdown_mode_enum), + { }, +}; + +#define _AD5446_CHANNEL(bits, storage, shift, ext) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = 0, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SHARED_BIT, \ + .scan_type = IIO_ST('u', (bits), (storage), (shift)), \ + .ext_info = (ext), \ +} + +#define AD5446_CHANNEL(bits, storage, shift) \ + _AD5446_CHANNEL(bits, storage, shift, NULL) + +#define AD5446_CHANNEL_POWERDOWN(bits, storage, shift) \ + _AD5446_CHANNEL(bits, storage, shift, ad5064_ext_info_powerdown) + +static const struct ad5446_chip_info ad5446_chip_info_tbl[] = { + [ID_AD5444] = { + .channel = AD5446_CHANNEL(12, 16, 2), + .write = ad5446_write, + }, + [ID_AD5446] = { + .channel = AD5446_CHANNEL(14, 16, 0), + .write = ad5446_write, + }, + [ID_AD5541A] = { + .channel = AD5446_CHANNEL(16, 16, 0), + .write = ad5446_write, + }, + [ID_AD5512A] = { + .channel = AD5446_CHANNEL(12, 16, 4), + .write = ad5446_write, + }, + [ID_AD5553] = { + .channel = AD5446_CHANNEL(14, 16, 0), + .write = ad5446_write, + }, + [ID_AD5601] = { + .channel = AD5446_CHANNEL_POWERDOWN(8, 16, 6), + .write = ad5446_write, + }, + [ID_AD5611] = { + .channel = AD5446_CHANNEL_POWERDOWN(10, 16, 4), + .write = ad5446_write, + }, + [ID_AD5621] = { + .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2), + .write = ad5446_write, + }, + [ID_AD5620_2500] = { + .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2), + .int_vref_mv = 2500, + .write = ad5446_write, + }, + [ID_AD5620_1250] = { + .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2), + .int_vref_mv = 1250, + .write = ad5446_write, + }, + [ID_AD5640_2500] = { + .channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0), + .int_vref_mv = 2500, + .write = ad5446_write, + }, + [ID_AD5640_1250] = { + .channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0), + .int_vref_mv = 1250, + .write = ad5446_write, + }, + [ID_AD5660_2500] = { + .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0), + .int_vref_mv = 2500, + .write = ad5660_write, + }, + [ID_AD5660_1250] = { + .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0), + .int_vref_mv = 1250, + .write = ad5660_write, + }, + [ID_AD5662] = { + .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0), + .write = ad5660_write, + }, +}; + +static int ad5446_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad5446_state *st = iio_priv(indio_dev); + unsigned long scale_uv; + + switch (m) { + case IIO_CHAN_INFO_RAW: + *val = st->cached_val; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; + *val = scale_uv / 1000; + *val2 = (scale_uv % 1000) * 1000; + return IIO_VAL_INT_PLUS_MICRO; + + } + return -EINVAL; +} + +static int ad5446_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad5446_state *st = iio_priv(indio_dev); + int ret = 0; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val >= (1 << chan->scan_type.realbits) || val < 0) + return -EINVAL; + + val <<= chan->scan_type.shift; + mutex_lock(&indio_dev->mlock); + st->cached_val = val; + if (!st->pwr_down) + ret = st->chip_info->write(st, val); + mutex_unlock(&indio_dev->mlock); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct iio_info ad5446_info = { + .read_raw = ad5446_read_raw, + .write_raw = ad5446_write_raw, + .driver_module = THIS_MODULE, +}; + +static int __devinit ad5446_probe(struct spi_device *spi) +{ + struct ad5446_state *st; + struct iio_dev *indio_dev; + struct regulator *reg; + int ret, voltage_uv = 0; + + reg = regulator_get(&spi->dev, "vcc"); + if (!IS_ERR(reg)) { + ret = regulator_enable(reg); + if (ret) + goto error_put_reg; + + voltage_uv = regulator_get_voltage(reg); + } + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) { + ret = -ENOMEM; + goto error_disable_reg; + } + st = iio_priv(indio_dev); + st->chip_info = + &ad5446_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + + spi_set_drvdata(spi, indio_dev); + st->reg = reg; + st->spi = spi; + + /* Establish that the iio_dev is a child of the spi device */ + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &ad5446_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = &st->chip_info->channel; + indio_dev->num_channels = 1; + + st->pwr_down_mode = MODE_PWRDWN_1k; + + if (st->chip_info->int_vref_mv) + st->vref_mv = st->chip_info->int_vref_mv; + else if (voltage_uv) + st->vref_mv = voltage_uv / 1000; + else + dev_warn(&spi->dev, "reference voltage unspecified\n"); + + ret = iio_device_register(indio_dev); + if (ret) + goto error_free_device; + + return 0; + +error_free_device: + iio_device_free(indio_dev); +error_disable_reg: + if (!IS_ERR(reg)) + regulator_disable(reg); +error_put_reg: + if (!IS_ERR(reg)) + regulator_put(reg); + + return ret; +} + +static int ad5446_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad5446_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + if (!IS_ERR(st->reg)) { + regulator_disable(st->reg); + regulator_put(st->reg); + } + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad5446_id[] = { + {"ad5444", ID_AD5444}, + {"ad5446", ID_AD5446}, + {"ad5512a", ID_AD5512A}, + {"ad5541a", ID_AD5541A}, + {"ad5542a", ID_AD5541A}, /* ad5541a and ad5542a are compatible */ + {"ad5543", ID_AD5541A}, /* ad5541a and ad5543 are compatible */ + {"ad5553", ID_AD5553}, + {"ad5601", ID_AD5601}, + {"ad5611", ID_AD5611}, + {"ad5621", ID_AD5621}, + {"ad5620-2500", ID_AD5620_2500}, /* AD5620/40/60: */ + {"ad5620-1250", ID_AD5620_1250}, /* part numbers may look differently */ + {"ad5640-2500", ID_AD5640_2500}, + {"ad5640-1250", ID_AD5640_1250}, + {"ad5660-2500", ID_AD5660_2500}, + {"ad5660-1250", ID_AD5660_1250}, + {"ad5662", ID_AD5662}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad5446_id); + +static struct spi_driver ad5446_driver = { + .driver = { + .name = "ad5446", + .owner = THIS_MODULE, + }, + .probe = ad5446_probe, + .remove = __devexit_p(ad5446_remove), + .id_table = ad5446_id, +}; +module_spi_driver(ad5446_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD5444/AD5446 DAC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5446.h b/drivers/iio/dac/ad5446.h new file mode 100644 index 000000000000..dfd68ce7427e --- /dev/null +++ b/drivers/iio/dac/ad5446.h @@ -0,0 +1,89 @@ +/* + * AD5446 SPI DAC driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ +#ifndef IIO_DAC_AD5446_H_ +#define IIO_DAC_AD5446_H_ + +/* DAC Control Bits */ + +#define AD5446_LOAD (0x0 << 14) /* Load and update */ +#define AD5446_SDO_DIS (0x1 << 14) /* Disable SDO */ +#define AD5446_NOP (0x2 << 14) /* No operation */ +#define AD5446_CLK_RISING (0x3 << 14) /* Clock data on rising edge */ + +#define AD5620_LOAD (0x0 << 14) /* Load and update Norm Operation*/ +#define AD5620_PWRDWN_1k (0x1 << 14) /* Power-down: 1kOhm to GND */ +#define AD5620_PWRDWN_100k (0x2 << 14) /* Power-down: 100kOhm to GND */ +#define AD5620_PWRDWN_TRISTATE (0x3 << 14) /* Power-down: Three-state */ + +#define AD5660_LOAD (0x0 << 16) /* Load and update Norm Operation*/ +#define AD5660_PWRDWN_1k (0x1 << 16) /* Power-down: 1kOhm to GND */ +#define AD5660_PWRDWN_100k (0x2 << 16) /* Power-down: 100kOhm to GND */ +#define AD5660_PWRDWN_TRISTATE (0x3 << 16) /* Power-down: Three-state */ + +#define MODE_PWRDWN_1k 0x1 +#define MODE_PWRDWN_100k 0x2 +#define MODE_PWRDWN_TRISTATE 0x3 + +/** + * struct ad5446_state - driver instance specific data + * @spi: spi_device + * @chip_info: chip model specific constants, available modes etc + * @reg: supply regulator + * @vref_mv: actual reference voltage used + */ + +struct ad5446_state { + struct spi_device *spi; + const struct ad5446_chip_info *chip_info; + struct regulator *reg; + unsigned short vref_mv; + unsigned cached_val; + unsigned pwr_down_mode; + unsigned pwr_down; +}; + +/** + * struct ad5446_chip_info - chip specific information + * @channel: channel spec for the DAC + * @int_vref_mv: AD5620/40/60: the internal reference voltage + * @write: chip specific helper function to write to the register + */ + +struct ad5446_chip_info { + struct iio_chan_spec channel; + u16 int_vref_mv; + int (*write)(struct ad5446_state *st, unsigned val); +}; + +/** + * ad5446_supported_device_ids: + * The AD5620/40/60 parts are available in different fixed internal reference + * voltage options. The actual part numbers may look differently + * (and a bit cryptic), however this style is used to make clear which + * parts are supported here. + */ + +enum ad5446_supported_device_ids { + ID_AD5444, + ID_AD5446, + ID_AD5541A, + ID_AD5512A, + ID_AD5553, + ID_AD5601, + ID_AD5611, + ID_AD5621, + ID_AD5620_2500, + ID_AD5620_1250, + ID_AD5640_2500, + ID_AD5640_1250, + ID_AD5660_2500, + ID_AD5660_1250, + ID_AD5662, +}; + +#endif /* IIO_DAC_AD5446_H_ */ diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c new file mode 100644 index 000000000000..242bdc7d0044 --- /dev/null +++ b/drivers/iio/dac/ad5504.c @@ -0,0 +1,393 @@ +/* + * AD5504, AD5501 High Voltage Digital to Analog Converter + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define AD5505_BITS 12 +#define AD5504_RES_MASK ((1 << (AD5505_BITS)) - 1) + +#define AD5504_CMD_READ (1 << 15) +#define AD5504_CMD_WRITE (0 << 15) +#define AD5504_ADDR(addr) ((addr) << 12) + +/* Registers */ +#define AD5504_ADDR_NOOP 0 +#define AD5504_ADDR_DAC(x) ((x) + 1) +#define AD5504_ADDR_ALL_DAC 5 +#define AD5504_ADDR_CTRL 7 + +/* Control Register */ +#define AD5504_DAC_PWR(ch) ((ch) << 2) +#define AD5504_DAC_PWRDWN_MODE(mode) ((mode) << 6) +#define AD5504_DAC_PWRDN_20K 0 +#define AD5504_DAC_PWRDN_3STATE 1 + +/** + * struct ad5446_state - driver instance specific data + * @us: spi_device + * @reg: supply regulator + * @vref_mv: actual reference voltage used + * @pwr_down_mask power down mask + * @pwr_down_mode current power down mode + */ + +struct ad5504_state { + struct spi_device *spi; + struct regulator *reg; + unsigned short vref_mv; + unsigned pwr_down_mask; + unsigned pwr_down_mode; +}; + +/** + * ad5504_supported_device_ids: + */ + +enum ad5504_supported_device_ids { + ID_AD5504, + ID_AD5501, +}; + +static int ad5504_spi_write(struct spi_device *spi, u8 addr, u16 val) +{ + u16 tmp = cpu_to_be16(AD5504_CMD_WRITE | + AD5504_ADDR(addr) | + (val & AD5504_RES_MASK)); + + return spi_write(spi, (u8 *)&tmp, 2); +} + +static int ad5504_spi_read(struct spi_device *spi, u8 addr) +{ + u16 tmp = cpu_to_be16(AD5504_CMD_READ | AD5504_ADDR(addr)); + u16 val; + int ret; + struct spi_transfer t = { + .tx_buf = &tmp, + .rx_buf = &val, + .len = 2, + }; + struct spi_message m; + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + ret = spi_sync(spi, &m); + + if (ret < 0) + return ret; + + return be16_to_cpu(val) & AD5504_RES_MASK; +} + +static int ad5504_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad5504_state *st = iio_priv(indio_dev); + unsigned long scale_uv; + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = ad5504_spi_read(st->spi, chan->address); + if (ret < 0) + return ret; + + *val = ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; + *val = scale_uv / 1000; + *val2 = (scale_uv % 1000) * 1000; + return IIO_VAL_INT_PLUS_MICRO; + + } + return -EINVAL; +} + +static int ad5504_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad5504_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val >= (1 << chan->scan_type.realbits) || val < 0) + return -EINVAL; + + return ad5504_spi_write(st->spi, chan->address, val); + default: + ret = -EINVAL; + } + + return -EINVAL; +} + +static const char * const ad5504_powerdown_modes[] = { + "20kohm_to_gnd", + "three_state", +}; + +static int ad5504_get_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5504_state *st = iio_priv(indio_dev); + + return st->pwr_down_mode; +} + +static int ad5504_set_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int mode) +{ + struct ad5504_state *st = iio_priv(indio_dev); + + st->pwr_down_mode = mode; + + return 0; +} + +static const struct iio_enum ad5504_powerdown_mode_enum = { + .items = ad5504_powerdown_modes, + .num_items = ARRAY_SIZE(ad5504_powerdown_modes), + .get = ad5504_get_powerdown_mode, + .set = ad5504_set_powerdown_mode, +}; + +static ssize_t ad5504_read_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, char *buf) +{ + struct ad5504_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", + !(st->pwr_down_mask & (1 << chan->channel))); +} + +static ssize_t ad5504_write_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, const char *buf, + size_t len) +{ + bool pwr_down; + int ret; + struct ad5504_state *st = iio_priv(indio_dev); + + ret = strtobool(buf, &pwr_down); + if (ret) + return ret; + + if (pwr_down) + st->pwr_down_mask |= (1 << chan->channel); + else + st->pwr_down_mask &= ~(1 << chan->channel); + + ret = ad5504_spi_write(st->spi, AD5504_ADDR_CTRL, + AD5504_DAC_PWRDWN_MODE(st->pwr_down_mode) | + AD5504_DAC_PWR(st->pwr_down_mask)); + + /* writes to the CTRL register must be followed by a NOOP */ + ad5504_spi_write(st->spi, AD5504_ADDR_NOOP, 0); + + return ret ? ret : len; +} + +static IIO_CONST_ATTR(temp0_thresh_rising_value, "110000"); +static IIO_CONST_ATTR(temp0_thresh_rising_en, "1"); + +static struct attribute *ad5504_ev_attributes[] = { + &iio_const_attr_temp0_thresh_rising_value.dev_attr.attr, + &iio_const_attr_temp0_thresh_rising_en.dev_attr.attr, + NULL, +}; + +static struct attribute_group ad5504_ev_attribute_group = { + .attrs = ad5504_ev_attributes, + .name = "events", +}; + +static irqreturn_t ad5504_event_handler(int irq, void *private) +{ + iio_push_event(private, + IIO_UNMOD_EVENT_CODE(IIO_TEMP, + 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + iio_get_time_ns()); + + return IRQ_HANDLED; +} + +static const struct iio_info ad5504_info = { + .write_raw = ad5504_write_raw, + .read_raw = ad5504_read_raw, + .event_attrs = &ad5504_ev_attribute_group, + .driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec_ext_info ad5504_ext_info[] = { + { + .name = "powerdown", + .read = ad5504_read_dac_powerdown, + .write = ad5504_write_dac_powerdown, + }, + IIO_ENUM("powerdown_mode", true, &ad5504_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", &ad5504_powerdown_mode_enum), + { }, +}; + +#define AD5504_CHANNEL(_chan) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = (_chan), \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SHARED_BIT, \ + .address = AD5504_ADDR_DAC(_chan), \ + .scan_type = IIO_ST('u', 12, 16, 0), \ + .ext_info = ad5504_ext_info, \ +} + +static const struct iio_chan_spec ad5504_channels[] = { + AD5504_CHANNEL(0), + AD5504_CHANNEL(1), + AD5504_CHANNEL(2), + AD5504_CHANNEL(3), +}; + +static int __devinit ad5504_probe(struct spi_device *spi) +{ + struct ad5504_platform_data *pdata = spi->dev.platform_data; + struct iio_dev *indio_dev; + struct ad5504_state *st; + struct regulator *reg; + int ret, voltage_uv = 0; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) { + ret = -ENOMEM; + goto error_ret; + } + reg = regulator_get(&spi->dev, "vcc"); + if (!IS_ERR(reg)) { + ret = regulator_enable(reg); + if (ret) + goto error_put_reg; + + voltage_uv = regulator_get_voltage(reg); + } + + spi_set_drvdata(spi, indio_dev); + st = iio_priv(indio_dev); + if (voltage_uv) + st->vref_mv = voltage_uv / 1000; + else if (pdata) + st->vref_mv = pdata->vref_mv; + else + dev_warn(&spi->dev, "reference voltage unspecified\n"); + + st->reg = reg; + st->spi = spi; + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(st->spi)->name; + indio_dev->info = &ad5504_info; + if (spi_get_device_id(st->spi)->driver_data == ID_AD5501) + indio_dev->num_channels = 1; + else + indio_dev->num_channels = 4; + indio_dev->channels = ad5504_channels; + indio_dev->modes = INDIO_DIRECT_MODE; + + if (spi->irq) { + ret = request_threaded_irq(spi->irq, + NULL, + &ad5504_event_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + spi_get_device_id(st->spi)->name, + indio_dev); + if (ret) + goto error_disable_reg; + } + + ret = iio_device_register(indio_dev); + if (ret) + goto error_free_irq; + + return 0; + +error_free_irq: + if (spi->irq) + free_irq(spi->irq, indio_dev); +error_disable_reg: + if (!IS_ERR(reg)) + regulator_disable(reg); +error_put_reg: + if (!IS_ERR(reg)) + regulator_put(reg); + + iio_device_free(indio_dev); +error_ret: + return ret; +} + +static int __devexit ad5504_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad5504_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + if (spi->irq) + free_irq(spi->irq, indio_dev); + + if (!IS_ERR(st->reg)) { + regulator_disable(st->reg); + regulator_put(st->reg); + } + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad5504_id[] = { + {"ad5504", ID_AD5504}, + {"ad5501", ID_AD5501}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad5504_id); + +static struct spi_driver ad5504_driver = { + .driver = { + .name = "ad5504", + .owner = THIS_MODULE, + }, + .probe = ad5504_probe, + .remove = __devexit_p(ad5504_remove), + .id_table = ad5504_id, +}; +module_spi_driver(ad5504_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD5501/AD5501 DAC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5624r.h b/drivers/iio/dac/ad5624r.h new file mode 100644 index 000000000000..5dca3028cdfd --- /dev/null +++ b/drivers/iio/dac/ad5624r.h @@ -0,0 +1,79 @@ +/* + * AD5624R SPI DAC driver + * + * Copyright 2010-2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ +#ifndef SPI_AD5624R_H_ +#define SPI_AD5624R_H_ + +#define AD5624R_DAC_CHANNELS 4 + +#define AD5624R_ADDR_DAC0 0x0 +#define AD5624R_ADDR_DAC1 0x1 +#define AD5624R_ADDR_DAC2 0x2 +#define AD5624R_ADDR_DAC3 0x3 +#define AD5624R_ADDR_ALL_DAC 0x7 + +#define AD5624R_CMD_WRITE_INPUT_N 0x0 +#define AD5624R_CMD_UPDATE_DAC_N 0x1 +#define AD5624R_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2 +#define AD5624R_CMD_WRITE_INPUT_N_UPDATE_N 0x3 +#define AD5624R_CMD_POWERDOWN_DAC 0x4 +#define AD5624R_CMD_RESET 0x5 +#define AD5624R_CMD_LDAC_SETUP 0x6 +#define AD5624R_CMD_INTERNAL_REFER_SETUP 0x7 + +#define AD5624R_LDAC_PWRDN_NONE 0x0 +#define AD5624R_LDAC_PWRDN_1K 0x1 +#define AD5624R_LDAC_PWRDN_100K 0x2 +#define AD5624R_LDAC_PWRDN_3STATE 0x3 + +/** + * struct ad5624r_chip_info - chip specific information + * @channels: channel spec for the DAC + * @int_vref_mv: AD5620/40/60: the internal reference voltage + */ + +struct ad5624r_chip_info { + const struct iio_chan_spec *channels; + u16 int_vref_mv; +}; + +/** + * struct ad5446_state - driver instance specific data + * @indio_dev: the industrial I/O device + * @us: spi_device + * @chip_info: chip model specific constants, available modes etc + * @reg: supply regulator + * @vref_mv: actual reference voltage used + * @pwr_down_mask power down mask + * @pwr_down_mode current power down mode + */ + +struct ad5624r_state { + struct spi_device *us; + const struct ad5624r_chip_info *chip_info; + struct regulator *reg; + unsigned short vref_mv; + unsigned pwr_down_mask; + unsigned pwr_down_mode; +}; + +/** + * ad5624r_supported_device_ids: + * The AD5624/44/64 parts are available in different + * fixed internal reference voltage options. + */ + +enum ad5624r_supported_device_ids { + ID_AD5624R3, + ID_AD5644R3, + ID_AD5664R3, + ID_AD5624R5, + ID_AD5644R5, + ID_AD5664R5, +}; + +#endif /* SPI_AD5624R_H_ */ diff --git a/drivers/iio/dac/ad5624r_spi.c b/drivers/iio/dac/ad5624r_spi.c new file mode 100644 index 000000000000..6a7d6a48cc6d --- /dev/null +++ b/drivers/iio/dac/ad5624r_spi.c @@ -0,0 +1,324 @@ +/* + * AD5624R, AD5644R, AD5664R Digital to analog convertors spi driver + * + * Copyright 2010-2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ad5624r.h" + +static int ad5624r_spi_write(struct spi_device *spi, + u8 cmd, u8 addr, u16 val, u8 len) +{ + u32 data; + u8 msg[3]; + + /* + * The input shift register is 24 bits wide. The first two bits are + * don't care bits. The next three are the command bits, C2 to C0, + * followed by the 3-bit DAC address, A2 to A0, and then the + * 16-, 14-, 12-bit data-word. The data-word comprises the 16-, + * 14-, 12-bit input code followed by 0, 2, or 4 don't care bits, + * for the AD5664R, AD5644R, and AD5624R, respectively. + */ + data = (0 << 22) | (cmd << 19) | (addr << 16) | (val << (16 - len)); + msg[0] = data >> 16; + msg[1] = data >> 8; + msg[2] = data; + + return spi_write(spi, msg, 3); +} + +static int ad5624r_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad5624r_state *st = iio_priv(indio_dev); + unsigned long scale_uv; + + switch (m) { + case IIO_CHAN_INFO_SCALE: + scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; + *val = scale_uv / 1000; + *val2 = (scale_uv % 1000) * 1000; + return IIO_VAL_INT_PLUS_MICRO; + + } + return -EINVAL; +} + +static int ad5624r_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad5624r_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val >= (1 << chan->scan_type.realbits) || val < 0) + return -EINVAL; + + return ad5624r_spi_write(st->us, + AD5624R_CMD_WRITE_INPUT_N_UPDATE_N, + chan->address, val, + chan->scan_type.shift); + default: + ret = -EINVAL; + } + + return -EINVAL; +} + +static const char * const ad5624r_powerdown_modes[] = { + "1kohm_to_gnd", + "100kohm_to_gnd", + "three_state" +}; + +static int ad5624r_get_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5624r_state *st = iio_priv(indio_dev); + + return st->pwr_down_mode; +} + +static int ad5624r_set_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int mode) +{ + struct ad5624r_state *st = iio_priv(indio_dev); + + st->pwr_down_mode = mode; + + return 0; +} + +static const struct iio_enum ad5624r_powerdown_mode_enum = { + .items = ad5624r_powerdown_modes, + .num_items = ARRAY_SIZE(ad5624r_powerdown_modes), + .get = ad5624r_get_powerdown_mode, + .set = ad5624r_set_powerdown_mode, +}; + +static ssize_t ad5624r_read_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, char *buf) +{ + struct ad5624r_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", + !!(st->pwr_down_mask & (1 << chan->channel))); +} + +static ssize_t ad5624r_write_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, const char *buf, + size_t len) +{ + bool pwr_down; + int ret; + struct ad5624r_state *st = iio_priv(indio_dev); + + ret = strtobool(buf, &pwr_down); + if (ret) + return ret; + + if (pwr_down) + st->pwr_down_mask |= (1 << chan->channel); + else + st->pwr_down_mask &= ~(1 << chan->channel); + + ret = ad5624r_spi_write(st->us, AD5624R_CMD_POWERDOWN_DAC, 0, + (st->pwr_down_mode << 4) | + st->pwr_down_mask, 16); + + return ret ? ret : len; +} + +static const struct iio_info ad5624r_info = { + .write_raw = ad5624r_write_raw, + .read_raw = ad5624r_read_raw, + .driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec_ext_info ad5624r_ext_info[] = { + { + .name = "powerdown", + .read = ad5624r_read_dac_powerdown, + .write = ad5624r_write_dac_powerdown, + }, + IIO_ENUM("powerdown_mode", true, &ad5624r_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", &ad5624r_powerdown_mode_enum), + { }, +}; + +#define AD5624R_CHANNEL(_chan, _bits) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = (_chan), \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SHARED_BIT, \ + .address = (_chan), \ + .scan_type = IIO_ST('u', (_bits), 16, 16 - (_bits)), \ + .ext_info = ad5624r_ext_info, \ +} + +#define DECLARE_AD5624R_CHANNELS(_name, _bits) \ + const struct iio_chan_spec _name##_channels[] = { \ + AD5624R_CHANNEL(0, _bits), \ + AD5624R_CHANNEL(1, _bits), \ + AD5624R_CHANNEL(2, _bits), \ + AD5624R_CHANNEL(3, _bits), \ +} + +static DECLARE_AD5624R_CHANNELS(ad5624r, 12); +static DECLARE_AD5624R_CHANNELS(ad5644r, 14); +static DECLARE_AD5624R_CHANNELS(ad5664r, 16); + +static const struct ad5624r_chip_info ad5624r_chip_info_tbl[] = { + [ID_AD5624R3] = { + .channels = ad5624r_channels, + .int_vref_mv = 1250, + }, + [ID_AD5624R5] = { + .channels = ad5624r_channels, + .int_vref_mv = 2500, + }, + [ID_AD5644R3] = { + .channels = ad5644r_channels, + .int_vref_mv = 1250, + }, + [ID_AD5644R5] = { + .channels = ad5644r_channels, + .int_vref_mv = 2500, + }, + [ID_AD5664R3] = { + .channels = ad5664r_channels, + .int_vref_mv = 1250, + }, + [ID_AD5664R5] = { + .channels = ad5664r_channels, + .int_vref_mv = 2500, + }, +}; + +static int __devinit ad5624r_probe(struct spi_device *spi) +{ + struct ad5624r_state *st; + struct iio_dev *indio_dev; + int ret, voltage_uv = 0; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) { + ret = -ENOMEM; + goto error_ret; + } + st = iio_priv(indio_dev); + st->reg = regulator_get(&spi->dev, "vcc"); + if (!IS_ERR(st->reg)) { + ret = regulator_enable(st->reg); + if (ret) + goto error_put_reg; + + voltage_uv = regulator_get_voltage(st->reg); + } + + spi_set_drvdata(spi, indio_dev); + st->chip_info = + &ad5624r_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + + if (voltage_uv) + st->vref_mv = voltage_uv / 1000; + else + st->vref_mv = st->chip_info->int_vref_mv; + + st->us = spi; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &ad5624r_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = AD5624R_DAC_CHANNELS; + + ret = ad5624r_spi_write(spi, AD5624R_CMD_INTERNAL_REFER_SETUP, 0, + !!voltage_uv, 16); + if (ret) + goto error_disable_reg; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_disable_reg; + + return 0; + +error_disable_reg: + if (!IS_ERR(st->reg)) + regulator_disable(st->reg); +error_put_reg: + if (!IS_ERR(st->reg)) + regulator_put(st->reg); + iio_device_free(indio_dev); +error_ret: + + return ret; +} + +static int __devexit ad5624r_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad5624r_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + if (!IS_ERR(st->reg)) { + regulator_disable(st->reg); + regulator_put(st->reg); + } + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad5624r_id[] = { + {"ad5624r3", ID_AD5624R3}, + {"ad5644r3", ID_AD5644R3}, + {"ad5664r3", ID_AD5664R3}, + {"ad5624r5", ID_AD5624R5}, + {"ad5644r5", ID_AD5644R5}, + {"ad5664r5", ID_AD5664R5}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad5624r_id); + +static struct spi_driver ad5624r_driver = { + .driver = { + .name = "ad5624r", + .owner = THIS_MODULE, + }, + .probe = ad5624r_probe, + .remove = __devexit_p(ad5624r_remove), + .id_table = ad5624r_id, +}; +module_spi_driver(ad5624r_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices AD5624/44/64R DAC spi driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c new file mode 100644 index 000000000000..6948d75e1036 --- /dev/null +++ b/drivers/iio/dac/ad5686.c @@ -0,0 +1,418 @@ +/* + * AD5686R, AD5685R, AD5684R Digital to analog converters driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define AD5686_DAC_CHANNELS 4 + +#define AD5686_ADDR(x) ((x) << 16) +#define AD5686_CMD(x) ((x) << 20) + +#define AD5686_ADDR_DAC(chan) (0x1 << (chan)) +#define AD5686_ADDR_ALL_DAC 0xF + +#define AD5686_CMD_NOOP 0x0 +#define AD5686_CMD_WRITE_INPUT_N 0x1 +#define AD5686_CMD_UPDATE_DAC_N 0x2 +#define AD5686_CMD_WRITE_INPUT_N_UPDATE_N 0x3 +#define AD5686_CMD_POWERDOWN_DAC 0x4 +#define AD5686_CMD_LDAC_MASK 0x5 +#define AD5686_CMD_RESET 0x6 +#define AD5686_CMD_INTERNAL_REFER_SETUP 0x7 +#define AD5686_CMD_DAISY_CHAIN_ENABLE 0x8 +#define AD5686_CMD_READBACK_ENABLE 0x9 + +#define AD5686_LDAC_PWRDN_NONE 0x0 +#define AD5686_LDAC_PWRDN_1K 0x1 +#define AD5686_LDAC_PWRDN_100K 0x2 +#define AD5686_LDAC_PWRDN_3STATE 0x3 + +/** + * struct ad5686_chip_info - chip specific information + * @int_vref_mv: AD5620/40/60: the internal reference voltage + * @channel: channel specification +*/ + +struct ad5686_chip_info { + u16 int_vref_mv; + struct iio_chan_spec channel[AD5686_DAC_CHANNELS]; +}; + +/** + * struct ad5446_state - driver instance specific data + * @spi: spi_device + * @chip_info: chip model specific constants, available modes etc + * @reg: supply regulator + * @vref_mv: actual reference voltage used + * @pwr_down_mask: power down mask + * @pwr_down_mode: current power down mode + * @data: spi transfer buffers + */ + +struct ad5686_state { + struct spi_device *spi; + const struct ad5686_chip_info *chip_info; + struct regulator *reg; + unsigned short vref_mv; + unsigned pwr_down_mask; + unsigned pwr_down_mode; + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + + union { + u32 d32; + u8 d8[4]; + } data[3] ____cacheline_aligned; +}; + +/** + * ad5686_supported_device_ids: + */ + +enum ad5686_supported_device_ids { + ID_AD5684, + ID_AD5685, + ID_AD5686, +}; +static int ad5686_spi_write(struct ad5686_state *st, + u8 cmd, u8 addr, u16 val, u8 shift) +{ + val <<= shift; + + st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | + AD5686_ADDR(addr) | + val); + + return spi_write(st->spi, &st->data[0].d8[1], 3); +} + +static int ad5686_spi_read(struct ad5686_state *st, u8 addr) +{ + struct spi_transfer t[] = { + { + .tx_buf = &st->data[0].d8[1], + .len = 3, + .cs_change = 1, + }, { + .tx_buf = &st->data[1].d8[1], + .rx_buf = &st->data[2].d8[1], + .len = 3, + }, + }; + struct spi_message m; + int ret; + + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + st->data[0].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_READBACK_ENABLE) | + AD5686_ADDR(addr)); + st->data[1].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP)); + + ret = spi_sync(st->spi, &m); + if (ret < 0) + return ret; + + return be32_to_cpu(st->data[2].d32); +} + +static const char * const ad5686_powerdown_modes[] = { + "1kohm_to_gnd", + "100kohm_to_gnd", + "three_state" +}; + +static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5686_state *st = iio_priv(indio_dev); + + return ((st->pwr_down_mode >> (chan->channel * 2)) & 0x3) - 1; +} + +static int ad5686_set_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int mode) +{ + struct ad5686_state *st = iio_priv(indio_dev); + + st->pwr_down_mode &= ~(0x3 << (chan->channel * 2)); + st->pwr_down_mode |= ((mode + 1) << (chan->channel * 2)); + + return 0; +} + +static const struct iio_enum ad5686_powerdown_mode_enum = { + .items = ad5686_powerdown_modes, + .num_items = ARRAY_SIZE(ad5686_powerdown_modes), + .get = ad5686_get_powerdown_mode, + .set = ad5686_set_powerdown_mode, +}; + +static ssize_t ad5686_read_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, char *buf) +{ + struct ad5686_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", !!(st->pwr_down_mask & + (0x3 << (chan->channel * 2)))); +} + +static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, const char *buf, + size_t len) +{ + bool readin; + int ret; + struct ad5686_state *st = iio_priv(indio_dev); + + ret = strtobool(buf, &readin); + if (ret) + return ret; + + if (readin == true) + st->pwr_down_mask |= (0x3 << (chan->channel * 2)); + else + st->pwr_down_mask &= ~(0x3 << (chan->channel * 2)); + + ret = ad5686_spi_write(st, AD5686_CMD_POWERDOWN_DAC, 0, + st->pwr_down_mask & st->pwr_down_mode, 0); + + return ret ? ret : len; +} + +static int ad5686_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad5686_state *st = iio_priv(indio_dev); + unsigned long scale_uv; + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&indio_dev->mlock); + ret = ad5686_spi_read(st, chan->address); + mutex_unlock(&indio_dev->mlock); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SCALE: + scale_uv = (st->vref_mv * 100000) + >> (chan->scan_type.realbits); + *val = scale_uv / 100000; + *val2 = (scale_uv % 100000) * 10; + return IIO_VAL_INT_PLUS_MICRO; + + } + return -EINVAL; +} + +static int ad5686_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad5686_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val > (1 << chan->scan_type.realbits) || val < 0) + return -EINVAL; + + mutex_lock(&indio_dev->mlock); + ret = ad5686_spi_write(st, + AD5686_CMD_WRITE_INPUT_N_UPDATE_N, + chan->address, + val, + chan->scan_type.shift); + mutex_unlock(&indio_dev->mlock); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct iio_info ad5686_info = { + .read_raw = ad5686_read_raw, + .write_raw = ad5686_write_raw, + .driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec_ext_info ad5686_ext_info[] = { + { + .name = "powerdown", + .read = ad5686_read_dac_powerdown, + .write = ad5686_write_dac_powerdown, + }, + IIO_ENUM("powerdown_mode", false, &ad5686_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", &ad5686_powerdown_mode_enum), + { }, +}; + +#define AD5868_CHANNEL(chan, bits, shift) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = chan, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SHARED_BIT, \ + .address = AD5686_ADDR_DAC(chan), \ + .scan_type = IIO_ST('u', bits, 16, shift), \ + .ext_info = ad5686_ext_info, \ +} + +static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { + [ID_AD5684] = { + .channel[0] = AD5868_CHANNEL(0, 12, 4), + .channel[1] = AD5868_CHANNEL(1, 12, 4), + .channel[2] = AD5868_CHANNEL(2, 12, 4), + .channel[3] = AD5868_CHANNEL(3, 12, 4), + .int_vref_mv = 2500, + }, + [ID_AD5685] = { + .channel[0] = AD5868_CHANNEL(0, 14, 2), + .channel[1] = AD5868_CHANNEL(1, 14, 2), + .channel[2] = AD5868_CHANNEL(2, 14, 2), + .channel[3] = AD5868_CHANNEL(3, 14, 2), + .int_vref_mv = 2500, + }, + [ID_AD5686] = { + .channel[0] = AD5868_CHANNEL(0, 16, 0), + .channel[1] = AD5868_CHANNEL(1, 16, 0), + .channel[2] = AD5868_CHANNEL(2, 16, 0), + .channel[3] = AD5868_CHANNEL(3, 16, 0), + .int_vref_mv = 2500, + }, +}; + + +static int __devinit ad5686_probe(struct spi_device *spi) +{ + struct ad5686_state *st; + struct iio_dev *indio_dev; + int ret, regdone = 0, voltage_uv = 0; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + + st->reg = regulator_get(&spi->dev, "vcc"); + if (!IS_ERR(st->reg)) { + ret = regulator_enable(st->reg); + if (ret) + goto error_put_reg; + + voltage_uv = regulator_get_voltage(st->reg); + } + + st->chip_info = + &ad5686_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + + if (voltage_uv) + st->vref_mv = voltage_uv / 1000; + else + st->vref_mv = st->chip_info->int_vref_mv; + + st->spi = spi; + + /* Set all the power down mode for all channels to 1K pulldown */ + st->pwr_down_mode = 0x55; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &ad5686_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->chip_info->channel; + indio_dev->num_channels = AD5686_DAC_CHANNELS; + + regdone = 1; + ret = ad5686_spi_write(st, AD5686_CMD_INTERNAL_REFER_SETUP, 0, + !!voltage_uv, 0); + if (ret) + goto error_disable_reg; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_disable_reg; + + return 0; + +error_disable_reg: + if (!IS_ERR(st->reg)) + regulator_disable(st->reg); +error_put_reg: + if (!IS_ERR(st->reg)) + regulator_put(st->reg); + + iio_device_free(indio_dev); + + return ret; +} + +static int __devexit ad5686_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad5686_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + if (!IS_ERR(st->reg)) { + regulator_disable(st->reg); + regulator_put(st->reg); + } + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad5686_id[] = { + {"ad5684", ID_AD5684}, + {"ad5685", ID_AD5685}, + {"ad5686", ID_AD5686}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad5686_id); + +static struct spi_driver ad5686_driver = { + .driver = { + .name = "ad5686", + .owner = THIS_MODULE, + }, + .probe = ad5686_probe, + .remove = __devexit_p(ad5686_remove), + .id_table = ad5686_id, +}; +module_spi_driver(ad5686_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD5686/85/84 DAC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5764.c b/drivers/iio/dac/ad5764.c new file mode 100644 index 000000000000..ffce30447445 --- /dev/null +++ b/drivers/iio/dac/ad5764.c @@ -0,0 +1,382 @@ +/* + * Analog devices AD5764, AD5764R, AD5744, AD5744R quad-channel + * Digital to Analog Converters driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define AD5764_REG_SF_NOP 0x0 +#define AD5764_REG_SF_CONFIG 0x1 +#define AD5764_REG_SF_CLEAR 0x4 +#define AD5764_REG_SF_LOAD 0x5 +#define AD5764_REG_DATA(x) ((2 << 3) | (x)) +#define AD5764_REG_COARSE_GAIN(x) ((3 << 3) | (x)) +#define AD5764_REG_FINE_GAIN(x) ((4 << 3) | (x)) +#define AD5764_REG_OFFSET(x) ((5 << 3) | (x)) + +#define AD5764_NUM_CHANNELS 4 + +/** + * struct ad5764_chip_info - chip specific information + * @int_vref: Value of the internal reference voltage in uV - 0 if external + * reference voltage is used + * @channel channel specification +*/ + +struct ad5764_chip_info { + unsigned long int_vref; + const struct iio_chan_spec *channels; +}; + +/** + * struct ad5764_state - driver instance specific data + * @spi: spi_device + * @chip_info: chip info + * @vref_reg: vref supply regulators + * @data: spi transfer buffers + */ + +struct ad5764_state { + struct spi_device *spi; + const struct ad5764_chip_info *chip_info; + struct regulator_bulk_data vref_reg[2]; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + union { + __be32 d32; + u8 d8[4]; + } data[2] ____cacheline_aligned; +}; + +enum ad5764_type { + ID_AD5744, + ID_AD5744R, + ID_AD5764, + ID_AD5764R, +}; + +#define AD5764_CHANNEL(_chan, _bits) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = (_chan), \ + .address = (_chan), \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_OFFSET_SHARED_BIT | \ + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \ + IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | \ + IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, \ + .scan_type = IIO_ST('u', (_bits), 16, 16 - (_bits)) \ +} + +#define DECLARE_AD5764_CHANNELS(_name, _bits) \ +const struct iio_chan_spec _name##_channels[] = { \ + AD5764_CHANNEL(0, (_bits)), \ + AD5764_CHANNEL(1, (_bits)), \ + AD5764_CHANNEL(2, (_bits)), \ + AD5764_CHANNEL(3, (_bits)), \ +}; + +static DECLARE_AD5764_CHANNELS(ad5764, 16); +static DECLARE_AD5764_CHANNELS(ad5744, 14); + +static const struct ad5764_chip_info ad5764_chip_infos[] = { + [ID_AD5744] = { + .int_vref = 0, + .channels = ad5744_channels, + }, + [ID_AD5744R] = { + .int_vref = 5000000, + .channels = ad5744_channels, + }, + [ID_AD5764] = { + .int_vref = 0, + .channels = ad5764_channels, + }, + [ID_AD5764R] = { + .int_vref = 5000000, + .channels = ad5764_channels, + }, +}; + +static int ad5764_write(struct iio_dev *indio_dev, unsigned int reg, + unsigned int val) +{ + struct ad5764_state *st = iio_priv(indio_dev); + int ret; + + mutex_lock(&indio_dev->mlock); + st->data[0].d32 = cpu_to_be32((reg << 16) | val); + + ret = spi_write(st->spi, &st->data[0].d8[1], 3); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int ad5764_read(struct iio_dev *indio_dev, unsigned int reg, + unsigned int *val) +{ + struct ad5764_state *st = iio_priv(indio_dev); + struct spi_message m; + int ret; + struct spi_transfer t[] = { + { + .tx_buf = &st->data[0].d8[1], + .len = 3, + .cs_change = 1, + }, { + .rx_buf = &st->data[1].d8[1], + .len = 3, + }, + }; + + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + mutex_lock(&indio_dev->mlock); + + st->data[0].d32 = cpu_to_be32((1 << 23) | (reg << 16)); + + ret = spi_sync(st->spi, &m); + if (ret >= 0) + *val = be32_to_cpu(st->data[1].d32) & 0xffff; + + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int ad5764_chan_info_to_reg(struct iio_chan_spec const *chan, long info) +{ + switch (info) { + case 0: + return AD5764_REG_DATA(chan->address); + case IIO_CHAN_INFO_CALIBBIAS: + return AD5764_REG_OFFSET(chan->address); + case IIO_CHAN_INFO_CALIBSCALE: + return AD5764_REG_FINE_GAIN(chan->address); + default: + break; + } + + return 0; +} + +static int ad5764_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long info) +{ + const int max_val = (1 << chan->scan_type.realbits); + unsigned int reg; + + switch (info) { + case IIO_CHAN_INFO_RAW: + if (val >= max_val || val < 0) + return -EINVAL; + val <<= chan->scan_type.shift; + break; + case IIO_CHAN_INFO_CALIBBIAS: + if (val >= 128 || val < -128) + return -EINVAL; + break; + case IIO_CHAN_INFO_CALIBSCALE: + if (val >= 32 || val < -32) + return -EINVAL; + break; + default: + return -EINVAL; + } + + reg = ad5764_chan_info_to_reg(chan, info); + return ad5764_write(indio_dev, reg, (u16)val); +} + +static int ad5764_get_channel_vref(struct ad5764_state *st, + unsigned int channel) +{ + if (st->chip_info->int_vref) + return st->chip_info->int_vref; + else + return regulator_get_voltage(st->vref_reg[channel / 2].consumer); +} + +static int ad5764_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long info) +{ + struct ad5764_state *st = iio_priv(indio_dev); + unsigned long scale_uv; + unsigned int reg; + int vref; + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + reg = AD5764_REG_DATA(chan->address); + ret = ad5764_read(indio_dev, reg, val); + if (ret < 0) + return ret; + *val >>= chan->scan_type.shift; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + reg = AD5764_REG_OFFSET(chan->address); + ret = ad5764_read(indio_dev, reg, val); + if (ret < 0) + return ret; + *val = sign_extend32(*val, 7); + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBSCALE: + reg = AD5764_REG_FINE_GAIN(chan->address); + ret = ad5764_read(indio_dev, reg, val); + if (ret < 0) + return ret; + *val = sign_extend32(*val, 5); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* vout = 4 * vref + ((dac_code / 65535) - 0.5) */ + vref = ad5764_get_channel_vref(st, chan->channel); + if (vref < 0) + return vref; + + scale_uv = (vref * 4 * 100) >> chan->scan_type.realbits; + *val = scale_uv / 100000; + *val2 = (scale_uv % 100000) * 10; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OFFSET: + *val = -(1 << chan->scan_type.realbits) / 2; + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static const struct iio_info ad5764_info = { + .read_raw = ad5764_read_raw, + .write_raw = ad5764_write_raw, + .driver_module = THIS_MODULE, +}; + +static int __devinit ad5764_probe(struct spi_device *spi) +{ + enum ad5764_type type = spi_get_device_id(spi)->driver_data; + struct iio_dev *indio_dev; + struct ad5764_state *st; + int ret; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) { + dev_err(&spi->dev, "Failed to allocate iio device\n"); + return -ENOMEM; + } + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + + st->spi = spi; + st->chip_info = &ad5764_chip_infos[type]; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &ad5764_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->num_channels = AD5764_NUM_CHANNELS; + indio_dev->channels = st->chip_info->channels; + + if (st->chip_info->int_vref == 0) { + st->vref_reg[0].supply = "vrefAB"; + st->vref_reg[1].supply = "vrefCD"; + + ret = regulator_bulk_get(&st->spi->dev, + ARRAY_SIZE(st->vref_reg), st->vref_reg); + if (ret) { + dev_err(&spi->dev, "Failed to request vref regulators: %d\n", + ret); + goto error_free; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(st->vref_reg), + st->vref_reg); + if (ret) { + dev_err(&spi->dev, "Failed to enable vref regulators: %d\n", + ret); + goto error_free_reg; + } + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&spi->dev, "Failed to register iio device: %d\n", ret); + goto error_disable_reg; + } + + return 0; + +error_disable_reg: + if (st->chip_info->int_vref == 0) + regulator_bulk_disable(ARRAY_SIZE(st->vref_reg), st->vref_reg); +error_free_reg: + if (st->chip_info->int_vref == 0) + regulator_bulk_free(ARRAY_SIZE(st->vref_reg), st->vref_reg); +error_free: + iio_device_free(indio_dev); + + return ret; +} + +static int __devexit ad5764_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad5764_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + if (st->chip_info->int_vref == 0) { + regulator_bulk_disable(ARRAY_SIZE(st->vref_reg), st->vref_reg); + regulator_bulk_free(ARRAY_SIZE(st->vref_reg), st->vref_reg); + } + + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad5764_ids[] = { + { "ad5744", ID_AD5744 }, + { "ad5744r", ID_AD5744R }, + { "ad5764", ID_AD5764 }, + { "ad5764r", ID_AD5764R }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad5764_ids); + +static struct spi_driver ad5764_driver = { + .driver = { + .name = "ad5764", + .owner = THIS_MODULE, + }, + .probe = ad5764_probe, + .remove = __devexit_p(ad5764_remove), + .id_table = ad5764_ids, +}; +module_spi_driver(ad5764_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Analog Devices AD5744/AD5744R/AD5764/AD5764R DAC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c new file mode 100644 index 000000000000..2bd2e37280ff --- /dev/null +++ b/drivers/iio/dac/ad5791.c @@ -0,0 +1,485 @@ +/* + * AD5760, AD5780, AD5781, AD5790, AD5791 Voltage Output Digital to Analog + * Converter + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define AD5791_RES_MASK(x) ((1 << (x)) - 1) +#define AD5791_DAC_MASK AD5791_RES_MASK(20) +#define AD5791_DAC_MSB (1 << 19) + +#define AD5791_CMD_READ (1 << 23) +#define AD5791_CMD_WRITE (0 << 23) +#define AD5791_ADDR(addr) ((addr) << 20) + +/* Registers */ +#define AD5791_ADDR_NOOP 0 +#define AD5791_ADDR_DAC0 1 +#define AD5791_ADDR_CTRL 2 +#define AD5791_ADDR_CLRCODE 3 +#define AD5791_ADDR_SW_CTRL 4 + +/* Control Register */ +#define AD5791_CTRL_RBUF (1 << 1) +#define AD5791_CTRL_OPGND (1 << 2) +#define AD5791_CTRL_DACTRI (1 << 3) +#define AD5791_CTRL_BIN2SC (1 << 4) +#define AD5791_CTRL_SDODIS (1 << 5) +#define AD5761_CTRL_LINCOMP(x) ((x) << 6) + +#define AD5791_LINCOMP_0_10 0 +#define AD5791_LINCOMP_10_12 1 +#define AD5791_LINCOMP_12_16 2 +#define AD5791_LINCOMP_16_19 3 +#define AD5791_LINCOMP_19_20 12 + +#define AD5780_LINCOMP_0_10 0 +#define AD5780_LINCOMP_10_20 12 + +/* Software Control Register */ +#define AD5791_SWCTRL_LDAC (1 << 0) +#define AD5791_SWCTRL_CLR (1 << 1) +#define AD5791_SWCTRL_RESET (1 << 2) + +#define AD5791_DAC_PWRDN_6K 0 +#define AD5791_DAC_PWRDN_3STATE 1 + +/** + * struct ad5791_chip_info - chip specific information + * @get_lin_comp: function pointer to the device specific function + */ + +struct ad5791_chip_info { + int (*get_lin_comp) (unsigned int span); +}; + +/** + * struct ad5791_state - driver instance specific data + * @us: spi_device + * @reg_vdd: positive supply regulator + * @reg_vss: negative supply regulator + * @chip_info: chip model specific constants + * @vref_mv: actual reference voltage used + * @vref_neg_mv: voltage of the negative supply + * @pwr_down_mode current power down mode + */ + +struct ad5791_state { + struct spi_device *spi; + struct regulator *reg_vdd; + struct regulator *reg_vss; + const struct ad5791_chip_info *chip_info; + unsigned short vref_mv; + unsigned int vref_neg_mv; + unsigned ctrl; + unsigned pwr_down_mode; + bool pwr_down; +}; + +/** + * ad5791_supported_device_ids: + */ + +enum ad5791_supported_device_ids { + ID_AD5760, + ID_AD5780, + ID_AD5781, + ID_AD5791, +}; + +static int ad5791_spi_write(struct spi_device *spi, u8 addr, u32 val) +{ + union { + u32 d32; + u8 d8[4]; + } data; + + data.d32 = cpu_to_be32(AD5791_CMD_WRITE | + AD5791_ADDR(addr) | + (val & AD5791_DAC_MASK)); + + return spi_write(spi, &data.d8[1], 3); +} + +static int ad5791_spi_read(struct spi_device *spi, u8 addr, u32 *val) +{ + union { + u32 d32; + u8 d8[4]; + } data[3]; + int ret; + struct spi_message msg; + struct spi_transfer xfers[] = { + { + .tx_buf = &data[0].d8[1], + .bits_per_word = 8, + .len = 3, + .cs_change = 1, + }, { + .tx_buf = &data[1].d8[1], + .rx_buf = &data[2].d8[1], + .bits_per_word = 8, + .len = 3, + }, + }; + + data[0].d32 = cpu_to_be32(AD5791_CMD_READ | + AD5791_ADDR(addr)); + data[1].d32 = cpu_to_be32(AD5791_ADDR(AD5791_ADDR_NOOP)); + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(spi, &msg); + + *val = be32_to_cpu(data[2].d32); + + return ret; +} + +static const char * const ad5791_powerdown_modes[] = { + "6kohm_to_gnd", + "three_state", +}; + +static int ad5791_get_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5791_state *st = iio_priv(indio_dev); + + return st->pwr_down_mode; +} + +static int ad5791_set_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int mode) +{ + struct ad5791_state *st = iio_priv(indio_dev); + + st->pwr_down_mode = mode; + + return 0; +} + +static const struct iio_enum ad5791_powerdown_mode_enum = { + .items = ad5791_powerdown_modes, + .num_items = ARRAY_SIZE(ad5791_powerdown_modes), + .get = ad5791_get_powerdown_mode, + .set = ad5791_set_powerdown_mode, +}; + +static ssize_t ad5791_read_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, char *buf) +{ + struct ad5791_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", st->pwr_down); +} + +static ssize_t ad5791_write_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, const char *buf, + size_t len) +{ + bool pwr_down; + int ret; + struct ad5791_state *st = iio_priv(indio_dev); + + ret = strtobool(buf, &pwr_down); + if (ret) + return ret; + + if (!pwr_down) { + st->ctrl &= ~(AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI); + } else { + if (st->pwr_down_mode == AD5791_DAC_PWRDN_6K) + st->ctrl |= AD5791_CTRL_OPGND; + else if (st->pwr_down_mode == AD5791_DAC_PWRDN_3STATE) + st->ctrl |= AD5791_CTRL_DACTRI; + } + st->pwr_down = pwr_down; + + ret = ad5791_spi_write(st->spi, AD5791_ADDR_CTRL, st->ctrl); + + return ret ? ret : len; +} + +static int ad5791_get_lin_comp(unsigned int span) +{ + if (span <= 10000) + return AD5791_LINCOMP_0_10; + else if (span <= 12000) + return AD5791_LINCOMP_10_12; + else if (span <= 16000) + return AD5791_LINCOMP_12_16; + else if (span <= 19000) + return AD5791_LINCOMP_16_19; + else + return AD5791_LINCOMP_19_20; +} + +static int ad5780_get_lin_comp(unsigned int span) +{ + if (span <= 10000) + return AD5780_LINCOMP_0_10; + else + return AD5780_LINCOMP_10_20; +} +static const struct ad5791_chip_info ad5791_chip_info_tbl[] = { + [ID_AD5760] = { + .get_lin_comp = ad5780_get_lin_comp, + }, + [ID_AD5780] = { + .get_lin_comp = ad5780_get_lin_comp, + }, + [ID_AD5781] = { + .get_lin_comp = ad5791_get_lin_comp, + }, + [ID_AD5791] = { + .get_lin_comp = ad5791_get_lin_comp, + }, +}; + +static int ad5791_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad5791_state *st = iio_priv(indio_dev); + u64 val64; + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = ad5791_spi_read(st->spi, chan->address, val); + if (ret) + return ret; + *val &= AD5791_DAC_MASK; + *val >>= chan->scan_type.shift; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = (((u64)st->vref_mv) * 1000000ULL) >> chan->scan_type.realbits; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OFFSET: + val64 = (((u64)st->vref_neg_mv) << chan->scan_type.realbits); + do_div(val64, st->vref_mv); + *val = -val64; + return IIO_VAL_INT; + default: + return -EINVAL; + } + +}; + +static const struct iio_chan_spec_ext_info ad5791_ext_info[] = { + { + .name = "powerdown", + .shared = true, + .read = ad5791_read_dac_powerdown, + .write = ad5791_write_dac_powerdown, + }, + IIO_ENUM("powerdown_mode", true, &ad5791_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", &ad5791_powerdown_mode_enum), + { }, +}; + +#define AD5791_CHAN(bits, shift) { \ + .type = IIO_VOLTAGE, \ + .output = 1, \ + .indexed = 1, \ + .address = AD5791_ADDR_DAC0, \ + .channel = 0, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SHARED_BIT | \ + IIO_CHAN_INFO_OFFSET_SHARED_BIT, \ + .scan_type = IIO_ST('u', bits, 24, shift), \ + .ext_info = ad5791_ext_info, \ +} + +static const struct iio_chan_spec ad5791_channels[] = { + [ID_AD5760] = AD5791_CHAN(16, 4), + [ID_AD5780] = AD5791_CHAN(18, 2), + [ID_AD5781] = AD5791_CHAN(18, 2), + [ID_AD5791] = AD5791_CHAN(20, 0) +}; + +static int ad5791_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad5791_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + val &= AD5791_RES_MASK(chan->scan_type.realbits); + val <<= chan->scan_type.shift; + + return ad5791_spi_write(st->spi, chan->address, val); + + default: + return -EINVAL; + } +} + +static const struct iio_info ad5791_info = { + .read_raw = &ad5791_read_raw, + .write_raw = &ad5791_write_raw, + .driver_module = THIS_MODULE, +}; + +static int __devinit ad5791_probe(struct spi_device *spi) +{ + struct ad5791_platform_data *pdata = spi->dev.platform_data; + struct iio_dev *indio_dev; + struct ad5791_state *st; + int ret, pos_voltage_uv = 0, neg_voltage_uv = 0; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) { + ret = -ENOMEM; + goto error_ret; + } + st = iio_priv(indio_dev); + st->reg_vdd = regulator_get(&spi->dev, "vdd"); + if (!IS_ERR(st->reg_vdd)) { + ret = regulator_enable(st->reg_vdd); + if (ret) + goto error_put_reg_pos; + + pos_voltage_uv = regulator_get_voltage(st->reg_vdd); + } + + st->reg_vss = regulator_get(&spi->dev, "vss"); + if (!IS_ERR(st->reg_vss)) { + ret = regulator_enable(st->reg_vss); + if (ret) + goto error_put_reg_neg; + + neg_voltage_uv = regulator_get_voltage(st->reg_vss); + } + + st->pwr_down = true; + st->spi = spi; + + if (!IS_ERR(st->reg_vss) && !IS_ERR(st->reg_vdd)) { + st->vref_mv = (pos_voltage_uv + neg_voltage_uv) / 1000; + st->vref_neg_mv = neg_voltage_uv / 1000; + } else if (pdata) { + st->vref_mv = pdata->vref_pos_mv + pdata->vref_neg_mv; + st->vref_neg_mv = pdata->vref_neg_mv; + } else { + dev_warn(&spi->dev, "reference voltage unspecified\n"); + } + + ret = ad5791_spi_write(spi, AD5791_ADDR_SW_CTRL, AD5791_SWCTRL_RESET); + if (ret) + goto error_disable_reg_neg; + + st->chip_info = &ad5791_chip_info_tbl[spi_get_device_id(spi) + ->driver_data]; + + + st->ctrl = AD5761_CTRL_LINCOMP(st->chip_info->get_lin_comp(st->vref_mv)) + | ((pdata && pdata->use_rbuf_gain2) ? 0 : AD5791_CTRL_RBUF) | + AD5791_CTRL_BIN2SC; + + ret = ad5791_spi_write(spi, AD5791_ADDR_CTRL, st->ctrl | + AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI); + if (ret) + goto error_disable_reg_neg; + + spi_set_drvdata(spi, indio_dev); + indio_dev->dev.parent = &spi->dev; + indio_dev->info = &ad5791_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels + = &ad5791_channels[spi_get_device_id(spi)->driver_data]; + indio_dev->num_channels = 1; + indio_dev->name = spi_get_device_id(st->spi)->name; + ret = iio_device_register(indio_dev); + if (ret) + goto error_disable_reg_neg; + + return 0; + +error_disable_reg_neg: + if (!IS_ERR(st->reg_vss)) + regulator_disable(st->reg_vss); +error_put_reg_neg: + if (!IS_ERR(st->reg_vss)) + regulator_put(st->reg_vss); + + if (!IS_ERR(st->reg_vdd)) + regulator_disable(st->reg_vdd); +error_put_reg_pos: + if (!IS_ERR(st->reg_vdd)) + regulator_put(st->reg_vdd); + iio_device_free(indio_dev); +error_ret: + + return ret; +} + +static int __devexit ad5791_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad5791_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + if (!IS_ERR(st->reg_vdd)) { + regulator_disable(st->reg_vdd); + regulator_put(st->reg_vdd); + } + + if (!IS_ERR(st->reg_vss)) { + regulator_disable(st->reg_vss); + regulator_put(st->reg_vss); + } + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad5791_id[] = { + {"ad5760", ID_AD5760}, + {"ad5780", ID_AD5780}, + {"ad5781", ID_AD5781}, + {"ad5790", ID_AD5791}, + {"ad5791", ID_AD5791}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad5791_id); + +static struct spi_driver ad5791_driver = { + .driver = { + .name = "ad5791", + .owner = THIS_MODULE, + }, + .probe = ad5791_probe, + .remove = __devexit_p(ad5791_remove), + .id_table = ad5791_id, +}; +module_spi_driver(ad5791_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/max517.c b/drivers/iio/dac/max517.c new file mode 100644 index 000000000000..92c77c82026c --- /dev/null +++ b/drivers/iio/dac/max517.c @@ -0,0 +1,243 @@ +/* + * max517.c - Support for Maxim MAX517, MAX518 and MAX519 + * + * Copyright (C) 2010, 2011 Roland Stigge + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MAX517_DRV_NAME "max517" + +/* Commands */ +#define COMMAND_CHANNEL0 0x00 +#define COMMAND_CHANNEL1 0x01 /* for MAX518 and MAX519 */ +#define COMMAND_PD 0x08 /* Power Down */ + +enum max517_device_ids { + ID_MAX517, + ID_MAX518, + ID_MAX519, +}; + +struct max517_data { + struct iio_dev *indio_dev; + struct i2c_client *client; + unsigned short vref_mv[2]; +}; + +/* + * channel: bit 0: channel 1 + * bit 1: channel 2 + * (this way, it's possible to set both channels at once) + */ +static int max517_set_value(struct iio_dev *indio_dev, + long val, int channel) +{ + struct max517_data *data = iio_priv(indio_dev); + struct i2c_client *client = data->client; + u8 outbuf[2]; + int res; + + if (val < 0 || val > 255) + return -EINVAL; + + outbuf[0] = channel; + outbuf[1] = val; + + res = i2c_master_send(client, outbuf, 2); + if (res < 0) + return res; + else if (res != 2) + return -EIO; + else + return 0; +} + +static int max517_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct max517_data *data = iio_priv(indio_dev); + unsigned int scale_uv; + + switch (m) { + case IIO_CHAN_INFO_SCALE: + /* Corresponds to Vref / 2^(bits) */ + scale_uv = (data->vref_mv[chan->channel] * 1000) >> 8; + *val = scale_uv / 1000000; + *val2 = scale_uv % 1000000; + return IIO_VAL_INT_PLUS_MICRO; + default: + break; + } + return -EINVAL; +} + +static int max517_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long mask) +{ + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = max517_set_value(indio_dev, val, chan->channel); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +#ifdef CONFIG_PM_SLEEP +static int max517_suspend(struct device *dev) +{ + u8 outbuf = COMMAND_PD; + + return i2c_master_send(to_i2c_client(dev), &outbuf, 1); +} + +static int max517_resume(struct device *dev) +{ + u8 outbuf = 0; + + return i2c_master_send(to_i2c_client(dev), &outbuf, 1); +} + +static SIMPLE_DEV_PM_OPS(max517_pm_ops, max517_suspend, max517_resume); +#define MAX517_PM_OPS (&max517_pm_ops) +#else +#define MAX517_PM_OPS NULL +#endif + +static const struct iio_info max517_info = { + .read_raw = max517_read_raw, + .write_raw = max517_write_raw, + .driver_module = THIS_MODULE, +}; + +#define MAX517_CHANNEL(chan) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = (chan), \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \ + .scan_type = IIO_ST('u', 8, 8, 0), \ +} + +static const struct iio_chan_spec max517_channels[] = { + MAX517_CHANNEL(0), + MAX517_CHANNEL(1) +}; + +static int max517_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max517_data *data; + struct iio_dev *indio_dev; + struct max517_platform_data *platform_data = client->dev.platform_data; + int err; + + indio_dev = iio_device_alloc(sizeof(*data)); + if (indio_dev == NULL) { + err = -ENOMEM; + goto exit; + } + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + + /* establish that the iio_dev is a child of the i2c device */ + indio_dev->dev.parent = &client->dev; + + /* reduced channel set for MAX517 */ + if (id->driver_data == ID_MAX517) + indio_dev->num_channels = 1; + else + indio_dev->num_channels = 2; + indio_dev->channels = max517_channels; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &max517_info; + + /* + * Reference voltage on MAX518 and default is 5V, else take vref_mv + * from platform_data + */ + if (id->driver_data == ID_MAX518 || !platform_data) { + data->vref_mv[0] = data->vref_mv[1] = 5000; /* mV */ + } else { + data->vref_mv[0] = platform_data->vref_mv[0]; + data->vref_mv[1] = platform_data->vref_mv[1]; + } + + err = iio_device_register(indio_dev); + if (err) + goto exit_free_device; + + dev_info(&client->dev, "DAC registered\n"); + + return 0; + +exit_free_device: + iio_device_free(indio_dev); +exit: + return err; +} + +static int max517_remove(struct i2c_client *client) +{ + iio_device_unregister(i2c_get_clientdata(client)); + iio_device_free(i2c_get_clientdata(client)); + + return 0; +} + +static const struct i2c_device_id max517_id[] = { + { "max517", ID_MAX517 }, + { "max518", ID_MAX518 }, + { "max519", ID_MAX519 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max517_id); + +static struct i2c_driver max517_driver = { + .driver = { + .name = MAX517_DRV_NAME, + .pm = MAX517_PM_OPS, + }, + .probe = max517_probe, + .remove = max517_remove, + .id_table = max517_id, +}; +module_i2c_driver(max517_driver); + +MODULE_AUTHOR("Roland Stigge "); +MODULE_DESCRIPTION("MAX517/MAX518/MAX519 8-bit DAC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig index 3c8e5ec26ac1..04cd6ec1f70f 100644 --- a/drivers/staging/iio/Kconfig +++ b/drivers/staging/iio/Kconfig @@ -29,7 +29,6 @@ source "drivers/staging/iio/accel/Kconfig" source "drivers/staging/iio/adc/Kconfig" source "drivers/staging/iio/addac/Kconfig" source "drivers/staging/iio/cdc/Kconfig" -source "drivers/staging/iio/dac/Kconfig" source "drivers/staging/iio/frequency/Kconfig" source "drivers/staging/iio/gyro/Kconfig" source "drivers/staging/iio/impedance-analyzer/Kconfig" diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile index 6a46d5afb380..fa6937d92ee3 100644 --- a/drivers/staging/iio/Makefile +++ b/drivers/staging/iio/Makefile @@ -17,7 +17,6 @@ obj-y += accel/ obj-y += adc/ obj-y += addac/ obj-y += cdc/ -obj-y += dac/ obj-y += frequency/ obj-y += gyro/ obj-y += impedance-analyzer/ diff --git a/drivers/staging/iio/dac/Kconfig b/drivers/staging/iio/dac/Kconfig deleted file mode 100644 index a626f03871ec..000000000000 --- a/drivers/staging/iio/dac/Kconfig +++ /dev/null @@ -1,121 +0,0 @@ -# -# DAC drivers -# -menu "Digital to analog converters" - -config AD5064 - tristate "Analog Devices AD5064/64-1/65/44/45/24/25, AD5628/48/66/68 DAC driver" - depends on SPI - help - Say yes here to build support for Analog Devices AD5024, AD5025, AD5044, - AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5648, AD5666, AD5668 Digital - to Analog Converter. - - To compile this driver as a module, choose M here: the - module will be called ad5064. - -config AD5360 - tristate "Analog Devices Analog Devices AD5360/61/62/63/70/71/73 DAC driver" - depends on SPI - help - Say yes here to build support for Analog Devices AD5360, AD5361, - AD5362, AD5363, AD5370, AD5371, AD5373 multi-channel - Digital to Analog Converters (DAC). - - To compile this driver as module choose M here: the module will be called - ad5360. - -config AD5380 - tristate "Analog Devices AD5380/81/82/83/84/90/91/92 DAC driver" - depends on (SPI_MASTER || I2C) - select REGMAP_I2C if I2C - select REGMAP_SPI if SPI_MASTER - help - Say yes here to build support for Analog Devices AD5380, AD5381, - AD5382, AD5383, AD5384, AD5390, AD5391, AD5392 multi-channel - Digital to Analog Converters (DAC). - - To compile this driver as module choose M here: the module will be called - ad5380. - -config AD5421 - tristate "Analog Devices AD5421 DAC driver" - depends on SPI - help - Say yes here to build support for Analog Devices AD5421 loop-powered - digital-to-analog convertors (DAC). - - To compile this driver as module choose M here: the module will be called - ad5421. - -config AD5624R_SPI - tristate "Analog Devices AD5624/44/64R DAC spi driver" - depends on SPI - help - Say yes here to build support for Analog Devices AD5624R, AD5644R and - AD5664R converters (DAC). This driver uses the common SPI interface. - -config AD5446 - tristate "Analog Devices AD5446 and similar single channel DACs driver" - depends on SPI - help - Say yes here to build support for Analog Devices AD5444, AD5446, - AD5512A, AD5541A, AD5542A, AD5543, AD5553, AD5601, AD5611, AD5620, - AD5621, AD5640, AD5660, AD5662 DACs. - - To compile this driver as a module, choose M here: the - module will be called ad5446. - -config AD5504 - tristate "Analog Devices AD5504/AD5501 DAC SPI driver" - depends on SPI - help - Say yes here to build support for Analog Devices AD5504, AD5501, - High Voltage Digital to Analog Converter. - - To compile this driver as a module, choose M here: the - module will be called ad5504. - -config AD5764 - tristate "Analog Devices AD5764/64R/44/44R DAC driver" - depends on SPI_MASTER - help - Say yes here to build support for Analog Devices AD5764, AD5764R, AD5744, - AD5744R Digital to Analog Converter. - - To compile this driver as a module, choose M here: the - module will be called ad5764. - -config AD5791 - tristate "Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC SPI driver" - depends on SPI - help - Say yes here to build support for Analog Devices AD5760, AD5780, - AD5781, AD5790, AD5791 High Resolution Voltage Output Digital to - Analog Converter. - - To compile this driver as a module, choose M here: the - module will be called ad5791. - -config AD5686 - tristate "Analog Devices AD5686R/AD5685R/AD5684R DAC SPI driver" - depends on SPI - help - Say yes here to build support for Analog Devices AD5686R, AD5685R, - AD5684R, AD5791 Voltage Output Digital to - Analog Converter. - - To compile this driver as a module, choose M here: the - module will be called ad5686. - -config MAX517 - tristate "Maxim MAX517/518/519 DAC driver" - depends on I2C && EXPERIMENTAL - help - If you say yes here you get support for the Maxim chips MAX517, - MAX518 and MAX519 (I2C 8-Bit DACs with rail-to-rail outputs). - - This driver can also be built as a module. If so, the module - will be called max517. - -endmenu diff --git a/drivers/staging/iio/dac/Makefile b/drivers/staging/iio/dac/Makefile deleted file mode 100644 index 8ab1d264aab7..000000000000 --- a/drivers/staging/iio/dac/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -# -# Makefile for industrial I/O DAC drivers -# - -obj-$(CONFIG_AD5360) += ad5360.o -obj-$(CONFIG_AD5380) += ad5380.o -obj-$(CONFIG_AD5421) += ad5421.o -obj-$(CONFIG_AD5624R_SPI) += ad5624r_spi.o -obj-$(CONFIG_AD5064) += ad5064.o -obj-$(CONFIG_AD5504) += ad5504.o -obj-$(CONFIG_AD5446) += ad5446.o -obj-$(CONFIG_AD5764) += ad5764.o -obj-$(CONFIG_AD5791) += ad5791.o -obj-$(CONFIG_AD5686) += ad5686.o -obj-$(CONFIG_MAX517) += max517.o diff --git a/drivers/staging/iio/dac/ad5064.c b/drivers/staging/iio/dac/ad5064.c deleted file mode 100644 index 276af02520af..000000000000 --- a/drivers/staging/iio/dac/ad5064.c +++ /dev/null @@ -1,538 +0,0 @@ -/* - * AD5024, AD5025, AD5044, AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5648, - * AD5666, AD5668 Digital to analog converters driver - * - * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define AD5064_MAX_DAC_CHANNELS 8 -#define AD5064_MAX_VREFS 4 - -#define AD5064_ADDR(x) ((x) << 20) -#define AD5064_CMD(x) ((x) << 24) - -#define AD5064_ADDR_DAC(chan) (chan) -#define AD5064_ADDR_ALL_DAC 0xF - -#define AD5064_CMD_WRITE_INPUT_N 0x0 -#define AD5064_CMD_UPDATE_DAC_N 0x1 -#define AD5064_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2 -#define AD5064_CMD_WRITE_INPUT_N_UPDATE_N 0x3 -#define AD5064_CMD_POWERDOWN_DAC 0x4 -#define AD5064_CMD_CLEAR 0x5 -#define AD5064_CMD_LDAC_MASK 0x6 -#define AD5064_CMD_RESET 0x7 -#define AD5064_CMD_CONFIG 0x8 - -#define AD5064_CONFIG_DAISY_CHAIN_ENABLE BIT(1) -#define AD5064_CONFIG_INT_VREF_ENABLE BIT(0) - -#define AD5064_LDAC_PWRDN_NONE 0x0 -#define AD5064_LDAC_PWRDN_1K 0x1 -#define AD5064_LDAC_PWRDN_100K 0x2 -#define AD5064_LDAC_PWRDN_3STATE 0x3 - -/** - * struct ad5064_chip_info - chip specific information - * @shared_vref: whether the vref supply is shared between channels - * @internal_vref: internal reference voltage. 0 if the chip has no internal - * vref. - * @channel: channel specification - * @num_channels: number of channels - */ - -struct ad5064_chip_info { - bool shared_vref; - unsigned long internal_vref; - const struct iio_chan_spec *channels; - unsigned int num_channels; -}; - -/** - * struct ad5064_state - driver instance specific data - * @spi: spi_device - * @chip_info: chip model specific constants, available modes etc - * @vref_reg: vref supply regulators - * @pwr_down: whether channel is powered down - * @pwr_down_mode: channel's current power down mode - * @dac_cache: current DAC raw value (chip does not support readback) - * @use_internal_vref: set to true if the internal reference voltage should be - * used. - * @data: spi transfer buffers - */ - -struct ad5064_state { - struct spi_device *spi; - const struct ad5064_chip_info *chip_info; - struct regulator_bulk_data vref_reg[AD5064_MAX_VREFS]; - bool pwr_down[AD5064_MAX_DAC_CHANNELS]; - u8 pwr_down_mode[AD5064_MAX_DAC_CHANNELS]; - unsigned int dac_cache[AD5064_MAX_DAC_CHANNELS]; - bool use_internal_vref; - - /* - * DMA (thus cache coherency maintenance) requires the - * transfer buffers to live in their own cache lines. - */ - __be32 data ____cacheline_aligned; -}; - -enum ad5064_type { - ID_AD5024, - ID_AD5025, - ID_AD5044, - ID_AD5045, - ID_AD5064, - ID_AD5064_1, - ID_AD5065, - ID_AD5628_1, - ID_AD5628_2, - ID_AD5648_1, - ID_AD5648_2, - ID_AD5666_1, - ID_AD5666_2, - ID_AD5668_1, - ID_AD5668_2, -}; - -static int ad5064_spi_write(struct ad5064_state *st, unsigned int cmd, - unsigned int addr, unsigned int val, unsigned int shift) -{ - val <<= shift; - - st->data = cpu_to_be32(AD5064_CMD(cmd) | AD5064_ADDR(addr) | val); - - return spi_write(st->spi, &st->data, sizeof(st->data)); -} - -static int ad5064_sync_powerdown_mode(struct ad5064_state *st, - unsigned int channel) -{ - unsigned int val; - int ret; - - val = (0x1 << channel); - - if (st->pwr_down[channel]) - val |= st->pwr_down_mode[channel] << 8; - - ret = ad5064_spi_write(st, AD5064_CMD_POWERDOWN_DAC, 0, val, 0); - - return ret; -} - -static const char * const ad5064_powerdown_modes[] = { - "1kohm_to_gnd", - "100kohm_to_gnd", - "three_state", -}; - -static int ad5064_get_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - struct ad5064_state *st = iio_priv(indio_dev); - - return st->pwr_down_mode[chan->channel] - 1; -} - -static int ad5064_set_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int mode) -{ - struct ad5064_state *st = iio_priv(indio_dev); - int ret; - - mutex_lock(&indio_dev->mlock); - st->pwr_down_mode[chan->channel] = mode + 1; - - ret = ad5064_sync_powerdown_mode(st, chan->channel); - mutex_unlock(&indio_dev->mlock); - - return ret; -} - -static const struct iio_enum ad5064_powerdown_mode_enum = { - .items = ad5064_powerdown_modes, - .num_items = ARRAY_SIZE(ad5064_powerdown_modes), - .get = ad5064_get_powerdown_mode, - .set = ad5064_set_powerdown_mode, -}; - -static ssize_t ad5064_read_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, char *buf) -{ - struct ad5064_state *st = iio_priv(indio_dev); - - return sprintf(buf, "%d\n", st->pwr_down[chan->channel]); -} - -static ssize_t ad5064_write_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, const char *buf, - size_t len) -{ - struct ad5064_state *st = iio_priv(indio_dev); - bool pwr_down; - int ret; - - ret = strtobool(buf, &pwr_down); - if (ret) - return ret; - - mutex_lock(&indio_dev->mlock); - st->pwr_down[chan->channel] = pwr_down; - - ret = ad5064_sync_powerdown_mode(st, chan->channel); - mutex_unlock(&indio_dev->mlock); - return ret ? ret : len; -} - -static int ad5064_get_vref(struct ad5064_state *st, - struct iio_chan_spec const *chan) -{ - unsigned int i; - - if (st->use_internal_vref) - return st->chip_info->internal_vref; - - i = st->chip_info->shared_vref ? 0 : chan->channel; - return regulator_get_voltage(st->vref_reg[i].consumer); -} - -static int ad5064_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long m) -{ - struct ad5064_state *st = iio_priv(indio_dev); - int scale_uv; - - switch (m) { - case IIO_CHAN_INFO_RAW: - *val = st->dac_cache[chan->channel]; - return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - scale_uv = ad5064_get_vref(st, chan); - if (scale_uv < 0) - return scale_uv; - - scale_uv = (scale_uv * 100) >> chan->scan_type.realbits; - *val = scale_uv / 100000; - *val2 = (scale_uv % 100000) * 10; - return IIO_VAL_INT_PLUS_MICRO; - default: - break; - } - return -EINVAL; -} - -static int ad5064_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int val, int val2, long mask) -{ - struct ad5064_state *st = iio_priv(indio_dev); - int ret; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - if (val > (1 << chan->scan_type.realbits) || val < 0) - return -EINVAL; - - mutex_lock(&indio_dev->mlock); - ret = ad5064_spi_write(st, AD5064_CMD_WRITE_INPUT_N_UPDATE_N, - chan->address, val, chan->scan_type.shift); - if (ret == 0) - st->dac_cache[chan->channel] = val; - mutex_unlock(&indio_dev->mlock); - break; - default: - ret = -EINVAL; - } - - return ret; -} - -static const struct iio_info ad5064_info = { - .read_raw = ad5064_read_raw, - .write_raw = ad5064_write_raw, - .driver_module = THIS_MODULE, -}; - -static const struct iio_chan_spec_ext_info ad5064_ext_info[] = { - { - .name = "powerdown", - .read = ad5064_read_dac_powerdown, - .write = ad5064_write_dac_powerdown, - }, - IIO_ENUM("powerdown_mode", false, &ad5064_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5064_powerdown_mode_enum), - { }, -}; - -#define AD5064_CHANNEL(chan, bits) { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .output = 1, \ - .channel = (chan), \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \ - .address = AD5064_ADDR_DAC(chan), \ - .scan_type = IIO_ST('u', (bits), 16, 20 - (bits)), \ - .ext_info = ad5064_ext_info, \ -} - -#define DECLARE_AD5064_CHANNELS(name, bits) \ -const struct iio_chan_spec name[] = { \ - AD5064_CHANNEL(0, bits), \ - AD5064_CHANNEL(1, bits), \ - AD5064_CHANNEL(2, bits), \ - AD5064_CHANNEL(3, bits), \ - AD5064_CHANNEL(4, bits), \ - AD5064_CHANNEL(5, bits), \ - AD5064_CHANNEL(6, bits), \ - AD5064_CHANNEL(7, bits), \ -} - -static DECLARE_AD5064_CHANNELS(ad5024_channels, 12); -static DECLARE_AD5064_CHANNELS(ad5044_channels, 14); -static DECLARE_AD5064_CHANNELS(ad5064_channels, 16); - -static const struct ad5064_chip_info ad5064_chip_info_tbl[] = { - [ID_AD5024] = { - .shared_vref = false, - .channels = ad5024_channels, - .num_channels = 4, - }, - [ID_AD5025] = { - .shared_vref = false, - .channels = ad5024_channels, - .num_channels = 2, - }, - [ID_AD5044] = { - .shared_vref = false, - .channels = ad5044_channels, - .num_channels = 4, - }, - [ID_AD5045] = { - .shared_vref = false, - .channels = ad5044_channels, - .num_channels = 2, - }, - [ID_AD5064] = { - .shared_vref = false, - .channels = ad5064_channels, - .num_channels = 4, - }, - [ID_AD5064_1] = { - .shared_vref = true, - .channels = ad5064_channels, - .num_channels = 4, - }, - [ID_AD5065] = { - .shared_vref = false, - .channels = ad5064_channels, - .num_channels = 2, - }, - [ID_AD5628_1] = { - .shared_vref = true, - .internal_vref = 2500000, - .channels = ad5024_channels, - .num_channels = 8, - }, - [ID_AD5628_2] = { - .shared_vref = true, - .internal_vref = 5000000, - .channels = ad5024_channels, - .num_channels = 8, - }, - [ID_AD5648_1] = { - .shared_vref = true, - .internal_vref = 2500000, - .channels = ad5044_channels, - .num_channels = 8, - }, - [ID_AD5648_2] = { - .shared_vref = true, - .internal_vref = 5000000, - .channels = ad5044_channels, - .num_channels = 8, - }, - [ID_AD5666_1] = { - .shared_vref = true, - .internal_vref = 2500000, - .channels = ad5064_channels, - .num_channels = 4, - }, - [ID_AD5666_2] = { - .shared_vref = true, - .internal_vref = 5000000, - .channels = ad5064_channels, - .num_channels = 4, - }, - [ID_AD5668_1] = { - .shared_vref = true, - .internal_vref = 2500000, - .channels = ad5064_channels, - .num_channels = 8, - }, - [ID_AD5668_2] = { - .shared_vref = true, - .internal_vref = 5000000, - .channels = ad5064_channels, - .num_channels = 8, - }, -}; - -static inline unsigned int ad5064_num_vref(struct ad5064_state *st) -{ - return st->chip_info->shared_vref ? 1 : st->chip_info->num_channels; -} - -static const char * const ad5064_vref_names[] = { - "vrefA", - "vrefB", - "vrefC", - "vrefD", -}; - -static const char * const ad5064_vref_name(struct ad5064_state *st, - unsigned int vref) -{ - return st->chip_info->shared_vref ? "vref" : ad5064_vref_names[vref]; -} - -static int __devinit ad5064_probe(struct spi_device *spi) -{ - enum ad5064_type type = spi_get_device_id(spi)->driver_data; - struct iio_dev *indio_dev; - struct ad5064_state *st; - unsigned int i; - int ret; - - indio_dev = iio_device_alloc(sizeof(*st)); - if (indio_dev == NULL) - return -ENOMEM; - - st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); - - st->chip_info = &ad5064_chip_info_tbl[type]; - st->spi = spi; - - for (i = 0; i < ad5064_num_vref(st); ++i) - st->vref_reg[i].supply = ad5064_vref_name(st, i); - - ret = regulator_bulk_get(&st->spi->dev, ad5064_num_vref(st), - st->vref_reg); - if (ret) { - if (!st->chip_info->internal_vref) - goto error_free; - st->use_internal_vref = true; - ret = ad5064_spi_write(st, AD5064_CMD_CONFIG, 0, - AD5064_CONFIG_INT_VREF_ENABLE, 0); - if (ret) { - dev_err(&spi->dev, "Failed to enable internal vref: %d\n", - ret); - goto error_free; - } - } else { - ret = regulator_bulk_enable(ad5064_num_vref(st), st->vref_reg); - if (ret) - goto error_free_reg; - } - - for (i = 0; i < st->chip_info->num_channels; ++i) { - st->pwr_down_mode[i] = AD5064_LDAC_PWRDN_1K; - st->dac_cache[i] = 0x8000; - } - - indio_dev->dev.parent = &spi->dev; - indio_dev->name = spi_get_device_id(spi)->name; - indio_dev->info = &ad5064_info; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = st->chip_info->channels; - indio_dev->num_channels = st->chip_info->num_channels; - - ret = iio_device_register(indio_dev); - if (ret) - goto error_disable_reg; - - return 0; - -error_disable_reg: - if (!st->use_internal_vref) - regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg); -error_free_reg: - if (!st->use_internal_vref) - regulator_bulk_free(ad5064_num_vref(st), st->vref_reg); -error_free: - iio_device_free(indio_dev); - - return ret; -} - - -static int __devexit ad5064_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad5064_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - if (!st->use_internal_vref) { - regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg); - regulator_bulk_free(ad5064_num_vref(st), st->vref_reg); - } - - iio_device_free(indio_dev); - - return 0; -} - -static const struct spi_device_id ad5064_id[] = { - {"ad5024", ID_AD5024}, - {"ad5025", ID_AD5025}, - {"ad5044", ID_AD5044}, - {"ad5045", ID_AD5045}, - {"ad5064", ID_AD5064}, - {"ad5064-1", ID_AD5064_1}, - {"ad5065", ID_AD5065}, - {"ad5628-1", ID_AD5628_1}, - {"ad5628-2", ID_AD5628_2}, - {"ad5648-1", ID_AD5648_1}, - {"ad5648-2", ID_AD5648_2}, - {"ad5666-1", ID_AD5666_1}, - {"ad5666-2", ID_AD5666_2}, - {"ad5668-1", ID_AD5668_1}, - {"ad5668-2", ID_AD5668_2}, - {"ad5668-3", ID_AD5668_2}, /* similar enough to ad5668-2 */ - {} -}; -MODULE_DEVICE_TABLE(spi, ad5064_id); - -static struct spi_driver ad5064_driver = { - .driver = { - .name = "ad5064", - .owner = THIS_MODULE, - }, - .probe = ad5064_probe, - .remove = __devexit_p(ad5064_remove), - .id_table = ad5064_id, -}; -module_spi_driver(ad5064_driver); - -MODULE_AUTHOR("Lars-Peter Clausen "); -MODULE_DESCRIPTION("Analog Devices AD5024/25/44/45/64/64-1/65, AD5628/48/66/68 DAC"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/dac/ad5360.c b/drivers/staging/iio/dac/ad5360.c deleted file mode 100644 index 8fce84fe70b1..000000000000 --- a/drivers/staging/iio/dac/ad5360.c +++ /dev/null @@ -1,570 +0,0 @@ -/* - * Analog devices AD5360, AD5361, AD5362, AD5363, AD5370, AD5371, AD5373 - * multi-channel Digital to Analog Converters driver - * - * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define AD5360_CMD(x) ((x) << 22) -#define AD5360_ADDR(x) ((x) << 16) - -#define AD5360_READBACK_TYPE(x) ((x) << 13) -#define AD5360_READBACK_ADDR(x) ((x) << 7) - -#define AD5360_CHAN_ADDR(chan) ((chan) + 0x8) - -#define AD5360_CMD_WRITE_DATA 0x3 -#define AD5360_CMD_WRITE_OFFSET 0x2 -#define AD5360_CMD_WRITE_GAIN 0x1 -#define AD5360_CMD_SPECIAL_FUNCTION 0x0 - -/* Special function register addresses */ -#define AD5360_REG_SF_NOP 0x0 -#define AD5360_REG_SF_CTRL 0x1 -#define AD5360_REG_SF_OFS(x) (0x2 + (x)) -#define AD5360_REG_SF_READBACK 0x5 - -#define AD5360_SF_CTRL_PWR_DOWN BIT(0) - -#define AD5360_READBACK_X1A 0x0 -#define AD5360_READBACK_X1B 0x1 -#define AD5360_READBACK_OFFSET 0x2 -#define AD5360_READBACK_GAIN 0x3 -#define AD5360_READBACK_SF 0x4 - - -/** - * struct ad5360_chip_info - chip specific information - * @channel_template: channel specification template - * @num_channels: number of channels - * @channels_per_group: number of channels per group - * @num_vrefs: number of vref supplies for the chip -*/ - -struct ad5360_chip_info { - struct iio_chan_spec channel_template; - unsigned int num_channels; - unsigned int channels_per_group; - unsigned int num_vrefs; -}; - -/** - * struct ad5360_state - driver instance specific data - * @spi: spi_device - * @chip_info: chip model specific constants, available modes etc - * @vref_reg: vref supply regulators - * @ctrl: control register cache - * @data: spi transfer buffers - */ - -struct ad5360_state { - struct spi_device *spi; - const struct ad5360_chip_info *chip_info; - struct regulator_bulk_data vref_reg[3]; - unsigned int ctrl; - - /* - * DMA (thus cache coherency maintenance) requires the - * transfer buffers to live in their own cache lines. - */ - union { - __be32 d32; - u8 d8[4]; - } data[2] ____cacheline_aligned; -}; - -enum ad5360_type { - ID_AD5360, - ID_AD5361, - ID_AD5362, - ID_AD5363, - ID_AD5370, - ID_AD5371, - ID_AD5372, - ID_AD5373, -}; - -#define AD5360_CHANNEL(bits) { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .output = 1, \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \ - IIO_CHAN_INFO_OFFSET_SEPARATE_BIT | \ - IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | \ - IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, \ - .scan_type = IIO_ST('u', (bits), 16, 16 - (bits)) \ -} - -static const struct ad5360_chip_info ad5360_chip_info_tbl[] = { - [ID_AD5360] = { - .channel_template = AD5360_CHANNEL(16), - .num_channels = 16, - .channels_per_group = 8, - .num_vrefs = 2, - }, - [ID_AD5361] = { - .channel_template = AD5360_CHANNEL(14), - .num_channels = 16, - .channels_per_group = 8, - .num_vrefs = 2, - }, - [ID_AD5362] = { - .channel_template = AD5360_CHANNEL(16), - .num_channels = 8, - .channels_per_group = 4, - .num_vrefs = 2, - }, - [ID_AD5363] = { - .channel_template = AD5360_CHANNEL(14), - .num_channels = 8, - .channels_per_group = 4, - .num_vrefs = 2, - }, - [ID_AD5370] = { - .channel_template = AD5360_CHANNEL(16), - .num_channels = 40, - .channels_per_group = 8, - .num_vrefs = 2, - }, - [ID_AD5371] = { - .channel_template = AD5360_CHANNEL(14), - .num_channels = 40, - .channels_per_group = 8, - .num_vrefs = 3, - }, - [ID_AD5372] = { - .channel_template = AD5360_CHANNEL(16), - .num_channels = 32, - .channels_per_group = 8, - .num_vrefs = 2, - }, - [ID_AD5373] = { - .channel_template = AD5360_CHANNEL(14), - .num_channels = 32, - .channels_per_group = 8, - .num_vrefs = 2, - }, -}; - -static unsigned int ad5360_get_channel_vref_index(struct ad5360_state *st, - unsigned int channel) -{ - unsigned int i; - - /* The first groups have their own vref, while the remaining groups - * share the last vref */ - i = channel / st->chip_info->channels_per_group; - if (i >= st->chip_info->num_vrefs) - i = st->chip_info->num_vrefs - 1; - - return i; -} - -static int ad5360_get_channel_vref(struct ad5360_state *st, - unsigned int channel) -{ - unsigned int i = ad5360_get_channel_vref_index(st, channel); - - return regulator_get_voltage(st->vref_reg[i].consumer); -} - - -static int ad5360_write_unlocked(struct iio_dev *indio_dev, - unsigned int cmd, unsigned int addr, unsigned int val, - unsigned int shift) -{ - struct ad5360_state *st = iio_priv(indio_dev); - - val <<= shift; - val |= AD5360_CMD(cmd) | AD5360_ADDR(addr); - st->data[0].d32 = cpu_to_be32(val); - - return spi_write(st->spi, &st->data[0].d8[1], 3); -} - -static int ad5360_write(struct iio_dev *indio_dev, unsigned int cmd, - unsigned int addr, unsigned int val, unsigned int shift) -{ - int ret; - - mutex_lock(&indio_dev->mlock); - ret = ad5360_write_unlocked(indio_dev, cmd, addr, val, shift); - mutex_unlock(&indio_dev->mlock); - - return ret; -} - -static int ad5360_read(struct iio_dev *indio_dev, unsigned int type, - unsigned int addr) -{ - struct ad5360_state *st = iio_priv(indio_dev); - struct spi_message m; - int ret; - struct spi_transfer t[] = { - { - .tx_buf = &st->data[0].d8[1], - .len = 3, - .cs_change = 1, - }, { - .rx_buf = &st->data[1].d8[1], - .len = 3, - }, - }; - - spi_message_init(&m); - spi_message_add_tail(&t[0], &m); - spi_message_add_tail(&t[1], &m); - - mutex_lock(&indio_dev->mlock); - - st->data[0].d32 = cpu_to_be32(AD5360_CMD(AD5360_CMD_SPECIAL_FUNCTION) | - AD5360_ADDR(AD5360_REG_SF_READBACK) | - AD5360_READBACK_TYPE(type) | - AD5360_READBACK_ADDR(addr)); - - ret = spi_sync(st->spi, &m); - if (ret >= 0) - ret = be32_to_cpu(st->data[1].d32) & 0xffff; - - mutex_unlock(&indio_dev->mlock); - - return ret; -} - -static ssize_t ad5360_read_dac_powerdown(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ad5360_state *st = iio_priv(indio_dev); - - return sprintf(buf, "%d\n", (bool)(st->ctrl & AD5360_SF_CTRL_PWR_DOWN)); -} - -static int ad5360_update_ctrl(struct iio_dev *indio_dev, unsigned int set, - unsigned int clr) -{ - struct ad5360_state *st = iio_priv(indio_dev); - unsigned int ret; - - mutex_lock(&indio_dev->mlock); - - st->ctrl |= set; - st->ctrl &= ~clr; - - ret = ad5360_write_unlocked(indio_dev, AD5360_CMD_SPECIAL_FUNCTION, - AD5360_REG_SF_CTRL, st->ctrl, 0); - - mutex_unlock(&indio_dev->mlock); - - return ret; -} - -static ssize_t ad5360_write_dac_powerdown(struct device *dev, - struct device_attribute *attr, const char *buf, size_t len) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - bool pwr_down; - int ret; - - ret = strtobool(buf, &pwr_down); - if (ret) - return ret; - - if (pwr_down) - ret = ad5360_update_ctrl(indio_dev, AD5360_SF_CTRL_PWR_DOWN, 0); - else - ret = ad5360_update_ctrl(indio_dev, 0, AD5360_SF_CTRL_PWR_DOWN); - - return ret ? ret : len; -} - -static IIO_DEVICE_ATTR(out_voltage_powerdown, - S_IRUGO | S_IWUSR, - ad5360_read_dac_powerdown, - ad5360_write_dac_powerdown, 0); - -static struct attribute *ad5360_attributes[] = { - &iio_dev_attr_out_voltage_powerdown.dev_attr.attr, - NULL, -}; - -static const struct attribute_group ad5360_attribute_group = { - .attrs = ad5360_attributes, -}; - -static int ad5360_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) -{ - struct ad5360_state *st = iio_priv(indio_dev); - int max_val = (1 << chan->scan_type.realbits); - unsigned int ofs_index; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - if (val >= max_val || val < 0) - return -EINVAL; - - return ad5360_write(indio_dev, AD5360_CMD_WRITE_DATA, - chan->address, val, chan->scan_type.shift); - - case IIO_CHAN_INFO_CALIBBIAS: - if (val >= max_val || val < 0) - return -EINVAL; - - return ad5360_write(indio_dev, AD5360_CMD_WRITE_OFFSET, - chan->address, val, chan->scan_type.shift); - - case IIO_CHAN_INFO_CALIBSCALE: - if (val >= max_val || val < 0) - return -EINVAL; - - return ad5360_write(indio_dev, AD5360_CMD_WRITE_GAIN, - chan->address, val, chan->scan_type.shift); - - case IIO_CHAN_INFO_OFFSET: - if (val <= -max_val || val > 0) - return -EINVAL; - - val = -val; - - /* offset is supposed to have the same scale as raw, but it - * is always 14bits wide, so on a chip where the raw value has - * more bits, we need to shift offset. */ - val >>= (chan->scan_type.realbits - 14); - - /* There is one DAC offset register per vref. Changing one - * channels offset will also change the offset for all other - * channels which share the same vref supply. */ - ofs_index = ad5360_get_channel_vref_index(st, chan->channel); - return ad5360_write(indio_dev, AD5360_CMD_SPECIAL_FUNCTION, - AD5360_REG_SF_OFS(ofs_index), val, 0); - default: - break; - } - - return -EINVAL; -} - -static int ad5360_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long m) -{ - struct ad5360_state *st = iio_priv(indio_dev); - unsigned int ofs_index; - int scale_uv; - int ret; - - switch (m) { - case IIO_CHAN_INFO_RAW: - ret = ad5360_read(indio_dev, AD5360_READBACK_X1A, - chan->address); - if (ret < 0) - return ret; - *val = ret >> chan->scan_type.shift; - return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - /* vout = 4 * vref * dac_code */ - scale_uv = ad5360_get_channel_vref(st, chan->channel) * 4 * 100; - if (scale_uv < 0) - return scale_uv; - - scale_uv >>= (chan->scan_type.realbits); - *val = scale_uv / 100000; - *val2 = (scale_uv % 100000) * 10; - return IIO_VAL_INT_PLUS_MICRO; - case IIO_CHAN_INFO_CALIBBIAS: - ret = ad5360_read(indio_dev, AD5360_READBACK_OFFSET, - chan->address); - if (ret < 0) - return ret; - *val = ret; - return IIO_VAL_INT; - case IIO_CHAN_INFO_CALIBSCALE: - ret = ad5360_read(indio_dev, AD5360_READBACK_GAIN, - chan->address); - if (ret < 0) - return ret; - *val = ret; - return IIO_VAL_INT; - case IIO_CHAN_INFO_OFFSET: - ofs_index = ad5360_get_channel_vref_index(st, chan->channel); - ret = ad5360_read(indio_dev, AD5360_READBACK_SF, - AD5360_REG_SF_OFS(ofs_index)); - if (ret < 0) - return ret; - - ret <<= (chan->scan_type.realbits - 14); - *val = -ret; - return IIO_VAL_INT; - } - - return -EINVAL; -} - -static const struct iio_info ad5360_info = { - .read_raw = ad5360_read_raw, - .write_raw = ad5360_write_raw, - .attrs = &ad5360_attribute_group, - .driver_module = THIS_MODULE, -}; - -static const char * const ad5360_vref_name[] = { - "vref0", "vref1", "vref2" -}; - -static int __devinit ad5360_alloc_channels(struct iio_dev *indio_dev) -{ - struct ad5360_state *st = iio_priv(indio_dev); - struct iio_chan_spec *channels; - unsigned int i; - - channels = kcalloc(st->chip_info->num_channels, - sizeof(struct iio_chan_spec), GFP_KERNEL); - - if (!channels) - return -ENOMEM; - - for (i = 0; i < st->chip_info->num_channels; ++i) { - channels[i] = st->chip_info->channel_template; - channels[i].channel = i; - channels[i].address = AD5360_CHAN_ADDR(i); - } - - indio_dev->channels = channels; - - return 0; -} - -static int __devinit ad5360_probe(struct spi_device *spi) -{ - enum ad5360_type type = spi_get_device_id(spi)->driver_data; - struct iio_dev *indio_dev; - struct ad5360_state *st; - unsigned int i; - int ret; - - indio_dev = iio_device_alloc(sizeof(*st)); - if (indio_dev == NULL) { - dev_err(&spi->dev, "Failed to allocate iio device\n"); - return -ENOMEM; - } - - st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); - - st->chip_info = &ad5360_chip_info_tbl[type]; - st->spi = spi; - - indio_dev->dev.parent = &spi->dev; - indio_dev->name = spi_get_device_id(spi)->name; - indio_dev->info = &ad5360_info; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->num_channels = st->chip_info->num_channels; - - ret = ad5360_alloc_channels(indio_dev); - if (ret) { - dev_err(&spi->dev, "Failed to allocate channel spec: %d\n", ret); - goto error_free; - } - - for (i = 0; i < st->chip_info->num_vrefs; ++i) - st->vref_reg[i].supply = ad5360_vref_name[i]; - - ret = regulator_bulk_get(&st->spi->dev, st->chip_info->num_vrefs, - st->vref_reg); - if (ret) { - dev_err(&spi->dev, "Failed to request vref regulators: %d\n", ret); - goto error_free_channels; - } - - ret = regulator_bulk_enable(st->chip_info->num_vrefs, st->vref_reg); - if (ret) { - dev_err(&spi->dev, "Failed to enable vref regulators: %d\n", ret); - goto error_free_reg; - } - - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&spi->dev, "Failed to register iio device: %d\n", ret); - goto error_disable_reg; - } - - return 0; - -error_disable_reg: - regulator_bulk_disable(st->chip_info->num_vrefs, st->vref_reg); -error_free_reg: - regulator_bulk_free(st->chip_info->num_vrefs, st->vref_reg); -error_free_channels: - kfree(indio_dev->channels); -error_free: - iio_device_free(indio_dev); - - return ret; -} - -static int __devexit ad5360_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad5360_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - kfree(indio_dev->channels); - - regulator_bulk_disable(st->chip_info->num_vrefs, st->vref_reg); - regulator_bulk_free(st->chip_info->num_vrefs, st->vref_reg); - - iio_device_free(indio_dev); - - return 0; -} - -static const struct spi_device_id ad5360_ids[] = { - { "ad5360", ID_AD5360 }, - { "ad5361", ID_AD5361 }, - { "ad5362", ID_AD5362 }, - { "ad5363", ID_AD5363 }, - { "ad5370", ID_AD5370 }, - { "ad5371", ID_AD5371 }, - { "ad5372", ID_AD5372 }, - { "ad5373", ID_AD5373 }, - {} -}; -MODULE_DEVICE_TABLE(spi, ad5360_ids); - -static struct spi_driver ad5360_driver = { - .driver = { - .name = "ad5360", - .owner = THIS_MODULE, - }, - .probe = ad5360_probe, - .remove = __devexit_p(ad5360_remove), - .id_table = ad5360_ids, -}; -module_spi_driver(ad5360_driver); - -MODULE_AUTHOR("Lars-Peter Clausen "); -MODULE_DESCRIPTION("Analog Devices AD5360/61/62/63/70/71/72/73 DAC"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/dac/ad5380.c b/drivers/staging/iio/dac/ad5380.c deleted file mode 100644 index 5dfb4451728f..000000000000 --- a/drivers/staging/iio/dac/ad5380.c +++ /dev/null @@ -1,657 +0,0 @@ -/* - * Analog devices AD5380, AD5381, AD5382, AD5383, AD5390, AD5391, AD5392 - * multi-channel Digital to Analog Converters driver - * - * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define AD5380_REG_DATA(x) (((x) << 2) | 3) -#define AD5380_REG_OFFSET(x) (((x) << 2) | 2) -#define AD5380_REG_GAIN(x) (((x) << 2) | 1) -#define AD5380_REG_SF_PWR_DOWN (8 << 2) -#define AD5380_REG_SF_PWR_UP (9 << 2) -#define AD5380_REG_SF_CTRL (12 << 2) - -#define AD5380_CTRL_PWR_DOWN_MODE_OFFSET 13 -#define AD5380_CTRL_INT_VREF_2V5 BIT(12) -#define AD5380_CTRL_INT_VREF_EN BIT(10) - -/** - * struct ad5380_chip_info - chip specific information - * @channel_template: channel specification template - * @num_channels: number of channels - * @int_vref: internal vref in uV -*/ - -struct ad5380_chip_info { - struct iio_chan_spec channel_template; - unsigned int num_channels; - unsigned int int_vref; -}; - -/** - * struct ad5380_state - driver instance specific data - * @regmap: regmap instance used by the device - * @chip_info: chip model specific constants, available modes etc - * @vref_reg: vref supply regulator - * @vref: actual reference voltage used in uA - * @pwr_down: whether the chip is currently in power down mode - */ - -struct ad5380_state { - struct regmap *regmap; - const struct ad5380_chip_info *chip_info; - struct regulator *vref_reg; - int vref; - bool pwr_down; -}; - -enum ad5380_type { - ID_AD5380_3, - ID_AD5380_5, - ID_AD5381_3, - ID_AD5381_5, - ID_AD5382_3, - ID_AD5382_5, - ID_AD5383_3, - ID_AD5383_5, - ID_AD5390_3, - ID_AD5390_5, - ID_AD5391_3, - ID_AD5391_5, - ID_AD5392_3, - ID_AD5392_5, -}; - -static ssize_t ad5380_read_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, char *buf) -{ - struct ad5380_state *st = iio_priv(indio_dev); - - return sprintf(buf, "%d\n", st->pwr_down); -} - -static ssize_t ad5380_write_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, const char *buf, - size_t len) -{ - struct ad5380_state *st = iio_priv(indio_dev); - bool pwr_down; - int ret; - - ret = strtobool(buf, &pwr_down); - if (ret) - return ret; - - mutex_lock(&indio_dev->mlock); - - if (pwr_down) - ret = regmap_write(st->regmap, AD5380_REG_SF_PWR_DOWN, 0); - else - ret = regmap_write(st->regmap, AD5380_REG_SF_PWR_UP, 0); - - st->pwr_down = pwr_down; - - mutex_unlock(&indio_dev->mlock); - - return ret ? ret : len; -} - -static const char * const ad5380_powerdown_modes[] = { - "100kohm_to_gnd", - "three_state", -}; - -static int ad5380_get_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - struct ad5380_state *st = iio_priv(indio_dev); - unsigned int mode; - int ret; - - ret = regmap_read(st->regmap, AD5380_REG_SF_CTRL, &mode); - if (ret) - return ret; - - mode = (mode >> AD5380_CTRL_PWR_DOWN_MODE_OFFSET) & 1; - - return mode; -} - -static int ad5380_set_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int mode) -{ - struct ad5380_state *st = iio_priv(indio_dev); - int ret; - - ret = regmap_update_bits(st->regmap, AD5380_REG_SF_CTRL, - 1 << AD5380_CTRL_PWR_DOWN_MODE_OFFSET, - mode << AD5380_CTRL_PWR_DOWN_MODE_OFFSET); - - return ret; -} - -static const struct iio_enum ad5380_powerdown_mode_enum = { - .items = ad5380_powerdown_modes, - .num_items = ARRAY_SIZE(ad5380_powerdown_modes), - .get = ad5380_get_powerdown_mode, - .set = ad5380_set_powerdown_mode, -}; - -static unsigned int ad5380_info_to_reg(struct iio_chan_spec const *chan, - long info) -{ - switch (info) { - case 0: - return AD5380_REG_DATA(chan->address); - case IIO_CHAN_INFO_CALIBBIAS: - return AD5380_REG_OFFSET(chan->address); - case IIO_CHAN_INFO_CALIBSCALE: - return AD5380_REG_GAIN(chan->address); - default: - break; - } - - return 0; -} - -static int ad5380_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int val, int val2, long info) -{ - const unsigned int max_val = (1 << chan->scan_type.realbits); - struct ad5380_state *st = iio_priv(indio_dev); - - switch (info) { - case IIO_CHAN_INFO_RAW: - case IIO_CHAN_INFO_CALIBSCALE: - if (val >= max_val || val < 0) - return -EINVAL; - - return regmap_write(st->regmap, - ad5380_info_to_reg(chan, info), - val << chan->scan_type.shift); - case IIO_CHAN_INFO_CALIBBIAS: - val += (1 << chan->scan_type.realbits) / 2; - if (val >= max_val || val < 0) - return -EINVAL; - - return regmap_write(st->regmap, - AD5380_REG_OFFSET(chan->address), - val << chan->scan_type.shift); - default: - break; - } - return -EINVAL; -} - -static int ad5380_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int *val, int *val2, long info) -{ - struct ad5380_state *st = iio_priv(indio_dev); - unsigned long scale_uv; - int ret; - - switch (info) { - case IIO_CHAN_INFO_RAW: - case IIO_CHAN_INFO_CALIBSCALE: - ret = regmap_read(st->regmap, ad5380_info_to_reg(chan, info), - val); - if (ret) - return ret; - *val >>= chan->scan_type.shift; - return IIO_VAL_INT; - case IIO_CHAN_INFO_CALIBBIAS: - ret = regmap_read(st->regmap, AD5380_REG_OFFSET(chan->address), - val); - if (ret) - return ret; - *val >>= chan->scan_type.shift; - val -= (1 << chan->scan_type.realbits) / 2; - return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - scale_uv = ((2 * st->vref) >> chan->scan_type.realbits) * 100; - *val = scale_uv / 100000; - *val2 = (scale_uv % 100000) * 10; - return IIO_VAL_INT_PLUS_MICRO; - default: - break; - } - - return -EINVAL; -} - -static const struct iio_info ad5380_info = { - .read_raw = ad5380_read_raw, - .write_raw = ad5380_write_raw, - .driver_module = THIS_MODULE, -}; - -static struct iio_chan_spec_ext_info ad5380_ext_info[] = { - { - .name = "powerdown", - .read = ad5380_read_dac_powerdown, - .write = ad5380_write_dac_powerdown, - }, - IIO_ENUM("powerdown_mode", true, &ad5380_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5380_powerdown_mode_enum), - { }, -}; - -#define AD5380_CHANNEL(_bits) { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .output = 1, \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SHARED_BIT | \ - IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | \ - IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, \ - .scan_type = IIO_ST('u', (_bits), 16, 14 - (_bits)), \ - .ext_info = ad5380_ext_info, \ -} - -static const struct ad5380_chip_info ad5380_chip_info_tbl[] = { - [ID_AD5380_3] = { - .channel_template = AD5380_CHANNEL(14), - .num_channels = 40, - .int_vref = 1250000, - }, - [ID_AD5380_5] = { - .channel_template = AD5380_CHANNEL(14), - .num_channels = 40, - .int_vref = 2500000, - }, - [ID_AD5381_3] = { - .channel_template = AD5380_CHANNEL(12), - .num_channels = 16, - .int_vref = 1250000, - }, - [ID_AD5381_5] = { - .channel_template = AD5380_CHANNEL(12), - .num_channels = 16, - .int_vref = 2500000, - }, - [ID_AD5382_3] = { - .channel_template = AD5380_CHANNEL(14), - .num_channels = 32, - .int_vref = 1250000, - }, - [ID_AD5382_5] = { - .channel_template = AD5380_CHANNEL(14), - .num_channels = 32, - .int_vref = 2500000, - }, - [ID_AD5383_3] = { - .channel_template = AD5380_CHANNEL(12), - .num_channels = 32, - .int_vref = 1250000, - }, - [ID_AD5383_5] = { - .channel_template = AD5380_CHANNEL(12), - .num_channels = 32, - .int_vref = 2500000, - }, - [ID_AD5390_3] = { - .channel_template = AD5380_CHANNEL(14), - .num_channels = 16, - .int_vref = 1250000, - }, - [ID_AD5390_5] = { - .channel_template = AD5380_CHANNEL(14), - .num_channels = 16, - .int_vref = 2500000, - }, - [ID_AD5391_3] = { - .channel_template = AD5380_CHANNEL(12), - .num_channels = 16, - .int_vref = 1250000, - }, - [ID_AD5391_5] = { - .channel_template = AD5380_CHANNEL(12), - .num_channels = 16, - .int_vref = 2500000, - }, - [ID_AD5392_3] = { - .channel_template = AD5380_CHANNEL(14), - .num_channels = 8, - .int_vref = 1250000, - }, - [ID_AD5392_5] = { - .channel_template = AD5380_CHANNEL(14), - .num_channels = 8, - .int_vref = 2500000, - }, -}; - -static int __devinit ad5380_alloc_channels(struct iio_dev *indio_dev) -{ - struct ad5380_state *st = iio_priv(indio_dev); - struct iio_chan_spec *channels; - unsigned int i; - - channels = kcalloc(st->chip_info->num_channels, - sizeof(struct iio_chan_spec), GFP_KERNEL); - - if (!channels) - return -ENOMEM; - - for (i = 0; i < st->chip_info->num_channels; ++i) { - channels[i] = st->chip_info->channel_template; - channels[i].channel = i; - channels[i].address = i; - } - - indio_dev->channels = channels; - - return 0; -} - -static int __devinit ad5380_probe(struct device *dev, struct regmap *regmap, - enum ad5380_type type, const char *name) -{ - struct iio_dev *indio_dev; - struct ad5380_state *st; - unsigned int ctrl = 0; - int ret; - - indio_dev = iio_device_alloc(sizeof(*st)); - if (indio_dev == NULL) { - dev_err(dev, "Failed to allocate iio device\n"); - ret = -ENOMEM; - goto error_regmap_exit; - } - - st = iio_priv(indio_dev); - dev_set_drvdata(dev, indio_dev); - - st->chip_info = &ad5380_chip_info_tbl[type]; - st->regmap = regmap; - - indio_dev->dev.parent = dev; - indio_dev->name = name; - indio_dev->info = &ad5380_info; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->num_channels = st->chip_info->num_channels; - - ret = ad5380_alloc_channels(indio_dev); - if (ret) { - dev_err(dev, "Failed to allocate channel spec: %d\n", ret); - goto error_free; - } - - if (st->chip_info->int_vref == 2500000) - ctrl |= AD5380_CTRL_INT_VREF_2V5; - - st->vref_reg = regulator_get(dev, "vref"); - if (!IS_ERR(st->vref_reg)) { - ret = regulator_enable(st->vref_reg); - if (ret) { - dev_err(dev, "Failed to enable vref regulators: %d\n", - ret); - goto error_free_reg; - } - - st->vref = regulator_get_voltage(st->vref_reg); - } else { - st->vref = st->chip_info->int_vref; - ctrl |= AD5380_CTRL_INT_VREF_EN; - } - - ret = regmap_write(st->regmap, AD5380_REG_SF_CTRL, ctrl); - if (ret) { - dev_err(dev, "Failed to write to device: %d\n", ret); - goto error_disable_reg; - } - - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(dev, "Failed to register iio device: %d\n", ret); - goto error_disable_reg; - } - - return 0; - -error_disable_reg: - if (!IS_ERR(st->vref_reg)) - regulator_disable(st->vref_reg); -error_free_reg: - if (!IS_ERR(st->vref_reg)) - regulator_put(st->vref_reg); - - kfree(indio_dev->channels); -error_free: - iio_device_free(indio_dev); -error_regmap_exit: - regmap_exit(regmap); - - return ret; -} - -static int __devexit ad5380_remove(struct device *dev) -{ - struct iio_dev *indio_dev = dev_get_drvdata(dev); - struct ad5380_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - kfree(indio_dev->channels); - - if (!IS_ERR(st->vref_reg)) { - regulator_disable(st->vref_reg); - regulator_put(st->vref_reg); - } - - regmap_exit(st->regmap); - iio_device_free(indio_dev); - - return 0; -} - -static bool ad5380_reg_false(struct device *dev, unsigned int reg) -{ - return false; -} - -static const struct regmap_config ad5380_regmap_config = { - .reg_bits = 10, - .val_bits = 14, - - .max_register = AD5380_REG_DATA(40), - .cache_type = REGCACHE_RBTREE, - - .volatile_reg = ad5380_reg_false, - .readable_reg = ad5380_reg_false, -}; - -#if IS_ENABLED(CONFIG_SPI_MASTER) - -static int __devinit ad5380_spi_probe(struct spi_device *spi) -{ - const struct spi_device_id *id = spi_get_device_id(spi); - struct regmap *regmap; - - regmap = regmap_init_spi(spi, &ad5380_regmap_config); - - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - - return ad5380_probe(&spi->dev, regmap, id->driver_data, id->name); -} - -static int __devexit ad5380_spi_remove(struct spi_device *spi) -{ - return ad5380_remove(&spi->dev); -} - -static const struct spi_device_id ad5380_spi_ids[] = { - { "ad5380-3", ID_AD5380_3 }, - { "ad5380-5", ID_AD5380_5 }, - { "ad5381-3", ID_AD5381_3 }, - { "ad5381-5", ID_AD5381_5 }, - { "ad5382-3", ID_AD5382_3 }, - { "ad5382-5", ID_AD5382_5 }, - { "ad5383-3", ID_AD5383_3 }, - { "ad5383-5", ID_AD5383_5 }, - { "ad5384-3", ID_AD5380_3 }, - { "ad5384-5", ID_AD5380_5 }, - { "ad5390-3", ID_AD5390_3 }, - { "ad5390-5", ID_AD5390_5 }, - { "ad5391-3", ID_AD5391_3 }, - { "ad5391-5", ID_AD5391_5 }, - { "ad5392-3", ID_AD5392_3 }, - { "ad5392-5", ID_AD5392_5 }, - { } -}; -MODULE_DEVICE_TABLE(spi, ad5380_spi_ids); - -static struct spi_driver ad5380_spi_driver = { - .driver = { - .name = "ad5380", - .owner = THIS_MODULE, - }, - .probe = ad5380_spi_probe, - .remove = __devexit_p(ad5380_spi_remove), - .id_table = ad5380_spi_ids, -}; - -static inline int ad5380_spi_register_driver(void) -{ - return spi_register_driver(&ad5380_spi_driver); -} - -static inline void ad5380_spi_unregister_driver(void) -{ - spi_unregister_driver(&ad5380_spi_driver); -} - -#else - -static inline int ad5380_spi_register_driver(void) -{ - return 0; -} - -static inline void ad5380_spi_unregister_driver(void) -{ -} - -#endif - -#if IS_ENABLED(CONFIG_I2C) - -static int __devinit ad5380_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) -{ - struct regmap *regmap; - - regmap = regmap_init_i2c(i2c, &ad5380_regmap_config); - - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - - return ad5380_probe(&i2c->dev, regmap, id->driver_data, id->name); -} - -static int __devexit ad5380_i2c_remove(struct i2c_client *i2c) -{ - return ad5380_remove(&i2c->dev); -} - -static const struct i2c_device_id ad5380_i2c_ids[] = { - { "ad5380-3", ID_AD5380_3 }, - { "ad5380-5", ID_AD5380_5 }, - { "ad5381-3", ID_AD5381_3 }, - { "ad5381-5", ID_AD5381_5 }, - { "ad5382-3", ID_AD5382_3 }, - { "ad5382-5", ID_AD5382_5 }, - { "ad5383-3", ID_AD5383_3 }, - { "ad5383-5", ID_AD5383_5 }, - { "ad5384-3", ID_AD5380_3 }, - { "ad5384-5", ID_AD5380_5 }, - { "ad5390-3", ID_AD5390_3 }, - { "ad5390-5", ID_AD5390_5 }, - { "ad5391-3", ID_AD5391_3 }, - { "ad5391-5", ID_AD5391_5 }, - { "ad5392-3", ID_AD5392_3 }, - { "ad5392-5", ID_AD5392_5 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ad5380_i2c_ids); - -static struct i2c_driver ad5380_i2c_driver = { - .driver = { - .name = "ad5380", - .owner = THIS_MODULE, - }, - .probe = ad5380_i2c_probe, - .remove = __devexit_p(ad5380_i2c_remove), - .id_table = ad5380_i2c_ids, -}; - -static inline int ad5380_i2c_register_driver(void) -{ - return i2c_add_driver(&ad5380_i2c_driver); -} - -static inline void ad5380_i2c_unregister_driver(void) -{ - i2c_del_driver(&ad5380_i2c_driver); -} - -#else - -static inline int ad5380_i2c_register_driver(void) -{ - return 0; -} - -static inline void ad5380_i2c_unregister_driver(void) -{ -} - -#endif - -static int __init ad5380_spi_init(void) -{ - int ret; - - ret = ad5380_spi_register_driver(); - if (ret) - return ret; - - ret = ad5380_i2c_register_driver(); - if (ret) { - ad5380_spi_unregister_driver(); - return ret; - } - - return 0; -} -module_init(ad5380_spi_init); - -static void __exit ad5380_spi_exit(void) -{ - ad5380_i2c_unregister_driver(); - ad5380_spi_unregister_driver(); - -} -module_exit(ad5380_spi_exit); - -MODULE_AUTHOR("Lars-Peter Clausen "); -MODULE_DESCRIPTION("Analog Devices AD5380/81/82/83/84/90/91/92 DAC"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/dac/ad5421.c b/drivers/staging/iio/dac/ad5421.c deleted file mode 100644 index ea2f83b4e357..000000000000 --- a/drivers/staging/iio/dac/ad5421.c +++ /dev/null @@ -1,544 +0,0 @@ -/* - * AD5421 Digital to analog converters driver - * - * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include "ad5421.h" - - -#define AD5421_REG_DAC_DATA 0x1 -#define AD5421_REG_CTRL 0x2 -#define AD5421_REG_OFFSET 0x3 -#define AD5421_REG_GAIN 0x4 -/* load dac and fault shared the same register number. Writing to it will cause - * a dac load command, reading from it will return the fault status register */ -#define AD5421_REG_LOAD_DAC 0x5 -#define AD5421_REG_FAULT 0x5 -#define AD5421_REG_FORCE_ALARM_CURRENT 0x6 -#define AD5421_REG_RESET 0x7 -#define AD5421_REG_START_CONVERSION 0x8 -#define AD5421_REG_NOOP 0x9 - -#define AD5421_CTRL_WATCHDOG_DISABLE BIT(12) -#define AD5421_CTRL_AUTO_FAULT_READBACK BIT(11) -#define AD5421_CTRL_MIN_CURRENT BIT(9) -#define AD5421_CTRL_ADC_SOURCE_TEMP BIT(8) -#define AD5421_CTRL_ADC_ENABLE BIT(7) -#define AD5421_CTRL_PWR_DOWN_INT_VREF BIT(6) - -#define AD5421_FAULT_SPI BIT(15) -#define AD5421_FAULT_PEC BIT(14) -#define AD5421_FAULT_OVER_CURRENT BIT(13) -#define AD5421_FAULT_UNDER_CURRENT BIT(12) -#define AD5421_FAULT_TEMP_OVER_140 BIT(11) -#define AD5421_FAULT_TEMP_OVER_100 BIT(10) -#define AD5421_FAULT_UNDER_VOLTAGE_6V BIT(9) -#define AD5421_FAULT_UNDER_VOLTAGE_12V BIT(8) - -/* These bits will cause the fault pin to go high */ -#define AD5421_FAULT_TRIGGER_IRQ \ - (AD5421_FAULT_SPI | AD5421_FAULT_PEC | AD5421_FAULT_OVER_CURRENT | \ - AD5421_FAULT_UNDER_CURRENT | AD5421_FAULT_TEMP_OVER_140) - -/** - * struct ad5421_state - driver instance specific data - * @spi: spi_device - * @ctrl: control register cache - * @current_range: current range which the device is configured for - * @data: spi transfer buffers - * @fault_mask: software masking of events - */ -struct ad5421_state { - struct spi_device *spi; - unsigned int ctrl; - enum ad5421_current_range current_range; - unsigned int fault_mask; - - /* - * DMA (thus cache coherency maintenance) requires the - * transfer buffers to live in their own cache lines. - */ - union { - u32 d32; - u8 d8[4]; - } data[2] ____cacheline_aligned; -}; - -static const struct iio_chan_spec ad5421_channels[] = { - { - .type = IIO_CURRENT, - .indexed = 1, - .output = 1, - .channel = 0, - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | - IIO_CHAN_INFO_SCALE_SHARED_BIT | - IIO_CHAN_INFO_OFFSET_SHARED_BIT | - IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | - IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, - .scan_type = IIO_ST('u', 16, 16, 0), - .event_mask = IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) | - IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), - }, - { - .type = IIO_TEMP, - .channel = -1, - .event_mask = IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), - }, -}; - -static int ad5421_write_unlocked(struct iio_dev *indio_dev, - unsigned int reg, unsigned int val) -{ - struct ad5421_state *st = iio_priv(indio_dev); - - st->data[0].d32 = cpu_to_be32((reg << 16) | val); - - return spi_write(st->spi, &st->data[0].d8[1], 3); -} - -static int ad5421_write(struct iio_dev *indio_dev, unsigned int reg, - unsigned int val) -{ - int ret; - - mutex_lock(&indio_dev->mlock); - ret = ad5421_write_unlocked(indio_dev, reg, val); - mutex_unlock(&indio_dev->mlock); - - return ret; -} - -static int ad5421_read(struct iio_dev *indio_dev, unsigned int reg) -{ - struct ad5421_state *st = iio_priv(indio_dev); - struct spi_message m; - int ret; - struct spi_transfer t[] = { - { - .tx_buf = &st->data[0].d8[1], - .len = 3, - .cs_change = 1, - }, { - .rx_buf = &st->data[1].d8[1], - .len = 3, - }, - }; - - spi_message_init(&m); - spi_message_add_tail(&t[0], &m); - spi_message_add_tail(&t[1], &m); - - mutex_lock(&indio_dev->mlock); - - st->data[0].d32 = cpu_to_be32((1 << 23) | (reg << 16)); - - ret = spi_sync(st->spi, &m); - if (ret >= 0) - ret = be32_to_cpu(st->data[1].d32) & 0xffff; - - mutex_unlock(&indio_dev->mlock); - - return ret; -} - -static int ad5421_update_ctrl(struct iio_dev *indio_dev, unsigned int set, - unsigned int clr) -{ - struct ad5421_state *st = iio_priv(indio_dev); - unsigned int ret; - - mutex_lock(&indio_dev->mlock); - - st->ctrl &= ~clr; - st->ctrl |= set; - - ret = ad5421_write_unlocked(indio_dev, AD5421_REG_CTRL, st->ctrl); - - mutex_unlock(&indio_dev->mlock); - - return ret; -} - -static irqreturn_t ad5421_fault_handler(int irq, void *data) -{ - struct iio_dev *indio_dev = data; - struct ad5421_state *st = iio_priv(indio_dev); - unsigned int fault; - unsigned int old_fault = 0; - unsigned int events; - - fault = ad5421_read(indio_dev, AD5421_REG_FAULT); - if (!fault) - return IRQ_NONE; - - /* If we had a fault, this might mean that the DAC has lost its state - * and has been reset. Make sure that the control register actually - * contains what we expect it to contain. Otherwise the watchdog might - * be enabled and we get watchdog timeout faults, which will render the - * DAC unusable. */ - ad5421_update_ctrl(indio_dev, 0, 0); - - - /* The fault pin stays high as long as a fault condition is present and - * it is not possible to mask fault conditions. For certain fault - * conditions for example like over-temperature it takes some time - * until the fault condition disappears. If we would exit the interrupt - * handler immediately after handling the event it would be entered - * again instantly. Thus we fall back to polling in case we detect that - * a interrupt condition is still present. - */ - do { - /* 0xffff is a invalid value for the register and will only be - * read if there has been a communication error */ - if (fault == 0xffff) - fault = 0; - - /* we are only interested in new events */ - events = (old_fault ^ fault) & fault; - events &= st->fault_mask; - - if (events & AD5421_FAULT_OVER_CURRENT) { - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_CURRENT, - 0, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_RISING), - iio_get_time_ns()); - } - - if (events & AD5421_FAULT_UNDER_CURRENT) { - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_CURRENT, - 0, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_FALLING), - iio_get_time_ns()); - } - - if (events & AD5421_FAULT_TEMP_OVER_140) { - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_TEMP, - 0, - IIO_EV_TYPE_MAG, - IIO_EV_DIR_RISING), - iio_get_time_ns()); - } - - old_fault = fault; - fault = ad5421_read(indio_dev, AD5421_REG_FAULT); - - /* still active? go to sleep for some time */ - if (fault & AD5421_FAULT_TRIGGER_IRQ) - msleep(1000); - - } while (fault & AD5421_FAULT_TRIGGER_IRQ); - - - return IRQ_HANDLED; -} - -static void ad5421_get_current_min_max(struct ad5421_state *st, - unsigned int *min, unsigned int *max) -{ - /* The current range is configured using external pins, which are - * usually hard-wired and not run-time switchable. */ - switch (st->current_range) { - case AD5421_CURRENT_RANGE_4mA_20mA: - *min = 4000; - *max = 20000; - break; - case AD5421_CURRENT_RANGE_3mA8_21mA: - *min = 3800; - *max = 21000; - break; - case AD5421_CURRENT_RANGE_3mA2_24mA: - *min = 3200; - *max = 24000; - break; - default: - *min = 0; - *max = 1; - break; - } -} - -static inline unsigned int ad5421_get_offset(struct ad5421_state *st) -{ - unsigned int min, max; - - ad5421_get_current_min_max(st, &min, &max); - return (min * (1 << 16)) / (max - min); -} - -static inline unsigned int ad5421_get_scale(struct ad5421_state *st) -{ - unsigned int min, max; - - ad5421_get_current_min_max(st, &min, &max); - return ((max - min) * 1000) / (1 << 16); -} - -static int ad5421_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int *val, int *val2, long m) -{ - struct ad5421_state *st = iio_priv(indio_dev); - int ret; - - if (chan->type != IIO_CURRENT) - return -EINVAL; - - switch (m) { - case IIO_CHAN_INFO_RAW: - ret = ad5421_read(indio_dev, AD5421_REG_DAC_DATA); - if (ret < 0) - return ret; - *val = ret; - return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - *val = 0; - *val2 = ad5421_get_scale(st); - return IIO_VAL_INT_PLUS_MICRO; - case IIO_CHAN_INFO_OFFSET: - *val = ad5421_get_offset(st); - return IIO_VAL_INT; - case IIO_CHAN_INFO_CALIBBIAS: - ret = ad5421_read(indio_dev, AD5421_REG_OFFSET); - if (ret < 0) - return ret; - *val = ret - 32768; - return IIO_VAL_INT; - case IIO_CHAN_INFO_CALIBSCALE: - ret = ad5421_read(indio_dev, AD5421_REG_GAIN); - if (ret < 0) - return ret; - *val = ret; - return IIO_VAL_INT; - } - - return -EINVAL; -} - -static int ad5421_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int val, int val2, long mask) -{ - const unsigned int max_val = 1 << 16; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - if (val >= max_val || val < 0) - return -EINVAL; - - return ad5421_write(indio_dev, AD5421_REG_DAC_DATA, val); - case IIO_CHAN_INFO_CALIBBIAS: - val += 32768; - if (val >= max_val || val < 0) - return -EINVAL; - - return ad5421_write(indio_dev, AD5421_REG_OFFSET, val); - case IIO_CHAN_INFO_CALIBSCALE: - if (val >= max_val || val < 0) - return -EINVAL; - - return ad5421_write(indio_dev, AD5421_REG_GAIN, val); - default: - break; - } - - return -EINVAL; -} - -static int ad5421_write_event_config(struct iio_dev *indio_dev, - u64 event_code, int state) -{ - struct ad5421_state *st = iio_priv(indio_dev); - unsigned int mask; - - switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) { - case IIO_CURRENT: - if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == - IIO_EV_DIR_RISING) - mask = AD5421_FAULT_OVER_CURRENT; - else - mask = AD5421_FAULT_UNDER_CURRENT; - break; - case IIO_TEMP: - mask = AD5421_FAULT_TEMP_OVER_140; - break; - default: - return -EINVAL; - } - - mutex_lock(&indio_dev->mlock); - if (state) - st->fault_mask |= mask; - else - st->fault_mask &= ~mask; - mutex_unlock(&indio_dev->mlock); - - return 0; -} - -static int ad5421_read_event_config(struct iio_dev *indio_dev, - u64 event_code) -{ - struct ad5421_state *st = iio_priv(indio_dev); - unsigned int mask; - - switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) { - case IIO_CURRENT: - if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == - IIO_EV_DIR_RISING) - mask = AD5421_FAULT_OVER_CURRENT; - else - mask = AD5421_FAULT_UNDER_CURRENT; - break; - case IIO_TEMP: - mask = AD5421_FAULT_TEMP_OVER_140; - break; - default: - return -EINVAL; - } - - return (bool)(st->fault_mask & mask); -} - -static int ad5421_read_event_value(struct iio_dev *indio_dev, u64 event_code, - int *val) -{ - int ret; - - switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) { - case IIO_CURRENT: - ret = ad5421_read(indio_dev, AD5421_REG_DAC_DATA); - if (ret < 0) - return ret; - *val = ret; - break; - case IIO_TEMP: - *val = 140000; - break; - default: - return -EINVAL; - } - - return 0; -} - -static const struct iio_info ad5421_info = { - .read_raw = ad5421_read_raw, - .write_raw = ad5421_write_raw, - .read_event_config = ad5421_read_event_config, - .write_event_config = ad5421_write_event_config, - .read_event_value = ad5421_read_event_value, - .driver_module = THIS_MODULE, -}; - -static int __devinit ad5421_probe(struct spi_device *spi) -{ - struct ad5421_platform_data *pdata = dev_get_platdata(&spi->dev); - struct iio_dev *indio_dev; - struct ad5421_state *st; - int ret; - - indio_dev = iio_device_alloc(sizeof(*st)); - if (indio_dev == NULL) { - dev_err(&spi->dev, "Failed to allocate iio device\n"); - return -ENOMEM; - } - - st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); - - st->spi = spi; - - indio_dev->dev.parent = &spi->dev; - indio_dev->name = "ad5421"; - indio_dev->info = &ad5421_info; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = ad5421_channels; - indio_dev->num_channels = ARRAY_SIZE(ad5421_channels); - - st->ctrl = AD5421_CTRL_WATCHDOG_DISABLE | - AD5421_CTRL_AUTO_FAULT_READBACK; - - if (pdata) { - st->current_range = pdata->current_range; - if (pdata->external_vref) - st->ctrl |= AD5421_CTRL_PWR_DOWN_INT_VREF; - } else { - st->current_range = AD5421_CURRENT_RANGE_4mA_20mA; - } - - /* write initial ctrl register value */ - ad5421_update_ctrl(indio_dev, 0, 0); - - if (spi->irq) { - ret = request_threaded_irq(spi->irq, - NULL, - ad5421_fault_handler, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT, - "ad5421 fault", - indio_dev); - if (ret) - goto error_free; - } - - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&spi->dev, "Failed to register iio device: %d\n", ret); - goto error_free_irq; - } - - return 0; - -error_free_irq: - if (spi->irq) - free_irq(spi->irq, indio_dev); -error_free: - iio_device_free(indio_dev); - - return ret; -} - -static int __devexit ad5421_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - - iio_device_unregister(indio_dev); - if (spi->irq) - free_irq(spi->irq, indio_dev); - iio_device_free(indio_dev); - - return 0; -} - -static struct spi_driver ad5421_driver = { - .driver = { - .name = "ad5421", - .owner = THIS_MODULE, - }, - .probe = ad5421_probe, - .remove = __devexit_p(ad5421_remove), -}; -module_spi_driver(ad5421_driver); - -MODULE_AUTHOR("Lars-Peter Clausen "); -MODULE_DESCRIPTION("Analog Devices AD5421 DAC"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("spi:ad5421"); diff --git a/drivers/staging/iio/dac/ad5421.h b/drivers/staging/iio/dac/ad5421.h deleted file mode 100644 index cd2bb84ff1b0..000000000000 --- a/drivers/staging/iio/dac/ad5421.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef __IIO_DAC_AD5421_H__ -#define __IIO_DAC_AD5421_H__ - -/* - * TODO: This file needs to go into include/linux/iio - */ - -/** - * enum ad5421_current_range - Current range the AD5421 is configured for. - * @AD5421_CURRENT_RANGE_4mA_20mA: 4 mA to 20 mA (RANGE1,0 pins = 00) - * @AD5421_CURRENT_RANGE_3mA8_21mA: 3.8 mA to 21 mA (RANGE1,0 pins = x1) - * @AD5421_CURRENT_RANGE_3mA2_24mA: 3.2 mA to 24 mA (RANGE1,0 pins = 10) - */ - -enum ad5421_current_range { - AD5421_CURRENT_RANGE_4mA_20mA, - AD5421_CURRENT_RANGE_3mA8_21mA, - AD5421_CURRENT_RANGE_3mA2_24mA, -}; - -/** - * struct ad5421_platform_data - AD5421 DAC driver platform data - * @external_vref: whether an external reference voltage is used or not - * @current_range: Current range the AD5421 is configured for - */ - -struct ad5421_platform_data { - bool external_vref; - enum ad5421_current_range current_range; -}; - -#endif diff --git a/drivers/staging/iio/dac/ad5446.c b/drivers/staging/iio/dac/ad5446.c deleted file mode 100644 index 49f557fc673b..000000000000 --- a/drivers/staging/iio/dac/ad5446.c +++ /dev/null @@ -1,381 +0,0 @@ -/* - * AD5446 SPI DAC driver - * - * Copyright 2010 Analog Devices Inc. - * - * Licensed under the GPL-2 or later. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "ad5446.h" - -static int ad5446_write(struct ad5446_state *st, unsigned val) -{ - __be16 data = cpu_to_be16(val); - return spi_write(st->spi, &data, sizeof(data)); -} - -static int ad5660_write(struct ad5446_state *st, unsigned val) -{ - uint8_t data[3]; - - data[0] = (val >> 16) & 0xFF; - data[1] = (val >> 8) & 0xFF; - data[2] = val & 0xFF; - - return spi_write(st->spi, data, sizeof(data)); -} - -static const char * const ad5446_powerdown_modes[] = { - "1kohm_to_gnd", "100kohm_to_gnd", "three_state" -}; - -static int ad5446_set_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int mode) -{ - struct ad5446_state *st = iio_priv(indio_dev); - - st->pwr_down_mode = mode + 1; - - return 0; -} - -static int ad5446_get_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - struct ad5446_state *st = iio_priv(indio_dev); - - return st->pwr_down_mode - 1; -} - -static const struct iio_enum ad5446_powerdown_mode_enum = { - .items = ad5446_powerdown_modes, - .num_items = ARRAY_SIZE(ad5446_powerdown_modes), - .get = ad5446_get_powerdown_mode, - .set = ad5446_set_powerdown_mode, -}; - -static ssize_t ad5446_read_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, - const struct iio_chan_spec *chan, - char *buf) -{ - struct ad5446_state *st = iio_priv(indio_dev); - - return sprintf(buf, "%d\n", st->pwr_down); -} - -static ssize_t ad5446_write_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, - const struct iio_chan_spec *chan, - const char *buf, size_t len) -{ - struct ad5446_state *st = iio_priv(indio_dev); - unsigned int shift; - unsigned int val; - bool powerdown; - int ret; - - ret = strtobool(buf, &powerdown); - if (ret) - return ret; - - mutex_lock(&indio_dev->mlock); - st->pwr_down = powerdown; - - if (st->pwr_down) { - shift = chan->scan_type.realbits + chan->scan_type.shift; - val = st->pwr_down_mode << shift; - } else { - val = st->cached_val; - } - - ret = st->chip_info->write(st, val); - mutex_unlock(&indio_dev->mlock); - - return ret ? ret : len; -} - -static const struct iio_chan_spec_ext_info ad5064_ext_info_powerdown[] = { - { - .name = "powerdown", - .read = ad5446_read_dac_powerdown, - .write = ad5446_write_dac_powerdown, - }, - IIO_ENUM("powerdown_mode", false, &ad5446_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5446_powerdown_mode_enum), - { }, -}; - -#define _AD5446_CHANNEL(bits, storage, shift, ext) { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .output = 1, \ - .channel = 0, \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SHARED_BIT, \ - .scan_type = IIO_ST('u', (bits), (storage), (shift)), \ - .ext_info = (ext), \ -} - -#define AD5446_CHANNEL(bits, storage, shift) \ - _AD5446_CHANNEL(bits, storage, shift, NULL) - -#define AD5446_CHANNEL_POWERDOWN(bits, storage, shift) \ - _AD5446_CHANNEL(bits, storage, shift, ad5064_ext_info_powerdown) - -static const struct ad5446_chip_info ad5446_chip_info_tbl[] = { - [ID_AD5444] = { - .channel = AD5446_CHANNEL(12, 16, 2), - .write = ad5446_write, - }, - [ID_AD5446] = { - .channel = AD5446_CHANNEL(14, 16, 0), - .write = ad5446_write, - }, - [ID_AD5541A] = { - .channel = AD5446_CHANNEL(16, 16, 0), - .write = ad5446_write, - }, - [ID_AD5512A] = { - .channel = AD5446_CHANNEL(12, 16, 4), - .write = ad5446_write, - }, - [ID_AD5553] = { - .channel = AD5446_CHANNEL(14, 16, 0), - .write = ad5446_write, - }, - [ID_AD5601] = { - .channel = AD5446_CHANNEL_POWERDOWN(8, 16, 6), - .write = ad5446_write, - }, - [ID_AD5611] = { - .channel = AD5446_CHANNEL_POWERDOWN(10, 16, 4), - .write = ad5446_write, - }, - [ID_AD5621] = { - .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2), - .write = ad5446_write, - }, - [ID_AD5620_2500] = { - .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2), - .int_vref_mv = 2500, - .write = ad5446_write, - }, - [ID_AD5620_1250] = { - .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2), - .int_vref_mv = 1250, - .write = ad5446_write, - }, - [ID_AD5640_2500] = { - .channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0), - .int_vref_mv = 2500, - .write = ad5446_write, - }, - [ID_AD5640_1250] = { - .channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0), - .int_vref_mv = 1250, - .write = ad5446_write, - }, - [ID_AD5660_2500] = { - .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0), - .int_vref_mv = 2500, - .write = ad5660_write, - }, - [ID_AD5660_1250] = { - .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0), - .int_vref_mv = 1250, - .write = ad5660_write, - }, - [ID_AD5662] = { - .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0), - .write = ad5660_write, - }, -}; - -static int ad5446_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long m) -{ - struct ad5446_state *st = iio_priv(indio_dev); - unsigned long scale_uv; - - switch (m) { - case IIO_CHAN_INFO_RAW: - *val = st->cached_val; - return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; - *val = scale_uv / 1000; - *val2 = (scale_uv % 1000) * 1000; - return IIO_VAL_INT_PLUS_MICRO; - - } - return -EINVAL; -} - -static int ad5446_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) -{ - struct ad5446_state *st = iio_priv(indio_dev); - int ret = 0; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - if (val >= (1 << chan->scan_type.realbits) || val < 0) - return -EINVAL; - - val <<= chan->scan_type.shift; - mutex_lock(&indio_dev->mlock); - st->cached_val = val; - if (!st->pwr_down) - ret = st->chip_info->write(st, val); - mutex_unlock(&indio_dev->mlock); - break; - default: - ret = -EINVAL; - } - - return ret; -} - -static const struct iio_info ad5446_info = { - .read_raw = ad5446_read_raw, - .write_raw = ad5446_write_raw, - .driver_module = THIS_MODULE, -}; - -static int __devinit ad5446_probe(struct spi_device *spi) -{ - struct ad5446_state *st; - struct iio_dev *indio_dev; - struct regulator *reg; - int ret, voltage_uv = 0; - - reg = regulator_get(&spi->dev, "vcc"); - if (!IS_ERR(reg)) { - ret = regulator_enable(reg); - if (ret) - goto error_put_reg; - - voltage_uv = regulator_get_voltage(reg); - } - - indio_dev = iio_device_alloc(sizeof(*st)); - if (indio_dev == NULL) { - ret = -ENOMEM; - goto error_disable_reg; - } - st = iio_priv(indio_dev); - st->chip_info = - &ad5446_chip_info_tbl[spi_get_device_id(spi)->driver_data]; - - spi_set_drvdata(spi, indio_dev); - st->reg = reg; - st->spi = spi; - - /* Establish that the iio_dev is a child of the spi device */ - indio_dev->dev.parent = &spi->dev; - indio_dev->name = spi_get_device_id(spi)->name; - indio_dev->info = &ad5446_info; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = &st->chip_info->channel; - indio_dev->num_channels = 1; - - st->pwr_down_mode = MODE_PWRDWN_1k; - - if (st->chip_info->int_vref_mv) - st->vref_mv = st->chip_info->int_vref_mv; - else if (voltage_uv) - st->vref_mv = voltage_uv / 1000; - else - dev_warn(&spi->dev, "reference voltage unspecified\n"); - - ret = iio_device_register(indio_dev); - if (ret) - goto error_free_device; - - return 0; - -error_free_device: - iio_device_free(indio_dev); -error_disable_reg: - if (!IS_ERR(reg)) - regulator_disable(reg); -error_put_reg: - if (!IS_ERR(reg)) - regulator_put(reg); - - return ret; -} - -static int ad5446_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad5446_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - if (!IS_ERR(st->reg)) { - regulator_disable(st->reg); - regulator_put(st->reg); - } - iio_device_free(indio_dev); - - return 0; -} - -static const struct spi_device_id ad5446_id[] = { - {"ad5444", ID_AD5444}, - {"ad5446", ID_AD5446}, - {"ad5512a", ID_AD5512A}, - {"ad5541a", ID_AD5541A}, - {"ad5542a", ID_AD5541A}, /* ad5541a and ad5542a are compatible */ - {"ad5543", ID_AD5541A}, /* ad5541a and ad5543 are compatible */ - {"ad5553", ID_AD5553}, - {"ad5601", ID_AD5601}, - {"ad5611", ID_AD5611}, - {"ad5621", ID_AD5621}, - {"ad5620-2500", ID_AD5620_2500}, /* AD5620/40/60: */ - {"ad5620-1250", ID_AD5620_1250}, /* part numbers may look differently */ - {"ad5640-2500", ID_AD5640_2500}, - {"ad5640-1250", ID_AD5640_1250}, - {"ad5660-2500", ID_AD5660_2500}, - {"ad5660-1250", ID_AD5660_1250}, - {"ad5662", ID_AD5662}, - {} -}; -MODULE_DEVICE_TABLE(spi, ad5446_id); - -static struct spi_driver ad5446_driver = { - .driver = { - .name = "ad5446", - .owner = THIS_MODULE, - }, - .probe = ad5446_probe, - .remove = __devexit_p(ad5446_remove), - .id_table = ad5446_id, -}; -module_spi_driver(ad5446_driver); - -MODULE_AUTHOR("Michael Hennerich "); -MODULE_DESCRIPTION("Analog Devices AD5444/AD5446 DAC"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/dac/ad5446.h b/drivers/staging/iio/dac/ad5446.h deleted file mode 100644 index dfd68ce7427e..000000000000 --- a/drivers/staging/iio/dac/ad5446.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * AD5446 SPI DAC driver - * - * Copyright 2010 Analog Devices Inc. - * - * Licensed under the GPL-2 or later. - */ -#ifndef IIO_DAC_AD5446_H_ -#define IIO_DAC_AD5446_H_ - -/* DAC Control Bits */ - -#define AD5446_LOAD (0x0 << 14) /* Load and update */ -#define AD5446_SDO_DIS (0x1 << 14) /* Disable SDO */ -#define AD5446_NOP (0x2 << 14) /* No operation */ -#define AD5446_CLK_RISING (0x3 << 14) /* Clock data on rising edge */ - -#define AD5620_LOAD (0x0 << 14) /* Load and update Norm Operation*/ -#define AD5620_PWRDWN_1k (0x1 << 14) /* Power-down: 1kOhm to GND */ -#define AD5620_PWRDWN_100k (0x2 << 14) /* Power-down: 100kOhm to GND */ -#define AD5620_PWRDWN_TRISTATE (0x3 << 14) /* Power-down: Three-state */ - -#define AD5660_LOAD (0x0 << 16) /* Load and update Norm Operation*/ -#define AD5660_PWRDWN_1k (0x1 << 16) /* Power-down: 1kOhm to GND */ -#define AD5660_PWRDWN_100k (0x2 << 16) /* Power-down: 100kOhm to GND */ -#define AD5660_PWRDWN_TRISTATE (0x3 << 16) /* Power-down: Three-state */ - -#define MODE_PWRDWN_1k 0x1 -#define MODE_PWRDWN_100k 0x2 -#define MODE_PWRDWN_TRISTATE 0x3 - -/** - * struct ad5446_state - driver instance specific data - * @spi: spi_device - * @chip_info: chip model specific constants, available modes etc - * @reg: supply regulator - * @vref_mv: actual reference voltage used - */ - -struct ad5446_state { - struct spi_device *spi; - const struct ad5446_chip_info *chip_info; - struct regulator *reg; - unsigned short vref_mv; - unsigned cached_val; - unsigned pwr_down_mode; - unsigned pwr_down; -}; - -/** - * struct ad5446_chip_info - chip specific information - * @channel: channel spec for the DAC - * @int_vref_mv: AD5620/40/60: the internal reference voltage - * @write: chip specific helper function to write to the register - */ - -struct ad5446_chip_info { - struct iio_chan_spec channel; - u16 int_vref_mv; - int (*write)(struct ad5446_state *st, unsigned val); -}; - -/** - * ad5446_supported_device_ids: - * The AD5620/40/60 parts are available in different fixed internal reference - * voltage options. The actual part numbers may look differently - * (and a bit cryptic), however this style is used to make clear which - * parts are supported here. - */ - -enum ad5446_supported_device_ids { - ID_AD5444, - ID_AD5446, - ID_AD5541A, - ID_AD5512A, - ID_AD5553, - ID_AD5601, - ID_AD5611, - ID_AD5621, - ID_AD5620_2500, - ID_AD5620_1250, - ID_AD5640_2500, - ID_AD5640_1250, - ID_AD5660_2500, - ID_AD5660_1250, - ID_AD5662, -}; - -#endif /* IIO_DAC_AD5446_H_ */ diff --git a/drivers/staging/iio/dac/ad5504.c b/drivers/staging/iio/dac/ad5504.c deleted file mode 100644 index 1289e9bc1688..000000000000 --- a/drivers/staging/iio/dac/ad5504.c +++ /dev/null @@ -1,394 +0,0 @@ -/* - * AD5504, AD5501 High Voltage Digital to Analog Converter - * - * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "ad5504.h" - -#define AD5505_BITS 12 -#define AD5504_RES_MASK ((1 << (AD5505_BITS)) - 1) - -#define AD5504_CMD_READ (1 << 15) -#define AD5504_CMD_WRITE (0 << 15) -#define AD5504_ADDR(addr) ((addr) << 12) - -/* Registers */ -#define AD5504_ADDR_NOOP 0 -#define AD5504_ADDR_DAC(x) ((x) + 1) -#define AD5504_ADDR_ALL_DAC 5 -#define AD5504_ADDR_CTRL 7 - -/* Control Register */ -#define AD5504_DAC_PWR(ch) ((ch) << 2) -#define AD5504_DAC_PWRDWN_MODE(mode) ((mode) << 6) -#define AD5504_DAC_PWRDN_20K 0 -#define AD5504_DAC_PWRDN_3STATE 1 - -/** - * struct ad5446_state - driver instance specific data - * @us: spi_device - * @reg: supply regulator - * @vref_mv: actual reference voltage used - * @pwr_down_mask power down mask - * @pwr_down_mode current power down mode - */ - -struct ad5504_state { - struct spi_device *spi; - struct regulator *reg; - unsigned short vref_mv; - unsigned pwr_down_mask; - unsigned pwr_down_mode; -}; - -/** - * ad5504_supported_device_ids: - */ - -enum ad5504_supported_device_ids { - ID_AD5504, - ID_AD5501, -}; - -static int ad5504_spi_write(struct spi_device *spi, u8 addr, u16 val) -{ - u16 tmp = cpu_to_be16(AD5504_CMD_WRITE | - AD5504_ADDR(addr) | - (val & AD5504_RES_MASK)); - - return spi_write(spi, (u8 *)&tmp, 2); -} - -static int ad5504_spi_read(struct spi_device *spi, u8 addr) -{ - u16 tmp = cpu_to_be16(AD5504_CMD_READ | AD5504_ADDR(addr)); - u16 val; - int ret; - struct spi_transfer t = { - .tx_buf = &tmp, - .rx_buf = &val, - .len = 2, - }; - struct spi_message m; - - spi_message_init(&m); - spi_message_add_tail(&t, &m); - ret = spi_sync(spi, &m); - - if (ret < 0) - return ret; - - return be16_to_cpu(val) & AD5504_RES_MASK; -} - -static int ad5504_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long m) -{ - struct ad5504_state *st = iio_priv(indio_dev); - unsigned long scale_uv; - int ret; - - switch (m) { - case IIO_CHAN_INFO_RAW: - ret = ad5504_spi_read(st->spi, chan->address); - if (ret < 0) - return ret; - - *val = ret; - - return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; - *val = scale_uv / 1000; - *val2 = (scale_uv % 1000) * 1000; - return IIO_VAL_INT_PLUS_MICRO; - - } - return -EINVAL; -} - -static int ad5504_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) -{ - struct ad5504_state *st = iio_priv(indio_dev); - int ret; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - if (val >= (1 << chan->scan_type.realbits) || val < 0) - return -EINVAL; - - return ad5504_spi_write(st->spi, chan->address, val); - default: - ret = -EINVAL; - } - - return -EINVAL; -} - -static const char * const ad5504_powerdown_modes[] = { - "20kohm_to_gnd", - "three_state", -}; - -static int ad5504_get_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - struct ad5504_state *st = iio_priv(indio_dev); - - return st->pwr_down_mode; -} - -static int ad5504_set_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int mode) -{ - struct ad5504_state *st = iio_priv(indio_dev); - - st->pwr_down_mode = mode; - - return 0; -} - -static const struct iio_enum ad5504_powerdown_mode_enum = { - .items = ad5504_powerdown_modes, - .num_items = ARRAY_SIZE(ad5504_powerdown_modes), - .get = ad5504_get_powerdown_mode, - .set = ad5504_set_powerdown_mode, -}; - -static ssize_t ad5504_read_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, char *buf) -{ - struct ad5504_state *st = iio_priv(indio_dev); - - return sprintf(buf, "%d\n", - !(st->pwr_down_mask & (1 << chan->channel))); -} - -static ssize_t ad5504_write_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, const char *buf, - size_t len) -{ - bool pwr_down; - int ret; - struct ad5504_state *st = iio_priv(indio_dev); - - ret = strtobool(buf, &pwr_down); - if (ret) - return ret; - - if (pwr_down) - st->pwr_down_mask |= (1 << chan->channel); - else - st->pwr_down_mask &= ~(1 << chan->channel); - - ret = ad5504_spi_write(st->spi, AD5504_ADDR_CTRL, - AD5504_DAC_PWRDWN_MODE(st->pwr_down_mode) | - AD5504_DAC_PWR(st->pwr_down_mask)); - - /* writes to the CTRL register must be followed by a NOOP */ - ad5504_spi_write(st->spi, AD5504_ADDR_NOOP, 0); - - return ret ? ret : len; -} - -static IIO_CONST_ATTR(temp0_thresh_rising_value, "110000"); -static IIO_CONST_ATTR(temp0_thresh_rising_en, "1"); - -static struct attribute *ad5504_ev_attributes[] = { - &iio_const_attr_temp0_thresh_rising_value.dev_attr.attr, - &iio_const_attr_temp0_thresh_rising_en.dev_attr.attr, - NULL, -}; - -static struct attribute_group ad5504_ev_attribute_group = { - .attrs = ad5504_ev_attributes, - .name = "events", -}; - -static irqreturn_t ad5504_event_handler(int irq, void *private) -{ - iio_push_event(private, - IIO_UNMOD_EVENT_CODE(IIO_TEMP, - 0, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_RISING), - iio_get_time_ns()); - - return IRQ_HANDLED; -} - -static const struct iio_info ad5504_info = { - .write_raw = ad5504_write_raw, - .read_raw = ad5504_read_raw, - .event_attrs = &ad5504_ev_attribute_group, - .driver_module = THIS_MODULE, -}; - -static const struct iio_chan_spec_ext_info ad5504_ext_info[] = { - { - .name = "powerdown", - .read = ad5504_read_dac_powerdown, - .write = ad5504_write_dac_powerdown, - }, - IIO_ENUM("powerdown_mode", true, &ad5504_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5504_powerdown_mode_enum), - { }, -}; - -#define AD5504_CHANNEL(_chan) { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .output = 1, \ - .channel = (_chan), \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SHARED_BIT, \ - .address = AD5504_ADDR_DAC(_chan), \ - .scan_type = IIO_ST('u', 12, 16, 0), \ - .ext_info = ad5504_ext_info, \ -} - -static const struct iio_chan_spec ad5504_channels[] = { - AD5504_CHANNEL(0), - AD5504_CHANNEL(1), - AD5504_CHANNEL(2), - AD5504_CHANNEL(3), -}; - -static int __devinit ad5504_probe(struct spi_device *spi) -{ - struct ad5504_platform_data *pdata = spi->dev.platform_data; - struct iio_dev *indio_dev; - struct ad5504_state *st; - struct regulator *reg; - int ret, voltage_uv = 0; - - indio_dev = iio_device_alloc(sizeof(*st)); - if (indio_dev == NULL) { - ret = -ENOMEM; - goto error_ret; - } - reg = regulator_get(&spi->dev, "vcc"); - if (!IS_ERR(reg)) { - ret = regulator_enable(reg); - if (ret) - goto error_put_reg; - - voltage_uv = regulator_get_voltage(reg); - } - - spi_set_drvdata(spi, indio_dev); - st = iio_priv(indio_dev); - if (voltage_uv) - st->vref_mv = voltage_uv / 1000; - else if (pdata) - st->vref_mv = pdata->vref_mv; - else - dev_warn(&spi->dev, "reference voltage unspecified\n"); - - st->reg = reg; - st->spi = spi; - indio_dev->dev.parent = &spi->dev; - indio_dev->name = spi_get_device_id(st->spi)->name; - indio_dev->info = &ad5504_info; - if (spi_get_device_id(st->spi)->driver_data == ID_AD5501) - indio_dev->num_channels = 1; - else - indio_dev->num_channels = 4; - indio_dev->channels = ad5504_channels; - indio_dev->modes = INDIO_DIRECT_MODE; - - if (spi->irq) { - ret = request_threaded_irq(spi->irq, - NULL, - &ad5504_event_handler, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - spi_get_device_id(st->spi)->name, - indio_dev); - if (ret) - goto error_disable_reg; - } - - ret = iio_device_register(indio_dev); - if (ret) - goto error_free_irq; - - return 0; - -error_free_irq: - if (spi->irq) - free_irq(spi->irq, indio_dev); -error_disable_reg: - if (!IS_ERR(reg)) - regulator_disable(reg); -error_put_reg: - if (!IS_ERR(reg)) - regulator_put(reg); - - iio_device_free(indio_dev); -error_ret: - return ret; -} - -static int __devexit ad5504_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad5504_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - if (spi->irq) - free_irq(spi->irq, indio_dev); - - if (!IS_ERR(st->reg)) { - regulator_disable(st->reg); - regulator_put(st->reg); - } - iio_device_free(indio_dev); - - return 0; -} - -static const struct spi_device_id ad5504_id[] = { - {"ad5504", ID_AD5504}, - {"ad5501", ID_AD5501}, - {} -}; -MODULE_DEVICE_TABLE(spi, ad5504_id); - -static struct spi_driver ad5504_driver = { - .driver = { - .name = "ad5504", - .owner = THIS_MODULE, - }, - .probe = ad5504_probe, - .remove = __devexit_p(ad5504_remove), - .id_table = ad5504_id, -}; -module_spi_driver(ad5504_driver); - -MODULE_AUTHOR("Michael Hennerich "); -MODULE_DESCRIPTION("Analog Devices AD5501/AD5501 DAC"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/dac/ad5504.h b/drivers/staging/iio/dac/ad5504.h deleted file mode 100644 index d4980bf688bf..000000000000 --- a/drivers/staging/iio/dac/ad5504.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * AD5504 SPI DAC driver - * - * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#ifndef SPI_AD5504_H_ -#define SPI_AD5504_H_ - -/* - * TODO: struct ad5504_platform_data needs to go into include/linux/iio - */ - -struct ad5504_platform_data { - u16 vref_mv; -}; - -#endif /* SPI_AD5504_H_ */ diff --git a/drivers/staging/iio/dac/ad5624r.h b/drivers/staging/iio/dac/ad5624r.h deleted file mode 100644 index 5dca3028cdfd..000000000000 --- a/drivers/staging/iio/dac/ad5624r.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * AD5624R SPI DAC driver - * - * Copyright 2010-2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ -#ifndef SPI_AD5624R_H_ -#define SPI_AD5624R_H_ - -#define AD5624R_DAC_CHANNELS 4 - -#define AD5624R_ADDR_DAC0 0x0 -#define AD5624R_ADDR_DAC1 0x1 -#define AD5624R_ADDR_DAC2 0x2 -#define AD5624R_ADDR_DAC3 0x3 -#define AD5624R_ADDR_ALL_DAC 0x7 - -#define AD5624R_CMD_WRITE_INPUT_N 0x0 -#define AD5624R_CMD_UPDATE_DAC_N 0x1 -#define AD5624R_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2 -#define AD5624R_CMD_WRITE_INPUT_N_UPDATE_N 0x3 -#define AD5624R_CMD_POWERDOWN_DAC 0x4 -#define AD5624R_CMD_RESET 0x5 -#define AD5624R_CMD_LDAC_SETUP 0x6 -#define AD5624R_CMD_INTERNAL_REFER_SETUP 0x7 - -#define AD5624R_LDAC_PWRDN_NONE 0x0 -#define AD5624R_LDAC_PWRDN_1K 0x1 -#define AD5624R_LDAC_PWRDN_100K 0x2 -#define AD5624R_LDAC_PWRDN_3STATE 0x3 - -/** - * struct ad5624r_chip_info - chip specific information - * @channels: channel spec for the DAC - * @int_vref_mv: AD5620/40/60: the internal reference voltage - */ - -struct ad5624r_chip_info { - const struct iio_chan_spec *channels; - u16 int_vref_mv; -}; - -/** - * struct ad5446_state - driver instance specific data - * @indio_dev: the industrial I/O device - * @us: spi_device - * @chip_info: chip model specific constants, available modes etc - * @reg: supply regulator - * @vref_mv: actual reference voltage used - * @pwr_down_mask power down mask - * @pwr_down_mode current power down mode - */ - -struct ad5624r_state { - struct spi_device *us; - const struct ad5624r_chip_info *chip_info; - struct regulator *reg; - unsigned short vref_mv; - unsigned pwr_down_mask; - unsigned pwr_down_mode; -}; - -/** - * ad5624r_supported_device_ids: - * The AD5624/44/64 parts are available in different - * fixed internal reference voltage options. - */ - -enum ad5624r_supported_device_ids { - ID_AD5624R3, - ID_AD5644R3, - ID_AD5664R3, - ID_AD5624R5, - ID_AD5644R5, - ID_AD5664R5, -}; - -#endif /* SPI_AD5624R_H_ */ diff --git a/drivers/staging/iio/dac/ad5624r_spi.c b/drivers/staging/iio/dac/ad5624r_spi.c deleted file mode 100644 index 6a7d6a48cc6d..000000000000 --- a/drivers/staging/iio/dac/ad5624r_spi.c +++ /dev/null @@ -1,324 +0,0 @@ -/* - * AD5624R, AD5644R, AD5664R Digital to analog convertors spi driver - * - * Copyright 2010-2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "ad5624r.h" - -static int ad5624r_spi_write(struct spi_device *spi, - u8 cmd, u8 addr, u16 val, u8 len) -{ - u32 data; - u8 msg[3]; - - /* - * The input shift register is 24 bits wide. The first two bits are - * don't care bits. The next three are the command bits, C2 to C0, - * followed by the 3-bit DAC address, A2 to A0, and then the - * 16-, 14-, 12-bit data-word. The data-word comprises the 16-, - * 14-, 12-bit input code followed by 0, 2, or 4 don't care bits, - * for the AD5664R, AD5644R, and AD5624R, respectively. - */ - data = (0 << 22) | (cmd << 19) | (addr << 16) | (val << (16 - len)); - msg[0] = data >> 16; - msg[1] = data >> 8; - msg[2] = data; - - return spi_write(spi, msg, 3); -} - -static int ad5624r_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long m) -{ - struct ad5624r_state *st = iio_priv(indio_dev); - unsigned long scale_uv; - - switch (m) { - case IIO_CHAN_INFO_SCALE: - scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; - *val = scale_uv / 1000; - *val2 = (scale_uv % 1000) * 1000; - return IIO_VAL_INT_PLUS_MICRO; - - } - return -EINVAL; -} - -static int ad5624r_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) -{ - struct ad5624r_state *st = iio_priv(indio_dev); - int ret; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - if (val >= (1 << chan->scan_type.realbits) || val < 0) - return -EINVAL; - - return ad5624r_spi_write(st->us, - AD5624R_CMD_WRITE_INPUT_N_UPDATE_N, - chan->address, val, - chan->scan_type.shift); - default: - ret = -EINVAL; - } - - return -EINVAL; -} - -static const char * const ad5624r_powerdown_modes[] = { - "1kohm_to_gnd", - "100kohm_to_gnd", - "three_state" -}; - -static int ad5624r_get_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - struct ad5624r_state *st = iio_priv(indio_dev); - - return st->pwr_down_mode; -} - -static int ad5624r_set_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int mode) -{ - struct ad5624r_state *st = iio_priv(indio_dev); - - st->pwr_down_mode = mode; - - return 0; -} - -static const struct iio_enum ad5624r_powerdown_mode_enum = { - .items = ad5624r_powerdown_modes, - .num_items = ARRAY_SIZE(ad5624r_powerdown_modes), - .get = ad5624r_get_powerdown_mode, - .set = ad5624r_set_powerdown_mode, -}; - -static ssize_t ad5624r_read_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, char *buf) -{ - struct ad5624r_state *st = iio_priv(indio_dev); - - return sprintf(buf, "%d\n", - !!(st->pwr_down_mask & (1 << chan->channel))); -} - -static ssize_t ad5624r_write_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, const char *buf, - size_t len) -{ - bool pwr_down; - int ret; - struct ad5624r_state *st = iio_priv(indio_dev); - - ret = strtobool(buf, &pwr_down); - if (ret) - return ret; - - if (pwr_down) - st->pwr_down_mask |= (1 << chan->channel); - else - st->pwr_down_mask &= ~(1 << chan->channel); - - ret = ad5624r_spi_write(st->us, AD5624R_CMD_POWERDOWN_DAC, 0, - (st->pwr_down_mode << 4) | - st->pwr_down_mask, 16); - - return ret ? ret : len; -} - -static const struct iio_info ad5624r_info = { - .write_raw = ad5624r_write_raw, - .read_raw = ad5624r_read_raw, - .driver_module = THIS_MODULE, -}; - -static const struct iio_chan_spec_ext_info ad5624r_ext_info[] = { - { - .name = "powerdown", - .read = ad5624r_read_dac_powerdown, - .write = ad5624r_write_dac_powerdown, - }, - IIO_ENUM("powerdown_mode", true, &ad5624r_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5624r_powerdown_mode_enum), - { }, -}; - -#define AD5624R_CHANNEL(_chan, _bits) { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .output = 1, \ - .channel = (_chan), \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SHARED_BIT, \ - .address = (_chan), \ - .scan_type = IIO_ST('u', (_bits), 16, 16 - (_bits)), \ - .ext_info = ad5624r_ext_info, \ -} - -#define DECLARE_AD5624R_CHANNELS(_name, _bits) \ - const struct iio_chan_spec _name##_channels[] = { \ - AD5624R_CHANNEL(0, _bits), \ - AD5624R_CHANNEL(1, _bits), \ - AD5624R_CHANNEL(2, _bits), \ - AD5624R_CHANNEL(3, _bits), \ -} - -static DECLARE_AD5624R_CHANNELS(ad5624r, 12); -static DECLARE_AD5624R_CHANNELS(ad5644r, 14); -static DECLARE_AD5624R_CHANNELS(ad5664r, 16); - -static const struct ad5624r_chip_info ad5624r_chip_info_tbl[] = { - [ID_AD5624R3] = { - .channels = ad5624r_channels, - .int_vref_mv = 1250, - }, - [ID_AD5624R5] = { - .channels = ad5624r_channels, - .int_vref_mv = 2500, - }, - [ID_AD5644R3] = { - .channels = ad5644r_channels, - .int_vref_mv = 1250, - }, - [ID_AD5644R5] = { - .channels = ad5644r_channels, - .int_vref_mv = 2500, - }, - [ID_AD5664R3] = { - .channels = ad5664r_channels, - .int_vref_mv = 1250, - }, - [ID_AD5664R5] = { - .channels = ad5664r_channels, - .int_vref_mv = 2500, - }, -}; - -static int __devinit ad5624r_probe(struct spi_device *spi) -{ - struct ad5624r_state *st; - struct iio_dev *indio_dev; - int ret, voltage_uv = 0; - - indio_dev = iio_device_alloc(sizeof(*st)); - if (indio_dev == NULL) { - ret = -ENOMEM; - goto error_ret; - } - st = iio_priv(indio_dev); - st->reg = regulator_get(&spi->dev, "vcc"); - if (!IS_ERR(st->reg)) { - ret = regulator_enable(st->reg); - if (ret) - goto error_put_reg; - - voltage_uv = regulator_get_voltage(st->reg); - } - - spi_set_drvdata(spi, indio_dev); - st->chip_info = - &ad5624r_chip_info_tbl[spi_get_device_id(spi)->driver_data]; - - if (voltage_uv) - st->vref_mv = voltage_uv / 1000; - else - st->vref_mv = st->chip_info->int_vref_mv; - - st->us = spi; - - indio_dev->dev.parent = &spi->dev; - indio_dev->name = spi_get_device_id(spi)->name; - indio_dev->info = &ad5624r_info; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = st->chip_info->channels; - indio_dev->num_channels = AD5624R_DAC_CHANNELS; - - ret = ad5624r_spi_write(spi, AD5624R_CMD_INTERNAL_REFER_SETUP, 0, - !!voltage_uv, 16); - if (ret) - goto error_disable_reg; - - ret = iio_device_register(indio_dev); - if (ret) - goto error_disable_reg; - - return 0; - -error_disable_reg: - if (!IS_ERR(st->reg)) - regulator_disable(st->reg); -error_put_reg: - if (!IS_ERR(st->reg)) - regulator_put(st->reg); - iio_device_free(indio_dev); -error_ret: - - return ret; -} - -static int __devexit ad5624r_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad5624r_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - if (!IS_ERR(st->reg)) { - regulator_disable(st->reg); - regulator_put(st->reg); - } - iio_device_free(indio_dev); - - return 0; -} - -static const struct spi_device_id ad5624r_id[] = { - {"ad5624r3", ID_AD5624R3}, - {"ad5644r3", ID_AD5644R3}, - {"ad5664r3", ID_AD5664R3}, - {"ad5624r5", ID_AD5624R5}, - {"ad5644r5", ID_AD5644R5}, - {"ad5664r5", ID_AD5664R5}, - {} -}; -MODULE_DEVICE_TABLE(spi, ad5624r_id); - -static struct spi_driver ad5624r_driver = { - .driver = { - .name = "ad5624r", - .owner = THIS_MODULE, - }, - .probe = ad5624r_probe, - .remove = __devexit_p(ad5624r_remove), - .id_table = ad5624r_id, -}; -module_spi_driver(ad5624r_driver); - -MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); -MODULE_DESCRIPTION("Analog Devices AD5624/44/64R DAC spi driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/dac/ad5686.c b/drivers/staging/iio/dac/ad5686.c deleted file mode 100644 index 6948d75e1036..000000000000 --- a/drivers/staging/iio/dac/ad5686.c +++ /dev/null @@ -1,418 +0,0 @@ -/* - * AD5686R, AD5685R, AD5684R Digital to analog converters driver - * - * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define AD5686_DAC_CHANNELS 4 - -#define AD5686_ADDR(x) ((x) << 16) -#define AD5686_CMD(x) ((x) << 20) - -#define AD5686_ADDR_DAC(chan) (0x1 << (chan)) -#define AD5686_ADDR_ALL_DAC 0xF - -#define AD5686_CMD_NOOP 0x0 -#define AD5686_CMD_WRITE_INPUT_N 0x1 -#define AD5686_CMD_UPDATE_DAC_N 0x2 -#define AD5686_CMD_WRITE_INPUT_N_UPDATE_N 0x3 -#define AD5686_CMD_POWERDOWN_DAC 0x4 -#define AD5686_CMD_LDAC_MASK 0x5 -#define AD5686_CMD_RESET 0x6 -#define AD5686_CMD_INTERNAL_REFER_SETUP 0x7 -#define AD5686_CMD_DAISY_CHAIN_ENABLE 0x8 -#define AD5686_CMD_READBACK_ENABLE 0x9 - -#define AD5686_LDAC_PWRDN_NONE 0x0 -#define AD5686_LDAC_PWRDN_1K 0x1 -#define AD5686_LDAC_PWRDN_100K 0x2 -#define AD5686_LDAC_PWRDN_3STATE 0x3 - -/** - * struct ad5686_chip_info - chip specific information - * @int_vref_mv: AD5620/40/60: the internal reference voltage - * @channel: channel specification -*/ - -struct ad5686_chip_info { - u16 int_vref_mv; - struct iio_chan_spec channel[AD5686_DAC_CHANNELS]; -}; - -/** - * struct ad5446_state - driver instance specific data - * @spi: spi_device - * @chip_info: chip model specific constants, available modes etc - * @reg: supply regulator - * @vref_mv: actual reference voltage used - * @pwr_down_mask: power down mask - * @pwr_down_mode: current power down mode - * @data: spi transfer buffers - */ - -struct ad5686_state { - struct spi_device *spi; - const struct ad5686_chip_info *chip_info; - struct regulator *reg; - unsigned short vref_mv; - unsigned pwr_down_mask; - unsigned pwr_down_mode; - /* - * DMA (thus cache coherency maintenance) requires the - * transfer buffers to live in their own cache lines. - */ - - union { - u32 d32; - u8 d8[4]; - } data[3] ____cacheline_aligned; -}; - -/** - * ad5686_supported_device_ids: - */ - -enum ad5686_supported_device_ids { - ID_AD5684, - ID_AD5685, - ID_AD5686, -}; -static int ad5686_spi_write(struct ad5686_state *st, - u8 cmd, u8 addr, u16 val, u8 shift) -{ - val <<= shift; - - st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | - AD5686_ADDR(addr) | - val); - - return spi_write(st->spi, &st->data[0].d8[1], 3); -} - -static int ad5686_spi_read(struct ad5686_state *st, u8 addr) -{ - struct spi_transfer t[] = { - { - .tx_buf = &st->data[0].d8[1], - .len = 3, - .cs_change = 1, - }, { - .tx_buf = &st->data[1].d8[1], - .rx_buf = &st->data[2].d8[1], - .len = 3, - }, - }; - struct spi_message m; - int ret; - - spi_message_init(&m); - spi_message_add_tail(&t[0], &m); - spi_message_add_tail(&t[1], &m); - - st->data[0].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_READBACK_ENABLE) | - AD5686_ADDR(addr)); - st->data[1].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP)); - - ret = spi_sync(st->spi, &m); - if (ret < 0) - return ret; - - return be32_to_cpu(st->data[2].d32); -} - -static const char * const ad5686_powerdown_modes[] = { - "1kohm_to_gnd", - "100kohm_to_gnd", - "three_state" -}; - -static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - struct ad5686_state *st = iio_priv(indio_dev); - - return ((st->pwr_down_mode >> (chan->channel * 2)) & 0x3) - 1; -} - -static int ad5686_set_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int mode) -{ - struct ad5686_state *st = iio_priv(indio_dev); - - st->pwr_down_mode &= ~(0x3 << (chan->channel * 2)); - st->pwr_down_mode |= ((mode + 1) << (chan->channel * 2)); - - return 0; -} - -static const struct iio_enum ad5686_powerdown_mode_enum = { - .items = ad5686_powerdown_modes, - .num_items = ARRAY_SIZE(ad5686_powerdown_modes), - .get = ad5686_get_powerdown_mode, - .set = ad5686_set_powerdown_mode, -}; - -static ssize_t ad5686_read_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, char *buf) -{ - struct ad5686_state *st = iio_priv(indio_dev); - - return sprintf(buf, "%d\n", !!(st->pwr_down_mask & - (0x3 << (chan->channel * 2)))); -} - -static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, const char *buf, - size_t len) -{ - bool readin; - int ret; - struct ad5686_state *st = iio_priv(indio_dev); - - ret = strtobool(buf, &readin); - if (ret) - return ret; - - if (readin == true) - st->pwr_down_mask |= (0x3 << (chan->channel * 2)); - else - st->pwr_down_mask &= ~(0x3 << (chan->channel * 2)); - - ret = ad5686_spi_write(st, AD5686_CMD_POWERDOWN_DAC, 0, - st->pwr_down_mask & st->pwr_down_mode, 0); - - return ret ? ret : len; -} - -static int ad5686_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long m) -{ - struct ad5686_state *st = iio_priv(indio_dev); - unsigned long scale_uv; - int ret; - - switch (m) { - case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); - ret = ad5686_spi_read(st, chan->address); - mutex_unlock(&indio_dev->mlock); - if (ret < 0) - return ret; - *val = ret; - return IIO_VAL_INT; - break; - case IIO_CHAN_INFO_SCALE: - scale_uv = (st->vref_mv * 100000) - >> (chan->scan_type.realbits); - *val = scale_uv / 100000; - *val2 = (scale_uv % 100000) * 10; - return IIO_VAL_INT_PLUS_MICRO; - - } - return -EINVAL; -} - -static int ad5686_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) -{ - struct ad5686_state *st = iio_priv(indio_dev); - int ret; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - if (val > (1 << chan->scan_type.realbits) || val < 0) - return -EINVAL; - - mutex_lock(&indio_dev->mlock); - ret = ad5686_spi_write(st, - AD5686_CMD_WRITE_INPUT_N_UPDATE_N, - chan->address, - val, - chan->scan_type.shift); - mutex_unlock(&indio_dev->mlock); - break; - default: - ret = -EINVAL; - } - - return ret; -} - -static const struct iio_info ad5686_info = { - .read_raw = ad5686_read_raw, - .write_raw = ad5686_write_raw, - .driver_module = THIS_MODULE, -}; - -static const struct iio_chan_spec_ext_info ad5686_ext_info[] = { - { - .name = "powerdown", - .read = ad5686_read_dac_powerdown, - .write = ad5686_write_dac_powerdown, - }, - IIO_ENUM("powerdown_mode", false, &ad5686_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5686_powerdown_mode_enum), - { }, -}; - -#define AD5868_CHANNEL(chan, bits, shift) { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .output = 1, \ - .channel = chan, \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SHARED_BIT, \ - .address = AD5686_ADDR_DAC(chan), \ - .scan_type = IIO_ST('u', bits, 16, shift), \ - .ext_info = ad5686_ext_info, \ -} - -static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { - [ID_AD5684] = { - .channel[0] = AD5868_CHANNEL(0, 12, 4), - .channel[1] = AD5868_CHANNEL(1, 12, 4), - .channel[2] = AD5868_CHANNEL(2, 12, 4), - .channel[3] = AD5868_CHANNEL(3, 12, 4), - .int_vref_mv = 2500, - }, - [ID_AD5685] = { - .channel[0] = AD5868_CHANNEL(0, 14, 2), - .channel[1] = AD5868_CHANNEL(1, 14, 2), - .channel[2] = AD5868_CHANNEL(2, 14, 2), - .channel[3] = AD5868_CHANNEL(3, 14, 2), - .int_vref_mv = 2500, - }, - [ID_AD5686] = { - .channel[0] = AD5868_CHANNEL(0, 16, 0), - .channel[1] = AD5868_CHANNEL(1, 16, 0), - .channel[2] = AD5868_CHANNEL(2, 16, 0), - .channel[3] = AD5868_CHANNEL(3, 16, 0), - .int_vref_mv = 2500, - }, -}; - - -static int __devinit ad5686_probe(struct spi_device *spi) -{ - struct ad5686_state *st; - struct iio_dev *indio_dev; - int ret, regdone = 0, voltage_uv = 0; - - indio_dev = iio_device_alloc(sizeof(*st)); - if (indio_dev == NULL) - return -ENOMEM; - - st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); - - st->reg = regulator_get(&spi->dev, "vcc"); - if (!IS_ERR(st->reg)) { - ret = regulator_enable(st->reg); - if (ret) - goto error_put_reg; - - voltage_uv = regulator_get_voltage(st->reg); - } - - st->chip_info = - &ad5686_chip_info_tbl[spi_get_device_id(spi)->driver_data]; - - if (voltage_uv) - st->vref_mv = voltage_uv / 1000; - else - st->vref_mv = st->chip_info->int_vref_mv; - - st->spi = spi; - - /* Set all the power down mode for all channels to 1K pulldown */ - st->pwr_down_mode = 0x55; - - indio_dev->dev.parent = &spi->dev; - indio_dev->name = spi_get_device_id(spi)->name; - indio_dev->info = &ad5686_info; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = st->chip_info->channel; - indio_dev->num_channels = AD5686_DAC_CHANNELS; - - regdone = 1; - ret = ad5686_spi_write(st, AD5686_CMD_INTERNAL_REFER_SETUP, 0, - !!voltage_uv, 0); - if (ret) - goto error_disable_reg; - - ret = iio_device_register(indio_dev); - if (ret) - goto error_disable_reg; - - return 0; - -error_disable_reg: - if (!IS_ERR(st->reg)) - regulator_disable(st->reg); -error_put_reg: - if (!IS_ERR(st->reg)) - regulator_put(st->reg); - - iio_device_free(indio_dev); - - return ret; -} - -static int __devexit ad5686_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad5686_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - if (!IS_ERR(st->reg)) { - regulator_disable(st->reg); - regulator_put(st->reg); - } - iio_device_free(indio_dev); - - return 0; -} - -static const struct spi_device_id ad5686_id[] = { - {"ad5684", ID_AD5684}, - {"ad5685", ID_AD5685}, - {"ad5686", ID_AD5686}, - {} -}; -MODULE_DEVICE_TABLE(spi, ad5686_id); - -static struct spi_driver ad5686_driver = { - .driver = { - .name = "ad5686", - .owner = THIS_MODULE, - }, - .probe = ad5686_probe, - .remove = __devexit_p(ad5686_remove), - .id_table = ad5686_id, -}; -module_spi_driver(ad5686_driver); - -MODULE_AUTHOR("Michael Hennerich "); -MODULE_DESCRIPTION("Analog Devices AD5686/85/84 DAC"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/dac/ad5764.c b/drivers/staging/iio/dac/ad5764.c deleted file mode 100644 index ffce30447445..000000000000 --- a/drivers/staging/iio/dac/ad5764.c +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Analog devices AD5764, AD5764R, AD5744, AD5744R quad-channel - * Digital to Analog Converters driver - * - * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define AD5764_REG_SF_NOP 0x0 -#define AD5764_REG_SF_CONFIG 0x1 -#define AD5764_REG_SF_CLEAR 0x4 -#define AD5764_REG_SF_LOAD 0x5 -#define AD5764_REG_DATA(x) ((2 << 3) | (x)) -#define AD5764_REG_COARSE_GAIN(x) ((3 << 3) | (x)) -#define AD5764_REG_FINE_GAIN(x) ((4 << 3) | (x)) -#define AD5764_REG_OFFSET(x) ((5 << 3) | (x)) - -#define AD5764_NUM_CHANNELS 4 - -/** - * struct ad5764_chip_info - chip specific information - * @int_vref: Value of the internal reference voltage in uV - 0 if external - * reference voltage is used - * @channel channel specification -*/ - -struct ad5764_chip_info { - unsigned long int_vref; - const struct iio_chan_spec *channels; -}; - -/** - * struct ad5764_state - driver instance specific data - * @spi: spi_device - * @chip_info: chip info - * @vref_reg: vref supply regulators - * @data: spi transfer buffers - */ - -struct ad5764_state { - struct spi_device *spi; - const struct ad5764_chip_info *chip_info; - struct regulator_bulk_data vref_reg[2]; - - /* - * DMA (thus cache coherency maintenance) requires the - * transfer buffers to live in their own cache lines. - */ - union { - __be32 d32; - u8 d8[4]; - } data[2] ____cacheline_aligned; -}; - -enum ad5764_type { - ID_AD5744, - ID_AD5744R, - ID_AD5764, - ID_AD5764R, -}; - -#define AD5764_CHANNEL(_chan, _bits) { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .output = 1, \ - .channel = (_chan), \ - .address = (_chan), \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_OFFSET_SHARED_BIT | \ - IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \ - IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | \ - IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, \ - .scan_type = IIO_ST('u', (_bits), 16, 16 - (_bits)) \ -} - -#define DECLARE_AD5764_CHANNELS(_name, _bits) \ -const struct iio_chan_spec _name##_channels[] = { \ - AD5764_CHANNEL(0, (_bits)), \ - AD5764_CHANNEL(1, (_bits)), \ - AD5764_CHANNEL(2, (_bits)), \ - AD5764_CHANNEL(3, (_bits)), \ -}; - -static DECLARE_AD5764_CHANNELS(ad5764, 16); -static DECLARE_AD5764_CHANNELS(ad5744, 14); - -static const struct ad5764_chip_info ad5764_chip_infos[] = { - [ID_AD5744] = { - .int_vref = 0, - .channels = ad5744_channels, - }, - [ID_AD5744R] = { - .int_vref = 5000000, - .channels = ad5744_channels, - }, - [ID_AD5764] = { - .int_vref = 0, - .channels = ad5764_channels, - }, - [ID_AD5764R] = { - .int_vref = 5000000, - .channels = ad5764_channels, - }, -}; - -static int ad5764_write(struct iio_dev *indio_dev, unsigned int reg, - unsigned int val) -{ - struct ad5764_state *st = iio_priv(indio_dev); - int ret; - - mutex_lock(&indio_dev->mlock); - st->data[0].d32 = cpu_to_be32((reg << 16) | val); - - ret = spi_write(st->spi, &st->data[0].d8[1], 3); - mutex_unlock(&indio_dev->mlock); - - return ret; -} - -static int ad5764_read(struct iio_dev *indio_dev, unsigned int reg, - unsigned int *val) -{ - struct ad5764_state *st = iio_priv(indio_dev); - struct spi_message m; - int ret; - struct spi_transfer t[] = { - { - .tx_buf = &st->data[0].d8[1], - .len = 3, - .cs_change = 1, - }, { - .rx_buf = &st->data[1].d8[1], - .len = 3, - }, - }; - - spi_message_init(&m); - spi_message_add_tail(&t[0], &m); - spi_message_add_tail(&t[1], &m); - - mutex_lock(&indio_dev->mlock); - - st->data[0].d32 = cpu_to_be32((1 << 23) | (reg << 16)); - - ret = spi_sync(st->spi, &m); - if (ret >= 0) - *val = be32_to_cpu(st->data[1].d32) & 0xffff; - - mutex_unlock(&indio_dev->mlock); - - return ret; -} - -static int ad5764_chan_info_to_reg(struct iio_chan_spec const *chan, long info) -{ - switch (info) { - case 0: - return AD5764_REG_DATA(chan->address); - case IIO_CHAN_INFO_CALIBBIAS: - return AD5764_REG_OFFSET(chan->address); - case IIO_CHAN_INFO_CALIBSCALE: - return AD5764_REG_FINE_GAIN(chan->address); - default: - break; - } - - return 0; -} - -static int ad5764_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int val, int val2, long info) -{ - const int max_val = (1 << chan->scan_type.realbits); - unsigned int reg; - - switch (info) { - case IIO_CHAN_INFO_RAW: - if (val >= max_val || val < 0) - return -EINVAL; - val <<= chan->scan_type.shift; - break; - case IIO_CHAN_INFO_CALIBBIAS: - if (val >= 128 || val < -128) - return -EINVAL; - break; - case IIO_CHAN_INFO_CALIBSCALE: - if (val >= 32 || val < -32) - return -EINVAL; - break; - default: - return -EINVAL; - } - - reg = ad5764_chan_info_to_reg(chan, info); - return ad5764_write(indio_dev, reg, (u16)val); -} - -static int ad5764_get_channel_vref(struct ad5764_state *st, - unsigned int channel) -{ - if (st->chip_info->int_vref) - return st->chip_info->int_vref; - else - return regulator_get_voltage(st->vref_reg[channel / 2].consumer); -} - -static int ad5764_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int *val, int *val2, long info) -{ - struct ad5764_state *st = iio_priv(indio_dev); - unsigned long scale_uv; - unsigned int reg; - int vref; - int ret; - - switch (info) { - case IIO_CHAN_INFO_RAW: - reg = AD5764_REG_DATA(chan->address); - ret = ad5764_read(indio_dev, reg, val); - if (ret < 0) - return ret; - *val >>= chan->scan_type.shift; - return IIO_VAL_INT; - case IIO_CHAN_INFO_CALIBBIAS: - reg = AD5764_REG_OFFSET(chan->address); - ret = ad5764_read(indio_dev, reg, val); - if (ret < 0) - return ret; - *val = sign_extend32(*val, 7); - return IIO_VAL_INT; - case IIO_CHAN_INFO_CALIBSCALE: - reg = AD5764_REG_FINE_GAIN(chan->address); - ret = ad5764_read(indio_dev, reg, val); - if (ret < 0) - return ret; - *val = sign_extend32(*val, 5); - return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - /* vout = 4 * vref + ((dac_code / 65535) - 0.5) */ - vref = ad5764_get_channel_vref(st, chan->channel); - if (vref < 0) - return vref; - - scale_uv = (vref * 4 * 100) >> chan->scan_type.realbits; - *val = scale_uv / 100000; - *val2 = (scale_uv % 100000) * 10; - return IIO_VAL_INT_PLUS_MICRO; - case IIO_CHAN_INFO_OFFSET: - *val = -(1 << chan->scan_type.realbits) / 2; - return IIO_VAL_INT; - } - - return -EINVAL; -} - -static const struct iio_info ad5764_info = { - .read_raw = ad5764_read_raw, - .write_raw = ad5764_write_raw, - .driver_module = THIS_MODULE, -}; - -static int __devinit ad5764_probe(struct spi_device *spi) -{ - enum ad5764_type type = spi_get_device_id(spi)->driver_data; - struct iio_dev *indio_dev; - struct ad5764_state *st; - int ret; - - indio_dev = iio_device_alloc(sizeof(*st)); - if (indio_dev == NULL) { - dev_err(&spi->dev, "Failed to allocate iio device\n"); - return -ENOMEM; - } - - st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); - - st->spi = spi; - st->chip_info = &ad5764_chip_infos[type]; - - indio_dev->dev.parent = &spi->dev; - indio_dev->name = spi_get_device_id(spi)->name; - indio_dev->info = &ad5764_info; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->num_channels = AD5764_NUM_CHANNELS; - indio_dev->channels = st->chip_info->channels; - - if (st->chip_info->int_vref == 0) { - st->vref_reg[0].supply = "vrefAB"; - st->vref_reg[1].supply = "vrefCD"; - - ret = regulator_bulk_get(&st->spi->dev, - ARRAY_SIZE(st->vref_reg), st->vref_reg); - if (ret) { - dev_err(&spi->dev, "Failed to request vref regulators: %d\n", - ret); - goto error_free; - } - - ret = regulator_bulk_enable(ARRAY_SIZE(st->vref_reg), - st->vref_reg); - if (ret) { - dev_err(&spi->dev, "Failed to enable vref regulators: %d\n", - ret); - goto error_free_reg; - } - } - - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&spi->dev, "Failed to register iio device: %d\n", ret); - goto error_disable_reg; - } - - return 0; - -error_disable_reg: - if (st->chip_info->int_vref == 0) - regulator_bulk_disable(ARRAY_SIZE(st->vref_reg), st->vref_reg); -error_free_reg: - if (st->chip_info->int_vref == 0) - regulator_bulk_free(ARRAY_SIZE(st->vref_reg), st->vref_reg); -error_free: - iio_device_free(indio_dev); - - return ret; -} - -static int __devexit ad5764_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad5764_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - if (st->chip_info->int_vref == 0) { - regulator_bulk_disable(ARRAY_SIZE(st->vref_reg), st->vref_reg); - regulator_bulk_free(ARRAY_SIZE(st->vref_reg), st->vref_reg); - } - - iio_device_free(indio_dev); - - return 0; -} - -static const struct spi_device_id ad5764_ids[] = { - { "ad5744", ID_AD5744 }, - { "ad5744r", ID_AD5744R }, - { "ad5764", ID_AD5764 }, - { "ad5764r", ID_AD5764R }, - { } -}; -MODULE_DEVICE_TABLE(spi, ad5764_ids); - -static struct spi_driver ad5764_driver = { - .driver = { - .name = "ad5764", - .owner = THIS_MODULE, - }, - .probe = ad5764_probe, - .remove = __devexit_p(ad5764_remove), - .id_table = ad5764_ids, -}; -module_spi_driver(ad5764_driver); - -MODULE_AUTHOR("Lars-Peter Clausen "); -MODULE_DESCRIPTION("Analog Devices AD5744/AD5744R/AD5764/AD5764R DAC"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/dac/ad5791.c b/drivers/staging/iio/dac/ad5791.c deleted file mode 100644 index 5de28c2a57ef..000000000000 --- a/drivers/staging/iio/dac/ad5791.c +++ /dev/null @@ -1,486 +0,0 @@ -/* - * AD5760, AD5780, AD5781, AD5790, AD5791 Voltage Output Digital to Analog - * Converter - * - * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "ad5791.h" - -#define AD5791_RES_MASK(x) ((1 << (x)) - 1) -#define AD5791_DAC_MASK AD5791_RES_MASK(20) -#define AD5791_DAC_MSB (1 << 19) - -#define AD5791_CMD_READ (1 << 23) -#define AD5791_CMD_WRITE (0 << 23) -#define AD5791_ADDR(addr) ((addr) << 20) - -/* Registers */ -#define AD5791_ADDR_NOOP 0 -#define AD5791_ADDR_DAC0 1 -#define AD5791_ADDR_CTRL 2 -#define AD5791_ADDR_CLRCODE 3 -#define AD5791_ADDR_SW_CTRL 4 - -/* Control Register */ -#define AD5791_CTRL_RBUF (1 << 1) -#define AD5791_CTRL_OPGND (1 << 2) -#define AD5791_CTRL_DACTRI (1 << 3) -#define AD5791_CTRL_BIN2SC (1 << 4) -#define AD5791_CTRL_SDODIS (1 << 5) -#define AD5761_CTRL_LINCOMP(x) ((x) << 6) - -#define AD5791_LINCOMP_0_10 0 -#define AD5791_LINCOMP_10_12 1 -#define AD5791_LINCOMP_12_16 2 -#define AD5791_LINCOMP_16_19 3 -#define AD5791_LINCOMP_19_20 12 - -#define AD5780_LINCOMP_0_10 0 -#define AD5780_LINCOMP_10_20 12 - -/* Software Control Register */ -#define AD5791_SWCTRL_LDAC (1 << 0) -#define AD5791_SWCTRL_CLR (1 << 1) -#define AD5791_SWCTRL_RESET (1 << 2) - -#define AD5791_DAC_PWRDN_6K 0 -#define AD5791_DAC_PWRDN_3STATE 1 - -/** - * struct ad5791_chip_info - chip specific information - * @get_lin_comp: function pointer to the device specific function - */ - -struct ad5791_chip_info { - int (*get_lin_comp) (unsigned int span); -}; - -/** - * struct ad5791_state - driver instance specific data - * @us: spi_device - * @reg_vdd: positive supply regulator - * @reg_vss: negative supply regulator - * @chip_info: chip model specific constants - * @vref_mv: actual reference voltage used - * @vref_neg_mv: voltage of the negative supply - * @pwr_down_mode current power down mode - */ - -struct ad5791_state { - struct spi_device *spi; - struct regulator *reg_vdd; - struct regulator *reg_vss; - const struct ad5791_chip_info *chip_info; - unsigned short vref_mv; - unsigned int vref_neg_mv; - unsigned ctrl; - unsigned pwr_down_mode; - bool pwr_down; -}; - -/** - * ad5791_supported_device_ids: - */ - -enum ad5791_supported_device_ids { - ID_AD5760, - ID_AD5780, - ID_AD5781, - ID_AD5791, -}; - -static int ad5791_spi_write(struct spi_device *spi, u8 addr, u32 val) -{ - union { - u32 d32; - u8 d8[4]; - } data; - - data.d32 = cpu_to_be32(AD5791_CMD_WRITE | - AD5791_ADDR(addr) | - (val & AD5791_DAC_MASK)); - - return spi_write(spi, &data.d8[1], 3); -} - -static int ad5791_spi_read(struct spi_device *spi, u8 addr, u32 *val) -{ - union { - u32 d32; - u8 d8[4]; - } data[3]; - int ret; - struct spi_message msg; - struct spi_transfer xfers[] = { - { - .tx_buf = &data[0].d8[1], - .bits_per_word = 8, - .len = 3, - .cs_change = 1, - }, { - .tx_buf = &data[1].d8[1], - .rx_buf = &data[2].d8[1], - .bits_per_word = 8, - .len = 3, - }, - }; - - data[0].d32 = cpu_to_be32(AD5791_CMD_READ | - AD5791_ADDR(addr)); - data[1].d32 = cpu_to_be32(AD5791_ADDR(AD5791_ADDR_NOOP)); - - spi_message_init(&msg); - spi_message_add_tail(&xfers[0], &msg); - spi_message_add_tail(&xfers[1], &msg); - ret = spi_sync(spi, &msg); - - *val = be32_to_cpu(data[2].d32); - - return ret; -} - -static const char * const ad5791_powerdown_modes[] = { - "6kohm_to_gnd", - "three_state", -}; - -static int ad5791_get_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - struct ad5791_state *st = iio_priv(indio_dev); - - return st->pwr_down_mode; -} - -static int ad5791_set_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int mode) -{ - struct ad5791_state *st = iio_priv(indio_dev); - - st->pwr_down_mode = mode; - - return 0; -} - -static const struct iio_enum ad5791_powerdown_mode_enum = { - .items = ad5791_powerdown_modes, - .num_items = ARRAY_SIZE(ad5791_powerdown_modes), - .get = ad5791_get_powerdown_mode, - .set = ad5791_set_powerdown_mode, -}; - -static ssize_t ad5791_read_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, char *buf) -{ - struct ad5791_state *st = iio_priv(indio_dev); - - return sprintf(buf, "%d\n", st->pwr_down); -} - -static ssize_t ad5791_write_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, const char *buf, - size_t len) -{ - bool pwr_down; - int ret; - struct ad5791_state *st = iio_priv(indio_dev); - - ret = strtobool(buf, &pwr_down); - if (ret) - return ret; - - if (!pwr_down) { - st->ctrl &= ~(AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI); - } else { - if (st->pwr_down_mode == AD5791_DAC_PWRDN_6K) - st->ctrl |= AD5791_CTRL_OPGND; - else if (st->pwr_down_mode == AD5791_DAC_PWRDN_3STATE) - st->ctrl |= AD5791_CTRL_DACTRI; - } - st->pwr_down = pwr_down; - - ret = ad5791_spi_write(st->spi, AD5791_ADDR_CTRL, st->ctrl); - - return ret ? ret : len; -} - -static int ad5791_get_lin_comp(unsigned int span) -{ - if (span <= 10000) - return AD5791_LINCOMP_0_10; - else if (span <= 12000) - return AD5791_LINCOMP_10_12; - else if (span <= 16000) - return AD5791_LINCOMP_12_16; - else if (span <= 19000) - return AD5791_LINCOMP_16_19; - else - return AD5791_LINCOMP_19_20; -} - -static int ad5780_get_lin_comp(unsigned int span) -{ - if (span <= 10000) - return AD5780_LINCOMP_0_10; - else - return AD5780_LINCOMP_10_20; -} -static const struct ad5791_chip_info ad5791_chip_info_tbl[] = { - [ID_AD5760] = { - .get_lin_comp = ad5780_get_lin_comp, - }, - [ID_AD5780] = { - .get_lin_comp = ad5780_get_lin_comp, - }, - [ID_AD5781] = { - .get_lin_comp = ad5791_get_lin_comp, - }, - [ID_AD5791] = { - .get_lin_comp = ad5791_get_lin_comp, - }, -}; - -static int ad5791_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long m) -{ - struct ad5791_state *st = iio_priv(indio_dev); - u64 val64; - int ret; - - switch (m) { - case IIO_CHAN_INFO_RAW: - ret = ad5791_spi_read(st->spi, chan->address, val); - if (ret) - return ret; - *val &= AD5791_DAC_MASK; - *val >>= chan->scan_type.shift; - return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - *val = 0; - *val2 = (((u64)st->vref_mv) * 1000000ULL) >> chan->scan_type.realbits; - return IIO_VAL_INT_PLUS_MICRO; - case IIO_CHAN_INFO_OFFSET: - val64 = (((u64)st->vref_neg_mv) << chan->scan_type.realbits); - do_div(val64, st->vref_mv); - *val = -val64; - return IIO_VAL_INT; - default: - return -EINVAL; - } - -}; - -static const struct iio_chan_spec_ext_info ad5791_ext_info[] = { - { - .name = "powerdown", - .shared = true, - .read = ad5791_read_dac_powerdown, - .write = ad5791_write_dac_powerdown, - }, - IIO_ENUM("powerdown_mode", true, &ad5791_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5791_powerdown_mode_enum), - { }, -}; - -#define AD5791_CHAN(bits, shift) { \ - .type = IIO_VOLTAGE, \ - .output = 1, \ - .indexed = 1, \ - .address = AD5791_ADDR_DAC0, \ - .channel = 0, \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SHARED_BIT | \ - IIO_CHAN_INFO_OFFSET_SHARED_BIT, \ - .scan_type = IIO_ST('u', bits, 24, shift), \ - .ext_info = ad5791_ext_info, \ -} - -static const struct iio_chan_spec ad5791_channels[] = { - [ID_AD5760] = AD5791_CHAN(16, 4), - [ID_AD5780] = AD5791_CHAN(18, 2), - [ID_AD5781] = AD5791_CHAN(18, 2), - [ID_AD5791] = AD5791_CHAN(20, 0) -}; - -static int ad5791_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) -{ - struct ad5791_state *st = iio_priv(indio_dev); - - switch (mask) { - case IIO_CHAN_INFO_RAW: - val &= AD5791_RES_MASK(chan->scan_type.realbits); - val <<= chan->scan_type.shift; - - return ad5791_spi_write(st->spi, chan->address, val); - - default: - return -EINVAL; - } -} - -static const struct iio_info ad5791_info = { - .read_raw = &ad5791_read_raw, - .write_raw = &ad5791_write_raw, - .driver_module = THIS_MODULE, -}; - -static int __devinit ad5791_probe(struct spi_device *spi) -{ - struct ad5791_platform_data *pdata = spi->dev.platform_data; - struct iio_dev *indio_dev; - struct ad5791_state *st; - int ret, pos_voltage_uv = 0, neg_voltage_uv = 0; - - indio_dev = iio_device_alloc(sizeof(*st)); - if (indio_dev == NULL) { - ret = -ENOMEM; - goto error_ret; - } - st = iio_priv(indio_dev); - st->reg_vdd = regulator_get(&spi->dev, "vdd"); - if (!IS_ERR(st->reg_vdd)) { - ret = regulator_enable(st->reg_vdd); - if (ret) - goto error_put_reg_pos; - - pos_voltage_uv = regulator_get_voltage(st->reg_vdd); - } - - st->reg_vss = regulator_get(&spi->dev, "vss"); - if (!IS_ERR(st->reg_vss)) { - ret = regulator_enable(st->reg_vss); - if (ret) - goto error_put_reg_neg; - - neg_voltage_uv = regulator_get_voltage(st->reg_vss); - } - - st->pwr_down = true; - st->spi = spi; - - if (!IS_ERR(st->reg_vss) && !IS_ERR(st->reg_vdd)) { - st->vref_mv = (pos_voltage_uv + neg_voltage_uv) / 1000; - st->vref_neg_mv = neg_voltage_uv / 1000; - } else if (pdata) { - st->vref_mv = pdata->vref_pos_mv + pdata->vref_neg_mv; - st->vref_neg_mv = pdata->vref_neg_mv; - } else { - dev_warn(&spi->dev, "reference voltage unspecified\n"); - } - - ret = ad5791_spi_write(spi, AD5791_ADDR_SW_CTRL, AD5791_SWCTRL_RESET); - if (ret) - goto error_disable_reg_neg; - - st->chip_info = &ad5791_chip_info_tbl[spi_get_device_id(spi) - ->driver_data]; - - - st->ctrl = AD5761_CTRL_LINCOMP(st->chip_info->get_lin_comp(st->vref_mv)) - | ((pdata && pdata->use_rbuf_gain2) ? 0 : AD5791_CTRL_RBUF) | - AD5791_CTRL_BIN2SC; - - ret = ad5791_spi_write(spi, AD5791_ADDR_CTRL, st->ctrl | - AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI); - if (ret) - goto error_disable_reg_neg; - - spi_set_drvdata(spi, indio_dev); - indio_dev->dev.parent = &spi->dev; - indio_dev->info = &ad5791_info; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels - = &ad5791_channels[spi_get_device_id(spi)->driver_data]; - indio_dev->num_channels = 1; - indio_dev->name = spi_get_device_id(st->spi)->name; - ret = iio_device_register(indio_dev); - if (ret) - goto error_disable_reg_neg; - - return 0; - -error_disable_reg_neg: - if (!IS_ERR(st->reg_vss)) - regulator_disable(st->reg_vss); -error_put_reg_neg: - if (!IS_ERR(st->reg_vss)) - regulator_put(st->reg_vss); - - if (!IS_ERR(st->reg_vdd)) - regulator_disable(st->reg_vdd); -error_put_reg_pos: - if (!IS_ERR(st->reg_vdd)) - regulator_put(st->reg_vdd); - iio_device_free(indio_dev); -error_ret: - - return ret; -} - -static int __devexit ad5791_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad5791_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - if (!IS_ERR(st->reg_vdd)) { - regulator_disable(st->reg_vdd); - regulator_put(st->reg_vdd); - } - - if (!IS_ERR(st->reg_vss)) { - regulator_disable(st->reg_vss); - regulator_put(st->reg_vss); - } - iio_device_free(indio_dev); - - return 0; -} - -static const struct spi_device_id ad5791_id[] = { - {"ad5760", ID_AD5760}, - {"ad5780", ID_AD5780}, - {"ad5781", ID_AD5781}, - {"ad5790", ID_AD5791}, - {"ad5791", ID_AD5791}, - {} -}; -MODULE_DEVICE_TABLE(spi, ad5791_id); - -static struct spi_driver ad5791_driver = { - .driver = { - .name = "ad5791", - .owner = THIS_MODULE, - }, - .probe = ad5791_probe, - .remove = __devexit_p(ad5791_remove), - .id_table = ad5791_id, -}; -module_spi_driver(ad5791_driver); - -MODULE_AUTHOR("Michael Hennerich "); -MODULE_DESCRIPTION("Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/dac/ad5791.h b/drivers/staging/iio/dac/ad5791.h deleted file mode 100644 index 87a6c922f18e..000000000000 --- a/drivers/staging/iio/dac/ad5791.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * AD5791 SPI DAC driver - * - * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#ifndef SPI_AD5791_H_ -#define SPI_AD5791_H_ - -/* - * TODO: struct ad5791_platform_data needs to go into include/linux/iio - */ - -/** - * struct ad5791_platform_data - platform specific information - * @vref_pos_mv: Vdd Positive Analog Supply Volatge (mV) - * @vref_neg_mv: Vdd Negative Analog Supply Volatge (mV) - * @use_rbuf_gain2: ext. amplifier connected in gain of two configuration - */ - -struct ad5791_platform_data { - u16 vref_pos_mv; - u16 vref_neg_mv; - bool use_rbuf_gain2; -}; - -#endif /* SPI_AD5791_H_ */ diff --git a/drivers/staging/iio/dac/max517.c b/drivers/staging/iio/dac/max517.c deleted file mode 100644 index 403e06fbc39e..000000000000 --- a/drivers/staging/iio/dac/max517.c +++ /dev/null @@ -1,244 +0,0 @@ -/* - * max517.c - Support for Maxim MAX517, MAX518 and MAX519 - * - * Copyright (C) 2010, 2011 Roland Stigge - * - * 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. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "max517.h" - -#define MAX517_DRV_NAME "max517" - -/* Commands */ -#define COMMAND_CHANNEL0 0x00 -#define COMMAND_CHANNEL1 0x01 /* for MAX518 and MAX519 */ -#define COMMAND_PD 0x08 /* Power Down */ - -enum max517_device_ids { - ID_MAX517, - ID_MAX518, - ID_MAX519, -}; - -struct max517_data { - struct iio_dev *indio_dev; - struct i2c_client *client; - unsigned short vref_mv[2]; -}; - -/* - * channel: bit 0: channel 1 - * bit 1: channel 2 - * (this way, it's possible to set both channels at once) - */ -static int max517_set_value(struct iio_dev *indio_dev, - long val, int channel) -{ - struct max517_data *data = iio_priv(indio_dev); - struct i2c_client *client = data->client; - u8 outbuf[2]; - int res; - - if (val < 0 || val > 255) - return -EINVAL; - - outbuf[0] = channel; - outbuf[1] = val; - - res = i2c_master_send(client, outbuf, 2); - if (res < 0) - return res; - else if (res != 2) - return -EIO; - else - return 0; -} - -static int max517_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long m) -{ - struct max517_data *data = iio_priv(indio_dev); - unsigned int scale_uv; - - switch (m) { - case IIO_CHAN_INFO_SCALE: - /* Corresponds to Vref / 2^(bits) */ - scale_uv = (data->vref_mv[chan->channel] * 1000) >> 8; - *val = scale_uv / 1000000; - *val2 = scale_uv % 1000000; - return IIO_VAL_INT_PLUS_MICRO; - default: - break; - } - return -EINVAL; -} - -static int max517_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int val, int val2, long mask) -{ - int ret; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - ret = max517_set_value(indio_dev, val, chan->channel); - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - -#ifdef CONFIG_PM_SLEEP -static int max517_suspend(struct device *dev) -{ - u8 outbuf = COMMAND_PD; - - return i2c_master_send(to_i2c_client(dev), &outbuf, 1); -} - -static int max517_resume(struct device *dev) -{ - u8 outbuf = 0; - - return i2c_master_send(to_i2c_client(dev), &outbuf, 1); -} - -static SIMPLE_DEV_PM_OPS(max517_pm_ops, max517_suspend, max517_resume); -#define MAX517_PM_OPS (&max517_pm_ops) -#else -#define MAX517_PM_OPS NULL -#endif - -static const struct iio_info max517_info = { - .read_raw = max517_read_raw, - .write_raw = max517_write_raw, - .driver_module = THIS_MODULE, -}; - -#define MAX517_CHANNEL(chan) { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .output = 1, \ - .channel = (chan), \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \ - .scan_type = IIO_ST('u', 8, 8, 0), \ -} - -static const struct iio_chan_spec max517_channels[] = { - MAX517_CHANNEL(0), - MAX517_CHANNEL(1) -}; - -static int max517_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct max517_data *data; - struct iio_dev *indio_dev; - struct max517_platform_data *platform_data = client->dev.platform_data; - int err; - - indio_dev = iio_device_alloc(sizeof(*data)); - if (indio_dev == NULL) { - err = -ENOMEM; - goto exit; - } - data = iio_priv(indio_dev); - i2c_set_clientdata(client, indio_dev); - data->client = client; - - /* establish that the iio_dev is a child of the i2c device */ - indio_dev->dev.parent = &client->dev; - - /* reduced channel set for MAX517 */ - if (id->driver_data == ID_MAX517) - indio_dev->num_channels = 1; - else - indio_dev->num_channels = 2; - indio_dev->channels = max517_channels; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->info = &max517_info; - - /* - * Reference voltage on MAX518 and default is 5V, else take vref_mv - * from platform_data - */ - if (id->driver_data == ID_MAX518 || !platform_data) { - data->vref_mv[0] = data->vref_mv[1] = 5000; /* mV */ - } else { - data->vref_mv[0] = platform_data->vref_mv[0]; - data->vref_mv[1] = platform_data->vref_mv[1]; - } - - err = iio_device_register(indio_dev); - if (err) - goto exit_free_device; - - dev_info(&client->dev, "DAC registered\n"); - - return 0; - -exit_free_device: - iio_device_free(indio_dev); -exit: - return err; -} - -static int max517_remove(struct i2c_client *client) -{ - iio_device_unregister(i2c_get_clientdata(client)); - iio_device_free(i2c_get_clientdata(client)); - - return 0; -} - -static const struct i2c_device_id max517_id[] = { - { "max517", ID_MAX517 }, - { "max518", ID_MAX518 }, - { "max519", ID_MAX519 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, max517_id); - -static struct i2c_driver max517_driver = { - .driver = { - .name = MAX517_DRV_NAME, - .pm = MAX517_PM_OPS, - }, - .probe = max517_probe, - .remove = max517_remove, - .id_table = max517_id, -}; -module_i2c_driver(max517_driver); - -MODULE_AUTHOR("Roland Stigge "); -MODULE_DESCRIPTION("MAX517/MAX518/MAX519 8-bit DAC"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/iio/dac/max517.h b/drivers/staging/iio/dac/max517.h deleted file mode 100644 index 8106cf24642a..000000000000 --- a/drivers/staging/iio/dac/max517.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * MAX517 DAC driver - * - * Copyright 2011 Roland Stigge - * - * Licensed under the GPL-2 or later. - */ -#ifndef IIO_DAC_MAX517_H_ -#define IIO_DAC_MAX517_H_ - -/* - * TODO: struct max517_platform_data needs to go into include/linux/iio - */ - -struct max517_platform_data { - u16 vref_mv[2]; -}; - -#endif /* IIO_DAC_MAX517_H_ */ diff --git a/include/linux/iio/dac/ad5421.h b/include/linux/iio/dac/ad5421.h new file mode 100644 index 000000000000..8fd8f057a890 --- /dev/null +++ b/include/linux/iio/dac/ad5421.h @@ -0,0 +1,28 @@ +#ifndef __IIO_DAC_AD5421_H__ +#define __IIO_DAC_AD5421_H__ + +/** + * enum ad5421_current_range - Current range the AD5421 is configured for. + * @AD5421_CURRENT_RANGE_4mA_20mA: 4 mA to 20 mA (RANGE1,0 pins = 00) + * @AD5421_CURRENT_RANGE_3mA8_21mA: 3.8 mA to 21 mA (RANGE1,0 pins = x1) + * @AD5421_CURRENT_RANGE_3mA2_24mA: 3.2 mA to 24 mA (RANGE1,0 pins = 10) + */ + +enum ad5421_current_range { + AD5421_CURRENT_RANGE_4mA_20mA, + AD5421_CURRENT_RANGE_3mA8_21mA, + AD5421_CURRENT_RANGE_3mA2_24mA, +}; + +/** + * struct ad5421_platform_data - AD5421 DAC driver platform data + * @external_vref: whether an external reference voltage is used or not + * @current_range: Current range the AD5421 is configured for + */ + +struct ad5421_platform_data { + bool external_vref; + enum ad5421_current_range current_range; +}; + +#endif diff --git a/include/linux/iio/dac/ad5504.h b/include/linux/iio/dac/ad5504.h new file mode 100644 index 000000000000..43895376a9ca --- /dev/null +++ b/include/linux/iio/dac/ad5504.h @@ -0,0 +1,16 @@ +/* + * AD5504 SPI DAC driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#ifndef SPI_AD5504_H_ +#define SPI_AD5504_H_ + +struct ad5504_platform_data { + u16 vref_mv; +}; + +#endif /* SPI_AD5504_H_ */ diff --git a/include/linux/iio/dac/ad5791.h b/include/linux/iio/dac/ad5791.h new file mode 100644 index 000000000000..45ee281c6660 --- /dev/null +++ b/include/linux/iio/dac/ad5791.h @@ -0,0 +1,25 @@ +/* + * AD5791 SPI DAC driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#ifndef SPI_AD5791_H_ +#define SPI_AD5791_H_ + +/** + * struct ad5791_platform_data - platform specific information + * @vref_pos_mv: Vdd Positive Analog Supply Volatge (mV) + * @vref_neg_mv: Vdd Negative Analog Supply Volatge (mV) + * @use_rbuf_gain2: ext. amplifier connected in gain of two configuration + */ + +struct ad5791_platform_data { + u16 vref_pos_mv; + u16 vref_neg_mv; + bool use_rbuf_gain2; +}; + +#endif /* SPI_AD5791_H_ */ diff --git a/include/linux/iio/dac/max517.h b/include/linux/iio/dac/max517.h new file mode 100644 index 000000000000..f6d1d252f08d --- /dev/null +++ b/include/linux/iio/dac/max517.h @@ -0,0 +1,15 @@ +/* + * MAX517 DAC driver + * + * Copyright 2011 Roland Stigge + * + * Licensed under the GPL-2 or later. + */ +#ifndef IIO_DAC_MAX517_H_ +#define IIO_DAC_MAX517_H_ + +struct max517_platform_data { + u16 vref_mv[2]; +}; + +#endif /* IIO_DAC_MAX517_H_ */ -- cgit v1.2.3 From c1a7b32a14138f908df52d7c53b5ce3415ec6b50 Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Sun, 20 May 2012 13:15:07 +0900 Subject: KVM: Avoid wasting pages for small lpage_info arrays lpage_info is created for each large level even when the memory slot is not for RAM. This means that when we add one slot for a PCI device, we end up allocating at least KVM_NR_PAGE_SIZES - 1 pages by vmalloc(). To make things worse, there is an increasing number of devices which would result in more pages being wasted this way. This patch mitigates this problem by using kvm_kvzalloc(). Signed-off-by: Takuya Yoshikawa Signed-off-by: Avi Kivity --- arch/x86/kvm/x86.c | 4 ++-- include/linux/kvm_host.h | 3 +++ virt/kvm/kvm_main.c | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index be6d54929fa7..f12a52408cda 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -6304,7 +6304,7 @@ void kvm_arch_free_memslot(struct kvm_memory_slot *free, for (i = 0; i < KVM_NR_PAGE_SIZES - 1; ++i) { if (!dont || free->arch.lpage_info[i] != dont->arch.lpage_info[i]) { - vfree(free->arch.lpage_info[i]); + kvm_kvfree(free->arch.lpage_info[i]); free->arch.lpage_info[i] = NULL; } } @@ -6323,7 +6323,7 @@ int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages) slot->base_gfn, level) + 1; slot->arch.lpage_info[i] = - vzalloc(lpages * sizeof(*slot->arch.lpage_info[i])); + kvm_kvzalloc(lpages * sizeof(*slot->arch.lpage_info[i])); if (!slot->arch.lpage_info[i]) goto out_free; diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index c4464356b35b..19b83f6efa49 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -535,6 +535,9 @@ int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu); void kvm_free_physmem(struct kvm *kvm); +void *kvm_kvzalloc(unsigned long size); +void kvm_kvfree(const void *addr); + #ifndef __KVM_HAVE_ARCH_VM_ALLOC static inline struct kvm *kvm_arch_alloc_vm(void) { diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 1148c96a4817..02cb440f802d 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -520,7 +520,7 @@ out_err_nodisable: * Avoid using vmalloc for a small buffer. * Should not be used when the size is statically known. */ -static void *kvm_kvzalloc(unsigned long size) +void *kvm_kvzalloc(unsigned long size) { if (size > PAGE_SIZE) return vzalloc(size); @@ -528,7 +528,7 @@ static void *kvm_kvzalloc(unsigned long size) return kzalloc(size, GFP_KERNEL); } -static void kvm_kvfree(const void *addr) +void kvm_kvfree(const void *addr) { if (is_vmalloc_addr(addr)) vfree(addr); -- cgit v1.2.3 From bacef661acdb634170a8faddbc1cf28e8f8b9eee Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Fri, 25 May 2012 16:20:31 +0100 Subject: x86-64/efi: Use EFI to deal with platform wall clock Other than ix86, x86-64 on EFI so far didn't set the {g,s}et_wallclock accessors to the EFI routines, thus incorrectly using raw RTC accesses instead. Simply removing the #ifdef around the respective code isn't enough, however: While so far early get-time calls were done in physical mode, this doesn't work properly for x86-64, as virtual addresses would still need to be set up for all runtime regions (which wasn't the case on the system I have access to), so instead the patch moves the call to efi_enter_virtual_mode() ahead (which in turn allows to drop all code related to calling efi-get-time in physical mode). Additionally the earlier calling of efi_set_executable() requires the CPA code to cope, i.e. during early boot it must be avoided to call cpa_flush_array(), as the first thing this function does is a BUG_ON(irqs_disabled()). Also make the two EFI functions in question here static - they're not being referenced elsewhere. Signed-off-by: Jan Beulich Tested-by: Matt Fleming Acked-by: Matthew Garrett Cc: Linus Torvalds Cc: Andrew Morton Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/4FBFBF5F020000780008637F@nat28.tlf.novell.com Signed-off-by: Ingo Molnar --- arch/x86/mm/pageattr.c | 10 ++++++---- arch/x86/platform/efi/efi.c | 30 ++++-------------------------- include/linux/efi.h | 2 -- init/main.c | 8 ++++---- 4 files changed, 14 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index e1ebde315210..ee09aca63998 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -919,11 +919,13 @@ static int change_page_attr_set_clr(unsigned long *addr, int numpages, /* * On success we use clflush, when the CPU supports it to - * avoid the wbindv. If the CPU does not support it and in the - * error case we fall back to cpa_flush_all (which uses - * wbindv): + * avoid the wbindv. If the CPU does not support it, in the + * error case, and during early boot (for EFI) we fall back + * to cpa_flush_all (which uses wbinvd): */ - if (!ret && cpu_has_clflush) { + if (early_boot_irqs_disabled) + __cpa_flush_all((void *)(long)cache); + else if (!ret && cpu_has_clflush) { if (cpa.flags & (CPA_PAGES_ARRAY | CPA_ARRAY)) { cpa_flush_array(addr, numpages, cache, cpa.flags, pages); diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 92660edaa1e7..2dc29f51e75a 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -234,22 +234,7 @@ static efi_status_t __init phys_efi_set_virtual_address_map( return status; } -static efi_status_t __init phys_efi_get_time(efi_time_t *tm, - efi_time_cap_t *tc) -{ - unsigned long flags; - efi_status_t status; - - spin_lock_irqsave(&rtc_lock, flags); - efi_call_phys_prelog(); - status = efi_call_phys2(efi_phys.get_time, virt_to_phys(tm), - virt_to_phys(tc)); - efi_call_phys_epilog(); - spin_unlock_irqrestore(&rtc_lock, flags); - return status; -} - -int efi_set_rtc_mmss(unsigned long nowtime) +static int efi_set_rtc_mmss(unsigned long nowtime) { int real_seconds, real_minutes; efi_status_t status; @@ -278,7 +263,7 @@ int efi_set_rtc_mmss(unsigned long nowtime) return 0; } -unsigned long efi_get_time(void) +static unsigned long efi_get_time(void) { efi_status_t status; efi_time_t eft; @@ -621,18 +606,13 @@ static int __init efi_runtime_init(void) } /* * We will only need *early* access to the following - * two EFI runtime services before set_virtual_address_map + * EFI runtime service before set_virtual_address_map * is invoked. */ - efi_phys.get_time = (efi_get_time_t *)runtime->get_time; efi_phys.set_virtual_address_map = (efi_set_virtual_address_map_t *) runtime->set_virtual_address_map; - /* - * Make efi_get_time can be called before entering - * virtual mode. - */ - efi.get_time = phys_efi_get_time; + early_iounmap(runtime, sizeof(efi_runtime_services_t)); return 0; @@ -720,12 +700,10 @@ void __init efi_init(void) efi_enabled = 0; return; } -#ifdef CONFIG_X86_32 if (efi_native) { x86_platform.get_wallclock = efi_get_time; x86_platform.set_wallclock = efi_set_rtc_mmss; } -#endif #if EFI_DEBUG print_efi_memmap(); diff --git a/include/linux/efi.h b/include/linux/efi.h index ec45ccd8708a..103adc6d7e3a 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -503,8 +503,6 @@ extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size); extern int __init efi_uart_console_only (void); extern void efi_initialize_iomem_resources(struct resource *code_resource, struct resource *data_resource, struct resource *bss_resource); -extern unsigned long efi_get_time(void); -extern int efi_set_rtc_mmss(unsigned long nowtime); extern void efi_reserve_boot_services(void); extern struct efi_memory_map memmap; diff --git a/init/main.c b/init/main.c index 1ca6b32c4828..eef30128321a 100644 --- a/init/main.c +++ b/init/main.c @@ -460,6 +460,10 @@ static void __init mm_init(void) percpu_init_late(); pgtable_cache_init(); vmalloc_init(); +#ifdef CONFIG_X86 + if (efi_enabled) + efi_enter_virtual_mode(); +#endif } asmlinkage void __init start_kernel(void) @@ -601,10 +605,6 @@ asmlinkage void __init start_kernel(void) calibrate_delay(); pidmap_init(); anon_vma_init(); -#ifdef CONFIG_X86 - if (efi_enabled) - efi_enter_virtual_mode(); -#endif thread_info_cache_init(); cred_init(); fork_init(totalram_pages); -- cgit v1.2.3 From eac1f14fd1e7243aa782ef85f2a217e0c3a709ad Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 5 Jun 2012 15:24:24 -0700 Subject: drm/i915: Inifite timeout for wait ioctl Change the ns_timeout parameter of the wait ioctl to a signed value. Doing this allows the kernel to provide an infinite wait when a timeout of less than 0 is provided. This mimics select/poll. Initially the parameter was meant to match up with the GL spec 1:1, but after being made aware of how much 2^64 - 1 nanoseconds actually is, I do not think anyone will ever notice the loss of 1 bit. The infinite timeout on waiting is similar to the existing i915 userspace interface with the exception that struct_mutex is dropped while doing the wait in this ioctl. Cc: Chris Wilson Signed-off-by: Ben Widawsky Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 15 ++++++++++----- include/drm/i915_drm.h | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index af67803e635f..deaa0d4bb456 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2082,11 +2082,14 @@ i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file) struct drm_i915_gem_wait *args = data; struct drm_i915_gem_object *obj; struct intel_ring_buffer *ring = NULL; - struct timespec timeout; + struct timespec timeout_stack, *timeout = NULL; u32 seqno = 0; int ret = 0; - timeout = ns_to_timespec(args->timeout_ns); + if (args->timeout_ns >= 0) { + timeout_stack = ns_to_timespec(args->timeout_ns); + timeout = &timeout_stack; + } ret = i915_mutex_lock_interruptible(dev); if (ret) @@ -2122,9 +2125,11 @@ i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file) drm_gem_object_unreference(&obj->base); mutex_unlock(&dev->struct_mutex); - ret = __wait_seqno(ring, seqno, true, &timeout); - WARN_ON(!timespec_valid(&timeout)); - args->timeout_ns = timespec_to_ns(&timeout); + ret = __wait_seqno(ring, seqno, true, timeout); + if (timeout) { + WARN_ON(!timespec_valid(timeout)); + args->timeout_ns = timespec_to_ns(timeout); + } return ret; out: diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index bab174334da5..aae346e7f635 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/i915_drm.h @@ -893,7 +893,7 @@ struct drm_i915_gem_wait { __u32 bo_handle; __u32 flags; /** Number of nanoseconds to wait, Returns time remaining. */ - __u64 timeout_ns; + __s64 timeout_ns; }; #endif /* _I915_DRM_H_ */ -- cgit v1.2.3 From 23d0bb834e264f38335f19fe601564b8422431e7 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Mon, 28 May 2012 15:26:56 +0100 Subject: GFS2: Add "top dir" flag support This patch adds support for the "top dir" flag. Currently this is unused but a subsequent patch is planned which will add support for the Orlov allocation policy when allocating subdirectories in a parent with this flag set. In order to ensure backward compatible behaviour, mkfs.gfs2 does not currently tag the root directory with this flag, it must always be set manually. Signed-off-by: Steven Whitehouse --- fs/gfs2/file.c | 4 ++++ include/linux/gfs2_ondisk.h | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 26e2905070ed..6fbf3cbd974d 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -142,6 +142,7 @@ static const u32 fsflags_to_gfs2[32] = { [7] = GFS2_DIF_NOATIME, [12] = GFS2_DIF_EXHASH, [14] = GFS2_DIF_INHERIT_JDATA, + [17] = GFS2_DIF_TOPDIR, }; static const u32 gfs2_to_fsflags[32] = { @@ -150,6 +151,7 @@ static const u32 gfs2_to_fsflags[32] = { [gfs2fl_AppendOnly] = FS_APPEND_FL, [gfs2fl_NoAtime] = FS_NOATIME_FL, [gfs2fl_ExHash] = FS_INDEX_FL, + [gfs2fl_TopLevel] = FS_TOPDIR_FL, [gfs2fl_InheritJdata] = FS_JOURNAL_DATA_FL, }; @@ -203,6 +205,7 @@ void gfs2_set_inode_flags(struct inode *inode) GFS2_DIF_NOATIME| \ GFS2_DIF_SYNC| \ GFS2_DIF_SYSTEM| \ + GFS2_DIF_TOPDIR| \ GFS2_DIF_INHERIT_JDATA) /** @@ -298,6 +301,7 @@ static int gfs2_set_flags(struct file *filp, u32 __user *ptr) gfsflags = fsflags_cvt(fsflags_to_gfs2, fsflags); if (!S_ISDIR(inode->i_mode)) { + gfsflags &= ~GFS2_DIF_TOPDIR; if (gfsflags & GFS2_DIF_INHERIT_JDATA) gfsflags ^= (GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA); return do_gfs2_set_flags(filp, gfsflags, ~0); diff --git a/include/linux/gfs2_ondisk.h b/include/linux/gfs2_ondisk.h index fa98bdb073b9..e8ccf6ff3b4d 100644 --- a/include/linux/gfs2_ondisk.h +++ b/include/linux/gfs2_ondisk.h @@ -214,6 +214,7 @@ enum { gfs2fl_NoAtime = 7, gfs2fl_Sync = 8, gfs2fl_System = 9, + gfs2fl_TopLevel = 10, gfs2fl_TruncInProg = 29, gfs2fl_InheritDirectio = 30, gfs2fl_InheritJdata = 31, @@ -230,8 +231,9 @@ enum { #define GFS2_DIF_NOATIME 0x00000080 #define GFS2_DIF_SYNC 0x00000100 #define GFS2_DIF_SYSTEM 0x00000200 /* New in gfs2 */ +#define GFS2_DIF_TOPDIR 0x00000400 /* New in gfs2 */ #define GFS2_DIF_TRUNC_IN_PROG 0x20000000 /* New in gfs2 */ -#define GFS2_DIF_INHERIT_DIRECTIO 0x40000000 +#define GFS2_DIF_INHERIT_DIRECTIO 0x40000000 /* only in gfs1 */ #define GFS2_DIF_INHERIT_JDATA 0x80000000 struct gfs2_dinode { -- cgit v1.2.3 From 172cf15d18889313bf2c3bfb81fcea08369274ef Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 5 Jun 2012 15:24:25 -0700 Subject: drm/i915: Add wait render timeout get param Signed-off-by: Ben Widawsky Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 3 +++ include/drm/i915_drm.h | 1 + 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 262a74d1f852..97a5a5857f5b 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1006,6 +1006,9 @@ static int i915_getparam(struct drm_device *dev, void *data, case I915_PARAM_HAS_ALIASING_PPGTT: value = dev_priv->mm.aliasing_ppgtt ? 1 : 0; break; + case I915_PARAM_HAS_WAIT_TIMEOUT: + value = 1; + break; default: DRM_DEBUG_DRIVER("Unknown parameter %d\n", param->param); diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index aae346e7f635..5c28ee1d1911 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/i915_drm.h @@ -300,6 +300,7 @@ typedef struct drm_i915_irq_wait { #define I915_PARAM_HAS_GEN7_SOL_RESET 16 #define I915_PARAM_HAS_LLC 17 #define I915_PARAM_HAS_ALIASING_PPGTT 18 +#define I915_PARAM_HAS_WAIT_TIMEOUT 19 typedef struct drm_i915_getparam { int param; -- cgit v1.2.3 From a737f256bf14adf94920aa70d150ab4dcd145109 Mon Sep 17 00:00:00 2001 From: Christoffer Dall Date: Sun, 3 Jun 2012 21:17:48 +0300 Subject: KVM: Cleanup the kvm_print functions and introduce pr_XX wrappers Introduces a couple of print functions, which are essentially wrappers around standard printk functions, with a KVM: prefix. Functions introduced or modified are: - kvm_err(fmt, ...) - kvm_info(fmt, ...) - kvm_debug(fmt, ...) - kvm_pr_unimpl(fmt, ...) - pr_unimpl(vcpu, fmt, ...) -> vcpu_unimpl(vcpu, fmt, ...) Signed-off-by: Christoffer Dall Signed-off-by: Avi Kivity --- arch/x86/kvm/svm.c | 6 +++--- arch/x86/kvm/vmx.c | 2 +- arch/x86/kvm/x86.c | 54 ++++++++++++++++++++++++------------------------ include/linux/kvm_host.h | 18 ++++++++++------ 4 files changed, 43 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index f75af406b268..7a418783259d 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -3185,8 +3185,8 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, unsigned ecx, u64 data) break; case MSR_IA32_DEBUGCTLMSR: if (!boot_cpu_has(X86_FEATURE_LBRV)) { - pr_unimpl(vcpu, "%s: MSR_IA32_DEBUGCTL 0x%llx, nop\n", - __func__, data); + vcpu_unimpl(vcpu, "%s: MSR_IA32_DEBUGCTL 0x%llx, nop\n", + __func__, data); break; } if (data & DEBUGCTL_RESERVED_BITS) @@ -3205,7 +3205,7 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, unsigned ecx, u64 data) case MSR_VM_CR: return svm_set_vm_cr(vcpu, data); case MSR_VM_IGNNE: - pr_unimpl(vcpu, "unimplemented wrmsr: 0x%x data 0x%llx\n", ecx, data); + vcpu_unimpl(vcpu, "unimplemented wrmsr: 0x%x data 0x%llx\n", ecx, data); break; default: return kvm_set_msr_common(vcpu, ecx, data); diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index f78662ec8677..eeeb4a25aed6 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -4549,7 +4549,7 @@ static int handle_cr(struct kvm_vcpu *vcpu) break; } vcpu->run->exit_reason = 0; - pr_unimpl(vcpu, "unhandled control register: op %d cr %d\n", + vcpu_unimpl(vcpu, "unhandled control register: op %d cr %d\n", (int)(exit_qualification >> 4) & 3, cr); return 0; } diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index f12a52408cda..a01a4241bc6b 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1437,8 +1437,8 @@ static int set_msr_hyperv_pw(struct kvm_vcpu *vcpu, u32 msr, u64 data) break; } default: - pr_unimpl(vcpu, "HYPER-V unimplemented wrmsr: 0x%x " - "data 0x%llx\n", msr, data); + vcpu_unimpl(vcpu, "HYPER-V unimplemented wrmsr: 0x%x " + "data 0x%llx\n", msr, data); return 1; } return 0; @@ -1470,8 +1470,8 @@ static int set_msr_hyperv(struct kvm_vcpu *vcpu, u32 msr, u64 data) case HV_X64_MSR_TPR: return kvm_hv_vapic_msr_write(vcpu, APIC_TASKPRI, data); default: - pr_unimpl(vcpu, "HYPER-V unimplemented wrmsr: 0x%x " - "data 0x%llx\n", msr, data); + vcpu_unimpl(vcpu, "HYPER-V unimplemented wrmsr: 0x%x " + "data 0x%llx\n", msr, data); return 1; } @@ -1551,15 +1551,15 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) data &= ~(u64)0x100; /* ignore ignne emulation enable */ data &= ~(u64)0x8; /* ignore TLB cache disable */ if (data != 0) { - pr_unimpl(vcpu, "unimplemented HWCR wrmsr: 0x%llx\n", - data); + vcpu_unimpl(vcpu, "unimplemented HWCR wrmsr: 0x%llx\n", + data); return 1; } break; case MSR_FAM10H_MMIO_CONF_BASE: if (data != 0) { - pr_unimpl(vcpu, "unimplemented MMIO_CONF_BASE wrmsr: " - "0x%llx\n", data); + vcpu_unimpl(vcpu, "unimplemented MMIO_CONF_BASE wrmsr: " + "0x%llx\n", data); return 1; } break; @@ -1574,8 +1574,8 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) thus reserved and should throw a #GP */ return 1; } - pr_unimpl(vcpu, "%s: MSR_IA32_DEBUGCTLMSR 0x%llx, nop\n", - __func__, data); + vcpu_unimpl(vcpu, "%s: MSR_IA32_DEBUGCTLMSR 0x%llx, nop\n", + __func__, data); break; case MSR_IA32_UCODE_REV: case MSR_IA32_UCODE_WRITE: @@ -1671,8 +1671,8 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) case MSR_K7_EVNTSEL2: case MSR_K7_EVNTSEL3: if (data != 0) - pr_unimpl(vcpu, "unimplemented perfctr wrmsr: " - "0x%x data 0x%llx\n", msr, data); + vcpu_unimpl(vcpu, "unimplemented perfctr wrmsr: " + "0x%x data 0x%llx\n", msr, data); break; /* at least RHEL 4 unconditionally writes to the perfctr registers, * so we ignore writes to make it happy. @@ -1681,8 +1681,8 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) case MSR_K7_PERFCTR1: case MSR_K7_PERFCTR2: case MSR_K7_PERFCTR3: - pr_unimpl(vcpu, "unimplemented perfctr wrmsr: " - "0x%x data 0x%llx\n", msr, data); + vcpu_unimpl(vcpu, "unimplemented perfctr wrmsr: " + "0x%x data 0x%llx\n", msr, data); break; case MSR_P6_PERFCTR0: case MSR_P6_PERFCTR1: @@ -1693,8 +1693,8 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) return kvm_pmu_set_msr(vcpu, msr, data); if (pr || data != 0) - pr_unimpl(vcpu, "disabled perfctr wrmsr: " - "0x%x data 0x%llx\n", msr, data); + vcpu_unimpl(vcpu, "disabled perfctr wrmsr: " + "0x%x data 0x%llx\n", msr, data); break; case MSR_K7_CLK_CTL: /* @@ -1720,7 +1720,7 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) /* Drop writes to this legacy MSR -- see rdmsr * counterpart for further detail. */ - pr_unimpl(vcpu, "ignored wrmsr: 0x%x data %llx\n", msr, data); + vcpu_unimpl(vcpu, "ignored wrmsr: 0x%x data %llx\n", msr, data); break; case MSR_AMD64_OSVW_ID_LENGTH: if (!guest_cpuid_has_osvw(vcpu)) @@ -1738,12 +1738,12 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) if (kvm_pmu_msr(vcpu, msr)) return kvm_pmu_set_msr(vcpu, msr, data); if (!ignore_msrs) { - pr_unimpl(vcpu, "unhandled wrmsr: 0x%x data %llx\n", - msr, data); + vcpu_unimpl(vcpu, "unhandled wrmsr: 0x%x data %llx\n", + msr, data); return 1; } else { - pr_unimpl(vcpu, "ignored wrmsr: 0x%x data %llx\n", - msr, data); + vcpu_unimpl(vcpu, "ignored wrmsr: 0x%x data %llx\n", + msr, data); break; } } @@ -1846,7 +1846,7 @@ static int get_msr_hyperv_pw(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) data = kvm->arch.hv_hypercall; break; default: - pr_unimpl(vcpu, "Hyper-V unhandled rdmsr: 0x%x\n", msr); + vcpu_unimpl(vcpu, "Hyper-V unhandled rdmsr: 0x%x\n", msr); return 1; } @@ -1877,7 +1877,7 @@ static int get_msr_hyperv(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) data = vcpu->arch.hv_vapic; break; default: - pr_unimpl(vcpu, "Hyper-V unhandled rdmsr: 0x%x\n", msr); + vcpu_unimpl(vcpu, "Hyper-V unhandled rdmsr: 0x%x\n", msr); return 1; } *pdata = data; @@ -2030,10 +2030,10 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) if (kvm_pmu_msr(vcpu, msr)) return kvm_pmu_get_msr(vcpu, msr, pdata); if (!ignore_msrs) { - pr_unimpl(vcpu, "unhandled rdmsr: 0x%x\n", msr); + vcpu_unimpl(vcpu, "unhandled rdmsr: 0x%x\n", msr); return 1; } else { - pr_unimpl(vcpu, "ignored rdmsr: 0x%x\n", msr); + vcpu_unimpl(vcpu, "ignored rdmsr: 0x%x\n", msr); data = 0; } break; @@ -4116,7 +4116,7 @@ static unsigned long emulator_get_cr(struct x86_emulate_ctxt *ctxt, int cr) value = kvm_get_cr8(vcpu); break; default: - vcpu_printf(vcpu, "%s: unexpected cr %u\n", __func__, cr); + kvm_err("%s: unexpected cr %u\n", __func__, cr); return 0; } @@ -4145,7 +4145,7 @@ static int emulator_set_cr(struct x86_emulate_ctxt *ctxt, int cr, ulong val) res = kvm_set_cr8(vcpu, val); break; default: - vcpu_printf(vcpu, "%s: unexpected cr %u\n", __func__, cr); + kvm_err("%s: unexpected cr %u\n", __func__, cr); res = -1; } diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 19b83f6efa49..27ac8a4767fa 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -314,13 +314,19 @@ struct kvm { long tlbs_dirty; }; -/* The guest did something we don't support. */ -#define pr_unimpl(vcpu, fmt, ...) \ - pr_err_ratelimited("kvm: %i: cpu%i " fmt, \ - current->tgid, (vcpu)->vcpu_id , ## __VA_ARGS__) +#define kvm_err(fmt, ...) \ + pr_err("kvm [%i]: " fmt, task_pid_nr(current), ## __VA_ARGS__) +#define kvm_info(fmt, ...) \ + pr_info("kvm [%i]: " fmt, task_pid_nr(current), ## __VA_ARGS__) +#define kvm_debug(fmt, ...) \ + pr_debug("kvm [%i]: " fmt, task_pid_nr(current), ## __VA_ARGS__) +#define kvm_pr_unimpl(fmt, ...) \ + pr_err_ratelimited("kvm [%i]: " fmt, \ + task_tgid_nr(current), ## __VA_ARGS__) -#define kvm_printf(kvm, fmt ...) printk(KERN_DEBUG fmt) -#define vcpu_printf(vcpu, fmt...) kvm_printf(vcpu->kvm, fmt) +/* The guest did something we don't support. */ +#define vcpu_unimpl(vcpu, fmt, ...) \ + kvm_pr_unimpl("vcpu%i " fmt, (vcpu)->vcpu_id, ## __VA_ARGS__) static inline struct kvm_vcpu *kvm_get_vcpu(struct kvm *kvm, int i) { -- cgit v1.2.3 From 67130934fb579fdf0f2f6d745960264378b57dc8 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sat, 26 May 2012 23:26:43 -0500 Subject: libceph: embed ceph connection structure in mon_client A monitor client has a pointer to a ceph connection structure in it. This is the only one of the three ceph client types that do it this way; the OSD and MDS clients embed the connection into their main structures. There is always exactly one ceph connection for a monitor client, so there is no need to allocate it separate from the monitor client structure. So switch the ceph_mon_client structure to embed its ceph_connection structure. Signed-off-by: Alex Elder Reviewed-by: Sage Weil --- include/linux/ceph/mon_client.h | 2 +- net/ceph/mon_client.c | 47 ++++++++++++++++++----------------------- 2 files changed, 21 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/mon_client.h b/include/linux/ceph/mon_client.h index 545f85917780..2113e3850a4e 100644 --- a/include/linux/ceph/mon_client.h +++ b/include/linux/ceph/mon_client.h @@ -70,7 +70,7 @@ struct ceph_mon_client { bool hunting; int cur_mon; /* last monitor i contacted */ unsigned long sub_sent, sub_renew_after; - struct ceph_connection *con; + struct ceph_connection con; bool have_fsid; /* pending generic requests */ diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c index 704dc95dc620..ac4d6b100730 100644 --- a/net/ceph/mon_client.c +++ b/net/ceph/mon_client.c @@ -106,9 +106,9 @@ static void __send_prepared_auth_request(struct ceph_mon_client *monc, int len) monc->pending_auth = 1; monc->m_auth->front.iov_len = len; monc->m_auth->hdr.front_len = cpu_to_le32(len); - ceph_con_revoke(monc->con, monc->m_auth); + ceph_con_revoke(&monc->con, monc->m_auth); ceph_msg_get(monc->m_auth); /* keep our ref */ - ceph_con_send(monc->con, monc->m_auth); + ceph_con_send(&monc->con, monc->m_auth); } /* @@ -117,8 +117,8 @@ static void __send_prepared_auth_request(struct ceph_mon_client *monc, int len) static void __close_session(struct ceph_mon_client *monc) { dout("__close_session closing mon%d\n", monc->cur_mon); - ceph_con_revoke(monc->con, monc->m_auth); - ceph_con_close(monc->con); + ceph_con_revoke(&monc->con, monc->m_auth); + ceph_con_close(&monc->con); monc->cur_mon = -1; monc->pending_auth = 0; ceph_auth_reset(monc->auth); @@ -142,9 +142,9 @@ static int __open_session(struct ceph_mon_client *monc) monc->want_next_osdmap = !!monc->want_next_osdmap; dout("open_session mon%d opening\n", monc->cur_mon); - monc->con->peer_name.type = CEPH_ENTITY_TYPE_MON; - monc->con->peer_name.num = cpu_to_le64(monc->cur_mon); - ceph_con_open(monc->con, + monc->con.peer_name.type = CEPH_ENTITY_TYPE_MON; + monc->con.peer_name.num = cpu_to_le64(monc->cur_mon); + ceph_con_open(&monc->con, &monc->monmap->mon_inst[monc->cur_mon].addr); /* initiatiate authentication handshake */ @@ -226,8 +226,8 @@ static void __send_subscribe(struct ceph_mon_client *monc) msg->front.iov_len = p - msg->front.iov_base; msg->hdr.front_len = cpu_to_le32(msg->front.iov_len); - ceph_con_revoke(monc->con, msg); - ceph_con_send(monc->con, ceph_msg_get(msg)); + ceph_con_revoke(&monc->con, msg); + ceph_con_send(&monc->con, ceph_msg_get(msg)); monc->sub_sent = jiffies | 1; /* never 0 */ } @@ -247,7 +247,7 @@ static void handle_subscribe_ack(struct ceph_mon_client *monc, if (monc->hunting) { pr_info("mon%d %s session established\n", monc->cur_mon, - ceph_pr_addr(&monc->con->peer_addr.in_addr)); + ceph_pr_addr(&monc->con.peer_addr.in_addr)); monc->hunting = false; } dout("handle_subscribe_ack after %d seconds\n", seconds); @@ -461,7 +461,7 @@ static int do_generic_request(struct ceph_mon_client *monc, req->request->hdr.tid = cpu_to_le64(req->tid); __insert_generic_request(monc, req); monc->num_generic_requests++; - ceph_con_send(monc->con, ceph_msg_get(req->request)); + ceph_con_send(&monc->con, ceph_msg_get(req->request)); mutex_unlock(&monc->mutex); err = wait_for_completion_interruptible(&req->completion); @@ -684,8 +684,8 @@ static void __resend_generic_request(struct ceph_mon_client *monc) for (p = rb_first(&monc->generic_request_tree); p; p = rb_next(p)) { req = rb_entry(p, struct ceph_mon_generic_request, node); - ceph_con_revoke(monc->con, req->request); - ceph_con_send(monc->con, ceph_msg_get(req->request)); + ceph_con_revoke(&monc->con, req->request); + ceph_con_send(&monc->con, ceph_msg_get(req->request)); } } @@ -705,7 +705,7 @@ static void delayed_work(struct work_struct *work) __close_session(monc); __open_session(monc); /* continue hunting */ } else { - ceph_con_keepalive(monc->con); + ceph_con_keepalive(&monc->con); __validate_auth(monc); @@ -760,19 +760,16 @@ int ceph_monc_init(struct ceph_mon_client *monc, struct ceph_client *cl) goto out; /* connection */ - monc->con = kmalloc(sizeof(*monc->con), GFP_KERNEL); - if (!monc->con) - goto out_monmap; - ceph_con_init(&monc->client->msgr, monc->con); - monc->con->private = monc; - monc->con->ops = &mon_con_ops; + ceph_con_init(&monc->client->msgr, &monc->con); + monc->con.private = monc; + monc->con.ops = &mon_con_ops; /* authentication */ monc->auth = ceph_auth_init(cl->options->name, cl->options->key); if (IS_ERR(monc->auth)) { err = PTR_ERR(monc->auth); - goto out_con; + goto out_monmap; } monc->auth->want_keys = CEPH_ENTITY_TYPE_AUTH | CEPH_ENTITY_TYPE_MON | @@ -824,8 +821,6 @@ out_subscribe_ack: ceph_msg_put(monc->m_subscribe_ack); out_auth: ceph_auth_destroy(monc->auth); -out_con: - monc->con->ops->put(monc->con); out_monmap: kfree(monc->monmap); out: @@ -841,9 +836,7 @@ void ceph_monc_stop(struct ceph_mon_client *monc) mutex_lock(&monc->mutex); __close_session(monc); - monc->con->private = NULL; - monc->con->ops->put(monc->con); - monc->con = NULL; + monc->con.private = NULL; mutex_unlock(&monc->mutex); @@ -1021,7 +1014,7 @@ static void mon_fault(struct ceph_connection *con) if (!monc->hunting) pr_info("mon%d %s session lost, " "hunting for new mon\n", monc->cur_mon, - ceph_pr_addr(&monc->con->peer_addr.in_addr)); + ceph_pr_addr(&monc->con.peer_addr.in_addr)); __close_session(monc); if (!monc->hunting) { -- cgit v1.2.3 From 1bfd89f4e6e1adc6a782d94aa5d4c53be1e404d7 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sat, 26 May 2012 23:26:43 -0500 Subject: libceph: fully initialize connection in con_init() Move the initialization of a ceph connection's private pointer, operations vector pointer, and peer name information into ceph_con_init(). Rearrange the arguments so the connection pointer is first. Hide the byte-swapping of the peer entity number inside ceph_con_init() Signed-off-by: Alex Elder Reviewed-by: Sage Weil --- fs/ceph/mds_client.c | 7 ++----- include/linux/ceph/messenger.h | 6 ++++-- net/ceph/messenger.c | 9 ++++++++- net/ceph/mon_client.c | 8 +++----- net/ceph/osd_client.c | 7 ++----- 5 files changed, 19 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index ad30261cd4c0..ecd7f15741c1 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -394,11 +394,8 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc, s->s_seq = 0; mutex_init(&s->s_mutex); - ceph_con_init(&mdsc->fsc->client->msgr, &s->s_con); - s->s_con.private = s; - s->s_con.ops = &mds_con_ops; - s->s_con.peer_name.type = CEPH_ENTITY_TYPE_MDS; - s->s_con.peer_name.num = cpu_to_le64(mds); + ceph_con_init(&s->s_con, s, &mds_con_ops, &mdsc->fsc->client->msgr, + CEPH_ENTITY_TYPE_MDS, mds); spin_lock_init(&s->s_gen_ttl_lock); s->s_cap_gen = 0; diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index 5e852f444f68..dd27837f79ac 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -227,8 +227,10 @@ extern void ceph_messenger_init(struct ceph_messenger *msgr, u32 required_features, bool nocrc); -extern void ceph_con_init(struct ceph_messenger *msgr, - struct ceph_connection *con); +extern void ceph_con_init(struct ceph_connection *con, void *private, + const struct ceph_connection_operations *ops, + struct ceph_messenger *msgr, __u8 entity_type, + __u64 entity_num); extern void ceph_con_open(struct ceph_connection *con, struct ceph_entity_addr *addr); extern bool ceph_con_opened(struct ceph_connection *con); diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 36b440a00cc2..3b65f6e6911b 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -521,15 +521,22 @@ void ceph_con_put(struct ceph_connection *con) /* * initialize a new connection. */ -void ceph_con_init(struct ceph_messenger *msgr, struct ceph_connection *con) +void ceph_con_init(struct ceph_connection *con, void *private, + const struct ceph_connection_operations *ops, + struct ceph_messenger *msgr, __u8 entity_type, __u64 entity_num) { dout("con_init %p\n", con); memset(con, 0, sizeof(*con)); + con->private = private; + con->ops = ops; atomic_set(&con->nref, 1); con->msgr = msgr; con_sock_state_init(con); + con->peer_name.type = (__u8) entity_type; + con->peer_name.num = cpu_to_le64(entity_num); + mutex_init(&con->mutex); INIT_LIST_HEAD(&con->out_queue); INIT_LIST_HEAD(&con->out_sent); diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c index 6adbea78b168..ab6b24a5169e 100644 --- a/net/ceph/mon_client.c +++ b/net/ceph/mon_client.c @@ -142,11 +142,9 @@ static int __open_session(struct ceph_mon_client *monc) monc->sub_renew_after = jiffies; /* i.e., expired */ monc->want_next_osdmap = !!monc->want_next_osdmap; - ceph_con_init(&monc->client->msgr, &monc->con); - monc->con.private = monc; - monc->con.ops = &mon_con_ops; - monc->con.peer_name.type = CEPH_ENTITY_TYPE_MON; - monc->con.peer_name.num = cpu_to_le64(monc->cur_mon); + ceph_con_init(&monc->con, monc, &mon_con_ops, + &monc->client->msgr, + CEPH_ENTITY_TYPE_MON, monc->cur_mon); dout("open_session mon%d opening\n", monc->cur_mon); ceph_con_open(&monc->con, diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 5b41a6929cd9..448c9da8beff 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -640,11 +640,8 @@ static struct ceph_osd *create_osd(struct ceph_osd_client *osdc, int onum) INIT_LIST_HEAD(&osd->o_osd_lru); osd->o_incarnation = 1; - ceph_con_init(&osdc->client->msgr, &osd->o_con); - osd->o_con.private = osd; - osd->o_con.ops = &osd_con_ops; - osd->o_con.peer_name.type = CEPH_ENTITY_TYPE_OSD; - osd->o_con.peer_name.num = cpu_to_le64(onum); + ceph_con_init(&osd->o_con, osd, &osd_con_ops, &osdc->client->msgr, + CEPH_ENTITY_TYPE_OSD, onum); INIT_LIST_HEAD(&osd->o_keepalive_item); return osd; -- cgit v1.2.3 From 38941f8031bf042dba3ced6394ba3a3b16c244ea Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 1 Jun 2012 14:56:43 -0500 Subject: libceph: have messages point to their connection When a ceph message is queued for sending it is placed on a list of pending messages (ceph_connection->out_queue). When they are actually sent over the wire, they are moved from that list to another (ceph_connection->out_sent). When acknowledgement for the message is received, it is removed from the sent messages list. During that entire time the message is "in the possession" of a single ceph connection. Keep track of that connection in the message. This will be used in the next patch (and is a helpful bit of information for debugging anyway). Signed-off-by: Alex Elder Reviewed-by: Sage Weil --- include/linux/ceph/messenger.h | 3 +++ net/ceph/messenger.c | 27 +++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index dd27837f79ac..6df837f72761 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -77,7 +77,10 @@ struct ceph_msg { unsigned nr_pages; /* size of page array */ unsigned page_alignment; /* io offset in first page */ struct ceph_pagelist *pagelist; /* instead of pages */ + + struct ceph_connection *con; struct list_head list_head; + struct kref kref; struct bio *bio; /* instead of pages/pagelist */ struct bio *bio_iter; /* bio iterator */ diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 98ca23726ea6..68b49b5b8e86 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -414,6 +414,9 @@ static int con_close_socket(struct ceph_connection *con) static void ceph_msg_remove(struct ceph_msg *msg) { list_del_init(&msg->list_head); + BUG_ON(msg->con == NULL); + msg->con = NULL; + ceph_msg_put(msg); } static void ceph_msg_remove_list(struct list_head *head) @@ -433,6 +436,8 @@ static void reset_connection(struct ceph_connection *con) ceph_msg_remove_list(&con->out_sent); if (con->in_msg) { + BUG_ON(con->in_msg->con != con); + con->in_msg->con = NULL; ceph_msg_put(con->in_msg); con->in_msg = NULL; } @@ -625,8 +630,10 @@ static void prepare_write_message(struct ceph_connection *con) &con->out_temp_ack); } + BUG_ON(list_empty(&con->out_queue)); m = list_first_entry(&con->out_queue, struct ceph_msg, list_head); con->out_msg = m; + BUG_ON(m->con != con); /* put message on sent list */ ceph_msg_get(m); @@ -1806,6 +1813,8 @@ static int read_partial_message(struct ceph_connection *con) "error allocating memory for incoming message"; return -ENOMEM; } + + BUG_ON(con->in_msg->con != con); m = con->in_msg; m->front.iov_len = 0; /* haven't read it yet */ if (m->middle) @@ -1901,6 +1910,8 @@ static void process_message(struct ceph_connection *con) { struct ceph_msg *msg; + BUG_ON(con->in_msg->con != con); + con->in_msg->con = NULL; msg = con->in_msg; con->in_msg = NULL; @@ -2260,6 +2271,8 @@ static void ceph_fault(struct ceph_connection *con) con_close_socket(con); if (con->in_msg) { + BUG_ON(con->in_msg->con != con); + con->in_msg->con = NULL; ceph_msg_put(con->in_msg); con->in_msg = NULL; } @@ -2378,6 +2391,8 @@ void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg) /* queue */ mutex_lock(&con->mutex); + BUG_ON(msg->con != NULL); + msg->con = con; BUG_ON(!list_empty(&msg->list_head)); list_add_tail(&msg->list_head, &con->out_queue); dout("----- %p to %s%lld %d=%s len %d+%d+%d -----\n", msg, @@ -2403,13 +2418,16 @@ void ceph_con_revoke(struct ceph_connection *con, struct ceph_msg *msg) { mutex_lock(&con->mutex); if (!list_empty(&msg->list_head)) { - dout("con_revoke %p msg %p - was on queue\n", con, msg); + dout("%s %p msg %p - was on queue\n", __func__, con, msg); list_del_init(&msg->list_head); + BUG_ON(msg->con == NULL); + msg->con = NULL; + ceph_msg_put(msg); msg->hdr.seq = 0; } if (con->out_msg == msg) { - dout("con_revoke %p msg %p - was sending\n", con, msg); + dout("%s %p msg %p - was sending\n", __func__, con, msg); con->out_msg = NULL; if (con->out_kvec_is_msg) { con->out_skip = con->out_kvec_bytes; @@ -2478,6 +2496,8 @@ struct ceph_msg *ceph_msg_new(int type, int front_len, gfp_t flags, if (m == NULL) goto out; kref_init(&m->kref); + + m->con = NULL; INIT_LIST_HEAD(&m->list_head); m->hdr.tid = 0; @@ -2598,6 +2618,8 @@ static bool ceph_con_in_msg_alloc(struct ceph_connection *con, mutex_unlock(&con->mutex); con->in_msg = con->ops->alloc_msg(con, hdr, &skip); mutex_lock(&con->mutex); + if (con->in_msg) + con->in_msg->con = con; if (skip) con->in_msg = NULL; @@ -2611,6 +2633,7 @@ static bool ceph_con_in_msg_alloc(struct ceph_connection *con, type, front_len); return false; } + con->in_msg->con = con; con->in_msg->page_alignment = le16_to_cpu(hdr->data_off); } memcpy(&con->in_msg->hdr, &con->in_hdr, sizeof(con->in_hdr)); -- cgit v1.2.3 From 6740a845b2543cc46e1902ba21bac743fbadd0dc Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 1 Jun 2012 14:56:43 -0500 Subject: libceph: make ceph_con_revoke() a msg operation ceph_con_revoke() is passed both a message and a ceph connection. Now that any message associated with a connection holds a pointer to that connection, there's no need to provide the connection when revoking a message. This has the added benefit of precluding the possibility of the providing the wrong connection pointer. If the message's connection pointer is null, it is not being tracked by any connection, so revoking it is a no-op. This is supported as a convenience for upper layers, so they can revoke a message that is not actually "in flight." Rename the function ceph_msg_revoke() to reflect that it is really an operation on a message, not a connection. Signed-off-by: Alex Elder Reviewed-by: Sage Weil --- include/linux/ceph/messenger.h | 3 ++- net/ceph/messenger.c | 7 ++++++- net/ceph/mon_client.c | 8 ++++---- net/ceph/osd_client.c | 4 ++-- 4 files changed, 14 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index 6df837f72761..9008f81c20cd 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -239,7 +239,8 @@ extern void ceph_con_open(struct ceph_connection *con, extern bool ceph_con_opened(struct ceph_connection *con); extern void ceph_con_close(struct ceph_connection *con); extern void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg); -extern void ceph_con_revoke(struct ceph_connection *con, struct ceph_msg *msg); + +extern void ceph_msg_revoke(struct ceph_msg *msg); extern void ceph_con_revoke_message(struct ceph_connection *con, struct ceph_msg *msg); extern void ceph_con_keepalive(struct ceph_connection *con); diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 88ac083bb995..d636903ad4b2 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -2421,8 +2421,13 @@ EXPORT_SYMBOL(ceph_con_send); /* * Revoke a message that was previously queued for send */ -void ceph_con_revoke(struct ceph_connection *con, struct ceph_msg *msg) +void ceph_msg_revoke(struct ceph_msg *msg) { + struct ceph_connection *con = msg->con; + + if (!con) + return; /* Message not in our possession */ + mutex_lock(&con->mutex); if (!list_empty(&msg->list_head)) { dout("%s %p msg %p - was on queue\n", __func__, con, msg); diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c index 8462ccec6333..7a16750d62a6 100644 --- a/net/ceph/mon_client.c +++ b/net/ceph/mon_client.c @@ -106,7 +106,7 @@ static void __send_prepared_auth_request(struct ceph_mon_client *monc, int len) monc->pending_auth = 1; monc->m_auth->front.iov_len = len; monc->m_auth->hdr.front_len = cpu_to_le32(len); - ceph_con_revoke(&monc->con, monc->m_auth); + ceph_msg_revoke(monc->m_auth); ceph_msg_get(monc->m_auth); /* keep our ref */ ceph_con_send(&monc->con, monc->m_auth); } @@ -117,7 +117,7 @@ static void __send_prepared_auth_request(struct ceph_mon_client *monc, int len) static void __close_session(struct ceph_mon_client *monc) { dout("__close_session closing mon%d\n", monc->cur_mon); - ceph_con_revoke(&monc->con, monc->m_auth); + ceph_msg_revoke(monc->m_auth); ceph_con_close(&monc->con); monc->con.private = NULL; monc->cur_mon = -1; @@ -229,7 +229,7 @@ static void __send_subscribe(struct ceph_mon_client *monc) msg->front.iov_len = p - msg->front.iov_base; msg->hdr.front_len = cpu_to_le32(msg->front.iov_len); - ceph_con_revoke(&monc->con, msg); + ceph_msg_revoke(msg); ceph_con_send(&monc->con, ceph_msg_get(msg)); monc->sub_sent = jiffies | 1; /* never 0 */ @@ -688,7 +688,7 @@ static void __resend_generic_request(struct ceph_mon_client *monc) for (p = rb_first(&monc->generic_request_tree); p; p = rb_next(p)) { req = rb_entry(p, struct ceph_mon_generic_request, node); - ceph_con_revoke(&monc->con, req->request); + ceph_msg_revoke(req->request); ceph_con_send(&monc->con, ceph_msg_get(req->request)); } } diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 24b427b1eca4..ad78705a4aff 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -852,7 +852,7 @@ static void __unregister_request(struct ceph_osd_client *osdc, if (req->r_osd) { /* make sure the original request isn't in flight. */ - ceph_con_revoke(&req->r_osd->o_con, req->r_request); + ceph_msg_revoke(req->r_request); list_del_init(&req->r_osd_item); if (list_empty(&req->r_osd->o_requests) && @@ -879,7 +879,7 @@ static void __unregister_request(struct ceph_osd_client *osdc, static void __cancel_request(struct ceph_osd_request *req) { if (req->r_sent && req->r_osd) { - ceph_con_revoke(&req->r_osd->o_con, req->r_request); + ceph_msg_revoke(req->r_request); req->r_sent = 0; } } -- cgit v1.2.3 From 8921d114f5574c6da2cdd00749d185633ecf88f3 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 1 Jun 2012 14:56:43 -0500 Subject: libceph: make ceph_con_revoke_message() a msg op ceph_con_revoke_message() is passed both a message and a ceph connection. A ceph_msg allocated for incoming messages on a connection always has a pointer to that connection, so there's no need to provide the connection when revoking such a message. Note that the existing logic does not preclude the message supplied being a null/bogus message pointer. The only user of this interface is the OSD client, and the only value an osd client passes is a request's r_reply field. That is always non-null (except briefly in an error path in ceph_osdc_alloc_request(), and that drops the only reference so the request won't ever have a reply to revoke). So we can safely assume the passed-in message is non-null, but add a BUG_ON() to make it very obvious we are imposing this restriction. Rename the function ceph_msg_revoke_incoming() to reflect that it is really an operation on an incoming message. Signed-off-by: Alex Elder Reviewed-by: Sage Weil --- include/linux/ceph/messenger.h | 4 ++-- net/ceph/messenger.c | 22 ++++++++++++++++------ net/ceph/osd_client.c | 9 ++++----- 3 files changed, 22 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index 9008f81c20cd..a334dbd1b324 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -241,8 +241,8 @@ extern void ceph_con_close(struct ceph_connection *con); extern void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg); extern void ceph_msg_revoke(struct ceph_msg *msg); -extern void ceph_con_revoke_message(struct ceph_connection *con, - struct ceph_msg *msg); +extern void ceph_msg_revoke_incoming(struct ceph_msg *msg); + extern void ceph_con_keepalive(struct ceph_connection *con); extern struct ceph_connection *ceph_con_get(struct ceph_connection *con); extern void ceph_con_put(struct ceph_connection *con); diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index d636903ad4b2..3857f815c035 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -2456,17 +2456,27 @@ void ceph_msg_revoke(struct ceph_msg *msg) /* * Revoke a message that we may be reading data into */ -void ceph_con_revoke_message(struct ceph_connection *con, struct ceph_msg *msg) +void ceph_msg_revoke_incoming(struct ceph_msg *msg) { + struct ceph_connection *con; + + BUG_ON(msg == NULL); + if (!msg->con) { + dout("%s msg %p null con\n", __func__, msg); + + return; /* Message not in our possession */ + } + + con = msg->con; mutex_lock(&con->mutex); - if (con->in_msg && con->in_msg == msg) { + if (con->in_msg == msg) { unsigned front_len = le32_to_cpu(con->in_hdr.front_len); unsigned middle_len = le32_to_cpu(con->in_hdr.middle_len); unsigned data_len = le32_to_cpu(con->in_hdr.data_len); /* skip rest of message */ - dout("con_revoke_pages %p msg %p revoked\n", con, msg); - con->in_base_pos = con->in_base_pos - + dout("%s %p msg %p revoked\n", __func__, con, msg); + con->in_base_pos = con->in_base_pos - sizeof(struct ceph_msg_header) - front_len - middle_len - @@ -2477,8 +2487,8 @@ void ceph_con_revoke_message(struct ceph_connection *con, struct ceph_msg *msg) con->in_tag = CEPH_MSGR_TAG_READY; con->in_seq++; } else { - dout("con_revoke_pages %p msg %p pages %p no-op\n", - con, con->in_msg, msg); + dout("%s %p in_msg %p msg %p no-op\n", + __func__, con, con->in_msg, msg); } mutex_unlock(&con->mutex); } diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index ad78705a4aff..c178c770acb4 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -140,10 +140,9 @@ void ceph_osdc_release_request(struct kref *kref) if (req->r_request) ceph_msg_put(req->r_request); if (req->r_con_filling_msg) { - dout("release_request revoking pages %p from con %p\n", + dout("%s revoking pages %p from con %p\n", __func__, req->r_pages, req->r_con_filling_msg); - ceph_con_revoke_message(req->r_con_filling_msg, - req->r_reply); + ceph_msg_revoke_incoming(req->r_reply); req->r_con_filling_msg->ops->put(req->r_con_filling_msg); } if (req->r_reply) @@ -2022,9 +2021,9 @@ static struct ceph_msg *get_reply(struct ceph_connection *con, } if (req->r_con_filling_msg) { - dout("get_reply revoking msg %p from old con %p\n", + dout("%s revoking msg %p from old con %p\n", __func__, req->r_reply, req->r_con_filling_msg); - ceph_con_revoke_message(req->r_con_filling_msg, req->r_reply); + ceph_msg_revoke_incoming(req->r_reply); req->r_con_filling_msg->ops->put(req->r_con_filling_msg); req->r_con_filling_msg = NULL; } -- cgit v1.2.3 From 6be96a5c905178637ec06a5d456a76b2b304fca3 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Wed, 6 Jun 2012 19:12:30 -0700 Subject: cgroup: remove hierarchy_mutex It was introduced for memcg to iterate cgroup hierarchy without holding cgroup_mutex, but soon after that it was replaced with a lockless way in memcg. No one used hierarchy_mutex since that, so remove it. Signed-off-by: Li Zefan Signed-off-by: Tejun Heo --- Documentation/cgroups/cgroups.txt | 2 +- include/linux/cgroup.h | 17 ++------------- kernel/cgroup.c | 45 --------------------------------------- 3 files changed, 3 insertions(+), 61 deletions(-) (limited to 'include') diff --git a/Documentation/cgroups/cgroups.txt b/Documentation/cgroups/cgroups.txt index 8e74980ab385..e86faaea7d66 100644 --- a/Documentation/cgroups/cgroups.txt +++ b/Documentation/cgroups/cgroups.txt @@ -656,7 +656,7 @@ example in cpusets, no task may attach before 'cpus' and 'mems' are set up. void bind(struct cgroup *root) -(cgroup_mutex and ss->hierarchy_mutex held by caller) +(cgroup_mutex held by caller) Called when a cgroup subsystem is rebound to a different hierarchy and root cgroup. Currently this will only involve movement between diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index d3f5fba2c159..c90eaa803440 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -499,22 +499,9 @@ struct cgroup_subsys { #define MAX_CGROUP_TYPE_NAMELEN 32 const char *name; - /* - * Protects sibling/children links of cgroups in this - * hierarchy, plus protects which hierarchy (or none) the - * subsystem is a part of (i.e. root/sibling). To avoid - * potential deadlocks, the following operations should not be - * undertaken while holding any hierarchy_mutex: - * - * - allocating memory - * - initiating hotplug events - */ - struct mutex hierarchy_mutex; - struct lock_class_key subsys_key; - /* * Link to parent, and list entry in parent's children. - * Protected by this->hierarchy_mutex and cgroup_lock() + * Protected by cgroup_lock() */ struct cgroupfs_root *root; struct list_head sibling; @@ -602,7 +589,7 @@ int cgroup_attach_task_all(struct task_struct *from, struct task_struct *); * the lifetime of cgroup_subsys_state is subsys's matter. * * Looking up and scanning function should be called under rcu_read_lock(). - * Taking cgroup_mutex()/hierarchy_mutex() is not necessary for following calls. + * Taking cgroup_mutex is not necessary for following calls. * But the css returned by this routine can be "not populated yet" or "being * destroyed". The caller should check css and cgroup's status. */ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index ceeafe874b3f..dec62f5936ef 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -1073,28 +1073,24 @@ static int rebind_subsystems(struct cgroupfs_root *root, BUG_ON(cgrp->subsys[i]); BUG_ON(!dummytop->subsys[i]); BUG_ON(dummytop->subsys[i]->cgroup != dummytop); - mutex_lock(&ss->hierarchy_mutex); cgrp->subsys[i] = dummytop->subsys[i]; cgrp->subsys[i]->cgroup = cgrp; list_move(&ss->sibling, &root->subsys_list); ss->root = root; if (ss->bind) ss->bind(cgrp); - mutex_unlock(&ss->hierarchy_mutex); /* refcount was already taken, and we're keeping it */ } else if (bit & removed_bits) { /* We're removing this subsystem */ BUG_ON(ss == NULL); BUG_ON(cgrp->subsys[i] != dummytop->subsys[i]); BUG_ON(cgrp->subsys[i]->cgroup != cgrp); - mutex_lock(&ss->hierarchy_mutex); if (ss->bind) ss->bind(dummytop); dummytop->subsys[i]->cgroup = dummytop; cgrp->subsys[i] = NULL; subsys[i]->root = &rootnode; list_move(&ss->sibling, &rootnode.subsys_list); - mutex_unlock(&ss->hierarchy_mutex); /* subsystem is now free - drop reference on module */ module_put(ss->module); } else if (bit & final_bits) { @@ -3917,37 +3913,6 @@ static void init_cgroup_css(struct cgroup_subsys_state *css, set_bit(CSS_CLEAR_CSS_REFS, &css->flags); } -static void cgroup_lock_hierarchy(struct cgroupfs_root *root) -{ - /* We need to take each hierarchy_mutex in a consistent order */ - int i; - - /* - * No worry about a race with rebind_subsystems that might mess up the - * locking order, since both parties are under cgroup_mutex. - */ - for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) { - struct cgroup_subsys *ss = subsys[i]; - if (ss == NULL) - continue; - if (ss->root == root) - mutex_lock(&ss->hierarchy_mutex); - } -} - -static void cgroup_unlock_hierarchy(struct cgroupfs_root *root) -{ - int i; - - for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) { - struct cgroup_subsys *ss = subsys[i]; - if (ss == NULL) - continue; - if (ss->root == root) - mutex_unlock(&ss->hierarchy_mutex); - } -} - /* * cgroup_create - create a cgroup * @parent: cgroup that will be parent of the new cgroup @@ -4008,9 +3973,7 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, ss->post_clone(cgrp); } - cgroup_lock_hierarchy(root); list_add(&cgrp->sibling, &cgrp->parent->children); - cgroup_unlock_hierarchy(root); root->number_of_cgroups++; err = cgroup_create_dir(cgrp, dentry, mode); @@ -4037,9 +4000,7 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, err_remove: - cgroup_lock_hierarchy(root); list_del(&cgrp->sibling); - cgroup_unlock_hierarchy(root); root->number_of_cgroups--; err_destroy: @@ -4247,10 +4208,8 @@ again: list_del_init(&cgrp->release_list); raw_spin_unlock(&release_list_lock); - cgroup_lock_hierarchy(cgrp->root); /* delete this cgroup from parent->children */ list_del_init(&cgrp->sibling); - cgroup_unlock_hierarchy(cgrp->root); list_del_init(&cgrp->allcg_node); @@ -4324,8 +4283,6 @@ static void __init cgroup_init_subsys(struct cgroup_subsys *ss) * need to invoke fork callbacks here. */ BUG_ON(!list_empty(&init_task.tasks)); - mutex_init(&ss->hierarchy_mutex); - lockdep_set_class(&ss->hierarchy_mutex, &ss->subsys_key); ss->active = 1; /* this function shouldn't be used with modular subsystems, since they @@ -4452,8 +4409,6 @@ int __init_or_module cgroup_load_subsys(struct cgroup_subsys *ss) } write_unlock(&css_set_lock); - mutex_init(&ss->hierarchy_mutex); - lockdep_set_class(&ss->hierarchy_mutex, &ss->subsys_key); ss->active = 1; /* success! */ -- cgit v1.2.3 From 4fd1e324b7b5f80bd521b58593ada74ef89e80c4 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 6 Jun 2012 10:55:26 +0530 Subject: dma: dmaengine: add slave req id in slave_config The DMA controller like Nvidia's Tegra Dma controller supports the different slave requestor id from different slave. This need to be configure in dma controller to handle the request properly. Adding the slave-id in the slave configuration so that information can be passed from client when configuring for slave. Signed-off-by: Laxman Dewangan Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 56377df39124..ccec62f8e501 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -338,6 +338,9 @@ enum dma_slave_buswidth { * @device_fc: Flow Controller Settings. Only valid for slave channels. Fill * with 'true' if peripheral should be flow controller. Direction will be * selected at Runtime. + * @slave_id: Slave requester id. Only valid for slave channels. The dma + * slave peripheral will have unique id as dma requester which need to be + * pass as slave config. * * This struct is passed in as configuration data to a DMA engine * in order to set up a certain channel for DMA transport at runtime. @@ -365,6 +368,7 @@ struct dma_slave_config { u32 src_maxburst; u32 dst_maxburst; bool device_fc; + unsigned int slave_id; }; static inline const char *dma_chan_name(struct dma_chan *chan) -- cgit v1.2.3 From 90306c41dc3d8e5f12ecd0193dae99e0e7f6e896 Mon Sep 17 00:00:00 2001 From: Benjamin Marzinski Date: Tue, 29 May 2012 23:01:09 -0500 Subject: GFS2: Use lvbs for storing rgrp information with mount option Instead of reading in the resource groups when gfs2 is checking for free space to allocate from, gfs2 can store the necessary infromation in the resource group's lvb. Also, instead of searching for unlinked inodes in every resource group that's checked for free space, gfs2 can store the number of unlinked but inodes in the lvb, and only check for unlinked inodes if it will find some. The first time a resource group is locked, the lvb must initialized. Since this involves counting the unlinked inodes in the resource group, this takes a little extra time. But after that, if the resource group is locked with GL_SKIP, the buffer head won't be read in unless it's actually needed. Enabling the resource groups lvbs is done via the rgrplvb mount option. If this option isn't set, the lvbs will still be set and updated, but they won't be verfied or used by the filesystem. To safely turn on this option, all of the nodes mounting the filesystem must be running code with this patch, and the filesystem must have been completely unmounted since they were updated. Signed-off-by: Benjamin Marzinski Signed-off-by: Steven Whitehouse --- fs/gfs2/glock.c | 1 + fs/gfs2/incore.h | 2 + fs/gfs2/rgrp.c | 147 +++++++++++++++++++++++++++++++++++++++++--- fs/gfs2/super.c | 12 ++++ include/linux/gfs2_ondisk.h | 10 +++ 5 files changed, 163 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 3ad8cb3eeb88..10ae1645d9a5 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -769,6 +769,7 @@ int gfs2_glock_get(struct gfs2_sbd *sdp, u64 number, gl->gl_stats.stats[GFS2_LKS_DCOUNT] = 0; gl->gl_stats.stats[GFS2_LKS_QCOUNT] = 0; memset(&gl->gl_lksb, 0, sizeof(struct dlm_lksb)); + memset(gl->gl_lvb, 0, 32 * sizeof(char)); gl->gl_lksb.sb_lvbptr = gl->gl_lvb; gl->gl_tchange = jiffies; gl->gl_object = NULL; diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 5cda51a3e3bd..dc730700b3b4 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -89,6 +89,7 @@ struct gfs2_rgrpd { u64 rd_igeneration; struct gfs2_bitmap *rd_bits; struct gfs2_sbd *rd_sbd; + struct gfs2_rgrp_lvb *rd_rgl; u32 rd_last_alloc; u32 rd_flags; #define GFS2_RDF_CHECK 0x10000000 /* check for unlinked inodes */ @@ -470,6 +471,7 @@ struct gfs2_args { unsigned int ar_discard:1; /* discard requests */ unsigned int ar_errors:2; /* errors=withdraw | panic */ unsigned int ar_nobarrier:1; /* do not send barriers */ + unsigned int ar_rgrplvb:1; /* use lvbs for rgrp info */ int ar_commit; /* Commit interval */ int ar_statfs_quantum; /* The fast statfs interval */ int ar_quota_quantum; /* The quota interval */ diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index 9eca6a9cff8f..3c6f7ed16a3b 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -660,6 +660,7 @@ static int read_rindex_entry(struct gfs2_inode *ip) goto fail; rgd->rd_gl->gl_object = rgd; + rgd->rd_rgl = (struct gfs2_rgrp_lvb *)rgd->rd_gl->gl_lvb; rgd->rd_flags &= ~GFS2_RDF_UPTODATE; if (rgd->rd_data > sdp->sd_max_rg_data) sdp->sd_max_rg_data = rgd->rd_data; @@ -769,9 +770,65 @@ static void gfs2_rgrp_out(struct gfs2_rgrpd *rgd, void *buf) memset(&str->rg_reserved, 0, sizeof(str->rg_reserved)); } +static int gfs2_rgrp_lvb_valid(struct gfs2_rgrpd *rgd) +{ + struct gfs2_rgrp_lvb *rgl = rgd->rd_rgl; + struct gfs2_rgrp *str = (struct gfs2_rgrp *)rgd->rd_bits[0].bi_bh->b_data; + + if (rgl->rl_flags != str->rg_flags || rgl->rl_free != str->rg_free || + rgl->rl_dinodes != str->rg_dinodes || + rgl->rl_igeneration != str->rg_igeneration) + return 0; + return 1; +} + +static void gfs2_rgrp_ondisk2lvb(struct gfs2_rgrp_lvb *rgl, const void *buf) +{ + const struct gfs2_rgrp *str = buf; + + rgl->rl_magic = cpu_to_be32(GFS2_MAGIC); + rgl->rl_flags = str->rg_flags; + rgl->rl_free = str->rg_free; + rgl->rl_dinodes = str->rg_dinodes; + rgl->rl_igeneration = str->rg_igeneration; + rgl->__pad = 0UL; +} + +static void update_rgrp_lvb_unlinked(struct gfs2_rgrpd *rgd, u32 change) +{ + struct gfs2_rgrp_lvb *rgl = rgd->rd_rgl; + u32 unlinked = be32_to_cpu(rgl->rl_unlinked) + change; + rgl->rl_unlinked = cpu_to_be32(unlinked); +} + +static u32 count_unlinked(struct gfs2_rgrpd *rgd) +{ + struct gfs2_bitmap *bi; + const u32 length = rgd->rd_length; + const u8 *buffer = NULL; + u32 i, goal, count = 0; + + for (i = 0, bi = rgd->rd_bits; i < length; i++, bi++) { + goal = 0; + buffer = bi->bi_bh->b_data + bi->bi_offset; + WARN_ON(!buffer_uptodate(bi->bi_bh)); + while (goal < bi->bi_len * GFS2_NBBY) { + goal = gfs2_bitfit(buffer, bi->bi_len, goal, + GFS2_BLKST_UNLINKED); + if (goal == BFITNOENT) + break; + count++; + goal++; + } + } + + return count; +} + + /** - * gfs2_rgrp_go_lock - Read in a RG's header and bitmaps - * @gh: The glock holder for the resource group + * gfs2_rgrp_bh_get - Read in a RG's header and bitmaps + * @rgd: the struct gfs2_rgrpd describing the RG to read in * * Read in all of a Resource Group's header and bitmap blocks. * Caller must eventually call gfs2_rgrp_relse() to free the bitmaps. @@ -779,9 +836,8 @@ static void gfs2_rgrp_out(struct gfs2_rgrpd *rgd, void *buf) * Returns: errno */ -int gfs2_rgrp_go_lock(struct gfs2_holder *gh) +int gfs2_rgrp_bh_get(struct gfs2_rgrpd *rgd) { - struct gfs2_rgrpd *rgd = gh->gh_gl->gl_object; struct gfs2_sbd *sdp = rgd->rd_sbd; struct gfs2_glock *gl = rgd->rd_gl; unsigned int length = rgd->rd_length; @@ -789,6 +845,9 @@ int gfs2_rgrp_go_lock(struct gfs2_holder *gh) unsigned int x, y; int error; + if (rgd->rd_bits[0].bi_bh != NULL) + return 0; + for (x = 0; x < length; x++) { bi = rgd->rd_bits + x; error = gfs2_meta_read(gl, rgd->rd_addr + x, 0, &bi->bi_bh); @@ -815,7 +874,20 @@ int gfs2_rgrp_go_lock(struct gfs2_holder *gh) rgd->rd_flags |= (GFS2_RDF_UPTODATE | GFS2_RDF_CHECK); rgd->rd_free_clone = rgd->rd_free; } - + if (be32_to_cpu(GFS2_MAGIC) != rgd->rd_rgl->rl_magic) { + rgd->rd_rgl->rl_unlinked = cpu_to_be32(count_unlinked(rgd)); + gfs2_rgrp_ondisk2lvb(rgd->rd_rgl, + rgd->rd_bits[0].bi_bh->b_data); + } + else if (sdp->sd_args.ar_rgrplvb) { + if (!gfs2_rgrp_lvb_valid(rgd)){ + gfs2_consist_rgrpd(rgd); + error = -EIO; + goto fail; + } + if (rgd->rd_rgl->rl_unlinked == 0) + rgd->rd_flags &= ~GFS2_RDF_CHECK; + } return 0; fail: @@ -829,6 +901,39 @@ fail: return error; } +int update_rgrp_lvb(struct gfs2_rgrpd *rgd) +{ + u32 rl_flags; + + if (rgd->rd_flags & GFS2_RDF_UPTODATE) + return 0; + + if (be32_to_cpu(GFS2_MAGIC) != rgd->rd_rgl->rl_magic) + return gfs2_rgrp_bh_get(rgd); + + rl_flags = be32_to_cpu(rgd->rd_rgl->rl_flags); + rl_flags &= ~GFS2_RDF_MASK; + rgd->rd_flags &= GFS2_RDF_MASK; + rgd->rd_flags |= (rl_flags | GFS2_RDF_UPTODATE | GFS2_RDF_CHECK); + if (rgd->rd_rgl->rl_unlinked == 0) + rgd->rd_flags &= ~GFS2_RDF_CHECK; + rgd->rd_free = be32_to_cpu(rgd->rd_rgl->rl_free); + rgd->rd_free_clone = rgd->rd_free; + rgd->rd_dinodes = be32_to_cpu(rgd->rd_rgl->rl_dinodes); + rgd->rd_igeneration = be64_to_cpu(rgd->rd_rgl->rl_igeneration); + return 0; +} + +int gfs2_rgrp_go_lock(struct gfs2_holder *gh) +{ + struct gfs2_rgrpd *rgd = gh->gh_gl->gl_object; + struct gfs2_sbd *sdp = rgd->rd_sbd; + + if (gh->gh_flags & GL_SKIP && sdp->sd_args.ar_rgrplvb) + return 0; + return gfs2_rgrp_bh_get((struct gfs2_rgrpd *)gh->gh_gl->gl_object); +} + /** * gfs2_rgrp_go_unlock - Release RG bitmaps read in with gfs2_rgrp_bh_get() * @gh: The glock holder for the resource group @@ -842,8 +947,10 @@ void gfs2_rgrp_go_unlock(struct gfs2_holder *gh) for (x = 0; x < length; x++) { struct gfs2_bitmap *bi = rgd->rd_bits + x; - brelse(bi->bi_bh); - bi->bi_bh = NULL; + if (bi->bi_bh) { + brelse(bi->bi_bh); + bi->bi_bh = NULL; + } } } @@ -987,6 +1094,7 @@ int gfs2_fitrim(struct file *filp, void __user *argp) rgd->rd_flags |= GFS2_RGF_TRIMMED; gfs2_trans_add_bh(rgd->rd_gl, bh, 1); gfs2_rgrp_out(rgd, bh->b_data); + gfs2_rgrp_ondisk2lvb(rgd->rd_rgl, bh->b_data); gfs2_trans_end(sdp); } } @@ -1116,6 +1224,9 @@ static int get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked) int error, rg_locked, flags = LM_FLAG_TRY; int loops = 0; + if (sdp->sd_args.ar_rgrplvb) + flags |= GL_SKIP; + if (ip->i_rgd && rgrp_contains_block(ip->i_rgd, ip->i_goal)) rgd = begin = ip->i_rgd; else @@ -1133,22 +1244,34 @@ static int get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked) } else { error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE, flags, &rs->rs_rgd_gh); + if (!error && sdp->sd_args.ar_rgrplvb) { + error = update_rgrp_lvb(rgd); + if (error) { + gfs2_glock_dq_uninit(&rs->rs_rgd_gh); + return error; + } + } } switch (error) { case 0: if (try_rgrp_fit(rgd, ip)) { + if (sdp->sd_args.ar_rgrplvb) + gfs2_rgrp_bh_get(rgd); ip->i_rgd = rgd; return 0; } - if (rgd->rd_flags & GFS2_RDF_CHECK) + if (rgd->rd_flags & GFS2_RDF_CHECK) { + if (sdp->sd_args.ar_rgrplvb) + gfs2_rgrp_bh_get(rgd); try_rgrp_unlink(rgd, last_unlinked, ip->i_no_addr); + } if (!rg_locked) gfs2_glock_dq_uninit(&rs->rs_rgd_gh); /* fall through */ case GLR_TRYFAILED: rgd = gfs2_rgrpd_get_next(rgd); if (rgd == begin) { - flags = 0; + flags &= ~LM_FLAG_TRY; loops++; } break; @@ -1529,6 +1652,7 @@ int gfs2_alloc_blocks(struct gfs2_inode *ip, u64 *bn, unsigned int *nblocks, gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1); gfs2_rgrp_out(rgd, rgd->rd_bits[0].bi_bh->b_data); + gfs2_rgrp_ondisk2lvb(rgd->rd_rgl, rgd->rd_bits[0].bi_bh->b_data); gfs2_statfs_change(sdp, 0, -(s64)*nblocks, dinode ? 1 : 0); if (dinode) @@ -1575,6 +1699,7 @@ void __gfs2_free_blocks(struct gfs2_inode *ip, u64 bstart, u32 blen, int meta) rgd->rd_flags &= ~GFS2_RGF_TRIMMED; gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1); gfs2_rgrp_out(rgd, rgd->rd_bits[0].bi_bh->b_data); + gfs2_rgrp_ondisk2lvb(rgd->rd_rgl, rgd->rd_bits[0].bi_bh->b_data); /* Directories keep their data in the metadata address space */ if (meta || ip->i_depth) @@ -1611,6 +1736,8 @@ void gfs2_unlink_di(struct inode *inode) trace_gfs2_block_alloc(ip, rgd, blkno, 1, GFS2_BLKST_UNLINKED); gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1); gfs2_rgrp_out(rgd, rgd->rd_bits[0].bi_bh->b_data); + gfs2_rgrp_ondisk2lvb(rgd->rd_rgl, rgd->rd_bits[0].bi_bh->b_data); + update_rgrp_lvb_unlinked(rgd, 1); } static void gfs2_free_uninit_di(struct gfs2_rgrpd *rgd, u64 blkno) @@ -1630,6 +1757,8 @@ static void gfs2_free_uninit_di(struct gfs2_rgrpd *rgd, u64 blkno) gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1); gfs2_rgrp_out(rgd, rgd->rd_bits[0].bi_bh->b_data); + gfs2_rgrp_ondisk2lvb(rgd->rd_rgl, rgd->rd_bits[0].bi_bh->b_data); + update_rgrp_lvb_unlinked(rgd, -1); gfs2_statfs_change(sdp, 0, +1, -1); } diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 81fc76264ed4..788068758f3a 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -78,6 +78,8 @@ enum { Opt_quota_quantum, Opt_barrier, Opt_nobarrier, + Opt_rgrplvb, + Opt_norgrplvb, Opt_error, }; @@ -115,6 +117,8 @@ static const match_table_t tokens = { {Opt_quota_quantum, "quota_quantum=%d"}, {Opt_barrier, "barrier"}, {Opt_nobarrier, "nobarrier"}, + {Opt_rgrplvb, "rgrplvb"}, + {Opt_norgrplvb, "norgrplvb"}, {Opt_error, NULL} }; @@ -267,6 +271,12 @@ int gfs2_mount_args(struct gfs2_args *args, char *options) case Opt_nobarrier: args->ar_nobarrier = 1; break; + case Opt_rgrplvb: + args->ar_rgrplvb = 1; + break; + case Opt_norgrplvb: + args->ar_rgrplvb = 0; + break; case Opt_error: default: printk(KERN_WARNING "GFS2: invalid mount option: %s\n", o); @@ -1379,6 +1389,8 @@ static int gfs2_show_options(struct seq_file *s, struct dentry *root) seq_printf(s, ",nobarrier"); if (test_bit(SDF_DEMOTE, &sdp->sd_flags)) seq_printf(s, ",demote_interface_used"); + if (args->ar_rgrplvb) + seq_printf(s, ",rgrplvb"); return 0; } diff --git a/include/linux/gfs2_ondisk.h b/include/linux/gfs2_ondisk.h index e8ccf6ff3b4d..b2de1f9a88d6 100644 --- a/include/linux/gfs2_ondisk.h +++ b/include/linux/gfs2_ondisk.h @@ -170,6 +170,16 @@ struct gfs2_rindex { #define GFS2_RGF_NOALLOC 0x00000008 #define GFS2_RGF_TRIMMED 0x00000010 +struct gfs2_rgrp_lvb { + __be32 rl_magic; + __be32 rl_flags; + __be32 rl_free; + __be32 rl_dinodes; + __be64 rl_igeneration; + __be32 rl_unlinked; + __be32 __pad; +}; + struct gfs2_rgrp { struct gfs2_meta_header rg_header; -- cgit v1.2.3 From f3109a51f8dc88e8a94f620240b7474b91bed37a Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 24 May 2012 18:59:10 +0200 Subject: lib: Proportions with flexible period Implement code computing proportions of events of different type (like code in lib/proportions.c) but allowing periods to have different lengths. This allows us to have aging periods of fixed wallclock time which gives better proportion estimates given the hugely varying throughput of different devices - previous measuring of aging period by number of events has the problem that a reasonable period length for a system with low-end USB stick is not a reasonable period length for a system with high-end storage array resulting either in too slow proportion updates or too fluctuating proportion updates. Acked-by: Peter Zijlstra Signed-off-by: Jan Kara Signed-off-by: Fengguang Wu --- include/linux/flex_proportions.h | 101 +++++++++++++++ lib/Makefile | 2 +- lib/flex_proportions.c | 266 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 368 insertions(+), 1 deletion(-) create mode 100644 include/linux/flex_proportions.h create mode 100644 lib/flex_proportions.c (limited to 'include') diff --git a/include/linux/flex_proportions.h b/include/linux/flex_proportions.h new file mode 100644 index 000000000000..4ebc49fae391 --- /dev/null +++ b/include/linux/flex_proportions.h @@ -0,0 +1,101 @@ +/* + * Floating proportions with flexible aging period + * + * Copyright (C) 2011, SUSE, Jan Kara + */ + +#ifndef _LINUX_FLEX_PROPORTIONS_H +#define _LINUX_FLEX_PROPORTIONS_H + +#include +#include +#include + +/* + * When maximum proportion of some event type is specified, this is the + * precision with which we allow limitting. Note that this creates an upper + * bound on the number of events per period like + * ULLONG_MAX >> FPROP_FRAC_SHIFT. + */ +#define FPROP_FRAC_SHIFT 10 +#define FPROP_FRAC_BASE (1UL << FPROP_FRAC_SHIFT) + +/* + * ---- Global proportion definitions ---- + */ +struct fprop_global { + /* Number of events in the current period */ + struct percpu_counter events; + /* Current period */ + unsigned int period; + /* Synchronization with period transitions */ + seqcount_t sequence; +}; + +int fprop_global_init(struct fprop_global *p); +void fprop_global_destroy(struct fprop_global *p); +bool fprop_new_period(struct fprop_global *p, int periods); + +/* + * ---- SINGLE ---- + */ +struct fprop_local_single { + /* the local events counter */ + unsigned long events; + /* Period in which we last updated events */ + unsigned int period; + raw_spinlock_t lock; /* Protect period and numerator */ +}; + +#define INIT_FPROP_LOCAL_SINGLE(name) \ +{ .lock = __RAW_SPIN_LOCK_UNLOCKED(name.lock), \ +} + +int fprop_local_init_single(struct fprop_local_single *pl); +void fprop_local_destroy_single(struct fprop_local_single *pl); +void __fprop_inc_single(struct fprop_global *p, struct fprop_local_single *pl); +void fprop_fraction_single(struct fprop_global *p, + struct fprop_local_single *pl, unsigned long *numerator, + unsigned long *denominator); + +static inline +void fprop_inc_single(struct fprop_global *p, struct fprop_local_single *pl) +{ + unsigned long flags; + + local_irq_save(flags); + __fprop_inc_single(p, pl); + local_irq_restore(flags); +} + +/* + * ---- PERCPU ---- + */ +struct fprop_local_percpu { + /* the local events counter */ + struct percpu_counter events; + /* Period in which we last updated events */ + unsigned int period; + raw_spinlock_t lock; /* Protect period and numerator */ +}; + +int fprop_local_init_percpu(struct fprop_local_percpu *pl); +void fprop_local_destroy_percpu(struct fprop_local_percpu *pl); +void __fprop_inc_percpu(struct fprop_global *p, struct fprop_local_percpu *pl); +void __fprop_inc_percpu_max(struct fprop_global *p, struct fprop_local_percpu *pl, + int max_frac); +void fprop_fraction_percpu(struct fprop_global *p, + struct fprop_local_percpu *pl, unsigned long *numerator, + unsigned long *denominator); + +static inline +void fprop_inc_percpu(struct fprop_global *p, struct fprop_local_percpu *pl) +{ + unsigned long flags; + + local_irq_save(flags); + __fprop_inc_percpu(p, pl); + local_irq_restore(flags); +} + +#endif diff --git a/lib/Makefile b/lib/Makefile index 8c31a0cb75e9..ee837378b107 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -11,7 +11,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \ rbtree.o radix-tree.o dump_stack.o timerqueue.o\ idr.o int_sqrt.o extable.o prio_tree.o \ sha1.o md5.o irq_regs.o reciprocal_div.o argv_split.o \ - proportions.o prio_heap.o ratelimit.o show_mem.o \ + proportions.o flex_proportions.o prio_heap.o ratelimit.o show_mem.o \ is_single_threaded.o plist.o decompress.o lib-$(CONFIG_MMU) += ioremap.o diff --git a/lib/flex_proportions.c b/lib/flex_proportions.c new file mode 100644 index 000000000000..e02a3883ae01 --- /dev/null +++ b/lib/flex_proportions.c @@ -0,0 +1,266 @@ +/* + * Floating proportions with flexible aging period + * + * Copyright (C) 2011, SUSE, Jan Kara + * + * The goal of this code is: Given different types of event, measure proportion + * of each type of event over time. The proportions are measured with + * exponentially decaying history to give smooth transitions. A formula + * expressing proportion of event of type 'j' is: + * + * p_{j} = (\Sum_{i>=0} x_{i,j}/2^{i+1})/(\Sum_{i>=0} x_i/2^{i+1}) + * + * Where x_{i,j} is j's number of events in i-th last time period and x_i is + * total number of events in i-th last time period. + * + * Note that p_{j}'s are normalised, i.e. + * + * \Sum_{j} p_{j} = 1, + * + * This formula can be straightforwardly computed by maintaing denominator + * (let's call it 'd') and for each event type its numerator (let's call it + * 'n_j'). When an event of type 'j' happens, we simply need to do: + * n_j++; d++; + * + * When a new period is declared, we could do: + * d /= 2 + * for each j + * n_j /= 2 + * + * To avoid iteration over all event types, we instead shift numerator of event + * j lazily when someone asks for a proportion of event j or when event j + * occurs. This can bit trivially implemented by remembering last period in + * which something happened with proportion of type j. + */ +#include + +int fprop_global_init(struct fprop_global *p) +{ + int err; + + p->period = 0; + /* Use 1 to avoid dealing with periods with 0 events... */ + err = percpu_counter_init(&p->events, 1); + if (err) + return err; + seqcount_init(&p->sequence); + return 0; +} + +void fprop_global_destroy(struct fprop_global *p) +{ + percpu_counter_destroy(&p->events); +} + +/* + * Declare @periods new periods. It is upto the caller to make sure period + * transitions cannot happen in parallel. + * + * The function returns true if the proportions are still defined and false + * if aging zeroed out all events. This can be used to detect whether declaring + * further periods has any effect. + */ +bool fprop_new_period(struct fprop_global *p, int periods) +{ + u64 events = percpu_counter_sum(&p->events); + + /* + * Don't do anything if there are no events. + */ + if (events <= 1) + return false; + write_seqcount_begin(&p->sequence); + if (periods < 64) + events -= events >> periods; + /* Use addition to avoid losing events happening between sum and set */ + percpu_counter_add(&p->events, -events); + p->period += periods; + write_seqcount_end(&p->sequence); + + return true; +} + +/* + * ---- SINGLE ---- + */ + +int fprop_local_init_single(struct fprop_local_single *pl) +{ + pl->events = 0; + pl->period = 0; + raw_spin_lock_init(&pl->lock); + return 0; +} + +void fprop_local_destroy_single(struct fprop_local_single *pl) +{ +} + +static void fprop_reflect_period_single(struct fprop_global *p, + struct fprop_local_single *pl) +{ + unsigned int period = p->period; + unsigned long flags; + + /* Fast path - period didn't change */ + if (pl->period == period) + return; + raw_spin_lock_irqsave(&pl->lock, flags); + /* Someone updated pl->period while we were spinning? */ + if (pl->period >= period) { + raw_spin_unlock_irqrestore(&pl->lock, flags); + return; + } + /* Aging zeroed our fraction? */ + if (period - pl->period < BITS_PER_LONG) + pl->events >>= period - pl->period; + else + pl->events = 0; + pl->period = period; + raw_spin_unlock_irqrestore(&pl->lock, flags); +} + +/* Event of type pl happened */ +void __fprop_inc_single(struct fprop_global *p, struct fprop_local_single *pl) +{ + fprop_reflect_period_single(p, pl); + pl->events++; + percpu_counter_add(&p->events, 1); +} + +/* Return fraction of events of type pl */ +void fprop_fraction_single(struct fprop_global *p, + struct fprop_local_single *pl, + unsigned long *numerator, unsigned long *denominator) +{ + unsigned int seq; + s64 num, den; + + do { + seq = read_seqcount_begin(&p->sequence); + fprop_reflect_period_single(p, pl); + num = pl->events; + den = percpu_counter_read_positive(&p->events); + } while (read_seqcount_retry(&p->sequence, seq)); + + /* + * Make fraction <= 1 and denominator > 0 even in presence of percpu + * counter errors + */ + if (den <= num) { + if (num) + den = num; + else + den = 1; + } + *denominator = den; + *numerator = num; +} + +/* + * ---- PERCPU ---- + */ +#define PROP_BATCH (8*(1+ilog2(nr_cpu_ids))) + +int fprop_local_init_percpu(struct fprop_local_percpu *pl) +{ + int err; + + err = percpu_counter_init(&pl->events, 0); + if (err) + return err; + pl->period = 0; + raw_spin_lock_init(&pl->lock); + return 0; +} + +void fprop_local_destroy_percpu(struct fprop_local_percpu *pl) +{ + percpu_counter_destroy(&pl->events); +} + +static void fprop_reflect_period_percpu(struct fprop_global *p, + struct fprop_local_percpu *pl) +{ + unsigned int period = p->period; + unsigned long flags; + + /* Fast path - period didn't change */ + if (pl->period == period) + return; + raw_spin_lock_irqsave(&pl->lock, flags); + /* Someone updated pl->period while we were spinning? */ + if (pl->period >= period) { + raw_spin_unlock_irqrestore(&pl->lock, flags); + return; + } + /* Aging zeroed our fraction? */ + if (period - pl->period < BITS_PER_LONG) { + s64 val = percpu_counter_read(&pl->events); + + if (val < (nr_cpu_ids * PROP_BATCH)) + val = percpu_counter_sum(&pl->events); + + __percpu_counter_add(&pl->events, + -val + (val >> (period-pl->period)), PROP_BATCH); + } else + percpu_counter_set(&pl->events, 0); + pl->period = period; + raw_spin_unlock_irqrestore(&pl->lock, flags); +} + +/* Event of type pl happened */ +void __fprop_inc_percpu(struct fprop_global *p, struct fprop_local_percpu *pl) +{ + fprop_reflect_period_percpu(p, pl); + __percpu_counter_add(&pl->events, 1, PROP_BATCH); + percpu_counter_add(&p->events, 1); +} + +void fprop_fraction_percpu(struct fprop_global *p, + struct fprop_local_percpu *pl, + unsigned long *numerator, unsigned long *denominator) +{ + unsigned int seq; + s64 num, den; + + do { + seq = read_seqcount_begin(&p->sequence); + fprop_reflect_period_percpu(p, pl); + num = percpu_counter_read_positive(&pl->events); + den = percpu_counter_read_positive(&p->events); + } while (read_seqcount_retry(&p->sequence, seq)); + + /* + * Make fraction <= 1 and denominator > 0 even in presence of percpu + * counter errors + */ + if (den <= num) { + if (num) + den = num; + else + den = 1; + } + *denominator = den; + *numerator = num; +} + +/* + * Like __fprop_inc_percpu() except that event is counted only if the given + * type has fraction smaller than @max_frac/FPROP_FRAC_BASE + */ +void __fprop_inc_percpu_max(struct fprop_global *p, + struct fprop_local_percpu *pl, int max_frac) +{ + if (unlikely(max_frac < FPROP_FRAC_BASE)) { + unsigned long numerator, denominator; + + fprop_fraction_percpu(p, pl, &numerator, &denominator); + if (numerator > + (((u64)denominator) * max_frac) >> FPROP_FRAC_SHIFT) + return; + } else + fprop_reflect_period_percpu(p, pl); + __percpu_counter_add(&pl->events, 1, PROP_BATCH); + percpu_counter_add(&p->events, 1); +} -- cgit v1.2.3 From eb608e3a344b3af21300360fcf868f8b4e808a8e Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 24 May 2012 18:59:11 +0200 Subject: block: Convert BDI proportion calculations to flexible proportions Convert calculations of proportion of writeback each bdi does to new flexible proportion code. That allows us to use aging period of fixed wallclock time which gives better proportion estimates given the hugely varying throughput of different devices. Acked-by: Peter Zijlstra Signed-off-by: Jan Kara Signed-off-by: Fengguang Wu --- include/linux/backing-dev.h | 4 +- mm/backing-dev.c | 6 +-- mm/page-writeback.c | 103 +++++++++++++++++++++++++++----------------- 3 files changed, 69 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index b1038bd686ac..489de625cd25 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include #include @@ -89,7 +89,7 @@ struct backing_dev_info { unsigned long dirty_ratelimit; unsigned long balanced_dirty_ratelimit; - struct prop_local_percpu completions; + struct fprop_local_percpu completions; int dirty_exceeded; unsigned int min_ratio; diff --git a/mm/backing-dev.c b/mm/backing-dev.c index dd8e2aafb07e..3387aea11209 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -677,7 +677,7 @@ int bdi_init(struct backing_dev_info *bdi) bdi->min_ratio = 0; bdi->max_ratio = 100; - bdi->max_prop_frac = PROP_FRAC_BASE; + bdi->max_prop_frac = FPROP_FRAC_BASE; spin_lock_init(&bdi->wb_lock); INIT_LIST_HEAD(&bdi->bdi_list); INIT_LIST_HEAD(&bdi->work_list); @@ -700,7 +700,7 @@ int bdi_init(struct backing_dev_info *bdi) bdi->write_bandwidth = INIT_BW; bdi->avg_write_bandwidth = INIT_BW; - err = prop_local_init_percpu(&bdi->completions); + err = fprop_local_init_percpu(&bdi->completions); if (err) { err: @@ -744,7 +744,7 @@ void bdi_destroy(struct backing_dev_info *bdi) for (i = 0; i < NR_BDI_STAT_ITEMS; i++) percpu_counter_destroy(&bdi->bdi_stat[i]); - prop_local_destroy_percpu(&bdi->completions); + fprop_local_destroy_percpu(&bdi->completions); } EXPORT_SYMBOL(bdi_destroy); diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 93d8d2f7108c..ec14419e53b5 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -34,6 +34,7 @@ #include #include /* __set_page_dirty_buffers */ #include +#include #include /* @@ -135,7 +136,20 @@ unsigned long global_dirty_limit; * measured in page writeback completions. * */ -static struct prop_descriptor vm_completions; +static struct fprop_global writeout_completions; + +static void writeout_period(unsigned long t); +/* Timer for aging of writeout_completions */ +static struct timer_list writeout_period_timer = + TIMER_DEFERRED_INITIALIZER(writeout_period, 0, 0); +static unsigned long writeout_period_time = 0; + +/* + * Length of period for aging writeout fractions of bdis. This is an + * arbitrarily chosen number. The longer the period, the slower fractions will + * reflect changes in current writeout rate. + */ +#define VM_COMPLETIONS_PERIOD_LEN (3*HZ) /* * Work out the current dirty-memory clamping and background writeout @@ -322,34 +336,6 @@ bool zone_dirty_ok(struct zone *zone) zone_page_state(zone, NR_WRITEBACK) <= limit; } -/* - * couple the period to the dirty_ratio: - * - * period/2 ~ roundup_pow_of_two(dirty limit) - */ -static int calc_period_shift(void) -{ - unsigned long dirty_total; - - if (vm_dirty_bytes) - dirty_total = vm_dirty_bytes / PAGE_SIZE; - else - dirty_total = (vm_dirty_ratio * global_dirtyable_memory()) / - 100; - return 2 + ilog2(dirty_total - 1); -} - -/* - * update the period when the dirty threshold changes. - */ -static void update_completion_period(void) -{ - int shift = calc_period_shift(); - prop_change_shift(&vm_completions, shift); - - writeback_set_ratelimit(); -} - int dirty_background_ratio_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) @@ -383,7 +369,7 @@ int dirty_ratio_handler(struct ctl_table *table, int write, ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); if (ret == 0 && write && vm_dirty_ratio != old_ratio) { - update_completion_period(); + writeback_set_ratelimit(); vm_dirty_bytes = 0; } return ret; @@ -398,12 +384,21 @@ int dirty_bytes_handler(struct ctl_table *table, int write, ret = proc_doulongvec_minmax(table, write, buffer, lenp, ppos); if (ret == 0 && write && vm_dirty_bytes != old_bytes) { - update_completion_period(); + writeback_set_ratelimit(); vm_dirty_ratio = 0; } return ret; } +static unsigned long wp_next_time(unsigned long cur_time) +{ + cur_time += VM_COMPLETIONS_PERIOD_LEN; + /* 0 has a special meaning... */ + if (!cur_time) + return 1; + return cur_time; +} + /* * Increment the BDI's writeout completion count and the global writeout * completion count. Called from test_clear_page_writeback(). @@ -411,8 +406,19 @@ int dirty_bytes_handler(struct ctl_table *table, int write, static inline void __bdi_writeout_inc(struct backing_dev_info *bdi) { __inc_bdi_stat(bdi, BDI_WRITTEN); - __prop_inc_percpu_max(&vm_completions, &bdi->completions, - bdi->max_prop_frac); + __fprop_inc_percpu_max(&writeout_completions, &bdi->completions, + bdi->max_prop_frac); + /* First event after period switching was turned off? */ + if (!unlikely(writeout_period_time)) { + /* + * We can race with other __bdi_writeout_inc calls here but + * it does not cause any harm since the resulting time when + * timer will fire and what is in writeout_period_time will be + * roughly the same. + */ + writeout_period_time = wp_next_time(jiffies); + mod_timer(&writeout_period_timer, writeout_period_time); + } } void bdi_writeout_inc(struct backing_dev_info *bdi) @@ -431,10 +437,32 @@ EXPORT_SYMBOL_GPL(bdi_writeout_inc); static void bdi_writeout_fraction(struct backing_dev_info *bdi, long *numerator, long *denominator) { - prop_fraction_percpu(&vm_completions, &bdi->completions, + fprop_fraction_percpu(&writeout_completions, &bdi->completions, numerator, denominator); } +/* + * On idle system, we can be called long after we scheduled because we use + * deferred timers so count with missed periods. + */ +static void writeout_period(unsigned long t) +{ + int miss_periods = (jiffies - writeout_period_time) / + VM_COMPLETIONS_PERIOD_LEN; + + if (fprop_new_period(&writeout_completions, miss_periods + 1)) { + writeout_period_time = wp_next_time(writeout_period_time + + miss_periods * VM_COMPLETIONS_PERIOD_LEN); + mod_timer(&writeout_period_timer, writeout_period_time); + } else { + /* + * Aging has zeroed all fractions. Stop wasting CPU on period + * updates. + */ + writeout_period_time = 0; + } +} + /* * bdi_min_ratio keeps the sum of the minimum dirty shares of all * registered backing devices, which, for obvious reasons, can not @@ -475,7 +503,7 @@ int bdi_set_max_ratio(struct backing_dev_info *bdi, unsigned max_ratio) ret = -EINVAL; } else { bdi->max_ratio = max_ratio; - bdi->max_prop_frac = (PROP_FRAC_BASE * max_ratio) / 100; + bdi->max_prop_frac = (FPROP_FRAC_BASE * max_ratio) / 100; } spin_unlock_bh(&bdi_lock); @@ -1606,13 +1634,10 @@ static struct notifier_block __cpuinitdata ratelimit_nb = { */ void __init page_writeback_init(void) { - int shift; - writeback_set_ratelimit(); register_cpu_notifier(&ratelimit_nb); - shift = calc_period_shift(); - prop_descriptor_init(&vm_completions, shift); + fprop_global_init(&writeout_completions); } /** -- cgit v1.2.3 From a4808147dcf1ecf2f76212a78fd9692b3c112f47 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Mon, 11 Jun 2012 13:16:35 +0100 Subject: seq_file: Add seq_vprintf function and export it The existing seq_printf function is rewritten in terms of the new seq_vprintf which is also exported to modules. This allows GFS2 (and potentially other seq_file users) to have a vprintf based interface and to avoid an extra copy into a temporary buffer in some cases. Signed-off-by: Steven Whitehouse Reported-by: Eric Dumazet Acked-by: Al Viro --- fs/seq_file.c | 18 ++++++++++++++---- include/linux/seq_file.h | 1 + 2 files changed, 15 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/fs/seq_file.c b/fs/seq_file.c index 0cbd0494b79e..14cf9de1dbe1 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c @@ -385,15 +385,12 @@ int seq_escape(struct seq_file *m, const char *s, const char *esc) } EXPORT_SYMBOL(seq_escape); -int seq_printf(struct seq_file *m, const char *f, ...) +int seq_vprintf(struct seq_file *m, const char *f, va_list args) { - va_list args; int len; if (m->count < m->size) { - va_start(args, f); len = vsnprintf(m->buf + m->count, m->size - m->count, f, args); - va_end(args); if (m->count + len < m->size) { m->count += len; return 0; @@ -402,6 +399,19 @@ int seq_printf(struct seq_file *m, const char *f, ...) seq_set_overflow(m); return -1; } +EXPORT_SYMBOL(seq_vprintf); + +int seq_printf(struct seq_file *m, const char *f, ...) +{ + int ret; + va_list args; + + va_start(args, f); + ret = seq_vprintf(m, f, args); + va_end(args); + + return ret; +} EXPORT_SYMBOL(seq_printf); /** diff --git a/include/linux/seq_file.h b/include/linux/seq_file.h index fc61854f6224..83c44eefe698 100644 --- a/include/linux/seq_file.h +++ b/include/linux/seq_file.h @@ -86,6 +86,7 @@ int seq_puts(struct seq_file *m, const char *s); int seq_write(struct seq_file *seq, const void *data, size_t len); __printf(2, 3) int seq_printf(struct seq_file *, const char *, ...); +__printf(2, 0) int seq_vprintf(struct seq_file *, const char *, va_list args); int seq_path(struct seq_file *, const struct path *, const char *); int seq_dentry(struct seq_file *, struct dentry *, const char *); -- cgit v1.2.3 From 53f2d02898755d1b24bde1975e202815d29fdb81 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 23 Feb 2012 08:10:34 -0300 Subject: RAS: Add a tracepoint for reporting memory controller events Add a new tracepoint-based hardware events report method for reporting Memory Controller events. Part of the description bellow is shamelessly copied from Tony Luck's notes about the Hardware Error BoF during LPC 2010 [1]. Tony, thanks for your notes and discussions to generate the h/w error reporting requirements. [1] http://lwn.net/Articles/416669/ We have several subsystems & methods for reporting hardware errors: 1) EDAC ("Error Detection and Correction"). In its original form this consisted of a platform specific driver that read topology information and error counts from chipset registers and reported the results via a sysfs interface. 2) mcelog - x86 specific decoding of machine check bank registers reporting in binary form via /dev/mcelog. Recent additions make use of the APEI extensions that were documented in version 4.0a of the ACPI specification to acquire more information about errors without having to rely reading chipset registers directly. A user level programs decodes into somewhat human readable format. 3) drivers/edac/mce_amd.c - this driver hooks into the mcelog path and decodes errors reported via machine check bank registers in AMD processors to the console log using printk(); Each of these mechanisms has a band of followers ... and none of them appear to meet all the needs of all users. As part of a RAS subsystem, let's encapsulate the memory error hardware events into a trace facility. The tracepoint printk will be displayed like: mc_event: [quant] (Corrected|Uncorrected|Fatal) error:[error msg] on [label] ([location] [edac_mc detail] [driver_detail] Where: [quant] is the quantity of errors [error msg] is the driver-specific error message (e. g. "memory read", "bus error", ...); [location] is the location in terms of memory controller and branch/channel/slot, channel/slot or csrow/channel; [label] is the memory stick label; [edac_mc detail] describes the address location of the error and the syndrome; [driver detail] is driver-specifig error message details, when needed/provided (e. g. "area:DMA", ...) For example: mc_event: 1 Corrected error:memory read on memory stick DIMM_1A (mc:0 location:0:0:0 page:0x586b6e offset:0xa66 grain:32 syndrome:0x0 area:DMA) Of course, any userspace tools meant to handle errors should not parse the above data. They should, instead, use the binary fields provided by the tracepoint, mapping them directly into their Management Information Base. NOTE: The original patch was providing an additional mechanism for MCA-based trace events that also contained MCA error register data. However, as no agreement was reached so far for the MCA-based trace events, for now, let's add events only for memory errors. A latter patch is planned to change the tracepoint, for those types of event. Cc: Aristeu Rozanski Cc: Doug Thompson Cc: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/edac_core.h | 8 ++-- drivers/edac/edac_mc.c | 72 +++++++++++++++++++++++++-------- include/ras/ras_event.h | 102 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+), 20 deletions(-) create mode 100644 include/ras/ras_event.h (limited to 'include') diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h index f06ce9ab692c..740c7e22c023 100644 --- a/drivers/edac/edac_core.h +++ b/drivers/edac/edac_core.h @@ -463,12 +463,12 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, const unsigned long page_frame_number, const unsigned long offset_in_page, const unsigned long syndrome, - const int layer0, - const int layer1, - const int layer2, + const int top_layer, + const int mid_layer, + const int low_layer, const char *msg, const char *other_detail, - const void *mcelog); + const void *arch_log); /* * edac_device APIs diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 10f375032e96..ce25750a83f9 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -27,12 +27,17 @@ #include #include #include +#include #include #include #include #include "edac_core.h" #include "edac_module.h" +#define CREATE_TRACE_POINTS +#define TRACE_INCLUDE_PATH ../../include/ras +#include + /* lock to memory controller's control array */ static DEFINE_MUTEX(mem_ctls_mutex); static LIST_HEAD(mc_devices); @@ -384,6 +389,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, * which will perform kobj unregistration and the actual free * will occur during the kobject callback operation */ + return mci; } EXPORT_SYMBOL_GPL(edac_mc_alloc); @@ -902,19 +908,19 @@ static void edac_ce_error(struct mem_ctl_info *mci, const bool enable_per_layer_report, const unsigned long page_frame_number, const unsigned long offset_in_page, - u32 grain) + long grain) { unsigned long remapped_page; if (edac_mc_get_log_ce()) { if (other_detail && *other_detail) edac_mc_printk(mci, KERN_WARNING, - "CE %s on %s (%s%s - %s)\n", + "CE %s on %s (%s %s - %s)\n", msg, label, location, detail, other_detail); else edac_mc_printk(mci, KERN_WARNING, - "CE %s on %s (%s%s)\n", + "CE %s on %s (%s %s)\n", msg, label, location, detail); } @@ -953,12 +959,12 @@ static void edac_ue_error(struct mem_ctl_info *mci, if (edac_mc_get_log_ue()) { if (other_detail && *other_detail) edac_mc_printk(mci, KERN_WARNING, - "UE %s on %s (%s%s - %s)\n", + "UE %s on %s (%s %s - %s)\n", msg, label, location, detail, other_detail); else edac_mc_printk(mci, KERN_WARNING, - "UE %s on %s (%s%s)\n", + "UE %s on %s (%s %s)\n", msg, label, location, detail); } @@ -975,27 +981,50 @@ static void edac_ue_error(struct mem_ctl_info *mci, } #define OTHER_LABEL " or " + +/** + * edac_mc_handle_error - reports a memory event to userspace + * + * @type: severity of the error (CE/UE/Fatal) + * @mci: a struct mem_ctl_info pointer + * @page_frame_number: mem page where the error occurred + * @offset_in_page: offset of the error inside the page + * @syndrome: ECC syndrome + * @top_layer: Memory layer[0] position + * @mid_layer: Memory layer[1] position + * @low_layer: Memory layer[2] position + * @msg: Message meaningful to the end users that + * explains the event + * @other_detail: Technical details about the event that + * may help hardware manufacturers and + * EDAC developers to analyse the event + * @arch_log: Architecture-specific struct that can + * be used to add extended information to the + * tracepoint, like dumping MCE registers. + */ void edac_mc_handle_error(const enum hw_event_mc_err_type type, struct mem_ctl_info *mci, const unsigned long page_frame_number, const unsigned long offset_in_page, const unsigned long syndrome, - const int layer0, - const int layer1, - const int layer2, + const int top_layer, + const int mid_layer, + const int low_layer, const char *msg, const char *other_detail, - const void *mcelog) + const void *arch_log) { /* FIXME: too much for stack: move it to some pre-alocated area */ char detail[80], location[80]; char label[(EDAC_MC_LABEL_LEN + 1 + sizeof(OTHER_LABEL)) * mci->tot_dimms]; char *p; int row = -1, chan = -1; - int pos[EDAC_MAX_LAYERS] = { layer0, layer1, layer2 }; + int pos[EDAC_MAX_LAYERS] = { top_layer, mid_layer, low_layer }; int i; - u32 grain; + long grain; bool enable_per_layer_report = false; + u16 error_count; /* FIXME: make it a parameter */ + u8 grain_bits; debugf3("MC%d: %s()\n", mci->mc_idx, __func__); @@ -1045,11 +1074,11 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, for (i = 0; i < mci->tot_dimms; i++) { struct dimm_info *dimm = &mci->dimms[i]; - if (layer0 >= 0 && layer0 != dimm->location[0]) + if (top_layer >= 0 && top_layer != dimm->location[0]) continue; - if (layer1 >= 0 && layer1 != dimm->location[1]) + if (mid_layer >= 0 && mid_layer != dimm->location[1]) continue; - if (layer2 >= 0 && layer2 != dimm->location[2]) + if (low_layer >= 0 && low_layer != dimm->location[2]) continue; /* get the max grain, over the error match range */ @@ -1120,11 +1149,22 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, edac_layer_name[mci->layers[i].type], pos[i]); } + if (p > location) + *(p - 1) = '\0'; + + /* Report the error via the trace interface */ + + error_count = 1; /* FIXME: allow change it */ + grain_bits = fls_long(grain) + 1; + trace_mc_event(type, msg, label, error_count, + mci->mc_idx, top_layer, mid_layer, low_layer, + PAGES_TO_MiB(page_frame_number) | offset_in_page, + grain_bits, syndrome, other_detail); /* Memory type dependent details about the error */ if (type == HW_EVENT_ERR_CORRECTED) { snprintf(detail, sizeof(detail), - "page:0x%lx offset:0x%lx grain:%d syndrome:0x%lx", + "page:0x%lx offset:0x%lx grain:%ld syndrome:0x%lx", page_frame_number, offset_in_page, grain, syndrome); edac_ce_error(mci, pos, msg, location, label, detail, @@ -1132,7 +1172,7 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, page_frame_number, offset_in_page, grain); } else { snprintf(detail, sizeof(detail), - "page:0x%lx offset:0x%lx grain:%d", + "page:0x%lx offset:0x%lx grain:%ld", page_frame_number, offset_in_page, grain); edac_ue_error(mci, pos, msg, location, label, detail, diff --git a/include/ras/ras_event.h b/include/ras/ras_event.h new file mode 100644 index 000000000000..260470e72483 --- /dev/null +++ b/include/ras/ras_event.h @@ -0,0 +1,102 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM ras +#define TRACE_INCLUDE_FILE ras_event + +#if !defined(_TRACE_HW_EVENT_MC_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_HW_EVENT_MC_H + +#include +#include +#include + +/* + * Hardware Events Report + * + * Those events are generated when hardware detected a corrected or + * uncorrected event, and are meant to replace the current API to report + * errors defined on both EDAC and MCE subsystems. + * + * FIXME: Add events for handling memory errors originated from the + * MCE subsystem. + */ + +/* + * Hardware-independent Memory Controller specific events + */ + +/* + * Default error mechanisms for Memory Controller errors (CE and UE) + */ +TRACE_EVENT(mc_event, + + TP_PROTO(const unsigned int err_type, + const char *error_msg, + const char *label, + const int error_count, + const u8 mc_index, + const s8 top_layer, + const s8 mid_layer, + const s8 low_layer, + unsigned long address, + const u8 grain_bits, + unsigned long syndrome, + const char *driver_detail), + + TP_ARGS(err_type, error_msg, label, error_count, mc_index, + top_layer, mid_layer, low_layer, address, grain_bits, + syndrome, driver_detail), + + TP_STRUCT__entry( + __field( unsigned int, error_type ) + __string( msg, error_msg ) + __string( label, label ) + __field( u16, error_count ) + __field( u8, mc_index ) + __field( s8, top_layer ) + __field( s8, middle_layer ) + __field( s8, lower_layer ) + __field( long, address ) + __field( u8, grain_bits ) + __field( long, syndrome ) + __string( driver_detail, driver_detail ) + ), + + TP_fast_assign( + __entry->error_type = err_type; + __assign_str(msg, error_msg); + __assign_str(label, label); + __entry->error_count = error_count; + __entry->mc_index = mc_index; + __entry->top_layer = top_layer; + __entry->middle_layer = mid_layer; + __entry->lower_layer = low_layer; + __entry->address = address; + __entry->grain_bits = grain_bits; + __entry->syndrome = syndrome; + __assign_str(driver_detail, driver_detail); + ), + + TP_printk("%d %s error%s:%s%s on %s (mc:%d location:%d:%d:%d address:0x%08lx grain:%d syndrome:0x%08lx%s%s)", + __entry->error_count, + (__entry->error_type == HW_EVENT_ERR_CORRECTED) ? "Corrected" : + ((__entry->error_type == HW_EVENT_ERR_FATAL) ? + "Fatal" : "Uncorrected"), + __entry->error_count > 1 ? "s" : "", + ((char *)__get_str(msg))[0] ? " " : "", + __get_str(msg), + __get_str(label), + __entry->mc_index, + __entry->top_layer, + __entry->middle_layer, + __entry->lower_layer, + __entry->address, + 1 << __entry->grain_bits, + __entry->syndrome, + ((char *)__get_str(driver_detail))[0] ? " " : "", + __get_str(driver_detail)) +); + +#endif /* _TRACE_HW_EVENT_MC_H */ + +/* This part must be outside protection */ +#include -- cgit v1.2.3 From fd687502dc8037aa5a4b84c570ada971106574ee Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 16 Mar 2012 07:44:18 -0300 Subject: edac: Rename the parent dev to pdev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As EDAC doesn't use struct device itself, it created a parent dev pointer called as "pdev". Now that we'll be converting it to use struct device, instead of struct devsys, this needs to be fixed. No functional changes. Reviewed-by: Aristeu Rozanski Acked-by: Chris Metcalf Cc: Doug Thompson Cc: Borislav Petkov Cc: Mark Gross Cc: Jason Uhlenkott Cc: Tim Small Cc: Ranganathan Desikan Cc: "Arvind R." Cc: Olof Johansson Cc: Egor Martovetsky Cc: Michal Marek Cc: Jiri Kosina Cc: Joe Perches Cc: Dmitry Eremin-Solenikov Cc: Benjamin Herrenschmidt Cc: Hitoshi Mitake Cc: Andrew Morton Cc: "Niklas Söderlund" Cc: Shaohui Xie Cc: Josh Boyer Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/amd64_edac.c | 2 +- drivers/edac/amd76x_edac.c | 4 ++-- drivers/edac/cell_edac.c | 12 ++++++------ drivers/edac/cpc925_edac.c | 2 +- drivers/edac/e752x_edac.c | 2 +- drivers/edac/e7xxx_edac.c | 2 +- drivers/edac/edac_mc.c | 8 ++++---- drivers/edac/edac_mc_sysfs.c | 2 +- drivers/edac/i3000_edac.c | 4 ++-- drivers/edac/i3200_edac.c | 6 +++--- drivers/edac/i5000_edac.c | 2 +- drivers/edac/i5100_edac.c | 2 +- drivers/edac/i5400_edac.c | 2 +- drivers/edac/i7300_edac.c | 2 +- drivers/edac/i7core_edac.c | 4 ++-- drivers/edac/i82443bxgx_edac.c | 4 ++-- drivers/edac/i82860_edac.c | 4 ++-- drivers/edac/i82875p_edac.c | 4 ++-- drivers/edac/i82975x_edac.c | 4 ++-- drivers/edac/mpc85xx_edac.c | 4 ++-- drivers/edac/mv64x60_edac.c | 2 +- drivers/edac/pasemi_edac.c | 6 +++--- drivers/edac/ppc4xx_edac.c | 8 ++++---- drivers/edac/r82600_edac.c | 4 ++-- drivers/edac/sb_edac.c | 4 ++-- drivers/edac/tile_edac.c | 4 ++-- drivers/edac/x38_edac.c | 6 +++--- include/linux/edac.h | 2 +- 28 files changed, 56 insertions(+), 56 deletions(-) (limited to 'include') diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 7be9b7288e90..821bc2cdd2de 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -2601,7 +2601,7 @@ static int amd64_init_one_instance(struct pci_dev *F2) goto err_siblings; mci->pvt_info = pvt; - mci->dev = &pvt->F2->dev; + mci->pdev = &pvt->F2->dev; setup_mci_misc_attrs(mci, fam_type); diff --git a/drivers/edac/amd76x_edac.c b/drivers/edac/amd76x_edac.c index 9774d443fa57..7439786f3bef 100644 --- a/drivers/edac/amd76x_edac.c +++ b/drivers/edac/amd76x_edac.c @@ -105,7 +105,7 @@ static void amd76x_get_error_info(struct mem_ctl_info *mci, { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); pci_read_config_dword(pdev, AMD76X_ECC_MODE_STATUS, &info->ecc_mode_status); @@ -257,7 +257,7 @@ static int amd76x_probe1(struct pci_dev *pdev, int dev_idx) return -ENOMEM; debugf0("%s(): mci = %p\n", __func__, mci); - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_RDDR; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED; mci->edac_cap = ems_mode ? diff --git a/drivers/edac/cell_edac.c b/drivers/edac/cell_edac.c index 69ee6aab5c71..2e5b95374dc6 100644 --- a/drivers/edac/cell_edac.c +++ b/drivers/edac/cell_edac.c @@ -36,7 +36,7 @@ static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar) struct csrow_info *csrow = &mci->csrows[0]; unsigned long address, pfn, offset, syndrome; - dev_dbg(mci->dev, "ECC CE err on node %d, channel %d, ar = 0x%016llx\n", + dev_dbg(mci->pdev, "ECC CE err on node %d, channel %d, ar = 0x%016llx\n", priv->node, chan, ar); /* Address decoding is likely a bit bogus, to dbl check */ @@ -59,7 +59,7 @@ static void cell_edac_count_ue(struct mem_ctl_info *mci, int chan, u64 ar) struct csrow_info *csrow = &mci->csrows[0]; unsigned long address, pfn, offset; - dev_dbg(mci->dev, "ECC UE err on node %d, channel %d, ar = 0x%016llx\n", + dev_dbg(mci->pdev, "ECC UE err on node %d, channel %d, ar = 0x%016llx\n", priv->node, chan, ar); /* Address decoding is likely a bit bogus, to dbl check */ @@ -83,7 +83,7 @@ static void cell_edac_check(struct mem_ctl_info *mci) fir = in_be64(&priv->regs->mic_fir); #ifdef DEBUG if (fir != priv->prev_fir) { - dev_dbg(mci->dev, "fir change : 0x%016lx\n", fir); + dev_dbg(mci->pdev, "fir change : 0x%016lx\n", fir); priv->prev_fir = fir; } #endif @@ -119,7 +119,7 @@ static void cell_edac_check(struct mem_ctl_info *mci) mb(); /* sync up */ #ifdef DEBUG fir = in_be64(&priv->regs->mic_fir); - dev_dbg(mci->dev, "fir clear : 0x%016lx\n", fir); + dev_dbg(mci->pdev, "fir clear : 0x%016lx\n", fir); #endif } } @@ -155,7 +155,7 @@ static void __devinit cell_edac_init_csrows(struct mem_ctl_info *mci) dimm->edac_mode = EDAC_SECDED; dimm->nr_pages = nr_pages / csrow->nr_channels; } - dev_dbg(mci->dev, + dev_dbg(mci->pdev, "Initialized on node %d, chanmask=0x%x," " first_page=0x%lx, nr_pages=0x%x\n", priv->node, priv->chanmask, @@ -212,7 +212,7 @@ static int __devinit cell_edac_probe(struct platform_device *pdev) priv->regs = regs; priv->node = pdev->id; priv->chanmask = chanmask; - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_XDR; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED; mci->edac_cap = EDAC_FLAG_EC | EDAC_FLAG_SECDED; diff --git a/drivers/edac/cpc925_edac.c b/drivers/edac/cpc925_edac.c index e22030a9de66..9488723f8138 100644 --- a/drivers/edac/cpc925_edac.c +++ b/drivers/edac/cpc925_edac.c @@ -995,7 +995,7 @@ static int __devinit cpc925_probe(struct platform_device *pdev) pdata->edac_idx = edac_mc_idx++; pdata->name = pdev->name; - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; platform_set_drvdata(pdev, mci); mci->dev_name = dev_name(&pdev->dev); mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR; diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c index 3186512c9739..d75660634b43 100644 --- a/drivers/edac/e752x_edac.c +++ b/drivers/edac/e752x_edac.c @@ -1308,7 +1308,7 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx) /* FIXME - what if different memory types are in different csrows? */ mci->mod_name = EDAC_MOD_STR; mci->mod_ver = E752X_REVISION; - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; debugf3("%s(): init pvt\n", __func__); pvt = (struct e752x_pvt *)mci->pvt_info; diff --git a/drivers/edac/e7xxx_edac.c b/drivers/edac/e7xxx_edac.c index 9a9c1a546797..b111266dadf3 100644 --- a/drivers/edac/e7xxx_edac.c +++ b/drivers/edac/e7xxx_edac.c @@ -458,7 +458,7 @@ static int e7xxx_probe1(struct pci_dev *pdev, int dev_idx) /* FIXME - what if different memory types are in different csrows? */ mci->mod_name = EDAC_MOD_STR; mci->mod_ver = E7XXX_REVISION; - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; debugf3("%s(): init pvt\n", __func__); pvt = (struct e7xxx_pvt *)mci->pvt_info; pvt->dev_info = &e7xxx_devs[dev_idx]; diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index ce25750a83f9..811f09a38f3a 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -93,7 +93,7 @@ static void edac_mc_dump_mci(struct mem_ctl_info *mci) mci->nr_csrows, mci->csrows); debugf3("\tmci->nr_dimms = %d, dimms = %p\n", mci->tot_dimms, mci->dimms); - debugf3("\tdev = %p\n", mci->dev); + debugf3("\tdev = %p\n", mci->pdev); debugf3("\tmod_name:ctl_name = %s:%s\n", mci->mod_name, mci->ctl_name); debugf3("\tpvt_info = %p\n\n", mci->pvt_info); } @@ -428,7 +428,7 @@ struct mem_ctl_info *find_mci_by_dev(struct device *dev) list_for_each(item, &mc_devices) { mci = list_entry(item, struct mem_ctl_info, link); - if (mci->dev == dev) + if (mci->pdev == dev) return mci; } @@ -580,7 +580,7 @@ static int add_mc_to_global_list(struct mem_ctl_info *mci) insert_before = &mc_devices; - p = find_mci_by_dev(mci->dev); + p = find_mci_by_dev(mci->pdev); if (unlikely(p != NULL)) goto fail0; @@ -602,7 +602,7 @@ static int add_mc_to_global_list(struct mem_ctl_info *mci) fail0: edac_printk(KERN_WARNING, EDAC_MC, - "%s (%s) %s %s already assigned %d\n", dev_name(p->dev), + "%s (%s) %s %s already assigned %d\n", dev_name(p->pdev), edac_dev_name(mci), p->mod_name, p->ctl_name, p->mc_idx); return 1; diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index f6a29b0eedc8..595371941ef9 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -916,7 +916,7 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) INIT_LIST_HEAD(&mci->grp_kobj_list); /* create a symlink for the device */ - err = sysfs_create_link(kobj_mci, &mci->dev->kobj, + err = sysfs_create_link(kobj_mci, &mci->pdev->kobj, EDAC_DEVICE_SYMLINK); if (err) { debugf1("%s() failure to create symlink\n", __func__); diff --git a/drivers/edac/i3000_edac.c b/drivers/edac/i3000_edac.c index 8ad1744faacd..d1ebd9b9ad6f 100644 --- a/drivers/edac/i3000_edac.c +++ b/drivers/edac/i3000_edac.c @@ -194,7 +194,7 @@ static void i3000_get_error_info(struct mem_ctl_info *mci, { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); /* * This is a mess because there is no atomic way to read all the @@ -368,7 +368,7 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx) debugf3("MC: %s(): init mci\n", __func__); - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR2; mci->edac_ctl_cap = EDAC_FLAG_SECDED; diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c index bbe43ef71823..600a05df3759 100644 --- a/drivers/edac/i3200_edac.c +++ b/drivers/edac/i3200_edac.c @@ -159,7 +159,7 @@ static void i3200_clear_error_info(struct mem_ctl_info *mci) { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); /* * Clear any error bits. @@ -176,7 +176,7 @@ static void i3200_get_and_clear_error_info(struct mem_ctl_info *mci, struct i3200_priv *priv = mci->pvt_info; void __iomem *window = priv->window; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); /* * This is a mess because there is no atomic way to read all the @@ -354,7 +354,7 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx) debugf3("MC: %s(): init mci\n", __func__); - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR2; mci->edac_ctl_cap = EDAC_FLAG_SECDED; diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c index 11ea835f155a..a69245ad5f32 100644 --- a/drivers/edac/i5000_edac.c +++ b/drivers/edac/i5000_edac.c @@ -1409,7 +1409,7 @@ static int i5000_probe1(struct pci_dev *pdev, int dev_idx) kobject_get(&mci->edac_mci_kobj); debugf0("MC: %s: %s(): mci = %p\n", __FILE__, __func__, mci); - mci->dev = &pdev->dev; /* record ptr to the generic device */ + mci->pdev = &pdev->dev; /* record ptr to the generic device */ pvt = mci->pvt_info; pvt->system_address = pdev; /* Record this device in our private */ diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c index e9e7c2a29dc3..19aa2843c46a 100644 --- a/drivers/edac/i5100_edac.c +++ b/drivers/edac/i5100_edac.c @@ -943,7 +943,7 @@ static int __devinit i5100_init_one(struct pci_dev *pdev, goto bail_disable_ch1; } - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; priv = mci->pvt_info; priv->ranksperchan = ranksperch; diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c index 6640c29e1885..ba60694437bd 100644 --- a/drivers/edac/i5400_edac.c +++ b/drivers/edac/i5400_edac.c @@ -1299,7 +1299,7 @@ static int i5400_probe1(struct pci_dev *pdev, int dev_idx) debugf0("MC: %s: %s(): mci = %p\n", __FILE__, __func__, mci); - mci->dev = &pdev->dev; /* record ptr to the generic device */ + mci->pdev = &pdev->dev; /* record ptr to the generic device */ pvt = mci->pvt_info; pvt->system_address = pdev; /* Record this device in our private */ diff --git a/drivers/edac/i7300_edac.c b/drivers/edac/i7300_edac.c index 97c22fd650ee..db84456e65d9 100644 --- a/drivers/edac/i7300_edac.c +++ b/drivers/edac/i7300_edac.c @@ -1057,7 +1057,7 @@ static int __devinit i7300_init_one(struct pci_dev *pdev, debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci); - mci->dev = &pdev->dev; /* record ptr to the generic device */ + mci->pdev = &pdev->dev; /* record ptr to the generic device */ pvt = mci->pvt_info; pvt->pci_dev_16_0_fsb_ctlr = pdev; /* Record this device in our private */ diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index c05e1ada7a3d..598d215f7bd5 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -2122,7 +2122,7 @@ static void i7core_unregister_mci(struct i7core_dev *i7core_dev) i7core_pci_ctl_release(pvt); /* Remove MC sysfs nodes */ - edac_mc_del_mc(mci->dev); + edac_mc_del_mc(mci->pdev); debugf1("%s: free mci struct\n", mci->ctl_name); kfree(mci->ctl_name); @@ -2188,7 +2188,7 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev) /* Get dimm basic config */ get_dimm_config(mci); /* record ptr to the generic device */ - mci->dev = &i7core_dev->pdev[0]->dev; + mci->pdev = &i7core_dev->pdev[0]->dev; /* Set the function pointer to an actual operation function */ mci->edac_check = i7core_check_error; diff --git a/drivers/edac/i82443bxgx_edac.c b/drivers/edac/i82443bxgx_edac.c index 52072c28a8a6..65fd2e1eceb8 100644 --- a/drivers/edac/i82443bxgx_edac.c +++ b/drivers/edac/i82443bxgx_edac.c @@ -124,7 +124,7 @@ static void i82443bxgx_edacmc_get_error_info(struct mem_ctl_info *mci, *info) { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); pci_read_config_dword(pdev, I82443BXGX_EAP, &info->eap); if (info->eap & I82443BXGX_EAP_OFFSET_SBE) /* Clear error to allow next error to be reported [p.61] */ @@ -260,7 +260,7 @@ static int i82443bxgx_edacmc_probe1(struct pci_dev *pdev, int dev_idx) return -ENOMEM; debugf0("MC: %s: %s(): mci = %p\n", __FILE__, __func__, mci); - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_EDO | MEM_FLAG_SDR | MEM_FLAG_RDR; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED; pci_read_config_byte(pdev, I82443BXGX_DRAMC, &dramc); diff --git a/drivers/edac/i82860_edac.c b/drivers/edac/i82860_edac.c index 08045059d10b..8f3350000942 100644 --- a/drivers/edac/i82860_edac.c +++ b/drivers/edac/i82860_edac.c @@ -67,7 +67,7 @@ static void i82860_get_error_info(struct mem_ctl_info *mci, { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); /* * This is a mess because there is no atomic way to read all the @@ -211,7 +211,7 @@ static int i82860_probe1(struct pci_dev *pdev, int dev_idx) return -ENOMEM; debugf3("%s(): init mci\n", __func__); - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; /* I"m not sure about this but I think that all RDRAM is SECDED */ diff --git a/drivers/edac/i82875p_edac.c b/drivers/edac/i82875p_edac.c index b613e31c16e5..1cc682d0678d 100644 --- a/drivers/edac/i82875p_edac.c +++ b/drivers/edac/i82875p_edac.c @@ -189,7 +189,7 @@ static void i82875p_get_error_info(struct mem_ctl_info *mci, { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); /* * This is a mess because there is no atomic way to read all the @@ -430,7 +430,7 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx) kobject_get(&mci->edac_mci_kobj); debugf3("%s(): init mci\n", __func__); - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; mci->edac_cap = EDAC_FLAG_UNKNOWN; diff --git a/drivers/edac/i82975x_edac.c b/drivers/edac/i82975x_edac.c index 433332c7cdba..8b26401efa19 100644 --- a/drivers/edac/i82975x_edac.c +++ b/drivers/edac/i82975x_edac.c @@ -241,7 +241,7 @@ static void i82975x_get_error_info(struct mem_ctl_info *mci, { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); /* * This is a mess because there is no atomic way to read all the @@ -559,7 +559,7 @@ static int i82975x_probe1(struct pci_dev *pdev, int dev_idx) } debugf3("%s(): init mci\n", __func__); - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR2; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; mci->edac_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c index 4c402353ba98..67fb3280f333 100644 --- a/drivers/edac/mpc85xx_edac.c +++ b/drivers/edac/mpc85xx_edac.c @@ -989,9 +989,9 @@ static int __devinit mpc85xx_mc_err_probe(struct platform_device *op) pdata = mci->pvt_info; pdata->name = "mpc85xx_mc_err"; pdata->irq = NO_IRQ; - mci->dev = &op->dev; + mci->pdev = &op->dev; pdata->edac_idx = edac_mc_idx++; - dev_set_drvdata(mci->dev, mci); + dev_set_drvdata(mci->pdev, mci); mci->ctl_name = pdata->name; mci->dev_name = pdata->name; diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c index b0bb5a3d2527..ff6b8e248e89 100644 --- a/drivers/edac/mv64x60_edac.c +++ b/drivers/edac/mv64x60_edac.c @@ -724,7 +724,7 @@ static int __devinit mv64x60_mc_err_probe(struct platform_device *pdev) } pdata = mci->pvt_info; - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; platform_set_drvdata(pdev, mci); pdata->name = "mv64x60_mc_err"; pdata->irq = NO_IRQ; diff --git a/drivers/edac/pasemi_edac.c b/drivers/edac/pasemi_edac.c index b095a906a994..92becaa8722a 100644 --- a/drivers/edac/pasemi_edac.c +++ b/drivers/edac/pasemi_edac.c @@ -74,7 +74,7 @@ static int system_mmc_id; static u32 pasemi_edac_get_error_info(struct mem_ctl_info *mci) { - struct pci_dev *pdev = to_pci_dev(mci->dev); + struct pci_dev *pdev = to_pci_dev(mci->pdev); u32 tmp; pci_read_config_dword(pdev, MCDEBUG_ERRSTA, @@ -95,7 +95,7 @@ static u32 pasemi_edac_get_error_info(struct mem_ctl_info *mci) static void pasemi_edac_process_error_info(struct mem_ctl_info *mci, u32 errsta) { - struct pci_dev *pdev = to_pci_dev(mci->dev); + struct pci_dev *pdev = to_pci_dev(mci->pdev); u32 errlog1a; u32 cs; @@ -225,7 +225,7 @@ static int __devinit pasemi_edac_probe(struct pci_dev *pdev, MCCFG_ERRCOR_ECC_GEN_EN | MCCFG_ERRCOR_ECC_CRR_EN; - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR | MEM_FLAG_RDDR; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED; mci->edac_cap = (errcor & MCCFG_ERRCOR_ECC_GEN_EN) ? diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c index f3f9fed06ad7..53519828cc36 100644 --- a/drivers/edac/ppc4xx_edac.c +++ b/drivers/edac/ppc4xx_edac.c @@ -1027,9 +1027,9 @@ ppc4xx_edac_mc_init(struct mem_ctl_info *mci, /* Initial driver pointers and private data */ - mci->dev = &op->dev; + mci->pdev = &op->dev; - dev_set_drvdata(mci->dev, mci); + dev_set_drvdata(mci->pdev, mci); pdata = mci->pvt_info; @@ -1334,7 +1334,7 @@ static int __devinit ppc4xx_edac_probe(struct platform_device *op) return 0; fail1: - edac_mc_del_mc(mci->dev); + edac_mc_del_mc(mci->pdev); fail: edac_mc_free(mci); @@ -1368,7 +1368,7 @@ ppc4xx_edac_remove(struct platform_device *op) dcr_unmap(pdata->dcr_host, SDRAM_DCR_RESOURCE_LEN); - edac_mc_del_mc(mci->dev); + edac_mc_del_mc(mci->pdev); edac_mc_free(mci); return 0; diff --git a/drivers/edac/r82600_edac.c b/drivers/edac/r82600_edac.c index e1cacd164f31..cf4ccbdba85d 100644 --- a/drivers/edac/r82600_edac.c +++ b/drivers/edac/r82600_edac.c @@ -140,7 +140,7 @@ static void r82600_get_error_info(struct mem_ctl_info *mci, { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); pci_read_config_dword(pdev, R82600_EAP, &info->eapr); if (info->eapr & BIT(0)) @@ -296,7 +296,7 @@ static int r82600_probe1(struct pci_dev *pdev, int dev_idx) return -ENOMEM; debugf0("%s(): mci = %p\n", __func__, mci); - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED; /* FIXME try to work out if the chip leads have been used for COM2 diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c index e834dfd034d6..efa488357ae6 100644 --- a/drivers/edac/sb_edac.c +++ b/drivers/edac/sb_edac.c @@ -1607,7 +1607,7 @@ static void sbridge_unregister_mci(struct sbridge_dev *sbridge_dev) mce_unregister_decode_chain(&sbridge_mce_dec); /* Remove MC sysfs nodes */ - edac_mc_del_mc(mci->dev); + edac_mc_del_mc(mci->pdev); debugf1("%s: free mci struct\n", mci->ctl_name); kfree(mci->ctl_name); @@ -1672,7 +1672,7 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev) get_memory_layout(mci); /* record ptr to the generic device */ - mci->dev = &sbridge_dev->pdev[0]->dev; + mci->pdev = &sbridge_dev->pdev[0]->dev; /* add this new MC control structure to EDAC's list of MCs */ if (unlikely(edac_mc_add_mc(mci))) { diff --git a/drivers/edac/tile_edac.c b/drivers/edac/tile_edac.c index 7bb4614730db..604bc4df653a 100644 --- a/drivers/edac/tile_edac.c +++ b/drivers/edac/tile_edac.c @@ -69,7 +69,7 @@ static void tile_edac_check(struct mem_ctl_info *mci) /* Check if the current error count is different from the saved one. */ if (mem_error.sbe_count != priv->ce_count) { - dev_dbg(mci->dev, "ECC CE err on node %d\n", priv->node); + dev_dbg(mci->pdev, "ECC CE err on node %d\n", priv->node); priv->ce_count = mem_error.sbe_count; edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 0, 0, 0, @@ -149,7 +149,7 @@ static int __devinit tile_edac_mc_probe(struct platform_device *pdev) priv->node = pdev->id; priv->hv_devhdl = hv_devhdl; - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR2; mci->edac_ctl_cap = EDAC_FLAG_SECDED; diff --git a/drivers/edac/x38_edac.c b/drivers/edac/x38_edac.c index 1ac7962d63ea..f9506f26e2bf 100644 --- a/drivers/edac/x38_edac.c +++ b/drivers/edac/x38_edac.c @@ -151,7 +151,7 @@ static void x38_clear_error_info(struct mem_ctl_info *mci) { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); /* * Clear any error bits. @@ -172,7 +172,7 @@ static void x38_get_and_clear_error_info(struct mem_ctl_info *mci, struct pci_dev *pdev; void __iomem *window = mci->pvt_info; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); /* * This is a mess because there is no atomic way to read all the @@ -354,7 +354,7 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx) debugf3("MC: %s(): init mci\n", __func__); - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR2; mci->edac_ctl_cap = EDAC_FLAG_SECDED; diff --git a/include/linux/edac.h b/include/linux/edac.h index 91ba3bae42ee..ec1b5278b4cc 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -567,7 +567,7 @@ struct mem_ctl_info { * unique. dev pointer should be sufficiently unique, but * BUS:SLOT.FUNC numbers may not be unique. */ - struct device *dev; + struct device *pdev; const char *mod_name; const char *mod_ver; const char *ctl_name; -- cgit v1.2.3 From b0610bb82abd1c4ac97c33f0312cd7fd72eaa325 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 21 Mar 2012 16:21:07 -0300 Subject: edac: use Documentation-nano format for some data structs No functional changes. Just comment improvements. Reviewed-by: Aristeu Rozanski Cc: Doug Thompson Signed-off-by: Mauro Carvalho Chehab --- include/linux/edac.h | 82 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/linux/edac.h b/include/linux/edac.h index ec1b5278b4cc..4e32e8d31e0a 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -49,7 +49,19 @@ static inline void opstate_init(void) #define EDAC_MC_LABEL_LEN 31 #define MC_PROC_NAME_MAX_LEN 7 -/* memory devices */ +/** + * enum dev_type - describe the type of memory DRAM chips used at the stick + * @DEV_UNKNOWN: Can't be determined, or MC doesn't support detect it + * @DEV_X1: 1 bit for data + * @DEV_X2: 2 bits for data + * @DEV_X4: 4 bits for data + * @DEV_X8: 8 bits for data + * @DEV_X16: 16 bits for data + * @DEV_X32: 32 bits for data + * @DEV_X64: 64 bits for data + * + * Typical values are x4 and x8. + */ enum dev_type { DEV_UNKNOWN = 0, DEV_X1, @@ -167,18 +179,30 @@ enum mem_type { #define MEM_FLAG_DDR3 BIT(MEM_DDR3) #define MEM_FLAG_RDDR3 BIT(MEM_RDDR3) -/* chipset Error Detection and Correction capabilities and mode */ +/** + * enum edac-type - Error Detection and Correction capabilities and mode + * @EDAC_UNKNOWN: Unknown if ECC is available + * @EDAC_NONE: Doesn't support ECC + * @EDAC_RESERVED: Reserved ECC type + * @EDAC_PARITY: Detects parity errors + * @EDAC_EC: Error Checking - no correction + * @EDAC_SECDED: Single bit error correction, Double detection + * @EDAC_S2ECD2ED: Chipkill x2 devices - do these exist? + * @EDAC_S4ECD4ED: Chipkill x4 devices + * @EDAC_S8ECD8ED: Chipkill x8 devices + * @EDAC_S16ECD16ED: Chipkill x16 devices + */ enum edac_type { - EDAC_UNKNOWN = 0, /* Unknown if ECC is available */ - EDAC_NONE, /* Doesn't support ECC */ - EDAC_RESERVED, /* Reserved ECC type */ - EDAC_PARITY, /* Detects parity errors */ - EDAC_EC, /* Error Checking - no correction */ - EDAC_SECDED, /* Single bit error correction, Double detection */ - EDAC_S2ECD2ED, /* Chipkill x2 devices - do these exist? */ - EDAC_S4ECD4ED, /* Chipkill x4 devices */ - EDAC_S8ECD8ED, /* Chipkill x8 devices */ - EDAC_S16ECD16ED, /* Chipkill x16 devices */ + EDAC_UNKNOWN = 0, + EDAC_NONE, + EDAC_RESERVED, + EDAC_PARITY, + EDAC_EC, + EDAC_SECDED, + EDAC_S2ECD2ED, + EDAC_S4ECD4ED, + EDAC_S8ECD8ED, + EDAC_S16ECD16ED, }; #define EDAC_FLAG_UNKNOWN BIT(EDAC_UNKNOWN) @@ -191,18 +215,30 @@ enum edac_type { #define EDAC_FLAG_S8ECD8ED BIT(EDAC_S8ECD8ED) #define EDAC_FLAG_S16ECD16ED BIT(EDAC_S16ECD16ED) -/* scrubbing capabilities */ +/** + * enum scrub_type - scrubbing capabilities + * @SCRUB_UNKNOWN Unknown if scrubber is available + * @SCRUB_NONE: No scrubber + * @SCRUB_SW_PROG: SW progressive (sequential) scrubbing + * @SCRUB_SW_SRC: Software scrub only errors + * @SCRUB_SW_PROG_SRC: Progressive software scrub from an error + * @SCRUB_SW_TUNABLE: Software scrub frequency is tunable + * @SCRUB_HW_PROG: HW progressive (sequential) scrubbing + * @SCRUB_HW_SRC: Hardware scrub only errors + * @SCRUB_HW_PROG_SRC: Progressive hardware scrub from an error + * SCRUB_HW_TUNABLE: Hardware scrub frequency is tunable + */ enum scrub_type { - SCRUB_UNKNOWN = 0, /* Unknown if scrubber is available */ - SCRUB_NONE, /* No scrubber */ - SCRUB_SW_PROG, /* SW progressive (sequential) scrubbing */ - SCRUB_SW_SRC, /* Software scrub only errors */ - SCRUB_SW_PROG_SRC, /* Progressive software scrub from an error */ - SCRUB_SW_TUNABLE, /* Software scrub frequency is tunable */ - SCRUB_HW_PROG, /* HW progressive (sequential) scrubbing */ - SCRUB_HW_SRC, /* Hardware scrub only errors */ - SCRUB_HW_PROG_SRC, /* Progressive hardware scrub from an error */ - SCRUB_HW_TUNABLE /* Hardware scrub frequency is tunable */ + SCRUB_UNKNOWN = 0, + SCRUB_NONE, + SCRUB_SW_PROG, + SCRUB_SW_SRC, + SCRUB_SW_PROG_SRC, + SCRUB_SW_TUNABLE, + SCRUB_HW_PROG, + SCRUB_HW_SRC, + SCRUB_HW_PROG_SRC, + SCRUB_HW_TUNABLE }; #define SCRUB_FLAG_SW_PROG BIT(SCRUB_SW_PROG) -- cgit v1.2.3 From 7a623c039075e4ea21648d88133fafa6dcfd113d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 16 Apr 2012 16:41:11 -0300 Subject: edac: rewrite the sysfs code to use struct device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The EDAC subsystem uses the old struct sysdev approach, creating all nodes using the raw sysfs API. This is bad, as the API is deprecated. As we'll be changing the EDAC API, let's first port the existing code to struct device. There's one drawback on this patch: driver-specific sysfs nodes, used by mpc85xx_edac, amd64_edac and i7core_edac won't be created anymore. While it would be possible to also port the device-specific code, that would mix kobj with struct device, with is not recommended. Also, it is easier and nicer to move the code to the drivers, instead, as the core can get rid of some complex logic that just emulates what the device_add() and device_create_file() already does. The next patches will convert the driver-specific code to use the device-specific calls. Then, the remaining bits of the old sysfs API will be removed. NOTE: a per-MC bus is required, otherwise devices with more than one memory controller will hit a bug like the one below: [ 819.094946] EDAC DEBUG: find_mci_by_dev: find_mci_by_dev() [ 819.094948] EDAC DEBUG: edac_create_sysfs_mci_device: edac_create_sysfs_mci_device() idx=1 [ 819.094952] EDAC DEBUG: edac_create_sysfs_mci_device: edac_create_sysfs_mci_device(): creating device mc1 [ 819.094967] EDAC DEBUG: edac_create_sysfs_mci_device: edac_create_sysfs_mci_device creating dimm0, located at channel 0 slot 0 [ 819.094984] ------------[ cut here ]------------ [ 819.100142] WARNING: at fs/sysfs/dir.c:481 sysfs_add_one+0xc1/0xf0() [ 819.107282] Hardware name: S2600CP [ 819.111078] sysfs: cannot create duplicate filename '/bus/edac/devices/dimm0' [ 819.119062] Modules linked in: sb_edac(+) edac_core ip6table_filter ip6_tables ebtable_nat ebtables ipt_MASQUERADE iptable_nat nf_nat nf_conntrack_ipv4 nf_defrag_ipv4 xt_state nf_conntrack ipt_REJECT xt_CHECKSUM iptable_mangle iptable_filter ip_tables bridge stp llc sunrpc binfmt_misc dm_mirror dm_region_hash dm_log vhost_net macvtap macvlan tun kvm microcode pcspkr iTCO_wdt iTCO_vendor_support igb i2c_i801 i2c_core sg ioatdma dca sr_mod cdrom sd_mod crc_t10dif ahci libahci isci libsas libata scsi_transport_sas scsi_mod wmi dm_mod [last unloaded: scsi_wait_scan] [ 819.175748] Pid: 10902, comm: modprobe Not tainted 3.3.0-0.11.el7.v12.2.x86_64 #1 [ 819.184113] Call Trace: [ 819.186868] [] warn_slowpath_common+0x7f/0xc0 [ 819.193573] [] warn_slowpath_fmt+0x46/0x50 [ 819.200000] [] sysfs_add_one+0xc1/0xf0 [ 819.206025] [] sysfs_do_create_link+0x135/0x220 [ 819.212944] [] ? sysfs_create_group+0x13/0x20 [ 819.219656] [] sysfs_create_link+0x13/0x20 [ 819.226109] [] bus_add_device+0xe6/0x1b0 [ 819.232350] [] device_add+0x2db/0x460 [ 819.238300] [] edac_create_dimm_object+0x84/0xf0 [edac_core] [ 819.246460] [] edac_create_sysfs_mci_device+0xe8/0x290 [edac_core] [ 819.255215] [] edac_mc_add_mc+0x5a/0x2c0 [edac_core] [ 819.262611] [] sbridge_register_mci+0x1bc/0x279 [sb_edac] [ 819.270493] [] sbridge_probe+0xef/0x175 [sb_edac] [ 819.277630] [] ? pm_runtime_enable+0x58/0x90 [ 819.284268] [] local_pci_probe+0x5c/0xd0 [ 819.290508] [] __pci_device_probe+0xf1/0x100 [ 819.297117] [] pci_device_probe+0x3a/0x60 [ 819.303457] [] really_probe+0x73/0x270 [ 819.309496] [] driver_probe_device+0x4e/0xb0 [ 819.316104] [] __driver_attach+0xab/0xb0 [ 819.322337] [] ? driver_probe_device+0xb0/0xb0 [ 819.329151] [] bus_for_each_dev+0x56/0x90 [ 819.335489] [] driver_attach+0x1e/0x20 [ 819.341534] [] bus_add_driver+0x1b0/0x2a0 [ 819.347884] [] ? 0xffffffffa0346fff [ 819.353641] [] driver_register+0x76/0x140 [ 819.359980] [] ? printk+0x51/0x53 [ 819.365524] [] ? 0xffffffffa0346fff [ 819.371291] [] __pci_register_driver+0x56/0xd0 [ 819.378096] [] sbridge_init+0x54/0x1000 [sb_edac] [ 819.385231] [] do_one_initcall+0x3f/0x170 [ 819.391577] [] sys_init_module+0xbe/0x230 [ 819.397926] [] system_call_fastpath+0x16/0x1b [ 819.404633] ---[ end trace 1654fdd39556689f ]--- This happens because the bus is not being properly initialized. Instead of putting the memory sub-devices inside the memory controller, it is putting everything under the same directory: $ tree /sys/bus/edac/ /sys/bus/edac/ ├── devices │ ├── all_channel_counts -> ../../../devices/system/edac/mc/mc0/all_channel_counts │ ├── csrow0 -> ../../../devices/system/edac/mc/mc0/csrow0 │ ├── csrow1 -> ../../../devices/system/edac/mc/mc0/csrow1 │ ├── csrow2 -> ../../../devices/system/edac/mc/mc0/csrow2 │ ├── dimm0 -> ../../../devices/system/edac/mc/mc0/dimm0 │ ├── dimm1 -> ../../../devices/system/edac/mc/mc0/dimm1 │ ├── dimm3 -> ../../../devices/system/edac/mc/mc0/dimm3 │ ├── dimm6 -> ../../../devices/system/edac/mc/mc0/dimm6 │ ├── inject_addrmatch -> ../../../devices/system/edac/mc/mc0/inject_addrmatch │ ├── mc -> ../../../devices/system/edac/mc │ └── mc0 -> ../../../devices/system/edac/mc/mc0 ├── drivers ├── drivers_autoprobe ├── drivers_probe └── uevent On a multi-memory controller system, the names "csrow%d" and "dimm%d" should be under "mc%d", and not at the main hierarchy level. So, we need to create a per-MC bus, in order to have its own namespace. Reviewed-by: Aristeu Rozanski Cc: Doug Thompson Cc: Greg K H Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/edac_mc.c | 13 +- drivers/edac/edac_mc_sysfs.c | 1074 ++++++++++++++++-------------------------- drivers/edac/edac_module.c | 13 +- drivers/edac/edac_module.h | 9 +- include/linux/edac.h | 47 +- 5 files changed, 450 insertions(+), 706 deletions(-) (limited to 'include') diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 811f09a38f3a..61ae34643b49 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -218,7 +218,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, unsigned size, tot_dimms = 1, count = 1; unsigned tot_csrows = 1, tot_channels = 1, tot_errcount = 0; void *pvt, *p, *ptr = NULL; - int i, j, err, row, chn, n, len; + int i, j, row, chn, n, len; bool per_rank = false; BUG_ON(n_layers > EDAC_MAX_LAYERS || n_layers == 0); @@ -374,15 +374,6 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, mci->op_state = OP_ALLOC; INIT_LIST_HEAD(&mci->grp_kobj_list); - /* - * Initialize the 'root' kobj for the edac_mc controller - */ - err = edac_mc_register_sysfs_main_kobj(mci); - if (err) { - kfree(mci); - return NULL; - } - /* at this point, the root kobj is valid, and in order to * 'free' the object, then the function: * edac_mc_unregister_sysfs_main_kobj() must be called @@ -403,7 +394,7 @@ void edac_mc_free(struct mem_ctl_info *mci) { debugf1("%s()\n", __func__); - edac_mc_unregister_sysfs_main_kobj(mci); + edac_unregister_sysfs(mci); /* free the mci instance memory here */ kfree(mci); diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 595371941ef9..7002c9cab999 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -7,17 +7,20 @@ * * Written Doug Thompson www.softwarebitmaker.com * + * (c) 2012 - Mauro Carvalho Chehab + * The entire API were re-written, and ported to use struct device + * */ #include #include #include #include +#include #include "edac_core.h" #include "edac_module.h" - /* MC EDAC Controls, setable by module parameter, and sysfs */ static int edac_mc_log_ue = 1; static int edac_mc_log_ce = 1; @@ -78,6 +81,8 @@ module_param_call(edac_mc_poll_msec, edac_set_poll_msec, param_get_int, &edac_mc_poll_msec, 0644); MODULE_PARM_DESC(edac_mc_poll_msec, "Polling period in milliseconds"); +static struct device mci_pdev; + /* * various constants for Memory Controllers */ @@ -125,308 +130,336 @@ static const char *edac_caps[] = { [EDAC_S16ECD16ED] = "S16ECD16ED" }; -/* EDAC sysfs CSROW data structures and methods +/* + * EDAC sysfs CSROW data structures and methods + */ + +#define to_csrow(k) container_of(k, struct csrow_info, dev) + +/* + * We need it to avoid namespace conflicts between the legacy API + * and the per-dimm/per-rank one */ +#define DEVICE_ATTR_LEGACY(_name, _mode, _show, _store) \ + struct device_attribute dev_attr_legacy_##_name = __ATTR(_name, _mode, _show, _store) + +struct dev_ch_attribute { + struct device_attribute attr; + int channel; +}; + +#define DEVICE_CHANNEL(_name, _mode, _show, _store, _var) \ + struct dev_ch_attribute dev_attr_legacy_##_name = \ + { __ATTR(_name, _mode, _show, _store), (_var) } + +#define to_channel(k) (container_of(k, struct dev_ch_attribute, attr)->channel) /* Set of more default csrow attribute show/store functions */ -static ssize_t csrow_ue_count_show(struct csrow_info *csrow, char *data, - int private) +static ssize_t csrow_ue_count_show(struct device *dev, + struct device_attribute *mattr, char *data) { + struct csrow_info *csrow = to_csrow(dev); + return sprintf(data, "%u\n", csrow->ue_count); } -static ssize_t csrow_ce_count_show(struct csrow_info *csrow, char *data, - int private) +static ssize_t csrow_ce_count_show(struct device *dev, + struct device_attribute *mattr, char *data) { + struct csrow_info *csrow = to_csrow(dev); + return sprintf(data, "%u\n", csrow->ce_count); } -static ssize_t csrow_size_show(struct csrow_info *csrow, char *data, - int private) +static ssize_t csrow_size_show(struct device *dev, + struct device_attribute *mattr, char *data) { + struct csrow_info *csrow = to_csrow(dev); int i; u32 nr_pages = 0; for (i = 0; i < csrow->nr_channels; i++) nr_pages += csrow->channels[i].dimm->nr_pages; - return sprintf(data, "%u\n", PAGES_TO_MiB(nr_pages)); } -static ssize_t csrow_mem_type_show(struct csrow_info *csrow, char *data, - int private) +static ssize_t csrow_mem_type_show(struct device *dev, + struct device_attribute *mattr, char *data) { + struct csrow_info *csrow = to_csrow(dev); + return sprintf(data, "%s\n", mem_types[csrow->channels[0].dimm->mtype]); } -static ssize_t csrow_dev_type_show(struct csrow_info *csrow, char *data, - int private) +static ssize_t csrow_dev_type_show(struct device *dev, + struct device_attribute *mattr, char *data) { + struct csrow_info *csrow = to_csrow(dev); + return sprintf(data, "%s\n", dev_types[csrow->channels[0].dimm->dtype]); } -static ssize_t csrow_edac_mode_show(struct csrow_info *csrow, char *data, - int private) +static ssize_t csrow_edac_mode_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct csrow_info *csrow = to_csrow(dev); + return sprintf(data, "%s\n", edac_caps[csrow->channels[0].dimm->edac_mode]); } /* show/store functions for DIMM Label attributes */ -static ssize_t channel_dimm_label_show(struct csrow_info *csrow, - char *data, int channel) +static ssize_t channel_dimm_label_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct csrow_info *csrow = to_csrow(dev); + unsigned chan = to_channel(mattr); + struct rank_info *rank = &csrow->channels[chan]; + /* if field has not been initialized, there is nothing to send */ - if (!csrow->channels[channel].dimm->label[0]) + if (!rank->dimm->label[0]) return 0; return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n", - csrow->channels[channel].dimm->label); + rank->dimm->label); } -static ssize_t channel_dimm_label_store(struct csrow_info *csrow, - const char *data, - size_t count, int channel) +static ssize_t channel_dimm_label_store(struct device *dev, + struct device_attribute *mattr, + const char *data, size_t count) { + struct csrow_info *csrow = to_csrow(dev); + unsigned chan = to_channel(mattr); + struct rank_info *rank = &csrow->channels[chan]; + ssize_t max_size = 0; max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1); - strncpy(csrow->channels[channel].dimm->label, data, max_size); - csrow->channels[channel].dimm->label[max_size] = '\0'; + strncpy(rank->dimm->label, data, max_size); + rank->dimm->label[max_size] = '\0'; return max_size; } /* show function for dynamic chX_ce_count attribute */ -static ssize_t channel_ce_count_show(struct csrow_info *csrow, - char *data, int channel) +static ssize_t channel_ce_count_show(struct device *dev, + struct device_attribute *mattr, char *data) { - return sprintf(data, "%u\n", csrow->channels[channel].ce_count); + struct csrow_info *csrow = to_csrow(dev); + unsigned chan = to_channel(mattr); + struct rank_info *rank = &csrow->channels[chan]; + + return sprintf(data, "%u\n", rank->ce_count); } -/* csrow specific attribute structure */ -struct csrowdev_attribute { - struct attribute attr; - ssize_t(*show) (struct csrow_info *, char *, int); - ssize_t(*store) (struct csrow_info *, const char *, size_t, int); - int private; -}; +/* cwrow/attribute files */ +DEVICE_ATTR_LEGACY(size_mb, S_IRUGO, csrow_size_show, NULL); +DEVICE_ATTR_LEGACY(dev_type, S_IRUGO, csrow_dev_type_show, NULL); +DEVICE_ATTR_LEGACY(mem_type, S_IRUGO, csrow_mem_type_show, NULL); +DEVICE_ATTR_LEGACY(edac_mode, S_IRUGO, csrow_edac_mode_show, NULL); +DEVICE_ATTR_LEGACY(ue_count, S_IRUGO, csrow_ue_count_show, NULL); +DEVICE_ATTR_LEGACY(ce_count, S_IRUGO, csrow_ce_count_show, NULL); -#define to_csrow(k) container_of(k, struct csrow_info, kobj) -#define to_csrowdev_attr(a) container_of(a, struct csrowdev_attribute, attr) +/* default attributes of the CSROW object */ +static struct attribute *csrow_attrs[] = { + &dev_attr_legacy_dev_type.attr, + &dev_attr_legacy_mem_type.attr, + &dev_attr_legacy_edac_mode.attr, + &dev_attr_legacy_size_mb.attr, + &dev_attr_legacy_ue_count.attr, + &dev_attr_legacy_ce_count.attr, + NULL, +}; -/* Set of show/store higher level functions for default csrow attributes */ -static ssize_t csrowdev_show(struct kobject *kobj, - struct attribute *attr, char *buffer) -{ - struct csrow_info *csrow = to_csrow(kobj); - struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr); +static struct attribute_group csrow_attr_grp = { + .attrs = csrow_attrs, +}; - if (csrowdev_attr->show) - return csrowdev_attr->show(csrow, - buffer, csrowdev_attr->private); - return -EIO; -} +static const struct attribute_group *csrow_attr_groups[] = { + &csrow_attr_grp, + NULL +}; -static ssize_t csrowdev_store(struct kobject *kobj, struct attribute *attr, - const char *buffer, size_t count) +static void csrow_attr_release(struct device *device) { - struct csrow_info *csrow = to_csrow(kobj); - struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr); - - if (csrowdev_attr->store) - return csrowdev_attr->store(csrow, - buffer, - count, csrowdev_attr->private); - return -EIO; + debugf1("Releasing csrow device %s\n", dev_name(device)); } -static const struct sysfs_ops csrowfs_ops = { - .show = csrowdev_show, - .store = csrowdev_store +static struct device_type csrow_attr_type = { + .groups = csrow_attr_groups, + .release = csrow_attr_release, }; -#define CSROWDEV_ATTR(_name,_mode,_show,_store,_private) \ -static struct csrowdev_attribute attr_##_name = { \ - .attr = {.name = __stringify(_name), .mode = _mode }, \ - .show = _show, \ - .store = _store, \ - .private = _private, \ -}; - -/* default cwrow/attribute files */ -CSROWDEV_ATTR(size_mb, S_IRUGO, csrow_size_show, NULL, 0); -CSROWDEV_ATTR(dev_type, S_IRUGO, csrow_dev_type_show, NULL, 0); -CSROWDEV_ATTR(mem_type, S_IRUGO, csrow_mem_type_show, NULL, 0); -CSROWDEV_ATTR(edac_mode, S_IRUGO, csrow_edac_mode_show, NULL, 0); -CSROWDEV_ATTR(ue_count, S_IRUGO, csrow_ue_count_show, NULL, 0); -CSROWDEV_ATTR(ce_count, S_IRUGO, csrow_ce_count_show, NULL, 0); +/* + * possible dynamic channel DIMM Label attribute files + * + */ -/* default attributes of the CSROW object */ -static struct csrowdev_attribute *default_csrow_attr[] = { - &attr_dev_type, - &attr_mem_type, - &attr_edac_mode, - &attr_size_mb, - &attr_ue_count, - &attr_ce_count, - NULL, -}; +#define EDAC_NR_CHANNELS 6 -/* possible dynamic channel DIMM Label attribute files */ -CSROWDEV_ATTR(ch0_dimm_label, S_IRUGO | S_IWUSR, +DEVICE_CHANNEL(ch0_dimm_label, S_IRUGO | S_IWUSR, channel_dimm_label_show, channel_dimm_label_store, 0); -CSROWDEV_ATTR(ch1_dimm_label, S_IRUGO | S_IWUSR, +DEVICE_CHANNEL(ch1_dimm_label, S_IRUGO | S_IWUSR, channel_dimm_label_show, channel_dimm_label_store, 1); -CSROWDEV_ATTR(ch2_dimm_label, S_IRUGO | S_IWUSR, +DEVICE_CHANNEL(ch2_dimm_label, S_IRUGO | S_IWUSR, channel_dimm_label_show, channel_dimm_label_store, 2); -CSROWDEV_ATTR(ch3_dimm_label, S_IRUGO | S_IWUSR, +DEVICE_CHANNEL(ch3_dimm_label, S_IRUGO | S_IWUSR, channel_dimm_label_show, channel_dimm_label_store, 3); -CSROWDEV_ATTR(ch4_dimm_label, S_IRUGO | S_IWUSR, +DEVICE_CHANNEL(ch4_dimm_label, S_IRUGO | S_IWUSR, channel_dimm_label_show, channel_dimm_label_store, 4); -CSROWDEV_ATTR(ch5_dimm_label, S_IRUGO | S_IWUSR, +DEVICE_CHANNEL(ch5_dimm_label, S_IRUGO | S_IWUSR, channel_dimm_label_show, channel_dimm_label_store, 5); /* Total possible dynamic DIMM Label attribute file table */ -static struct csrowdev_attribute *dynamic_csrow_dimm_attr[] = { - &attr_ch0_dimm_label, - &attr_ch1_dimm_label, - &attr_ch2_dimm_label, - &attr_ch3_dimm_label, - &attr_ch4_dimm_label, - &attr_ch5_dimm_label +static struct device_attribute *dynamic_csrow_dimm_attr[] = { + &dev_attr_legacy_ch0_dimm_label.attr, + &dev_attr_legacy_ch1_dimm_label.attr, + &dev_attr_legacy_ch2_dimm_label.attr, + &dev_attr_legacy_ch3_dimm_label.attr, + &dev_attr_legacy_ch4_dimm_label.attr, + &dev_attr_legacy_ch5_dimm_label.attr }; /* possible dynamic channel ce_count attribute files */ -CSROWDEV_ATTR(ch0_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 0); -CSROWDEV_ATTR(ch1_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 1); -CSROWDEV_ATTR(ch2_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 2); -CSROWDEV_ATTR(ch3_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 3); -CSROWDEV_ATTR(ch4_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 4); -CSROWDEV_ATTR(ch5_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 5); +DEVICE_CHANNEL(ch0_ce_count, S_IRUGO | S_IWUSR, + channel_ce_count_show, NULL, 0); +DEVICE_CHANNEL(ch1_ce_count, S_IRUGO | S_IWUSR, + channel_ce_count_show, NULL, 1); +DEVICE_CHANNEL(ch2_ce_count, S_IRUGO | S_IWUSR, + channel_ce_count_show, NULL, 2); +DEVICE_CHANNEL(ch3_ce_count, S_IRUGO | S_IWUSR, + channel_ce_count_show, NULL, 3); +DEVICE_CHANNEL(ch4_ce_count, S_IRUGO | S_IWUSR, + channel_ce_count_show, NULL, 4); +DEVICE_CHANNEL(ch5_ce_count, S_IRUGO | S_IWUSR, + channel_ce_count_show, NULL, 5); /* Total possible dynamic ce_count attribute file table */ -static struct csrowdev_attribute *dynamic_csrow_ce_count_attr[] = { - &attr_ch0_ce_count, - &attr_ch1_ce_count, - &attr_ch2_ce_count, - &attr_ch3_ce_count, - &attr_ch4_ce_count, - &attr_ch5_ce_count +static struct device_attribute *dynamic_csrow_ce_count_attr[] = { + &dev_attr_legacy_ch0_ce_count.attr, + &dev_attr_legacy_ch1_ce_count.attr, + &dev_attr_legacy_ch2_ce_count.attr, + &dev_attr_legacy_ch3_ce_count.attr, + &dev_attr_legacy_ch4_ce_count.attr, + &dev_attr_legacy_ch5_ce_count.attr }; -#define EDAC_NR_CHANNELS 6 - -/* Create dynamic CHANNEL files, indexed by 'chan', under specifed CSROW */ -static int edac_create_channel_files(struct kobject *kobj, int chan) +/* Create a CSROW object under specifed edac_mc_device */ +static int edac_create_csrow_object(struct mem_ctl_info *mci, + struct csrow_info *csrow, int index) { - int err = -ENODEV; + int err, chan; - if (chan >= EDAC_NR_CHANNELS) - return err; + if (csrow->nr_channels >= EDAC_NR_CHANNELS) + return -ENODEV; - /* create the DIMM label attribute file */ - err = sysfs_create_file(kobj, - (struct attribute *) - dynamic_csrow_dimm_attr[chan]); - - if (!err) { - /* create the CE Count attribute file */ - err = sysfs_create_file(kobj, - (struct attribute *) - dynamic_csrow_ce_count_attr[chan]); - } else { - debugf1("%s() dimm labels and ce_count files created", - __func__); - } + csrow->dev.type = &csrow_attr_type; + csrow->dev.bus = &mci->bus; + device_initialize(&csrow->dev); + csrow->dev.parent = &mci->dev; + dev_set_name(&csrow->dev, "csrow%d", index); + dev_set_drvdata(&csrow->dev, csrow); - return err; -} + debugf0("%s(): creating (virtual) csrow node %s\n", __func__, + dev_name(&csrow->dev)); -/* No memory to release for this kobj */ -static void edac_csrow_instance_release(struct kobject *kobj) -{ - struct mem_ctl_info *mci; - struct csrow_info *cs; + err = device_add(&csrow->dev); + if (err < 0) + return err; - debugf1("%s()\n", __func__); + for (chan = 0; chan < csrow->nr_channels; chan++) { + err = device_create_file(&csrow->dev, + dynamic_csrow_dimm_attr[chan]); + if (err < 0) + goto error; + err = device_create_file(&csrow->dev, + dynamic_csrow_ce_count_attr[chan]); + if (err < 0) { + device_remove_file(&csrow->dev, + dynamic_csrow_dimm_attr[chan]); + goto error; + } + } - cs = container_of(kobj, struct csrow_info, kobj); - mci = cs->mci; + return 0; - kobject_put(&mci->edac_mci_kobj); -} +error: + for (--chan; chan >= 0; chan--) { + device_remove_file(&csrow->dev, + dynamic_csrow_dimm_attr[chan]); + device_remove_file(&csrow->dev, + dynamic_csrow_ce_count_attr[chan]); + } + put_device(&csrow->dev); -/* the kobj_type instance for a CSROW */ -static struct kobj_type ktype_csrow = { - .release = edac_csrow_instance_release, - .sysfs_ops = &csrowfs_ops, - .default_attrs = (struct attribute **)default_csrow_attr, -}; + return err; +} /* Create a CSROW object under specifed edac_mc_device */ -static int edac_create_csrow_object(struct mem_ctl_info *mci, - struct csrow_info *csrow, int index) +static int edac_create_csrow_objects(struct mem_ctl_info *mci) { - struct kobject *kobj_mci = &mci->edac_mci_kobj; - struct kobject *kobj; - int chan; - int err; + int err, i, chan; + struct csrow_info *csrow; - /* generate ..../edac/mc/mc/csrow */ - memset(&csrow->kobj, 0, sizeof(csrow->kobj)); - csrow->mci = mci; /* include container up link */ + for (i = 0; i < mci->nr_csrows; i++) { + err = edac_create_csrow_object(mci, &mci->csrows[i], i); + if (err < 0) + goto error; + } + return 0; - /* bump the mci instance's kobject's ref count */ - kobj = kobject_get(&mci->edac_mci_kobj); - if (!kobj) { - err = -ENODEV; - goto err_out; +error: + for (--i; i >= 0; i--) { + csrow = &mci->csrows[i]; + for (chan = csrow->nr_channels - 1; chan >= 0; chan--) { + device_remove_file(&csrow->dev, + dynamic_csrow_dimm_attr[chan]); + device_remove_file(&csrow->dev, + dynamic_csrow_ce_count_attr[chan]); + } + put_device(&mci->csrows[i].dev); } - /* Instanstiate the csrow object */ - err = kobject_init_and_add(&csrow->kobj, &ktype_csrow, kobj_mci, - "csrow%d", index); - if (err) - goto err_release_top_kobj; + return err; +} - /* At this point, to release a csrow kobj, one must - * call the kobject_put and allow that tear down - * to work the releasing - */ +static void edac_delete_csrow_objects(struct mem_ctl_info *mci) +{ + int i, chan; + struct csrow_info *csrow; - /* Create the dyanmic attribute files on this csrow, - * namely, the DIMM labels and the channel ce_count - */ - for (chan = 0; chan < csrow->nr_channels; chan++) { - err = edac_create_channel_files(&csrow->kobj, chan); - if (err) { - /* special case the unregister here */ - kobject_put(&csrow->kobj); - goto err_out; + for (i = mci->nr_csrows - 1; i >= 0; i--) { + csrow = &mci->csrows[i]; + for (chan = csrow->nr_channels - 1; chan >= 0; chan--) { + debugf1("Removing csrow %d channel %d sysfs nodes\n", + i, chan); + device_remove_file(&csrow->dev, + dynamic_csrow_dimm_attr[chan]); + device_remove_file(&csrow->dev, + dynamic_csrow_ce_count_attr[chan]); } + put_device(&mci->csrows[i].dev); + device_del(&mci->csrows[i].dev); } - kobject_uevent(&csrow->kobj, KOBJ_ADD); - return 0; - - /* error unwind stack */ -err_release_top_kobj: - kobject_put(&mci->edac_mci_kobj); - -err_out: - return err; } -/* default sysfs methods and data structures for the main MCI kobject */ +/* + * Memory controller device + */ + +#define to_mci(k) container_of(k, struct mem_ctl_info, dev) -static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci, +static ssize_t mci_reset_counters_store(struct device *dev, + struct device_attribute *mattr, const char *data, size_t count) { - int row, chan; - - mci->ue_noinfo_count = 0; - mci->ce_noinfo_count = 0; + struct mem_ctl_info *mci = to_mci(dev); + int cnt, row, chan, i; mci->ue_mc = 0; mci->ce_mc = 0; + mci->ue_noinfo_count = 0; + mci->ce_noinfo_count = 0; for (row = 0; row < mci->nr_csrows; row++) { struct csrow_info *ri = &mci->csrows[row]; @@ -438,6 +471,13 @@ static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci, ri->channels[chan].ce_count = 0; } + cnt = 1; + for (i = 0; i < mci->n_layers; i++) { + cnt *= mci->layers[i].size; + memset(mci->ce_per_layer[i], 0, cnt * sizeof(u32)); + memset(mci->ue_per_layer[i], 0, cnt * sizeof(u32)); + } + mci->start_time = jiffies; return count; } @@ -451,9 +491,11 @@ static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci, * Negative value still means that an error has occurred while setting * the scrub rate. */ -static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci, +static ssize_t mci_sdram_scrub_rate_store(struct device *dev, + struct device_attribute *mattr, const char *data, size_t count) { + struct mem_ctl_info *mci = to_mci(dev); unsigned long bandwidth = 0; int new_bw = 0; @@ -476,8 +518,11 @@ static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci, /* * ->get_sdram_scrub_rate() return value semantics same as above. */ -static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data) +static ssize_t mci_sdram_scrub_rate_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); int bandwidth = 0; if (!mci->get_sdram_scrub_rate) @@ -493,38 +538,65 @@ static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data) } /* default attribute files for the MCI object */ -static ssize_t mci_ue_count_show(struct mem_ctl_info *mci, char *data) +static ssize_t mci_ue_count_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); + return sprintf(data, "%d\n", mci->ue_mc); } -static ssize_t mci_ce_count_show(struct mem_ctl_info *mci, char *data) +static ssize_t mci_ce_count_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); + return sprintf(data, "%d\n", mci->ce_mc); } -static ssize_t mci_ce_noinfo_show(struct mem_ctl_info *mci, char *data) +static ssize_t mci_ce_noinfo_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); + return sprintf(data, "%d\n", mci->ce_noinfo_count); } -static ssize_t mci_ue_noinfo_show(struct mem_ctl_info *mci, char *data) +static ssize_t mci_ue_noinfo_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); + return sprintf(data, "%d\n", mci->ue_noinfo_count); } -static ssize_t mci_seconds_show(struct mem_ctl_info *mci, char *data) +static ssize_t mci_seconds_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); + return sprintf(data, "%ld\n", (jiffies - mci->start_time) / HZ); } -static ssize_t mci_ctl_name_show(struct mem_ctl_info *mci, char *data) +static ssize_t mci_ctl_name_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); + return sprintf(data, "%s\n", mci->ctl_name); } -static ssize_t mci_size_mb_show(struct mem_ctl_info *mci, char *data) +static ssize_t mci_size_mb_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); int total_pages = 0, csrow_idx, j; for (csrow_idx = 0; csrow_idx < mci->nr_csrows; csrow_idx++) { @@ -540,360 +612,53 @@ static ssize_t mci_size_mb_show(struct mem_ctl_info *mci, char *data) return sprintf(data, "%u\n", PAGES_TO_MiB(total_pages)); } -#define to_mci(k) container_of(k, struct mem_ctl_info, edac_mci_kobj) -#define to_mcidev_attr(a) container_of(a,struct mcidev_sysfs_attribute,attr) - -/* MCI show/store functions for top most object */ -static ssize_t mcidev_show(struct kobject *kobj, struct attribute *attr, - char *buffer) -{ - struct mem_ctl_info *mem_ctl_info = to_mci(kobj); - struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); - - debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info); - - if (mcidev_attr->show) - return mcidev_attr->show(mem_ctl_info, buffer); - - return -EIO; -} - -static ssize_t mcidev_store(struct kobject *kobj, struct attribute *attr, - const char *buffer, size_t count) -{ - struct mem_ctl_info *mem_ctl_info = to_mci(kobj); - struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); - - debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info); - - if (mcidev_attr->store) - return mcidev_attr->store(mem_ctl_info, buffer, count); - - return -EIO; -} - -/* Intermediate show/store table */ -static const struct sysfs_ops mci_ops = { - .show = mcidev_show, - .store = mcidev_store -}; - -#define MCIDEV_ATTR(_name,_mode,_show,_store) \ -static struct mcidev_sysfs_attribute mci_attr_##_name = { \ - .attr = {.name = __stringify(_name), .mode = _mode }, \ - .show = _show, \ - .store = _store, \ -}; - /* default Control file */ -MCIDEV_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store); +DEVICE_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store); /* default Attribute files */ -MCIDEV_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL); -MCIDEV_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL); -MCIDEV_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL); -MCIDEV_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL); -MCIDEV_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL); -MCIDEV_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL); -MCIDEV_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL); +DEVICE_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL); +DEVICE_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL); +DEVICE_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL); +DEVICE_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL); +DEVICE_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL); +DEVICE_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL); +DEVICE_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL); /* memory scrubber attribute file */ -MCIDEV_ATTR(sdram_scrub_rate, S_IRUGO | S_IWUSR, mci_sdram_scrub_rate_show, +DEVICE_ATTR(sdram_scrub_rate, S_IRUGO | S_IWUSR, mci_sdram_scrub_rate_show, mci_sdram_scrub_rate_store); -static struct mcidev_sysfs_attribute *mci_attr[] = { - &mci_attr_reset_counters, - &mci_attr_mc_name, - &mci_attr_size_mb, - &mci_attr_seconds_since_reset, - &mci_attr_ue_noinfo_count, - &mci_attr_ce_noinfo_count, - &mci_attr_ue_count, - &mci_attr_ce_count, - &mci_attr_sdram_scrub_rate, +static struct attribute *mci_attrs[] = { + &dev_attr_reset_counters.attr, + &dev_attr_mc_name.attr, + &dev_attr_size_mb.attr, + &dev_attr_seconds_since_reset.attr, + &dev_attr_ue_noinfo_count.attr, + &dev_attr_ce_noinfo_count.attr, + &dev_attr_ue_count.attr, + &dev_attr_ce_count.attr, + &dev_attr_sdram_scrub_rate.attr, NULL }; - -/* - * Release of a MC controlling instance - * - * each MC control instance has the following resources upon entry: - * a) a ref count on the top memctl kobj - * b) a ref count on this module - * - * this function must decrement those ref counts and then - * issue a free on the instance's memory - */ -static void edac_mci_control_release(struct kobject *kobj) -{ - struct mem_ctl_info *mci; - - mci = to_mci(kobj); - - debugf0("%s() mci instance idx=%d releasing\n", __func__, mci->mc_idx); - - /* decrement the module ref count */ - module_put(mci->owner); -} - -static struct kobj_type ktype_mci = { - .release = edac_mci_control_release, - .sysfs_ops = &mci_ops, - .default_attrs = (struct attribute **)mci_attr, -}; - -/* EDAC memory controller sysfs kset: - * /sys/devices/system/edac/mc - */ -static struct kset *mc_kset; - -/* - * edac_mc_register_sysfs_main_kobj - * - * setups and registers the main kobject for each mci - */ -int edac_mc_register_sysfs_main_kobj(struct mem_ctl_info *mci) -{ - struct kobject *kobj_mci; - int err; - - debugf1("%s()\n", __func__); - - kobj_mci = &mci->edac_mci_kobj; - - /* Init the mci's kobject */ - memset(kobj_mci, 0, sizeof(*kobj_mci)); - - /* Record which module 'owns' this control structure - * and bump the ref count of the module - */ - mci->owner = THIS_MODULE; - - /* bump ref count on this module */ - if (!try_module_get(mci->owner)) { - err = -ENODEV; - goto fail_out; - } - - /* this instance become part of the mc_kset */ - kobj_mci->kset = mc_kset; - - /* register the mc kobject to the mc_kset */ - err = kobject_init_and_add(kobj_mci, &ktype_mci, NULL, - "mc%d", mci->mc_idx); - if (err) { - debugf1("%s()Failed to register '.../edac/mc%d'\n", - __func__, mci->mc_idx); - goto kobj_reg_fail; - } - kobject_uevent(kobj_mci, KOBJ_ADD); - - /* At this point, to 'free' the control struct, - * edac_mc_unregister_sysfs_main_kobj() must be used - */ - - debugf1("%s() Registered '.../edac/mc%d' kobject\n", - __func__, mci->mc_idx); - - return 0; - - /* Error exit stack */ - -kobj_reg_fail: - module_put(mci->owner); - -fail_out: - return err; -} - -/* - * edac_mc_register_sysfs_main_kobj - * - * tears down and the main mci kobject from the mc_kset - */ -void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci) -{ - debugf1("%s()\n", __func__); - - /* delete the kobj from the mc_kset */ - kobject_put(&mci->edac_mci_kobj); -} - -#define EDAC_DEVICE_SYMLINK "device" - -#define grp_to_mci(k) (container_of(k, struct mcidev_sysfs_group_kobj, kobj)->mci) - -/* MCI show/store functions for top most object */ -static ssize_t inst_grp_show(struct kobject *kobj, struct attribute *attr, - char *buffer) -{ - struct mem_ctl_info *mem_ctl_info = grp_to_mci(kobj); - struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); - - debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info); - - if (mcidev_attr->show) - return mcidev_attr->show(mem_ctl_info, buffer); - - return -EIO; -} - -static ssize_t inst_grp_store(struct kobject *kobj, struct attribute *attr, - const char *buffer, size_t count) -{ - struct mem_ctl_info *mem_ctl_info = grp_to_mci(kobj); - struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); - - debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info); - - if (mcidev_attr->store) - return mcidev_attr->store(mem_ctl_info, buffer, count); - - return -EIO; -} - -/* No memory to release for this kobj */ -static void edac_inst_grp_release(struct kobject *kobj) -{ - struct mcidev_sysfs_group_kobj *grp; - struct mem_ctl_info *mci; - - debugf1("%s()\n", __func__); - - grp = container_of(kobj, struct mcidev_sysfs_group_kobj, kobj); - mci = grp->mci; -} - -/* Intermediate show/store table */ -static struct sysfs_ops inst_grp_ops = { - .show = inst_grp_show, - .store = inst_grp_store +static struct attribute_group mci_attr_grp = { + .attrs = mci_attrs, }; -/* the kobj_type instance for a instance group */ -static struct kobj_type ktype_inst_grp = { - .release = edac_inst_grp_release, - .sysfs_ops = &inst_grp_ops, +static const struct attribute_group *mci_attr_groups[] = { + &mci_attr_grp, + NULL }; - -/* - * edac_create_mci_instance_attributes - * create MC driver specific attributes bellow an specified kobj - * This routine calls itself recursively, in order to create an entire - * object tree. - */ -static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci, - const struct mcidev_sysfs_attribute *sysfs_attrib, - struct kobject *kobj) +static void mci_attr_release(struct device *device) { - int err; - - debugf4("%s()\n", __func__); - - while (sysfs_attrib) { - debugf4("%s() sysfs_attrib = %p\n",__func__, sysfs_attrib); - if (sysfs_attrib->grp) { - struct mcidev_sysfs_group_kobj *grp_kobj; - - grp_kobj = kzalloc(sizeof(*grp_kobj), GFP_KERNEL); - if (!grp_kobj) - return -ENOMEM; - - grp_kobj->grp = sysfs_attrib->grp; - grp_kobj->mci = mci; - list_add_tail(&grp_kobj->list, &mci->grp_kobj_list); - - debugf0("%s() grp %s, mci %p\n", __func__, - sysfs_attrib->grp->name, mci); - - err = kobject_init_and_add(&grp_kobj->kobj, - &ktype_inst_grp, - &mci->edac_mci_kobj, - sysfs_attrib->grp->name); - if (err < 0) { - printk(KERN_ERR "kobject_init_and_add failed: %d\n", err); - return err; - } - err = edac_create_mci_instance_attributes(mci, - grp_kobj->grp->mcidev_attr, - &grp_kobj->kobj); - - if (err < 0) - return err; - } else if (sysfs_attrib->attr.name) { - debugf4("%s() file %s\n", __func__, - sysfs_attrib->attr.name); - - err = sysfs_create_file(kobj, &sysfs_attrib->attr); - if (err < 0) { - printk(KERN_ERR "sysfs_create_file failed: %d\n", err); - return err; - } - } else - break; - - sysfs_attrib++; - } - - return 0; + debugf1("Releasing mci device %s\n", dev_name(device)); } -/* - * edac_remove_mci_instance_attributes - * remove MC driver specific attributes at the topmost level - * directory of this mci instance. - */ -static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci, - const struct mcidev_sysfs_attribute *sysfs_attrib, - struct kobject *kobj, int count) -{ - struct mcidev_sysfs_group_kobj *grp_kobj, *tmp; - - debugf1("%s()\n", __func__); - - /* - * loop if there are attributes and until we hit a NULL entry - * Remove first all the attributes - */ - while (sysfs_attrib) { - debugf4("%s() sysfs_attrib = %p\n",__func__, sysfs_attrib); - if (sysfs_attrib->grp) { - debugf4("%s() seeking for group %s\n", - __func__, sysfs_attrib->grp->name); - list_for_each_entry(grp_kobj, - &mci->grp_kobj_list, list) { - debugf4("%s() grp_kobj->grp = %p\n",__func__, grp_kobj->grp); - if (grp_kobj->grp == sysfs_attrib->grp) { - edac_remove_mci_instance_attributes(mci, - grp_kobj->grp->mcidev_attr, - &grp_kobj->kobj, count + 1); - debugf4("%s() group %s\n", __func__, - sysfs_attrib->grp->name); - kobject_put(&grp_kobj->kobj); - } - } - debugf4("%s() end of seeking for group %s\n", - __func__, sysfs_attrib->grp->name); - } else if (sysfs_attrib->attr.name) { - debugf4("%s() file %s\n", __func__, - sysfs_attrib->attr.name); - sysfs_remove_file(kobj, &sysfs_attrib->attr); - } else - break; - sysfs_attrib++; - } - - /* Remove the group objects */ - if (count) - return; - list_for_each_entry_safe(grp_kobj, tmp, - &mci->grp_kobj_list, list) { - list_del(&grp_kobj->list); - kfree(grp_kobj); - } -} +static struct device_type mci_attr_type = { + .groups = mci_attr_groups, + .release = mci_attr_release, +}; /* @@ -906,77 +671,80 @@ static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci, */ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) { - int i, j; - int err; - struct csrow_info *csrow; - struct kobject *kobj_mci = &mci->edac_mci_kobj; + int i, err; debugf0("%s() idx=%d\n", __func__, mci->mc_idx); - INIT_LIST_HEAD(&mci->grp_kobj_list); + /* get the /sys/devices/system/edac subsys reference */ - /* create a symlink for the device */ - err = sysfs_create_link(kobj_mci, &mci->pdev->kobj, - EDAC_DEVICE_SYMLINK); - if (err) { - debugf1("%s() failure to create symlink\n", __func__); - goto fail0; - } + mci->dev.type = &mci_attr_type; + device_initialize(&mci->dev); - /* If the low level driver desires some attributes, - * then create them now for the driver. + mci->dev.parent = &mci_pdev; + mci->dev.bus = &mci->bus; + dev_set_name(&mci->dev, "mc%d", mci->mc_idx); + dev_set_drvdata(&mci->dev, mci); + pm_runtime_forbid(&mci->dev); + + /* + * The memory controller needs its own bus, in order to avoid + * namespace conflicts at /sys/bus/edac. */ - if (mci->mc_driver_sysfs_attributes) { - err = edac_create_mci_instance_attributes(mci, - mci->mc_driver_sysfs_attributes, - &mci->edac_mci_kobj); - if (err) { - debugf1("%s() failure to create mci attributes\n", - __func__); - goto fail0; - } + debugf0("creating bus %s\n",mci->bus.name); + mci->bus.name = kstrdup(dev_name(&mci->dev), GFP_KERNEL); + err = bus_register(&mci->bus); + if (err < 0) + return err; + + debugf0("%s(): creating device %s\n", __func__, + dev_name(&mci->dev)); + err = device_add(&mci->dev); + if (err < 0) { + bus_unregister(&mci->bus); + kfree(mci->bus.name); + return err; } - /* Make directories for each CSROW object under the mc kobject + /* + * Create the dimm/rank devices */ - for (i = 0; i < mci->nr_csrows; i++) { - int nr_pages = 0; - - csrow = &mci->csrows[i]; - for (j = 0; j < csrow->nr_channels; j++) - nr_pages += csrow->channels[j].dimm->nr_pages; - - if (nr_pages > 0) { - err = edac_create_csrow_object(mci, csrow, i); - if (err) { - debugf1("%s() failure: create csrow %d obj\n", - __func__, i); - goto fail1; - } + for (i = 0; i < mci->tot_dimms; i++) { + struct dimm_info *dimm = &mci->dimms[i]; + /* Only expose populated DIMMs */ + if (dimm->nr_pages == 0) + continue; +#ifdef CONFIG_EDAC_DEBUG + debugf1("%s creating dimm%d, located at ", + __func__, i); + if (edac_debug_level >= 1) { + int lay; + for (lay = 0; lay < mci->n_layers; lay++) + printk(KERN_CONT "%s %d ", + edac_layer_name[mci->layers[lay].type], + dimm->location[lay]); + printk(KERN_CONT "\n"); } +#endif } + err = edac_create_csrow_objects(mci); + if (err < 0) + goto fail; + return 0; -fail1: +fail: for (i--; i >= 0; i--) { - int nr_pages = 0; - - csrow = &mci->csrows[i]; - for (j = 0; j < csrow->nr_channels; j++) - nr_pages += csrow->channels[j].dimm->nr_pages; - if (nr_pages > 0) - kobject_put(&mci->csrows[i].kobj); + struct dimm_info *dimm = &mci->dimms[i]; + if (dimm->nr_pages == 0) + continue; + put_device(&dimm->dev); + device_del(&dimm->dev); } - - /* remove the mci instance's attributes, if any */ - edac_remove_mci_instance_attributes(mci, - mci->mc_driver_sysfs_attributes, &mci->edac_mci_kobj, 0); - - /* remove the symlink */ - sysfs_remove_link(kobj_mci, EDAC_DEVICE_SYMLINK); - -fail0: + put_device(&mci->dev); + device_del(&mci->dev); + bus_unregister(&mci->bus); + kfree(mci->bus.name); return err; } @@ -985,98 +753,70 @@ fail0: */ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) { - struct csrow_info *csrow; - int i, j; + int i; debugf0("%s()\n", __func__); - /* remove all csrow kobjects */ - debugf4("%s() unregister this mci kobj\n", __func__); - for (i = 0; i < mci->nr_csrows; i++) { - int nr_pages = 0; - - csrow = &mci->csrows[i]; - for (j = 0; j < csrow->nr_channels; j++) - nr_pages += csrow->channels[j].dimm->nr_pages; - if (nr_pages > 0) { - debugf0("%s() unreg csrow-%d\n", __func__, i); - kobject_put(&mci->csrows[i].kobj); - } - } + edac_delete_csrow_objects(mci); - /* remove this mci instance's attribtes */ - if (mci->mc_driver_sysfs_attributes) { - debugf4("%s() unregister mci private attributes\n", __func__); - edac_remove_mci_instance_attributes(mci, - mci->mc_driver_sysfs_attributes, - &mci->edac_mci_kobj, 0); + for (i = 0; i < mci->tot_dimms; i++) { + struct dimm_info *dimm = &mci->dimms[i]; + if (dimm->nr_pages == 0) + continue; + debugf0("%s(): removing device %s\n", __func__, + dev_name(&dimm->dev)); + put_device(&dimm->dev); + device_del(&dimm->dev); } - - /* remove the symlink */ - debugf4("%s() remove_link\n", __func__); - sysfs_remove_link(&mci->edac_mci_kobj, EDAC_DEVICE_SYMLINK); - - /* unregister this instance's kobject */ - debugf4("%s() remove_mci_instance\n", __func__); - kobject_put(&mci->edac_mci_kobj); } +void edac_unregister_sysfs(struct mem_ctl_info *mci) +{ + debugf1("Unregistering device %s\n", dev_name(&mci->dev)); + put_device(&mci->dev); + device_del(&mci->dev); + bus_unregister(&mci->bus); + kfree(mci->bus.name); +} +static void mc_attr_release(struct device *device) +{ + debugf1("Releasing device %s\n", dev_name(device)); +} - +static struct device_type mc_attr_type = { + .release = mc_attr_release, +}; /* - * edac_setup_sysfs_mc_kset(void) - * - * Initialize the mc_kset for the 'mc' entry - * This requires creating the top 'mc' directory with a kset - * and its controls/attributes. - * - * To this 'mc' kset, instance 'mci' will be grouped as children. - * - * Return: 0 SUCCESS - * !0 FAILURE error code + * Init/exit code for the module. Basically, creates/removes /sys/class/rc */ -int edac_sysfs_setup_mc_kset(void) +int __init edac_mc_sysfs_init(void) { - int err = -EINVAL; struct bus_type *edac_subsys; - - debugf1("%s()\n", __func__); + int err; /* get the /sys/devices/system/edac subsys reference */ edac_subsys = edac_get_sysfs_subsys(); if (edac_subsys == NULL) { - debugf1("%s() no edac_subsys error=%d\n", __func__, err); - goto fail_out; + debugf1("%s() no edac_subsys\n", __func__); + return -EINVAL; } - /* Init the MC's kobject */ - mc_kset = kset_create_and_add("mc", NULL, &edac_subsys->dev_root->kobj); - if (!mc_kset) { - err = -ENOMEM; - debugf1("%s() Failed to register '.../edac/mc'\n", __func__); - goto fail_kset; - } + mci_pdev.bus = edac_subsys; + mci_pdev.type = &mc_attr_type; + device_initialize(&mci_pdev); + dev_set_name(&mci_pdev, "mc"); - debugf1("%s() Registered '.../edac/mc' kobject\n", __func__); + err = device_add(&mci_pdev); + if (err < 0) + return err; return 0; - -fail_kset: - edac_put_sysfs_subsys(); - -fail_out: - return err; } -/* - * edac_sysfs_teardown_mc_kset - * - * deconstruct the mc_ket for memory controllers - */ -void edac_sysfs_teardown_mc_kset(void) +void __exit edac_mc_sysfs_exit(void) { - kset_unregister(mc_kset); + put_device(&mci_pdev); + device_del(&mci_pdev); edac_put_sysfs_subsys(); } - diff --git a/drivers/edac/edac_module.c b/drivers/edac/edac_module.c index 5ddaa86d6a6e..8735a0d3ed0c 100644 --- a/drivers/edac/edac_module.c +++ b/drivers/edac/edac_module.c @@ -90,10 +90,7 @@ static int __init edac_init(void) */ edac_pci_clear_parity_errors(); - /* - * now set up the mc_kset under the edac class object - */ - err = edac_sysfs_setup_mc_kset(); + err = edac_mc_sysfs_init(); if (err) goto error; @@ -101,15 +98,11 @@ static int __init edac_init(void) err = edac_workqueue_setup(); if (err) { edac_printk(KERN_ERR, EDAC_MC, "init WorkQueue failure\n"); - goto workq_fail; + goto error; } return 0; - /* Error teardown stack */ -workq_fail: - edac_sysfs_teardown_mc_kset(); - error: return err; } @@ -124,7 +117,7 @@ static void __exit edac_exit(void) /* tear down the various subsystems */ edac_workqueue_teardown(); - edac_sysfs_teardown_mc_kset(); + edac_mc_sysfs_exit(); } /* diff --git a/drivers/edac/edac_module.h b/drivers/edac/edac_module.h index 0ea7d14cb930..1af13676e857 100644 --- a/drivers/edac/edac_module.h +++ b/drivers/edac/edac_module.h @@ -19,12 +19,12 @@ * * edac_mc objects */ -extern int edac_sysfs_setup_mc_kset(void); -extern void edac_sysfs_teardown_mc_kset(void); -extern int edac_mc_register_sysfs_main_kobj(struct mem_ctl_info *mci); -extern void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci); + /* on edac_mc_sysfs.c */ +int edac_mc_sysfs_init(void); +void edac_mc_sysfs_exit(void); extern int edac_create_sysfs_mci_device(struct mem_ctl_info *mci); extern void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci); +void edac_unregister_sysfs(struct mem_ctl_info *mci); extern int edac_get_log_ue(void); extern int edac_get_log_ce(void); extern int edac_get_panic_on_ue(void); @@ -34,6 +34,7 @@ extern int edac_mc_get_panic_on_ue(void); extern int edac_get_poll_msec(void); extern int edac_mc_get_poll_msec(void); + /* on edac_device.c */ extern int edac_device_register_sysfs_main_kobj( struct edac_device_ctl_info *edac_dev); extern void edac_device_unregister_sysfs_main_kobj( diff --git a/include/linux/edac.h b/include/linux/edac.h index 4e32e8d31e0a..a2b0b6fc002c 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -13,6 +13,7 @@ #define _LINUX_EDAC_H_ #include +#include #include #include #include @@ -448,14 +449,15 @@ struct edac_mc_layer { __p; \ }) - -/* FIXME: add the proper per-location error counts */ struct dimm_info { + struct device dev; + char label[EDAC_MC_LABEL_LEN + 1]; /* DIMM label on motherboard */ /* Memory location data */ unsigned location[EDAC_MAX_LAYERS]; + struct kobject kobj; /* sysfs kobject for this csrow */ struct mem_ctl_info *mci; /* the parent */ u32 grain; /* granularity of reported error in bytes */ @@ -484,6 +486,8 @@ struct dimm_info { * patches in this series will fix this issue. */ struct rank_info { + struct device dev; + int chan_idx; struct csrow_info *csrow; struct dimm_info *dimm; @@ -492,6 +496,8 @@ struct rank_info { }; struct csrow_info { + struct device dev; + /* Used only by edac_mc_find_csrow_by_page() */ unsigned long first_page; /* first page number in csrow */ unsigned long last_page; /* last page number in csrow */ @@ -517,15 +523,6 @@ struct mcidev_sysfs_group { const struct mcidev_sysfs_attribute *mcidev_attr; /* group attributes */ }; -struct mcidev_sysfs_group_kobj { - struct list_head list; /* list for all instances within a mc */ - - struct kobject kobj; /* kobj for the group */ - - const struct mcidev_sysfs_group *grp; /* group description table */ - struct mem_ctl_info *mci; /* the parent */ -}; - /* mcidev_sysfs_attribute structure * used for driver sysfs attributes and in mem_ctl_info * sysfs top level entries @@ -536,13 +533,27 @@ struct mcidev_sysfs_attribute { const struct mcidev_sysfs_group *grp; /* Points to a group of attributes */ /* Ops for show/store values at the attribute - not used on group */ - ssize_t (*show)(struct mem_ctl_info *,char *); - ssize_t (*store)(struct mem_ctl_info *, const char *,size_t); + ssize_t (*show)(struct mem_ctl_info *, char *); + ssize_t (*store)(struct mem_ctl_info *, const char *, size_t); + + void *priv; +}; + +/* + * struct errcount_attribute - used to store the several error counts + */ +struct errcount_attribute_data { + int n_layers; + int pos[EDAC_MAX_LAYERS]; + int layer0, layer1, layer2; }; /* MEMORY controller information structure */ struct mem_ctl_info { + struct device dev; + struct bus_type bus; + struct list_head link; /* for global list of mem_ctl_info structs */ struct module *owner; /* Module owner of this control struct */ @@ -587,7 +598,15 @@ struct mem_ctl_info { struct csrow_info *csrows; unsigned nr_csrows, num_cschannel; - /* Memory Controller hierarchy */ + /* + * Memory Controller hierarchy + * + * There are basically two types of memory controller: the ones that + * sees memory sticks ("dimms"), and the ones that sees memory ranks. + * All old memory controllers enumerate memories per rank, but most + * of the recent drivers enumerate memories per DIMM, instead. + * When the memory controller is per rank, mem_is_per_rank is true. + */ unsigned n_layers; struct edac_mc_layer *layers; bool mem_is_per_rank; -- cgit v1.2.3 From d90c008963ef638cb7ab7d5eb76362b3c2d379bc Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 21 Mar 2012 16:55:02 -0300 Subject: edac: Get rid of the old kobj's from the edac mc code Now that al users for the old kobj raw access are gone, we can get rid of the legacy kobj-based structures and data. Reviewed-by: Aristeu Rozanski Cc: Doug Thompson Cc: Michal Marek Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/edac_mc.c | 1 - drivers/edac/i5000_edac.c | 3 --- drivers/edac/i82875p_edac.c | 4 ---- include/linux/edac.h | 30 ------------------------------ 4 files changed, 38 deletions(-) (limited to 'include') diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 61ae34643b49..4a6fdc03740e 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -372,7 +372,6 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, } mci->op_state = OP_ALLOC; - INIT_LIST_HEAD(&mci->grp_kobj_list); /* at this point, the root kobj is valid, and in order to * 'free' the object, then the function: diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c index a69245ad5f32..a7da4c7ad7fa 100644 --- a/drivers/edac/i5000_edac.c +++ b/drivers/edac/i5000_edac.c @@ -1406,7 +1406,6 @@ static int i5000_probe1(struct pci_dev *pdev, int dev_idx) if (mci == NULL) return -ENOMEM; - kobject_get(&mci->edac_mci_kobj); debugf0("MC: %s: %s(): mci = %p\n", __FILE__, __func__, mci); mci->pdev = &pdev->dev; /* record ptr to the generic device */ @@ -1479,7 +1478,6 @@ fail1: i5000_put_devices(mci); fail0: - kobject_put(&mci->edac_mci_kobj); edac_mc_free(mci); return -ENODEV; } @@ -1525,7 +1523,6 @@ static void __devexit i5000_remove_one(struct pci_dev *pdev) /* retrieve references to resources, and free those resources */ i5000_put_devices(mci); - kobject_put(&mci->edac_mci_kobj); edac_mc_free(mci); } diff --git a/drivers/edac/i82875p_edac.c b/drivers/edac/i82875p_edac.c index 1cc682d0678d..a47c6b25db31 100644 --- a/drivers/edac/i82875p_edac.c +++ b/drivers/edac/i82875p_edac.c @@ -426,9 +426,6 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx) goto fail0; } - /* Keeps mci available after edac_mc_del_mc() till edac_mc_free() */ - kobject_get(&mci->edac_mci_kobj); - debugf3("%s(): init mci\n", __func__); mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR; @@ -471,7 +468,6 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx) return 0; fail1: - kobject_put(&mci->edac_mci_kobj); edac_mc_free(mci); fail0: diff --git a/include/linux/edac.h b/include/linux/edac.h index a2b0b6fc002c..8a2da47daa47 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -457,7 +457,6 @@ struct dimm_info { /* Memory location data */ unsigned location[EDAC_MAX_LAYERS]; - struct kobject kobj; /* sysfs kobject for this csrow */ struct mem_ctl_info *mci; /* the parent */ u32 grain; /* granularity of reported error in bytes */ @@ -511,34 +510,11 @@ struct csrow_info { struct mem_ctl_info *mci; /* the parent */ - struct kobject kobj; /* sysfs kobject for this csrow */ - /* channel information for this csrow */ u32 nr_channels; struct rank_info *channels; }; -struct mcidev_sysfs_group { - const char *name; /* group name */ - const struct mcidev_sysfs_attribute *mcidev_attr; /* group attributes */ -}; - -/* mcidev_sysfs_attribute structure - * used for driver sysfs attributes and in mem_ctl_info - * sysfs top level entries - */ -struct mcidev_sysfs_attribute { - /* It should use either attr or grp */ - struct attribute attr; - const struct mcidev_sysfs_group *grp; /* Points to a group of attributes */ - - /* Ops for show/store values at the attribute - not used on group */ - ssize_t (*show)(struct mem_ctl_info *, char *); - ssize_t (*store)(struct mem_ctl_info *, const char *, size_t); - - void *priv; -}; - /* * struct errcount_attribute - used to store the several error counts */ @@ -641,12 +617,6 @@ struct mem_ctl_info { struct completion complete; - /* edac sysfs device control */ - struct kobject edac_mci_kobj; - - /* list for all grp instances within a mc */ - struct list_head grp_kobj_list; - /* Additional top controller level attributes, but specified * by the low level driver. * -- cgit v1.2.3 From 452a6bf955ee1842361742833e40e046287308f4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 26 Mar 2012 09:35:11 -0300 Subject: edac: Add debufs nodes to allow doing fake error inject Sometimes, it is useful to have a mechanism that generates fake errors, in order to test the EDAC core code, and the userspace tools. Provide such mechanism by adding a few debugfs nodes. Reviewed-by: Aristeu Rozanski Cc: Doug Thompson Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/edac_mc_sysfs.c | 87 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/edac.h | 7 ++++ 2 files changed, 94 insertions(+) (limited to 'include') diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 87fb396bc550..daa418b61525 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "edac_core.h" #include "edac_module.h" @@ -783,6 +784,47 @@ static ssize_t mci_max_location_show(struct device *dev, return p - data; } +#ifdef CONFIG_EDAC_DEBUG +static ssize_t edac_fake_inject_write(struct file *file, + const char __user *data, + size_t count, loff_t *ppos) +{ + struct device *dev = file->private_data; + struct mem_ctl_info *mci = to_mci(dev); + static enum hw_event_mc_err_type type; + + type = mci->fake_inject_ue ? HW_EVENT_ERR_UNCORRECTED + : HW_EVENT_ERR_CORRECTED; + + printk(KERN_DEBUG + "Generating a %s fake error to %d.%d.%d to test core handling. NOTE: this won't test the driver-specific decoding logic.\n", + (type == HW_EVENT_ERR_UNCORRECTED) ? "UE" : "CE", + mci->fake_inject_layer[0], + mci->fake_inject_layer[1], + mci->fake_inject_layer[2] + ); + edac_mc_handle_error(type, mci, 0, 0, 0, + mci->fake_inject_layer[0], + mci->fake_inject_layer[1], + mci->fake_inject_layer[2], + "FAKE ERROR", "for EDAC testing only", NULL); + + return count; +} + +static int debugfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static const struct file_operations debug_fake_inject_fops = { + .open = debugfs_open, + .write = edac_fake_inject_write, + .llseek = generic_file_llseek, +}; +#endif + /* default Control file */ DEVICE_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store); @@ -833,6 +875,45 @@ static struct device_type mci_attr_type = { .release = mci_attr_release, }; +#ifdef CONFIG_EDAC_DEBUG +int edac_create_debug_nodes(struct mem_ctl_info *mci) +{ + struct dentry *d, *parent; + char name[80]; + int i; + + d = debugfs_create_dir(mci->dev.kobj.name, mci->debugfs); + if (!d) + return -ENOMEM; + parent = d; + + for (i = 0; i < mci->n_layers; i++) { + sprintf(name, "fake_inject_%s", + edac_layer_name[mci->layers[i].type]); + d = debugfs_create_u8(name, S_IRUGO | S_IWUSR, parent, + &mci->fake_inject_layer[i]); + if (!d) + goto nomem; + } + + d = debugfs_create_bool("fake_inject_ue", S_IRUGO | S_IWUSR, parent, + &mci->fake_inject_ue); + if (!d) + goto nomem; + + d = debugfs_create_file("fake_inject", S_IWUSR, parent, + &mci->dev, + &debug_fake_inject_fops); + if (!d) + goto nomem; + + return 0; +nomem: + debugfs_remove(mci->debugfs); + return -ENOMEM; +} +#endif + /* * Create a new Memory Controller kobject instance, * mc under the 'mc' directory @@ -911,6 +992,9 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) goto fail; #endif +#ifdef CONFIG_EDAC_DEBUG + edac_create_debug_nodes(mci); +#endif return 0; fail: @@ -937,6 +1021,9 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) debugf0("%s()\n", __func__); +#ifdef CONFIG_EDAC_DEBUG + debugfs_remove(mci->debugfs); +#endif #ifdef CONFIG_EDAC_LEGACY_SYSFS edac_delete_csrow_objects(mci); #endif diff --git a/include/linux/edac.h b/include/linux/edac.h index 8a2da47daa47..64ae0c5cf62e 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -17,6 +17,7 @@ #include #include #include +#include struct device; @@ -634,6 +635,12 @@ struct mem_ctl_info { /* the internal state of this controller instance */ int op_state; + +#ifdef CONFIG_EDAC_DEBUG + struct dentry *debugfs; + u8 fake_inject_layer[EDAC_MAX_LAYERS]; + u32 fake_inject_ue; +#endif }; #endif -- cgit v1.2.3 From de3910eb79ac8c0f29a11224661c0ebaaf813039 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 24 Apr 2012 15:05:43 -0300 Subject: edac: change the mem allocation scheme to make Documentation/kobject.txt happy Kernel kobjects have rigid rules: each container object should be dynamically allocated, and can't be allocated into a single kmalloc. EDAC never obeyed this rule: it has a single malloc function that allocates all needed data into a single kzalloc. As this is not accepted anymore, change the allocation schema of the EDAC *_info structs to enforce this Kernel standard. Acked-by: Chris Metcalf Cc: Aristeu Rozanski Cc: Doug Thompson Cc: Greg K H Cc: Borislav Petkov Cc: Mark Gross Cc: Tim Small Cc: Ranganathan Desikan Cc: "Arvind R." Cc: Olof Johansson Cc: Egor Martovetsky Cc: Michal Marek Cc: Jiri Kosina Cc: Dmitry Eremin-Solenikov Cc: Benjamin Herrenschmidt Cc: Hitoshi Mitake Cc: Andrew Morton Cc: Shaohui Xie Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/amd64_edac.c | 10 ++-- drivers/edac/amd76x_edac.c | 8 +-- drivers/edac/cell_edac.c | 8 +-- drivers/edac/cpc925_edac.c | 8 +-- drivers/edac/e752x_edac.c | 4 +- drivers/edac/e7xxx_edac.c | 4 +- drivers/edac/edac_mc.c | 107 ++++++++++++++++++++++------------ drivers/edac/edac_mc_sysfs.c | 126 +++++++++++++++++++++++------------------ drivers/edac/i3000_edac.c | 6 +- drivers/edac/i3200_edac.c | 4 +- drivers/edac/i5400_edac.c | 6 +- drivers/edac/i82443bxgx_edac.c | 4 +- drivers/edac/i82860_edac.c | 6 +- drivers/edac/i82875p_edac.c | 6 +- drivers/edac/i82975x_edac.c | 10 ++-- drivers/edac/mpc85xx_edac.c | 6 +- drivers/edac/mv64x60_edac.c | 4 +- drivers/edac/pasemi_edac.c | 8 +-- drivers/edac/r82600_edac.c | 4 +- drivers/edac/tile_edac.c | 4 +- drivers/edac/x38_edac.c | 4 +- include/linux/edac.h | 59 +++++++++++++------ 22 files changed, 242 insertions(+), 164 deletions(-) (limited to 'include') diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 9905834b560f..9fbced7f65ee 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -2205,6 +2205,7 @@ static u32 amd64_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr) static int init_csrows(struct mem_ctl_info *mci) { struct csrow_info *csrow; + struct dimm_info *dimm; struct amd64_pvt *pvt = mci->pvt_info; u64 base, mask; u32 val; @@ -2222,7 +2223,7 @@ static int init_csrows(struct mem_ctl_info *mci) !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE)); for_each_chip_select(i, 0, pvt) { - csrow = &mci->csrows[i]; + csrow = mci->csrows[i]; if (!csrow_enabled(i, 0, pvt) && !csrow_enabled(i, 1, pvt)) { debugf1("----CSROW %d EMPTY for node %d\n", i, @@ -2257,9 +2258,10 @@ static int init_csrows(struct mem_ctl_info *mci) edac_mode = EDAC_NONE; for (j = 0; j < pvt->channel_count; j++) { - csrow->channels[j].dimm->mtype = mtype; - csrow->channels[j].dimm->edac_mode = edac_mode; - csrow->channels[j].dimm->nr_pages = nr_pages; + dimm = csrow->channels[j]->dimm; + dimm->mtype = mtype; + dimm->edac_mode = edac_mode; + dimm->nr_pages = nr_pages; } } diff --git a/drivers/edac/amd76x_edac.c b/drivers/edac/amd76x_edac.c index 7439786f3bef..a0c9f82875cd 100644 --- a/drivers/edac/amd76x_edac.c +++ b/drivers/edac/amd76x_edac.c @@ -146,7 +146,7 @@ static int amd76x_process_error_info(struct mem_ctl_info *mci, if (handle_errors) { row = (info->ecc_mode_status >> 4) & 0xf; edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, - mci->csrows[row].first_page, 0, 0, + mci->csrows[row]->first_page, 0, 0, row, 0, -1, mci->ctl_name, "", NULL); } @@ -161,7 +161,7 @@ static int amd76x_process_error_info(struct mem_ctl_info *mci, if (handle_errors) { row = info->ecc_mode_status & 0xf; edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, - mci->csrows[row].first_page, 0, 0, + mci->csrows[row]->first_page, 0, 0, row, 0, -1, mci->ctl_name, "", NULL); } @@ -194,8 +194,8 @@ static void amd76x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, int index; for (index = 0; index < mci->nr_csrows; index++) { - csrow = &mci->csrows[index]; - dimm = csrow->channels[0].dimm; + csrow = mci->csrows[index]; + dimm = csrow->channels[0]->dimm; /* find the DRAM Chip Select Base address and mask */ pci_read_config_dword(pdev, diff --git a/drivers/edac/cell_edac.c b/drivers/edac/cell_edac.c index 2e5b95374dc6..478d8ee434df 100644 --- a/drivers/edac/cell_edac.c +++ b/drivers/edac/cell_edac.c @@ -33,7 +33,7 @@ struct cell_edac_priv static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar) { struct cell_edac_priv *priv = mci->pvt_info; - struct csrow_info *csrow = &mci->csrows[0]; + struct csrow_info *csrow = mci->csrows[0]; unsigned long address, pfn, offset, syndrome; dev_dbg(mci->pdev, "ECC CE err on node %d, channel %d, ar = 0x%016llx\n", @@ -56,7 +56,7 @@ static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar) static void cell_edac_count_ue(struct mem_ctl_info *mci, int chan, u64 ar) { struct cell_edac_priv *priv = mci->pvt_info; - struct csrow_info *csrow = &mci->csrows[0]; + struct csrow_info *csrow = mci->csrows[0]; unsigned long address, pfn, offset; dev_dbg(mci->pdev, "ECC UE err on node %d, channel %d, ar = 0x%016llx\n", @@ -126,7 +126,7 @@ static void cell_edac_check(struct mem_ctl_info *mci) static void __devinit cell_edac_init_csrows(struct mem_ctl_info *mci) { - struct csrow_info *csrow = &mci->csrows[0]; + struct csrow_info *csrow = mci->csrows[0]; struct dimm_info *dimm; struct cell_edac_priv *priv = mci->pvt_info; struct device_node *np; @@ -150,7 +150,7 @@ static void __devinit cell_edac_init_csrows(struct mem_ctl_info *mci) csrow->last_page = csrow->first_page + nr_pages - 1; for (j = 0; j < csrow->nr_channels; j++) { - dimm = csrow->channels[j].dimm; + dimm = csrow->channels[j]->dimm; dimm->mtype = MEM_XDR; dimm->edac_mode = EDAC_SECDED; dimm->nr_pages = nr_pages / csrow->nr_channels; diff --git a/drivers/edac/cpc925_edac.c b/drivers/edac/cpc925_edac.c index 3510aa446297..534491d67159 100644 --- a/drivers/edac/cpc925_edac.c +++ b/drivers/edac/cpc925_edac.c @@ -348,7 +348,7 @@ static void cpc925_init_csrows(struct mem_ctl_info *mci) if (bba == 0) continue; /* not populated */ - csrow = &mci->csrows[index]; + csrow = mci->csrows[index]; row_size = bba * (1UL << 28); /* 256M */ csrow->first_page = last_nr_pages; @@ -380,7 +380,7 @@ static void cpc925_init_csrows(struct mem_ctl_info *mci) break; } for (j = 0; j < csrow->nr_channels; j++) { - dimm = csrow->channels[j].dimm; + dimm = csrow->channels[j]->dimm; dimm->nr_pages = nr_pages / csrow->nr_channels; dimm->mtype = MEM_RDDR; dimm->edac_mode = EDAC_SECDED; @@ -463,7 +463,7 @@ static void cpc925_mc_get_pfn(struct mem_ctl_info *mci, u32 mear, *csrow = rank; #ifdef CONFIG_EDAC_DEBUG - if (mci->csrows[rank].first_page == 0) { + if (mci->csrows[rank]->first_page == 0) { cpc925_mc_printk(mci, KERN_ERR, "ECC occurs in a " "non-populated csrow, broken hardware?\n"); return; @@ -471,7 +471,7 @@ static void cpc925_mc_get_pfn(struct mem_ctl_info *mci, u32 mear, #endif /* Revert csrow number */ - pa = mci->csrows[rank].first_page << PAGE_SHIFT; + pa = mci->csrows[rank]->first_page << PAGE_SHIFT; /* Revert column address */ col += bcnt; diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c index d1142ed8bd88..7cde7f1aafb7 100644 --- a/drivers/edac/e752x_edac.c +++ b/drivers/edac/e752x_edac.c @@ -1096,7 +1096,7 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, for (last_cumul_size = index = 0; index < mci->nr_csrows; index++) { /* mem_dev 0=x8, 1=x4 */ mem_dev = (dra >> (index * 4 + 2)) & 0x3; - csrow = &mci->csrows[remap_csrow_index(mci, index)]; + csrow = mci->csrows[remap_csrow_index(mci, index)]; mem_dev = (mem_dev == 2); pci_read_config_byte(pdev, E752X_DRB + index, &value); @@ -1127,7 +1127,7 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, } else edac_mode = EDAC_NONE; for (i = 0; i < csrow->nr_channels; i++) { - struct dimm_info *dimm = csrow->channels[i].dimm; + struct dimm_info *dimm = csrow->channels[i]->dimm; debugf3("Initializing rank at (%i,%i)\n", index, i); dimm->nr_pages = nr_pages / csrow->nr_channels; diff --git a/drivers/edac/e7xxx_edac.c b/drivers/edac/e7xxx_edac.c index bab31aab983d..c6c0ebaca371 100644 --- a/drivers/edac/e7xxx_edac.c +++ b/drivers/edac/e7xxx_edac.c @@ -378,7 +378,7 @@ static void e7xxx_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, for (index = 0; index < mci->nr_csrows; index++) { /* mem_dev 0=x8, 1=x4 */ mem_dev = (dra >> (index * 4 + 3)) & 0x1; - csrow = &mci->csrows[index]; + csrow = mci->csrows[index]; pci_read_config_byte(pdev, E7XXX_DRB + index, &value); /* convert a 64 or 32 MiB DRB to a page size. */ @@ -409,7 +409,7 @@ static void e7xxx_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, edac_mode = EDAC_NONE; for (j = 0; j < drc_chan + 1; j++) { - dimm = csrow->channels[j].dimm; + dimm = csrow->channels[j]->dimm; dimm->nr_pages = nr_pages / (drc_chan + 1); dimm->grain = 1 << 12; /* 4KiB - resolution of CELOG */ diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 4a6fdc03740e..db2ba31ba2b1 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -210,15 +210,15 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, { struct mem_ctl_info *mci; struct edac_mc_layer *layer; - struct csrow_info *csi, *csr; - struct rank_info *chi, *chp, *chan; + struct csrow_info *csr; + struct rank_info *chan; struct dimm_info *dimm; u32 *ce_per_layer[EDAC_MAX_LAYERS], *ue_per_layer[EDAC_MAX_LAYERS]; unsigned pos[EDAC_MAX_LAYERS]; unsigned size, tot_dimms = 1, count = 1; unsigned tot_csrows = 1, tot_channels = 1, tot_errcount = 0; void *pvt, *p, *ptr = NULL; - int i, j, row, chn, n, len; + int i, j, row, chn, n, len, off; bool per_rank = false; BUG_ON(n_layers > EDAC_MAX_LAYERS || n_layers == 0); @@ -244,9 +244,6 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, */ mci = edac_align_ptr(&ptr, sizeof(*mci), 1); layer = edac_align_ptr(&ptr, sizeof(*layer), n_layers); - csi = edac_align_ptr(&ptr, sizeof(*csi), tot_csrows); - chi = edac_align_ptr(&ptr, sizeof(*chi), tot_csrows * tot_channels); - dimm = edac_align_ptr(&ptr, sizeof(*dimm), tot_dimms); for (i = 0; i < n_layers; i++) { count *= layers[i].size; debugf4("%s: errcount layer %d size %d\n", __func__, i, count); @@ -264,6 +261,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, tot_dimms, per_rank ? "ranks" : "dimms", tot_csrows * tot_channels); + mci = kzalloc(size, GFP_KERNEL); if (mci == NULL) return NULL; @@ -272,9 +270,6 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, * rather than an imaginary chunk of memory located at address 0. */ layer = (struct edac_mc_layer *)(((char *)mci) + ((unsigned long)layer)); - csi = (struct csrow_info *)(((char *)mci) + ((unsigned long)csi)); - chi = (struct rank_info *)(((char *)mci) + ((unsigned long)chi)); - dimm = (struct dimm_info *)(((char *)mci) + ((unsigned long)dimm)); for (i = 0; i < n_layers; i++) { mci->ce_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ce_per_layer[i])); mci->ue_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ue_per_layer[i])); @@ -283,8 +278,6 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, /* setup index and various internal pointers */ mci->mc_idx = mc_num; - mci->csrows = csi; - mci->dimms = dimm; mci->tot_dimms = tot_dimms; mci->pvt_info = pvt; mci->n_layers = n_layers; @@ -295,39 +288,60 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, mci->mem_is_per_rank = per_rank; /* - * Fill the csrow struct + * Alocate and fill the csrow/channels structs */ + mci->csrows = kcalloc(sizeof(*mci->csrows), tot_csrows, GFP_KERNEL); + if (!mci->csrows) + goto error; for (row = 0; row < tot_csrows; row++) { - csr = &csi[row]; + csr = kzalloc(sizeof(**mci->csrows), GFP_KERNEL); + if (!csr) + goto error; + mci->csrows[row] = csr; csr->csrow_idx = row; csr->mci = mci; csr->nr_channels = tot_channels; - chp = &chi[row * tot_channels]; - csr->channels = chp; + csr->channels = kcalloc(sizeof(*csr->channels), tot_channels, + GFP_KERNEL); + if (!csr->channels) + goto error; for (chn = 0; chn < tot_channels; chn++) { - chan = &chp[chn]; + chan = kzalloc(sizeof(**csr->channels), GFP_KERNEL); + if (!chan) + goto error; + csr->channels[chn] = chan; chan->chan_idx = chn; chan->csrow = csr; } } /* - * Fill the dimm struct + * Allocate and fill the dimm structs */ + mci->dimms = kcalloc(sizeof(*mci->dimms), tot_dimms, GFP_KERNEL); + if (!mci->dimms) + goto error; + memset(&pos, 0, sizeof(pos)); row = 0; chn = 0; debugf4("%s: initializing %d %s\n", __func__, tot_dimms, per_rank ? "ranks" : "dimms"); for (i = 0; i < tot_dimms; i++) { - chan = &csi[row].channels[chn]; - dimm = EDAC_DIMM_PTR(layer, mci->dimms, n_layers, - pos[0], pos[1], pos[2]); + chan = mci->csrows[row]->channels[chn]; + off = EDAC_DIMM_OFF(layer, n_layers, pos[0], pos[1], pos[2]); + if (off < 0 || off >= tot_dimms) { + edac_mc_printk(mci, KERN_ERR, "EDAC core bug: EDAC_DIMM_OFF is trying to do an illegal data access\n"); + goto error; + } + + dimm = kzalloc(sizeof(**mci->dimms), GFP_KERNEL); + mci->dimms[off] = dimm; dimm->mci = mci; - debugf2("%s: %d: %s%zd (%d:%d:%d): row %d, chan %d\n", __func__, - i, per_rank ? "rank" : "dimm", (dimm - mci->dimms), + debugf2("%s: %d: %s%i (%d:%d:%d): row %d, chan %d\n", __func__, + i, per_rank ? "rank" : "dimm", off, pos[0], pos[1], pos[2], row, chn); /* @@ -381,6 +395,28 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, */ return mci; + +error: + if (mci->dimms) { + for (i = 0; i < tot_dimms; i++) + kfree(mci->dimms[i]); + kfree(mci->dimms); + } + if (mci->csrows) { + for (chn = 0; chn < tot_channels; chn++) { + csr = mci->csrows[chn]; + if (csr) { + for (chn = 0; chn < tot_channels; chn++) + kfree(csr->channels[chn]); + kfree(csr); + } + kfree(mci->csrows[i]); + } + kfree(mci->csrows); + } + kfree(mci); + + return NULL; } EXPORT_SYMBOL_GPL(edac_mc_alloc); @@ -393,10 +429,8 @@ void edac_mc_free(struct mem_ctl_info *mci) { debugf1("%s()\n", __func__); + /* the mci instance is freed here, when the sysfs object is dropped */ edac_unregister_sysfs(mci); - - /* free the mci instance memory here */ - kfree(mci); } EXPORT_SYMBOL_GPL(edac_mc_free); @@ -668,13 +702,12 @@ int edac_mc_add_mc(struct mem_ctl_info *mci) for (i = 0; i < mci->nr_csrows; i++) { int j; - edac_mc_dump_csrow(&mci->csrows[i]); - for (j = 0; j < mci->csrows[i].nr_channels; j++) - edac_mc_dump_channel(&mci->csrows[i]. - channels[j]); + edac_mc_dump_csrow(mci->csrows[i]); + for (j = 0; j < mci->csrows[i]->nr_channels; j++) + edac_mc_dump_channel(mci->csrows[i]->channels[j]); } for (i = 0; i < mci->tot_dimms; i++) - edac_mc_dump_dimm(&mci->dimms[i]); + edac_mc_dump_dimm(mci->dimms[i]); } #endif mutex_lock(&mem_ctls_mutex); @@ -793,17 +826,17 @@ static void edac_mc_scrub_block(unsigned long page, unsigned long offset, /* FIXME - should return -1 */ int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, unsigned long page) { - struct csrow_info *csrows = mci->csrows; + struct csrow_info **csrows = mci->csrows; int row, i, j, n; debugf1("MC%d: %s(): 0x%lx\n", mci->mc_idx, __func__, page); row = -1; for (i = 0; i < mci->nr_csrows; i++) { - struct csrow_info *csrow = &csrows[i]; + struct csrow_info *csrow = csrows[i]; n = 0; for (j = 0; j < csrow->nr_channels; j++) { - struct dimm_info *dimm = csrow->channels[j].dimm; + struct dimm_info *dimm = csrow->channels[j]->dimm; n += dimm->nr_pages; } if (n == 0) @@ -1062,7 +1095,7 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, p = label; *p = '\0'; for (i = 0; i < mci->tot_dimms; i++) { - struct dimm_info *dimm = &mci->dimms[i]; + struct dimm_info *dimm = mci->dimms[i]; if (top_layer >= 0 && top_layer != dimm->location[0]) continue; @@ -1120,13 +1153,13 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, strcpy(label, "unknown memory"); if (type == HW_EVENT_ERR_CORRECTED) { if (row >= 0) { - mci->csrows[row].ce_count++; + mci->csrows[row]->ce_count++; if (chan >= 0) - mci->csrows[row].channels[chan].ce_count++; + mci->csrows[row]->channels[chan]->ce_count++; } } else if (row >= 0) - mci->csrows[row].ue_count++; + mci->csrows[row]->ue_count++; } /* Fill the RAM location data */ diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 0f671907c90b..87b8d7d6385f 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -82,7 +82,7 @@ module_param_call(edac_mc_poll_msec, edac_set_poll_msec, param_get_int, &edac_mc_poll_msec, 0644); MODULE_PARM_DESC(edac_mc_poll_msec, "Polling period in milliseconds"); -static struct device mci_pdev; +static struct device *mci_pdev; /* * various constants for Memory Controllers @@ -181,7 +181,7 @@ static ssize_t csrow_size_show(struct device *dev, u32 nr_pages = 0; for (i = 0; i < csrow->nr_channels; i++) - nr_pages += csrow->channels[i].dimm->nr_pages; + nr_pages += csrow->channels[i]->dimm->nr_pages; return sprintf(data, "%u\n", PAGES_TO_MiB(nr_pages)); } @@ -190,7 +190,7 @@ static ssize_t csrow_mem_type_show(struct device *dev, { struct csrow_info *csrow = to_csrow(dev); - return sprintf(data, "%s\n", mem_types[csrow->channels[0].dimm->mtype]); + return sprintf(data, "%s\n", mem_types[csrow->channels[0]->dimm->mtype]); } static ssize_t csrow_dev_type_show(struct device *dev, @@ -198,7 +198,7 @@ static ssize_t csrow_dev_type_show(struct device *dev, { struct csrow_info *csrow = to_csrow(dev); - return sprintf(data, "%s\n", dev_types[csrow->channels[0].dimm->dtype]); + return sprintf(data, "%s\n", dev_types[csrow->channels[0]->dimm->dtype]); } static ssize_t csrow_edac_mode_show(struct device *dev, @@ -207,7 +207,7 @@ static ssize_t csrow_edac_mode_show(struct device *dev, { struct csrow_info *csrow = to_csrow(dev); - return sprintf(data, "%s\n", edac_caps[csrow->channels[0].dimm->edac_mode]); + return sprintf(data, "%s\n", edac_caps[csrow->channels[0]->dimm->edac_mode]); } /* show/store functions for DIMM Label attributes */ @@ -217,7 +217,7 @@ static ssize_t channel_dimm_label_show(struct device *dev, { struct csrow_info *csrow = to_csrow(dev); unsigned chan = to_channel(mattr); - struct rank_info *rank = &csrow->channels[chan]; + struct rank_info *rank = csrow->channels[chan]; /* if field has not been initialized, there is nothing to send */ if (!rank->dimm->label[0]) @@ -233,7 +233,7 @@ static ssize_t channel_dimm_label_store(struct device *dev, { struct csrow_info *csrow = to_csrow(dev); unsigned chan = to_channel(mattr); - struct rank_info *rank = &csrow->channels[chan]; + struct rank_info *rank = csrow->channels[chan]; ssize_t max_size = 0; @@ -250,7 +250,7 @@ static ssize_t channel_ce_count_show(struct device *dev, { struct csrow_info *csrow = to_csrow(dev); unsigned chan = to_channel(mattr); - struct rank_info *rank = &csrow->channels[chan]; + struct rank_info *rank = csrow->channels[chan]; return sprintf(data, "%u\n", rank->ce_count); } @@ -283,9 +283,12 @@ static const struct attribute_group *csrow_attr_groups[] = { NULL }; -static void csrow_attr_release(struct device *device) +static void csrow_attr_release(struct device *dev) { - debugf1("Releasing csrow device %s\n", dev_name(device)); + struct csrow_info *csrow = container_of(dev, struct csrow_info, dev); + + debugf1("Releasing csrow device %s\n", dev_name(dev)); + kfree(csrow); } static struct device_type csrow_attr_type = { @@ -352,7 +355,7 @@ static inline int nr_pages_per_csrow(struct csrow_info *csrow) int chan, nr_pages = 0; for (chan = 0; chan < csrow->nr_channels; chan++) - nr_pages += csrow->channels[chan].dimm->nr_pages; + nr_pages += csrow->channels[chan]->dimm->nr_pages; return nr_pages; } @@ -382,7 +385,7 @@ static int edac_create_csrow_object(struct mem_ctl_info *mci, for (chan = 0; chan < csrow->nr_channels; chan++) { /* Only expose populated DIMMs */ - if (!csrow->channels[chan].dimm->nr_pages) + if (!csrow->channels[chan]->dimm->nr_pages) continue; err = device_create_file(&csrow->dev, dynamic_csrow_dimm_attr[chan]); @@ -418,10 +421,10 @@ static int edac_create_csrow_objects(struct mem_ctl_info *mci) struct csrow_info *csrow; for (i = 0; i < mci->nr_csrows; i++) { - csrow = &mci->csrows[i]; + csrow = mci->csrows[i]; if (!nr_pages_per_csrow(csrow)) continue; - err = edac_create_csrow_object(mci, &mci->csrows[i], i); + err = edac_create_csrow_object(mci, mci->csrows[i], i); if (err < 0) goto error; } @@ -429,18 +432,18 @@ static int edac_create_csrow_objects(struct mem_ctl_info *mci) error: for (--i; i >= 0; i--) { - csrow = &mci->csrows[i]; + csrow = mci->csrows[i]; if (!nr_pages_per_csrow(csrow)) continue; for (chan = csrow->nr_channels - 1; chan >= 0; chan--) { - if (!csrow->channels[chan].dimm->nr_pages) + if (!csrow->channels[chan]->dimm->nr_pages) continue; device_remove_file(&csrow->dev, dynamic_csrow_dimm_attr[chan]); device_remove_file(&csrow->dev, dynamic_csrow_ce_count_attr[chan]); } - put_device(&mci->csrows[i].dev); + put_device(&mci->csrows[i]->dev); } return err; @@ -452,11 +455,11 @@ static void edac_delete_csrow_objects(struct mem_ctl_info *mci) struct csrow_info *csrow; for (i = mci->nr_csrows - 1; i >= 0; i--) { - csrow = &mci->csrows[i]; + csrow = mci->csrows[i]; if (!nr_pages_per_csrow(csrow)) continue; for (chan = csrow->nr_channels - 1; chan >= 0; chan--) { - if (!csrow->channels[chan].dimm->nr_pages) + if (!csrow->channels[chan]->dimm->nr_pages) continue; debugf1("Removing csrow %d channel %d sysfs nodes\n", i, chan); @@ -465,8 +468,8 @@ static void edac_delete_csrow_objects(struct mem_ctl_info *mci) device_remove_file(&csrow->dev, dynamic_csrow_ce_count_attr[chan]); } - put_device(&mci->csrows[i].dev); - device_del(&mci->csrows[i].dev); + put_device(&mci->csrows[i]->dev); + device_del(&mci->csrows[i]->dev); } } #endif @@ -585,9 +588,12 @@ static const struct attribute_group *dimm_attr_groups[] = { NULL }; -static void dimm_attr_release(struct device *device) +static void dimm_attr_release(struct device *dev) { - debugf1("Releasing dimm device %s\n", dev_name(device)); + struct dimm_info *dimm = container_of(dev, struct dimm_info, dev); + + debugf1("Releasing dimm device %s\n", dev_name(dev)); + kfree(dimm); } static struct device_type dimm_attr_type = { @@ -641,13 +647,13 @@ static ssize_t mci_reset_counters_store(struct device *dev, mci->ce_noinfo_count = 0; for (row = 0; row < mci->nr_csrows; row++) { - struct csrow_info *ri = &mci->csrows[row]; + struct csrow_info *ri = mci->csrows[row]; ri->ue_count = 0; ri->ce_count = 0; for (chan = 0; chan < ri->nr_channels; chan++) - ri->channels[chan].ce_count = 0; + ri->channels[chan]->ce_count = 0; } cnt = 1; @@ -779,10 +785,10 @@ static ssize_t mci_size_mb_show(struct device *dev, int total_pages = 0, csrow_idx, j; for (csrow_idx = 0; csrow_idx < mci->nr_csrows; csrow_idx++) { - struct csrow_info *csrow = &mci->csrows[csrow_idx]; + struct csrow_info *csrow = mci->csrows[csrow_idx]; for (j = 0; j < csrow->nr_channels; j++) { - struct dimm_info *dimm = csrow->channels[j].dimm; + struct dimm_info *dimm = csrow->channels[j]->dimm; total_pages += dimm->nr_pages; } @@ -889,9 +895,12 @@ static const struct attribute_group *mci_attr_groups[] = { NULL }; -static void mci_attr_release(struct device *device) +static void mci_attr_release(struct device *dev) { - debugf1("Releasing mci device %s\n", dev_name(device)); + struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev); + + debugf1("Releasing csrow device %s\n", dev_name(dev)); + kfree(mci); } static struct device_type mci_attr_type = { @@ -950,29 +959,28 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) { int i, err; - debugf0("%s() idx=%d\n", __func__, mci->mc_idx); + /* + * The memory controller needs its own bus, in order to avoid + * namespace conflicts at /sys/bus/edac. + */ + mci->bus.name = kasprintf(GFP_KERNEL, "mc%d", mci->mc_idx); + if (!mci->bus.name) + return -ENOMEM; + debugf0("creating bus %s\n",mci->bus.name); + err = bus_register(&mci->bus); + if (err < 0) + return err; /* get the /sys/devices/system/edac subsys reference */ - mci->dev.type = &mci_attr_type; device_initialize(&mci->dev); - mci->dev.parent = &mci_pdev; + mci->dev.parent = mci_pdev; mci->dev.bus = &mci->bus; dev_set_name(&mci->dev, "mc%d", mci->mc_idx); dev_set_drvdata(&mci->dev, mci); pm_runtime_forbid(&mci->dev); - /* - * The memory controller needs its own bus, in order to avoid - * namespace conflicts at /sys/bus/edac. - */ - debugf0("creating bus %s\n",mci->bus.name); - mci->bus.name = kstrdup(dev_name(&mci->dev), GFP_KERNEL); - err = bus_register(&mci->bus); - if (err < 0) - return err; - debugf0("%s(): creating device %s\n", __func__, dev_name(&mci->dev)); err = device_add(&mci->dev); @@ -986,7 +994,7 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) * Create the dimm/rank devices */ for (i = 0; i < mci->tot_dimms; i++) { - struct dimm_info *dimm = &mci->dimms[i]; + struct dimm_info *dimm = mci->dimms[i]; /* Only expose populated DIMMs */ if (dimm->nr_pages == 0) continue; @@ -1023,7 +1031,7 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) fail: for (i--; i >= 0; i--) { - struct dimm_info *dimm = &mci->dimms[i]; + struct dimm_info *dimm = mci->dimms[i]; if (dimm->nr_pages == 0) continue; put_device(&dimm->dev); @@ -1053,7 +1061,7 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) #endif for (i = 0; i < mci->tot_dimms; i++) { - struct dimm_info *dimm = &mci->dimms[i]; + struct dimm_info *dimm = mci->dimms[i]; if (dimm->nr_pages == 0) continue; debugf0("%s(): removing device %s\n", __func__, @@ -1072,9 +1080,15 @@ void edac_unregister_sysfs(struct mem_ctl_info *mci) kfree(mci->bus.name); } -static void mc_attr_release(struct device *device) +static void mc_attr_release(struct device *dev) { - debugf1("Releasing device %s\n", dev_name(device)); + /* + * There's no container structure here, as this is just the mci + * parent device, used to create the /sys/devices/mc sysfs node. + * So, there are no attributes on it. + */ + debugf1("Releasing device %s\n", dev_name(dev)); + kfree(dev); } static struct device_type mc_attr_type = { @@ -1095,21 +1109,25 @@ int __init edac_mc_sysfs_init(void) return -EINVAL; } - mci_pdev.bus = edac_subsys; - mci_pdev.type = &mc_attr_type; - device_initialize(&mci_pdev); - dev_set_name(&mci_pdev, "mc"); + mci_pdev = kzalloc(sizeof(*mci_pdev), GFP_KERNEL); + + mci_pdev->bus = edac_subsys; + mci_pdev->type = &mc_attr_type; + device_initialize(mci_pdev); + dev_set_name(mci_pdev, "mc"); - err = device_add(&mci_pdev); + err = device_add(mci_pdev); if (err < 0) return err; + debugf0("device %s created\n", dev_name(mci_pdev)); + return 0; } void __exit edac_mc_sysfs_exit(void) { - put_device(&mci_pdev); - device_del(&mci_pdev); + put_device(mci_pdev); + device_del(mci_pdev); edac_put_sysfs_subsys(); } diff --git a/drivers/edac/i3000_edac.c b/drivers/edac/i3000_edac.c index d1ebd9b9ad6f..812213da7f91 100644 --- a/drivers/edac/i3000_edac.c +++ b/drivers/edac/i3000_edac.c @@ -236,7 +236,7 @@ static int i3000_process_error_info(struct mem_ctl_info *mci, int row, multi_chan, channel; unsigned long pfn, offset; - multi_chan = mci->csrows[0].nr_channels - 1; + multi_chan = mci->csrows[0]->nr_channels - 1; if (!(info->errsts & I3000_ERRSTS_BITS)) return 0; @@ -393,7 +393,7 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx) for (last_cumul_size = i = 0; i < mci->nr_csrows; i++) { u8 value; u32 cumul_size; - struct csrow_info *csrow = &mci->csrows[i]; + struct csrow_info *csrow = mci->csrows[i]; value = drb[i]; cumul_size = value << (I3000_DRB_SHIFT - PAGE_SHIFT); @@ -410,7 +410,7 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx) last_cumul_size = cumul_size; for (j = 0; j < nr_channels; j++) { - struct dimm_info *dimm = csrow->channels[j].dimm; + struct dimm_info *dimm = csrow->channels[j]->dimm; dimm->nr_pages = nr_pages / nr_channels; dimm->grain = I3000_DEAP_GRAIN; diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c index 600a05df3759..c5f0fb31d5e0 100644 --- a/drivers/edac/i3200_edac.c +++ b/drivers/edac/i3200_edac.c @@ -379,7 +379,7 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx) */ for (i = 0; i < mci->nr_csrows; i++) { unsigned long nr_pages; - struct csrow_info *csrow = &mci->csrows[i]; + struct csrow_info *csrow = mci->csrows[i]; nr_pages = drb_to_nr_pages(drbs, stacked, i / I3200_RANKS_PER_CHANNEL, @@ -389,7 +389,7 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx) continue; for (j = 0; j < nr_channels; j++) { - struct dimm_info *dimm = csrow->channels[j].dimm; + struct dimm_info *dimm = csrow->channels[j]->dimm; dimm->nr_pages = nr_pages / nr_channels; dimm->grain = nr_pages << PAGE_SHIFT; diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c index ba60694437bd..0570cf3d2563 100644 --- a/drivers/edac/i5400_edac.c +++ b/drivers/edac/i5400_edac.c @@ -1203,8 +1203,8 @@ static int i5400_init_dimms(struct mem_ctl_info *mci) size_mb = pvt->dimm_info[slot][channel].megabytes; - debugf2("%s: dimm%zd (branch %d channel %d slot %d): %d.%03d GB\n", - __func__, dimm - mci->dimms, + debugf2("%s: dimm (branch %d channel %d slot %d): %d.%03d GB\n", + __func__, channel / 2, channel % 2, slot, size_mb / 1000, size_mb % 1000); @@ -1227,7 +1227,7 @@ static int i5400_init_dimms(struct mem_ctl_info *mci) * With such single-DIMM mode, the SDCC algorithm degrades to SECDEC+. */ if (ndimms == 1) - mci->dimms[0].edac_mode = EDAC_SECDED; + mci->dimms[0]->edac_mode = EDAC_SECDED; return (ndimms == 0); } diff --git a/drivers/edac/i82443bxgx_edac.c b/drivers/edac/i82443bxgx_edac.c index 65fd2e1eceb8..0f2751bf3ffe 100644 --- a/drivers/edac/i82443bxgx_edac.c +++ b/drivers/edac/i82443bxgx_edac.c @@ -197,8 +197,8 @@ static void i82443bxgx_init_csrows(struct mem_ctl_info *mci, pci_read_config_byte(pdev, I82443BXGX_DRAMC, &dramc); row_high_limit_last = 0; for (index = 0; index < mci->nr_csrows; index++) { - csrow = &mci->csrows[index]; - dimm = csrow->channels[0].dimm; + csrow = mci->csrows[index]; + dimm = csrow->channels[0]->dimm; pci_read_config_byte(pdev, I82443BXGX_DRB + index, &drbar); debugf1("MC%d: %s: %s() Row=%d DRB = %#0x\n", diff --git a/drivers/edac/i82860_edac.c b/drivers/edac/i82860_edac.c index 8f3350000942..06a3c8d26d19 100644 --- a/drivers/edac/i82860_edac.c +++ b/drivers/edac/i82860_edac.c @@ -116,7 +116,7 @@ static int i82860_process_error_info(struct mem_ctl_info *mci, info->eap >>= PAGE_SHIFT; row = edac_mc_find_csrow_by_page(mci, info->eap); - dimm = mci->csrows[row].channels[0].dimm; + dimm = mci->csrows[row]->channels[0]->dimm; if (info->errsts & 0x0002) edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, @@ -161,8 +161,8 @@ static void i82860_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev) * in all eight rows. */ for (index = 0; index < mci->nr_csrows; index++) { - csrow = &mci->csrows[index]; - dimm = csrow->channels[0].dimm; + csrow = mci->csrows[index]; + dimm = csrow->channels[0]->dimm; pci_read_config_word(pdev, I82860_GBA + index * 2, &value); cumul_size = (value & I82860_GBA_MASK) << diff --git a/drivers/edac/i82875p_edac.c b/drivers/edac/i82875p_edac.c index a47c6b25db31..97fd6b769c84 100644 --- a/drivers/edac/i82875p_edac.c +++ b/drivers/edac/i82875p_edac.c @@ -227,7 +227,7 @@ static int i82875p_process_error_info(struct mem_ctl_info *mci, { int row, multi_chan; - multi_chan = mci->csrows[0].nr_channels - 1; + multi_chan = mci->csrows[0]->nr_channels - 1; if (!(info->errsts & 0x0081)) return 0; @@ -367,7 +367,7 @@ static void i82875p_init_csrows(struct mem_ctl_info *mci, */ for (index = 0; index < mci->nr_csrows; index++) { - csrow = &mci->csrows[index]; + csrow = mci->csrows[index]; value = readb(ovrfl_window + I82875P_DRB + index); cumul_size = value << (I82875P_DRB_SHIFT - PAGE_SHIFT); @@ -382,7 +382,7 @@ static void i82875p_init_csrows(struct mem_ctl_info *mci, last_cumul_size = cumul_size; for (j = 0; j < nr_chans; j++) { - dimm = csrow->channels[j].dimm; + dimm = csrow->channels[j]->dimm; dimm->nr_pages = nr_pages / nr_chans; dimm->grain = 1 << 12; /* I82875P_EAP has 4KiB reolution */ diff --git a/drivers/edac/i82975x_edac.c b/drivers/edac/i82975x_edac.c index 8b26401efa19..4d239ab31e34 100644 --- a/drivers/edac/i82975x_edac.c +++ b/drivers/edac/i82975x_edac.c @@ -308,10 +308,10 @@ static int i82975x_process_error_info(struct mem_ctl_info *mci, (info->xeap & 1) ? 1 : 0, info->eap, (unsigned int) page); return 0; } - chan = (mci->csrows[row].nr_channels == 1) ? 0 : info->eap & 1; + chan = (mci->csrows[row]->nr_channels == 1) ? 0 : info->eap & 1; offst = info->eap & ((1 << PAGE_SHIFT) - - (1 << mci->csrows[row].channels[chan].dimm->grain)); + (1 << mci->csrows[row]->channels[chan]->dimm->grain)); if (info->errsts & 0x0002) edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, @@ -394,7 +394,7 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci, */ for (index = 0; index < mci->nr_csrows; index++) { - csrow = &mci->csrows[index]; + csrow = mci->csrows[index]; value = readb(mch_window + I82975X_DRB + index + ((index >= 4) ? 0x80 : 0)); @@ -421,10 +421,10 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci, */ dtype = i82975x_dram_type(mch_window, index); for (chan = 0; chan < csrow->nr_channels; chan++) { - dimm = mci->csrows[index].channels[chan].dimm; + dimm = mci->csrows[index]->channels[chan]->dimm; dimm->nr_pages = nr_pages / csrow->nr_channels; - strncpy(csrow->channels[chan].dimm->label, + strncpy(csrow->channels[chan]->dimm->label, labels[(index >> 1) + (chan * 2)], EDAC_MC_LABEL_LEN); dimm->grain = 1 << 7; /* 128Byte cache-line resolution */ diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c index d132dbbd9bd7..0db6f1e84656 100644 --- a/drivers/edac/mpc85xx_edac.c +++ b/drivers/edac/mpc85xx_edac.c @@ -825,7 +825,7 @@ static void mpc85xx_mc_check(struct mem_ctl_info *mci) pfn = err_addr >> PAGE_SHIFT; for (row_index = 0; row_index < mci->nr_csrows; row_index++) { - csrow = &mci->csrows[row_index]; + csrow = mci->csrows[row_index]; if ((pfn >= csrow->first_page) && (pfn <= csrow->last_page)) break; } @@ -945,8 +945,8 @@ static void __devinit mpc85xx_init_csrows(struct mem_ctl_info *mci) u32 start; u32 end; - csrow = &mci->csrows[index]; - dimm = csrow->channels[0].dimm; + csrow = mci->csrows[index]; + dimm = csrow->channels[0]->dimm; cs_bnds = in_be32(pdata->mc_vbase + MPC85XX_MC_CS_BNDS_0 + (index * MPC85XX_MC_CS_BNDS_OFS)); diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c index ff6b8e248e89..3a58ba9158db 100644 --- a/drivers/edac/mv64x60_edac.c +++ b/drivers/edac/mv64x60_edac.c @@ -670,8 +670,8 @@ static void mv64x60_init_csrows(struct mem_ctl_info *mci, ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_CONFIG); - csrow = &mci->csrows[0]; - dimm = csrow->channels[0].dimm; + csrow = mci->csrows[0]; + dimm = csrow->channels[0]->dimm; dimm->nr_pages = pdata->total_mem >> PAGE_SHIFT; dimm->grain = 8; diff --git a/drivers/edac/pasemi_edac.c b/drivers/edac/pasemi_edac.c index 92becaa8722a..44f73b00df01 100644 --- a/drivers/edac/pasemi_edac.c +++ b/drivers/edac/pasemi_edac.c @@ -111,14 +111,14 @@ static void pasemi_edac_process_error_info(struct mem_ctl_info *mci, u32 errsta) if (errsta & (MCDEBUG_ERRSTA_MBE_STATUS | MCDEBUG_ERRSTA_RFL_STATUS)) { edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, - mci->csrows[cs].first_page, 0, 0, + mci->csrows[cs]->first_page, 0, 0, cs, 0, -1, mci->ctl_name, "", NULL); } /* correctable/single-bit errors */ if (errsta & MCDEBUG_ERRSTA_SBE_STATUS) edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, - mci->csrows[cs].first_page, 0, 0, + mci->csrows[cs]->first_page, 0, 0, cs, 0, -1, mci->ctl_name, "", NULL); } @@ -141,8 +141,8 @@ static int pasemi_edac_init_csrows(struct mem_ctl_info *mci, int index; for (index = 0; index < mci->nr_csrows; index++) { - csrow = &mci->csrows[index]; - dimm = csrow->channels[0].dimm; + csrow = mci->csrows[index]; + dimm = csrow->channels[0]->dimm; pci_read_config_dword(pdev, MCDRAM_RANKCFG + (index * 12), diff --git a/drivers/edac/r82600_edac.c b/drivers/edac/r82600_edac.c index cf4ccbdba85d..445c9ff27b88 100644 --- a/drivers/edac/r82600_edac.c +++ b/drivers/edac/r82600_edac.c @@ -230,8 +230,8 @@ static void r82600_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, row_high_limit_last = 0; for (index = 0; index < mci->nr_csrows; index++) { - csrow = &mci->csrows[index]; - dimm = csrow->channels[0].dimm; + csrow = mci->csrows[index]; + dimm = csrow->channels[0]->dimm; /* find the DRAM Chip Select Base address and mask */ pci_read_config_byte(pdev, R82600_DRBA + index, &drbar); diff --git a/drivers/edac/tile_edac.c b/drivers/edac/tile_edac.c index 604bc4df653a..fc77f77fa065 100644 --- a/drivers/edac/tile_edac.c +++ b/drivers/edac/tile_edac.c @@ -84,10 +84,10 @@ static void tile_edac_check(struct mem_ctl_info *mci) */ static int __devinit tile_edac_init_csrows(struct mem_ctl_info *mci) { - struct csrow_info *csrow = &mci->csrows[0]; + struct csrow_info *csrow = mci->csrows[0]; struct tile_edac_priv *priv = mci->pvt_info; struct mshim_mem_info mem_info; - struct dimm_info *dimm = csrow->channels[0].dimm; + struct dimm_info *dimm = csrow->channels[0]->dimm; if (hv_dev_pread(priv->hv_devhdl, 0, (HV_VirtAddr)&mem_info, sizeof(struct mshim_mem_info), MSHIM_MEM_INFO_OFF) != diff --git a/drivers/edac/x38_edac.c b/drivers/edac/x38_edac.c index f9506f26e2bf..ae699be78b24 100644 --- a/drivers/edac/x38_edac.c +++ b/drivers/edac/x38_edac.c @@ -378,7 +378,7 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx) */ for (i = 0; i < mci->nr_csrows; i++) { unsigned long nr_pages; - struct csrow_info *csrow = &mci->csrows[i]; + struct csrow_info *csrow = mci->csrows[i]; nr_pages = drb_to_nr_pages(drbs, stacked, i / X38_RANKS_PER_CHANNEL, @@ -388,7 +388,7 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx) continue; for (j = 0; j < x38_channel_num; j++) { - struct dimm_info *dimm = csrow->channels[j].dimm; + struct dimm_info *dimm = csrow->channels[j]->dimm; dimm->nr_pages = nr_pages / x38_channel_num; dimm->grain = nr_pages << PAGE_SHIFT; diff --git a/include/linux/edac.h b/include/linux/edac.h index 64ae0c5cf62e..6677af853e30 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -412,23 +412,21 @@ struct edac_mc_layer { #define EDAC_MAX_LAYERS 3 /** - * EDAC_DIMM_PTR - Macro responsible to find a pointer inside a pointer array + * EDAC_DIMM_OFF - Macro responsible to get a pointer offset inside a pointer array * for the element given by [layer0,layer1,layer2] position * * @layers: a struct edac_mc_layer array, describing how many elements * were allocated for each layer - * @var: name of the var where we want to get the pointer - * (like mci->dimms) * @n_layers: Number of layers at the @layers array * @layer0: layer0 position * @layer1: layer1 position. Unused if n_layers < 2 * @layer2: layer2 position. Unused if n_layers < 3 * - * For 1 layer, this macro returns &var[layer0] + * For 1 layer, this macro returns &var[layer0] - &var * For 2 layers, this macro is similar to allocate a bi-dimensional array - * and to return "&var[layer0][layer1]" + * and to return "&var[layer0][layer1] - &var" * For 3 layers, this macro is similar to allocate a tri-dimensional array - * and to return "&var[layer0][layer1][layer2]" + * and to return "&var[layer0][layer1][layer2] - &var" * * A loop could be used here to make it more generic, but, as we only have * 3 layers, this is a little faster. @@ -436,17 +434,46 @@ struct edac_mc_layer { * a NULL is returned, causing an OOPS during the memory allocation routine, * with would point to the developer that he's doing something wrong. */ -#define EDAC_DIMM_PTR(layers, var, nlayers, layer0, layer1, layer2) ({ \ - typeof(var) __p; \ +#define EDAC_DIMM_OFF(layers, nlayers, layer0, layer1, layer2) ({ \ + int __i; \ if ((nlayers) == 1) \ - __p = &var[layer0]; \ + __i = layer0; \ else if ((nlayers) == 2) \ - __p = &var[(layer1) + ((layers[1]).size * (layer0))]; \ + __i = (layer1) + ((layers[1]).size * (layer0)); \ else if ((nlayers) == 3) \ - __p = &var[(layer2) + ((layers[2]).size * ((layer1) + \ - ((layers[1]).size * (layer0))))]; \ + __i = (layer2) + ((layers[2]).size * ((layer1) + \ + ((layers[1]).size * (layer0)))); \ else \ + __i = -EINVAL; \ + __i; \ +}) + +/** + * EDAC_DIMM_PTR - Macro responsible to get a pointer inside a pointer array + * for the element given by [layer0,layer1,layer2] position + * + * @layers: a struct edac_mc_layer array, describing how many elements + * were allocated for each layer + * @var: name of the var where we want to get the pointer + * (like mci->dimms) + * @n_layers: Number of layers at the @layers array + * @layer0: layer0 position + * @layer1: layer1 position. Unused if n_layers < 2 + * @layer2: layer2 position. Unused if n_layers < 3 + * + * For 1 layer, this macro returns &var[layer0] + * For 2 layers, this macro is similar to allocate a bi-dimensional array + * and to return "&var[layer0][layer1]" + * For 3 layers, this macro is similar to allocate a tri-dimensional array + * and to return "&var[layer0][layer1][layer2]" + */ +#define EDAC_DIMM_PTR(layers, var, nlayers, layer0, layer1, layer2) ({ \ + typeof(*var) __p; \ + int ___i = EDAC_DIMM_OFF(layers, nlayers, layer0, layer1, layer2); \ + if (___i < 0) \ __p = NULL; \ + else \ + __p = (var)[___i]; \ __p; \ }) @@ -486,8 +513,6 @@ struct dimm_info { * patches in this series will fix this issue. */ struct rank_info { - struct device dev; - int chan_idx; struct csrow_info *csrow; struct dimm_info *dimm; @@ -513,7 +538,7 @@ struct csrow_info { /* channel information for this csrow */ u32 nr_channels; - struct rank_info *channels; + struct rank_info **channels; }; /* @@ -572,7 +597,7 @@ struct mem_ctl_info { unsigned long (*ctl_page_to_phys) (struct mem_ctl_info * mci, unsigned long page); int mc_idx; - struct csrow_info *csrows; + struct csrow_info **csrows; unsigned nr_csrows, num_cschannel; /* @@ -592,7 +617,7 @@ struct mem_ctl_info { * DIMM info. Will eventually remove the entire csrows_info some day */ unsigned tot_dimms; - struct dimm_info *dimms; + struct dimm_info **dimms; /* * FIXME - what about controllers on other busses? - IDs must be -- cgit v1.2.3 From 109cdbc223f6e2d6c80f8371f22415b50c18a366 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 18 May 2012 16:52:19 -0600 Subject: PCI: remove pci_bus_find_ext_capability() (unused) pci_bus_find_ext_capability() is unused, and this patch removes it. Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 43 ------------------------------------------- include/linux/pci.h | 2 -- 2 files changed, 45 deletions(-) (limited to 'include') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c01..de9386da2eb2 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -329,49 +329,6 @@ int pci_find_ext_capability(struct pci_dev *dev, int cap) } EXPORT_SYMBOL_GPL(pci_find_ext_capability); -/** - * pci_bus_find_ext_capability - find an extended capability - * @bus: the PCI bus to query - * @devfn: PCI device to query - * @cap: capability code - * - * Like pci_find_ext_capability() but works for pci devices that do not have a - * pci_dev structure set up yet. - * - * Returns the address of the requested capability structure within the - * device's PCI configuration space or 0 in case the device does not - * support it. - */ -int pci_bus_find_ext_capability(struct pci_bus *bus, unsigned int devfn, - int cap) -{ - u32 header; - int ttl; - int pos = PCI_CFG_SPACE_SIZE; - - /* minimum 8 bytes per capability */ - ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8; - - if (!pci_bus_read_config_dword(bus, devfn, pos, &header)) - return 0; - if (header == 0xffffffff || header == 0) - return 0; - - while (ttl-- > 0) { - if (PCI_EXT_CAP_ID(header) == cap) - return pos; - - pos = PCI_EXT_CAP_NEXT(header); - if (pos < PCI_CFG_SPACE_SIZE) - break; - - if (!pci_bus_read_config_dword(bus, devfn, pos, &header)) - break; - } - - return 0; -} - static int __pci_find_next_ht_cap(struct pci_dev *dev, int pos, int ht_cap) { int rc, ttl = PCI_FIND_CAP_TTL; diff --git a/include/linux/pci.h b/include/linux/pci.h index d8c379dba6ad..2618ad996535 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -714,8 +714,6 @@ enum pci_lost_interrupt_reason pci_lost_interrupt(struct pci_dev *dev); int pci_find_capability(struct pci_dev *dev, int cap); int pci_find_next_capability(struct pci_dev *dev, u8 pos, int cap); int pci_find_ext_capability(struct pci_dev *dev, int cap); -int pci_bus_find_ext_capability(struct pci_bus *bus, unsigned int devfn, - int cap); int pci_find_ht_capability(struct pci_dev *dev, int ht_cap); int pci_find_next_ht_capability(struct pci_dev *dev, int pos, int ht_cap); struct pci_bus *pci_find_next_bus(const struct pci_bus *from); -- cgit v1.2.3 From a6e51c1e3425fe61040e9e770f514cfdf576a1f0 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Fri, 8 Jun 2012 12:22:46 +0200 Subject: staging: Delete if_strip.h Commit f80a3f62383bf673c310926d55142d51f118926d ("Staging: strip: delete the driver") left if_strip.h unused: nothing in the tree includes it anymore. It is still exported, but since nothing in the kernel uses struct MetricomAddress, that seems pointless. Delete this header too. Signed-off-by: Paul Bolle Signed-off-by: Greg Kroah-Hartman --- include/linux/Kbuild | 1 - include/linux/if_strip.h | 27 --------------------------- 2 files changed, 28 deletions(-) delete mode 100644 include/linux/if_strip.h (limited to 'include') diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 8760be30b375..9366142ec7e5 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -183,7 +183,6 @@ header-y += if_ppp.h header-y += if_pppol2tp.h header-y += if_pppox.h header-y += if_slip.h -header-y += if_strip.h header-y += if_team.h header-y += if_tun.h header-y += if_tunnel.h diff --git a/include/linux/if_strip.h b/include/linux/if_strip.h deleted file mode 100644 index 6526a6235832..000000000000 --- a/include/linux/if_strip.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * if_strip.h -- - * - * Definitions for the STRIP interface - * - * Copyright 1996 The Board of Trustees of The Leland Stanford - * Junior University. All Rights Reserved. - * - * Permission to use, copy, modify, and distribute this - * software and its documentation for any purpose and without - * fee is hereby granted, provided that the above copyright - * notice appear in all copies. Stanford University - * makes no representations about the suitability of this - * software for any purpose. It is provided "as is" without - * express or implied warranty. - */ - -#ifndef __LINUX_STRIP_H -#define __LINUX_STRIP_H - -#include - -typedef struct { - __u8 c[6]; -} MetricomAddress; - -#endif -- cgit v1.2.3 From afbaade3dc99838a0c39699bea175674f27322a1 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Fri, 8 Jun 2012 10:56:48 +0200 Subject: delete seven tty headers Commit 51c9d654c2def97827395a7fbfd0c6f865c26544 ("Staging: delete tty drivers") left seven headers unused: nothing in the tree includes them anymore. Two of those headers were still exported, but since nothing in the kernel actually uses the things those two headers provide, that seems pointless. Delete these seven tty headers too. Signed-off-by: Paul Bolle Cc: Arnd Bergmann Cc: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- include/linux/Kbuild | 2 - include/linux/cd1400.h | 292 ------------------------- include/linux/cdk.h | 486 ------------------------------------------ include/linux/comstats.h | 119 ----------- include/linux/istallion.h | 123 ----------- include/linux/sc26198.h | 533 ---------------------------------------------- include/linux/serial167.h | 157 -------------- include/linux/stallion.h | 147 ------------- 8 files changed, 1859 deletions(-) delete mode 100644 include/linux/cd1400.h delete mode 100644 include/linux/cdk.h delete mode 100644 include/linux/comstats.h delete mode 100644 include/linux/istallion.h delete mode 100644 include/linux/sc26198.h delete mode 100644 include/linux/serial167.h delete mode 100644 include/linux/stallion.h (limited to 'include') diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 8760be30b375..0a8bcb6b9e2f 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -84,7 +84,6 @@ header-y += capability.h header-y += capi.h header-y += cciss_defs.h header-y += cciss_ioctl.h -header-y += cdk.h header-y += cdrom.h header-y += cgroupstats.h header-y += chio.h @@ -93,7 +92,6 @@ header-y += cn_proc.h header-y += coda.h header-y += coda_psdev.h header-y += coff.h -header-y += comstats.h header-y += connector.h header-y += const.h header-y += cramfs_fs.h diff --git a/include/linux/cd1400.h b/include/linux/cd1400.h deleted file mode 100644 index 1dc3ab0523fd..000000000000 --- a/include/linux/cd1400.h +++ /dev/null @@ -1,292 +0,0 @@ -/*****************************************************************************/ - -/* - * cd1400.h -- cd1400 UART hardware info. - * - * Copyright (C) 1996-1998 Stallion Technologies - * Copyright (C) 1994-1996 Greg Ungerer. - * - * 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. - */ - -/*****************************************************************************/ -#ifndef _CD1400_H -#define _CD1400_H -/*****************************************************************************/ - -/* - * Define the number of async ports per cd1400 uart chip. - */ -#define CD1400_PORTS 4 - -/* - * Define the cd1400 uarts internal FIFO sizes. - */ -#define CD1400_TXFIFOSIZE 12 -#define CD1400_RXFIFOSIZE 12 - -/* - * Local RX FIFO thresh hold level. Also define the RTS thresh hold - * based on the RX thresh hold. - */ -#define FIFO_RXTHRESHOLD 6 -#define FIFO_RTSTHRESHOLD 7 - -/*****************************************************************************/ - -/* - * Define the cd1400 register addresses. These are all the valid - * registers with the cd1400. Some are global, some virtual, some - * per port. - */ -#define GFRCR 0x40 -#define CAR 0x68 -#define GCR 0x4b -#define SVRR 0x67 -#define RICR 0x44 -#define TICR 0x45 -#define MICR 0x46 -#define RIR 0x6b -#define TIR 0x6a -#define MIR 0x69 -#define PPR 0x7e - -#define RIVR 0x43 -#define TIVR 0x42 -#define MIVR 0x41 -#define TDR 0x63 -#define RDSR 0x62 -#define MISR 0x4c -#define EOSRR 0x60 - -#define LIVR 0x18 -#define CCR 0x05 -#define SRER 0x06 -#define COR1 0x08 -#define COR2 0x09 -#define COR3 0x0a -#define COR4 0x1e -#define COR5 0x1f -#define CCSR 0x0b -#define RDCR 0x0e -#define SCHR1 0x1a -#define SCHR2 0x1b -#define SCHR3 0x1c -#define SCHR4 0x1d -#define SCRL 0x22 -#define SCRH 0x23 -#define LNC 0x24 -#define MCOR1 0x15 -#define MCOR2 0x16 -#define RTPR 0x21 -#define MSVR1 0x6c -#define MSVR2 0x6d -#define PSVR 0x6f -#define RBPR 0x78 -#define RCOR 0x7c -#define TBPR 0x72 -#define TCOR 0x76 - -/*****************************************************************************/ - -/* - * Define the set of baud rate clock divisors. - */ -#define CD1400_CLK0 8 -#define CD1400_CLK1 32 -#define CD1400_CLK2 128 -#define CD1400_CLK3 512 -#define CD1400_CLK4 2048 - -#define CD1400_NUMCLKS 5 - -/*****************************************************************************/ - -/* - * Define the clock pre-scalar value to be a 5 ms clock. This should be - * OK for now. It would probably be better to make it 10 ms, but we - * can't fit that divisor into 8 bits! - */ -#define PPR_SCALAR 244 - -/*****************************************************************************/ - -/* - * Define values used to set character size options. - */ -#define COR1_CHL5 0x00 -#define COR1_CHL6 0x01 -#define COR1_CHL7 0x02 -#define COR1_CHL8 0x03 - -/* - * Define values used to set the number of stop bits. - */ -#define COR1_STOP1 0x00 -#define COR1_STOP15 0x04 -#define COR1_STOP2 0x08 - -/* - * Define values used to set the parity scheme in use. - */ -#define COR1_PARNONE 0x00 -#define COR1_PARFORCE 0x20 -#define COR1_PARENB 0x40 -#define COR1_PARIGNORE 0x10 - -#define COR1_PARODD 0x80 -#define COR1_PAREVEN 0x00 - -#define COR2_IXM 0x80 -#define COR2_TXIBE 0x40 -#define COR2_ETC 0x20 -#define COR2_LLM 0x10 -#define COR2_RLM 0x08 -#define COR2_RTSAO 0x04 -#define COR2_CTSAE 0x02 - -#define COR3_SCDRNG 0x80 -#define COR3_SCD34 0x40 -#define COR3_FCT 0x20 -#define COR3_SCD12 0x10 - -/* - * Define values used by COR4. - */ -#define COR4_BRKINT 0x08 -#define COR4_IGNBRK 0x18 - -/*****************************************************************************/ - -/* - * Define the modem control register values. - * Note that the actual hardware is a little different to the conventional - * pin names on the cd1400. - */ -#define MSVR1_DTR 0x01 -#define MSVR1_DSR 0x10 -#define MSVR1_RI 0x20 -#define MSVR1_CTS 0x40 -#define MSVR1_DCD 0x80 - -#define MSVR2_RTS 0x02 -#define MSVR2_DSR 0x10 -#define MSVR2_RI 0x20 -#define MSVR2_CTS 0x40 -#define MSVR2_DCD 0x80 - -#define MCOR1_DCD 0x80 -#define MCOR1_CTS 0x40 -#define MCOR1_RI 0x20 -#define MCOR1_DSR 0x10 - -#define MCOR2_DCD 0x80 -#define MCOR2_CTS 0x40 -#define MCOR2_RI 0x20 -#define MCOR2_DSR 0x10 - -/*****************************************************************************/ - -/* - * Define the bits used with the service (interrupt) enable register. - */ -#define SRER_NNDT 0x01 -#define SRER_TXEMPTY 0x02 -#define SRER_TXDATA 0x04 -#define SRER_RXDATA 0x10 -#define SRER_MODEM 0x80 - -/*****************************************************************************/ - -/* - * Define operational commands for the command register. - */ -#define CCR_RESET 0x80 -#define CCR_CORCHANGE 0x4e -#define CCR_SENDCH 0x20 -#define CCR_CHANCTRL 0x10 - -#define CCR_TXENABLE (CCR_CHANCTRL | 0x08) -#define CCR_TXDISABLE (CCR_CHANCTRL | 0x04) -#define CCR_RXENABLE (CCR_CHANCTRL | 0x02) -#define CCR_RXDISABLE (CCR_CHANCTRL | 0x01) - -#define CCR_SENDSCHR1 (CCR_SENDCH | 0x01) -#define CCR_SENDSCHR2 (CCR_SENDCH | 0x02) -#define CCR_SENDSCHR3 (CCR_SENDCH | 0x03) -#define CCR_SENDSCHR4 (CCR_SENDCH | 0x04) - -#define CCR_RESETCHAN (CCR_RESET | 0x00) -#define CCR_RESETFULL (CCR_RESET | 0x01) -#define CCR_TXFLUSHFIFO (CCR_RESET | 0x02) - -#define CCR_MAXWAIT 10000 - -/*****************************************************************************/ - -/* - * Define the valid acknowledgement types (for hw ack cycle). - */ -#define ACK_TYPMASK 0x07 -#define ACK_TYPTX 0x02 -#define ACK_TYPMDM 0x01 -#define ACK_TYPRXGOOD 0x03 -#define ACK_TYPRXBAD 0x07 - -#define SVRR_RX 0x01 -#define SVRR_TX 0x02 -#define SVRR_MDM 0x04 - -#define ST_OVERRUN 0x01 -#define ST_FRAMING 0x02 -#define ST_PARITY 0x04 -#define ST_BREAK 0x08 -#define ST_SCHAR1 0x10 -#define ST_SCHAR2 0x20 -#define ST_SCHAR3 0x30 -#define ST_SCHAR4 0x40 -#define ST_RANGE 0x70 -#define ST_SCHARMASK 0x70 -#define ST_TIMEOUT 0x80 - -#define MISR_DCD 0x80 -#define MISR_CTS 0x40 -#define MISR_RI 0x20 -#define MISR_DSR 0x10 - -/*****************************************************************************/ - -/* - * Defines for the CCSR status register. - */ -#define CCSR_RXENABLED 0x80 -#define CCSR_RXFLOWON 0x40 -#define CCSR_RXFLOWOFF 0x20 -#define CCSR_TXENABLED 0x08 -#define CCSR_TXFLOWON 0x04 -#define CCSR_TXFLOWOFF 0x02 - -/*****************************************************************************/ - -/* - * Define the embedded commands. - */ -#define ETC_CMD 0x00 -#define ETC_STARTBREAK 0x81 -#define ETC_DELAY 0x82 -#define ETC_STOPBREAK 0x83 - -/*****************************************************************************/ -#endif diff --git a/include/linux/cdk.h b/include/linux/cdk.h deleted file mode 100644 index 80093a8d4f64..000000000000 --- a/include/linux/cdk.h +++ /dev/null @@ -1,486 +0,0 @@ -/*****************************************************************************/ - -/* - * cdk.h -- CDK interface definitions. - * - * Copyright (C) 1996-1998 Stallion Technologies - * Copyright (C) 1994-1996 Greg Ungerer. - * - * 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. - */ - -/*****************************************************************************/ -#ifndef _CDK_H -#define _CDK_H -/*****************************************************************************/ - -#pragma pack(2) - -/* - * The following set of definitions is used to communicate with the - * shared memory interface of the Stallion intelligent multiport serial - * boards. The definitions in this file are taken directly from the - * document titled "Generic Stackable Interface, Downloader and - * Communications Development Kit". - */ - -/* - * Define the set of important shared memory addresses. These are - * required to initialize the board and get things started. All of these - * addresses are relative to the start of the shared memory. - */ -#define CDK_SIGADDR 0x200 -#define CDK_FEATADDR 0x280 -#define CDK_CDKADDR 0x300 -#define CDK_RDYADDR 0x262 - -#define CDK_ALIVEMARKER 13 - -/* - * On hardware power up the ROMs located on the EasyConnection 8/64 will - * fill out the following signature information into shared memory. This - * way the host system can quickly determine that the board is present - * and is operational. - */ -typedef struct cdkecpsig { - unsigned long magic; - unsigned short romver; - unsigned short cputype; - unsigned char panelid[8]; -} cdkecpsig_t; - -#define ECP_MAGIC 0x21504345 - -/* - * On hardware power up the ROMs located on the ONboard, Stallion and - * Brumbys will fill out the following signature information into shared - * memory. This way the host system can quickly determine that the board - * is present and is operational. - */ -typedef struct cdkonbsig { - unsigned short magic0; - unsigned short magic1; - unsigned short magic2; - unsigned short magic3; - unsigned short romver; - unsigned short memoff; - unsigned short memseg; - unsigned short amask0; - unsigned short pic; - unsigned short status; - unsigned short btype; - unsigned short clkticks; - unsigned short clkspeed; - unsigned short amask1; - unsigned short amask2; -} cdkonbsig_t; - -#define ONB_MAGIC0 0xf2a7 -#define ONB_MAGIC1 0xa149 -#define ONB_MAGIC2 0x6352 -#define ONB_MAGIC3 0xf121 - -/* - * Define the feature area structure. The feature area is the set of - * startup parameters used by the slave image when it starts executing. - * They allow for the specification of buffer sizes, debug trace, etc. - */ -typedef struct cdkfeature { - unsigned long debug; - unsigned long banner; - unsigned long etype; - unsigned long nrdevs; - unsigned long brdspec; - unsigned long txrqsize; - unsigned long rxrqsize; - unsigned long flags; -} cdkfeature_t; - -#define ETYP_DDK 0 -#define ETYP_CDK 1 - -/* - * Define the CDK header structure. This is the info that the slave - * environment sets up after it has been downloaded and started. It - * essentially provides a memory map for the shared memory interface. - */ -typedef struct cdkhdr { - unsigned short command; - unsigned short status; - unsigned short port; - unsigned short mode; - unsigned long cmd_buf[14]; - unsigned short alive_cnt; - unsigned short intrpt_mode; - unsigned char intrpt_id[8]; - unsigned char ver_release; - unsigned char ver_modification; - unsigned char ver_fix; - unsigned char deadman_restart; - unsigned short deadman; - unsigned short nrdevs; - unsigned long memp; - unsigned long hostp; - unsigned long slavep; - unsigned char hostreq; - unsigned char slavereq; - unsigned char cmd_reserved[30]; -} cdkhdr_t; - -#define MODE_DDK 0 -#define MODE_CDK 1 - -#define IMD_INTR 0x0 -#define IMD_PPINTR 0x1 -#define IMD_POLL 0xff - -/* - * Define the memory mapping structure. This structure is pointed to by - * the memp field in the stlcdkhdr struct. As many as these structures - * as required are laid out in shared memory to define how the rest of - * shared memory is divided up. There will be one for each port. - */ -typedef struct cdkmem { - unsigned short dtype; - unsigned long offset; -} cdkmem_t; - -#define TYP_UNDEFINED 0x0 -#define TYP_ASYNCTRL 0x1 -#define TYP_ASYNC 0x20 -#define TYP_PARALLEL 0x40 -#define TYP_SYNCX21 0x60 - -/*****************************************************************************/ - -/* - * Following is a set of defines and structures used to actually deal - * with the serial ports on the board. Firstly is the set of commands - * that can be applied to ports. - */ -#define ASYCMD (((unsigned long) 'a') << 8) - -#define A_NULL (ASYCMD | 0) -#define A_FLUSH (ASYCMD | 1) -#define A_BREAK (ASYCMD | 2) -#define A_GETPORT (ASYCMD | 3) -#define A_SETPORT (ASYCMD | 4) -#define A_SETPORTF (ASYCMD | 5) -#define A_SETPORTFTX (ASYCMD | 6) -#define A_SETPORTFRX (ASYCMD | 7) -#define A_GETSIGNALS (ASYCMD | 8) -#define A_SETSIGNALS (ASYCMD | 9) -#define A_SETSIGNALSF (ASYCMD | 10) -#define A_SETSIGNALSFTX (ASYCMD | 11) -#define A_SETSIGNALSFRX (ASYCMD | 12) -#define A_GETNOTIFY (ASYCMD | 13) -#define A_SETNOTIFY (ASYCMD | 14) -#define A_NOTIFY (ASYCMD | 15) -#define A_PORTCTRL (ASYCMD | 16) -#define A_GETSTATS (ASYCMD | 17) -#define A_RQSTATE (ASYCMD | 18) -#define A_FLOWSTATE (ASYCMD | 19) -#define A_CLEARSTATS (ASYCMD | 20) - -/* - * Define those arguments used for simple commands. - */ -#define FLUSHRX 0x1 -#define FLUSHTX 0x2 - -#define BREAKON -1 -#define BREAKOFF -2 - -/* - * Define the port setting structure, and all those defines that go along - * with it. Basically this structure defines the characteristics of this - * port: baud rate, chars, parity, input/output char cooking etc. - */ -typedef struct asyport { - unsigned long baudout; - unsigned long baudin; - unsigned long iflag; - unsigned long oflag; - unsigned long lflag; - unsigned long pflag; - unsigned long flow; - unsigned long spare1; - unsigned short vtime; - unsigned short vmin; - unsigned short txlo; - unsigned short txhi; - unsigned short rxlo; - unsigned short rxhi; - unsigned short rxhog; - unsigned short spare2; - unsigned char csize; - unsigned char stopbs; - unsigned char parity; - unsigned char stopin; - unsigned char startin; - unsigned char stopout; - unsigned char startout; - unsigned char parmark; - unsigned char brkmark; - unsigned char cc[11]; -} asyport_t; - -#define PT_STOP1 0x0 -#define PT_STOP15 0x1 -#define PT_STOP2 0x2 - -#define PT_NOPARITY 0x0 -#define PT_ODDPARITY 0x1 -#define PT_EVENPARITY 0x2 -#define PT_MARKPARITY 0x3 -#define PT_SPACEPARITY 0x4 - -#define F_NONE 0x0 -#define F_IXON 0x1 -#define F_IXOFF 0x2 -#define F_IXANY 0x4 -#define F_IOXANY 0x8 -#define F_RTSFLOW 0x10 -#define F_CTSFLOW 0x20 -#define F_DTRFLOW 0x40 -#define F_DCDFLOW 0x80 -#define F_DSROFLOW 0x100 -#define F_DSRIFLOW 0x200 - -#define FI_NORX 0x1 -#define FI_RAW 0x2 -#define FI_ISTRIP 0x4 -#define FI_UCLC 0x8 -#define FI_INLCR 0x10 -#define FI_ICRNL 0x20 -#define FI_IGNCR 0x40 -#define FI_IGNBREAK 0x80 -#define FI_DSCRDBREAK 0x100 -#define FI_1MARKBREAK 0x200 -#define FI_2MARKBREAK 0x400 -#define FI_XCHNGBREAK 0x800 -#define FI_IGNRXERRS 0x1000 -#define FI_DSCDRXERRS 0x2000 -#define FI_1MARKRXERRS 0x4000 -#define FI_2MARKRXERRS 0x8000 -#define FI_XCHNGRXERRS 0x10000 -#define FI_DSCRDNULL 0x20000 - -#define FO_OLCUC 0x1 -#define FO_ONLCR 0x2 -#define FO_OOCRNL 0x4 -#define FO_ONOCR 0x8 -#define FO_ONLRET 0x10 -#define FO_ONL 0x20 -#define FO_OBS 0x40 -#define FO_OVT 0x80 -#define FO_OFF 0x100 -#define FO_OTAB1 0x200 -#define FO_OTAB2 0x400 -#define FO_OTAB3 0x800 -#define FO_OCR1 0x1000 -#define FO_OCR2 0x2000 -#define FO_OCR3 0x4000 -#define FO_OFILL 0x8000 -#define FO_ODELL 0x10000 - -#define P_RTSLOCK 0x1 -#define P_CTSLOCK 0x2 -#define P_MAPRTS 0x4 -#define P_MAPCTS 0x8 -#define P_LOOPBACK 0x10 -#define P_DTRFOLLOW 0x20 -#define P_FAKEDCD 0x40 - -#define P_RXIMIN 0x10000 -#define P_RXITIME 0x20000 -#define P_RXTHOLD 0x40000 - -/* - * Define a structure to communicate serial port signal and data state - * information. - */ -typedef struct asysigs { - unsigned long data; - unsigned long signal; - unsigned long sigvalue; -} asysigs_t; - -#define DT_TXBUSY 0x1 -#define DT_TXEMPTY 0x2 -#define DT_TXLOW 0x4 -#define DT_TXHIGH 0x8 -#define DT_TXFULL 0x10 -#define DT_TXHOG 0x20 -#define DT_TXFLOWED 0x40 -#define DT_TXBREAK 0x80 - -#define DT_RXBUSY 0x100 -#define DT_RXEMPTY 0x200 -#define DT_RXLOW 0x400 -#define DT_RXHIGH 0x800 -#define DT_RXFULL 0x1000 -#define DT_RXHOG 0x2000 -#define DT_RXFLOWED 0x4000 -#define DT_RXBREAK 0x8000 - -#define SG_DTR 0x1 -#define SG_DCD 0x2 -#define SG_RTS 0x4 -#define SG_CTS 0x8 -#define SG_DSR 0x10 -#define SG_RI 0x20 - -/* - * Define the notification setting structure. This is used to tell the - * port what events we want to be informed about. Fields here use the - * same defines as for the asysigs structure above. - */ -typedef struct asynotify { - unsigned long ctrl; - unsigned long data; - unsigned long signal; - unsigned long sigvalue; -} asynotify_t; - -/* - * Define the port control structure. It is used to do fine grain - * control operations on the port. - */ -typedef struct { - unsigned long rxctrl; - unsigned long txctrl; - char rximdch; - char tximdch; - char spare1; - char spare2; -} asyctrl_t; - -#define CT_ENABLE 0x1 -#define CT_DISABLE 0x2 -#define CT_STOP 0x4 -#define CT_START 0x8 -#define CT_STARTFLOW 0x10 -#define CT_STOPFLOW 0x20 -#define CT_SENDCHR 0x40 - -/* - * Define the stats structure kept for each port. This is a useful set - * of data collected for each port on the slave. The A_GETSTATS command - * is used to retrieve this data from the slave. - */ -typedef struct asystats { - unsigned long opens; - unsigned long txchars; - unsigned long rxchars; - unsigned long txringq; - unsigned long rxringq; - unsigned long txmsgs; - unsigned long rxmsgs; - unsigned long txflushes; - unsigned long rxflushes; - unsigned long overruns; - unsigned long framing; - unsigned long parity; - unsigned long ringover; - unsigned long lost; - unsigned long rxstart; - unsigned long rxstop; - unsigned long txstart; - unsigned long txstop; - unsigned long dcdcnt; - unsigned long dtrcnt; - unsigned long ctscnt; - unsigned long rtscnt; - unsigned long dsrcnt; - unsigned long ricnt; - unsigned long txbreaks; - unsigned long rxbreaks; - unsigned long signals; - unsigned long state; - unsigned long hwid; -} asystats_t; - -/*****************************************************************************/ - -/* - * All command and control communication with a device on the slave is - * via a control block in shared memory. Each device has its own control - * block, defined by the following structure. The control block allows - * the host to open, close and control the device on the slave. - */ -typedef struct cdkctrl { - unsigned char open; - unsigned char close; - unsigned long openarg; - unsigned long closearg; - unsigned long cmd; - unsigned long status; - unsigned long args[32]; -} cdkctrl_t; - -/* - * Each device on the slave passes data to and from the host via a ring - * queue in shared memory. Define a ring queue structure to hold the - * vital information about each ring queue. Two ring queues will be - * allocated for each port, one for receive data and one for transmit - * data. - */ -typedef struct cdkasyrq { - unsigned long offset; - unsigned short size; - unsigned short head; - unsigned short tail; -} cdkasyrq_t; - -/* - * Each asynchronous port is defined in shared memory by the following - * structure. It contains a control block to command a device, and also - * the necessary data channel information as well. - */ -typedef struct cdkasy { - cdkctrl_t ctrl; - unsigned short notify; - asynotify_t changed; - unsigned short receive; - cdkasyrq_t rxq; - unsigned short transmit; - cdkasyrq_t txq; -} cdkasy_t; - -#pragma pack() - -/*****************************************************************************/ - -/* - * Define the set of ioctls used by the driver to do special things - * to the board. These include interrupting it, and initializing - * the driver after board startup and shutdown. - */ -#include - -#define STL_BINTR _IO('s',20) -#define STL_BSTART _IO('s',21) -#define STL_BSTOP _IO('s',22) -#define STL_BRESET _IO('s',23) - -/* - * Define a set of ioctl extensions, used to get at special stuff. - */ -#define STL_GETPFLAG _IO('s',80) -#define STL_SETPFLAG _IO('s',81) - -/*****************************************************************************/ -#endif diff --git a/include/linux/comstats.h b/include/linux/comstats.h deleted file mode 100644 index 3f5ea8e8026d..000000000000 --- a/include/linux/comstats.h +++ /dev/null @@ -1,119 +0,0 @@ -/*****************************************************************************/ - -/* - * comstats.h -- Serial Port Stats. - * - * Copyright (C) 1996-1998 Stallion Technologies - * Copyright (C) 1994-1996 Greg Ungerer. - * - * 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. - */ - -/*****************************************************************************/ -#ifndef _COMSTATS_H -#define _COMSTATS_H -/*****************************************************************************/ - -/* - * Serial port stats structure. The structure itself is UART - * independent, but some fields may be UART/driver specific (for - * example state). - */ - -typedef struct { - unsigned long brd; - unsigned long panel; - unsigned long port; - unsigned long hwid; - unsigned long type; - unsigned long txtotal; - unsigned long rxtotal; - unsigned long txbuffered; - unsigned long rxbuffered; - unsigned long rxoverrun; - unsigned long rxparity; - unsigned long rxframing; - unsigned long rxlost; - unsigned long txbreaks; - unsigned long rxbreaks; - unsigned long txxon; - unsigned long txxoff; - unsigned long rxxon; - unsigned long rxxoff; - unsigned long txctson; - unsigned long txctsoff; - unsigned long rxrtson; - unsigned long rxrtsoff; - unsigned long modem; - unsigned long state; - unsigned long flags; - unsigned long ttystate; - unsigned long cflags; - unsigned long iflags; - unsigned long oflags; - unsigned long lflags; - unsigned long signals; -} comstats_t; - - -/* - * Board stats structure. Returns useful info about the board. - */ - -#define COM_MAXPANELS 8 - -typedef struct { - unsigned long panel; - unsigned long type; - unsigned long hwid; - unsigned long nrports; -} companel_t; - -typedef struct { - unsigned long brd; - unsigned long type; - unsigned long hwid; - unsigned long state; - unsigned long ioaddr; - unsigned long ioaddr2; - unsigned long memaddr; - unsigned long irq; - unsigned long nrpanels; - unsigned long nrports; - companel_t panels[COM_MAXPANELS]; -} combrd_t; - - -/* - * Define the ioctl operations for stats stuff. - */ -#include - -#define COM_GETPORTSTATS _IO('c',30) -#define COM_CLRPORTSTATS _IO('c',31) -#define COM_GETBRDSTATS _IO('c',32) - - -/* - * Define the set of ioctls that give user level access to the - * private port, panel and board structures. The argument required - * will be driver dependent! - */ -#define COM_READPORT _IO('c',40) -#define COM_READBOARD _IO('c',41) -#define COM_READPANEL _IO('c',42) - -/*****************************************************************************/ -#endif diff --git a/include/linux/istallion.h b/include/linux/istallion.h deleted file mode 100644 index ad700a60c158..000000000000 --- a/include/linux/istallion.h +++ /dev/null @@ -1,123 +0,0 @@ -/*****************************************************************************/ - -/* - * istallion.h -- stallion intelligent multiport serial driver. - * - * Copyright (C) 1996-1998 Stallion Technologies - * Copyright (C) 1994-1996 Greg Ungerer. - * - * 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. - */ - -/*****************************************************************************/ -#ifndef _ISTALLION_H -#define _ISTALLION_H -/*****************************************************************************/ - -/* - * Define important driver constants here. - */ -#define STL_MAXBRDS 4 -#define STL_MAXPANELS 4 -#define STL_MAXPORTS 64 -#define STL_MAXCHANS (STL_MAXPORTS + 1) -#define STL_MAXDEVS (STL_MAXBRDS * STL_MAXPORTS) - - -/* - * Define a set of structures to hold all the board/panel/port info - * for our ports. These will be dynamically allocated as required at - * driver initialization time. - */ - -/* - * Port and board structures to hold status info about each object. - * The board structure contains pointers to structures for each port - * connected to it. Panels are not distinguished here, since - * communication with the slave board will always be on a per port - * basis. - */ -struct stliport { - unsigned long magic; - struct tty_port port; - unsigned int portnr; - unsigned int panelnr; - unsigned int brdnr; - unsigned long state; - unsigned int devnr; - int baud_base; - int custom_divisor; - int closing_wait; - int rc; - int argsize; - void *argp; - unsigned int rxmarkmsk; - wait_queue_head_t raw_wait; - struct asysigs asig; - unsigned long addr; - unsigned long rxoffset; - unsigned long txoffset; - unsigned long sigs; - unsigned long pflag; - unsigned int rxsize; - unsigned int txsize; - unsigned char reqbit; - unsigned char portidx; - unsigned char portbit; -}; - -/* - * Use a structure of function pointers to do board level operations. - * These include, enable/disable, paging shared memory, interrupting, etc. - */ -struct stlibrd { - unsigned long magic; - unsigned int brdnr; - unsigned int brdtype; - unsigned long state; - unsigned int nrpanels; - unsigned int nrports; - unsigned int nrdevs; - unsigned int iobase; - int iosize; - unsigned long memaddr; - void __iomem *membase; - unsigned long memsize; - int pagesize; - int hostoffset; - int slaveoffset; - int bitsize; - int enabval; - unsigned int panels[STL_MAXPANELS]; - int panelids[STL_MAXPANELS]; - void (*init)(struct stlibrd *brdp); - void (*enable)(struct stlibrd *brdp); - void (*reenable)(struct stlibrd *brdp); - void (*disable)(struct stlibrd *brdp); - void __iomem *(*getmemptr)(struct stlibrd *brdp, unsigned long offset, int line); - void (*intr)(struct stlibrd *brdp); - void (*reset)(struct stlibrd *brdp); - struct stliport *ports[STL_MAXPORTS]; -}; - - -/* - * Define MAGIC numbers used for above structures. - */ -#define STLI_PORTMAGIC 0xe671c7a1 -#define STLI_BOARDMAGIC 0x4bc6c825 - -/*****************************************************************************/ -#endif diff --git a/include/linux/sc26198.h b/include/linux/sc26198.h deleted file mode 100644 index 7ca35abad387..000000000000 --- a/include/linux/sc26198.h +++ /dev/null @@ -1,533 +0,0 @@ -/*****************************************************************************/ - -/* - * sc26198.h -- SC26198 UART hardware info. - * - * Copyright (C) 1995-1998 Stallion Technologies - * - * 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. - */ - -/*****************************************************************************/ -#ifndef _SC26198_H -#define _SC26198_H -/*****************************************************************************/ - -/* - * Define the number of async ports per sc26198 uart device. - */ -#define SC26198_PORTS 8 - -/* - * Baud rate timing clocks. All derived from a master 14.7456 MHz clock. - */ -#define SC26198_MASTERCLOCK 14745600L -#define SC26198_DCLK (SC26198_MASTERCLOCK) -#define SC26198_CCLK (SC26198_MASTERCLOCK / 2) -#define SC26198_BCLK (SC26198_MASTERCLOCK / 4) - -/* - * Define internal FIFO sizes for the 26198 ports. - */ -#define SC26198_TXFIFOSIZE 16 -#define SC26198_RXFIFOSIZE 16 - -/*****************************************************************************/ - -/* - * Global register definitions. These registers are global to each 26198 - * device, not specific ports on it. - */ -#define TSTR 0x0d -#define GCCR 0x0f -#define ICR 0x1b -#define WDTRCR 0x1d -#define IVR 0x1f -#define BRGTRUA 0x84 -#define GPOSR 0x87 -#define GPOC 0x8b -#define UCIR 0x8c -#define CIR 0x8c -#define BRGTRUB 0x8d -#define GRXFIFO 0x8e -#define GTXFIFO 0x8e -#define GCCR2 0x8f -#define BRGTRLA 0x94 -#define GPOR 0x97 -#define GPOD 0x9b -#define BRGTCR 0x9c -#define GICR 0x9c -#define BRGTRLB 0x9d -#define GIBCR 0x9d -#define GITR 0x9f - -/* - * Per port channel registers. These are the register offsets within - * the port address space, so need to have the port address (0 to 7) - * inserted in bit positions 4:6. - */ -#define MR0 0x00 -#define MR1 0x01 -#define IOPCR 0x02 -#define BCRBRK 0x03 -#define BCRCOS 0x04 -#define BCRX 0x06 -#define BCRA 0x07 -#define XONCR 0x08 -#define XOFFCR 0x09 -#define ARCR 0x0a -#define RXCSR 0x0c -#define TXCSR 0x0e -#define MR2 0x80 -#define SR 0x81 -#define SCCR 0x81 -#define ISR 0x82 -#define IMR 0x82 -#define TXFIFO 0x83 -#define RXFIFO 0x83 -#define IPR 0x84 -#define IOPIOR 0x85 -#define XISR 0x86 - -/* - * For any given port calculate the address to use to access a specified - * register. This is only used for unusual access, mostly this is done - * through the assembler access routines. - */ -#define SC26198_PORTREG(port,reg) ((((port) & 0x07) << 4) | (reg)) - -/*****************************************************************************/ - -/* - * Global configuration control register bit definitions. - */ -#define GCCR_NOACK 0x00 -#define GCCR_IVRACK 0x02 -#define GCCR_IVRCHANACK 0x04 -#define GCCR_IVRTYPCHANACK 0x06 -#define GCCR_ASYNCCYCLE 0x00 -#define GCCR_SYNCCYCLE 0x40 - -/*****************************************************************************/ - -/* - * Mode register 0 bit definitions. - */ -#define MR0_ADDRNONE 0x00 -#define MR0_AUTOWAKE 0x01 -#define MR0_AUTODOZE 0x02 -#define MR0_AUTOWAKEDOZE 0x03 -#define MR0_SWFNONE 0x00 -#define MR0_SWFTX 0x04 -#define MR0_SWFRX 0x08 -#define MR0_SWFRXTX 0x0c -#define MR0_TXMASK 0x30 -#define MR0_TXEMPTY 0x00 -#define MR0_TXHIGH 0x10 -#define MR0_TXHALF 0x20 -#define MR0_TXRDY 0x00 -#define MR0_ADDRNT 0x00 -#define MR0_ADDRT 0x40 -#define MR0_SWFNT 0x00 -#define MR0_SWFT 0x80 - -/* - * Mode register 1 bit definitions. - */ -#define MR1_CS5 0x00 -#define MR1_CS6 0x01 -#define MR1_CS7 0x02 -#define MR1_CS8 0x03 -#define MR1_PAREVEN 0x00 -#define MR1_PARODD 0x04 -#define MR1_PARENB 0x00 -#define MR1_PARFORCE 0x08 -#define MR1_PARNONE 0x10 -#define MR1_PARSPECIAL 0x18 -#define MR1_ERRCHAR 0x00 -#define MR1_ERRBLOCK 0x20 -#define MR1_ISRUNMASKED 0x00 -#define MR1_ISRMASKED 0x40 -#define MR1_AUTORTS 0x80 - -/* - * Mode register 2 bit definitions. - */ -#define MR2_STOP1 0x00 -#define MR2_STOP15 0x01 -#define MR2_STOP2 0x02 -#define MR2_STOP916 0x03 -#define MR2_RXFIFORDY 0x00 -#define MR2_RXFIFOHALF 0x04 -#define MR2_RXFIFOHIGH 0x08 -#define MR2_RXFIFOFULL 0x0c -#define MR2_AUTOCTS 0x10 -#define MR2_TXRTS 0x20 -#define MR2_MODENORM 0x00 -#define MR2_MODEAUTOECHO 0x40 -#define MR2_MODELOOP 0x80 -#define MR2_MODEREMECHO 0xc0 - -/*****************************************************************************/ - -/* - * Baud Rate Generator (BRG) selector values. - */ -#define BRG_50 0x00 -#define BRG_75 0x01 -#define BRG_150 0x02 -#define BRG_200 0x03 -#define BRG_300 0x04 -#define BRG_450 0x05 -#define BRG_600 0x06 -#define BRG_900 0x07 -#define BRG_1200 0x08 -#define BRG_1800 0x09 -#define BRG_2400 0x0a -#define BRG_3600 0x0b -#define BRG_4800 0x0c -#define BRG_7200 0x0d -#define BRG_9600 0x0e -#define BRG_14400 0x0f -#define BRG_19200 0x10 -#define BRG_28200 0x11 -#define BRG_38400 0x12 -#define BRG_57600 0x13 -#define BRG_115200 0x14 -#define BRG_230400 0x15 -#define BRG_GIN0 0x16 -#define BRG_GIN1 0x17 -#define BRG_CT0 0x18 -#define BRG_CT1 0x19 -#define BRG_RX2TX316 0x1b -#define BRG_RX2TX31 0x1c - -#define SC26198_MAXBAUD 921600 - -/*****************************************************************************/ - -/* - * Command register command definitions. - */ -#define CR_NULL 0x04 -#define CR_ADDRNORMAL 0x0c -#define CR_RXRESET 0x14 -#define CR_TXRESET 0x1c -#define CR_CLEARRXERR 0x24 -#define CR_BREAKRESET 0x2c -#define CR_TXSTARTBREAK 0x34 -#define CR_TXSTOPBREAK 0x3c -#define CR_RTSON 0x44 -#define CR_RTSOFF 0x4c -#define CR_ADDRINIT 0x5c -#define CR_RXERRBLOCK 0x6c -#define CR_TXSENDXON 0x84 -#define CR_TXSENDXOFF 0x8c -#define CR_GANGXONSET 0x94 -#define CR_GANGXOFFSET 0x9c -#define CR_GANGXONINIT 0xa4 -#define CR_GANGXOFFINIT 0xac -#define CR_HOSTXON 0xb4 -#define CR_HOSTXOFF 0xbc -#define CR_CANCELXOFF 0xc4 -#define CR_ADDRRESET 0xdc -#define CR_RESETALLPORTS 0xf4 -#define CR_RESETALL 0xfc - -#define CR_RXENABLE 0x01 -#define CR_TXENABLE 0x02 - -/*****************************************************************************/ - -/* - * Channel status register. - */ -#define SR_RXRDY 0x01 -#define SR_RXFULL 0x02 -#define SR_TXRDY 0x04 -#define SR_TXEMPTY 0x08 -#define SR_RXOVERRUN 0x10 -#define SR_RXPARITY 0x20 -#define SR_RXFRAMING 0x40 -#define SR_RXBREAK 0x80 - -#define SR_RXERRS (SR_RXPARITY | SR_RXFRAMING | SR_RXOVERRUN) - -/*****************************************************************************/ - -/* - * Interrupt status register and interrupt mask register bit definitions. - */ -#define IR_TXRDY 0x01 -#define IR_RXRDY 0x02 -#define IR_RXBREAK 0x04 -#define IR_XONXOFF 0x10 -#define IR_ADDRRECOG 0x20 -#define IR_RXWATCHDOG 0x40 -#define IR_IOPORT 0x80 - -/*****************************************************************************/ - -/* - * Interrupt vector register field definitions. - */ -#define IVR_CHANMASK 0x07 -#define IVR_TYPEMASK 0x18 -#define IVR_CONSTMASK 0xc0 - -#define IVR_RXDATA 0x10 -#define IVR_RXBADDATA 0x18 -#define IVR_TXDATA 0x08 -#define IVR_OTHER 0x00 - -/*****************************************************************************/ - -/* - * BRG timer control register bit definitions. - */ -#define BRGCTCR_DISABCLK0 0x00 -#define BRGCTCR_ENABCLK0 0x08 -#define BRGCTCR_DISABCLK1 0x00 -#define BRGCTCR_ENABCLK1 0x80 - -#define BRGCTCR_0SCLK16 0x00 -#define BRGCTCR_0SCLK32 0x01 -#define BRGCTCR_0SCLK64 0x02 -#define BRGCTCR_0SCLK128 0x03 -#define BRGCTCR_0X1 0x04 -#define BRGCTCR_0X12 0x05 -#define BRGCTCR_0IO1A 0x06 -#define BRGCTCR_0GIN0 0x07 - -#define BRGCTCR_1SCLK16 0x00 -#define BRGCTCR_1SCLK32 0x10 -#define BRGCTCR_1SCLK64 0x20 -#define BRGCTCR_1SCLK128 0x30 -#define BRGCTCR_1X1 0x40 -#define BRGCTCR_1X12 0x50 -#define BRGCTCR_1IO1B 0x60 -#define BRGCTCR_1GIN1 0x70 - -/*****************************************************************************/ - -/* - * Watch dog timer enable register. - */ -#define WDTRCR_ENABALL 0xff - -/*****************************************************************************/ - -/* - * XON/XOFF interrupt status register. - */ -#define XISR_TXCHARMASK 0x03 -#define XISR_TXCHARNORMAL 0x00 -#define XISR_TXWAIT 0x01 -#define XISR_TXXOFFPEND 0x02 -#define XISR_TXXONPEND 0x03 - -#define XISR_TXFLOWMASK 0x0c -#define XISR_TXNORMAL 0x00 -#define XISR_TXSTOPPEND 0x04 -#define XISR_TXSTARTED 0x08 -#define XISR_TXSTOPPED 0x0c - -#define XISR_RXFLOWMASK 0x30 -#define XISR_RXFLOWNONE 0x00 -#define XISR_RXXONSENT 0x10 -#define XISR_RXXOFFSENT 0x20 - -#define XISR_RXXONGOT 0x40 -#define XISR_RXXOFFGOT 0x80 - -/*****************************************************************************/ - -/* - * Current interrupt register. - */ -#define CIR_TYPEMASK 0xc0 -#define CIR_TYPEOTHER 0x00 -#define CIR_TYPETX 0x40 -#define CIR_TYPERXGOOD 0x80 -#define CIR_TYPERXBAD 0xc0 - -#define CIR_RXDATA 0x80 -#define CIR_RXBADDATA 0x40 -#define CIR_TXDATA 0x40 - -#define CIR_CHANMASK 0x07 -#define CIR_CNTMASK 0x38 - -#define CIR_SUBTYPEMASK 0x38 -#define CIR_SUBNONE 0x00 -#define CIR_SUBCOS 0x08 -#define CIR_SUBADDR 0x10 -#define CIR_SUBXONXOFF 0x18 -#define CIR_SUBBREAK 0x28 - -/*****************************************************************************/ - -/* - * Global interrupting channel register. - */ -#define GICR_CHANMASK 0x07 - -/*****************************************************************************/ - -/* - * Global interrupting byte count register. - */ -#define GICR_COUNTMASK 0x0f - -/*****************************************************************************/ - -/* - * Global interrupting type register. - */ -#define GITR_RXMASK 0xc0 -#define GITR_RXNONE 0x00 -#define GITR_RXBADDATA 0x80 -#define GITR_RXGOODDATA 0xc0 -#define GITR_TXDATA 0x20 - -#define GITR_SUBTYPEMASK 0x07 -#define GITR_SUBNONE 0x00 -#define GITR_SUBCOS 0x01 -#define GITR_SUBADDR 0x02 -#define GITR_SUBXONXOFF 0x03 -#define GITR_SUBBREAK 0x05 - -/*****************************************************************************/ - -/* - * Input port change register. - */ -#define IPR_CTS 0x01 -#define IPR_DTR 0x02 -#define IPR_RTS 0x04 -#define IPR_DCD 0x08 -#define IPR_CTSCHANGE 0x10 -#define IPR_DTRCHANGE 0x20 -#define IPR_RTSCHANGE 0x40 -#define IPR_DCDCHANGE 0x80 - -#define IPR_CHANGEMASK 0xf0 - -/*****************************************************************************/ - -/* - * IO port interrupt and output register. - */ -#define IOPR_CTS 0x01 -#define IOPR_DTR 0x02 -#define IOPR_RTS 0x04 -#define IOPR_DCD 0x08 -#define IOPR_CTSCOS 0x10 -#define IOPR_DTRCOS 0x20 -#define IOPR_RTSCOS 0x40 -#define IOPR_DCDCOS 0x80 - -/*****************************************************************************/ - -/* - * IO port configuration register. - */ -#define IOPCR_SETCTS 0x00 -#define IOPCR_SETDTR 0x04 -#define IOPCR_SETRTS 0x10 -#define IOPCR_SETDCD 0x00 - -#define IOPCR_SETSIGS (IOPCR_SETRTS | IOPCR_SETRTS | IOPCR_SETDTR | IOPCR_SETDCD) - -/*****************************************************************************/ - -/* - * General purpose output select register. - */ -#define GPORS_TXC1XA 0x08 -#define GPORS_TXC16XA 0x09 -#define GPORS_RXC16XA 0x0a -#define GPORS_TXC16XB 0x0b -#define GPORS_GPOR3 0x0c -#define GPORS_GPOR2 0x0d -#define GPORS_GPOR1 0x0e -#define GPORS_GPOR0 0x0f - -/*****************************************************************************/ - -/* - * General purpose output register. - */ -#define GPOR_0 0x01 -#define GPOR_1 0x02 -#define GPOR_2 0x04 -#define GPOR_3 0x08 - -/*****************************************************************************/ - -/* - * General purpose output clock register. - */ -#define GPORC_0NONE 0x00 -#define GPORC_0GIN0 0x01 -#define GPORC_0GIN1 0x02 -#define GPORC_0IO3A 0x02 - -#define GPORC_1NONE 0x00 -#define GPORC_1GIN0 0x04 -#define GPORC_1GIN1 0x08 -#define GPORC_1IO3C 0x0c - -#define GPORC_2NONE 0x00 -#define GPORC_2GIN0 0x10 -#define GPORC_2GIN1 0x20 -#define GPORC_2IO3E 0x20 - -#define GPORC_3NONE 0x00 -#define GPORC_3GIN0 0x40 -#define GPORC_3GIN1 0x80 -#define GPORC_3IO3G 0xc0 - -/*****************************************************************************/ - -/* - * General purpose output data register. - */ -#define GPOD_0MASK 0x03 -#define GPOD_0SET1 0x00 -#define GPOD_0SET0 0x01 -#define GPOD_0SETR0 0x02 -#define GPOD_0SETIO3B 0x03 - -#define GPOD_1MASK 0x0c -#define GPOD_1SET1 0x00 -#define GPOD_1SET0 0x04 -#define GPOD_1SETR0 0x08 -#define GPOD_1SETIO3D 0x0c - -#define GPOD_2MASK 0x30 -#define GPOD_2SET1 0x00 -#define GPOD_2SET0 0x10 -#define GPOD_2SETR0 0x20 -#define GPOD_2SETIO3F 0x30 - -#define GPOD_3MASK 0xc0 -#define GPOD_3SET1 0x00 -#define GPOD_3SET0 0x40 -#define GPOD_3SETR0 0x80 -#define GPOD_3SETIO3H 0xc0 - -/*****************************************************************************/ -#endif diff --git a/include/linux/serial167.h b/include/linux/serial167.h deleted file mode 100644 index 59c81b708562..000000000000 --- a/include/linux/serial167.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - * serial167.h - * - * Richard Hirst [richard@sleepie.demon.co.uk] - * - * Based on cyclades.h - */ - -struct cyclades_monitor { - unsigned long int_count; - unsigned long char_count; - unsigned long char_max; - unsigned long char_last; -}; - -/* - * This is our internal structure for each serial port's state. - * - * Many fields are paralleled by the structure used by the serial_struct - * structure. - * - * For definitions of the flags field, see tty.h - */ - -struct cyclades_port { - int magic; - int type; - int card; - int line; - int flags; /* defined in tty.h */ - struct tty_struct *tty; - int read_status_mask; - int timeout; - int xmit_fifo_size; - int cor1,cor2,cor3,cor4,cor5,cor6,cor7; - int tbpr,tco,rbpr,rco; - int ignore_status_mask; - int close_delay; - int IER; /* Interrupt Enable Register */ - unsigned long last_active; - int count; /* # of fd on device */ - int x_char; /* to be pushed out ASAP */ - int x_break; - int blocked_open; /* # of blocked opens */ - unsigned char *xmit_buf; - int xmit_head; - int xmit_tail; - int xmit_cnt; - int default_threshold; - int default_timeout; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; - struct cyclades_monitor mon; -}; - -#define CYCLADES_MAGIC 0x4359 - -#define CYGETMON 0x435901 -#define CYGETTHRESH 0x435902 -#define CYSETTHRESH 0x435903 -#define CYGETDEFTHRESH 0x435904 -#define CYSETDEFTHRESH 0x435905 -#define CYGETTIMEOUT 0x435906 -#define CYSETTIMEOUT 0x435907 -#define CYGETDEFTIMEOUT 0x435908 -#define CYSETDEFTIMEOUT 0x435909 - -#define CyMaxChipsPerCard 1 - -/**** cd2401 registers ****/ - -#define CyGFRCR (0x81) -#define CyCCR (0x13) -#define CyCLR_CHAN (0x40) -#define CyINIT_CHAN (0x20) -#define CyCHIP_RESET (0x10) -#define CyENB_XMTR (0x08) -#define CyDIS_XMTR (0x04) -#define CyENB_RCVR (0x02) -#define CyDIS_RCVR (0x01) -#define CyCAR (0xee) -#define CyIER (0x11) -#define CyMdmCh (0x80) -#define CyRxExc (0x20) -#define CyRxData (0x08) -#define CyTxMpty (0x02) -#define CyTxRdy (0x01) -#define CyLICR (0x26) -#define CyRISR (0x89) -#define CyTIMEOUT (0x80) -#define CySPECHAR (0x70) -#define CyOVERRUN (0x08) -#define CyPARITY (0x04) -#define CyFRAME (0x02) -#define CyBREAK (0x01) -#define CyREOIR (0x84) -#define CyTEOIR (0x85) -#define CyMEOIR (0x86) -#define CyNOTRANS (0x08) -#define CyRFOC (0x30) -#define CyRDR (0xf8) -#define CyTDR (0xf8) -#define CyMISR (0x8b) -#define CyRISR (0x89) -#define CyTISR (0x8a) -#define CyMSVR1 (0xde) -#define CyMSVR2 (0xdf) -#define CyDSR (0x80) -#define CyDCD (0x40) -#define CyCTS (0x20) -#define CyDTR (0x02) -#define CyRTS (0x01) -#define CyRTPRL (0x25) -#define CyRTPRH (0x24) -#define CyCOR1 (0x10) -#define CyPARITY_NONE (0x00) -#define CyPARITY_E (0x40) -#define CyPARITY_O (0xC0) -#define Cy_5_BITS (0x04) -#define Cy_6_BITS (0x05) -#define Cy_7_BITS (0x06) -#define Cy_8_BITS (0x07) -#define CyCOR2 (0x17) -#define CyETC (0x20) -#define CyCtsAE (0x02) -#define CyCOR3 (0x16) -#define Cy_1_STOP (0x02) -#define Cy_2_STOP (0x04) -#define CyCOR4 (0x15) -#define CyREC_FIFO (0x0F) /* Receive FIFO threshold */ -#define CyCOR5 (0x14) -#define CyCOR6 (0x18) -#define CyCOR7 (0x07) -#define CyRBPR (0xcb) -#define CyRCOR (0xc8) -#define CyTBPR (0xc3) -#define CyTCOR (0xc0) -#define CySCHR1 (0x1f) -#define CySCHR2 (0x1e) -#define CyTPR (0xda) -#define CyPILR1 (0xe3) -#define CyPILR2 (0xe0) -#define CyPILR3 (0xe1) -#define CyCMR (0x1b) -#define CyASYNC (0x02) -#define CyLICR (0x26) -#define CyLIVR (0x09) -#define CySCRL (0x23) -#define CySCRH (0x22) -#define CyTFTC (0x80) - - -/* max number of chars in the FIFO */ - -#define CyMAX_CHAR_FIFO 12 - -/***************************************************************************/ diff --git a/include/linux/stallion.h b/include/linux/stallion.h deleted file mode 100644 index 336af33c6ea4..000000000000 --- a/include/linux/stallion.h +++ /dev/null @@ -1,147 +0,0 @@ -/*****************************************************************************/ - -/* - * stallion.h -- stallion multiport serial driver. - * - * Copyright (C) 1996-1998 Stallion Technologies - * Copyright (C) 1994-1996 Greg Ungerer. - * - * 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. - */ - -/*****************************************************************************/ -#ifndef _STALLION_H -#define _STALLION_H -/*****************************************************************************/ - -/* - * Define important driver constants here. - */ -#define STL_MAXBRDS 4 -#define STL_MAXPANELS 4 -#define STL_MAXBANKS 8 -#define STL_PORTSPERPANEL 16 -#define STL_MAXPORTS 64 -#define STL_MAXDEVS (STL_MAXBRDS * STL_MAXPORTS) - - -/* - * Define a set of structures to hold all the board/panel/port info - * for our ports. These will be dynamically allocated as required. - */ - -/* - * Define a ring queue structure for each port. This will hold the - * TX data waiting to be output. Characters are fed into this buffer - * from the line discipline (or even direct from user space!) and - * then fed into the UARTs during interrupts. Will use a classic ring - * queue here for this. The good thing about this type of ring queue - * is that the head and tail pointers can be updated without interrupt - * protection - since "write" code only needs to change the head, and - * interrupt code only needs to change the tail. - */ -struct stlrq { - char *buf; - char *head; - char *tail; -}; - -/* - * Port, panel and board structures to hold status info about each. - * The board structure contains pointers to structures for each panel - * connected to it, and in turn each panel structure contains pointers - * for each port structure for each port on that panel. Note that - * the port structure also contains the board and panel number that it - * is associated with, this makes it (fairly) easy to get back to the - * board/panel info for a port. - */ -struct stlport { - unsigned long magic; - struct tty_port port; - unsigned int portnr; - unsigned int panelnr; - unsigned int brdnr; - int ioaddr; - int uartaddr; - unsigned int pagenr; - unsigned long istate; - int baud_base; - int custom_divisor; - int close_delay; - int closing_wait; - int openwaitcnt; - int brklen; - unsigned int sigs; - unsigned int rxignoremsk; - unsigned int rxmarkmsk; - unsigned int imr; - unsigned int crenable; - unsigned long clk; - unsigned long hwid; - void *uartp; - comstats_t stats; - struct stlrq tx; -}; - -struct stlpanel { - unsigned long magic; - unsigned int panelnr; - unsigned int brdnr; - unsigned int pagenr; - unsigned int nrports; - int iobase; - void *uartp; - void (*isr)(struct stlpanel *panelp, unsigned int iobase); - unsigned int hwid; - unsigned int ackmask; - struct stlport *ports[STL_PORTSPERPANEL]; -}; - -struct stlbrd { - unsigned long magic; - unsigned int brdnr; - unsigned int brdtype; - unsigned int state; - unsigned int nrpanels; - unsigned int nrports; - unsigned int nrbnks; - int irq; - int irqtype; - int (*isr)(struct stlbrd *brdp); - unsigned int ioaddr1; - unsigned int ioaddr2; - unsigned int iosize1; - unsigned int iosize2; - unsigned int iostatus; - unsigned int ioctrl; - unsigned int ioctrlval; - unsigned int hwid; - unsigned long clk; - unsigned int bnkpageaddr[STL_MAXBANKS]; - unsigned int bnkstataddr[STL_MAXBANKS]; - struct stlpanel *bnk2panel[STL_MAXBANKS]; - struct stlpanel *panels[STL_MAXPANELS]; -}; - - -/* - * Define MAGIC numbers used for above structures. - */ -#define STL_PORTMAGIC 0x5a7182c9 -#define STL_PANELMAGIC 0x7ef621a1 -#define STL_BOARDMAGIC 0xa2267f52 - -/*****************************************************************************/ -#endif -- cgit v1.2.3 From 4a055c9c9e1b9c6ea677a3f8187e70339ff47358 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Fri, 8 Jun 2012 11:37:00 +0200 Subject: Delete generic_serial.h Commit bb2a97e9ccd525dd9c3326988e8c676d15d3e12a ("Staging: delete generic_serial drivers") left generic_serial.h unused: nothing in the tree includes it anymore. It is still exported, but since nothing in the kernel uses the named constants this header provides, that seems pointless. Delete this header too. Signed-off-by: Paul Bolle Cc: Arnd Bergmann Cc: Alan Cox Cc: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- include/linux/Kbuild | 1 - include/linux/generic_serial.h | 35 ----------------------------------- 2 files changed, 36 deletions(-) delete mode 100644 include/linux/generic_serial.h (limited to 'include') diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 0a8bcb6b9e2f..cf41085e3331 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -138,7 +138,6 @@ header-y += fuse.h header-y += futex.h header-y += gameport.h header-y += gen_stats.h -header-y += generic_serial.h header-y += genetlink.h header-y += gfs2_ondisk.h header-y += gigaset_dev.h diff --git a/include/linux/generic_serial.h b/include/linux/generic_serial.h deleted file mode 100644 index 79b3eb37243a..000000000000 --- a/include/linux/generic_serial.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * generic_serial.h - * - * Copyright (C) 1998 R.E.Wolff@BitWizard.nl - * - * written for the SX serial driver. - * - * Version 0.1 -- December, 1998. - */ - -#ifndef GENERIC_SERIAL_H -#define GENERIC_SERIAL_H - -#warning Use of this header is deprecated. -#warning Since nobody sets the constants defined here for you, you should not, in any case, use them. Including the header is thus pointless. - -/* Flags */ -/* Warning: serial.h defines some ASYNC_ flags, they say they are "only" - used in serial.c, but they are also used in all other serial drivers. - Make sure they don't clash with these here... */ -#define GS_TX_INTEN 0x00800000 -#define GS_RX_INTEN 0x00400000 -#define GS_ACTIVE 0x00200000 - -#define GS_TYPE_NORMAL 1 - -#define GS_DEBUG_FLUSH 0x00000001 -#define GS_DEBUG_BTR 0x00000002 -#define GS_DEBUG_TERMIOS 0x00000004 -#define GS_DEBUG_STUFF 0x00000008 -#define GS_DEBUG_CLOSE 0x00000010 -#define GS_DEBUG_FLOW 0x00000020 -#define GS_DEBUG_WRITE 0x00000040 - -#endif -- cgit v1.2.3 From 12ea6cad1c7d046e21decc18b0e2170c6794dc51 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 11 Jun 2012 05:26:55 +0000 Subject: PCI: add PCI DMA source ID quirk DMA transactions are tagged with the source ID of the device making the request. Occasionally hardware screws this up and uses the source ID of a different device (often the wrong function number of a multifunction device). A specific Ricoh multifunction device is a prime example of this problem and included in this patch. Given a pci_dev, this function returns the pci_dev to use as the source ID for DMA. When hardware works correctly, this returns the input device. For the components of the Ricoh multifunction device, it returns the pci_dev for function 0. This will be used by IOMMU drivers for determining the boundaries of IOMMU groups as multiple devices using the same source ID must be contained within the same group. This can also be used by existing streaming DMA paths for the same purpose. [bhelgaas: fold in pci_dev_get() for !CONFIG_PCI] Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/quirks.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 8 ++++++++ 2 files changed, 59 insertions(+) (limited to 'include') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 2a7521677541..acd3956b44bd 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3179,3 +3179,54 @@ int pci_dev_specific_reset(struct pci_dev *dev, int probe) return -ENOTTY; } + +static struct pci_dev *pci_func_0_dma_source(struct pci_dev *dev) +{ + if (!PCI_FUNC(dev->devfn)) + return pci_dev_get(dev); + + return pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); +} + +static const struct pci_dev_dma_source { + u16 vendor; + u16 device; + struct pci_dev *(*dma_source)(struct pci_dev *dev); +} pci_dev_dma_source[] = { + /* + * https://bugzilla.redhat.com/show_bug.cgi?id=605888 + * + * Some Ricoh devices use the function 0 source ID for DMA on + * other functions of a multifunction device. The DMA devices + * is therefore function 0, which will have implications of the + * iommu grouping of these devices. + */ + { PCI_VENDOR_ID_RICOH, 0xe822, pci_func_0_dma_source }, + { PCI_VENDOR_ID_RICOH, 0xe230, pci_func_0_dma_source }, + { PCI_VENDOR_ID_RICOH, 0xe832, pci_func_0_dma_source }, + { PCI_VENDOR_ID_RICOH, 0xe476, pci_func_0_dma_source }, + { 0 } +}; + +/* + * IOMMUs with isolation capabilities need to be programmed with the + * correct source ID of a device. In most cases, the source ID matches + * the device doing the DMA, but sometimes hardware is broken and will + * tag the DMA as being sourced from a different device. This function + * allows that translation. Note that the reference count of the + * returned device is incremented on all paths. + */ +struct pci_dev *pci_get_dma_source(struct pci_dev *dev) +{ + const struct pci_dev_dma_source *i; + + for (i = pci_dev_dma_source; i->dma_source; i++) { + if ((i->vendor == dev->vendor || + i->vendor == (u16)PCI_ANY_ID) && + (i->device == dev->device || + i->device == (u16)PCI_ANY_ID)) + return i->dma_source(dev); + } + + return pci_dev_get(dev); +} diff --git a/include/linux/pci.h b/include/linux/pci.h index d8c379dba6ad..39983be7b25b 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1332,6 +1332,9 @@ static inline struct pci_dev *pci_get_bus_and_slot(unsigned int bus, static inline int pci_domain_nr(struct pci_bus *bus) { return 0; } +static inline struct pci_dev *pci_dev_get(struct pci_dev *dev) +{ return NULL; } + #define dev_is_pci(d) (false) #define dev_is_pf(d) (false) #define dev_num_vf(d) (0) @@ -1486,9 +1489,14 @@ enum pci_fixup_pass { #ifdef CONFIG_PCI_QUIRKS void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev); +struct pci_dev *pci_get_dma_source(struct pci_dev *dev); #else static inline void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) {} +static inline struct pci_dev *pci_get_dma_source(struct pci_dev *dev) +{ + return pci_dev_get(dev); +} #endif void __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen); -- cgit v1.2.3 From c32823f82b42abc1f08b365085862fd1d57c0b61 Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Fri, 1 Jun 2012 15:16:25 -0600 Subject: PCI: make pci_ltr_supported() static The PCI Express Latency Tolerance Reporting (LTR) feature's pci_ltr_supported() routine is currently only used within drivers/pci/pci.c so make it static. Acked-by: Donald Dutile Signed-off-by: Myron Stowe Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 3 +-- include/linux/pci.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c01..847e0c35cdb7 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2169,7 +2169,7 @@ EXPORT_SYMBOL(pci_disable_obff); * RETURNS: * True if @dev supports latency tolerance reporting, false otherwise. */ -bool pci_ltr_supported(struct pci_dev *dev) +static bool pci_ltr_supported(struct pci_dev *dev) { int pos; u32 cap; @@ -2185,7 +2185,6 @@ bool pci_ltr_supported(struct pci_dev *dev) return cap & PCI_EXP_DEVCAP2_LTR; } -EXPORT_SYMBOL(pci_ltr_supported); /** * pci_enable_ltr - enable latency tolerance reporting diff --git a/include/linux/pci.h b/include/linux/pci.h index d8c379dba6ad..b2bec26b7f0a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -875,7 +875,6 @@ enum pci_obff_signal_type { int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type); void pci_disable_obff(struct pci_dev *dev); -bool pci_ltr_supported(struct pci_dev *dev); int pci_enable_ltr(struct pci_dev *dev); void pci_disable_ltr(struct pci_dev *dev); int pci_set_ltr(struct pci_dev *dev, int snoop_lat_ns, int nosnoop_lat_ns); -- cgit v1.2.3 From c463b8cb9350cf1230cefe467a1cf279140a5437 Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Fri, 1 Jun 2012 15:16:37 -0600 Subject: PCI: add pci_pcie_cap2() check for PCIe feature capabilities >= v2 This patch resolves potential issues when accessing PCI Express Capability structures. The makeup of the capability varies substantially between v1 and v2: Version 1 of the PCI Express Capability (defined by PCI Express 1.0 and 1.1 base) neither requires the endpoint to implement the entire PCIe capability structure nor specifies default values of registers that are not implemented by the device. Version 2 of the PCI Express Capability (defined by PCIe 1.1 Capability Structure Expansion ECN, PCIe 2.0, 2.1, and 3.0) added additional registers to the structure and requires all registers to be either implemented or hardwired to 0. Due to the differences in the capability structures, code dealing with capability features must be careful not to access the additional registers introduced with v2 unless the device is specifically known to be a v2 capable device. Otherwise, attempts to access non-existant registers will occur. This is a subtle issue that is hard to track down when it occurs (and it has - see commit 864d296cf94). To try and help mitigate such occurrences, this patch introduces pci_pcie_cap2() which is similar to pci_pcie_cap() but also checks that the PCIe capability version is >= 2. pci_pcie_cap2() should be used for qualifying PCIe capability features introduced after v1. Suggested by Don Dutile. Acked-by: Donald Dutile Signed-off-by: Myron Stowe Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 65 +++++++++++++++++++++++++++++++++++++----------- include/linux/pci_regs.h | 6 +++++ 2 files changed, 56 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 766bb13bb0a3..985df63aa59f 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -277,6 +277,38 @@ int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap) return pos; } +/** + * pci_pcie_cap2 - query for devices' PCI_CAP_ID_EXP v2 capability structure + * @dev: PCI device to check + * + * Like pci_pcie_cap() but also checks that the PCIe capability version is + * >= 2. Note that v1 capability structures could be sparse in that not + * all register fields were required. v2 requires the entire structure to + * be present size wise, while still allowing for non-implemented registers + * to exist but they must be hardwired to 0. + * + * Due to the differences in the versions of capability structures, one + * must be careful not to try and access non-existant registers that may + * exist in early versions - v1 - of Express devices. + * + * Returns the offset of the PCIe capability structure as long as the + * capability version is >= 2; otherwise 0 is returned. + */ +static int pci_pcie_cap2(struct pci_dev *dev) +{ + u16 flags; + int pos; + + pos = pci_pcie_cap(dev); + if (pos) { + pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &flags); + if ((flags & PCI_EXP_FLAGS_VERS) < 2) + pos = 0; + } + + return pos; +} + /** * pci_find_ext_capability - Find an extended capability * @dev: PCI device to query @@ -1983,7 +2015,7 @@ void pci_enable_ari(struct pci_dev *dev) { int pos; u32 cap; - u16 flags, ctrl; + u16 ctrl; struct pci_dev *bridge; if (pcie_ari_disabled || !pci_is_pcie(dev) || dev->devfn) @@ -1997,15 +2029,11 @@ void pci_enable_ari(struct pci_dev *dev) if (!bridge) return; - pos = pci_pcie_cap(bridge); + /* ARI is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(bridge); if (!pos) return; - /* ARI is a PCIe v2 feature */ - pci_read_config_word(bridge, pos + PCI_EXP_FLAGS, &flags); - if ((flags & PCI_EXP_FLAGS_VERS) < 2) - return; - pci_read_config_dword(bridge, pos + PCI_EXP_DEVCAP2, &cap); if (!(cap & PCI_EXP_DEVCAP2_ARI)) return; @@ -2018,7 +2046,7 @@ void pci_enable_ari(struct pci_dev *dev) } /** - * pci_enable_ido - enable ID-based ordering on a device + * pci_enable_ido - enable ID-based Ordering on a device * @dev: the PCI device * @type: which types of IDO to enable * @@ -2031,7 +2059,8 @@ void pci_enable_ido(struct pci_dev *dev, unsigned long type) int pos; u16 ctrl; - pos = pci_pcie_cap(dev); + /* ID-based Ordering is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return; @@ -2054,7 +2083,8 @@ void pci_disable_ido(struct pci_dev *dev, unsigned long type) int pos; u16 ctrl; - pos = pci_pcie_cap(dev); + /* ID-based Ordering is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return; @@ -2093,7 +2123,8 @@ int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type type) u16 ctrl; int ret; - pos = pci_pcie_cap(dev); + /* OBFF is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return -ENOTSUPP; @@ -2143,7 +2174,8 @@ void pci_disable_obff(struct pci_dev *dev) int pos; u16 ctrl; - pos = pci_pcie_cap(dev); + /* OBFF is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return; @@ -2165,7 +2197,8 @@ static bool pci_ltr_supported(struct pci_dev *dev) int pos; u32 cap; - pos = pci_pcie_cap(dev); + /* LTR is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return false; @@ -2193,7 +2226,8 @@ int pci_enable_ltr(struct pci_dev *dev) if (!pci_ltr_supported(dev)) return -ENOTSUPP; - pos = pci_pcie_cap(dev); + /* LTR is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return -ENOTSUPP; @@ -2228,7 +2262,8 @@ void pci_disable_ltr(struct pci_dev *dev) if (!pci_ltr_supported(dev)) return; - pos = pci_pcie_cap(dev); + /* LTR is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return; diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index 4b608f543412..e7642f5bb12a 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -507,6 +507,12 @@ #define PCI_EXP_RTSTA 32 /* Root Status */ #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. + */ #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 */ -- cgit v1.2.3 From f242e50eee1ec7692c4854d94e8cd543991cce71 Mon Sep 17 00:00:00 2001 From: Ola Lilja Date: Thu, 7 Jun 2012 14:00:46 +0200 Subject: mfd/ab8500: Move platform-data for ab8500-codec into mfd-driver The platform-data used by the Ux500 ASoC-driver is moved from the machine-driver context into the codec-driver context. This means adding the platform-data for 'ab8500-codec' into the main AB8500 platform-data. Signed-off-by: Ola Lilja Signed-off-by: Mark Brown --- arch/arm/mach-ux500/board-mop500.c | 14 +++++++++ include/linux/mfd/abx500/ab8500-codec.h | 52 +++++++++++++++++++++++++++++++++ include/linux/mfd/abx500/ab8500.h | 2 ++ 3 files changed, 68 insertions(+) create mode 100644 include/linux/mfd/abx500/ab8500-codec.h (limited to 'include') diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index 9c74ac545849..c8a8fde777bb 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -97,6 +98,18 @@ static struct ab8500_gpio_platform_data ab8500_gpio_pdata = { 0x7A, 0x00, 0x00}, }; +/* ab8500-codec */ +static struct ab8500_codec_platform_data ab8500_codec_pdata = { + .amics = { + .mic1_type = AMIC_TYPE_DIFFERENTIAL, + .mic2_type = AMIC_TYPE_DIFFERENTIAL, + .mic1a_micbias = AMIC_MICBIAS_VAMIC1, + .mic1b_micbias = AMIC_MICBIAS_VAMIC1, + .mic2_micbias = AMIC_MICBIAS_VAMIC2 + }, + .ear_cmv = EAR_CMV_0_95V +}; + static struct gpio_keys_button snowball_key_array[] = { { .gpio = 32, @@ -195,6 +208,7 @@ static struct ab8500_platform_data ab8500_platdata = { .regulator = ab8500_regulators, .num_regulator = ARRAY_SIZE(ab8500_regulators), .gpio = &ab8500_gpio_pdata, + .codec = &ab8500_codec_pdata, }; static struct resource ab8500_resources[] = { diff --git a/include/linux/mfd/abx500/ab8500-codec.h b/include/linux/mfd/abx500/ab8500-codec.h new file mode 100644 index 000000000000..dc6529202cdd --- /dev/null +++ b/include/linux/mfd/abx500/ab8500-codec.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) ST-Ericsson SA 2012 + * + * Author: Ola Lilja + * for ST-Ericsson. + * + * License terms: + * + * 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 AB8500_CORE_CODEC_H +#define AB8500_CORE_CODEC_H + +/* Mic-types */ +enum amic_type { + AMIC_TYPE_SINGLE_ENDED, + AMIC_TYPE_DIFFERENTIAL +}; + +/* Mic-biases */ +enum amic_micbias { + AMIC_MICBIAS_VAMIC1, + AMIC_MICBIAS_VAMIC2 +}; + +/* Bias-voltage */ +enum ear_cm_voltage { + EAR_CMV_0_95V, + EAR_CMV_1_10V, + EAR_CMV_1_27V, + EAR_CMV_1_58V +}; + +/* Analog microphone settings */ +struct amic_settings { + enum amic_type mic1_type; + enum amic_type mic2_type; + enum amic_micbias mic1a_micbias; + enum amic_micbias mic1b_micbias; + enum amic_micbias mic2_micbias; +}; + +/* Platform data structure for the audio-parts of the AB8500 */ +struct ab8500_codec_platform_data { + struct amic_settings amics; + enum ear_cm_voltage ear_cmv; +}; + +#endif diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h index 91dd3ef63e99..bc9b84b60ec6 100644 --- a/include/linux/mfd/abx500/ab8500.h +++ b/include/linux/mfd/abx500/ab8500.h @@ -266,6 +266,7 @@ struct ab8500 { struct regulator_reg_init; struct regulator_init_data; struct ab8500_gpio_platform_data; +struct ab8500_codec_platform_data; /** * struct ab8500_platform_data - AB8500 platform data @@ -284,6 +285,7 @@ struct ab8500_platform_data { int num_regulator; struct regulator_init_data *regulator; struct ab8500_gpio_platform_data *gpio; + struct ab8500_codec_platform_data *codec; }; extern int __devinit ab8500_init(struct ab8500 *ab8500, -- cgit v1.2.3 From ad805758c0eb25bce7b2e3b298d63dc62a1bc71c Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 11 Jun 2012 05:27:07 +0000 Subject: PCI: add ACS validation utility In a PCI environment, transactions aren't always required to reach the root bus before being re-routed. Intermediate switches between an endpoint and the root bus can redirect DMA back downstream before things like IOMMUs have a chance to intervene. Legacy PCI is always susceptible to this as it operates on a shared bus. PCIe added a new capability to describe and control this behavior, Access Control Services, or ACS. The utility function pci_acs_enabled() allows us to test the ACS capabilities of an individual devices against a set of flags while pci_acs_path_enabled() tests a complete path from a given downstream device up to the specified upstream device. We also include the ability to add device specific tests as it's likely we'll see devices that do not implement ACS, but want to indicate support for various capabilities in this space. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/quirks.c | 33 +++++++++++++++++++++++++ include/linux/pci.h | 10 +++++++- 3 files changed, 111 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c01..1ccf7d49f522 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2359,6 +2359,75 @@ void pci_enable_acs(struct pci_dev *dev) pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl); } +/** + * pci_acs_enabled - test ACS against required flags for a given device + * @pdev: device to test + * @acs_flags: required PCI ACS flags + * + * Return true if the device supports the provided flags. Automatically + * filters out flags that are not implemented on multifunction devices. + */ +bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags) +{ + int pos, ret; + u16 ctrl; + + ret = pci_dev_specific_acs_enabled(pdev, acs_flags); + if (ret >= 0) + return ret > 0; + + if (!pci_is_pcie(pdev)) + return false; + + /* Filter out flags not applicable to multifunction */ + if (pdev->multifunction) + acs_flags &= (PCI_ACS_RR | PCI_ACS_CR | + PCI_ACS_EC | PCI_ACS_DT); + + if (pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM || + pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT || + pdev->multifunction) { + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ACS); + if (!pos) + return false; + + pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl); + if ((ctrl & acs_flags) != acs_flags) + return false; + } + + return true; +} + +/** + * pci_acs_path_enable - test ACS flags from start to end in a hierarchy + * @start: starting downstream device + * @end: ending upstream device or NULL to search to the root bus + * @acs_flags: required flags + * + * Walk up a device tree from start to end testing PCI ACS support. If + * any step along the way does not support the required flags, return false. + */ +bool pci_acs_path_enabled(struct pci_dev *start, + struct pci_dev *end, u16 acs_flags) +{ + struct pci_dev *pdev, *parent = start; + + do { + pdev = parent; + + if (!pci_acs_enabled(pdev, acs_flags)) + return false; + + if (pci_is_root_bus(pdev->bus)) + return (end == NULL); + + parent = pdev->bus->self; + } while (pdev != end); + + return true; +} + /** * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge * @dev: the PCI device diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index acd3956b44bd..27e2c8f4ec73 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3230,3 +3230,36 @@ struct pci_dev *pci_get_dma_source(struct pci_dev *dev) return pci_dev_get(dev); } + +static const struct pci_dev_acs_enabled { + u16 vendor; + u16 device; + int (*acs_enabled)(struct pci_dev *dev, u16 acs_flags); +} pci_dev_acs_enabled[] = { + { 0 } +}; + +int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags) +{ + const struct pci_dev_acs_enabled *i; + int ret; + + /* + * Allow devices that do not expose standard PCIe ACS capabilities + * or control to indicate their support here. Multi-function express + * devices which do not allow internal peer-to-peer between functions, + * but do not implement PCIe ACS may wish to return true here. + */ + for (i = pci_dev_acs_enabled; i->acs_enabled; i++) { + if ((i->vendor == dev->vendor || + i->vendor == (u16)PCI_ANY_ID) && + (i->device == dev->device || + i->device == (u16)PCI_ANY_ID)) { + ret = i->acs_enabled(dev, acs_flags); + if (ret >= 0) + return ret; + } + } + + return -ENOTTY; +} diff --git a/include/linux/pci.h b/include/linux/pci.h index 39983be7b25b..dd7af0f37b3a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1490,6 +1490,7 @@ enum pci_fixup_pass { #ifdef CONFIG_PCI_QUIRKS void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev); struct pci_dev *pci_get_dma_source(struct pci_dev *dev); +int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags); #else static inline void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) {} @@ -1497,6 +1498,11 @@ static inline struct pci_dev *pci_get_dma_source(struct pci_dev *dev) { return pci_dev_get(dev); } +static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev, + u16 acs_flags) +{ + return -ENOTTY; +} #endif void __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen); @@ -1599,7 +1605,9 @@ static inline bool pci_is_pcie(struct pci_dev *dev) } void pci_request_acs(void); - +bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags); +bool pci_acs_path_enabled(struct pci_dev *start, + struct pci_dev *end, u16 acs_flags); #define PCI_VPD_LRDT 0x80 /* Large Resource Data Type */ #define PCI_VPD_LRDT_ID(x) (x | PCI_VPD_LRDT) -- cgit v1.2.3 From c63587d7f5b9db84e71daf5962dc0394eb657da2 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 11 Jun 2012 05:27:19 +0000 Subject: PCI: export pci_user functions for use by other drivers VFIO PCI support will make use of these for user-initiated PCI config accesses. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/access.c | 6 ++++-- drivers/pci/pci.h | 7 ------- include/linux/pci.h | 8 ++++++++ 3 files changed, 12 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 2a581642c237..ba91a7e17519 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -162,7 +162,8 @@ int pci_user_read_config_##size \ if (ret > 0) \ ret = -EINVAL; \ return ret; \ -} +} \ +EXPORT_SYMBOL_GPL(pci_user_read_config_##size); /* Returns 0 on success, negative values indicate error. */ #define PCI_USER_WRITE_CONFIG(size,type) \ @@ -181,7 +182,8 @@ int pci_user_write_config_##size \ if (ret > 0) \ ret = -EINVAL; \ return ret; \ -} +} \ +EXPORT_SYMBOL_GPL(pci_user_write_config_##size); PCI_USER_READ_CONFIG(byte, u8) PCI_USER_READ_CONFIG(word, u16) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index e4943479b234..f2dcc46bdece 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -86,13 +86,6 @@ static inline bool pci_is_bridge(struct pci_dev *pci_dev) return !!(pci_dev->subordinate); } -extern int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val); -extern int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val); -extern int pci_user_read_config_dword(struct pci_dev *dev, int where, u32 *val); -extern int pci_user_write_config_byte(struct pci_dev *dev, int where, u8 val); -extern int pci_user_write_config_word(struct pci_dev *dev, int where, u16 val); -extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val); - struct pci_vpd_ops { ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf); ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf); diff --git a/include/linux/pci.h b/include/linux/pci.h index dd7af0f37b3a..9d04599c6bd9 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -777,6 +777,14 @@ static inline int pci_write_config_dword(const struct pci_dev *dev, int where, return pci_bus_write_config_dword(dev->bus, dev->devfn, where, val); } +/* user-space driven config access */ +int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val); +int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val); +int pci_user_read_config_dword(struct pci_dev *dev, int where, u32 *val); +int pci_user_write_config_byte(struct pci_dev *dev, int where, u8 val); +int pci_user_write_config_word(struct pci_dev *dev, int where, u16 val); +int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val); + int __must_check pci_enable_device(struct pci_dev *dev); int __must_check pci_enable_device_io(struct pci_dev *dev); int __must_check pci_enable_device_mem(struct pci_dev *dev); -- cgit v1.2.3 From a6961651408afa9387d6df43c4a1dc4fd35dcb1b Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 11 Jun 2012 05:27:33 +0000 Subject: PCI: create common pcibios_err_to_errno For returning errors out to non-PCI code. Re-name xen's version. Acked-by: Konrad Rzeszutek Wilk Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/xen/xen-pciback/conf_space.c | 6 +++--- include/linux/pci.h | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/xen/xen-pciback/conf_space.c b/drivers/xen/xen-pciback/conf_space.c index 30d7be026c18..46ae0f9f02ad 100644 --- a/drivers/xen/xen-pciback/conf_space.c +++ b/drivers/xen/xen-pciback/conf_space.c @@ -124,7 +124,7 @@ static inline u32 merge_value(u32 val, u32 new_val, u32 new_val_mask, return val; } -static int pcibios_err_to_errno(int err) +static int xen_pcibios_err_to_errno(int err) { switch (err) { case PCIBIOS_SUCCESSFUL: @@ -202,7 +202,7 @@ out: pci_name(dev), size, offset, value); *ret_val = value; - return pcibios_err_to_errno(err); + return xen_pcibios_err_to_errno(err); } int xen_pcibk_config_write(struct pci_dev *dev, int offset, int size, u32 value) @@ -290,7 +290,7 @@ int xen_pcibk_config_write(struct pci_dev *dev, int offset, int size, u32 value) } } - return pcibios_err_to_errno(err); + return xen_pcibios_err_to_errno(err); } void xen_pcibk_config_free_dyn_fields(struct pci_dev *dev) diff --git a/include/linux/pci.h b/include/linux/pci.h index 9d04599c6bd9..a691f62bcf89 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -474,6 +474,32 @@ static inline bool pci_dev_msi_enabled(struct pci_dev *pci_dev) { return false; #define PCIBIOS_SET_FAILED 0x88 #define PCIBIOS_BUFFER_TOO_SMALL 0x89 +/* + * Translate above to generic errno for passing back through non-pci. + */ +static inline int pcibios_err_to_errno(int err) +{ + if (err <= PCIBIOS_SUCCESSFUL) + return err; /* Assume already errno */ + + switch (err) { + case PCIBIOS_FUNC_NOT_SUPPORTED: + return -ENOENT; + case PCIBIOS_BAD_VENDOR_ID: + return -EINVAL; + case PCIBIOS_DEVICE_NOT_FOUND: + return -ENODEV; + case PCIBIOS_BAD_REGISTER_NUMBER: + return -EFAULT; + case PCIBIOS_SET_FAILED: + return -EIO; + case PCIBIOS_BUFFER_TOO_SMALL: + return -ENOSPC; + } + + return -ENOTTY; +} + /* Low-level architecture-dependent routines */ struct pci_ops { -- cgit v1.2.3 From a0dee2ed0cdc666b5622f1fc74979355a6b36850 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 11 Jun 2012 05:27:45 +0000 Subject: PCI: misc pci_reg additions Fill in many missing definitions and add sizeof fields for many sections allowing for more extensive config parsing. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- include/linux/pci_regs.h | 113 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 101 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index 4b608f543412..526d2c4bc3a6 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -26,6 +26,7 @@ * Under PCI, each device has 256 bytes of configuration address space, * of which the first 64 bytes are standardized as follows: */ +#define PCI_STD_HEADER_SIZEOF 64 #define PCI_VENDOR_ID 0x00 /* 16 bits */ #define PCI_DEVICE_ID 0x02 /* 16 bits */ #define PCI_COMMAND 0x04 /* 16 bits */ @@ -209,9 +210,12 @@ #define PCI_CAP_ID_SHPC 0x0C /* PCI Standard Hot-Plug Controller */ #define PCI_CAP_ID_SSVID 0x0D /* Bridge subsystem vendor/device ID */ #define PCI_CAP_ID_AGP3 0x0E /* AGP Target PCI-PCI bridge */ +#define PCI_CAP_ID_SECDEV 0x0F /* Secure Device */ #define PCI_CAP_ID_EXP 0x10 /* PCI Express */ #define PCI_CAP_ID_MSIX 0x11 /* MSI-X */ +#define PCI_CAP_ID_SATA 0x12 /* SATA Data/Index Conf. */ #define PCI_CAP_ID_AF 0x13 /* PCI Advanced Features */ +#define PCI_CAP_ID_MAX PCI_CAP_ID_AF #define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */ #define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */ #define PCI_CAP_SIZEOF 4 @@ -276,6 +280,7 @@ #define PCI_VPD_ADDR_MASK 0x7fff /* Address mask */ #define PCI_VPD_ADDR_F 0x8000 /* Write 0, 1 indicates completion */ #define PCI_VPD_DATA 4 /* 32-bits of data returned here */ +#define PCI_CAP_VPD_SIZEOF 8 /* Slot Identification */ @@ -297,8 +302,10 @@ #define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */ #define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */ #define PCI_MSI_MASK_32 12 /* Mask bits register for 32-bit devices */ +#define PCI_MSI_PENDING_32 16 /* Pending intrs for 32-bit devices */ #define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */ #define PCI_MSI_MASK_64 16 /* Mask bits register for 64-bit devices */ +#define PCI_MSI_PENDING_64 20 /* Pending intrs for 64-bit devices */ /* MSI-X registers */ #define PCI_MSIX_FLAGS 2 @@ -308,6 +315,7 @@ #define PCI_MSIX_TABLE 4 #define PCI_MSIX_PBA 8 #define PCI_MSIX_FLAGS_BIRMASK (7 << 0) +#define PCI_CAP_MSIX_SIZEOF 12 /* size of MSIX registers */ /* MSI-X entry's format */ #define PCI_MSIX_ENTRY_SIZE 16 @@ -338,6 +346,7 @@ #define PCI_AF_CTRL_FLR 0x01 #define PCI_AF_STATUS 5 #define PCI_AF_STATUS_TP 0x01 +#define PCI_CAP_AF_SIZEOF 6 /* size of AF registers */ /* PCI-X registers */ @@ -374,6 +383,10 @@ #define PCI_X_STATUS_SPL_ERR 0x20000000 /* Rcvd Split Completion Error Msg */ #define PCI_X_STATUS_266MHZ 0x40000000 /* 266 MHz capable */ #define PCI_X_STATUS_533MHZ 0x80000000 /* 533 MHz capable */ +#define PCI_X_ECC_CSR 8 /* ECC control and status */ +#define PCI_CAP_PCIX_SIZEOF_V0 8 /* size of registers for Version 0 */ +#define PCI_CAP_PCIX_SIZEOF_V1 24 /* size for Version 1 */ +#define PCI_CAP_PCIX_SIZEOF_V2 PCI_CAP_PCIX_SIZEOF_V1 /* Same for v2 */ /* PCI Bridge Subsystem ID registers */ @@ -462,6 +475,7 @@ #define PCI_EXP_LNKSTA_DLLLA 0x2000 /* Data Link Layer Link Active */ #define PCI_EXP_LNKSTA_LBMS 0x4000 /* Link Bandwidth Management Status */ #define PCI_EXP_LNKSTA_LABS 0x8000 /* Link Autonomous Bandwidth Status */ +#define PCI_CAP_EXP_ENDPOINT_SIZEOF_V1 20 /* v1 endpoints end here */ #define PCI_EXP_SLTCAP 20 /* Slot Capabilities */ #define PCI_EXP_SLTCAP_ABP 0x00000001 /* Attention Button Present */ #define PCI_EXP_SLTCAP_PCP 0x00000002 /* Power Controller Present */ @@ -521,6 +535,7 @@ #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_CAP_EXP_ENDPOINT_SIZEOF_V2 44 /* v2 endpoints end here */ #define PCI_EXP_LNKCTL2 48 /* Link Control 2 */ #define PCI_EXP_SLTCTL2 56 /* Slot Control 2 */ @@ -529,23 +544,43 @@ #define PCI_EXT_CAP_VER(header) ((header >> 16) & 0xf) #define PCI_EXT_CAP_NEXT(header) ((header >> 20) & 0xffc) -#define PCI_EXT_CAP_ID_ERR 1 -#define PCI_EXT_CAP_ID_VC 2 -#define PCI_EXT_CAP_ID_DSN 3 -#define PCI_EXT_CAP_ID_PWR 4 -#define PCI_EXT_CAP_ID_VNDR 11 -#define PCI_EXT_CAP_ID_ACS 13 -#define PCI_EXT_CAP_ID_ARI 14 -#define PCI_EXT_CAP_ID_ATS 15 -#define PCI_EXT_CAP_ID_SRIOV 16 -#define PCI_EXT_CAP_ID_PRI 19 -#define PCI_EXT_CAP_ID_LTR 24 -#define PCI_EXT_CAP_ID_PASID 27 +#define PCI_EXT_CAP_ID_ERR 0x01 /* Advanced Error Reporting */ +#define PCI_EXT_CAP_ID_VC 0x02 /* Virtual Channel Capability */ +#define PCI_EXT_CAP_ID_DSN 0x03 /* Device Serial Number */ +#define PCI_EXT_CAP_ID_PWR 0x04 /* Power Budgeting */ +#define PCI_EXT_CAP_ID_RCLD 0x05 /* Root Complex Link Declaration */ +#define PCI_EXT_CAP_ID_RCILC 0x06 /* Root Complex Internal Link Control */ +#define PCI_EXT_CAP_ID_RCEC 0x07 /* Root Complex Event Collector */ +#define PCI_EXT_CAP_ID_MFVC 0x08 /* Multi-Function VC Capability */ +#define PCI_EXT_CAP_ID_VC9 0x09 /* same as _VC */ +#define PCI_EXT_CAP_ID_RCRB 0x0A /* Root Complex RB? */ +#define PCI_EXT_CAP_ID_VNDR 0x0B /* Vendor Specific */ +#define PCI_EXT_CAP_ID_CAC 0x0C /* Config Access - obsolete */ +#define PCI_EXT_CAP_ID_ACS 0x0D /* Access Control Services */ +#define PCI_EXT_CAP_ID_ARI 0x0E /* Alternate Routing ID */ +#define PCI_EXT_CAP_ID_ATS 0x0F /* Address Translation Services */ +#define PCI_EXT_CAP_ID_SRIOV 0x10 /* Single Root I/O Virtualization */ +#define PCI_EXT_CAP_ID_MRIOV 0x11 /* Multi Root I/O Virtualization */ +#define PCI_EXT_CAP_ID_MCAST 0x12 /* Multicast */ +#define PCI_EXT_CAP_ID_PRI 0x13 /* Page Request Interface */ +#define PCI_EXT_CAP_ID_AMD_XXX 0x14 /* reserved for AMD */ +#define PCI_EXT_CAP_ID_REBAR 0x15 /* resizable BAR */ +#define PCI_EXT_CAP_ID_DPA 0x16 /* dynamic power alloc */ +#define PCI_EXT_CAP_ID_TPH 0x17 /* TPH request */ +#define PCI_EXT_CAP_ID_LTR 0x18 /* latency tolerance reporting */ +#define PCI_EXT_CAP_ID_SECPCI 0x19 /* Secondary PCIe */ +#define PCI_EXT_CAP_ID_PMUX 0x1A /* Protocol Multiplexing */ +#define PCI_EXT_CAP_ID_PASID 0x1B /* Process Address Space ID */ +#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PASID + +#define PCI_EXT_CAP_DSN_SIZEOF 12 +#define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40 /* Advanced Error Reporting */ #define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */ #define PCI_ERR_UNC_TRAIN 0x00000001 /* Training */ #define PCI_ERR_UNC_DLP 0x00000010 /* Data Link Protocol */ +#define PCI_ERR_UNC_SURPDN 0x00000020 /* Surprise Down */ #define PCI_ERR_UNC_POISON_TLP 0x00001000 /* Poisoned TLP */ #define PCI_ERR_UNC_FCP 0x00002000 /* Flow Control Protocol */ #define PCI_ERR_UNC_COMP_TIME 0x00004000 /* Completion Timeout */ @@ -555,6 +590,11 @@ #define PCI_ERR_UNC_MALF_TLP 0x00040000 /* Malformed TLP */ #define PCI_ERR_UNC_ECRC 0x00080000 /* ECRC Error Status */ #define PCI_ERR_UNC_UNSUP 0x00100000 /* Unsupported Request */ +#define PCI_ERR_UNC_ACSV 0x00200000 /* ACS Violation */ +#define PCI_ERR_UNC_INTN 0x00400000 /* internal error */ +#define PCI_ERR_UNC_MCBTLP 0x00800000 /* MC blocked TLP */ +#define PCI_ERR_UNC_ATOMEG 0x01000000 /* Atomic egress blocked */ +#define PCI_ERR_UNC_TLPPRE 0x02000000 /* TLP prefix blocked */ #define PCI_ERR_UNCOR_MASK 8 /* Uncorrectable Error Mask */ /* Same bits as above */ #define PCI_ERR_UNCOR_SEVER 12 /* Uncorrectable Error Severity */ @@ -565,6 +605,9 @@ #define PCI_ERR_COR_BAD_DLLP 0x00000080 /* Bad DLLP Status */ #define PCI_ERR_COR_REP_ROLL 0x00000100 /* REPLAY_NUM Rollover */ #define PCI_ERR_COR_REP_TIMER 0x00001000 /* Replay Timer Timeout */ +#define PCI_ERR_COR_ADV_NFAT 0x00002000 /* Advisory Non-Fatal */ +#define PCI_ERR_COR_INTERNAL 0x00004000 /* Corrected Internal */ +#define PCI_ERR_COR_LOG_OVER 0x00008000 /* Header Log Overflow */ #define PCI_ERR_COR_MASK 20 /* Correctable Error Mask */ /* Same bits as above */ #define PCI_ERR_CAP 24 /* Advanced Error Capabilities */ @@ -596,12 +639,18 @@ /* Virtual Channel */ #define PCI_VC_PORT_REG1 4 +#define PCI_VC_REG1_EVCC 0x7 /* extended vc count */ #define PCI_VC_PORT_REG2 8 +#define PCI_VC_REG2_32_PHASE 0x2 +#define PCI_VC_REG2_64_PHASE 0x4 +#define PCI_VC_REG2_128_PHASE 0x8 #define PCI_VC_PORT_CTRL 12 #define PCI_VC_PORT_STATUS 14 #define PCI_VC_RES_CAP 16 #define PCI_VC_RES_CTRL 20 #define PCI_VC_RES_STATUS 26 +#define PCI_CAP_VC_BASE_SIZEOF 0x10 +#define PCI_CAP_VC_PER_VC_SIZEOF 0x0C /* Power Budgeting */ #define PCI_PWR_DSR 4 /* Data Select Register */ @@ -614,6 +663,7 @@ #define PCI_PWR_DATA_RAIL(x) (((x) >> 18) & 7) /* Power Rail */ #define PCI_PWR_CAP 12 /* Capability */ #define PCI_PWR_CAP_BUDGET(x) ((x) & 1) /* Included in system budget */ +#define PCI_EXT_CAP_PWR_SIZEOF 16 /* * Hypertransport sub capability types @@ -646,6 +696,8 @@ #define HT_CAPTYPE_ERROR_RETRY 0xC0 /* Retry on error configuration */ #define HT_CAPTYPE_GEN3 0xD0 /* Generation 3 hypertransport configuration */ #define HT_CAPTYPE_PM 0xE0 /* Hypertransport powermanagement configuration */ +#define HT_CAP_SIZEOF_LONG 28 /* slave & primary */ +#define HT_CAP_SIZEOF_SHORT 24 /* host & secondary */ /* Alternative Routing-ID Interpretation */ #define PCI_ARI_CAP 0x04 /* ARI Capability Register */ @@ -656,6 +708,7 @@ #define PCI_ARI_CTRL_MFVC 0x0001 /* MFVC Function Groups Enable */ #define PCI_ARI_CTRL_ACS 0x0002 /* ACS Function Groups Enable */ #define PCI_ARI_CTRL_FG(x) (((x) >> 4) & 7) /* Function Group */ +#define PCI_EXT_CAP_ARI_SIZEOF 8 /* Address Translation Service */ #define PCI_ATS_CAP 0x04 /* ATS Capability Register */ @@ -665,6 +718,7 @@ #define PCI_ATS_CTRL_ENABLE 0x8000 /* ATS Enable */ #define PCI_ATS_CTRL_STU(x) ((x) & 0x1f) /* Smallest Translation Unit */ #define PCI_ATS_MIN_STU 12 /* shift of minimum STU block */ +#define PCI_EXT_CAP_ATS_SIZEOF 8 /* Page Request Interface */ #define PCI_PRI_CTRL 0x04 /* PRI control register */ @@ -676,6 +730,7 @@ #define PCI_PRI_STATUS_STOPPED 0x100 /* PRI Stopped */ #define PCI_PRI_MAX_REQ 0x08 /* PRI max reqs supported */ #define PCI_PRI_ALLOC_REQ 0x0c /* PRI max reqs allowed */ +#define PCI_EXT_CAP_PRI_SIZEOF 16 /* PASID capability */ #define PCI_PASID_CAP 0x04 /* PASID feature register */ @@ -685,6 +740,7 @@ #define PCI_PASID_CTRL_ENABLE 0x01 /* Enable bit */ #define PCI_PASID_CTRL_EXEC 0x02 /* Exec permissions Enable */ #define PCI_PASID_CTRL_PRIV 0x04 /* Priviledge Mode Enable */ +#define PCI_EXT_CAP_PASID_SIZEOF 8 /* Single Root I/O Virtualization */ #define PCI_SRIOV_CAP 0x04 /* SR-IOV Capabilities */ @@ -716,12 +772,14 @@ #define PCI_SRIOV_VFM_MI 0x1 /* Dormant.MigrateIn */ #define PCI_SRIOV_VFM_MO 0x2 /* Active.MigrateOut */ #define PCI_SRIOV_VFM_AV 0x3 /* Active.Available */ +#define PCI_EXT_CAP_SRIOV_SIZEOF 64 #define PCI_LTR_MAX_SNOOP_LAT 0x4 #define PCI_LTR_MAX_NOSNOOP_LAT 0x6 #define PCI_LTR_VALUE_MASK 0x000003ff #define PCI_LTR_SCALE_MASK 0x00001c00 #define PCI_LTR_SCALE_SHIFT 10 +#define PCI_EXT_CAP_LTR_SIZEOF 8 /* Access Control Service */ #define PCI_ACS_CAP 0x04 /* ACS Capability Register */ @@ -732,7 +790,38 @@ #define PCI_ACS_UF 0x10 /* Upstream Forwarding */ #define PCI_ACS_EC 0x20 /* P2P Egress Control */ #define PCI_ACS_DT 0x40 /* Direct Translated P2P */ +#define PCI_ACS_EGRESS_BITS 0x05 /* ACS Egress Control Vector Size */ #define PCI_ACS_CTRL 0x06 /* ACS Control Register */ #define PCI_ACS_EGRESS_CTL_V 0x08 /* ACS Egress Control Vector */ +#define PCI_VSEC_HDR 4 /* extended cap - vendor specific */ +#define PCI_VSEC_HDR_LEN_SHIFT 20 /* shift for length field */ + +/* sata capability */ +#define PCI_SATA_REGS 4 /* SATA REGs specifier */ +#define PCI_SATA_REGS_MASK 0xF /* location - BAR#/inline */ +#define PCI_SATA_REGS_INLINE 0xF /* REGS in config space */ +#define PCI_SATA_SIZEOF_SHORT 8 +#define PCI_SATA_SIZEOF_LONG 16 + +/* resizable BARs */ +#define PCI_REBAR_CTRL 8 /* control register */ +#define PCI_REBAR_CTRL_NBAR_MASK (7 << 5) /* mask for # bars */ +#define PCI_REBAR_CTRL_NBAR_SHIFT 5 /* shift for # bars */ + +/* dynamic power allocation */ +#define PCI_DPA_CAP 4 /* capability register */ +#define PCI_DPA_CAP_SUBSTATE_MASK 0x1F /* # substates - 1 */ +#define PCI_DPA_BASE_SIZEOF 16 /* size with 0 substates */ + +/* TPH Requester */ +#define PCI_TPH_CAP 4 /* capability register */ +#define PCI_TPH_CAP_LOC_MASK 0x600 /* location mask */ +#define PCI_TPH_LOC_NONE 0x000 /* no location */ +#define PCI_TPH_LOC_CAP 0x200 /* in capability */ +#define PCI_TPH_LOC_MSIX 0x400 /* in MSI-X */ +#define PCI_TPH_CAP_ST_MASK 0x07FF0000 /* st table mask */ +#define PCI_TPH_CAP_ST_SHIFT 16 /* st table shift */ +#define PCI_TPH_BASE_SIZEOF 12 /* size with no st table */ + #endif /* LINUX_PCI_REGS_H */ -- cgit v1.2.3 From dd2757f8b557ab2030154896eac9b2285557dda6 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 7 Jun 2012 15:55:57 +0200 Subject: drm/i915: stop using dev->agp->base For that to work we need to export the base address of the gtt mmio window from intel-gtt. Also replace all other uses of dev->agp by values we already have at hand. Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/char/agp/intel-gtt.c | 5 ++--- drivers/gpu/drm/i915/i915_dma.c | 21 +++++++++++++-------- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_gem.c | 2 +- drivers/gpu/drm/i915/i915_gem_debug.c | 3 ++- drivers/gpu/drm/i915/intel_display.c | 2 +- drivers/gpu/drm/i915/intel_fb.c | 4 +++- drivers/gpu/drm/i915/intel_ringbuffer.c | 6 ++++-- include/drm/intel-gtt.h | 2 ++ 9 files changed, 29 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 53c4c7fca10b..2aab0a03ee42 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -66,7 +66,6 @@ static struct _intel_private { struct pci_dev *bridge_dev; u8 __iomem *registers; phys_addr_t gtt_bus_addr; - phys_addr_t gma_bus_addr; u32 PGETBL_save; u32 __iomem *gtt; /* I915G */ bool clear_fake_agp; /* on first access via agp, fill with scratch */ @@ -779,7 +778,7 @@ static bool intel_enable_gtt(void) pci_read_config_dword(intel_private.pcidev, I915_GMADDR, &gma_addr); - intel_private.gma_bus_addr = (gma_addr & PCI_BASE_ADDRESS_MEM_MASK); + intel_private.base.gma_bus_addr = (gma_addr & PCI_BASE_ADDRESS_MEM_MASK); if (INTEL_GTT_GEN >= 6) return true; @@ -860,7 +859,7 @@ static int intel_fake_agp_configure(void) return -EIO; intel_private.clear_fake_agp = true; - agp_bridge->gart_bus_addr = intel_private.gma_bus_addr; + agp_bridge->gart_bus_addr = intel_private.base.gma_bus_addr; return 0; } diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 97a5a5857f5b..c639d431ad66 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1085,8 +1085,8 @@ static int i915_set_status_page(struct drm_device *dev, void *data, ring->status_page.gfx_addr = hws->addr & (0x1ffff<<12); - dev_priv->dri1.gfx_hws_cpu_addr = ioremap_wc(dev->agp->base + hws->addr, - 4096); + dev_priv->dri1.gfx_hws_cpu_addr = + ioremap_wc(dev_priv->mm.gtt_base_addr + hws->addr, 4096); if (dev_priv->dri1.gfx_hws_cpu_addr == NULL) { i915_dma_cleanup(dev); ring->status_page.gfx_addr = 0; @@ -1482,15 +1482,18 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) } aperture_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; + dev_priv->mm.gtt_base_addr = dev_priv->mm.gtt->gma_bus_addr; dev_priv->mm.gtt_mapping = - io_mapping_create_wc(dev->agp->base, aperture_size); + io_mapping_create_wc(dev_priv->mm.gtt_base_addr, + aperture_size); if (dev_priv->mm.gtt_mapping == NULL) { ret = -EIO; goto out_rmmap; } - i915_mtrr_setup(dev_priv, dev->agp->base, aperture_size); + i915_mtrr_setup(dev_priv, dev_priv->mm.gtt_base_addr, + aperture_size); /* The i915 workqueue is primarily used for batched retirement of * requests (and thus managing bo) once the task has been completed @@ -1602,8 +1605,9 @@ out_gem_unload: destroy_workqueue(dev_priv->wq); out_mtrrfree: if (dev_priv->mm.gtt_mtrr >= 0) { - mtrr_del(dev_priv->mm.gtt_mtrr, dev->agp->base, - dev->agp->agp_info.aper_size * 1024 * 1024); + mtrr_del(dev_priv->mm.gtt_mtrr, + dev_priv->mm.gtt_base_addr, + aperture_size); dev_priv->mm.gtt_mtrr = -1; } io_mapping_free(dev_priv->mm.gtt_mapping); @@ -1640,8 +1644,9 @@ int i915_driver_unload(struct drm_device *dev) io_mapping_free(dev_priv->mm.gtt_mapping); if (dev_priv->mm.gtt_mtrr >= 0) { - mtrr_del(dev_priv->mm.gtt_mtrr, dev->agp->base, - dev->agp->agp_info.aper_size * 1024 * 1024); + mtrr_del(dev_priv->mm.gtt_mtrr, + dev_priv->mm.gtt_base_addr, + dev_priv->mm.gtt->gtt_mappable_entries * PAGE_SIZE); dev_priv->mm.gtt_mtrr = -1; } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ccabadd2b6c3..ae4129b3cb3e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -651,6 +651,7 @@ typedef struct drm_i915_private { unsigned long gtt_end; struct io_mapping *gtt_mapping; + phys_addr_t gtt_base_addr; int gtt_mtrr; /** PPGTT used for aliasing the PPGTT with the GTT */ diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index deaa0d4bb456..108e4c2b5ffa 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1122,7 +1122,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) obj->fault_mappable = true; - pfn = ((dev->agp->base + obj->gtt_offset) >> PAGE_SHIFT) + + pfn = ((dev_priv->mm.gtt_base_addr + obj->gtt_offset) >> PAGE_SHIFT) + page_offset; /* Finally, remap it using the new GTT offset */ diff --git a/drivers/gpu/drm/i915/i915_gem_debug.c b/drivers/gpu/drm/i915/i915_gem_debug.c index a4f6aaabca99..bddf7bed183f 100644 --- a/drivers/gpu/drm/i915/i915_gem_debug.c +++ b/drivers/gpu/drm/i915/i915_gem_debug.c @@ -132,7 +132,8 @@ i915_gem_object_check_coherency(struct drm_i915_gem_object *obj, int handle) __func__, obj, obj->gtt_offset, handle, obj->size / 1024); - gtt_mapping = ioremap(dev->agp->base + obj->gtt_offset, obj->base.size); + gtt_mapping = ioremap(dev_priv->mm.gtt_base_addr + obj->gtt_offset, + obj->base.size); if (gtt_mapping == NULL) { DRM_ERROR("failed to map GTT space\n"); return; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index bad36e0b8045..174549df5929 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6954,7 +6954,7 @@ void intel_modeset_init(struct drm_device *dev) dev->mode_config.max_width = 8192; dev->mode_config.max_height = 8192; } - dev->mode_config.fb_base = dev->agp->base; + dev->mode_config.fb_base = dev_priv->mm.gtt_base_addr; DRM_DEBUG_KMS("%d display pipe%s available.\n", dev_priv->num_pipe, dev_priv->num_pipe > 1 ? "s" : ""); diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index bf8690720a0c..e9f8338bd802 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -140,7 +140,9 @@ static int intelfb_create(struct intel_fbdev *ifbdev, info->fix.smem_start = dev->mode_config.fb_base + obj->gtt_offset; info->fix.smem_len = size; - info->screen_base = ioremap_wc(dev->agp->base + obj->gtt_offset, size); + info->screen_base = + ioremap_wc(dev_priv->mm.gtt_base_addr + obj->gtt_offset, + size); if (!info->screen_base) { ret = -ENOSPC; goto out_unpin; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 89a5e7f89d7a..14025ab9d4ca 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -968,6 +968,7 @@ static int intel_init_ring_buffer(struct drm_device *dev, struct intel_ring_buffer *ring) { struct drm_i915_gem_object *obj; + struct drm_i915_private *dev_priv = dev->dev_private; int ret; ring->dev = dev; @@ -997,8 +998,9 @@ static int intel_init_ring_buffer(struct drm_device *dev, if (ret) goto err_unref; - ring->virtual_start = ioremap_wc(dev->agp->base + obj->gtt_offset, - ring->size); + ring->virtual_start = + ioremap_wc(dev_priv->mm.gtt->gma_bus_addr + obj->gtt_offset, + ring->size); if (ring->virtual_start == NULL) { DRM_ERROR("Failed to map ringbuffer.\n"); ret = -EINVAL; diff --git a/include/drm/intel-gtt.h b/include/drm/intel-gtt.h index 923afb5dcf0c..8048c005c6f6 100644 --- a/include/drm/intel-gtt.h +++ b/include/drm/intel-gtt.h @@ -19,6 +19,8 @@ const struct intel_gtt { dma_addr_t scratch_page_dma; /* for ppgtt PDE access */ u32 __iomem *gtt; + /* needed for ioremap in drm/i915 */ + phys_addr_t gma_bus_addr; } *intel_gtt_get(void); void intel_gtt_chipset_flush(void); -- cgit v1.2.3 From 14be93ddff61eb196382aeaa3ac86f4db844aeb0 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 8 Jun 2012 15:55:40 +0200 Subject: drm/i915 + agp/intel-gtt: prep work for direct setup To be able to directly set up the intel-gtt code from drm/i915 and avoid setting up the fake-agp driver we need to prepare a few things: - pass both the bridge and gpu pci_dev to the probe function and add code to handle the gpu pdev both being present (for drm/i915) and not present (fake agp). - add refcounting to the remove function so that unloading drm/i915 doesn't kill the fake agp driver v2: Fix up the cleanup and refcount, noticed by Jani Nikula. Reviewed-by: Jani Nikula Signed-Off-by: Daniel Vetter --- drivers/char/agp/intel-agp.c | 5 +++-- drivers/char/agp/intel-agp.h | 3 --- drivers/char/agp/intel-gtt.c | 46 +++++++++++++++++++++++++++++++++-------- drivers/gpu/drm/i915/i915_dma.c | 13 ++++++++++-- include/drm/intel-gtt.h | 4 ++++ 5 files changed, 55 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 764f70c5e690..c98c5689bb0b 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -12,6 +12,7 @@ #include #include "agp.h" #include "intel-agp.h" +#include int intel_agp_enabled; EXPORT_SYMBOL(intel_agp_enabled); @@ -747,7 +748,7 @@ static int __devinit agp_intel_probe(struct pci_dev *pdev, bridge->capndx = cap_ptr; - if (intel_gmch_probe(pdev, bridge)) + if (intel_gmch_probe(pdev, NULL, bridge)) goto found_gmch; for (i = 0; intel_agp_chipsets[i].name != NULL; i++) { @@ -824,7 +825,7 @@ static void __devexit agp_intel_remove(struct pci_dev *pdev) agp_remove_bridge(bridge); - intel_gmch_remove(pdev); + intel_gmch_remove(); agp_put_bridge(bridge); } diff --git a/drivers/char/agp/intel-agp.h b/drivers/char/agp/intel-agp.h index c0091753a0d1..cf2e764b1760 100644 --- a/drivers/char/agp/intel-agp.h +++ b/drivers/char/agp/intel-agp.h @@ -250,7 +250,4 @@ #define PCI_DEVICE_ID_INTEL_HASWELL_SDV 0x0c16 /* SDV */ #define PCI_DEVICE_ID_INTEL_HASWELL_E_HB 0x0c04 -int intel_gmch_probe(struct pci_dev *pdev, - struct agp_bridge_data *bridge); -void intel_gmch_remove(struct pci_dev *pdev); #endif diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 5e6c89e1d5eb..cea9f9905c7d 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -75,6 +75,7 @@ static struct _intel_private { struct resource ifp_resource; int resource_valid; struct page *scratch_page; + int refcount; } intel_private; #define INTEL_GTT_GEN intel_private.driver->gen @@ -1522,14 +1523,32 @@ static int find_gmch(u16 device) return 1; } -int intel_gmch_probe(struct pci_dev *pdev, - struct agp_bridge_data *bridge) +int intel_gmch_probe(struct pci_dev *bridge_pdev, struct pci_dev *gpu_pdev, + struct agp_bridge_data *bridge) { int i, mask; - intel_private.driver = NULL; + + /* + * Can be called from the fake agp driver but also directly from + * drm/i915.ko. Hence we need to check whether everything is set up + * already. + */ + if (intel_private.driver) { + intel_private.refcount++; + return 1; + } for (i = 0; intel_gtt_chipsets[i].name != NULL; i++) { - if (find_gmch(intel_gtt_chipsets[i].gmch_chip_id)) { + if (gpu_pdev) { + if (gpu_pdev->device == + intel_gtt_chipsets[i].gmch_chip_id) { + intel_private.pcidev = pci_dev_get(gpu_pdev); + intel_private.driver = + intel_gtt_chipsets[i].gtt_driver; + + break; + } + } else if (find_gmch(intel_gtt_chipsets[i].gmch_chip_id)) { intel_private.driver = intel_gtt_chipsets[i].gtt_driver; break; @@ -1539,15 +1558,17 @@ int intel_gmch_probe(struct pci_dev *pdev, if (!intel_private.driver) return 0; + intel_private.refcount++; + if (bridge) { bridge->driver = &intel_fake_agp_driver; bridge->dev_private_data = &intel_private; - bridge->dev = pdev; + bridge->dev = bridge_pdev; } - intel_private.bridge_dev = pci_dev_get(pdev); + intel_private.bridge_dev = pci_dev_get(bridge_pdev); - dev_info(&pdev->dev, "Intel %s Chipset\n", intel_gtt_chipsets[i].name); + dev_info(&bridge_pdev->dev, "Intel %s Chipset\n", intel_gtt_chipsets[i].name); mask = intel_private.driver->dma_mask_size; if (pci_set_dma_mask(intel_private.pcidev, DMA_BIT_MASK(mask))) @@ -1557,8 +1578,11 @@ int intel_gmch_probe(struct pci_dev *pdev, pci_set_consistent_dma_mask(intel_private.pcidev, DMA_BIT_MASK(mask)); - if (intel_gtt_init() != 0) + if (intel_gtt_init() != 0) { + intel_gmch_remove(); + return 0; + } return 1; } @@ -1577,12 +1601,16 @@ void intel_gtt_chipset_flush(void) } EXPORT_SYMBOL(intel_gtt_chipset_flush); -void intel_gmch_remove(struct pci_dev *pdev) +void intel_gmch_remove(void) { + if (--intel_private.refcount) + return; + if (intel_private.pcidev) pci_dev_put(intel_private.pcidev); if (intel_private.bridge_dev) pci_dev_put(intel_private.bridge_dev); + intel_private.driver = NULL; } EXPORT_SYMBOL(intel_gmch_remove); diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index c639d431ad66..cf512e7178b4 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1474,11 +1474,18 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) goto put_bridge; } + ret = intel_gmch_probe(dev_priv->bridge_dev, dev->pdev, NULL); + if (!ret) { + DRM_ERROR("failed to set up gmch\n"); + ret = -EIO; + goto out_rmmap; + } + dev_priv->mm.gtt = intel_gtt_get(); if (!dev_priv->mm.gtt) { DRM_ERROR("Failed to initialize GTT\n"); ret = -ENODEV; - goto out_rmmap; + goto put_gmch; } aperture_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; @@ -1489,7 +1496,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) aperture_size); if (dev_priv->mm.gtt_mapping == NULL) { ret = -EIO; - goto out_rmmap; + goto put_gmch; } i915_mtrr_setup(dev_priv, dev_priv->mm.gtt_base_addr, @@ -1611,6 +1618,8 @@ out_mtrrfree: dev_priv->mm.gtt_mtrr = -1; } io_mapping_free(dev_priv->mm.gtt_mapping); +put_gmch: + intel_gmch_remove(); out_rmmap: pci_iounmap(dev->pdev, dev_priv->regs); put_bridge: diff --git a/include/drm/intel-gtt.h b/include/drm/intel-gtt.h index 8048c005c6f6..84ebd7188fc6 100644 --- a/include/drm/intel-gtt.h +++ b/include/drm/intel-gtt.h @@ -23,6 +23,10 @@ const struct intel_gtt { phys_addr_t gma_bus_addr; } *intel_gtt_get(void); +int intel_gmch_probe(struct pci_dev *bridge_pdev, struct pci_dev *gpu_pdev, + struct agp_bridge_data *bridge); +void intel_gmch_remove(void); + void intel_gtt_chipset_flush(void); void intel_gtt_unmap_memory(struct scatterlist *sg_list, int num_sg); void intel_gtt_clear_range(unsigned int first_entry, unsigned int num_entries); -- cgit v1.2.3 From 8ecd1a6615f0d9de6759aafe229bc1cc4ee99c7b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 7 Jun 2012 15:56:03 +0200 Subject: drm/i915: call intel_enable_gtt When drm/i915 is in control of the gtt, we need to call the enable function at all the relevant places ourselves. Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/char/agp/intel-gtt.c | 3 ++- drivers/gpu/drm/i915/i915_gem.c | 3 +++ include/drm/intel-gtt.h | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 4387e69f8b11..419a25eeefd8 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -777,7 +777,7 @@ static void i830_write_entry(dma_addr_t addr, unsigned int entry, writel(addr | pte_flags, intel_private.gtt + entry); } -static bool intel_enable_gtt(void) +bool intel_enable_gtt(void) { u8 __iomem *reg; @@ -823,6 +823,7 @@ static bool intel_enable_gtt(void) return true; } +EXPORT_SYMBOL(intel_enable_gtt); static int i830_setup(void) { diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 108e4c2b5ffa..2884b0865473 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3689,6 +3689,9 @@ i915_gem_init_hw(struct drm_device *dev) drm_i915_private_t *dev_priv = dev->dev_private; int ret; + if (!intel_enable_gtt()) + return -EIO; + i915_gem_l3_remap(dev); i915_gem_init_swizzling(dev); diff --git a/include/drm/intel-gtt.h b/include/drm/intel-gtt.h index 84ebd7188fc6..8e29d551bb3c 100644 --- a/include/drm/intel-gtt.h +++ b/include/drm/intel-gtt.h @@ -27,6 +27,8 @@ int intel_gmch_probe(struct pci_dev *bridge_pdev, struct pci_dev *gpu_pdev, struct agp_bridge_data *bridge); void intel_gmch_remove(void); +bool intel_enable_gtt(void); + void intel_gtt_chipset_flush(void); void intel_gtt_unmap_memory(struct scatterlist *sg_list, int num_sg); void intel_gtt_clear_range(unsigned int first_entry, unsigned int num_entries); -- cgit v1.2.3 From cf35ad61aca2c0c8983fa1e140c901f6588aba7e Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Fri, 8 Jun 2012 18:06:45 +0200 Subject: iio: add mcp4725 I2C DAC driver v5: * fix warnings (Jonathan Cameron) v4: * remove unused indio_dev pointer in mcp4725_data (Jonathan Cameron) * use u16 instead of unsigned short in mcp4725_data (Jonathan Cameron) * #include mcp4725.h from linux/iio/dac/ v3: * move from staging to drivers/iio * switch to chan_spec * dev_get_drvdata() -> dev_to_iio_dev() * annotate probe() and remove() with __devinit and __devexit v2 (based on comments from Jonathan Cameron and Lars-Peter Clausen): * did NOT switch to chan_spec yet * rebase to staging-next tree, update iio header locations * dropped dac.h #include, not needed * strict_strtol() -> kstrtol() * call iio_device_unregister() in remove() * everything in one patch Signed-off-by: Peter Meerwald Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/dac/Kconfig | 11 ++ drivers/iio/dac/Makefile | 1 + drivers/iio/dac/mcp4725.c | 227 ++++++++++++++++++++++++++++++++++++++++ include/linux/iio/dac/mcp4725.h | 16 +++ 4 files changed, 255 insertions(+) create mode 100644 drivers/iio/dac/mcp4725.c create mode 100644 include/linux/iio/dac/mcp4725.h (limited to 'include') diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index a626f03871ec..92fb3a003510 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -118,4 +118,15 @@ config MAX517 This driver can also be built as a module. If so, the module will be called max517. +config MCP4725 + tristate "MCP4725 DAC driver" + depends on I2C + ---help--- + Say Y here if you want to build a driver for the Microchip + MCP 4725 12-bit digital-to-analog converter (DAC) with I2C + interface. + + To compile this driver as a module, choose M here: the module + will be called mcp4725. + endmenu diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 8ab1d264aab7..9ea3ceeefc07 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_AD5764) += ad5764.o obj-$(CONFIG_AD5791) += ad5791.o obj-$(CONFIG_AD5686) += ad5686.o obj-$(CONFIG_MAX517) += max517.o +obj-$(CONFIG_MCP4725) += mcp4725.o diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c new file mode 100644 index 000000000000..e0e168bd5b45 --- /dev/null +++ b/drivers/iio/dac/mcp4725.c @@ -0,0 +1,227 @@ +/* + * mcp4725.c - Support for Microchip MCP4725 + * + * Copyright (C) 2012 Peter Meerwald + * + * Based on max517 by Roland Stigge + * + * 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. + * + * driver for the Microchip I2C 12-bit digital-to-analog converter (DAC) + * (7-bit I2C slave address 0x60, the three LSBs can be configured in + * hardware) + * + * writing the DAC value to EEPROM is not supported + */ + +#include +#include +#include +#include + +#include +#include + +#include + +#define MCP4725_DRV_NAME "mcp4725" + +struct mcp4725_data { + struct i2c_client *client; + u16 vref_mv; + u16 dac_value; +}; + +#ifdef CONFIG_PM_SLEEP +static int mcp4725_suspend(struct device *dev) +{ + u8 outbuf[2]; + + outbuf[0] = 0x3 << 4; /* power-down bits, 500 kOhm resistor */ + outbuf[1] = 0; + + return i2c_master_send(to_i2c_client(dev), outbuf, 2); +} + +static int mcp4725_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct mcp4725_data *data = iio_priv(indio_dev); + u8 outbuf[2]; + + /* restore previous DAC value */ + outbuf[0] = (data->dac_value >> 8) & 0xf; + outbuf[1] = data->dac_value & 0xff; + + return i2c_master_send(to_i2c_client(dev), outbuf, 2); +} + +static SIMPLE_DEV_PM_OPS(mcp4725_pm_ops, mcp4725_suspend, mcp4725_resume); +#define MCP4725_PM_OPS (&mcp4725_pm_ops) +#else +#define MCP4725_PM_OPS NULL +#endif + +static const struct iio_chan_spec mcp4725_channel = { + .type = IIO_VOLTAGE, + .indexed = 1, + .output = 1, + .channel = 0, + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT, + .scan_type = IIO_ST('u', 12, 16, 0), +}; + +static int mcp4725_set_value(struct iio_dev *indio_dev, int val) +{ + struct mcp4725_data *data = iio_priv(indio_dev); + u8 outbuf[2]; + int ret; + + if (val >= (1 << 12) || val < 0) + return -EINVAL; + + outbuf[0] = (val >> 8) & 0xf; + outbuf[1] = val & 0xff; + + ret = i2c_master_send(data->client, outbuf, 2); + if (ret < 0) + return ret; + else if (ret != 2) + return -EIO; + else + return 0; +} + +static int mcp4725_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mcp4725_data *data = iio_priv(indio_dev); + unsigned long scale_uv; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + *val = data->dac_value; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + scale_uv = (data->vref_mv * 1000) >> 12; + *val = scale_uv / 1000000; + *val2 = scale_uv % 1000000; + return IIO_VAL_INT_PLUS_MICRO; + } + return -EINVAL; +} + +static int mcp4725_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct mcp4725_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = mcp4725_set_value(indio_dev, val); + data->dac_value = val; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct iio_info mcp4725_info = { + .read_raw = mcp4725_read_raw, + .write_raw = mcp4725_write_raw, + .driver_module = THIS_MODULE, +}; + +static int __devinit mcp4725_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mcp4725_data *data; + struct iio_dev *indio_dev; + struct mcp4725_platform_data *platform_data = client->dev.platform_data; + u8 inbuf[3]; + int err; + + if (!platform_data || !platform_data->vref_mv) { + dev_err(&client->dev, "invalid platform data"); + err = -EINVAL; + goto exit; + } + + indio_dev = iio_device_alloc(sizeof(*data)); + if (indio_dev == NULL) { + err = -ENOMEM; + goto exit; + } + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + + indio_dev->dev.parent = &client->dev; + indio_dev->info = &mcp4725_info; + indio_dev->channels = &mcp4725_channel; + indio_dev->num_channels = 1; + indio_dev->modes = INDIO_DIRECT_MODE; + + data->vref_mv = platform_data->vref_mv; + + /* read current DAC value */ + err = i2c_master_recv(client, inbuf, 3); + if (err < 0) { + dev_err(&client->dev, "failed to read DAC value"); + goto exit_free_device; + } + data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4); + + err = iio_device_register(indio_dev); + if (err) + goto exit_free_device; + + dev_info(&client->dev, "MCP4725 DAC registered\n"); + + return 0; + +exit_free_device: + iio_device_free(indio_dev); +exit: + return err; +} + +static int __devexit mcp4725_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + iio_device_unregister(indio_dev); + iio_device_free(indio_dev); + + return 0; +} + +static const struct i2c_device_id mcp4725_id[] = { + { "mcp4725", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mcp4725_id); + +static struct i2c_driver mcp4725_driver = { + .driver = { + .name = MCP4725_DRV_NAME, + .pm = MCP4725_PM_OPS, + }, + .probe = mcp4725_probe, + .remove = __devexit_p(mcp4725_remove), + .id_table = mcp4725_id, +}; +module_i2c_driver(mcp4725_driver); + +MODULE_AUTHOR("Peter Meerwald "); +MODULE_DESCRIPTION("MCP4725 12-bit DAC"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/iio/dac/mcp4725.h b/include/linux/iio/dac/mcp4725.h new file mode 100644 index 000000000000..91530e6611e9 --- /dev/null +++ b/include/linux/iio/dac/mcp4725.h @@ -0,0 +1,16 @@ +/* + * MCP4725 DAC driver + * + * Copyright (C) 2012 Peter Meerwald + * + * Licensed under the GPL-2 or later. + */ + +#ifndef IIO_DAC_MCP4725_H_ +#define IIO_DAC_MCP4725_H_ + +struct mcp4725_platform_data { + u16 vref_mv; +}; + +#endif /* IIO_DAC_MCP4725_H_ */ -- cgit v1.2.3 From 988bb033d703c8b2e0e71c63f3f55616a0220ced Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Tue, 12 Jun 2012 14:39:37 +0200 Subject: iio: drop comment about 'real' channels Signed-off-by: Peter Meerwald Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- include/linux/iio/types.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index 1b073b1cc7c2..d086736a9033 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -11,7 +11,6 @@ #define _IIO_TYPES_H_ enum iio_chan_type { - /* real channel types */ IIO_VOLTAGE, IIO_CURRENT, IIO_POWER, -- cgit v1.2.3 From 1787948873fd2ca730fb5891b6a2ade510367ace Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Tue, 12 Jun 2012 15:38:45 +0200 Subject: iio: fix typos in iio.h v2: * "used in in-kernel" (Jonathan Cameron) Signed-off-by: Peter Meerwald Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- include/linux/iio/iio.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 944c63511731..01afaa08ff21 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -131,7 +131,7 @@ struct iio_chan_spec_ext_info { /** * struct iio_enum - Enum channel info attribute - * items: A array of strings. + * items: An array of strings. * num_items: Length of the item array. * set: Set callback function, may be NULL. * get: Get callback function, may be NULL. @@ -218,7 +218,7 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, * @extend_name: Allows labeling of channel attributes with an * informative name. Note this has no effect codes etc, * unlike modifiers. - * @datasheet_name: A name used in in kernel mapping of channels. It should + * @datasheet_name: A name used in in-kernel mapping of channels. It should * correspond to the first name that the channel is referred * to by in the datasheet (e.g. IND), or the nearest * possible compound name (e.g. IND-INC). @@ -393,7 +393,7 @@ struct iio_buffer_setup_ops { * @trig: [INTERN] current device trigger (buffer modes) * @pollfunc: [DRIVER] function run on trigger being received * @channels: [DRIVER] channel specification structure table - * @num_channels: [DRIVER] number of chanels specified in @channels. + * @num_channels: [DRIVER] number of channels specified in @channels. * @channel_attr_list: [INTERN] keep track of automatically created channel * attributes * @chan_attr_group: [INTERN] group for all attrs in base directory -- cgit v1.2.3 From a16561c6f713630270b905297f3eedf02db7fa98 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Tue, 12 Jun 2012 15:38:46 +0200 Subject: iio: clarify channel and indexed in struct iio_chan_spec Signed-off-by: Peter Meerwald Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- include/linux/iio/iio.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 01afaa08ff21..9a9e6d52a6de 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -196,7 +196,7 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, /** * struct iio_chan_spec - specification of a single channel * @type: What type of measurement is the channel making. - * @channel: What number or name do we wish to assign the channel. + * @channel: What number do we wish to assign the channel. * @channel2: If there is a second number for a differential * channel then this is it. If modified is set then the * value here specifies the modifier. @@ -227,9 +227,8 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, * channel2. Examples are IIO_MOD_X for axial sensors about * the 'x' axis. * @indexed: Specify the channel has a numerical index. If not, - * the value in channel will be suppressed for attribute - * but not for event codes. Typically set it to 0 when - * the index is false. + * the channel index number will be suppressed for sysfs + * attributes but not for event codes. * @differential: Channel is differential. */ struct iio_chan_spec { -- cgit v1.2.3 From f4c349395e8ad4fe07f1222502568f0d9d5d1dfc Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Tue, 12 Jun 2012 15:38:47 +0200 Subject: iio: mark struct iio_enum elements with @ in comment Signed-off-by: Peter Meerwald Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- include/linux/iio/iio.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 9a9e6d52a6de..bd3e7217ceee 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -131,10 +131,10 @@ struct iio_chan_spec_ext_info { /** * struct iio_enum - Enum channel info attribute - * items: An array of strings. - * num_items: Length of the item array. - * set: Set callback function, may be NULL. - * get: Get callback function, may be NULL. + * @items: An array of strings. + * @num_items: Length of the item array. + * @set: Set callback function, may be NULL. + * @get: Get callback function, may be NULL. * * The iio_enum struct can be used to implement enum style channel attributes. * Enum style attributes are those which have a set of strings which map to -- cgit v1.2.3 From 7dcd7b60724cab551deba54f31752fa8959efab4 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Tue, 12 Jun 2012 15:38:48 +0200 Subject: iio: cleanup iio/iio.h indentation of parameter description, fix parameter name (@dev -> @indio_dev) in comments, IIO device info structure -> IIO device structure Signed-off-by: Peter Meerwald Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- include/linux/iio/iio.h | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index bd3e7217ceee..ae0cad908182 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -131,10 +131,10 @@ struct iio_chan_spec_ext_info { /** * struct iio_enum - Enum channel info attribute - * @items: An array of strings. - * @num_items: Length of the item array. - * @set: Set callback function, may be NULL. - * @get: Get callback function, may be NULL. + * @items: An array of strings. + * @num_items: Length of the item array. + * @set: Set callback function, may be NULL. + * @get: Get callback function, may be NULL. * * The iio_enum struct can be used to implement enum style channel attributes. * Enum style attributes are those which have a set of strings which map to @@ -162,9 +162,9 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, /** * IIO_ENUM() - Initialize enum extended channel attribute - * @_name: Attribute name - * @_shared: Whether the attribute is shared between all channels - * @_e: Pointer to a iio_enum struct + * @_name: Attribute name + * @_shared: Whether the attribute is shared between all channels + * @_e: Pointer to a iio_enum struct * * This should usually be used together with IIO_ENUM_AVAILABLE() */ @@ -179,8 +179,8 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, /** * IIO_ENUM_AVAILABLE() - Initialize enum available extended channel attribute - * @_name: Attribute name ("_available" will be appended to the name) - * @_e: Pointer to a iio_enum struct + * @_name: Attribute name ("_available" will be appended to the name) + * @_e: Pointer to a iio_enum struct * * Creates a read only attribute which list all the available enum items in a * space separated list. This should usually be used together with IIO_ENUM() @@ -201,7 +201,7 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, * channel then this is it. If modified is set then the * value here specifies the modifier. * @address: Driver specific identifier. - * @scan_index: Monotonic index to give ordering in scans when read + * @scan_index: Monotonic index to give ordering in scans when read * from a buffer. * @scan_type: Sign: 's' or 'u' to specify signed or unsigned * realbits: Number of valid bits of data @@ -211,7 +211,7 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, * endianness: little or big endian * @info_mask: What information is to be exported about this channel. * This includes calibbias, scale etc. - * @event_mask: What events can this channel produce. + * @event_mask: What events can this channel produce. * @ext_info: Array of extended info attributes for this channel. * The array is NULL terminated, the last element should * have it's name field set to NULL. @@ -482,7 +482,7 @@ extern struct bus_type iio_bus_type; /** * iio_device_put() - reference counted deallocation of struct device - * @dev: the iio_device containing the device + * @indio_dev: IIO device structure containing the device **/ static inline void iio_device_put(struct iio_dev *indio_dev) { @@ -492,7 +492,7 @@ static inline void iio_device_put(struct iio_dev *indio_dev) /** * dev_to_iio_dev() - Get IIO device struct from a device struct - * @dev: The device embedded in the IIO device + * @dev: The device embedded in the IIO device * * Note: The device must be a IIO device, otherwise the result is undefined. */ @@ -503,7 +503,7 @@ static inline struct iio_dev *dev_to_iio_dev(struct device *dev) /** * iio_device_get() - increment reference count for the device - * @indio_dev: IIO device structure + * @indio_dev: IIO device structure * * Returns: The passed IIO device **/ @@ -516,7 +516,7 @@ static inline struct iio_dev *iio_device_get(struct iio_dev *indio_dev) #define IIO_ALIGN L1_CACHE_BYTES /** * iio_device_alloc() - allocate an iio_dev from a driver - * @sizeof_priv: Space to allocate for private structure. + * @sizeof_priv: Space to allocate for private structure. **/ struct iio_dev *iio_device_alloc(int sizeof_priv); @@ -533,13 +533,13 @@ static inline struct iio_dev *iio_priv_to_dev(void *priv) /** * iio_device_free() - free an iio_dev from a driver - * @dev: the iio_dev associated with the device + * @indio_dev: the iio_dev associated with the device **/ void iio_device_free(struct iio_dev *indio_dev); /** * iio_buffer_enabled() - helper function to test if the buffer is enabled - * @indio_dev: IIO device info structure for device + * @indio_dev: IIO device structure for device **/ static inline bool iio_buffer_enabled(struct iio_dev *indio_dev) { @@ -549,7 +549,7 @@ static inline bool iio_buffer_enabled(struct iio_dev *indio_dev) /** * iio_get_debugfs_dentry() - helper function to get the debugfs_dentry - * @indio_dev: IIO device info structure for device + * @indio_dev: IIO device structure for device **/ #if defined(CONFIG_DEBUG_FS) static inline struct dentry *iio_get_debugfs_dentry(struct iio_dev *indio_dev) -- cgit v1.2.3 From 7a5145965c9807732135630642c49f280b375f56 Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Mon, 11 Jun 2012 21:57:13 +0200 Subject: serial/8250: Add LPC3220 standard UART type LPC32xx has "Standard" UARTs that are actually 16550A compatible but have bigger FIFOs. Since the already supported 16X50 line still doesn't match here, we agreed on adding a new type. Signed-off-by: Roland Stigge Acked-by: Alan Cox Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250.c | 8 ++++++++ include/linux/serial_core.h | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c index 47d061b9ad4d..349d12c55169 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c @@ -282,6 +282,14 @@ static const struct serial8250_config uart_config[] = { .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, .flags = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR, }, + [PORT_LPC3220] = { + .name = "LPC3220", + .fifo_size = 64, + .tx_loadsz = 32, + .fcr = UART_FCR_DMA_SELECT | UART_FCR_ENABLE_FIFO | + UART_FCR_R_TRIG_00 | UART_FCR_T_TRIG_00, + .flags = UART_CAP_FIFO, + }, }; /* Uart divisor latch read */ diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 65db9928e15f..0253c2022e53 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -47,7 +47,8 @@ #define PORT_U6_16550A 19 /* ST-Ericsson U6xxx internal UART */ #define PORT_TEGRA 20 /* NVIDIA Tegra internal UART */ #define PORT_XR17D15X 21 /* Exar XR17D15x UART */ -#define PORT_MAX_8250 21 /* max port ID */ +#define PORT_LPC3220 22 /* NXP LPC32xx SoC "Standard" UART */ +#define PORT_MAX_8250 22 /* max port ID */ /* * ARM specific type numbers. These are not currently guaranteed -- cgit v1.2.3 From a3cc9fcff84c4c8aaecda2420acd89a1418d57e9 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 4 Jun 2012 13:35:16 +0200 Subject: TTY: ircomm, add tty_port And use close/open_wait from there. Signed-off-by: Jiri Slaby Cc: Samuel Ortiz Cc: netdev@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- include/net/irda/ircomm_tty.h | 3 +-- net/irda/ircomm/ircomm_tty.c | 21 +++++++++++---------- net/irda/ircomm/ircomm_tty_attach.c | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/irda/ircomm_tty.h b/include/net/irda/ircomm_tty.h index 59ba38bc400f..365fa6ec5298 100644 --- a/include/net/irda/ircomm_tty.h +++ b/include/net/irda/ircomm_tty.h @@ -62,6 +62,7 @@ */ struct ircomm_tty_cb { irda_queue_t queue; /* Must be first */ + struct tty_port port; magic_t magic; int state; /* Connect state */ @@ -97,8 +98,6 @@ struct ircomm_tty_cb { void *skey; void *ckey; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; struct timer_list watchdog_timer; struct work_struct tqueue; diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c index 6b9d5a0e42f9..8eeaa8b7f4a6 100644 --- a/net/irda/ircomm/ircomm_tty.c +++ b/net/irda/ircomm/ircomm_tty.c @@ -278,7 +278,7 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, */ retval = 0; - add_wait_queue(&self->open_wait, &wait); + add_wait_queue(&self->port.open_wait, &wait); IRDA_DEBUG(2, "%s(%d):block_til_ready before block on %s open_count=%d\n", __FILE__,__LINE__, tty->driver->name, self->open_count ); @@ -336,7 +336,7 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, } __set_current_state(TASK_RUNNING); - remove_wait_queue(&self->open_wait, &wait); + remove_wait_queue(&self->port.open_wait, &wait); if (extra_count) { /* ++ is not atomic, so this should be protected - Jean II */ @@ -381,6 +381,7 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) return -ENOMEM; } + tty_port_init(&self->port); self->magic = IRCOMM_TTY_MAGIC; self->flow = FLOW_STOP; @@ -393,8 +394,6 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) /* Init some important stuff */ init_timer(&self->watchdog_timer); - init_waitqueue_head(&self->open_wait); - init_waitqueue_head(&self->close_wait); spin_lock_init(&self->spinlock); /* @@ -408,6 +407,7 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) tty->termios->c_oflag = 0; /* Insert into hash */ + /* FIXME there is a window from find to here */ hashbin_insert(ircomm_tty, (irda_queue_t *) self, line, NULL); } /* ++ is not atomic, so this should be protected - Jean II */ @@ -438,7 +438,8 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) * probably better sleep uninterruptible? */ - if (wait_event_interruptible(self->close_wait, !test_bit(ASYNC_B_CLOSING, &self->flags))) { + if (wait_event_interruptible(self->port.close_wait, + !test_bit(ASYNC_B_CLOSING, &self->flags))) { IRDA_WARNING("%s - got signal while blocking on ASYNC_CLOSING!\n", __func__); return -ERESTARTSYS; @@ -559,11 +560,11 @@ static void ircomm_tty_close(struct tty_struct *tty, struct file *filp) if (self->blocked_open) { if (self->close_delay) schedule_timeout_interruptible(self->close_delay); - wake_up_interruptible(&self->open_wait); + wake_up_interruptible(&self->port.open_wait); } self->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&self->close_wait); + wake_up_interruptible(&self->port.close_wait); } /* @@ -1011,7 +1012,7 @@ static void ircomm_tty_hangup(struct tty_struct *tty) self->open_count = 0; spin_unlock_irqrestore(&self->spinlock, flags); - wake_up_interruptible(&self->open_wait); + wake_up_interruptible(&self->port.open_wait); } /* @@ -1084,7 +1085,7 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self) (status & IRCOMM_CD) ? "on" : "off"); if (status & IRCOMM_CD) { - wake_up_interruptible(&self->open_wait); + wake_up_interruptible(&self->port.open_wait); } else { IRDA_DEBUG(2, "%s(), Doing serial hangup..\n", __func__ ); @@ -1103,7 +1104,7 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self) tty->hw_stopped = 0; /* Wake up processes blocked on open */ - wake_up_interruptible(&self->open_wait); + wake_up_interruptible(&self->port.open_wait); schedule_work(&self->tqueue); return; diff --git a/net/irda/ircomm/ircomm_tty_attach.c b/net/irda/ircomm/ircomm_tty_attach.c index b65d66e0d817..bb1e9356bb18 100644 --- a/net/irda/ircomm/ircomm_tty_attach.c +++ b/net/irda/ircomm/ircomm_tty_attach.c @@ -575,7 +575,7 @@ void ircomm_tty_link_established(struct ircomm_tty_cb *self) self->tty->hw_stopped = 0; /* Wake up processes blocked on open */ - wake_up_interruptible(&self->open_wait); + wake_up_interruptible(&self->port.open_wait); } schedule_work(&self->tqueue); -- cgit v1.2.3 From 2a0213cb1e1ca6b2838595b0d70f09ecc4953ba9 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 4 Jun 2012 13:35:17 +0200 Subject: TTY: ircomm, use close times from tty_port Switch to tty_port->close_delay and closing_wait. Signed-off-by: Jiri Slaby Cc: Samuel Ortiz Cc: netdev@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- include/net/irda/ircomm_tty.h | 3 --- net/irda/ircomm/ircomm_tty.c | 10 ++++------ net/irda/ircomm/ircomm_tty_ioctl.c | 4 ++-- 3 files changed, 6 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/irda/ircomm_tty.h b/include/net/irda/ircomm_tty.h index 365fa6ec5298..b4184d089cc4 100644 --- a/include/net/irda/ircomm_tty.h +++ b/include/net/irda/ircomm_tty.h @@ -101,9 +101,6 @@ struct ircomm_tty_cb { struct timer_list watchdog_timer; struct work_struct tqueue; - unsigned short close_delay; - unsigned short closing_wait; /* time to wait before closing */ - int open_count; int blocked_open; /* # of blocked opens */ diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c index 8eeaa8b7f4a6..61e0adcab964 100644 --- a/net/irda/ircomm/ircomm_tty.c +++ b/net/irda/ircomm/ircomm_tty.c @@ -389,8 +389,6 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) INIT_WORK(&self->tqueue, ircomm_tty_do_softint); self->max_header_size = IRCOMM_TTY_HDR_UNINITIALISED; self->max_data_size = IRCOMM_TTY_DATA_UNINITIALISED; - self->close_delay = 5*HZ/10; - self->closing_wait = 30*HZ; /* Init some important stuff */ init_timer(&self->watchdog_timer); @@ -546,8 +544,8 @@ static void ircomm_tty_close(struct tty_struct *tty, struct file *filp) * the line discipline to only process XON/XOFF characters. */ tty->closing = 1; - if (self->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent_from_close(tty, self->closing_wait); + if (self->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent_from_close(tty, self->port.closing_wait); ircomm_tty_shutdown(self); @@ -558,8 +556,8 @@ static void ircomm_tty_close(struct tty_struct *tty, struct file *filp) self->tty = NULL; if (self->blocked_open) { - if (self->close_delay) - schedule_timeout_interruptible(self->close_delay); + if (self->port.close_delay) + schedule_timeout_interruptible(self->port.close_delay); wake_up_interruptible(&self->port.open_wait); } diff --git a/net/irda/ircomm/ircomm_tty_ioctl.c b/net/irda/ircomm/ircomm_tty_ioctl.c index d0667d68351d..a6d25e37b6b2 100644 --- a/net/irda/ircomm/ircomm_tty_ioctl.c +++ b/net/irda/ircomm/ircomm_tty_ioctl.c @@ -272,8 +272,8 @@ static int ircomm_tty_get_serial_info(struct ircomm_tty_cb *self, info.line = self->line; info.flags = self->flags; info.baud_base = self->settings.data_rate; - info.close_delay = self->close_delay; - info.closing_wait = self->closing_wait; + info.close_delay = self->port.close_delay; + info.closing_wait = self->port.closing_wait; /* For compatibility */ info.type = PORT_16550A; -- cgit v1.2.3 From 580d27b449cb8f540bba1cc54066bb44f4e6242d Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 4 Jun 2012 13:35:18 +0200 Subject: TTY: ircomm, use open counts from tty_port Switch to tty_port->count and blocked_open. Signed-off-by: Jiri Slaby Cc: Samuel Ortiz Cc: netdev@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- include/net/irda/ircomm_tty.h | 3 --- net/irda/ircomm/ircomm_tty.c | 42 +++++++++++++++++++++--------------------- 2 files changed, 21 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/net/irda/ircomm_tty.h b/include/net/irda/ircomm_tty.h index b4184d089cc4..5e94bad92620 100644 --- a/include/net/irda/ircomm_tty.h +++ b/include/net/irda/ircomm_tty.h @@ -101,9 +101,6 @@ struct ircomm_tty_cb { struct timer_list watchdog_timer; struct work_struct tqueue; - int open_count; - int blocked_open; /* # of blocked opens */ - /* Protect concurent access to : * o self->open_count * o self->ctrl_skb diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c index 61e0adcab964..787578f9f312 100644 --- a/net/irda/ircomm/ircomm_tty.c +++ b/net/irda/ircomm/ircomm_tty.c @@ -272,7 +272,7 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, /* Wait for carrier detect and the line to become * free (i.e., not in use by the callout). While we are in - * this loop, self->open_count is dropped by one, so that + * this loop, self->port.count is dropped by one, so that * mgsl_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ @@ -281,16 +281,16 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, add_wait_queue(&self->port.open_wait, &wait); IRDA_DEBUG(2, "%s(%d):block_til_ready before block on %s open_count=%d\n", - __FILE__,__LINE__, tty->driver->name, self->open_count ); + __FILE__, __LINE__, tty->driver->name, self->port.count); - /* As far as I can see, we protect open_count - Jean II */ + /* As far as I can see, we protect port.count - Jean II */ spin_lock_irqsave(&self->spinlock, flags); if (!tty_hung_up_p(filp)) { extra_count = 1; - self->open_count--; + self->port.count--; } spin_unlock_irqrestore(&self->spinlock, flags); - self->blocked_open++; + self->port.blocked_open++; while (1) { if (tty->termios->c_cflag & CBAUD) { @@ -330,7 +330,7 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, } IRDA_DEBUG(1, "%s(%d):block_til_ready blocking on %s open_count=%d\n", - __FILE__,__LINE__, tty->driver->name, self->open_count ); + __FILE__, __LINE__, tty->driver->name, self->port.count); schedule(); } @@ -341,13 +341,13 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, if (extra_count) { /* ++ is not atomic, so this should be protected - Jean II */ spin_lock_irqsave(&self->spinlock, flags); - self->open_count++; + self->port.count++; spin_unlock_irqrestore(&self->spinlock, flags); } - self->blocked_open--; + self->port.blocked_open--; IRDA_DEBUG(1, "%s(%d):block_til_ready after blocking on %s open_count=%d\n", - __FILE__,__LINE__, tty->driver->name, self->open_count); + __FILE__, __LINE__, tty->driver->name, self->port.count); if (!retval) self->flags |= ASYNC_NORMAL_ACTIVE; @@ -410,14 +410,14 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) } /* ++ is not atomic, so this should be protected - Jean II */ spin_lock_irqsave(&self->spinlock, flags); - self->open_count++; + self->port.count++; tty->driver_data = self; self->tty = tty; spin_unlock_irqrestore(&self->spinlock, flags); IRDA_DEBUG(1, "%s(), %s%d, count = %d\n", __func__ , tty->driver->name, - self->line, self->open_count); + self->line, self->port.count); /* Not really used by us, but lets do it anyway */ self->tty->low_latency = (self->flags & ASYNC_LOW_LATENCY) ? 1 : 0; @@ -504,7 +504,7 @@ static void ircomm_tty_close(struct tty_struct *tty, struct file *filp) return; } - if ((tty->count == 1) && (self->open_count != 1)) { + if ((tty->count == 1) && (self->port.count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. state->count should always @@ -514,16 +514,16 @@ static void ircomm_tty_close(struct tty_struct *tty, struct file *filp) */ IRDA_DEBUG(0, "%s(), bad serial port count; " "tty->count is 1, state->count is %d\n", __func__ , - self->open_count); - self->open_count = 1; + self->port.count); + self->port.count = 1; } - if (--self->open_count < 0) { + if (--self->port.count < 0) { IRDA_ERROR("%s(), bad serial port count for ttys%d: %d\n", - __func__, self->line, self->open_count); - self->open_count = 0; + __func__, self->line, self->port.count); + self->port.count = 0; } - if (self->open_count) { + if (self->port.count) { spin_unlock_irqrestore(&self->spinlock, flags); IRDA_DEBUG(0, "%s(), open count > 0\n", __func__ ); @@ -555,7 +555,7 @@ static void ircomm_tty_close(struct tty_struct *tty, struct file *filp) tty->closing = 0; self->tty = NULL; - if (self->blocked_open) { + if (self->port.blocked_open) { if (self->port.close_delay) schedule_timeout_interruptible(self->port.close_delay); wake_up_interruptible(&self->port.open_wait); @@ -1007,7 +1007,7 @@ static void ircomm_tty_hangup(struct tty_struct *tty) spin_lock_irqsave(&self->spinlock, flags); self->flags &= ~ASYNC_NORMAL_ACTIVE; self->tty = NULL; - self->open_count = 0; + self->port.count = 0; spin_unlock_irqrestore(&self->spinlock, flags); wake_up_interruptible(&self->port.open_wait); @@ -1354,7 +1354,7 @@ static void ircomm_tty_line_info(struct ircomm_tty_cb *self, struct seq_file *m) seq_putc(m, '\n'); seq_printf(m, "Role: %s\n", self->client ? "client" : "server"); - seq_printf(m, "Open count: %d\n", self->open_count); + seq_printf(m, "Open count: %d\n", self->port.count); seq_printf(m, "Max data size: %d\n", self->max_data_size); seq_printf(m, "Max header size: %d\n", self->max_header_size); -- cgit v1.2.3 From 849d5a997fe6a9e44401daed62a98121390ec0d3 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 4 Jun 2012 13:35:19 +0200 Subject: TTY: ircomm, use flags from tty_port Switch to tty_port->flags. And while at it, remove redefined flags for them. Signed-off-by: Jiri Slaby Cc: Samuel Ortiz Cc: netdev@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- include/net/irda/ircomm_tty.h | 6 ----- net/irda/ircomm/ircomm_tty.c | 46 ++++++++++++++++++------------------- net/irda/ircomm/ircomm_tty_attach.c | 5 ++-- net/irda/ircomm/ircomm_tty_ioctl.c | 10 ++++---- 4 files changed, 31 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/include/net/irda/ircomm_tty.h b/include/net/irda/ircomm_tty.h index 5e94bad92620..e4db3b5f6e4c 100644 --- a/include/net/irda/ircomm_tty.h +++ b/include/net/irda/ircomm_tty.h @@ -52,11 +52,6 @@ /* Same for payload size. See qos.c for the smallest max data size */ #define IRCOMM_TTY_DATA_UNINITIALISED (64 - IRCOMM_TTY_HDR_UNINITIALISED) -/* Those are really defined in include/linux/serial.h - Jean II */ -#define ASYNC_B_INITIALIZED 31 /* Serial port was initialized */ -#define ASYNC_B_NORMAL_ACTIVE 29 /* Normal device is active */ -#define ASYNC_B_CLOSING 27 /* Serial port is closing */ - /* * IrCOMM TTY driver state */ @@ -81,7 +76,6 @@ struct ircomm_tty_cb { LOCAL_FLOW flow; /* IrTTP flow status */ int line; - unsigned long flags; __u8 dlsap_sel; __u8 slsap_sel; diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c index 787578f9f312..8e61026b9dd4 100644 --- a/net/irda/ircomm/ircomm_tty.c +++ b/net/irda/ircomm/ircomm_tty.c @@ -194,7 +194,7 @@ static int ircomm_tty_startup(struct ircomm_tty_cb *self) IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); /* Check if already open */ - if (test_and_set_bit(ASYNC_B_INITIALIZED, &self->flags)) { + if (test_and_set_bit(ASYNCB_INITIALIZED, &self->port.flags)) { IRDA_DEBUG(2, "%s(), already open so break out!\n", __func__ ); return 0; } @@ -231,7 +231,7 @@ static int ircomm_tty_startup(struct ircomm_tty_cb *self) return 0; err: - clear_bit(ASYNC_B_INITIALIZED, &self->flags); + clear_bit(ASYNCB_INITIALIZED, &self->port.flags); return ret; } @@ -260,7 +260,7 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, */ if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){ /* nonblock mode is set or port is not enabled */ - self->flags |= ASYNC_NORMAL_ACTIVE; + self->port.flags |= ASYNC_NORMAL_ACTIVE; IRDA_DEBUG(1, "%s(), O_NONBLOCK requested!\n", __func__ ); return 0; } @@ -306,8 +306,8 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, current->state = TASK_INTERRUPTIBLE; if (tty_hung_up_p(filp) || - !test_bit(ASYNC_B_INITIALIZED, &self->flags)) { - retval = (self->flags & ASYNC_HUP_NOTIFY) ? + !test_bit(ASYNCB_INITIALIZED, &self->port.flags)) { + retval = (self->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS; break; } @@ -317,7 +317,7 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, * specified, we cannot return before the IrCOMM link is * ready */ - if (!test_bit(ASYNC_B_CLOSING, &self->flags) && + if (!test_bit(ASYNCB_CLOSING, &self->port.flags) && (do_clocal || (self->settings.dce & IRCOMM_CD)) && self->state == IRCOMM_TTY_READY) { @@ -350,7 +350,7 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, __FILE__, __LINE__, tty->driver->name, self->port.count); if (!retval) - self->flags |= ASYNC_NORMAL_ACTIVE; + self->port.flags |= ASYNC_NORMAL_ACTIVE; return retval; } @@ -420,13 +420,13 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) self->line, self->port.count); /* Not really used by us, but lets do it anyway */ - self->tty->low_latency = (self->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + tty->low_latency = (self->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; /* * If the port is the middle of closing, bail out now */ if (tty_hung_up_p(filp) || - test_bit(ASYNC_B_CLOSING, &self->flags)) { + test_bit(ASYNCB_CLOSING, &self->port.flags)) { /* Hm, why are we blocking on ASYNC_CLOSING if we * do return -EAGAIN/-ERESTARTSYS below anyway? @@ -437,14 +437,14 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) */ if (wait_event_interruptible(self->port.close_wait, - !test_bit(ASYNC_B_CLOSING, &self->flags))) { + !test_bit(ASYNCB_CLOSING, &self->port.flags))) { IRDA_WARNING("%s - got signal while blocking on ASYNC_CLOSING!\n", __func__); return -ERESTARTSYS; } #ifdef SERIAL_DO_RESTART - return (self->flags & ASYNC_HUP_NOTIFY) ? + return (self->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS; #else return -EAGAIN; @@ -531,7 +531,7 @@ static void ircomm_tty_close(struct tty_struct *tty, struct file *filp) } /* Hum... Should be test_and_set_bit ??? - Jean II */ - set_bit(ASYNC_B_CLOSING, &self->flags); + set_bit(ASYNCB_CLOSING, &self->port.flags); /* We need to unlock here (we were unlocking at the end of this * function), because tty_wait_until_sent() may schedule. @@ -561,7 +561,7 @@ static void ircomm_tty_close(struct tty_struct *tty, struct file *filp) wake_up_interruptible(&self->port.open_wait); } - self->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + self->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); wake_up_interruptible(&self->port.close_wait); } @@ -954,7 +954,7 @@ static void ircomm_tty_shutdown(struct ircomm_tty_cb *self) IRDA_DEBUG(0, "%s()\n", __func__ ); - if (!test_and_clear_bit(ASYNC_B_INITIALIZED, &self->flags)) + if (!test_and_clear_bit(ASYNCB_INITIALIZED, &self->port.flags)) return; ircomm_tty_detach_cable(self); @@ -1005,7 +1005,7 @@ static void ircomm_tty_hangup(struct tty_struct *tty) /* I guess we need to lock here - Jean II */ spin_lock_irqsave(&self->spinlock, flags); - self->flags &= ~ASYNC_NORMAL_ACTIVE; + self->port.flags &= ~ASYNC_NORMAL_ACTIVE; self->tty = NULL; self->port.count = 0; spin_unlock_irqrestore(&self->spinlock, flags); @@ -1077,7 +1077,7 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self) if (status & IRCOMM_DCE_DELTA_ANY) { /*wake_up_interruptible(&self->delta_msr_wait);*/ } - if ((self->flags & ASYNC_CHECK_CD) && (status & IRCOMM_DELTA_CD)) { + if ((self->port.flags & ASYNC_CHECK_CD) && (status & IRCOMM_DELTA_CD)) { IRDA_DEBUG(2, "%s(), ircomm%d CD now %s...\n", __func__ , self->line, (status & IRCOMM_CD) ? "on" : "off"); @@ -1094,7 +1094,7 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self) return; } } - if (self->flags & ASYNC_CTS_FLOW) { + if (self->port.flags & ASYNC_CTS_FLOW) { if (tty->hw_stopped) { if (status & IRCOMM_CTS) { IRDA_DEBUG(2, @@ -1327,27 +1327,27 @@ static void ircomm_tty_line_info(struct ircomm_tty_cb *self, struct seq_file *m) seq_puts(m, "Flags:"); sep = ' '; - if (self->flags & ASYNC_CTS_FLOW) { + if (self->port.flags & ASYNC_CTS_FLOW) { seq_printf(m, "%cASYNC_CTS_FLOW", sep); sep = '|'; } - if (self->flags & ASYNC_CHECK_CD) { + if (self->port.flags & ASYNC_CHECK_CD) { seq_printf(m, "%cASYNC_CHECK_CD", sep); sep = '|'; } - if (self->flags & ASYNC_INITIALIZED) { + if (self->port.flags & ASYNC_INITIALIZED) { seq_printf(m, "%cASYNC_INITIALIZED", sep); sep = '|'; } - if (self->flags & ASYNC_LOW_LATENCY) { + if (self->port.flags & ASYNC_LOW_LATENCY) { seq_printf(m, "%cASYNC_LOW_LATENCY", sep); sep = '|'; } - if (self->flags & ASYNC_CLOSING) { + if (self->port.flags & ASYNC_CLOSING) { seq_printf(m, "%cASYNC_CLOSING", sep); sep = '|'; } - if (self->flags & ASYNC_NORMAL_ACTIVE) { + if (self->port.flags & ASYNC_NORMAL_ACTIVE) { seq_printf(m, "%cASYNC_NORMAL_ACTIVE", sep); sep = '|'; } diff --git a/net/irda/ircomm/ircomm_tty_attach.c b/net/irda/ircomm/ircomm_tty_attach.c index bb1e9356bb18..bed311af7311 100644 --- a/net/irda/ircomm/ircomm_tty_attach.c +++ b/net/irda/ircomm/ircomm_tty_attach.c @@ -566,7 +566,8 @@ void ircomm_tty_link_established(struct ircomm_tty_cb *self) * will have to wait for the peer device (DCE) to raise the CTS * line. */ - if ((self->flags & ASYNC_CTS_FLOW) && ((self->settings.dce & IRCOMM_CTS) == 0)) { + if ((self->port.flags & ASYNC_CTS_FLOW) && + ((self->settings.dce & IRCOMM_CTS) == 0)) { IRDA_DEBUG(0, "%s(), waiting for CTS ...\n", __func__ ); return; } else { @@ -977,7 +978,7 @@ static int ircomm_tty_state_ready(struct ircomm_tty_cb *self, ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH); ircomm_tty_start_watchdog_timer(self, 3*HZ); - if (self->flags & ASYNC_CHECK_CD) { + if (self->port.flags & ASYNC_CHECK_CD) { /* Drop carrier */ self->settings.dce = IRCOMM_DELTA_CD; ircomm_tty_check_modem_status(self); diff --git a/net/irda/ircomm/ircomm_tty_ioctl.c b/net/irda/ircomm/ircomm_tty_ioctl.c index a6d25e37b6b2..31b917e9c8d8 100644 --- a/net/irda/ircomm/ircomm_tty_ioctl.c +++ b/net/irda/ircomm/ircomm_tty_ioctl.c @@ -90,19 +90,19 @@ static void ircomm_tty_change_speed(struct ircomm_tty_cb *self) /* CTS flow control flag and modem status interrupts */ if (cflag & CRTSCTS) { - self->flags |= ASYNC_CTS_FLOW; + self->port.flags |= ASYNC_CTS_FLOW; self->settings.flow_control |= IRCOMM_RTS_CTS_IN; /* This got me. Bummer. Jean II */ if (self->service_type == IRCOMM_3_WIRE_RAW) IRDA_WARNING("%s(), enabling RTS/CTS on link that doesn't support it (3-wire-raw)\n", __func__); } else { - self->flags &= ~ASYNC_CTS_FLOW; + self->port.flags &= ~ASYNC_CTS_FLOW; self->settings.flow_control &= ~IRCOMM_RTS_CTS_IN; } if (cflag & CLOCAL) - self->flags &= ~ASYNC_CHECK_CD; + self->port.flags &= ~ASYNC_CHECK_CD; else - self->flags |= ASYNC_CHECK_CD; + self->port.flags |= ASYNC_CHECK_CD; #if 0 /* * Set up parity check flag @@ -270,7 +270,7 @@ static int ircomm_tty_get_serial_info(struct ircomm_tty_cb *self, memset(&info, 0, sizeof(info)); info.line = self->line; - info.flags = self->flags; + info.flags = self->port.flags; info.baud_base = self->settings.data_rate; info.close_delay = self->port.close_delay; info.closing_wait = self->port.closing_wait; -- cgit v1.2.3 From e673927d8a210ab1db27047080fc1bdb47f7e372 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 4 Jun 2012 13:35:20 +0200 Subject: TTY: ircomm, revamp locking Use self->spinlock only for ctrl_skb and tx_skb. TTY stuff is now protected by tty_port->lock. This is needed for further cleanup (and conversion to tty_port helpers). This also closes the race in the end of close. Signed-off-by: Jiri Slaby Cc: Samuel Ortiz Cc: netdev@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- include/net/irda/ircomm_tty.h | 1 - net/irda/ircomm/ircomm_tty.c | 38 ++++++++++++++++++-------------------- 2 files changed, 18 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/net/irda/ircomm_tty.h b/include/net/irda/ircomm_tty.h index e4db3b5f6e4c..a9027d8f670d 100644 --- a/include/net/irda/ircomm_tty.h +++ b/include/net/irda/ircomm_tty.h @@ -96,7 +96,6 @@ struct ircomm_tty_cb { struct work_struct tqueue; /* Protect concurent access to : - * o self->open_count * o self->ctrl_skb * o self->tx_skb * Maybe other things may gain to be protected as well... diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c index 8e61026b9dd4..7b2152cfd42a 100644 --- a/net/irda/ircomm/ircomm_tty.c +++ b/net/irda/ircomm/ircomm_tty.c @@ -283,13 +283,12 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, IRDA_DEBUG(2, "%s(%d):block_til_ready before block on %s open_count=%d\n", __FILE__, __LINE__, tty->driver->name, self->port.count); - /* As far as I can see, we protect port.count - Jean II */ - spin_lock_irqsave(&self->spinlock, flags); + spin_lock_irqsave(&self->port.lock, flags); if (!tty_hung_up_p(filp)) { extra_count = 1; self->port.count--; } - spin_unlock_irqrestore(&self->spinlock, flags); + spin_unlock_irqrestore(&self->port.lock, flags); self->port.blocked_open++; while (1) { @@ -340,9 +339,9 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, if (extra_count) { /* ++ is not atomic, so this should be protected - Jean II */ - spin_lock_irqsave(&self->spinlock, flags); + spin_lock_irqsave(&self->port.lock, flags); self->port.count++; - spin_unlock_irqrestore(&self->spinlock, flags); + spin_unlock_irqrestore(&self->port.lock, flags); } self->port.blocked_open--; @@ -409,12 +408,12 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) hashbin_insert(ircomm_tty, (irda_queue_t *) self, line, NULL); } /* ++ is not atomic, so this should be protected - Jean II */ - spin_lock_irqsave(&self->spinlock, flags); + spin_lock_irqsave(&self->port.lock, flags); self->port.count++; tty->driver_data = self; self->tty = tty; - spin_unlock_irqrestore(&self->spinlock, flags); + spin_unlock_irqrestore(&self->port.lock, flags); IRDA_DEBUG(1, "%s(), %s%d, count = %d\n", __func__ , tty->driver->name, self->line, self->port.count); @@ -495,10 +494,10 @@ static void ircomm_tty_close(struct tty_struct *tty, struct file *filp) IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - spin_lock_irqsave(&self->spinlock, flags); + spin_lock_irqsave(&self->port.lock, flags); if (tty_hung_up_p(filp)) { - spin_unlock_irqrestore(&self->spinlock, flags); + spin_unlock_irqrestore(&self->port.lock, flags); IRDA_DEBUG(0, "%s(), returning 1\n", __func__ ); return; @@ -524,20 +523,15 @@ static void ircomm_tty_close(struct tty_struct *tty, struct file *filp) self->port.count = 0; } if (self->port.count) { - spin_unlock_irqrestore(&self->spinlock, flags); + spin_unlock_irqrestore(&self->port.lock, flags); IRDA_DEBUG(0, "%s(), open count > 0\n", __func__ ); return; } - /* Hum... Should be test_and_set_bit ??? - Jean II */ set_bit(ASYNCB_CLOSING, &self->port.flags); - /* We need to unlock here (we were unlocking at the end of this - * function), because tty_wait_until_sent() may schedule. - * I don't know if the rest should be protected somehow, - * so someone should check. - Jean II */ - spin_unlock_irqrestore(&self->spinlock, flags); + spin_unlock_irqrestore(&self->port.lock, flags); /* * Now we wait for the transmit buffer to clear; and we notify @@ -552,16 +546,21 @@ static void ircomm_tty_close(struct tty_struct *tty, struct file *filp) tty_driver_flush_buffer(tty); tty_ldisc_flush(tty); + spin_lock_irqsave(&self->port.lock, flags); tty->closing = 0; self->tty = NULL; if (self->port.blocked_open) { - if (self->port.close_delay) + if (self->port.close_delay) { + spin_unlock_irqrestore(&self->port.lock, flags); schedule_timeout_interruptible(self->port.close_delay); + spin_lock_irqsave(&self->port.lock, flags); + } wake_up_interruptible(&self->port.open_wait); } self->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + spin_unlock_irqrestore(&self->port.lock, flags); wake_up_interruptible(&self->port.close_wait); } @@ -1003,12 +1002,11 @@ static void ircomm_tty_hangup(struct tty_struct *tty) /* ircomm_tty_flush_buffer(tty); */ ircomm_tty_shutdown(self); - /* I guess we need to lock here - Jean II */ - spin_lock_irqsave(&self->spinlock, flags); + spin_lock_irqsave(&self->port.lock, flags); self->port.flags &= ~ASYNC_NORMAL_ACTIVE; self->tty = NULL; self->port.count = 0; - spin_unlock_irqrestore(&self->spinlock, flags); + spin_unlock_irqrestore(&self->port.lock, flags); wake_up_interruptible(&self->port.open_wait); } -- cgit v1.2.3 From 62f228acb807c370c3b1583bf0f1aa4ab8e19aca Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 4 Jun 2012 13:35:21 +0200 Subject: TTY: ircomm, use tty from tty_port This also includes a switch to tty refcounting. It makes sure, the code no longer can access a freed TTY struct. Sometimes the only thing needed is to pass tty down to the callies. Signed-off-by: Jiri Slaby Cc: Samuel Ortiz Cc: netdev@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- include/net/irda/ircomm_tty.h | 1 - net/irda/ircomm/ircomm_param.c | 5 --- net/irda/ircomm/ircomm_tty.c | 62 +++++++++++++++++++++++-------------- net/irda/ircomm/ircomm_tty_attach.c | 33 +++++++++++++++----- net/irda/ircomm/ircomm_tty_ioctl.c | 11 ++++--- 5 files changed, 70 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/include/net/irda/ircomm_tty.h b/include/net/irda/ircomm_tty.h index a9027d8f670d..80ffde3bb164 100644 --- a/include/net/irda/ircomm_tty.h +++ b/include/net/irda/ircomm_tty.h @@ -62,7 +62,6 @@ struct ircomm_tty_cb { int state; /* Connect state */ - struct tty_struct *tty; struct ircomm_cb *ircomm; /* IrCOMM layer instance */ struct sk_buff *tx_skb; /* Transmit buffer */ diff --git a/net/irda/ircomm/ircomm_param.c b/net/irda/ircomm/ircomm_param.c index 8b915f3ac3b9..308939128359 100644 --- a/net/irda/ircomm/ircomm_param.c +++ b/net/irda/ircomm/ircomm_param.c @@ -99,7 +99,6 @@ pi_param_info_t ircomm_param_info = { pi_major_call_table, 3, 0x0f, 4 }; */ int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush) { - struct tty_struct *tty; unsigned long flags; struct sk_buff *skb; int count; @@ -109,10 +108,6 @@ int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush) IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - tty = self->tty; - if (!tty) - return 0; - /* Make sure we don't send parameters for raw mode */ if (self->service_type == IRCOMM_3_WIRE_RAW) return 0; diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c index 7b2152cfd42a..03acc073b8c3 100644 --- a/net/irda/ircomm/ircomm_tty.c +++ b/net/irda/ircomm/ircomm_tty.c @@ -242,18 +242,15 @@ err: * */ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, - struct file *filp) + struct tty_struct *tty, struct file *filp) { DECLARE_WAITQUEUE(wait, current); int retval; int do_clocal = 0, extra_count = 0; unsigned long flags; - struct tty_struct *tty; IRDA_DEBUG(2, "%s()\n", __func__ ); - tty = self->tty; - /* * If non-blocking mode is set, or the port is not enabled, * then make the check up front and then exit. @@ -412,8 +409,8 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) self->port.count++; tty->driver_data = self; - self->tty = tty; spin_unlock_irqrestore(&self->port.lock, flags); + tty_port_tty_set(&self->port, tty); IRDA_DEBUG(1, "%s(), %s%d, count = %d\n", __func__ , tty->driver->name, self->line, self->port.count); @@ -467,7 +464,7 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) if (ret) return ret; - ret = ircomm_tty_block_til_ready(self, filp); + ret = ircomm_tty_block_til_ready(self, tty, filp); if (ret) { IRDA_DEBUG(2, "%s(), returning after block_til_ready with %d\n", __func__ , @@ -548,7 +545,6 @@ static void ircomm_tty_close(struct tty_struct *tty, struct file *filp) spin_lock_irqsave(&self->port.lock, flags); tty->closing = 0; - self->tty = NULL; if (self->port.blocked_open) { if (self->port.close_delay) { @@ -562,6 +558,7 @@ static void ircomm_tty_close(struct tty_struct *tty, struct file *filp) self->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); spin_unlock_irqrestore(&self->port.lock, flags); wake_up_interruptible(&self->port.close_wait); + tty_port_tty_set(&self->port, NULL); } /* @@ -604,7 +601,7 @@ static void ircomm_tty_do_softint(struct work_struct *work) if (!self || self->magic != IRCOMM_TTY_MAGIC) return; - tty = self->tty; + tty = tty_port_tty_get(&self->port); if (!tty) return; @@ -625,7 +622,7 @@ static void ircomm_tty_do_softint(struct work_struct *work) } if (tty->hw_stopped) - return; + goto put; /* Unlink transmit buffer */ spin_lock_irqsave(&self->spinlock, flags); @@ -644,6 +641,8 @@ static void ircomm_tty_do_softint(struct work_struct *work) /* Check if user (still) wants to be waken up */ tty_wakeup(tty); +put: + tty_kref_put(tty); } /* @@ -1004,7 +1003,11 @@ static void ircomm_tty_hangup(struct tty_struct *tty) spin_lock_irqsave(&self->port.lock, flags); self->port.flags &= ~ASYNC_NORMAL_ACTIVE; - self->tty = NULL; + if (self->port.tty) { + set_bit(TTY_IO_ERROR, &self->port.tty->flags); + tty_kref_put(self->port.tty); + } + self->port.tty = NULL; self->port.count = 0; spin_unlock_irqrestore(&self->port.lock, flags); @@ -1068,7 +1071,7 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self) IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - tty = self->tty; + tty = tty_port_tty_get(&self->port); status = self->settings.dce; @@ -1089,10 +1092,10 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self) tty_hangup(tty); /* Hangup will remote the tty, so better break out */ - return; + goto put; } } - if (self->port.flags & ASYNC_CTS_FLOW) { + if (tty && self->port.flags & ASYNC_CTS_FLOW) { if (tty->hw_stopped) { if (status & IRCOMM_CTS) { IRDA_DEBUG(2, @@ -1103,7 +1106,7 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self) wake_up_interruptible(&self->port.open_wait); schedule_work(&self->tqueue); - return; + goto put; } } else { if (!(status & IRCOMM_CTS)) { @@ -1113,6 +1116,8 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self) } } } +put: + tty_kref_put(tty); } /* @@ -1125,6 +1130,7 @@ static int ircomm_tty_data_indication(void *instance, void *sap, struct sk_buff *skb) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; + struct tty_struct *tty; IRDA_DEBUG(2, "%s()\n", __func__ ); @@ -1132,7 +1138,8 @@ static int ircomm_tty_data_indication(void *instance, void *sap, IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); IRDA_ASSERT(skb != NULL, return -1;); - if (!self->tty) { + tty = tty_port_tty_get(&self->port); + if (!tty) { IRDA_DEBUG(0, "%s(), no tty!\n", __func__ ); return 0; } @@ -1143,7 +1150,7 @@ static int ircomm_tty_data_indication(void *instance, void *sap, * Devices like WinCE can do this, and since they don't send any * params, we can just as well declare the hardware for running. */ - if (self->tty->hw_stopped && (self->flow == FLOW_START)) { + if (tty->hw_stopped && (self->flow == FLOW_START)) { IRDA_DEBUG(0, "%s(), polling for line settings!\n", __func__ ); ircomm_param_request(self, IRCOMM_POLL, TRUE); @@ -1156,8 +1163,9 @@ static int ircomm_tty_data_indication(void *instance, void *sap, * Use flip buffer functions since the code may be called from interrupt * context */ - tty_insert_flip_string(self->tty, skb->data, skb->len); - tty_flip_buffer_push(self->tty); + tty_insert_flip_string(tty, skb->data, skb->len); + tty_flip_buffer_push(tty); + tty_kref_put(tty); /* No need to kfree_skb - see ircomm_ttp_data_indication() */ @@ -1208,12 +1216,13 @@ static void ircomm_tty_flow_indication(void *instance, void *sap, IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - tty = self->tty; + tty = tty_port_tty_get(&self->port); switch (cmd) { case FLOW_START: IRDA_DEBUG(2, "%s(), hw start!\n", __func__ ); - tty->hw_stopped = 0; + if (tty) + tty->hw_stopped = 0; /* ircomm_tty_do_softint will take care of the rest */ schedule_work(&self->tqueue); @@ -1221,15 +1230,19 @@ static void ircomm_tty_flow_indication(void *instance, void *sap, default: /* If we get here, something is very wrong, better stop */ case FLOW_STOP: IRDA_DEBUG(2, "%s(), hw stopped!\n", __func__ ); - tty->hw_stopped = 1; + if (tty) + tty->hw_stopped = 1; break; } + + tty_kref_put(tty); self->flow = cmd; } #ifdef CONFIG_PROC_FS static void ircomm_tty_line_info(struct ircomm_tty_cb *self, struct seq_file *m) { + struct tty_struct *tty; char sep; seq_printf(m, "State: %s\n", ircomm_tty_state[self->state]); @@ -1356,9 +1369,12 @@ static void ircomm_tty_line_info(struct ircomm_tty_cb *self, struct seq_file *m) seq_printf(m, "Max data size: %d\n", self->max_data_size); seq_printf(m, "Max header size: %d\n", self->max_header_size); - if (self->tty) + tty = tty_port_tty_get(&self->port); + if (tty) { seq_printf(m, "Hardware: %s\n", - self->tty->hw_stopped ? "Stopped" : "Running"); + tty->hw_stopped ? "Stopped" : "Running"); + tty_kref_put(tty); + } } static int ircomm_tty_proc_show(struct seq_file *m, void *v) diff --git a/net/irda/ircomm/ircomm_tty_attach.c b/net/irda/ircomm/ircomm_tty_attach.c index bed311af7311..3ab70e7a0715 100644 --- a/net/irda/ircomm/ircomm_tty_attach.c +++ b/net/irda/ircomm/ircomm_tty_attach.c @@ -130,6 +130,8 @@ static int (*state[])(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event, */ int ircomm_tty_attach_cable(struct ircomm_tty_cb *self) { + struct tty_struct *tty; + IRDA_DEBUG(0, "%s()\n", __func__ ); IRDA_ASSERT(self != NULL, return -1;); @@ -142,7 +144,11 @@ int ircomm_tty_attach_cable(struct ircomm_tty_cb *self) } /* Make sure nobody tries to write before the link is up */ - self->tty->hw_stopped = 1; + tty = tty_port_tty_get(&self->port); + if (tty) { + tty->hw_stopped = 1; + tty_kref_put(tty); + } ircomm_tty_ias_register(self); @@ -398,23 +404,26 @@ void ircomm_tty_disconnect_indication(void *instance, void *sap, struct sk_buff *skb) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; + struct tty_struct *tty; IRDA_DEBUG(2, "%s()\n", __func__ ); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - if (!self->tty) + tty = tty_port_tty_get(&self->port); + if (!tty) return; /* This will stop control data transfers */ self->flow = FLOW_STOP; /* Stop data transfers */ - self->tty->hw_stopped = 1; + tty->hw_stopped = 1; ircomm_tty_do_event(self, IRCOMM_TTY_DISCONNECT_INDICATION, NULL, NULL); + tty_kref_put(tty); } /* @@ -550,12 +559,15 @@ void ircomm_tty_connect_indication(void *instance, void *sap, */ void ircomm_tty_link_established(struct ircomm_tty_cb *self) { + struct tty_struct *tty; + IRDA_DEBUG(2, "%s()\n", __func__ ); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - if (!self->tty) + tty = tty_port_tty_get(&self->port); + if (!tty) return; del_timer(&self->watchdog_timer); @@ -569,17 +581,19 @@ void ircomm_tty_link_established(struct ircomm_tty_cb *self) if ((self->port.flags & ASYNC_CTS_FLOW) && ((self->settings.dce & IRCOMM_CTS) == 0)) { IRDA_DEBUG(0, "%s(), waiting for CTS ...\n", __func__ ); - return; + goto put; } else { IRDA_DEBUG(1, "%s(), starting hardware!\n", __func__ ); - self->tty->hw_stopped = 0; + tty->hw_stopped = 0; /* Wake up processes blocked on open */ wake_up_interruptible(&self->port.open_wait); } schedule_work(&self->tqueue); +put: + tty_kref_put(tty); } /* @@ -983,9 +997,12 @@ static int ircomm_tty_state_ready(struct ircomm_tty_cb *self, self->settings.dce = IRCOMM_DELTA_CD; ircomm_tty_check_modem_status(self); } else { + struct tty_struct *tty = tty_port_tty_get(&self->port); IRDA_DEBUG(0, "%s(), hanging up!\n", __func__ ); - if (self->tty) - tty_hangup(self->tty); + if (tty) { + tty_hangup(tty); + tty_kref_put(tty); + } } break; default: diff --git a/net/irda/ircomm/ircomm_tty_ioctl.c b/net/irda/ircomm/ircomm_tty_ioctl.c index 31b917e9c8d8..0eab6500e99f 100644 --- a/net/irda/ircomm/ircomm_tty_ioctl.c +++ b/net/irda/ircomm/ircomm_tty_ioctl.c @@ -52,17 +52,18 @@ * Change speed of the driver. If the remote device is a DCE, then this * should make it change the speed of its serial port */ -static void ircomm_tty_change_speed(struct ircomm_tty_cb *self) +static void ircomm_tty_change_speed(struct ircomm_tty_cb *self, + struct tty_struct *tty) { unsigned int cflag, cval; int baud; IRDA_DEBUG(2, "%s()\n", __func__ ); - if (!self->tty || !self->tty->termios || !self->ircomm) + if (!self->ircomm) return; - cflag = self->tty->termios->c_cflag; + cflag = tty->termios->c_cflag; /* byte size and parity */ switch (cflag & CSIZE) { @@ -81,7 +82,7 @@ static void ircomm_tty_change_speed(struct ircomm_tty_cb *self) cval |= IRCOMM_PARITY_EVEN; /* Determine divisor based on baud rate */ - baud = tty_get_baud_rate(self->tty); + baud = tty_get_baud_rate(tty); if (!baud) baud = 9600; /* B0 transition handled in rs_set_termios */ @@ -159,7 +160,7 @@ void ircomm_tty_set_termios(struct tty_struct *tty, return; } - ircomm_tty_change_speed(self); + ircomm_tty_change_speed(self, tty); /* Handle transition to B0 status */ if ((old_termios->c_cflag & CBAUD) && -- cgit v1.2.3 From 7a824e214e25a49442fe868dac0af8a904b24f58 Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Mon, 11 Jun 2012 18:04:38 +0800 Subject: ASoC: mmp: add audio dma support mmp-pcm handle audio dma based on soc-dmaengine Support mmp and pxa910 Signed-off-by: Zhangfei Gao Signed-off-by: Leo Yan Signed-off-by: Qiao Zhou Signed-off-by: Mark Brown --- include/linux/platform_data/mmp_audio.h | 22 +++ sound/soc/pxa/Kconfig | 9 + sound/soc/pxa/Makefile | 2 + sound/soc/pxa/mmp-pcm.c | 297 ++++++++++++++++++++++++++++++++ 4 files changed, 330 insertions(+) create mode 100644 include/linux/platform_data/mmp_audio.h create mode 100644 sound/soc/pxa/mmp-pcm.c (limited to 'include') diff --git a/include/linux/platform_data/mmp_audio.h b/include/linux/platform_data/mmp_audio.h new file mode 100644 index 000000000000..0f25d165abd6 --- /dev/null +++ b/include/linux/platform_data/mmp_audio.h @@ -0,0 +1,22 @@ +/* + * MMP Platform AUDIO Management + * + * Copyright (c) 2011 Marvell Semiconductors Inc. + * + * 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 MMP_AUDIO_H +#define MMP_AUDIO_H + +struct mmp_audio_platdata { + u32 period_max_capture; + u32 buffer_max_capture; + u32 period_max_playback; + u32 buffer_max_playback; +}; + +#endif /* MMP_AUDIO_H */ diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index a0f7d3cfa470..5d76e2971fbe 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -8,6 +8,15 @@ config SND_PXA2XX_SOC the PXA2xx AC97, I2S or SSP interface. You will also need to select the audio interfaces to support below. +config SND_MMP_SOC + bool "Soc Audio for Marvell MMP chips" + depends on ARCH_MMP + select SND_SOC_DMAENGINE_PCM + select SND_ARM + help + Say Y if you want to add support for codecs attached to + the MMP SSPA interface. + config SND_PXA2XX_AC97 tristate select SND_AC97_CODEC diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile index af357623be9d..f913e9bfce4f 100644 --- a/sound/soc/pxa/Makefile +++ b/sound/soc/pxa/Makefile @@ -3,11 +3,13 @@ snd-soc-pxa2xx-objs := pxa2xx-pcm.o snd-soc-pxa2xx-ac97-objs := pxa2xx-ac97.o snd-soc-pxa2xx-i2s-objs := pxa2xx-i2s.o snd-soc-pxa-ssp-objs := pxa-ssp.o +snd-soc-mmp-objs := mmp-pcm.o obj-$(CONFIG_SND_PXA2XX_SOC) += snd-soc-pxa2xx.o obj-$(CONFIG_SND_PXA2XX_SOC_AC97) += snd-soc-pxa2xx-ac97.o obj-$(CONFIG_SND_PXA2XX_SOC_I2S) += snd-soc-pxa2xx-i2s.o obj-$(CONFIG_SND_PXA_SOC_SSP) += snd-soc-pxa-ssp.o +obj-$(CONFIG_SND_MMP_SOC) += snd-soc-mmp.o # PXA Machine Support snd-soc-corgi-objs := corgi.o diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c new file mode 100644 index 000000000000..73ac5463c9e4 --- /dev/null +++ b/sound/soc/pxa/mmp-pcm.c @@ -0,0 +1,297 @@ +/* + * linux/sound/soc/pxa/mmp-pcm.c + * + * Copyright (C) 2011 Marvell International Ltd. + * + * 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 + +struct mmp_dma_data { + int ssp_id; + struct resource *dma_res; +}; + +#define MMP_PCM_INFO (SNDRV_PCM_INFO_MMAP | \ + SNDRV_PCM_INFO_MMAP_VALID | \ + SNDRV_PCM_INFO_INTERLEAVED | \ + SNDRV_PCM_INFO_PAUSE | \ + SNDRV_PCM_INFO_RESUME) + +#define MMP_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_pcm_hardware mmp_pcm_hardware[] = { + { + .info = MMP_PCM_INFO, + .formats = MMP_PCM_FORMATS, + .period_bytes_min = 1024, + .period_bytes_max = 2048, + .periods_min = 2, + .periods_max = 32, + .buffer_bytes_max = 4096, + .fifo_size = 32, + }, + { + .info = MMP_PCM_INFO, + .formats = MMP_PCM_FORMATS, + .period_bytes_min = 1024, + .period_bytes_max = 2048, + .periods_min = 2, + .periods_max = 32, + .buffer_bytes_max = 4096, + .fifo_size = 32, + }, +}; + +static int mmp_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + 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 dma_slave_config slave_config; + int ret; + + dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + if (!dma_params) + return 0; + + ret = snd_hwparams_to_dma_slave_config(substream, params, &slave_config); + if (ret) + return ret; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + slave_config.dst_addr = dma_params->dev_addr; + slave_config.dst_maxburst = 4; + } else { + slave_config.src_addr = dma_params->dev_addr; + slave_config.src_maxburst = 4; + } + + ret = dmaengine_slave_config(chan, &slave_config); + if (ret) + return ret; + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + return 0; +} + +static bool filter(struct dma_chan *chan, void *param) +{ + struct mmp_dma_data *dma_data = param; + bool found = false; + char *devname; + + devname = kasprintf(GFP_KERNEL, "%s.%d", dma_data->dma_res->name, + dma_data->ssp_id); + if ((strcmp(dev_name(chan->device->dev), devname) == 0) && + (chan->chan_id == dma_data->dma_res->start)) { + found = true; + } + + kfree(devname); + return found; +} + +static int mmp_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct platform_device *pdev = to_platform_device(rtd->platform->dev); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct mmp_dma_data *dma_data; + struct resource *r; + int ret; + + r = platform_get_resource(pdev, IORESOURCE_DMA, substream->stream); + if (!r) + return -EBUSY; + + snd_soc_set_runtime_hwparams(substream, + &mmp_pcm_hardware[substream->stream]); + dma_data = devm_kzalloc(&pdev->dev, + sizeof(struct mmp_dma_data), GFP_KERNEL); + if (dma_data == NULL) + return -ENOMEM; + + dma_data->dma_res = r; + dma_data->ssp_id = cpu_dai->id; + + ret = snd_dmaengine_pcm_open(substream, filter, dma_data); + if (ret) { + devm_kfree(&pdev->dev, dma_data); + return ret; + } + + snd_dmaengine_pcm_set_data(substream, dma_data); + return 0; +} + +static int mmp_pcm_close(struct snd_pcm_substream *substream) +{ + struct mmp_dma_data *dma_data = snd_dmaengine_pcm_get_data(substream); + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct platform_device *pdev = to_platform_device(rtd->platform->dev); + + snd_dmaengine_pcm_close(substream); + devm_kfree(&pdev->dev, dma_data); + return 0; +} + +static int mmp_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned long off = vma->vm_pgoff; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + return remap_pfn_range(vma, vma->vm_start, + __phys_to_pfn(runtime->dma_addr) + off, + vma->vm_end - vma->vm_start, vma->vm_page_prot); +} + +struct snd_pcm_ops mmp_pcm_ops = { + .open = mmp_pcm_open, + .close = mmp_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = mmp_pcm_hw_params, + .trigger = snd_dmaengine_pcm_trigger, + .pointer = snd_dmaengine_pcm_pointer, + .mmap = mmp_pcm_mmap, +}; + +static void mmp_pcm_free_dma_buffers(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + struct snd_dma_buffer *buf; + int stream; + struct gen_pool *gpool; + + gpool = sram_get_gpool("asram"); + if (!gpool) + return; + + for (stream = 0; stream < 2; stream++) { + size_t size = mmp_pcm_hardware[stream].buffer_bytes_max; + + substream = pcm->streams[stream].substream; + if (!substream) + continue; + + buf = &substream->dma_buffer; + if (!buf->area) + continue; + gen_pool_free(gpool, (unsigned long)buf->area, size); + buf->area = NULL; + } + + return; +} + +static int mmp_pcm_preallocate_dma_buffer(struct snd_pcm_substream *substream, + int stream) +{ + struct snd_dma_buffer *buf = &substream->dma_buffer; + size_t size = mmp_pcm_hardware[stream].buffer_bytes_max; + struct gen_pool *gpool; + + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = substream->pcm->card->dev; + buf->private_data = NULL; + + gpool = sram_get_gpool("asram"); + if (!gpool) + return -ENOMEM; + + buf->area = (unsigned char *)gen_pool_alloc(gpool, size); + if (!buf->area) + return -ENOMEM; + buf->addr = gen_pool_virt_to_phys(gpool, (unsigned long)buf->area); + buf->bytes = size; + return 0; +} + +int mmp_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm_substream *substream; + struct snd_pcm *pcm = rtd->pcm; + int ret = 0, stream; + + for (stream = 0; stream < 2; stream++) { + substream = pcm->streams[stream].substream; + + ret = mmp_pcm_preallocate_dma_buffer(substream, stream); + if (ret) + goto err; + } + + return 0; + +err: + mmp_pcm_free_dma_buffers(pcm); + return ret; +} + +struct snd_soc_platform_driver mmp_soc_platform = { + .ops = &mmp_pcm_ops, + .pcm_new = mmp_pcm_new, + .pcm_free = mmp_pcm_free_dma_buffers, +}; + +static __devinit int mmp_pcm_probe(struct platform_device *pdev) +{ + struct mmp_audio_platdata *pdata = pdev->dev.platform_data; + + if (pdata) { + mmp_pcm_hardware[SNDRV_PCM_STREAM_PLAYBACK].buffer_bytes_max = + pdata->buffer_max_playback; + mmp_pcm_hardware[SNDRV_PCM_STREAM_PLAYBACK].period_bytes_max = + pdata->period_max_playback; + mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].buffer_bytes_max = + pdata->buffer_max_capture; + mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].period_bytes_max = + pdata->period_max_capture; + } + return snd_soc_register_platform(&pdev->dev, &mmp_soc_platform); +} + +static int __devexit mmp_pcm_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static struct platform_driver mmp_pcm_driver = { + .driver = { + .name = "mmp-pcm-audio", + .owner = THIS_MODULE, + }, + + .probe = mmp_pcm_probe, + .remove = __devexit_p(mmp_pcm_remove), +}; + +module_platform_driver(mmp_pcm_driver); + +MODULE_AUTHOR("Leo Yan "); +MODULE_DESCRIPTION("MMP Soc Audio DMA module"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 4527715979a34601b783f5f12774586c679c2a89 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 28 May 2012 23:31:39 -0700 Subject: usb: renesas_usbhs: add DMAEngine ID specification note renesas_usbhs DMAEngine uses D0FIFO/D1FIFO, but the data transfer direction should be fixed for keeping consistency. This patch explain about it on renesas_usbhs.h Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/renesas_usbhs.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include') diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index 547e59cc00ea..c5d36c65c33b 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -132,6 +132,14 @@ struct renesas_usbhs_driver_param { * option: * * dma id for dmaengine + * The data transfer direction on D0FIFO/D1FIFO should be + * fixed for keeping consistency. + * So, the platform id settings will be.. + * .d0_tx_id = xx_TX, + * .d1_rx_id = xx_RX, + * or + * .d1_tx_id = xx_TX, + * .d0_rx_id = xx_RX, */ int d0_tx_id; int d0_rx_id; -- cgit v1.2.3 From c2e935a7db6e7354e9dd138b7f6f4c53affc09d9 Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Wed, 13 Jun 2012 20:34:12 +0800 Subject: USB: move transceiver from ehci_hcd and ohci_hcd to hcd and rename it as phy - to decrease redundant since both ehci_hcd and ohci_hcd have the same variable - it helps access phy in usb core code - phy is more meaningful than transceiver Signed-off-by: Richard Zhao Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-fsl.c | 21 ++++++++++----------- drivers/usb/host/ehci-hub.c | 2 +- drivers/usb/host/ehci.h | 4 ---- drivers/usb/host/ohci-omap.c | 27 ++++++++++++++------------- drivers/usb/host/ohci.h | 5 ----- include/linux/usb/hcd.h | 6 ++++++ 6 files changed, 31 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 43362577b54a..3379945b095e 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -142,19 +142,19 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, if (pdata->operating_mode == FSL_USB2_DR_OTG) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); - ehci->transceiver = usb_get_transceiver(); - dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, transceiver=0x%p\n", - hcd, ehci, ehci->transceiver); + hcd->phy = usb_get_transceiver(); + dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, phy=0x%p\n", + hcd, ehci, hcd->phy); - if (ehci->transceiver) { - retval = otg_set_host(ehci->transceiver->otg, + if (hcd->phy) { + retval = otg_set_host(hcd->phy->otg, &ehci_to_hcd(ehci)->self); if (retval) { - usb_put_transceiver(ehci->transceiver); + usb_put_transceiver(hcd->phy); goto err4; } } else { - dev_err(&pdev->dev, "can't find transceiver\n"); + dev_err(&pdev->dev, "can't find phy\n"); retval = -ENODEV; goto err4; } @@ -190,11 +190,10 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd, struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - if (ehci->transceiver) { - otg_set_host(ehci->transceiver->otg, NULL); - usb_put_transceiver(ehci->transceiver); + if (hcd->phy) { + otg_set_host(hcd->phy->otg, NULL); + usb_put_transceiver(hcd->phy); } usb_remove_hcd(hcd); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index fc9e7cc6ac9b..dd5eef6af6df 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -724,7 +724,7 @@ static int ehci_hub_control ( #ifdef CONFIG_USB_OTG if ((hcd->self.otg_port == (wIndex + 1)) && hcd->self.b_hnp_enable) { - otg_start_hnp(ehci->transceiver->otg); + otg_start_hnp(hcd->phy->otg); break; } #endif diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 2694ed6558d2..85c3572155d1 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -175,10 +175,6 @@ struct ehci_hcd { /* one per controller */ #ifdef DEBUG struct dentry *debug_dir; #endif - /* - * OTG controllers and transceivers need software interaction - */ - struct usb_phy *transceiver; }; /* convert between an HCD pointer and the corresponding EHCI_HCD */ diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 9ce35d0d9d5d..eccddb461396 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -167,14 +167,15 @@ static int omap_1510_local_bus_init(void) static void start_hnp(struct ohci_hcd *ohci) { - const unsigned port = ohci_to_hcd(ohci)->self.otg_port - 1; + struct usb_hcd *hcd = ohci_to_hcd(ohci); + const unsigned port = hcd->self.otg_port - 1; unsigned long flags; u32 l; - otg_start_hnp(ohci->transceiver->otg); + otg_start_hnp(hcd->phy->otg); local_irq_save(flags); - ohci->transceiver->state = OTG_STATE_A_SUSPEND; + hcd->phy->state = OTG_STATE_A_SUSPEND; writel (RH_PS_PSS, &ohci->regs->roothub.portstatus [port]); l = omap_readl(OTG_CTRL); l &= ~OTG_A_BUSREQ; @@ -211,18 +212,18 @@ static int ohci_omap_init(struct usb_hcd *hcd) #ifdef CONFIG_USB_OTG if (need_transceiver) { - ohci->transceiver = usb_get_transceiver(); - if (ohci->transceiver) { - int status = otg_set_host(ohci->transceiver->otg, + hcd->phy = usb_get_transceiver(); + if (hcd->phy) { + int status = otg_set_host(hcd->phy->otg, &ohci_to_hcd(ohci)->self); - dev_dbg(hcd->self.controller, "init %s transceiver, status %d\n", - ohci->transceiver->label, status); + dev_dbg(hcd->self.controller, "init %s phy, status %d\n", + hcd->phy->label, status); if (status) { - usb_put_transceiver(ohci->transceiver); + usb_put_transceiver(hcd->phy); return status; } } else { - dev_err(hcd->self.controller, "can't find transceiver\n"); + dev_err(hcd->self.controller, "can't find phy\n"); return -ENODEV; } ohci->start_hnp = start_hnp; @@ -403,9 +404,9 @@ usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev) struct ohci_hcd *ohci = hcd_to_ohci (hcd); usb_remove_hcd(hcd); - if (ohci->transceiver) { - (void) otg_set_host(ohci->transceiver->otg, 0); - usb_put_transceiver(ohci->transceiver); + if (hcd->phy) { + (void) otg_set_host(hcd->phy->otg, 0); + usb_put_transceiver(hcd->phy); } if (machine_is_omap_osk()) gpio_free(9); diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 1b19aea25a2b..d3299143d9e2 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -372,11 +372,6 @@ struct ohci_hcd { struct ed *ed_controltail; /* last in ctrl list */ struct ed *periodic [NUM_INTS]; /* shadow int_table */ - /* - * OTG controllers and transceivers need software interaction; - * other external transceivers should be software-transparent - */ - struct usb_phy *transceiver; void (*start_hnp)(struct ohci_hcd *ohci); /* diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 7f855d50cdf5..c532cbeabfbc 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -93,6 +93,12 @@ struct usb_hcd { */ const struct hc_driver *driver; /* hw-specific hooks */ + /* + * OTG and some Host controllers need software interaction with phys; + * other external phys should be software-transparent + */ + struct usb_phy *phy; + /* Flags that need to be manipulated atomically because they can * change while the host controller is running. Always use * set_bit() or clear_bit() to change their values. -- cgit v1.2.3 From 92f02430934ca1c1e991a1ab3541880575042697 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:51:11 -0700 Subject: PCI: add busn_res in struct pci_bus This adds a busn_res resource in struct pci_bus. This will replace the secondary/subordinate members and will be used to build a bus number resource tree to help with bus number allocation. [bhelgaas: changelog] CC: Andrew Morton Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- include/linux/pci.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/pci.h b/include/linux/pci.h index d8c379dba6ad..ba7c5cd314b7 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -419,6 +419,7 @@ struct pci_bus { struct list_head slots; /* list of slots on this bus */ struct resource *resource[PCI_BRIDGE_RESOURCE_NUM]; struct list_head resources; /* address space routed to this bus */ + struct resource busn_res; /* bus numbers routed to this bus */ struct pci_ops *ops; /* configuration access functions */ void *sysdata; /* hook for sys-specific extension */ -- cgit v1.2.3 From 3527ed81ca01bbaf09df952e68528377a9cd092f Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:51:11 -0700 Subject: PCI: remove secondary/subordinate in struct pci_bus The pci_bus secondary/subordinate members are now unused, so remove them. Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- include/linux/pci.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/linux/pci.h b/include/linux/pci.h index ba7c5cd314b7..6b10966cd1b9 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -427,8 +427,6 @@ struct pci_bus { unsigned char number; /* bus number */ unsigned char primary; /* number of primary bridge */ - unsigned char secondary; /* number of secondary bridge */ - unsigned char subordinate; /* max number of subordinate buses */ unsigned char max_bus_speed; /* enum pci_bus_speed */ unsigned char cur_bus_speed; /* enum pci_bus_speed */ -- cgit v1.2.3 From 98a3583107ed587ed3cfe2a1d8e5347421de5a80 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 18 May 2012 11:35:50 -0600 Subject: PCI: add busn_res operation functions Will use them insert/update busn res in pci_bus struct. [bhelgaas: print conflicting entry if insertion fails] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 3 +++ 2 files changed, 71 insertions(+) (limited to 'include') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 674a477a6486..7662ab7b2640 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1714,6 +1714,74 @@ err_out: return NULL; } +int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_max) +{ + struct resource *res = &b->busn_res; + struct resource *parent_res, *conflict; + + res->start = bus; + res->end = bus_max; + res->flags = IORESOURCE_BUS; + + if (!pci_is_root_bus(b)) + parent_res = &b->parent->busn_res; + else { + parent_res = get_pci_domain_busn_res(pci_domain_nr(b)); + res->flags |= IORESOURCE_PCI_FIXED; + } + + conflict = insert_resource_conflict(parent_res, res); + + if (conflict) + dev_printk(KERN_DEBUG, &b->dev, + "busn_res: can not insert %pR under %s%pR (conflicts with %s %pR)\n", + res, pci_is_root_bus(b) ? "domain " : "", + parent_res, conflict->name, conflict); + else + dev_printk(KERN_DEBUG, &b->dev, + "busn_res: %pR is inserted under %s%pR\n", + res, pci_is_root_bus(b) ? "domain " : "", + parent_res); + + return conflict == NULL; +} + +int pci_bus_update_busn_res_end(struct pci_bus *b, int bus_max) +{ + struct resource *res = &b->busn_res; + struct resource old_res = *res; + resource_size_t size; + int ret; + + if (res->start > bus_max) + return -EINVAL; + + size = bus_max - res->start + 1; + ret = adjust_resource(res, res->start, size); + dev_printk(KERN_DEBUG, &b->dev, + "busn_res: %pR end %s updated to %02x\n", + &old_res, ret ? "can not be" : "is", bus_max); + + if (!ret && !res->parent) + pci_bus_insert_busn_res(b, res->start, res->end); + + return ret; +} + +void pci_bus_release_busn_res(struct pci_bus *b) +{ + struct resource *res = &b->busn_res; + int ret; + + if (!res->flags || !res->parent) + return; + + ret = release_resource(res); + dev_printk(KERN_DEBUG, &b->dev, + "busn_res: %pR %s released\n", + res, ret ? "can not be" : "is"); +} + struct pci_bus * __devinit pci_scan_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources) { diff --git a/include/linux/pci.h b/include/linux/pci.h index 6b10966cd1b9..c4df570f3bbb 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -667,6 +667,9 @@ struct pci_bus *pci_scan_bus(int bus, struct pci_ops *ops, void *sysdata); struct pci_bus *pci_create_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources); +int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int busmax); +int pci_bus_update_busn_res_end(struct pci_bus *b, int busmax); +void pci_bus_release_busn_res(struct pci_bus *b); struct pci_bus * __devinit pci_scan_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources); -- cgit v1.2.3 From 67cdc827286366acb6c60c821013c1185ee00b36 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:51:12 -0700 Subject: PCI: add default busn_resource We need to put into the resources list for legacy system. Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 7 +++++++ include/linux/pci.h | 2 ++ 2 files changed, 9 insertions(+) (limited to 'include') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 6258f6f24983..68e75cb0831b 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -16,6 +16,13 @@ #define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */ #define CARDBUS_RESERVE_BUSNR 3 +struct resource busn_resource = { + .name = "PCI busn", + .start = 0, + .end = 255, + .flags = IORESOURCE_BUS, +}; + /* Ugh. Need to stop exporting this to modules. */ LIST_HEAD(pci_root_buses); EXPORT_SYMBOL(pci_root_buses); diff --git a/include/linux/pci.h b/include/linux/pci.h index c4df570f3bbb..8c8b44d62105 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -368,6 +368,8 @@ static inline int pci_channel_offline(struct pci_dev *pdev) return (pdev->error_state != pci_channel_io_normal); } +extern struct resource busn_resource; + struct pci_host_bridge_window { struct list_head list; struct resource *res; /* host bridge aperture (CPU address) */ -- cgit v1.2.3 From 81df2d594340dcb6d1a02191976be88a1ca8120c Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Fri, 18 May 2012 21:27:43 +0200 Subject: USB: allow match on bInterfaceNumber MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some composite USB devices provide multiple interfaces with different functions, all using "vendor-specific" for class/subclass/protocol. Another OS use interface numbers to match the driver and interface. It seems these devices are designed with that in mind - using static interface numbers for the different functions. This adds support for matching against the bInterfaceNumber, allowing such devices to be supported without having to resort to testing against interface number whitelists and/or blacklists in the probe. Signed-off-by: Bjørn Mork Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 9 +++++++-- drivers/usb/core/message.c | 5 +++-- drivers/usb/core/sysfs.c | 5 +++-- include/linux/mod_devicetable.h | 7 +++++++ include/linux/usb.h | 16 ++++++++++++++++ scripts/mod/file2alias.c | 5 ++++- 6 files changed, 40 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index f536aebc958e..23d7bbd199a5 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -622,14 +622,15 @@ int usb_match_one_id(struct usb_interface *interface, if (!usb_match_device(dev, id)) return 0; - /* The interface class, subclass, and protocol should never be + /* The interface class, subclass, protocol and number should never be * checked for a match if the device class is Vendor Specific, * unless the match record specifies the Vendor ID. */ if (dev->descriptor.bDeviceClass == USB_CLASS_VENDOR_SPEC && !(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && (id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS | - USB_DEVICE_ID_MATCH_INT_PROTOCOL))) + USB_DEVICE_ID_MATCH_INT_PROTOCOL | + USB_DEVICE_ID_MATCH_INT_NUMBER))) return 0; if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) && @@ -644,6 +645,10 @@ int usb_match_one_id(struct usb_interface *interface, (id->bInterfaceProtocol != intf->desc.bInterfaceProtocol)) return 0; + if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER) && + (id->bInterfaceNumber != intf->desc.bInterfaceNumber)) + return 0; + return 1; } EXPORT_SYMBOL_GPL(usb_match_one_id); diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index b548cf1dbc62..ca7fc392fd9e 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1559,7 +1559,7 @@ static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env) if (add_uevent_var(env, "MODALIAS=usb:" - "v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X", + "v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02Xin%02X", le16_to_cpu(usb_dev->descriptor.idVendor), le16_to_cpu(usb_dev->descriptor.idProduct), le16_to_cpu(usb_dev->descriptor.bcdDevice), @@ -1568,7 +1568,8 @@ static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env) usb_dev->descriptor.bDeviceProtocol, alt->desc.bInterfaceClass, alt->desc.bInterfaceSubClass, - alt->desc.bInterfaceProtocol)) + alt->desc.bInterfaceProtocol, + alt->desc.bInterfaceNumber)) return -ENOMEM; return 0; diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 9a56e3adf476..777f03c37725 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -840,7 +840,7 @@ static ssize_t show_modalias(struct device *dev, alt = intf->cur_altsetting; return sprintf(buf, "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02X" - "ic%02Xisc%02Xip%02X\n", + "ic%02Xisc%02Xip%02Xin%02X\n", le16_to_cpu(udev->descriptor.idVendor), le16_to_cpu(udev->descriptor.idProduct), le16_to_cpu(udev->descriptor.bcdDevice), @@ -849,7 +849,8 @@ static ssize_t show_modalias(struct device *dev, udev->descriptor.bDeviceProtocol, alt->desc.bInterfaceClass, alt->desc.bInterfaceSubClass, - alt->desc.bInterfaceProtocol); + alt->desc.bInterfaceProtocol, + alt->desc.bInterfaceNumber); } static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 5db93821f9c7..7771d453e5f3 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -78,6 +78,9 @@ struct ieee1394_device_id { * of a given interface; other interfaces may support other classes. * @bInterfaceSubClass: Subclass of interface; associated with bInterfaceClass. * @bInterfaceProtocol: Protocol of interface; associated with bInterfaceClass. + * @bInterfaceNumber: Number of interface; composite devices may use + * fixed interface numbers to differentiate between vendor-specific + * interfaces. * @driver_info: Holds information used by the driver. Usually it holds * a pointer to a descriptor understood by the driver, or perhaps * device flags. @@ -115,6 +118,9 @@ struct usb_device_id { __u8 bInterfaceSubClass; __u8 bInterfaceProtocol; + /* Used for vendor-specific interface matches */ + __u8 bInterfaceNumber; + /* not matched against */ kernel_ulong_t driver_info; }; @@ -130,6 +136,7 @@ struct usb_device_id { #define USB_DEVICE_ID_MATCH_INT_CLASS 0x0080 #define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100 #define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200 +#define USB_DEVICE_ID_MATCH_INT_NUMBER 0x0400 #define HID_ANY_ID (~0) #define HID_BUS_ANY 0xffff diff --git a/include/linux/usb.h b/include/linux/usb.h index dea39dc551d4..f717fbdaee8e 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -776,6 +776,22 @@ static inline int usb_make_path(struct usb_device *dev, char *buf, size_t size) .idProduct = (prod), \ .bInterfaceProtocol = (pr) +/** + * USB_DEVICE_INTERFACE_NUMBER - describe a usb device with a specific interface number + * @vend: the 16 bit USB Vendor ID + * @prod: the 16 bit USB Product ID + * @num: bInterfaceNumber value + * + * This macro is used to create a struct usb_device_id that matches a + * specific interface number of devices. + */ +#define USB_DEVICE_INTERFACE_NUMBER(vend, prod, num) \ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ + USB_DEVICE_ID_MATCH_INT_NUMBER, \ + .idVendor = (vend), \ + .idProduct = (prod), \ + .bInterfaceNumber = (num) + /** * USB_DEVICE_INFO - macro used to describe a class of usb devices * @cl: bDeviceClass value diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 5759751a1f61..7ed6864ef65b 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -156,7 +156,7 @@ static void device_id_check(const char *modname, const char *device_id, } /* USB is special because the bcdDevice can be matched against a numeric range */ -/* Looks like "usb:vNpNdNdcNdscNdpNicNiscNipN" */ +/* Looks like "usb:vNpNdNdcNdscNdpNicNiscNipNinN" */ static void do_usb_entry(struct usb_device_id *id, unsigned int bcdDevice_initial, int bcdDevice_initial_digits, unsigned char range_lo, unsigned char range_hi, @@ -210,6 +210,9 @@ static void do_usb_entry(struct usb_device_id *id, ADD(alias, "ip", id->match_flags&USB_DEVICE_ID_MATCH_INT_PROTOCOL, id->bInterfaceProtocol); + ADD(alias, "in", + id->match_flags&USB_DEVICE_ID_MATCH_INT_NUMBER, + id->bInterfaceNumber); add_wildcard(alias); buf_printf(&mod->dev_table_buf, -- cgit v1.2.3 From dcce0489477f07ac1331aee71f18d6274e19a9c1 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Mon, 11 Jun 2012 18:39:50 +0200 Subject: KVM: trace events: update list of exit reasons The list of exit reasons for the kvm_userspace_exit event was missing recent additions; bring it into sync again. Reviewed-by: Christian Borntraeger Signed-off-by: Cornelia Huck Signed-off-by: Marcelo Tosatti --- include/trace/events/kvm.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/trace/events/kvm.h b/include/trace/events/kvm.h index 46e3cd8e197a..3df5925fe641 100644 --- a/include/trace/events/kvm.h +++ b/include/trace/events/kvm.h @@ -13,7 +13,8 @@ ERSN(DEBUG), ERSN(HLT), ERSN(MMIO), ERSN(IRQ_WINDOW_OPEN), \ ERSN(SHUTDOWN), ERSN(FAIL_ENTRY), ERSN(INTR), ERSN(SET_TPR), \ ERSN(TPR_ACCESS), ERSN(S390_SIEIC), ERSN(S390_RESET), ERSN(DCR),\ - ERSN(NMI), ERSN(INTERNAL_ERROR), ERSN(OSI) + ERSN(NMI), ERSN(INTERNAL_ERROR), ERSN(OSI), ERSN(PAPR_HCALL), \ + ERSN(S390_UCONTROL) TRACE_EVENT(kvm_userspace_exit, TP_PROTO(__u32 reason, int errno), -- cgit v1.2.3 From f29e5956aebafe63f81e80f972c44c4a666e5c7f Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Sat, 26 May 2012 06:20:19 -0700 Subject: pstore: Add console log messages support Pstore doesn't support logging kernel messages in run-time, it only dumps dmesg when kernel oopses/panics. This makes pstore useless for debugging hangs caused by HW issues or improper use of HW (e.g. weird device inserted -> driver tried to write a reserved bits -> SoC hanged. In that case we don't get any messages in the pstore. Therefore, let's add a runtime logging support: PSTORE_TYPE_CONSOLE. Signed-off-by: Anton Vorontsov Acked-by: Kees Cook Acked-by: Colin Cross Signed-off-by: Greg Kroah-Hartman --- fs/pstore/Kconfig | 7 +++++++ fs/pstore/inode.c | 3 +++ fs/pstore/platform.c | 37 +++++++++++++++++++++++++++++++++++++ include/linux/pstore.h | 1 + 4 files changed, 48 insertions(+) (limited to 'include') diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig index 23ade2680a4a..d044de6ee308 100644 --- a/fs/pstore/Kconfig +++ b/fs/pstore/Kconfig @@ -12,6 +12,13 @@ config PSTORE If you don't have a platform persistent store driver, say N. +config PSTORE_CONSOLE + bool "Log kernel console messages" + depends on PSTORE + help + When the option is enabled, pstore will log all kernel + messages, even if no oops or panic happened. + config PSTORE_RAM tristate "Log panic/oops to a RAM buffer" depends on PSTORE diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c index 11a2aa2a56c4..45bff5441b04 100644 --- a/fs/pstore/inode.c +++ b/fs/pstore/inode.c @@ -212,6 +212,9 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, case PSTORE_TYPE_DMESG: sprintf(name, "dmesg-%s-%lld", psname, id); break; + case PSTORE_TYPE_CONSOLE: + sprintf(name, "console-%s", psname); + break; case PSTORE_TYPE_MCE: sprintf(name, "mce-%s-%lld", psname, id); break; diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 82c585f715e3..61461ed9b6c8 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -1,6 +1,7 @@ /* * Persistent Storage - platform driver interface parts. * + * Copyright (C) 2007-2008 Google, Inc. * Copyright (C) 2010 Intel Corporation * * This program is free software; you can redistribute it and/or modify @@ -22,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -156,6 +158,40 @@ static struct kmsg_dumper pstore_dumper = { .dump = pstore_dump, }; +#ifdef CONFIG_PSTORE_CONSOLE +static void pstore_console_write(struct console *con, const char *s, unsigned c) +{ + const char *e = s + c; + + while (s < e) { + unsigned long flags; + + if (c > psinfo->bufsize) + c = psinfo->bufsize; + spin_lock_irqsave(&psinfo->buf_lock, flags); + memcpy(psinfo->buf, s, c); + psinfo->write(PSTORE_TYPE_CONSOLE, 0, NULL, 0, c, psinfo); + spin_unlock_irqrestore(&psinfo->buf_lock, flags); + s += c; + c = e - s; + } +} + +static struct console pstore_console = { + .name = "pstore", + .write = pstore_console_write, + .flags = CON_PRINTBUFFER | CON_ENABLED | CON_ANYTIME, + .index = -1, +}; + +static void pstore_register_console(void) +{ + register_console(&pstore_console); +} +#else +static void pstore_register_console(void) {} +#endif + /* * platform specific persistent storage driver registers with * us here. If pstore is already mounted, call the platform @@ -193,6 +229,7 @@ int pstore_register(struct pstore_info *psi) pstore_get_records(0); kmsg_dump_register(&pstore_dumper); + pstore_register_console(); pstore_timer.expires = jiffies + PSTORE_INTERVAL; add_timer(&pstore_timer); diff --git a/include/linux/pstore.h b/include/linux/pstore.h index e1461e143be2..1bd014b8e432 100644 --- a/include/linux/pstore.h +++ b/include/linux/pstore.h @@ -29,6 +29,7 @@ enum pstore_type_id { PSTORE_TYPE_DMESG = 0, PSTORE_TYPE_MCE = 1, + PSTORE_TYPE_CONSOLE = 2, PSTORE_TYPE_UNKNOWN = 255 }; -- cgit v1.2.3 From b5d38e9bf1b0c4db19e336b59b38dfb5d28bf1bf Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Sat, 26 May 2012 06:20:23 -0700 Subject: pstore/ram: Add console messages handling The console log size is configurable via ramoops.console_size module option, and the log itself is available via /console-ramoops file. Signed-off-by: Anton Vorontsov Acked-by: Kees Cook Signed-off-by: Greg Kroah-Hartman --- fs/pstore/ram.c | 100 ++++++++++++++++++++++++++++++++++++++------- include/linux/pstore_ram.h | 1 + 2 files changed, 87 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index d770d7266e96..c7acf94ff475 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -41,6 +41,10 @@ module_param(record_size, ulong, 0400); MODULE_PARM_DESC(record_size, "size of each dump done on oops/panic"); +static ulong ramoops_console_size = MIN_MEM_SIZE; +module_param_named(console_size, ramoops_console_size, ulong, 0400); +MODULE_PARM_DESC(console_size, "size of kernel console log"); + static ulong mem_address; module_param(mem_address, ulong, 0400); MODULE_PARM_DESC(mem_address, @@ -63,14 +67,17 @@ MODULE_PARM_DESC(ramoops_ecc, struct ramoops_context { struct persistent_ram_zone **przs; + struct persistent_ram_zone *cprz; phys_addr_t phys_addr; unsigned long size; size_t record_size; + size_t console_size; int dump_oops; bool ecc; unsigned int max_dump_cnt; unsigned int dump_write_cnt; unsigned int dump_read_cnt; + unsigned int console_read_cnt; struct pstore_info pstore; }; @@ -82,6 +89,7 @@ static int ramoops_pstore_open(struct pstore_info *psi) struct ramoops_context *cxt = psi->data; cxt->dump_read_cnt = 0; + cxt->console_read_cnt = 0; return 0; } @@ -124,6 +132,9 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, prz = ramoops_get_next_prz(cxt->przs, &cxt->dump_read_cnt, cxt->max_dump_cnt, id, type, PSTORE_TYPE_DMESG, 1); + if (!prz) + prz = ramoops_get_next_prz(&cxt->cprz, &cxt->console_read_cnt, + 1, id, type, PSTORE_TYPE_CONSOLE, 0); if (!prz) return 0; @@ -167,7 +178,13 @@ static int ramoops_pstore_write(enum pstore_type_id type, struct persistent_ram_zone *prz = cxt->przs[cxt->dump_write_cnt]; size_t hlen; - /* Currently ramoops is designed to only store dmesg dumps. */ + if (type == PSTORE_TYPE_CONSOLE) { + if (!cxt->cprz) + return -ENOMEM; + persistent_ram_write(cxt->cprz, cxt->pstore.buf, size); + return 0; + } + if (type != PSTORE_TYPE_DMESG) return -EINVAL; @@ -204,12 +221,23 @@ static int ramoops_pstore_erase(enum pstore_type_id type, u64 id, struct pstore_info *psi) { struct ramoops_context *cxt = psi->data; + struct persistent_ram_zone *prz; - if (id >= cxt->max_dump_cnt) + switch (type) { + case PSTORE_TYPE_DMESG: + if (id >= cxt->max_dump_cnt) + return -EINVAL; + prz = cxt->przs[id]; + break; + case PSTORE_TYPE_CONSOLE: + prz = cxt->cprz; + break; + default: return -EINVAL; + } - persistent_ram_free_old(cxt->przs[id]); - persistent_ram_zap(cxt->przs[id]); + persistent_ram_free_old(prz); + persistent_ram_zap(prz); return 0; } @@ -276,6 +304,32 @@ fail_prz: return err; } +static int ramoops_init_prz(struct device *dev, struct ramoops_context *cxt, + struct persistent_ram_zone **prz, + phys_addr_t *paddr, size_t sz) +{ + if (!sz) + return 0; + + if (*paddr + sz > *paddr + cxt->size) + return -ENOMEM; + + *prz = persistent_ram_new(*paddr, sz, cxt->ecc); + if (IS_ERR(*prz)) { + int err = PTR_ERR(*prz); + + dev_err(dev, "failed to request mem region (0x%zx@0x%llx): %d\n", + sz, (unsigned long long)*paddr, err); + return err; + } + + persistent_ram_zap(*prz); + + *paddr += sz; + + return 0; +} + static int __init ramoops_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -291,34 +345,50 @@ static int __init ramoops_probe(struct platform_device *pdev) if (cxt->max_dump_cnt) goto fail_out; - if (!pdata->mem_size || !pdata->record_size) { - pr_err("The memory size and the record size must be " + if (!pdata->mem_size || (!pdata->record_size && !pdata->console_size)) { + pr_err("The memory size and the record/console size must be " "non-zero\n"); goto fail_out; } pdata->mem_size = rounddown_pow_of_two(pdata->mem_size); pdata->record_size = rounddown_pow_of_two(pdata->record_size); + pdata->console_size = rounddown_pow_of_two(pdata->console_size); cxt->dump_read_cnt = 0; cxt->size = pdata->mem_size; cxt->phys_addr = pdata->mem_address; cxt->record_size = pdata->record_size; + cxt->console_size = pdata->console_size; cxt->dump_oops = pdata->dump_oops; cxt->ecc = pdata->ecc; paddr = cxt->phys_addr; - dump_mem_sz = cxt->size; + dump_mem_sz = cxt->size - cxt->console_size; err = ramoops_init_przs(dev, cxt, &paddr, dump_mem_sz); - if (err) { + if (err) + goto fail_out; + + err = ramoops_init_prz(dev, cxt, &cxt->cprz, &paddr, cxt->console_size); + if (err) + goto fail_init_cprz; + + if (!cxt->przs && !cxt->cprz) { pr_err("memory size too small, minimum is %lu\n", - cxt->record_size); - goto fail_count; + cxt->console_size + cxt->record_size); + goto fail_cnt; } cxt->pstore.data = cxt; - cxt->pstore.bufsize = cxt->przs[0]->buffer_size; + /* + * Console can handle any buffer size, so prefer dumps buffer + * size since usually it is smaller. + */ + if (cxt->przs) + cxt->pstore.bufsize = cxt->przs[0]->buffer_size; + else + cxt->pstore.bufsize = cxt->cprz->buffer_size; cxt->pstore.buf = kmalloc(cxt->pstore.bufsize, GFP_KERNEL); spin_lock_init(&cxt->pstore.buf_lock); if (!cxt->pstore.buf) { @@ -341,9 +411,8 @@ static int __init ramoops_probe(struct platform_device *pdev) record_size = pdata->record_size; dump_oops = pdata->dump_oops; - pr_info("attached 0x%lx@0x%llx (%ux0x%zx), ecc: %s\n", + pr_info("attached 0x%lx@0x%llx, ecc: %s\n", cxt->size, (unsigned long long)cxt->phys_addr, - cxt->max_dump_cnt, cxt->record_size, ramoops_ecc ? "on" : "off"); return 0; @@ -353,7 +422,9 @@ fail_buf: fail_clear: cxt->pstore.bufsize = 0; cxt->max_dump_cnt = 0; -fail_count: +fail_cnt: + kfree(cxt->cprz); +fail_init_cprz: ramoops_free_przs(cxt); fail_out: return err; @@ -405,6 +476,7 @@ static int __init ramoops_init(void) dummy_data->mem_size = mem_size; dummy_data->mem_address = mem_address; dummy_data->record_size = record_size; + dummy_data->console_size = ramoops_console_size; dummy_data->dump_oops = dump_oops; dummy_data->ecc = ramoops_ecc; dummy = platform_create_bundle(&ramoops_driver, ramoops_probe, diff --git a/include/linux/pstore_ram.h b/include/linux/pstore_ram.h index 3b823d49a85a..9385d41cb1c3 100644 --- a/include/linux/pstore_ram.h +++ b/include/linux/pstore_ram.h @@ -93,6 +93,7 @@ struct ramoops_platform_data { unsigned long mem_size; unsigned long mem_address; unsigned long record_size; + unsigned long console_size; int dump_oops; bool ecc; }; -- cgit v1.2.3 From b8587daa756141da776e3d4c3a5a315f5af78708 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Sat, 26 May 2012 06:20:27 -0700 Subject: pstore/ram_core: Remove now unused code The code tried to maintain the global list of persistent ram zones, which isn't a great idea overall, plus since Android's ram_console is no longer there, we can remove some unused functions. Signed-off-by: Anton Vorontsov Acked-by: Kees Cook Acked-by: Colin Cross Signed-off-by: Greg Kroah-Hartman --- fs/pstore/ram_core.c | 77 ---------------------------------------------- include/linux/pstore_ram.h | 19 ------------ 2 files changed, 96 deletions(-) (limited to 'include') diff --git a/fs/pstore/ram_core.c b/fs/pstore/ram_core.c index 78f6d4b2addb..0fd81611525c 100644 --- a/fs/pstore/ram_core.c +++ b/fs/pstore/ram_core.c @@ -35,8 +35,6 @@ struct persistent_ram_buffer { #define PERSISTENT_RAM_SIG (0x43474244) /* DBGC */ -static __initdata LIST_HEAD(persistent_ram_list); - static inline size_t buffer_size(struct persistent_ram_zone *prz) { return atomic_read(&prz->buffer->size); @@ -462,78 +460,3 @@ err: kfree(prz); return ERR_PTR(ret); } - -#ifndef MODULE -static int __init persistent_ram_buffer_init(const char *name, - struct persistent_ram_zone *prz) -{ - int i; - struct persistent_ram *ram; - struct persistent_ram_descriptor *desc; - phys_addr_t start; - - list_for_each_entry(ram, &persistent_ram_list, node) { - start = ram->start; - for (i = 0; i < ram->num_descs; i++) { - desc = &ram->descs[i]; - if (!strcmp(desc->name, name)) - return persistent_ram_buffer_map(start, - desc->size, prz); - start += desc->size; - } - } - - return -EINVAL; -} - -static __init -struct persistent_ram_zone *__persistent_ram_init(struct device *dev, bool ecc) -{ - struct persistent_ram_zone *prz; - int ret = -ENOMEM; - - prz = kzalloc(sizeof(struct persistent_ram_zone), GFP_KERNEL); - if (!prz) { - pr_err("persistent_ram: failed to allocate persistent ram zone\n"); - goto err; - } - - ret = persistent_ram_buffer_init(dev_name(dev), prz); - if (ret) { - pr_err("persistent_ram: failed to initialize buffer\n"); - goto err; - } - - persistent_ram_post_init(prz, ecc); - - return prz; -err: - kfree(prz); - return ERR_PTR(ret); -} - -struct persistent_ram_zone * __init -persistent_ram_init_ringbuffer(struct device *dev, bool ecc) -{ - return __persistent_ram_init(dev, ecc); -} - -int __init persistent_ram_early_init(struct persistent_ram *ram) -{ - int ret; - - ret = memblock_reserve(ram->start, ram->size); - if (ret) { - pr_err("Failed to reserve persistent memory from %08lx-%08lx\n", - (long)ram->start, (long)(ram->start + ram->size - 1)); - return ret; - } - - list_add_tail(&ram->node, &persistent_ram_list); - - pr_info("Initialized persistent memory from %08lx-%08lx\n", - (long)ram->start, (long)(ram->start + ram->size - 1)); - - return 0; -} -#endif diff --git a/include/linux/pstore_ram.h b/include/linux/pstore_ram.h index 9385d41cb1c3..2470bb591434 100644 --- a/include/linux/pstore_ram.h +++ b/include/linux/pstore_ram.h @@ -25,21 +25,6 @@ struct persistent_ram_buffer; -struct persistent_ram_descriptor { - const char *name; - phys_addr_t size; -}; - -struct persistent_ram { - phys_addr_t start; - phys_addr_t size; - - int num_descs; - struct persistent_ram_descriptor *descs; - - struct list_head node; -}; - struct persistent_ram_zone { phys_addr_t paddr; size_t size; @@ -63,15 +48,11 @@ struct persistent_ram_zone { size_t old_log_size; }; -int persistent_ram_early_init(struct persistent_ram *ram); - struct persistent_ram_zone * __init persistent_ram_new(phys_addr_t start, size_t size, bool ecc); void persistent_ram_free(struct persistent_ram_zone *prz); void persistent_ram_zap(struct persistent_ram_zone *prz); -struct persistent_ram_zone *persistent_ram_init_ringbuffer(struct device *dev, - bool ecc); int persistent_ram_write(struct persistent_ram_zone *prz, const void *s, unsigned int count); -- cgit v1.2.3 From 4c2ef53d3bfb36659c47ba589f35bcab24f425c7 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 4 Jun 2012 13:35:31 +0200 Subject: TTY: vt, remove con_schedule_flip This is identical to tty_schedule_flip. So let us use that instead. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/keyboard.c | 6 +++--- drivers/tty/vt/vt.c | 2 +- include/linux/kbd_kern.h | 12 ------------ 3 files changed, 4 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 48cc6f25cfd3..0b6217c93036 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -310,7 +310,7 @@ static void put_queue(struct vc_data *vc, int ch) if (tty) { tty_insert_flip_char(tty, ch, 0); - con_schedule_flip(tty); + tty_schedule_flip(tty); } } @@ -325,7 +325,7 @@ static void puts_queue(struct vc_data *vc, char *cp) tty_insert_flip_char(tty, *cp, 0); cp++; } - con_schedule_flip(tty); + tty_schedule_flip(tty); } static void applkey(struct vc_data *vc, int key, char mode) @@ -586,7 +586,7 @@ static void fn_send_intr(struct vc_data *vc) if (!tty) return; tty_insert_flip_char(tty, 0, TTY_BREAK); - con_schedule_flip(tty); + tty_schedule_flip(tty); } static void fn_scroll_forw(struct vc_data *vc) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 84cbf298c094..88a4914ef557 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1380,7 +1380,7 @@ static void respond_string(const char *p, struct tty_struct *tty) tty_insert_flip_char(tty, *p, 0); p++; } - con_schedule_flip(tty); + tty_schedule_flip(tty); } static void cursor_report(struct vc_data *vc, struct tty_struct *tty) diff --git a/include/linux/kbd_kern.h b/include/linux/kbd_kern.h index daf4a3a40ee0..af9137db3035 100644 --- a/include/linux/kbd_kern.h +++ b/include/linux/kbd_kern.h @@ -145,16 +145,4 @@ void compute_shiftstate(void); extern unsigned int keymap_count; -/* console.c */ - -static inline void con_schedule_flip(struct tty_struct *t) -{ - unsigned long flags; - spin_lock_irqsave(&t->buf.lock, flags); - if (t->buf.tail != NULL) - t->buf.tail->commit = t->buf.tail->used; - spin_unlock_irqrestore(&t->buf.lock, flags); - schedule_work(&t->buf.work); -} - #endif -- cgit v1.2.3 From 695586ca20c56cf8cfa87160383307a288d32496 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 4 Jun 2012 13:35:32 +0200 Subject: TTY: provide drivers with tty_port_install This will be used in tty_ops->install to set tty->port (and to call tty_standard_install). Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_port.c | 8 ++++++++ include/linux/tty.h | 2 ++ 2 files changed, 10 insertions(+) (limited to 'include') diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index bf6e238146ae..1ac8abf4708d 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -413,6 +413,14 @@ void tty_port_close(struct tty_port *port, struct tty_struct *tty, } EXPORT_SYMBOL(tty_port_close); +int tty_port_install(struct tty_port *port, struct tty_driver *driver, + struct tty_struct *tty) +{ + tty->port = port; + return tty_standard_install(driver, tty); +} +EXPORT_SYMBOL_GPL(tty_port_install); + int tty_port_open(struct tty_port *port, struct tty_struct *tty, struct file *filp) { diff --git a/include/linux/tty.h b/include/linux/tty.h index 9f47ab540f65..45ef71df0e72 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -521,6 +521,8 @@ extern int tty_port_close_start(struct tty_port *port, extern void tty_port_close_end(struct tty_port *port, struct tty_struct *tty); extern void tty_port_close(struct tty_port *port, struct tty_struct *tty, struct file *filp); +extern int tty_port_install(struct tty_port *port, struct tty_driver *driver, + struct tty_struct *tty); extern int tty_port_open(struct tty_port *port, struct tty_struct *tty, struct file *filp); static inline int tty_port_users(struct tty_port *port) -- cgit v1.2.3 From 04831dc154df9b83c3e5fd54b18448da507871f7 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 4 Jun 2012 13:35:36 +0200 Subject: TTY: add ports array to tty_driver It will hold tty_port structures for all drivers which do not want to define tty->ops->install hook. We ignore PTY here because it wants 1 million lines and it installs tty_port in ->install anyway. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 18 +++++++++++++++++- include/linux/tty_driver.h | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index d6e045b7079a..ac96f74573d0 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1407,6 +1407,9 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) if (retval < 0) goto err_deinit_tty; + if (!tty->port) + tty->port = driver->ports[idx]; + /* * Structures all installed ... call the ldisc open routines. * If we fail here just call release_tty to clean up. No need @@ -3094,6 +3097,7 @@ static void destruct_tty_driver(struct kref *kref) kfree(p); cdev_del(&driver->cdev); } + kfree(driver->ports); kfree(driver); } @@ -3132,6 +3136,18 @@ int tty_register_driver(struct tty_driver *driver) if (!p) return -ENOMEM; } + /* + * There is too many lines in PTY and we won't need the array there + * since it has an ->install hook where it assigns ports properly. + */ + if (driver->type != TTY_DRIVER_TYPE_PTY) { + driver->ports = kcalloc(driver->num, sizeof(struct tty_port *), + GFP_KERNEL); + if (!driver->ports) { + error = -ENOMEM; + goto err_free_p; + } + } if (!driver->major) { error = alloc_chrdev_region(&dev, driver->minor_start, @@ -3190,7 +3206,7 @@ err_unreg_char: unregister_chrdev_region(dev, driver->num); driver->ttys = NULL; driver->termios = NULL; -err_free_p: +err_free_p: /* destruct_tty_driver will free driver->ports */ kfree(p); return error; } diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 6e6dbb7447b6..04419c141b00 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -313,6 +313,7 @@ struct tty_driver { * Pointer to the tty data structures */ struct tty_struct **ttys; + struct tty_port **ports; struct ktermios **termios; void *driver_state; -- cgit v1.2.3 From 057eb856eda1d957c0f1155eaa90739221f809a7 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 4 Jun 2012 13:35:37 +0200 Subject: TTY: add tty_port_register_device helper This will automatically assign tty_port to tty_driver's port array for later recall in tty_init_dev. This is intended to be called instead of tty_register_device. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_port.c | 9 +++++++++ include/linux/tty.h | 3 +++ 2 files changed, 12 insertions(+) (limited to 'include') diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 1ac8abf4708d..4e9d2b291f4a 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -33,6 +33,15 @@ void tty_port_init(struct tty_port *port) } EXPORT_SYMBOL(tty_port_init); +struct device *tty_port_register_device(struct tty_port *port, + struct tty_driver *driver, unsigned index, + struct device *device) +{ + driver->ports[index] = port; + return tty_register_device(driver, index, device); +} +EXPORT_SYMBOL_GPL(tty_port_register_device); + int tty_port_alloc_xmit_buf(struct tty_port *port) { /* We may sleep in get_zeroed_page() */ diff --git a/include/linux/tty.h b/include/linux/tty.h index 45ef71df0e72..40b18d7ad929 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -497,6 +497,9 @@ extern int tty_write_lock(struct tty_struct *tty, int ndelay); #define tty_is_writelocked(tty) (mutex_is_locked(&tty->atomic_write_lock)) extern void tty_port_init(struct tty_port *port); +extern struct device *tty_port_register_device(struct tty_port *port, + struct tty_driver *driver, unsigned index, + struct device *device); extern int tty_port_alloc_xmit_buf(struct tty_port *port); extern void tty_port_free_xmit_buf(struct tty_port *port); extern void tty_port_put(struct tty_port *port); -- cgit v1.2.3 From b8c24c4aef94b1f0daafb450363fef13a1163780 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 13 Jun 2012 10:24:52 -0500 Subject: slob: Define page struct fields used in mm_types.h Define the fields used by slob in mm_types.h and use struct page instead of struct slob_page in slob. This cleans up numerous of typecasts in slob.c and makes readers aware of slob's use of page struct fields. [Also cleans up some bitrot in slob.c. The page struct field layout in slob.c is an old layout and does not match the one in mm_types.h] Reviewed-by: Glauber Costa Acked-by: David Rientjes Reviewed-by: Joonsoo Kim Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/mm_types.h | 7 +++- mm/slob.c | 95 +++++++++++++++++++----------------------------- 2 files changed, 42 insertions(+), 60 deletions(-) (limited to 'include') diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index dad95bdd06d7..5922c3452592 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -53,7 +53,7 @@ struct page { struct { union { pgoff_t index; /* Our offset within mapping. */ - void *freelist; /* slub first free object */ + void *freelist; /* slub/slob first free object */ }; union { @@ -81,11 +81,12 @@ struct page { */ atomic_t _mapcount; - struct { + struct { /* SLUB */ unsigned inuse:16; unsigned objects:15; unsigned frozen:1; }; + int units; /* SLOB */ }; atomic_t _count; /* Usage count, see below. */ }; @@ -107,6 +108,8 @@ struct page { short int pobjects; #endif }; + + struct list_head list; /* slobs list of pages */ }; /* Remainder is not double word aligned */ diff --git a/mm/slob.c b/mm/slob.c index 8105be42cad1..30862a2d56a9 100644 --- a/mm/slob.c +++ b/mm/slob.c @@ -91,34 +91,13 @@ struct slob_block { }; typedef struct slob_block slob_t; -/* - * We use struct page fields to manage some slob allocation aspects, - * however to avoid the horrible mess in include/linux/mm_types.h, we'll - * just define our own struct page type variant here. - */ -struct slob_page { - union { - struct { - unsigned long flags; /* mandatory */ - atomic_t _count; /* mandatory */ - slobidx_t units; /* free units left in page */ - unsigned long pad[2]; - slob_t *free; /* first free slob_t in page */ - struct list_head list; /* linked list of free pages */ - }; - struct page page; - }; -}; -static inline void struct_slob_page_wrong_size(void) -{ BUILD_BUG_ON(sizeof(struct slob_page) != sizeof(struct page)); } - /* * free_slob_page: call before a slob_page is returned to the page allocator. */ -static inline void free_slob_page(struct slob_page *sp) +static inline void free_slob_page(struct page *sp) { - reset_page_mapcount(&sp->page); - sp->page.mapping = NULL; + reset_page_mapcount(sp); + sp->mapping = NULL; } /* @@ -133,44 +112,44 @@ static LIST_HEAD(free_slob_large); /* * is_slob_page: True for all slob pages (false for bigblock pages) */ -static inline int is_slob_page(struct slob_page *sp) +static inline int is_slob_page(struct page *sp) { - return PageSlab((struct page *)sp); + return PageSlab(sp); } -static inline void set_slob_page(struct slob_page *sp) +static inline void set_slob_page(struct page *sp) { - __SetPageSlab((struct page *)sp); + __SetPageSlab(sp); } -static inline void clear_slob_page(struct slob_page *sp) +static inline void clear_slob_page(struct page *sp) { - __ClearPageSlab((struct page *)sp); + __ClearPageSlab(sp); } -static inline struct slob_page *slob_page(const void *addr) +static inline struct page *slob_page(const void *addr) { - return (struct slob_page *)virt_to_page(addr); + return virt_to_page(addr); } /* * slob_page_free: true for pages on free_slob_pages list. */ -static inline int slob_page_free(struct slob_page *sp) +static inline int slob_page_free(struct page *sp) { - return PageSlobFree((struct page *)sp); + return PageSlobFree(sp); } -static void set_slob_page_free(struct slob_page *sp, struct list_head *list) +static void set_slob_page_free(struct page *sp, struct list_head *list) { list_add(&sp->list, list); - __SetPageSlobFree((struct page *)sp); + __SetPageSlobFree(sp); } -static inline void clear_slob_page_free(struct slob_page *sp) +static inline void clear_slob_page_free(struct page *sp) { list_del(&sp->list); - __ClearPageSlobFree((struct page *)sp); + __ClearPageSlobFree(sp); } #define SLOB_UNIT sizeof(slob_t) @@ -267,12 +246,12 @@ static void slob_free_pages(void *b, int order) /* * Allocate a slob block within a given slob_page sp. */ -static void *slob_page_alloc(struct slob_page *sp, size_t size, int align) +static void *slob_page_alloc(struct page *sp, size_t size, int align) { slob_t *prev, *cur, *aligned = NULL; int delta = 0, units = SLOB_UNITS(size); - for (prev = NULL, cur = sp->free; ; prev = cur, cur = slob_next(cur)) { + for (prev = NULL, cur = sp->freelist; ; prev = cur, cur = slob_next(cur)) { slobidx_t avail = slob_units(cur); if (align) { @@ -296,12 +275,12 @@ static void *slob_page_alloc(struct slob_page *sp, size_t size, int align) if (prev) set_slob(prev, slob_units(prev), next); else - sp->free = next; + sp->freelist = next; } else { /* fragment */ if (prev) set_slob(prev, slob_units(prev), cur + units); else - sp->free = cur + units; + sp->freelist = cur + units; set_slob(cur + units, avail - units, next); } @@ -320,7 +299,7 @@ static void *slob_page_alloc(struct slob_page *sp, size_t size, int align) */ static void *slob_alloc(size_t size, gfp_t gfp, int align, int node) { - struct slob_page *sp; + struct page *sp; struct list_head *prev; struct list_head *slob_list; slob_t *b = NULL; @@ -341,7 +320,7 @@ static void *slob_alloc(size_t size, gfp_t gfp, int align, int node) * If there's a node specification, search for a partial * page with a matching node id in the freelist. */ - if (node != -1 && page_to_nid(&sp->page) != node) + if (node != -1 && page_to_nid(sp) != node) continue; #endif /* Enough room on this page? */ @@ -374,7 +353,7 @@ static void *slob_alloc(size_t size, gfp_t gfp, int align, int node) spin_lock_irqsave(&slob_lock, flags); sp->units = SLOB_UNITS(PAGE_SIZE); - sp->free = b; + sp->freelist = b; INIT_LIST_HEAD(&sp->list); set_slob(b, SLOB_UNITS(PAGE_SIZE), b + SLOB_UNITS(PAGE_SIZE)); set_slob_page_free(sp, slob_list); @@ -392,7 +371,7 @@ static void *slob_alloc(size_t size, gfp_t gfp, int align, int node) */ static void slob_free(void *block, int size) { - struct slob_page *sp; + struct page *sp; slob_t *prev, *next, *b = (slob_t *)block; slobidx_t units; unsigned long flags; @@ -421,7 +400,7 @@ static void slob_free(void *block, int size) if (!slob_page_free(sp)) { /* This slob page is about to become partially free. Easy! */ sp->units = units; - sp->free = b; + sp->freelist = b; set_slob(b, units, (void *)((unsigned long)(b + SLOB_UNITS(PAGE_SIZE)) & PAGE_MASK)); @@ -441,15 +420,15 @@ static void slob_free(void *block, int size) */ sp->units += units; - if (b < sp->free) { - if (b + units == sp->free) { - units += slob_units(sp->free); - sp->free = slob_next(sp->free); + if (b < (slob_t *)sp->freelist) { + if (b + units == sp->freelist) { + units += slob_units(sp->freelist); + sp->freelist = slob_next(sp->freelist); } - set_slob(b, units, sp->free); - sp->free = b; + set_slob(b, units, sp->freelist); + sp->freelist = b; } else { - prev = sp->free; + prev = sp->freelist; next = slob_next(prev); while (b > next) { prev = next; @@ -522,7 +501,7 @@ EXPORT_SYMBOL(__kmalloc_node); void kfree(const void *block) { - struct slob_page *sp; + struct page *sp; trace_kfree(_RET_IP_, block); @@ -536,14 +515,14 @@ void kfree(const void *block) unsigned int *m = (unsigned int *)(block - align); slob_free(m, *m + align); } else - put_page(&sp->page); + put_page(sp); } EXPORT_SYMBOL(kfree); /* can't use ksize for kmem_cache_alloc memory, only kmalloc */ size_t ksize(const void *block) { - struct slob_page *sp; + struct page *sp; BUG_ON(!block); if (unlikely(block == ZERO_SIZE_PTR)) @@ -555,7 +534,7 @@ size_t ksize(const void *block) unsigned int *m = (unsigned int *)(block - align); return SLOB_UNITS(*m) * SLOB_UNIT; } else - return sp->page.private; + return sp->private; } EXPORT_SYMBOL(ksize); -- cgit v1.2.3 From e571b0ad3495be5793e54e21cd244c4545c49d88 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 13 Jun 2012 10:24:55 -0500 Subject: slab: Use page struct fields instead of casting Add fields to the page struct so that it is properly documented that slab overlays the lru fields. This cleans up some casts in slab. Reviewed-by: Glauber Costa Reviewed-by: Joonsoo Kim Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/mm_types.h | 4 ++++ mm/slab.c | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 5922c3452592..680a5e4e8cd5 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -110,6 +110,10 @@ struct page { }; struct list_head list; /* slobs list of pages */ + struct { /* slab fields */ + struct kmem_cache *slab_cache; + struct slab *slab_page; + }; }; /* Remainder is not double word aligned */ diff --git a/mm/slab.c b/mm/slab.c index e901a36e2520..af05147d7abd 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -496,25 +496,25 @@ static bool slab_max_order_set __initdata; */ static inline void page_set_cache(struct page *page, struct kmem_cache *cache) { - page->lru.next = (struct list_head *)cache; + page->slab_cache = cache; } static inline struct kmem_cache *page_get_cache(struct page *page) { page = compound_head(page); BUG_ON(!PageSlab(page)); - return (struct kmem_cache *)page->lru.next; + return page->slab_cache; } static inline void page_set_slab(struct page *page, struct slab *slab) { - page->lru.prev = (struct list_head *)slab; + page->slab_page = slab; } static inline struct slab *page_get_slab(struct page *page) { BUG_ON(!PageSlab(page)); - return (struct slab *)page->lru.prev; + return page->slab_page; } static inline struct kmem_cache *virt_to_cache(const void *obj) -- cgit v1.2.3 From 3b0efdfa1e719303536c04d9abca43abeb40f80a Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 13 Jun 2012 10:24:57 -0500 Subject: mm, sl[aou]b: Extract common fields from struct kmem_cache Define a struct that describes common fields used in all slab allocators. A slab allocator either uses the common definition (like SLOB) or is required to provide members of kmem_cache with the definition given. After that it will be possible to share code that only operates on those fields of kmem_cache. The patch basically takes the slob definition of kmem cache and uses the field namees for the other allocators. It also standardizes the names used for basic object lengths in allocators: object_size Struct size specified at kmem_cache_create. Basically the payload expected to be used by the subsystem. size The size of memory allocator for each object. This size is larger than object_size and includes padding, alignment and extra metadata for each object (f.e. for debugging and rcu). Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/slab.h | 24 ++++++++++ include/linux/slab_def.h | 10 ++-- include/linux/slub_def.h | 2 +- mm/slab.c | 117 +++++++++++++++++++++++------------------------ mm/slob.c | 9 +--- mm/slub.c | 80 ++++++++++++++++---------------- 6 files changed, 130 insertions(+), 112 deletions(-) (limited to 'include') diff --git a/include/linux/slab.h b/include/linux/slab.h index 67d5d94b783a..0dd2dfa7beca 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -92,6 +92,30 @@ #define ZERO_OR_NULL_PTR(x) ((unsigned long)(x) <= \ (unsigned long)ZERO_SIZE_PTR) +/* + * Common fields provided in kmem_cache by all slab allocators + * This struct is either used directly by the allocator (SLOB) + * or the allocator must include definitions for all fields + * provided in kmem_cache_common in their definition of kmem_cache. + * + * Once we can do anonymous structs (C11 standard) we could put a + * anonymous struct definition in these allocators so that the + * separate allocations in the kmem_cache structure of SLAB and + * SLUB is no longer needed. + */ +#ifdef CONFIG_SLOB +struct kmem_cache { + unsigned int object_size;/* The original size of the object */ + unsigned int size; /* The aligned/padded/added on size */ + unsigned int align; /* Alignment as calculated */ + unsigned long flags; /* Active flags on the slab */ + const char *name; /* Slab name for sysfs */ + int refcount; /* Use counter */ + void (*ctor)(void *); /* Called on object slot creation */ + struct list_head list; /* List of all slab caches on the system */ +}; +#endif + /* * struct kmem_cache related prototypes */ diff --git a/include/linux/slab_def.h b/include/linux/slab_def.h index fbd1117fdfde..1d93f27d81de 100644 --- a/include/linux/slab_def.h +++ b/include/linux/slab_def.h @@ -27,7 +27,7 @@ struct kmem_cache { unsigned int limit; unsigned int shared; - unsigned int buffer_size; + unsigned int size; u32 reciprocal_buffer_size; /* 2) touched by every alloc & free from the backend */ @@ -52,7 +52,10 @@ struct kmem_cache { /* 4) cache creation/removal */ const char *name; - struct list_head next; + struct list_head list; + int refcount; + int object_size; + int align; /* 5) statistics */ #ifdef CONFIG_DEBUG_SLAB @@ -73,12 +76,11 @@ struct kmem_cache { /* * If debugging is enabled, then the allocator can add additional - * fields and/or padding to every object. buffer_size contains the total + * fields and/or padding to every object. size contains the total * object size including these internal fields, the following two * variables contain the offset to the user object and its size. */ int obj_offset; - int obj_size; #endif /* CONFIG_DEBUG_SLAB */ /* 6) per-cpu/per-node data, touched during every alloc/free */ diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index ebdcf4ba42ee..df448adb7283 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -82,7 +82,7 @@ struct kmem_cache { unsigned long flags; unsigned long min_partial; int size; /* The size of an object including meta data */ - int objsize; /* The size of an object without meta data */ + int object_size; /* The size of an object without meta data */ int offset; /* Free pointer offset. */ int cpu_partial; /* Number of per cpu partial objects to keep around */ struct kmem_cache_order_objects oo; diff --git a/mm/slab.c b/mm/slab.c index 28a8f7d29d4a..e2b3907b7b0c 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -424,8 +424,8 @@ static void kmem_list3_init(struct kmem_list3 *parent) * cachep->obj_offset - BYTES_PER_WORD .. cachep->obj_offset - 1: * redzone word. * cachep->obj_offset: The real object. - * cachep->buffer_size - 2* BYTES_PER_WORD: redzone word [BYTES_PER_WORD long] - * cachep->buffer_size - 1* BYTES_PER_WORD: last caller address + * cachep->size - 2* BYTES_PER_WORD: redzone word [BYTES_PER_WORD long] + * cachep->size - 1* BYTES_PER_WORD: last caller address * [BYTES_PER_WORD long] */ static int obj_offset(struct kmem_cache *cachep) @@ -435,7 +435,7 @@ static int obj_offset(struct kmem_cache *cachep) static int obj_size(struct kmem_cache *cachep) { - return cachep->obj_size; + return cachep->object_size; } static unsigned long long *dbg_redzone1(struct kmem_cache *cachep, void *objp) @@ -449,23 +449,23 @@ static unsigned long long *dbg_redzone2(struct kmem_cache *cachep, void *objp) { BUG_ON(!(cachep->flags & SLAB_RED_ZONE)); if (cachep->flags & SLAB_STORE_USER) - return (unsigned long long *)(objp + cachep->buffer_size - + return (unsigned long long *)(objp + cachep->size - sizeof(unsigned long long) - REDZONE_ALIGN); - return (unsigned long long *) (objp + cachep->buffer_size - + return (unsigned long long *) (objp + cachep->size - sizeof(unsigned long long)); } static void **dbg_userword(struct kmem_cache *cachep, void *objp) { BUG_ON(!(cachep->flags & SLAB_STORE_USER)); - return (void **)(objp + cachep->buffer_size - BYTES_PER_WORD); + return (void **)(objp + cachep->size - BYTES_PER_WORD); } #else #define obj_offset(x) 0 -#define obj_size(cachep) (cachep->buffer_size) +#define obj_size(cachep) (cachep->size) #define dbg_redzone1(cachep, objp) ({BUG(); (unsigned long long *)NULL;}) #define dbg_redzone2(cachep, objp) ({BUG(); (unsigned long long *)NULL;}) #define dbg_userword(cachep, objp) ({BUG(); (void **)NULL;}) @@ -475,7 +475,7 @@ static void **dbg_userword(struct kmem_cache *cachep, void *objp) #ifdef CONFIG_TRACING size_t slab_buffer_size(struct kmem_cache *cachep) { - return cachep->buffer_size; + return cachep->size; } EXPORT_SYMBOL(slab_buffer_size); #endif @@ -513,13 +513,13 @@ static inline struct slab *virt_to_slab(const void *obj) static inline void *index_to_obj(struct kmem_cache *cache, struct slab *slab, unsigned int idx) { - return slab->s_mem + cache->buffer_size * idx; + return slab->s_mem + cache->size * idx; } /* - * We want to avoid an expensive divide : (offset / cache->buffer_size) - * Using the fact that buffer_size is a constant for a particular cache, - * we can replace (offset / cache->buffer_size) by + * We want to avoid an expensive divide : (offset / cache->size) + * Using the fact that size is a constant for a particular cache, + * we can replace (offset / cache->size) by * reciprocal_divide(offset, cache->reciprocal_buffer_size) */ static inline unsigned int obj_to_index(const struct kmem_cache *cache, @@ -565,7 +565,7 @@ static struct kmem_cache cache_cache = { .batchcount = 1, .limit = BOOT_CPUCACHE_ENTRIES, .shared = 1, - .buffer_size = sizeof(struct kmem_cache), + .size = sizeof(struct kmem_cache), .name = "kmem_cache", }; @@ -1134,7 +1134,7 @@ static int init_cache_nodelists_node(int node) struct kmem_list3 *l3; const int memsize = sizeof(struct kmem_list3); - list_for_each_entry(cachep, &cache_chain, next) { + list_for_each_entry(cachep, &cache_chain, list) { /* * Set up the size64 kmemlist for cpu before we can * begin anything. Make sure some other cpu on this @@ -1172,7 +1172,7 @@ static void __cpuinit cpuup_canceled(long cpu) int node = cpu_to_mem(cpu); const struct cpumask *mask = cpumask_of_node(node); - list_for_each_entry(cachep, &cache_chain, next) { + list_for_each_entry(cachep, &cache_chain, list) { struct array_cache *nc; struct array_cache *shared; struct array_cache **alien; @@ -1222,7 +1222,7 @@ free_array_cache: * the respective cache's slabs, now we can go ahead and * shrink each nodelist to its limit. */ - list_for_each_entry(cachep, &cache_chain, next) { + list_for_each_entry(cachep, &cache_chain, list) { l3 = cachep->nodelists[node]; if (!l3) continue; @@ -1251,7 +1251,7 @@ static int __cpuinit cpuup_prepare(long cpu) * Now we can go ahead with allocating the shared arrays and * array caches */ - list_for_each_entry(cachep, &cache_chain, next) { + list_for_each_entry(cachep, &cache_chain, list) { struct array_cache *nc; struct array_cache *shared = NULL; struct array_cache **alien = NULL; @@ -1383,7 +1383,7 @@ static int __meminit drain_cache_nodelists_node(int node) struct kmem_cache *cachep; int ret = 0; - list_for_each_entry(cachep, &cache_chain, next) { + list_for_each_entry(cachep, &cache_chain, list) { struct kmem_list3 *l3; l3 = cachep->nodelists[node]; @@ -1526,7 +1526,7 @@ void __init kmem_cache_init(void) /* 1) create the cache_cache */ INIT_LIST_HEAD(&cache_chain); - list_add(&cache_cache.next, &cache_chain); + list_add(&cache_cache.list, &cache_chain); cache_cache.colour_off = cache_line_size(); cache_cache.array[smp_processor_id()] = &initarray_cache.cache; cache_cache.nodelists[node] = &initkmem_list3[CACHE_CACHE + node]; @@ -1534,18 +1534,16 @@ void __init kmem_cache_init(void) /* * struct kmem_cache size depends on nr_node_ids & nr_cpu_ids */ - cache_cache.buffer_size = offsetof(struct kmem_cache, array[nr_cpu_ids]) + + cache_cache.size = offsetof(struct kmem_cache, array[nr_cpu_ids]) + nr_node_ids * sizeof(struct kmem_list3 *); -#if DEBUG - cache_cache.obj_size = cache_cache.buffer_size; -#endif - cache_cache.buffer_size = ALIGN(cache_cache.buffer_size, + cache_cache.object_size = cache_cache.size; + cache_cache.size = ALIGN(cache_cache.size, cache_line_size()); cache_cache.reciprocal_buffer_size = - reciprocal_value(cache_cache.buffer_size); + reciprocal_value(cache_cache.size); for (order = 0; order < MAX_ORDER; order++) { - cache_estimate(order, cache_cache.buffer_size, + cache_estimate(order, cache_cache.size, cache_line_size(), 0, &left_over, &cache_cache.num); if (cache_cache.num) break; @@ -1671,7 +1669,7 @@ void __init kmem_cache_init_late(void) /* 6) resize the head arrays to their final sizes */ mutex_lock(&cache_chain_mutex); - list_for_each_entry(cachep, &cache_chain, next) + list_for_each_entry(cachep, &cache_chain, list) if (enable_cpucache(cachep, GFP_NOWAIT)) BUG(); mutex_unlock(&cache_chain_mutex); @@ -1724,7 +1722,7 @@ slab_out_of_memory(struct kmem_cache *cachep, gfp_t gfpflags, int nodeid) "SLAB: Unable to allocate memory on node %d (gfp=0x%x)\n", nodeid, gfpflags); printk(KERN_WARNING " cache: %s, object size: %d, order: %d\n", - cachep->name, cachep->buffer_size, cachep->gfporder); + cachep->name, cachep->size, cachep->gfporder); for_each_online_node(node) { unsigned long active_objs = 0, num_objs = 0, free_objects = 0; @@ -2028,10 +2026,10 @@ static void slab_destroy_debugcheck(struct kmem_cache *cachep, struct slab *slab if (cachep->flags & SLAB_POISON) { #ifdef CONFIG_DEBUG_PAGEALLOC - if (cachep->buffer_size % PAGE_SIZE == 0 && + if (cachep->size % PAGE_SIZE == 0 && OFF_SLAB(cachep)) kernel_map_pages(virt_to_page(objp), - cachep->buffer_size / PAGE_SIZE, 1); + cachep->size / PAGE_SIZE, 1); else check_poison_obj(cachep, objp); #else @@ -2281,7 +2279,7 @@ kmem_cache_create (const char *name, size_t size, size_t align, mutex_lock(&cache_chain_mutex); } - list_for_each_entry(pc, &cache_chain, next) { + list_for_each_entry(pc, &cache_chain, list) { char tmp; int res; @@ -2294,7 +2292,7 @@ kmem_cache_create (const char *name, size_t size, size_t align, if (res) { printk(KERN_ERR "SLAB: cache with size %d has lost its name\n", - pc->buffer_size); + pc->size); continue; } @@ -2399,8 +2397,9 @@ kmem_cache_create (const char *name, size_t size, size_t align, goto oops; cachep->nodelists = (struct kmem_list3 **)&cachep->array[nr_cpu_ids]; + cachep->object_size = size; + cachep->align = align; #if DEBUG - cachep->obj_size = size; /* * Both debugging options require word-alignment which is calculated @@ -2423,7 +2422,7 @@ kmem_cache_create (const char *name, size_t size, size_t align, } #if FORCED_DEBUG && defined(CONFIG_DEBUG_PAGEALLOC) if (size >= malloc_sizes[INDEX_L3 + 1].cs_size - && cachep->obj_size > cache_line_size() && ALIGN(size, align) < PAGE_SIZE) { + && cachep->object_size > cache_line_size() && ALIGN(size, align) < PAGE_SIZE) { cachep->obj_offset += PAGE_SIZE - ALIGN(size, align); size = PAGE_SIZE; } @@ -2492,7 +2491,7 @@ kmem_cache_create (const char *name, size_t size, size_t align, cachep->gfpflags = 0; if (CONFIG_ZONE_DMA_FLAG && (flags & SLAB_CACHE_DMA)) cachep->gfpflags |= GFP_DMA; - cachep->buffer_size = size; + cachep->size = size; cachep->reciprocal_buffer_size = reciprocal_value(size); if (flags & CFLGS_OFF_SLAB) { @@ -2526,7 +2525,7 @@ kmem_cache_create (const char *name, size_t size, size_t align, } /* cache setup completed, link it into the list */ - list_add(&cachep->next, &cache_chain); + list_add(&cachep->list, &cache_chain); oops: if (!cachep && (flags & SLAB_PANIC)) panic("kmem_cache_create(): failed to create slab `%s'\n", @@ -2721,10 +2720,10 @@ void kmem_cache_destroy(struct kmem_cache *cachep) /* * the chain is never empty, cache_cache is never destroyed */ - list_del(&cachep->next); + list_del(&cachep->list); if (__cache_shrink(cachep)) { slab_error(cachep, "Can't free all objects"); - list_add(&cachep->next, &cache_chain); + list_add(&cachep->list, &cache_chain); mutex_unlock(&cache_chain_mutex); put_online_cpus(); return; @@ -2821,10 +2820,10 @@ static void cache_init_objs(struct kmem_cache *cachep, slab_error(cachep, "constructor overwrote the" " start of an object"); } - if ((cachep->buffer_size % PAGE_SIZE) == 0 && + if ((cachep->size % PAGE_SIZE) == 0 && OFF_SLAB(cachep) && cachep->flags & SLAB_POISON) kernel_map_pages(virt_to_page(objp), - cachep->buffer_size / PAGE_SIZE, 0); + cachep->size / PAGE_SIZE, 0); #else if (cachep->ctor) cachep->ctor(objp); @@ -3058,10 +3057,10 @@ static void *cache_free_debugcheck(struct kmem_cache *cachep, void *objp, #endif if (cachep->flags & SLAB_POISON) { #ifdef CONFIG_DEBUG_PAGEALLOC - if ((cachep->buffer_size % PAGE_SIZE)==0 && OFF_SLAB(cachep)) { + if ((cachep->size % PAGE_SIZE)==0 && OFF_SLAB(cachep)) { store_stackinfo(cachep, objp, (unsigned long)caller); kernel_map_pages(virt_to_page(objp), - cachep->buffer_size / PAGE_SIZE, 0); + cachep->size / PAGE_SIZE, 0); } else { poison_obj(cachep, objp, POISON_FREE); } @@ -3211,9 +3210,9 @@ static void *cache_alloc_debugcheck_after(struct kmem_cache *cachep, return objp; if (cachep->flags & SLAB_POISON) { #ifdef CONFIG_DEBUG_PAGEALLOC - if ((cachep->buffer_size % PAGE_SIZE) == 0 && OFF_SLAB(cachep)) + if ((cachep->size % PAGE_SIZE) == 0 && OFF_SLAB(cachep)) kernel_map_pages(virt_to_page(objp), - cachep->buffer_size / PAGE_SIZE, 1); + cachep->size / PAGE_SIZE, 1); else check_poison_obj(cachep, objp); #else @@ -3243,7 +3242,7 @@ static void *cache_alloc_debugcheck_after(struct kmem_cache *cachep, unsigned objnr; slabp = virt_to_head_page(objp)->slab_page; - objnr = (unsigned)(objp - slabp->s_mem) / cachep->buffer_size; + objnr = (unsigned)(objp - slabp->s_mem) / cachep->size; slab_bufctl(slabp)[objnr] = BUFCTL_ACTIVE; } #endif @@ -3747,7 +3746,7 @@ void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags) void *ret = __cache_alloc(cachep, flags, __builtin_return_address(0)); trace_kmem_cache_alloc(_RET_IP_, ret, - obj_size(cachep), cachep->buffer_size, flags); + obj_size(cachep), cachep->size, flags); return ret; } @@ -3775,7 +3774,7 @@ void *kmem_cache_alloc_node(struct kmem_cache *cachep, gfp_t flags, int nodeid) __builtin_return_address(0)); trace_kmem_cache_alloc_node(_RET_IP_, ret, - obj_size(cachep), cachep->buffer_size, + obj_size(cachep), cachep->size, flags, nodeid); return ret; @@ -3857,7 +3856,7 @@ static __always_inline void *__do_kmalloc(size_t size, gfp_t flags, ret = __cache_alloc(cachep, flags, caller); trace_kmalloc((unsigned long) caller, ret, - size, cachep->buffer_size, flags); + size, cachep->size, flags); return ret; } @@ -4011,7 +4010,7 @@ static int alloc_kmemlist(struct kmem_cache *cachep, gfp_t gfp) return 0; fail: - if (!cachep->next.next) { + if (!cachep->list.next) { /* Cache is not active yet. Roll back what we did */ node--; while (node >= 0) { @@ -4105,13 +4104,13 @@ static int enable_cpucache(struct kmem_cache *cachep, gfp_t gfp) * The numbers are guessed, we should auto-tune as described by * Bonwick. */ - if (cachep->buffer_size > 131072) + if (cachep->size > 131072) limit = 1; - else if (cachep->buffer_size > PAGE_SIZE) + else if (cachep->size > PAGE_SIZE) limit = 8; - else if (cachep->buffer_size > 1024) + else if (cachep->size > 1024) limit = 24; - else if (cachep->buffer_size > 256) + else if (cachep->size > 256) limit = 54; else limit = 120; @@ -4126,7 +4125,7 @@ static int enable_cpucache(struct kmem_cache *cachep, gfp_t gfp) * to a larger limit. Thus disabled by default. */ shared = 0; - if (cachep->buffer_size <= PAGE_SIZE && num_possible_cpus() > 1) + if (cachep->size <= PAGE_SIZE && num_possible_cpus() > 1) shared = 8; #if DEBUG @@ -4196,7 +4195,7 @@ static void cache_reap(struct work_struct *w) /* Give up. Setup the next iteration. */ goto out; - list_for_each_entry(searchp, &cache_chain, next) { + list_for_each_entry(searchp, &cache_chain, list) { check_irq_on(); /* @@ -4289,7 +4288,7 @@ static void s_stop(struct seq_file *m, void *p) static int s_show(struct seq_file *m, void *p) { - struct kmem_cache *cachep = list_entry(p, struct kmem_cache, next); + struct kmem_cache *cachep = list_entry(p, struct kmem_cache, list); struct slab *slabp; unsigned long active_objs; unsigned long num_objs; @@ -4345,7 +4344,7 @@ static int s_show(struct seq_file *m, void *p) printk(KERN_ERR "slab: cache %s error: %s\n", name, error); seq_printf(m, "%-17s %6lu %6lu %6u %4u %4d", - name, active_objs, num_objs, cachep->buffer_size, + name, active_objs, num_objs, cachep->size, cachep->num, (1 << cachep->gfporder)); seq_printf(m, " : tunables %4u %4u %4u", cachep->limit, cachep->batchcount, cachep->shared); @@ -4437,7 +4436,7 @@ static ssize_t slabinfo_write(struct file *file, const char __user *buffer, /* Find the cache in the chain of caches. */ mutex_lock(&cache_chain_mutex); res = -EINVAL; - list_for_each_entry(cachep, &cache_chain, next) { + list_for_each_entry(cachep, &cache_chain, list) { if (!strcmp(cachep->name, kbuf)) { if (limit < 1 || batchcount < 1 || batchcount > limit || shared < 0) { @@ -4513,7 +4512,7 @@ static void handle_slab(unsigned long *n, struct kmem_cache *c, struct slab *s) int i; if (n[0] == n[1]) return; - for (i = 0, p = s->s_mem; i < c->num; i++, p += c->buffer_size) { + for (i = 0, p = s->s_mem; i < c->num; i++, p += c->size) { if (slab_bufctl(s)[i] != BUFCTL_ACTIVE) continue; if (!add_caller(n, (unsigned long)*dbg_userword(c, p))) diff --git a/mm/slob.c b/mm/slob.c index c85265d22e08..95d1c7dd88e0 100644 --- a/mm/slob.c +++ b/mm/slob.c @@ -506,13 +506,6 @@ size_t ksize(const void *block) } EXPORT_SYMBOL(ksize); -struct kmem_cache { - unsigned int size, align; - unsigned long flags; - const char *name; - void (*ctor)(void *); -}; - struct kmem_cache *kmem_cache_create(const char *name, size_t size, size_t align, unsigned long flags, void (*ctor)(void *)) { @@ -523,7 +516,7 @@ struct kmem_cache *kmem_cache_create(const char *name, size_t size, if (c) { c->name = name; - c->size = size; + c->size = c->object_size; if (flags & SLAB_DESTROY_BY_RCU) { /* leave room for rcu footer at the end of object */ c->size += sizeof(struct slob_rcu); diff --git a/mm/slub.c b/mm/slub.c index 2de3c996f327..797271f5afb8 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -311,7 +311,7 @@ static inline size_t slab_ksize(const struct kmem_cache *s) * and whatever may come after it. */ if (s->flags & (SLAB_RED_ZONE | SLAB_POISON)) - return s->objsize; + return s->object_size; #endif /* @@ -609,11 +609,11 @@ static void print_trailer(struct kmem_cache *s, struct page *page, u8 *p) if (p > addr + 16) print_section("Bytes b4 ", p - 16, 16); - print_section("Object ", p, min_t(unsigned long, s->objsize, + print_section("Object ", p, min_t(unsigned long, s->object_size, PAGE_SIZE)); if (s->flags & SLAB_RED_ZONE) - print_section("Redzone ", p + s->objsize, - s->inuse - s->objsize); + print_section("Redzone ", p + s->object_size, + s->inuse - s->object_size); if (s->offset) off = s->offset + sizeof(void *); @@ -655,12 +655,12 @@ static void init_object(struct kmem_cache *s, void *object, u8 val) u8 *p = object; if (s->flags & __OBJECT_POISON) { - memset(p, POISON_FREE, s->objsize - 1); - p[s->objsize - 1] = POISON_END; + memset(p, POISON_FREE, s->object_size - 1); + p[s->object_size - 1] = POISON_END; } if (s->flags & SLAB_RED_ZONE) - memset(p + s->objsize, val, s->inuse - s->objsize); + memset(p + s->object_size, val, s->inuse - s->object_size); } static void restore_bytes(struct kmem_cache *s, char *message, u8 data, @@ -705,10 +705,10 @@ static int check_bytes_and_report(struct kmem_cache *s, struct page *page, * Poisoning uses 0x6b (POISON_FREE) and the last byte is * 0xa5 (POISON_END) * - * object + s->objsize + * object + s->object_size * Padding to reach word boundary. This is also used for Redzoning. * Padding is extended by another word if Redzoning is enabled and - * objsize == inuse. + * object_size == inuse. * * We fill with 0xbb (RED_INACTIVE) for inactive objects and with * 0xcc (RED_ACTIVE) for objects in use. @@ -727,7 +727,7 @@ static int check_bytes_and_report(struct kmem_cache *s, struct page *page, * object + s->size * Nothing is used beyond s->size. * - * If slabcaches are merged then the objsize and inuse boundaries are mostly + * If slabcaches are merged then the object_size and inuse boundaries are mostly * ignored. And therefore no slab options that rely on these boundaries * may be used with merged slabcaches. */ @@ -787,25 +787,25 @@ static int check_object(struct kmem_cache *s, struct page *page, void *object, u8 val) { u8 *p = object; - u8 *endobject = object + s->objsize; + u8 *endobject = object + s->object_size; if (s->flags & SLAB_RED_ZONE) { if (!check_bytes_and_report(s, page, object, "Redzone", - endobject, val, s->inuse - s->objsize)) + endobject, val, s->inuse - s->object_size)) return 0; } else { - if ((s->flags & SLAB_POISON) && s->objsize < s->inuse) { + if ((s->flags & SLAB_POISON) && s->object_size < s->inuse) { check_bytes_and_report(s, page, p, "Alignment padding", - endobject, POISON_INUSE, s->inuse - s->objsize); + endobject, POISON_INUSE, s->inuse - s->object_size); } } if (s->flags & SLAB_POISON) { if (val != SLUB_RED_ACTIVE && (s->flags & __OBJECT_POISON) && (!check_bytes_and_report(s, page, p, "Poison", p, - POISON_FREE, s->objsize - 1) || + POISON_FREE, s->object_size - 1) || !check_bytes_and_report(s, page, p, "Poison", - p + s->objsize - 1, POISON_END, 1))) + p + s->object_size - 1, POISON_END, 1))) return 0; /* * check_pad_bytes cleans up on its own. @@ -926,7 +926,7 @@ static void trace(struct kmem_cache *s, struct page *page, void *object, page->freelist); if (!alloc) - print_section("Object ", (void *)object, s->objsize); + print_section("Object ", (void *)object, s->object_size); dump_stack(); } @@ -942,14 +942,14 @@ static inline int slab_pre_alloc_hook(struct kmem_cache *s, gfp_t flags) lockdep_trace_alloc(flags); might_sleep_if(flags & __GFP_WAIT); - return should_failslab(s->objsize, flags, s->flags); + return should_failslab(s->object_size, flags, s->flags); } static inline void slab_post_alloc_hook(struct kmem_cache *s, gfp_t flags, void *object) { flags &= gfp_allowed_mask; kmemcheck_slab_alloc(s, flags, object, slab_ksize(s)); - kmemleak_alloc_recursive(object, s->objsize, 1, s->flags, flags); + kmemleak_alloc_recursive(object, s->object_size, 1, s->flags, flags); } static inline void slab_free_hook(struct kmem_cache *s, void *x) @@ -966,13 +966,13 @@ static inline void slab_free_hook(struct kmem_cache *s, void *x) unsigned long flags; local_irq_save(flags); - kmemcheck_slab_free(s, x, s->objsize); - debug_check_no_locks_freed(x, s->objsize); + kmemcheck_slab_free(s, x, s->object_size); + debug_check_no_locks_freed(x, s->object_size); local_irq_restore(flags); } #endif if (!(s->flags & SLAB_DEBUG_OBJECTS)) - debug_check_no_obj_freed(x, s->objsize); + debug_check_no_obj_freed(x, s->object_size); } /* @@ -1207,7 +1207,7 @@ out: __setup("slub_debug", setup_slub_debug); -static unsigned long kmem_cache_flags(unsigned long objsize, +static unsigned long kmem_cache_flags(unsigned long object_size, unsigned long flags, const char *name, void (*ctor)(void *)) { @@ -1237,7 +1237,7 @@ static inline int check_object(struct kmem_cache *s, struct page *page, static inline void add_full(struct kmem_cache *s, struct kmem_cache_node *n, struct page *page) {} static inline void remove_full(struct kmem_cache *s, struct page *page) {} -static inline unsigned long kmem_cache_flags(unsigned long objsize, +static inline unsigned long kmem_cache_flags(unsigned long object_size, unsigned long flags, const char *name, void (*ctor)(void *)) { @@ -2098,10 +2098,10 @@ slab_out_of_memory(struct kmem_cache *s, gfp_t gfpflags, int nid) "SLUB: Unable to allocate memory on node %d (gfp=0x%x)\n", nid, gfpflags); printk(KERN_WARNING " cache: %s, object size: %d, buffer size: %d, " - "default order: %d, min order: %d\n", s->name, s->objsize, + "default order: %d, min order: %d\n", s->name, s->object_size, s->size, oo_order(s->oo), oo_order(s->min)); - if (oo_order(s->min) > get_order(s->objsize)) + if (oo_order(s->min) > get_order(s->object_size)) printk(KERN_WARNING " %s debugging increased min order, use " "slub_debug=O to disable.\n", s->name); @@ -2374,7 +2374,7 @@ redo: } if (unlikely(gfpflags & __GFP_ZERO) && object) - memset(object, 0, s->objsize); + memset(object, 0, s->object_size); slab_post_alloc_hook(s, gfpflags, object); @@ -2385,7 +2385,7 @@ void *kmem_cache_alloc(struct kmem_cache *s, gfp_t gfpflags) { void *ret = slab_alloc(s, gfpflags, NUMA_NO_NODE, _RET_IP_); - trace_kmem_cache_alloc(_RET_IP_, ret, s->objsize, s->size, gfpflags); + trace_kmem_cache_alloc(_RET_IP_, ret, s->object_size, s->size, gfpflags); return ret; } @@ -2415,7 +2415,7 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node) void *ret = slab_alloc(s, gfpflags, node, _RET_IP_); trace_kmem_cache_alloc_node(_RET_IP_, ret, - s->objsize, s->size, gfpflags, node); + s->object_size, s->size, gfpflags, node); return ret; } @@ -2910,7 +2910,7 @@ static void set_min_partial(struct kmem_cache *s, unsigned long min) static int calculate_sizes(struct kmem_cache *s, int forced_order) { unsigned long flags = s->flags; - unsigned long size = s->objsize; + unsigned long size = s->object_size; unsigned long align = s->align; int order; @@ -2939,7 +2939,7 @@ static int calculate_sizes(struct kmem_cache *s, int forced_order) * end of the object and the free pointer. If not then add an * additional word to have some bytes to store Redzone information. */ - if ((flags & SLAB_RED_ZONE) && size == s->objsize) + if ((flags & SLAB_RED_ZONE) && size == s->object_size) size += sizeof(void *); #endif @@ -2987,7 +2987,7 @@ static int calculate_sizes(struct kmem_cache *s, int forced_order) * user specified and the dynamic determination of cache line size * on bootup. */ - align = calculate_alignment(flags, align, s->objsize); + align = calculate_alignment(flags, align, s->object_size); s->align = align; /* @@ -3035,7 +3035,7 @@ static int kmem_cache_open(struct kmem_cache *s, memset(s, 0, kmem_size); s->name = name; s->ctor = ctor; - s->objsize = size; + s->object_size = size; s->align = align; s->flags = kmem_cache_flags(size, flags, name, ctor); s->reserved = 0; @@ -3050,7 +3050,7 @@ static int kmem_cache_open(struct kmem_cache *s, * Disable debugging flags that store metadata if the min slab * order increased. */ - if (get_order(s->size) > get_order(s->objsize)) { + if (get_order(s->size) > get_order(s->object_size)) { s->flags &= ~DEBUG_METADATA_FLAGS; s->offset = 0; if (!calculate_sizes(s, -1)) @@ -3124,7 +3124,7 @@ error: */ unsigned int kmem_cache_size(struct kmem_cache *s) { - return s->objsize; + return s->object_size; } EXPORT_SYMBOL(kmem_cache_size); @@ -3853,11 +3853,11 @@ void __init kmem_cache_init(void) if (s && s->size) { char *name = kasprintf(GFP_NOWAIT, - "dma-kmalloc-%d", s->objsize); + "dma-kmalloc-%d", s->object_size); BUG_ON(!name); kmalloc_dma_caches[i] = create_kmalloc_cache(name, - s->objsize, SLAB_CACHE_DMA); + s->object_size, SLAB_CACHE_DMA); } } #endif @@ -3951,7 +3951,7 @@ struct kmem_cache *kmem_cache_create(const char *name, size_t size, * Adjust the object sizes so that we clear * the complete object on kzalloc. */ - s->objsize = max(s->objsize, (int)size); + s->object_size = max(s->object_size, (int)size); s->inuse = max_t(int, s->inuse, ALIGN(size, sizeof(void *))); if (sysfs_slab_alias(s, name)) { @@ -4634,7 +4634,7 @@ SLAB_ATTR_RO(align); static ssize_t object_size_show(struct kmem_cache *s, char *buf) { - return sprintf(buf, "%d\n", s->objsize); + return sprintf(buf, "%d\n", s->object_size); } SLAB_ATTR_RO(object_size); @@ -5438,7 +5438,7 @@ __initcall(slab_sysfs_init); static void print_slabinfo_header(struct seq_file *m) { seq_puts(m, "slabinfo - version: 2.1\n"); - seq_puts(m, "# name " + seq_puts(m, "# name " " "); seq_puts(m, " : tunables "); seq_puts(m, " : slabdata "); -- cgit v1.2.3 From 846248136dd3a6723135b8515ed7dc4c52a7b2ae Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Mon, 4 Jun 2012 14:42:54 -0700 Subject: drm/i915/context: create & destroy ioctls Add the interfaces to allow user space to create and destroy contexts. Contexts are destroyed automatically if the file descriptor for the dri device is closed. Following convention as usual here causes checkpatch warnings. v2: with is_initialized, no longer need to init at create drop the context switch on create (daniel) v3: Use interruptible lock (Chris) return -ENODEV in !GEM case (Chris) Signed-off-by: Ben Widawsky --- drivers/gpu/drm/i915/i915_dma.c | 2 ++ drivers/gpu/drm/i915/i915_drv.h | 4 +++ drivers/gpu/drm/i915/i915_gem_context.c | 55 +++++++++++++++++++++++++++++++++ include/drm/i915_drm.h | 15 +++++++++ 4 files changed, 76 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 3f3aca8c3767..ba75af12f1fd 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1848,6 +1848,8 @@ struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF_DRV(I915_SET_SPRITE_COLORKEY, intel_sprite_set_colorkey, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(I915_GET_SPRITE_COLORKEY, intel_sprite_get_colorkey, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(I915_GEM_WAIT, i915_gem_wait_ioctl, DRM_AUTH|DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_CREATE, i915_gem_context_create_ioctl, DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_DESTROY, i915_gem_context_destroy_ioctl, DRM_UNLOCKED), }; int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 6edc02b6dda4..03e7f9e683e3 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1386,6 +1386,10 @@ void i915_gem_context_open(struct drm_device *dev, struct drm_file *file); void i915_gem_context_close(struct drm_device *dev, struct drm_file *file); int i915_switch_context(struct intel_ring_buffer *ring, struct drm_file *file, int to_id); +int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); +int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); /* i915_gem_gtt.c */ int __must_check i915_gem_init_aliasing_ppgtt(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 6a7b67d9f43f..5642c4019b53 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -489,3 +489,58 @@ int i915_switch_context(struct intel_ring_buffer *ring, drm_gem_object_unreference(&from_obj->base); return ret; } + +int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_gem_context_create *args = data; + struct drm_i915_file_private *file_priv = file->driver_priv; + struct i915_hw_context *ctx; + int ret; + + if (!(dev->driver->driver_features & DRIVER_GEM)) + return -ENODEV; + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return ret; + + ret = create_hw_context(dev, file_priv, &ctx); + mutex_unlock(&dev->struct_mutex); + + args->ctx_id = ctx->id; + DRM_DEBUG_DRIVER("HW context %d created\n", args->ctx_id); + + return ret; +} + +int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_i915_gem_context_destroy *args = data; + struct drm_i915_file_private *file_priv = file->driver_priv; + struct drm_i915_private *dev_priv = dev->dev_private; + struct i915_hw_context *ctx; + int ret; + + if (!(dev->driver->driver_features & DRIVER_GEM)) + return -ENODEV; + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return ret; + + ctx = i915_gem_context_get(file_priv, args->ctx_id); + if (!ctx) { + mutex_unlock(&dev->struct_mutex); + return -EINVAL; + } + + do_destroy(ctx); + + mutex_unlock(&dev->struct_mutex); + + DRM_DEBUG_DRIVER("HW context %d destroyed\n", args->ctx_id); + return 0; +} diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index 5c28ee1d1911..5da73244486a 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/i915_drm.h @@ -201,6 +201,8 @@ typedef struct _drm_i915_sarea { #define DRM_I915_GET_SPRITE_COLORKEY 0x2a #define DRM_I915_SET_SPRITE_COLORKEY 0x2b #define DRM_I915_GEM_WAIT 0x2c +#define DRM_I915_GEM_CONTEXT_CREATE 0x2d +#define DRM_I915_GEM_CONTEXT_DESTROY 0x2e #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) #define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) @@ -245,6 +247,8 @@ typedef struct _drm_i915_sarea { #define DRM_IOCTL_I915_SET_SPRITE_COLORKEY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_SET_SPRITE_COLORKEY, struct drm_intel_sprite_colorkey) #define DRM_IOCTL_I915_GET_SPRITE_COLORKEY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_SET_SPRITE_COLORKEY, struct drm_intel_sprite_colorkey) #define DRM_IOCTL_I915_GEM_WAIT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_WAIT, struct drm_i915_gem_wait) +#define DRM_IOCTL_I915_GEM_CONTEXT_CREATE DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_CREATE, struct drm_i915_gem_context_create) +#define DRM_IOCTL_I915_GEM_CONTEXT_DESTROY DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_DESTROY, struct drm_i915_gem_context_destroy) /* Allow drivers to submit batchbuffers directly to hardware, relying * on the security mechanisms provided by hardware. @@ -897,4 +901,15 @@ struct drm_i915_gem_wait { __s64 timeout_ns; }; +struct drm_i915_gem_context_create { + /* output: id of new context*/ + __u32 ctx_id; + __u32 pad; +}; + +struct drm_i915_gem_context_destroy { + __u32 ctx_id; + __u32 pad; +}; + #endif /* _I915_DRM_H_ */ -- cgit v1.2.3 From 6e0a69dbc81b88f5a42e08344203021571f6fb2f Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Mon, 4 Jun 2012 14:42:55 -0700 Subject: drm/i915/context: switch contexts with execbuf2 Use the rsvd1 field in execbuf2 to specify the context ID associated with the workload. This will allow the driver to do the proper context switch when/if needed. v2: Add checks for context switches on rings not supporting contexts. Before the code would silently ignore such requests. Signed-off-by: Ben Widawsky --- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 16 ++++++++++++++++ include/drm/i915_drm.h | 8 +++++++- 2 files changed, 23 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 974a9f1068a3..f32d02464bce 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -1044,6 +1044,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, struct drm_i915_gem_object *batch_obj; struct drm_clip_rect *cliprects = NULL; struct intel_ring_buffer *ring; + u32 ctx_id = i915_execbuffer2_get_context_id(*args); u32 exec_start, exec_len; u32 seqno; u32 mask; @@ -1065,9 +1066,19 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, break; case I915_EXEC_BSD: ring = &dev_priv->ring[VCS]; + if (ctx_id != 0) { + DRM_DEBUG("Ring %s doesn't support contexts\n", + ring->name); + return -EPERM; + } break; case I915_EXEC_BLT: ring = &dev_priv->ring[BCS]; + if (ctx_id != 0) { + DRM_DEBUG("Ring %s doesn't support contexts\n", + ring->name); + return -EPERM; + } break; default: DRM_DEBUG("execbuf with unknown ring: %d\n", @@ -1261,6 +1272,10 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, goto err; } + ret = i915_switch_context(ring, file, ctx_id); + if (ret) + goto err; + trace_i915_gem_ring_dispatch(ring, seqno); exec_start = batch_obj->gtt_offset + args->batch_start_offset; @@ -1367,6 +1382,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, exec2.num_cliprects = args->num_cliprects; exec2.cliprects_ptr = args->cliprects_ptr; exec2.flags = I915_EXEC_RENDER; + i915_execbuffer2_set_context_id(exec2, 0); ret = i915_gem_do_execbuffer(dev, data, file, &exec2, exec2_list); if (!ret) { diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index 5da73244486a..8cc70837f929 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/i915_drm.h @@ -663,13 +663,19 @@ struct drm_i915_gem_execbuffer2 { #define I915_EXEC_CONSTANTS_ABSOLUTE (1<<6) #define I915_EXEC_CONSTANTS_REL_SURFACE (2<<6) /* gen4/5 only */ __u64 flags; - __u64 rsvd1; + __u64 rsvd1; /* now used for context info */ __u64 rsvd2; }; /** Resets the SO write offset registers for transform feedback on gen7. */ #define I915_EXEC_GEN7_SOL_RESET (1<<8) +#define I915_EXEC_CONTEXT_ID_MASK (0xffffffff) +#define i915_execbuffer2_set_context_id(eb2, context) \ + (eb2).rsvd1 = context & I915_EXEC_CONTEXT_ID_MASK +#define i915_execbuffer2_get_context_id(eb2) \ + ((eb2).rsvd1 & I915_EXEC_CONTEXT_ID_MASK) + struct drm_i915_gem_pin { /** Handle of the buffer to be pinned. */ __u32 handle; -- cgit v1.2.3 From 0c2498f1660878339350bea8d18550b1b87ca055 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 28 Jan 2011 09:40:40 +0100 Subject: pwm: Add PWM framework support This patch adds framework support for PWM (pulse width modulation) devices. The is a barebone PWM API already in the kernel under include/linux/pwm.h, but it does not allow for multiple drivers as each of them implements the pwm_*() functions. There are other PWM framework patches around from Bill Gatliff. Unlike his framework this one does not change the existing API for PWMs so that this framework can act as a drop in replacement for the existing API. Why another framework? Several people argue that there should not be another framework for PWMs but they should be integrated into one of the existing frameworks like led or hwmon. Unlike these frameworks the PWM framework is agnostic to the purpose of the PWM. In fact, a PWM can drive a LED, but this makes the LED framework a user of a PWM, like already done in leds-pwm.c. The gpio framework also is not suitable for PWMs. Every gpio could be turned into a PWM using timer based toggling, but on the other hand not every PWM hardware device can be turned into a gpio due to the lack of hardware capabilities. This patch does not try to improve the PWM API yet, this could be done in subsequent patches. Signed-off-by: Sascha Hauer Acked-by: Kurt Van Dijck Reviewed-by: Arnd Bergmann Reviewed-by: Matthias Kaehlcke Reviewed-by: Mark Brown Reviewed-by: Shawn Guo [thierry.reding@avionic-design.de: fixup typos, kerneldoc comments] Signed-off-by: Thierry Reding --- Documentation/pwm.txt | 54 ++++++++++++ MAINTAINERS | 6 ++ drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/pwm/Kconfig | 12 +++ drivers/pwm/Makefile | 1 + drivers/pwm/core.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pwm.h | 38 +++++++++ 8 files changed, 341 insertions(+) create mode 100644 Documentation/pwm.txt create mode 100644 drivers/pwm/Kconfig create mode 100644 drivers/pwm/Makefile create mode 100644 drivers/pwm/core.c (limited to 'include') diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt new file mode 100644 index 000000000000..03e39d145911 --- /dev/null +++ b/Documentation/pwm.txt @@ -0,0 +1,54 @@ +Pulse Width Modulation (PWM) interface + +This provides an overview about the Linux PWM interface + +PWMs are commonly used for controlling LEDs, fans or vibrators in +cell phones. PWMs with a fixed purpose have no need implementing +the Linux PWM API (although they could). However, PWMs are often +found as discrete devices on SoCs which have no fixed purpose. It's +up to the board designer to connect them to LEDs or fans. To provide +this kind of flexibility the generic PWM API exists. + +Identifying PWMs +---------------- + +Users of the legacy PWM API use unique IDs to refer to PWM devices. One +goal of the new PWM framework is to get rid of this global namespace. + +Using PWMs +---------- + +A PWM can be requested using pwm_request() and freed after usage with +pwm_free(). After being requested a PWM has to be configured using + +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns); + +To start/stop toggling the PWM output use pwm_enable()/pwm_disable(). + +Implementing a PWM driver +------------------------- + +Currently there are two ways to implement pwm drivers. Traditionally +there only has been the barebone API meaning that each driver has +to implement the pwm_*() functions itself. This means that it's impossible +to have multiple PWM drivers in the system. For this reason it's mandatory +for new drivers to use the generic PWM framework. +A new PWM device can be added using pwmchip_add() and removed again with +pwmchip_remove(). pwmchip_add() takes a filled in struct pwm_chip as +argument which provides the ops and the pwm id to the framework. + +Locking +------- + +The PWM core list manipulations are protected by a mutex, so pwm_request() +and pwm_free() may not be called from an atomic context. Currently the +PWM core does not enforce any locking to pwm_enable(), pwm_disable() and +pwm_config(), so the calling context is currently driver specific. This +is an issue derived from the former barebone API and should be fixed soon. + +Helpers +------- + +Currently a PWM can only be configured with period_ns and duty_ns. For several +use cases freq_hz and duty_percent might be better. Instead of calculating +this in your driver please consider adding appropriate helpers to the framework. diff --git a/MAINTAINERS b/MAINTAINERS index 14bc7071f9df..67d6cb70cb7e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5487,6 +5487,12 @@ S: Maintained F: Documentation/video4linux/README.pvrusb2 F: drivers/media/video/pvrusb2/ +PWM core +M: Sascha Hauer +L: linux-kernel@vger.kernel.org +S: Maintained +F: drivers/pwm/ + PXA2xx/PXA3xx SUPPORT M: Eric Miao M: Russell King diff --git a/drivers/Kconfig b/drivers/Kconfig index bfc918633fd9..805c432c9439 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -148,4 +148,6 @@ source "drivers/iio/Kconfig" source "drivers/vme/Kconfig" +source "drivers/pwm/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 2ba29ffef2cb..bd36f09f2246 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -8,6 +8,7 @@ # GPIO must come after pinctrl as gpios may need to mux pins etc obj-y += pinctrl/ obj-y += gpio/ +obj-y += pwm/ obj-$(CONFIG_PCI) += pci/ obj-$(CONFIG_PARISC) += parisc/ obj-$(CONFIG_RAPIDIO) += rapidio/ diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig new file mode 100644 index 000000000000..93c1052291a3 --- /dev/null +++ b/drivers/pwm/Kconfig @@ -0,0 +1,12 @@ +menuconfig PWM + bool "PWM Support" + help + This enables PWM support through the generic PWM framework. + You only need to enable this, if you also want to enable + one or more of the PWM drivers below. + + If unsure, say N. + +if PWM + +endif diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile new file mode 100644 index 000000000000..3469c3d28b7a --- /dev/null +++ b/drivers/pwm/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_PWM) += core.o diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c new file mode 100644 index 000000000000..0b8a38eca23c --- /dev/null +++ b/drivers/pwm/core.c @@ -0,0 +1,227 @@ +/* + * Generic pwmlib implementation + * + * Copyright (C) 2011 Sascha Hauer + * + * 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, 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct pwm_device { + struct pwm_chip *chip; + const char *label; + unsigned long flags; +#define FLAG_REQUESTED 0 +#define FLAG_ENABLED 1 + struct list_head node; +}; + +static LIST_HEAD(pwm_list); + +static DEFINE_MUTEX(pwm_lock); + +static struct pwm_device *_find_pwm(int pwm_id) +{ + struct pwm_device *pwm; + + list_for_each_entry(pwm, &pwm_list, node) { + if (pwm->chip->pwm_id == pwm_id) + return pwm; + } + + return NULL; +} + +/** + * pwmchip_add() - register a new PWM chip + * @chip: the PWM chip to add + */ +int pwmchip_add(struct pwm_chip *chip) +{ + struct pwm_device *pwm; + int ret = 0; + + pwm = kzalloc(sizeof(*pwm), GFP_KERNEL); + if (!pwm) + return -ENOMEM; + + pwm->chip = chip; + + mutex_lock(&pwm_lock); + + if (chip->pwm_id >= 0 && _find_pwm(chip->pwm_id)) { + ret = -EBUSY; + goto out; + } + + list_add_tail(&pwm->node, &pwm_list); +out: + mutex_unlock(&pwm_lock); + + if (ret) + kfree(pwm); + + return ret; +} +EXPORT_SYMBOL_GPL(pwmchip_add); + +/** + * pwmchip_remove() - remove a PWM chip + * @chip: the PWM chip to remove + * + * Removes a PWM chip. This function may return busy if the PWM chip provides + * a PWM device that is still requested. + */ +int pwmchip_remove(struct pwm_chip *chip) +{ + struct pwm_device *pwm; + int ret = 0; + + mutex_lock(&pwm_lock); + + pwm = _find_pwm(chip->pwm_id); + if (!pwm) { + ret = -ENOENT; + goto out; + } + + if (test_bit(FLAG_REQUESTED, &pwm->flags)) { + ret = -EBUSY; + goto out; + } + + list_del(&pwm->node); + + kfree(pwm); +out: + mutex_unlock(&pwm_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(pwmchip_remove); + +/** + * pwm_request() - request a PWM device + * @pwm_id: global PWM device index + * @label: PWM device label + */ +struct pwm_device *pwm_request(int pwm_id, const char *label) +{ + struct pwm_device *pwm; + int ret; + + mutex_lock(&pwm_lock); + + pwm = _find_pwm(pwm_id); + if (!pwm) { + pwm = ERR_PTR(-ENOENT); + goto out; + } + + if (test_bit(FLAG_REQUESTED, &pwm->flags)) { + pwm = ERR_PTR(-EBUSY); + goto out; + } + + if (!try_module_get(pwm->chip->ops->owner)) { + pwm = ERR_PTR(-ENODEV); + goto out; + } + + if (pwm->chip->ops->request) { + ret = pwm->chip->ops->request(pwm->chip); + if (ret) { + pwm = ERR_PTR(ret); + goto out_put; + } + } + + pwm->label = label; + set_bit(FLAG_REQUESTED, &pwm->flags); + + goto out; + +out_put: + module_put(pwm->chip->ops->owner); +out: + mutex_unlock(&pwm_lock); + + return pwm; +} +EXPORT_SYMBOL_GPL(pwm_request); + +/** + * pwm_free() - free a PWM device + * @pwm: PWM device + */ +void pwm_free(struct pwm_device *pwm) +{ + mutex_lock(&pwm_lock); + + if (!test_and_clear_bit(FLAG_REQUESTED, &pwm->flags)) { + pr_warning("PWM device already freed\n"); + goto out; + } + + pwm->label = NULL; + + module_put(pwm->chip->ops->owner); +out: + mutex_unlock(&pwm_lock); +} +EXPORT_SYMBOL_GPL(pwm_free); + +/** + * pwm_config() - change a PWM device configuration + * @pwm: PWM device + * @duty_ns: "on" time (in nanoseconds) + * @period_ns: duration (in nanoseconds) of one cycle + */ +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) +{ + return pwm->chip->ops->config(pwm->chip, duty_ns, period_ns); +} +EXPORT_SYMBOL_GPL(pwm_config); + +/** + * pwm_enable() - start a PWM output toggling + * @pwm: PWM device + */ +int pwm_enable(struct pwm_device *pwm) +{ + if (!test_and_set_bit(FLAG_ENABLED, &pwm->flags)) + return pwm->chip->ops->enable(pwm->chip); + + return 0; +} +EXPORT_SYMBOL_GPL(pwm_enable); + +/** + * pwm_disable() - stop a PWM output toggling + * @pwm: PWM device + */ +void pwm_disable(struct pwm_device *pwm) +{ + if (test_and_clear_bit(FLAG_ENABLED, &pwm->flags)) + pwm->chip->ops->disable(pwm->chip); +} +EXPORT_SYMBOL_GPL(pwm_disable); diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 7c775751392c..1f308a13105f 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -28,4 +28,42 @@ int pwm_enable(struct pwm_device *pwm); */ void pwm_disable(struct pwm_device *pwm); +#ifdef CONFIG_PWM +struct pwm_chip; + +/** + * struct pwm_ops - PWM controller operations + * @request: optional hook for requesting a PWM + * @free: optional hook for freeing a PWM + * @config: configure duty cycles and period length for this PWM + * @enable: enable PWM output toggling + * @disable: disable PWM output toggling + * @owner: helps prevent removal of modules exporting active PWMs + */ +struct pwm_ops { + int (*request)(struct pwm_chip *chip); + void (*free)(struct pwm_chip *chip); + int (*config)(struct pwm_chip *chip, int duty_ns, + int period_ns); + int (*enable)(struct pwm_chip *chip); + void (*disable)(struct pwm_chip *chip); + struct module *owner; +}; + +/** + * struct pwm_chip - abstract a PWM + * @pwm_id: global PWM device index + * @label: PWM device label + * @ops: controller operations + */ +struct pwm_chip { + int pwm_id; + const char *label; + struct pwm_ops *ops; +}; + +int pwmchip_add(struct pwm_chip *chip); +int pwmchip_remove(struct pwm_chip *chip); +#endif + #endif /* __LINUX_PWM_H */ -- cgit v1.2.3 From f051c466cf690ac661d713d3ceb56b4efcecc853 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 14 Dec 2011 11:12:23 +0100 Subject: pwm: Allow chips to support multiple PWMs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Many PWM controllers provide access to more than a single PWM output and may even share some resource among them. Allowing a PWM chip to provide multiple PWM devices enables better sharing of those resources. As a side-effect this change allows easy integration with the device tree where a given PWM can be looked up based on the PWM chip's phandle and a corresponding index. This commit modifies the PWM core to support multiple PWMs per struct pwm_chip. It achieves this in a similar way to how gpiolib works, by allowing PWM ranges to be requested dynamically (pwm_chip.base == -1) or starting at a given offset (pwm_chip.base >= 0). A chip specifies how many PWMs it controls using the npwm member. Each of the functions in the pwm_ops structure gets an additional argument that specified the PWM number (it can be converted to a per-chip index by subtracting the chip's base). The total maximum number of PWM devices is currently fixed to 1024 while the data is actually stored in a radix tree, thus saving resources if not all of them are used. Reviewed-by: Mark Brown Reviewed-by: Shawn Guo [eric@eukrea.com: fix error handling in pwmchip_add] Signed-off-by: Eric Bénard Signed-off-by: Thierry Reding --- Documentation/pwm.txt | 9 +- drivers/pwm/core.c | 267 +++++++++++++++++++++++++++++++++++--------------- include/linux/pwm.h | 71 +++++++++++--- 3 files changed, 254 insertions(+), 93 deletions(-) (limited to 'include') diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt index 03e39d145911..48f598acdd16 100644 --- a/Documentation/pwm.txt +++ b/Documentation/pwm.txt @@ -33,9 +33,12 @@ there only has been the barebone API meaning that each driver has to implement the pwm_*() functions itself. This means that it's impossible to have multiple PWM drivers in the system. For this reason it's mandatory for new drivers to use the generic PWM framework. -A new PWM device can be added using pwmchip_add() and removed again with -pwmchip_remove(). pwmchip_add() takes a filled in struct pwm_chip as -argument which provides the ops and the pwm id to the framework. + +A new PWM controller/chip can be added using pwmchip_add() and removed +again with pwmchip_remove(). pwmchip_add() takes a filled in struct +pwm_chip as argument which provides a description of the PWM chip, the +number of PWM devices provider by the chip and the chip-specific +implementation of the supported PWM operations to the framework. Locking ------- diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 0b8a38eca23c..a447be128328 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -2,6 +2,7 @@ * Generic pwmlib implementation * * Copyright (C) 2011 Sascha Hauer + * Copyright (C) 2011-2012 Avionic Design GmbH * * 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 @@ -20,66 +21,161 @@ #include #include +#include #include #include #include #include #include -struct pwm_device { - struct pwm_chip *chip; - const char *label; - unsigned long flags; -#define FLAG_REQUESTED 0 -#define FLAG_ENABLED 1 - struct list_head node; -}; - -static LIST_HEAD(pwm_list); +#define MAX_PWMS 1024 static DEFINE_MUTEX(pwm_lock); +static LIST_HEAD(pwm_chips); +static DECLARE_BITMAP(allocated_pwms, MAX_PWMS); +static RADIX_TREE(pwm_tree, GFP_KERNEL); -static struct pwm_device *_find_pwm(int pwm_id) +static struct pwm_device *pwm_to_device(unsigned int pwm) { - struct pwm_device *pwm; + return radix_tree_lookup(&pwm_tree, pwm); +} + +static int alloc_pwms(int pwm, unsigned int count) +{ + unsigned int from = 0; + unsigned int start; + + if (pwm >= MAX_PWMS) + return -EINVAL; + + if (pwm >= 0) + from = pwm; - list_for_each_entry(pwm, &pwm_list, node) { - if (pwm->chip->pwm_id == pwm_id) - return pwm; + start = bitmap_find_next_zero_area(allocated_pwms, MAX_PWMS, from, + count, 0); + + if (pwm >= 0 && start != pwm) + return -EEXIST; + + if (start + count > MAX_PWMS) + return -ENOSPC; + + return start; +} + +static void free_pwms(struct pwm_chip *chip) +{ + unsigned int i; + + for (i = 0; i < chip->npwm; i++) { + struct pwm_device *pwm = &chip->pwms[i]; + radix_tree_delete(&pwm_tree, pwm->pwm); } - return NULL; + bitmap_clear(allocated_pwms, chip->base, chip->npwm); + + kfree(chip->pwms); + chip->pwms = NULL; +} + +static int pwm_device_request(struct pwm_device *pwm, const char *label) +{ + int err; + + if (test_bit(PWMF_REQUESTED, &pwm->flags)) + return -EBUSY; + + if (!try_module_get(pwm->chip->ops->owner)) + return -ENODEV; + + if (pwm->chip->ops->request) { + err = pwm->chip->ops->request(pwm->chip, pwm); + if (err) { + module_put(pwm->chip->ops->owner); + return err; + } + } + + set_bit(PWMF_REQUESTED, &pwm->flags); + pwm->label = label; + + return 0; +} + +/** + * pwm_set_chip_data() - set private chip data for a PWM + * @pwm: PWM device + * @data: pointer to chip-specific data + */ +int pwm_set_chip_data(struct pwm_device *pwm, void *data) +{ + if (!pwm) + return -EINVAL; + + pwm->chip_data = data; + + return 0; +} + +/** + * pwm_get_chip_data() - get private chip data for a PWM + * @pwm: PWM device + */ +void *pwm_get_chip_data(struct pwm_device *pwm) +{ + return pwm ? pwm->chip_data : NULL; } /** * pwmchip_add() - register a new PWM chip * @chip: the PWM chip to add + * + * Register a new PWM chip. If chip->base < 0 then a dynamically assigned base + * will be used. */ int pwmchip_add(struct pwm_chip *chip) { struct pwm_device *pwm; - int ret = 0; - - pwm = kzalloc(sizeof(*pwm), GFP_KERNEL); - if (!pwm) - return -ENOMEM; + unsigned int i; + int ret; - pwm->chip = chip; + if (!chip || !chip->dev || !chip->ops || !chip->ops->config || + !chip->ops->enable || !chip->ops->disable) + return -EINVAL; mutex_lock(&pwm_lock); - if (chip->pwm_id >= 0 && _find_pwm(chip->pwm_id)) { - ret = -EBUSY; + ret = alloc_pwms(chip->base, chip->npwm); + if (ret < 0) + goto out; + + chip->pwms = kzalloc(chip->npwm * sizeof(*pwm), GFP_KERNEL); + if (!chip->pwms) { + ret = -ENOMEM; goto out; } - list_add_tail(&pwm->node, &pwm_list); -out: - mutex_unlock(&pwm_lock); + chip->base = ret; + + for (i = 0; i < chip->npwm; i++) { + pwm = &chip->pwms[i]; + + pwm->chip = chip; + pwm->pwm = chip->base + i; + pwm->hwpwm = i; - if (ret) - kfree(pwm); + radix_tree_insert(&pwm_tree, pwm->pwm, pwm); + } + + bitmap_set(allocated_pwms, chip->base, chip->npwm); + + INIT_LIST_HEAD(&chip->list); + list_add(&chip->list, &pwm_chips); + ret = 0; + +out: + mutex_unlock(&pwm_lock); return ret; } EXPORT_SYMBOL_GPL(pwmchip_add); @@ -93,28 +189,25 @@ EXPORT_SYMBOL_GPL(pwmchip_add); */ int pwmchip_remove(struct pwm_chip *chip) { - struct pwm_device *pwm; + unsigned int i; int ret = 0; mutex_lock(&pwm_lock); - pwm = _find_pwm(chip->pwm_id); - if (!pwm) { - ret = -ENOENT; - goto out; - } + for (i = 0; i < chip->npwm; i++) { + struct pwm_device *pwm = &chip->pwms[i]; - if (test_bit(FLAG_REQUESTED, &pwm->flags)) { - ret = -EBUSY; - goto out; + if (test_bit(PWMF_REQUESTED, &pwm->flags)) { + ret = -EBUSY; + goto out; + } } - list_del(&pwm->node); + list_del_init(&chip->list); + free_pwms(chip); - kfree(pwm); out: mutex_unlock(&pwm_lock); - return ret; } EXPORT_SYMBOL_GPL(pwmchip_remove); @@ -124,50 +217,64 @@ EXPORT_SYMBOL_GPL(pwmchip_remove); * @pwm_id: global PWM device index * @label: PWM device label */ -struct pwm_device *pwm_request(int pwm_id, const char *label) +struct pwm_device *pwm_request(int pwm, const char *label) { - struct pwm_device *pwm; - int ret; + struct pwm_device *dev; + int err; + + if (pwm < 0 || pwm >= MAX_PWMS) + return ERR_PTR(-EINVAL); mutex_lock(&pwm_lock); - pwm = _find_pwm(pwm_id); - if (!pwm) { - pwm = ERR_PTR(-ENOENT); + dev = pwm_to_device(pwm); + if (!dev) { + dev = ERR_PTR(-EPROBE_DEFER); goto out; } - if (test_bit(FLAG_REQUESTED, &pwm->flags)) { - pwm = ERR_PTR(-EBUSY); - goto out; - } + err = pwm_device_request(dev, label); + if (err < 0) + dev = ERR_PTR(err); - if (!try_module_get(pwm->chip->ops->owner)) { - pwm = ERR_PTR(-ENODEV); - goto out; - } +out: + mutex_unlock(&pwm_lock); - if (pwm->chip->ops->request) { - ret = pwm->chip->ops->request(pwm->chip); - if (ret) { - pwm = ERR_PTR(ret); - goto out_put; - } - } + return dev; +} +EXPORT_SYMBOL_GPL(pwm_request); - pwm->label = label; - set_bit(FLAG_REQUESTED, &pwm->flags); +/** + * pwm_request_from_chip() - request a PWM device relative to a PWM chip + * @chip: PWM chip + * @index: per-chip index of the PWM to request + * @label: a literal description string of this PWM + * + * Returns the PWM at the given index of the given PWM chip. A negative error + * code is returned if the index is not valid for the specified PWM chip or + * if the PWM device cannot be requested. + */ +struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, + unsigned int index, + const char *label) +{ + struct pwm_device *pwm; + int err; - goto out; + if (!chip || index >= chip->npwm) + return ERR_PTR(-EINVAL); -out_put: - module_put(pwm->chip->ops->owner); -out: - mutex_unlock(&pwm_lock); + mutex_lock(&pwm_lock); + pwm = &chip->pwms[index]; + err = pwm_device_request(pwm, label); + if (err < 0) + pwm = ERR_PTR(err); + + mutex_unlock(&pwm_lock); return pwm; } -EXPORT_SYMBOL_GPL(pwm_request); +EXPORT_SYMBOL_GPL(pwm_request_from_chip); /** * pwm_free() - free a PWM device @@ -177,11 +284,14 @@ void pwm_free(struct pwm_device *pwm) { mutex_lock(&pwm_lock); - if (!test_and_clear_bit(FLAG_REQUESTED, &pwm->flags)) { + if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) { pr_warning("PWM device already freed\n"); goto out; } + if (pwm->chip->ops->free) + pwm->chip->ops->free(pwm->chip, pwm); + pwm->label = NULL; module_put(pwm->chip->ops->owner); @@ -198,7 +308,10 @@ EXPORT_SYMBOL_GPL(pwm_free); */ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) { - return pwm->chip->ops->config(pwm->chip, duty_ns, period_ns); + if (!pwm || period_ns == 0 || duty_ns > period_ns) + return -EINVAL; + + return pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns); } EXPORT_SYMBOL_GPL(pwm_config); @@ -208,10 +321,10 @@ EXPORT_SYMBOL_GPL(pwm_config); */ int pwm_enable(struct pwm_device *pwm) { - if (!test_and_set_bit(FLAG_ENABLED, &pwm->flags)) - return pwm->chip->ops->enable(pwm->chip); + if (pwm && !test_and_set_bit(PWMF_ENABLED, &pwm->flags)) + return pwm->chip->ops->enable(pwm->chip, pwm); - return 0; + return pwm ? 0 : -EINVAL; } EXPORT_SYMBOL_GPL(pwm_enable); @@ -221,7 +334,7 @@ EXPORT_SYMBOL_GPL(pwm_enable); */ void pwm_disable(struct pwm_device *pwm) { - if (test_and_clear_bit(FLAG_ENABLED, &pwm->flags)) - pwm->chip->ops->disable(pwm->chip); + if (pwm && test_and_clear_bit(PWMF_ENABLED, &pwm->flags)) + pwm->chip->ops->disable(pwm->chip, pwm); } EXPORT_SYMBOL_GPL(pwm_disable); diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 1f308a13105f..57103911f4c7 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -31,6 +31,33 @@ void pwm_disable(struct pwm_device *pwm); #ifdef CONFIG_PWM struct pwm_chip; +enum { + PWMF_REQUESTED = 1 << 0, + PWMF_ENABLED = 1 << 1, +}; + +struct pwm_device { + const char *label; + unsigned long flags; + unsigned int hwpwm; + unsigned int pwm; + struct pwm_chip *chip; + void *chip_data; + + unsigned int period; /* in nanoseconds */ +}; + +static inline void pwm_set_period(struct pwm_device *pwm, unsigned int period) +{ + if (pwm) + pwm->period = period; +} + +static inline unsigned int pwm_get_period(struct pwm_device *pwm) +{ + return pwm ? pwm->period : 0; +} + /** * struct pwm_ops - PWM controller operations * @request: optional hook for requesting a PWM @@ -41,29 +68,47 @@ struct pwm_chip; * @owner: helps prevent removal of modules exporting active PWMs */ struct pwm_ops { - int (*request)(struct pwm_chip *chip); - void (*free)(struct pwm_chip *chip); - int (*config)(struct pwm_chip *chip, int duty_ns, - int period_ns); - int (*enable)(struct pwm_chip *chip); - void (*disable)(struct pwm_chip *chip); + int (*request)(struct pwm_chip *chip, + struct pwm_device *pwm); + void (*free)(struct pwm_chip *chip, + struct pwm_device *pwm); + int (*config)(struct pwm_chip *chip, + struct pwm_device *pwm, + int duty_ns, int period_ns); + int (*enable)(struct pwm_chip *chip, + struct pwm_device *pwm); + void (*disable)(struct pwm_chip *chip, + struct pwm_device *pwm); struct module *owner; }; /** - * struct pwm_chip - abstract a PWM - * @pwm_id: global PWM device index - * @label: PWM device label - * @ops: controller operations + * struct pwm_chip - abstract a PWM controller + * @dev: device providing the PWMs + * @list: list node for internal use + * @ops: callbacks for this PWM controller + * @base: number of first PWM controlled by this chip + * @npwm: number of PWMs controlled by this chip + * @pwms: array of PWM devices allocated by the framework */ struct pwm_chip { - int pwm_id; - const char *label; - struct pwm_ops *ops; + struct device *dev; + struct list_head list; + const struct pwm_ops *ops; + int base; + unsigned int npwm; + + struct pwm_device *pwms; }; +int pwm_set_chip_data(struct pwm_device *pwm, void *data); +void *pwm_get_chip_data(struct pwm_device *pwm); + int pwmchip_add(struct pwm_chip *chip); int pwmchip_remove(struct pwm_chip *chip); +struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, + unsigned int index, + const char *label); #endif #endif /* __LINUX_PWM_H */ -- cgit v1.2.3 From 62099abf67a20cfb98d4c031fb1925e10a78ee1b Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 26 Mar 2012 09:31:48 +0200 Subject: pwm: Add debugfs interface This commit adds a debugfs interface that can be used to list the current internal state of the PWM devices registered with the PWM framework. Reviewed-by: Mark Brown Reviewed-by: Shawn Guo Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pwm.h | 6 ++++ 2 files changed, 96 insertions(+) (limited to 'include') diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index a447be128328..aadc1d797449 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #define MAX_PWMS 1024 @@ -338,3 +340,91 @@ void pwm_disable(struct pwm_device *pwm) pwm->chip->ops->disable(pwm->chip, pwm); } EXPORT_SYMBOL_GPL(pwm_disable); + +#ifdef CONFIG_DEBUG_FS +static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s) +{ + unsigned int i; + + for (i = 0; i < chip->npwm; i++) { + struct pwm_device *pwm = &chip->pwms[i]; + + seq_printf(s, " pwm-%-3d (%-20.20s):", i, pwm->label); + + if (test_bit(PWMF_REQUESTED, &pwm->flags)) + seq_printf(s, " requested"); + + if (test_bit(PWMF_ENABLED, &pwm->flags)) + seq_printf(s, " enabled"); + + seq_printf(s, "\n"); + } +} + +static void *pwm_seq_start(struct seq_file *s, loff_t *pos) +{ + mutex_lock(&pwm_lock); + s->private = ""; + + return seq_list_start(&pwm_chips, *pos); +} + +static void *pwm_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + s->private = "\n"; + + return seq_list_next(v, &pwm_chips, pos); +} + +static void pwm_seq_stop(struct seq_file *s, void *v) +{ + mutex_unlock(&pwm_lock); +} + +static int pwm_seq_show(struct seq_file *s, void *v) +{ + struct pwm_chip *chip = list_entry(v, struct pwm_chip, list); + + seq_printf(s, "%s%s/%s, %d PWM device%s\n", (char *)s->private, + chip->dev->bus ? chip->dev->bus->name : "no-bus", + dev_name(chip->dev), chip->npwm, + (chip->npwm != 1) ? "s" : ""); + + if (chip->ops->dbg_show) + chip->ops->dbg_show(chip, s); + else + pwm_dbg_show(chip, s); + + return 0; +} + +static const struct seq_operations pwm_seq_ops = { + .start = pwm_seq_start, + .next = pwm_seq_next, + .stop = pwm_seq_stop, + .show = pwm_seq_show, +}; + +static int pwm_seq_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &pwm_seq_ops); +} + +static const struct file_operations pwm_debugfs_ops = { + .owner = THIS_MODULE, + .open = pwm_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int __init pwm_debugfs_init(void) +{ + debugfs_create_file("pwm", S_IFREG | S_IRUGO, NULL, NULL, + &pwm_debugfs_ops); + + return 0; +} + +subsys_initcall(pwm_debugfs_init); +#endif /* CONFIG_DEBUG_FS */ diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 57103911f4c7..047cd5351a3b 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -2,6 +2,7 @@ #define __LINUX_PWM_H struct pwm_device; +struct seq_file; /* * pwm_request - request a PWM device @@ -65,6 +66,7 @@ static inline unsigned int pwm_get_period(struct pwm_device *pwm) * @config: configure duty cycles and period length for this PWM * @enable: enable PWM output toggling * @disable: disable PWM output toggling + * @dbg_show: optional routine to show contents in debugfs * @owner: helps prevent removal of modules exporting active PWMs */ struct pwm_ops { @@ -79,6 +81,10 @@ struct pwm_ops { struct pwm_device *pwm); void (*disable)(struct pwm_chip *chip, struct pwm_device *pwm); +#ifdef CONFIG_DEBUG_FS + void (*dbg_show)(struct pwm_chip *chip, + struct seq_file *s); +#endif struct module *owner; }; -- cgit v1.2.3 From 8138d2ddbcca2a100482dac390133f83c5a60f94 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 26 Mar 2012 08:42:48 +0200 Subject: pwm: Add table-based lookup for static mappings In order to get rid of the global namespace for PWM devices, this commit provides an alternative method, similar to that of the regulator or clock frameworks, for registering a static mapping for PWM devices. This works by providing a table with a provider/consumer map in the board setup code. With the new pwm_get() and pwm_put() functions available, usage of pwm_request() and pwm_free() becomes deprecated. Reviewed-by: Shawn Guo Reviewed-by: Mark Brown Signed-off-by: Thierry Reding --- Documentation/pwm.txt | 27 ++++++-- drivers/pwm/core.c | 169 +++++++++++++++++++++++++++++++++++++++++++++----- include/linux/pwm.h | 22 +++++++ 3 files changed, 199 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt index 48f598acdd16..554290ebab94 100644 --- a/Documentation/pwm.txt +++ b/Documentation/pwm.txt @@ -12,14 +12,33 @@ this kind of flexibility the generic PWM API exists. Identifying PWMs ---------------- -Users of the legacy PWM API use unique IDs to refer to PWM devices. One -goal of the new PWM framework is to get rid of this global namespace. +Users of the legacy PWM API use unique IDs to refer to PWM devices. + +Instead of referring to a PWM device via its unique ID, board setup code +should instead register a static mapping that can be used to match PWM +consumers to providers, as given in the following example: + + static struct pwm_lookup board_pwm_lookup[] = { + PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight", NULL), + }; + + static void __init board_init(void) + { + ... + pwm_add_table(board_pwm_lookup, ARRAY_SIZE(board_pwm_lookup)); + ... + } Using PWMs ---------- -A PWM can be requested using pwm_request() and freed after usage with -pwm_free(). After being requested a PWM has to be configured using +Legacy users can request a PWM device using pwm_request() and free it +after usage with pwm_free(). + +New users should use the pwm_get() function and pass to it the consumer +device or a consumer name. pwm_put() is used to free the PWM device. + +After being requested a PWM has to be configured using: int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns); diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index aadc1d797449..a2af599e61ae 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -32,6 +32,8 @@ #define MAX_PWMS 1024 +static DEFINE_MUTEX(pwm_lookup_lock); +static LIST_HEAD(pwm_lookup_list); static DEFINE_MUTEX(pwm_lock); static LIST_HEAD(pwm_chips); static DECLARE_BITMAP(allocated_pwms, MAX_PWMS); @@ -80,6 +82,29 @@ static void free_pwms(struct pwm_chip *chip) chip->pwms = NULL; } +static struct pwm_chip *pwmchip_find_by_name(const char *name) +{ + struct pwm_chip *chip; + + if (!name) + return NULL; + + mutex_lock(&pwm_lock); + + list_for_each_entry(chip, &pwm_chips, list) { + const char *chip_name = dev_name(chip->dev); + + if (chip_name && strcmp(chip_name, name) == 0) { + mutex_unlock(&pwm_lock); + return chip; + } + } + + mutex_unlock(&pwm_lock); + + return NULL; +} + static int pwm_device_request(struct pwm_device *pwm, const char *label) { int err; @@ -218,6 +243,8 @@ EXPORT_SYMBOL_GPL(pwmchip_remove); * pwm_request() - request a PWM device * @pwm_id: global PWM device index * @label: PWM device label + * + * This function is deprecated, use pwm_get() instead. */ struct pwm_device *pwm_request(int pwm, const char *label) { @@ -281,24 +308,12 @@ EXPORT_SYMBOL_GPL(pwm_request_from_chip); /** * pwm_free() - free a PWM device * @pwm: PWM device + * + * This function is deprecated, use pwm_put() instead. */ void pwm_free(struct pwm_device *pwm) { - mutex_lock(&pwm_lock); - - if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) { - pr_warning("PWM device already freed\n"); - goto out; - } - - if (pwm->chip->ops->free) - pwm->chip->ops->free(pwm->chip, pwm); - - pwm->label = NULL; - - module_put(pwm->chip->ops->owner); -out: - mutex_unlock(&pwm_lock); + pwm_put(pwm); } EXPORT_SYMBOL_GPL(pwm_free); @@ -341,6 +356,130 @@ void pwm_disable(struct pwm_device *pwm) } EXPORT_SYMBOL_GPL(pwm_disable); +/** + * pwm_add_table() - register PWM device consumers + * @table: array of consumers to register + * @num: number of consumers in table + */ +void __init pwm_add_table(struct pwm_lookup *table, size_t num) +{ + mutex_lock(&pwm_lookup_lock); + + while (num--) { + list_add_tail(&table->list, &pwm_lookup_list); + table++; + } + + mutex_unlock(&pwm_lookup_lock); +} + +/** + * pwm_get() - look up and request a PWM device + * @dev: device for PWM consumer + * @con_id: consumer name + * + * Look up a PWM chip and a relative index via a table supplied by board setup + * code (see pwm_add_table()). + * + * Once a PWM chip has been found the specified PWM device will be requested + * and is ready to be used. + */ +struct pwm_device *pwm_get(struct device *dev, const char *con_id) +{ + struct pwm_device *pwm = ERR_PTR(-EPROBE_DEFER); + const char *dev_id = dev ? dev_name(dev): NULL; + struct pwm_chip *chip = NULL; + unsigned int best = 0; + struct pwm_lookup *p; + unsigned int index; + unsigned int match; + + /* + * We look up the provider in the static table typically provided by + * board setup code. We first try to lookup the consumer device by + * name. If the consumer device was passed in as NULL or if no match + * was found, we try to find the consumer by directly looking it up + * by name. + * + * If a match is found, the provider PWM chip is looked up by name + * and a PWM device is requested using the PWM device per-chip index. + * + * The lookup algorithm was shamelessly taken from the clock + * framework: + * + * We do slightly fuzzy matching here: + * An entry with a NULL ID is assumed to be a wildcard. + * If an entry has a device ID, it must match + * If an entry has a connection ID, it must match + * Then we take the most specific entry - with the following order + * of precedence: dev+con > dev only > con only. + */ + mutex_lock(&pwm_lookup_lock); + + list_for_each_entry(p, &pwm_lookup_list, list) { + match = 0; + + if (p->dev_id) { + if (!dev_id || strcmp(p->dev_id, dev_id)) + continue; + + match += 2; + } + + if (p->con_id) { + if (!con_id || strcmp(p->con_id, con_id)) + continue; + + match += 1; + } + + if (match > best) { + chip = pwmchip_find_by_name(p->provider); + index = p->index; + + if (match != 3) + best = match; + else + break; + } + } + + if (chip) + pwm = pwm_request_from_chip(chip, index, con_id ?: dev_id); + + mutex_unlock(&pwm_lookup_lock); + + return pwm; +} +EXPORT_SYMBOL_GPL(pwm_get); + +/** + * pwm_put() - release a PWM device + * @pwm: PWM device + */ +void pwm_put(struct pwm_device *pwm) +{ + if (!pwm) + return; + + mutex_lock(&pwm_lock); + + if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) { + pr_warning("PWM device already freed\n"); + goto out; + } + + if (pwm->chip->ops->free) + pwm->chip->ops->free(pwm->chip, pwm); + + pwm->label = NULL; + + module_put(pwm->chip->ops->owner); +out: + mutex_unlock(&pwm_lock); +} +EXPORT_SYMBOL_GPL(pwm_put); + #ifdef CONFIG_DEBUG_FS static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s) { diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 047cd5351a3b..2947a4fea6ad 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -115,6 +115,28 @@ int pwmchip_remove(struct pwm_chip *chip); struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, unsigned int index, const char *label); + +struct pwm_device *pwm_get(struct device *dev, const char *consumer); +void pwm_put(struct pwm_device *pwm); + +struct pwm_lookup { + struct list_head list; + const char *provider; + unsigned int index; + const char *dev_id; + const char *con_id; +}; + +#define PWM_LOOKUP(_provider, _index, _dev_id, _con_id) \ + { \ + .provider = _provider, \ + .index = _index, \ + .dev_id = _dev_id, \ + .con_id = _con_id, \ + } + +void pwm_add_table(struct pwm_lookup *table, size_t num); + #endif #endif /* __LINUX_PWM_H */ -- cgit v1.2.3 From bd3d5500f0c41a30149cb184362716096a17bc75 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 13 Apr 2012 16:18:34 +0200 Subject: dt: Add empty of_property_match_string() function This commit adds an empty of_property_match_string() function for !CONFIG_OF builds. Acked-by: Rob Herring Signed-off-by: Thierry Reding --- include/linux/of.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/linux/of.h b/include/linux/of.h index 2ec1083af7ff..597e571110a2 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -377,6 +377,13 @@ static inline int of_property_read_u64(const struct device_node *np, return -ENOSYS; } +static inline int of_property_match_string(struct device_node *np, + const char *propname, + const char *string) +{ + return -ENOSYS; +} + static inline struct device_node *of_parse_phandle(struct device_node *np, const char *phandle_name, int index) -- cgit v1.2.3 From e05e5070f0ec2557d2d2ff3655ba03f29e297151 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 13 Apr 2012 16:19:21 +0200 Subject: dt: Add empty of_parse_phandle_with_args() function This commit adds an empty of_parse_phandle_with_args() function for !CONFIG_OF builds. Acked-by: Rob Herring Signed-off-by: Thierry Reding --- include/linux/of.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include') diff --git a/include/linux/of.h b/include/linux/of.h index 597e571110a2..d5dd5c06efd3 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -391,6 +391,15 @@ static inline struct device_node *of_parse_phandle(struct device_node *np, return NULL; } +static inline int of_parse_phandle_with_args(struct device_node *np, + const char *list_name, + const char *cells_name, + int index, + struct of_phandle_args *out_args) +{ + return -ENOSYS; +} + static inline int of_alias_get_id(struct device_node *np, const char *stem) { return -ENOSYS; -- cgit v1.2.3 From 7299ab70e68e20e70cb45fe4ab4b6029fe964acd Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 14 Dec 2011 11:10:32 +0100 Subject: pwm: Add device tree support This patch adds helpers to support device tree bindings for the generic PWM API. Device tree binding documentation for PWM controllers is also provided. Acked-by: Arnd Bergmann Reviewed-by: Shawn Guo Signed-off-by: Thierry Reding --- Documentation/devicetree/bindings/pwm/pwm.txt | 57 ++++++++++ drivers/pwm/core.c | 148 +++++++++++++++++++++++++- include/linux/pwm.h | 6 ++ 3 files changed, 209 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/pwm/pwm.txt (limited to 'include') diff --git a/Documentation/devicetree/bindings/pwm/pwm.txt b/Documentation/devicetree/bindings/pwm/pwm.txt new file mode 100644 index 000000000000..73ec962bfe8c --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm.txt @@ -0,0 +1,57 @@ +Specifying PWM information for devices +====================================== + +1) PWM user nodes +----------------- + +PWM users should specify a list of PWM devices that they want to use +with a property containing a 'pwm-list': + + pwm-list ::= [pwm-list] + single-pwm ::= + pwm-phandle : phandle to PWM controller node + pwm-specifier : array of #pwm-cells specifying the given PWM + (controller specific) + +PWM properties should be named "pwms". The exact meaning of each pwms +property must be documented in the device tree binding for each device. +An optional property "pwm-names" may contain a list of strings to label +each of the PWM devices listed in the "pwms" property. If no "pwm-names" +property is given, the name of the user node will be used as fallback. + +Drivers for devices that use more than a single PWM device can use the +"pwm-names" property to map the name of the PWM device requested by the +pwm_get() call to an index into the list given by the "pwms" property. + +The following example could be used to describe a PWM-based backlight +device: + + pwm: pwm { + #pwm-cells = <2>; + }; + + [...] + + bl: backlight { + pwms = <&pwm 0 5000000>; + pwm-names = "backlight"; + }; + +pwm-specifier typically encodes the chip-relative PWM number and the PWM +period in nanoseconds. Note that in the example above, specifying the +"pwm-names" is redundant because the name "backlight" would be used as +fallback anyway. + +2) PWM controller nodes +----------------------- + +PWM controller nodes must specify the number of cells used for the +specifier using the '#pwm-cells' property. + +An example PWM controller might look like this: + + pwm: pwm@7000a000 { + compatible = "nvidia,tegra20-pwm"; + reg = <0x7000a000 0x100>; + #pwm-cells = <2>; + }; diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index a2af599e61ae..dbab53005da8 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -129,6 +129,45 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label) return 0; } +static struct pwm_device *of_pwm_simple_xlate(struct pwm_chip *pc, + const struct of_phandle_args *args) +{ + struct pwm_device *pwm; + + if (pc->of_pwm_n_cells < 2) + return ERR_PTR(-EINVAL); + + if (args->args[0] >= pc->npwm) + return ERR_PTR(-EINVAL); + + pwm = pwm_request_from_chip(pc, args->args[0], NULL); + if (IS_ERR(pwm)) + return pwm; + + pwm_set_period(pwm, args->args[1]); + + return pwm; +} + +void of_pwmchip_add(struct pwm_chip *chip) +{ + if (!chip->dev || !chip->dev->of_node) + return; + + if (!chip->of_xlate) { + chip->of_xlate = of_pwm_simple_xlate; + chip->of_pwm_n_cells = 2; + } + + of_node_get(chip->dev->of_node); +} + +void of_pwmchip_remove(struct pwm_chip *chip) +{ + if (chip->dev && chip->dev->of_node) + of_node_put(chip->dev->of_node); +} + /** * pwm_set_chip_data() - set private chip data for a PWM * @pwm: PWM device @@ -201,6 +240,9 @@ int pwmchip_add(struct pwm_chip *chip) ret = 0; + if (IS_ENABLED(CONFIG_OF)) + of_pwmchip_add(chip); + out: mutex_unlock(&pwm_lock); return ret; @@ -231,6 +273,10 @@ int pwmchip_remove(struct pwm_chip *chip) } list_del_init(&chip->list); + + if (IS_ENABLED(CONFIG_OF)) + of_pwmchip_remove(chip); + free_pwms(chip); out: @@ -356,6 +402,99 @@ void pwm_disable(struct pwm_device *pwm) } EXPORT_SYMBOL_GPL(pwm_disable); +static struct pwm_chip *of_node_to_pwmchip(struct device_node *np) +{ + struct pwm_chip *chip; + + mutex_lock(&pwm_lock); + + list_for_each_entry(chip, &pwm_chips, list) + if (chip->dev && chip->dev->of_node == np) { + mutex_unlock(&pwm_lock); + return chip; + } + + mutex_unlock(&pwm_lock); + + return ERR_PTR(-EPROBE_DEFER); +} + +/** + * of_pwm_request() - request a PWM via the PWM framework + * @np: device node to get the PWM from + * @con_id: consumer name + * + * Returns the PWM device parsed from the phandle and index specified in the + * "pwms" property of a device tree node or a negative error-code on failure. + * Values parsed from the device tree are stored in the returned PWM device + * object. + * + * If con_id is NULL, the first PWM device listed in the "pwms" property will + * be requested. Otherwise the "pwm-names" property is used to do a reverse + * lookup of the PWM index. This also means that the "pwm-names" property + * becomes mandatory for devices that look up the PWM device via the con_id + * parameter. + */ +static struct pwm_device *of_pwm_request(struct device_node *np, + const char *con_id) +{ + struct pwm_device *pwm = NULL; + struct of_phandle_args args; + struct pwm_chip *pc; + int index = 0; + int err; + + if (con_id) { + index = of_property_match_string(np, "pwm-names", con_id); + if (index < 0) + return ERR_PTR(index); + } + + err = of_parse_phandle_with_args(np, "pwms", "#pwm-cells", index, + &args); + if (err) { + pr_debug("%s(): can't parse \"pwms\" property\n", __func__); + return ERR_PTR(err); + } + + pc = of_node_to_pwmchip(args.np); + if (IS_ERR(pc)) { + pr_debug("%s(): PWM chip not found\n", __func__); + pwm = ERR_CAST(pc); + goto put; + } + + if (args.args_count != pc->of_pwm_n_cells) { + pr_debug("%s: wrong #pwm-cells for %s\n", np->full_name, + args.np->full_name); + pwm = ERR_PTR(-EINVAL); + goto put; + } + + pwm = pc->of_xlate(pc, &args); + if (IS_ERR(pwm)) + goto put; + + /* + * If a consumer name was not given, try to look it up from the + * "pwm-names" property if it exists. Otherwise use the name of + * the user device node. + */ + if (!con_id) { + err = of_property_read_string_index(np, "pwm-names", index, + &con_id); + if (err < 0) + con_id = np->name; + } + + pwm->label = con_id; + +put: + of_node_put(args.np); + + return pwm; +} + /** * pwm_add_table() - register PWM device consumers * @table: array of consumers to register @@ -378,8 +517,9 @@ void __init pwm_add_table(struct pwm_lookup *table, size_t num) * @dev: device for PWM consumer * @con_id: consumer name * - * Look up a PWM chip and a relative index via a table supplied by board setup - * code (see pwm_add_table()). + * Lookup is first attempted using DT. If the device was not instantiated from + * a device tree, a PWM chip and a relative index is looked up via a table + * supplied by board setup code (see pwm_add_table()). * * Once a PWM chip has been found the specified PWM device will be requested * and is ready to be used. @@ -394,6 +534,10 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) unsigned int index; unsigned int match; + /* look up via DT first */ + if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) + return of_pwm_request(dev->of_node, con_id); + /* * We look up the provider in the static table typically provided by * board setup code. We first try to lookup the consumer device by diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 2947a4fea6ad..21d076c5089e 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -1,6 +1,8 @@ #ifndef __LINUX_PWM_H #define __LINUX_PWM_H +#include + struct pwm_device; struct seq_file; @@ -105,6 +107,10 @@ struct pwm_chip { unsigned int npwm; struct pwm_device *pwms; + + struct pwm_device * (*of_xlate)(struct pwm_chip *pc, + const struct of_phandle_args *args); + unsigned int of_pwm_n_cells; }; int pwm_set_chip_data(struct pwm_device *pwm, void *data); -- cgit v1.2.3 From efd68e7254503f3207805f674a1ea1d743f5dfe2 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sun, 3 Jun 2012 22:04:33 -0700 Subject: devicetree: add helper inline for retrieving a node's full name The pattern (np ? np->full_name : "") is rather common in the kernel, but can also make for quite long lines. This patch adds a new inline function, of_node_full_name() so that the test for a valid node pointer doesn't need to be open coded at all call sites. Signed-off-by: Grant Likely Cc: Paul Mundt Cc: Benjamin Herrenschmidt Cc: Thomas Gleixner --- arch/microblaze/pci/pci-common.c | 6 ++---- arch/powerpc/kernel/pci-common.c | 6 ++---- arch/powerpc/kernel/vio.c | 5 ++--- arch/powerpc/platforms/cell/iommu.c | 3 +-- arch/powerpc/platforms/pseries/iommu.c | 2 +- arch/sparc/kernel/of_device_64.c | 2 +- drivers/of/base.c | 2 +- drivers/of/irq.c | 2 +- include/linux/of.h | 10 ++++++++++ kernel/irq/irqdomain.c | 8 ++++---- 10 files changed, 25 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/arch/microblaze/pci/pci-common.c b/arch/microblaze/pci/pci-common.c index ed22bfc5db14..ca8f6e769960 100644 --- a/arch/microblaze/pci/pci-common.c +++ b/arch/microblaze/pci/pci-common.c @@ -249,8 +249,7 @@ int pci_read_irq_line(struct pci_dev *pci_dev) } else { pr_debug(" Got one, spec %d cells (0x%08x 0x%08x...) on %s\n", oirq.size, oirq.specifier[0], oirq.specifier[1], - oirq.controller ? oirq.controller->full_name : - ""); + of_node_full_name(oirq.controller)); virq = irq_create_of_mapping(oirq.controller, oirq.specifier, oirq.size); @@ -1493,8 +1492,7 @@ static void __devinit pcibios_scan_phb(struct pci_controller *hose) struct pci_bus *bus; struct device_node *node = hose->dn; - pr_debug("PCI: Scanning PHB %s\n", - node ? node->full_name : ""); + pr_debug("PCI: Scanning PHB %s\n", of_node_full_name(node)); pcibios_setup_phb_resources(hose, &resources); diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 8e78e93c8185..886c254fd565 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -248,8 +248,7 @@ static int pci_read_irq_line(struct pci_dev *pci_dev) } else { pr_debug(" Got one, spec %d cells (0x%08x 0x%08x...) on %s\n", oirq.size, oirq.specifier[0], oirq.specifier[1], - oirq.controller ? oirq.controller->full_name : - ""); + of_node_full_name(oirq.controller)); virq = irq_create_of_mapping(oirq.controller, oirq.specifier, oirq.size); @@ -1628,8 +1627,7 @@ void __devinit pcibios_scan_phb(struct pci_controller *hose) struct device_node *node = hose->dn; int mode; - pr_debug("PCI: Scanning PHB %s\n", - node ? node->full_name : ""); + pr_debug("PCI: Scanning PHB %s\n", of_node_full_name(node)); /* Get some IO space for the new PHB */ pcibios_setup_phb_io_space(hose); diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c index cb87301ccd55..63f72ede4341 100644 --- a/arch/powerpc/kernel/vio.c +++ b/arch/powerpc/kernel/vio.c @@ -1296,8 +1296,7 @@ static void __devinit vio_dev_release(struct device *dev) struct iommu_table *tbl = get_iommu_table_base(dev); if (tbl) - iommu_free_table(tbl, dev->of_node ? - dev->of_node->full_name : dev_name(dev)); + iommu_free_table(tbl, of_node_full_name(dev->of_node)); of_node_put(dev->of_node); kfree(to_vio_dev(dev)); } @@ -1509,7 +1508,7 @@ static ssize_t devspec_show(struct device *dev, { struct device_node *of_node = dev->of_node; - return sprintf(buf, "%s\n", of_node ? of_node->full_name : "none"); + return sprintf(buf, "%s\n", of_node_full_name(of_node)); } static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c index b9f509a34c01..b6732004c882 100644 --- a/arch/powerpc/platforms/cell/iommu.c +++ b/arch/powerpc/platforms/cell/iommu.c @@ -552,8 +552,7 @@ static struct iommu_table *cell_get_iommu_table(struct device *dev) iommu = cell_iommu_for_node(dev_to_node(dev)); if (iommu == NULL || list_empty(&iommu->windows)) { printk(KERN_ERR "iommu: missing iommu for %s (node %d)\n", - dev->of_node ? dev->of_node->full_name : "?", - dev_to_node(dev)); + of_node_full_name(dev->of_node), dev_to_node(dev)); return NULL; } window = list_entry(iommu->windows.next, struct iommu_window, list); diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index 0915b1ad66ce..aab5fbc924e6 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -1051,7 +1051,7 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev) if (!pdn || !PCI_DN(pdn)) { printk(KERN_WARNING "pci_dma_dev_setup_pSeriesLP: " "no DMA window found for pci dev=%s dn=%s\n", - pci_name(dev), dn? dn->full_name : ""); + pci_name(dev), of_node_full_name(dn)); return; } pr_debug(" parent is %s\n", pdn->full_name); diff --git a/arch/sparc/kernel/of_device_64.c b/arch/sparc/kernel/of_device_64.c index 7a3be6f6737a..7bbdc26d9512 100644 --- a/arch/sparc/kernel/of_device_64.c +++ b/arch/sparc/kernel/of_device_64.c @@ -580,7 +580,7 @@ static unsigned int __init build_one_device_irq(struct platform_device *op, printk("%s: Apply [%s:%x] imap --> [%s:%x]\n", op->dev.of_node->full_name, pp->full_name, this_orig_irq, - (iret ? iret->full_name : "NULL"), irq); + of_node_full_name(iret), irq); if (!iret) break; diff --git a/drivers/of/base.c b/drivers/of/base.c index d9bfd49b1935..9282d4cc1091 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1173,7 +1173,7 @@ static void of_alias_add(struct alias_prop *ap, struct device_node *np, ap->stem[stem_len] = 0; list_add_tail(&ap->link, &aliases_lookup); pr_debug("adding DT alias:%s: stem=%s id=%i node=%s\n", - ap->alias, ap->stem, ap->id, np ? np->full_name : NULL); + ap->alias, ap->stem, ap->id, of_node_full_name(np)); } /** diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 9cf00602f566..ff8ab7b27373 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -255,7 +255,7 @@ int of_irq_map_raw(struct device_node *parent, const __be32 *intspec, skiplevel: /* Iterate again with new parent */ - pr_debug(" -> new parent: %s\n", newpar ? newpar->full_name : "<>"); + pr_debug(" -> new parent: %s\n", of_node_full_name(newpar)); of_node_put(ipar); ipar = newpar; newpar = NULL; diff --git a/include/linux/of.h b/include/linux/of.h index 2ec1083af7ff..1012377cae92 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -163,6 +163,11 @@ static inline int of_node_to_nid(struct device_node *np) { return -1; } #define of_node_to_nid of_node_to_nid #endif +static inline const char* of_node_full_name(struct device_node *np) +{ + return np ? np->full_name : ""; +} + extern struct device_node *of_find_node_by_name(struct device_node *from, const char *name); #define for_each_node_by_name(dn, name) \ @@ -303,6 +308,11 @@ const char *of_prop_next_string(struct property *prop, const char *cur); #else /* CONFIG_OF */ +static inline const char* of_node_full_name(struct device_node *np) +{ + return ""; +} + static inline bool of_have_populated_dt(void) { return false; diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 41c1564103f1..38c5eb839c92 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -448,7 +448,7 @@ unsigned int irq_create_mapping(struct irq_domain *domain, } pr_debug("irq %lu on domain %s mapped to virtual irq %u\n", - hwirq, domain->of_node ? domain->of_node->full_name : "null", virq); + hwirq, of_node_full_name(domain->of_node), virq); return virq; } @@ -477,7 +477,7 @@ unsigned int irq_create_of_mapping(struct device_node *controller, return intspec[0]; #endif pr_warning("no irq domain found for %s !\n", - controller->full_name); + of_node_full_name(controller)); return 0; } @@ -725,8 +725,8 @@ static int virq_debug_show(struct seq_file *m, void *private) data = irq_desc_get_chip_data(desc); seq_printf(m, data ? "0x%p " : " %p ", data); - if (desc->irq_data.domain && desc->irq_data.domain->of_node) - p = desc->irq_data.domain->of_node->full_name; + if (desc->irq_data.domain) + p = of_node_full_name(desc->irq_data.domain->of_node); else p = none; seq_printf(m, "%s\n", p); -- cgit v1.2.3 From 5ca4db61e859526b2dbee3bcea3626d3de49a0b2 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Sun, 3 Jun 2012 22:04:34 -0700 Subject: irqdomain: Simple NUMA awareness. While common irqdesc allocation is node aware, the irqdomain code is not. Presently we observe a number of regressions/inconsistencies on NUMA-capable platforms: - Platforms using irqdomains with legacy mappings, where the irq_descs are allocated node-local and the irqdomain data structure is not. - Drivers implementing irqdomains will lose node locality regardless of the underlying struct device's node id. This plugs in NUMA node id proliferation across the various allocation callsites by way of_node_to_nid() node lookup. While of_node_to_nid() does the right thing for OF-capable platforms it doesn't presently handle the non-DT case. This is trivially dealt with by simply wraping in to numa_node_id() unconditionally. Signed-off-by: Paul Mundt Cc: Benjamin Herrenschmidt Cc: Thomas Gleixner Cc: Rob Herring Signed-off-by: Grant Likely --- include/linux/of.h | 15 ++++++++++----- kernel/irq/irqdomain.c | 13 ++++++++----- 2 files changed, 18 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/of.h b/include/linux/of.h index 1012377cae92..76930ee78db5 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -158,11 +159,6 @@ static inline unsigned long of_read_ulong(const __be32 *cell, int size) #define OF_BAD_ADDR ((u64)-1) -#ifndef of_node_to_nid -static inline int of_node_to_nid(struct device_node *np) { return -1; } -#define of_node_to_nid of_node_to_nid -#endif - static inline const char* of_node_full_name(struct device_node *np) { return np ? np->full_name : ""; @@ -412,6 +408,15 @@ static inline int of_machine_is_compatible(const char *compat) while (0) #endif /* CONFIG_OF */ +#ifndef of_node_to_nid +static inline int of_node_to_nid(struct device_node *np) +{ + return numa_node_id(); +} + +#define of_node_to_nid of_node_to_nid +#endif + /** * of_property_read_bool - Findfrom a property * @np: device node from which the property value is to be read. diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 38c5eb839c92..79ae0ebb90ba 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -45,7 +46,8 @@ static struct irq_domain *irq_domain_alloc(struct device_node *of_node, { struct irq_domain *domain; - domain = kzalloc(sizeof(*domain), GFP_KERNEL); + domain = kzalloc_node(sizeof(*domain), GFP_KERNEL, + of_node_to_nid(of_node)); if (WARN_ON(!domain)) return NULL; @@ -229,7 +231,8 @@ struct irq_domain *irq_domain_add_linear(struct device_node *of_node, struct irq_domain *domain; unsigned int *revmap; - revmap = kzalloc(sizeof(*revmap) * size, GFP_KERNEL); + revmap = kzalloc_node(sizeof(*revmap) * size, GFP_KERNEL, + of_node_to_nid(of_node)); if (WARN_ON(!revmap)) return NULL; @@ -367,7 +370,7 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain) BUG_ON(domain == NULL); WARN_ON(domain->revmap_type != IRQ_DOMAIN_MAP_NOMAP); - virq = irq_alloc_desc_from(1, 0); + virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node)); if (!virq) { pr_debug("create_direct virq allocation failed\n"); return 0; @@ -433,9 +436,9 @@ unsigned int irq_create_mapping(struct irq_domain *domain, hint = hwirq % nr_irqs; if (hint == 0) hint++; - virq = irq_alloc_desc_from(hint, 0); + virq = irq_alloc_desc_from(hint, of_node_to_nid(domain->of_node)); if (virq <= 0) - virq = irq_alloc_desc_from(1, 0); + virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node)); if (virq <= 0) { pr_debug("-> virq allocation failed\n"); return 0; -- cgit v1.2.3 From 33e0c249801c6913b2bd102683ab65ab61e12952 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Fri, 15 Jun 2012 19:25:25 +0200 Subject: iio: iio/machine.h typo Signed-off-by: Peter Meerwald Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- include/linux/iio/machine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/iio/machine.h b/include/linux/iio/machine.h index 0b1f19bfdc44..400a453ff67b 100644 --- a/include/linux/iio/machine.h +++ b/include/linux/iio/machine.h @@ -14,7 +14,7 @@ * This is matched against the datasheet_name element * of struct iio_chan_spec. * @consumer_dev_name: Name to uniquely identify the consumer device. - * @consumer_channel: Unique name used to idenitify the channel on the + * @consumer_channel: Unique name used to identify the channel on the * consumer side. */ struct iio_map { -- cgit v1.2.3 From fb1c4bcd721fcc30d6e43f60a244483dc0d07056 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Fri, 15 Jun 2012 19:25:28 +0200 Subject: iio: iio/events.h typos Signed-off-by: Peter Meerwald Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- include/linux/iio/events.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/iio/events.h b/include/linux/iio/events.h index b5acbf93c5da..13ce220c7003 100644 --- a/include/linux/iio/events.h +++ b/include/linux/iio/events.h @@ -46,7 +46,7 @@ enum iio_event_direction { * @diff: Whether the event is for an differential channel or not. * @modifier: Modifier for the channel. Should be one of enum iio_modifier. * @direction: Direction of the event. One of enum iio_event_direction. - * @type: Type of the event. Should be one enum iio_event_type. + * @type: Type of the event. Should be one of enum iio_event_type. * @chan: Channel number for non-differential channels. * @chan1: First channel number for differential channels. * @chan2: Second channel number for differential channels. @@ -69,7 +69,7 @@ enum iio_event_direction { * @chan_type: Type of the channel. Should be one of enum iio_chan_type. * @number: Channel number. * @modifier: Modifier for the channel. Should be one of enum iio_modifier. - * @type: Type of the event. Should be one enum iio_event_type. + * @type: Type of the event. Should be one of enum iio_event_type. * @direction: Direction of the event. One of enum iio_event_direction. */ @@ -81,7 +81,7 @@ enum iio_event_direction { * IIO_UNMOD_EVENT_CODE() - create event identifier for unmodified channels * @chan_type: Type of the channel. Should be one of enum iio_chan_type. * @number: Channel number. - * @type: Type of the event. Should be one enum iio_event_type. + * @type: Type of the event. Should be one of enum iio_event_type. * @direction: Direction of the event. One of enum iio_event_direction. */ -- cgit v1.2.3 From fbebb9fd22581b6422d60669c4ff86ce99d6cdba Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sat, 16 Jun 2012 14:40:22 -0600 Subject: PCI: add infrastructure for devices with broken INTx masking pci_intx_mask_supported() assumes INTx masking is supported if the PCI_COMMAND_INTX_DISABLE bit is writable. But when that bit is set, some devices don't actually mask INTx or update PCI_STATUS_INTERRUPT as we expect. This patch adds a way for quirks to identify these broken devices. [bhelgaas: split out from Chelsio quirk addition] Signed-off-by: Jan Kiszka Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 3 +++ drivers/pci/quirks.c | 10 ++++++++++ include/linux/pci.h | 1 + 3 files changed, 14 insertions(+) (limited to 'include') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c01..9ae517a68360 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2876,6 +2876,9 @@ bool pci_intx_mask_supported(struct pci_dev *dev) bool mask_supported = false; u16 orig, new; + if (dev->broken_intx_masking) + return false; + pci_cfg_access_lock(dev); pci_read_config_word(dev, PCI_COMMAND, &orig); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 2a7521677541..cc13415416d7 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2929,6 +2929,16 @@ static void __devinit disable_igfx_irq(struct pci_dev *dev) DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0102, disable_igfx_irq); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq); +/* + * Some devices may pass our check in pci_intx_mask_supported if + * PCI_COMMAND_INTX_DISABLE works though they actually do not properly + * support this feature. + */ +static void __devinit quirk_broken_intx_masking(struct pci_dev *dev) +{ + dev->broken_intx_masking = 1; +} + static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, struct pci_fixup *end) { diff --git a/include/linux/pci.h b/include/linux/pci.h index d8c379dba6ad..40a039f1dffb 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -324,6 +324,7 @@ struct pci_dev { unsigned int is_hotplug_bridge:1; unsigned int __aer_firmware_first_valid:1; unsigned int __aer_firmware_first:1; + unsigned int broken_intx_masking:1; pci_dev_flags_t dev_flags; atomic_t enable_cnt; /* pci_enable_device has been called */ -- cgit v1.2.3 From 3be330bf8860dc6079da5acc81295787a04cf4c9 Mon Sep 17 00:00:00 2001 From: Jenny TC Date: Wed, 9 May 2012 20:36:47 +0530 Subject: power_supply: Register battery as a thermal zone Battery and charger contribute to Thermals in most of the embedded devices. So, it makes sense to identify them as Thermal zones in a particular platform. This patch registers a thermal zone if the power supply is reporting a temperature property. The thermal zone will be used by platform's thermal management solution. Signed-off-by: Jenny TC Signed-off-by: Anton Vorontsov --- drivers/power/power_supply_core.c | 65 +++++++++++++++++++++++++++++++++++++++ include/linux/power_supply.h | 3 ++ 2 files changed, 68 insertions(+) (limited to 'include') diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 6ad612726785..ff990d26a0c0 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "power_supply.h" /* exported for the APM Power driver, APM emulation */ @@ -169,6 +170,63 @@ static void power_supply_dev_release(struct device *dev) kfree(dev); } +#ifdef CONFIG_THERMAL +static int power_supply_read_temp(struct thermal_zone_device *tzd, + unsigned long *temp) +{ + struct power_supply *psy; + union power_supply_propval val; + int ret; + + WARN_ON(tzd == NULL); + psy = tzd->devdata; + ret = psy->get_property(psy, POWER_SUPPLY_PROP_TEMP, &val); + + /* Convert tenths of degree Celsius to milli degree Celsius. */ + if (!ret) + *temp = val.intval * 100; + + return ret; +} + +static struct thermal_zone_device_ops psy_tzd_ops = { + .get_temp = power_supply_read_temp, +}; + +static int psy_register_thermal(struct power_supply *psy) +{ + int i; + + /* Register battery zone device psy reports temperature */ + for (i = 0; i < psy->num_properties; i++) { + if (psy->properties[i] == POWER_SUPPLY_PROP_TEMP) { + psy->tzd = thermal_zone_device_register(psy->name, 0, + psy, &psy_tzd_ops, 0, 0, 0, 0); + if (IS_ERR(psy->tzd)) + return PTR_ERR(psy->tzd); + break; + } + } + return 0; +} + +static void psy_unregister_thermal(struct power_supply *psy) +{ + if (IS_ERR_OR_NULL(psy->tzd)) + return; + thermal_zone_device_unregister(psy->tzd); +} +#else +static int psy_register_thermal(struct power_supply *psy) +{ + return 0; +} + +static void psy_unregister_thermal(struct power_supply *psy) +{ +} +#endif + int power_supply_register(struct device *parent, struct power_supply *psy) { struct device *dev; @@ -197,6 +255,10 @@ int power_supply_register(struct device *parent, struct power_supply *psy) if (rc) goto device_add_failed; + rc = psy_register_thermal(psy); + if (rc) + goto register_thermal_failed; + rc = power_supply_create_triggers(psy); if (rc) goto create_triggers_failed; @@ -206,6 +268,8 @@ int power_supply_register(struct device *parent, struct power_supply *psy) goto success; create_triggers_failed: + psy_unregister_thermal(psy); +register_thermal_failed: device_del(dev); kobject_set_name_failed: device_add_failed: @@ -220,6 +284,7 @@ void power_supply_unregister(struct power_supply *psy) cancel_work_sync(&psy->changed_work); sysfs_remove_link(&psy->dev->kobj, "powers"); power_supply_remove_triggers(psy); + psy_unregister_thermal(psy); 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 3b912bee28d1..59ed2dd9dba9 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -173,6 +173,9 @@ struct power_supply { /* private */ struct device *dev; struct work_struct changed_work; +#ifdef CONFIG_THERMAL + struct thermal_zone_device *tzd; +#endif #ifdef CONFIG_LEDS_TRIGGERS struct led_trigger *charging_full_trig; -- cgit v1.2.3 From 4be77a530be1ea62574f31c20dd9848e7e2ab0f6 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Fri, 15 Jun 2012 16:35:28 +0100 Subject: ALSA: pcm: Add snd_pcm_rate_bit_to_rate() This is essentially the reverse of snd_pcm_rate_to_rate_bit(). This is generally useful as the Compress API uses the rate bit directly and it helps to be able to map back to the actual sample rate. Signed-off-by: Dimitris Papastamos Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 1 + sound/core/pcm_misc.c | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) (limited to 'include') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 0d1112815be3..68372bc1e11b 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -893,6 +893,7 @@ extern const struct snd_pcm_hw_constraint_list snd_pcm_known_rates; int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime); unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate); +unsigned int snd_pcm_rate_bit_to_rate(unsigned int rate_bit); static inline void snd_pcm_set_runtime_buffer(struct snd_pcm_substream *substream, struct snd_dma_buffer *bufp) diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index 9c9eff9afbac..d4fc1bfbe457 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -488,3 +488,21 @@ unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate) return SNDRV_PCM_RATE_KNOT; } EXPORT_SYMBOL(snd_pcm_rate_to_rate_bit); + +/** + * snd_pcm_rate_bit_to_rate - converts SNDRV_PCM_RATE_xxx bit to sample rate + * @rate_bit: the rate bit to convert + * + * Returns the sample rate that corresponds to the given SNDRV_PCM_RATE_xxx flag + * or 0 for an unknown rate bit + */ +unsigned int snd_pcm_rate_bit_to_rate(unsigned int rate_bit) +{ + unsigned int i; + + for (i = 0; i < snd_pcm_known_rates.count; i++) + if ((1u << i) == rate_bit) + return snd_pcm_known_rates.list[i]; + return 0; +} +EXPORT_SYMBOL(snd_pcm_rate_bit_to_rate); -- cgit v1.2.3 From ace3d8614ab0e6544f5f85921085b55b915fe9aa Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:14 +0200 Subject: HID: uhid: add internal message buffer When receiving messages from the HID subsystem, we need to process them and store them in an internal buffer so user-space can read() on the char device to retrieve the messages. This adds a static buffer for 32 messages to each uhid device. Each message is dynamically allocated so the uhid_device structure does not get too big. uhid_queue() adds a message to the buffer. If the buffer is full, the message is discarded. uhid_queue_event() is an helper for messages without payload. This also adds a public header: uhid.h. It contains the declarations for the user-space API. It is built around "struct uhid_event" which contains a type field which specifies the event type and each event can then add a variable-length payload. For now, there is only a dummy event but later patches will add new event types and payloads. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/Kbuild | 1 + include/linux/uhid.h | 33 ++++++++++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 include/linux/uhid.h (limited to 'include') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 5b02d6cb0e60..05ef4b05a63e 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -25,16 +25,81 @@ #include #define UHID_NAME "uhid" +#define UHID_BUFSIZE 32 + +struct uhid_device { + struct hid_device *hid; + + wait_queue_head_t waitq; + spinlock_t qlock; + __u8 head; + __u8 tail; + struct uhid_event *outq[UHID_BUFSIZE]; +}; static struct miscdevice uhid_misc; +static void uhid_queue(struct uhid_device *uhid, struct uhid_event *ev) +{ + __u8 newhead; + + newhead = (uhid->head + 1) % UHID_BUFSIZE; + + if (newhead != uhid->tail) { + uhid->outq[uhid->head] = ev; + uhid->head = newhead; + wake_up_interruptible(&uhid->waitq); + } else { + hid_warn(uhid->hid, "Output queue is full\n"); + kfree(ev); + } +} + +static int uhid_queue_event(struct uhid_device *uhid, __u32 event) +{ + unsigned long flags; + struct uhid_event *ev; + + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) + return -ENOMEM; + + ev->type = event; + + spin_lock_irqsave(&uhid->qlock, flags); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + return 0; +} + static int uhid_char_open(struct inode *inode, struct file *file) { + struct uhid_device *uhid; + + uhid = kzalloc(sizeof(*uhid), GFP_KERNEL); + if (!uhid) + return -ENOMEM; + + spin_lock_init(&uhid->qlock); + init_waitqueue_head(&uhid->waitq); + + file->private_data = uhid; + nonseekable_open(inode, file); + return 0; } static int uhid_char_release(struct inode *inode, struct file *file) { + struct uhid_device *uhid = file->private_data; + unsigned int i; + + for (i = 0; i < UHID_BUFSIZE; ++i) + kfree(uhid->outq[i]); + + kfree(uhid); + return 0; } diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 39737839ce29..8cdabecfbe27 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -373,6 +373,7 @@ header-y += tty.h header-y += types.h header-y += udf_fs_i.h header-y += udp.h +header-y += uhid.h header-y += uinput.h header-y += uio.h header-y += ultrasound.h diff --git a/include/linux/uhid.h b/include/linux/uhid.h new file mode 100644 index 000000000000..16b786a2b18f --- /dev/null +++ b/include/linux/uhid.h @@ -0,0 +1,33 @@ +#ifndef __UHID_H_ +#define __UHID_H_ + +/* + * User-space I/O driver support for HID subsystem + * Copyright (c) 2012 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. + */ + +/* + * Public header for user-space communication. We try to keep every structure + * aligned but to be safe we also use __attribute__((__packed__)). Therefore, + * the communication should be ABI compatible even between architectures. + */ + +#include +#include + +enum uhid_event_type { + UHID_DUMMY, +}; + +struct uhid_event { + __u32 type; +} __attribute__((__packed__)); + +#endif /* __UHID_H_ */ -- cgit v1.2.3 From d365c6cfd337a2bccdc65eacce271a311ea1072c Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:18 +0200 Subject: HID: uhid: add UHID_CREATE and UHID_DESTROY events UHID_CREATE and UHID_DESTROY are used to create and destroy a device on an open uhid char-device. Internally, we allocate and register an HID device with the HID core and immediately start the device. From now on events may be received or sent to the device. The UHID_CREATE event has a payload similar to the data used by Bluetooth-HIDP when creating a new connection. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/uhid.h | 21 +++++++- 2 files changed, 164 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 31e8379cfd15..61ee7cc32ccf 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -29,6 +29,11 @@ struct uhid_device { struct mutex devlock; + bool running; + + __u8 *rd_data; + uint rd_size; + struct hid_device *hid; struct uhid_event input_buf; @@ -75,6 +80,136 @@ static int uhid_queue_event(struct uhid_device *uhid, __u32 event) return 0; } +static int uhid_hid_start(struct hid_device *hid) +{ + return 0; +} + +static void uhid_hid_stop(struct hid_device *hid) +{ +} + +static int uhid_hid_open(struct hid_device *hid) +{ + return 0; +} + +static void uhid_hid_close(struct hid_device *hid) +{ +} + +static int uhid_hid_input(struct input_dev *input, unsigned int type, + unsigned int code, int value) +{ + return 0; +} + +static int uhid_hid_parse(struct hid_device *hid) +{ + return 0; +} + +static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, + __u8 *buf, size_t count, unsigned char rtype) +{ + return 0; +} + +static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, + unsigned char report_type) +{ + return 0; +} + +static struct hid_ll_driver uhid_hid_driver = { + .start = uhid_hid_start, + .stop = uhid_hid_stop, + .open = uhid_hid_open, + .close = uhid_hid_close, + .hidinput_input_event = uhid_hid_input, + .parse = uhid_hid_parse, +}; + +static int uhid_dev_create(struct uhid_device *uhid, + const struct uhid_event *ev) +{ + struct hid_device *hid; + int ret; + + if (uhid->running) + return -EALREADY; + + uhid->rd_size = ev->u.create.rd_size; + if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE) + return -EINVAL; + + uhid->rd_data = kmalloc(uhid->rd_size, GFP_KERNEL); + if (!uhid->rd_data) + return -ENOMEM; + + if (copy_from_user(uhid->rd_data, ev->u.create.rd_data, + uhid->rd_size)) { + ret = -EFAULT; + goto err_free; + } + + hid = hid_allocate_device(); + if (IS_ERR(hid)) { + ret = PTR_ERR(hid); + goto err_free; + } + + strncpy(hid->name, ev->u.create.name, 127); + hid->name[127] = 0; + strncpy(hid->phys, ev->u.create.phys, 63); + hid->phys[63] = 0; + strncpy(hid->uniq, ev->u.create.uniq, 63); + hid->uniq[63] = 0; + + hid->ll_driver = &uhid_hid_driver; + hid->hid_get_raw_report = uhid_hid_get_raw; + hid->hid_output_raw_report = uhid_hid_output_raw; + hid->bus = ev->u.create.bus; + hid->vendor = ev->u.create.vendor; + hid->product = ev->u.create.product; + hid->version = ev->u.create.version; + hid->country = ev->u.create.country; + hid->driver_data = uhid; + hid->dev.parent = uhid_misc.this_device; + + uhid->hid = hid; + uhid->running = true; + + ret = hid_add_device(hid); + if (ret) { + hid_err(hid, "Cannot register HID device\n"); + goto err_hid; + } + + return 0; + +err_hid: + hid_destroy_device(hid); + uhid->hid = NULL; + uhid->running = false; +err_free: + kfree(uhid->rd_data); + return ret; +} + +static int uhid_dev_destroy(struct uhid_device *uhid) +{ + if (!uhid->running) + return -EINVAL; + + uhid->running = false; + + hid_destroy_device(uhid->hid); + kfree(uhid->rd_data); + + return 0; +} + static int uhid_char_open(struct inode *inode, struct file *file) { struct uhid_device *uhid; @@ -86,6 +221,7 @@ static int uhid_char_open(struct inode *inode, struct file *file) mutex_init(&uhid->devlock); spin_lock_init(&uhid->qlock); init_waitqueue_head(&uhid->waitq); + uhid->running = false; file->private_data = uhid; nonseekable_open(inode, file); @@ -98,6 +234,8 @@ static int uhid_char_release(struct inode *inode, struct file *file) struct uhid_device *uhid = file->private_data; unsigned int i; + uhid_dev_destroy(uhid); + for (i = 0; i < UHID_BUFSIZE; ++i) kfree(uhid->outq[i]); @@ -177,6 +315,12 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer, } switch (uhid->input_buf.type) { + case UHID_CREATE: + ret = uhid_dev_create(uhid, &uhid->input_buf); + break; + case UHID_DESTROY: + ret = uhid_dev_destroy(uhid); + break; default: ret = -EOPNOTSUPP; } diff --git a/include/linux/uhid.h b/include/linux/uhid.h index 16b786a2b18f..8a493e604a77 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -23,11 +23,30 @@ #include enum uhid_event_type { - UHID_DUMMY, + UHID_CREATE, + UHID_DESTROY, }; +struct uhid_create_req { + __u8 name[128]; + __u8 phys[64]; + __u8 uniq[64]; + __u8 __user *rd_data; + __u16 rd_size; + + __u16 bus; + __u32 vendor; + __u32 product; + __u32 version; + __u32 country; +} __attribute__((__packed__)); + struct uhid_event { __u32 type; + + union { + struct uhid_create_req create; + } u; } __attribute__((__packed__)); #endif /* __UHID_H_ */ -- cgit v1.2.3 From 5e87a36ae375297b71cc21ac7e32846832bcfb34 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:19 +0200 Subject: HID: uhid: allow feeding input data into uhid devices This adds a new event type UHID_INPUT which allows user-space to feed raw HID reports into the HID subsystem. We copy the data into kernel memory and directly feed it into the HID core. There is no error handling of the events couldn't be parsed so user-space should consider all events successfull unless read() returns an error. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 14 ++++++++++++++ include/linux/uhid.h | 9 +++++++++ 2 files changed, 23 insertions(+) (limited to 'include') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 61ee7cc32ccf..3d1ebda122e5 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -210,6 +210,17 @@ static int uhid_dev_destroy(struct uhid_device *uhid) return 0; } +static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev) +{ + if (!uhid->running) + return -EINVAL; + + hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input.data, + min_t(size_t, ev->u.input.size, UHID_DATA_MAX), 0); + + return 0; +} + static int uhid_char_open(struct inode *inode, struct file *file) { struct uhid_device *uhid; @@ -321,6 +332,9 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer, case UHID_DESTROY: ret = uhid_dev_destroy(uhid); break; + case UHID_INPUT: + ret = uhid_dev_input(uhid, &uhid->input_buf); + break; default: ret = -EOPNOTSUPP; } diff --git a/include/linux/uhid.h b/include/linux/uhid.h index 8a493e604a77..6eb42bea86a2 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -25,6 +25,7 @@ enum uhid_event_type { UHID_CREATE, UHID_DESTROY, + UHID_INPUT, }; struct uhid_create_req { @@ -41,11 +42,19 @@ struct uhid_create_req { __u32 country; } __attribute__((__packed__)); +#define UHID_DATA_MAX 4096 + +struct uhid_input_req { + __u8 data[UHID_DATA_MAX]; + __u16 size; +} __attribute__((__packed__)); + struct uhid_event { __u32 type; union { struct uhid_create_req create; + struct uhid_input_req input; } u; } __attribute__((__packed__)); -- cgit v1.2.3 From ec4b7dea453e0f9fd0fbf1761b2d01eff64f264b Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:21 +0200 Subject: HID: uhid: add UHID_START and UHID_STOP events We send UHID_START and UHID_STOP events to user-space when the HID core starts/stops the device. This notifies user-space about driver readiness and data-I/O can start now. This directly forwards the callbacks from hid-core to user-space. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 8 +++++++- include/linux/uhid.h | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 0d011db30a46..2e7f3a023975 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -82,11 +82,17 @@ static int uhid_queue_event(struct uhid_device *uhid, __u32 event) static int uhid_hid_start(struct hid_device *hid) { - return 0; + struct uhid_device *uhid = hid->driver_data; + + return uhid_queue_event(uhid, UHID_START); } static void uhid_hid_stop(struct hid_device *hid) { + struct uhid_device *uhid = hid->driver_data; + + hid->claimed = 0; + uhid_queue_event(uhid, UHID_STOP); } static int uhid_hid_open(struct hid_device *hid) diff --git a/include/linux/uhid.h b/include/linux/uhid.h index 6eb42bea86a2..f8ce6f7571d8 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -25,6 +25,8 @@ enum uhid_event_type { UHID_CREATE, UHID_DESTROY, + UHID_START, + UHID_STOP, UHID_INPUT, }; -- cgit v1.2.3 From e7191474a5459e6ba7039fadca6a32f3a5731909 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:22 +0200 Subject: HID: uhid: forward open/close events to user-space HID core notifies us with *_open/*_close callbacks when there is an actual user of our device. We forward these to user-space so they can react on this. This allows user-space to skip I/O unless they receive an OPEN event. When they receive a CLOSE event they can stop I/O again to save energy. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 7 ++++++- include/linux/uhid.h | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 2e7f3a023975..0226ba3f5307 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -97,11 +97,16 @@ static void uhid_hid_stop(struct hid_device *hid) static int uhid_hid_open(struct hid_device *hid) { - return 0; + struct uhid_device *uhid = hid->driver_data; + + return uhid_queue_event(uhid, UHID_OPEN); } static void uhid_hid_close(struct hid_device *hid) { + struct uhid_device *uhid = hid->driver_data; + + uhid_queue_event(uhid, UHID_CLOSE); } static int uhid_hid_input(struct input_dev *input, unsigned int type, diff --git a/include/linux/uhid.h b/include/linux/uhid.h index f8ce6f7571d8..351650b7a0f6 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -27,6 +27,8 @@ enum uhid_event_type { UHID_DESTROY, UHID_START, UHID_STOP, + UHID_OPEN, + UHID_CLOSE, UHID_INPUT, }; -- cgit v1.2.3 From f80e13601c51a836b2aac583b8a3b4327c0c27ce Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:23 +0200 Subject: HID: uhid: forward output request to user-space If the hid-driver wants to send standardized data to the device it uses a linux input_event. We forward this to the user-space transport-level driver so they can perform the requested action on the device. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 18 ++++++++++++++++++ include/linux/uhid.h | 8 ++++++++ 2 files changed, 26 insertions(+) (limited to 'include') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 0226ba3f5307..4dd693e1c8b8 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -112,6 +112,24 @@ static void uhid_hid_close(struct hid_device *hid) 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; } diff --git a/include/linux/uhid.h b/include/linux/uhid.h index 351650b7a0f6..3fa484921010 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -29,6 +29,7 @@ enum uhid_event_type { UHID_STOP, UHID_OPEN, UHID_CLOSE, + UHID_OUTPUT_EV, UHID_INPUT, }; @@ -53,12 +54,19 @@ struct uhid_input_req { __u16 size; } __attribute__((__packed__)); +struct uhid_output_ev_req { + __u16 type; + __u16 code; + __s32 value; +} __attribute__((__packed__)); + struct uhid_event { __u32 type; union { struct uhid_create_req create; struct uhid_input_req input; + struct uhid_output_ev_req output_ev; } u; } __attribute__((__packed__)); -- cgit v1.2.3 From 3b3baa82e4306b5160692643fab2fa322ceb94f9 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:24 +0200 Subject: HID: uhid: forward raw output reports to user-space Some drivers that use non-standard HID features require raw output reports sent to the device. We now forward these requests directly to user-space so the transport-level driver can correctly send it to the device or handle it correspondingly. There is no way to signal back whether the transmission was successful, moreover, there might be lots of messages coming out from the driver flushing the output-queue. However, there is currently no driver that causes this so we are safe. If some drivers need to transmit lots of data this way, we need a method to synchronize this and can implement another UHID_OUTPUT_SYNC event. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 34 +++++++++++++++++++++++++++++++++- include/linux/uhid.h | 14 ++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 4dd693e1c8b8..421c492dc824 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -149,7 +149,39 @@ static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, unsigned char report_type) { - return 0; + struct uhid_device *uhid = hid->driver_data; + __u8 rtype; + unsigned long flags; + struct uhid_event *ev; + + switch (report_type) { + case HID_FEATURE_REPORT: + rtype = UHID_FEATURE_REPORT; + break; + case HID_OUTPUT_REPORT: + rtype = UHID_OUTPUT_REPORT; + break; + default: + return -EINVAL; + } + + if (count < 1 || count > UHID_DATA_MAX) + return -EINVAL; + + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) + return -ENOMEM; + + ev->type = UHID_OUTPUT; + ev->u.output.size = count; + ev->u.output.rtype = rtype; + memcpy(ev->u.output.data, buf, count); + + spin_lock_irqsave(&uhid->qlock, flags); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + return count; } static struct hid_ll_driver uhid_hid_driver = { diff --git a/include/linux/uhid.h b/include/linux/uhid.h index 3fa484921010..2c972550a624 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -29,6 +29,7 @@ enum uhid_event_type { UHID_STOP, UHID_OPEN, UHID_CLOSE, + UHID_OUTPUT, UHID_OUTPUT_EV, UHID_INPUT, }; @@ -49,11 +50,23 @@ struct uhid_create_req { #define UHID_DATA_MAX 4096 +enum uhid_report_type { + UHID_FEATURE_REPORT, + UHID_OUTPUT_REPORT, + UHID_INPUT_REPORT, +}; + struct uhid_input_req { __u8 data[UHID_DATA_MAX]; __u16 size; } __attribute__((__packed__)); +struct uhid_output_req { + __u8 data[UHID_DATA_MAX]; + __u16 size; + __u8 rtype; +} __attribute__((__packed__)); + struct uhid_output_ev_req { __u16 type; __u16 code; @@ -66,6 +79,7 @@ struct uhid_event { union { struct uhid_create_req create; struct uhid_input_req input; + struct uhid_output_req output; struct uhid_output_ev_req output_ev; } u; } __attribute__((__packed__)); -- cgit v1.2.3 From fcfcf0deb89ece6eb9ae23768fec1bc1718f9b7f Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:25 +0200 Subject: HID: uhid: implement feature requests HID standard allows sending a feature request to the device which is answered by an HID report. uhid implements this by sending a UHID_FEATURE event to user-space which then must answer with UHID_FEATURE_ANSWER. If it doesn't do this in a timely manner, the request is discarded silently. We serialize the feature requests, that is, there is always only a single active feature-request sent to user-space, other requests have to wait. HIDP and USB-HID do it the same way. Because we discard feature-requests silently, we must make sure to match a response to the corresponding request. We use sequence-IDs for this so user-space must copy the ID from the request into the answer. Feature-answers are ignored if they do not contain the same ID as the currently pending feature request. Internally, we must make sure that feature-requests are synchronized with UHID_DESTROY and close() events. We must not dead-lock when closing the HID device, either, so we have to use separate locks. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++- include/linux/uhid.h | 17 ++++++++ 2 files changed, 136 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 421c492dc824..ea560bfa033d 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -42,6 +42,12 @@ struct uhid_device { __u8 head; __u8 tail; struct uhid_event *outq[UHID_BUFSIZE]; + + struct mutex report_lock; + wait_queue_head_t report_wait; + atomic_t report_done; + atomic_t report_id; + struct uhid_event report_buf; }; static struct miscdevice uhid_misc; @@ -143,7 +149,84 @@ static int uhid_hid_parse(struct hid_device *hid) static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, __u8 *buf, size_t count, unsigned char rtype) { - return 0; + struct uhid_device *uhid = hid->driver_data; + __u8 report_type; + struct uhid_event *ev; + unsigned long flags; + int ret; + size_t len; + struct uhid_feature_answer_req *req; + + if (!uhid->running) + return -EIO; + + switch (rtype) { + case HID_FEATURE_REPORT: + report_type = UHID_FEATURE_REPORT; + break; + case HID_OUTPUT_REPORT: + report_type = UHID_OUTPUT_REPORT; + break; + case HID_INPUT_REPORT: + report_type = UHID_INPUT_REPORT; + break; + default: + return -EINVAL; + } + + ret = mutex_lock_interruptible(&uhid->report_lock); + if (ret) + return ret; + + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) { + ret = -ENOMEM; + goto unlock; + } + + spin_lock_irqsave(&uhid->qlock, flags); + ev->type = UHID_FEATURE; + ev->u.feature.id = atomic_inc_return(&uhid->report_id); + ev->u.feature.rnum = rnum; + ev->u.feature.rtype = report_type; + + atomic_set(&uhid->report_done, 0); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + ret = wait_event_interruptible_timeout(uhid->report_wait, + atomic_read(&uhid->report_done), 5 * HZ); + + /* + * Make sure "uhid->running" is cleared on shutdown before + * "uhid->report_done" is set. + */ + smp_rmb(); + if (!ret || !uhid->running) { + ret = -EIO; + } else if (ret < 0) { + ret = -ERESTARTSYS; + } else { + spin_lock_irqsave(&uhid->qlock, flags); + req = &uhid->report_buf.u.feature_answer; + + if (req->err) { + ret = -EIO; + } else { + ret = 0; + len = min(count, + min_t(size_t, req->size, UHID_DATA_MAX)); + memcpy(buf, req->data, len); + } + + spin_unlock_irqrestore(&uhid->qlock, flags); + } + + atomic_set(&uhid->report_done, 1); + +unlock: + mutex_unlock(&uhid->report_lock); + return ret ? ret : len; } static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, @@ -265,7 +348,11 @@ static int uhid_dev_destroy(struct uhid_device *uhid) if (!uhid->running) return -EINVAL; + /* clear "running" before setting "report_done" */ uhid->running = false; + smp_wmb(); + atomic_set(&uhid->report_done, 1); + wake_up_interruptible(&uhid->report_wait); hid_destroy_device(uhid->hid); kfree(uhid->rd_data); @@ -284,6 +371,31 @@ static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev) return 0; } +static int uhid_dev_feature_answer(struct uhid_device *uhid, + struct uhid_event *ev) +{ + unsigned long flags; + + if (!uhid->running) + return -EINVAL; + + spin_lock_irqsave(&uhid->qlock, flags); + + /* id for old report; drop it silently */ + if (atomic_read(&uhid->report_id) != ev->u.feature_answer.id) + goto unlock; + if (atomic_read(&uhid->report_done)) + goto unlock; + + memcpy(&uhid->report_buf, ev, sizeof(*ev)); + atomic_set(&uhid->report_done, 1); + wake_up_interruptible(&uhid->report_wait); + +unlock: + spin_unlock_irqrestore(&uhid->qlock, flags); + return 0; +} + static int uhid_char_open(struct inode *inode, struct file *file) { struct uhid_device *uhid; @@ -293,9 +405,12 @@ static int uhid_char_open(struct inode *inode, struct file *file) return -ENOMEM; mutex_init(&uhid->devlock); + mutex_init(&uhid->report_lock); spin_lock_init(&uhid->qlock); init_waitqueue_head(&uhid->waitq); + init_waitqueue_head(&uhid->report_wait); uhid->running = false; + atomic_set(&uhid->report_done, 1); file->private_data = uhid; nonseekable_open(inode, file); @@ -398,6 +513,9 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer, case UHID_INPUT: ret = uhid_dev_input(uhid, &uhid->input_buf); break; + case UHID_FEATURE_ANSWER: + ret = uhid_dev_feature_answer(uhid, &uhid->input_buf); + break; default: ret = -EOPNOTSUPP; } diff --git a/include/linux/uhid.h b/include/linux/uhid.h index 2c972550a624..9c6974f16966 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -32,6 +32,8 @@ enum uhid_event_type { UHID_OUTPUT, UHID_OUTPUT_EV, UHID_INPUT, + UHID_FEATURE, + UHID_FEATURE_ANSWER, }; struct uhid_create_req { @@ -73,6 +75,19 @@ struct uhid_output_ev_req { __s32 value; } __attribute__((__packed__)); +struct uhid_feature_req { + __u32 id; + __u8 rnum; + __u8 rtype; +} __attribute__((__packed__)); + +struct uhid_feature_answer_req { + __u32 id; + __u16 err; + __u16 size; + __u8 data[UHID_DATA_MAX]; +}; + struct uhid_event { __u32 type; @@ -81,6 +96,8 @@ struct uhid_event { struct uhid_input_req input; struct uhid_output_req output; struct uhid_output_ev_req output_ev; + struct uhid_feature_req feature; + struct uhid_feature_answer_req feature_answer; } u; } __attribute__((__packed__)); -- cgit v1.2.3 From 9900b4b48b095895cf962054e45aafa49ef70f74 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 15 Jun 2012 15:07:02 -0400 Subject: KVM: use KVM_CAP_IRQ_ROUTING to protect the routing related code The KVM code sometimes uses CONFIG_HAVE_KVM_IRQCHIP to protect code that is related to IRQ routing, which not all in-kernel irqchips may support. Use KVM_CAP_IRQ_ROUTING instead. Signed-off-by: Marc Zyngier Signed-off-by: Christoffer Dall Signed-off-by: Avi Kivity --- include/linux/kvm_host.h | 2 +- virt/kvm/kvm_main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 27ac8a4767fa..c7f77876c9b3 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -802,7 +802,7 @@ static inline int mmu_notifier_retry(struct kvm_vcpu *vcpu, unsigned long mmu_se } #endif -#ifdef CONFIG_HAVE_KVM_IRQCHIP +#ifdef KVM_CAP_IRQ_ROUTING #define KVM_MAX_IRQ_ROUTES 1024 diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 02cb440f802d..636bd08bb399 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -2225,7 +2225,7 @@ static long kvm_dev_ioctl_check_extension_generic(long arg) case KVM_CAP_SIGNAL_MSI: #endif return 1; -#ifdef CONFIG_HAVE_KVM_IRQCHIP +#ifdef KVM_CAP_IRQ_ROUTING case KVM_CAP_IRQ_ROUTING: return KVM_MAX_IRQ_ROUTES; #endif -- cgit v1.2.3 From a1e4ccb990447df0fe83d164d9a7bc2e6c4b7db7 Mon Sep 17 00:00:00 2001 From: Christoffer Dall Date: Fri, 15 Jun 2012 15:07:13 -0400 Subject: KVM: Introduce __KVM_HAVE_IRQ_LINE This is a preparatory patch for the KVM/ARM implementation. KVM/ARM will use the KVM_IRQ_LINE ioctl, which is currently conditional on __KVM_HAVE_IOAPIC, but ARM obviously doesn't have any IOAPIC support and we need a separate define. Signed-off-by: Christoffer Dall Signed-off-by: Avi Kivity --- arch/ia64/include/asm/kvm.h | 1 + arch/x86/include/asm/kvm.h | 1 + include/trace/events/kvm.h | 4 +++- 3 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/ia64/include/asm/kvm.h b/arch/ia64/include/asm/kvm.h index b9f82c84f093..ec6c6b301238 100644 --- a/arch/ia64/include/asm/kvm.h +++ b/arch/ia64/include/asm/kvm.h @@ -26,6 +26,7 @@ /* Select x86 specific features in */ #define __KVM_HAVE_IOAPIC +#define __KVM_HAVE_IRQ_LINE #define __KVM_HAVE_DEVICE_ASSIGNMENT /* Architectural interrupt line count. */ diff --git a/arch/x86/include/asm/kvm.h b/arch/x86/include/asm/kvm.h index e7d1c194d272..246617efd67f 100644 --- a/arch/x86/include/asm/kvm.h +++ b/arch/x86/include/asm/kvm.h @@ -12,6 +12,7 @@ /* Select x86 specific features in */ #define __KVM_HAVE_PIT #define __KVM_HAVE_IOAPIC +#define __KVM_HAVE_IRQ_LINE #define __KVM_HAVE_DEVICE_ASSIGNMENT #define __KVM_HAVE_MSI #define __KVM_HAVE_USER_NMI diff --git a/include/trace/events/kvm.h b/include/trace/events/kvm.h index 3df5925fe641..7ef9e759f499 100644 --- a/include/trace/events/kvm.h +++ b/include/trace/events/kvm.h @@ -37,7 +37,7 @@ TRACE_EVENT(kvm_userspace_exit, __entry->errno < 0 ? -__entry->errno : __entry->reason) ); -#if defined(__KVM_HAVE_IOAPIC) +#if defined(__KVM_HAVE_IRQ_LINE) TRACE_EVENT(kvm_set_irq, TP_PROTO(unsigned int gsi, int level, int irq_source_id), TP_ARGS(gsi, level, irq_source_id), @@ -57,7 +57,9 @@ TRACE_EVENT(kvm_set_irq, TP_printk("gsi %u level %d source %d", __entry->gsi, __entry->level, __entry->irq_source_id) ); +#endif +#if defined(__KVM_HAVE_IOAPIC) #define kvm_deliver_mode \ {0x0, "Fixed"}, \ {0x1, "LowPrio"}, \ -- cgit v1.2.3 From fec1868e23099023bc545e199b78d99840b1abc9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 18 Jun 2012 15:38:22 -0700 Subject: USB: properly pad out usb_device_id.driver_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On some systems, struct usb_device_id doesn't align properly due to the recent changes in it. So pad out the driver_info field to align on a boundry that systems can handle it. Reported-by: Geert Uytterhoeven Acked-by: Geert Uytterhoeven Cc: Bjørn Mork Signed-off-by: Greg Kroah-Hartman --- include/linux/mod_devicetable.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 7771d453e5f3..6955045199b0 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -122,7 +122,8 @@ struct usb_device_id { __u8 bInterfaceNumber; /* not matched against */ - kernel_ulong_t driver_info; + kernel_ulong_t driver_info + __attribute__((aligned(sizeof(kernel_ulong_t)))); }; /* Some useful macros to use to create struct usb_device_id */ -- cgit v1.2.3 From 86b0905516460b87542686248690337e1d703544 Mon Sep 17 00:00:00 2001 From: Albert Wang Date: Thu, 7 Jun 2012 05:42:24 -0300 Subject: [media] videobuf2: correct the #ifndef text mistake in videobuf2-dma-contig.h It should be a mistake due to copy & paste in header file Correct it in videobuf2-dma-config.h for avoiding duplicate include it Change-Id: I1f71fcec2889c033c7db380c58d9a1369c5afb35 Signed-off-by: Albert Wang Signed-off-by: Mauro Carvalho Chehab --- include/media/videobuf2-dma-contig.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/media/videobuf2-dma-contig.h b/include/media/videobuf2-dma-contig.h index 19ae1e350567..8197f87d6c61 100644 --- a/include/media/videobuf2-dma-contig.h +++ b/include/media/videobuf2-dma-contig.h @@ -1,5 +1,5 @@ /* - * videobuf2-dma-coherent.h - DMA coherent memory allocator for videobuf2 + * videobuf2-dma-contig.h - DMA contig memory allocator for videobuf2 * * Copyright (C) 2010 Samsung Electronics * @@ -10,8 +10,8 @@ * the Free Software Foundation. */ -#ifndef _MEDIA_VIDEOBUF2_DMA_COHERENT_H -#define _MEDIA_VIDEOBUF2_DMA_COHERENT_H +#ifndef _MEDIA_VIDEOBUF2_DMA_CONTIG_H +#define _MEDIA_VIDEOBUF2_DMA_CONTIG_H #include #include -- cgit v1.2.3 From 6aeec0eacd3c6ac259e38d95a9398ac258470b4c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 27 May 2012 06:54:29 -0300 Subject: [media] videodev2.h: add new hwseek capability bits Tell the application whether the hardware seek is bounded and/or wraps around. Signed-off-by: Hans Verkuil Acked-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- include/linux/videodev2.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 370d11106c11..2339678de092 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -2039,6 +2039,8 @@ struct v4l2_modulator { /* Flags for the 'capability' field */ #define V4L2_TUNER_CAP_LOW 0x0001 #define V4L2_TUNER_CAP_NORM 0x0002 +#define V4L2_TUNER_CAP_HWSEEK_BOUNDED 0x0004 +#define V4L2_TUNER_CAP_HWSEEK_WRAP 0x0008 #define V4L2_TUNER_CAP_STEREO 0x0010 #define V4L2_TUNER_CAP_LANG2 0x0020 #define V4L2_TUNER_CAP_SAP 0x0020 -- cgit v1.2.3 From 23f2d735a932c7833d2d00da5e3ecdf4a6836210 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 18 Jun 2012 18:33:48 +0200 Subject: iio: Add helper function for initializing triggered buffers Add a helper function for executing the common tasks which are usually involved in setting up a simple software ringbuffer. It will allocate the buffer, allocate the pollfunc and register the buffer. Signed-off-by: Lars-Peter Clausen Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/Kconfig | 7 ++ drivers/iio/Makefile | 1 + drivers/iio/industrialio-triggered-buffer.c | 110 ++++++++++++++++++++++++++++ include/linux/iio/triggered_buffer.h | 15 ++++ 4 files changed, 133 insertions(+) create mode 100644 drivers/iio/industrialio-triggered-buffer.c create mode 100644 include/linux/iio/triggered_buffer.h (limited to 'include') diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 103349f2b3b5..612073f6c540 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -30,6 +30,13 @@ config IIO_KFIFO_BUF no buffer events so it is up to userspace to work out how often to read from the buffer. +config IIO_TRIGGERED_BUFFER + tristate + select IIO_TRIGGER + select IIO_KFIFO_BUF + help + Provides helper functions for setting up triggered buffers. + endif # IIO_BUFFER config IIO_TRIGGER diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index c38fa2a40af2..34309abb7979 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -7,6 +7,7 @@ industrialio-y := industrialio-core.o industrialio-event.o inkern.o industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o +obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o obj-y += adc/ diff --git a/drivers/iio/industrialio-triggered-buffer.c b/drivers/iio/industrialio-triggered-buffer.c new file mode 100644 index 000000000000..46c619b0d8c5 --- /dev/null +++ b/drivers/iio/industrialio-triggered-buffer.c @@ -0,0 +1,110 @@ + /* + * Copyright (c) 2012 Analog Devices, Inc. + * Author: Lars-Peter Clausen + * + * 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 + +static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = { + .preenable = &iio_sw_buffer_preenable, + .postenable = &iio_triggered_buffer_postenable, + .predisable = &iio_triggered_buffer_predisable, +}; + +/** + * iio_triggered_buffer_setup() - Setup triggered buffer and pollfunc + * @indio_dev: IIO device structure + * @pollfunc_bh: Function which will be used as pollfunc bottom half + * @pollfunc_th: Function which will be used as pollfunc top half + * @setup_ops: Buffer setup functions to use for this device. + * If NULL the default setup functions for triggered + * buffers will be used. + * + * This function combines some common tasks which will normally be performed + * when setting up a triggered buffer. It will allocate the buffer and the + * pollfunc, as well as register the buffer with the IIO core. + * + * Before calling this function the indio_dev structure should already be + * completely initialized, but not yet registered. In practice this means that + * this function should be called right before iio_device_register(). + * + * To free the resources allocated by this function call + * iio_triggered_buffer_cleanup(). + */ +int iio_triggered_buffer_setup(struct iio_dev *indio_dev, + irqreturn_t (*pollfunc_bh)(int irq, void *p), + irqreturn_t (*pollfunc_th)(int irq, void *p), + const struct iio_buffer_setup_ops *setup_ops) +{ + int ret; + + indio_dev->buffer = iio_kfifo_allocate(indio_dev); + if (!indio_dev->buffer) { + ret = -ENOMEM; + goto error_ret; + } + + indio_dev->pollfunc = iio_alloc_pollfunc(pollfunc_bh, + pollfunc_th, + IRQF_ONESHOT, + indio_dev, + "%s_consumer%d", + indio_dev->name, + indio_dev->id); + if (indio_dev->pollfunc == NULL) { + ret = -ENOMEM; + goto error_kfifo_free; + } + + /* Ring buffer functions - here trigger setup related */ + if (setup_ops) + indio_dev->setup_ops = setup_ops; + else + indio_dev->setup_ops = &iio_triggered_buffer_setup_ops; + + /* Flag that polled ring buffering is possible */ + indio_dev->modes |= INDIO_BUFFER_TRIGGERED; + + ret = iio_buffer_register(indio_dev, + indio_dev->channels, + indio_dev->num_channels); + if (ret) + goto error_dealloc_pollfunc; + + return 0; + +error_dealloc_pollfunc: + iio_dealloc_pollfunc(indio_dev->pollfunc); +error_kfifo_free: + iio_kfifo_free(indio_dev->buffer); +error_ret: + return ret; +} +EXPORT_SYMBOL(iio_triggered_buffer_setup); + +/** + * iio_triggered_buffer_cleanup() - Free resources allocated by iio_triggered_buffer_setup() + * @indio_dev: IIO device structure + */ +void iio_triggered_buffer_cleanup(struct iio_dev *indio_dev) +{ + iio_buffer_unregister(indio_dev); + iio_dealloc_pollfunc(indio_dev->pollfunc); + iio_kfifo_free(indio_dev->buffer); +} +EXPORT_SYMBOL(iio_triggered_buffer_cleanup); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("IIO helper functions for setting up triggered buffers"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/iio/triggered_buffer.h b/include/linux/iio/triggered_buffer.h new file mode 100644 index 000000000000..c378ebec605e --- /dev/null +++ b/include/linux/iio/triggered_buffer.h @@ -0,0 +1,15 @@ +#ifndef _LINUX_IIO_TRIGGERED_BUFFER_H_ +#define _LINUX_IIO_TRIGGERED_BUFFER_H_ + +#include + +struct iio_dev; +struct iio_buffer_setup_ops; + +int iio_triggered_buffer_setup(struct iio_dev *indio_dev, + irqreturn_t (*pollfunc_bh)(int irq, void *p), + irqreturn_t (*pollfunc_th)(int irq, void *p), + const struct iio_buffer_setup_ops *setup_ops); +void iio_triggered_buffer_cleanup(struct iio_dev *indio_dev); + +#endif -- cgit v1.2.3 From 9cc113bc84e683435c110268712d25aea05b8198 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Mon, 18 Jun 2012 20:33:01 +0200 Subject: iio: typo in iio_chan_spec.ext_info comment Signed-off-by: Peter Meerwald Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- include/linux/iio/iio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index ae0cad908182..2afbb6f01afc 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -214,7 +214,7 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, * @event_mask: What events can this channel produce. * @ext_info: Array of extended info attributes for this channel. * The array is NULL terminated, the last element should - * have it's name field set to NULL. + * have its name field set to NULL. * @extend_name: Allows labeling of channel attributes with an * informative name. Note this has no effect codes etc, * unlike modifiers. -- cgit v1.2.3 From b90406f1e5f4cb673e331ce6d435f4bdbf2136a2 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Mon, 18 Jun 2012 20:33:02 +0200 Subject: iio: correct documentation for IIO_CONST_ATTR_SAMP_FREQ_AVAIL, match name of #define Signed-off-by: Peter Meerwald Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- include/linux/iio/sysfs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/iio/sysfs.h b/include/linux/iio/sysfs.h index bfedb73b850e..b7a934b9431b 100644 --- a/include/linux/iio/sysfs.h +++ b/include/linux/iio/sysfs.h @@ -97,7 +97,7 @@ struct iio_const_attr { #define IIO_DEV_ATTR_SAMP_FREQ_AVAIL(_show) \ IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO, _show, NULL, 0) /** - * IIO_CONST_ATTR_AVAIL_SAMP_FREQ - list available sampling frequencies + * IIO_CONST_ATTR_SAMP_FREQ_AVAIL - list available sampling frequencies * @_string: frequency string for the attribute * * Constant version -- cgit v1.2.3 From 19ea4752fc1f45c778ae66443c1023aced14ba05 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Mon, 18 Jun 2012 20:33:03 +0200 Subject: iio: remove extra ; after function definition Signed-off-by: Peter Meerwald Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- include/linux/iio/buffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h index fb0fe46fd659..ad4fb1af0f7d 100644 --- a/include/linux/iio/buffer.h +++ b/include/linux/iio/buffer.h @@ -184,7 +184,7 @@ static inline int iio_buffer_register(struct iio_dev *indio_dev, } static inline void iio_buffer_unregister(struct iio_dev *indio_dev) -{}; +{} #endif /* CONFIG_IIO_BUFFER */ -- cgit v1.2.3 From 3824c47714f28091f74ca2505146514b4da1f390 Mon Sep 17 00:00:00 2001 From: Ramakrishna Pallala Date: Sun, 6 May 2012 18:16:44 +0530 Subject: power_supply: Add constant charge_current and charge_voltage properties Constant Charge Current(CC) is charging parameter which limit the maximum current which can be pumped into the battery during charge cycle. Constant Charge Voltage(CV) is also charging parameter which limit the maximum voltage that battery can reach during charge cycle. It is very common practice that at low or high temperatures we do not charge the batteries upto it's fullest charge voltage to avoid battery and user safety issues. These sysfs properties will be useful for debug and to implement certain user space policies like "Charging limited due to OverTemp". Signed-off-by: Ramakrishna Pallala Signed-off-by: Anton Vorontsov --- Documentation/power/power_supply_class.txt | 4 ++++ drivers/power/power_supply_sysfs.c | 2 ++ include/linux/power_supply.h | 4 ++++ 3 files changed, 10 insertions(+) (limited to 'include') diff --git a/Documentation/power/power_supply_class.txt b/Documentation/power/power_supply_class.txt index 211831d4095f..c0f62ae2b5e0 100644 --- a/Documentation/power/power_supply_class.txt +++ b/Documentation/power/power_supply_class.txt @@ -112,6 +112,10 @@ CHARGE_COUNTER - the current charge counter (in µAh). This could easily be negative; there is no empty or full value. It is only useful for relative, time-based measurements. +CONSTANT_CHARGE_CURRENT - constant charge current programmed by charger. + +CONSTANT_CHARGE_VOLTAGE - constant charge voltage programmed by charger. + ENERGY_FULL, ENERGY_EMPTY - same as above but for energy. CAPACITY - capacity in percents. diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 4150747f9186..58846d929b34 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -159,6 +159,8 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(charge_now), POWER_SUPPLY_ATTR(charge_avg), POWER_SUPPLY_ATTR(charge_counter), + POWER_SUPPLY_ATTR(constant_charge_current), + POWER_SUPPLY_ATTR(constant_charge_voltage), POWER_SUPPLY_ATTR(energy_full_design), POWER_SUPPLY_ATTR(energy_empty_design), POWER_SUPPLY_ATTR(energy_full), diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 59ed2dd9dba9..53f177db6ac9 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -109,6 +109,8 @@ enum power_supply_property { POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_AVG, POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN, POWER_SUPPLY_PROP_ENERGY_FULL, @@ -239,6 +241,7 @@ static inline bool power_supply_is_amp_property(enum power_supply_property psp) case POWER_SUPPLY_PROP_CHARGE_NOW: case POWER_SUPPLY_PROP_CHARGE_AVG: case POWER_SUPPLY_PROP_CHARGE_COUNTER: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: case POWER_SUPPLY_PROP_CURRENT_MAX: case POWER_SUPPLY_PROP_CURRENT_NOW: case POWER_SUPPLY_PROP_CURRENT_AVG: @@ -266,6 +269,7 @@ static inline bool power_supply_is_watt_property(enum power_supply_property psp) case POWER_SUPPLY_PROP_VOLTAGE_NOW: case POWER_SUPPLY_PROP_VOLTAGE_AVG: case POWER_SUPPLY_PROP_VOLTAGE_OCV: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: case POWER_SUPPLY_PROP_POWER_NOW: return 1; default: -- cgit v1.2.3 From 210d4bc8a3128e3e61ac3bf4657114f8e6450e2a Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Wed, 9 May 2012 07:40:40 +1000 Subject: twl4030_charger: Add backup-battery charging This allows a voltage and current (bb_uvolts and bb_uamps) to be specified in the platform_data, and charging of the backup battery will be enabled with those specification. As it is not possible to monitor the backup battery at all there is no new device created to represent it. Signed-off-by: NeilBrown Signed-off-by: Anton Vorontsov --- drivers/power/twl4030_charger.c | 59 +++++++++++++++++++++++++++++++++++++++++ include/linux/i2c/twl.h | 2 ++ 2 files changed, 61 insertions(+) (limited to 'include') diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 3e6e99101106..0511610b235f 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -28,6 +28,7 @@ #define TWL4030_BCIVBUS 0x0c #define TWL4030_BCIMFSTS4 0x10 #define TWL4030_BCICTL1 0x23 +#define TWL4030_BB_CFG 0x12 #define TWL4030_BCIAUTOWEN BIT(5) #define TWL4030_CONFIG_DONE BIT(4) @@ -37,6 +38,17 @@ #define TWL4030_USBFASTMCHG BIT(2) #define TWL4030_STS_VBUS BIT(7) #define TWL4030_STS_USB_ID BIT(2) +#define TWL4030_BBCHEN BIT(4) +#define TWL4030_BBSEL_MASK 0b1100 +#define TWL4030_BBSEL_2V5 0b0000 +#define TWL4030_BBSEL_3V0 0b0100 +#define TWL4030_BBSEL_3V1 0b1000 +#define TWL4030_BBSEL_3V2 0b1100 +#define TWL4030_BBISEL_MASK 0b11 +#define TWL4030_BBISEL_25uA 0b00 +#define TWL4030_BBISEL_150uA 0b01 +#define TWL4030_BBISEL_500uA 0b10 +#define TWL4030_BBISEL_1000uA 0b11 /* BCI interrupts */ #define TWL4030_WOVF BIT(0) /* Watchdog overflow */ @@ -201,6 +213,49 @@ static int twl4030_charger_enable_ac(bool enable) return ret; } +/* + * Enable/Disable charging of Backup Battery. + */ +static int twl4030_charger_enable_backup(int uvolt, int uamp) +{ + int ret; + u8 flags; + + if (uvolt < 2500000 || + uamp < 25) { + /* disable charging of backup battery */ + ret = twl4030_clear_set(TWL4030_MODULE_PM_RECEIVER, + TWL4030_BBCHEN, 0, TWL4030_BB_CFG); + return ret; + } + + flags = TWL4030_BBCHEN; + if (uvolt >= 3200000) + flags |= TWL4030_BBSEL_3V2; + else if (uvolt >= 3100000) + flags |= TWL4030_BBSEL_3V1; + else if (uvolt >= 3000000) + flags |= TWL4030_BBSEL_3V0; + else + flags |= TWL4030_BBSEL_2V5; + + if (uamp >= 1000) + flags |= TWL4030_BBISEL_1000uA; + else if (uamp >= 500) + flags |= TWL4030_BBISEL_500uA; + else if (uamp >= 150) + flags |= TWL4030_BBISEL_150uA; + else + flags |= TWL4030_BBISEL_25uA; + + ret = twl4030_clear_set(TWL4030_MODULE_PM_RECEIVER, + TWL4030_BBSEL_MASK | TWL4030_BBISEL_MASK, + flags, + TWL4030_BB_CFG); + + return ret; +} + /* * TWL4030 CHG_PRES (AC charger presence) events */ @@ -424,6 +479,7 @@ static enum power_supply_property twl4030_charger_props[] = { static int __init twl4030_bci_probe(struct platform_device *pdev) { struct twl4030_bci *bci; + struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data; int ret; u32 reg; @@ -503,6 +559,8 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) twl4030_charger_enable_ac(true); twl4030_charger_enable_usb(bci, true); + twl4030_charger_enable_backup(pdata->bb_uvolt, + pdata->bb_uamp); return 0; @@ -531,6 +589,7 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) twl4030_charger_enable_ac(false); twl4030_charger_enable_usb(bci, false); + twl4030_charger_enable_backup(0, 0); /* mask interrupts */ twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 3993477103a5..8eec4403b170 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -555,6 +555,8 @@ struct twl4030_clock_init_data { struct twl4030_bci_platform_data { int *battery_tmp_tbl; unsigned int tblsize; + int bb_uvolt; /* voltage to charge backup battery */ + int bb_uamp; /* current for backup battery charging */ }; /* TWL4030_GPIO_MAX (18) GPIOs, with interrupts */ -- cgit v1.2.3 From 33eb3311f3ad4a14f2e55d36fdb0d3ec54712231 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 19 Jun 2012 16:20:24 -0300 Subject: sound: Remove unused include/linux/ac97_codec.h header This file has been superseded by include/sound/ac97_codec.h, and has currently no users. Cc: Ralf Baechle Cc: Jaroslav Kysela Cc: Clemens Ladisch Signed-off-by: Ezequiel Garcia Signed-off-by: Takashi Iwai --- include/linux/ac97_codec.h | 362 --------------------------------------------- 1 file changed, 362 deletions(-) delete mode 100644 include/linux/ac97_codec.h (limited to 'include') diff --git a/include/linux/ac97_codec.h b/include/linux/ac97_codec.h deleted file mode 100644 index 0260c3e79fdd..000000000000 --- a/include/linux/ac97_codec.h +++ /dev/null @@ -1,362 +0,0 @@ -#ifndef _AC97_CODEC_H_ -#define _AC97_CODEC_H_ - -#include -#include - -/* AC97 1.0 */ -#define AC97_RESET 0x0000 // -#define AC97_MASTER_VOL_STEREO 0x0002 // Line Out -#define AC97_HEADPHONE_VOL 0x0004 // -#define AC97_MASTER_VOL_MONO 0x0006 // TAD Output -#define AC97_MASTER_TONE 0x0008 // -#define AC97_PCBEEP_VOL 0x000a // none -#define AC97_PHONE_VOL 0x000c // TAD Input (mono) -#define AC97_MIC_VOL 0x000e // MIC Input (mono) -#define AC97_LINEIN_VOL 0x0010 // Line Input (stereo) -#define AC97_CD_VOL 0x0012 // CD Input (stereo) -#define AC97_VIDEO_VOL 0x0014 // none -#define AC97_AUX_VOL 0x0016 // Aux Input (stereo) -#define AC97_PCMOUT_VOL 0x0018 // Wave Output (stereo) -#define AC97_RECORD_SELECT 0x001a // -#define AC97_RECORD_GAIN 0x001c -#define AC97_RECORD_GAIN_MIC 0x001e -#define AC97_GENERAL_PURPOSE 0x0020 -#define AC97_3D_CONTROL 0x0022 -#define AC97_MODEM_RATE 0x0024 -#define AC97_POWER_CONTROL 0x0026 - -/* AC'97 2.0 */ -#define AC97_EXTENDED_ID 0x0028 /* Extended Audio ID */ -#define AC97_EXTENDED_STATUS 0x002A /* Extended Audio Status */ -#define AC97_PCM_FRONT_DAC_RATE 0x002C /* PCM Front DAC Rate */ -#define AC97_PCM_SURR_DAC_RATE 0x002E /* PCM Surround DAC Rate */ -#define AC97_PCM_LFE_DAC_RATE 0x0030 /* PCM LFE DAC Rate */ -#define AC97_PCM_LR_ADC_RATE 0x0032 /* PCM LR ADC Rate */ -#define AC97_PCM_MIC_ADC_RATE 0x0034 /* PCM MIC ADC Rate */ -#define AC97_CENTER_LFE_MASTER 0x0036 /* Center + LFE Master Volume */ -#define AC97_SURROUND_MASTER 0x0038 /* Surround (Rear) Master Volume */ -#define AC97_RESERVED_3A 0x003A /* Reserved in AC '97 < 2.2 */ - -/* AC'97 2.2 */ -#define AC97_SPDIF_CONTROL 0x003A /* S/PDIF Control */ - -/* range 0x3c-0x58 - MODEM */ -#define AC97_EXTENDED_MODEM_ID 0x003C -#define AC97_EXTEND_MODEM_STAT 0x003E -#define AC97_LINE1_RATE 0x0040 -#define AC97_LINE2_RATE 0x0042 -#define AC97_HANDSET_RATE 0x0044 -#define AC97_LINE1_LEVEL 0x0046 -#define AC97_LINE2_LEVEL 0x0048 -#define AC97_HANDSET_LEVEL 0x004A -#define AC97_GPIO_CONFIG 0x004C -#define AC97_GPIO_POLARITY 0x004E -#define AC97_GPIO_STICKY 0x0050 -#define AC97_GPIO_WAKE_UP 0x0052 -#define AC97_GPIO_STATUS 0x0054 -#define AC97_MISC_MODEM_STAT 0x0056 -#define AC97_RESERVED_58 0x0058 - -/* registers 0x005a - 0x007a are vendor reserved */ - -#define AC97_VENDOR_ID1 0x007c -#define AC97_VENDOR_ID2 0x007e - -/* volume control bit defines */ -#define AC97_MUTE 0x8000 -#define AC97_MICBOOST 0x0040 -#define AC97_LEFTVOL 0x3f00 -#define AC97_RIGHTVOL 0x003f - -/* record mux defines */ -#define AC97_RECMUX_MIC 0x0000 -#define AC97_RECMUX_CD 0x0101 -#define AC97_RECMUX_VIDEO 0x0202 -#define AC97_RECMUX_AUX 0x0303 -#define AC97_RECMUX_LINE 0x0404 -#define AC97_RECMUX_STEREO_MIX 0x0505 -#define AC97_RECMUX_MONO_MIX 0x0606 -#define AC97_RECMUX_PHONE 0x0707 - -/* general purpose register bit defines */ -#define AC97_GP_LPBK 0x0080 /* Loopback mode */ -#define AC97_GP_MS 0x0100 /* Mic Select 0=Mic1, 1=Mic2 */ -#define AC97_GP_MIX 0x0200 /* Mono output select 0=Mix, 1=Mic */ -#define AC97_GP_RLBK 0x0400 /* Remote Loopback - Modem line codec */ -#define AC97_GP_LLBK 0x0800 /* Local Loopback - Modem Line codec */ -#define AC97_GP_LD 0x1000 /* Loudness 1=on */ -#define AC97_GP_3D 0x2000 /* 3D Enhancement 1=on */ -#define AC97_GP_ST 0x4000 /* Stereo Enhancement 1=on */ -#define AC97_GP_POP 0x8000 /* Pcm Out Path, 0=pre 3D, 1=post 3D */ - -/* extended audio status and control bit defines */ -#define AC97_EA_VRA 0x0001 /* Variable bit rate enable bit */ -#define AC97_EA_DRA 0x0002 /* Double-rate audio enable bit */ -#define AC97_EA_SPDIF 0x0004 /* S/PDIF Enable bit */ -#define AC97_EA_VRM 0x0008 /* Variable bit rate for MIC enable bit */ -#define AC97_EA_CDAC 0x0040 /* PCM Center DAC is ready (Read only) */ -#define AC97_EA_SDAC 0x0040 /* PCM Surround DACs are ready (Read only) */ -#define AC97_EA_LDAC 0x0080 /* PCM LFE DAC is ready (Read only) */ -#define AC97_EA_MDAC 0x0100 /* MIC ADC is ready (Read only) */ -#define AC97_EA_SPCV 0x0400 /* S/PDIF configuration valid (Read only) */ -#define AC97_EA_PRI 0x0800 /* Turns the PCM Center DAC off */ -#define AC97_EA_PRJ 0x1000 /* Turns the PCM Surround DACs off */ -#define AC97_EA_PRK 0x2000 /* Turns the PCM LFE DAC off */ -#define AC97_EA_PRL 0x4000 /* Turns the MIC ADC off */ -#define AC97_EA_SLOT_MASK 0xffcf /* Mask for slot assignment bits */ -#define AC97_EA_SPSA_3_4 0x0000 /* Slot assigned to 3 & 4 */ -#define AC97_EA_SPSA_7_8 0x0010 /* Slot assigned to 7 & 8 */ -#define AC97_EA_SPSA_6_9 0x0020 /* Slot assigned to 6 & 9 */ -#define AC97_EA_SPSA_10_11 0x0030 /* Slot assigned to 10 & 11 */ - -/* S/PDIF control bit defines */ -#define AC97_SC_PRO 0x0001 /* Professional status */ -#define AC97_SC_NAUDIO 0x0002 /* Non audio stream */ -#define AC97_SC_COPY 0x0004 /* Copyright status */ -#define AC97_SC_PRE 0x0008 /* Preemphasis status */ -#define AC97_SC_CC_MASK 0x07f0 /* Category Code mask */ -#define AC97_SC_L 0x0800 /* Generation Level status */ -#define AC97_SC_SPSR_MASK 0xcfff /* S/PDIF Sample Rate bits */ -#define AC97_SC_SPSR_44K 0x0000 /* Use 44.1kHz Sample rate */ -#define AC97_SC_SPSR_48K 0x2000 /* Use 48kHz Sample rate */ -#define AC97_SC_SPSR_32K 0x3000 /* Use 32kHz Sample rate */ -#define AC97_SC_DRS 0x4000 /* Double Rate S/PDIF */ -#define AC97_SC_V 0x8000 /* Validity status */ - -/* powerdown control and status bit defines */ - -/* status */ -#define AC97_PWR_MDM 0x0010 /* Modem section ready */ -#define AC97_PWR_REF 0x0008 /* Vref nominal */ -#define AC97_PWR_ANL 0x0004 /* Analog section ready */ -#define AC97_PWR_DAC 0x0002 /* DAC section ready */ -#define AC97_PWR_ADC 0x0001 /* ADC section ready */ - -/* control */ -#define AC97_PWR_PR0 0x0100 /* ADC and Mux powerdown */ -#define AC97_PWR_PR1 0x0200 /* DAC powerdown */ -#define AC97_PWR_PR2 0x0400 /* Output mixer powerdown (Vref on) */ -#define AC97_PWR_PR3 0x0800 /* Output mixer powerdown (Vref off) */ -#define AC97_PWR_PR4 0x1000 /* AC-link powerdown */ -#define AC97_PWR_PR5 0x2000 /* Internal Clk disable */ -#define AC97_PWR_PR6 0x4000 /* HP amp powerdown */ -#define AC97_PWR_PR7 0x8000 /* Modem off - if supported */ - -/* extended audio ID register bit defines */ -#define AC97_EXTID_VRA 0x0001 -#define AC97_EXTID_DRA 0x0002 -#define AC97_EXTID_SPDIF 0x0004 -#define AC97_EXTID_VRM 0x0008 -#define AC97_EXTID_DSA0 0x0010 -#define AC97_EXTID_DSA1 0x0020 -#define AC97_EXTID_CDAC 0x0040 -#define AC97_EXTID_SDAC 0x0080 -#define AC97_EXTID_LDAC 0x0100 -#define AC97_EXTID_AMAP 0x0200 -#define AC97_EXTID_REV0 0x0400 -#define AC97_EXTID_REV1 0x0800 -#define AC97_EXTID_ID0 0x4000 -#define AC97_EXTID_ID1 0x8000 - -/* extended status register bit defines */ -#define AC97_EXTSTAT_VRA 0x0001 -#define AC97_EXTSTAT_DRA 0x0002 -#define AC97_EXTSTAT_SPDIF 0x0004 -#define AC97_EXTSTAT_VRM 0x0008 -#define AC97_EXTSTAT_SPSA0 0x0010 -#define AC97_EXTSTAT_SPSA1 0x0020 -#define AC97_EXTSTAT_CDAC 0x0040 -#define AC97_EXTSTAT_SDAC 0x0080 -#define AC97_EXTSTAT_LDAC 0x0100 -#define AC97_EXTSTAT_MADC 0x0200 -#define AC97_EXTSTAT_SPCV 0x0400 -#define AC97_EXTSTAT_PRI 0x0800 -#define AC97_EXTSTAT_PRJ 0x1000 -#define AC97_EXTSTAT_PRK 0x2000 -#define AC97_EXTSTAT_PRL 0x4000 - -/* extended audio ID register bit defines */ -#define AC97_EXTID_VRA 0x0001 -#define AC97_EXTID_DRA 0x0002 -#define AC97_EXTID_SPDIF 0x0004 -#define AC97_EXTID_VRM 0x0008 -#define AC97_EXTID_DSA0 0x0010 -#define AC97_EXTID_DSA1 0x0020 -#define AC97_EXTID_CDAC 0x0040 -#define AC97_EXTID_SDAC 0x0080 -#define AC97_EXTID_LDAC 0x0100 -#define AC97_EXTID_AMAP 0x0200 -#define AC97_EXTID_REV0 0x0400 -#define AC97_EXTID_REV1 0x0800 -#define AC97_EXTID_ID0 0x4000 -#define AC97_EXTID_ID1 0x8000 - -/* extended status register bit defines */ -#define AC97_EXTSTAT_VRA 0x0001 -#define AC97_EXTSTAT_DRA 0x0002 -#define AC97_EXTSTAT_SPDIF 0x0004 -#define AC97_EXTSTAT_VRM 0x0008 -#define AC97_EXTSTAT_SPSA0 0x0010 -#define AC97_EXTSTAT_SPSA1 0x0020 -#define AC97_EXTSTAT_CDAC 0x0040 -#define AC97_EXTSTAT_SDAC 0x0080 -#define AC97_EXTSTAT_LDAC 0x0100 -#define AC97_EXTSTAT_MADC 0x0200 -#define AC97_EXTSTAT_SPCV 0x0400 -#define AC97_EXTSTAT_PRI 0x0800 -#define AC97_EXTSTAT_PRJ 0x1000 -#define AC97_EXTSTAT_PRK 0x2000 -#define AC97_EXTSTAT_PRL 0x4000 - -/* useful power states */ -#define AC97_PWR_D0 0x0000 /* everything on */ -#define AC97_PWR_D1 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR4 -#define AC97_PWR_D2 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR3|AC97_PWR_PR4 -#define AC97_PWR_D3 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR3|AC97_PWR_PR4 -#define AC97_PWR_ANLOFF AC97_PWR_PR2|AC97_PWR_PR3 /* analog section off */ - -/* Total number of defined registers. */ -#define AC97_REG_CNT 64 - - -/* OSS interface to the ac97s.. */ -#define AC97_STEREO_MASK (SOUND_MASK_VOLUME|SOUND_MASK_PCM|\ - SOUND_MASK_LINE|SOUND_MASK_CD|\ - SOUND_MASK_ALTPCM|SOUND_MASK_IGAIN|\ - SOUND_MASK_LINE1|SOUND_MASK_VIDEO) - -#define AC97_SUPPORTED_MASK (AC97_STEREO_MASK | \ - SOUND_MASK_BASS|SOUND_MASK_TREBLE|\ - SOUND_MASK_SPEAKER|SOUND_MASK_MIC|\ - SOUND_MASK_PHONEIN|SOUND_MASK_PHONEOUT) - -#define AC97_RECORD_MASK (SOUND_MASK_MIC|\ - SOUND_MASK_CD|SOUND_MASK_IGAIN|SOUND_MASK_VIDEO|\ - SOUND_MASK_LINE1| SOUND_MASK_LINE|\ - SOUND_MASK_PHONEIN) - -/* original check is not good enough in case FOO is greater than - * SOUND_MIXER_NRDEVICES because the supported_mixers has exactly - * SOUND_MIXER_NRDEVICES elements. - * before matching the given mixer against the bitmask in supported_mixers we - * check if mixer number exceeds maximum allowed size which is as mentioned - * above SOUND_MIXER_NRDEVICES */ -#define supported_mixer(CODEC,FOO) ((FOO >= 0) && \ - (FOO < SOUND_MIXER_NRDEVICES) && \ - (CODEC)->supported_mixers & (1< Date: Sat, 9 Jun 2012 02:40:03 -0700 Subject: slab/mempolicy: always use local policy from interrupt context slab_node() could access current->mempolicy from interrupt context. However there's a race condition during exit where the mempolicy is first freed and then the pointer zeroed. Using this from interrupts seems bogus anyways. The interrupt will interrupt a random process and therefore get a random mempolicy. Many times, this will be idle's, which noone can change. Just disable this here and always use local for slab from interrupts. I also cleaned up the callers of slab_node a bit which always passed the same argument. I believe the original mempolicy code did that in fact, so it's likely a regression. v2: send version with correct logic v3: simplify. fix typo. Reported-by: Arun Sharma Cc: penberg@kernel.org Cc: cl@linux.com Signed-off-by: Andi Kleen [tdmackey@twitter.com: Rework control flow based on feedback from cl@linux.com, fix logic, and cleanup current task_struct reference] Acked-by: David Rientjes Acked-by: Christoph Lameter Acked-by: KOSAKI Motohiro Signed-off-by: David Mackey Signed-off-by: Pekka Enberg --- include/linux/mempolicy.h | 2 +- mm/mempolicy.c | 8 +++++++- mm/slab.c | 4 ++-- mm/slub.c | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/mempolicy.h b/include/linux/mempolicy.h index 4aa42732e47f..95b738c7abff 100644 --- a/include/linux/mempolicy.h +++ b/include/linux/mempolicy.h @@ -215,7 +215,7 @@ extern struct zonelist *huge_zonelist(struct vm_area_struct *vma, extern bool init_nodemask_of_mempolicy(nodemask_t *mask); extern bool mempolicy_nodemask_intersects(struct task_struct *tsk, const nodemask_t *mask); -extern unsigned slab_node(struct mempolicy *policy); +extern unsigned slab_node(void); extern enum zone_type policy_zone; diff --git a/mm/mempolicy.c b/mm/mempolicy.c index f15c1b24ca18..cb0b230aa3f2 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -1602,8 +1602,14 @@ static unsigned interleave_nodes(struct mempolicy *policy) * task can change it's policy. The system default policy requires no * such protection. */ -unsigned slab_node(struct mempolicy *policy) +unsigned slab_node(void) { + struct mempolicy *policy; + + if (in_interrupt()) + return numa_node_id(); + + policy = current->mempolicy; if (!policy || policy->flags & MPOL_F_LOCAL) return numa_node_id(); diff --git a/mm/slab.c b/mm/slab.c index fc4a77446700..dd607a8e6706 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -3310,7 +3310,7 @@ static void *alternate_node_alloc(struct kmem_cache *cachep, gfp_t flags) if (cpuset_do_slab_mem_spread() && (cachep->flags & SLAB_MEM_SPREAD)) nid_alloc = cpuset_slab_spread_node(); else if (current->mempolicy) - nid_alloc = slab_node(current->mempolicy); + nid_alloc = slab_node(); if (nid_alloc != nid_here) return ____cache_alloc_node(cachep, flags, nid_alloc); return NULL; @@ -3342,7 +3342,7 @@ static void *fallback_alloc(struct kmem_cache *cache, gfp_t flags) retry_cpuset: cpuset_mems_cookie = get_mems_allowed(); - zonelist = node_zonelist(slab_node(current->mempolicy), flags); + zonelist = node_zonelist(slab_node(), flags); retry: /* diff --git a/mm/slub.c b/mm/slub.c index 797271f5afb8..348fed1643f4 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1617,7 +1617,7 @@ static void *get_any_partial(struct kmem_cache *s, gfp_t flags, do { cpuset_mems_cookie = get_mems_allowed(); - zonelist = node_zonelist(slab_node(current->mempolicy), flags); + zonelist = node_zonelist(slab_node(), flags); for_each_zone_zonelist(zone, z, zonelist, high_zoneidx) { struct kmem_cache_node *n; -- cgit v1.2.3 From c5deac3c9b2284a64326e8799dfe7416bc619c02 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 12 Dec 2011 18:43:16 +0100 Subject: fbdev: sh_mobile_lcdc: Implement overlays support Signed-off-by: Laurent Pinchart --- .../sysfs-devices-platform-sh_mobile_lcdc_fb | 44 + drivers/video/sh_mobile_lcdcfb.c | 932 +++++++++++++++++++-- include/video/sh_mobile_lcdc.h | 7 + 3 files changed, 911 insertions(+), 72 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-devices-platform-sh_mobile_lcdc_fb (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-devices-platform-sh_mobile_lcdc_fb b/Documentation/ABI/testing/sysfs-devices-platform-sh_mobile_lcdc_fb new file mode 100644 index 000000000000..2107082426da --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-platform-sh_mobile_lcdc_fb @@ -0,0 +1,44 @@ +What: /sys/devices/platform/sh_mobile_lcdc_fb.[0-3]/graphics/fb[0-9]/ovl_alpha +Date: May 2012 +Contact: Laurent Pinchart +Description: + This file is only available on fb[0-9] devices corresponding + to overlay planes. + + Stores the alpha blending value for the overlay. Values range + from 0 (transparent) to 255 (opaque). The value is ignored if + the mode is not set to Alpha Blending. + +What: /sys/devices/platform/sh_mobile_lcdc_fb.[0-3]/graphics/fb[0-9]/ovl_mode +Date: May 2012 +Contact: Laurent Pinchart +Description: + This file is only available on fb[0-9] devices corresponding + to overlay planes. + + Selects the composition mode for the overlay. Possible values + are + + 0 - Alpha Blending + 1 - ROP3 + +What: /sys/devices/platform/sh_mobile_lcdc_fb.[0-3]/graphics/fb[0-9]/ovl_position +Date: May 2012 +Contact: Laurent Pinchart +Description: + This file is only available on fb[0-9] devices corresponding + to overlay planes. + + Stores the x,y overlay position on the display in pixels. The + position format is `[0-9]+,[0-9]+'. + +What: /sys/devices/platform/sh_mobile_lcdc_fb.[0-3]/graphics/fb[0-9]/ovl_rop3 +Date: May 2012 +Contact: Laurent Pinchart +Description: + This file is only available on fb[0-9] devices corresponding + to overlay planes. + + Stores the raster operation (ROP3) for the overlay. Values + range from 0 to 255. The value is ignored if the mode is not + set to ROP3. diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index 799f87171e04..98e81b31fbc4 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -32,12 +33,176 @@ #include "sh_mobile_lcdcfb.h" +/* ---------------------------------------------------------------------------- + * Overlay register definitions + */ + +#define LDBCR 0xb00 +#define LDBCR_UPC(n) (1 << ((n) + 16)) +#define LDBCR_UPF(n) (1 << ((n) + 8)) +#define LDBCR_UPD(n) (1 << ((n) + 0)) +#define LDBnBSIFR(n) (0xb20 + (n) * 0x20 + 0x00) +#define LDBBSIFR_EN (1 << 31) +#define LDBBSIFR_VS (1 << 29) +#define LDBBSIFR_BRSEL (1 << 28) +#define LDBBSIFR_MX (1 << 27) +#define LDBBSIFR_MY (1 << 26) +#define LDBBSIFR_CV3 (3 << 24) +#define LDBBSIFR_CV2 (2 << 24) +#define LDBBSIFR_CV1 (1 << 24) +#define LDBBSIFR_CV0 (0 << 24) +#define LDBBSIFR_CV_MASK (3 << 24) +#define LDBBSIFR_LAY_MASK (0xff << 16) +#define LDBBSIFR_LAY_SHIFT 16 +#define LDBBSIFR_ROP3_MASK (0xff << 16) +#define LDBBSIFR_ROP3_SHIFT 16 +#define LDBBSIFR_AL_PL8 (3 << 14) +#define LDBBSIFR_AL_PL1 (2 << 14) +#define LDBBSIFR_AL_PK (1 << 14) +#define LDBBSIFR_AL_1 (0 << 14) +#define LDBBSIFR_AL_MASK (3 << 14) +#define LDBBSIFR_SWPL (1 << 10) +#define LDBBSIFR_SWPW (1 << 9) +#define LDBBSIFR_SWPB (1 << 8) +#define LDBBSIFR_RY (1 << 7) +#define LDBBSIFR_CHRR_420 (2 << 0) +#define LDBBSIFR_CHRR_422 (1 << 0) +#define LDBBSIFR_CHRR_444 (0 << 0) +#define LDBBSIFR_RPKF_ARGB32 (0x00 << 0) +#define LDBBSIFR_RPKF_RGB16 (0x03 << 0) +#define LDBBSIFR_RPKF_RGB24 (0x0b << 0) +#define LDBBSIFR_RPKF_MASK (0x1f << 0) +#define LDBnBSSZR(n) (0xb20 + (n) * 0x20 + 0x04) +#define LDBBSSZR_BVSS_MASK (0xfff << 16) +#define LDBBSSZR_BVSS_SHIFT 16 +#define LDBBSSZR_BHSS_MASK (0xfff << 0) +#define LDBBSSZR_BHSS_SHIFT 0 +#define LDBnBLOCR(n) (0xb20 + (n) * 0x20 + 0x08) +#define LDBBLOCR_CVLC_MASK (0xfff << 16) +#define LDBBLOCR_CVLC_SHIFT 16 +#define LDBBLOCR_CHLC_MASK (0xfff << 0) +#define LDBBLOCR_CHLC_SHIFT 0 +#define LDBnBSMWR(n) (0xb20 + (n) * 0x20 + 0x0c) +#define LDBBSMWR_BSMWA_MASK (0xffff << 16) +#define LDBBSMWR_BSMWA_SHIFT 16 +#define LDBBSMWR_BSMW_MASK (0xffff << 0) +#define LDBBSMWR_BSMW_SHIFT 0 +#define LDBnBSAYR(n) (0xb20 + (n) * 0x20 + 0x10) +#define LDBBSAYR_FG1A_MASK (0xff << 24) +#define LDBBSAYR_FG1A_SHIFT 24 +#define LDBBSAYR_FG1R_MASK (0xff << 16) +#define LDBBSAYR_FG1R_SHIFT 16 +#define LDBBSAYR_FG1G_MASK (0xff << 8) +#define LDBBSAYR_FG1G_SHIFT 8 +#define LDBBSAYR_FG1B_MASK (0xff << 0) +#define LDBBSAYR_FG1B_SHIFT 0 +#define LDBnBSACR(n) (0xb20 + (n) * 0x20 + 0x14) +#define LDBBSACR_FG2A_MASK (0xff << 24) +#define LDBBSACR_FG2A_SHIFT 24 +#define LDBBSACR_FG2R_MASK (0xff << 16) +#define LDBBSACR_FG2R_SHIFT 16 +#define LDBBSACR_FG2G_MASK (0xff << 8) +#define LDBBSACR_FG2G_SHIFT 8 +#define LDBBSACR_FG2B_MASK (0xff << 0) +#define LDBBSACR_FG2B_SHIFT 0 +#define LDBnBSAAR(n) (0xb20 + (n) * 0x20 + 0x18) +#define LDBBSAAR_AP_MASK (0xff << 24) +#define LDBBSAAR_AP_SHIFT 24 +#define LDBBSAAR_R_MASK (0xff << 16) +#define LDBBSAAR_R_SHIFT 16 +#define LDBBSAAR_GY_MASK (0xff << 8) +#define LDBBSAAR_GY_SHIFT 8 +#define LDBBSAAR_B_MASK (0xff << 0) +#define LDBBSAAR_B_SHIFT 0 +#define LDBnBPPCR(n) (0xb20 + (n) * 0x20 + 0x1c) +#define LDBBPPCR_AP_MASK (0xff << 24) +#define LDBBPPCR_AP_SHIFT 24 +#define LDBBPPCR_R_MASK (0xff << 16) +#define LDBBPPCR_R_SHIFT 16 +#define LDBBPPCR_GY_MASK (0xff << 8) +#define LDBBPPCR_GY_SHIFT 8 +#define LDBBPPCR_B_MASK (0xff << 0) +#define LDBBPPCR_B_SHIFT 0 +#define LDBnBBGCL(n) (0xb10 + (n) * 0x04) +#define LDBBBGCL_BGA_MASK (0xff << 24) +#define LDBBBGCL_BGA_SHIFT 24 +#define LDBBBGCL_BGR_MASK (0xff << 16) +#define LDBBBGCL_BGR_SHIFT 16 +#define LDBBBGCL_BGG_MASK (0xff << 8) +#define LDBBBGCL_BGG_SHIFT 8 +#define LDBBBGCL_BGB_MASK (0xff << 0) +#define LDBBBGCL_BGB_SHIFT 0 + #define SIDE_B_OFFSET 0x1000 #define MIRROR_OFFSET 0x2000 #define MAX_XRES 1920 #define MAX_YRES 1080 +enum sh_mobile_lcdc_overlay_mode { + LCDC_OVERLAY_BLEND, + LCDC_OVERLAY_ROP3, +}; + +/* + * struct sh_mobile_lcdc_overlay - LCDC display overlay + * + * @channel: LCDC channel this overlay belongs to + * @cfg: Overlay configuration + * @info: Frame buffer device + * @index: Overlay index (0-3) + * @base: Overlay registers base address + * @enabled: True if the overlay is enabled + * @mode: Overlay blending mode (alpha blend or ROP3) + * @alpha: Global alpha blending value (0-255, for alpha blending mode) + * @rop3: Raster operation (for ROP3 mode) + * @fb_mem: Frame buffer virtual memory address + * @fb_size: Frame buffer size in bytes + * @dma_handle: Frame buffer DMA address + * @base_addr_y: Overlay base address (RGB or luma component) + * @base_addr_c: Overlay base address (chroma component) + * @pan_offset: Current pan offset in bytes + * @format: Current pixelf format + * @xres: Horizontal visible resolution + * @xres_virtual: Horizontal total resolution + * @yres: Vertical visible resolution + * @yres_virtual: Vertical total resolution + * @pitch: Overlay line pitch + * @pos_x: Horizontal overlay position + * @pos_y: Vertical overlay position + */ +struct sh_mobile_lcdc_overlay { + struct sh_mobile_lcdc_chan *channel; + + const struct sh_mobile_lcdc_overlay_cfg *cfg; + struct fb_info *info; + + unsigned int index; + unsigned long base; + + bool enabled; + enum sh_mobile_lcdc_overlay_mode mode; + unsigned int alpha; + unsigned int rop3; + + void *fb_mem; + unsigned long fb_size; + + dma_addr_t dma_handle; + unsigned long base_addr_y; + unsigned long base_addr_c; + unsigned long pan_offset; + + const struct sh_mobile_lcdc_format_info *format; + unsigned int xres; + unsigned int xres_virtual; + unsigned int yres; + unsigned int yres_virtual; + unsigned int pitch; + int pos_x; + int pos_y; +}; + struct sh_mobile_lcdc_priv { void __iomem *base; int irq; @@ -45,7 +210,10 @@ struct sh_mobile_lcdc_priv { struct device *dev; struct clk *dot_clk; unsigned long lddckr; + struct sh_mobile_lcdc_chan ch[2]; + struct sh_mobile_lcdc_overlay overlays[4]; + struct notifier_block notifier; int started; int forced_fourcc; /* 2 channel LCDC must share fourcc setting */ @@ -141,6 +309,13 @@ static unsigned long lcdc_read_chan(struct sh_mobile_lcdc_chan *chan, return ioread32(chan->lcdc->base + chan->reg_offs[reg_nr]); } +static void lcdc_write_overlay(struct sh_mobile_lcdc_overlay *ovl, + int reg, unsigned long data) +{ + iowrite32(data, ovl->channel->lcdc->base + reg); + iowrite32(data, ovl->channel->lcdc->base + reg + SIDE_B_OFFSET); +} + static void lcdc_write(struct sh_mobile_lcdc_priv *priv, unsigned long reg_offs, unsigned long data) { @@ -685,6 +860,96 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch) lcdc_write_chan(ch, LDHAJR, tmp); } +static void sh_mobile_lcdc_overlay_setup(struct sh_mobile_lcdc_overlay *ovl) +{ + u32 format = 0; + + if (!ovl->enabled) { + lcdc_write(ovl->channel->lcdc, LDBCR, LDBCR_UPC(ovl->index)); + lcdc_write_overlay(ovl, LDBnBSIFR(ovl->index), 0); + lcdc_write(ovl->channel->lcdc, LDBCR, + LDBCR_UPF(ovl->index) | LDBCR_UPD(ovl->index)); + return; + } + + ovl->base_addr_y = ovl->dma_handle; + ovl->base_addr_c = ovl->base_addr_y + ovl->xres + * ovl->yres_virtual; + + switch (ovl->mode) { + case LCDC_OVERLAY_BLEND: + format = LDBBSIFR_EN | (ovl->alpha << LDBBSIFR_LAY_SHIFT); + break; + + case LCDC_OVERLAY_ROP3: + format = LDBBSIFR_EN | LDBBSIFR_BRSEL + | (ovl->rop3 << LDBBSIFR_ROP3_SHIFT); + break; + } + + switch (ovl->format->fourcc) { + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_NV42: + format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW; + break; + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV24: + format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW | LDBBSIFR_SWPB; + break; + case V4L2_PIX_FMT_BGR32: + default: + format |= LDBBSIFR_SWPL; + break; + } + + switch (ovl->format->fourcc) { + case V4L2_PIX_FMT_RGB565: + format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB16; + break; + case V4L2_PIX_FMT_BGR24: + format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB24; + break; + case V4L2_PIX_FMT_BGR32: + format |= LDBBSIFR_AL_PK | LDBBSIFR_RY | LDDFR_PKF_ARGB32; + break; + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_420; + break; + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_422; + break; + case V4L2_PIX_FMT_NV24: + case V4L2_PIX_FMT_NV42: + format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_444; + break; + } + + lcdc_write(ovl->channel->lcdc, LDBCR, LDBCR_UPC(ovl->index)); + + lcdc_write_overlay(ovl, LDBnBSIFR(ovl->index), format); + + lcdc_write_overlay(ovl, LDBnBSSZR(ovl->index), + (ovl->yres << LDBBSSZR_BVSS_SHIFT) | + (ovl->xres << LDBBSSZR_BHSS_SHIFT)); + lcdc_write_overlay(ovl, LDBnBLOCR(ovl->index), + (ovl->pos_y << LDBBLOCR_CVLC_SHIFT) | + (ovl->pos_x << LDBBLOCR_CHLC_SHIFT)); + lcdc_write_overlay(ovl, LDBnBSMWR(ovl->index), + ovl->pitch << LDBBSMWR_BSMW_SHIFT); + + lcdc_write_overlay(ovl, LDBnBSAYR(ovl->index), ovl->base_addr_y); + lcdc_write_overlay(ovl, LDBnBSACR(ovl->index), ovl->base_addr_c); + + lcdc_write(ovl->channel->lcdc, LDBCR, + LDBCR_UPF(ovl->index) | LDBCR_UPD(ovl->index)); +} + /* * __sh_mobile_lcdc_start - Configure and start the LCDC * @priv: LCDC device @@ -892,6 +1157,11 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) } } + for (k = 0; k < ARRAY_SIZE(priv->overlays); ++k) { + struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[k]; + sh_mobile_lcdc_overlay_setup(ovl); + } + /* Start the LCDC. */ __sh_mobile_lcdc_start(priv); @@ -975,8 +1245,506 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) sh_mobile_lcdc_clk_off(priv); } +static int __sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + if (var->xres > MAX_XRES || var->yres > MAX_YRES) + return -EINVAL; + + /* Make sure the virtual resolution is at least as big as the visible + * resolution. + */ + if (var->xres_virtual < var->xres) + var->xres_virtual = var->xres; + if (var->yres_virtual < var->yres) + var->yres_virtual = var->yres; + + if (sh_mobile_format_is_fourcc(var)) { + const struct sh_mobile_lcdc_format_info *format; + + format = sh_mobile_format_info(var->grayscale); + if (format == NULL) + return -EINVAL; + var->bits_per_pixel = format->bpp; + + /* Default to RGB and JPEG color-spaces for RGB and YUV formats + * respectively. + */ + if (!format->yuv) + var->colorspace = V4L2_COLORSPACE_SRGB; + else if (var->colorspace != V4L2_COLORSPACE_REC709) + var->colorspace = V4L2_COLORSPACE_JPEG; + } else { + if (var->bits_per_pixel <= 16) { /* RGB 565 */ + var->bits_per_pixel = 16; + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; + var->transp.offset = 0; + var->transp.length = 0; + } else if (var->bits_per_pixel <= 24) { /* RGB 888 */ + var->bits_per_pixel = 24; + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 0; + var->transp.length = 0; + } else if (var->bits_per_pixel <= 32) { /* RGBA 888 */ + var->bits_per_pixel = 32; + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 24; + var->transp.length = 8; + } else + return -EINVAL; + + var->red.msb_right = 0; + var->green.msb_right = 0; + var->blue.msb_right = 0; + var->transp.msb_right = 0; + } + + /* Make sure we don't exceed our allocated memory. */ + if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 > + info->fix.smem_len) + return -EINVAL; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Frame buffer operations - Overlays + */ + +static ssize_t +overlay_alpha_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct sh_mobile_lcdc_overlay *ovl = info->par; + + return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->alpha); +} + +static ssize_t +overlay_alpha_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct sh_mobile_lcdc_overlay *ovl = info->par; + unsigned int alpha; + char *endp; + + alpha = simple_strtoul(buf, &endp, 10); + if (isspace(*endp)) + endp++; + + if (endp - buf != count) + return -EINVAL; + + if (alpha > 255) + return -EINVAL; + + if (ovl->alpha != alpha) { + ovl->alpha = alpha; + + if (ovl->mode == LCDC_OVERLAY_BLEND && ovl->enabled) + sh_mobile_lcdc_overlay_setup(ovl); + } + + return count; +} + +static ssize_t +overlay_mode_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct sh_mobile_lcdc_overlay *ovl = info->par; + + return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->mode); +} + +static ssize_t +overlay_mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct sh_mobile_lcdc_overlay *ovl = info->par; + unsigned int mode; + char *endp; + + mode = simple_strtoul(buf, &endp, 10); + if (isspace(*endp)) + endp++; + + if (endp - buf != count) + return -EINVAL; + + if (mode != LCDC_OVERLAY_BLEND && mode != LCDC_OVERLAY_ROP3) + return -EINVAL; + + if (ovl->mode != mode) { + ovl->mode = mode; + + if (ovl->enabled) + sh_mobile_lcdc_overlay_setup(ovl); + } + + return count; +} + +static ssize_t +overlay_position_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct sh_mobile_lcdc_overlay *ovl = info->par; + + return scnprintf(buf, PAGE_SIZE, "%d,%d\n", ovl->pos_x, ovl->pos_y); +} + +static ssize_t +overlay_position_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct sh_mobile_lcdc_overlay *ovl = info->par; + char *endp; + int pos_x; + int pos_y; + + pos_x = simple_strtol(buf, &endp, 10); + if (*endp != ',') + return -EINVAL; + + pos_y = simple_strtol(endp + 1, &endp, 10); + if (isspace(*endp)) + endp++; + + if (endp - buf != count) + return -EINVAL; + + if (ovl->pos_x != pos_x || ovl->pos_y != pos_y) { + ovl->pos_x = pos_x; + ovl->pos_y = pos_y; + + if (ovl->enabled) + sh_mobile_lcdc_overlay_setup(ovl); + } + + return count; +} + +static ssize_t +overlay_rop3_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct sh_mobile_lcdc_overlay *ovl = info->par; + + return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->rop3); +} + +static ssize_t +overlay_rop3_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct sh_mobile_lcdc_overlay *ovl = info->par; + unsigned int rop3; + char *endp; + + rop3 = !!simple_strtoul(buf, &endp, 10); + if (isspace(*endp)) + endp++; + + if (endp - buf != count) + return -EINVAL; + + if (rop3 > 255) + return -EINVAL; + + if (ovl->rop3 != rop3) { + ovl->rop3 = rop3; + + if (ovl->mode == LCDC_OVERLAY_ROP3 && ovl->enabled) + sh_mobile_lcdc_overlay_setup(ovl); + } + + return count; +} + +static const struct device_attribute overlay_sysfs_attrs[] = { + __ATTR(ovl_alpha, S_IRUGO|S_IWUSR, + overlay_alpha_show, overlay_alpha_store), + __ATTR(ovl_mode, S_IRUGO|S_IWUSR, + overlay_mode_show, overlay_mode_store), + __ATTR(ovl_position, S_IRUGO|S_IWUSR, + overlay_position_show, overlay_position_store), + __ATTR(ovl_rop3, S_IRUGO|S_IWUSR, + overlay_rop3_show, overlay_rop3_store), +}; + +static const struct fb_fix_screeninfo sh_mobile_lcdc_overlay_fix = { + .id = "SH Mobile LCDC", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .accel = FB_ACCEL_NONE, + .xpanstep = 0, + .ypanstep = 1, + .ywrapstep = 0, + .capabilities = FB_CAP_FOURCC, +}; + +static int sh_mobile_lcdc_overlay_pan(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct sh_mobile_lcdc_overlay *ovl = info->par; + unsigned long base_addr_y; + unsigned long base_addr_c; + unsigned long pan_offset; + unsigned long c_offset; + + if (!ovl->format->yuv) + pan_offset = var->yoffset * ovl->pitch + + var->xoffset * (ovl->format->bpp / 8); + else + pan_offset = var->yoffset * ovl->pitch + var->xoffset; + + if (pan_offset == ovl->pan_offset) + return 0; /* No change, do nothing */ + + /* Set the source address for the next refresh */ + base_addr_y = ovl->dma_handle + pan_offset; + + ovl->base_addr_y = base_addr_y; + ovl->base_addr_c = base_addr_y; + + if (ovl->format->yuv) { + /* Set Y offset */ + c_offset = var->yoffset * ovl->pitch + * (ovl->format->bpp - 8) / 8; + base_addr_c = ovl->dma_handle + + ovl->xres * ovl->yres_virtual + + c_offset; + /* Set X offset */ + if (ovl->format->fourcc == V4L2_PIX_FMT_NV24) + base_addr_c += 2 * var->xoffset; + else + base_addr_c += var->xoffset; + + ovl->base_addr_c = base_addr_c; + } + + lcdc_write_overlay(ovl, LDBnBSAYR(ovl->index), ovl->base_addr_y); + lcdc_write_overlay(ovl, LDBnBSACR(ovl->index), ovl->base_addr_c); + + ovl->pan_offset = pan_offset; + + return 0; +} + +static int sh_mobile_lcdc_overlay_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + struct sh_mobile_lcdc_overlay *ovl = info->par; + + switch (cmd) { + case FBIO_WAITFORVSYNC: + return sh_mobile_lcdc_wait_for_vsync(ovl->channel); + + default: + return -ENOIOCTLCMD; + } +} + +static int sh_mobile_lcdc_overlay_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + return __sh_mobile_lcdc_check_var(var, info); +} + +static int sh_mobile_lcdc_overlay_set_par(struct fb_info *info) +{ + struct sh_mobile_lcdc_overlay *ovl = info->par; + + ovl->format = + sh_mobile_format_info(sh_mobile_format_fourcc(&info->var)); + + ovl->xres = info->var.xres; + ovl->xres_virtual = info->var.xres_virtual; + ovl->yres = info->var.yres; + ovl->yres_virtual = info->var.yres_virtual; + + if (ovl->format->yuv) + ovl->pitch = info->var.xres; + else + ovl->pitch = info->var.xres * ovl->format->bpp / 8; + + sh_mobile_lcdc_overlay_setup(ovl); + + info->fix.line_length = ovl->pitch; + + if (sh_mobile_format_is_fourcc(&info->var)) { + info->fix.type = FB_TYPE_FOURCC; + info->fix.visual = FB_VISUAL_FOURCC; + } else { + info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.visual = FB_VISUAL_TRUECOLOR; + } + + return 0; +} + +/* Overlay blanking. Disable the overlay when blanked. */ +static int sh_mobile_lcdc_overlay_blank(int blank, struct fb_info *info) +{ + struct sh_mobile_lcdc_overlay *ovl = info->par; + + ovl->enabled = !blank; + sh_mobile_lcdc_overlay_setup(ovl); + + /* Prevent the backlight from receiving a blanking event by returning + * a non-zero value. + */ + return 1; +} + +static struct fb_ops sh_mobile_lcdc_overlay_ops = { + .owner = THIS_MODULE, + .fb_read = fb_sys_read, + .fb_write = fb_sys_write, + .fb_fillrect = sys_fillrect, + .fb_copyarea = sys_copyarea, + .fb_imageblit = sys_imageblit, + .fb_blank = sh_mobile_lcdc_overlay_blank, + .fb_pan_display = sh_mobile_lcdc_overlay_pan, + .fb_ioctl = sh_mobile_lcdc_overlay_ioctl, + .fb_check_var = sh_mobile_lcdc_overlay_check_var, + .fb_set_par = sh_mobile_lcdc_overlay_set_par, +}; + +static void +sh_mobile_lcdc_overlay_fb_unregister(struct sh_mobile_lcdc_overlay *ovl) +{ + struct fb_info *info = ovl->info; + + if (info == NULL || info->dev == NULL) + return; + + unregister_framebuffer(ovl->info); +} + +static int __devinit +sh_mobile_lcdc_overlay_fb_register(struct sh_mobile_lcdc_overlay *ovl) +{ + struct sh_mobile_lcdc_priv *lcdc = ovl->channel->lcdc; + struct fb_info *info = ovl->info; + unsigned int i; + int ret; + + if (info == NULL) + return 0; + + ret = register_framebuffer(info); + if (ret < 0) + return ret; + + dev_info(lcdc->dev, "registered %s/overlay %u as %dx%d %dbpp.\n", + dev_name(lcdc->dev), ovl->index, info->var.xres, + info->var.yres, info->var.bits_per_pixel); + + for (i = 0; i < ARRAY_SIZE(overlay_sysfs_attrs); ++i) { + ret = device_create_file(info->dev, &overlay_sysfs_attrs[i]); + if (ret < 0) + return ret; + } + + return 0; +} + +static void +sh_mobile_lcdc_overlay_fb_cleanup(struct sh_mobile_lcdc_overlay *ovl) +{ + struct fb_info *info = ovl->info; + + if (info == NULL || info->device == NULL) + return; + + framebuffer_release(info); +} + +static int __devinit +sh_mobile_lcdc_overlay_fb_init(struct sh_mobile_lcdc_overlay *ovl) +{ + struct sh_mobile_lcdc_priv *priv = ovl->channel->lcdc; + struct fb_var_screeninfo *var; + struct fb_info *info; + + /* Allocate and initialize the frame buffer device. */ + info = framebuffer_alloc(0, priv->dev); + if (info == NULL) { + dev_err(priv->dev, "unable to allocate fb_info\n"); + return -ENOMEM; + } + + ovl->info = info; + + info->flags = FBINFO_FLAG_DEFAULT; + info->fbops = &sh_mobile_lcdc_overlay_ops; + info->device = priv->dev; + info->screen_base = ovl->fb_mem; + info->par = ovl; + + /* Initialize fixed screen information. Restrict pan to 2 lines steps + * for NV12 and NV21. + */ + info->fix = sh_mobile_lcdc_overlay_fix; + snprintf(info->fix.id, sizeof(info->fix.id), + "SH Mobile LCDC Overlay %u", ovl->index); + info->fix.smem_start = ovl->dma_handle; + info->fix.smem_len = ovl->fb_size; + info->fix.line_length = ovl->pitch; + + if (ovl->format->yuv) + info->fix.visual = FB_VISUAL_FOURCC; + else + info->fix.visual = FB_VISUAL_TRUECOLOR; + + if (ovl->format->fourcc == V4L2_PIX_FMT_NV12 || + ovl->format->fourcc == V4L2_PIX_FMT_NV21) + info->fix.ypanstep = 2; + + /* Initialize variable screen information. */ + var = &info->var; + memset(var, 0, sizeof(*var)); + var->xres = ovl->xres; + var->yres = ovl->yres; + var->xres_virtual = ovl->xres_virtual; + var->yres_virtual = ovl->yres_virtual; + var->activate = FB_ACTIVATE_NOW; + + /* Use the legacy API by default for RGB formats, and the FOURCC API + * for YUV formats. + */ + if (!ovl->format->yuv) + var->bits_per_pixel = ovl->format->bpp; + else + var->grayscale = ovl->format->fourcc; + + return sh_mobile_lcdc_overlay_check_var(var, info); +} + /* ----------------------------------------------------------------------------- - * Frame buffer operations + * Frame buffer operations - main frame buffer */ static int sh_mobile_lcdc_setcolreg(u_int regno, @@ -1202,9 +1970,7 @@ static int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var, unsigned int best_xres = 0; unsigned int best_yres = 0; unsigned int i; - - if (var->xres > MAX_XRES || var->yres > MAX_YRES) - return -EINVAL; + int ret; /* If board code provides us with a list of available modes, make sure * we use one of them. Find the mode closest to the requested one. The @@ -1239,73 +2005,9 @@ static int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var, var->yres = best_yres; } - /* Make sure the virtual resolution is at least as big as the visible - * resolution. - */ - if (var->xres_virtual < var->xres) - var->xres_virtual = var->xres; - if (var->yres_virtual < var->yres) - var->yres_virtual = var->yres; - - if (sh_mobile_format_is_fourcc(var)) { - const struct sh_mobile_lcdc_format_info *format; - - format = sh_mobile_format_info(var->grayscale); - if (format == NULL) - return -EINVAL; - var->bits_per_pixel = format->bpp; - - /* Default to RGB and JPEG color-spaces for RGB and YUV formats - * respectively. - */ - if (!format->yuv) - var->colorspace = V4L2_COLORSPACE_SRGB; - else if (var->colorspace != V4L2_COLORSPACE_REC709) - var->colorspace = V4L2_COLORSPACE_JPEG; - } else { - if (var->bits_per_pixel <= 16) { /* RGB 565 */ - var->bits_per_pixel = 16; - var->red.offset = 11; - var->red.length = 5; - var->green.offset = 5; - var->green.length = 6; - var->blue.offset = 0; - var->blue.length = 5; - var->transp.offset = 0; - var->transp.length = 0; - } else if (var->bits_per_pixel <= 24) { /* RGB 888 */ - var->bits_per_pixel = 24; - var->red.offset = 16; - var->red.length = 8; - var->green.offset = 8; - var->green.length = 8; - var->blue.offset = 0; - var->blue.length = 8; - var->transp.offset = 0; - var->transp.length = 0; - } else if (var->bits_per_pixel <= 32) { /* RGBA 888 */ - var->bits_per_pixel = 32; - var->red.offset = 16; - var->red.length = 8; - var->green.offset = 8; - var->green.length = 8; - var->blue.offset = 0; - var->blue.length = 8; - var->transp.offset = 24; - var->transp.length = 8; - } else - return -EINVAL; - - var->red.msb_right = 0; - var->green.msb_right = 0; - var->blue.msb_right = 0; - var->transp.msb_right = 0; - } - - /* Make sure we don't exceed our allocated memory. */ - if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 > - info->fix.smem_len) - return -EINVAL; + ret = __sh_mobile_lcdc_check_var(var, info); + if (ret < 0) + return ret; /* only accept the forced_fourcc for dual channel configurations */ if (p->forced_fourcc && @@ -1714,15 +2416,27 @@ static const struct fb_videomode default_720p __devinitconst = { static int sh_mobile_lcdc_remove(struct platform_device *pdev) { struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev); - int i; + unsigned int i; fb_unregister_client(&priv->notifier); + for (i = 0; i < ARRAY_SIZE(priv->overlays); i++) + sh_mobile_lcdc_overlay_fb_unregister(&priv->overlays[i]); for (i = 0; i < ARRAY_SIZE(priv->ch); i++) sh_mobile_lcdc_channel_fb_unregister(&priv->ch[i]); sh_mobile_lcdc_stop(priv); + for (i = 0; i < ARRAY_SIZE(priv->overlays); i++) { + struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i]; + + sh_mobile_lcdc_overlay_fb_cleanup(ovl); + + if (ovl->fb_mem) + dma_free_coherent(&pdev->dev, ovl->fb_size, + ovl->fb_mem, ovl->dma_handle); + } + for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { struct sh_mobile_lcdc_chan *ch = &priv->ch[i]; @@ -1797,6 +2511,61 @@ static int __devinit sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan * return 0; } +static int __devinit +sh_mobile_lcdc_overlay_init(struct sh_mobile_lcdc_priv *priv, + struct sh_mobile_lcdc_overlay *ovl) +{ + const struct sh_mobile_lcdc_format_info *format; + int ret; + + if (ovl->cfg->fourcc == 0) + return 0; + + /* Validate the format. */ + format = sh_mobile_format_info(ovl->cfg->fourcc); + if (format == NULL) { + dev_err(priv->dev, "Invalid FOURCC %08x\n", ovl->cfg->fourcc); + return -EINVAL; + } + + ovl->enabled = false; + ovl->mode = LCDC_OVERLAY_BLEND; + ovl->alpha = 255; + ovl->rop3 = 0; + ovl->pos_x = 0; + ovl->pos_y = 0; + + /* The default Y virtual resolution is twice the panel size to allow for + * double-buffering. + */ + ovl->format = format; + ovl->xres = ovl->cfg->max_xres; + ovl->xres_virtual = ovl->xres; + ovl->yres = ovl->cfg->max_yres; + ovl->yres_virtual = ovl->yres * 2; + + if (!format->yuv) + ovl->pitch = ovl->xres * format->bpp / 8; + else + ovl->pitch = ovl->xres; + + /* Allocate frame buffer memory. */ + ovl->fb_size = ovl->cfg->max_xres * ovl->cfg->max_yres + * format->bpp / 8 * 2; + ovl->fb_mem = dma_alloc_coherent(priv->dev, ovl->fb_size, + &ovl->dma_handle, GFP_KERNEL); + if (!ovl->fb_mem) { + dev_err(priv->dev, "unable to allocate buffer\n"); + return -ENOMEM; + } + + ret = sh_mobile_lcdc_overlay_fb_init(ovl); + if (ret < 0) + return ret; + + return 0; +} + static int __devinit sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv, struct sh_mobile_lcdc_chan *ch) @@ -2005,6 +2774,17 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) goto err1; } + for (i = 0; i < ARRAY_SIZE(pdata->overlays); i++) { + struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i]; + + ovl->cfg = &pdata->overlays[i]; + ovl->channel = &priv->ch[0]; + + error = sh_mobile_lcdc_overlay_init(priv, ovl); + if (error) + goto err1; + } + error = sh_mobile_lcdc_start(priv); if (error) { dev_err(&pdev->dev, "unable to start hardware\n"); @@ -2019,6 +2799,14 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) goto err1; } + for (i = 0; i < ARRAY_SIZE(pdata->overlays); i++) { + struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i]; + + error = sh_mobile_lcdc_overlay_fb_register(ovl); + if (error) + goto err1; + } + /* Failure ignored */ priv->notifier.notifier_call = sh_mobile_lcdc_notify; fb_register_client(&priv->notifier); diff --git a/include/video/sh_mobile_lcdc.h b/include/video/sh_mobile_lcdc.h index 7571b27a0ba1..ff43ffc1aab2 100644 --- a/include/video/sh_mobile_lcdc.h +++ b/include/video/sh_mobile_lcdc.h @@ -166,6 +166,12 @@ struct sh_mobile_lcdc_bl_info { int (*get_brightness)(void); }; +struct sh_mobile_lcdc_overlay_cfg { + int fourcc; + unsigned int max_xres; + unsigned int max_yres; +}; + struct sh_mobile_lcdc_chan_cfg { int chan; int fourcc; @@ -186,6 +192,7 @@ struct sh_mobile_lcdc_chan_cfg { struct sh_mobile_lcdc_info { int clock_source; struct sh_mobile_lcdc_chan_cfg ch[2]; + struct sh_mobile_lcdc_overlay_cfg overlays[4]; struct sh_mobile_meram_info *meram_dev; }; -- cgit v1.2.3 From f3761c3950bd2ad813095a240d6a3dcb885d2431 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 19 Jun 2012 19:31:48 +0100 Subject: ALSA: Add missing include of pcm.h to pcm_params.h There's a dependency but no #include. Signed-off-by: Mark Brown Signed-off-by: Takashi Iwai --- include/sound/pcm_params.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/sound/pcm_params.h b/include/sound/pcm_params.h index f494f1e3c900..37ae12e0ab06 100644 --- a/include/sound/pcm_params.h +++ b/include/sound/pcm_params.h @@ -22,6 +22,8 @@ * */ +#include + int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var, int *dir); -- cgit v1.2.3 From 3052cc2c92f11875d111d5b7b9b3ad535b3128b9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 11 Jun 2012 20:11:40 +0200 Subject: dmaengine: Add wrapper for device_tx_status callback This patch adds a small inline wrapper for the devivce_tx_status callback of a dma device. This makes the source code of users of this function a bit more compact and a bit more legible. E.g.: -status = chan->device->device_tx_status(chan, cookie, &state) +status = dmaengine_tx_status(chan, cookie, &state) Signed-off-by: Lars-Peter Clausen Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index ccec62f8e501..9c02a4508b25 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -674,6 +674,12 @@ static inline int dmaengine_resume(struct dma_chan *chan) return dmaengine_device_control(chan, DMA_RESUME, 0); } +static inline enum dma_status dmaengine_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *state) +{ + return chan->device->device_tx_status(chan, cookie, state); +} + static inline dma_cookie_t dmaengine_submit(struct dma_async_tx_descriptor *desc) { return desc->tx_submit(desc); -- cgit v1.2.3 From c32c44cb58d212513243744878423abd207bc8a8 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 11 Jun 2012 20:11:40 +0200 Subject: dmaengine: Add wrapper for device_tx_status callback This patch adds a small inline wrapper for the devivce_tx_status callback of a dma device. This makes the source code of users of this function a bit more compact and a bit more legible. E.g.: -status = chan->device->device_tx_status(chan, cookie, &state) +status = dmaengine_tx_status(chan, cookie, &state) Signed-off-by: Lars-Peter Clausen Acked-by Vinod Koul Signed-off-by: Mark Brown --- include/linux/dmaengine.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 56377df39124..cc0756a35ae3 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -670,6 +670,12 @@ static inline int dmaengine_resume(struct dma_chan *chan) return dmaengine_device_control(chan, DMA_RESUME, 0); } +static inline enum dma_status dmaengine_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *state) +{ + return chan->device->device_tx_status(chan, cookie, state); +} + static inline dma_cookie_t dmaengine_submit(struct dma_async_tx_descriptor *desc) { return desc->tx_submit(desc); -- cgit v1.2.3 From 9883ab229d61b884323f9186b1bd4a41373a491b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 11 Jun 2012 20:11:41 +0200 Subject: ASoC: dmaengine-pcm: Rename and deprecate snd_dmaengine_pcm_pointer Currently the sound dmaengine pcm helper functions implement the pcm_pointer callback by trying to count the number of elapsed periods. This is done by advancing the stream position in the dmaengine callback by one period. Unfortunately there is no guarantee that the callback will be called for each elapsed period. It may be possible that under high system load it is only called once for multiple elapsed periods. This patch renames the current implementation and documents its shortcomings and that it should not be used anymore in new drivers. The next patch will introduce a new snd_dmaengine_pcm_pointer which will be implemented based on querying the current stream position from the dma device. Signed-off-by: Lars-Peter Clausen Acked-by Vinod Koul Acked-by: Dong Aisheng --- include/sound/dmaengine_pcm.h | 2 +- sound/soc/ep93xx/ep93xx-pcm.c | 2 +- sound/soc/fsl/imx-pcm-dma.c | 2 +- sound/soc/mxs/mxs-pcm.c | 2 +- sound/soc/soc-dmaengine-pcm.c | 10 +++++----- sound/soc/ux500/ux500_pcm.c | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index a8fcaa6d531f..ea5791583fed 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -38,7 +38,7 @@ void *snd_dmaengine_pcm_get_data(struct snd_pcm_substream *substream); int snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream, const struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config); int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd); -snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream); +snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream); int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, dma_filter_fn filter_fn, void *filter_data); diff --git a/sound/soc/ep93xx/ep93xx-pcm.c b/sound/soc/ep93xx/ep93xx-pcm.c index 162dbb74f4cc..4eea98b42bc8 100644 --- a/sound/soc/ep93xx/ep93xx-pcm.c +++ b/sound/soc/ep93xx/ep93xx-pcm.c @@ -136,7 +136,7 @@ static struct snd_pcm_ops ep93xx_pcm_ops = { .hw_params = ep93xx_pcm_hw_params, .hw_free = ep93xx_pcm_hw_free, .trigger = snd_dmaengine_pcm_trigger, - .pointer = snd_dmaengine_pcm_pointer, + .pointer = snd_dmaengine_pcm_pointer_no_residue, .mmap = ep93xx_pcm_mmap, }; diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c index f3c0a5ef35c8..48f9d886f020 100644 --- a/sound/soc/fsl/imx-pcm-dma.c +++ b/sound/soc/fsl/imx-pcm-dma.c @@ -141,7 +141,7 @@ static struct snd_pcm_ops imx_pcm_ops = { .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_imx_pcm_hw_params, .trigger = snd_dmaengine_pcm_trigger, - .pointer = snd_dmaengine_pcm_pointer, + .pointer = snd_dmaengine_pcm_pointer_no_residue, .mmap = snd_imx_pcm_mmap, }; diff --git a/sound/soc/mxs/mxs-pcm.c b/sound/soc/mxs/mxs-pcm.c index 373dec90579f..f82d766cbf9e 100644 --- a/sound/soc/mxs/mxs-pcm.c +++ b/sound/soc/mxs/mxs-pcm.c @@ -141,7 +141,7 @@ static struct snd_pcm_ops mxs_pcm_ops = { .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_mxs_pcm_hw_params, .trigger = snd_dmaengine_pcm_trigger, - .pointer = snd_dmaengine_pcm_pointer, + .pointer = snd_dmaengine_pcm_pointer_no_residue, .mmap = snd_mxs_pcm_mmap, }; diff --git a/sound/soc/soc-dmaengine-pcm.c b/sound/soc/soc-dmaengine-pcm.c index 475695234b3d..7c0877e3731c 100644 --- a/sound/soc/soc-dmaengine-pcm.c +++ b/sound/soc/soc-dmaengine-pcm.c @@ -200,18 +200,18 @@ int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd) EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_trigger); /** - * snd_dmaengine_pcm_pointer - dmaengine based PCM pointer implementation + * snd_dmaengine_pcm_pointer_no_residue - dmaengine based PCM pointer implementation * @substream: PCM substream * - * This function can be used as the PCM pointer callback for dmaengine based PCM - * driver implementations. + * This function is deprecated and should not be used by new drivers, as its + * results may be unreliable. */ -snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream) +snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream) { struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); return bytes_to_frames(substream->runtime, prtd->pos); } -EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer); +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer_no_residue); static int dmaengine_pcm_request_channel(struct dmaengine_pcm_runtime_data *prtd, dma_filter_fn filter_fn, void *filter_data) diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c index 97d8e4de29c2..1a04e248453c 100644 --- a/sound/soc/ux500/ux500_pcm.c +++ b/sound/soc/ux500/ux500_pcm.c @@ -261,7 +261,7 @@ static struct snd_pcm_ops ux500_pcm_ops = { .hw_params = ux500_pcm_hw_params, .hw_free = ux500_pcm_hw_free, .trigger = snd_dmaengine_pcm_trigger, - .pointer = snd_dmaengine_pcm_pointer, + .pointer = snd_dmaengine_pcm_pointer_no_residue, .mmap = ux500_pcm_mmap }; -- cgit v1.2.3 From 3528f27a5d4ac299e2d8cbe7297c1e9edd601ee6 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 11 Jun 2012 20:11:42 +0200 Subject: ASoC: dmaengine-pcm: Add support for querying stream position from DMA driver Currently the sound dmaengine pcm helper functions implement the pcm_pointer callback by trying to count the number of elapsed periods. This is done by advancing the stream position in the dmaengine callback by one period. Unfortunately there is no guarantee that the callback will be called for each elapsed period. It may be possible that under high system load it is only called once for multiple elapsed periods. This patch addresses the issue by implementing support for querying the current stream position directly from the dmaengine driver. Since not all dmaengine drivers support reporting the stream position yet the old period counting implementation is kept for now. Furthermore the new mechanism allows to report the stream position with a sub-period granularity, given that the dmaengine driver supports this. Signed-off-by: Lars-Peter Clausen Acked-by: Vinod Koul Signed-off-by: Mark Brown --- include/sound/dmaengine_pcm.h | 1 + sound/soc/soc-dmaengine-pcm.c | 29 ++++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index ea5791583fed..b877334bbb0f 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -38,6 +38,7 @@ void *snd_dmaengine_pcm_get_data(struct snd_pcm_substream *substream); int snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream, const struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config); int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd); +snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream); snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream); int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, diff --git a/sound/soc/soc-dmaengine-pcm.c b/sound/soc/soc-dmaengine-pcm.c index 7c0877e3731c..2995334d8000 100644 --- a/sound/soc/soc-dmaengine-pcm.c +++ b/sound/soc/soc-dmaengine-pcm.c @@ -30,6 +30,7 @@ struct dmaengine_pcm_runtime_data { struct dma_chan *dma_chan; + dma_cookie_t cookie; unsigned int pos; @@ -153,7 +154,7 @@ static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream) desc->callback = dmaengine_pcm_dma_complete; desc->callback_param = substream; - dmaengine_submit(desc); + prtd->cookie = dmaengine_submit(desc); return 0; } @@ -213,6 +214,32 @@ snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream } EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer_no_residue); +/** + * snd_dmaengine_pcm_pointer - dmaengine based PCM pointer implementation + * @substream: PCM substream + * + * This function can be used as the PCM pointer callback for dmaengine based PCM + * driver implementations. + */ +snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + struct dma_tx_state state; + enum dma_status status; + unsigned int buf_size; + unsigned int pos = 0; + + status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state); + if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) { + buf_size = snd_pcm_lib_buffer_bytes(substream); + if (state.residue > 0 && state.residue <= buf_size) + pos = buf_size - state.residue; + } + + return bytes_to_frames(substream->runtime, pos); +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer); + static int dmaengine_pcm_request_channel(struct dmaengine_pcm_runtime_data *prtd, dma_filter_fn filter_fn, void *filter_data) { -- cgit v1.2.3 From 924d37118f9e18825294b2012a10c6245d6c25e1 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Mon, 18 Jun 2012 19:15:50 -0700 Subject: pstore/ram: Probe as early as possible Registering the platform driver before module_init allows us to log oopses that happen during device probing. This requires changing module_init to postcore_initcall, and switching from platform_driver_probe to platform_driver_register because the platform device is not registered when the platform driver is registered; and because we use driver_register, now can't use create_bundle() (since it will try to register the same driver once again), so we have to switch to platform_device_register_data(). Also, some __init -> __devinit changes were needed. Overall, the registration logic is now much clearer, since we have only one driver registration point, and just an optional dummy device, which is created from the module parameters. Suggested-by: Colin Cross Signed-off-by: Anton Vorontsov Acked-by: Kees Cook Signed-off-by: Greg Kroah-Hartman --- fs/pstore/ram.c | 63 +++++++++++++++++++++++----------------------- fs/pstore/ram_core.c | 9 ++++--- include/linux/pstore_ram.h | 6 ++--- 3 files changed, 40 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index c7acf94ff475..0b36e91978e6 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -330,7 +330,7 @@ static int ramoops_init_prz(struct device *dev, struct ramoops_context *cxt, return 0; } -static int __init ramoops_probe(struct platform_device *pdev) +static int __devinit ramoops_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ramoops_platform_data *pdata = pdev->dev.platform_data; @@ -452,6 +452,7 @@ static int __exit ramoops_remove(struct platform_device *pdev) } static struct platform_driver ramoops_driver = { + .probe = ramoops_probe, .remove = __exit_p(ramoops_remove), .driver = { .name = "ramoops", @@ -459,46 +460,46 @@ static struct platform_driver ramoops_driver = { }, }; -static int __init ramoops_init(void) +static void ramoops_register_dummy(void) { - int ret; - ret = platform_driver_probe(&ramoops_driver, ramoops_probe); - if (ret == -ENODEV) { - /* - * If we didn't find a platform device, we use module parameters - * building platform data on the fly. - */ - pr_info("platform device not found, using module parameters\n"); - dummy_data = kzalloc(sizeof(struct ramoops_platform_data), - GFP_KERNEL); - if (!dummy_data) - return -ENOMEM; - dummy_data->mem_size = mem_size; - dummy_data->mem_address = mem_address; - dummy_data->record_size = record_size; - dummy_data->console_size = ramoops_console_size; - dummy_data->dump_oops = dump_oops; - dummy_data->ecc = ramoops_ecc; - dummy = platform_create_bundle(&ramoops_driver, ramoops_probe, - NULL, 0, dummy_data, - sizeof(struct ramoops_platform_data)); - - if (IS_ERR(dummy)) - ret = PTR_ERR(dummy); - else - ret = 0; + if (!mem_size) + return; + + pr_info("using module parameters\n"); + + dummy_data = kzalloc(sizeof(*dummy_data), GFP_KERNEL); + if (!dummy_data) { + pr_info("could not allocate pdata\n"); + return; } - return ret; + dummy_data->mem_size = mem_size; + dummy_data->mem_address = mem_address; + dummy_data->record_size = record_size; + dummy_data->console_size = ramoops_console_size; + dummy_data->dump_oops = dump_oops; + dummy_data->ecc = ramoops_ecc; + + dummy = platform_device_register_data(NULL, "ramoops", -1, + dummy_data, sizeof(struct ramoops_platform_data)); + if (IS_ERR(dummy)) { + pr_info("could not create platform device: %ld\n", + PTR_ERR(dummy)); + } +} + +static int __init ramoops_init(void) +{ + ramoops_register_dummy(); + return platform_driver_register(&ramoops_driver); } +postcore_initcall(ramoops_init); static void __exit ramoops_exit(void) { platform_driver_unregister(&ramoops_driver); kfree(dummy_data); } - -module_init(ramoops_init); module_exit(ramoops_exit); MODULE_LICENSE("GPL"); diff --git a/fs/pstore/ram_core.c b/fs/pstore/ram_core.c index 0fd81611525c..26531856daf8 100644 --- a/fs/pstore/ram_core.c +++ b/fs/pstore/ram_core.c @@ -390,7 +390,8 @@ static int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size, return 0; } -static int __init persistent_ram_post_init(struct persistent_ram_zone *prz, bool ecc) +static int __devinit persistent_ram_post_init(struct persistent_ram_zone *prz, + bool ecc) { int ret; @@ -436,9 +437,9 @@ void persistent_ram_free(struct persistent_ram_zone *prz) kfree(prz); } -struct persistent_ram_zone * __init persistent_ram_new(phys_addr_t start, - size_t size, - bool ecc) +struct persistent_ram_zone * __devinit persistent_ram_new(phys_addr_t start, + size_t size, + bool ecc) { struct persistent_ram_zone *prz; int ret = -ENOMEM; diff --git a/include/linux/pstore_ram.h b/include/linux/pstore_ram.h index 2470bb591434..e681af92c04b 100644 --- a/include/linux/pstore_ram.h +++ b/include/linux/pstore_ram.h @@ -48,9 +48,9 @@ struct persistent_ram_zone { size_t old_log_size; }; -struct persistent_ram_zone * __init persistent_ram_new(phys_addr_t start, - size_t size, - bool ecc); +struct persistent_ram_zone * __devinit persistent_ram_new(phys_addr_t start, + size_t size, + bool ecc); void persistent_ram_free(struct persistent_ram_zone *prz); void persistent_ram_zap(struct persistent_ram_zone *prz); -- cgit v1.2.3 From 3a9cf8efd7b64f26f1e0f02afb70382f90cc11ca Mon Sep 17 00:00:00 2001 From: Rajeev Kumar Date: Thu, 21 Jun 2012 15:54:51 +0530 Subject: ASoC: Add support for synopsys i2s controller as per ASoC framework. This patch add support for synopsys I2S controller as per the ASoC framework. Signed-off-by: Rajeev Kumar Signed-off-by: Mark Brown --- include/sound/designware_i2s.h | 69 +++++++ sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/dwc/Kconfig | 8 + sound/soc/dwc/Makefile | 3 + sound/soc/dwc/designware_i2s.c | 454 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 536 insertions(+) create mode 100644 include/sound/designware_i2s.h create mode 100644 sound/soc/dwc/Kconfig create mode 100644 sound/soc/dwc/Makefile create mode 100644 sound/soc/dwc/designware_i2s.c (limited to 'include') diff --git a/include/sound/designware_i2s.h b/include/sound/designware_i2s.h new file mode 100644 index 000000000000..26f406e0f673 --- /dev/null +++ b/include/sound/designware_i2s.h @@ -0,0 +1,69 @@ +/* + * Copyright (ST) 2012 Rajeev Kumar (rajeev-dlh.kumar@st.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. + * + * 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 __SOUND_DESIGNWARE_I2S_H +#define __SOUND_DESIGNWARE_I2S_H + +#include +#include + +/* + * struct i2s_clk_config_data - represent i2s clk configuration data + * @chan_nr: number of channel + * @data_width: number of bits per sample (8/16/24/32 bit) + * @sample_rate: sampling frequency (8Khz, 16Khz, 32Khz, 44Khz, 48Khz) + */ +struct i2s_clk_config_data { + int chan_nr; + u32 data_width; + u32 sample_rate; +}; + +struct i2s_platform_data { + #define DWC_I2S_PLAY (1 << 0) + #define DWC_I2S_RECORD (1 << 1) + unsigned int cap; + int channel; + u32 snd_fmts; + u32 snd_rates; + + void *play_dma_data; + void *capture_dma_data; + bool (*filter)(struct dma_chan *chan, void *slave); + int (*i2s_clk_cfg)(struct i2s_clk_config_data *config); +}; + +struct i2s_dma_data { + void *data; + dma_addr_t addr; + u32 max_burst; + enum dma_slave_buswidth addr_width; + bool (*filter)(struct dma_chan *chan, void *slave); +}; + +/* I2S DMA registers */ +#define I2S_RXDMA 0x01C0 +#define I2S_TXDMA 0x01C8 + +#define TWO_CHANNEL_SUPPORT 2 /* up to 2.0 */ +#define FOUR_CHANNEL_SUPPORT 4 /* up to 3.1 */ +#define SIX_CHANNEL_SUPPORT 6 /* up to 5.1 */ +#define EIGHT_CHANNEL_SUPPORT 8 /* up to 7.1 */ + +#endif /* __SOUND_DESIGNWARE_I2S_H */ diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 40b2ad1bb1cd..c5de0a84566f 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -33,6 +33,7 @@ source "sound/soc/atmel/Kconfig" source "sound/soc/au1x/Kconfig" source "sound/soc/blackfin/Kconfig" source "sound/soc/davinci/Kconfig" +source "sound/soc/dwc/Kconfig" source "sound/soc/ep93xx/Kconfig" source "sound/soc/fsl/Kconfig" source "sound/soc/jz4740/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 70990f4017f4..00a555a743b6 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_SND_SOC) += atmel/ obj-$(CONFIG_SND_SOC) += au1x/ obj-$(CONFIG_SND_SOC) += blackfin/ obj-$(CONFIG_SND_SOC) += davinci/ +obj-$(CONFIG_SND_SOC) += dwc/ obj-$(CONFIG_SND_SOC) += ep93xx/ obj-$(CONFIG_SND_SOC) += fsl/ obj-$(CONFIG_SND_SOC) += jz4740/ diff --git a/sound/soc/dwc/Kconfig b/sound/soc/dwc/Kconfig new file mode 100644 index 000000000000..93e9fc33560c --- /dev/null +++ b/sound/soc/dwc/Kconfig @@ -0,0 +1,8 @@ +config SND_DESIGNWARE_I2S + tristate "Synopsys I2S Device Driver" + help + Say Y or M if you want to add support for I2S driver for + Synopsys desigwnware I2S device. The device supports upto + maximum of 8 channels each for play and record. + + diff --git a/sound/soc/dwc/Makefile b/sound/soc/dwc/Makefile new file mode 100644 index 000000000000..319371f690f4 --- /dev/null +++ b/sound/soc/dwc/Makefile @@ -0,0 +1,3 @@ +# SYNOPSYS Platform Support +obj-$(CONFIG_SND_DESIGNWARE_I2S) += designware_i2s.o + diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c new file mode 100644 index 000000000000..e667e2b45e67 --- /dev/null +++ b/sound/soc/dwc/designware_i2s.c @@ -0,0 +1,454 @@ +/* + * ALSA SoC Synopsys I2S Audio Layer + * + * sound/soc/spear/designware_i2s.c + * + * Copyright (C) 2010 ST Microelectronics + * Rajeev Kumar + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* common register for all channel */ +#define IER 0x000 +#define IRER 0x004 +#define ITER 0x008 +#define CER 0x00C +#define CCR 0x010 +#define RXFFR 0x014 +#define TXFFR 0x018 + +/* I2STxRxRegisters for all channels */ +#define LRBR_LTHR(x) (0x40 * x + 0x020) +#define RRBR_RTHR(x) (0x40 * x + 0x024) +#define RER(x) (0x40 * x + 0x028) +#define TER(x) (0x40 * x + 0x02C) +#define RCR(x) (0x40 * x + 0x030) +#define TCR(x) (0x40 * x + 0x034) +#define ISR(x) (0x40 * x + 0x038) +#define IMR(x) (0x40 * x + 0x03C) +#define ROR(x) (0x40 * x + 0x040) +#define TOR(x) (0x40 * x + 0x044) +#define RFCR(x) (0x40 * x + 0x048) +#define TFCR(x) (0x40 * x + 0x04C) +#define RFF(x) (0x40 * x + 0x050) +#define TFF(x) (0x40 * x + 0x054) + +/* I2SCOMPRegisters */ +#define I2S_COMP_PARAM_2 0x01F0 +#define I2S_COMP_PARAM_1 0x01F4 +#define I2S_COMP_VERSION 0x01F8 +#define I2S_COMP_TYPE 0x01FC + +#define MAX_CHANNEL_NUM 8 +#define MIN_CHANNEL_NUM 2 + +struct dw_i2s_dev { + void __iomem *i2s_base; + struct clk *clk; + int active; + unsigned int capability; + struct device *dev; + + /* data related to DMA transfers b/w i2s and DMAC */ + struct i2s_dma_data play_dma_data; + struct i2s_dma_data capture_dma_data; + struct i2s_clk_config_data config; + int (*i2s_clk_cfg)(struct i2s_clk_config_data *config); +}; + +static inline void i2s_write_reg(void *io_base, int reg, u32 val) +{ + writel(val, io_base + reg); +} + +static inline u32 i2s_read_reg(void *io_base, int reg) +{ + return readl(io_base + reg); +} + +static inline void i2s_disable_channels(struct dw_i2s_dev *dev, u32 stream) +{ + u32 i = 0; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + for (i = 0; i < 4; i++) + i2s_write_reg(dev->i2s_base, TER(i), 0); + } else { + for (i = 0; i < 4; i++) + i2s_write_reg(dev->i2s_base, RER(i), 0); + } +} + +static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream) +{ + u32 i = 0; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + for (i = 0; i < 4; i++) + i2s_write_reg(dev->i2s_base, TOR(i), 0); + } else { + for (i = 0; i < 4; i++) + i2s_write_reg(dev->i2s_base, ROR(i), 0); + } +} + +void i2s_start(struct dw_i2s_dev *dev, struct snd_pcm_substream *substream) +{ + + i2s_write_reg(dev->i2s_base, IER, 1); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + i2s_write_reg(dev->i2s_base, ITER, 1); + else + i2s_write_reg(dev->i2s_base, IRER, 1); + + i2s_write_reg(dev->i2s_base, CER, 1); +} + +static void i2s_stop(struct dw_i2s_dev *dev, + struct snd_pcm_substream *substream) +{ + u32 i = 0, irq; + + i2s_clear_irqs(dev, substream->stream); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + i2s_write_reg(dev->i2s_base, ITER, 0); + + for (i = 0; i < 4; i++) { + irq = i2s_read_reg(dev->i2s_base, IMR(i)); + i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x30); + } + } else { + i2s_write_reg(dev->i2s_base, IRER, 0); + + for (i = 0; i < 4; i++) { + irq = i2s_read_reg(dev->i2s_base, IMR(i)); + i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x03); + } + } + + if (!dev->active) { + i2s_write_reg(dev->i2s_base, CER, 0); + i2s_write_reg(dev->i2s_base, IER, 0); + } +} + +static int dw_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); + struct i2s_dma_data *dma_data = NULL; + + if (!(dev->capability & DWC_I2S_RECORD) && + (substream->stream == SNDRV_PCM_STREAM_CAPTURE)) + return -EINVAL; + + if (!(dev->capability & DWC_I2S_PLAY) && + (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) + return -EINVAL; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dma_data = &dev->play_dma_data; + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + dma_data = &dev->capture_dma_data; + + snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)dma_data); + + return 0; +} + +static int dw_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + struct i2s_clk_config_data *config = &dev->config; + u32 ccr, xfer_resolution, ch_reg, irq; + int ret; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + config->data_width = 16; + ccr = 0x00; + xfer_resolution = 0x02; + break; + + case SNDRV_PCM_FORMAT_S24_LE: + config->data_width = 24; + ccr = 0x08; + xfer_resolution = 0x04; + break; + + case SNDRV_PCM_FORMAT_S32_LE: + config->data_width = 32; + ccr = 0x10; + xfer_resolution = 0x05; + break; + + default: + dev_err(dev->dev, "designware-i2s: unsuppted PCM fmt"); + return -EINVAL; + } + + config->chan_nr = params_channels(params); + + switch (config->chan_nr) { + case EIGHT_CHANNEL_SUPPORT: + ch_reg = 3; + case SIX_CHANNEL_SUPPORT: + ch_reg = 2; + case FOUR_CHANNEL_SUPPORT: + ch_reg = 1; + case TWO_CHANNEL_SUPPORT: + ch_reg = 0; + break; + default: + dev_err(dev->dev, "channel not supported\n"); + } + + i2s_disable_channels(dev, substream->stream); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + i2s_write_reg(dev->i2s_base, TCR(ch_reg), xfer_resolution); + i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02); + irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); + i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30); + i2s_write_reg(dev->i2s_base, TER(ch_reg), 1); + } else { + i2s_write_reg(dev->i2s_base, RCR(ch_reg), xfer_resolution); + i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07); + irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); + i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03); + i2s_write_reg(dev->i2s_base, RER(ch_reg), 1); + } + + i2s_write_reg(dev->i2s_base, CCR, ccr); + + config->sample_rate = params_rate(params); + + if (!dev->i2s_clk_cfg) + return -EINVAL; + + ret = dev->i2s_clk_cfg(config); + if (ret < 0) { + dev_err(dev->dev, "runtime audio clk config fail\n"); + return ret; + } + + return 0; +} + +static void dw_i2s_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int dw_i2s_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + dev->active++; + i2s_start(dev, substream); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + dev->active--; + i2s_stop(dev, substream); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static struct snd_soc_dai_ops dw_i2s_dai_ops = { + .startup = dw_i2s_startup, + .shutdown = dw_i2s_shutdown, + .hw_params = dw_i2s_hw_params, + .trigger = dw_i2s_trigger, +}; + +#ifdef CONFIG_PM + +static int dw_i2s_suspend(struct snd_soc_dai *dai) +{ + struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + + clk_disable(dev->clk); + return 0; +} + +static int dw_i2s_resume(struct snd_soc_dai *dai) +{ + struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + + clk_enable(dev->clk); + return 0; +} + +#else +#define dw_i2s_suspend NULL +#define dw_i2s_resume NULL +#endif + +static int dw_i2s_probe(struct platform_device *pdev) +{ + const struct i2s_platform_data *pdata = pdev->dev.platform_data; + struct dw_i2s_dev *dev; + struct resource *res; + int ret; + unsigned int cap; + struct snd_soc_dai_driver *dw_i2s_dai; + + if (!pdata) { + dev_err(&pdev->dev, "Invalid platform data\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no i2s resource defined\n"); + return -ENODEV; + } + + if (!devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "i2s region already claimed\n"); + return -EBUSY; + } + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) { + dev_warn(&pdev->dev, "kzalloc fail\n"); + return -ENOMEM; + } + + dev->i2s_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!dev->i2s_base) { + dev_err(&pdev->dev, "ioremap fail for i2s_region\n"); + return -ENOMEM; + } + + cap = pdata->cap; + dev->capability = cap; + dev->i2s_clk_cfg = pdata->i2s_clk_cfg; + + /* Set DMA slaves info */ + + dev->play_dma_data.data = pdata->play_dma_data; + dev->capture_dma_data.data = pdata->capture_dma_data; + dev->play_dma_data.addr = res->start + I2S_TXDMA; + dev->capture_dma_data.addr = res->start + I2S_RXDMA; + dev->play_dma_data.max_burst = 16; + dev->capture_dma_data.max_burst = 16; + dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + dev->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + dev->play_dma_data.filter = pdata->filter; + dev->capture_dma_data.filter = pdata->filter; + + dev->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(dev->clk)) + return PTR_ERR(dev->clk); + + ret = clk_enable(dev->clk); + if (ret < 0) + goto err_clk_put; + + dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL); + if (!dw_i2s_dai) { + dev_err(&pdev->dev, "mem allocation failed for dai driver\n"); + ret = -ENOMEM; + goto err_clk_disable; + } + + if (cap & DWC_I2S_PLAY) { + dev_dbg(&pdev->dev, " SPEAr: play supported\n"); + dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM; + dw_i2s_dai->playback.channels_max = pdata->channel; + dw_i2s_dai->playback.formats = pdata->snd_fmts; + dw_i2s_dai->playback.rates = pdata->snd_rates; + } + + if (cap & DWC_I2S_RECORD) { + dev_dbg(&pdev->dev, "SPEAr: record supported\n"); + dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM; + dw_i2s_dai->capture.channels_max = pdata->channel; + dw_i2s_dai->capture.formats = pdata->snd_fmts; + dw_i2s_dai->capture.rates = pdata->snd_rates; + } + + dw_i2s_dai->ops = &dw_i2s_dai_ops; + dw_i2s_dai->suspend = dw_i2s_suspend; + dw_i2s_dai->resume = dw_i2s_resume; + + dev->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, dev); + ret = snd_soc_register_dai(&pdev->dev, dw_i2s_dai); + if (ret != 0) { + dev_err(&pdev->dev, "not able to register dai\n"); + goto err_set_drvdata; + } + + return 0; + +err_set_drvdata: + dev_set_drvdata(&pdev->dev, NULL); +err_clk_disable: + clk_disable(dev->clk); +err_clk_put: + clk_put(dev->clk); + return ret; +} + +static int dw_i2s_remove(struct platform_device *pdev) +{ + struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev); + + snd_soc_unregister_dai(&pdev->dev); + dev_set_drvdata(&pdev->dev, NULL); + + clk_put(dev->clk); + + return 0; +} + +static struct platform_driver dw_i2s_driver = { + .probe = dw_i2s_probe, + .remove = dw_i2s_remove, + .driver = { + .name = "designware-i2s", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(dw_i2s_driver); + +MODULE_AUTHOR("Rajeev Kumar "); +MODULE_DESCRIPTION("DESIGNWARE I2S SoC Interface"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:designware_i2s"); -- cgit v1.2.3 From 241b446f30de171b627524c107ce03e5ecee0124 Mon Sep 17 00:00:00 2001 From: Rajeev Kumar Date: Thu, 21 Jun 2012 15:54:52 +0530 Subject: ASoC: Add support for SPEAr ASoC pcm layer. This patch add support for the SPEAr ASoC pcm layer in ASoC framework. The pcm layer uses common snd_dmaengine framework. Signed-off-by: Rajeev Kumar Signed-off-by: Mark Brown --- include/sound/spear_dma.h | 35 ++++++++ sound/soc/spear/spear_pcm.c | 214 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 249 insertions(+) create mode 100644 include/sound/spear_dma.h create mode 100644 sound/soc/spear/spear_pcm.c (limited to 'include') diff --git a/include/sound/spear_dma.h b/include/sound/spear_dma.h new file mode 100644 index 000000000000..1b365bfdfb37 --- /dev/null +++ b/include/sound/spear_dma.h @@ -0,0 +1,35 @@ +/* +* linux/spear_dma.h +* +* Copyright (ST) 2012 Rajeev Kumar (rajeev-dlh.kumar@st.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. +* +* 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 SPEAR_DMA_H +#define SPEAR_DMA_H + +#include + +struct spear_dma_data { + void *data; + dma_addr_t addr; + u32 max_burst; + enum dma_slave_buswidth addr_width; + bool (*filter)(struct dma_chan *chan, void *slave); +}; + +#endif /* SPEAR_DMA_H */ diff --git a/sound/soc/spear/spear_pcm.c b/sound/soc/spear/spear_pcm.c new file mode 100644 index 000000000000..97c2cac8e92c --- /dev/null +++ b/sound/soc/spear/spear_pcm.c @@ -0,0 +1,214 @@ +/* + * ALSA PCM interface for ST SPEAr Processors + * + * sound/soc/spear/spear_pcm.c + * + * Copyright (C) 2012 ST Microelectronics + * Rajeev Kumar + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct snd_pcm_hardware spear_pcm_hardware = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .buffer_bytes_max = 16 * 1024, /* max buffer size */ + .period_bytes_min = 2 * 1024, /* 1 msec data minimum period size */ + .period_bytes_max = 2 * 1024, /* maximum period size */ + .periods_min = 1, /* min # periods */ + .periods_max = 8, /* max # of periods */ + .fifo_size = 0, /* fifo size in bytes */ +}; + +static int spear_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + return 0; +} + +static int spear_pcm_hw_free(struct snd_pcm_substream *substream) +{ + snd_pcm_set_runtime_buffer(substream, NULL); + + return 0; +} + +static int spear_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + struct spear_dma_data *dma_data = (struct spear_dma_data *) + snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + int ret; + + ret = snd_soc_set_runtime_hwparams(substream, &spear_pcm_hardware); + if (ret) + return ret; + + ret = snd_dmaengine_pcm_open(substream, dma_data->filter, dma_data); + if (ret) + return ret; + + snd_dmaengine_pcm_set_data(substream, dma_data); + + return 0; +} + +static int spear_pcm_close(struct snd_pcm_substream *substream) +{ + + snd_dmaengine_pcm_close(substream); + + return 0; +} + +static int spear_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + return dma_mmap_writecombine(substream->pcm->card->dev, vma, + runtime->dma_area, runtime->dma_addr, + runtime->dma_bytes); +} + +static struct snd_pcm_ops spear_pcm_ops = { + .open = spear_pcm_open, + .close = spear_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = spear_pcm_hw_params, + .hw_free = spear_pcm_hw_free, + .trigger = snd_dmaengine_pcm_trigger, + .pointer = snd_dmaengine_pcm_pointer, + .mmap = spear_pcm_mmap, +}; + +static int +spear_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream, + size_t size) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + + buf->area = dma_alloc_writecombine(pcm->card->dev, size, + &buf->addr, GFP_KERNEL); + if (!buf->area) + return -ENOMEM; + + dev_info(buf->dev.dev, + " preallocate_dma_buffer: area=%p, addr=%p, size=%d\n", + (void *)buf->area, (void *)buf->addr, size); + + buf->bytes = size; + return 0; +} + +static void spear_pcm_free(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + struct snd_dma_buffer *buf; + int stream; + + for (stream = 0; stream < 2; stream++) { + substream = pcm->streams[stream].substream; + if (!substream) + continue; + + buf = &substream->dma_buffer; + if (!buf && !buf->area) + continue; + + dma_free_writecombine(pcm->card->dev, buf->bytes, + buf->area, buf->addr); + buf->area = NULL; + } +} + +static u64 spear_pcm_dmamask = DMA_BIT_MASK(32); + +static int spear_pcm_new(struct snd_card *card, + struct snd_soc_dai *dai, struct snd_pcm *pcm) +{ + int ret; + + if (!card->dev->dma_mask) + card->dev->dma_mask = &spear_pcm_dmamask; + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + + if (dai->driver->playback.channels_min) { + ret = spear_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_PLAYBACK, + spear_pcm_hardware.buffer_bytes_max); + if (ret) + return ret; + } + + if (dai->driver->capture.channels_min) { + ret = spear_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_CAPTURE, + spear_pcm_hardware.buffer_bytes_max); + if (ret) + return ret; + } + + return 0; +} + +struct snd_soc_platform_driver spear_soc_platform = { + .ops = &spear_pcm_ops, + .pcm_new = spear_pcm_new, + .pcm_free = spear_pcm_free, +}; + +static int __devinit spear_soc_platform_probe(struct platform_device *pdev) +{ + return snd_soc_register_platform(&pdev->dev, &spear_soc_platform); +} + +static int __devexit spear_soc_platform_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + + return 0; +} + +static struct platform_driver spear_pcm_driver = { + .driver = { + .name = "spear-pcm-audio", + .owner = THIS_MODULE, + }, + + .probe = spear_soc_platform_probe, + .remove = __devexit_p(spear_soc_platform_remove), +}; + +module_platform_driver(spear_pcm_driver); + +MODULE_AUTHOR("Rajeev Kumar "); +MODULE_DESCRIPTION("SPEAr PCM DMA module"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:spear-pcm-audio"); -- cgit v1.2.3 From d59315ca8c0de00df9b363f94a2641a30961ca1c Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 21 Jun 2012 12:49:23 -0700 Subject: libceph: drop ceph_con_get/put helpers and nref member These are no longer used. Every ceph_connection instance is embedded in another structure, and refcounts manipulated via the get/put ops. Signed-off-by: Sage Weil --- include/linux/ceph/messenger.h | 1 - net/ceph/messenger.c | 28 +--------------------------- 2 files changed, 1 insertion(+), 28 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index a334dbd1b324..cc6f9bdcf466 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -135,7 +135,6 @@ struct ceph_msg_pos { */ struct ceph_connection { void *private; - atomic_t nref; const struct ceph_connection_operations *ops; diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index fc0cee7c9aa2..ab690e2e1206 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -500,30 +500,6 @@ bool ceph_con_opened(struct ceph_connection *con) return con->connect_seq > 0; } -/* - * generic get/put - */ -struct ceph_connection *ceph_con_get(struct ceph_connection *con) -{ - int nref = __atomic_add_unless(&con->nref, 1, 0); - - dout("con_get %p nref = %d -> %d\n", con, nref, nref + 1); - - return nref ? con : NULL; -} - -void ceph_con_put(struct ceph_connection *con) -{ - int nref = atomic_dec_return(&con->nref); - - BUG_ON(nref < 0); - if (nref == 0) { - BUG_ON(con->sock); - kfree(con); - } - dout("con_put %p nref = %d -> %d\n", con, nref + 1, nref); -} - /* * initialize a new connection. */ @@ -535,7 +511,6 @@ void ceph_con_init(struct ceph_connection *con, void *private, memset(con, 0, sizeof(*con)); con->private = private; con->ops = ops; - atomic_set(&con->nref, 1); con->msgr = msgr; con_sock_state_init(con); @@ -1951,8 +1926,7 @@ static int try_write(struct ceph_connection *con) { int ret = 1; - dout("try_write start %p state %lu nref %d\n", con, con->state, - atomic_read(&con->nref)); + dout("try_write start %p state %lu\n", con, con->state); more: dout("try_write out_kvec_bytes %d\n", con->out_kvec_bytes); -- cgit v1.2.3 From f4b57a3b4352f72e461e362cb25917e28bdba80f Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 22 Jun 2012 14:55:16 +0800 Subject: PCI/ACPI: provide MMCONFIG address for PCI host bridges This patch provide MMCONFIG address for PCI host bridges, which will be used to support host bridge hotplug. It gets MMCONFIG address by evaluating _CBA method if available. Reviewed-by: Yinghai Lu Signed-off-by: Jiang Liu Signed-off-by: Bjorn Helgaas --- drivers/acpi/pci_root.c | 2 ++ drivers/pci/pci-acpi.c | 14 ++++++++++++++ include/acpi/acnames.h | 1 + include/acpi/acpi_bus.h | 1 + include/linux/pci-acpi.h | 1 + 5 files changed, 19 insertions(+) (limited to 'include') diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 7aff6312ce7c..ec54014c321c 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -505,6 +505,8 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) strcpy(acpi_device_class(device), ACPI_PCI_ROOT_CLASS); device->driver_data = root; + root->mcfg_addr = acpi_pci_root_get_mcfg_addr(device->handle); + /* * All supported architectures that use ACPI have support for * PCI domains, so we indicate this in _OSC support capabilities. diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 61e2fefeedab..87f4c504eafb 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -162,6 +162,20 @@ acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev) return remove_pm_notifier(dev, pci_acpi_wake_dev); } +phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle) +{ + acpi_status status = AE_NOT_EXIST; + unsigned long long mcfg_addr; + + if (handle) + status = acpi_evaluate_integer(handle, METHOD_NAME__CBA, + NULL, &mcfg_addr); + if (ACPI_FAILURE(status)) + return 0; + + return (phys_addr_t)mcfg_addr; +} + /* * _SxD returns the D-state with the highest power * (lowest D-state number) supported in the S-state "x". diff --git a/include/acpi/acnames.h b/include/acpi/acnames.h index 38f508816e4a..b177f97f53b6 100644 --- a/include/acpi/acnames.h +++ b/include/acpi/acnames.h @@ -62,6 +62,7 @@ #define METHOD_NAME__AEI "_AEI" #define METHOD_NAME__PRW "_PRW" #define METHOD_NAME__SRS "_SRS" +#define METHOD_NAME__CBA "_CBA" /* Method names - these methods must appear at the namespace root */ diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 9e6e1c6eb60a..457974073994 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -401,6 +401,7 @@ struct acpi_pci_root { u32 osc_support_set; /* _OSC state of support bits */ u32 osc_control_set; /* _OSC state of control bits */ + phys_addr_t mcfg_addr; }; /* helper */ diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 44623500f419..248fba2af98a 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -17,6 +17,7 @@ extern acpi_status pci_acpi_remove_bus_pm_notifier(struct acpi_device *dev); extern acpi_status pci_acpi_add_pm_notifier(struct acpi_device *dev, struct pci_dev *pci_dev); extern acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev); +extern phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle); static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev) { -- cgit v1.2.3 From 397038519f2c2ac68c125c0cd766e91041c52b30 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 22 Jun 2012 14:55:21 +0800 Subject: ACPI: mark acpi_sfi_table_parse() as __init Mark function acpi_sfi_table_parse() as __init to avoid warning messages: WARNING: vmlinux.o(.text+0x4cd2d2): Section mismatch in reference from the function acpi_sfi_table_parse.clone.0() to the function Function acpi_sfi_table_parse() calls acpi_table_parse() and pci_parse_mcfg(), which are both marked as __init. Currently acpi_sfi_table_parse() is only used by MMCONFIG to scan MCFG table at boot time only, so it's safe to mark acpi_sfi_table_parse() as __init. Reviewed-by: Yinghai Lu Signed-off-by: Jiang Liu Signed-off-by: Bjorn Helgaas --- include/linux/sfi_acpi.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/sfi_acpi.h b/include/linux/sfi_acpi.h index c4a5a8cd4469..631af63af42d 100644 --- a/include/linux/sfi_acpi.h +++ b/include/linux/sfi_acpi.h @@ -66,7 +66,7 @@ extern int sfi_acpi_table_parse(char *signature, char *oem_id, char *oem_table_id, int (*handler)(struct acpi_table_header *)); -static inline int acpi_sfi_table_parse(char *signature, +static inline int __init acpi_sfi_table_parse(char *signature, int (*handler)(struct acpi_table_header *)) { if (!acpi_table_parse(signature, handler)) @@ -83,7 +83,7 @@ static inline int sfi_acpi_table_parse(char *signature, char *oem_id, return -1; } -static inline int acpi_sfi_table_parse(char *signature, +static inline int __init acpi_sfi_table_parse(char *signature, int (*handler)(struct acpi_table_header *)) { return acpi_table_parse(signature, handler); -- cgit v1.2.3 From ace36d85809f6005b559802f194d44c6aa61af88 Mon Sep 17 00:00:00 2001 From: Vipin Kumar Date: Thu, 21 Jun 2012 15:54:53 +0530 Subject: ASoC: SPEAr spdif_in: Add spdif IN support This patch implements the spdif IN driver for ST peripheral Signed-off-by: Vipin Kumar Signed-off-by: Rajeev Kumar Signed-off-by: Mark Brown --- include/sound/spear_spdif.h | 29 ++++ sound/soc/spear/spdif_in.c | 297 ++++++++++++++++++++++++++++++++++++++++ sound/soc/spear/spdif_in_regs.h | 60 ++++++++ 3 files changed, 386 insertions(+) create mode 100644 include/sound/spear_spdif.h create mode 100644 sound/soc/spear/spdif_in.c create mode 100644 sound/soc/spear/spdif_in_regs.h (limited to 'include') diff --git a/include/sound/spear_spdif.h b/include/sound/spear_spdif.h new file mode 100644 index 000000000000..a12f39695610 --- /dev/null +++ b/include/sound/spear_spdif.h @@ -0,0 +1,29 @@ +/* + * Copyright (ST) 2012 Vipin Kumar (vipin.kumar@st.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. + * + * 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 __SOUND_SPDIF_H +#define __SOUND_SPDIF_H + +struct spear_spdif_platform_data { + /* DMA params */ + void *dma_params; + bool (*filter)(struct dma_chan *chan, void *slave); + void (*reset_perip)(void); +}; + +#endif /* SOUND_SPDIF_H */ diff --git a/sound/soc/spear/spdif_in.c b/sound/soc/spear/spdif_in.c new file mode 100644 index 000000000000..c7c4b20395bb --- /dev/null +++ b/sound/soc/spear/spdif_in.c @@ -0,0 +1,297 @@ +/* + * ALSA SoC SPDIF In Audio Layer for spear processors + * + * Copyright (C) 2012 ST Microelectronics + * Vipin Kumar + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "spdif_in_regs.h" + +struct spdif_in_params { + u32 format; +}; + +struct spdif_in_dev { + struct clk *clk; + struct spear_dma_data dma_params; + struct spdif_in_params saved_params; + void *io_base; + struct device *dev; + void (*reset_perip)(void); + int irq; +}; + +static void spdif_in_configure(struct spdif_in_dev *host) +{ + u32 ctrl = SPDIF_IN_PRTYEN | SPDIF_IN_STATEN | SPDIF_IN_USREN | + SPDIF_IN_VALEN | SPDIF_IN_BLKEN; + ctrl |= SPDIF_MODE_16BIT | SPDIF_FIFO_THRES_16; + + writel(ctrl, host->io_base + SPDIF_IN_CTRL); + writel(0xF, host->io_base + SPDIF_IN_IRQ_MASK); +} + +static int spdif_in_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct spdif_in_dev *host = snd_soc_dai_get_drvdata(cpu_dai); + + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) + return -EINVAL; + + snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)&host->dma_params); + return 0; +} + +static void spdif_in_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai); + + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) + return; + + writel(0x0, host->io_base + SPDIF_IN_IRQ_MASK); + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static void spdif_in_format(struct spdif_in_dev *host, u32 format) +{ + u32 ctrl = readl(host->io_base + SPDIF_IN_CTRL); + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + ctrl |= SPDIF_XTRACT_16BIT; + break; + + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: + ctrl &= ~SPDIF_XTRACT_16BIT; + break; + } + + writel(ctrl, host->io_base + SPDIF_IN_CTRL); +} + +static int spdif_in_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai); + u32 format; + + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) + return -EINVAL; + + format = params_format(params); + host->saved_params.format = format; + + return 0; +} + +static int spdif_in_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai); + u32 ctrl; + int ret = 0; + + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) + return -EINVAL; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + clk_enable(host->clk); + spdif_in_configure(host); + spdif_in_format(host, host->saved_params.format); + + ctrl = readl(host->io_base + SPDIF_IN_CTRL); + ctrl |= SPDIF_IN_SAMPLE | SPDIF_IN_ENB; + writel(ctrl, host->io_base + SPDIF_IN_CTRL); + writel(0xF, host->io_base + SPDIF_IN_IRQ_MASK); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ctrl = readl(host->io_base + SPDIF_IN_CTRL); + ctrl &= ~(SPDIF_IN_SAMPLE | SPDIF_IN_ENB); + writel(ctrl, host->io_base + SPDIF_IN_CTRL); + writel(0x0, host->io_base + SPDIF_IN_IRQ_MASK); + + if (host->reset_perip) + host->reset_perip(); + clk_disable(host->clk); + break; + + default: + ret = -EINVAL; + break; + } + return ret; +} + +static struct snd_soc_dai_ops spdif_in_dai_ops = { + .startup = spdif_in_startup, + .shutdown = spdif_in_shutdown, + .trigger = spdif_in_trigger, + .hw_params = spdif_in_hw_params, +}; + +struct snd_soc_dai_driver spdif_in_dai = { + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_192000), + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE, + }, + .ops = &spdif_in_dai_ops, +}; + +static irqreturn_t spdif_in_irq(int irq, void *arg) +{ + struct spdif_in_dev *host = (struct spdif_in_dev *)arg; + + u32 irq_status = readl(host->io_base + SPDIF_IN_IRQ); + + if (!irq_status) + return IRQ_NONE; + + if (irq_status & SPDIF_IRQ_FIFOWRITE) + dev_err(host->dev, "spdif in: fifo write error"); + if (irq_status & SPDIF_IRQ_EMPTYFIFOREAD) + dev_err(host->dev, "spdif in: empty fifo read error"); + if (irq_status & SPDIF_IRQ_FIFOFULL) + dev_err(host->dev, "spdif in: fifo full error"); + if (irq_status & SPDIF_IRQ_OUTOFRANGE) + dev_err(host->dev, "spdif in: out of range error"); + + writel(0, host->io_base + SPDIF_IN_IRQ); + + return IRQ_HANDLED; +} + +static int spdif_in_probe(struct platform_device *pdev) +{ + struct spdif_in_dev *host; + struct spear_spdif_platform_data *pdata; + struct resource *res, *res_fifo; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + res_fifo = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res_fifo) + return -EINVAL; + + if (!devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), pdev->name)) { + dev_warn(&pdev->dev, "Failed to get memory resourse\n"); + return -ENOENT; + } + + host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); + if (!host) { + dev_warn(&pdev->dev, "kzalloc fail\n"); + return -ENOMEM; + } + + host->io_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!host->io_base) { + dev_warn(&pdev->dev, "ioremap failed\n"); + return -ENOMEM; + } + + host->irq = platform_get_irq(pdev, 0); + if (host->irq < 0) + return -EINVAL; + + host->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(host->clk)) + return PTR_ERR(host->clk); + + pdata = dev_get_platdata(&pdev->dev); + + if (!pdata) + return -EINVAL; + + host->dma_params.data = pdata->dma_params; + host->dma_params.addr = res_fifo->start; + host->dma_params.max_burst = 16; + host->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + host->dma_params.filter = pdata->filter; + host->reset_perip = pdata->reset_perip; + + host->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, host); + + ret = devm_request_irq(&pdev->dev, host->irq, spdif_in_irq, 0, + "spdif-in", host); + if (ret) { + clk_put(host->clk); + dev_warn(&pdev->dev, "request_irq failed\n"); + return ret; + } + + ret = snd_soc_register_dai(&pdev->dev, &spdif_in_dai); + if (ret != 0) { + clk_put(host->clk); + return ret; + } + + return 0; +} + +static int spdif_in_remove(struct platform_device *pdev) +{ + struct spdif_in_dev *host = dev_get_drvdata(&pdev->dev); + + snd_soc_unregister_dai(&pdev->dev); + dev_set_drvdata(&pdev->dev, NULL); + + clk_put(host->clk); + + return 0; +} + + +static struct platform_driver spdif_in_driver = { + .probe = spdif_in_probe, + .remove = spdif_in_remove, + .driver = { + .name = "spdif-in", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(spdif_in_driver); + +MODULE_AUTHOR("Vipin Kumar "); +MODULE_DESCRIPTION("SPEAr SPDIF IN SoC Interface"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:spdif_in"); diff --git a/sound/soc/spear/spdif_in_regs.h b/sound/soc/spear/spdif_in_regs.h new file mode 100644 index 000000000000..37af7bc66b7f --- /dev/null +++ b/sound/soc/spear/spdif_in_regs.h @@ -0,0 +1,60 @@ +/* + * SPEAr SPDIF IN controller header file + * + * Copyright (ST) 2011 Vipin Kumar (vipin.kumar@st.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. + * + * 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 SPDIF_IN_REGS_H +#define SPDIF_IN_REGS_H + +#define SPDIF_IN_CTRL 0x00 + #define SPDIF_IN_PRTYEN (1 << 20) + #define SPDIF_IN_STATEN (1 << 19) + #define SPDIF_IN_USREN (1 << 18) + #define SPDIF_IN_VALEN (1 << 17) + #define SPDIF_IN_BLKEN (1 << 16) + + #define SPDIF_MODE_24BIT (8 << 12) + #define SPDIF_MODE_23BIT (7 << 12) + #define SPDIF_MODE_22BIT (6 << 12) + #define SPDIF_MODE_21BIT (5 << 12) + #define SPDIF_MODE_20BIT (4 << 12) + #define SPDIF_MODE_19BIT (3 << 12) + #define SPDIF_MODE_18BIT (2 << 12) + #define SPDIF_MODE_17BIT (1 << 12) + #define SPDIF_MODE_16BIT (0 << 12) + #define SPDIF_MODE_MASK (0x0F << 12) + + #define SPDIF_IN_VALID (1 << 11) + #define SPDIF_IN_SAMPLE (1 << 10) + #define SPDIF_DATA_SWAP (1 << 9) + #define SPDIF_IN_ENB (1 << 8) + #define SPDIF_DATA_REVERT (1 << 7) + #define SPDIF_XTRACT_16BIT (1 << 6) + #define SPDIF_FIFO_THRES_16 (16 << 0) + +#define SPDIF_IN_IRQ_MASK 0x04 +#define SPDIF_IN_IRQ 0x08 + #define SPDIF_IRQ_FIFOWRITE (1 << 0) + #define SPDIF_IRQ_EMPTYFIFOREAD (1 << 1) + #define SPDIF_IRQ_FIFOFULL (1 << 2) + #define SPDIF_IRQ_OUTOFRANGE (1 << 3) + +#define SPDIF_IN_STA 0x0C + #define SPDIF_IN_LOCK (0x1 << 0) + +#endif /* SPDIF_IN_REGS_H */ -- cgit v1.2.3 From 229e3fdc1ba49b747e9434b55b3f6bd68a4db251 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 22 Jun 2012 11:40:55 +0100 Subject: ASoC: core: Add DOUBLE_R variants of the _RANGE controls The code handles this fine already, we just need new macros in the header for drivers to create the controls. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index e4348d25fca3..e063380f63a2 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -42,6 +42,10 @@ ((unsigned long)&(struct soc_mixer_control) \ {.reg = xlreg, .rreg = xrreg, .shift = xshift, .rshift = xshift, \ .max = xmax, .platform_max = xmax, .invert = xinvert}) +#define SOC_DOUBLE_R_RANGE_VALUE(xlreg, xrreg, xshift, xmin, xmax, xinvert) \ + ((unsigned long)&(struct soc_mixer_control) \ + {.reg = xlreg, .rreg = xrreg, .shift = xshift, .rshift = xshift, \ + .min = xmin, .max = xmax, .platform_max = xmax, .invert = xinvert}) #define SOC_SINGLE(xname, reg, shift, max, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ @@ -96,6 +100,13 @@ .get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \ .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \ xmax, xinvert) } +#define SOC_DOUBLE_R_RANGE(xname, reg_left, reg_right, xshift, xmin, \ + xmax, xinvert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ + .info = snd_soc_info_volsw_range, \ + .get = snd_soc_get_volsw_range, .put = snd_soc_put_volsw_range, \ + .private_value = SOC_DOUBLE_R_RANGE_VALUE(reg_left, reg_right, \ + xshift, xmin, xmax, xinvert) } #define SOC_DOUBLE_TLV(xname, reg, shift_left, shift_right, max, invert, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ @@ -114,6 +125,16 @@ .get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \ .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \ xmax, xinvert) } +#define SOC_DOUBLE_R_RANGE_TLV(xname, reg_left, reg_right, xshift, xmin, \ + xmax, xinvert, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ + SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw_range, \ + .get = snd_soc_get_volsw_range, .put = snd_soc_put_volsw_range, \ + .private_value = SOC_DOUBLE_R_RANGE_VALUE(reg_left, reg_right, \ + xshift, xmin, xmax, xinvert) } #define SOC_DOUBLE_R_SX_TLV(xname, xreg, xrreg, xshift, xmin, xmax, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ -- cgit v1.2.3 From c46a019a7941ff92291cda1cc2774bf720552ad9 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 19 Jun 2012 16:28:40 +0100 Subject: mfd: arizona: Register definitions Several forthcoming Wolfson devices are based on a common platform known as Arizona allowing a great deal of reuse of driver code. This patch adds register definitions for these devices. Signed-off-by: Mark Brown --- include/linux/mfd/arizona/registers.h | 6222 +++++++++++++++++++++++++++++++++ 1 file changed, 6222 insertions(+) create mode 100644 include/linux/mfd/arizona/registers.h (limited to 'include') diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h new file mode 100644 index 000000000000..989c47d681e0 --- /dev/null +++ b/include/linux/mfd/arizona/registers.h @@ -0,0 +1,6222 @@ +/* + * ARIZONA register definitions + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown + * + * 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_REGISTERS_H +#define _ARIZONA_REGISTERS_H + +/* + * Register values. + */ +#define ARIZONA_SOFTWARE_RESET 0x00 +#define ARIZONA_DEVICE_REVISION 0x01 +#define ARIZONA_CTRL_IF_SPI_CFG_1 0x08 +#define ARIZONA_CTRL_IF_I2C1_CFG_1 0x09 +#define ARIZONA_CTRL_IF_STATUS_1 0x0D +#define ARIZONA_WRITE_SEQUENCER_CTRL_0 0x16 +#define ARIZONA_WRITE_SEQUENCER_CTRL_1 0x17 +#define ARIZONA_WRITE_SEQUENCER_CTRL_2 0x18 +#define ARIZONA_WRITE_SEQUENCER_PROM 0x1A +#define ARIZONA_TONE_GENERATOR_1 0x20 +#define ARIZONA_TONE_GENERATOR_2 0x21 +#define ARIZONA_TONE_GENERATOR_3 0x22 +#define ARIZONA_TONE_GENERATOR_4 0x23 +#define ARIZONA_TONE_GENERATOR_5 0x24 +#define ARIZONA_PWM_DRIVE_1 0x30 +#define ARIZONA_PWM_DRIVE_2 0x31 +#define ARIZONA_PWM_DRIVE_3 0x32 +#define ARIZONA_WAKE_CONTROL 0x40 +#define ARIZONA_SEQUENCE_CONTROL 0x41 +#define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_1 0x61 +#define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_2 0x62 +#define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_3 0x63 +#define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_4 0x64 +#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_1 0x68 +#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_2 0x69 +#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_3 0x6A +#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4 0x6B +#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_5 0x6C +#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_6 0x6D +#define ARIZONA_COMFORT_NOISE_GENERATOR 0x70 +#define ARIZONA_HAPTICS_CONTROL_1 0x90 +#define ARIZONA_HAPTICS_CONTROL_2 0x91 +#define ARIZONA_HAPTICS_PHASE_1_INTENSITY 0x92 +#define ARIZONA_HAPTICS_PHASE_1_DURATION 0x93 +#define ARIZONA_HAPTICS_PHASE_2_INTENSITY 0x94 +#define ARIZONA_HAPTICS_PHASE_2_DURATION 0x95 +#define ARIZONA_HAPTICS_PHASE_3_INTENSITY 0x96 +#define ARIZONA_HAPTICS_PHASE_3_DURATION 0x97 +#define ARIZONA_HAPTICS_STATUS 0x98 +#define ARIZONA_CLOCK_32K_1 0x100 +#define ARIZONA_SYSTEM_CLOCK_1 0x101 +#define ARIZONA_SAMPLE_RATE_1 0x102 +#define ARIZONA_SAMPLE_RATE_2 0x103 +#define ARIZONA_SAMPLE_RATE_3 0x104 +#define ARIZONA_SAMPLE_RATE_1_STATUS 0x10A +#define ARIZONA_SAMPLE_RATE_2_STATUS 0x10B +#define ARIZONA_SAMPLE_RATE_3_STATUS 0x10C +#define ARIZONA_ASYNC_CLOCK_1 0x112 +#define ARIZONA_ASYNC_SAMPLE_RATE_1 0x113 +#define ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS 0x11B +#define ARIZONA_OUTPUT_SYSTEM_CLOCK 0x149 +#define ARIZONA_OUTPUT_ASYNC_CLOCK 0x14A +#define ARIZONA_RATE_ESTIMATOR_1 0x152 +#define ARIZONA_RATE_ESTIMATOR_2 0x153 +#define ARIZONA_RATE_ESTIMATOR_3 0x154 +#define ARIZONA_RATE_ESTIMATOR_4 0x155 +#define ARIZONA_RATE_ESTIMATOR_5 0x156 +#define ARIZONA_FLL1_CONTROL_1 0x171 +#define ARIZONA_FLL1_CONTROL_2 0x172 +#define ARIZONA_FLL1_CONTROL_3 0x173 +#define ARIZONA_FLL1_CONTROL_4 0x174 +#define ARIZONA_FLL1_CONTROL_5 0x175 +#define ARIZONA_FLL1_CONTROL_6 0x176 +#define ARIZONA_FLL1_LOOP_FILTER_TEST_1 0x177 +#define ARIZONA_FLL1_SYNCHRONISER_1 0x181 +#define ARIZONA_FLL1_SYNCHRONISER_2 0x182 +#define ARIZONA_FLL1_SYNCHRONISER_3 0x183 +#define ARIZONA_FLL1_SYNCHRONISER_4 0x184 +#define ARIZONA_FLL1_SYNCHRONISER_5 0x185 +#define ARIZONA_FLL1_SYNCHRONISER_6 0x186 +#define ARIZONA_FLL1_SPREAD_SPECTRUM 0x189 +#define ARIZONA_FLL1_GPIO_CLOCK 0x18A +#define ARIZONA_FLL2_CONTROL_1 0x191 +#define ARIZONA_FLL2_CONTROL_2 0x192 +#define ARIZONA_FLL2_CONTROL_3 0x193 +#define ARIZONA_FLL2_CONTROL_4 0x194 +#define ARIZONA_FLL2_CONTROL_5 0x195 +#define ARIZONA_FLL2_CONTROL_6 0x196 +#define ARIZONA_FLL2_LOOP_FILTER_TEST_1 0x197 +#define ARIZONA_FLL2_SYNCHRONISER_1 0x1A1 +#define ARIZONA_FLL2_SYNCHRONISER_2 0x1A2 +#define ARIZONA_FLL2_SYNCHRONISER_3 0x1A3 +#define ARIZONA_FLL2_SYNCHRONISER_4 0x1A4 +#define ARIZONA_FLL2_SYNCHRONISER_5 0x1A5 +#define ARIZONA_FLL2_SYNCHRONISER_6 0x1A6 +#define ARIZONA_FLL2_SPREAD_SPECTRUM 0x1A9 +#define ARIZONA_FLL2_GPIO_CLOCK 0x1AA +#define ARIZONA_MIC_CHARGE_PUMP_1 0x200 +#define ARIZONA_LDO1_CONTROL_1 0x210 +#define ARIZONA_LDO2_CONTROL_1 0x213 +#define ARIZONA_MIC_BIAS_CTRL_1 0x218 +#define ARIZONA_MIC_BIAS_CTRL_2 0x219 +#define ARIZONA_MIC_BIAS_CTRL_3 0x21A +#define ARIZONA_ACCESSORY_DETECT_MODE_1 0x293 +#define ARIZONA_HEADPHONE_DETECT_1 0x29B +#define ARIZONA_HEADPHONE_DETECT_2 0x29C +#define ARIZONA_MIC_DETECT_1 0x2A3 +#define ARIZONA_MIC_DETECT_2 0x2A4 +#define ARIZONA_MIC_DETECT_3 0x2A5 +#define ARIZONA_MIC_NOISE_MIX_CONTROL_1 0x2C3 +#define ARIZONA_ISOLATION_CONTROL 0x2CB +#define ARIZONA_JACK_DETECT_ANALOGUE 0x2D3 +#define ARIZONA_INPUT_ENABLES 0x300 +#define ARIZONA_INPUT_RATE 0x308 +#define ARIZONA_INPUT_VOLUME_RAMP 0x309 +#define ARIZONA_IN1L_CONTROL 0x310 +#define ARIZONA_ADC_DIGITAL_VOLUME_1L 0x311 +#define ARIZONA_DMIC1L_CONTROL 0x312 +#define ARIZONA_IN1R_CONTROL 0x314 +#define ARIZONA_ADC_DIGITAL_VOLUME_1R 0x315 +#define ARIZONA_DMIC1R_CONTROL 0x316 +#define ARIZONA_IN2L_CONTROL 0x318 +#define ARIZONA_ADC_DIGITAL_VOLUME_2L 0x319 +#define ARIZONA_DMIC2L_CONTROL 0x31A +#define ARIZONA_IN2R_CONTROL 0x31C +#define ARIZONA_ADC_DIGITAL_VOLUME_2R 0x31D +#define ARIZONA_DMIC2R_CONTROL 0x31E +#define ARIZONA_IN3L_CONTROL 0x320 +#define ARIZONA_ADC_DIGITAL_VOLUME_3L 0x321 +#define ARIZONA_DMIC3L_CONTROL 0x322 +#define ARIZONA_IN3R_CONTROL 0x324 +#define ARIZONA_ADC_DIGITAL_VOLUME_3R 0x325 +#define ARIZONA_DMIC3R_CONTROL 0x326 +#define ARIZONA_OUTPUT_ENABLES_1 0x400 +#define ARIZONA_OUTPUT_STATUS_1 0x401 +#define ARIZONA_OUTPUT_RATE_1 0x408 +#define ARIZONA_OUTPUT_VOLUME_RAMP 0x409 +#define ARIZONA_OUTPUT_PATH_CONFIG_1L 0x410 +#define ARIZONA_DAC_DIGITAL_VOLUME_1L 0x411 +#define ARIZONA_DAC_VOLUME_LIMIT_1L 0x412 +#define ARIZONA_NOISE_GATE_SELECT_1L 0x413 +#define ARIZONA_OUTPUT_PATH_CONFIG_1R 0x414 +#define ARIZONA_DAC_DIGITAL_VOLUME_1R 0x415 +#define ARIZONA_DAC_VOLUME_LIMIT_1R 0x416 +#define ARIZONA_NOISE_GATE_SELECT_1R 0x417 +#define ARIZONA_OUTPUT_PATH_CONFIG_2L 0x418 +#define ARIZONA_DAC_DIGITAL_VOLUME_2L 0x419 +#define ARIZONA_DAC_VOLUME_LIMIT_2L 0x41A +#define ARIZONA_NOISE_GATE_SELECT_2L 0x41B +#define ARIZONA_OUTPUT_PATH_CONFIG_2R 0x41C +#define ARIZONA_DAC_DIGITAL_VOLUME_2R 0x41D +#define ARIZONA_DAC_VOLUME_LIMIT_2R 0x41E +#define ARIZONA_NOISE_GATE_SELECT_2R 0x41F +#define ARIZONA_OUTPUT_PATH_CONFIG_3L 0x420 +#define ARIZONA_DAC_DIGITAL_VOLUME_3L 0x421 +#define ARIZONA_DAC_VOLUME_LIMIT_3L 0x422 +#define ARIZONA_NOISE_GATE_SELECT_3L 0x423 +#define ARIZONA_OUTPUT_PATH_CONFIG_3R 0x424 +#define ARIZONA_DAC_DIGITAL_VOLUME_3R 0x425 +#define ARIZONA_DAC_VOLUME_LIMIT_3R 0x426 +#define ARIZONA_OUTPUT_PATH_CONFIG_4L 0x428 +#define ARIZONA_DAC_DIGITAL_VOLUME_4L 0x429 +#define ARIZONA_OUT_VOLUME_4L 0x42A +#define ARIZONA_NOISE_GATE_SELECT_4L 0x42B +#define ARIZONA_OUTPUT_PATH_CONFIG_4R 0x42C +#define ARIZONA_DAC_DIGITAL_VOLUME_4R 0x42D +#define ARIZONA_OUT_VOLUME_4R 0x42E +#define ARIZONA_NOISE_GATE_SELECT_4R 0x42F +#define ARIZONA_OUTPUT_PATH_CONFIG_5L 0x430 +#define ARIZONA_DAC_DIGITAL_VOLUME_5L 0x431 +#define ARIZONA_DAC_VOLUME_LIMIT_5L 0x432 +#define ARIZONA_NOISE_GATE_SELECT_5L 0x433 +#define ARIZONA_OUTPUT_PATH_CONFIG_5R 0x434 +#define ARIZONA_DAC_DIGITAL_VOLUME_5R 0x435 +#define ARIZONA_DAC_VOLUME_LIMIT_5R 0x436 +#define ARIZONA_NOISE_GATE_SELECT_5R 0x437 +#define ARIZONA_DAC_AEC_CONTROL_1 0x450 +#define ARIZONA_NOISE_GATE_CONTROL 0x458 +#define ARIZONA_PDM_SPK1_CTRL_1 0x490 +#define ARIZONA_PDM_SPK1_CTRL_2 0x491 +#define ARIZONA_DAC_COMP_1 0x4DC +#define ARIZONA_DAC_COMP_2 0x4DD +#define ARIZONA_DAC_COMP_3 0x4DE +#define ARIZONA_DAC_COMP_4 0x4DF +#define ARIZONA_AIF1_BCLK_CTRL 0x500 +#define ARIZONA_AIF1_TX_PIN_CTRL 0x501 +#define ARIZONA_AIF1_RX_PIN_CTRL 0x502 +#define ARIZONA_AIF1_RATE_CTRL 0x503 +#define ARIZONA_AIF1_FORMAT 0x504 +#define ARIZONA_AIF1_TX_BCLK_RATE 0x505 +#define ARIZONA_AIF1_RX_BCLK_RATE 0x506 +#define ARIZONA_AIF1_FRAME_CTRL_1 0x507 +#define ARIZONA_AIF1_FRAME_CTRL_2 0x508 +#define ARIZONA_AIF1_FRAME_CTRL_3 0x509 +#define ARIZONA_AIF1_FRAME_CTRL_4 0x50A +#define ARIZONA_AIF1_FRAME_CTRL_5 0x50B +#define ARIZONA_AIF1_FRAME_CTRL_6 0x50C +#define ARIZONA_AIF1_FRAME_CTRL_7 0x50D +#define ARIZONA_AIF1_FRAME_CTRL_8 0x50E +#define ARIZONA_AIF1_FRAME_CTRL_9 0x50F +#define ARIZONA_AIF1_FRAME_CTRL_10 0x510 +#define ARIZONA_AIF1_FRAME_CTRL_11 0x511 +#define ARIZONA_AIF1_FRAME_CTRL_12 0x512 +#define ARIZONA_AIF1_FRAME_CTRL_13 0x513 +#define ARIZONA_AIF1_FRAME_CTRL_14 0x514 +#define ARIZONA_AIF1_FRAME_CTRL_15 0x515 +#define ARIZONA_AIF1_FRAME_CTRL_16 0x516 +#define ARIZONA_AIF1_FRAME_CTRL_17 0x517 +#define ARIZONA_AIF1_FRAME_CTRL_18 0x518 +#define ARIZONA_AIF1_TX_ENABLES 0x519 +#define ARIZONA_AIF1_RX_ENABLES 0x51A +#define ARIZONA_AIF1_FORCE_WRITE 0x51B +#define ARIZONA_AIF2_BCLK_CTRL 0x540 +#define ARIZONA_AIF2_TX_PIN_CTRL 0x541 +#define ARIZONA_AIF2_RX_PIN_CTRL 0x542 +#define ARIZONA_AIF2_RATE_CTRL 0x543 +#define ARIZONA_AIF2_FORMAT 0x544 +#define ARIZONA_AIF2_TX_BCLK_RATE 0x545 +#define ARIZONA_AIF2_RX_BCLK_RATE 0x546 +#define ARIZONA_AIF2_FRAME_CTRL_1 0x547 +#define ARIZONA_AIF2_FRAME_CTRL_2 0x548 +#define ARIZONA_AIF2_FRAME_CTRL_3 0x549 +#define ARIZONA_AIF2_FRAME_CTRL_4 0x54A +#define ARIZONA_AIF2_FRAME_CTRL_11 0x551 +#define ARIZONA_AIF2_FRAME_CTRL_12 0x552 +#define ARIZONA_AIF2_TX_ENABLES 0x559 +#define ARIZONA_AIF2_RX_ENABLES 0x55A +#define ARIZONA_AIF2_FORCE_WRITE 0x55B +#define ARIZONA_AIF3_BCLK_CTRL 0x580 +#define ARIZONA_AIF3_TX_PIN_CTRL 0x581 +#define ARIZONA_AIF3_RX_PIN_CTRL 0x582 +#define ARIZONA_AIF3_RATE_CTRL 0x583 +#define ARIZONA_AIF3_FORMAT 0x584 +#define ARIZONA_AIF3_TX_BCLK_RATE 0x585 +#define ARIZONA_AIF3_RX_BCLK_RATE 0x586 +#define ARIZONA_AIF3_FRAME_CTRL_1 0x587 +#define ARIZONA_AIF3_FRAME_CTRL_2 0x588 +#define ARIZONA_AIF3_FRAME_CTRL_3 0x589 +#define ARIZONA_AIF3_FRAME_CTRL_4 0x58A +#define ARIZONA_AIF3_FRAME_CTRL_11 0x591 +#define ARIZONA_AIF3_FRAME_CTRL_12 0x592 +#define ARIZONA_AIF3_TX_ENABLES 0x599 +#define ARIZONA_AIF3_RX_ENABLES 0x59A +#define ARIZONA_AIF3_FORCE_WRITE 0x59B +#define ARIZONA_SLIMBUS_FRAMER_REF_GEAR 0x5E3 +#define ARIZONA_SLIMBUS_RATES_1 0x5E5 +#define ARIZONA_SLIMBUS_RATES_2 0x5E6 +#define ARIZONA_SLIMBUS_RATES_3 0x5E7 +#define ARIZONA_SLIMBUS_RATES_4 0x5E8 +#define ARIZONA_SLIMBUS_RATES_5 0x5E9 +#define ARIZONA_SLIMBUS_RATES_6 0x5EA +#define ARIZONA_SLIMBUS_RATES_7 0x5EB +#define ARIZONA_SLIMBUS_RATES_8 0x5EC +#define ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE 0x5F5 +#define ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE 0x5F6 +#define ARIZONA_SLIMBUS_RX_PORT_STATUS 0x5F7 +#define ARIZONA_SLIMBUS_TX_PORT_STATUS 0x5F8 +#define ARIZONA_PWM1MIX_INPUT_1_SOURCE 0x640 +#define ARIZONA_PWM1MIX_INPUT_1_VOLUME 0x641 +#define ARIZONA_PWM1MIX_INPUT_2_SOURCE 0x642 +#define ARIZONA_PWM1MIX_INPUT_2_VOLUME 0x643 +#define ARIZONA_PWM1MIX_INPUT_3_SOURCE 0x644 +#define ARIZONA_PWM1MIX_INPUT_3_VOLUME 0x645 +#define ARIZONA_PWM1MIX_INPUT_4_SOURCE 0x646 +#define ARIZONA_PWM1MIX_INPUT_4_VOLUME 0x647 +#define ARIZONA_PWM2MIX_INPUT_1_SOURCE 0x648 +#define ARIZONA_PWM2MIX_INPUT_1_VOLUME 0x649 +#define ARIZONA_PWM2MIX_INPUT_2_SOURCE 0x64A +#define ARIZONA_PWM2MIX_INPUT_2_VOLUME 0x64B +#define ARIZONA_PWM2MIX_INPUT_3_SOURCE 0x64C +#define ARIZONA_PWM2MIX_INPUT_3_VOLUME 0x64D +#define ARIZONA_PWM2MIX_INPUT_4_SOURCE 0x64E +#define ARIZONA_PWM2MIX_INPUT_4_VOLUME 0x64F +#define ARIZONA_MICMIX_INPUT_1_SOURCE 0x660 +#define ARIZONA_MICMIX_INPUT_1_VOLUME 0x661 +#define ARIZONA_MICMIX_INPUT_2_SOURCE 0x662 +#define ARIZONA_MICMIX_INPUT_2_VOLUME 0x663 +#define ARIZONA_MICMIX_INPUT_3_SOURCE 0x664 +#define ARIZONA_MICMIX_INPUT_3_VOLUME 0x665 +#define ARIZONA_MICMIX_INPUT_4_SOURCE 0x666 +#define ARIZONA_MICMIX_INPUT_4_VOLUME 0x667 +#define ARIZONA_NOISEMIX_INPUT_1_SOURCE 0x668 +#define ARIZONA_NOISEMIX_INPUT_1_VOLUME 0x669 +#define ARIZONA_NOISEMIX_INPUT_2_SOURCE 0x66A +#define ARIZONA_NOISEMIX_INPUT_2_VOLUME 0x66B +#define ARIZONA_NOISEMIX_INPUT_3_SOURCE 0x66C +#define ARIZONA_NOISEMIX_INPUT_3_VOLUME 0x66D +#define ARIZONA_NOISEMIX_INPUT_4_SOURCE 0x66E +#define ARIZONA_NOISEMIX_INPUT_4_VOLUME 0x66F +#define ARIZONA_OUT1LMIX_INPUT_1_SOURCE 0x680 +#define ARIZONA_OUT1LMIX_INPUT_1_VOLUME 0x681 +#define ARIZONA_OUT1LMIX_INPUT_2_SOURCE 0x682 +#define ARIZONA_OUT1LMIX_INPUT_2_VOLUME 0x683 +#define ARIZONA_OUT1LMIX_INPUT_3_SOURCE 0x684 +#define ARIZONA_OUT1LMIX_INPUT_3_VOLUME 0x685 +#define ARIZONA_OUT1LMIX_INPUT_4_SOURCE 0x686 +#define ARIZONA_OUT1LMIX_INPUT_4_VOLUME 0x687 +#define ARIZONA_OUT1RMIX_INPUT_1_SOURCE 0x688 +#define ARIZONA_OUT1RMIX_INPUT_1_VOLUME 0x689 +#define ARIZONA_OUT1RMIX_INPUT_2_SOURCE 0x68A +#define ARIZONA_OUT1RMIX_INPUT_2_VOLUME 0x68B +#define ARIZONA_OUT1RMIX_INPUT_3_SOURCE 0x68C +#define ARIZONA_OUT1RMIX_INPUT_3_VOLUME 0x68D +#define ARIZONA_OUT1RMIX_INPUT_4_SOURCE 0x68E +#define ARIZONA_OUT1RMIX_INPUT_4_VOLUME 0x68F +#define ARIZONA_OUT2LMIX_INPUT_1_SOURCE 0x690 +#define ARIZONA_OUT2LMIX_INPUT_1_VOLUME 0x691 +#define ARIZONA_OUT2LMIX_INPUT_2_SOURCE 0x692 +#define ARIZONA_OUT2LMIX_INPUT_2_VOLUME 0x693 +#define ARIZONA_OUT2LMIX_INPUT_3_SOURCE 0x694 +#define ARIZONA_OUT2LMIX_INPUT_3_VOLUME 0x695 +#define ARIZONA_OUT2LMIX_INPUT_4_SOURCE 0x696 +#define ARIZONA_OUT2LMIX_INPUT_4_VOLUME 0x697 +#define ARIZONA_OUT2RMIX_INPUT_1_SOURCE 0x698 +#define ARIZONA_OUT2RMIX_INPUT_1_VOLUME 0x699 +#define ARIZONA_OUT2RMIX_INPUT_2_SOURCE 0x69A +#define ARIZONA_OUT2RMIX_INPUT_2_VOLUME 0x69B +#define ARIZONA_OUT2RMIX_INPUT_3_SOURCE 0x69C +#define ARIZONA_OUT2RMIX_INPUT_3_VOLUME 0x69D +#define ARIZONA_OUT2RMIX_INPUT_4_SOURCE 0x69E +#define ARIZONA_OUT2RMIX_INPUT_4_VOLUME 0x69F +#define ARIZONA_OUT3LMIX_INPUT_1_SOURCE 0x6A0 +#define ARIZONA_OUT3LMIX_INPUT_1_VOLUME 0x6A1 +#define ARIZONA_OUT3LMIX_INPUT_2_SOURCE 0x6A2 +#define ARIZONA_OUT3LMIX_INPUT_2_VOLUME 0x6A3 +#define ARIZONA_OUT3LMIX_INPUT_3_SOURCE 0x6A4 +#define ARIZONA_OUT3LMIX_INPUT_3_VOLUME 0x6A5 +#define ARIZONA_OUT3LMIX_INPUT_4_SOURCE 0x6A6 +#define ARIZONA_OUT3LMIX_INPUT_4_VOLUME 0x6A7 +#define ARIZONA_OUT4LMIX_INPUT_1_SOURCE 0x6B0 +#define ARIZONA_OUT4LMIX_INPUT_1_VOLUME 0x6B1 +#define ARIZONA_OUT4LMIX_INPUT_2_SOURCE 0x6B2 +#define ARIZONA_OUT4LMIX_INPUT_2_VOLUME 0x6B3 +#define ARIZONA_OUT4LMIX_INPUT_3_SOURCE 0x6B4 +#define ARIZONA_OUT4LMIX_INPUT_3_VOLUME 0x6B5 +#define ARIZONA_OUT4LMIX_INPUT_4_SOURCE 0x6B6 +#define ARIZONA_OUT4LMIX_INPUT_4_VOLUME 0x6B7 +#define ARIZONA_OUT4RMIX_INPUT_1_SOURCE 0x6B8 +#define ARIZONA_OUT4RMIX_INPUT_1_VOLUME 0x6B9 +#define ARIZONA_OUT4RMIX_INPUT_2_SOURCE 0x6BA +#define ARIZONA_OUT4RMIX_INPUT_2_VOLUME 0x6BB +#define ARIZONA_OUT4RMIX_INPUT_3_SOURCE 0x6BC +#define ARIZONA_OUT4RMIX_INPUT_3_VOLUME 0x6BD +#define ARIZONA_OUT4RMIX_INPUT_4_SOURCE 0x6BE +#define ARIZONA_OUT4RMIX_INPUT_4_VOLUME 0x6BF +#define ARIZONA_OUT5LMIX_INPUT_1_SOURCE 0x6C0 +#define ARIZONA_OUT5LMIX_INPUT_1_VOLUME 0x6C1 +#define ARIZONA_OUT5LMIX_INPUT_2_SOURCE 0x6C2 +#define ARIZONA_OUT5LMIX_INPUT_2_VOLUME 0x6C3 +#define ARIZONA_OUT5LMIX_INPUT_3_SOURCE 0x6C4 +#define ARIZONA_OUT5LMIX_INPUT_3_VOLUME 0x6C5 +#define ARIZONA_OUT5LMIX_INPUT_4_SOURCE 0x6C6 +#define ARIZONA_OUT5LMIX_INPUT_4_VOLUME 0x6C7 +#define ARIZONA_OUT5RMIX_INPUT_1_SOURCE 0x6C8 +#define ARIZONA_OUT5RMIX_INPUT_1_VOLUME 0x6C9 +#define ARIZONA_OUT5RMIX_INPUT_2_SOURCE 0x6CA +#define ARIZONA_OUT5RMIX_INPUT_2_VOLUME 0x6CB +#define ARIZONA_OUT5RMIX_INPUT_3_SOURCE 0x6CC +#define ARIZONA_OUT5RMIX_INPUT_3_VOLUME 0x6CD +#define ARIZONA_OUT5RMIX_INPUT_4_SOURCE 0x6CE +#define ARIZONA_OUT5RMIX_INPUT_4_VOLUME 0x6CF +#define ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE 0x700 +#define ARIZONA_AIF1TX1MIX_INPUT_1_VOLUME 0x701 +#define ARIZONA_AIF1TX1MIX_INPUT_2_SOURCE 0x702 +#define ARIZONA_AIF1TX1MIX_INPUT_2_VOLUME 0x703 +#define ARIZONA_AIF1TX1MIX_INPUT_3_SOURCE 0x704 +#define ARIZONA_AIF1TX1MIX_INPUT_3_VOLUME 0x705 +#define ARIZONA_AIF1TX1MIX_INPUT_4_SOURCE 0x706 +#define ARIZONA_AIF1TX1MIX_INPUT_4_VOLUME 0x707 +#define ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE 0x708 +#define ARIZONA_AIF1TX2MIX_INPUT_1_VOLUME 0x709 +#define ARIZONA_AIF1TX2MIX_INPUT_2_SOURCE 0x70A +#define ARIZONA_AIF1TX2MIX_INPUT_2_VOLUME 0x70B +#define ARIZONA_AIF1TX2MIX_INPUT_3_SOURCE 0x70C +#define ARIZONA_AIF1TX2MIX_INPUT_3_VOLUME 0x70D +#define ARIZONA_AIF1TX2MIX_INPUT_4_SOURCE 0x70E +#define ARIZONA_AIF1TX2MIX_INPUT_4_VOLUME 0x70F +#define ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE 0x710 +#define ARIZONA_AIF1TX3MIX_INPUT_1_VOLUME 0x711 +#define ARIZONA_AIF1TX3MIX_INPUT_2_SOURCE 0x712 +#define ARIZONA_AIF1TX3MIX_INPUT_2_VOLUME 0x713 +#define ARIZONA_AIF1TX3MIX_INPUT_3_SOURCE 0x714 +#define ARIZONA_AIF1TX3MIX_INPUT_3_VOLUME 0x715 +#define ARIZONA_AIF1TX3MIX_INPUT_4_SOURCE 0x716 +#define ARIZONA_AIF1TX3MIX_INPUT_4_VOLUME 0x717 +#define ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE 0x718 +#define ARIZONA_AIF1TX4MIX_INPUT_1_VOLUME 0x719 +#define ARIZONA_AIF1TX4MIX_INPUT_2_SOURCE 0x71A +#define ARIZONA_AIF1TX4MIX_INPUT_2_VOLUME 0x71B +#define ARIZONA_AIF1TX4MIX_INPUT_3_SOURCE 0x71C +#define ARIZONA_AIF1TX4MIX_INPUT_3_VOLUME 0x71D +#define ARIZONA_AIF1TX4MIX_INPUT_4_SOURCE 0x71E +#define ARIZONA_AIF1TX4MIX_INPUT_4_VOLUME 0x71F +#define ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE 0x720 +#define ARIZONA_AIF1TX5MIX_INPUT_1_VOLUME 0x721 +#define ARIZONA_AIF1TX5MIX_INPUT_2_SOURCE 0x722 +#define ARIZONA_AIF1TX5MIX_INPUT_2_VOLUME 0x723 +#define ARIZONA_AIF1TX5MIX_INPUT_3_SOURCE 0x724 +#define ARIZONA_AIF1TX5MIX_INPUT_3_VOLUME 0x725 +#define ARIZONA_AIF1TX5MIX_INPUT_4_SOURCE 0x726 +#define ARIZONA_AIF1TX5MIX_INPUT_4_VOLUME 0x727 +#define ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE 0x728 +#define ARIZONA_AIF1TX6MIX_INPUT_1_VOLUME 0x729 +#define ARIZONA_AIF1TX6MIX_INPUT_2_SOURCE 0x72A +#define ARIZONA_AIF1TX6MIX_INPUT_2_VOLUME 0x72B +#define ARIZONA_AIF1TX6MIX_INPUT_3_SOURCE 0x72C +#define ARIZONA_AIF1TX6MIX_INPUT_3_VOLUME 0x72D +#define ARIZONA_AIF1TX6MIX_INPUT_4_SOURCE 0x72E +#define ARIZONA_AIF1TX6MIX_INPUT_4_VOLUME 0x72F +#define ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE 0x730 +#define ARIZONA_AIF1TX7MIX_INPUT_1_VOLUME 0x731 +#define ARIZONA_AIF1TX7MIX_INPUT_2_SOURCE 0x732 +#define ARIZONA_AIF1TX7MIX_INPUT_2_VOLUME 0x733 +#define ARIZONA_AIF1TX7MIX_INPUT_3_SOURCE 0x734 +#define ARIZONA_AIF1TX7MIX_INPUT_3_VOLUME 0x735 +#define ARIZONA_AIF1TX7MIX_INPUT_4_SOURCE 0x736 +#define ARIZONA_AIF1TX7MIX_INPUT_4_VOLUME 0x737 +#define ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE 0x738 +#define ARIZONA_AIF1TX8MIX_INPUT_1_VOLUME 0x739 +#define ARIZONA_AIF1TX8MIX_INPUT_2_SOURCE 0x73A +#define ARIZONA_AIF1TX8MIX_INPUT_2_VOLUME 0x73B +#define ARIZONA_AIF1TX8MIX_INPUT_3_SOURCE 0x73C +#define ARIZONA_AIF1TX8MIX_INPUT_3_VOLUME 0x73D +#define ARIZONA_AIF1TX8MIX_INPUT_4_SOURCE 0x73E +#define ARIZONA_AIF1TX8MIX_INPUT_4_VOLUME 0x73F +#define ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE 0x740 +#define ARIZONA_AIF2TX1MIX_INPUT_1_VOLUME 0x741 +#define ARIZONA_AIF2TX1MIX_INPUT_2_SOURCE 0x742 +#define ARIZONA_AIF2TX1MIX_INPUT_2_VOLUME 0x743 +#define ARIZONA_AIF2TX1MIX_INPUT_3_SOURCE 0x744 +#define ARIZONA_AIF2TX1MIX_INPUT_3_VOLUME 0x745 +#define ARIZONA_AIF2TX1MIX_INPUT_4_SOURCE 0x746 +#define ARIZONA_AIF2TX1MIX_INPUT_4_VOLUME 0x747 +#define ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE 0x748 +#define ARIZONA_AIF2TX2MIX_INPUT_1_VOLUME 0x749 +#define ARIZONA_AIF2TX2MIX_INPUT_2_SOURCE 0x74A +#define ARIZONA_AIF2TX2MIX_INPUT_2_VOLUME 0x74B +#define ARIZONA_AIF2TX2MIX_INPUT_3_SOURCE 0x74C +#define ARIZONA_AIF2TX2MIX_INPUT_3_VOLUME 0x74D +#define ARIZONA_AIF2TX2MIX_INPUT_4_SOURCE 0x74E +#define ARIZONA_AIF2TX2MIX_INPUT_4_VOLUME 0x74F +#define ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE 0x780 +#define ARIZONA_AIF3TX1MIX_INPUT_1_VOLUME 0x781 +#define ARIZONA_AIF3TX1MIX_INPUT_2_SOURCE 0x782 +#define ARIZONA_AIF3TX1MIX_INPUT_2_VOLUME 0x783 +#define ARIZONA_AIF3TX1MIX_INPUT_3_SOURCE 0x784 +#define ARIZONA_AIF3TX1MIX_INPUT_3_VOLUME 0x785 +#define ARIZONA_AIF3TX1MIX_INPUT_4_SOURCE 0x786 +#define ARIZONA_AIF3TX1MIX_INPUT_4_VOLUME 0x787 +#define ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE 0x788 +#define ARIZONA_AIF3TX2MIX_INPUT_1_VOLUME 0x789 +#define ARIZONA_AIF3TX2MIX_INPUT_2_SOURCE 0x78A +#define ARIZONA_AIF3TX2MIX_INPUT_2_VOLUME 0x78B +#define ARIZONA_AIF3TX2MIX_INPUT_3_SOURCE 0x78C +#define ARIZONA_AIF3TX2MIX_INPUT_3_VOLUME 0x78D +#define ARIZONA_AIF3TX2MIX_INPUT_4_SOURCE 0x78E +#define ARIZONA_AIF3TX2MIX_INPUT_4_VOLUME 0x78F +#define ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE 0x7C0 +#define ARIZONA_SLIMTX1MIX_INPUT_1_VOLUME 0x7C1 +#define ARIZONA_SLIMTX1MIX_INPUT_2_SOURCE 0x7C2 +#define ARIZONA_SLIMTX1MIX_INPUT_2_VOLUME 0x7C3 +#define ARIZONA_SLIMTX1MIX_INPUT_3_SOURCE 0x7C4 +#define ARIZONA_SLIMTX1MIX_INPUT_3_VOLUME 0x7C5 +#define ARIZONA_SLIMTX1MIX_INPUT_4_SOURCE 0x7C6 +#define ARIZONA_SLIMTX1MIX_INPUT_4_VOLUME 0x7C7 +#define ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE 0x7C8 +#define ARIZONA_SLIMTX2MIX_INPUT_1_VOLUME 0x7C9 +#define ARIZONA_SLIMTX2MIX_INPUT_2_SOURCE 0x7CA +#define ARIZONA_SLIMTX2MIX_INPUT_2_VOLUME 0x7CB +#define ARIZONA_SLIMTX2MIX_INPUT_3_SOURCE 0x7CC +#define ARIZONA_SLIMTX2MIX_INPUT_3_VOLUME 0x7CD +#define ARIZONA_SLIMTX2MIX_INPUT_4_SOURCE 0x7CE +#define ARIZONA_SLIMTX2MIX_INPUT_4_VOLUME 0x7CF +#define ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE 0x7D0 +#define ARIZONA_SLIMTX3MIX_INPUT_1_VOLUME 0x7D1 +#define ARIZONA_SLIMTX3MIX_INPUT_2_SOURCE 0x7D2 +#define ARIZONA_SLIMTX3MIX_INPUT_2_VOLUME 0x7D3 +#define ARIZONA_SLIMTX3MIX_INPUT_3_SOURCE 0x7D4 +#define ARIZONA_SLIMTX3MIX_INPUT_3_VOLUME 0x7D5 +#define ARIZONA_SLIMTX3MIX_INPUT_4_SOURCE 0x7D6 +#define ARIZONA_SLIMTX3MIX_INPUT_4_VOLUME 0x7D7 +#define ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE 0x7D8 +#define ARIZONA_SLIMTX4MIX_INPUT_1_VOLUME 0x7D9 +#define ARIZONA_SLIMTX4MIX_INPUT_2_SOURCE 0x7DA +#define ARIZONA_SLIMTX4MIX_INPUT_2_VOLUME 0x7DB +#define ARIZONA_SLIMTX4MIX_INPUT_3_SOURCE 0x7DC +#define ARIZONA_SLIMTX4MIX_INPUT_3_VOLUME 0x7DD +#define ARIZONA_SLIMTX4MIX_INPUT_4_SOURCE 0x7DE +#define ARIZONA_SLIMTX4MIX_INPUT_4_VOLUME 0x7DF +#define ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE 0x7E0 +#define ARIZONA_SLIMTX5MIX_INPUT_1_VOLUME 0x7E1 +#define ARIZONA_SLIMTX5MIX_INPUT_2_SOURCE 0x7E2 +#define ARIZONA_SLIMTX5MIX_INPUT_2_VOLUME 0x7E3 +#define ARIZONA_SLIMTX5MIX_INPUT_3_SOURCE 0x7E4 +#define ARIZONA_SLIMTX5MIX_INPUT_3_VOLUME 0x7E5 +#define ARIZONA_SLIMTX5MIX_INPUT_4_SOURCE 0x7E6 +#define ARIZONA_SLIMTX5MIX_INPUT_4_VOLUME 0x7E7 +#define ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE 0x7E8 +#define ARIZONA_SLIMTX6MIX_INPUT_1_VOLUME 0x7E9 +#define ARIZONA_SLIMTX6MIX_INPUT_2_SOURCE 0x7EA +#define ARIZONA_SLIMTX6MIX_INPUT_2_VOLUME 0x7EB +#define ARIZONA_SLIMTX6MIX_INPUT_3_SOURCE 0x7EC +#define ARIZONA_SLIMTX6MIX_INPUT_3_VOLUME 0x7ED +#define ARIZONA_SLIMTX6MIX_INPUT_4_SOURCE 0x7EE +#define ARIZONA_SLIMTX6MIX_INPUT_4_VOLUME 0x7EF +#define ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE 0x7F0 +#define ARIZONA_SLIMTX7MIX_INPUT_1_VOLUME 0x7F1 +#define ARIZONA_SLIMTX7MIX_INPUT_2_SOURCE 0x7F2 +#define ARIZONA_SLIMTX7MIX_INPUT_2_VOLUME 0x7F3 +#define ARIZONA_SLIMTX7MIX_INPUT_3_SOURCE 0x7F4 +#define ARIZONA_SLIMTX7MIX_INPUT_3_VOLUME 0x7F5 +#define ARIZONA_SLIMTX7MIX_INPUT_4_SOURCE 0x7F6 +#define ARIZONA_SLIMTX7MIX_INPUT_4_VOLUME 0x7F7 +#define ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE 0x7F8 +#define ARIZONA_SLIMTX8MIX_INPUT_1_VOLUME 0x7F9 +#define ARIZONA_SLIMTX8MIX_INPUT_2_SOURCE 0x7FA +#define ARIZONA_SLIMTX8MIX_INPUT_2_VOLUME 0x7FB +#define ARIZONA_SLIMTX8MIX_INPUT_3_SOURCE 0x7FC +#define ARIZONA_SLIMTX8MIX_INPUT_3_VOLUME 0x7FD +#define ARIZONA_SLIMTX8MIX_INPUT_4_SOURCE 0x7FE +#define ARIZONA_SLIMTX8MIX_INPUT_4_VOLUME 0x7FF +#define ARIZONA_EQ1MIX_INPUT_1_SOURCE 0x880 +#define ARIZONA_EQ1MIX_INPUT_1_VOLUME 0x881 +#define ARIZONA_EQ1MIX_INPUT_2_SOURCE 0x882 +#define ARIZONA_EQ1MIX_INPUT_2_VOLUME 0x883 +#define ARIZONA_EQ1MIX_INPUT_3_SOURCE 0x884 +#define ARIZONA_EQ1MIX_INPUT_3_VOLUME 0x885 +#define ARIZONA_EQ1MIX_INPUT_4_SOURCE 0x886 +#define ARIZONA_EQ1MIX_INPUT_4_VOLUME 0x887 +#define ARIZONA_EQ2MIX_INPUT_1_SOURCE 0x888 +#define ARIZONA_EQ2MIX_INPUT_1_VOLUME 0x889 +#define ARIZONA_EQ2MIX_INPUT_2_SOURCE 0x88A +#define ARIZONA_EQ2MIX_INPUT_2_VOLUME 0x88B +#define ARIZONA_EQ2MIX_INPUT_3_SOURCE 0x88C +#define ARIZONA_EQ2MIX_INPUT_3_VOLUME 0x88D +#define ARIZONA_EQ2MIX_INPUT_4_SOURCE 0x88E +#define ARIZONA_EQ2MIX_INPUT_4_VOLUME 0x88F +#define ARIZONA_EQ3MIX_INPUT_1_SOURCE 0x890 +#define ARIZONA_EQ3MIX_INPUT_1_VOLUME 0x891 +#define ARIZONA_EQ3MIX_INPUT_2_SOURCE 0x892 +#define ARIZONA_EQ3MIX_INPUT_2_VOLUME 0x893 +#define ARIZONA_EQ3MIX_INPUT_3_SOURCE 0x894 +#define ARIZONA_EQ3MIX_INPUT_3_VOLUME 0x895 +#define ARIZONA_EQ3MIX_INPUT_4_SOURCE 0x896 +#define ARIZONA_EQ3MIX_INPUT_4_VOLUME 0x897 +#define ARIZONA_EQ4MIX_INPUT_1_SOURCE 0x898 +#define ARIZONA_EQ4MIX_INPUT_1_VOLUME 0x899 +#define ARIZONA_EQ4MIX_INPUT_2_SOURCE 0x89A +#define ARIZONA_EQ4MIX_INPUT_2_VOLUME 0x89B +#define ARIZONA_EQ4MIX_INPUT_3_SOURCE 0x89C +#define ARIZONA_EQ4MIX_INPUT_3_VOLUME 0x89D +#define ARIZONA_EQ4MIX_INPUT_4_SOURCE 0x89E +#define ARIZONA_EQ4MIX_INPUT_4_VOLUME 0x89F +#define ARIZONA_DRC1LMIX_INPUT_1_SOURCE 0x8C0 +#define ARIZONA_DRC1LMIX_INPUT_1_VOLUME 0x8C1 +#define ARIZONA_DRC1LMIX_INPUT_2_SOURCE 0x8C2 +#define ARIZONA_DRC1LMIX_INPUT_2_VOLUME 0x8C3 +#define ARIZONA_DRC1LMIX_INPUT_3_SOURCE 0x8C4 +#define ARIZONA_DRC1LMIX_INPUT_3_VOLUME 0x8C5 +#define ARIZONA_DRC1LMIX_INPUT_4_SOURCE 0x8C6 +#define ARIZONA_DRC1LMIX_INPUT_4_VOLUME 0x8C7 +#define ARIZONA_DRC1RMIX_INPUT_1_SOURCE 0x8C8 +#define ARIZONA_DRC1RMIX_INPUT_1_VOLUME 0x8C9 +#define ARIZONA_DRC1RMIX_INPUT_2_SOURCE 0x8CA +#define ARIZONA_DRC1RMIX_INPUT_2_VOLUME 0x8CB +#define ARIZONA_DRC1RMIX_INPUT_3_SOURCE 0x8CC +#define ARIZONA_DRC1RMIX_INPUT_3_VOLUME 0x8CD +#define ARIZONA_DRC1RMIX_INPUT_4_SOURCE 0x8CE +#define ARIZONA_DRC1RMIX_INPUT_4_VOLUME 0x8CF +#define ARIZONA_DRC2LMIX_INPUT_1_SOURCE 0x8D0 +#define ARIZONA_DRC2LMIX_INPUT_1_VOLUME 0x8D1 +#define ARIZONA_DRC2LMIX_INPUT_2_SOURCE 0x8D2 +#define ARIZONA_DRC2LMIX_INPUT_2_VOLUME 0x8D3 +#define ARIZONA_DRC2LMIX_INPUT_3_SOURCE 0x8D4 +#define ARIZONA_DRC2LMIX_INPUT_3_VOLUME 0x8D5 +#define ARIZONA_DRC2LMIX_INPUT_4_SOURCE 0x8D6 +#define ARIZONA_DRC2LMIX_INPUT_4_VOLUME 0x8D7 +#define ARIZONA_DRC2RMIX_INPUT_1_SOURCE 0x8D8 +#define ARIZONA_DRC2RMIX_INPUT_1_VOLUME 0x8D9 +#define ARIZONA_DRC2RMIX_INPUT_2_SOURCE 0x8DA +#define ARIZONA_DRC2RMIX_INPUT_2_VOLUME 0x8DB +#define ARIZONA_DRC2RMIX_INPUT_3_SOURCE 0x8DC +#define ARIZONA_DRC2RMIX_INPUT_3_VOLUME 0x8DD +#define ARIZONA_DRC2RMIX_INPUT_4_SOURCE 0x8DE +#define ARIZONA_DRC2RMIX_INPUT_4_VOLUME 0x8DF +#define ARIZONA_HPLP1MIX_INPUT_1_SOURCE 0x900 +#define ARIZONA_HPLP1MIX_INPUT_1_VOLUME 0x901 +#define ARIZONA_HPLP1MIX_INPUT_2_SOURCE 0x902 +#define ARIZONA_HPLP1MIX_INPUT_2_VOLUME 0x903 +#define ARIZONA_HPLP1MIX_INPUT_3_SOURCE 0x904 +#define ARIZONA_HPLP1MIX_INPUT_3_VOLUME 0x905 +#define ARIZONA_HPLP1MIX_INPUT_4_SOURCE 0x906 +#define ARIZONA_HPLP1MIX_INPUT_4_VOLUME 0x907 +#define ARIZONA_HPLP2MIX_INPUT_1_SOURCE 0x908 +#define ARIZONA_HPLP2MIX_INPUT_1_VOLUME 0x909 +#define ARIZONA_HPLP2MIX_INPUT_2_SOURCE 0x90A +#define ARIZONA_HPLP2MIX_INPUT_2_VOLUME 0x90B +#define ARIZONA_HPLP2MIX_INPUT_3_SOURCE 0x90C +#define ARIZONA_HPLP2MIX_INPUT_3_VOLUME 0x90D +#define ARIZONA_HPLP2MIX_INPUT_4_SOURCE 0x90E +#define ARIZONA_HPLP2MIX_INPUT_4_VOLUME 0x90F +#define ARIZONA_HPLP3MIX_INPUT_1_SOURCE 0x910 +#define ARIZONA_HPLP3MIX_INPUT_1_VOLUME 0x911 +#define ARIZONA_HPLP3MIX_INPUT_2_SOURCE 0x912 +#define ARIZONA_HPLP3MIX_INPUT_2_VOLUME 0x913 +#define ARIZONA_HPLP3MIX_INPUT_3_SOURCE 0x914 +#define ARIZONA_HPLP3MIX_INPUT_3_VOLUME 0x915 +#define ARIZONA_HPLP3MIX_INPUT_4_SOURCE 0x916 +#define ARIZONA_HPLP3MIX_INPUT_4_VOLUME 0x917 +#define ARIZONA_HPLP4MIX_INPUT_1_SOURCE 0x918 +#define ARIZONA_HPLP4MIX_INPUT_1_VOLUME 0x919 +#define ARIZONA_HPLP4MIX_INPUT_2_SOURCE 0x91A +#define ARIZONA_HPLP4MIX_INPUT_2_VOLUME 0x91B +#define ARIZONA_HPLP4MIX_INPUT_3_SOURCE 0x91C +#define ARIZONA_HPLP4MIX_INPUT_3_VOLUME 0x91D +#define ARIZONA_HPLP4MIX_INPUT_4_SOURCE 0x91E +#define ARIZONA_HPLP4MIX_INPUT_4_VOLUME 0x91F +#define ARIZONA_DSP1LMIX_INPUT_1_SOURCE 0x940 +#define ARIZONA_DSP1LMIX_INPUT_1_VOLUME 0x941 +#define ARIZONA_DSP1LMIX_INPUT_2_SOURCE 0x942 +#define ARIZONA_DSP1LMIX_INPUT_2_VOLUME 0x943 +#define ARIZONA_DSP1LMIX_INPUT_3_SOURCE 0x944 +#define ARIZONA_DSP1LMIX_INPUT_3_VOLUME 0x945 +#define ARIZONA_DSP1LMIX_INPUT_4_SOURCE 0x946 +#define ARIZONA_DSP1LMIX_INPUT_4_VOLUME 0x947 +#define ARIZONA_DSP1RMIX_INPUT_1_SOURCE 0x948 +#define ARIZONA_DSP1RMIX_INPUT_1_VOLUME 0x949 +#define ARIZONA_DSP1RMIX_INPUT_2_SOURCE 0x94A +#define ARIZONA_DSP1RMIX_INPUT_2_VOLUME 0x94B +#define ARIZONA_DSP1RMIX_INPUT_3_SOURCE 0x94C +#define ARIZONA_DSP1RMIX_INPUT_3_VOLUME 0x94D +#define ARIZONA_DSP1RMIX_INPUT_4_SOURCE 0x94E +#define ARIZONA_DSP1RMIX_INPUT_4_VOLUME 0x94F +#define ARIZONA_DSP1AUX1MIX_INPUT_1_SOURCE 0x950 +#define ARIZONA_DSP1AUX2MIX_INPUT_1_SOURCE 0x958 +#define ARIZONA_DSP1AUX3MIX_INPUT_1_SOURCE 0x960 +#define ARIZONA_DSP1AUX4MIX_INPUT_1_SOURCE 0x968 +#define ARIZONA_DSP1AUX5MIX_INPUT_1_SOURCE 0x970 +#define ARIZONA_DSP1AUX6MIX_INPUT_1_SOURCE 0x978 +#define ARIZONA_ASRC1LMIX_INPUT_1_SOURCE 0xA80 +#define ARIZONA_ASRC1RMIX_INPUT_1_SOURCE 0xA88 +#define ARIZONA_ASRC2LMIX_INPUT_1_SOURCE 0xA90 +#define ARIZONA_ASRC2RMIX_INPUT_1_SOURCE 0xA98 +#define ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE 0xB00 +#define ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE 0xB08 +#define ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE 0xB20 +#define ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE 0xB28 +#define ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE 0xB40 +#define ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE 0xB48 +#define ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE 0xB60 +#define ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE 0xB68 +#define ARIZONA_GPIO1_CTRL 0xC00 +#define ARIZONA_GPIO2_CTRL 0xC01 +#define ARIZONA_GPIO3_CTRL 0xC02 +#define ARIZONA_GPIO4_CTRL 0xC03 +#define ARIZONA_GPIO5_CTRL 0xC04 +#define ARIZONA_IRQ_CTRL_1 0xC0F +#define ARIZONA_GPIO_DEBOUNCE_CONFIG 0xC10 +#define ARIZONA_MISC_PAD_CTRL_1 0xC20 +#define ARIZONA_MISC_PAD_CTRL_2 0xC21 +#define ARIZONA_MISC_PAD_CTRL_3 0xC22 +#define ARIZONA_MISC_PAD_CTRL_4 0xC23 +#define ARIZONA_MISC_PAD_CTRL_5 0xC24 +#define ARIZONA_MISC_PAD_CTRL_6 0xC25 +#define ARIZONA_INTERRUPT_STATUS_1 0xD00 +#define ARIZONA_INTERRUPT_STATUS_2 0xD01 +#define ARIZONA_INTERRUPT_STATUS_3 0xD02 +#define ARIZONA_INTERRUPT_STATUS_4 0xD03 +#define ARIZONA_INTERRUPT_STATUS_5 0xD04 +#define ARIZONA_INTERRUPT_STATUS_1_MASK 0xD08 +#define ARIZONA_INTERRUPT_STATUS_2_MASK 0xD09 +#define ARIZONA_INTERRUPT_STATUS_3_MASK 0xD0A +#define ARIZONA_INTERRUPT_STATUS_4_MASK 0xD0B +#define ARIZONA_INTERRUPT_STATUS_5_MASK 0xD0C +#define ARIZONA_INTERRUPT_CONTROL 0xD0F +#define ARIZONA_IRQ2_STATUS_1 0xD10 +#define ARIZONA_IRQ2_STATUS_2 0xD11 +#define ARIZONA_IRQ2_STATUS_3 0xD12 +#define ARIZONA_IRQ2_STATUS_4 0xD13 +#define ARIZONA_IRQ2_STATUS_5 0xD14 +#define ARIZONA_IRQ2_STATUS_1_MASK 0xD18 +#define ARIZONA_IRQ2_STATUS_2_MASK 0xD19 +#define ARIZONA_IRQ2_STATUS_3_MASK 0xD1A +#define ARIZONA_IRQ2_STATUS_4_MASK 0xD1B +#define ARIZONA_IRQ2_STATUS_5_MASK 0xD1C +#define ARIZONA_IRQ2_CONTROL 0xD1F +#define ARIZONA_INTERRUPT_RAW_STATUS_2 0xD20 +#define ARIZONA_INTERRUPT_RAW_STATUS_3 0xD21 +#define ARIZONA_INTERRUPT_RAW_STATUS_4 0xD22 +#define ARIZONA_INTERRUPT_RAW_STATUS_5 0xD23 +#define ARIZONA_INTERRUPT_RAW_STATUS_6 0xD24 +#define ARIZONA_INTERRUPT_RAW_STATUS_7 0xD25 +#define ARIZONA_INTERRUPT_RAW_STATUS_8 0xD26 +#define ARIZONA_IRQ_PIN_STATUS 0xD40 +#define ARIZONA_ADSP2_IRQ0 0xD41 +#define ARIZONA_AOD_WKUP_AND_TRIG 0xD50 +#define ARIZONA_AOD_IRQ1 0xD51 +#define ARIZONA_AOD_IRQ2 0xD52 +#define ARIZONA_AOD_IRQ_MASK_IRQ1 0xD53 +#define ARIZONA_AOD_IRQ_MASK_IRQ2 0xD54 +#define ARIZONA_AOD_IRQ_RAW_STATUS 0xD55 +#define ARIZONA_JACK_DETECT_DEBOUNCE 0xD56 +#define ARIZONA_FX_CTRL1 0xE00 +#define ARIZONA_FX_CTRL2 0xE01 +#define ARIZONA_EQ1_1 0xE10 +#define ARIZONA_EQ1_2 0xE11 +#define ARIZONA_EQ1_3 0xE12 +#define ARIZONA_EQ1_4 0xE13 +#define ARIZONA_EQ1_5 0xE14 +#define ARIZONA_EQ1_6 0xE15 +#define ARIZONA_EQ1_7 0xE16 +#define ARIZONA_EQ1_8 0xE17 +#define ARIZONA_EQ1_9 0xE18 +#define ARIZONA_EQ1_10 0xE19 +#define ARIZONA_EQ1_11 0xE1A +#define ARIZONA_EQ1_12 0xE1B +#define ARIZONA_EQ1_13 0xE1C +#define ARIZONA_EQ1_14 0xE1D +#define ARIZONA_EQ1_15 0xE1E +#define ARIZONA_EQ1_16 0xE1F +#define ARIZONA_EQ1_17 0xE20 +#define ARIZONA_EQ1_18 0xE21 +#define ARIZONA_EQ1_19 0xE22 +#define ARIZONA_EQ1_20 0xE23 +#define ARIZONA_EQ1_21 0xE24 +#define ARIZONA_EQ2_1 0xE26 +#define ARIZONA_EQ2_2 0xE27 +#define ARIZONA_EQ2_3 0xE28 +#define ARIZONA_EQ2_4 0xE29 +#define ARIZONA_EQ2_5 0xE2A +#define ARIZONA_EQ2_6 0xE2B +#define ARIZONA_EQ2_7 0xE2C +#define ARIZONA_EQ2_8 0xE2D +#define ARIZONA_EQ2_9 0xE2E +#define ARIZONA_EQ2_10 0xE2F +#define ARIZONA_EQ2_11 0xE30 +#define ARIZONA_EQ2_12 0xE31 +#define ARIZONA_EQ2_13 0xE32 +#define ARIZONA_EQ2_14 0xE33 +#define ARIZONA_EQ2_15 0xE34 +#define ARIZONA_EQ2_16 0xE35 +#define ARIZONA_EQ2_17 0xE36 +#define ARIZONA_EQ2_18 0xE37 +#define ARIZONA_EQ2_19 0xE38 +#define ARIZONA_EQ2_20 0xE39 +#define ARIZONA_EQ2_21 0xE3A +#define ARIZONA_EQ3_1 0xE3C +#define ARIZONA_EQ3_2 0xE3D +#define ARIZONA_EQ3_3 0xE3E +#define ARIZONA_EQ3_4 0xE3F +#define ARIZONA_EQ3_5 0xE40 +#define ARIZONA_EQ3_6 0xE41 +#define ARIZONA_EQ3_7 0xE42 +#define ARIZONA_EQ3_8 0xE43 +#define ARIZONA_EQ3_9 0xE44 +#define ARIZONA_EQ3_10 0xE45 +#define ARIZONA_EQ3_11 0xE46 +#define ARIZONA_EQ3_12 0xE47 +#define ARIZONA_EQ3_13 0xE48 +#define ARIZONA_EQ3_14 0xE49 +#define ARIZONA_EQ3_15 0xE4A +#define ARIZONA_EQ3_16 0xE4B +#define ARIZONA_EQ3_17 0xE4C +#define ARIZONA_EQ3_18 0xE4D +#define ARIZONA_EQ3_19 0xE4E +#define ARIZONA_EQ3_20 0xE4F +#define ARIZONA_EQ3_21 0xE50 +#define ARIZONA_EQ4_1 0xE52 +#define ARIZONA_EQ4_2 0xE53 +#define ARIZONA_EQ4_3 0xE54 +#define ARIZONA_EQ4_4 0xE55 +#define ARIZONA_EQ4_5 0xE56 +#define ARIZONA_EQ4_6 0xE57 +#define ARIZONA_EQ4_7 0xE58 +#define ARIZONA_EQ4_8 0xE59 +#define ARIZONA_EQ4_9 0xE5A +#define ARIZONA_EQ4_10 0xE5B +#define ARIZONA_EQ4_11 0xE5C +#define ARIZONA_EQ4_12 0xE5D +#define ARIZONA_EQ4_13 0xE5E +#define ARIZONA_EQ4_14 0xE5F +#define ARIZONA_EQ4_15 0xE60 +#define ARIZONA_EQ4_16 0xE61 +#define ARIZONA_EQ4_17 0xE62 +#define ARIZONA_EQ4_18 0xE63 +#define ARIZONA_EQ4_19 0xE64 +#define ARIZONA_EQ4_20 0xE65 +#define ARIZONA_EQ4_21 0xE66 +#define ARIZONA_DRC1_CTRL1 0xE80 +#define ARIZONA_DRC1_CTRL2 0xE81 +#define ARIZONA_DRC1_CTRL3 0xE82 +#define ARIZONA_DRC1_CTRL4 0xE83 +#define ARIZONA_DRC1_CTRL5 0xE84 +#define ARIZONA_DRC2_CTRL1 0xE89 +#define ARIZONA_DRC2_CTRL2 0xE8A +#define ARIZONA_DRC2_CTRL3 0xE8B +#define ARIZONA_DRC2_CTRL4 0xE8C +#define ARIZONA_DRC2_CTRL5 0xE8D +#define ARIZONA_HPLPF1_1 0xEC0 +#define ARIZONA_HPLPF1_2 0xEC1 +#define ARIZONA_HPLPF2_1 0xEC4 +#define ARIZONA_HPLPF2_2 0xEC5 +#define ARIZONA_HPLPF3_1 0xEC8 +#define ARIZONA_HPLPF3_2 0xEC9 +#define ARIZONA_HPLPF4_1 0xECC +#define ARIZONA_HPLPF4_2 0xECD +#define ARIZONA_ASRC_ENABLE 0xEE0 +#define ARIZONA_ASRC_RATE1 0xEE2 +#define ARIZONA_ASRC_RATE2 0xEE3 +#define ARIZONA_ISRC_1_CTRL_1 0xEF0 +#define ARIZONA_ISRC_1_CTRL_2 0xEF1 +#define ARIZONA_ISRC_1_CTRL_3 0xEF2 +#define ARIZONA_ISRC_2_CTRL_1 0xEF3 +#define ARIZONA_ISRC_2_CTRL_2 0xEF4 +#define ARIZONA_ISRC_2_CTRL_3 0xEF5 +#define ARIZONA_ISRC_3_CTRL_1 0xEF6 +#define ARIZONA_ISRC_3_CTRL_2 0xEF7 +#define ARIZONA_ISRC_3_CTRL_3 0xEF8 +#define ARIZONA_DSP1_CONTROL_1 0x1100 +#define ARIZONA_DSP1_CLOCKING_1 0x1101 +#define ARIZONA_DSP1_STATUS_1 0x1104 +#define ARIZONA_DSP1_STATUS_2 0x1105 + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - software reset + */ +#define ARIZONA_SW_RST_DEV_ID1_MASK 0xFFFF /* SW_RST_DEV_ID1 - [15:0] */ +#define ARIZONA_SW_RST_DEV_ID1_SHIFT 0 /* SW_RST_DEV_ID1 - [15:0] */ +#define ARIZONA_SW_RST_DEV_ID1_WIDTH 16 /* SW_RST_DEV_ID1 - [15:0] */ + +/* + * R1 (0x01) - Device Revision + */ +#define ARIZONA_DEVICE_REVISION_MASK 0x00FF /* DEVICE_REVISION - [7:0] */ +#define ARIZONA_DEVICE_REVISION_SHIFT 0 /* DEVICE_REVISION - [7:0] */ +#define ARIZONA_DEVICE_REVISION_WIDTH 8 /* DEVICE_REVISION - [7:0] */ + +/* + * R8 (0x08) - Ctrl IF SPI CFG 1 + */ +#define ARIZONA_SPI_CFG 0x0010 /* SPI_CFG */ +#define ARIZONA_SPI_CFG_MASK 0x0010 /* SPI_CFG */ +#define ARIZONA_SPI_CFG_SHIFT 4 /* SPI_CFG */ +#define ARIZONA_SPI_CFG_WIDTH 1 /* SPI_CFG */ +#define ARIZONA_SPI_4WIRE 0x0008 /* SPI_4WIRE */ +#define ARIZONA_SPI_4WIRE_MASK 0x0008 /* SPI_4WIRE */ +#define ARIZONA_SPI_4WIRE_SHIFT 3 /* SPI_4WIRE */ +#define ARIZONA_SPI_4WIRE_WIDTH 1 /* SPI_4WIRE */ +#define ARIZONA_SPI_AUTO_INC_MASK 0x0003 /* SPI_AUTO_INC - [1:0] */ +#define ARIZONA_SPI_AUTO_INC_SHIFT 0 /* SPI_AUTO_INC - [1:0] */ +#define ARIZONA_SPI_AUTO_INC_WIDTH 2 /* SPI_AUTO_INC - [1:0] */ + +/* + * R9 (0x09) - Ctrl IF I2C1 CFG 1 + */ +#define ARIZONA_I2C1_AUTO_INC_MASK 0x0003 /* I2C1_AUTO_INC - [1:0] */ +#define ARIZONA_I2C1_AUTO_INC_SHIFT 0 /* I2C1_AUTO_INC - [1:0] */ +#define ARIZONA_I2C1_AUTO_INC_WIDTH 2 /* I2C1_AUTO_INC - [1:0] */ + +/* + * R13 (0x0D) - Ctrl IF Status 1 + */ +#define ARIZONA_I2C1_BUSY 0x0020 /* I2C1_BUSY */ +#define ARIZONA_I2C1_BUSY_MASK 0x0020 /* I2C1_BUSY */ +#define ARIZONA_I2C1_BUSY_SHIFT 5 /* I2C1_BUSY */ +#define ARIZONA_I2C1_BUSY_WIDTH 1 /* I2C1_BUSY */ +#define ARIZONA_SPI_BUSY 0x0010 /* SPI_BUSY */ +#define ARIZONA_SPI_BUSY_MASK 0x0010 /* SPI_BUSY */ +#define ARIZONA_SPI_BUSY_SHIFT 4 /* SPI_BUSY */ +#define ARIZONA_SPI_BUSY_WIDTH 1 /* SPI_BUSY */ + +/* + * R22 (0x16) - Write Sequencer Ctrl 0 + */ +#define ARIZONA_WSEQ_ABORT 0x0800 /* WSEQ_ABORT */ +#define ARIZONA_WSEQ_ABORT_MASK 0x0800 /* WSEQ_ABORT */ +#define ARIZONA_WSEQ_ABORT_SHIFT 11 /* WSEQ_ABORT */ +#define ARIZONA_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */ +#define ARIZONA_WSEQ_START 0x0400 /* WSEQ_START */ +#define ARIZONA_WSEQ_START_MASK 0x0400 /* WSEQ_START */ +#define ARIZONA_WSEQ_START_SHIFT 10 /* WSEQ_START */ +#define ARIZONA_WSEQ_START_WIDTH 1 /* WSEQ_START */ +#define ARIZONA_WSEQ_ENA 0x0200 /* WSEQ_ENA */ +#define ARIZONA_WSEQ_ENA_MASK 0x0200 /* WSEQ_ENA */ +#define ARIZONA_WSEQ_ENA_SHIFT 9 /* WSEQ_ENA */ +#define ARIZONA_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */ +#define ARIZONA_WSEQ_START_INDEX_MASK 0x01FF /* WSEQ_START_INDEX - [8:0] */ +#define ARIZONA_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [8:0] */ +#define ARIZONA_WSEQ_START_INDEX_WIDTH 9 /* WSEQ_START_INDEX - [8:0] */ + +/* + * R23 (0x17) - Write Sequencer Ctrl 1 + */ +#define ARIZONA_WSEQ_BUSY 0x0200 /* WSEQ_BUSY */ +#define ARIZONA_WSEQ_BUSY_MASK 0x0200 /* WSEQ_BUSY */ +#define ARIZONA_WSEQ_BUSY_SHIFT 9 /* WSEQ_BUSY */ +#define ARIZONA_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */ +#define ARIZONA_WSEQ_CURRENT_INDEX_MASK 0x01FF /* WSEQ_CURRENT_INDEX - [8:0] */ +#define ARIZONA_WSEQ_CURRENT_INDEX_SHIFT 0 /* WSEQ_CURRENT_INDEX - [8:0] */ +#define ARIZONA_WSEQ_CURRENT_INDEX_WIDTH 9 /* WSEQ_CURRENT_INDEX - [8:0] */ + +/* + * R24 (0x18) - Write Sequencer Ctrl 2 + */ +#define ARIZONA_LOAD_DEFAULTS 0x0002 /* LOAD_DEFAULTS */ +#define ARIZONA_LOAD_DEFAULTS_MASK 0x0002 /* LOAD_DEFAULTS */ +#define ARIZONA_LOAD_DEFAULTS_SHIFT 1 /* LOAD_DEFAULTS */ +#define ARIZONA_LOAD_DEFAULTS_WIDTH 1 /* LOAD_DEFAULTS */ +#define ARIZONA_WSEQ_LOAD_MEM 0x0001 /* WSEQ_LOAD_MEM */ +#define ARIZONA_WSEQ_LOAD_MEM_MASK 0x0001 /* WSEQ_LOAD_MEM */ +#define ARIZONA_WSEQ_LOAD_MEM_SHIFT 0 /* WSEQ_LOAD_MEM */ +#define ARIZONA_WSEQ_LOAD_MEM_WIDTH 1 /* WSEQ_LOAD_MEM */ + +/* + * R26 (0x1A) - Write Sequencer PROM + */ +#define ARIZONA_WSEQ_OTP_WRITE 0x0001 /* WSEQ_OTP_WRITE */ +#define ARIZONA_WSEQ_OTP_WRITE_MASK 0x0001 /* WSEQ_OTP_WRITE */ +#define ARIZONA_WSEQ_OTP_WRITE_SHIFT 0 /* WSEQ_OTP_WRITE */ +#define ARIZONA_WSEQ_OTP_WRITE_WIDTH 1 /* WSEQ_OTP_WRITE */ + +/* + * R32 (0x20) - Tone Generator 1 + */ +#define ARIZONA_TONE_RATE_MASK 0x7800 /* TONE_RATE - [14:11] */ +#define ARIZONA_TONE_RATE_SHIFT 11 /* TONE_RATE - [14:11] */ +#define ARIZONA_TONE_RATE_WIDTH 4 /* TONE_RATE - [14:11] */ +#define ARIZONA_TONE_OFFSET_MASK 0x0300 /* TONE_OFFSET - [9:8] */ +#define ARIZONA_TONE_OFFSET_SHIFT 8 /* TONE_OFFSET - [9:8] */ +#define ARIZONA_TONE_OFFSET_WIDTH 2 /* TONE_OFFSET - [9:8] */ +#define ARIZONA_TONE2_OVD 0x0020 /* TONE2_OVD */ +#define ARIZONA_TONE2_OVD_MASK 0x0020 /* TONE2_OVD */ +#define ARIZONA_TONE2_OVD_SHIFT 5 /* TONE2_OVD */ +#define ARIZONA_TONE2_OVD_WIDTH 1 /* TONE2_OVD */ +#define ARIZONA_TONE1_OVD 0x0010 /* TONE1_OVD */ +#define ARIZONA_TONE1_OVD_MASK 0x0010 /* TONE1_OVD */ +#define ARIZONA_TONE1_OVD_SHIFT 4 /* TONE1_OVD */ +#define ARIZONA_TONE1_OVD_WIDTH 1 /* TONE1_OVD */ +#define ARIZONA_TONE2_ENA 0x0002 /* TONE2_ENA */ +#define ARIZONA_TONE2_ENA_MASK 0x0002 /* TONE2_ENA */ +#define ARIZONA_TONE2_ENA_SHIFT 1 /* TONE2_ENA */ +#define ARIZONA_TONE2_ENA_WIDTH 1 /* TONE2_ENA */ +#define ARIZONA_TONE1_ENA 0x0001 /* TONE1_ENA */ +#define ARIZONA_TONE1_ENA_MASK 0x0001 /* TONE1_ENA */ +#define ARIZONA_TONE1_ENA_SHIFT 0 /* TONE1_ENA */ +#define ARIZONA_TONE1_ENA_WIDTH 1 /* TONE1_ENA */ + +/* + * R33 (0x21) - Tone Generator 2 + */ +#define ARIZONA_TONE1_LVL_0_MASK 0xFFFF /* TONE1_LVL - [15:0] */ +#define ARIZONA_TONE1_LVL_0_SHIFT 0 /* TONE1_LVL - [15:0] */ +#define ARIZONA_TONE1_LVL_0_WIDTH 16 /* TONE1_LVL - [15:0] */ + +/* + * R34 (0x22) - Tone Generator 3 + */ +#define ARIZONA_TONE1_LVL_MASK 0x00FF /* TONE1_LVL - [7:0] */ +#define ARIZONA_TONE1_LVL_SHIFT 0 /* TONE1_LVL - [7:0] */ +#define ARIZONA_TONE1_LVL_WIDTH 8 /* TONE1_LVL - [7:0] */ + +/* + * R35 (0x23) - Tone Generator 4 + */ +#define ARIZONA_TONE2_LVL_0_MASK 0xFFFF /* TONE2_LVL - [15:0] */ +#define ARIZONA_TONE2_LVL_0_SHIFT 0 /* TONE2_LVL - [15:0] */ +#define ARIZONA_TONE2_LVL_0_WIDTH 16 /* TONE2_LVL - [15:0] */ + +/* + * R36 (0x24) - Tone Generator 5 + */ +#define ARIZONA_TONE2_LVL_MASK 0x00FF /* TONE2_LVL - [7:0] */ +#define ARIZONA_TONE2_LVL_SHIFT 0 /* TONE2_LVL - [7:0] */ +#define ARIZONA_TONE2_LVL_WIDTH 8 /* TONE2_LVL - [7:0] */ + +/* + * R48 (0x30) - PWM Drive 1 + */ +#define ARIZONA_PWM_RATE_MASK 0x7800 /* PWM_RATE - [14:11] */ +#define ARIZONA_PWM_RATE_SHIFT 11 /* PWM_RATE - [14:11] */ +#define ARIZONA_PWM_RATE_WIDTH 4 /* PWM_RATE - [14:11] */ +#define ARIZONA_PWM_CLK_SEL_MASK 0x0700 /* PWM_CLK_SEL - [10:8] */ +#define ARIZONA_PWM_CLK_SEL_SHIFT 8 /* PWM_CLK_SEL - [10:8] */ +#define ARIZONA_PWM_CLK_SEL_WIDTH 3 /* PWM_CLK_SEL - [10:8] */ +#define ARIZONA_PWM2_OVD 0x0020 /* PWM2_OVD */ +#define ARIZONA_PWM2_OVD_MASK 0x0020 /* PWM2_OVD */ +#define ARIZONA_PWM2_OVD_SHIFT 5 /* PWM2_OVD */ +#define ARIZONA_PWM2_OVD_WIDTH 1 /* PWM2_OVD */ +#define ARIZONA_PWM1_OVD 0x0010 /* PWM1_OVD */ +#define ARIZONA_PWM1_OVD_MASK 0x0010 /* PWM1_OVD */ +#define ARIZONA_PWM1_OVD_SHIFT 4 /* PWM1_OVD */ +#define ARIZONA_PWM1_OVD_WIDTH 1 /* PWM1_OVD */ +#define ARIZONA_PWM2_ENA 0x0002 /* PWM2_ENA */ +#define ARIZONA_PWM2_ENA_MASK 0x0002 /* PWM2_ENA */ +#define ARIZONA_PWM2_ENA_SHIFT 1 /* PWM2_ENA */ +#define ARIZONA_PWM2_ENA_WIDTH 1 /* PWM2_ENA */ +#define ARIZONA_PWM1_ENA 0x0001 /* PWM1_ENA */ +#define ARIZONA_PWM1_ENA_MASK 0x0001 /* PWM1_ENA */ +#define ARIZONA_PWM1_ENA_SHIFT 0 /* PWM1_ENA */ +#define ARIZONA_PWM1_ENA_WIDTH 1 /* PWM1_ENA */ + +/* + * R49 (0x31) - PWM Drive 2 + */ +#define ARIZONA_PWM1_LVL_MASK 0x03FF /* PWM1_LVL - [9:0] */ +#define ARIZONA_PWM1_LVL_SHIFT 0 /* PWM1_LVL - [9:0] */ +#define ARIZONA_PWM1_LVL_WIDTH 10 /* PWM1_LVL - [9:0] */ + +/* + * R50 (0x32) - PWM Drive 3 + */ +#define ARIZONA_PWM2_LVL_MASK 0x03FF /* PWM2_LVL - [9:0] */ +#define ARIZONA_PWM2_LVL_SHIFT 0 /* PWM2_LVL - [9:0] */ +#define ARIZONA_PWM2_LVL_WIDTH 10 /* PWM2_LVL - [9:0] */ + +/* + * R64 (0x40) - Wake control + */ +#define ARIZONA_WKUP_GP5_FALL 0x0020 /* WKUP_GP5_FALL */ +#define ARIZONA_WKUP_GP5_FALL_MASK 0x0020 /* WKUP_GP5_FALL */ +#define ARIZONA_WKUP_GP5_FALL_SHIFT 5 /* WKUP_GP5_FALL */ +#define ARIZONA_WKUP_GP5_FALL_WIDTH 1 /* WKUP_GP5_FALL */ +#define ARIZONA_WKUP_GP5_RISE 0x0010 /* WKUP_GP5_RISE */ +#define ARIZONA_WKUP_GP5_RISE_MASK 0x0010 /* WKUP_GP5_RISE */ +#define ARIZONA_WKUP_GP5_RISE_SHIFT 4 /* WKUP_GP5_RISE */ +#define ARIZONA_WKUP_GP5_RISE_WIDTH 1 /* WKUP_GP5_RISE */ +#define ARIZONA_WKUP_JD1_FALL 0x0008 /* WKUP_JD1_FALL */ +#define ARIZONA_WKUP_JD1_FALL_MASK 0x0008 /* WKUP_JD1_FALL */ +#define ARIZONA_WKUP_JD1_FALL_SHIFT 3 /* WKUP_JD1_FALL */ +#define ARIZONA_WKUP_JD1_FALL_WIDTH 1 /* WKUP_JD1_FALL */ +#define ARIZONA_WKUP_JD1_RISE 0x0004 /* WKUP_JD1_RISE */ +#define ARIZONA_WKUP_JD1_RISE_MASK 0x0004 /* WKUP_JD1_RISE */ +#define ARIZONA_WKUP_JD1_RISE_SHIFT 2 /* WKUP_JD1_RISE */ +#define ARIZONA_WKUP_JD1_RISE_WIDTH 1 /* WKUP_JD1_RISE */ +#define ARIZONA_WKUP_JD2_FALL 0x0002 /* WKUP_JD2_FALL */ +#define ARIZONA_WKUP_JD2_FALL_MASK 0x0002 /* WKUP_JD2_FALL */ +#define ARIZONA_WKUP_JD2_FALL_SHIFT 1 /* WKUP_JD2_FALL */ +#define ARIZONA_WKUP_JD2_FALL_WIDTH 1 /* WKUP_JD2_FALL */ +#define ARIZONA_WKUP_JD2_RISE 0x0001 /* WKUP_JD2_RISE */ +#define ARIZONA_WKUP_JD2_RISE_MASK 0x0001 /* WKUP_JD2_RISE */ +#define ARIZONA_WKUP_JD2_RISE_SHIFT 0 /* WKUP_JD2_RISE */ +#define ARIZONA_WKUP_JD2_RISE_WIDTH 1 /* WKUP_JD2_RISE */ + +/* + * R65 (0x41) - Sequence control + */ +#define ARIZONA_WSEQ_ENA_GP5_FALL 0x0020 /* WSEQ_ENA_GP5_FALL */ +#define ARIZONA_WSEQ_ENA_GP5_FALL_MASK 0x0020 /* WSEQ_ENA_GP5_FALL */ +#define ARIZONA_WSEQ_ENA_GP5_FALL_SHIFT 5 /* WSEQ_ENA_GP5_FALL */ +#define ARIZONA_WSEQ_ENA_GP5_FALL_WIDTH 1 /* WSEQ_ENA_GP5_FALL */ +#define ARIZONA_WSEQ_ENA_GP5_RISE 0x0010 /* WSEQ_ENA_GP5_RISE */ +#define ARIZONA_WSEQ_ENA_GP5_RISE_MASK 0x0010 /* WSEQ_ENA_GP5_RISE */ +#define ARIZONA_WSEQ_ENA_GP5_RISE_SHIFT 4 /* WSEQ_ENA_GP5_RISE */ +#define ARIZONA_WSEQ_ENA_GP5_RISE_WIDTH 1 /* WSEQ_ENA_GP5_RISE */ +#define ARIZONA_WSEQ_ENA_JD1_FALL 0x0008 /* WSEQ_ENA_JD1_FALL */ +#define ARIZONA_WSEQ_ENA_JD1_FALL_MASK 0x0008 /* WSEQ_ENA_JD1_FALL */ +#define ARIZONA_WSEQ_ENA_JD1_FALL_SHIFT 3 /* WSEQ_ENA_JD1_FALL */ +#define ARIZONA_WSEQ_ENA_JD1_FALL_WIDTH 1 /* WSEQ_ENA_JD1_FALL */ +#define ARIZONA_WSEQ_ENA_JD1_RISE 0x0004 /* WSEQ_ENA_JD1_RISE */ +#define ARIZONA_WSEQ_ENA_JD1_RISE_MASK 0x0004 /* WSEQ_ENA_JD1_RISE */ +#define ARIZONA_WSEQ_ENA_JD1_RISE_SHIFT 2 /* WSEQ_ENA_JD1_RISE */ +#define ARIZONA_WSEQ_ENA_JD1_RISE_WIDTH 1 /* WSEQ_ENA_JD1_RISE */ +#define ARIZONA_WSEQ_ENA_JD2_FALL 0x0002 /* WSEQ_ENA_JD2_FALL */ +#define ARIZONA_WSEQ_ENA_JD2_FALL_MASK 0x0002 /* WSEQ_ENA_JD2_FALL */ +#define ARIZONA_WSEQ_ENA_JD2_FALL_SHIFT 1 /* WSEQ_ENA_JD2_FALL */ +#define ARIZONA_WSEQ_ENA_JD2_FALL_WIDTH 1 /* WSEQ_ENA_JD2_FALL */ +#define ARIZONA_WSEQ_ENA_JD2_RISE 0x0001 /* WSEQ_ENA_JD2_RISE */ +#define ARIZONA_WSEQ_ENA_JD2_RISE_MASK 0x0001 /* WSEQ_ENA_JD2_RISE */ +#define ARIZONA_WSEQ_ENA_JD2_RISE_SHIFT 0 /* WSEQ_ENA_JD2_RISE */ +#define ARIZONA_WSEQ_ENA_JD2_RISE_WIDTH 1 /* WSEQ_ENA_JD2_RISE */ + +/* + * R97 (0x61) - Sample Rate Sequence Select 1 + */ +#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_A_SEQ_ADDR_MASK 0x01FF /* WSEQ_SAMPLE_RATE_DETECT_A_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_A_SEQ_ADDR_SHIFT 0 /* WSEQ_SAMPLE_RATE_DETECT_A_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_A_SEQ_ADDR_WIDTH 9 /* WSEQ_SAMPLE_RATE_DETECT_A_SEQ_ADDR - [8:0] */ + +/* + * R98 (0x62) - Sample Rate Sequence Select 2 + */ +#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_B_SEQ_ADDR_MASK 0x01FF /* WSEQ_SAMPLE_RATE_DETECT_B_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_B_SEQ_ADDR_SHIFT 0 /* WSEQ_SAMPLE_RATE_DETECT_B_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_B_SEQ_ADDR_WIDTH 9 /* WSEQ_SAMPLE_RATE_DETECT_B_SEQ_ADDR - [8:0] */ + +/* + * R99 (0x63) - Sample Rate Sequence Select 3 + */ +#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_C_SEQ_ADDR_MASK 0x01FF /* WSEQ_SAMPLE_RATE_DETECT_C_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_C_SEQ_ADDR_SHIFT 0 /* WSEQ_SAMPLE_RATE_DETECT_C_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_C_SEQ_ADDR_WIDTH 9 /* WSEQ_SAMPLE_RATE_DETECT_C_SEQ_ADDR - [8:0] */ + +/* + * R100 (0x64) - Sample Rate Sequence Select 4 + */ +#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_D_SEQ_ADDR_MASK 0x01FF /* WSEQ_SAMPLE_RATE_DETECT_D_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_D_SEQ_ADDR_SHIFT 0 /* WSEQ_SAMPLE_RATE_DETECT_D_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_D_SEQ_ADDR_WIDTH 9 /* WSEQ_SAMPLE_RATE_DETECT_D_SEQ_ADDR - [8:0] */ + +/* + * R104 (0x68) - Always On Triggers Sequence Select 1 + */ +#define ARIZONA_WSEQ_GP5_RISE_SEQ_ADDR_MASK 0x01FF /* WSEQ_GP5_RISE_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_GP5_RISE_SEQ_ADDR_SHIFT 0 /* WSEQ_GP5_RISE_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_GP5_RISE_SEQ_ADDR_WIDTH 9 /* WSEQ_GP5_RISE_SEQ_ADDR - [8:0] */ + +/* + * R105 (0x69) - Always On Triggers Sequence Select 2 + */ +#define ARIZONA_WSEQ_GP5_FALL_SEQ_ADDR_MASK 0x01FF /* WSEQ_GP5_FALL_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_GP5_FALL_SEQ_ADDR_SHIFT 0 /* WSEQ_GP5_FALL_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_GP5_FALL_SEQ_ADDR_WIDTH 9 /* WSEQ_GP5_FALL_SEQ_ADDR - [8:0] */ + +/* + * R106 (0x6A) - Always On Triggers Sequence Select 3 + */ +#define ARIZONA_WSEQ_JD1_RISE_SEQ_ADDR_MASK 0x01FF /* WSEQ_JD1_RISE_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_JD1_RISE_SEQ_ADDR_SHIFT 0 /* WSEQ_JD1_RISE_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_JD1_RISE_SEQ_ADDR_WIDTH 9 /* WSEQ_JD1_RISE_SEQ_ADDR - [8:0] */ + +/* + * R107 (0x6B) - Always On Triggers Sequence Select 4 + */ +#define ARIZONA_WSEQ_JD1_FALL_SEQ_ADDR_MASK 0x01FF /* WSEQ_JD1_FALL_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_JD1_FALL_SEQ_ADDR_SHIFT 0 /* WSEQ_JD1_FALL_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_JD1_FALL_SEQ_ADDR_WIDTH 9 /* WSEQ_JD1_FALL_SEQ_ADDR - [8:0] */ + +/* + * R108 (0x6C) - Always On Triggers Sequence Select 5 + */ +#define ARIZONA_WSEQ_JD2_RISE_SEQ_ADDR_MASK 0x01FF /* WSEQ_JD2_RISE_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_JD2_RISE_SEQ_ADDR_SHIFT 0 /* WSEQ_JD2_RISE_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_JD2_RISE_SEQ_ADDR_WIDTH 9 /* WSEQ_JD2_RISE_SEQ_ADDR - [8:0] */ + +/* + * R109 (0x6D) - Always On Triggers Sequence Select 6 + */ +#define ARIZONA_WSEQ_JD2_FALL_SEQ_ADDR_MASK 0x01FF /* WSEQ_JD2_FALL_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_JD2_FALL_SEQ_ADDR_SHIFT 0 /* WSEQ_JD2_FALL_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_JD2_FALL_SEQ_ADDR_WIDTH 9 /* WSEQ_JD2_FALL_SEQ_ADDR - [8:0] */ + +/* + * R112 (0x70) - Comfort Noise Generator + */ +#define ARIZONA_NOISE_GEN_RATE_MASK 0x7800 /* NOISE_GEN_RATE - [14:11] */ +#define ARIZONA_NOISE_GEN_RATE_SHIFT 11 /* NOISE_GEN_RATE - [14:11] */ +#define ARIZONA_NOISE_GEN_RATE_WIDTH 4 /* NOISE_GEN_RATE - [14:11] */ +#define ARIZONA_NOISE_GEN_ENA 0x0020 /* NOISE_GEN_ENA */ +#define ARIZONA_NOISE_GEN_ENA_MASK 0x0020 /* NOISE_GEN_ENA */ +#define ARIZONA_NOISE_GEN_ENA_SHIFT 5 /* NOISE_GEN_ENA */ +#define ARIZONA_NOISE_GEN_ENA_WIDTH 1 /* NOISE_GEN_ENA */ +#define ARIZONA_NOISE_GEN_GAIN_MASK 0x001F /* NOISE_GEN_GAIN - [4:0] */ +#define ARIZONA_NOISE_GEN_GAIN_SHIFT 0 /* NOISE_GEN_GAIN - [4:0] */ +#define ARIZONA_NOISE_GEN_GAIN_WIDTH 5 /* NOISE_GEN_GAIN - [4:0] */ + +/* + * R144 (0x90) - Haptics Control 1 + */ +#define ARIZONA_HAP_RATE_MASK 0x7800 /* HAP_RATE - [14:11] */ +#define ARIZONA_HAP_RATE_SHIFT 11 /* HAP_RATE - [14:11] */ +#define ARIZONA_HAP_RATE_WIDTH 4 /* HAP_RATE - [14:11] */ +#define ARIZONA_ONESHOT_TRIG 0x0010 /* ONESHOT_TRIG */ +#define ARIZONA_ONESHOT_TRIG_MASK 0x0010 /* ONESHOT_TRIG */ +#define ARIZONA_ONESHOT_TRIG_SHIFT 4 /* ONESHOT_TRIG */ +#define ARIZONA_ONESHOT_TRIG_WIDTH 1 /* ONESHOT_TRIG */ +#define ARIZONA_HAP_CTRL_MASK 0x000C /* HAP_CTRL - [3:2] */ +#define ARIZONA_HAP_CTRL_SHIFT 2 /* HAP_CTRL - [3:2] */ +#define ARIZONA_HAP_CTRL_WIDTH 2 /* HAP_CTRL - [3:2] */ +#define ARIZONA_HAP_ACT 0x0002 /* HAP_ACT */ +#define ARIZONA_HAP_ACT_MASK 0x0002 /* HAP_ACT */ +#define ARIZONA_HAP_ACT_SHIFT 1 /* HAP_ACT */ +#define ARIZONA_HAP_ACT_WIDTH 1 /* HAP_ACT */ + +/* + * R145 (0x91) - Haptics Control 2 + */ +#define ARIZONA_LRA_FREQ_MASK 0x7FFF /* LRA_FREQ - [14:0] */ +#define ARIZONA_LRA_FREQ_SHIFT 0 /* LRA_FREQ - [14:0] */ +#define ARIZONA_LRA_FREQ_WIDTH 15 /* LRA_FREQ - [14:0] */ + +/* + * R146 (0x92) - Haptics phase 1 intensity + */ +#define ARIZONA_PHASE1_INTENSITY_MASK 0x00FF /* PHASE1_INTENSITY - [7:0] */ +#define ARIZONA_PHASE1_INTENSITY_SHIFT 0 /* PHASE1_INTENSITY - [7:0] */ +#define ARIZONA_PHASE1_INTENSITY_WIDTH 8 /* PHASE1_INTENSITY - [7:0] */ + +/* + * R147 (0x93) - Haptics phase 1 duration + */ +#define ARIZONA_PHASE1_DURATION_MASK 0x01FF /* PHASE1_DURATION - [8:0] */ +#define ARIZONA_PHASE1_DURATION_SHIFT 0 /* PHASE1_DURATION - [8:0] */ +#define ARIZONA_PHASE1_DURATION_WIDTH 9 /* PHASE1_DURATION - [8:0] */ + +/* + * R148 (0x94) - Haptics phase 2 intensity + */ +#define ARIZONA_PHASE2_INTENSITY_MASK 0x00FF /* PHASE2_INTENSITY - [7:0] */ +#define ARIZONA_PHASE2_INTENSITY_SHIFT 0 /* PHASE2_INTENSITY - [7:0] */ +#define ARIZONA_PHASE2_INTENSITY_WIDTH 8 /* PHASE2_INTENSITY - [7:0] */ + +/* + * R149 (0x95) - Haptics phase 2 duration + */ +#define ARIZONA_PHASE2_DURATION_MASK 0x07FF /* PHASE2_DURATION - [10:0] */ +#define ARIZONA_PHASE2_DURATION_SHIFT 0 /* PHASE2_DURATION - [10:0] */ +#define ARIZONA_PHASE2_DURATION_WIDTH 11 /* PHASE2_DURATION - [10:0] */ + +/* + * R150 (0x96) - Haptics phase 3 intensity + */ +#define ARIZONA_PHASE3_INTENSITY_MASK 0x00FF /* PHASE3_INTENSITY - [7:0] */ +#define ARIZONA_PHASE3_INTENSITY_SHIFT 0 /* PHASE3_INTENSITY - [7:0] */ +#define ARIZONA_PHASE3_INTENSITY_WIDTH 8 /* PHASE3_INTENSITY - [7:0] */ + +/* + * R151 (0x97) - Haptics phase 3 duration + */ +#define ARIZONA_PHASE3_DURATION_MASK 0x01FF /* PHASE3_DURATION - [8:0] */ +#define ARIZONA_PHASE3_DURATION_SHIFT 0 /* PHASE3_DURATION - [8:0] */ +#define ARIZONA_PHASE3_DURATION_WIDTH 9 /* PHASE3_DURATION - [8:0] */ + +/* + * R152 (0x98) - Haptics Status + */ +#define ARIZONA_ONESHOT_STS 0x0001 /* ONESHOT_STS */ +#define ARIZONA_ONESHOT_STS_MASK 0x0001 /* ONESHOT_STS */ +#define ARIZONA_ONESHOT_STS_SHIFT 0 /* ONESHOT_STS */ +#define ARIZONA_ONESHOT_STS_WIDTH 1 /* ONESHOT_STS */ + +/* + * R256 (0x100) - Clock 32k 1 + */ +#define ARIZONA_CLK_32K_ENA 0x0040 /* CLK_32K_ENA */ +#define ARIZONA_CLK_32K_ENA_MASK 0x0040 /* CLK_32K_ENA */ +#define ARIZONA_CLK_32K_ENA_SHIFT 6 /* CLK_32K_ENA */ +#define ARIZONA_CLK_32K_ENA_WIDTH 1 /* CLK_32K_ENA */ +#define ARIZONA_CLK_32K_SRC_MASK 0x0003 /* CLK_32K_SRC - [1:0] */ +#define ARIZONA_CLK_32K_SRC_SHIFT 0 /* CLK_32K_SRC - [1:0] */ +#define ARIZONA_CLK_32K_SRC_WIDTH 2 /* CLK_32K_SRC - [1:0] */ + +/* + * R257 (0x101) - System Clock 1 + */ +#define ARIZONA_SYSCLK_FRAC 0x8000 /* SYSCLK_FRAC */ +#define ARIZONA_SYSCLK_FRAC_MASK 0x8000 /* SYSCLK_FRAC */ +#define ARIZONA_SYSCLK_FRAC_SHIFT 15 /* SYSCLK_FRAC */ +#define ARIZONA_SYSCLK_FRAC_WIDTH 1 /* SYSCLK_FRAC */ +#define ARIZONA_SYSCLK_FREQ_MASK 0x0700 /* SYSCLK_FREQ - [10:8] */ +#define ARIZONA_SYSCLK_FREQ_SHIFT 8 /* SYSCLK_FREQ - [10:8] */ +#define ARIZONA_SYSCLK_FREQ_WIDTH 3 /* SYSCLK_FREQ - [10:8] */ +#define ARIZONA_SYSCLK_ENA 0x0040 /* SYSCLK_ENA */ +#define ARIZONA_SYSCLK_ENA_MASK 0x0040 /* SYSCLK_ENA */ +#define ARIZONA_SYSCLK_ENA_SHIFT 6 /* SYSCLK_ENA */ +#define ARIZONA_SYSCLK_ENA_WIDTH 1 /* SYSCLK_ENA */ +#define ARIZONA_SYSCLK_SRC_MASK 0x000F /* SYSCLK_SRC - [3:0] */ +#define ARIZONA_SYSCLK_SRC_SHIFT 0 /* SYSCLK_SRC - [3:0] */ +#define ARIZONA_SYSCLK_SRC_WIDTH 4 /* SYSCLK_SRC - [3:0] */ + +/* + * R258 (0x102) - Sample rate 1 + */ +#define ARIZONA_SAMPLE_RATE_1_MASK 0x001F /* SAMPLE_RATE_1 - [4:0] */ +#define ARIZONA_SAMPLE_RATE_1_SHIFT 0 /* SAMPLE_RATE_1 - [4:0] */ +#define ARIZONA_SAMPLE_RATE_1_WIDTH 5 /* SAMPLE_RATE_1 - [4:0] */ + +/* + * R259 (0x103) - Sample rate 2 + */ +#define ARIZONA_SAMPLE_RATE_2_MASK 0x001F /* SAMPLE_RATE_2 - [4:0] */ +#define ARIZONA_SAMPLE_RATE_2_SHIFT 0 /* SAMPLE_RATE_2 - [4:0] */ +#define ARIZONA_SAMPLE_RATE_2_WIDTH 5 /* SAMPLE_RATE_2 - [4:0] */ + +/* + * R260 (0x104) - Sample rate 3 + */ +#define ARIZONA_SAMPLE_RATE_3_MASK 0x001F /* SAMPLE_RATE_3 - [4:0] */ +#define ARIZONA_SAMPLE_RATE_3_SHIFT 0 /* SAMPLE_RATE_3 - [4:0] */ +#define ARIZONA_SAMPLE_RATE_3_WIDTH 5 /* SAMPLE_RATE_3 - [4:0] */ + +/* + * R266 (0x10A) - Sample rate 1 status + */ +#define ARIZONA_SAMPLE_RATE_1_STS_MASK 0x001F /* SAMPLE_RATE_1_STS - [4:0] */ +#define ARIZONA_SAMPLE_RATE_1_STS_SHIFT 0 /* SAMPLE_RATE_1_STS - [4:0] */ +#define ARIZONA_SAMPLE_RATE_1_STS_WIDTH 5 /* SAMPLE_RATE_1_STS - [4:0] */ + +/* + * R267 (0x10B) - Sample rate 2 status + */ +#define ARIZONA_SAMPLE_RATE_2_STS_MASK 0x001F /* SAMPLE_RATE_2_STS - [4:0] */ +#define ARIZONA_SAMPLE_RATE_2_STS_SHIFT 0 /* SAMPLE_RATE_2_STS - [4:0] */ +#define ARIZONA_SAMPLE_RATE_2_STS_WIDTH 5 /* SAMPLE_RATE_2_STS - [4:0] */ + +/* + * R268 (0x10C) - Sample rate 3 status + */ +#define ARIZONA_SAMPLE_RATE_3_STS_MASK 0x001F /* SAMPLE_RATE_3_STS - [4:0] */ +#define ARIZONA_SAMPLE_RATE_3_STS_SHIFT 0 /* SAMPLE_RATE_3_STS - [4:0] */ +#define ARIZONA_SAMPLE_RATE_3_STS_WIDTH 5 /* SAMPLE_RATE_3_STS - [4:0] */ + +/* + * R274 (0x112) - Async clock 1 + */ +#define ARIZONA_ASYNC_CLK_FREQ_MASK 0x0700 /* ASYNC_CLK_FREQ - [10:8] */ +#define ARIZONA_ASYNC_CLK_FREQ_SHIFT 8 /* ASYNC_CLK_FREQ - [10:8] */ +#define ARIZONA_ASYNC_CLK_FREQ_WIDTH 3 /* ASYNC_CLK_FREQ - [10:8] */ +#define ARIZONA_ASYNC_CLK_ENA 0x0040 /* ASYNC_CLK_ENA */ +#define ARIZONA_ASYNC_CLK_ENA_MASK 0x0040 /* ASYNC_CLK_ENA */ +#define ARIZONA_ASYNC_CLK_ENA_SHIFT 6 /* ASYNC_CLK_ENA */ +#define ARIZONA_ASYNC_CLK_ENA_WIDTH 1 /* ASYNC_CLK_ENA */ +#define ARIZONA_ASYNC_CLK_SRC_MASK 0x000F /* ASYNC_CLK_SRC - [3:0] */ +#define ARIZONA_ASYNC_CLK_SRC_SHIFT 0 /* ASYNC_CLK_SRC - [3:0] */ +#define ARIZONA_ASYNC_CLK_SRC_WIDTH 4 /* ASYNC_CLK_SRC - [3:0] */ + +/* + * R275 (0x113) - Async sample rate 1 + */ +#define ARIZONA_ASYNC_SAMPLE_RATE_MASK 0x001F /* ASYNC_SAMPLE_RATE - [4:0] */ +#define ARIZONA_ASYNC_SAMPLE_RATE_SHIFT 0 /* ASYNC_SAMPLE_RATE - [4:0] */ +#define ARIZONA_ASYNC_SAMPLE_RATE_WIDTH 5 /* ASYNC_SAMPLE_RATE - [4:0] */ + +/* + * R283 (0x11B) - Async sample rate 1 status + */ +#define ARIZONA_ASYNC_SAMPLE_RATE_STS_MASK 0x001F /* ASYNC_SAMPLE_RATE_STS - [4:0] */ +#define ARIZONA_ASYNC_SAMPLE_RATE_STS_SHIFT 0 /* ASYNC_SAMPLE_RATE_STS - [4:0] */ +#define ARIZONA_ASYNC_SAMPLE_RATE_STS_WIDTH 5 /* ASYNC_SAMPLE_RATE_STS - [4:0] */ + +/* + * R329 (0x149) - Output system clock + */ +#define ARIZONA_OPCLK_ENA 0x8000 /* OPCLK_ENA */ +#define ARIZONA_OPCLK_ENA_MASK 0x8000 /* OPCLK_ENA */ +#define ARIZONA_OPCLK_ENA_SHIFT 15 /* OPCLK_ENA */ +#define ARIZONA_OPCLK_ENA_WIDTH 1 /* OPCLK_ENA */ +#define ARIZONA_OPCLK_DIV_MASK 0x00F8 /* OPCLK_DIV - [7:3] */ +#define ARIZONA_OPCLK_DIV_SHIFT 3 /* OPCLK_DIV - [7:3] */ +#define ARIZONA_OPCLK_DIV_WIDTH 5 /* OPCLK_DIV - [7:3] */ +#define ARIZONA_OPCLK_SEL_MASK 0x0007 /* OPCLK_SEL - [2:0] */ +#define ARIZONA_OPCLK_SEL_SHIFT 0 /* OPCLK_SEL - [2:0] */ +#define ARIZONA_OPCLK_SEL_WIDTH 3 /* OPCLK_SEL - [2:0] */ + +/* + * R330 (0x14A) - Output async clock + */ +#define ARIZONA_OPCLK_ASYNC_ENA 0x8000 /* OPCLK_ASYNC_ENA */ +#define ARIZONA_OPCLK_ASYNC_ENA_MASK 0x8000 /* OPCLK_ASYNC_ENA */ +#define ARIZONA_OPCLK_ASYNC_ENA_SHIFT 15 /* OPCLK_ASYNC_ENA */ +#define ARIZONA_OPCLK_ASYNC_ENA_WIDTH 1 /* OPCLK_ASYNC_ENA */ +#define ARIZONA_OPCLK_ASYNC_DIV_MASK 0x00F8 /* OPCLK_ASYNC_DIV - [7:3] */ +#define ARIZONA_OPCLK_ASYNC_DIV_SHIFT 3 /* OPCLK_ASYNC_DIV - [7:3] */ +#define ARIZONA_OPCLK_ASYNC_DIV_WIDTH 5 /* OPCLK_ASYNC_DIV - [7:3] */ +#define ARIZONA_OPCLK_ASYNC_SEL_MASK 0x0007 /* OPCLK_ASYNC_SEL - [2:0] */ +#define ARIZONA_OPCLK_ASYNC_SEL_SHIFT 0 /* OPCLK_ASYNC_SEL - [2:0] */ +#define ARIZONA_OPCLK_ASYNC_SEL_WIDTH 3 /* OPCLK_ASYNC_SEL - [2:0] */ + +/* + * R338 (0x152) - Rate Estimator 1 + */ +#define ARIZONA_TRIG_ON_STARTUP 0x0010 /* TRIG_ON_STARTUP */ +#define ARIZONA_TRIG_ON_STARTUP_MASK 0x0010 /* TRIG_ON_STARTUP */ +#define ARIZONA_TRIG_ON_STARTUP_SHIFT 4 /* TRIG_ON_STARTUP */ +#define ARIZONA_TRIG_ON_STARTUP_WIDTH 1 /* TRIG_ON_STARTUP */ +#define ARIZONA_LRCLK_SRC_MASK 0x000E /* LRCLK_SRC - [3:1] */ +#define ARIZONA_LRCLK_SRC_SHIFT 1 /* LRCLK_SRC - [3:1] */ +#define ARIZONA_LRCLK_SRC_WIDTH 3 /* LRCLK_SRC - [3:1] */ +#define ARIZONA_RATE_EST_ENA 0x0001 /* RATE_EST_ENA */ +#define ARIZONA_RATE_EST_ENA_MASK 0x0001 /* RATE_EST_ENA */ +#define ARIZONA_RATE_EST_ENA_SHIFT 0 /* RATE_EST_ENA */ +#define ARIZONA_RATE_EST_ENA_WIDTH 1 /* RATE_EST_ENA */ + +/* + * R339 (0x153) - Rate Estimator 2 + */ +#define ARIZONA_SAMPLE_RATE_DETECT_A_MASK 0x001F /* SAMPLE_RATE_DETECT_A - [4:0] */ +#define ARIZONA_SAMPLE_RATE_DETECT_A_SHIFT 0 /* SAMPLE_RATE_DETECT_A - [4:0] */ +#define ARIZONA_SAMPLE_RATE_DETECT_A_WIDTH 5 /* SAMPLE_RATE_DETECT_A - [4:0] */ + +/* + * R340 (0x154) - Rate Estimator 3 + */ +#define ARIZONA_SAMPLE_RATE_DETECT_B_MASK 0x001F /* SAMPLE_RATE_DETECT_B - [4:0] */ +#define ARIZONA_SAMPLE_RATE_DETECT_B_SHIFT 0 /* SAMPLE_RATE_DETECT_B - [4:0] */ +#define ARIZONA_SAMPLE_RATE_DETECT_B_WIDTH 5 /* SAMPLE_RATE_DETECT_B - [4:0] */ + +/* + * R341 (0x155) - Rate Estimator 4 + */ +#define ARIZONA_SAMPLE_RATE_DETECT_C_MASK 0x001F /* SAMPLE_RATE_DETECT_C - [4:0] */ +#define ARIZONA_SAMPLE_RATE_DETECT_C_SHIFT 0 /* SAMPLE_RATE_DETECT_C - [4:0] */ +#define ARIZONA_SAMPLE_RATE_DETECT_C_WIDTH 5 /* SAMPLE_RATE_DETECT_C - [4:0] */ + +/* + * R342 (0x156) - Rate Estimator 5 + */ +#define ARIZONA_SAMPLE_RATE_DETECT_D_MASK 0x001F /* SAMPLE_RATE_DETECT_D - [4:0] */ +#define ARIZONA_SAMPLE_RATE_DETECT_D_SHIFT 0 /* SAMPLE_RATE_DETECT_D - [4:0] */ +#define ARIZONA_SAMPLE_RATE_DETECT_D_WIDTH 5 /* SAMPLE_RATE_DETECT_D - [4:0] */ + +/* + * R369 (0x171) - FLL1 Control 1 + */ +#define ARIZONA_FLL1_FREERUN 0x0002 /* FLL1_FREERUN */ +#define ARIZONA_FLL1_FREERUN_MASK 0x0002 /* FLL1_FREERUN */ +#define ARIZONA_FLL1_FREERUN_SHIFT 1 /* FLL1_FREERUN */ +#define ARIZONA_FLL1_FREERUN_WIDTH 1 /* FLL1_FREERUN */ +#define ARIZONA_FLL1_ENA 0x0001 /* FLL1_ENA */ +#define ARIZONA_FLL1_ENA_MASK 0x0001 /* FLL1_ENA */ +#define ARIZONA_FLL1_ENA_SHIFT 0 /* FLL1_ENA */ +#define ARIZONA_FLL1_ENA_WIDTH 1 /* FLL1_ENA */ + +/* + * R370 (0x172) - FLL1 Control 2 + */ +#define ARIZONA_FLL1_CTRL_UPD 0x8000 /* FLL1_CTRL_UPD */ +#define ARIZONA_FLL1_CTRL_UPD_MASK 0x8000 /* FLL1_CTRL_UPD */ +#define ARIZONA_FLL1_CTRL_UPD_SHIFT 15 /* FLL1_CTRL_UPD */ +#define ARIZONA_FLL1_CTRL_UPD_WIDTH 1 /* FLL1_CTRL_UPD */ +#define ARIZONA_FLL1_N_MASK 0x03FF /* FLL1_N - [9:0] */ +#define ARIZONA_FLL1_N_SHIFT 0 /* FLL1_N - [9:0] */ +#define ARIZONA_FLL1_N_WIDTH 10 /* FLL1_N - [9:0] */ + +/* + * R371 (0x173) - FLL1 Control 3 + */ +#define ARIZONA_FLL1_THETA_MASK 0xFFFF /* FLL1_THETA - [15:0] */ +#define ARIZONA_FLL1_THETA_SHIFT 0 /* FLL1_THETA - [15:0] */ +#define ARIZONA_FLL1_THETA_WIDTH 16 /* FLL1_THETA - [15:0] */ + +/* + * R372 (0x174) - FLL1 Control 4 + */ +#define ARIZONA_FLL1_LAMBDA_MASK 0xFFFF /* FLL1_LAMBDA - [15:0] */ +#define ARIZONA_FLL1_LAMBDA_SHIFT 0 /* FLL1_LAMBDA - [15:0] */ +#define ARIZONA_FLL1_LAMBDA_WIDTH 16 /* FLL1_LAMBDA - [15:0] */ + +/* + * R373 (0x175) - FLL1 Control 5 + */ +#define ARIZONA_FLL1_FRATIO_MASK 0x0700 /* FLL1_FRATIO - [10:8] */ +#define ARIZONA_FLL1_FRATIO_SHIFT 8 /* FLL1_FRATIO - [10:8] */ +#define ARIZONA_FLL1_FRATIO_WIDTH 3 /* FLL1_FRATIO - [10:8] */ +#define ARIZONA_FLL1_OUTDIV_MASK 0x000E /* FLL1_OUTDIV - [3:1] */ +#define ARIZONA_FLL1_OUTDIV_SHIFT 1 /* FLL1_OUTDIV - [3:1] */ +#define ARIZONA_FLL1_OUTDIV_WIDTH 3 /* FLL1_OUTDIV - [3:1] */ + +/* + * R374 (0x176) - FLL1 Control 6 + */ +#define ARIZONA_FLL1_CLK_REF_DIV_MASK 0x00C0 /* FLL1_CLK_REF_DIV - [7:6] */ +#define ARIZONA_FLL1_CLK_REF_DIV_SHIFT 6 /* FLL1_CLK_REF_DIV - [7:6] */ +#define ARIZONA_FLL1_CLK_REF_DIV_WIDTH 2 /* FLL1_CLK_REF_DIV - [7:6] */ +#define ARIZONA_FLL1_CLK_REF_SRC_MASK 0x000F /* FLL1_CLK_REF_SRC - [3:0] */ +#define ARIZONA_FLL1_CLK_REF_SRC_SHIFT 0 /* FLL1_CLK_REF_SRC - [3:0] */ +#define ARIZONA_FLL1_CLK_REF_SRC_WIDTH 4 /* FLL1_CLK_REF_SRC - [3:0] */ + +/* + * R375 (0x177) - FLL1 Loop Filter Test 1 + */ +#define ARIZONA_FLL1_FRC_INTEG_UPD 0x8000 /* FLL1_FRC_INTEG_UPD */ +#define ARIZONA_FLL1_FRC_INTEG_UPD_MASK 0x8000 /* FLL1_FRC_INTEG_UPD */ +#define ARIZONA_FLL1_FRC_INTEG_UPD_SHIFT 15 /* FLL1_FRC_INTEG_UPD */ +#define ARIZONA_FLL1_FRC_INTEG_UPD_WIDTH 1 /* FLL1_FRC_INTEG_UPD */ +#define ARIZONA_FLL1_FRC_INTEG_VAL_MASK 0x0FFF /* FLL1_FRC_INTEG_VAL - [11:0] */ +#define ARIZONA_FLL1_FRC_INTEG_VAL_SHIFT 0 /* FLL1_FRC_INTEG_VAL - [11:0] */ +#define ARIZONA_FLL1_FRC_INTEG_VAL_WIDTH 12 /* FLL1_FRC_INTEG_VAL - [11:0] */ + +/* + * R385 (0x181) - FLL1 Synchroniser 1 + */ +#define ARIZONA_FLL1_SYNC_ENA 0x0001 /* FLL1_SYNC_ENA */ +#define ARIZONA_FLL1_SYNC_ENA_MASK 0x0001 /* FLL1_SYNC_ENA */ +#define ARIZONA_FLL1_SYNC_ENA_SHIFT 0 /* FLL1_SYNC_ENA */ +#define ARIZONA_FLL1_SYNC_ENA_WIDTH 1 /* FLL1_SYNC_ENA */ + +/* + * R386 (0x182) - FLL1 Synchroniser 2 + */ +#define ARIZONA_FLL1_SYNC_N_MASK 0x03FF /* FLL1_SYNC_N - [9:0] */ +#define ARIZONA_FLL1_SYNC_N_SHIFT 0 /* FLL1_SYNC_N - [9:0] */ +#define ARIZONA_FLL1_SYNC_N_WIDTH 10 /* FLL1_SYNC_N - [9:0] */ + +/* + * R387 (0x183) - FLL1 Synchroniser 3 + */ +#define ARIZONA_FLL1_SYNC_THETA_MASK 0xFFFF /* FLL1_SYNC_THETA - [15:0] */ +#define ARIZONA_FLL1_SYNC_THETA_SHIFT 0 /* FLL1_SYNC_THETA - [15:0] */ +#define ARIZONA_FLL1_SYNC_THETA_WIDTH 16 /* FLL1_SYNC_THETA - [15:0] */ + +/* + * R388 (0x184) - FLL1 Synchroniser 4 + */ +#define ARIZONA_FLL1_SYNC_LAMBDA_MASK 0xFFFF /* FLL1_SYNC_LAMBDA - [15:0] */ +#define ARIZONA_FLL1_SYNC_LAMBDA_SHIFT 0 /* FLL1_SYNC_LAMBDA - [15:0] */ +#define ARIZONA_FLL1_SYNC_LAMBDA_WIDTH 16 /* FLL1_SYNC_LAMBDA - [15:0] */ + +/* + * R389 (0x185) - FLL1 Synchroniser 5 + */ +#define ARIZONA_FLL1_SYNC_FRATIO_MASK 0x0700 /* FLL1_SYNC_FRATIO - [10:8] */ +#define ARIZONA_FLL1_SYNC_FRATIO_SHIFT 8 /* FLL1_SYNC_FRATIO - [10:8] */ +#define ARIZONA_FLL1_SYNC_FRATIO_WIDTH 3 /* FLL1_SYNC_FRATIO - [10:8] */ + +/* + * R390 (0x186) - FLL1 Synchroniser 6 + */ +#define ARIZONA_FLL1_CLK_SYNC_DIV_MASK 0x00C0 /* FLL1_CLK_SYNC_DIV - [7:6] */ +#define ARIZONA_FLL1_CLK_SYNC_DIV_SHIFT 6 /* FLL1_CLK_SYNC_DIV - [7:6] */ +#define ARIZONA_FLL1_CLK_SYNC_DIV_WIDTH 2 /* FLL1_CLK_SYNC_DIV - [7:6] */ +#define ARIZONA_FLL1_CLK_SYNC_SRC_MASK 0x000F /* FLL1_CLK_SYNC_SRC - [3:0] */ +#define ARIZONA_FLL1_CLK_SYNC_SRC_SHIFT 0 /* FLL1_CLK_SYNC_SRC - [3:0] */ +#define ARIZONA_FLL1_CLK_SYNC_SRC_WIDTH 4 /* FLL1_CLK_SYNC_SRC - [3:0] */ + +/* + * R393 (0x189) - FLL1 Spread Spectrum + */ +#define ARIZONA_FLL1_SS_AMPL_MASK 0x0030 /* FLL1_SS_AMPL - [5:4] */ +#define ARIZONA_FLL1_SS_AMPL_SHIFT 4 /* FLL1_SS_AMPL - [5:4] */ +#define ARIZONA_FLL1_SS_AMPL_WIDTH 2 /* FLL1_SS_AMPL - [5:4] */ +#define ARIZONA_FLL1_SS_FREQ_MASK 0x000C /* FLL1_SS_FREQ - [3:2] */ +#define ARIZONA_FLL1_SS_FREQ_SHIFT 2 /* FLL1_SS_FREQ - [3:2] */ +#define ARIZONA_FLL1_SS_FREQ_WIDTH 2 /* FLL1_SS_FREQ - [3:2] */ +#define ARIZONA_FLL1_SS_SEL_MASK 0x0003 /* FLL1_SS_SEL - [1:0] */ +#define ARIZONA_FLL1_SS_SEL_SHIFT 0 /* FLL1_SS_SEL - [1:0] */ +#define ARIZONA_FLL1_SS_SEL_WIDTH 2 /* FLL1_SS_SEL - [1:0] */ + +/* + * R394 (0x18A) - FLL1 GPIO Clock + */ +#define ARIZONA_FLL1_GPDIV_MASK 0x00FE /* FLL1_GPDIV - [7:1] */ +#define ARIZONA_FLL1_GPDIV_SHIFT 1 /* FLL1_GPDIV - [7:1] */ +#define ARIZONA_FLL1_GPDIV_WIDTH 7 /* FLL1_GPDIV - [7:1] */ +#define ARIZONA_FLL1_GPDIV_ENA 0x0001 /* FLL1_GPDIV_ENA */ +#define ARIZONA_FLL1_GPDIV_ENA_MASK 0x0001 /* FLL1_GPDIV_ENA */ +#define ARIZONA_FLL1_GPDIV_ENA_SHIFT 0 /* FLL1_GPDIV_ENA */ +#define ARIZONA_FLL1_GPDIV_ENA_WIDTH 1 /* FLL1_GPDIV_ENA */ + +/* + * R401 (0x191) - FLL2 Control 1 + */ +#define ARIZONA_FLL2_FREERUN 0x0002 /* FLL2_FREERUN */ +#define ARIZONA_FLL2_FREERUN_MASK 0x0002 /* FLL2_FREERUN */ +#define ARIZONA_FLL2_FREERUN_SHIFT 1 /* FLL2_FREERUN */ +#define ARIZONA_FLL2_FREERUN_WIDTH 1 /* FLL2_FREERUN */ +#define ARIZONA_FLL2_ENA 0x0001 /* FLL2_ENA */ +#define ARIZONA_FLL2_ENA_MASK 0x0001 /* FLL2_ENA */ +#define ARIZONA_FLL2_ENA_SHIFT 0 /* FLL2_ENA */ +#define ARIZONA_FLL2_ENA_WIDTH 1 /* FLL2_ENA */ + +/* + * R402 (0x192) - FLL2 Control 2 + */ +#define ARIZONA_FLL2_CTRL_UPD 0x8000 /* FLL2_CTRL_UPD */ +#define ARIZONA_FLL2_CTRL_UPD_MASK 0x8000 /* FLL2_CTRL_UPD */ +#define ARIZONA_FLL2_CTRL_UPD_SHIFT 15 /* FLL2_CTRL_UPD */ +#define ARIZONA_FLL2_CTRL_UPD_WIDTH 1 /* FLL2_CTRL_UPD */ +#define ARIZONA_FLL2_N_MASK 0x03FF /* FLL2_N - [9:0] */ +#define ARIZONA_FLL2_N_SHIFT 0 /* FLL2_N - [9:0] */ +#define ARIZONA_FLL2_N_WIDTH 10 /* FLL2_N - [9:0] */ + +/* + * R403 (0x193) - FLL2 Control 3 + */ +#define ARIZONA_FLL2_THETA_MASK 0xFFFF /* FLL2_THETA - [15:0] */ +#define ARIZONA_FLL2_THETA_SHIFT 0 /* FLL2_THETA - [15:0] */ +#define ARIZONA_FLL2_THETA_WIDTH 16 /* FLL2_THETA - [15:0] */ + +/* + * R404 (0x194) - FLL2 Control 4 + */ +#define ARIZONA_FLL2_LAMBDA_MASK 0xFFFF /* FLL2_LAMBDA - [15:0] */ +#define ARIZONA_FLL2_LAMBDA_SHIFT 0 /* FLL2_LAMBDA - [15:0] */ +#define ARIZONA_FLL2_LAMBDA_WIDTH 16 /* FLL2_LAMBDA - [15:0] */ + +/* + * R405 (0x195) - FLL2 Control 5 + */ +#define ARIZONA_FLL2_FRATIO_MASK 0x0700 /* FLL2_FRATIO - [10:8] */ +#define ARIZONA_FLL2_FRATIO_SHIFT 8 /* FLL2_FRATIO - [10:8] */ +#define ARIZONA_FLL2_FRATIO_WIDTH 3 /* FLL2_FRATIO - [10:8] */ +#define ARIZONA_FLL2_OUTDIV_MASK 0x000E /* FLL2_OUTDIV - [3:1] */ +#define ARIZONA_FLL2_OUTDIV_SHIFT 1 /* FLL2_OUTDIV - [3:1] */ +#define ARIZONA_FLL2_OUTDIV_WIDTH 3 /* FLL2_OUTDIV - [3:1] */ + +/* + * R406 (0x196) - FLL2 Control 6 + */ +#define ARIZONA_FLL2_CLK_REF_DIV_MASK 0x00C0 /* FLL2_CLK_REF_DIV - [7:6] */ +#define ARIZONA_FLL2_CLK_REF_DIV_SHIFT 6 /* FLL2_CLK_REF_DIV - [7:6] */ +#define ARIZONA_FLL2_CLK_REF_DIV_WIDTH 2 /* FLL2_CLK_REF_DIV - [7:6] */ +#define ARIZONA_FLL2_CLK_REF_SRC_MASK 0x000F /* FLL2_CLK_REF_SRC - [3:0] */ +#define ARIZONA_FLL2_CLK_REF_SRC_SHIFT 0 /* FLL2_CLK_REF_SRC - [3:0] */ +#define ARIZONA_FLL2_CLK_REF_SRC_WIDTH 4 /* FLL2_CLK_REF_SRC - [3:0] */ + +/* + * R407 (0x197) - FLL2 Loop Filter Test 1 + */ +#define ARIZONA_FLL2_FRC_INTEG_UPD 0x8000 /* FLL2_FRC_INTEG_UPD */ +#define ARIZONA_FLL2_FRC_INTEG_UPD_MASK 0x8000 /* FLL2_FRC_INTEG_UPD */ +#define ARIZONA_FLL2_FRC_INTEG_UPD_SHIFT 15 /* FLL2_FRC_INTEG_UPD */ +#define ARIZONA_FLL2_FRC_INTEG_UPD_WIDTH 1 /* FLL2_FRC_INTEG_UPD */ +#define ARIZONA_FLL2_FRC_INTEG_VAL_MASK 0x0FFF /* FLL2_FRC_INTEG_VAL - [11:0] */ +#define ARIZONA_FLL2_FRC_INTEG_VAL_SHIFT 0 /* FLL2_FRC_INTEG_VAL - [11:0] */ +#define ARIZONA_FLL2_FRC_INTEG_VAL_WIDTH 12 /* FLL2_FRC_INTEG_VAL - [11:0] */ + +/* + * R417 (0x1A1) - FLL2 Synchroniser 1 + */ +#define ARIZONA_FLL2_SYNC_ENA 0x0001 /* FLL2_SYNC_ENA */ +#define ARIZONA_FLL2_SYNC_ENA_MASK 0x0001 /* FLL2_SYNC_ENA */ +#define ARIZONA_FLL2_SYNC_ENA_SHIFT 0 /* FLL2_SYNC_ENA */ +#define ARIZONA_FLL2_SYNC_ENA_WIDTH 1 /* FLL2_SYNC_ENA */ + +/* + * R418 (0x1A2) - FLL2 Synchroniser 2 + */ +#define ARIZONA_FLL2_SYNC_N_MASK 0x03FF /* FLL2_SYNC_N - [9:0] */ +#define ARIZONA_FLL2_SYNC_N_SHIFT 0 /* FLL2_SYNC_N - [9:0] */ +#define ARIZONA_FLL2_SYNC_N_WIDTH 10 /* FLL2_SYNC_N - [9:0] */ + +/* + * R419 (0x1A3) - FLL2 Synchroniser 3 + */ +#define ARIZONA_FLL2_SYNC_THETA_MASK 0xFFFF /* FLL2_SYNC_THETA - [15:0] */ +#define ARIZONA_FLL2_SYNC_THETA_SHIFT 0 /* FLL2_SYNC_THETA - [15:0] */ +#define ARIZONA_FLL2_SYNC_THETA_WIDTH 16 /* FLL2_SYNC_THETA - [15:0] */ + +/* + * R420 (0x1A4) - FLL2 Synchroniser 4 + */ +#define ARIZONA_FLL2_SYNC_LAMBDA_MASK 0xFFFF /* FLL2_SYNC_LAMBDA - [15:0] */ +#define ARIZONA_FLL2_SYNC_LAMBDA_SHIFT 0 /* FLL2_SYNC_LAMBDA - [15:0] */ +#define ARIZONA_FLL2_SYNC_LAMBDA_WIDTH 16 /* FLL2_SYNC_LAMBDA - [15:0] */ + +/* + * R421 (0x1A5) - FLL2 Synchroniser 5 + */ +#define ARIZONA_FLL2_SYNC_FRATIO_MASK 0x0700 /* FLL2_SYNC_FRATIO - [10:8] */ +#define ARIZONA_FLL2_SYNC_FRATIO_SHIFT 8 /* FLL2_SYNC_FRATIO - [10:8] */ +#define ARIZONA_FLL2_SYNC_FRATIO_WIDTH 3 /* FLL2_SYNC_FRATIO - [10:8] */ + +/* + * R422 (0x1A6) - FLL2 Synchroniser 6 + */ +#define ARIZONA_FLL2_CLK_SYNC_DIV_MASK 0x00C0 /* FLL2_CLK_SYNC_DIV - [7:6] */ +#define ARIZONA_FLL2_CLK_SYNC_DIV_SHIFT 6 /* FLL2_CLK_SYNC_DIV - [7:6] */ +#define ARIZONA_FLL2_CLK_SYNC_DIV_WIDTH 2 /* FLL2_CLK_SYNC_DIV - [7:6] */ +#define ARIZONA_FLL2_CLK_SYNC_SRC_MASK 0x000F /* FLL2_CLK_SYNC_SRC - [3:0] */ +#define ARIZONA_FLL2_CLK_SYNC_SRC_SHIFT 0 /* FLL2_CLK_SYNC_SRC - [3:0] */ +#define ARIZONA_FLL2_CLK_SYNC_SRC_WIDTH 4 /* FLL2_CLK_SYNC_SRC - [3:0] */ + +/* + * R425 (0x1A9) - FLL2 Spread Spectrum + */ +#define ARIZONA_FLL2_SS_AMPL_MASK 0x0030 /* FLL2_SS_AMPL - [5:4] */ +#define ARIZONA_FLL2_SS_AMPL_SHIFT 4 /* FLL2_SS_AMPL - [5:4] */ +#define ARIZONA_FLL2_SS_AMPL_WIDTH 2 /* FLL2_SS_AMPL - [5:4] */ +#define ARIZONA_FLL2_SS_FREQ_MASK 0x000C /* FLL2_SS_FREQ - [3:2] */ +#define ARIZONA_FLL2_SS_FREQ_SHIFT 2 /* FLL2_SS_FREQ - [3:2] */ +#define ARIZONA_FLL2_SS_FREQ_WIDTH 2 /* FLL2_SS_FREQ - [3:2] */ +#define ARIZONA_FLL2_SS_SEL_MASK 0x0003 /* FLL2_SS_SEL - [1:0] */ +#define ARIZONA_FLL2_SS_SEL_SHIFT 0 /* FLL2_SS_SEL - [1:0] */ +#define ARIZONA_FLL2_SS_SEL_WIDTH 2 /* FLL2_SS_SEL - [1:0] */ + +/* + * R426 (0x1AA) - FLL2 GPIO Clock + */ +#define ARIZONA_FLL2_GPDIV_MASK 0x00FE /* FLL2_GPDIV - [7:1] */ +#define ARIZONA_FLL2_GPDIV_SHIFT 1 /* FLL2_GPDIV - [7:1] */ +#define ARIZONA_FLL2_GPDIV_WIDTH 7 /* FLL2_GPDIV - [7:1] */ +#define ARIZONA_FLL2_GPDIV_ENA 0x0001 /* FLL2_GPDIV_ENA */ +#define ARIZONA_FLL2_GPDIV_ENA_MASK 0x0001 /* FLL2_GPDIV_ENA */ +#define ARIZONA_FLL2_GPDIV_ENA_SHIFT 0 /* FLL2_GPDIV_ENA */ +#define ARIZONA_FLL2_GPDIV_ENA_WIDTH 1 /* FLL2_GPDIV_ENA */ + +/* + * R512 (0x200) - Mic Charge Pump 1 + */ +#define ARIZONA_CPMIC_DISCH 0x0004 /* CPMIC_DISCH */ +#define ARIZONA_CPMIC_DISCH_MASK 0x0004 /* CPMIC_DISCH */ +#define ARIZONA_CPMIC_DISCH_SHIFT 2 /* CPMIC_DISCH */ +#define ARIZONA_CPMIC_DISCH_WIDTH 1 /* CPMIC_DISCH */ +#define ARIZONA_CPMIC_BYPASS 0x0002 /* CPMIC_BYPASS */ +#define ARIZONA_CPMIC_BYPASS_MASK 0x0002 /* CPMIC_BYPASS */ +#define ARIZONA_CPMIC_BYPASS_SHIFT 1 /* CPMIC_BYPASS */ +#define ARIZONA_CPMIC_BYPASS_WIDTH 1 /* CPMIC_BYPASS */ +#define ARIZONA_CPMIC_ENA 0x0001 /* CPMIC_ENA */ +#define ARIZONA_CPMIC_ENA_MASK 0x0001 /* CPMIC_ENA */ +#define ARIZONA_CPMIC_ENA_SHIFT 0 /* CPMIC_ENA */ +#define ARIZONA_CPMIC_ENA_WIDTH 1 /* CPMIC_ENA */ + +/* + * R528 (0x210) - LDO1 Control 1 + */ +#define ARIZONA_LDO1_VSEL_MASK 0x07E0 /* LDO1_VSEL - [10:5] */ +#define ARIZONA_LDO1_VSEL_SHIFT 5 /* LDO1_VSEL - [10:5] */ +#define ARIZONA_LDO1_VSEL_WIDTH 6 /* LDO1_VSEL - [10:5] */ +#define ARIZONA_LDO1_FAST 0x0010 /* LDO1_FAST */ +#define ARIZONA_LDO1_FAST_MASK 0x0010 /* LDO1_FAST */ +#define ARIZONA_LDO1_FAST_SHIFT 4 /* LDO1_FAST */ +#define ARIZONA_LDO1_FAST_WIDTH 1 /* LDO1_FAST */ +#define ARIZONA_LDO1_DISCH 0x0004 /* LDO1_DISCH */ +#define ARIZONA_LDO1_DISCH_MASK 0x0004 /* LDO1_DISCH */ +#define ARIZONA_LDO1_DISCH_SHIFT 2 /* LDO1_DISCH */ +#define ARIZONA_LDO1_DISCH_WIDTH 1 /* LDO1_DISCH */ +#define ARIZONA_LDO1_BYPASS 0x0002 /* LDO1_BYPASS */ +#define ARIZONA_LDO1_BYPASS_MASK 0x0002 /* LDO1_BYPASS */ +#define ARIZONA_LDO1_BYPASS_SHIFT 1 /* LDO1_BYPASS */ +#define ARIZONA_LDO1_BYPASS_WIDTH 1 /* LDO1_BYPASS */ +#define ARIZONA_LDO1_ENA 0x0001 /* LDO1_ENA */ +#define ARIZONA_LDO1_ENA_MASK 0x0001 /* LDO1_ENA */ +#define ARIZONA_LDO1_ENA_SHIFT 0 /* LDO1_ENA */ +#define ARIZONA_LDO1_ENA_WIDTH 1 /* LDO1_ENA */ + +/* + * R531 (0x213) - LDO2 Control 1 + */ +#define ARIZONA_LDO2_VSEL_MASK 0x07E0 /* LDO2_VSEL - [10:5] */ +#define ARIZONA_LDO2_VSEL_SHIFT 5 /* LDO2_VSEL - [10:5] */ +#define ARIZONA_LDO2_VSEL_WIDTH 6 /* LDO2_VSEL - [10:5] */ +#define ARIZONA_LDO2_FAST 0x0010 /* LDO2_FAST */ +#define ARIZONA_LDO2_FAST_MASK 0x0010 /* LDO2_FAST */ +#define ARIZONA_LDO2_FAST_SHIFT 4 /* LDO2_FAST */ +#define ARIZONA_LDO2_FAST_WIDTH 1 /* LDO2_FAST */ +#define ARIZONA_LDO2_DISCH 0x0004 /* LDO2_DISCH */ +#define ARIZONA_LDO2_DISCH_MASK 0x0004 /* LDO2_DISCH */ +#define ARIZONA_LDO2_DISCH_SHIFT 2 /* LDO2_DISCH */ +#define ARIZONA_LDO2_DISCH_WIDTH 1 /* LDO2_DISCH */ +#define ARIZONA_LDO2_BYPASS 0x0002 /* LDO2_BYPASS */ +#define ARIZONA_LDO2_BYPASS_MASK 0x0002 /* LDO2_BYPASS */ +#define ARIZONA_LDO2_BYPASS_SHIFT 1 /* LDO2_BYPASS */ +#define ARIZONA_LDO2_BYPASS_WIDTH 1 /* LDO2_BYPASS */ +#define ARIZONA_LDO2_ENA 0x0001 /* LDO2_ENA */ +#define ARIZONA_LDO2_ENA_MASK 0x0001 /* LDO2_ENA */ +#define ARIZONA_LDO2_ENA_SHIFT 0 /* LDO2_ENA */ +#define ARIZONA_LDO2_ENA_WIDTH 1 /* LDO2_ENA */ + +/* + * R536 (0x218) - Mic Bias Ctrl 1 + */ +#define ARIZONA_MICB1_EXT_CAP 0x8000 /* MICB1_EXT_CAP */ +#define ARIZONA_MICB1_EXT_CAP_MASK 0x8000 /* MICB1_EXT_CAP */ +#define ARIZONA_MICB1_EXT_CAP_SHIFT 15 /* MICB1_EXT_CAP */ +#define ARIZONA_MICB1_EXT_CAP_WIDTH 1 /* MICB1_EXT_CAP */ +#define ARIZONA_MICB1_LVL_MASK 0x01E0 /* MICB1_LVL - [8:5] */ +#define ARIZONA_MICB1_LVL_SHIFT 5 /* MICB1_LVL - [8:5] */ +#define ARIZONA_MICB1_LVL_WIDTH 4 /* MICB1_LVL - [8:5] */ +#define ARIZONA_MICB1_FAST 0x0010 /* MICB1_FAST */ +#define ARIZONA_MICB1_FAST_MASK 0x0010 /* MICB1_FAST */ +#define ARIZONA_MICB1_FAST_SHIFT 4 /* MICB1_FAST */ +#define ARIZONA_MICB1_FAST_WIDTH 1 /* MICB1_FAST */ +#define ARIZONA_MICB1_RATE 0x0008 /* MICB1_RATE */ +#define ARIZONA_MICB1_RATE_MASK 0x0008 /* MICB1_RATE */ +#define ARIZONA_MICB1_RATE_SHIFT 3 /* MICB1_RATE */ +#define ARIZONA_MICB1_RATE_WIDTH 1 /* MICB1_RATE */ +#define ARIZONA_MICB1_DISCH 0x0004 /* MICB1_DISCH */ +#define ARIZONA_MICB1_DISCH_MASK 0x0004 /* MICB1_DISCH */ +#define ARIZONA_MICB1_DISCH_SHIFT 2 /* MICB1_DISCH */ +#define ARIZONA_MICB1_DISCH_WIDTH 1 /* MICB1_DISCH */ +#define ARIZONA_MICB1_BYPASS 0x0002 /* MICB1_BYPASS */ +#define ARIZONA_MICB1_BYPASS_MASK 0x0002 /* MICB1_BYPASS */ +#define ARIZONA_MICB1_BYPASS_SHIFT 1 /* MICB1_BYPASS */ +#define ARIZONA_MICB1_BYPASS_WIDTH 1 /* MICB1_BYPASS */ +#define ARIZONA_MICB1_ENA 0x0001 /* MICB1_ENA */ +#define ARIZONA_MICB1_ENA_MASK 0x0001 /* MICB1_ENA */ +#define ARIZONA_MICB1_ENA_SHIFT 0 /* MICB1_ENA */ +#define ARIZONA_MICB1_ENA_WIDTH 1 /* MICB1_ENA */ + +/* + * R537 (0x219) - Mic Bias Ctrl 2 + */ +#define ARIZONA_MICB2_EXT_CAP 0x8000 /* MICB2_EXT_CAP */ +#define ARIZONA_MICB2_EXT_CAP_MASK 0x8000 /* MICB2_EXT_CAP */ +#define ARIZONA_MICB2_EXT_CAP_SHIFT 15 /* MICB2_EXT_CAP */ +#define ARIZONA_MICB2_EXT_CAP_WIDTH 1 /* MICB2_EXT_CAP */ +#define ARIZONA_MICB2_LVL_MASK 0x01E0 /* MICB2_LVL - [8:5] */ +#define ARIZONA_MICB2_LVL_SHIFT 5 /* MICB2_LVL - [8:5] */ +#define ARIZONA_MICB2_LVL_WIDTH 4 /* MICB2_LVL - [8:5] */ +#define ARIZONA_MICB2_FAST 0x0010 /* MICB2_FAST */ +#define ARIZONA_MICB2_FAST_MASK 0x0010 /* MICB2_FAST */ +#define ARIZONA_MICB2_FAST_SHIFT 4 /* MICB2_FAST */ +#define ARIZONA_MICB2_FAST_WIDTH 1 /* MICB2_FAST */ +#define ARIZONA_MICB2_RATE 0x0008 /* MICB2_RATE */ +#define ARIZONA_MICB2_RATE_MASK 0x0008 /* MICB2_RATE */ +#define ARIZONA_MICB2_RATE_SHIFT 3 /* MICB2_RATE */ +#define ARIZONA_MICB2_RATE_WIDTH 1 /* MICB2_RATE */ +#define ARIZONA_MICB2_DISCH 0x0004 /* MICB2_DISCH */ +#define ARIZONA_MICB2_DISCH_MASK 0x0004 /* MICB2_DISCH */ +#define ARIZONA_MICB2_DISCH_SHIFT 2 /* MICB2_DISCH */ +#define ARIZONA_MICB2_DISCH_WIDTH 1 /* MICB2_DISCH */ +#define ARIZONA_MICB2_BYPASS 0x0002 /* MICB2_BYPASS */ +#define ARIZONA_MICB2_BYPASS_MASK 0x0002 /* MICB2_BYPASS */ +#define ARIZONA_MICB2_BYPASS_SHIFT 1 /* MICB2_BYPASS */ +#define ARIZONA_MICB2_BYPASS_WIDTH 1 /* MICB2_BYPASS */ +#define ARIZONA_MICB2_ENA 0x0001 /* MICB2_ENA */ +#define ARIZONA_MICB2_ENA_MASK 0x0001 /* MICB2_ENA */ +#define ARIZONA_MICB2_ENA_SHIFT 0 /* MICB2_ENA */ +#define ARIZONA_MICB2_ENA_WIDTH 1 /* MICB2_ENA */ + +/* + * R538 (0x21A) - Mic Bias Ctrl 3 + */ +#define ARIZONA_MICB3_EXT_CAP 0x8000 /* MICB3_EXT_CAP */ +#define ARIZONA_MICB3_EXT_CAP_MASK 0x8000 /* MICB3_EXT_CAP */ +#define ARIZONA_MICB3_EXT_CAP_SHIFT 15 /* MICB3_EXT_CAP */ +#define ARIZONA_MICB3_EXT_CAP_WIDTH 1 /* MICB3_EXT_CAP */ +#define ARIZONA_MICB3_LVL_MASK 0x01E0 /* MICB3_LVL - [8:5] */ +#define ARIZONA_MICB3_LVL_SHIFT 5 /* MICB3_LVL - [8:5] */ +#define ARIZONA_MICB3_LVL_WIDTH 4 /* MICB3_LVL - [8:5] */ +#define ARIZONA_MICB3_FAST 0x0010 /* MICB3_FAST */ +#define ARIZONA_MICB3_FAST_MASK 0x0010 /* MICB3_FAST */ +#define ARIZONA_MICB3_FAST_SHIFT 4 /* MICB3_FAST */ +#define ARIZONA_MICB3_FAST_WIDTH 1 /* MICB3_FAST */ +#define ARIZONA_MICB3_RATE 0x0008 /* MICB3_RATE */ +#define ARIZONA_MICB3_RATE_MASK 0x0008 /* MICB3_RATE */ +#define ARIZONA_MICB3_RATE_SHIFT 3 /* MICB3_RATE */ +#define ARIZONA_MICB3_RATE_WIDTH 1 /* MICB3_RATE */ +#define ARIZONA_MICB3_DISCH 0x0004 /* MICB3_DISCH */ +#define ARIZONA_MICB3_DISCH_MASK 0x0004 /* MICB3_DISCH */ +#define ARIZONA_MICB3_DISCH_SHIFT 2 /* MICB3_DISCH */ +#define ARIZONA_MICB3_DISCH_WIDTH 1 /* MICB3_DISCH */ +#define ARIZONA_MICB3_BYPASS 0x0002 /* MICB3_BYPASS */ +#define ARIZONA_MICB3_BYPASS_MASK 0x0002 /* MICB3_BYPASS */ +#define ARIZONA_MICB3_BYPASS_SHIFT 1 /* MICB3_BYPASS */ +#define ARIZONA_MICB3_BYPASS_WIDTH 1 /* MICB3_BYPASS */ +#define ARIZONA_MICB3_ENA 0x0001 /* MICB3_ENA */ +#define ARIZONA_MICB3_ENA_MASK 0x0001 /* MICB3_ENA */ +#define ARIZONA_MICB3_ENA_SHIFT 0 /* MICB3_ENA */ +#define ARIZONA_MICB3_ENA_WIDTH 1 /* MICB3_ENA */ + +/* + * R659 (0x293) - Accessory Detect Mode 1 + */ +#define ARIZONA_ACCDET_SRC 0x2000 /* ACCDET_SRC */ +#define ARIZONA_ACCDET_SRC_MASK 0x2000 /* ACCDET_SRC */ +#define ARIZONA_ACCDET_SRC_SHIFT 13 /* ACCDET_SRC */ +#define ARIZONA_ACCDET_SRC_WIDTH 1 /* ACCDET_SRC */ +#define ARIZONA_ACCDET_MODE_MASK 0x0003 /* ACCDET_MODE - [1:0] */ +#define ARIZONA_ACCDET_MODE_SHIFT 0 /* ACCDET_MODE - [1:0] */ +#define ARIZONA_ACCDET_MODE_WIDTH 2 /* ACCDET_MODE - [1:0] */ + +/* + * R667 (0x29B) - Headphone Detect 1 + */ +#define ARIZONA_HP_STEP_SIZE 0x0100 /* HP_STEP_SIZE */ +#define ARIZONA_HP_STEP_SIZE_MASK 0x0100 /* HP_STEP_SIZE */ +#define ARIZONA_HP_STEP_SIZE_SHIFT 8 /* HP_STEP_SIZE */ +#define ARIZONA_HP_STEP_SIZE_WIDTH 1 /* HP_STEP_SIZE */ +#define ARIZONA_HP_HOLDTIME_MASK 0x00E0 /* HP_HOLDTIME - [7:5] */ +#define ARIZONA_HP_HOLDTIME_SHIFT 5 /* HP_HOLDTIME - [7:5] */ +#define ARIZONA_HP_HOLDTIME_WIDTH 3 /* HP_HOLDTIME - [7:5] */ +#define ARIZONA_HP_CLK_DIV_MASK 0x0018 /* HP_CLK_DIV - [4:3] */ +#define ARIZONA_HP_CLK_DIV_SHIFT 3 /* HP_CLK_DIV - [4:3] */ +#define ARIZONA_HP_CLK_DIV_WIDTH 2 /* HP_CLK_DIV - [4:3] */ +#define ARIZONA_HP_IDAC_STEER 0x0004 /* HP_IDAC_STEER */ +#define ARIZONA_HP_IDAC_STEER_MASK 0x0004 /* HP_IDAC_STEER */ +#define ARIZONA_HP_IDAC_STEER_SHIFT 2 /* HP_IDAC_STEER */ +#define ARIZONA_HP_IDAC_STEER_WIDTH 1 /* HP_IDAC_STEER */ +#define ARIZONA_HP_RATE 0x0002 /* HP_RATE */ +#define ARIZONA_HP_RATE_MASK 0x0002 /* HP_RATE */ +#define ARIZONA_HP_RATE_SHIFT 1 /* HP_RATE */ +#define ARIZONA_HP_RATE_WIDTH 1 /* HP_RATE */ +#define ARIZONA_HP_POLL 0x0001 /* HP_POLL */ +#define ARIZONA_HP_POLL_MASK 0x0001 /* HP_POLL */ +#define ARIZONA_HP_POLL_SHIFT 0 /* HP_POLL */ +#define ARIZONA_HP_POLL_WIDTH 1 /* HP_POLL */ + +/* + * R668 (0x29C) - Headphone Detect 2 + */ +#define ARIZONA_HP_DONE 0x0080 /* HP_DONE */ +#define ARIZONA_HP_DONE_MASK 0x0080 /* HP_DONE */ +#define ARIZONA_HP_DONE_SHIFT 7 /* HP_DONE */ +#define ARIZONA_HP_DONE_WIDTH 1 /* HP_DONE */ +#define ARIZONA_HP_LVL_MASK 0x007F /* HP_LVL - [6:0] */ +#define ARIZONA_HP_LVL_SHIFT 0 /* HP_LVL - [6:0] */ +#define ARIZONA_HP_LVL_WIDTH 7 /* HP_LVL - [6:0] */ + +/* + * R675 (0x2A3) - Mic Detect 1 + */ +#define ARIZONA_MICD_BIAS_STARTTIME_MASK 0xF000 /* MICD_BIAS_STARTTIME - [15:12] */ +#define ARIZONA_MICD_BIAS_STARTTIME_SHIFT 12 /* MICD_BIAS_STARTTIME - [15:12] */ +#define ARIZONA_MICD_BIAS_STARTTIME_WIDTH 4 /* MICD_BIAS_STARTTIME - [15:12] */ +#define ARIZONA_MICD_RATE_MASK 0x0F00 /* MICD_RATE - [11:8] */ +#define ARIZONA_MICD_RATE_SHIFT 8 /* MICD_RATE - [11:8] */ +#define ARIZONA_MICD_RATE_WIDTH 4 /* MICD_RATE - [11:8] */ +#define ARIZONA_MICD_BIAS_SRC_MASK 0x0030 /* MICD_BIAS_SRC - [5:4] */ +#define ARIZONA_MICD_BIAS_SRC_SHIFT 4 /* MICD_BIAS_SRC - [5:4] */ +#define ARIZONA_MICD_BIAS_SRC_WIDTH 2 /* MICD_BIAS_SRC - [5:4] */ +#define ARIZONA_MICD_DBTIME 0x0002 /* MICD_DBTIME */ +#define ARIZONA_MICD_DBTIME_MASK 0x0002 /* MICD_DBTIME */ +#define ARIZONA_MICD_DBTIME_SHIFT 1 /* MICD_DBTIME */ +#define ARIZONA_MICD_DBTIME_WIDTH 1 /* MICD_DBTIME */ +#define ARIZONA_MICD_ENA 0x0001 /* MICD_ENA */ +#define ARIZONA_MICD_ENA_MASK 0x0001 /* MICD_ENA */ +#define ARIZONA_MICD_ENA_SHIFT 0 /* MICD_ENA */ +#define ARIZONA_MICD_ENA_WIDTH 1 /* MICD_ENA */ + +/* + * R676 (0x2A4) - Mic Detect 2 + */ +#define ARIZONA_MICD_LVL_SEL_MASK 0x00FF /* MICD_LVL_SEL - [7:0] */ +#define ARIZONA_MICD_LVL_SEL_SHIFT 0 /* MICD_LVL_SEL - [7:0] */ +#define ARIZONA_MICD_LVL_SEL_WIDTH 8 /* MICD_LVL_SEL - [7:0] */ + +/* + * R677 (0x2A5) - Mic Detect 3 + */ +#define ARIZONA_MICD_LVL_MASK 0x07FC /* MICD_LVL - [10:2] */ +#define ARIZONA_MICD_LVL_SHIFT 2 /* MICD_LVL - [10:2] */ +#define ARIZONA_MICD_LVL_WIDTH 9 /* MICD_LVL - [10:2] */ +#define ARIZONA_MICD_VALID 0x0002 /* MICD_VALID */ +#define ARIZONA_MICD_VALID_MASK 0x0002 /* MICD_VALID */ +#define ARIZONA_MICD_VALID_SHIFT 1 /* MICD_VALID */ +#define ARIZONA_MICD_VALID_WIDTH 1 /* MICD_VALID */ +#define ARIZONA_MICD_STS 0x0001 /* MICD_STS */ +#define ARIZONA_MICD_STS_MASK 0x0001 /* MICD_STS */ +#define ARIZONA_MICD_STS_SHIFT 0 /* MICD_STS */ +#define ARIZONA_MICD_STS_WIDTH 1 /* MICD_STS */ + +/* + * R707 (0x2C3) - Mic noise mix control 1 + */ +#define ARIZONA_MICMUTE_RATE_MASK 0x7800 /* MICMUTE_RATE - [14:11] */ +#define ARIZONA_MICMUTE_RATE_SHIFT 11 /* MICMUTE_RATE - [14:11] */ +#define ARIZONA_MICMUTE_RATE_WIDTH 4 /* MICMUTE_RATE - [14:11] */ +#define ARIZONA_MICMUTE_MIX_ENA 0x0040 /* MICMUTE_MIX_ENA */ +#define ARIZONA_MICMUTE_MIX_ENA_MASK 0x0040 /* MICMUTE_MIX_ENA */ +#define ARIZONA_MICMUTE_MIX_ENA_SHIFT 6 /* MICMUTE_MIX_ENA */ +#define ARIZONA_MICMUTE_MIX_ENA_WIDTH 1 /* MICMUTE_MIX_ENA */ + +/* + * R715 (0x2CB) - Isolation control + */ +#define ARIZONA_ISOLATE_DCVDD1 0x0001 /* ISOLATE_DCVDD1 */ +#define ARIZONA_ISOLATE_DCVDD1_MASK 0x0001 /* ISOLATE_DCVDD1 */ +#define ARIZONA_ISOLATE_DCVDD1_SHIFT 0 /* ISOLATE_DCVDD1 */ +#define ARIZONA_ISOLATE_DCVDD1_WIDTH 1 /* ISOLATE_DCVDD1 */ + +/* + * R723 (0x2D3) - Jack detect analogue + */ +#define ARIZONA_JD2_ENA 0x0002 /* JD2_ENA */ +#define ARIZONA_JD2_ENA_MASK 0x0002 /* JD2_ENA */ +#define ARIZONA_JD2_ENA_SHIFT 1 /* JD2_ENA */ +#define ARIZONA_JD2_ENA_WIDTH 1 /* JD2_ENA */ +#define ARIZONA_JD1_ENA 0x0001 /* JD1_ENA */ +#define ARIZONA_JD1_ENA_MASK 0x0001 /* JD1_ENA */ +#define ARIZONA_JD1_ENA_SHIFT 0 /* JD1_ENA */ +#define ARIZONA_JD1_ENA_WIDTH 1 /* JD1_ENA */ + +/* + * R768 (0x300) - Input Enables + */ +#define ARIZONA_IN3L_ENA 0x0020 /* IN3L_ENA */ +#define ARIZONA_IN3L_ENA_MASK 0x0020 /* IN3L_ENA */ +#define ARIZONA_IN3L_ENA_SHIFT 5 /* IN3L_ENA */ +#define ARIZONA_IN3L_ENA_WIDTH 1 /* IN3L_ENA */ +#define ARIZONA_IN3R_ENA 0x0010 /* IN3R_ENA */ +#define ARIZONA_IN3R_ENA_MASK 0x0010 /* IN3R_ENA */ +#define ARIZONA_IN3R_ENA_SHIFT 4 /* IN3R_ENA */ +#define ARIZONA_IN3R_ENA_WIDTH 1 /* IN3R_ENA */ +#define ARIZONA_IN2L_ENA 0x0008 /* IN2L_ENA */ +#define ARIZONA_IN2L_ENA_MASK 0x0008 /* IN2L_ENA */ +#define ARIZONA_IN2L_ENA_SHIFT 3 /* IN2L_ENA */ +#define ARIZONA_IN2L_ENA_WIDTH 1 /* IN2L_ENA */ +#define ARIZONA_IN2R_ENA 0x0004 /* IN2R_ENA */ +#define ARIZONA_IN2R_ENA_MASK 0x0004 /* IN2R_ENA */ +#define ARIZONA_IN2R_ENA_SHIFT 2 /* IN2R_ENA */ +#define ARIZONA_IN2R_ENA_WIDTH 1 /* IN2R_ENA */ +#define ARIZONA_IN1L_ENA 0x0002 /* IN1L_ENA */ +#define ARIZONA_IN1L_ENA_MASK 0x0002 /* IN1L_ENA */ +#define ARIZONA_IN1L_ENA_SHIFT 1 /* IN1L_ENA */ +#define ARIZONA_IN1L_ENA_WIDTH 1 /* IN1L_ENA */ +#define ARIZONA_IN1R_ENA 0x0001 /* IN1R_ENA */ +#define ARIZONA_IN1R_ENA_MASK 0x0001 /* IN1R_ENA */ +#define ARIZONA_IN1R_ENA_SHIFT 0 /* IN1R_ENA */ +#define ARIZONA_IN1R_ENA_WIDTH 1 /* IN1R_ENA */ + +/* + * R776 (0x308) - Input Rate + */ +#define ARIZONA_IN_RATE_MASK 0x7800 /* IN_RATE - [14:11] */ +#define ARIZONA_IN_RATE_SHIFT 11 /* IN_RATE - [14:11] */ +#define ARIZONA_IN_RATE_WIDTH 4 /* IN_RATE - [14:11] */ + +/* + * R777 (0x309) - Input Volume Ramp + */ +#define ARIZONA_IN_VD_RAMP_MASK 0x0070 /* IN_VD_RAMP - [6:4] */ +#define ARIZONA_IN_VD_RAMP_SHIFT 4 /* IN_VD_RAMP - [6:4] */ +#define ARIZONA_IN_VD_RAMP_WIDTH 3 /* IN_VD_RAMP - [6:4] */ +#define ARIZONA_IN_VI_RAMP_MASK 0x0007 /* IN_VI_RAMP - [2:0] */ +#define ARIZONA_IN_VI_RAMP_SHIFT 0 /* IN_VI_RAMP - [2:0] */ +#define ARIZONA_IN_VI_RAMP_WIDTH 3 /* IN_VI_RAMP - [2:0] */ + +/* + * R784 (0x310) - IN1L Control + */ +#define ARIZONA_IN1_OSR_MASK 0x6000 /* IN1_OSR - [14:13] */ +#define ARIZONA_IN1_OSR_SHIFT 13 /* IN1_OSR - [14:13] */ +#define ARIZONA_IN1_OSR_WIDTH 2 /* IN1_OSR - [14:13] */ +#define ARIZONA_IN1_DMIC_SUP_MASK 0x1800 /* IN1_DMIC_SUP - [12:11] */ +#define ARIZONA_IN1_DMIC_SUP_SHIFT 11 /* IN1_DMIC_SUP - [12:11] */ +#define ARIZONA_IN1_DMIC_SUP_WIDTH 2 /* IN1_DMIC_SUP - [12:11] */ +#define ARIZONA_IN1_MODE_MASK 0x0600 /* IN1_MODE - [10:9] */ +#define ARIZONA_IN1_MODE_SHIFT 9 /* IN1_MODE - [10:9] */ +#define ARIZONA_IN1_MODE_WIDTH 2 /* IN1_MODE - [10:9] */ +#define ARIZONA_IN1L_PGA_VOL_MASK 0x00FE /* IN1L_PGA_VOL - [7:1] */ +#define ARIZONA_IN1L_PGA_VOL_SHIFT 1 /* IN1L_PGA_VOL - [7:1] */ +#define ARIZONA_IN1L_PGA_VOL_WIDTH 7 /* IN1L_PGA_VOL - [7:1] */ + +/* + * R785 (0x311) - ADC Digital Volume 1L + */ +#define ARIZONA_IN_VU 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_MASK 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_SHIFT 9 /* IN_VU */ +#define ARIZONA_IN_VU_WIDTH 1 /* IN_VU */ +#define ARIZONA_IN1L_MUTE 0x0100 /* IN1L_MUTE */ +#define ARIZONA_IN1L_MUTE_MASK 0x0100 /* IN1L_MUTE */ +#define ARIZONA_IN1L_MUTE_SHIFT 8 /* IN1L_MUTE */ +#define ARIZONA_IN1L_MUTE_WIDTH 1 /* IN1L_MUTE */ +#define ARIZONA_IN1L_DIG_VOL_MASK 0x00FF /* IN1L_DIG_VOL - [7:0] */ +#define ARIZONA_IN1L_DIG_VOL_SHIFT 0 /* IN1L_DIG_VOL - [7:0] */ +#define ARIZONA_IN1L_DIG_VOL_WIDTH 8 /* IN1L_DIG_VOL - [7:0] */ + +/* + * R786 (0x312) - DMIC1L Control + */ +#define ARIZONA_IN1_DMICL_DLY_MASK 0x003F /* IN1_DMICL_DLY - [5:0] */ +#define ARIZONA_IN1_DMICL_DLY_SHIFT 0 /* IN1_DMICL_DLY - [5:0] */ +#define ARIZONA_IN1_DMICL_DLY_WIDTH 6 /* IN1_DMICL_DLY - [5:0] */ + +/* + * R788 (0x314) - IN1R Control + */ +#define ARIZONA_IN1R_PGA_VOL_MASK 0x00FE /* IN1R_PGA_VOL - [7:1] */ +#define ARIZONA_IN1R_PGA_VOL_SHIFT 1 /* IN1R_PGA_VOL - [7:1] */ +#define ARIZONA_IN1R_PGA_VOL_WIDTH 7 /* IN1R_PGA_VOL - [7:1] */ + +/* + * R789 (0x315) - ADC Digital Volume 1R + */ +#define ARIZONA_IN_VU 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_MASK 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_SHIFT 9 /* IN_VU */ +#define ARIZONA_IN_VU_WIDTH 1 /* IN_VU */ +#define ARIZONA_IN1R_MUTE 0x0100 /* IN1R_MUTE */ +#define ARIZONA_IN1R_MUTE_MASK 0x0100 /* IN1R_MUTE */ +#define ARIZONA_IN1R_MUTE_SHIFT 8 /* IN1R_MUTE */ +#define ARIZONA_IN1R_MUTE_WIDTH 1 /* IN1R_MUTE */ +#define ARIZONA_IN1R_DIG_VOL_MASK 0x00FF /* IN1R_DIG_VOL - [7:0] */ +#define ARIZONA_IN1R_DIG_VOL_SHIFT 0 /* IN1R_DIG_VOL - [7:0] */ +#define ARIZONA_IN1R_DIG_VOL_WIDTH 8 /* IN1R_DIG_VOL - [7:0] */ + +/* + * R790 (0x316) - DMIC1R Control + */ +#define ARIZONA_IN1_DMICR_DLY_MASK 0x003F /* IN1_DMICR_DLY - [5:0] */ +#define ARIZONA_IN1_DMICR_DLY_SHIFT 0 /* IN1_DMICR_DLY - [5:0] */ +#define ARIZONA_IN1_DMICR_DLY_WIDTH 6 /* IN1_DMICR_DLY - [5:0] */ + +/* + * R792 (0x318) - IN2L Control + */ +#define ARIZONA_IN2_OSR_MASK 0x6000 /* IN2_OSR - [14:13] */ +#define ARIZONA_IN2_OSR_SHIFT 13 /* IN2_OSR - [14:13] */ +#define ARIZONA_IN2_OSR_WIDTH 2 /* IN2_OSR - [14:13] */ +#define ARIZONA_IN2_DMIC_SUP_MASK 0x1800 /* IN2_DMIC_SUP - [12:11] */ +#define ARIZONA_IN2_DMIC_SUP_SHIFT 11 /* IN2_DMIC_SUP - [12:11] */ +#define ARIZONA_IN2_DMIC_SUP_WIDTH 2 /* IN2_DMIC_SUP - [12:11] */ +#define ARIZONA_IN2_MODE_MASK 0x0600 /* IN2_MODE - [10:9] */ +#define ARIZONA_IN2_MODE_SHIFT 9 /* IN2_MODE - [10:9] */ +#define ARIZONA_IN2_MODE_WIDTH 2 /* IN2_MODE - [10:9] */ +#define ARIZONA_IN2L_PGA_VOL_MASK 0x00FE /* IN2L_PGA_VOL - [7:1] */ +#define ARIZONA_IN2L_PGA_VOL_SHIFT 1 /* IN2L_PGA_VOL - [7:1] */ +#define ARIZONA_IN2L_PGA_VOL_WIDTH 7 /* IN2L_PGA_VOL - [7:1] */ + +/* + * R793 (0x319) - ADC Digital Volume 2L + */ +#define ARIZONA_IN_VU 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_MASK 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_SHIFT 9 /* IN_VU */ +#define ARIZONA_IN_VU_WIDTH 1 /* IN_VU */ +#define ARIZONA_IN2L_MUTE 0x0100 /* IN2L_MUTE */ +#define ARIZONA_IN2L_MUTE_MASK 0x0100 /* IN2L_MUTE */ +#define ARIZONA_IN2L_MUTE_SHIFT 8 /* IN2L_MUTE */ +#define ARIZONA_IN2L_MUTE_WIDTH 1 /* IN2L_MUTE */ +#define ARIZONA_IN2L_DIG_VOL_MASK 0x00FF /* IN2L_DIG_VOL - [7:0] */ +#define ARIZONA_IN2L_DIG_VOL_SHIFT 0 /* IN2L_DIG_VOL - [7:0] */ +#define ARIZONA_IN2L_DIG_VOL_WIDTH 8 /* IN2L_DIG_VOL - [7:0] */ + +/* + * R794 (0x31A) - DMIC2L Control + */ +#define ARIZONA_IN2_DMICL_DLY_MASK 0x003F /* IN2_DMICL_DLY - [5:0] */ +#define ARIZONA_IN2_DMICL_DLY_SHIFT 0 /* IN2_DMICL_DLY - [5:0] */ +#define ARIZONA_IN2_DMICL_DLY_WIDTH 6 /* IN2_DMICL_DLY - [5:0] */ + +/* + * R796 (0x31C) - IN2R Control + */ +#define ARIZONA_IN2R_PGA_VOL_MASK 0x00FE /* IN2R_PGA_VOL - [7:1] */ +#define ARIZONA_IN2R_PGA_VOL_SHIFT 1 /* IN2R_PGA_VOL - [7:1] */ +#define ARIZONA_IN2R_PGA_VOL_WIDTH 7 /* IN2R_PGA_VOL - [7:1] */ + +/* + * R797 (0x31D) - ADC Digital Volume 2R + */ +#define ARIZONA_IN_VU 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_MASK 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_SHIFT 9 /* IN_VU */ +#define ARIZONA_IN_VU_WIDTH 1 /* IN_VU */ +#define ARIZONA_IN2R_MUTE 0x0100 /* IN2R_MUTE */ +#define ARIZONA_IN2R_MUTE_MASK 0x0100 /* IN2R_MUTE */ +#define ARIZONA_IN2R_MUTE_SHIFT 8 /* IN2R_MUTE */ +#define ARIZONA_IN2R_MUTE_WIDTH 1 /* IN2R_MUTE */ +#define ARIZONA_IN2R_DIG_VOL_MASK 0x00FF /* IN2R_DIG_VOL - [7:0] */ +#define ARIZONA_IN2R_DIG_VOL_SHIFT 0 /* IN2R_DIG_VOL - [7:0] */ +#define ARIZONA_IN2R_DIG_VOL_WIDTH 8 /* IN2R_DIG_VOL - [7:0] */ + +/* + * R798 (0x31E) - DMIC2R Control + */ +#define ARIZONA_IN2_DMICR_DLY_MASK 0x003F /* IN2_DMICR_DLY - [5:0] */ +#define ARIZONA_IN2_DMICR_DLY_SHIFT 0 /* IN2_DMICR_DLY - [5:0] */ +#define ARIZONA_IN2_DMICR_DLY_WIDTH 6 /* IN2_DMICR_DLY - [5:0] */ + +/* + * R800 (0x320) - IN3L Control + */ +#define ARIZONA_IN3_OSR_MASK 0x6000 /* IN3_OSR - [14:13] */ +#define ARIZONA_IN3_OSR_SHIFT 13 /* IN3_OSR - [14:13] */ +#define ARIZONA_IN3_OSR_WIDTH 2 /* IN3_OSR - [14:13] */ +#define ARIZONA_IN3_DMIC_SUP_MASK 0x1800 /* IN3_DMIC_SUP - [12:11] */ +#define ARIZONA_IN3_DMIC_SUP_SHIFT 11 /* IN3_DMIC_SUP - [12:11] */ +#define ARIZONA_IN3_DMIC_SUP_WIDTH 2 /* IN3_DMIC_SUP - [12:11] */ +#define ARIZONA_IN3_MODE_MASK 0x0600 /* IN3_MODE - [10:9] */ +#define ARIZONA_IN3_MODE_SHIFT 9 /* IN3_MODE - [10:9] */ +#define ARIZONA_IN3_MODE_WIDTH 2 /* IN3_MODE - [10:9] */ +#define ARIZONA_IN3L_PGA_VOL_MASK 0x00FE /* IN3L_PGA_VOL - [7:1] */ +#define ARIZONA_IN3L_PGA_VOL_SHIFT 1 /* IN3L_PGA_VOL - [7:1] */ +#define ARIZONA_IN3L_PGA_VOL_WIDTH 7 /* IN3L_PGA_VOL - [7:1] */ + +/* + * R801 (0x321) - ADC Digital Volume 3L + */ +#define ARIZONA_IN_VU 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_MASK 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_SHIFT 9 /* IN_VU */ +#define ARIZONA_IN_VU_WIDTH 1 /* IN_VU */ +#define ARIZONA_IN3L_MUTE 0x0100 /* IN3L_MUTE */ +#define ARIZONA_IN3L_MUTE_MASK 0x0100 /* IN3L_MUTE */ +#define ARIZONA_IN3L_MUTE_SHIFT 8 /* IN3L_MUTE */ +#define ARIZONA_IN3L_MUTE_WIDTH 1 /* IN3L_MUTE */ +#define ARIZONA_IN3L_DIG_VOL_MASK 0x00FF /* IN3L_DIG_VOL - [7:0] */ +#define ARIZONA_IN3L_DIG_VOL_SHIFT 0 /* IN3L_DIG_VOL - [7:0] */ +#define ARIZONA_IN3L_DIG_VOL_WIDTH 8 /* IN3L_DIG_VOL - [7:0] */ + +/* + * R802 (0x322) - DMIC3L Control + */ +#define ARIZONA_IN3_DMICL_DLY_MASK 0x003F /* IN3_DMICL_DLY - [5:0] */ +#define ARIZONA_IN3_DMICL_DLY_SHIFT 0 /* IN3_DMICL_DLY - [5:0] */ +#define ARIZONA_IN3_DMICL_DLY_WIDTH 6 /* IN3_DMICL_DLY - [5:0] */ + +/* + * R804 (0x324) - IN3R Control + */ +#define ARIZONA_IN3R_PGA_VOL_MASK 0x00FE /* IN3R_PGA_VOL - [7:1] */ +#define ARIZONA_IN3R_PGA_VOL_SHIFT 1 /* IN3R_PGA_VOL - [7:1] */ +#define ARIZONA_IN3R_PGA_VOL_WIDTH 7 /* IN3R_PGA_VOL - [7:1] */ + +/* + * R805 (0x325) - ADC Digital Volume 3R + */ +#define ARIZONA_IN_VU 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_MASK 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_SHIFT 9 /* IN_VU */ +#define ARIZONA_IN_VU_WIDTH 1 /* IN_VU */ +#define ARIZONA_IN3R_MUTE 0x0100 /* IN3R_MUTE */ +#define ARIZONA_IN3R_MUTE_MASK 0x0100 /* IN3R_MUTE */ +#define ARIZONA_IN3R_MUTE_SHIFT 8 /* IN3R_MUTE */ +#define ARIZONA_IN3R_MUTE_WIDTH 1 /* IN3R_MUTE */ +#define ARIZONA_IN3R_DIG_VOL_MASK 0x00FF /* IN3R_DIG_VOL - [7:0] */ +#define ARIZONA_IN3R_DIG_VOL_SHIFT 0 /* IN3R_DIG_VOL - [7:0] */ +#define ARIZONA_IN3R_DIG_VOL_WIDTH 8 /* IN3R_DIG_VOL - [7:0] */ + +/* + * R806 (0x326) - DMIC3R Control + */ +#define ARIZONA_IN3_DMICR_DLY_MASK 0x003F /* IN3_DMICR_DLY - [5:0] */ +#define ARIZONA_IN3_DMICR_DLY_SHIFT 0 /* IN3_DMICR_DLY - [5:0] */ +#define ARIZONA_IN3_DMICR_DLY_WIDTH 6 /* IN3_DMICR_DLY - [5:0] */ + +/* + * R1024 (0x400) - Output Enables 1 + */ +#define ARIZONA_OUT5L_ENA 0x0200 /* OUT5L_ENA */ +#define ARIZONA_OUT5L_ENA_MASK 0x0200 /* OUT5L_ENA */ +#define ARIZONA_OUT5L_ENA_SHIFT 9 /* OUT5L_ENA */ +#define ARIZONA_OUT5L_ENA_WIDTH 1 /* OUT5L_ENA */ +#define ARIZONA_OUT5R_ENA 0x0100 /* OUT5R_ENA */ +#define ARIZONA_OUT5R_ENA_MASK 0x0100 /* OUT5R_ENA */ +#define ARIZONA_OUT5R_ENA_SHIFT 8 /* OUT5R_ENA */ +#define ARIZONA_OUT5R_ENA_WIDTH 1 /* OUT5R_ENA */ +#define ARIZONA_OUT4L_ENA 0x0080 /* OUT4L_ENA */ +#define ARIZONA_OUT4L_ENA_MASK 0x0080 /* OUT4L_ENA */ +#define ARIZONA_OUT4L_ENA_SHIFT 7 /* OUT4L_ENA */ +#define ARIZONA_OUT4L_ENA_WIDTH 1 /* OUT4L_ENA */ +#define ARIZONA_OUT4R_ENA 0x0040 /* OUT4R_ENA */ +#define ARIZONA_OUT4R_ENA_MASK 0x0040 /* OUT4R_ENA */ +#define ARIZONA_OUT4R_ENA_SHIFT 6 /* OUT4R_ENA */ +#define ARIZONA_OUT4R_ENA_WIDTH 1 /* OUT4R_ENA */ +#define ARIZONA_OUT3L_ENA 0x0020 /* OUT3L_ENA */ +#define ARIZONA_OUT3L_ENA_MASK 0x0020 /* OUT3L_ENA */ +#define ARIZONA_OUT3L_ENA_SHIFT 5 /* OUT3L_ENA */ +#define ARIZONA_OUT3L_ENA_WIDTH 1 /* OUT3L_ENA */ +#define ARIZONA_OUT3R_ENA 0x0010 /* OUT3R_ENA */ +#define ARIZONA_OUT3R_ENA_MASK 0x0010 /* OUT3R_ENA */ +#define ARIZONA_OUT3R_ENA_SHIFT 4 /* OUT3R_ENA */ +#define ARIZONA_OUT3R_ENA_WIDTH 1 /* OUT3R_ENA */ +#define ARIZONA_OUT2L_ENA 0x0008 /* OUT2L_ENA */ +#define ARIZONA_OUT2L_ENA_MASK 0x0008 /* OUT2L_ENA */ +#define ARIZONA_OUT2L_ENA_SHIFT 3 /* OUT2L_ENA */ +#define ARIZONA_OUT2L_ENA_WIDTH 1 /* OUT2L_ENA */ +#define ARIZONA_OUT2R_ENA 0x0004 /* OUT2R_ENA */ +#define ARIZONA_OUT2R_ENA_MASK 0x0004 /* OUT2R_ENA */ +#define ARIZONA_OUT2R_ENA_SHIFT 2 /* OUT2R_ENA */ +#define ARIZONA_OUT2R_ENA_WIDTH 1 /* OUT2R_ENA */ +#define ARIZONA_OUT1L_ENA 0x0002 /* OUT1L_ENA */ +#define ARIZONA_OUT1L_ENA_MASK 0x0002 /* OUT1L_ENA */ +#define ARIZONA_OUT1L_ENA_SHIFT 1 /* OUT1L_ENA */ +#define ARIZONA_OUT1L_ENA_WIDTH 1 /* OUT1L_ENA */ +#define ARIZONA_OUT1R_ENA 0x0001 /* OUT1R_ENA */ +#define ARIZONA_OUT1R_ENA_MASK 0x0001 /* OUT1R_ENA */ +#define ARIZONA_OUT1R_ENA_SHIFT 0 /* OUT1R_ENA */ +#define ARIZONA_OUT1R_ENA_WIDTH 1 /* OUT1R_ENA */ + +/* + * R1025 (0x401) - Output Status 1 + */ +#define ARIZONA_OUT6L_ENA_STS 0x0800 /* OUT6L_ENA_STS */ +#define ARIZONA_OUT6L_ENA_STS_MASK 0x0800 /* OUT6L_ENA_STS */ +#define ARIZONA_OUT6L_ENA_STS_SHIFT 11 /* OUT6L_ENA_STS */ +#define ARIZONA_OUT6L_ENA_STS_WIDTH 1 /* OUT6L_ENA_STS */ +#define ARIZONA_OUT6R_ENA_STS 0x0400 /* OUT6R_ENA_STS */ +#define ARIZONA_OUT6R_ENA_STS_MASK 0x0400 /* OUT6R_ENA_STS */ +#define ARIZONA_OUT6R_ENA_STS_SHIFT 10 /* OUT6R_ENA_STS */ +#define ARIZONA_OUT6R_ENA_STS_WIDTH 1 /* OUT6R_ENA_STS */ +#define ARIZONA_OUT5L_ENA_STS 0x0200 /* OUT5L_ENA_STS */ +#define ARIZONA_OUT5L_ENA_STS_MASK 0x0200 /* OUT5L_ENA_STS */ +#define ARIZONA_OUT5L_ENA_STS_SHIFT 9 /* OUT5L_ENA_STS */ +#define ARIZONA_OUT5L_ENA_STS_WIDTH 1 /* OUT5L_ENA_STS */ +#define ARIZONA_OUT5R_ENA_STS 0x0100 /* OUT5R_ENA_STS */ +#define ARIZONA_OUT5R_ENA_STS_MASK 0x0100 /* OUT5R_ENA_STS */ +#define ARIZONA_OUT5R_ENA_STS_SHIFT 8 /* OUT5R_ENA_STS */ +#define ARIZONA_OUT5R_ENA_STS_WIDTH 1 /* OUT5R_ENA_STS */ +#define ARIZONA_OUT4L_ENA_STS 0x0080 /* OUT4L_ENA_STS */ +#define ARIZONA_OUT4L_ENA_STS_MASK 0x0080 /* OUT4L_ENA_STS */ +#define ARIZONA_OUT4L_ENA_STS_SHIFT 7 /* OUT4L_ENA_STS */ +#define ARIZONA_OUT4L_ENA_STS_WIDTH 1 /* OUT4L_ENA_STS */ +#define ARIZONA_OUT4R_ENA_STS 0x0040 /* OUT4R_ENA_STS */ +#define ARIZONA_OUT4R_ENA_STS_MASK 0x0040 /* OUT4R_ENA_STS */ +#define ARIZONA_OUT4R_ENA_STS_SHIFT 6 /* OUT4R_ENA_STS */ +#define ARIZONA_OUT4R_ENA_STS_WIDTH 1 /* OUT4R_ENA_STS */ + +/* + * R1032 (0x408) - Output Rate 1 + */ +#define ARIZONA_OUT_RATE_MASK 0x7800 /* OUT_RATE - [14:11] */ +#define ARIZONA_OUT_RATE_SHIFT 11 /* OUT_RATE - [14:11] */ +#define ARIZONA_OUT_RATE_WIDTH 4 /* OUT_RATE - [14:11] */ + +/* + * R1033 (0x409) - Output Volume Ramp + */ +#define ARIZONA_OUT_VD_RAMP_MASK 0x0070 /* OUT_VD_RAMP - [6:4] */ +#define ARIZONA_OUT_VD_RAMP_SHIFT 4 /* OUT_VD_RAMP - [6:4] */ +#define ARIZONA_OUT_VD_RAMP_WIDTH 3 /* OUT_VD_RAMP - [6:4] */ +#define ARIZONA_OUT_VI_RAMP_MASK 0x0007 /* OUT_VI_RAMP - [2:0] */ +#define ARIZONA_OUT_VI_RAMP_SHIFT 0 /* OUT_VI_RAMP - [2:0] */ +#define ARIZONA_OUT_VI_RAMP_WIDTH 3 /* OUT_VI_RAMP - [2:0] */ + +/* + * R1040 (0x410) - Output Path Config 1L + */ +#define ARIZONA_OUT1_LP_MODE 0x8000 /* OUT1_LP_MODE */ +#define ARIZONA_OUT1_LP_MODE_MASK 0x8000 /* OUT1_LP_MODE */ +#define ARIZONA_OUT1_LP_MODE_SHIFT 15 /* OUT1_LP_MODE */ +#define ARIZONA_OUT1_LP_MODE_WIDTH 1 /* OUT1_LP_MODE */ +#define ARIZONA_OUT1_OSR 0x2000 /* OUT1_OSR */ +#define ARIZONA_OUT1_OSR_MASK 0x2000 /* OUT1_OSR */ +#define ARIZONA_OUT1_OSR_SHIFT 13 /* OUT1_OSR */ +#define ARIZONA_OUT1_OSR_WIDTH 1 /* OUT1_OSR */ +#define ARIZONA_OUT1_MONO 0x1000 /* OUT1_MONO */ +#define ARIZONA_OUT1_MONO_MASK 0x1000 /* OUT1_MONO */ +#define ARIZONA_OUT1_MONO_SHIFT 12 /* OUT1_MONO */ +#define ARIZONA_OUT1_MONO_WIDTH 1 /* OUT1_MONO */ +#define ARIZONA_OUT1L_ANC_SRC_MASK 0x0C00 /* OUT1L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT1L_ANC_SRC_SHIFT 10 /* OUT1L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT1L_ANC_SRC_WIDTH 2 /* OUT1L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT1L_PGA_VOL_MASK 0x00FE /* OUT1L_PGA_VOL - [7:1] */ +#define ARIZONA_OUT1L_PGA_VOL_SHIFT 1 /* OUT1L_PGA_VOL - [7:1] */ +#define ARIZONA_OUT1L_PGA_VOL_WIDTH 7 /* OUT1L_PGA_VOL - [7:1] */ + +/* + * R1041 (0x411) - DAC Digital Volume 1L + */ +#define ARIZONA_OUT_VU 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_SHIFT 9 /* OUT_VU */ +#define ARIZONA_OUT_VU_WIDTH 1 /* OUT_VU */ +#define ARIZONA_OUT1L_MUTE 0x0100 /* OUT1L_MUTE */ +#define ARIZONA_OUT1L_MUTE_MASK 0x0100 /* OUT1L_MUTE */ +#define ARIZONA_OUT1L_MUTE_SHIFT 8 /* OUT1L_MUTE */ +#define ARIZONA_OUT1L_MUTE_WIDTH 1 /* OUT1L_MUTE */ +#define ARIZONA_OUT1L_VOL_MASK 0x00FF /* OUT1L_VOL - [7:0] */ +#define ARIZONA_OUT1L_VOL_SHIFT 0 /* OUT1L_VOL - [7:0] */ +#define ARIZONA_OUT1L_VOL_WIDTH 8 /* OUT1L_VOL - [7:0] */ + +/* + * R1042 (0x412) - DAC Volume Limit 1L + */ +#define ARIZONA_OUT1L_VOL_LIM_MASK 0x00FF /* OUT1L_VOL_LIM - [7:0] */ +#define ARIZONA_OUT1L_VOL_LIM_SHIFT 0 /* OUT1L_VOL_LIM - [7:0] */ +#define ARIZONA_OUT1L_VOL_LIM_WIDTH 8 /* OUT1L_VOL_LIM - [7:0] */ + +/* + * R1043 (0x413) - Noise Gate Select 1L + */ +#define ARIZONA_OUT1L_NGATE_SRC_MASK 0x0FFF /* OUT1L_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT1L_NGATE_SRC_SHIFT 0 /* OUT1L_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT1L_NGATE_SRC_WIDTH 12 /* OUT1L_NGATE_SRC - [11:0] */ + +/* + * R1044 (0x414) - Output Path Config 1R + */ +#define ARIZONA_OUT1R_ANC_SRC_MASK 0x0C00 /* OUT1R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT1R_ANC_SRC_SHIFT 10 /* OUT1R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT1R_ANC_SRC_WIDTH 2 /* OUT1R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT1R_PGA_VOL_MASK 0x00FE /* OUT1R_PGA_VOL - [7:1] */ +#define ARIZONA_OUT1R_PGA_VOL_SHIFT 1 /* OUT1R_PGA_VOL - [7:1] */ +#define ARIZONA_OUT1R_PGA_VOL_WIDTH 7 /* OUT1R_PGA_VOL - [7:1] */ + +/* + * R1045 (0x415) - DAC Digital Volume 1R + */ +#define ARIZONA_OUT_VU 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_SHIFT 9 /* OUT_VU */ +#define ARIZONA_OUT_VU_WIDTH 1 /* OUT_VU */ +#define ARIZONA_OUT1R_MUTE 0x0100 /* OUT1R_MUTE */ +#define ARIZONA_OUT1R_MUTE_MASK 0x0100 /* OUT1R_MUTE */ +#define ARIZONA_OUT1R_MUTE_SHIFT 8 /* OUT1R_MUTE */ +#define ARIZONA_OUT1R_MUTE_WIDTH 1 /* OUT1R_MUTE */ +#define ARIZONA_OUT1R_VOL_MASK 0x00FF /* OUT1R_VOL - [7:0] */ +#define ARIZONA_OUT1R_VOL_SHIFT 0 /* OUT1R_VOL - [7:0] */ +#define ARIZONA_OUT1R_VOL_WIDTH 8 /* OUT1R_VOL - [7:0] */ + +/* + * R1046 (0x416) - DAC Volume Limit 1R + */ +#define ARIZONA_OUT1R_VOL_LIM_MASK 0x00FF /* OUT1R_VOL_LIM - [7:0] */ +#define ARIZONA_OUT1R_VOL_LIM_SHIFT 0 /* OUT1R_VOL_LIM - [7:0] */ +#define ARIZONA_OUT1R_VOL_LIM_WIDTH 8 /* OUT1R_VOL_LIM - [7:0] */ + +/* + * R1047 (0x417) - Noise Gate Select 1R + */ +#define ARIZONA_OUT1R_NGATE_SRC_MASK 0x0FFF /* OUT1R_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT1R_NGATE_SRC_SHIFT 0 /* OUT1R_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT1R_NGATE_SRC_WIDTH 12 /* OUT1R_NGATE_SRC - [11:0] */ + +/* + * R1048 (0x418) - Output Path Config 2L + */ +#define ARIZONA_OUT2_LP_MODE 0x8000 /* OUT2_LP_MODE */ +#define ARIZONA_OUT2_LP_MODE_MASK 0x8000 /* OUT2_LP_MODE */ +#define ARIZONA_OUT2_LP_MODE_SHIFT 15 /* OUT2_LP_MODE */ +#define ARIZONA_OUT2_LP_MODE_WIDTH 1 /* OUT2_LP_MODE */ +#define ARIZONA_OUT2_OSR 0x2000 /* OUT2_OSR */ +#define ARIZONA_OUT2_OSR_MASK 0x2000 /* OUT2_OSR */ +#define ARIZONA_OUT2_OSR_SHIFT 13 /* OUT2_OSR */ +#define ARIZONA_OUT2_OSR_WIDTH 1 /* OUT2_OSR */ +#define ARIZONA_OUT2_MONO 0x1000 /* OUT2_MONO */ +#define ARIZONA_OUT2_MONO_MASK 0x1000 /* OUT2_MONO */ +#define ARIZONA_OUT2_MONO_SHIFT 12 /* OUT2_MONO */ +#define ARIZONA_OUT2_MONO_WIDTH 1 /* OUT2_MONO */ +#define ARIZONA_OUT2L_ANC_SRC_MASK 0x0C00 /* OUT2L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT2L_ANC_SRC_SHIFT 10 /* OUT2L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT2L_ANC_SRC_WIDTH 2 /* OUT2L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT2L_PGA_VOL_MASK 0x00FE /* OUT2L_PGA_VOL - [7:1] */ +#define ARIZONA_OUT2L_PGA_VOL_SHIFT 1 /* OUT2L_PGA_VOL - [7:1] */ +#define ARIZONA_OUT2L_PGA_VOL_WIDTH 7 /* OUT2L_PGA_VOL - [7:1] */ + +/* + * R1049 (0x419) - DAC Digital Volume 2L + */ +#define ARIZONA_OUT_VU 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_SHIFT 9 /* OUT_VU */ +#define ARIZONA_OUT_VU_WIDTH 1 /* OUT_VU */ +#define ARIZONA_OUT2L_MUTE 0x0100 /* OUT2L_MUTE */ +#define ARIZONA_OUT2L_MUTE_MASK 0x0100 /* OUT2L_MUTE */ +#define ARIZONA_OUT2L_MUTE_SHIFT 8 /* OUT2L_MUTE */ +#define ARIZONA_OUT2L_MUTE_WIDTH 1 /* OUT2L_MUTE */ +#define ARIZONA_OUT2L_VOL_MASK 0x00FF /* OUT2L_VOL - [7:0] */ +#define ARIZONA_OUT2L_VOL_SHIFT 0 /* OUT2L_VOL - [7:0] */ +#define ARIZONA_OUT2L_VOL_WIDTH 8 /* OUT2L_VOL - [7:0] */ + +/* + * R1050 (0x41A) - DAC Volume Limit 2L + */ +#define ARIZONA_OUT2L_VOL_LIM_MASK 0x00FF /* OUT2L_VOL_LIM - [7:0] */ +#define ARIZONA_OUT2L_VOL_LIM_SHIFT 0 /* OUT2L_VOL_LIM - [7:0] */ +#define ARIZONA_OUT2L_VOL_LIM_WIDTH 8 /* OUT2L_VOL_LIM - [7:0] */ + +/* + * R1051 (0x41B) - Noise Gate Select 2L + */ +#define ARIZONA_OUT2L_NGATE_SRC_MASK 0x0FFF /* OUT2L_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT2L_NGATE_SRC_SHIFT 0 /* OUT2L_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT2L_NGATE_SRC_WIDTH 12 /* OUT2L_NGATE_SRC - [11:0] */ + +/* + * R1052 (0x41C) - Output Path Config 2R + */ +#define ARIZONA_OUT2R_ANC_SRC_MASK 0x0C00 /* OUT2R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT2R_ANC_SRC_SHIFT 10 /* OUT2R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT2R_ANC_SRC_WIDTH 2 /* OUT2R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT2R_PGA_VOL_MASK 0x00FE /* OUT2R_PGA_VOL - [7:1] */ +#define ARIZONA_OUT2R_PGA_VOL_SHIFT 1 /* OUT2R_PGA_VOL - [7:1] */ +#define ARIZONA_OUT2R_PGA_VOL_WIDTH 7 /* OUT2R_PGA_VOL - [7:1] */ + +/* + * R1053 (0x41D) - DAC Digital Volume 2R + */ +#define ARIZONA_OUT_VU 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_SHIFT 9 /* OUT_VU */ +#define ARIZONA_OUT_VU_WIDTH 1 /* OUT_VU */ +#define ARIZONA_OUT2R_MUTE 0x0100 /* OUT2R_MUTE */ +#define ARIZONA_OUT2R_MUTE_MASK 0x0100 /* OUT2R_MUTE */ +#define ARIZONA_OUT2R_MUTE_SHIFT 8 /* OUT2R_MUTE */ +#define ARIZONA_OUT2R_MUTE_WIDTH 1 /* OUT2R_MUTE */ +#define ARIZONA_OUT2R_VOL_MASK 0x00FF /* OUT2R_VOL - [7:0] */ +#define ARIZONA_OUT2R_VOL_SHIFT 0 /* OUT2R_VOL - [7:0] */ +#define ARIZONA_OUT2R_VOL_WIDTH 8 /* OUT2R_VOL - [7:0] */ + +/* + * R1054 (0x41E) - DAC Volume Limit 2R + */ +#define ARIZONA_OUT2R_VOL_LIM_MASK 0x00FF /* OUT2R_VOL_LIM - [7:0] */ +#define ARIZONA_OUT2R_VOL_LIM_SHIFT 0 /* OUT2R_VOL_LIM - [7:0] */ +#define ARIZONA_OUT2R_VOL_LIM_WIDTH 8 /* OUT2R_VOL_LIM - [7:0] */ + +/* + * R1055 (0x41F) - Noise Gate Select 2R + */ +#define ARIZONA_OUT2R_NGATE_SRC_MASK 0x0FFF /* OUT2R_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT2R_NGATE_SRC_SHIFT 0 /* OUT2R_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT2R_NGATE_SRC_WIDTH 12 /* OUT2R_NGATE_SRC - [11:0] */ + +/* + * R1056 (0x420) - Output Path Config 3L + */ +#define ARIZONA_OUT3_LP_MODE 0x8000 /* OUT3_LP_MODE */ +#define ARIZONA_OUT3_LP_MODE_MASK 0x8000 /* OUT3_LP_MODE */ +#define ARIZONA_OUT3_LP_MODE_SHIFT 15 /* OUT3_LP_MODE */ +#define ARIZONA_OUT3_LP_MODE_WIDTH 1 /* OUT3_LP_MODE */ +#define ARIZONA_OUT3_OSR 0x2000 /* OUT3_OSR */ +#define ARIZONA_OUT3_OSR_MASK 0x2000 /* OUT3_OSR */ +#define ARIZONA_OUT3_OSR_SHIFT 13 /* OUT3_OSR */ +#define ARIZONA_OUT3_OSR_WIDTH 1 /* OUT3_OSR */ +#define ARIZONA_OUT3_MONO 0x1000 /* OUT3_MONO */ +#define ARIZONA_OUT3_MONO_MASK 0x1000 /* OUT3_MONO */ +#define ARIZONA_OUT3_MONO_SHIFT 12 /* OUT3_MONO */ +#define ARIZONA_OUT3_MONO_WIDTH 1 /* OUT3_MONO */ +#define ARIZONA_OUT3L_ANC_SRC_MASK 0x0C00 /* OUT3L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT3L_ANC_SRC_SHIFT 10 /* OUT3L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT3L_ANC_SRC_WIDTH 2 /* OUT3L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT3L_PGA_VOL_MASK 0x00FE /* OUT3L_PGA_VOL - [7:1] */ +#define ARIZONA_OUT3L_PGA_VOL_SHIFT 1 /* OUT3L_PGA_VOL - [7:1] */ +#define ARIZONA_OUT3L_PGA_VOL_WIDTH 7 /* OUT3L_PGA_VOL - [7:1] */ + +/* + * R1057 (0x421) - DAC Digital Volume 3L + */ +#define ARIZONA_OUT_VU 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_SHIFT 9 /* OUT_VU */ +#define ARIZONA_OUT_VU_WIDTH 1 /* OUT_VU */ +#define ARIZONA_OUT3L_MUTE 0x0100 /* OUT3L_MUTE */ +#define ARIZONA_OUT3L_MUTE_MASK 0x0100 /* OUT3L_MUTE */ +#define ARIZONA_OUT3L_MUTE_SHIFT 8 /* OUT3L_MUTE */ +#define ARIZONA_OUT3L_MUTE_WIDTH 1 /* OUT3L_MUTE */ +#define ARIZONA_OUT3L_VOL_MASK 0x00FF /* OUT3L_VOL - [7:0] */ +#define ARIZONA_OUT3L_VOL_SHIFT 0 /* OUT3L_VOL - [7:0] */ +#define ARIZONA_OUT3L_VOL_WIDTH 8 /* OUT3L_VOL - [7:0] */ + +/* + * R1058 (0x422) - DAC Volume Limit 3L + */ +#define ARIZONA_OUT3L_VOL_LIM_MASK 0x00FF /* OUT3L_VOL_LIM - [7:0] */ +#define ARIZONA_OUT3L_VOL_LIM_SHIFT 0 /* OUT3L_VOL_LIM - [7:0] */ +#define ARIZONA_OUT3L_VOL_LIM_WIDTH 8 /* OUT3L_VOL_LIM - [7:0] */ + +/* + * R1059 (0x423) - Noise Gate Select 3L + */ +#define ARIZONA_OUT3_NGATE_SRC_MASK 0x0FFF /* OUT3_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT3_NGATE_SRC_SHIFT 0 /* OUT3_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT3_NGATE_SRC_WIDTH 12 /* OUT3_NGATE_SRC - [11:0] */ + +/* + * R1060 (0x424) - Output Path Config 3R + */ +#define ARIZONA_OUT3R_PGA_VOL_MASK 0x00FE /* OUT3R_PGA_VOL - [7:1] */ +#define ARIZONA_OUT3R_PGA_VOL_SHIFT 1 /* OUT3R_PGA_VOL - [7:1] */ +#define ARIZONA_OUT3R_PGA_VOL_WIDTH 7 /* OUT3R_PGA_VOL - [7:1] */ + +/* + * R1061 (0x425) - DAC Digital Volume 3R + */ +#define ARIZONA_OUT_VU 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_SHIFT 9 /* OUT_VU */ +#define ARIZONA_OUT_VU_WIDTH 1 /* OUT_VU */ +#define ARIZONA_OUT3R_MUTE 0x0100 /* OUT3R_MUTE */ +#define ARIZONA_OUT3R_MUTE_MASK 0x0100 /* OUT3R_MUTE */ +#define ARIZONA_OUT3R_MUTE_SHIFT 8 /* OUT3R_MUTE */ +#define ARIZONA_OUT3R_MUTE_WIDTH 1 /* OUT3R_MUTE */ +#define ARIZONA_OUT3R_VOL_MASK 0x00FF /* OUT3R_VOL - [7:0] */ +#define ARIZONA_OUT3R_VOL_SHIFT 0 /* OUT3R_VOL - [7:0] */ +#define ARIZONA_OUT3R_VOL_WIDTH 8 /* OUT3R_VOL - [7:0] */ + +/* + * R1062 (0x426) - DAC Volume Limit 3R + */ +#define ARIZONA_OUT3R_ANC_SRC_MASK 0x0C00 /* OUT3R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT3R_ANC_SRC_SHIFT 10 /* OUT3R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT3R_ANC_SRC_WIDTH 2 /* OUT3R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT3R_VOL_LIM_MASK 0x00FF /* OUT3R_VOL_LIM - [7:0] */ +#define ARIZONA_OUT3R_VOL_LIM_SHIFT 0 /* OUT3R_VOL_LIM - [7:0] */ +#define ARIZONA_OUT3R_VOL_LIM_WIDTH 8 /* OUT3R_VOL_LIM - [7:0] */ + +/* + * R1064 (0x428) - Output Path Config 4L + */ +#define ARIZONA_OUT4_OSR 0x2000 /* OUT4_OSR */ +#define ARIZONA_OUT4_OSR_MASK 0x2000 /* OUT4_OSR */ +#define ARIZONA_OUT4_OSR_SHIFT 13 /* OUT4_OSR */ +#define ARIZONA_OUT4_OSR_WIDTH 1 /* OUT4_OSR */ +#define ARIZONA_OUT4L_ANC_SRC_MASK 0x0C00 /* OUT4L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT4L_ANC_SRC_SHIFT 10 /* OUT4L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT4L_ANC_SRC_WIDTH 2 /* OUT4L_ANC_SRC - [11:10] */ + +/* + * R1065 (0x429) - DAC Digital Volume 4L + */ +#define ARIZONA_OUT_VU 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_SHIFT 9 /* OUT_VU */ +#define ARIZONA_OUT_VU_WIDTH 1 /* OUT_VU */ +#define ARIZONA_OUT4L_MUTE 0x0100 /* OUT4L_MUTE */ +#define ARIZONA_OUT4L_MUTE_MASK 0x0100 /* OUT4L_MUTE */ +#define ARIZONA_OUT4L_MUTE_SHIFT 8 /* OUT4L_MUTE */ +#define ARIZONA_OUT4L_MUTE_WIDTH 1 /* OUT4L_MUTE */ +#define ARIZONA_OUT4L_VOL_MASK 0x00FF /* OUT4L_VOL - [7:0] */ +#define ARIZONA_OUT4L_VOL_SHIFT 0 /* OUT4L_VOL - [7:0] */ +#define ARIZONA_OUT4L_VOL_WIDTH 8 /* OUT4L_VOL - [7:0] */ + +/* + * R1066 (0x42A) - Out Volume 4L + */ +#define ARIZONA_OUT4L_VOL_LIM_MASK 0x00FF /* OUT4L_VOL_LIM - [7:0] */ +#define ARIZONA_OUT4L_VOL_LIM_SHIFT 0 /* OUT4L_VOL_LIM - [7:0] */ +#define ARIZONA_OUT4L_VOL_LIM_WIDTH 8 /* OUT4L_VOL_LIM - [7:0] */ + +/* + * R1067 (0x42B) - Noise Gate Select 4L + */ +#define ARIZONA_OUT4L_NGATE_SRC_MASK 0x0FFF /* OUT4L_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT4L_NGATE_SRC_SHIFT 0 /* OUT4L_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT4L_NGATE_SRC_WIDTH 12 /* OUT4L_NGATE_SRC - [11:0] */ + +/* + * R1068 (0x42C) - Output Path Config 4R + */ +#define ARIZONA_OUT4R_ANC_SRC_MASK 0x0C00 /* OUT4R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT4R_ANC_SRC_SHIFT 10 /* OUT4R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT4R_ANC_SRC_WIDTH 2 /* OUT4R_ANC_SRC - [11:10] */ + +/* + * R1069 (0x42D) - DAC Digital Volume 4R + */ +#define ARIZONA_OUT_VU 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_SHIFT 9 /* OUT_VU */ +#define ARIZONA_OUT_VU_WIDTH 1 /* OUT_VU */ +#define ARIZONA_OUT4R_MUTE 0x0100 /* OUT4R_MUTE */ +#define ARIZONA_OUT4R_MUTE_MASK 0x0100 /* OUT4R_MUTE */ +#define ARIZONA_OUT4R_MUTE_SHIFT 8 /* OUT4R_MUTE */ +#define ARIZONA_OUT4R_MUTE_WIDTH 1 /* OUT4R_MUTE */ +#define ARIZONA_OUT4R_VOL_MASK 0x00FF /* OUT4R_VOL - [7:0] */ +#define ARIZONA_OUT4R_VOL_SHIFT 0 /* OUT4R_VOL - [7:0] */ +#define ARIZONA_OUT4R_VOL_WIDTH 8 /* OUT4R_VOL - [7:0] */ + +/* + * R1070 (0x42E) - Out Volume 4R + */ +#define ARIZONA_OUT4R_VOL_LIM_MASK 0x00FF /* OUT4R_VOL_LIM - [7:0] */ +#define ARIZONA_OUT4R_VOL_LIM_SHIFT 0 /* OUT4R_VOL_LIM - [7:0] */ +#define ARIZONA_OUT4R_VOL_LIM_WIDTH 8 /* OUT4R_VOL_LIM - [7:0] */ + +/* + * R1071 (0x42F) - Noise Gate Select 4R + */ +#define ARIZONA_OUT4R_NGATE_SRC_MASK 0x0FFF /* OUT4R_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT4R_NGATE_SRC_SHIFT 0 /* OUT4R_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT4R_NGATE_SRC_WIDTH 12 /* OUT4R_NGATE_SRC - [11:0] */ + +/* + * R1072 (0x430) - Output Path Config 5L + */ +#define ARIZONA_OUT5_OSR 0x2000 /* OUT5_OSR */ +#define ARIZONA_OUT5_OSR_MASK 0x2000 /* OUT5_OSR */ +#define ARIZONA_OUT5_OSR_SHIFT 13 /* OUT5_OSR */ +#define ARIZONA_OUT5_OSR_WIDTH 1 /* OUT5_OSR */ +#define ARIZONA_OUT5L_ANC_SRC_MASK 0x0C00 /* OUT5L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT5L_ANC_SRC_SHIFT 10 /* OUT5L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT5L_ANC_SRC_WIDTH 2 /* OUT5L_ANC_SRC - [11:10] */ + +/* + * R1073 (0x431) - DAC Digital Volume 5L + */ +#define ARIZONA_OUT_VU 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_SHIFT 9 /* OUT_VU */ +#define ARIZONA_OUT_VU_WIDTH 1 /* OUT_VU */ +#define ARIZONA_OUT5L_MUTE 0x0100 /* OUT5L_MUTE */ +#define ARIZONA_OUT5L_MUTE_MASK 0x0100 /* OUT5L_MUTE */ +#define ARIZONA_OUT5L_MUTE_SHIFT 8 /* OUT5L_MUTE */ +#define ARIZONA_OUT5L_MUTE_WIDTH 1 /* OUT5L_MUTE */ +#define ARIZONA_OUT5L_VOL_MASK 0x00FF /* OUT5L_VOL - [7:0] */ +#define ARIZONA_OUT5L_VOL_SHIFT 0 /* OUT5L_VOL - [7:0] */ +#define ARIZONA_OUT5L_VOL_WIDTH 8 /* OUT5L_VOL - [7:0] */ + +/* + * R1074 (0x432) - DAC Volume Limit 5L + */ +#define ARIZONA_OUT5L_VOL_LIM_MASK 0x00FF /* OUT5L_VOL_LIM - [7:0] */ +#define ARIZONA_OUT5L_VOL_LIM_SHIFT 0 /* OUT5L_VOL_LIM - [7:0] */ +#define ARIZONA_OUT5L_VOL_LIM_WIDTH 8 /* OUT5L_VOL_LIM - [7:0] */ + +/* + * R1075 (0x433) - Noise Gate Select 5L + */ +#define ARIZONA_OUT5L_NGATE_SRC_MASK 0x0FFF /* OUT5L_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT5L_NGATE_SRC_SHIFT 0 /* OUT5L_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT5L_NGATE_SRC_WIDTH 12 /* OUT5L_NGATE_SRC - [11:0] */ + +/* + * R1076 (0x434) - Output Path Config 5R + */ +#define ARIZONA_OUT5R_ANC_SRC_MASK 0x0C00 /* OUT5R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT5R_ANC_SRC_SHIFT 10 /* OUT5R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT5R_ANC_SRC_WIDTH 2 /* OUT5R_ANC_SRC - [11:10] */ + +/* + * R1077 (0x435) - DAC Digital Volume 5R + */ +#define ARIZONA_OUT_VU 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_SHIFT 9 /* OUT_VU */ +#define ARIZONA_OUT_VU_WIDTH 1 /* OUT_VU */ +#define ARIZONA_OUT5R_MUTE 0x0100 /* OUT5R_MUTE */ +#define ARIZONA_OUT5R_MUTE_MASK 0x0100 /* OUT5R_MUTE */ +#define ARIZONA_OUT5R_MUTE_SHIFT 8 /* OUT5R_MUTE */ +#define ARIZONA_OUT5R_MUTE_WIDTH 1 /* OUT5R_MUTE */ +#define ARIZONA_OUT5R_VOL_MASK 0x00FF /* OUT5R_VOL - [7:0] */ +#define ARIZONA_OUT5R_VOL_SHIFT 0 /* OUT5R_VOL - [7:0] */ +#define ARIZONA_OUT5R_VOL_WIDTH 8 /* OUT5R_VOL - [7:0] */ + +/* + * R1078 (0x436) - DAC Volume Limit 5R + */ +#define ARIZONA_OUT5R_VOL_LIM_MASK 0x00FF /* OUT5R_VOL_LIM - [7:0] */ +#define ARIZONA_OUT5R_VOL_LIM_SHIFT 0 /* OUT5R_VOL_LIM - [7:0] */ +#define ARIZONA_OUT5R_VOL_LIM_WIDTH 8 /* OUT5R_VOL_LIM - [7:0] */ + +/* + * R1079 (0x437) - Noise Gate Select 5R + */ +#define ARIZONA_OUT5R_NGATE_SRC_MASK 0x0FFF /* OUT5R_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT5R_NGATE_SRC_SHIFT 0 /* OUT5R_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT5R_NGATE_SRC_WIDTH 12 /* OUT5R_NGATE_SRC - [11:0] */ + +/* + * R1104 (0x450) - DAC AEC Control 1 + */ +#define ARIZONA_AEC_LOOPBACK_SRC_MASK 0x003C /* AEC_LOOPBACK_SRC - [5:2] */ +#define ARIZONA_AEC_LOOPBACK_SRC_SHIFT 2 /* AEC_LOOPBACK_SRC - [5:2] */ +#define ARIZONA_AEC_LOOPBACK_SRC_WIDTH 4 /* AEC_LOOPBACK_SRC - [5:2] */ +#define ARIZONA_AEC_ENA_STS 0x0002 /* AEC_ENA_STS */ +#define ARIZONA_AEC_ENA_STS_MASK 0x0002 /* AEC_ENA_STS */ +#define ARIZONA_AEC_ENA_STS_SHIFT 1 /* AEC_ENA_STS */ +#define ARIZONA_AEC_ENA_STS_WIDTH 1 /* AEC_ENA_STS */ +#define ARIZONA_AEC_LOOPBACK_ENA 0x0001 /* AEC_LOOPBACK_ENA */ +#define ARIZONA_AEC_LOOPBACK_ENA_MASK 0x0001 /* AEC_LOOPBACK_ENA */ +#define ARIZONA_AEC_LOOPBACK_ENA_SHIFT 0 /* AEC_LOOPBACK_ENA */ +#define ARIZONA_AEC_LOOPBACK_ENA_WIDTH 1 /* AEC_LOOPBACK_ENA */ + +/* + * R1112 (0x458) - Noise Gate Control + */ +#define ARIZONA_NGATE_HOLD_MASK 0x0030 /* NGATE_HOLD - [5:4] */ +#define ARIZONA_NGATE_HOLD_SHIFT 4 /* NGATE_HOLD - [5:4] */ +#define ARIZONA_NGATE_HOLD_WIDTH 2 /* NGATE_HOLD - [5:4] */ +#define ARIZONA_NGATE_THR_MASK 0x000E /* NGATE_THR - [3:1] */ +#define ARIZONA_NGATE_THR_SHIFT 1 /* NGATE_THR - [3:1] */ +#define ARIZONA_NGATE_THR_WIDTH 3 /* NGATE_THR - [3:1] */ +#define ARIZONA_NGATE_ENA 0x0001 /* NGATE_ENA */ +#define ARIZONA_NGATE_ENA_MASK 0x0001 /* NGATE_ENA */ +#define ARIZONA_NGATE_ENA_SHIFT 0 /* NGATE_ENA */ +#define ARIZONA_NGATE_ENA_WIDTH 1 /* NGATE_ENA */ + +/* + * R1168 (0x490) - PDM SPK1 CTRL 1 + */ +#define ARIZONA_SPK1R_MUTE 0x2000 /* SPK1R_MUTE */ +#define ARIZONA_SPK1R_MUTE_MASK 0x2000 /* SPK1R_MUTE */ +#define ARIZONA_SPK1R_MUTE_SHIFT 13 /* SPK1R_MUTE */ +#define ARIZONA_SPK1R_MUTE_WIDTH 1 /* SPK1R_MUTE */ +#define ARIZONA_SPK1L_MUTE 0x1000 /* SPK1L_MUTE */ +#define ARIZONA_SPK1L_MUTE_MASK 0x1000 /* SPK1L_MUTE */ +#define ARIZONA_SPK1L_MUTE_SHIFT 12 /* SPK1L_MUTE */ +#define ARIZONA_SPK1L_MUTE_WIDTH 1 /* SPK1L_MUTE */ +#define ARIZONA_SPK1_MUTE_ENDIAN 0x0100 /* SPK1_MUTE_ENDIAN */ +#define ARIZONA_SPK1_MUTE_ENDIAN_MASK 0x0100 /* SPK1_MUTE_ENDIAN */ +#define ARIZONA_SPK1_MUTE_ENDIAN_SHIFT 8 /* SPK1_MUTE_ENDIAN */ +#define ARIZONA_SPK1_MUTE_ENDIAN_WIDTH 1 /* SPK1_MUTE_ENDIAN */ +#define ARIZONA_SPK1_MUTE_SEQ1_MASK 0x00FF /* SPK1_MUTE_SEQ1 - [7:0] */ +#define ARIZONA_SPK1_MUTE_SEQ1_SHIFT 0 /* SPK1_MUTE_SEQ1 - [7:0] */ +#define ARIZONA_SPK1_MUTE_SEQ1_WIDTH 8 /* SPK1_MUTE_SEQ1 - [7:0] */ + +/* + * R1169 (0x491) - PDM SPK1 CTRL 2 + */ +#define ARIZONA_SPK1_FMT 0x0001 /* SPK1_FMT */ +#define ARIZONA_SPK1_FMT_MASK 0x0001 /* SPK1_FMT */ +#define ARIZONA_SPK1_FMT_SHIFT 0 /* SPK1_FMT */ +#define ARIZONA_SPK1_FMT_WIDTH 1 /* SPK1_FMT */ + +/* + * R1244 (0x4DC) - DAC comp 1 + */ +#define ARIZONA_OUT_COMP_COEFF_MASK 0xFFFF /* OUT_COMP_COEFF - [15:0] */ +#define ARIZONA_OUT_COMP_COEFF_SHIFT 0 /* OUT_COMP_COEFF - [15:0] */ +#define ARIZONA_OUT_COMP_COEFF_WIDTH 16 /* OUT_COMP_COEFF - [15:0] */ + +/* + * R1245 (0x4DD) - DAC comp 2 + */ +#define ARIZONA_OUT_COMP_COEFF_1 0x0002 /* OUT_COMP_COEFF */ +#define ARIZONA_OUT_COMP_COEFF_1_MASK 0x0002 /* OUT_COMP_COEFF */ +#define ARIZONA_OUT_COMP_COEFF_1_SHIFT 1 /* OUT_COMP_COEFF */ +#define ARIZONA_OUT_COMP_COEFF_1_WIDTH 1 /* OUT_COMP_COEFF */ +#define ARIZONA_OUT_COMP_COEFF_SEL 0x0001 /* OUT_COMP_COEFF_SEL */ +#define ARIZONA_OUT_COMP_COEFF_SEL_MASK 0x0001 /* OUT_COMP_COEFF_SEL */ +#define ARIZONA_OUT_COMP_COEFF_SEL_SHIFT 0 /* OUT_COMP_COEFF_SEL */ +#define ARIZONA_OUT_COMP_COEFF_SEL_WIDTH 1 /* OUT_COMP_COEFF_SEL */ + +/* + * R1246 (0x4DE) - DAC comp 3 + */ +#define ARIZONA_AEC_COMP_COEFF_MASK 0xFFFF /* AEC_COMP_COEFF - [15:0] */ +#define ARIZONA_AEC_COMP_COEFF_SHIFT 0 /* AEC_COMP_COEFF - [15:0] */ +#define ARIZONA_AEC_COMP_COEFF_WIDTH 16 /* AEC_COMP_COEFF - [15:0] */ + +/* + * R1247 (0x4DF) - DAC comp 4 + */ +#define ARIZONA_AEC_COMP_COEFF_1 0x0002 /* AEC_COMP_COEFF */ +#define ARIZONA_AEC_COMP_COEFF_1_MASK 0x0002 /* AEC_COMP_COEFF */ +#define ARIZONA_AEC_COMP_COEFF_1_SHIFT 1 /* AEC_COMP_COEFF */ +#define ARIZONA_AEC_COMP_COEFF_1_WIDTH 1 /* AEC_COMP_COEFF */ +#define ARIZONA_AEC_COMP_COEFF_SEL 0x0001 /* AEC_COMP_COEFF_SEL */ +#define ARIZONA_AEC_COMP_COEFF_SEL_MASK 0x0001 /* AEC_COMP_COEFF_SEL */ +#define ARIZONA_AEC_COMP_COEFF_SEL_SHIFT 0 /* AEC_COMP_COEFF_SEL */ +#define ARIZONA_AEC_COMP_COEFF_SEL_WIDTH 1 /* AEC_COMP_COEFF_SEL */ + +/* + * R1280 (0x500) - AIF1 BCLK Ctrl + */ +#define ARIZONA_AIF1_BCLK_INV 0x0080 /* AIF1_BCLK_INV */ +#define ARIZONA_AIF1_BCLK_INV_MASK 0x0080 /* AIF1_BCLK_INV */ +#define ARIZONA_AIF1_BCLK_INV_SHIFT 7 /* AIF1_BCLK_INV */ +#define ARIZONA_AIF1_BCLK_INV_WIDTH 1 /* AIF1_BCLK_INV */ +#define ARIZONA_AIF1_BCLK_FRC 0x0040 /* AIF1_BCLK_FRC */ +#define ARIZONA_AIF1_BCLK_FRC_MASK 0x0040 /* AIF1_BCLK_FRC */ +#define ARIZONA_AIF1_BCLK_FRC_SHIFT 6 /* AIF1_BCLK_FRC */ +#define ARIZONA_AIF1_BCLK_FRC_WIDTH 1 /* AIF1_BCLK_FRC */ +#define ARIZONA_AIF1_BCLK_MSTR 0x0020 /* AIF1_BCLK_MSTR */ +#define ARIZONA_AIF1_BCLK_MSTR_MASK 0x0020 /* AIF1_BCLK_MSTR */ +#define ARIZONA_AIF1_BCLK_MSTR_SHIFT 5 /* AIF1_BCLK_MSTR */ +#define ARIZONA_AIF1_BCLK_MSTR_WIDTH 1 /* AIF1_BCLK_MSTR */ +#define ARIZONA_AIF1_BCLK_FREQ_MASK 0x001F /* AIF1_BCLK_FREQ - [4:0] */ +#define ARIZONA_AIF1_BCLK_FREQ_SHIFT 0 /* AIF1_BCLK_FREQ - [4:0] */ +#define ARIZONA_AIF1_BCLK_FREQ_WIDTH 5 /* AIF1_BCLK_FREQ - [4:0] */ + +/* + * R1281 (0x501) - AIF1 Tx Pin Ctrl + */ +#define ARIZONA_AIF1TX_DAT_TRI 0x0020 /* AIF1TX_DAT_TRI */ +#define ARIZONA_AIF1TX_DAT_TRI_MASK 0x0020 /* AIF1TX_DAT_TRI */ +#define ARIZONA_AIF1TX_DAT_TRI_SHIFT 5 /* AIF1TX_DAT_TRI */ +#define ARIZONA_AIF1TX_DAT_TRI_WIDTH 1 /* AIF1TX_DAT_TRI */ +#define ARIZONA_AIF1TX_LRCLK_SRC 0x0008 /* AIF1TX_LRCLK_SRC */ +#define ARIZONA_AIF1TX_LRCLK_SRC_MASK 0x0008 /* AIF1TX_LRCLK_SRC */ +#define ARIZONA_AIF1TX_LRCLK_SRC_SHIFT 3 /* AIF1TX_LRCLK_SRC */ +#define ARIZONA_AIF1TX_LRCLK_SRC_WIDTH 1 /* AIF1TX_LRCLK_SRC */ +#define ARIZONA_AIF1TX_LRCLK_INV 0x0004 /* AIF1TX_LRCLK_INV */ +#define ARIZONA_AIF1TX_LRCLK_INV_MASK 0x0004 /* AIF1TX_LRCLK_INV */ +#define ARIZONA_AIF1TX_LRCLK_INV_SHIFT 2 /* AIF1TX_LRCLK_INV */ +#define ARIZONA_AIF1TX_LRCLK_INV_WIDTH 1 /* AIF1TX_LRCLK_INV */ +#define ARIZONA_AIF1TX_LRCLK_FRC 0x0002 /* AIF1TX_LRCLK_FRC */ +#define ARIZONA_AIF1TX_LRCLK_FRC_MASK 0x0002 /* AIF1TX_LRCLK_FRC */ +#define ARIZONA_AIF1TX_LRCLK_FRC_SHIFT 1 /* AIF1TX_LRCLK_FRC */ +#define ARIZONA_AIF1TX_LRCLK_FRC_WIDTH 1 /* AIF1TX_LRCLK_FRC */ +#define ARIZONA_AIF1TX_LRCLK_MSTR 0x0001 /* AIF1TX_LRCLK_MSTR */ +#define ARIZONA_AIF1TX_LRCLK_MSTR_MASK 0x0001 /* AIF1TX_LRCLK_MSTR */ +#define ARIZONA_AIF1TX_LRCLK_MSTR_SHIFT 0 /* AIF1TX_LRCLK_MSTR */ +#define ARIZONA_AIF1TX_LRCLK_MSTR_WIDTH 1 /* AIF1TX_LRCLK_MSTR */ + +/* + * R1282 (0x502) - AIF1 Rx Pin Ctrl + */ +#define ARIZONA_AIF1RX_LRCLK_INV 0x0004 /* AIF1RX_LRCLK_INV */ +#define ARIZONA_AIF1RX_LRCLK_INV_MASK 0x0004 /* AIF1RX_LRCLK_INV */ +#define ARIZONA_AIF1RX_LRCLK_INV_SHIFT 2 /* AIF1RX_LRCLK_INV */ +#define ARIZONA_AIF1RX_LRCLK_INV_WIDTH 1 /* AIF1RX_LRCLK_INV */ +#define ARIZONA_AIF1RX_LRCLK_FRC 0x0002 /* AIF1RX_LRCLK_FRC */ +#define ARIZONA_AIF1RX_LRCLK_FRC_MASK 0x0002 /* AIF1RX_LRCLK_FRC */ +#define ARIZONA_AIF1RX_LRCLK_FRC_SHIFT 1 /* AIF1RX_LRCLK_FRC */ +#define ARIZONA_AIF1RX_LRCLK_FRC_WIDTH 1 /* AIF1RX_LRCLK_FRC */ +#define ARIZONA_AIF1RX_LRCLK_MSTR 0x0001 /* AIF1RX_LRCLK_MSTR */ +#define ARIZONA_AIF1RX_LRCLK_MSTR_MASK 0x0001 /* AIF1RX_LRCLK_MSTR */ +#define ARIZONA_AIF1RX_LRCLK_MSTR_SHIFT 0 /* AIF1RX_LRCLK_MSTR */ +#define ARIZONA_AIF1RX_LRCLK_MSTR_WIDTH 1 /* AIF1RX_LRCLK_MSTR */ + +/* + * R1283 (0x503) - AIF1 Rate Ctrl + */ +#define ARIZONA_AIF1_RATE_MASK 0x7800 /* AIF1_RATE - [14:11] */ +#define ARIZONA_AIF1_RATE_SHIFT 11 /* AIF1_RATE - [14:11] */ +#define ARIZONA_AIF1_RATE_WIDTH 4 /* AIF1_RATE - [14:11] */ +#define ARIZONA_AIF1_TRI 0x0040 /* AIF1_TRI */ +#define ARIZONA_AIF1_TRI_MASK 0x0040 /* AIF1_TRI */ +#define ARIZONA_AIF1_TRI_SHIFT 6 /* AIF1_TRI */ +#define ARIZONA_AIF1_TRI_WIDTH 1 /* AIF1_TRI */ + +/* + * R1284 (0x504) - AIF1 Format + */ +#define ARIZONA_AIF1_FMT_MASK 0x0007 /* AIF1_FMT - [2:0] */ +#define ARIZONA_AIF1_FMT_SHIFT 0 /* AIF1_FMT - [2:0] */ +#define ARIZONA_AIF1_FMT_WIDTH 3 /* AIF1_FMT - [2:0] */ + +/* + * R1285 (0x505) - AIF1 Tx BCLK Rate + */ +#define ARIZONA_AIF1TX_BCPF_MASK 0x1FFF /* AIF1TX_BCPF - [12:0] */ +#define ARIZONA_AIF1TX_BCPF_SHIFT 0 /* AIF1TX_BCPF - [12:0] */ +#define ARIZONA_AIF1TX_BCPF_WIDTH 13 /* AIF1TX_BCPF - [12:0] */ + +/* + * R1286 (0x506) - AIF1 Rx BCLK Rate + */ +#define ARIZONA_AIF1RX_BCPF_MASK 0x1FFF /* AIF1RX_BCPF - [12:0] */ +#define ARIZONA_AIF1RX_BCPF_SHIFT 0 /* AIF1RX_BCPF - [12:0] */ +#define ARIZONA_AIF1RX_BCPF_WIDTH 13 /* AIF1RX_BCPF - [12:0] */ + +/* + * R1287 (0x507) - AIF1 Frame Ctrl 1 + */ +#define ARIZONA_AIF1TX_WL_MASK 0x3F00 /* AIF1TX_WL - [13:8] */ +#define ARIZONA_AIF1TX_WL_SHIFT 8 /* AIF1TX_WL - [13:8] */ +#define ARIZONA_AIF1TX_WL_WIDTH 6 /* AIF1TX_WL - [13:8] */ +#define ARIZONA_AIF1TX_SLOT_LEN_MASK 0x00FF /* AIF1TX_SLOT_LEN - [7:0] */ +#define ARIZONA_AIF1TX_SLOT_LEN_SHIFT 0 /* AIF1TX_SLOT_LEN - [7:0] */ +#define ARIZONA_AIF1TX_SLOT_LEN_WIDTH 8 /* AIF1TX_SLOT_LEN - [7:0] */ + +/* + * R1288 (0x508) - AIF1 Frame Ctrl 2 + */ +#define ARIZONA_AIF1RX_WL_MASK 0x3F00 /* AIF1RX_WL - [13:8] */ +#define ARIZONA_AIF1RX_WL_SHIFT 8 /* AIF1RX_WL - [13:8] */ +#define ARIZONA_AIF1RX_WL_WIDTH 6 /* AIF1RX_WL - [13:8] */ +#define ARIZONA_AIF1RX_SLOT_LEN_MASK 0x00FF /* AIF1RX_SLOT_LEN - [7:0] */ +#define ARIZONA_AIF1RX_SLOT_LEN_SHIFT 0 /* AIF1RX_SLOT_LEN - [7:0] */ +#define ARIZONA_AIF1RX_SLOT_LEN_WIDTH 8 /* AIF1RX_SLOT_LEN - [7:0] */ + +/* + * R1289 (0x509) - AIF1 Frame Ctrl 3 + */ +#define ARIZONA_AIF1TX1_SLOT_MASK 0x003F /* AIF1TX1_SLOT - [5:0] */ +#define ARIZONA_AIF1TX1_SLOT_SHIFT 0 /* AIF1TX1_SLOT - [5:0] */ +#define ARIZONA_AIF1TX1_SLOT_WIDTH 6 /* AIF1TX1_SLOT - [5:0] */ + +/* + * R1290 (0x50A) - AIF1 Frame Ctrl 4 + */ +#define ARIZONA_AIF1TX2_SLOT_MASK 0x003F /* AIF1TX2_SLOT - [5:0] */ +#define ARIZONA_AIF1TX2_SLOT_SHIFT 0 /* AIF1TX2_SLOT - [5:0] */ +#define ARIZONA_AIF1TX2_SLOT_WIDTH 6 /* AIF1TX2_SLOT - [5:0] */ + +/* + * R1291 (0x50B) - AIF1 Frame Ctrl 5 + */ +#define ARIZONA_AIF1TX3_SLOT_MASK 0x003F /* AIF1TX3_SLOT - [5:0] */ +#define ARIZONA_AIF1TX3_SLOT_SHIFT 0 /* AIF1TX3_SLOT - [5:0] */ +#define ARIZONA_AIF1TX3_SLOT_WIDTH 6 /* AIF1TX3_SLOT - [5:0] */ + +/* + * R1292 (0x50C) - AIF1 Frame Ctrl 6 + */ +#define ARIZONA_AIF1TX4_SLOT_MASK 0x003F /* AIF1TX4_SLOT - [5:0] */ +#define ARIZONA_AIF1TX4_SLOT_SHIFT 0 /* AIF1TX4_SLOT - [5:0] */ +#define ARIZONA_AIF1TX4_SLOT_WIDTH 6 /* AIF1TX4_SLOT - [5:0] */ + +/* + * R1293 (0x50D) - AIF1 Frame Ctrl 7 + */ +#define ARIZONA_AIF1TX5_SLOT_MASK 0x003F /* AIF1TX5_SLOT - [5:0] */ +#define ARIZONA_AIF1TX5_SLOT_SHIFT 0 /* AIF1TX5_SLOT - [5:0] */ +#define ARIZONA_AIF1TX5_SLOT_WIDTH 6 /* AIF1TX5_SLOT - [5:0] */ + +/* + * R1294 (0x50E) - AIF1 Frame Ctrl 8 + */ +#define ARIZONA_AIF1TX6_SLOT_MASK 0x003F /* AIF1TX6_SLOT - [5:0] */ +#define ARIZONA_AIF1TX6_SLOT_SHIFT 0 /* AIF1TX6_SLOT - [5:0] */ +#define ARIZONA_AIF1TX6_SLOT_WIDTH 6 /* AIF1TX6_SLOT - [5:0] */ + +/* + * R1295 (0x50F) - AIF1 Frame Ctrl 9 + */ +#define ARIZONA_AIF1TX7_SLOT_MASK 0x003F /* AIF1TX7_SLOT - [5:0] */ +#define ARIZONA_AIF1TX7_SLOT_SHIFT 0 /* AIF1TX7_SLOT - [5:0] */ +#define ARIZONA_AIF1TX7_SLOT_WIDTH 6 /* AIF1TX7_SLOT - [5:0] */ + +/* + * R1296 (0x510) - AIF1 Frame Ctrl 10 + */ +#define ARIZONA_AIF1TX8_SLOT_MASK 0x003F /* AIF1TX8_SLOT - [5:0] */ +#define ARIZONA_AIF1TX8_SLOT_SHIFT 0 /* AIF1TX8_SLOT - [5:0] */ +#define ARIZONA_AIF1TX8_SLOT_WIDTH 6 /* AIF1TX8_SLOT - [5:0] */ + +/* + * R1297 (0x511) - AIF1 Frame Ctrl 11 + */ +#define ARIZONA_AIF1RX1_SLOT_MASK 0x003F /* AIF1RX1_SLOT - [5:0] */ +#define ARIZONA_AIF1RX1_SLOT_SHIFT 0 /* AIF1RX1_SLOT - [5:0] */ +#define ARIZONA_AIF1RX1_SLOT_WIDTH 6 /* AIF1RX1_SLOT - [5:0] */ + +/* + * R1298 (0x512) - AIF1 Frame Ctrl 12 + */ +#define ARIZONA_AIF1RX2_SLOT_MASK 0x003F /* AIF1RX2_SLOT - [5:0] */ +#define ARIZONA_AIF1RX2_SLOT_SHIFT 0 /* AIF1RX2_SLOT - [5:0] */ +#define ARIZONA_AIF1RX2_SLOT_WIDTH 6 /* AIF1RX2_SLOT - [5:0] */ + +/* + * R1299 (0x513) - AIF1 Frame Ctrl 13 + */ +#define ARIZONA_AIF1RX3_SLOT_MASK 0x003F /* AIF1RX3_SLOT - [5:0] */ +#define ARIZONA_AIF1RX3_SLOT_SHIFT 0 /* AIF1RX3_SLOT - [5:0] */ +#define ARIZONA_AIF1RX3_SLOT_WIDTH 6 /* AIF1RX3_SLOT - [5:0] */ + +/* + * R1300 (0x514) - AIF1 Frame Ctrl 14 + */ +#define ARIZONA_AIF1RX4_SLOT_MASK 0x003F /* AIF1RX4_SLOT - [5:0] */ +#define ARIZONA_AIF1RX4_SLOT_SHIFT 0 /* AIF1RX4_SLOT - [5:0] */ +#define ARIZONA_AIF1RX4_SLOT_WIDTH 6 /* AIF1RX4_SLOT - [5:0] */ + +/* + * R1301 (0x515) - AIF1 Frame Ctrl 15 + */ +#define ARIZONA_AIF1RX5_SLOT_MASK 0x003F /* AIF1RX5_SLOT - [5:0] */ +#define ARIZONA_AIF1RX5_SLOT_SHIFT 0 /* AIF1RX5_SLOT - [5:0] */ +#define ARIZONA_AIF1RX5_SLOT_WIDTH 6 /* AIF1RX5_SLOT - [5:0] */ + +/* + * R1302 (0x516) - AIF1 Frame Ctrl 16 + */ +#define ARIZONA_AIF1RX6_SLOT_MASK 0x003F /* AIF1RX6_SLOT - [5:0] */ +#define ARIZONA_AIF1RX6_SLOT_SHIFT 0 /* AIF1RX6_SLOT - [5:0] */ +#define ARIZONA_AIF1RX6_SLOT_WIDTH 6 /* AIF1RX6_SLOT - [5:0] */ + +/* + * R1303 (0x517) - AIF1 Frame Ctrl 17 + */ +#define ARIZONA_AIF1RX7_SLOT_MASK 0x003F /* AIF1RX7_SLOT - [5:0] */ +#define ARIZONA_AIF1RX7_SLOT_SHIFT 0 /* AIF1RX7_SLOT - [5:0] */ +#define ARIZONA_AIF1RX7_SLOT_WIDTH 6 /* AIF1RX7_SLOT - [5:0] */ + +/* + * R1304 (0x518) - AIF1 Frame Ctrl 18 + */ +#define ARIZONA_AIF1RX8_SLOT_MASK 0x003F /* AIF1RX8_SLOT - [5:0] */ +#define ARIZONA_AIF1RX8_SLOT_SHIFT 0 /* AIF1RX8_SLOT - [5:0] */ +#define ARIZONA_AIF1RX8_SLOT_WIDTH 6 /* AIF1RX8_SLOT - [5:0] */ + +/* + * R1305 (0x519) - AIF1 Tx Enables + */ +#define ARIZONA_AIF1TX8_ENA 0x0080 /* AIF1TX8_ENA */ +#define ARIZONA_AIF1TX8_ENA_MASK 0x0080 /* AIF1TX8_ENA */ +#define ARIZONA_AIF1TX8_ENA_SHIFT 7 /* AIF1TX8_ENA */ +#define ARIZONA_AIF1TX8_ENA_WIDTH 1 /* AIF1TX8_ENA */ +#define ARIZONA_AIF1TX7_ENA 0x0040 /* AIF1TX7_ENA */ +#define ARIZONA_AIF1TX7_ENA_MASK 0x0040 /* AIF1TX7_ENA */ +#define ARIZONA_AIF1TX7_ENA_SHIFT 6 /* AIF1TX7_ENA */ +#define ARIZONA_AIF1TX7_ENA_WIDTH 1 /* AIF1TX7_ENA */ +#define ARIZONA_AIF1TX6_ENA 0x0020 /* AIF1TX6_ENA */ +#define ARIZONA_AIF1TX6_ENA_MASK 0x0020 /* AIF1TX6_ENA */ +#define ARIZONA_AIF1TX6_ENA_SHIFT 5 /* AIF1TX6_ENA */ +#define ARIZONA_AIF1TX6_ENA_WIDTH 1 /* AIF1TX6_ENA */ +#define ARIZONA_AIF1TX5_ENA 0x0010 /* AIF1TX5_ENA */ +#define ARIZONA_AIF1TX5_ENA_MASK 0x0010 /* AIF1TX5_ENA */ +#define ARIZONA_AIF1TX5_ENA_SHIFT 4 /* AIF1TX5_ENA */ +#define ARIZONA_AIF1TX5_ENA_WIDTH 1 /* AIF1TX5_ENA */ +#define ARIZONA_AIF1TX4_ENA 0x0008 /* AIF1TX4_ENA */ +#define ARIZONA_AIF1TX4_ENA_MASK 0x0008 /* AIF1TX4_ENA */ +#define ARIZONA_AIF1TX4_ENA_SHIFT 3 /* AIF1TX4_ENA */ +#define ARIZONA_AIF1TX4_ENA_WIDTH 1 /* AIF1TX4_ENA */ +#define ARIZONA_AIF1TX3_ENA 0x0004 /* AIF1TX3_ENA */ +#define ARIZONA_AIF1TX3_ENA_MASK 0x0004 /* AIF1TX3_ENA */ +#define ARIZONA_AIF1TX3_ENA_SHIFT 2 /* AIF1TX3_ENA */ +#define ARIZONA_AIF1TX3_ENA_WIDTH 1 /* AIF1TX3_ENA */ +#define ARIZONA_AIF1TX2_ENA 0x0002 /* AIF1TX2_ENA */ +#define ARIZONA_AIF1TX2_ENA_MASK 0x0002 /* AIF1TX2_ENA */ +#define ARIZONA_AIF1TX2_ENA_SHIFT 1 /* AIF1TX2_ENA */ +#define ARIZONA_AIF1TX2_ENA_WIDTH 1 /* AIF1TX2_ENA */ +#define ARIZONA_AIF1TX1_ENA 0x0001 /* AIF1TX1_ENA */ +#define ARIZONA_AIF1TX1_ENA_MASK 0x0001 /* AIF1TX1_ENA */ +#define ARIZONA_AIF1TX1_ENA_SHIFT 0 /* AIF1TX1_ENA */ +#define ARIZONA_AIF1TX1_ENA_WIDTH 1 /* AIF1TX1_ENA */ + +/* + * R1306 (0x51A) - AIF1 Rx Enables + */ +#define ARIZONA_AIF1RX8_ENA 0x0080 /* AIF1RX8_ENA */ +#define ARIZONA_AIF1RX8_ENA_MASK 0x0080 /* AIF1RX8_ENA */ +#define ARIZONA_AIF1RX8_ENA_SHIFT 7 /* AIF1RX8_ENA */ +#define ARIZONA_AIF1RX8_ENA_WIDTH 1 /* AIF1RX8_ENA */ +#define ARIZONA_AIF1RX7_ENA 0x0040 /* AIF1RX7_ENA */ +#define ARIZONA_AIF1RX7_ENA_MASK 0x0040 /* AIF1RX7_ENA */ +#define ARIZONA_AIF1RX7_ENA_SHIFT 6 /* AIF1RX7_ENA */ +#define ARIZONA_AIF1RX7_ENA_WIDTH 1 /* AIF1RX7_ENA */ +#define ARIZONA_AIF1RX6_ENA 0x0020 /* AIF1RX6_ENA */ +#define ARIZONA_AIF1RX6_ENA_MASK 0x0020 /* AIF1RX6_ENA */ +#define ARIZONA_AIF1RX6_ENA_SHIFT 5 /* AIF1RX6_ENA */ +#define ARIZONA_AIF1RX6_ENA_WIDTH 1 /* AIF1RX6_ENA */ +#define ARIZONA_AIF1RX5_ENA 0x0010 /* AIF1RX5_ENA */ +#define ARIZONA_AIF1RX5_ENA_MASK 0x0010 /* AIF1RX5_ENA */ +#define ARIZONA_AIF1RX5_ENA_SHIFT 4 /* AIF1RX5_ENA */ +#define ARIZONA_AIF1RX5_ENA_WIDTH 1 /* AIF1RX5_ENA */ +#define ARIZONA_AIF1RX4_ENA 0x0008 /* AIF1RX4_ENA */ +#define ARIZONA_AIF1RX4_ENA_MASK 0x0008 /* AIF1RX4_ENA */ +#define ARIZONA_AIF1RX4_ENA_SHIFT 3 /* AIF1RX4_ENA */ +#define ARIZONA_AIF1RX4_ENA_WIDTH 1 /* AIF1RX4_ENA */ +#define ARIZONA_AIF1RX3_ENA 0x0004 /* AIF1RX3_ENA */ +#define ARIZONA_AIF1RX3_ENA_MASK 0x0004 /* AIF1RX3_ENA */ +#define ARIZONA_AIF1RX3_ENA_SHIFT 2 /* AIF1RX3_ENA */ +#define ARIZONA_AIF1RX3_ENA_WIDTH 1 /* AIF1RX3_ENA */ +#define ARIZONA_AIF1RX2_ENA 0x0002 /* AIF1RX2_ENA */ +#define ARIZONA_AIF1RX2_ENA_MASK 0x0002 /* AIF1RX2_ENA */ +#define ARIZONA_AIF1RX2_ENA_SHIFT 1 /* AIF1RX2_ENA */ +#define ARIZONA_AIF1RX2_ENA_WIDTH 1 /* AIF1RX2_ENA */ +#define ARIZONA_AIF1RX1_ENA 0x0001 /* AIF1RX1_ENA */ +#define ARIZONA_AIF1RX1_ENA_MASK 0x0001 /* AIF1RX1_ENA */ +#define ARIZONA_AIF1RX1_ENA_SHIFT 0 /* AIF1RX1_ENA */ +#define ARIZONA_AIF1RX1_ENA_WIDTH 1 /* AIF1RX1_ENA */ + +/* + * R1307 (0x51B) - AIF1 Force Write + */ +#define ARIZONA_AIF1_FRC_WR 0x0001 /* AIF1_FRC_WR */ +#define ARIZONA_AIF1_FRC_WR_MASK 0x0001 /* AIF1_FRC_WR */ +#define ARIZONA_AIF1_FRC_WR_SHIFT 0 /* AIF1_FRC_WR */ +#define ARIZONA_AIF1_FRC_WR_WIDTH 1 /* AIF1_FRC_WR */ + +/* + * R1344 (0x540) - AIF2 BCLK Ctrl + */ +#define ARIZONA_AIF2_BCLK_INV 0x0080 /* AIF2_BCLK_INV */ +#define ARIZONA_AIF2_BCLK_INV_MASK 0x0080 /* AIF2_BCLK_INV */ +#define ARIZONA_AIF2_BCLK_INV_SHIFT 7 /* AIF2_BCLK_INV */ +#define ARIZONA_AIF2_BCLK_INV_WIDTH 1 /* AIF2_BCLK_INV */ +#define ARIZONA_AIF2_BCLK_FRC 0x0040 /* AIF2_BCLK_FRC */ +#define ARIZONA_AIF2_BCLK_FRC_MASK 0x0040 /* AIF2_BCLK_FRC */ +#define ARIZONA_AIF2_BCLK_FRC_SHIFT 6 /* AIF2_BCLK_FRC */ +#define ARIZONA_AIF2_BCLK_FRC_WIDTH 1 /* AIF2_BCLK_FRC */ +#define ARIZONA_AIF2_BCLK_MSTR 0x0020 /* AIF2_BCLK_MSTR */ +#define ARIZONA_AIF2_BCLK_MSTR_MASK 0x0020 /* AIF2_BCLK_MSTR */ +#define ARIZONA_AIF2_BCLK_MSTR_SHIFT 5 /* AIF2_BCLK_MSTR */ +#define ARIZONA_AIF2_BCLK_MSTR_WIDTH 1 /* AIF2_BCLK_MSTR */ +#define ARIZONA_AIF2_BCLK_FREQ_MASK 0x001F /* AIF2_BCLK_FREQ - [4:0] */ +#define ARIZONA_AIF2_BCLK_FREQ_SHIFT 0 /* AIF2_BCLK_FREQ - [4:0] */ +#define ARIZONA_AIF2_BCLK_FREQ_WIDTH 5 /* AIF2_BCLK_FREQ - [4:0] */ + +/* + * R1345 (0x541) - AIF2 Tx Pin Ctrl + */ +#define ARIZONA_AIF2TX_DAT_TRI 0x0020 /* AIF2TX_DAT_TRI */ +#define ARIZONA_AIF2TX_DAT_TRI_MASK 0x0020 /* AIF2TX_DAT_TRI */ +#define ARIZONA_AIF2TX_DAT_TRI_SHIFT 5 /* AIF2TX_DAT_TRI */ +#define ARIZONA_AIF2TX_DAT_TRI_WIDTH 1 /* AIF2TX_DAT_TRI */ +#define ARIZONA_AIF2TX_LRCLK_SRC 0x0008 /* AIF2TX_LRCLK_SRC */ +#define ARIZONA_AIF2TX_LRCLK_SRC_MASK 0x0008 /* AIF2TX_LRCLK_SRC */ +#define ARIZONA_AIF2TX_LRCLK_SRC_SHIFT 3 /* AIF2TX_LRCLK_SRC */ +#define ARIZONA_AIF2TX_LRCLK_SRC_WIDTH 1 /* AIF2TX_LRCLK_SRC */ +#define ARIZONA_AIF2TX_LRCLK_INV 0x0004 /* AIF2TX_LRCLK_INV */ +#define ARIZONA_AIF2TX_LRCLK_INV_MASK 0x0004 /* AIF2TX_LRCLK_INV */ +#define ARIZONA_AIF2TX_LRCLK_INV_SHIFT 2 /* AIF2TX_LRCLK_INV */ +#define ARIZONA_AIF2TX_LRCLK_INV_WIDTH 1 /* AIF2TX_LRCLK_INV */ +#define ARIZONA_AIF2TX_LRCLK_FRC 0x0002 /* AIF2TX_LRCLK_FRC */ +#define ARIZONA_AIF2TX_LRCLK_FRC_MASK 0x0002 /* AIF2TX_LRCLK_FRC */ +#define ARIZONA_AIF2TX_LRCLK_FRC_SHIFT 1 /* AIF2TX_LRCLK_FRC */ +#define ARIZONA_AIF2TX_LRCLK_FRC_WIDTH 1 /* AIF2TX_LRCLK_FRC */ +#define ARIZONA_AIF2TX_LRCLK_MSTR 0x0001 /* AIF2TX_LRCLK_MSTR */ +#define ARIZONA_AIF2TX_LRCLK_MSTR_MASK 0x0001 /* AIF2TX_LRCLK_MSTR */ +#define ARIZONA_AIF2TX_LRCLK_MSTR_SHIFT 0 /* AIF2TX_LRCLK_MSTR */ +#define ARIZONA_AIF2TX_LRCLK_MSTR_WIDTH 1 /* AIF2TX_LRCLK_MSTR */ + +/* + * R1346 (0x542) - AIF2 Rx Pin Ctrl + */ +#define ARIZONA_AIF2RX_LRCLK_INV 0x0004 /* AIF2RX_LRCLK_INV */ +#define ARIZONA_AIF2RX_LRCLK_INV_MASK 0x0004 /* AIF2RX_LRCLK_INV */ +#define ARIZONA_AIF2RX_LRCLK_INV_SHIFT 2 /* AIF2RX_LRCLK_INV */ +#define ARIZONA_AIF2RX_LRCLK_INV_WIDTH 1 /* AIF2RX_LRCLK_INV */ +#define ARIZONA_AIF2RX_LRCLK_FRC 0x0002 /* AIF2RX_LRCLK_FRC */ +#define ARIZONA_AIF2RX_LRCLK_FRC_MASK 0x0002 /* AIF2RX_LRCLK_FRC */ +#define ARIZONA_AIF2RX_LRCLK_FRC_SHIFT 1 /* AIF2RX_LRCLK_FRC */ +#define ARIZONA_AIF2RX_LRCLK_FRC_WIDTH 1 /* AIF2RX_LRCLK_FRC */ +#define ARIZONA_AIF2RX_LRCLK_MSTR 0x0001 /* AIF2RX_LRCLK_MSTR */ +#define ARIZONA_AIF2RX_LRCLK_MSTR_MASK 0x0001 /* AIF2RX_LRCLK_MSTR */ +#define ARIZONA_AIF2RX_LRCLK_MSTR_SHIFT 0 /* AIF2RX_LRCLK_MSTR */ +#define ARIZONA_AIF2RX_LRCLK_MSTR_WIDTH 1 /* AIF2RX_LRCLK_MSTR */ + +/* + * R1347 (0x543) - AIF2 Rate Ctrl + */ +#define ARIZONA_AIF2_RATE_MASK 0x7800 /* AIF2_RATE - [14:11] */ +#define ARIZONA_AIF2_RATE_SHIFT 11 /* AIF2_RATE - [14:11] */ +#define ARIZONA_AIF2_RATE_WIDTH 4 /* AIF2_RATE - [14:11] */ +#define ARIZONA_AIF2_TRI 0x0040 /* AIF2_TRI */ +#define ARIZONA_AIF2_TRI_MASK 0x0040 /* AIF2_TRI */ +#define ARIZONA_AIF2_TRI_SHIFT 6 /* AIF2_TRI */ +#define ARIZONA_AIF2_TRI_WIDTH 1 /* AIF2_TRI */ + +/* + * R1348 (0x544) - AIF2 Format + */ +#define ARIZONA_AIF2_FMT_MASK 0x0007 /* AIF2_FMT - [2:0] */ +#define ARIZONA_AIF2_FMT_SHIFT 0 /* AIF2_FMT - [2:0] */ +#define ARIZONA_AIF2_FMT_WIDTH 3 /* AIF2_FMT - [2:0] */ + +/* + * R1349 (0x545) - AIF2 Tx BCLK Rate + */ +#define ARIZONA_AIF2TX_BCPF_MASK 0x1FFF /* AIF2TX_BCPF - [12:0] */ +#define ARIZONA_AIF2TX_BCPF_SHIFT 0 /* AIF2TX_BCPF - [12:0] */ +#define ARIZONA_AIF2TX_BCPF_WIDTH 13 /* AIF2TX_BCPF - [12:0] */ + +/* + * R1350 (0x546) - AIF2 Rx BCLK Rate + */ +#define ARIZONA_AIF2RX_BCPF_MASK 0x1FFF /* AIF2RX_BCPF - [12:0] */ +#define ARIZONA_AIF2RX_BCPF_SHIFT 0 /* AIF2RX_BCPF - [12:0] */ +#define ARIZONA_AIF2RX_BCPF_WIDTH 13 /* AIF2RX_BCPF - [12:0] */ + +/* + * R1351 (0x547) - AIF2 Frame Ctrl 1 + */ +#define ARIZONA_AIF2TX_WL_MASK 0x3F00 /* AIF2TX_WL - [13:8] */ +#define ARIZONA_AIF2TX_WL_SHIFT 8 /* AIF2TX_WL - [13:8] */ +#define ARIZONA_AIF2TX_WL_WIDTH 6 /* AIF2TX_WL - [13:8] */ +#define ARIZONA_AIF2TX_SLOT_LEN_MASK 0x00FF /* AIF2TX_SLOT_LEN - [7:0] */ +#define ARIZONA_AIF2TX_SLOT_LEN_SHIFT 0 /* AIF2TX_SLOT_LEN - [7:0] */ +#define ARIZONA_AIF2TX_SLOT_LEN_WIDTH 8 /* AIF2TX_SLOT_LEN - [7:0] */ + +/* + * R1352 (0x548) - AIF2 Frame Ctrl 2 + */ +#define ARIZONA_AIF2RX_WL_MASK 0x3F00 /* AIF2RX_WL - [13:8] */ +#define ARIZONA_AIF2RX_WL_SHIFT 8 /* AIF2RX_WL - [13:8] */ +#define ARIZONA_AIF2RX_WL_WIDTH 6 /* AIF2RX_WL - [13:8] */ +#define ARIZONA_AIF2RX_SLOT_LEN_MASK 0x00FF /* AIF2RX_SLOT_LEN - [7:0] */ +#define ARIZONA_AIF2RX_SLOT_LEN_SHIFT 0 /* AIF2RX_SLOT_LEN - [7:0] */ +#define ARIZONA_AIF2RX_SLOT_LEN_WIDTH 8 /* AIF2RX_SLOT_LEN - [7:0] */ + +/* + * R1353 (0x549) - AIF2 Frame Ctrl 3 + */ +#define ARIZONA_AIF2TX1_SLOT_MASK 0x003F /* AIF2TX1_SLOT - [5:0] */ +#define ARIZONA_AIF2TX1_SLOT_SHIFT 0 /* AIF2TX1_SLOT - [5:0] */ +#define ARIZONA_AIF2TX1_SLOT_WIDTH 6 /* AIF2TX1_SLOT - [5:0] */ + +/* + * R1354 (0x54A) - AIF2 Frame Ctrl 4 + */ +#define ARIZONA_AIF2TX2_SLOT_MASK 0x003F /* AIF2TX2_SLOT - [5:0] */ +#define ARIZONA_AIF2TX2_SLOT_SHIFT 0 /* AIF2TX2_SLOT - [5:0] */ +#define ARIZONA_AIF2TX2_SLOT_WIDTH 6 /* AIF2TX2_SLOT - [5:0] */ + +/* + * R1361 (0x551) - AIF2 Frame Ctrl 11 + */ +#define ARIZONA_AIF2RX1_SLOT_MASK 0x003F /* AIF2RX1_SLOT - [5:0] */ +#define ARIZONA_AIF2RX1_SLOT_SHIFT 0 /* AIF2RX1_SLOT - [5:0] */ +#define ARIZONA_AIF2RX1_SLOT_WIDTH 6 /* AIF2RX1_SLOT - [5:0] */ + +/* + * R1362 (0x552) - AIF2 Frame Ctrl 12 + */ +#define ARIZONA_AIF2RX2_SLOT_MASK 0x003F /* AIF2RX2_SLOT - [5:0] */ +#define ARIZONA_AIF2RX2_SLOT_SHIFT 0 /* AIF2RX2_SLOT - [5:0] */ +#define ARIZONA_AIF2RX2_SLOT_WIDTH 6 /* AIF2RX2_SLOT - [5:0] */ + +/* + * R1369 (0x559) - AIF2 Tx Enables + */ +#define ARIZONA_AIF2TX2_ENA 0x0002 /* AIF2TX2_ENA */ +#define ARIZONA_AIF2TX2_ENA_MASK 0x0002 /* AIF2TX2_ENA */ +#define ARIZONA_AIF2TX2_ENA_SHIFT 1 /* AIF2TX2_ENA */ +#define ARIZONA_AIF2TX2_ENA_WIDTH 1 /* AIF2TX2_ENA */ +#define ARIZONA_AIF2TX1_ENA 0x0001 /* AIF2TX1_ENA */ +#define ARIZONA_AIF2TX1_ENA_MASK 0x0001 /* AIF2TX1_ENA */ +#define ARIZONA_AIF2TX1_ENA_SHIFT 0 /* AIF2TX1_ENA */ +#define ARIZONA_AIF2TX1_ENA_WIDTH 1 /* AIF2TX1_ENA */ + +/* + * R1370 (0x55A) - AIF2 Rx Enables + */ +#define ARIZONA_AIF2RX2_ENA 0x0002 /* AIF2RX2_ENA */ +#define ARIZONA_AIF2RX2_ENA_MASK 0x0002 /* AIF2RX2_ENA */ +#define ARIZONA_AIF2RX2_ENA_SHIFT 1 /* AIF2RX2_ENA */ +#define ARIZONA_AIF2RX2_ENA_WIDTH 1 /* AIF2RX2_ENA */ +#define ARIZONA_AIF2RX1_ENA 0x0001 /* AIF2RX1_ENA */ +#define ARIZONA_AIF2RX1_ENA_MASK 0x0001 /* AIF2RX1_ENA */ +#define ARIZONA_AIF2RX1_ENA_SHIFT 0 /* AIF2RX1_ENA */ +#define ARIZONA_AIF2RX1_ENA_WIDTH 1 /* AIF2RX1_ENA */ + +/* + * R1371 (0x55B) - AIF2 Force Write + */ +#define ARIZONA_AIF2_FRC_WR 0x0001 /* AIF2_FRC_WR */ +#define ARIZONA_AIF2_FRC_WR_MASK 0x0001 /* AIF2_FRC_WR */ +#define ARIZONA_AIF2_FRC_WR_SHIFT 0 /* AIF2_FRC_WR */ +#define ARIZONA_AIF2_FRC_WR_WIDTH 1 /* AIF2_FRC_WR */ + +/* + * R1408 (0x580) - AIF3 BCLK Ctrl + */ +#define ARIZONA_AIF3_BCLK_INV 0x0080 /* AIF3_BCLK_INV */ +#define ARIZONA_AIF3_BCLK_INV_MASK 0x0080 /* AIF3_BCLK_INV */ +#define ARIZONA_AIF3_BCLK_INV_SHIFT 7 /* AIF3_BCLK_INV */ +#define ARIZONA_AIF3_BCLK_INV_WIDTH 1 /* AIF3_BCLK_INV */ +#define ARIZONA_AIF3_BCLK_FRC 0x0040 /* AIF3_BCLK_FRC */ +#define ARIZONA_AIF3_BCLK_FRC_MASK 0x0040 /* AIF3_BCLK_FRC */ +#define ARIZONA_AIF3_BCLK_FRC_SHIFT 6 /* AIF3_BCLK_FRC */ +#define ARIZONA_AIF3_BCLK_FRC_WIDTH 1 /* AIF3_BCLK_FRC */ +#define ARIZONA_AIF3_BCLK_MSTR 0x0020 /* AIF3_BCLK_MSTR */ +#define ARIZONA_AIF3_BCLK_MSTR_MASK 0x0020 /* AIF3_BCLK_MSTR */ +#define ARIZONA_AIF3_BCLK_MSTR_SHIFT 5 /* AIF3_BCLK_MSTR */ +#define ARIZONA_AIF3_BCLK_MSTR_WIDTH 1 /* AIF3_BCLK_MSTR */ +#define ARIZONA_AIF3_BCLK_FREQ_MASK 0x001F /* AIF3_BCLK_FREQ - [4:0] */ +#define ARIZONA_AIF3_BCLK_FREQ_SHIFT 0 /* AIF3_BCLK_FREQ - [4:0] */ +#define ARIZONA_AIF3_BCLK_FREQ_WIDTH 5 /* AIF3_BCLK_FREQ - [4:0] */ + +/* + * R1409 (0x581) - AIF3 Tx Pin Ctrl + */ +#define ARIZONA_AIF3TX_DAT_TRI 0x0020 /* AIF3TX_DAT_TRI */ +#define ARIZONA_AIF3TX_DAT_TRI_MASK 0x0020 /* AIF3TX_DAT_TRI */ +#define ARIZONA_AIF3TX_DAT_TRI_SHIFT 5 /* AIF3TX_DAT_TRI */ +#define ARIZONA_AIF3TX_DAT_TRI_WIDTH 1 /* AIF3TX_DAT_TRI */ +#define ARIZONA_AIF3TX_LRCLK_SRC 0x0008 /* AIF3TX_LRCLK_SRC */ +#define ARIZONA_AIF3TX_LRCLK_SRC_MASK 0x0008 /* AIF3TX_LRCLK_SRC */ +#define ARIZONA_AIF3TX_LRCLK_SRC_SHIFT 3 /* AIF3TX_LRCLK_SRC */ +#define ARIZONA_AIF3TX_LRCLK_SRC_WIDTH 1 /* AIF3TX_LRCLK_SRC */ +#define ARIZONA_AIF3TX_LRCLK_INV 0x0004 /* AIF3TX_LRCLK_INV */ +#define ARIZONA_AIF3TX_LRCLK_INV_MASK 0x0004 /* AIF3TX_LRCLK_INV */ +#define ARIZONA_AIF3TX_LRCLK_INV_SHIFT 2 /* AIF3TX_LRCLK_INV */ +#define ARIZONA_AIF3TX_LRCLK_INV_WIDTH 1 /* AIF3TX_LRCLK_INV */ +#define ARIZONA_AIF3TX_LRCLK_FRC 0x0002 /* AIF3TX_LRCLK_FRC */ +#define ARIZONA_AIF3TX_LRCLK_FRC_MASK 0x0002 /* AIF3TX_LRCLK_FRC */ +#define ARIZONA_AIF3TX_LRCLK_FRC_SHIFT 1 /* AIF3TX_LRCLK_FRC */ +#define ARIZONA_AIF3TX_LRCLK_FRC_WIDTH 1 /* AIF3TX_LRCLK_FRC */ +#define ARIZONA_AIF3TX_LRCLK_MSTR 0x0001 /* AIF3TX_LRCLK_MSTR */ +#define ARIZONA_AIF3TX_LRCLK_MSTR_MASK 0x0001 /* AIF3TX_LRCLK_MSTR */ +#define ARIZONA_AIF3TX_LRCLK_MSTR_SHIFT 0 /* AIF3TX_LRCLK_MSTR */ +#define ARIZONA_AIF3TX_LRCLK_MSTR_WIDTH 1 /* AIF3TX_LRCLK_MSTR */ + +/* + * R1410 (0x582) - AIF3 Rx Pin Ctrl + */ +#define ARIZONA_AIF3RX_LRCLK_INV 0x0004 /* AIF3RX_LRCLK_INV */ +#define ARIZONA_AIF3RX_LRCLK_INV_MASK 0x0004 /* AIF3RX_LRCLK_INV */ +#define ARIZONA_AIF3RX_LRCLK_INV_SHIFT 2 /* AIF3RX_LRCLK_INV */ +#define ARIZONA_AIF3RX_LRCLK_INV_WIDTH 1 /* AIF3RX_LRCLK_INV */ +#define ARIZONA_AIF3RX_LRCLK_FRC 0x0002 /* AIF3RX_LRCLK_FRC */ +#define ARIZONA_AIF3RX_LRCLK_FRC_MASK 0x0002 /* AIF3RX_LRCLK_FRC */ +#define ARIZONA_AIF3RX_LRCLK_FRC_SHIFT 1 /* AIF3RX_LRCLK_FRC */ +#define ARIZONA_AIF3RX_LRCLK_FRC_WIDTH 1 /* AIF3RX_LRCLK_FRC */ +#define ARIZONA_AIF3RX_LRCLK_MSTR 0x0001 /* AIF3RX_LRCLK_MSTR */ +#define ARIZONA_AIF3RX_LRCLK_MSTR_MASK 0x0001 /* AIF3RX_LRCLK_MSTR */ +#define ARIZONA_AIF3RX_LRCLK_MSTR_SHIFT 0 /* AIF3RX_LRCLK_MSTR */ +#define ARIZONA_AIF3RX_LRCLK_MSTR_WIDTH 1 /* AIF3RX_LRCLK_MSTR */ + +/* + * R1411 (0x583) - AIF3 Rate Ctrl + */ +#define ARIZONA_AIF3_RATE_MASK 0x7800 /* AIF3_RATE - [14:11] */ +#define ARIZONA_AIF3_RATE_SHIFT 11 /* AIF3_RATE - [14:11] */ +#define ARIZONA_AIF3_RATE_WIDTH 4 /* AIF3_RATE - [14:11] */ +#define ARIZONA_AIF3_TRI 0x0040 /* AIF3_TRI */ +#define ARIZONA_AIF3_TRI_MASK 0x0040 /* AIF3_TRI */ +#define ARIZONA_AIF3_TRI_SHIFT 6 /* AIF3_TRI */ +#define ARIZONA_AIF3_TRI_WIDTH 1 /* AIF3_TRI */ + +/* + * R1412 (0x584) - AIF3 Format + */ +#define ARIZONA_AIF3_FMT_MASK 0x0007 /* AIF3_FMT - [2:0] */ +#define ARIZONA_AIF3_FMT_SHIFT 0 /* AIF3_FMT - [2:0] */ +#define ARIZONA_AIF3_FMT_WIDTH 3 /* AIF3_FMT - [2:0] */ + +/* + * R1413 (0x585) - AIF3 Tx BCLK Rate + */ +#define ARIZONA_AIF3TX_BCPF_MASK 0x1FFF /* AIF3TX_BCPF - [12:0] */ +#define ARIZONA_AIF3TX_BCPF_SHIFT 0 /* AIF3TX_BCPF - [12:0] */ +#define ARIZONA_AIF3TX_BCPF_WIDTH 13 /* AIF3TX_BCPF - [12:0] */ + +/* + * R1414 (0x586) - AIF3 Rx BCLK Rate + */ +#define ARIZONA_AIF3RX_BCPF_MASK 0x1FFF /* AIF3RX_BCPF - [12:0] */ +#define ARIZONA_AIF3RX_BCPF_SHIFT 0 /* AIF3RX_BCPF - [12:0] */ +#define ARIZONA_AIF3RX_BCPF_WIDTH 13 /* AIF3RX_BCPF - [12:0] */ + +/* + * R1415 (0x587) - AIF3 Frame Ctrl 1 + */ +#define ARIZONA_AIF3TX_WL_MASK 0x3F00 /* AIF3TX_WL - [13:8] */ +#define ARIZONA_AIF3TX_WL_SHIFT 8 /* AIF3TX_WL - [13:8] */ +#define ARIZONA_AIF3TX_WL_WIDTH 6 /* AIF3TX_WL - [13:8] */ +#define ARIZONA_AIF3TX_SLOT_LEN_MASK 0x00FF /* AIF3TX_SLOT_LEN - [7:0] */ +#define ARIZONA_AIF3TX_SLOT_LEN_SHIFT 0 /* AIF3TX_SLOT_LEN - [7:0] */ +#define ARIZONA_AIF3TX_SLOT_LEN_WIDTH 8 /* AIF3TX_SLOT_LEN - [7:0] */ + +/* + * R1416 (0x588) - AIF3 Frame Ctrl 2 + */ +#define ARIZONA_AIF3RX_WL_MASK 0x3F00 /* AIF3RX_WL - [13:8] */ +#define ARIZONA_AIF3RX_WL_SHIFT 8 /* AIF3RX_WL - [13:8] */ +#define ARIZONA_AIF3RX_WL_WIDTH 6 /* AIF3RX_WL - [13:8] */ +#define ARIZONA_AIF3RX_SLOT_LEN_MASK 0x00FF /* AIF3RX_SLOT_LEN - [7:0] */ +#define ARIZONA_AIF3RX_SLOT_LEN_SHIFT 0 /* AIF3RX_SLOT_LEN - [7:0] */ +#define ARIZONA_AIF3RX_SLOT_LEN_WIDTH 8 /* AIF3RX_SLOT_LEN - [7:0] */ + +/* + * R1417 (0x589) - AIF3 Frame Ctrl 3 + */ +#define ARIZONA_AIF3TX1_SLOT_MASK 0x003F /* AIF3TX1_SLOT - [5:0] */ +#define ARIZONA_AIF3TX1_SLOT_SHIFT 0 /* AIF3TX1_SLOT - [5:0] */ +#define ARIZONA_AIF3TX1_SLOT_WIDTH 6 /* AIF3TX1_SLOT - [5:0] */ + +/* + * R1418 (0x58A) - AIF3 Frame Ctrl 4 + */ +#define ARIZONA_AIF3TX2_SLOT_MASK 0x003F /* AIF3TX2_SLOT - [5:0] */ +#define ARIZONA_AIF3TX2_SLOT_SHIFT 0 /* AIF3TX2_SLOT - [5:0] */ +#define ARIZONA_AIF3TX2_SLOT_WIDTH 6 /* AIF3TX2_SLOT - [5:0] */ + +/* + * R1425 (0x591) - AIF3 Frame Ctrl 11 + */ +#define ARIZONA_AIF3RX1_SLOT_MASK 0x003F /* AIF3RX1_SLOT - [5:0] */ +#define ARIZONA_AIF3RX1_SLOT_SHIFT 0 /* AIF3RX1_SLOT - [5:0] */ +#define ARIZONA_AIF3RX1_SLOT_WIDTH 6 /* AIF3RX1_SLOT - [5:0] */ + +/* + * R1426 (0x592) - AIF3 Frame Ctrl 12 + */ +#define ARIZONA_AIF3RX2_SLOT_MASK 0x003F /* AIF3RX2_SLOT - [5:0] */ +#define ARIZONA_AIF3RX2_SLOT_SHIFT 0 /* AIF3RX2_SLOT - [5:0] */ +#define ARIZONA_AIF3RX2_SLOT_WIDTH 6 /* AIF3RX2_SLOT - [5:0] */ + +/* + * R1433 (0x599) - AIF3 Tx Enables + */ +#define ARIZONA_AIF3TX2_ENA 0x0002 /* AIF3TX2_ENA */ +#define ARIZONA_AIF3TX2_ENA_MASK 0x0002 /* AIF3TX2_ENA */ +#define ARIZONA_AIF3TX2_ENA_SHIFT 1 /* AIF3TX2_ENA */ +#define ARIZONA_AIF3TX2_ENA_WIDTH 1 /* AIF3TX2_ENA */ +#define ARIZONA_AIF3TX1_ENA 0x0001 /* AIF3TX1_ENA */ +#define ARIZONA_AIF3TX1_ENA_MASK 0x0001 /* AIF3TX1_ENA */ +#define ARIZONA_AIF3TX1_ENA_SHIFT 0 /* AIF3TX1_ENA */ +#define ARIZONA_AIF3TX1_ENA_WIDTH 1 /* AIF3TX1_ENA */ + +/* + * R1434 (0x59A) - AIF3 Rx Enables + */ +#define ARIZONA_AIF3RX2_ENA 0x0002 /* AIF3RX2_ENA */ +#define ARIZONA_AIF3RX2_ENA_MASK 0x0002 /* AIF3RX2_ENA */ +#define ARIZONA_AIF3RX2_ENA_SHIFT 1 /* AIF3RX2_ENA */ +#define ARIZONA_AIF3RX2_ENA_WIDTH 1 /* AIF3RX2_ENA */ +#define ARIZONA_AIF3RX1_ENA 0x0001 /* AIF3RX1_ENA */ +#define ARIZONA_AIF3RX1_ENA_MASK 0x0001 /* AIF3RX1_ENA */ +#define ARIZONA_AIF3RX1_ENA_SHIFT 0 /* AIF3RX1_ENA */ +#define ARIZONA_AIF3RX1_ENA_WIDTH 1 /* AIF3RX1_ENA */ + +/* + * R1435 (0x59B) - AIF3 Force Write + */ +#define ARIZONA_AIF3_FRC_WR 0x0001 /* AIF3_FRC_WR */ +#define ARIZONA_AIF3_FRC_WR_MASK 0x0001 /* AIF3_FRC_WR */ +#define ARIZONA_AIF3_FRC_WR_SHIFT 0 /* AIF3_FRC_WR */ +#define ARIZONA_AIF3_FRC_WR_WIDTH 1 /* AIF3_FRC_WR */ + +/* + * R1507 (0x5E3) - SLIMbus Framer Ref Gear + */ +#define ARIZONA_SLIMCLK_SRC 0x0010 /* SLIMCLK_SRC */ +#define ARIZONA_SLIMCLK_SRC_MASK 0x0010 /* SLIMCLK_SRC */ +#define ARIZONA_SLIMCLK_SRC_SHIFT 4 /* SLIMCLK_SRC */ +#define ARIZONA_SLIMCLK_SRC_WIDTH 1 /* SLIMCLK_SRC */ +#define ARIZONA_FRAMER_REF_GEAR_MASK 0x000F /* FRAMER_REF_GEAR - [3:0] */ +#define ARIZONA_FRAMER_REF_GEAR_SHIFT 0 /* FRAMER_REF_GEAR - [3:0] */ +#define ARIZONA_FRAMER_REF_GEAR_WIDTH 4 /* FRAMER_REF_GEAR - [3:0] */ + +/* + * R1509 (0x5E5) - SLIMbus Rates 1 + */ +#define ARIZONA_SLIMRX2_RATE_MASK 0x7800 /* SLIMRX2_RATE - [14:11] */ +#define ARIZONA_SLIMRX2_RATE_SHIFT 11 /* SLIMRX2_RATE - [14:11] */ +#define ARIZONA_SLIMRX2_RATE_WIDTH 4 /* SLIMRX2_RATE - [14:11] */ +#define ARIZONA_SLIMRX1_RATE_MASK 0x0078 /* SLIMRX1_RATE - [6:3] */ +#define ARIZONA_SLIMRX1_RATE_SHIFT 3 /* SLIMRX1_RATE - [6:3] */ +#define ARIZONA_SLIMRX1_RATE_WIDTH 4 /* SLIMRX1_RATE - [6:3] */ + +/* + * R1510 (0x5E6) - SLIMbus Rates 2 + */ +#define ARIZONA_SLIMRX4_RATE_MASK 0x7800 /* SLIMRX4_RATE - [14:11] */ +#define ARIZONA_SLIMRX4_RATE_SHIFT 11 /* SLIMRX4_RATE - [14:11] */ +#define ARIZONA_SLIMRX4_RATE_WIDTH 4 /* SLIMRX4_RATE - [14:11] */ +#define ARIZONA_SLIMRX3_RATE_MASK 0x0078 /* SLIMRX3_RATE - [6:3] */ +#define ARIZONA_SLIMRX3_RATE_SHIFT 3 /* SLIMRX3_RATE - [6:3] */ +#define ARIZONA_SLIMRX3_RATE_WIDTH 4 /* SLIMRX3_RATE - [6:3] */ + +/* + * R1511 (0x5E7) - SLIMbus Rates 3 + */ +#define ARIZONA_SLIMRX6_RATE_MASK 0x7800 /* SLIMRX6_RATE - [14:11] */ +#define ARIZONA_SLIMRX6_RATE_SHIFT 11 /* SLIMRX6_RATE - [14:11] */ +#define ARIZONA_SLIMRX6_RATE_WIDTH 4 /* SLIMRX6_RATE - [14:11] */ +#define ARIZONA_SLIMRX5_RATE_MASK 0x0078 /* SLIMRX5_RATE - [6:3] */ +#define ARIZONA_SLIMRX5_RATE_SHIFT 3 /* SLIMRX5_RATE - [6:3] */ +#define ARIZONA_SLIMRX5_RATE_WIDTH 4 /* SLIMRX5_RATE - [6:3] */ + +/* + * R1512 (0x5E8) - SLIMbus Rates 4 + */ +#define ARIZONA_SLIMRX8_RATE_MASK 0x7800 /* SLIMRX8_RATE - [14:11] */ +#define ARIZONA_SLIMRX8_RATE_SHIFT 11 /* SLIMRX8_RATE - [14:11] */ +#define ARIZONA_SLIMRX8_RATE_WIDTH 4 /* SLIMRX8_RATE - [14:11] */ +#define ARIZONA_SLIMRX7_RATE_MASK 0x0078 /* SLIMRX7_RATE - [6:3] */ +#define ARIZONA_SLIMRX7_RATE_SHIFT 3 /* SLIMRX7_RATE - [6:3] */ +#define ARIZONA_SLIMRX7_RATE_WIDTH 4 /* SLIMRX7_RATE - [6:3] */ + +/* + * R1513 (0x5E9) - SLIMbus Rates 5 + */ +#define ARIZONA_SLIMTX2_RATE_MASK 0x7800 /* SLIMTX2_RATE - [14:11] */ +#define ARIZONA_SLIMTX2_RATE_SHIFT 11 /* SLIMTX2_RATE - [14:11] */ +#define ARIZONA_SLIMTX2_RATE_WIDTH 4 /* SLIMTX2_RATE - [14:11] */ +#define ARIZONA_SLIMTX1_RATE_MASK 0x0078 /* SLIMTX1_RATE - [6:3] */ +#define ARIZONA_SLIMTX1_RATE_SHIFT 3 /* SLIMTX1_RATE - [6:3] */ +#define ARIZONA_SLIMTX1_RATE_WIDTH 4 /* SLIMTX1_RATE - [6:3] */ + +/* + * R1514 (0x5EA) - SLIMbus Rates 6 + */ +#define ARIZONA_SLIMTX4_RATE_MASK 0x7800 /* SLIMTX4_RATE - [14:11] */ +#define ARIZONA_SLIMTX4_RATE_SHIFT 11 /* SLIMTX4_RATE - [14:11] */ +#define ARIZONA_SLIMTX4_RATE_WIDTH 4 /* SLIMTX4_RATE - [14:11] */ +#define ARIZONA_SLIMTX3_RATE_MASK 0x0078 /* SLIMTX3_RATE - [6:3] */ +#define ARIZONA_SLIMTX3_RATE_SHIFT 3 /* SLIMTX3_RATE - [6:3] */ +#define ARIZONA_SLIMTX3_RATE_WIDTH 4 /* SLIMTX3_RATE - [6:3] */ + +/* + * R1515 (0x5EB) - SLIMbus Rates 7 + */ +#define ARIZONA_SLIMTX6_RATE_MASK 0x7800 /* SLIMTX6_RATE - [14:11] */ +#define ARIZONA_SLIMTX6_RATE_SHIFT 11 /* SLIMTX6_RATE - [14:11] */ +#define ARIZONA_SLIMTX6_RATE_WIDTH 4 /* SLIMTX6_RATE - [14:11] */ +#define ARIZONA_SLIMTX5_RATE_MASK 0x0078 /* SLIMTX5_RATE - [6:3] */ +#define ARIZONA_SLIMTX5_RATE_SHIFT 3 /* SLIMTX5_RATE - [6:3] */ +#define ARIZONA_SLIMTX5_RATE_WIDTH 4 /* SLIMTX5_RATE - [6:3] */ + +/* + * R1516 (0x5EC) - SLIMbus Rates 8 + */ +#define ARIZONA_SLIMTX8_RATE_MASK 0x7800 /* SLIMTX8_RATE - [14:11] */ +#define ARIZONA_SLIMTX8_RATE_SHIFT 11 /* SLIMTX8_RATE - [14:11] */ +#define ARIZONA_SLIMTX8_RATE_WIDTH 4 /* SLIMTX8_RATE - [14:11] */ +#define ARIZONA_SLIMTX7_RATE_MASK 0x0078 /* SLIMTX7_RATE - [6:3] */ +#define ARIZONA_SLIMTX7_RATE_SHIFT 3 /* SLIMTX7_RATE - [6:3] */ +#define ARIZONA_SLIMTX7_RATE_WIDTH 4 /* SLIMTX7_RATE - [6:3] */ + +/* + * R1525 (0x5F5) - SLIMbus RX Channel Enable + */ +#define ARIZONA_SLIMRX8_ENA 0x0080 /* SLIMRX8_ENA */ +#define ARIZONA_SLIMRX8_ENA_MASK 0x0080 /* SLIMRX8_ENA */ +#define ARIZONA_SLIMRX8_ENA_SHIFT 7 /* SLIMRX8_ENA */ +#define ARIZONA_SLIMRX8_ENA_WIDTH 1 /* SLIMRX8_ENA */ +#define ARIZONA_SLIMRX7_ENA 0x0040 /* SLIMRX7_ENA */ +#define ARIZONA_SLIMRX7_ENA_MASK 0x0040 /* SLIMRX7_ENA */ +#define ARIZONA_SLIMRX7_ENA_SHIFT 6 /* SLIMRX7_ENA */ +#define ARIZONA_SLIMRX7_ENA_WIDTH 1 /* SLIMRX7_ENA */ +#define ARIZONA_SLIMRX6_ENA 0x0020 /* SLIMRX6_ENA */ +#define ARIZONA_SLIMRX6_ENA_MASK 0x0020 /* SLIMRX6_ENA */ +#define ARIZONA_SLIMRX6_ENA_SHIFT 5 /* SLIMRX6_ENA */ +#define ARIZONA_SLIMRX6_ENA_WIDTH 1 /* SLIMRX6_ENA */ +#define ARIZONA_SLIMRX5_ENA 0x0010 /* SLIMRX5_ENA */ +#define ARIZONA_SLIMRX5_ENA_MASK 0x0010 /* SLIMRX5_ENA */ +#define ARIZONA_SLIMRX5_ENA_SHIFT 4 /* SLIMRX5_ENA */ +#define ARIZONA_SLIMRX5_ENA_WIDTH 1 /* SLIMRX5_ENA */ +#define ARIZONA_SLIMRX4_ENA 0x0008 /* SLIMRX4_ENA */ +#define ARIZONA_SLIMRX4_ENA_MASK 0x0008 /* SLIMRX4_ENA */ +#define ARIZONA_SLIMRX4_ENA_SHIFT 3 /* SLIMRX4_ENA */ +#define ARIZONA_SLIMRX4_ENA_WIDTH 1 /* SLIMRX4_ENA */ +#define ARIZONA_SLIMRX3_ENA 0x0004 /* SLIMRX3_ENA */ +#define ARIZONA_SLIMRX3_ENA_MASK 0x0004 /* SLIMRX3_ENA */ +#define ARIZONA_SLIMRX3_ENA_SHIFT 2 /* SLIMRX3_ENA */ +#define ARIZONA_SLIMRX3_ENA_WIDTH 1 /* SLIMRX3_ENA */ +#define ARIZONA_SLIMRX2_ENA 0x0002 /* SLIMRX2_ENA */ +#define ARIZONA_SLIMRX2_ENA_MASK 0x0002 /* SLIMRX2_ENA */ +#define ARIZONA_SLIMRX2_ENA_SHIFT 1 /* SLIMRX2_ENA */ +#define ARIZONA_SLIMRX2_ENA_WIDTH 1 /* SLIMRX2_ENA */ +#define ARIZONA_SLIMRX1_ENA 0x0001 /* SLIMRX1_ENA */ +#define ARIZONA_SLIMRX1_ENA_MASK 0x0001 /* SLIMRX1_ENA */ +#define ARIZONA_SLIMRX1_ENA_SHIFT 0 /* SLIMRX1_ENA */ +#define ARIZONA_SLIMRX1_ENA_WIDTH 1 /* SLIMRX1_ENA */ + +/* + * R1526 (0x5F6) - SLIMbus TX Channel Enable + */ +#define ARIZONA_SLIMTX8_ENA 0x0080 /* SLIMTX8_ENA */ +#define ARIZONA_SLIMTX8_ENA_MASK 0x0080 /* SLIMTX8_ENA */ +#define ARIZONA_SLIMTX8_ENA_SHIFT 7 /* SLIMTX8_ENA */ +#define ARIZONA_SLIMTX8_ENA_WIDTH 1 /* SLIMTX8_ENA */ +#define ARIZONA_SLIMTX7_ENA 0x0040 /* SLIMTX7_ENA */ +#define ARIZONA_SLIMTX7_ENA_MASK 0x0040 /* SLIMTX7_ENA */ +#define ARIZONA_SLIMTX7_ENA_SHIFT 6 /* SLIMTX7_ENA */ +#define ARIZONA_SLIMTX7_ENA_WIDTH 1 /* SLIMTX7_ENA */ +#define ARIZONA_SLIMTX6_ENA 0x0020 /* SLIMTX6_ENA */ +#define ARIZONA_SLIMTX6_ENA_MASK 0x0020 /* SLIMTX6_ENA */ +#define ARIZONA_SLIMTX6_ENA_SHIFT 5 /* SLIMTX6_ENA */ +#define ARIZONA_SLIMTX6_ENA_WIDTH 1 /* SLIMTX6_ENA */ +#define ARIZONA_SLIMTX5_ENA 0x0010 /* SLIMTX5_ENA */ +#define ARIZONA_SLIMTX5_ENA_MASK 0x0010 /* SLIMTX5_ENA */ +#define ARIZONA_SLIMTX5_ENA_SHIFT 4 /* SLIMTX5_ENA */ +#define ARIZONA_SLIMTX5_ENA_WIDTH 1 /* SLIMTX5_ENA */ +#define ARIZONA_SLIMTX4_ENA 0x0008 /* SLIMTX4_ENA */ +#define ARIZONA_SLIMTX4_ENA_MASK 0x0008 /* SLIMTX4_ENA */ +#define ARIZONA_SLIMTX4_ENA_SHIFT 3 /* SLIMTX4_ENA */ +#define ARIZONA_SLIMTX4_ENA_WIDTH 1 /* SLIMTX4_ENA */ +#define ARIZONA_SLIMTX3_ENA 0x0004 /* SLIMTX3_ENA */ +#define ARIZONA_SLIMTX3_ENA_MASK 0x0004 /* SLIMTX3_ENA */ +#define ARIZONA_SLIMTX3_ENA_SHIFT 2 /* SLIMTX3_ENA */ +#define ARIZONA_SLIMTX3_ENA_WIDTH 1 /* SLIMTX3_ENA */ +#define ARIZONA_SLIMTX2_ENA 0x0002 /* SLIMTX2_ENA */ +#define ARIZONA_SLIMTX2_ENA_MASK 0x0002 /* SLIMTX2_ENA */ +#define ARIZONA_SLIMTX2_ENA_SHIFT 1 /* SLIMTX2_ENA */ +#define ARIZONA_SLIMTX2_ENA_WIDTH 1 /* SLIMTX2_ENA */ +#define ARIZONA_SLIMTX1_ENA 0x0001 /* SLIMTX1_ENA */ +#define ARIZONA_SLIMTX1_ENA_MASK 0x0001 /* SLIMTX1_ENA */ +#define ARIZONA_SLIMTX1_ENA_SHIFT 0 /* SLIMTX1_ENA */ +#define ARIZONA_SLIMTX1_ENA_WIDTH 1 /* SLIMTX1_ENA */ + +/* + * R1527 (0x5F7) - SLIMbus RX Port Status + */ +#define ARIZONA_SLIMRX8_PORT_STS 0x0080 /* SLIMRX8_PORT_STS */ +#define ARIZONA_SLIMRX8_PORT_STS_MASK 0x0080 /* SLIMRX8_PORT_STS */ +#define ARIZONA_SLIMRX8_PORT_STS_SHIFT 7 /* SLIMRX8_PORT_STS */ +#define ARIZONA_SLIMRX8_PORT_STS_WIDTH 1 /* SLIMRX8_PORT_STS */ +#define ARIZONA_SLIMRX7_PORT_STS 0x0040 /* SLIMRX7_PORT_STS */ +#define ARIZONA_SLIMRX7_PORT_STS_MASK 0x0040 /* SLIMRX7_PORT_STS */ +#define ARIZONA_SLIMRX7_PORT_STS_SHIFT 6 /* SLIMRX7_PORT_STS */ +#define ARIZONA_SLIMRX7_PORT_STS_WIDTH 1 /* SLIMRX7_PORT_STS */ +#define ARIZONA_SLIMRX6_PORT_STS 0x0020 /* SLIMRX6_PORT_STS */ +#define ARIZONA_SLIMRX6_PORT_STS_MASK 0x0020 /* SLIMRX6_PORT_STS */ +#define ARIZONA_SLIMRX6_PORT_STS_SHIFT 5 /* SLIMRX6_PORT_STS */ +#define ARIZONA_SLIMRX6_PORT_STS_WIDTH 1 /* SLIMRX6_PORT_STS */ +#define ARIZONA_SLIMRX5_PORT_STS 0x0010 /* SLIMRX5_PORT_STS */ +#define ARIZONA_SLIMRX5_PORT_STS_MASK 0x0010 /* SLIMRX5_PORT_STS */ +#define ARIZONA_SLIMRX5_PORT_STS_SHIFT 4 /* SLIMRX5_PORT_STS */ +#define ARIZONA_SLIMRX5_PORT_STS_WIDTH 1 /* SLIMRX5_PORT_STS */ +#define ARIZONA_SLIMRX4_PORT_STS 0x0008 /* SLIMRX4_PORT_STS */ +#define ARIZONA_SLIMRX4_PORT_STS_MASK 0x0008 /* SLIMRX4_PORT_STS */ +#define ARIZONA_SLIMRX4_PORT_STS_SHIFT 3 /* SLIMRX4_PORT_STS */ +#define ARIZONA_SLIMRX4_PORT_STS_WIDTH 1 /* SLIMRX4_PORT_STS */ +#define ARIZONA_SLIMRX3_PORT_STS 0x0004 /* SLIMRX3_PORT_STS */ +#define ARIZONA_SLIMRX3_PORT_STS_MASK 0x0004 /* SLIMRX3_PORT_STS */ +#define ARIZONA_SLIMRX3_PORT_STS_SHIFT 2 /* SLIMRX3_PORT_STS */ +#define ARIZONA_SLIMRX3_PORT_STS_WIDTH 1 /* SLIMRX3_PORT_STS */ +#define ARIZONA_SLIMRX2_PORT_STS 0x0002 /* SLIMRX2_PORT_STS */ +#define ARIZONA_SLIMRX2_PORT_STS_MASK 0x0002 /* SLIMRX2_PORT_STS */ +#define ARIZONA_SLIMRX2_PORT_STS_SHIFT 1 /* SLIMRX2_PORT_STS */ +#define ARIZONA_SLIMRX2_PORT_STS_WIDTH 1 /* SLIMRX2_PORT_STS */ +#define ARIZONA_SLIMRX1_PORT_STS 0x0001 /* SLIMRX1_PORT_STS */ +#define ARIZONA_SLIMRX1_PORT_STS_MASK 0x0001 /* SLIMRX1_PORT_STS */ +#define ARIZONA_SLIMRX1_PORT_STS_SHIFT 0 /* SLIMRX1_PORT_STS */ +#define ARIZONA_SLIMRX1_PORT_STS_WIDTH 1 /* SLIMRX1_PORT_STS */ + +/* + * R1528 (0x5F8) - SLIMbus TX Port Status + */ +#define ARIZONA_SLIMTX8_PORT_STS 0x0080 /* SLIMTX8_PORT_STS */ +#define ARIZONA_SLIMTX8_PORT_STS_MASK 0x0080 /* SLIMTX8_PORT_STS */ +#define ARIZONA_SLIMTX8_PORT_STS_SHIFT 7 /* SLIMTX8_PORT_STS */ +#define ARIZONA_SLIMTX8_PORT_STS_WIDTH 1 /* SLIMTX8_PORT_STS */ +#define ARIZONA_SLIMTX7_PORT_STS 0x0040 /* SLIMTX7_PORT_STS */ +#define ARIZONA_SLIMTX7_PORT_STS_MASK 0x0040 /* SLIMTX7_PORT_STS */ +#define ARIZONA_SLIMTX7_PORT_STS_SHIFT 6 /* SLIMTX7_PORT_STS */ +#define ARIZONA_SLIMTX7_PORT_STS_WIDTH 1 /* SLIMTX7_PORT_STS */ +#define ARIZONA_SLIMTX6_PORT_STS 0x0020 /* SLIMTX6_PORT_STS */ +#define ARIZONA_SLIMTX6_PORT_STS_MASK 0x0020 /* SLIMTX6_PORT_STS */ +#define ARIZONA_SLIMTX6_PORT_STS_SHIFT 5 /* SLIMTX6_PORT_STS */ +#define ARIZONA_SLIMTX6_PORT_STS_WIDTH 1 /* SLIMTX6_PORT_STS */ +#define ARIZONA_SLIMTX5_PORT_STS 0x0010 /* SLIMTX5_PORT_STS */ +#define ARIZONA_SLIMTX5_PORT_STS_MASK 0x0010 /* SLIMTX5_PORT_STS */ +#define ARIZONA_SLIMTX5_PORT_STS_SHIFT 4 /* SLIMTX5_PORT_STS */ +#define ARIZONA_SLIMTX5_PORT_STS_WIDTH 1 /* SLIMTX5_PORT_STS */ +#define ARIZONA_SLIMTX4_PORT_STS 0x0008 /* SLIMTX4_PORT_STS */ +#define ARIZONA_SLIMTX4_PORT_STS_MASK 0x0008 /* SLIMTX4_PORT_STS */ +#define ARIZONA_SLIMTX4_PORT_STS_SHIFT 3 /* SLIMTX4_PORT_STS */ +#define ARIZONA_SLIMTX4_PORT_STS_WIDTH 1 /* SLIMTX4_PORT_STS */ +#define ARIZONA_SLIMTX3_PORT_STS 0x0004 /* SLIMTX3_PORT_STS */ +#define ARIZONA_SLIMTX3_PORT_STS_MASK 0x0004 /* SLIMTX3_PORT_STS */ +#define ARIZONA_SLIMTX3_PORT_STS_SHIFT 2 /* SLIMTX3_PORT_STS */ +#define ARIZONA_SLIMTX3_PORT_STS_WIDTH 1 /* SLIMTX3_PORT_STS */ +#define ARIZONA_SLIMTX2_PORT_STS 0x0002 /* SLIMTX2_PORT_STS */ +#define ARIZONA_SLIMTX2_PORT_STS_MASK 0x0002 /* SLIMTX2_PORT_STS */ +#define ARIZONA_SLIMTX2_PORT_STS_SHIFT 1 /* SLIMTX2_PORT_STS */ +#define ARIZONA_SLIMTX2_PORT_STS_WIDTH 1 /* SLIMTX2_PORT_STS */ +#define ARIZONA_SLIMTX1_PORT_STS 0x0001 /* SLIMTX1_PORT_STS */ +#define ARIZONA_SLIMTX1_PORT_STS_MASK 0x0001 /* SLIMTX1_PORT_STS */ +#define ARIZONA_SLIMTX1_PORT_STS_SHIFT 0 /* SLIMTX1_PORT_STS */ +#define ARIZONA_SLIMTX1_PORT_STS_WIDTH 1 /* SLIMTX1_PORT_STS */ + +/* + * R3087 (0xC0F) - IRQ CTRL 1 + */ +#define ARIZONA_IRQ_POL 0x0400 /* IRQ_POL */ +#define ARIZONA_IRQ_POL_MASK 0x0400 /* IRQ_POL */ +#define ARIZONA_IRQ_POL_SHIFT 10 /* IRQ_POL */ +#define ARIZONA_IRQ_POL_WIDTH 1 /* IRQ_POL */ +#define ARIZONA_IRQ_OP_CFG 0x0200 /* IRQ_OP_CFG */ +#define ARIZONA_IRQ_OP_CFG_MASK 0x0200 /* IRQ_OP_CFG */ +#define ARIZONA_IRQ_OP_CFG_SHIFT 9 /* IRQ_OP_CFG */ +#define ARIZONA_IRQ_OP_CFG_WIDTH 1 /* IRQ_OP_CFG */ + +/* + * R3088 (0xC10) - GPIO Debounce Config + */ +#define ARIZONA_GP_DBTIME_MASK 0xF000 /* GP_DBTIME - [15:12] */ +#define ARIZONA_GP_DBTIME_SHIFT 12 /* GP_DBTIME - [15:12] */ +#define ARIZONA_GP_DBTIME_WIDTH 4 /* GP_DBTIME - [15:12] */ + +/* + * R3104 (0xC20) - Misc Pad Ctrl 1 + */ +#define ARIZONA_LDO1ENA_PD 0x8000 /* LDO1ENA_PD */ +#define ARIZONA_LDO1ENA_PD_MASK 0x8000 /* LDO1ENA_PD */ +#define ARIZONA_LDO1ENA_PD_SHIFT 15 /* LDO1ENA_PD */ +#define ARIZONA_LDO1ENA_PD_WIDTH 1 /* LDO1ENA_PD */ +#define ARIZONA_MCLK2_PD 0x2000 /* MCLK2_PD */ +#define ARIZONA_MCLK2_PD_MASK 0x2000 /* MCLK2_PD */ +#define ARIZONA_MCLK2_PD_SHIFT 13 /* MCLK2_PD */ +#define ARIZONA_MCLK2_PD_WIDTH 1 /* MCLK2_PD */ +#define ARIZONA_RSTB_PU 0x0002 /* RSTB_PU */ +#define ARIZONA_RSTB_PU_MASK 0x0002 /* RSTB_PU */ +#define ARIZONA_RSTB_PU_SHIFT 1 /* RSTB_PU */ +#define ARIZONA_RSTB_PU_WIDTH 1 /* RSTB_PU */ + +/* + * R3105 (0xC21) - Misc Pad Ctrl 2 + */ +#define ARIZONA_MCLK1_PD 0x1000 /* MCLK1_PD */ +#define ARIZONA_MCLK1_PD_MASK 0x1000 /* MCLK1_PD */ +#define ARIZONA_MCLK1_PD_SHIFT 12 /* MCLK1_PD */ +#define ARIZONA_MCLK1_PD_WIDTH 1 /* MCLK1_PD */ +#define ARIZONA_MICD_PD 0x0100 /* MICD_PD */ +#define ARIZONA_MICD_PD_MASK 0x0100 /* MICD_PD */ +#define ARIZONA_MICD_PD_SHIFT 8 /* MICD_PD */ +#define ARIZONA_MICD_PD_WIDTH 1 /* MICD_PD */ +#define ARIZONA_ADDR_PD 0x0001 /* ADDR_PD */ +#define ARIZONA_ADDR_PD_MASK 0x0001 /* ADDR_PD */ +#define ARIZONA_ADDR_PD_SHIFT 0 /* ADDR_PD */ +#define ARIZONA_ADDR_PD_WIDTH 1 /* ADDR_PD */ + +/* + * R3106 (0xC22) - Misc Pad Ctrl 3 + */ +#define ARIZONA_DMICDAT4_PD 0x0008 /* DMICDAT4_PD */ +#define ARIZONA_DMICDAT4_PD_MASK 0x0008 /* DMICDAT4_PD */ +#define ARIZONA_DMICDAT4_PD_SHIFT 3 /* DMICDAT4_PD */ +#define ARIZONA_DMICDAT4_PD_WIDTH 1 /* DMICDAT4_PD */ +#define ARIZONA_DMICDAT3_PD 0x0004 /* DMICDAT3_PD */ +#define ARIZONA_DMICDAT3_PD_MASK 0x0004 /* DMICDAT3_PD */ +#define ARIZONA_DMICDAT3_PD_SHIFT 2 /* DMICDAT3_PD */ +#define ARIZONA_DMICDAT3_PD_WIDTH 1 /* DMICDAT3_PD */ +#define ARIZONA_DMICDAT2_PD 0x0002 /* DMICDAT2_PD */ +#define ARIZONA_DMICDAT2_PD_MASK 0x0002 /* DMICDAT2_PD */ +#define ARIZONA_DMICDAT2_PD_SHIFT 1 /* DMICDAT2_PD */ +#define ARIZONA_DMICDAT2_PD_WIDTH 1 /* DMICDAT2_PD */ +#define ARIZONA_DMICDAT1_PD 0x0001 /* DMICDAT1_PD */ +#define ARIZONA_DMICDAT1_PD_MASK 0x0001 /* DMICDAT1_PD */ +#define ARIZONA_DMICDAT1_PD_SHIFT 0 /* DMICDAT1_PD */ +#define ARIZONA_DMICDAT1_PD_WIDTH 1 /* DMICDAT1_PD */ + +/* + * R3107 (0xC23) - Misc Pad Ctrl 4 + */ +#define ARIZONA_AIF1RXLRCLK_PU 0x0020 /* AIF1RXLRCLK_PU */ +#define ARIZONA_AIF1RXLRCLK_PU_MASK 0x0020 /* AIF1RXLRCLK_PU */ +#define ARIZONA_AIF1RXLRCLK_PU_SHIFT 5 /* AIF1RXLRCLK_PU */ +#define ARIZONA_AIF1RXLRCLK_PU_WIDTH 1 /* AIF1RXLRCLK_PU */ +#define ARIZONA_AIF1RXLRCLK_PD 0x0010 /* AIF1RXLRCLK_PD */ +#define ARIZONA_AIF1RXLRCLK_PD_MASK 0x0010 /* AIF1RXLRCLK_PD */ +#define ARIZONA_AIF1RXLRCLK_PD_SHIFT 4 /* AIF1RXLRCLK_PD */ +#define ARIZONA_AIF1RXLRCLK_PD_WIDTH 1 /* AIF1RXLRCLK_PD */ +#define ARIZONA_AIF1BCLK_PU 0x0008 /* AIF1BCLK_PU */ +#define ARIZONA_AIF1BCLK_PU_MASK 0x0008 /* AIF1BCLK_PU */ +#define ARIZONA_AIF1BCLK_PU_SHIFT 3 /* AIF1BCLK_PU */ +#define ARIZONA_AIF1BCLK_PU_WIDTH 1 /* AIF1BCLK_PU */ +#define ARIZONA_AIF1BCLK_PD 0x0004 /* AIF1BCLK_PD */ +#define ARIZONA_AIF1BCLK_PD_MASK 0x0004 /* AIF1BCLK_PD */ +#define ARIZONA_AIF1BCLK_PD_SHIFT 2 /* AIF1BCLK_PD */ +#define ARIZONA_AIF1BCLK_PD_WIDTH 1 /* AIF1BCLK_PD */ +#define ARIZONA_AIF1RXDAT_PU 0x0002 /* AIF1RXDAT_PU */ +#define ARIZONA_AIF1RXDAT_PU_MASK 0x0002 /* AIF1RXDAT_PU */ +#define ARIZONA_AIF1RXDAT_PU_SHIFT 1 /* AIF1RXDAT_PU */ +#define ARIZONA_AIF1RXDAT_PU_WIDTH 1 /* AIF1RXDAT_PU */ +#define ARIZONA_AIF1RXDAT_PD 0x0001 /* AIF1RXDAT_PD */ +#define ARIZONA_AIF1RXDAT_PD_MASK 0x0001 /* AIF1RXDAT_PD */ +#define ARIZONA_AIF1RXDAT_PD_SHIFT 0 /* AIF1RXDAT_PD */ +#define ARIZONA_AIF1RXDAT_PD_WIDTH 1 /* AIF1RXDAT_PD */ + +/* + * R3108 (0xC24) - Misc Pad Ctrl 5 + */ +#define ARIZONA_AIF2RXLRCLK_PU 0x0020 /* AIF2RXLRCLK_PU */ +#define ARIZONA_AIF2RXLRCLK_PU_MASK 0x0020 /* AIF2RXLRCLK_PU */ +#define ARIZONA_AIF2RXLRCLK_PU_SHIFT 5 /* AIF2RXLRCLK_PU */ +#define ARIZONA_AIF2RXLRCLK_PU_WIDTH 1 /* AIF2RXLRCLK_PU */ +#define ARIZONA_AIF2RXLRCLK_PD 0x0010 /* AIF2RXLRCLK_PD */ +#define ARIZONA_AIF2RXLRCLK_PD_MASK 0x0010 /* AIF2RXLRCLK_PD */ +#define ARIZONA_AIF2RXLRCLK_PD_SHIFT 4 /* AIF2RXLRCLK_PD */ +#define ARIZONA_AIF2RXLRCLK_PD_WIDTH 1 /* AIF2RXLRCLK_PD */ +#define ARIZONA_AIF2BCLK_PU 0x0008 /* AIF2BCLK_PU */ +#define ARIZONA_AIF2BCLK_PU_MASK 0x0008 /* AIF2BCLK_PU */ +#define ARIZONA_AIF2BCLK_PU_SHIFT 3 /* AIF2BCLK_PU */ +#define ARIZONA_AIF2BCLK_PU_WIDTH 1 /* AIF2BCLK_PU */ +#define ARIZONA_AIF2BCLK_PD 0x0004 /* AIF2BCLK_PD */ +#define ARIZONA_AIF2BCLK_PD_MASK 0x0004 /* AIF2BCLK_PD */ +#define ARIZONA_AIF2BCLK_PD_SHIFT 2 /* AIF2BCLK_PD */ +#define ARIZONA_AIF2BCLK_PD_WIDTH 1 /* AIF2BCLK_PD */ +#define ARIZONA_AIF2RXDAT_PU 0x0002 /* AIF2RXDAT_PU */ +#define ARIZONA_AIF2RXDAT_PU_MASK 0x0002 /* AIF2RXDAT_PU */ +#define ARIZONA_AIF2RXDAT_PU_SHIFT 1 /* AIF2RXDAT_PU */ +#define ARIZONA_AIF2RXDAT_PU_WIDTH 1 /* AIF2RXDAT_PU */ +#define ARIZONA_AIF2RXDAT_PD 0x0001 /* AIF2RXDAT_PD */ +#define ARIZONA_AIF2RXDAT_PD_MASK 0x0001 /* AIF2RXDAT_PD */ +#define ARIZONA_AIF2RXDAT_PD_SHIFT 0 /* AIF2RXDAT_PD */ +#define ARIZONA_AIF2RXDAT_PD_WIDTH 1 /* AIF2RXDAT_PD */ + +/* + * R3109 (0xC25) - Misc Pad Ctrl 6 + */ +#define ARIZONA_AIF3RXLRCLK_PU 0x0020 /* AIF3RXLRCLK_PU */ +#define ARIZONA_AIF3RXLRCLK_PU_MASK 0x0020 /* AIF3RXLRCLK_PU */ +#define ARIZONA_AIF3RXLRCLK_PU_SHIFT 5 /* AIF3RXLRCLK_PU */ +#define ARIZONA_AIF3RXLRCLK_PU_WIDTH 1 /* AIF3RXLRCLK_PU */ +#define ARIZONA_AIF3RXLRCLK_PD 0x0010 /* AIF3RXLRCLK_PD */ +#define ARIZONA_AIF3RXLRCLK_PD_MASK 0x0010 /* AIF3RXLRCLK_PD */ +#define ARIZONA_AIF3RXLRCLK_PD_SHIFT 4 /* AIF3RXLRCLK_PD */ +#define ARIZONA_AIF3RXLRCLK_PD_WIDTH 1 /* AIF3RXLRCLK_PD */ +#define ARIZONA_AIF3BCLK_PU 0x0008 /* AIF3BCLK_PU */ +#define ARIZONA_AIF3BCLK_PU_MASK 0x0008 /* AIF3BCLK_PU */ +#define ARIZONA_AIF3BCLK_PU_SHIFT 3 /* AIF3BCLK_PU */ +#define ARIZONA_AIF3BCLK_PU_WIDTH 1 /* AIF3BCLK_PU */ +#define ARIZONA_AIF3BCLK_PD 0x0004 /* AIF3BCLK_PD */ +#define ARIZONA_AIF3BCLK_PD_MASK 0x0004 /* AIF3BCLK_PD */ +#define ARIZONA_AIF3BCLK_PD_SHIFT 2 /* AIF3BCLK_PD */ +#define ARIZONA_AIF3BCLK_PD_WIDTH 1 /* AIF3BCLK_PD */ +#define ARIZONA_AIF3RXDAT_PU 0x0002 /* AIF3RXDAT_PU */ +#define ARIZONA_AIF3RXDAT_PU_MASK 0x0002 /* AIF3RXDAT_PU */ +#define ARIZONA_AIF3RXDAT_PU_SHIFT 1 /* AIF3RXDAT_PU */ +#define ARIZONA_AIF3RXDAT_PU_WIDTH 1 /* AIF3RXDAT_PU */ +#define ARIZONA_AIF3RXDAT_PD 0x0001 /* AIF3RXDAT_PD */ +#define ARIZONA_AIF3RXDAT_PD_MASK 0x0001 /* AIF3RXDAT_PD */ +#define ARIZONA_AIF3RXDAT_PD_SHIFT 0 /* AIF3RXDAT_PD */ +#define ARIZONA_AIF3RXDAT_PD_WIDTH 1 /* AIF3RXDAT_PD */ + +/* + * R3328 (0xD00) - Interrupt Status 1 + */ +#define ARIZONA_GP4_EINT1 0x0008 /* GP4_EINT1 */ +#define ARIZONA_GP4_EINT1_MASK 0x0008 /* GP4_EINT1 */ +#define ARIZONA_GP4_EINT1_SHIFT 3 /* GP4_EINT1 */ +#define ARIZONA_GP4_EINT1_WIDTH 1 /* GP4_EINT1 */ +#define ARIZONA_GP3_EINT1 0x0004 /* GP3_EINT1 */ +#define ARIZONA_GP3_EINT1_MASK 0x0004 /* GP3_EINT1 */ +#define ARIZONA_GP3_EINT1_SHIFT 2 /* GP3_EINT1 */ +#define ARIZONA_GP3_EINT1_WIDTH 1 /* GP3_EINT1 */ +#define ARIZONA_GP2_EINT1 0x0002 /* GP2_EINT1 */ +#define ARIZONA_GP2_EINT1_MASK 0x0002 /* GP2_EINT1 */ +#define ARIZONA_GP2_EINT1_SHIFT 1 /* GP2_EINT1 */ +#define ARIZONA_GP2_EINT1_WIDTH 1 /* GP2_EINT1 */ +#define ARIZONA_GP1_EINT1 0x0001 /* GP1_EINT1 */ +#define ARIZONA_GP1_EINT1_MASK 0x0001 /* GP1_EINT1 */ +#define ARIZONA_GP1_EINT1_SHIFT 0 /* GP1_EINT1 */ +#define ARIZONA_GP1_EINT1_WIDTH 1 /* GP1_EINT1 */ + +/* + * R3329 (0xD01) - Interrupt Status 2 + */ +#define ARIZONA_DSP1_RAM_RDY_EINT1 0x0100 /* DSP1_RAM_RDY_EINT1 */ +#define ARIZONA_DSP1_RAM_RDY_EINT1_MASK 0x0100 /* DSP1_RAM_RDY_EINT1 */ +#define ARIZONA_DSP1_RAM_RDY_EINT1_SHIFT 8 /* DSP1_RAM_RDY_EINT1 */ +#define ARIZONA_DSP1_RAM_RDY_EINT1_WIDTH 1 /* DSP1_RAM_RDY_EINT1 */ +#define ARIZONA_DSP_IRQ2_EINT1 0x0002 /* DSP_IRQ2_EINT1 */ +#define ARIZONA_DSP_IRQ2_EINT1_MASK 0x0002 /* DSP_IRQ2_EINT1 */ +#define ARIZONA_DSP_IRQ2_EINT1_SHIFT 1 /* DSP_IRQ2_EINT1 */ +#define ARIZONA_DSP_IRQ2_EINT1_WIDTH 1 /* DSP_IRQ2_EINT1 */ +#define ARIZONA_DSP_IRQ1_EINT1 0x0001 /* DSP_IRQ1_EINT1 */ +#define ARIZONA_DSP_IRQ1_EINT1_MASK 0x0001 /* DSP_IRQ1_EINT1 */ +#define ARIZONA_DSP_IRQ1_EINT1_SHIFT 0 /* DSP_IRQ1_EINT1 */ +#define ARIZONA_DSP_IRQ1_EINT1_WIDTH 1 /* DSP_IRQ1_EINT1 */ + +/* + * R3330 (0xD02) - Interrupt Status 3 + */ +#define ARIZONA_SPK_SHUTDOWN_WARN_EINT1 0x8000 /* SPK_SHUTDOWN_WARN_EINT1 */ +#define ARIZONA_SPK_SHUTDOWN_WARN_EINT1_MASK 0x8000 /* SPK_SHUTDOWN_WARN_EINT1 */ +#define ARIZONA_SPK_SHUTDOWN_WARN_EINT1_SHIFT 15 /* SPK_SHUTDOWN_WARN_EINT1 */ +#define ARIZONA_SPK_SHUTDOWN_WARN_EINT1_WIDTH 1 /* SPK_SHUTDOWN_WARN_EINT1 */ +#define ARIZONA_SPK_SHUTDOWN_EINT1 0x4000 /* SPK_SHUTDOWN_EINT1 */ +#define ARIZONA_SPK_SHUTDOWN_EINT1_MASK 0x4000 /* SPK_SHUTDOWN_EINT1 */ +#define ARIZONA_SPK_SHUTDOWN_EINT1_SHIFT 14 /* SPK_SHUTDOWN_EINT1 */ +#define ARIZONA_SPK_SHUTDOWN_EINT1_WIDTH 1 /* SPK_SHUTDOWN_EINT1 */ +#define ARIZONA_HPDET_EINT1 0x2000 /* HPDET_EINT1 */ +#define ARIZONA_HPDET_EINT1_MASK 0x2000 /* HPDET_EINT1 */ +#define ARIZONA_HPDET_EINT1_SHIFT 13 /* HPDET_EINT1 */ +#define ARIZONA_HPDET_EINT1_WIDTH 1 /* HPDET_EINT1 */ +#define ARIZONA_MICDET_EINT1 0x1000 /* MICDET_EINT1 */ +#define ARIZONA_MICDET_EINT1_MASK 0x1000 /* MICDET_EINT1 */ +#define ARIZONA_MICDET_EINT1_SHIFT 12 /* MICDET_EINT1 */ +#define ARIZONA_MICDET_EINT1_WIDTH 1 /* MICDET_EINT1 */ +#define ARIZONA_WSEQ_DONE_EINT1 0x0800 /* WSEQ_DONE_EINT1 */ +#define ARIZONA_WSEQ_DONE_EINT1_MASK 0x0800 /* WSEQ_DONE_EINT1 */ +#define ARIZONA_WSEQ_DONE_EINT1_SHIFT 11 /* WSEQ_DONE_EINT1 */ +#define ARIZONA_WSEQ_DONE_EINT1_WIDTH 1 /* WSEQ_DONE_EINT1 */ +#define ARIZONA_DRC2_SIG_DET_EINT1 0x0400 /* DRC2_SIG_DET_EINT1 */ +#define ARIZONA_DRC2_SIG_DET_EINT1_MASK 0x0400 /* DRC2_SIG_DET_EINT1 */ +#define ARIZONA_DRC2_SIG_DET_EINT1_SHIFT 10 /* DRC2_SIG_DET_EINT1 */ +#define ARIZONA_DRC2_SIG_DET_EINT1_WIDTH 1 /* DRC2_SIG_DET_EINT1 */ +#define ARIZONA_DRC1_SIG_DET_EINT1 0x0200 /* DRC1_SIG_DET_EINT1 */ +#define ARIZONA_DRC1_SIG_DET_EINT1_MASK 0x0200 /* DRC1_SIG_DET_EINT1 */ +#define ARIZONA_DRC1_SIG_DET_EINT1_SHIFT 9 /* DRC1_SIG_DET_EINT1 */ +#define ARIZONA_DRC1_SIG_DET_EINT1_WIDTH 1 /* DRC1_SIG_DET_EINT1 */ +#define ARIZONA_ASRC2_LOCK_EINT1 0x0100 /* ASRC2_LOCK_EINT1 */ +#define ARIZONA_ASRC2_LOCK_EINT1_MASK 0x0100 /* ASRC2_LOCK_EINT1 */ +#define ARIZONA_ASRC2_LOCK_EINT1_SHIFT 8 /* ASRC2_LOCK_EINT1 */ +#define ARIZONA_ASRC2_LOCK_EINT1_WIDTH 1 /* ASRC2_LOCK_EINT1 */ +#define ARIZONA_ASRC1_LOCK_EINT1 0x0080 /* ASRC1_LOCK_EINT1 */ +#define ARIZONA_ASRC1_LOCK_EINT1_MASK 0x0080 /* ASRC1_LOCK_EINT1 */ +#define ARIZONA_ASRC1_LOCK_EINT1_SHIFT 7 /* ASRC1_LOCK_EINT1 */ +#define ARIZONA_ASRC1_LOCK_EINT1_WIDTH 1 /* ASRC1_LOCK_EINT1 */ +#define ARIZONA_UNDERCLOCKED_EINT1 0x0040 /* UNDERCLOCKED_EINT1 */ +#define ARIZONA_UNDERCLOCKED_EINT1_MASK 0x0040 /* UNDERCLOCKED_EINT1 */ +#define ARIZONA_UNDERCLOCKED_EINT1_SHIFT 6 /* UNDERCLOCKED_EINT1 */ +#define ARIZONA_UNDERCLOCKED_EINT1_WIDTH 1 /* UNDERCLOCKED_EINT1 */ +#define ARIZONA_OVERCLOCKED_EINT1 0x0020 /* OVERCLOCKED_EINT1 */ +#define ARIZONA_OVERCLOCKED_EINT1_MASK 0x0020 /* OVERCLOCKED_EINT1 */ +#define ARIZONA_OVERCLOCKED_EINT1_SHIFT 5 /* OVERCLOCKED_EINT1 */ +#define ARIZONA_OVERCLOCKED_EINT1_WIDTH 1 /* OVERCLOCKED_EINT1 */ +#define ARIZONA_FLL2_LOCK_EINT1 0x0008 /* FLL2_LOCK_EINT1 */ +#define ARIZONA_FLL2_LOCK_EINT1_MASK 0x0008 /* FLL2_LOCK_EINT1 */ +#define ARIZONA_FLL2_LOCK_EINT1_SHIFT 3 /* FLL2_LOCK_EINT1 */ +#define ARIZONA_FLL2_LOCK_EINT1_WIDTH 1 /* FLL2_LOCK_EINT1 */ +#define ARIZONA_FLL1_LOCK_EINT1 0x0004 /* FLL1_LOCK_EINT1 */ +#define ARIZONA_FLL1_LOCK_EINT1_MASK 0x0004 /* FLL1_LOCK_EINT1 */ +#define ARIZONA_FLL1_LOCK_EINT1_SHIFT 2 /* FLL1_LOCK_EINT1 */ +#define ARIZONA_FLL1_LOCK_EINT1_WIDTH 1 /* FLL1_LOCK_EINT1 */ +#define ARIZONA_CLKGEN_ERR_EINT1 0x0002 /* CLKGEN_ERR_EINT1 */ +#define ARIZONA_CLKGEN_ERR_EINT1_MASK 0x0002 /* CLKGEN_ERR_EINT1 */ +#define ARIZONA_CLKGEN_ERR_EINT1_SHIFT 1 /* CLKGEN_ERR_EINT1 */ +#define ARIZONA_CLKGEN_ERR_EINT1_WIDTH 1 /* CLKGEN_ERR_EINT1 */ +#define ARIZONA_CLKGEN_ERR_ASYNC_EINT1 0x0001 /* CLKGEN_ERR_ASYNC_EINT1 */ +#define ARIZONA_CLKGEN_ERR_ASYNC_EINT1_MASK 0x0001 /* CLKGEN_ERR_ASYNC_EINT1 */ +#define ARIZONA_CLKGEN_ERR_ASYNC_EINT1_SHIFT 0 /* CLKGEN_ERR_ASYNC_EINT1 */ +#define ARIZONA_CLKGEN_ERR_ASYNC_EINT1_WIDTH 1 /* CLKGEN_ERR_ASYNC_EINT1 */ + +/* + * R3331 (0xD03) - Interrupt Status 4 + */ +#define ARIZONA_ASRC_CFG_ERR_EINT1 0x8000 /* ASRC_CFG_ERR_EINT1 */ +#define ARIZONA_ASRC_CFG_ERR_EINT1_MASK 0x8000 /* ASRC_CFG_ERR_EINT1 */ +#define ARIZONA_ASRC_CFG_ERR_EINT1_SHIFT 15 /* ASRC_CFG_ERR_EINT1 */ +#define ARIZONA_ASRC_CFG_ERR_EINT1_WIDTH 1 /* ASRC_CFG_ERR_EINT1 */ +#define ARIZONA_AIF3_ERR_EINT1 0x4000 /* AIF3_ERR_EINT1 */ +#define ARIZONA_AIF3_ERR_EINT1_MASK 0x4000 /* AIF3_ERR_EINT1 */ +#define ARIZONA_AIF3_ERR_EINT1_SHIFT 14 /* AIF3_ERR_EINT1 */ +#define ARIZONA_AIF3_ERR_EINT1_WIDTH 1 /* AIF3_ERR_EINT1 */ +#define ARIZONA_AIF2_ERR_EINT1 0x2000 /* AIF2_ERR_EINT1 */ +#define ARIZONA_AIF2_ERR_EINT1_MASK 0x2000 /* AIF2_ERR_EINT1 */ +#define ARIZONA_AIF2_ERR_EINT1_SHIFT 13 /* AIF2_ERR_EINT1 */ +#define ARIZONA_AIF2_ERR_EINT1_WIDTH 1 /* AIF2_ERR_EINT1 */ +#define ARIZONA_AIF1_ERR_EINT1 0x1000 /* AIF1_ERR_EINT1 */ +#define ARIZONA_AIF1_ERR_EINT1_MASK 0x1000 /* AIF1_ERR_EINT1 */ +#define ARIZONA_AIF1_ERR_EINT1_SHIFT 12 /* AIF1_ERR_EINT1 */ +#define ARIZONA_AIF1_ERR_EINT1_WIDTH 1 /* AIF1_ERR_EINT1 */ +#define ARIZONA_CTRLIF_ERR_EINT1 0x0800 /* CTRLIF_ERR_EINT1 */ +#define ARIZONA_CTRLIF_ERR_EINT1_MASK 0x0800 /* CTRLIF_ERR_EINT1 */ +#define ARIZONA_CTRLIF_ERR_EINT1_SHIFT 11 /* CTRLIF_ERR_EINT1 */ +#define ARIZONA_CTRLIF_ERR_EINT1_WIDTH 1 /* CTRLIF_ERR_EINT1 */ +#define ARIZONA_MIXER_DROPPED_SAMPLE_EINT1 0x0400 /* MIXER_DROPPED_SAMPLE_EINT1 */ +#define ARIZONA_MIXER_DROPPED_SAMPLE_EINT1_MASK 0x0400 /* MIXER_DROPPED_SAMPLE_EINT1 */ +#define ARIZONA_MIXER_DROPPED_SAMPLE_EINT1_SHIFT 10 /* MIXER_DROPPED_SAMPLE_EINT1 */ +#define ARIZONA_MIXER_DROPPED_SAMPLE_EINT1_WIDTH 1 /* MIXER_DROPPED_SAMPLE_EINT1 */ +#define ARIZONA_ASYNC_CLK_ENA_LOW_EINT1 0x0200 /* ASYNC_CLK_ENA_LOW_EINT1 */ +#define ARIZONA_ASYNC_CLK_ENA_LOW_EINT1_MASK 0x0200 /* ASYNC_CLK_ENA_LOW_EINT1 */ +#define ARIZONA_ASYNC_CLK_ENA_LOW_EINT1_SHIFT 9 /* ASYNC_CLK_ENA_LOW_EINT1 */ +#define ARIZONA_ASYNC_CLK_ENA_LOW_EINT1_WIDTH 1 /* ASYNC_CLK_ENA_LOW_EINT1 */ +#define ARIZONA_SYSCLK_ENA_LOW_EINT1 0x0100 /* SYSCLK_ENA_LOW_EINT1 */ +#define ARIZONA_SYSCLK_ENA_LOW_EINT1_MASK 0x0100 /* SYSCLK_ENA_LOW_EINT1 */ +#define ARIZONA_SYSCLK_ENA_LOW_EINT1_SHIFT 8 /* SYSCLK_ENA_LOW_EINT1 */ +#define ARIZONA_SYSCLK_ENA_LOW_EINT1_WIDTH 1 /* SYSCLK_ENA_LOW_EINT1 */ +#define ARIZONA_ISRC1_CFG_ERR_EINT1 0x0080 /* ISRC1_CFG_ERR_EINT1 */ +#define ARIZONA_ISRC1_CFG_ERR_EINT1_MASK 0x0080 /* ISRC1_CFG_ERR_EINT1 */ +#define ARIZONA_ISRC1_CFG_ERR_EINT1_SHIFT 7 /* ISRC1_CFG_ERR_EINT1 */ +#define ARIZONA_ISRC1_CFG_ERR_EINT1_WIDTH 1 /* ISRC1_CFG_ERR_EINT1 */ +#define ARIZONA_ISRC2_CFG_ERR_EINT1 0x0040 /* ISRC2_CFG_ERR_EINT1 */ +#define ARIZONA_ISRC2_CFG_ERR_EINT1_MASK 0x0040 /* ISRC2_CFG_ERR_EINT1 */ +#define ARIZONA_ISRC2_CFG_ERR_EINT1_SHIFT 6 /* ISRC2_CFG_ERR_EINT1 */ +#define ARIZONA_ISRC2_CFG_ERR_EINT1_WIDTH 1 /* ISRC2_CFG_ERR_EINT1 */ + +/* + * R3332 (0xD04) - Interrupt Status 5 + */ +#define ARIZONA_BOOT_DONE_EINT1 0x0100 /* BOOT_DONE_EINT1 */ +#define ARIZONA_BOOT_DONE_EINT1_MASK 0x0100 /* BOOT_DONE_EINT1 */ +#define ARIZONA_BOOT_DONE_EINT1_SHIFT 8 /* BOOT_DONE_EINT1 */ +#define ARIZONA_BOOT_DONE_EINT1_WIDTH 1 /* BOOT_DONE_EINT1 */ +#define ARIZONA_DCS_DAC_DONE_EINT1 0x0080 /* DCS_DAC_DONE_EINT1 */ +#define ARIZONA_DCS_DAC_DONE_EINT1_MASK 0x0080 /* DCS_DAC_DONE_EINT1 */ +#define ARIZONA_DCS_DAC_DONE_EINT1_SHIFT 7 /* DCS_DAC_DONE_EINT1 */ +#define ARIZONA_DCS_DAC_DONE_EINT1_WIDTH 1 /* DCS_DAC_DONE_EINT1 */ +#define ARIZONA_DCS_HP_DONE_EINT1 0x0040 /* DCS_HP_DONE_EINT1 */ +#define ARIZONA_DCS_HP_DONE_EINT1_MASK 0x0040 /* DCS_HP_DONE_EINT1 */ +#define ARIZONA_DCS_HP_DONE_EINT1_SHIFT 6 /* DCS_HP_DONE_EINT1 */ +#define ARIZONA_DCS_HP_DONE_EINT1_WIDTH 1 /* DCS_HP_DONE_EINT1 */ +#define ARIZONA_FLL2_CLOCK_OK_EINT1 0x0002 /* FLL2_CLOCK_OK_EINT1 */ +#define ARIZONA_FLL2_CLOCK_OK_EINT1_MASK 0x0002 /* FLL2_CLOCK_OK_EINT1 */ +#define ARIZONA_FLL2_CLOCK_OK_EINT1_SHIFT 1 /* FLL2_CLOCK_OK_EINT1 */ +#define ARIZONA_FLL2_CLOCK_OK_EINT1_WIDTH 1 /* FLL2_CLOCK_OK_EINT1 */ +#define ARIZONA_FLL1_CLOCK_OK_EINT1 0x0001 /* FLL1_CLOCK_OK_EINT1 */ +#define ARIZONA_FLL1_CLOCK_OK_EINT1_MASK 0x0001 /* FLL1_CLOCK_OK_EINT1 */ +#define ARIZONA_FLL1_CLOCK_OK_EINT1_SHIFT 0 /* FLL1_CLOCK_OK_EINT1 */ +#define ARIZONA_FLL1_CLOCK_OK_EINT1_WIDTH 1 /* FLL1_CLOCK_OK_EINT1 */ + +/* + * R3336 (0xD08) - Interrupt Status 1 Mask + */ +#define ARIZONA_IM_GP4_EINT1 0x0008 /* IM_GP4_EINT1 */ +#define ARIZONA_IM_GP4_EINT1_MASK 0x0008 /* IM_GP4_EINT1 */ +#define ARIZONA_IM_GP4_EINT1_SHIFT 3 /* IM_GP4_EINT1 */ +#define ARIZONA_IM_GP4_EINT1_WIDTH 1 /* IM_GP4_EINT1 */ +#define ARIZONA_IM_GP3_EINT1 0x0004 /* IM_GP3_EINT1 */ +#define ARIZONA_IM_GP3_EINT1_MASK 0x0004 /* IM_GP3_EINT1 */ +#define ARIZONA_IM_GP3_EINT1_SHIFT 2 /* IM_GP3_EINT1 */ +#define ARIZONA_IM_GP3_EINT1_WIDTH 1 /* IM_GP3_EINT1 */ +#define ARIZONA_IM_GP2_EINT1 0x0002 /* IM_GP2_EINT1 */ +#define ARIZONA_IM_GP2_EINT1_MASK 0x0002 /* IM_GP2_EINT1 */ +#define ARIZONA_IM_GP2_EINT1_SHIFT 1 /* IM_GP2_EINT1 */ +#define ARIZONA_IM_GP2_EINT1_WIDTH 1 /* IM_GP2_EINT1 */ +#define ARIZONA_IM_GP1_EINT1 0x0001 /* IM_GP1_EINT1 */ +#define ARIZONA_IM_GP1_EINT1_MASK 0x0001 /* IM_GP1_EINT1 */ +#define ARIZONA_IM_GP1_EINT1_SHIFT 0 /* IM_GP1_EINT1 */ +#define ARIZONA_IM_GP1_EINT1_WIDTH 1 /* IM_GP1_EINT1 */ + +/* + * R3337 (0xD09) - Interrupt Status 2 Mask + */ +#define ARIZONA_IM_DSP1_RAM_RDY_EINT1 0x0100 /* IM_DSP1_RAM_RDY_EINT1 */ +#define ARIZONA_IM_DSP1_RAM_RDY_EINT1_MASK 0x0100 /* IM_DSP1_RAM_RDY_EINT1 */ +#define ARIZONA_IM_DSP1_RAM_RDY_EINT1_SHIFT 8 /* IM_DSP1_RAM_RDY_EINT1 */ +#define ARIZONA_IM_DSP1_RAM_RDY_EINT1_WIDTH 1 /* IM_DSP1_RAM_RDY_EINT1 */ +#define ARIZONA_IM_DSP_IRQ2_EINT1 0x0002 /* IM_DSP_IRQ2_EINT1 */ +#define ARIZONA_IM_DSP_IRQ2_EINT1_MASK 0x0002 /* IM_DSP_IRQ2_EINT1 */ +#define ARIZONA_IM_DSP_IRQ2_EINT1_SHIFT 1 /* IM_DSP_IRQ2_EINT1 */ +#define ARIZONA_IM_DSP_IRQ2_EINT1_WIDTH 1 /* IM_DSP_IRQ2_EINT1 */ +#define ARIZONA_IM_DSP_IRQ1_EINT1 0x0001 /* IM_DSP_IRQ1_EINT1 */ +#define ARIZONA_IM_DSP_IRQ1_EINT1_MASK 0x0001 /* IM_DSP_IRQ1_EINT1 */ +#define ARIZONA_IM_DSP_IRQ1_EINT1_SHIFT 0 /* IM_DSP_IRQ1_EINT1 */ +#define ARIZONA_IM_DSP_IRQ1_EINT1_WIDTH 1 /* IM_DSP_IRQ1_EINT1 */ + +/* + * R3338 (0xD0A) - Interrupt Status 3 Mask + */ +#define ARIZONA_IM_SPK_SHUTDOWN_WARN_EINT1 0x8000 /* IM_SPK_SHUTDOWN_WARN_EINT1 */ +#define ARIZONA_IM_SPK_SHUTDOWN_WARN_EINT1_MASK 0x8000 /* IM_SPK_SHUTDOWN_WARN_EINT1 */ +#define ARIZONA_IM_SPK_SHUTDOWN_WARN_EINT1_SHIFT 15 /* IM_SPK_SHUTDOWN_WARN_EINT1 */ +#define ARIZONA_IM_SPK_SHUTDOWN_WARN_EINT1_WIDTH 1 /* IM_SPK_SHUTDOWN_WARN_EINT1 */ +#define ARIZONA_IM_SPK_SHUTDOWN_EINT1 0x4000 /* IM_SPK_SHUTDOWN_EINT1 */ +#define ARIZONA_IM_SPK_SHUTDOWN_EINT1_MASK 0x4000 /* IM_SPK_SHUTDOWN_EINT1 */ +#define ARIZONA_IM_SPK_SHUTDOWN_EINT1_SHIFT 14 /* IM_SPK_SHUTDOWN_EINT1 */ +#define ARIZONA_IM_SPK_SHUTDOWN_EINT1_WIDTH 1 /* IM_SPK_SHUTDOWN_EINT1 */ +#define ARIZONA_IM_HPDET_EINT1 0x2000 /* IM_HPDET_EINT1 */ +#define ARIZONA_IM_HPDET_EINT1_MASK 0x2000 /* IM_HPDET_EINT1 */ +#define ARIZONA_IM_HPDET_EINT1_SHIFT 13 /* IM_HPDET_EINT1 */ +#define ARIZONA_IM_HPDET_EINT1_WIDTH 1 /* IM_HPDET_EINT1 */ +#define ARIZONA_IM_MICDET_EINT1 0x1000 /* IM_MICDET_EINT1 */ +#define ARIZONA_IM_MICDET_EINT1_MASK 0x1000 /* IM_MICDET_EINT1 */ +#define ARIZONA_IM_MICDET_EINT1_SHIFT 12 /* IM_MICDET_EINT1 */ +#define ARIZONA_IM_MICDET_EINT1_WIDTH 1 /* IM_MICDET_EINT1 */ +#define ARIZONA_IM_WSEQ_DONE_EINT1 0x0800 /* IM_WSEQ_DONE_EINT1 */ +#define ARIZONA_IM_WSEQ_DONE_EINT1_MASK 0x0800 /* IM_WSEQ_DONE_EINT1 */ +#define ARIZONA_IM_WSEQ_DONE_EINT1_SHIFT 11 /* IM_WSEQ_DONE_EINT1 */ +#define ARIZONA_IM_WSEQ_DONE_EINT1_WIDTH 1 /* IM_WSEQ_DONE_EINT1 */ +#define ARIZONA_IM_DRC2_SIG_DET_EINT1 0x0400 /* IM_DRC2_SIG_DET_EINT1 */ +#define ARIZONA_IM_DRC2_SIG_DET_EINT1_MASK 0x0400 /* IM_DRC2_SIG_DET_EINT1 */ +#define ARIZONA_IM_DRC2_SIG_DET_EINT1_SHIFT 10 /* IM_DRC2_SIG_DET_EINT1 */ +#define ARIZONA_IM_DRC2_SIG_DET_EINT1_WIDTH 1 /* IM_DRC2_SIG_DET_EINT1 */ +#define ARIZONA_IM_DRC1_SIG_DET_EINT1 0x0200 /* IM_DRC1_SIG_DET_EINT1 */ +#define ARIZONA_IM_DRC1_SIG_DET_EINT1_MASK 0x0200 /* IM_DRC1_SIG_DET_EINT1 */ +#define ARIZONA_IM_DRC1_SIG_DET_EINT1_SHIFT 9 /* IM_DRC1_SIG_DET_EINT1 */ +#define ARIZONA_IM_DRC1_SIG_DET_EINT1_WIDTH 1 /* IM_DRC1_SIG_DET_EINT1 */ +#define ARIZONA_IM_ASRC2_LOCK_EINT1 0x0100 /* IM_ASRC2_LOCK_EINT1 */ +#define ARIZONA_IM_ASRC2_LOCK_EINT1_MASK 0x0100 /* IM_ASRC2_LOCK_EINT1 */ +#define ARIZONA_IM_ASRC2_LOCK_EINT1_SHIFT 8 /* IM_ASRC2_LOCK_EINT1 */ +#define ARIZONA_IM_ASRC2_LOCK_EINT1_WIDTH 1 /* IM_ASRC2_LOCK_EINT1 */ +#define ARIZONA_IM_ASRC1_LOCK_EINT1 0x0080 /* IM_ASRC1_LOCK_EINT1 */ +#define ARIZONA_IM_ASRC1_LOCK_EINT1_MASK 0x0080 /* IM_ASRC1_LOCK_EINT1 */ +#define ARIZONA_IM_ASRC1_LOCK_EINT1_SHIFT 7 /* IM_ASRC1_LOCK_EINT1 */ +#define ARIZONA_IM_ASRC1_LOCK_EINT1_WIDTH 1 /* IM_ASRC1_LOCK_EINT1 */ +#define ARIZONA_IM_UNDERCLOCKED_EINT1 0x0040 /* IM_UNDERCLOCKED_EINT1 */ +#define ARIZONA_IM_UNDERCLOCKED_EINT1_MASK 0x0040 /* IM_UNDERCLOCKED_EINT1 */ +#define ARIZONA_IM_UNDERCLOCKED_EINT1_SHIFT 6 /* IM_UNDERCLOCKED_EINT1 */ +#define ARIZONA_IM_UNDERCLOCKED_EINT1_WIDTH 1 /* IM_UNDERCLOCKED_EINT1 */ +#define ARIZONA_IM_OVERCLOCKED_EINT1 0x0020 /* IM_OVERCLOCKED_EINT1 */ +#define ARIZONA_IM_OVERCLOCKED_EINT1_MASK 0x0020 /* IM_OVERCLOCKED_EINT1 */ +#define ARIZONA_IM_OVERCLOCKED_EINT1_SHIFT 5 /* IM_OVERCLOCKED_EINT1 */ +#define ARIZONA_IM_OVERCLOCKED_EINT1_WIDTH 1 /* IM_OVERCLOCKED_EINT1 */ +#define ARIZONA_IM_FLL2_LOCK_EINT1 0x0008 /* IM_FLL2_LOCK_EINT1 */ +#define ARIZONA_IM_FLL2_LOCK_EINT1_MASK 0x0008 /* IM_FLL2_LOCK_EINT1 */ +#define ARIZONA_IM_FLL2_LOCK_EINT1_SHIFT 3 /* IM_FLL2_LOCK_EINT1 */ +#define ARIZONA_IM_FLL2_LOCK_EINT1_WIDTH 1 /* IM_FLL2_LOCK_EINT1 */ +#define ARIZONA_IM_FLL1_LOCK_EINT1 0x0004 /* IM_FLL1_LOCK_EINT1 */ +#define ARIZONA_IM_FLL1_LOCK_EINT1_MASK 0x0004 /* IM_FLL1_LOCK_EINT1 */ +#define ARIZONA_IM_FLL1_LOCK_EINT1_SHIFT 2 /* IM_FLL1_LOCK_EINT1 */ +#define ARIZONA_IM_FLL1_LOCK_EINT1_WIDTH 1 /* IM_FLL1_LOCK_EINT1 */ +#define ARIZONA_IM_CLKGEN_ERR_EINT1 0x0002 /* IM_CLKGEN_ERR_EINT1 */ +#define ARIZONA_IM_CLKGEN_ERR_EINT1_MASK 0x0002 /* IM_CLKGEN_ERR_EINT1 */ +#define ARIZONA_IM_CLKGEN_ERR_EINT1_SHIFT 1 /* IM_CLKGEN_ERR_EINT1 */ +#define ARIZONA_IM_CLKGEN_ERR_EINT1_WIDTH 1 /* IM_CLKGEN_ERR_EINT1 */ +#define ARIZONA_IM_CLKGEN_ERR_ASYNC_EINT1 0x0001 /* IM_CLKGEN_ERR_ASYNC_EINT1 */ +#define ARIZONA_IM_CLKGEN_ERR_ASYNC_EINT1_MASK 0x0001 /* IM_CLKGEN_ERR_ASYNC_EINT1 */ +#define ARIZONA_IM_CLKGEN_ERR_ASYNC_EINT1_SHIFT 0 /* IM_CLKGEN_ERR_ASYNC_EINT1 */ +#define ARIZONA_IM_CLKGEN_ERR_ASYNC_EINT1_WIDTH 1 /* IM_CLKGEN_ERR_ASYNC_EINT1 */ + +/* + * R3339 (0xD0B) - Interrupt Status 4 Mask + */ +#define ARIZONA_IM_ASRC_CFG_ERR_EINT1 0x8000 /* IM_ASRC_CFG_ERR_EINT1 */ +#define ARIZONA_IM_ASRC_CFG_ERR_EINT1_MASK 0x8000 /* IM_ASRC_CFG_ERR_EINT1 */ +#define ARIZONA_IM_ASRC_CFG_ERR_EINT1_SHIFT 15 /* IM_ASRC_CFG_ERR_EINT1 */ +#define ARIZONA_IM_ASRC_CFG_ERR_EINT1_WIDTH 1 /* IM_ASRC_CFG_ERR_EINT1 */ +#define ARIZONA_IM_AIF3_ERR_EINT1 0x4000 /* IM_AIF3_ERR_EINT1 */ +#define ARIZONA_IM_AIF3_ERR_EINT1_MASK 0x4000 /* IM_AIF3_ERR_EINT1 */ +#define ARIZONA_IM_AIF3_ERR_EINT1_SHIFT 14 /* IM_AIF3_ERR_EINT1 */ +#define ARIZONA_IM_AIF3_ERR_EINT1_WIDTH 1 /* IM_AIF3_ERR_EINT1 */ +#define ARIZONA_IM_AIF2_ERR_EINT1 0x2000 /* IM_AIF2_ERR_EINT1 */ +#define ARIZONA_IM_AIF2_ERR_EINT1_MASK 0x2000 /* IM_AIF2_ERR_EINT1 */ +#define ARIZONA_IM_AIF2_ERR_EINT1_SHIFT 13 /* IM_AIF2_ERR_EINT1 */ +#define ARIZONA_IM_AIF2_ERR_EINT1_WIDTH 1 /* IM_AIF2_ERR_EINT1 */ +#define ARIZONA_IM_AIF1_ERR_EINT1 0x1000 /* IM_AIF1_ERR_EINT1 */ +#define ARIZONA_IM_AIF1_ERR_EINT1_MASK 0x1000 /* IM_AIF1_ERR_EINT1 */ +#define ARIZONA_IM_AIF1_ERR_EINT1_SHIFT 12 /* IM_AIF1_ERR_EINT1 */ +#define ARIZONA_IM_AIF1_ERR_EINT1_WIDTH 1 /* IM_AIF1_ERR_EINT1 */ +#define ARIZONA_IM_CTRLIF_ERR_EINT1 0x0800 /* IM_CTRLIF_ERR_EINT1 */ +#define ARIZONA_IM_CTRLIF_ERR_EINT1_MASK 0x0800 /* IM_CTRLIF_ERR_EINT1 */ +#define ARIZONA_IM_CTRLIF_ERR_EINT1_SHIFT 11 /* IM_CTRLIF_ERR_EINT1 */ +#define ARIZONA_IM_CTRLIF_ERR_EINT1_WIDTH 1 /* IM_CTRLIF_ERR_EINT1 */ +#define ARIZONA_IM_MIXER_DROPPED_SAMPLE_EINT1 0x0400 /* IM_MIXER_DROPPED_SAMPLE_EINT1 */ +#define ARIZONA_IM_MIXER_DROPPED_SAMPLE_EINT1_MASK 0x0400 /* IM_MIXER_DROPPED_SAMPLE_EINT1 */ +#define ARIZONA_IM_MIXER_DROPPED_SAMPLE_EINT1_SHIFT 10 /* IM_MIXER_DROPPED_SAMPLE_EINT1 */ +#define ARIZONA_IM_MIXER_DROPPED_SAMPLE_EINT1_WIDTH 1 /* IM_MIXER_DROPPED_SAMPLE_EINT1 */ +#define ARIZONA_IM_ASYNC_CLK_ENA_LOW_EINT1 0x0200 /* IM_ASYNC_CLK_ENA_LOW_EINT1 */ +#define ARIZONA_IM_ASYNC_CLK_ENA_LOW_EINT1_MASK 0x0200 /* IM_ASYNC_CLK_ENA_LOW_EINT1 */ +#define ARIZONA_IM_ASYNC_CLK_ENA_LOW_EINT1_SHIFT 9 /* IM_ASYNC_CLK_ENA_LOW_EINT1 */ +#define ARIZONA_IM_ASYNC_CLK_ENA_LOW_EINT1_WIDTH 1 /* IM_ASYNC_CLK_ENA_LOW_EINT1 */ +#define ARIZONA_IM_SYSCLK_ENA_LOW_EINT1 0x0100 /* IM_SYSCLK_ENA_LOW_EINT1 */ +#define ARIZONA_IM_SYSCLK_ENA_LOW_EINT1_MASK 0x0100 /* IM_SYSCLK_ENA_LOW_EINT1 */ +#define ARIZONA_IM_SYSCLK_ENA_LOW_EINT1_SHIFT 8 /* IM_SYSCLK_ENA_LOW_EINT1 */ +#define ARIZONA_IM_SYSCLK_ENA_LOW_EINT1_WIDTH 1 /* IM_SYSCLK_ENA_LOW_EINT1 */ +#define ARIZONA_IM_ISRC1_CFG_ERR_EINT1 0x0080 /* IM_ISRC1_CFG_ERR_EINT1 */ +#define ARIZONA_IM_ISRC1_CFG_ERR_EINT1_MASK 0x0080 /* IM_ISRC1_CFG_ERR_EINT1 */ +#define ARIZONA_IM_ISRC1_CFG_ERR_EINT1_SHIFT 7 /* IM_ISRC1_CFG_ERR_EINT1 */ +#define ARIZONA_IM_ISRC1_CFG_ERR_EINT1_WIDTH 1 /* IM_ISRC1_CFG_ERR_EINT1 */ +#define ARIZONA_IM_ISRC2_CFG_ERR_EINT1 0x0040 /* IM_ISRC2_CFG_ERR_EINT1 */ +#define ARIZONA_IM_ISRC2_CFG_ERR_EINT1_MASK 0x0040 /* IM_ISRC2_CFG_ERR_EINT1 */ +#define ARIZONA_IM_ISRC2_CFG_ERR_EINT1_SHIFT 6 /* IM_ISRC2_CFG_ERR_EINT1 */ +#define ARIZONA_IM_ISRC2_CFG_ERR_EINT1_WIDTH 1 /* IM_ISRC2_CFG_ERR_EINT1 */ + +/* + * R3340 (0xD0C) - Interrupt Status 5 Mask + */ +#define ARIZONA_IM_BOOT_DONE_EINT1 0x0100 /* IM_BOOT_DONE_EINT1 */ +#define ARIZONA_IM_BOOT_DONE_EINT1_MASK 0x0100 /* IM_BOOT_DONE_EINT1 */ +#define ARIZONA_IM_BOOT_DONE_EINT1_SHIFT 8 /* IM_BOOT_DONE_EINT1 */ +#define ARIZONA_IM_BOOT_DONE_EINT1_WIDTH 1 /* IM_BOOT_DONE_EINT1 */ +#define ARIZONA_IM_DCS_DAC_DONE_EINT1 0x0080 /* IM_DCS_DAC_DONE_EINT1 */ +#define ARIZONA_IM_DCS_DAC_DONE_EINT1_MASK 0x0080 /* IM_DCS_DAC_DONE_EINT1 */ +#define ARIZONA_IM_DCS_DAC_DONE_EINT1_SHIFT 7 /* IM_DCS_DAC_DONE_EINT1 */ +#define ARIZONA_IM_DCS_DAC_DONE_EINT1_WIDTH 1 /* IM_DCS_DAC_DONE_EINT1 */ +#define ARIZONA_IM_DCS_HP_DONE_EINT1 0x0040 /* IM_DCS_HP_DONE_EINT1 */ +#define ARIZONA_IM_DCS_HP_DONE_EINT1_MASK 0x0040 /* IM_DCS_HP_DONE_EINT1 */ +#define ARIZONA_IM_DCS_HP_DONE_EINT1_SHIFT 6 /* IM_DCS_HP_DONE_EINT1 */ +#define ARIZONA_IM_DCS_HP_DONE_EINT1_WIDTH 1 /* IM_DCS_HP_DONE_EINT1 */ +#define ARIZONA_IM_FLL2_CLOCK_OK_EINT1 0x0002 /* IM_FLL2_CLOCK_OK_EINT1 */ +#define ARIZONA_IM_FLL2_CLOCK_OK_EINT1_MASK 0x0002 /* IM_FLL2_CLOCK_OK_EINT1 */ +#define ARIZONA_IM_FLL2_CLOCK_OK_EINT1_SHIFT 1 /* IM_FLL2_CLOCK_OK_EINT1 */ +#define ARIZONA_IM_FLL2_CLOCK_OK_EINT1_WIDTH 1 /* IM_FLL2_CLOCK_OK_EINT1 */ +#define ARIZONA_IM_FLL1_CLOCK_OK_EINT1 0x0001 /* IM_FLL1_CLOCK_OK_EINT1 */ +#define ARIZONA_IM_FLL1_CLOCK_OK_EINT1_MASK 0x0001 /* IM_FLL1_CLOCK_OK_EINT1 */ +#define ARIZONA_IM_FLL1_CLOCK_OK_EINT1_SHIFT 0 /* IM_FLL1_CLOCK_OK_EINT1 */ +#define ARIZONA_IM_FLL1_CLOCK_OK_EINT1_WIDTH 1 /* IM_FLL1_CLOCK_OK_EINT1 */ + +/* + * R3343 (0xD0F) - Interrupt Control + */ +#define ARIZONA_IM_IRQ1 0x0001 /* IM_IRQ1 */ +#define ARIZONA_IM_IRQ1_MASK 0x0001 /* IM_IRQ1 */ +#define ARIZONA_IM_IRQ1_SHIFT 0 /* IM_IRQ1 */ +#define ARIZONA_IM_IRQ1_WIDTH 1 /* IM_IRQ1 */ + +/* + * R3344 (0xD10) - IRQ2 Status 1 + */ +#define ARIZONA_GP4_EINT2 0x0008 /* GP4_EINT2 */ +#define ARIZONA_GP4_EINT2_MASK 0x0008 /* GP4_EINT2 */ +#define ARIZONA_GP4_EINT2_SHIFT 3 /* GP4_EINT2 */ +#define ARIZONA_GP4_EINT2_WIDTH 1 /* GP4_EINT2 */ +#define ARIZONA_GP3_EINT2 0x0004 /* GP3_EINT2 */ +#define ARIZONA_GP3_EINT2_MASK 0x0004 /* GP3_EINT2 */ +#define ARIZONA_GP3_EINT2_SHIFT 2 /* GP3_EINT2 */ +#define ARIZONA_GP3_EINT2_WIDTH 1 /* GP3_EINT2 */ +#define ARIZONA_GP2_EINT2 0x0002 /* GP2_EINT2 */ +#define ARIZONA_GP2_EINT2_MASK 0x0002 /* GP2_EINT2 */ +#define ARIZONA_GP2_EINT2_SHIFT 1 /* GP2_EINT2 */ +#define ARIZONA_GP2_EINT2_WIDTH 1 /* GP2_EINT2 */ +#define ARIZONA_GP1_EINT2 0x0001 /* GP1_EINT2 */ +#define ARIZONA_GP1_EINT2_MASK 0x0001 /* GP1_EINT2 */ +#define ARIZONA_GP1_EINT2_SHIFT 0 /* GP1_EINT2 */ +#define ARIZONA_GP1_EINT2_WIDTH 1 /* GP1_EINT2 */ + +/* + * R3345 (0xD11) - IRQ2 Status 2 + */ +#define ARIZONA_DSP1_RAM_RDY_EINT2 0x0100 /* DSP1_RAM_RDY_EINT2 */ +#define ARIZONA_DSP1_RAM_RDY_EINT2_MASK 0x0100 /* DSP1_RAM_RDY_EINT2 */ +#define ARIZONA_DSP1_RAM_RDY_EINT2_SHIFT 8 /* DSP1_RAM_RDY_EINT2 */ +#define ARIZONA_DSP1_RAM_RDY_EINT2_WIDTH 1 /* DSP1_RAM_RDY_EINT2 */ +#define ARIZONA_DSP_IRQ2_EINT2 0x0002 /* DSP_IRQ2_EINT2 */ +#define ARIZONA_DSP_IRQ2_EINT2_MASK 0x0002 /* DSP_IRQ2_EINT2 */ +#define ARIZONA_DSP_IRQ2_EINT2_SHIFT 1 /* DSP_IRQ2_EINT2 */ +#define ARIZONA_DSP_IRQ2_EINT2_WIDTH 1 /* DSP_IRQ2_EINT2 */ +#define ARIZONA_DSP_IRQ1_EINT2 0x0001 /* DSP_IRQ1_EINT2 */ +#define ARIZONA_DSP_IRQ1_EINT2_MASK 0x0001 /* DSP_IRQ1_EINT2 */ +#define ARIZONA_DSP_IRQ1_EINT2_SHIFT 0 /* DSP_IRQ1_EINT2 */ +#define ARIZONA_DSP_IRQ1_EINT2_WIDTH 1 /* DSP_IRQ1_EINT2 */ + +/* + * R3346 (0xD12) - IRQ2 Status 3 + */ +#define ARIZONA_SPK_SHUTDOWN_WARN_EINT2 0x8000 /* SPK_SHUTDOWN_WARN_EINT2 */ +#define ARIZONA_SPK_SHUTDOWN_WARN_EINT2_MASK 0x8000 /* SPK_SHUTDOWN_WARN_EINT2 */ +#define ARIZONA_SPK_SHUTDOWN_WARN_EINT2_SHIFT 15 /* SPK_SHUTDOWN_WARN_EINT2 */ +#define ARIZONA_SPK_SHUTDOWN_WARN_EINT2_WIDTH 1 /* SPK_SHUTDOWN_WARN_EINT2 */ +#define ARIZONA_SPK_SHUTDOWN_EINT2 0x4000 /* SPK_SHUTDOWN_EINT2 */ +#define ARIZONA_SPK_SHUTDOWN_EINT2_MASK 0x4000 /* SPK_SHUTDOWN_EINT2 */ +#define ARIZONA_SPK_SHUTDOWN_EINT2_SHIFT 14 /* SPK_SHUTDOWN_EINT2 */ +#define ARIZONA_SPK_SHUTDOWN_EINT2_WIDTH 1 /* SPK_SHUTDOWN_EINT2 */ +#define ARIZONA_HPDET_EINT2 0x2000 /* HPDET_EINT2 */ +#define ARIZONA_HPDET_EINT2_MASK 0x2000 /* HPDET_EINT2 */ +#define ARIZONA_HPDET_EINT2_SHIFT 13 /* HPDET_EINT2 */ +#define ARIZONA_HPDET_EINT2_WIDTH 1 /* HPDET_EINT2 */ +#define ARIZONA_MICDET_EINT2 0x1000 /* MICDET_EINT2 */ +#define ARIZONA_MICDET_EINT2_MASK 0x1000 /* MICDET_EINT2 */ +#define ARIZONA_MICDET_EINT2_SHIFT 12 /* MICDET_EINT2 */ +#define ARIZONA_MICDET_EINT2_WIDTH 1 /* MICDET_EINT2 */ +#define ARIZONA_WSEQ_DONE_EINT2 0x0800 /* WSEQ_DONE_EINT2 */ +#define ARIZONA_WSEQ_DONE_EINT2_MASK 0x0800 /* WSEQ_DONE_EINT2 */ +#define ARIZONA_WSEQ_DONE_EINT2_SHIFT 11 /* WSEQ_DONE_EINT2 */ +#define ARIZONA_WSEQ_DONE_EINT2_WIDTH 1 /* WSEQ_DONE_EINT2 */ +#define ARIZONA_DRC2_SIG_DET_EINT2 0x0400 /* DRC2_SIG_DET_EINT2 */ +#define ARIZONA_DRC2_SIG_DET_EINT2_MASK 0x0400 /* DRC2_SIG_DET_EINT2 */ +#define ARIZONA_DRC2_SIG_DET_EINT2_SHIFT 10 /* DRC2_SIG_DET_EINT2 */ +#define ARIZONA_DRC2_SIG_DET_EINT2_WIDTH 1 /* DRC2_SIG_DET_EINT2 */ +#define ARIZONA_DRC1_SIG_DET_EINT2 0x0200 /* DRC1_SIG_DET_EINT2 */ +#define ARIZONA_DRC1_SIG_DET_EINT2_MASK 0x0200 /* DRC1_SIG_DET_EINT2 */ +#define ARIZONA_DRC1_SIG_DET_EINT2_SHIFT 9 /* DRC1_SIG_DET_EINT2 */ +#define ARIZONA_DRC1_SIG_DET_EINT2_WIDTH 1 /* DRC1_SIG_DET_EINT2 */ +#define ARIZONA_ASRC2_LOCK_EINT2 0x0100 /* ASRC2_LOCK_EINT2 */ +#define ARIZONA_ASRC2_LOCK_EINT2_MASK 0x0100 /* ASRC2_LOCK_EINT2 */ +#define ARIZONA_ASRC2_LOCK_EINT2_SHIFT 8 /* ASRC2_LOCK_EINT2 */ +#define ARIZONA_ASRC2_LOCK_EINT2_WIDTH 1 /* ASRC2_LOCK_EINT2 */ +#define ARIZONA_ASRC1_LOCK_EINT2 0x0080 /* ASRC1_LOCK_EINT2 */ +#define ARIZONA_ASRC1_LOCK_EINT2_MASK 0x0080 /* ASRC1_LOCK_EINT2 */ +#define ARIZONA_ASRC1_LOCK_EINT2_SHIFT 7 /* ASRC1_LOCK_EINT2 */ +#define ARIZONA_ASRC1_LOCK_EINT2_WIDTH 1 /* ASRC1_LOCK_EINT2 */ +#define ARIZONA_UNDERCLOCKED_EINT2 0x0040 /* UNDERCLOCKED_EINT2 */ +#define ARIZONA_UNDERCLOCKED_EINT2_MASK 0x0040 /* UNDERCLOCKED_EINT2 */ +#define ARIZONA_UNDERCLOCKED_EINT2_SHIFT 6 /* UNDERCLOCKED_EINT2 */ +#define ARIZONA_UNDERCLOCKED_EINT2_WIDTH 1 /* UNDERCLOCKED_EINT2 */ +#define ARIZONA_OVERCLOCKED_EINT2 0x0020 /* OVERCLOCKED_EINT2 */ +#define ARIZONA_OVERCLOCKED_EINT2_MASK 0x0020 /* OVERCLOCKED_EINT2 */ +#define ARIZONA_OVERCLOCKED_EINT2_SHIFT 5 /* OVERCLOCKED_EINT2 */ +#define ARIZONA_OVERCLOCKED_EINT2_WIDTH 1 /* OVERCLOCKED_EINT2 */ +#define ARIZONA_FLL2_LOCK_EINT2 0x0008 /* FLL2_LOCK_EINT2 */ +#define ARIZONA_FLL2_LOCK_EINT2_MASK 0x0008 /* FLL2_LOCK_EINT2 */ +#define ARIZONA_FLL2_LOCK_EINT2_SHIFT 3 /* FLL2_LOCK_EINT2 */ +#define ARIZONA_FLL2_LOCK_EINT2_WIDTH 1 /* FLL2_LOCK_EINT2 */ +#define ARIZONA_FLL1_LOCK_EINT2 0x0004 /* FLL1_LOCK_EINT2 */ +#define ARIZONA_FLL1_LOCK_EINT2_MASK 0x0004 /* FLL1_LOCK_EINT2 */ +#define ARIZONA_FLL1_LOCK_EINT2_SHIFT 2 /* FLL1_LOCK_EINT2 */ +#define ARIZONA_FLL1_LOCK_EINT2_WIDTH 1 /* FLL1_LOCK_EINT2 */ +#define ARIZONA_CLKGEN_ERR_EINT2 0x0002 /* CLKGEN_ERR_EINT2 */ +#define ARIZONA_CLKGEN_ERR_EINT2_MASK 0x0002 /* CLKGEN_ERR_EINT2 */ +#define ARIZONA_CLKGEN_ERR_EINT2_SHIFT 1 /* CLKGEN_ERR_EINT2 */ +#define ARIZONA_CLKGEN_ERR_EINT2_WIDTH 1 /* CLKGEN_ERR_EINT2 */ +#define ARIZONA_CLKGEN_ERR_ASYNC_EINT2 0x0001 /* CLKGEN_ERR_ASYNC_EINT2 */ +#define ARIZONA_CLKGEN_ERR_ASYNC_EINT2_MASK 0x0001 /* CLKGEN_ERR_ASYNC_EINT2 */ +#define ARIZONA_CLKGEN_ERR_ASYNC_EINT2_SHIFT 0 /* CLKGEN_ERR_ASYNC_EINT2 */ +#define ARIZONA_CLKGEN_ERR_ASYNC_EINT2_WIDTH 1 /* CLKGEN_ERR_ASYNC_EINT2 */ + +/* + * R3347 (0xD13) - IRQ2 Status 4 + */ +#define ARIZONA_ASRC_CFG_ERR_EINT2 0x8000 /* ASRC_CFG_ERR_EINT2 */ +#define ARIZONA_ASRC_CFG_ERR_EINT2_MASK 0x8000 /* ASRC_CFG_ERR_EINT2 */ +#define ARIZONA_ASRC_CFG_ERR_EINT2_SHIFT 15 /* ASRC_CFG_ERR_EINT2 */ +#define ARIZONA_ASRC_CFG_ERR_EINT2_WIDTH 1 /* ASRC_CFG_ERR_EINT2 */ +#define ARIZONA_AIF3_ERR_EINT2 0x4000 /* AIF3_ERR_EINT2 */ +#define ARIZONA_AIF3_ERR_EINT2_MASK 0x4000 /* AIF3_ERR_EINT2 */ +#define ARIZONA_AIF3_ERR_EINT2_SHIFT 14 /* AIF3_ERR_EINT2 */ +#define ARIZONA_AIF3_ERR_EINT2_WIDTH 1 /* AIF3_ERR_EINT2 */ +#define ARIZONA_AIF2_ERR_EINT2 0x2000 /* AIF2_ERR_EINT2 */ +#define ARIZONA_AIF2_ERR_EINT2_MASK 0x2000 /* AIF2_ERR_EINT2 */ +#define ARIZONA_AIF2_ERR_EINT2_SHIFT 13 /* AIF2_ERR_EINT2 */ +#define ARIZONA_AIF2_ERR_EINT2_WIDTH 1 /* AIF2_ERR_EINT2 */ +#define ARIZONA_AIF1_ERR_EINT2 0x1000 /* AIF1_ERR_EINT2 */ +#define ARIZONA_AIF1_ERR_EINT2_MASK 0x1000 /* AIF1_ERR_EINT2 */ +#define ARIZONA_AIF1_ERR_EINT2_SHIFT 12 /* AIF1_ERR_EINT2 */ +#define ARIZONA_AIF1_ERR_EINT2_WIDTH 1 /* AIF1_ERR_EINT2 */ +#define ARIZONA_CTRLIF_ERR_EINT2 0x0800 /* CTRLIF_ERR_EINT2 */ +#define ARIZONA_CTRLIF_ERR_EINT2_MASK 0x0800 /* CTRLIF_ERR_EINT2 */ +#define ARIZONA_CTRLIF_ERR_EINT2_SHIFT 11 /* CTRLIF_ERR_EINT2 */ +#define ARIZONA_CTRLIF_ERR_EINT2_WIDTH 1 /* CTRLIF_ERR_EINT2 */ +#define ARIZONA_MIXER_DROPPED_SAMPLE_EINT2 0x0400 /* MIXER_DROPPED_SAMPLE_EINT2 */ +#define ARIZONA_MIXER_DROPPED_SAMPLE_EINT2_MASK 0x0400 /* MIXER_DROPPED_SAMPLE_EINT2 */ +#define ARIZONA_MIXER_DROPPED_SAMPLE_EINT2_SHIFT 10 /* MIXER_DROPPED_SAMPLE_EINT2 */ +#define ARIZONA_MIXER_DROPPED_SAMPLE_EINT2_WIDTH 1 /* MIXER_DROPPED_SAMPLE_EINT2 */ +#define ARIZONA_ASYNC_CLK_ENA_LOW_EINT2 0x0200 /* ASYNC_CLK_ENA_LOW_EINT2 */ +#define ARIZONA_ASYNC_CLK_ENA_LOW_EINT2_MASK 0x0200 /* ASYNC_CLK_ENA_LOW_EINT2 */ +#define ARIZONA_ASYNC_CLK_ENA_LOW_EINT2_SHIFT 9 /* ASYNC_CLK_ENA_LOW_EINT2 */ +#define ARIZONA_ASYNC_CLK_ENA_LOW_EINT2_WIDTH 1 /* ASYNC_CLK_ENA_LOW_EINT2 */ +#define ARIZONA_SYSCLK_ENA_LOW_EINT2 0x0100 /* SYSCLK_ENA_LOW_EINT2 */ +#define ARIZONA_SYSCLK_ENA_LOW_EINT2_MASK 0x0100 /* SYSCLK_ENA_LOW_EINT2 */ +#define ARIZONA_SYSCLK_ENA_LOW_EINT2_SHIFT 8 /* SYSCLK_ENA_LOW_EINT2 */ +#define ARIZONA_SYSCLK_ENA_LOW_EINT2_WIDTH 1 /* SYSCLK_ENA_LOW_EINT2 */ +#define ARIZONA_ISRC1_CFG_ERR_EINT2 0x0080 /* ISRC1_CFG_ERR_EINT2 */ +#define ARIZONA_ISRC1_CFG_ERR_EINT2_MASK 0x0080 /* ISRC1_CFG_ERR_EINT2 */ +#define ARIZONA_ISRC1_CFG_ERR_EINT2_SHIFT 7 /* ISRC1_CFG_ERR_EINT2 */ +#define ARIZONA_ISRC1_CFG_ERR_EINT2_WIDTH 1 /* ISRC1_CFG_ERR_EINT2 */ +#define ARIZONA_ISRC2_CFG_ERR_EINT2 0x0040 /* ISRC2_CFG_ERR_EINT2 */ +#define ARIZONA_ISRC2_CFG_ERR_EINT2_MASK 0x0040 /* ISRC2_CFG_ERR_EINT2 */ +#define ARIZONA_ISRC2_CFG_ERR_EINT2_SHIFT 6 /* ISRC2_CFG_ERR_EINT2 */ +#define ARIZONA_ISRC2_CFG_ERR_EINT2_WIDTH 1 /* ISRC2_CFG_ERR_EINT2 */ + +/* + * R3348 (0xD14) - IRQ2 Status 5 + */ +#define ARIZONA_BOOT_DONE_EINT2 0x0100 /* BOOT_DONE_EINT2 */ +#define ARIZONA_BOOT_DONE_EINT2_MASK 0x0100 /* BOOT_DONE_EINT2 */ +#define ARIZONA_BOOT_DONE_EINT2_SHIFT 8 /* BOOT_DONE_EINT2 */ +#define ARIZONA_BOOT_DONE_EINT2_WIDTH 1 /* BOOT_DONE_EINT2 */ +#define ARIZONA_DCS_DAC_DONE_EINT2 0x0080 /* DCS_DAC_DONE_EINT2 */ +#define ARIZONA_DCS_DAC_DONE_EINT2_MASK 0x0080 /* DCS_DAC_DONE_EINT2 */ +#define ARIZONA_DCS_DAC_DONE_EINT2_SHIFT 7 /* DCS_DAC_DONE_EINT2 */ +#define ARIZONA_DCS_DAC_DONE_EINT2_WIDTH 1 /* DCS_DAC_DONE_EINT2 */ +#define ARIZONA_DCS_HP_DONE_EINT2 0x0040 /* DCS_HP_DONE_EINT2 */ +#define ARIZONA_DCS_HP_DONE_EINT2_MASK 0x0040 /* DCS_HP_DONE_EINT2 */ +#define ARIZONA_DCS_HP_DONE_EINT2_SHIFT 6 /* DCS_HP_DONE_EINT2 */ +#define ARIZONA_DCS_HP_DONE_EINT2_WIDTH 1 /* DCS_HP_DONE_EINT2 */ +#define ARIZONA_FLL2_CLOCK_OK_EINT2 0x0002 /* FLL2_CLOCK_OK_EINT2 */ +#define ARIZONA_FLL2_CLOCK_OK_EINT2_MASK 0x0002 /* FLL2_CLOCK_OK_EINT2 */ +#define ARIZONA_FLL2_CLOCK_OK_EINT2_SHIFT 1 /* FLL2_CLOCK_OK_EINT2 */ +#define ARIZONA_FLL2_CLOCK_OK_EINT2_WIDTH 1 /* FLL2_CLOCK_OK_EINT2 */ +#define ARIZONA_FLL1_CLOCK_OK_EINT2 0x0001 /* FLL1_CLOCK_OK_EINT2 */ +#define ARIZONA_FLL1_CLOCK_OK_EINT2_MASK 0x0001 /* FLL1_CLOCK_OK_EINT2 */ +#define ARIZONA_FLL1_CLOCK_OK_EINT2_SHIFT 0 /* FLL1_CLOCK_OK_EINT2 */ +#define ARIZONA_FLL1_CLOCK_OK_EINT2_WIDTH 1 /* FLL1_CLOCK_OK_EINT2 */ + +/* + * R3352 (0xD18) - IRQ2 Status 1 Mask + */ +#define ARIZONA_IM_GP4_EINT2 0x0008 /* IM_GP4_EINT2 */ +#define ARIZONA_IM_GP4_EINT2_MASK 0x0008 /* IM_GP4_EINT2 */ +#define ARIZONA_IM_GP4_EINT2_SHIFT 3 /* IM_GP4_EINT2 */ +#define ARIZONA_IM_GP4_EINT2_WIDTH 1 /* IM_GP4_EINT2 */ +#define ARIZONA_IM_GP3_EINT2 0x0004 /* IM_GP3_EINT2 */ +#define ARIZONA_IM_GP3_EINT2_MASK 0x0004 /* IM_GP3_EINT2 */ +#define ARIZONA_IM_GP3_EINT2_SHIFT 2 /* IM_GP3_EINT2 */ +#define ARIZONA_IM_GP3_EINT2_WIDTH 1 /* IM_GP3_EINT2 */ +#define ARIZONA_IM_GP2_EINT2 0x0002 /* IM_GP2_EINT2 */ +#define ARIZONA_IM_GP2_EINT2_MASK 0x0002 /* IM_GP2_EINT2 */ +#define ARIZONA_IM_GP2_EINT2_SHIFT 1 /* IM_GP2_EINT2 */ +#define ARIZONA_IM_GP2_EINT2_WIDTH 1 /* IM_GP2_EINT2 */ +#define ARIZONA_IM_GP1_EINT2 0x0001 /* IM_GP1_EINT2 */ +#define ARIZONA_IM_GP1_EINT2_MASK 0x0001 /* IM_GP1_EINT2 */ +#define ARIZONA_IM_GP1_EINT2_SHIFT 0 /* IM_GP1_EINT2 */ +#define ARIZONA_IM_GP1_EINT2_WIDTH 1 /* IM_GP1_EINT2 */ + +/* + * R3353 (0xD19) - IRQ2 Status 2 Mask + */ +#define ARIZONA_IM_DSP1_RAM_RDY_EINT2 0x0100 /* IM_DSP1_RAM_RDY_EINT2 */ +#define ARIZONA_IM_DSP1_RAM_RDY_EINT2_MASK 0x0100 /* IM_DSP1_RAM_RDY_EINT2 */ +#define ARIZONA_IM_DSP1_RAM_RDY_EINT2_SHIFT 8 /* IM_DSP1_RAM_RDY_EINT2 */ +#define ARIZONA_IM_DSP1_RAM_RDY_EINT2_WIDTH 1 /* IM_DSP1_RAM_RDY_EINT2 */ +#define ARIZONA_IM_DSP_IRQ2_EINT2 0x0002 /* IM_DSP_IRQ2_EINT2 */ +#define ARIZONA_IM_DSP_IRQ2_EINT2_MASK 0x0002 /* IM_DSP_IRQ2_EINT2 */ +#define ARIZONA_IM_DSP_IRQ2_EINT2_SHIFT 1 /* IM_DSP_IRQ2_EINT2 */ +#define ARIZONA_IM_DSP_IRQ2_EINT2_WIDTH 1 /* IM_DSP_IRQ2_EINT2 */ +#define ARIZONA_IM_DSP_IRQ1_EINT2 0x0001 /* IM_DSP_IRQ1_EINT2 */ +#define ARIZONA_IM_DSP_IRQ1_EINT2_MASK 0x0001 /* IM_DSP_IRQ1_EINT2 */ +#define ARIZONA_IM_DSP_IRQ1_EINT2_SHIFT 0 /* IM_DSP_IRQ1_EINT2 */ +#define ARIZONA_IM_DSP_IRQ1_EINT2_WIDTH 1 /* IM_DSP_IRQ1_EINT2 */ + +/* + * R3354 (0xD1A) - IRQ2 Status 3 Mask + */ +#define ARIZONA_IM_SPK_SHUTDOWN_WARN_EINT2 0x8000 /* IM_SPK_SHUTDOWN_WARN_EINT2 */ +#define ARIZONA_IM_SPK_SHUTDOWN_WARN_EINT2_MASK 0x8000 /* IM_SPK_SHUTDOWN_WARN_EINT2 */ +#define ARIZONA_IM_SPK_SHUTDOWN_WARN_EINT2_SHIFT 15 /* IM_SPK_SHUTDOWN_WARN_EINT2 */ +#define ARIZONA_IM_SPK_SHUTDOWN_WARN_EINT2_WIDTH 1 /* IM_SPK_SHUTDOWN_WARN_EINT2 */ +#define ARIZONA_IM_SPK_SHUTDOWN_EINT2 0x4000 /* IM_SPK_SHUTDOWN_EINT2 */ +#define ARIZONA_IM_SPK_SHUTDOWN_EINT2_MASK 0x4000 /* IM_SPK_SHUTDOWN_EINT2 */ +#define ARIZONA_IM_SPK_SHUTDOWN_EINT2_SHIFT 14 /* IM_SPK_SHUTDOWN_EINT2 */ +#define ARIZONA_IM_SPK_SHUTDOWN_EINT2_WIDTH 1 /* IM_SPK_SHUTDOWN_EINT2 */ +#define ARIZONA_IM_HPDET_EINT2 0x2000 /* IM_HPDET_EINT2 */ +#define ARIZONA_IM_HPDET_EINT2_MASK 0x2000 /* IM_HPDET_EINT2 */ +#define ARIZONA_IM_HPDET_EINT2_SHIFT 13 /* IM_HPDET_EINT2 */ +#define ARIZONA_IM_HPDET_EINT2_WIDTH 1 /* IM_HPDET_EINT2 */ +#define ARIZONA_IM_MICDET_EINT2 0x1000 /* IM_MICDET_EINT2 */ +#define ARIZONA_IM_MICDET_EINT2_MASK 0x1000 /* IM_MICDET_EINT2 */ +#define ARIZONA_IM_MICDET_EINT2_SHIFT 12 /* IM_MICDET_EINT2 */ +#define ARIZONA_IM_MICDET_EINT2_WIDTH 1 /* IM_MICDET_EINT2 */ +#define ARIZONA_IM_WSEQ_DONE_EINT2 0x0800 /* IM_WSEQ_DONE_EINT2 */ +#define ARIZONA_IM_WSEQ_DONE_EINT2_MASK 0x0800 /* IM_WSEQ_DONE_EINT2 */ +#define ARIZONA_IM_WSEQ_DONE_EINT2_SHIFT 11 /* IM_WSEQ_DONE_EINT2 */ +#define ARIZONA_IM_WSEQ_DONE_EINT2_WIDTH 1 /* IM_WSEQ_DONE_EINT2 */ +#define ARIZONA_IM_DRC2_SIG_DET_EINT2 0x0400 /* IM_DRC2_SIG_DET_EINT2 */ +#define ARIZONA_IM_DRC2_SIG_DET_EINT2_MASK 0x0400 /* IM_DRC2_SIG_DET_EINT2 */ +#define ARIZONA_IM_DRC2_SIG_DET_EINT2_SHIFT 10 /* IM_DRC2_SIG_DET_EINT2 */ +#define ARIZONA_IM_DRC2_SIG_DET_EINT2_WIDTH 1 /* IM_DRC2_SIG_DET_EINT2 */ +#define ARIZONA_IM_DRC1_SIG_DET_EINT2 0x0200 /* IM_DRC1_SIG_DET_EINT2 */ +#define ARIZONA_IM_DRC1_SIG_DET_EINT2_MASK 0x0200 /* IM_DRC1_SIG_DET_EINT2 */ +#define ARIZONA_IM_DRC1_SIG_DET_EINT2_SHIFT 9 /* IM_DRC1_SIG_DET_EINT2 */ +#define ARIZONA_IM_DRC1_SIG_DET_EINT2_WIDTH 1 /* IM_DRC1_SIG_DET_EINT2 */ +#define ARIZONA_IM_ASRC2_LOCK_EINT2 0x0100 /* IM_ASRC2_LOCK_EINT2 */ +#define ARIZONA_IM_ASRC2_LOCK_EINT2_MASK 0x0100 /* IM_ASRC2_LOCK_EINT2 */ +#define ARIZONA_IM_ASRC2_LOCK_EINT2_SHIFT 8 /* IM_ASRC2_LOCK_EINT2 */ +#define ARIZONA_IM_ASRC2_LOCK_EINT2_WIDTH 1 /* IM_ASRC2_LOCK_EINT2 */ +#define ARIZONA_IM_ASRC1_LOCK_EINT2 0x0080 /* IM_ASRC1_LOCK_EINT2 */ +#define ARIZONA_IM_ASRC1_LOCK_EINT2_MASK 0x0080 /* IM_ASRC1_LOCK_EINT2 */ +#define ARIZONA_IM_ASRC1_LOCK_EINT2_SHIFT 7 /* IM_ASRC1_LOCK_EINT2 */ +#define ARIZONA_IM_ASRC1_LOCK_EINT2_WIDTH 1 /* IM_ASRC1_LOCK_EINT2 */ +#define ARIZONA_IM_UNDERCLOCKED_EINT2 0x0040 /* IM_UNDERCLOCKED_EINT2 */ +#define ARIZONA_IM_UNDERCLOCKED_EINT2_MASK 0x0040 /* IM_UNDERCLOCKED_EINT2 */ +#define ARIZONA_IM_UNDERCLOCKED_EINT2_SHIFT 6 /* IM_UNDERCLOCKED_EINT2 */ +#define ARIZONA_IM_UNDERCLOCKED_EINT2_WIDTH 1 /* IM_UNDERCLOCKED_EINT2 */ +#define ARIZONA_IM_OVERCLOCKED_EINT2 0x0020 /* IM_OVERCLOCKED_EINT2 */ +#define ARIZONA_IM_OVERCLOCKED_EINT2_MASK 0x0020 /* IM_OVERCLOCKED_EINT2 */ +#define ARIZONA_IM_OVERCLOCKED_EINT2_SHIFT 5 /* IM_OVERCLOCKED_EINT2 */ +#define ARIZONA_IM_OVERCLOCKED_EINT2_WIDTH 1 /* IM_OVERCLOCKED_EINT2 */ +#define ARIZONA_IM_FLL2_LOCK_EINT2 0x0008 /* IM_FLL2_LOCK_EINT2 */ +#define ARIZONA_IM_FLL2_LOCK_EINT2_MASK 0x0008 /* IM_FLL2_LOCK_EINT2 */ +#define ARIZONA_IM_FLL2_LOCK_EINT2_SHIFT 3 /* IM_FLL2_LOCK_EINT2 */ +#define ARIZONA_IM_FLL2_LOCK_EINT2_WIDTH 1 /* IM_FLL2_LOCK_EINT2 */ +#define ARIZONA_IM_FLL1_LOCK_EINT2 0x0004 /* IM_FLL1_LOCK_EINT2 */ +#define ARIZONA_IM_FLL1_LOCK_EINT2_MASK 0x0004 /* IM_FLL1_LOCK_EINT2 */ +#define ARIZONA_IM_FLL1_LOCK_EINT2_SHIFT 2 /* IM_FLL1_LOCK_EINT2 */ +#define ARIZONA_IM_FLL1_LOCK_EINT2_WIDTH 1 /* IM_FLL1_LOCK_EINT2 */ +#define ARIZONA_IM_CLKGEN_ERR_EINT2 0x0002 /* IM_CLKGEN_ERR_EINT2 */ +#define ARIZONA_IM_CLKGEN_ERR_EINT2_MASK 0x0002 /* IM_CLKGEN_ERR_EINT2 */ +#define ARIZONA_IM_CLKGEN_ERR_EINT2_SHIFT 1 /* IM_CLKGEN_ERR_EINT2 */ +#define ARIZONA_IM_CLKGEN_ERR_EINT2_WIDTH 1 /* IM_CLKGEN_ERR_EINT2 */ +#define ARIZONA_IM_CLKGEN_ERR_ASYNC_EINT2 0x0001 /* IM_CLKGEN_ERR_ASYNC_EINT2 */ +#define ARIZONA_IM_CLKGEN_ERR_ASYNC_EINT2_MASK 0x0001 /* IM_CLKGEN_ERR_ASYNC_EINT2 */ +#define ARIZONA_IM_CLKGEN_ERR_ASYNC_EINT2_SHIFT 0 /* IM_CLKGEN_ERR_ASYNC_EINT2 */ +#define ARIZONA_IM_CLKGEN_ERR_ASYNC_EINT2_WIDTH 1 /* IM_CLKGEN_ERR_ASYNC_EINT2 */ + +/* + * R3355 (0xD1B) - IRQ2 Status 4 Mask + */ +#define ARIZONA_IM_ASRC_CFG_ERR_EINT2 0x8000 /* IM_ASRC_CFG_ERR_EINT2 */ +#define ARIZONA_IM_ASRC_CFG_ERR_EINT2_MASK 0x8000 /* IM_ASRC_CFG_ERR_EINT2 */ +#define ARIZONA_IM_ASRC_CFG_ERR_EINT2_SHIFT 15 /* IM_ASRC_CFG_ERR_EINT2 */ +#define ARIZONA_IM_ASRC_CFG_ERR_EINT2_WIDTH 1 /* IM_ASRC_CFG_ERR_EINT2 */ +#define ARIZONA_IM_AIF3_ERR_EINT2 0x4000 /* IM_AIF3_ERR_EINT2 */ +#define ARIZONA_IM_AIF3_ERR_EINT2_MASK 0x4000 /* IM_AIF3_ERR_EINT2 */ +#define ARIZONA_IM_AIF3_ERR_EINT2_SHIFT 14 /* IM_AIF3_ERR_EINT2 */ +#define ARIZONA_IM_AIF3_ERR_EINT2_WIDTH 1 /* IM_AIF3_ERR_EINT2 */ +#define ARIZONA_IM_AIF2_ERR_EINT2 0x2000 /* IM_AIF2_ERR_EINT2 */ +#define ARIZONA_IM_AIF2_ERR_EINT2_MASK 0x2000 /* IM_AIF2_ERR_EINT2 */ +#define ARIZONA_IM_AIF2_ERR_EINT2_SHIFT 13 /* IM_AIF2_ERR_EINT2 */ +#define ARIZONA_IM_AIF2_ERR_EINT2_WIDTH 1 /* IM_AIF2_ERR_EINT2 */ +#define ARIZONA_IM_AIF1_ERR_EINT2 0x1000 /* IM_AIF1_ERR_EINT2 */ +#define ARIZONA_IM_AIF1_ERR_EINT2_MASK 0x1000 /* IM_AIF1_ERR_EINT2 */ +#define ARIZONA_IM_AIF1_ERR_EINT2_SHIFT 12 /* IM_AIF1_ERR_EINT2 */ +#define ARIZONA_IM_AIF1_ERR_EINT2_WIDTH 1 /* IM_AIF1_ERR_EINT2 */ +#define ARIZONA_IM_CTRLIF_ERR_EINT2 0x0800 /* IM_CTRLIF_ERR_EINT2 */ +#define ARIZONA_IM_CTRLIF_ERR_EINT2_MASK 0x0800 /* IM_CTRLIF_ERR_EINT2 */ +#define ARIZONA_IM_CTRLIF_ERR_EINT2_SHIFT 11 /* IM_CTRLIF_ERR_EINT2 */ +#define ARIZONA_IM_CTRLIF_ERR_EINT2_WIDTH 1 /* IM_CTRLIF_ERR_EINT2 */ +#define ARIZONA_IM_MIXER_DROPPED_SAMPLE_EINT2 0x0400 /* IM_MIXER_DROPPED_SAMPLE_EINT2 */ +#define ARIZONA_IM_MIXER_DROPPED_SAMPLE_EINT2_MASK 0x0400 /* IM_MIXER_DROPPED_SAMPLE_EINT2 */ +#define ARIZONA_IM_MIXER_DROPPED_SAMPLE_EINT2_SHIFT 10 /* IM_MIXER_DROPPED_SAMPLE_EINT2 */ +#define ARIZONA_IM_MIXER_DROPPED_SAMPLE_EINT2_WIDTH 1 /* IM_MIXER_DROPPED_SAMPLE_EINT2 */ +#define ARIZONA_IM_ASYNC_CLK_ENA_LOW_EINT2 0x0200 /* IM_ASYNC_CLK_ENA_LOW_EINT2 */ +#define ARIZONA_IM_ASYNC_CLK_ENA_LOW_EINT2_MASK 0x0200 /* IM_ASYNC_CLK_ENA_LOW_EINT2 */ +#define ARIZONA_IM_ASYNC_CLK_ENA_LOW_EINT2_SHIFT 9 /* IM_ASYNC_CLK_ENA_LOW_EINT2 */ +#define ARIZONA_IM_ASYNC_CLK_ENA_LOW_EINT2_WIDTH 1 /* IM_ASYNC_CLK_ENA_LOW_EINT2 */ +#define ARIZONA_IM_SYSCLK_ENA_LOW_EINT2 0x0100 /* IM_SYSCLK_ENA_LOW_EINT2 */ +#define ARIZONA_IM_SYSCLK_ENA_LOW_EINT2_MASK 0x0100 /* IM_SYSCLK_ENA_LOW_EINT2 */ +#define ARIZONA_IM_SYSCLK_ENA_LOW_EINT2_SHIFT 8 /* IM_SYSCLK_ENA_LOW_EINT2 */ +#define ARIZONA_IM_SYSCLK_ENA_LOW_EINT2_WIDTH 1 /* IM_SYSCLK_ENA_LOW_EINT2 */ +#define ARIZONA_IM_ISRC1_CFG_ERR_EINT2 0x0080 /* IM_ISRC1_CFG_ERR_EINT2 */ +#define ARIZONA_IM_ISRC1_CFG_ERR_EINT2_MASK 0x0080 /* IM_ISRC1_CFG_ERR_EINT2 */ +#define ARIZONA_IM_ISRC1_CFG_ERR_EINT2_SHIFT 7 /* IM_ISRC1_CFG_ERR_EINT2 */ +#define ARIZONA_IM_ISRC1_CFG_ERR_EINT2_WIDTH 1 /* IM_ISRC1_CFG_ERR_EINT2 */ +#define ARIZONA_IM_ISRC2_CFG_ERR_EINT2 0x0040 /* IM_ISRC2_CFG_ERR_EINT2 */ +#define ARIZONA_IM_ISRC2_CFG_ERR_EINT2_MASK 0x0040 /* IM_ISRC2_CFG_ERR_EINT2 */ +#define ARIZONA_IM_ISRC2_CFG_ERR_EINT2_SHIFT 6 /* IM_ISRC2_CFG_ERR_EINT2 */ +#define ARIZONA_IM_ISRC2_CFG_ERR_EINT2_WIDTH 1 /* IM_ISRC2_CFG_ERR_EINT2 */ + +/* + * R3356 (0xD1C) - IRQ2 Status 5 Mask + */ + +#define ARIZONA_IM_BOOT_DONE_EINT2 0x0100 /* IM_BOOT_DONE_EINT2 */ +#define ARIZONA_IM_BOOT_DONE_EINT2_MASK 0x0100 /* IM_BOOT_DONE_EINT2 */ +#define ARIZONA_IM_BOOT_DONE_EINT2_SHIFT 8 /* IM_BOOT_DONE_EINT2 */ +#define ARIZONA_IM_BOOT_DONE_EINT2_WIDTH 1 /* IM_BOOT_DONE_EINT2 */ +#define ARIZONA_IM_DCS_DAC_DONE_EINT2 0x0080 /* IM_DCS_DAC_DONE_EINT2 */ +#define ARIZONA_IM_DCS_DAC_DONE_EINT2_MASK 0x0080 /* IM_DCS_DAC_DONE_EINT2 */ +#define ARIZONA_IM_DCS_DAC_DONE_EINT2_SHIFT 7 /* IM_DCS_DAC_DONE_EINT2 */ +#define ARIZONA_IM_DCS_DAC_DONE_EINT2_WIDTH 1 /* IM_DCS_DAC_DONE_EINT2 */ +#define ARIZONA_IM_DCS_HP_DONE_EINT2 0x0040 /* IM_DCS_HP_DONE_EINT2 */ +#define ARIZONA_IM_DCS_HP_DONE_EINT2_MASK 0x0040 /* IM_DCS_HP_DONE_EINT2 */ +#define ARIZONA_IM_DCS_HP_DONE_EINT2_SHIFT 6 /* IM_DCS_HP_DONE_EINT2 */ +#define ARIZONA_IM_DCS_HP_DONE_EINT2_WIDTH 1 /* IM_DCS_HP_DONE_EINT2 */ +#define ARIZONA_IM_FLL2_CLOCK_OK_EINT2 0x0002 /* IM_FLL2_CLOCK_OK_EINT2 */ +#define ARIZONA_IM_FLL2_CLOCK_OK_EINT2_MASK 0x0002 /* IM_FLL2_CLOCK_OK_EINT2 */ +#define ARIZONA_IM_FLL2_CLOCK_OK_EINT2_SHIFT 1 /* IM_FLL2_CLOCK_OK_EINT2 */ +#define ARIZONA_IM_FLL2_CLOCK_OK_EINT2_WIDTH 1 /* IM_FLL2_CLOCK_OK_EINT2 */ +#define ARIZONA_IM_FLL1_CLOCK_OK_EINT2 0x0001 /* IM_FLL1_CLOCK_OK_EINT2 */ +#define ARIZONA_IM_FLL1_CLOCK_OK_EINT2_MASK 0x0001 /* IM_FLL1_CLOCK_OK_EINT2 */ +#define ARIZONA_IM_FLL1_CLOCK_OK_EINT2_SHIFT 0 /* IM_FLL1_CLOCK_OK_EINT2 */ +#define ARIZONA_IM_FLL1_CLOCK_OK_EINT2_WIDTH 1 /* IM_FLL1_CLOCK_OK_EINT2 */ + +/* + * R3359 (0xD1F) - IRQ2 Control + */ +#define ARIZONA_IM_IRQ2 0x0001 /* IM_IRQ2 */ +#define ARIZONA_IM_IRQ2_MASK 0x0001 /* IM_IRQ2 */ +#define ARIZONA_IM_IRQ2_SHIFT 0 /* IM_IRQ2 */ +#define ARIZONA_IM_IRQ2_WIDTH 1 /* IM_IRQ2 */ + +/* + * R3360 (0xD20) - Interrupt Raw Status 2 + */ +#define ARIZONA_DSP1_RAM_RDY_STS 0x0100 /* DSP1_RAM_RDY_STS */ +#define ARIZONA_DSP1_RAM_RDY_STS_MASK 0x0100 /* DSP1_RAM_RDY_STS */ +#define ARIZONA_DSP1_RAM_RDY_STS_SHIFT 8 /* DSP1_RAM_RDY_STS */ +#define ARIZONA_DSP1_RAM_RDY_STS_WIDTH 1 /* DSP1_RAM_RDY_STS */ +#define ARIZONA_DSP_IRQ2_STS 0x0002 /* DSP_IRQ2_STS */ +#define ARIZONA_DSP_IRQ2_STS_MASK 0x0002 /* DSP_IRQ2_STS */ +#define ARIZONA_DSP_IRQ2_STS_SHIFT 1 /* DSP_IRQ2_STS */ +#define ARIZONA_DSP_IRQ2_STS_WIDTH 1 /* DSP_IRQ2_STS */ +#define ARIZONA_DSP_IRQ1_STS 0x0001 /* DSP_IRQ1_STS */ +#define ARIZONA_DSP_IRQ1_STS_MASK 0x0001 /* DSP_IRQ1_STS */ +#define ARIZONA_DSP_IRQ1_STS_SHIFT 0 /* DSP_IRQ1_STS */ +#define ARIZONA_DSP_IRQ1_STS_WIDTH 1 /* DSP_IRQ1_STS */ + +/* + * R3361 (0xD21) - Interrupt Raw Status 3 + */ +#define ARIZONA_SPK_SHUTDOWN_WARN_STS 0x8000 /* SPK_SHUTDOWN_WARN_STS */ +#define ARIZONA_SPK_SHUTDOWN_WARN_STS_MASK 0x8000 /* SPK_SHUTDOWN_WARN_STS */ +#define ARIZONA_SPK_SHUTDOWN_WARN_STS_SHIFT 15 /* SPK_SHUTDOWN_WARN_STS */ +#define ARIZONA_SPK_SHUTDOWN_WARN_STS_WIDTH 1 /* SPK_SHUTDOWN_WARN_STS */ +#define ARIZONA_SPK_SHUTDOWN_STS 0x4000 /* SPK_SHUTDOWN_STS */ +#define ARIZONA_SPK_SHUTDOWN_STS_MASK 0x4000 /* SPK_SHUTDOWN_STS */ +#define ARIZONA_SPK_SHUTDOWN_STS_SHIFT 14 /* SPK_SHUTDOWN_STS */ +#define ARIZONA_SPK_SHUTDOWN_STS_WIDTH 1 /* SPK_SHUTDOWN_STS */ +#define ARIZONA_HPDET_STS 0x2000 /* HPDET_STS */ +#define ARIZONA_HPDET_STS_MASK 0x2000 /* HPDET_STS */ +#define ARIZONA_HPDET_STS_SHIFT 13 /* HPDET_STS */ +#define ARIZONA_HPDET_STS_WIDTH 1 /* HPDET_STS */ +#define ARIZONA_MICDET_STS 0x1000 /* MICDET_STS */ +#define ARIZONA_MICDET_STS_MASK 0x1000 /* MICDET_STS */ +#define ARIZONA_MICDET_STS_SHIFT 12 /* MICDET_STS */ +#define ARIZONA_MICDET_STS_WIDTH 1 /* MICDET_STS */ +#define ARIZONA_WSEQ_DONE_STS 0x0800 /* WSEQ_DONE_STS */ +#define ARIZONA_WSEQ_DONE_STS_MASK 0x0800 /* WSEQ_DONE_STS */ +#define ARIZONA_WSEQ_DONE_STS_SHIFT 11 /* WSEQ_DONE_STS */ +#define ARIZONA_WSEQ_DONE_STS_WIDTH 1 /* WSEQ_DONE_STS */ +#define ARIZONA_DRC2_SIG_DET_STS 0x0400 /* DRC2_SIG_DET_STS */ +#define ARIZONA_DRC2_SIG_DET_STS_MASK 0x0400 /* DRC2_SIG_DET_STS */ +#define ARIZONA_DRC2_SIG_DET_STS_SHIFT 10 /* DRC2_SIG_DET_STS */ +#define ARIZONA_DRC2_SIG_DET_STS_WIDTH 1 /* DRC2_SIG_DET_STS */ +#define ARIZONA_DRC1_SIG_DET_STS 0x0200 /* DRC1_SIG_DET_STS */ +#define ARIZONA_DRC1_SIG_DET_STS_MASK 0x0200 /* DRC1_SIG_DET_STS */ +#define ARIZONA_DRC1_SIG_DET_STS_SHIFT 9 /* DRC1_SIG_DET_STS */ +#define ARIZONA_DRC1_SIG_DET_STS_WIDTH 1 /* DRC1_SIG_DET_STS */ +#define ARIZONA_ASRC2_LOCK_STS 0x0100 /* ASRC2_LOCK_STS */ +#define ARIZONA_ASRC2_LOCK_STS_MASK 0x0100 /* ASRC2_LOCK_STS */ +#define ARIZONA_ASRC2_LOCK_STS_SHIFT 8 /* ASRC2_LOCK_STS */ +#define ARIZONA_ASRC2_LOCK_STS_WIDTH 1 /* ASRC2_LOCK_STS */ +#define ARIZONA_ASRC1_LOCK_STS 0x0080 /* ASRC1_LOCK_STS */ +#define ARIZONA_ASRC1_LOCK_STS_MASK 0x0080 /* ASRC1_LOCK_STS */ +#define ARIZONA_ASRC1_LOCK_STS_SHIFT 7 /* ASRC1_LOCK_STS */ +#define ARIZONA_ASRC1_LOCK_STS_WIDTH 1 /* ASRC1_LOCK_STS */ +#define ARIZONA_UNDERCLOCKED_STS 0x0040 /* UNDERCLOCKED_STS */ +#define ARIZONA_UNDERCLOCKED_STS_MASK 0x0040 /* UNDERCLOCKED_STS */ +#define ARIZONA_UNDERCLOCKED_STS_SHIFT 6 /* UNDERCLOCKED_STS */ +#define ARIZONA_UNDERCLOCKED_STS_WIDTH 1 /* UNDERCLOCKED_STS */ +#define ARIZONA_OVERCLOCKED_STS 0x0020 /* OVERCLOCKED_STS */ +#define ARIZONA_OVERCLOCKED_STS_MASK 0x0020 /* OVERCLOCKED_STS */ +#define ARIZONA_OVERCLOCKED_STS_SHIFT 5 /* OVERCLOCKED_STS */ +#define ARIZONA_OVERCLOCKED_STS_WIDTH 1 /* OVERCLOCKED_STS */ +#define ARIZONA_FLL2_LOCK_STS 0x0008 /* FLL2_LOCK_STS */ +#define ARIZONA_FLL2_LOCK_STS_MASK 0x0008 /* FLL2_LOCK_STS */ +#define ARIZONA_FLL2_LOCK_STS_SHIFT 3 /* FLL2_LOCK_STS */ +#define ARIZONA_FLL2_LOCK_STS_WIDTH 1 /* FLL2_LOCK_STS */ +#define ARIZONA_FLL1_LOCK_STS 0x0004 /* FLL1_LOCK_STS */ +#define ARIZONA_FLL1_LOCK_STS_MASK 0x0004 /* FLL1_LOCK_STS */ +#define ARIZONA_FLL1_LOCK_STS_SHIFT 2 /* FLL1_LOCK_STS */ +#define ARIZONA_FLL1_LOCK_STS_WIDTH 1 /* FLL1_LOCK_STS */ +#define ARIZONA_CLKGEN_ERR_STS 0x0002 /* CLKGEN_ERR_STS */ +#define ARIZONA_CLKGEN_ERR_STS_MASK 0x0002 /* CLKGEN_ERR_STS */ +#define ARIZONA_CLKGEN_ERR_STS_SHIFT 1 /* CLKGEN_ERR_STS */ +#define ARIZONA_CLKGEN_ERR_STS_WIDTH 1 /* CLKGEN_ERR_STS */ +#define ARIZONA_CLKGEN_ERR_ASYNC_STS 0x0001 /* CLKGEN_ERR_ASYNC_STS */ +#define ARIZONA_CLKGEN_ERR_ASYNC_STS_MASK 0x0001 /* CLKGEN_ERR_ASYNC_STS */ +#define ARIZONA_CLKGEN_ERR_ASYNC_STS_SHIFT 0 /* CLKGEN_ERR_ASYNC_STS */ +#define ARIZONA_CLKGEN_ERR_ASYNC_STS_WIDTH 1 /* CLKGEN_ERR_ASYNC_STS */ + +/* + * R3362 (0xD22) - Interrupt Raw Status 4 + */ +#define ARIZONA_ASRC_CFG_ERR_STS 0x8000 /* ASRC_CFG_ERR_STS */ +#define ARIZONA_ASRC_CFG_ERR_STS_MASK 0x8000 /* ASRC_CFG_ERR_STS */ +#define ARIZONA_ASRC_CFG_ERR_STS_SHIFT 15 /* ASRC_CFG_ERR_STS */ +#define ARIZONA_ASRC_CFG_ERR_STS_WIDTH 1 /* ASRC_CFG_ERR_STS */ +#define ARIZONA_AIF3_ERR_STS 0x4000 /* AIF3_ERR_STS */ +#define ARIZONA_AIF3_ERR_STS_MASK 0x4000 /* AIF3_ERR_STS */ +#define ARIZONA_AIF3_ERR_STS_SHIFT 14 /* AIF3_ERR_STS */ +#define ARIZONA_AIF3_ERR_STS_WIDTH 1 /* AIF3_ERR_STS */ +#define ARIZONA_AIF2_ERR_STS 0x2000 /* AIF2_ERR_STS */ +#define ARIZONA_AIF2_ERR_STS_MASK 0x2000 /* AIF2_ERR_STS */ +#define ARIZONA_AIF2_ERR_STS_SHIFT 13 /* AIF2_ERR_STS */ +#define ARIZONA_AIF2_ERR_STS_WIDTH 1 /* AIF2_ERR_STS */ +#define ARIZONA_AIF1_ERR_STS 0x1000 /* AIF1_ERR_STS */ +#define ARIZONA_AIF1_ERR_STS_MASK 0x1000 /* AIF1_ERR_STS */ +#define ARIZONA_AIF1_ERR_STS_SHIFT 12 /* AIF1_ERR_STS */ +#define ARIZONA_AIF1_ERR_STS_WIDTH 1 /* AIF1_ERR_STS */ +#define ARIZONA_CTRLIF_ERR_STS 0x0800 /* CTRLIF_ERR_STS */ +#define ARIZONA_CTRLIF_ERR_STS_MASK 0x0800 /* CTRLIF_ERR_STS */ +#define ARIZONA_CTRLIF_ERR_STS_SHIFT 11 /* CTRLIF_ERR_STS */ +#define ARIZONA_CTRLIF_ERR_STS_WIDTH 1 /* CTRLIF_ERR_STS */ +#define ARIZONA_MIXER_DROPPED_SAMPLE_STS 0x0400 /* MIXER_DROPPED_SAMPLE_STS */ +#define ARIZONA_MIXER_DROPPED_SAMPLE_STS_MASK 0x0400 /* MIXER_DROPPED_SAMPLE_STS */ +#define ARIZONA_MIXER_DROPPED_SAMPLE_STS_SHIFT 10 /* MIXER_DROPPED_SAMPLE_STS */ +#define ARIZONA_MIXER_DROPPED_SAMPLE_STS_WIDTH 1 /* MIXER_DROPPED_SAMPLE_STS */ +#define ARIZONA_ASYNC_CLK_ENA_LOW_STS 0x0200 /* ASYNC_CLK_ENA_LOW_STS */ +#define ARIZONA_ASYNC_CLK_ENA_LOW_STS_MASK 0x0200 /* ASYNC_CLK_ENA_LOW_STS */ +#define ARIZONA_ASYNC_CLK_ENA_LOW_STS_SHIFT 9 /* ASYNC_CLK_ENA_LOW_STS */ +#define ARIZONA_ASYNC_CLK_ENA_LOW_STS_WIDTH 1 /* ASYNC_CLK_ENA_LOW_STS */ +#define ARIZONA_SYSCLK_ENA_LOW_STS 0x0100 /* SYSCLK_ENA_LOW_STS */ +#define ARIZONA_SYSCLK_ENA_LOW_STS_MASK 0x0100 /* SYSCLK_ENA_LOW_STS */ +#define ARIZONA_SYSCLK_ENA_LOW_STS_SHIFT 8 /* SYSCLK_ENA_LOW_STS */ +#define ARIZONA_SYSCLK_ENA_LOW_STS_WIDTH 1 /* SYSCLK_ENA_LOW_STS */ +#define ARIZONA_ISRC1_CFG_ERR_STS 0x0080 /* ISRC1_CFG_ERR_STS */ +#define ARIZONA_ISRC1_CFG_ERR_STS_MASK 0x0080 /* ISRC1_CFG_ERR_STS */ +#define ARIZONA_ISRC1_CFG_ERR_STS_SHIFT 7 /* ISRC1_CFG_ERR_STS */ +#define ARIZONA_ISRC1_CFG_ERR_STS_WIDTH 1 /* ISRC1_CFG_ERR_STS */ +#define ARIZONA_ISRC2_CFG_ERR_STS 0x0040 /* ISRC2_CFG_ERR_STS */ +#define ARIZONA_ISRC2_CFG_ERR_STS_MASK 0x0040 /* ISRC2_CFG_ERR_STS */ +#define ARIZONA_ISRC2_CFG_ERR_STS_SHIFT 6 /* ISRC2_CFG_ERR_STS */ +#define ARIZONA_ISRC2_CFG_ERR_STS_WIDTH 1 /* ISRC2_CFG_ERR_STS */ + +/* + * R3363 (0xD23) - Interrupt Raw Status 5 + */ +#define ARIZONA_BOOT_DONE_STS 0x0100 /* BOOT_DONE_STS */ +#define ARIZONA_BOOT_DONE_STS_MASK 0x0100 /* BOOT_DONE_STS */ +#define ARIZONA_BOOT_DONE_STS_SHIFT 8 /* BOOT_DONE_STS */ +#define ARIZONA_BOOT_DONE_STS_WIDTH 1 /* BOOT_DONE_STS */ +#define ARIZONA_DCS_DAC_DONE_STS 0x0080 /* DCS_DAC_DONE_STS */ +#define ARIZONA_DCS_DAC_DONE_STS_MASK 0x0080 /* DCS_DAC_DONE_STS */ +#define ARIZONA_DCS_DAC_DONE_STS_SHIFT 7 /* DCS_DAC_DONE_STS */ +#define ARIZONA_DCS_DAC_DONE_STS_WIDTH 1 /* DCS_DAC_DONE_STS */ +#define ARIZONA_DCS_HP_DONE_STS 0x0040 /* DCS_HP_DONE_STS */ +#define ARIZONA_DCS_HP_DONE_STS_MASK 0x0040 /* DCS_HP_DONE_STS */ +#define ARIZONA_DCS_HP_DONE_STS_SHIFT 6 /* DCS_HP_DONE_STS */ +#define ARIZONA_DCS_HP_DONE_STS_WIDTH 1 /* DCS_HP_DONE_STS */ +#define ARIZONA_FLL2_CLOCK_OK_STS 0x0002 /* FLL2_CLOCK_OK_STS */ +#define ARIZONA_FLL2_CLOCK_OK_STS_MASK 0x0002 /* FLL2_CLOCK_OK_STS */ +#define ARIZONA_FLL2_CLOCK_OK_STS_SHIFT 1 /* FLL2_CLOCK_OK_STS */ +#define ARIZONA_FLL2_CLOCK_OK_STS_WIDTH 1 /* FLL2_CLOCK_OK_STS */ +#define ARIZONA_FLL1_CLOCK_OK_STS 0x0001 /* FLL1_CLOCK_OK_STS */ +#define ARIZONA_FLL1_CLOCK_OK_STS_MASK 0x0001 /* FLL1_CLOCK_OK_STS */ +#define ARIZONA_FLL1_CLOCK_OK_STS_SHIFT 0 /* FLL1_CLOCK_OK_STS */ +#define ARIZONA_FLL1_CLOCK_OK_STS_WIDTH 1 /* FLL1_CLOCK_OK_STS */ + +/* + * R3364 (0xD24) - Interrupt Raw Status 6 + */ +#define ARIZONA_PWM_OVERCLOCKED_STS 0x2000 /* PWM_OVERCLOCKED_STS */ +#define ARIZONA_PWM_OVERCLOCKED_STS_MASK 0x2000 /* PWM_OVERCLOCKED_STS */ +#define ARIZONA_PWM_OVERCLOCKED_STS_SHIFT 13 /* PWM_OVERCLOCKED_STS */ +#define ARIZONA_PWM_OVERCLOCKED_STS_WIDTH 1 /* PWM_OVERCLOCKED_STS */ +#define ARIZONA_FX_CORE_OVERCLOCKED_STS 0x1000 /* FX_CORE_OVERCLOCKED_STS */ +#define ARIZONA_FX_CORE_OVERCLOCKED_STS_MASK 0x1000 /* FX_CORE_OVERCLOCKED_STS */ +#define ARIZONA_FX_CORE_OVERCLOCKED_STS_SHIFT 12 /* FX_CORE_OVERCLOCKED_STS */ +#define ARIZONA_FX_CORE_OVERCLOCKED_STS_WIDTH 1 /* FX_CORE_OVERCLOCKED_STS */ +#define ARIZONA_DAC_SYS_OVERCLOCKED_STS 0x0400 /* DAC_SYS_OVERCLOCKED_STS */ +#define ARIZONA_DAC_SYS_OVERCLOCKED_STS_MASK 0x0400 /* DAC_SYS_OVERCLOCKED_STS */ +#define ARIZONA_DAC_SYS_OVERCLOCKED_STS_SHIFT 10 /* DAC_SYS_OVERCLOCKED_STS */ +#define ARIZONA_DAC_SYS_OVERCLOCKED_STS_WIDTH 1 /* DAC_SYS_OVERCLOCKED_STS */ +#define ARIZONA_DAC_WARP_OVERCLOCKED_STS 0x0200 /* DAC_WARP_OVERCLOCKED_STS */ +#define ARIZONA_DAC_WARP_OVERCLOCKED_STS_MASK 0x0200 /* DAC_WARP_OVERCLOCKED_STS */ +#define ARIZONA_DAC_WARP_OVERCLOCKED_STS_SHIFT 9 /* DAC_WARP_OVERCLOCKED_STS */ +#define ARIZONA_DAC_WARP_OVERCLOCKED_STS_WIDTH 1 /* DAC_WARP_OVERCLOCKED_STS */ +#define ARIZONA_ADC_OVERCLOCKED_STS 0x0100 /* ADC_OVERCLOCKED_STS */ +#define ARIZONA_ADC_OVERCLOCKED_STS_MASK 0x0100 /* ADC_OVERCLOCKED_STS */ +#define ARIZONA_ADC_OVERCLOCKED_STS_SHIFT 8 /* ADC_OVERCLOCKED_STS */ +#define ARIZONA_ADC_OVERCLOCKED_STS_WIDTH 1 /* ADC_OVERCLOCKED_STS */ +#define ARIZONA_MIXER_OVERCLOCKED_STS 0x0080 /* MIXER_OVERCLOCKED_STS */ +#define ARIZONA_MIXER_OVERCLOCKED_STS_MASK 0x0080 /* MIXER_OVERCLOCKED_STS */ +#define ARIZONA_MIXER_OVERCLOCKED_STS_SHIFT 7 /* MIXER_OVERCLOCKED_STS */ +#define ARIZONA_MIXER_OVERCLOCKED_STS_WIDTH 1 /* MIXER_OVERCLOCKED_STS */ +#define ARIZONA_AIF3_ASYNC_OVERCLOCKED_STS 0x0040 /* AIF3_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF3_ASYNC_OVERCLOCKED_STS_MASK 0x0040 /* AIF3_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF3_ASYNC_OVERCLOCKED_STS_SHIFT 6 /* AIF3_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF3_ASYNC_OVERCLOCKED_STS_WIDTH 1 /* AIF3_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF2_ASYNC_OVERCLOCKED_STS 0x0020 /* AIF2_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF2_ASYNC_OVERCLOCKED_STS_MASK 0x0020 /* AIF2_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF2_ASYNC_OVERCLOCKED_STS_SHIFT 5 /* AIF2_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF2_ASYNC_OVERCLOCKED_STS_WIDTH 1 /* AIF2_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF1_ASYNC_OVERCLOCKED_STS 0x0010 /* AIF1_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF1_ASYNC_OVERCLOCKED_STS_MASK 0x0010 /* AIF1_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF1_ASYNC_OVERCLOCKED_STS_SHIFT 4 /* AIF1_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF1_ASYNC_OVERCLOCKED_STS_WIDTH 1 /* AIF1_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF3_SYNC_OVERCLOCKED_STS 0x0008 /* AIF3_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF3_SYNC_OVERCLOCKED_STS_MASK 0x0008 /* AIF3_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF3_SYNC_OVERCLOCKED_STS_SHIFT 3 /* AIF3_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF3_SYNC_OVERCLOCKED_STS_WIDTH 1 /* AIF3_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF2_SYNC_OVERCLOCKED_STS 0x0004 /* AIF2_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF2_SYNC_OVERCLOCKED_STS_MASK 0x0004 /* AIF2_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF2_SYNC_OVERCLOCKED_STS_SHIFT 2 /* AIF2_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF2_SYNC_OVERCLOCKED_STS_WIDTH 1 /* AIF2_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF1_SYNC_OVERCLOCKED_STS 0x0002 /* AIF1_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF1_SYNC_OVERCLOCKED_STS_MASK 0x0002 /* AIF1_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF1_SYNC_OVERCLOCKED_STS_SHIFT 1 /* AIF1_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF1_SYNC_OVERCLOCKED_STS_WIDTH 1 /* AIF1_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_PAD_CTRL_OVERCLOCKED_STS 0x0001 /* PAD_CTRL_OVERCLOCKED_STS */ +#define ARIZONA_PAD_CTRL_OVERCLOCKED_STS_MASK 0x0001 /* PAD_CTRL_OVERCLOCKED_STS */ +#define ARIZONA_PAD_CTRL_OVERCLOCKED_STS_SHIFT 0 /* PAD_CTRL_OVERCLOCKED_STS */ +#define ARIZONA_PAD_CTRL_OVERCLOCKED_STS_WIDTH 1 /* PAD_CTRL_OVERCLOCKED_STS */ + +/* + * R3365 (0xD25) - Interrupt Raw Status 7 + */ +#define ARIZONA_SLIMBUS_SUBSYS_OVERCLOCKED_STS 0x8000 /* SLIMBUS_SUBSYS_OVERCLOCKED_STS */ +#define ARIZONA_SLIMBUS_SUBSYS_OVERCLOCKED_STS_MASK 0x8000 /* SLIMBUS_SUBSYS_OVERCLOCKED_STS */ +#define ARIZONA_SLIMBUS_SUBSYS_OVERCLOCKED_STS_SHIFT 15 /* SLIMBUS_SUBSYS_OVERCLOCKED_STS */ +#define ARIZONA_SLIMBUS_SUBSYS_OVERCLOCKED_STS_WIDTH 1 /* SLIMBUS_SUBSYS_OVERCLOCKED_STS */ +#define ARIZONA_SLIMBUS_ASYNC_OVERCLOCKED_STS 0x4000 /* SLIMBUS_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_SLIMBUS_ASYNC_OVERCLOCKED_STS_MASK 0x4000 /* SLIMBUS_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_SLIMBUS_ASYNC_OVERCLOCKED_STS_SHIFT 14 /* SLIMBUS_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_SLIMBUS_ASYNC_OVERCLOCKED_STS_WIDTH 1 /* SLIMBUS_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_SLIMBUS_SYNC_OVERCLOCKED_STS 0x2000 /* SLIMBUS_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_SLIMBUS_SYNC_OVERCLOCKED_STS_MASK 0x2000 /* SLIMBUS_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_SLIMBUS_SYNC_OVERCLOCKED_STS_SHIFT 13 /* SLIMBUS_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_SLIMBUS_SYNC_OVERCLOCKED_STS_WIDTH 1 /* SLIMBUS_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_ASYNC_SYS_OVERCLOCKED_STS 0x1000 /* ASRC_ASYNC_SYS_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_ASYNC_SYS_OVERCLOCKED_STS_MASK 0x1000 /* ASRC_ASYNC_SYS_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_ASYNC_SYS_OVERCLOCKED_STS_SHIFT 12 /* ASRC_ASYNC_SYS_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_ASYNC_SYS_OVERCLOCKED_STS_WIDTH 1 /* ASRC_ASYNC_SYS_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_ASYNC_WARP_OVERCLOCKED_STS 0x0800 /* ASRC_ASYNC_WARP_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_ASYNC_WARP_OVERCLOCKED_STS_MASK 0x0800 /* ASRC_ASYNC_WARP_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_ASYNC_WARP_OVERCLOCKED_STS_SHIFT 11 /* ASRC_ASYNC_WARP_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_ASYNC_WARP_OVERCLOCKED_STS_WIDTH 1 /* ASRC_ASYNC_WARP_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_SYNC_SYS_OVERCLOCKED_STS 0x0400 /* ASRC_SYNC_SYS_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_SYNC_SYS_OVERCLOCKED_STS_MASK 0x0400 /* ASRC_SYNC_SYS_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_SYNC_SYS_OVERCLOCKED_STS_SHIFT 10 /* ASRC_SYNC_SYS_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_SYNC_SYS_OVERCLOCKED_STS_WIDTH 1 /* ASRC_SYNC_SYS_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_SYNC_WARP_OVERCLOCKED_STS 0x0200 /* ASRC_SYNC_WARP_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_SYNC_WARP_OVERCLOCKED_STS_MASK 0x0200 /* ASRC_SYNC_WARP_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_SYNC_WARP_OVERCLOCKED_STS_SHIFT 9 /* ASRC_SYNC_WARP_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_SYNC_WARP_OVERCLOCKED_STS_WIDTH 1 /* ASRC_SYNC_WARP_OVERCLOCKED_STS */ +#define ARIZONA_ADSP2_1_OVERCLOCKED_STS 0x0008 /* ADSP2_1_OVERCLOCKED_STS */ +#define ARIZONA_ADSP2_1_OVERCLOCKED_STS_MASK 0x0008 /* ADSP2_1_OVERCLOCKED_STS */ +#define ARIZONA_ADSP2_1_OVERCLOCKED_STS_SHIFT 3 /* ADSP2_1_OVERCLOCKED_STS */ +#define ARIZONA_ADSP2_1_OVERCLOCKED_STS_WIDTH 1 /* ADSP2_1_OVERCLOCKED_STS */ +#define ARIZONA_ISRC2_OVERCLOCKED_STS 0x0002 /* ISRC2_OVERCLOCKED_STS */ +#define ARIZONA_ISRC2_OVERCLOCKED_STS_MASK 0x0002 /* ISRC2_OVERCLOCKED_STS */ +#define ARIZONA_ISRC2_OVERCLOCKED_STS_SHIFT 1 /* ISRC2_OVERCLOCKED_STS */ +#define ARIZONA_ISRC2_OVERCLOCKED_STS_WIDTH 1 /* ISRC2_OVERCLOCKED_STS */ +#define ARIZONA_ISRC1_OVERCLOCKED_STS 0x0001 /* ISRC1_OVERCLOCKED_STS */ +#define ARIZONA_ISRC1_OVERCLOCKED_STS_MASK 0x0001 /* ISRC1_OVERCLOCKED_STS */ +#define ARIZONA_ISRC1_OVERCLOCKED_STS_SHIFT 0 /* ISRC1_OVERCLOCKED_STS */ +#define ARIZONA_ISRC1_OVERCLOCKED_STS_WIDTH 1 /* ISRC1_OVERCLOCKED_STS */ + +/* + * R3366 (0xD26) - Interrupt Raw Status 8 + */ +#define ARIZONA_AIF3_UNDERCLOCKED_STS 0x0400 /* AIF3_UNDERCLOCKED_STS */ +#define ARIZONA_AIF3_UNDERCLOCKED_STS_MASK 0x0400 /* AIF3_UNDERCLOCKED_STS */ +#define ARIZONA_AIF3_UNDERCLOCKED_STS_SHIFT 10 /* AIF3_UNDERCLOCKED_STS */ +#define ARIZONA_AIF3_UNDERCLOCKED_STS_WIDTH 1 /* AIF3_UNDERCLOCKED_STS */ +#define ARIZONA_AIF2_UNDERCLOCKED_STS 0x0200 /* AIF2_UNDERCLOCKED_STS */ +#define ARIZONA_AIF2_UNDERCLOCKED_STS_MASK 0x0200 /* AIF2_UNDERCLOCKED_STS */ +#define ARIZONA_AIF2_UNDERCLOCKED_STS_SHIFT 9 /* AIF2_UNDERCLOCKED_STS */ +#define ARIZONA_AIF2_UNDERCLOCKED_STS_WIDTH 1 /* AIF2_UNDERCLOCKED_STS */ +#define ARIZONA_AIF1_UNDERCLOCKED_STS 0x0100 /* AIF1_UNDERCLOCKED_STS */ +#define ARIZONA_AIF1_UNDERCLOCKED_STS_MASK 0x0100 /* AIF1_UNDERCLOCKED_STS */ +#define ARIZONA_AIF1_UNDERCLOCKED_STS_SHIFT 8 /* AIF1_UNDERCLOCKED_STS */ +#define ARIZONA_AIF1_UNDERCLOCKED_STS_WIDTH 1 /* AIF1_UNDERCLOCKED_STS */ +#define ARIZONA_ISRC2_UNDERCLOCKED_STS 0x0040 /* ISRC2_UNDERCLOCKED_STS */ +#define ARIZONA_ISRC2_UNDERCLOCKED_STS_MASK 0x0040 /* ISRC2_UNDERCLOCKED_STS */ +#define ARIZONA_ISRC2_UNDERCLOCKED_STS_SHIFT 6 /* ISRC2_UNDERCLOCKED_STS */ +#define ARIZONA_ISRC2_UNDERCLOCKED_STS_WIDTH 1 /* ISRC2_UNDERCLOCKED_STS */ +#define ARIZONA_ISRC1_UNDERCLOCKED_STS 0x0020 /* ISRC1_UNDERCLOCKED_STS */ +#define ARIZONA_ISRC1_UNDERCLOCKED_STS_MASK 0x0020 /* ISRC1_UNDERCLOCKED_STS */ +#define ARIZONA_ISRC1_UNDERCLOCKED_STS_SHIFT 5 /* ISRC1_UNDERCLOCKED_STS */ +#define ARIZONA_ISRC1_UNDERCLOCKED_STS_WIDTH 1 /* ISRC1_UNDERCLOCKED_STS */ +#define ARIZONA_FX_UNDERCLOCKED_STS 0x0010 /* FX_UNDERCLOCKED_STS */ +#define ARIZONA_FX_UNDERCLOCKED_STS_MASK 0x0010 /* FX_UNDERCLOCKED_STS */ +#define ARIZONA_FX_UNDERCLOCKED_STS_SHIFT 4 /* FX_UNDERCLOCKED_STS */ +#define ARIZONA_FX_UNDERCLOCKED_STS_WIDTH 1 /* FX_UNDERCLOCKED_STS */ +#define ARIZONA_ASRC_UNDERCLOCKED_STS 0x0008 /* ASRC_UNDERCLOCKED_STS */ +#define ARIZONA_ASRC_UNDERCLOCKED_STS_MASK 0x0008 /* ASRC_UNDERCLOCKED_STS */ +#define ARIZONA_ASRC_UNDERCLOCKED_STS_SHIFT 3 /* ASRC_UNDERCLOCKED_STS */ +#define ARIZONA_ASRC_UNDERCLOCKED_STS_WIDTH 1 /* ASRC_UNDERCLOCKED_STS */ +#define ARIZONA_DAC_UNDERCLOCKED_STS 0x0004 /* DAC_UNDERCLOCKED_STS */ +#define ARIZONA_DAC_UNDERCLOCKED_STS_MASK 0x0004 /* DAC_UNDERCLOCKED_STS */ +#define ARIZONA_DAC_UNDERCLOCKED_STS_SHIFT 2 /* DAC_UNDERCLOCKED_STS */ +#define ARIZONA_DAC_UNDERCLOCKED_STS_WIDTH 1 /* DAC_UNDERCLOCKED_STS */ +#define ARIZONA_ADC_UNDERCLOCKED_STS 0x0002 /* ADC_UNDERCLOCKED_STS */ +#define ARIZONA_ADC_UNDERCLOCKED_STS_MASK 0x0002 /* ADC_UNDERCLOCKED_STS */ +#define ARIZONA_ADC_UNDERCLOCKED_STS_SHIFT 1 /* ADC_UNDERCLOCKED_STS */ +#define ARIZONA_ADC_UNDERCLOCKED_STS_WIDTH 1 /* ADC_UNDERCLOCKED_STS */ +#define ARIZONA_MIXER_UNDERCLOCKED_STS 0x0001 /* MIXER_UNDERCLOCKED_STS */ +#define ARIZONA_MIXER_UNDERCLOCKED_STS_MASK 0x0001 /* MIXER_UNDERCLOCKED_STS */ +#define ARIZONA_MIXER_UNDERCLOCKED_STS_SHIFT 0 /* MIXER_UNDERCLOCKED_STS */ +#define ARIZONA_MIXER_UNDERCLOCKED_STS_WIDTH 1 /* MIXER_UNDERCLOCKED_STS */ + +/* + * R3392 (0xD40) - IRQ Pin Status + */ +#define ARIZONA_IRQ2_STS 0x0002 /* IRQ2_STS */ +#define ARIZONA_IRQ2_STS_MASK 0x0002 /* IRQ2_STS */ +#define ARIZONA_IRQ2_STS_SHIFT 1 /* IRQ2_STS */ +#define ARIZONA_IRQ2_STS_WIDTH 1 /* IRQ2_STS */ +#define ARIZONA_IRQ1_STS 0x0001 /* IRQ1_STS */ +#define ARIZONA_IRQ1_STS_MASK 0x0001 /* IRQ1_STS */ +#define ARIZONA_IRQ1_STS_SHIFT 0 /* IRQ1_STS */ +#define ARIZONA_IRQ1_STS_WIDTH 1 /* IRQ1_STS */ + +/* + * R3393 (0xD41) - ADSP2 IRQ0 + */ +#define ARIZONA_DSP_IRQ2 0x0002 /* DSP_IRQ2 */ +#define ARIZONA_DSP_IRQ2_MASK 0x0002 /* DSP_IRQ2 */ +#define ARIZONA_DSP_IRQ2_SHIFT 1 /* DSP_IRQ2 */ +#define ARIZONA_DSP_IRQ2_WIDTH 1 /* DSP_IRQ2 */ +#define ARIZONA_DSP_IRQ1 0x0001 /* DSP_IRQ1 */ +#define ARIZONA_DSP_IRQ1_MASK 0x0001 /* DSP_IRQ1 */ +#define ARIZONA_DSP_IRQ1_SHIFT 0 /* DSP_IRQ1 */ +#define ARIZONA_DSP_IRQ1_WIDTH 1 /* DSP_IRQ1 */ + +/* + * R3408 (0xD50) - AOD wkup and trig + */ +#define ARIZONA_GP5_FALL_TRIG_STS 0x0020 /* GP5_FALL_TRIG_STS */ +#define ARIZONA_GP5_FALL_TRIG_STS_MASK 0x0020 /* GP5_FALL_TRIG_STS */ +#define ARIZONA_GP5_FALL_TRIG_STS_SHIFT 5 /* GP5_FALL_TRIG_STS */ +#define ARIZONA_GP5_FALL_TRIG_STS_WIDTH 1 /* GP5_FALL_TRIG_STS */ +#define ARIZONA_GP5_RISE_TRIG_STS 0x0010 /* GP5_RISE_TRIG_STS */ +#define ARIZONA_GP5_RISE_TRIG_STS_MASK 0x0010 /* GP5_RISE_TRIG_STS */ +#define ARIZONA_GP5_RISE_TRIG_STS_SHIFT 4 /* GP5_RISE_TRIG_STS */ +#define ARIZONA_GP5_RISE_TRIG_STS_WIDTH 1 /* GP5_RISE_TRIG_STS */ +#define ARIZONA_JD1_FALL_TRIG_STS 0x0008 /* JD1_FALL_TRIG_STS */ +#define ARIZONA_JD1_FALL_TRIG_STS_MASK 0x0008 /* JD1_FALL_TRIG_STS */ +#define ARIZONA_JD1_FALL_TRIG_STS_SHIFT 3 /* JD1_FALL_TRIG_STS */ +#define ARIZONA_JD1_FALL_TRIG_STS_WIDTH 1 /* JD1_FALL_TRIG_STS */ +#define ARIZONA_JD1_RISE_TRIG_STS 0x0004 /* JD1_RISE_TRIG_STS */ +#define ARIZONA_JD1_RISE_TRIG_STS_MASK 0x0004 /* JD1_RISE_TRIG_STS */ +#define ARIZONA_JD1_RISE_TRIG_STS_SHIFT 2 /* JD1_RISE_TRIG_STS */ +#define ARIZONA_JD1_RISE_TRIG_STS_WIDTH 1 /* JD1_RISE_TRIG_STS */ +#define ARIZONA_JD2_FALL_TRIG_STS 0x0002 /* JD2_FALL_TRIG_STS */ +#define ARIZONA_JD2_FALL_TRIG_STS_MASK 0x0002 /* JD2_FALL_TRIG_STS */ +#define ARIZONA_JD2_FALL_TRIG_STS_SHIFT 1 /* JD2_FALL_TRIG_STS */ +#define ARIZONA_JD2_FALL_TRIG_STS_WIDTH 1 /* JD2_FALL_TRIG_STS */ +#define ARIZONA_JD2_RISE_TRIG_STS 0x0001 /* JD2_RISE_TRIG_STS */ +#define ARIZONA_JD2_RISE_TRIG_STS_MASK 0x0001 /* JD2_RISE_TRIG_STS */ +#define ARIZONA_JD2_RISE_TRIG_STS_SHIFT 0 /* JD2_RISE_TRIG_STS */ +#define ARIZONA_JD2_RISE_TRIG_STS_WIDTH 1 /* JD2_RISE_TRIG_STS */ + +/* + * R3409 (0xD51) - AOD IRQ1 + */ +#define ARIZONA_GP5_FALL_EINT1 0x0020 /* GP5_FALL_EINT1 */ +#define ARIZONA_GP5_FALL_EINT1_MASK 0x0020 /* GP5_FALL_EINT1 */ +#define ARIZONA_GP5_FALL_EINT1_SHIFT 5 /* GP5_FALL_EINT1 */ +#define ARIZONA_GP5_FALL_EINT1_WIDTH 1 /* GP5_FALL_EINT1 */ +#define ARIZONA_GP5_RISE_EINT1 0x0010 /* GP5_RISE_EINT1 */ +#define ARIZONA_GP5_RISE_EINT1_MASK 0x0010 /* GP5_RISE_EINT1 */ +#define ARIZONA_GP5_RISE_EINT1_SHIFT 4 /* GP5_RISE_EINT1 */ +#define ARIZONA_GP5_RISE_EINT1_WIDTH 1 /* GP5_RISE_EINT1 */ +#define ARIZONA_JD1_FALL_EINT1 0x0008 /* JD1_FALL_EINT1 */ +#define ARIZONA_JD1_FALL_EINT1_MASK 0x0008 /* JD1_FALL_EINT1 */ +#define ARIZONA_JD1_FALL_EINT1_SHIFT 3 /* JD1_FALL_EINT1 */ +#define ARIZONA_JD1_FALL_EINT1_WIDTH 1 /* JD1_FALL_EINT1 */ +#define ARIZONA_JD1_RISE_EINT1 0x0004 /* JD1_RISE_EINT1 */ +#define ARIZONA_JD1_RISE_EINT1_MASK 0x0004 /* JD1_RISE_EINT1 */ +#define ARIZONA_JD1_RISE_EINT1_SHIFT 2 /* JD1_RISE_EINT1 */ +#define ARIZONA_JD1_RISE_EINT1_WIDTH 1 /* JD1_RISE_EINT1 */ +#define ARIZONA_JD2_FALL_EINT1 0x0002 /* JD2_FALL_EINT1 */ +#define ARIZONA_JD2_FALL_EINT1_MASK 0x0002 /* JD2_FALL_EINT1 */ +#define ARIZONA_JD2_FALL_EINT1_SHIFT 1 /* JD2_FALL_EINT1 */ +#define ARIZONA_JD2_FALL_EINT1_WIDTH 1 /* JD2_FALL_EINT1 */ +#define ARIZONA_JD2_RISE_EINT1 0x0001 /* JD2_RISE_EINT1 */ +#define ARIZONA_JD2_RISE_EINT1_MASK 0x0001 /* JD2_RISE_EINT1 */ +#define ARIZONA_JD2_RISE_EINT1_SHIFT 0 /* JD2_RISE_EINT1 */ +#define ARIZONA_JD2_RISE_EINT1_WIDTH 1 /* JD2_RISE_EINT1 */ + +/* + * R3410 (0xD52) - AOD IRQ2 + */ +#define ARIZONA_GP5_FALL_EINT2 0x0020 /* GP5_FALL_EINT2 */ +#define ARIZONA_GP5_FALL_EINT2_MASK 0x0020 /* GP5_FALL_EINT2 */ +#define ARIZONA_GP5_FALL_EINT2_SHIFT 5 /* GP5_FALL_EINT2 */ +#define ARIZONA_GP5_FALL_EINT2_WIDTH 1 /* GP5_FALL_EINT2 */ +#define ARIZONA_GP5_RISE_EINT2 0x0010 /* GP5_RISE_EINT2 */ +#define ARIZONA_GP5_RISE_EINT2_MASK 0x0010 /* GP5_RISE_EINT2 */ +#define ARIZONA_GP5_RISE_EINT2_SHIFT 4 /* GP5_RISE_EINT2 */ +#define ARIZONA_GP5_RISE_EINT2_WIDTH 1 /* GP5_RISE_EINT2 */ +#define ARIZONA_JD1_FALL_EINT2 0x0008 /* JD1_FALL_EINT2 */ +#define ARIZONA_JD1_FALL_EINT2_MASK 0x0008 /* JD1_FALL_EINT2 */ +#define ARIZONA_JD1_FALL_EINT2_SHIFT 3 /* JD1_FALL_EINT2 */ +#define ARIZONA_JD1_FALL_EINT2_WIDTH 1 /* JD1_FALL_EINT2 */ +#define ARIZONA_JD1_RISE_EINT2 0x0004 /* JD1_RISE_EINT2 */ +#define ARIZONA_JD1_RISE_EINT2_MASK 0x0004 /* JD1_RISE_EINT2 */ +#define ARIZONA_JD1_RISE_EINT2_SHIFT 2 /* JD1_RISE_EINT2 */ +#define ARIZONA_JD1_RISE_EINT2_WIDTH 1 /* JD1_RISE_EINT2 */ +#define ARIZONA_JD2_FALL_EINT2 0x0002 /* JD2_FALL_EINT2 */ +#define ARIZONA_JD2_FALL_EINT2_MASK 0x0002 /* JD2_FALL_EINT2 */ +#define ARIZONA_JD2_FALL_EINT2_SHIFT 1 /* JD2_FALL_EINT2 */ +#define ARIZONA_JD2_FALL_EINT2_WIDTH 1 /* JD2_FALL_EINT2 */ +#define ARIZONA_JD2_RISE_EINT2 0x0001 /* JD2_RISE_EINT2 */ +#define ARIZONA_JD2_RISE_EINT2_MASK 0x0001 /* JD2_RISE_EINT2 */ +#define ARIZONA_JD2_RISE_EINT2_SHIFT 0 /* JD2_RISE_EINT2 */ +#define ARIZONA_JD2_RISE_EINT2_WIDTH 1 /* JD2_RISE_EINT2 */ + +/* + * R3411 (0xD53) - AOD IRQ Mask IRQ1 + */ +#define ARIZONA_IM_GP5_FALL_EINT1 0x0020 /* IM_GP5_FALL_EINT1 */ +#define ARIZONA_IM_GP5_FALL_EINT1_MASK 0x0020 /* IM_GP5_FALL_EINT1 */ +#define ARIZONA_IM_GP5_FALL_EINT1_SHIFT 5 /* IM_GP5_FALL_EINT1 */ +#define ARIZONA_IM_GP5_FALL_EINT1_WIDTH 1 /* IM_GP5_FALL_EINT1 */ +#define ARIZONA_IM_GP5_RISE_EINT1 0x0010 /* IM_GP5_RISE_EINT1 */ +#define ARIZONA_IM_GP5_RISE_EINT1_MASK 0x0010 /* IM_GP5_RISE_EINT1 */ +#define ARIZONA_IM_GP5_RISE_EINT1_SHIFT 4 /* IM_GP5_RISE_EINT1 */ +#define ARIZONA_IM_GP5_RISE_EINT1_WIDTH 1 /* IM_GP5_RISE_EINT1 */ +#define ARIZONA_IM_JD1_FALL_EINT1 0x0008 /* IM_JD1_FALL_EINT1 */ +#define ARIZONA_IM_JD1_FALL_EINT1_MASK 0x0008 /* IM_JD1_FALL_EINT1 */ +#define ARIZONA_IM_JD1_FALL_EINT1_SHIFT 3 /* IM_JD1_FALL_EINT1 */ +#define ARIZONA_IM_JD1_FALL_EINT1_WIDTH 1 /* IM_JD1_FALL_EINT1 */ +#define ARIZONA_IM_JD1_RISE_EINT1 0x0004 /* IM_JD1_RISE_EINT1 */ +#define ARIZONA_IM_JD1_RISE_EINT1_MASK 0x0004 /* IM_JD1_RISE_EINT1 */ +#define ARIZONA_IM_JD1_RISE_EINT1_SHIFT 2 /* IM_JD1_RISE_EINT1 */ +#define ARIZONA_IM_JD1_RISE_EINT1_WIDTH 1 /* IM_JD1_RISE_EINT1 */ +#define ARIZONA_IM_JD2_FALL_EINT1 0x0002 /* IM_JD2_FALL_EINT1 */ +#define ARIZONA_IM_JD2_FALL_EINT1_MASK 0x0002 /* IM_JD2_FALL_EINT1 */ +#define ARIZONA_IM_JD2_FALL_EINT1_SHIFT 1 /* IM_JD2_FALL_EINT1 */ +#define ARIZONA_IM_JD2_FALL_EINT1_WIDTH 1 /* IM_JD2_FALL_EINT1 */ +#define ARIZONA_IM_JD2_RISE_EINT1 0x0001 /* IM_JD2_RISE_EINT1 */ +#define ARIZONA_IM_JD2_RISE_EINT1_MASK 0x0001 /* IM_JD2_RISE_EINT1 */ +#define ARIZONA_IM_JD2_RISE_EINT1_SHIFT 0 /* IM_JD2_RISE_EINT1 */ +#define ARIZONA_IM_JD2_RISE_EINT1_WIDTH 1 /* IM_JD2_RISE_EINT1 */ + +/* + * R3412 (0xD54) - AOD IRQ Mask IRQ2 + */ +#define ARIZONA_IM_GP5_FALL_EINT2 0x0020 /* IM_GP5_FALL_EINT2 */ +#define ARIZONA_IM_GP5_FALL_EINT2_MASK 0x0020 /* IM_GP5_FALL_EINT2 */ +#define ARIZONA_IM_GP5_FALL_EINT2_SHIFT 5 /* IM_GP5_FALL_EINT2 */ +#define ARIZONA_IM_GP5_FALL_EINT2_WIDTH 1 /* IM_GP5_FALL_EINT2 */ +#define ARIZONA_IM_GP5_RISE_EINT2 0x0010 /* IM_GP5_RISE_EINT2 */ +#define ARIZONA_IM_GP5_RISE_EINT2_MASK 0x0010 /* IM_GP5_RISE_EINT2 */ +#define ARIZONA_IM_GP5_RISE_EINT2_SHIFT 4 /* IM_GP5_RISE_EINT2 */ +#define ARIZONA_IM_GP5_RISE_EINT2_WIDTH 1 /* IM_GP5_RISE_EINT2 */ +#define ARIZONA_IM_JD1_FALL_EINT2 0x0008 /* IM_JD1_FALL_EINT2 */ +#define ARIZONA_IM_JD1_FALL_EINT2_MASK 0x0008 /* IM_JD1_FALL_EINT2 */ +#define ARIZONA_IM_JD1_FALL_EINT2_SHIFT 3 /* IM_JD1_FALL_EINT2 */ +#define ARIZONA_IM_JD1_FALL_EINT2_WIDTH 1 /* IM_JD1_FALL_EINT2 */ +#define ARIZONA_IM_JD1_RISE_EINT2 0x0004 /* IM_JD1_RISE_EINT2 */ +#define ARIZONA_IM_JD1_RISE_EINT2_MASK 0x0004 /* IM_JD1_RISE_EINT2 */ +#define ARIZONA_IM_JD1_RISE_EINT2_SHIFT 2 /* IM_JD1_RISE_EINT2 */ +#define ARIZONA_IM_JD1_RISE_EINT2_WIDTH 1 /* IM_JD1_RISE_EINT2 */ +#define ARIZONA_IM_JD2_FALL_EINT2 0x0002 /* IM_JD2_FALL_EINT2 */ +#define ARIZONA_IM_JD2_FALL_EINT2_MASK 0x0002 /* IM_JD2_FALL_EINT2 */ +#define ARIZONA_IM_JD2_FALL_EINT2_SHIFT 1 /* IM_JD2_FALL_EINT2 */ +#define ARIZONA_IM_JD2_FALL_EINT2_WIDTH 1 /* IM_JD2_FALL_EINT2 */ +#define ARIZONA_IM_JD2_RISE_EINT2 0x0001 /* IM_JD2_RISE_EINT2 */ +#define ARIZONA_IM_JD2_RISE_EINT2_MASK 0x0001 /* IM_JD2_RISE_EINT2 */ +#define ARIZONA_IM_JD2_RISE_EINT2_SHIFT 0 /* IM_JD2_RISE_EINT2 */ +#define ARIZONA_IM_JD2_RISE_EINT2_WIDTH 1 /* IM_JD2_RISE_EINT2 */ + +/* + * R3413 (0xD55) - AOD IRQ Raw Status + */ +#define ARIZONA_GP5_STS 0x0004 /* GP5_STS */ +#define ARIZONA_GP5_STS_MASK 0x0004 /* GP5_STS */ +#define ARIZONA_GP5_STS_SHIFT 2 /* GP5_STS */ +#define ARIZONA_GP5_STS_WIDTH 1 /* GP5_STS */ +#define ARIZONA_JD2_STS 0x0002 /* JD2_STS */ +#define ARIZONA_JD2_STS_MASK 0x0002 /* JD2_STS */ +#define ARIZONA_JD2_STS_SHIFT 1 /* JD2_STS */ +#define ARIZONA_JD2_STS_WIDTH 1 /* JD2_STS */ +#define ARIZONA_JD1_STS 0x0001 /* JD1_STS */ +#define ARIZONA_JD1_STS_MASK 0x0001 /* JD1_STS */ +#define ARIZONA_JD1_STS_SHIFT 0 /* JD1_STS */ +#define ARIZONA_JD1_STS_WIDTH 1 /* JD1_STS */ + +/* + * R3414 (0xD56) - Jack detect debounce + */ +#define ARIZONA_JD2_DB 0x0002 /* JD2_DB */ +#define ARIZONA_JD2_DB_MASK 0x0002 /* JD2_DB */ +#define ARIZONA_JD2_DB_SHIFT 1 /* JD2_DB */ +#define ARIZONA_JD2_DB_WIDTH 1 /* JD2_DB */ +#define ARIZONA_JD1_DB 0x0001 /* JD1_DB */ +#define ARIZONA_JD1_DB_MASK 0x0001 /* JD1_DB */ +#define ARIZONA_JD1_DB_SHIFT 0 /* JD1_DB */ +#define ARIZONA_JD1_DB_WIDTH 1 /* JD1_DB */ + +/* + * R3584 (0xE00) - FX_Ctrl1 + */ +#define ARIZONA_FX_RATE_MASK 0x7800 /* FX_RATE - [14:11] */ +#define ARIZONA_FX_RATE_SHIFT 11 /* FX_RATE - [14:11] */ +#define ARIZONA_FX_RATE_WIDTH 4 /* FX_RATE - [14:11] */ + +/* + * R3585 (0xE01) - FX_Ctrl2 + */ +#define ARIZONA_FX_STS_MASK 0xFFF0 /* FX_STS - [15:4] */ +#define ARIZONA_FX_STS_SHIFT 4 /* FX_STS - [15:4] */ +#define ARIZONA_FX_STS_WIDTH 12 /* FX_STS - [15:4] */ + +/* + * R3600 (0xE10) - EQ1_1 + */ +#define ARIZONA_EQ1_B1_GAIN_MASK 0xF800 /* EQ1_B1_GAIN - [15:11] */ +#define ARIZONA_EQ1_B1_GAIN_SHIFT 11 /* EQ1_B1_GAIN - [15:11] */ +#define ARIZONA_EQ1_B1_GAIN_WIDTH 5 /* EQ1_B1_GAIN - [15:11] */ +#define ARIZONA_EQ1_B2_GAIN_MASK 0x07C0 /* EQ1_B2_GAIN - [10:6] */ +#define ARIZONA_EQ1_B2_GAIN_SHIFT 6 /* EQ1_B2_GAIN - [10:6] */ +#define ARIZONA_EQ1_B2_GAIN_WIDTH 5 /* EQ1_B2_GAIN - [10:6] */ +#define ARIZONA_EQ1_B3_GAIN_MASK 0x003E /* EQ1_B3_GAIN - [5:1] */ +#define ARIZONA_EQ1_B3_GAIN_SHIFT 1 /* EQ1_B3_GAIN - [5:1] */ +#define ARIZONA_EQ1_B3_GAIN_WIDTH 5 /* EQ1_B3_GAIN - [5:1] */ +#define ARIZONA_EQ1_ENA 0x0001 /* EQ1_ENA */ +#define ARIZONA_EQ1_ENA_MASK 0x0001 /* EQ1_ENA */ +#define ARIZONA_EQ1_ENA_SHIFT 0 /* EQ1_ENA */ +#define ARIZONA_EQ1_ENA_WIDTH 1 /* EQ1_ENA */ + +/* + * R3601 (0xE11) - EQ1_2 + */ +#define ARIZONA_EQ1_B4_GAIN_MASK 0xF800 /* EQ1_B4_GAIN - [15:11] */ +#define ARIZONA_EQ1_B4_GAIN_SHIFT 11 /* EQ1_B4_GAIN - [15:11] */ +#define ARIZONA_EQ1_B4_GAIN_WIDTH 5 /* EQ1_B4_GAIN - [15:11] */ +#define ARIZONA_EQ1_B5_GAIN_MASK 0x07C0 /* EQ1_B5_GAIN - [10:6] */ +#define ARIZONA_EQ1_B5_GAIN_SHIFT 6 /* EQ1_B5_GAIN - [10:6] */ +#define ARIZONA_EQ1_B5_GAIN_WIDTH 5 /* EQ1_B5_GAIN - [10:6] */ +#define ARIZONA_EQ1_B1_MODE 0x0001 /* EQ1_B1_MODE */ +#define ARIZONA_EQ1_B1_MODE_MASK 0x0001 /* EQ1_B1_MODE */ +#define ARIZONA_EQ1_B1_MODE_SHIFT 0 /* EQ1_B1_MODE */ +#define ARIZONA_EQ1_B1_MODE_WIDTH 1 /* EQ1_B1_MODE */ + +/* + * R3602 (0xE12) - EQ1_3 + */ +#define ARIZONA_EQ1_B1_A_MASK 0xFFFF /* EQ1_B1_A - [15:0] */ +#define ARIZONA_EQ1_B1_A_SHIFT 0 /* EQ1_B1_A - [15:0] */ +#define ARIZONA_EQ1_B1_A_WIDTH 16 /* EQ1_B1_A - [15:0] */ + +/* + * R3603 (0xE13) - EQ1_4 + */ +#define ARIZONA_EQ1_B1_B_MASK 0xFFFF /* EQ1_B1_B - [15:0] */ +#define ARIZONA_EQ1_B1_B_SHIFT 0 /* EQ1_B1_B - [15:0] */ +#define ARIZONA_EQ1_B1_B_WIDTH 16 /* EQ1_B1_B - [15:0] */ + +/* + * R3604 (0xE14) - EQ1_5 + */ +#define ARIZONA_EQ1_B1_PG_MASK 0xFFFF /* EQ1_B1_PG - [15:0] */ +#define ARIZONA_EQ1_B1_PG_SHIFT 0 /* EQ1_B1_PG - [15:0] */ +#define ARIZONA_EQ1_B1_PG_WIDTH 16 /* EQ1_B1_PG - [15:0] */ + +/* + * R3605 (0xE15) - EQ1_6 + */ +#define ARIZONA_EQ1_B2_A_MASK 0xFFFF /* EQ1_B2_A - [15:0] */ +#define ARIZONA_EQ1_B2_A_SHIFT 0 /* EQ1_B2_A - [15:0] */ +#define ARIZONA_EQ1_B2_A_WIDTH 16 /* EQ1_B2_A - [15:0] */ + +/* + * R3606 (0xE16) - EQ1_7 + */ +#define ARIZONA_EQ1_B2_B_MASK 0xFFFF /* EQ1_B2_B - [15:0] */ +#define ARIZONA_EQ1_B2_B_SHIFT 0 /* EQ1_B2_B - [15:0] */ +#define ARIZONA_EQ1_B2_B_WIDTH 16 /* EQ1_B2_B - [15:0] */ + +/* + * R3607 (0xE17) - EQ1_8 + */ +#define ARIZONA_EQ1_B2_C_MASK 0xFFFF /* EQ1_B2_C - [15:0] */ +#define ARIZONA_EQ1_B2_C_SHIFT 0 /* EQ1_B2_C - [15:0] */ +#define ARIZONA_EQ1_B2_C_WIDTH 16 /* EQ1_B2_C - [15:0] */ + +/* + * R3608 (0xE18) - EQ1_9 + */ +#define ARIZONA_EQ1_B2_PG_MASK 0xFFFF /* EQ1_B2_PG - [15:0] */ +#define ARIZONA_EQ1_B2_PG_SHIFT 0 /* EQ1_B2_PG - [15:0] */ +#define ARIZONA_EQ1_B2_PG_WIDTH 16 /* EQ1_B2_PG - [15:0] */ + +/* + * R3609 (0xE19) - EQ1_10 + */ +#define ARIZONA_EQ1_B3_A_MASK 0xFFFF /* EQ1_B3_A - [15:0] */ +#define ARIZONA_EQ1_B3_A_SHIFT 0 /* EQ1_B3_A - [15:0] */ +#define ARIZONA_EQ1_B3_A_WIDTH 16 /* EQ1_B3_A - [15:0] */ + +/* + * R3610 (0xE1A) - EQ1_11 + */ +#define ARIZONA_EQ1_B3_B_MASK 0xFFFF /* EQ1_B3_B - [15:0] */ +#define ARIZONA_EQ1_B3_B_SHIFT 0 /* EQ1_B3_B - [15:0] */ +#define ARIZONA_EQ1_B3_B_WIDTH 16 /* EQ1_B3_B - [15:0] */ + +/* + * R3611 (0xE1B) - EQ1_12 + */ +#define ARIZONA_EQ1_B3_C_MASK 0xFFFF /* EQ1_B3_C - [15:0] */ +#define ARIZONA_EQ1_B3_C_SHIFT 0 /* EQ1_B3_C - [15:0] */ +#define ARIZONA_EQ1_B3_C_WIDTH 16 /* EQ1_B3_C - [15:0] */ + +/* + * R3612 (0xE1C) - EQ1_13 + */ +#define ARIZONA_EQ1_B3_PG_MASK 0xFFFF /* EQ1_B3_PG - [15:0] */ +#define ARIZONA_EQ1_B3_PG_SHIFT 0 /* EQ1_B3_PG - [15:0] */ +#define ARIZONA_EQ1_B3_PG_WIDTH 16 /* EQ1_B3_PG - [15:0] */ + +/* + * R3613 (0xE1D) - EQ1_14 + */ +#define ARIZONA_EQ1_B4_A_MASK 0xFFFF /* EQ1_B4_A - [15:0] */ +#define ARIZONA_EQ1_B4_A_SHIFT 0 /* EQ1_B4_A - [15:0] */ +#define ARIZONA_EQ1_B4_A_WIDTH 16 /* EQ1_B4_A - [15:0] */ + +/* + * R3614 (0xE1E) - EQ1_15 + */ +#define ARIZONA_EQ1_B4_B_MASK 0xFFFF /* EQ1_B4_B - [15:0] */ +#define ARIZONA_EQ1_B4_B_SHIFT 0 /* EQ1_B4_B - [15:0] */ +#define ARIZONA_EQ1_B4_B_WIDTH 16 /* EQ1_B4_B - [15:0] */ + +/* + * R3615 (0xE1F) - EQ1_16 + */ +#define ARIZONA_EQ1_B4_C_MASK 0xFFFF /* EQ1_B4_C - [15:0] */ +#define ARIZONA_EQ1_B4_C_SHIFT 0 /* EQ1_B4_C - [15:0] */ +#define ARIZONA_EQ1_B4_C_WIDTH 16 /* EQ1_B4_C - [15:0] */ + +/* + * R3616 (0xE20) - EQ1_17 + */ +#define ARIZONA_EQ1_B4_PG_MASK 0xFFFF /* EQ1_B4_PG - [15:0] */ +#define ARIZONA_EQ1_B4_PG_SHIFT 0 /* EQ1_B4_PG - [15:0] */ +#define ARIZONA_EQ1_B4_PG_WIDTH 16 /* EQ1_B4_PG - [15:0] */ + +/* + * R3617 (0xE21) - EQ1_18 + */ +#define ARIZONA_EQ1_B5_A_MASK 0xFFFF /* EQ1_B5_A - [15:0] */ +#define ARIZONA_EQ1_B5_A_SHIFT 0 /* EQ1_B5_A - [15:0] */ +#define ARIZONA_EQ1_B5_A_WIDTH 16 /* EQ1_B5_A - [15:0] */ + +/* + * R3618 (0xE22) - EQ1_19 + */ +#define ARIZONA_EQ1_B5_B_MASK 0xFFFF /* EQ1_B5_B - [15:0] */ +#define ARIZONA_EQ1_B5_B_SHIFT 0 /* EQ1_B5_B - [15:0] */ +#define ARIZONA_EQ1_B5_B_WIDTH 16 /* EQ1_B5_B - [15:0] */ + +/* + * R3619 (0xE23) - EQ1_20 + */ +#define ARIZONA_EQ1_B5_PG_MASK 0xFFFF /* EQ1_B5_PG - [15:0] */ +#define ARIZONA_EQ1_B5_PG_SHIFT 0 /* EQ1_B5_PG - [15:0] */ +#define ARIZONA_EQ1_B5_PG_WIDTH 16 /* EQ1_B5_PG - [15:0] */ + +/* + * R3620 (0xE24) - EQ1_21 + */ +#define ARIZONA_EQ1_B1_C_MASK 0xFFFF /* EQ1_B1_C - [15:0] */ +#define ARIZONA_EQ1_B1_C_SHIFT 0 /* EQ1_B1_C - [15:0] */ +#define ARIZONA_EQ1_B1_C_WIDTH 16 /* EQ1_B1_C - [15:0] */ + +/* + * R3622 (0xE26) - EQ2_1 + */ +#define ARIZONA_EQ2_B1_GAIN_MASK 0xF800 /* EQ2_B1_GAIN - [15:11] */ +#define ARIZONA_EQ2_B1_GAIN_SHIFT 11 /* EQ2_B1_GAIN - [15:11] */ +#define ARIZONA_EQ2_B1_GAIN_WIDTH 5 /* EQ2_B1_GAIN - [15:11] */ +#define ARIZONA_EQ2_B2_GAIN_MASK 0x07C0 /* EQ2_B2_GAIN - [10:6] */ +#define ARIZONA_EQ2_B2_GAIN_SHIFT 6 /* EQ2_B2_GAIN - [10:6] */ +#define ARIZONA_EQ2_B2_GAIN_WIDTH 5 /* EQ2_B2_GAIN - [10:6] */ +#define ARIZONA_EQ2_B3_GAIN_MASK 0x003E /* EQ2_B3_GAIN - [5:1] */ +#define ARIZONA_EQ2_B3_GAIN_SHIFT 1 /* EQ2_B3_GAIN - [5:1] */ +#define ARIZONA_EQ2_B3_GAIN_WIDTH 5 /* EQ2_B3_GAIN - [5:1] */ +#define ARIZONA_EQ2_ENA 0x0001 /* EQ2_ENA */ +#define ARIZONA_EQ2_ENA_MASK 0x0001 /* EQ2_ENA */ +#define ARIZONA_EQ2_ENA_SHIFT 0 /* EQ2_ENA */ +#define ARIZONA_EQ2_ENA_WIDTH 1 /* EQ2_ENA */ + +/* + * R3623 (0xE27) - EQ2_2 + */ +#define ARIZONA_EQ2_B4_GAIN_MASK 0xF800 /* EQ2_B4_GAIN - [15:11] */ +#define ARIZONA_EQ2_B4_GAIN_SHIFT 11 /* EQ2_B4_GAIN - [15:11] */ +#define ARIZONA_EQ2_B4_GAIN_WIDTH 5 /* EQ2_B4_GAIN - [15:11] */ +#define ARIZONA_EQ2_B5_GAIN_MASK 0x07C0 /* EQ2_B5_GAIN - [10:6] */ +#define ARIZONA_EQ2_B5_GAIN_SHIFT 6 /* EQ2_B5_GAIN - [10:6] */ +#define ARIZONA_EQ2_B5_GAIN_WIDTH 5 /* EQ2_B5_GAIN - [10:6] */ +#define ARIZONA_EQ2_B1_MODE 0x0001 /* EQ2_B1_MODE */ +#define ARIZONA_EQ2_B1_MODE_MASK 0x0001 /* EQ2_B1_MODE */ +#define ARIZONA_EQ2_B1_MODE_SHIFT 0 /* EQ2_B1_MODE */ +#define ARIZONA_EQ2_B1_MODE_WIDTH 1 /* EQ2_B1_MODE */ + +/* + * R3624 (0xE28) - EQ2_3 + */ +#define ARIZONA_EQ2_B1_A_MASK 0xFFFF /* EQ2_B1_A - [15:0] */ +#define ARIZONA_EQ2_B1_A_SHIFT 0 /* EQ2_B1_A - [15:0] */ +#define ARIZONA_EQ2_B1_A_WIDTH 16 /* EQ2_B1_A - [15:0] */ + +/* + * R3625 (0xE29) - EQ2_4 + */ +#define ARIZONA_EQ2_B1_B_MASK 0xFFFF /* EQ2_B1_B - [15:0] */ +#define ARIZONA_EQ2_B1_B_SHIFT 0 /* EQ2_B1_B - [15:0] */ +#define ARIZONA_EQ2_B1_B_WIDTH 16 /* EQ2_B1_B - [15:0] */ + +/* + * R3626 (0xE2A) - EQ2_5 + */ +#define ARIZONA_EQ2_B1_PG_MASK 0xFFFF /* EQ2_B1_PG - [15:0] */ +#define ARIZONA_EQ2_B1_PG_SHIFT 0 /* EQ2_B1_PG - [15:0] */ +#define ARIZONA_EQ2_B1_PG_WIDTH 16 /* EQ2_B1_PG - [15:0] */ + +/* + * R3627 (0xE2B) - EQ2_6 + */ +#define ARIZONA_EQ2_B2_A_MASK 0xFFFF /* EQ2_B2_A - [15:0] */ +#define ARIZONA_EQ2_B2_A_SHIFT 0 /* EQ2_B2_A - [15:0] */ +#define ARIZONA_EQ2_B2_A_WIDTH 16 /* EQ2_B2_A - [15:0] */ + +/* + * R3628 (0xE2C) - EQ2_7 + */ +#define ARIZONA_EQ2_B2_B_MASK 0xFFFF /* EQ2_B2_B - [15:0] */ +#define ARIZONA_EQ2_B2_B_SHIFT 0 /* EQ2_B2_B - [15:0] */ +#define ARIZONA_EQ2_B2_B_WIDTH 16 /* EQ2_B2_B - [15:0] */ + +/* + * R3629 (0xE2D) - EQ2_8 + */ +#define ARIZONA_EQ2_B2_C_MASK 0xFFFF /* EQ2_B2_C - [15:0] */ +#define ARIZONA_EQ2_B2_C_SHIFT 0 /* EQ2_B2_C - [15:0] */ +#define ARIZONA_EQ2_B2_C_WIDTH 16 /* EQ2_B2_C - [15:0] */ + +/* + * R3630 (0xE2E) - EQ2_9 + */ +#define ARIZONA_EQ2_B2_PG_MASK 0xFFFF /* EQ2_B2_PG - [15:0] */ +#define ARIZONA_EQ2_B2_PG_SHIFT 0 /* EQ2_B2_PG - [15:0] */ +#define ARIZONA_EQ2_B2_PG_WIDTH 16 /* EQ2_B2_PG - [15:0] */ + +/* + * R3631 (0xE2F) - EQ2_10 + */ +#define ARIZONA_EQ2_B3_A_MASK 0xFFFF /* EQ2_B3_A - [15:0] */ +#define ARIZONA_EQ2_B3_A_SHIFT 0 /* EQ2_B3_A - [15:0] */ +#define ARIZONA_EQ2_B3_A_WIDTH 16 /* EQ2_B3_A - [15:0] */ + +/* + * R3632 (0xE30) - EQ2_11 + */ +#define ARIZONA_EQ2_B3_B_MASK 0xFFFF /* EQ2_B3_B - [15:0] */ +#define ARIZONA_EQ2_B3_B_SHIFT 0 /* EQ2_B3_B - [15:0] */ +#define ARIZONA_EQ2_B3_B_WIDTH 16 /* EQ2_B3_B - [15:0] */ + +/* + * R3633 (0xE31) - EQ2_12 + */ +#define ARIZONA_EQ2_B3_C_MASK 0xFFFF /* EQ2_B3_C - [15:0] */ +#define ARIZONA_EQ2_B3_C_SHIFT 0 /* EQ2_B3_C - [15:0] */ +#define ARIZONA_EQ2_B3_C_WIDTH 16 /* EQ2_B3_C - [15:0] */ + +/* + * R3634 (0xE32) - EQ2_13 + */ +#define ARIZONA_EQ2_B3_PG_MASK 0xFFFF /* EQ2_B3_PG - [15:0] */ +#define ARIZONA_EQ2_B3_PG_SHIFT 0 /* EQ2_B3_PG - [15:0] */ +#define ARIZONA_EQ2_B3_PG_WIDTH 16 /* EQ2_B3_PG - [15:0] */ + +/* + * R3635 (0xE33) - EQ2_14 + */ +#define ARIZONA_EQ2_B4_A_MASK 0xFFFF /* EQ2_B4_A - [15:0] */ +#define ARIZONA_EQ2_B4_A_SHIFT 0 /* EQ2_B4_A - [15:0] */ +#define ARIZONA_EQ2_B4_A_WIDTH 16 /* EQ2_B4_A - [15:0] */ + +/* + * R3636 (0xE34) - EQ2_15 + */ +#define ARIZONA_EQ2_B4_B_MASK 0xFFFF /* EQ2_B4_B - [15:0] */ +#define ARIZONA_EQ2_B4_B_SHIFT 0 /* EQ2_B4_B - [15:0] */ +#define ARIZONA_EQ2_B4_B_WIDTH 16 /* EQ2_B4_B - [15:0] */ + +/* + * R3637 (0xE35) - EQ2_16 + */ +#define ARIZONA_EQ2_B4_C_MASK 0xFFFF /* EQ2_B4_C - [15:0] */ +#define ARIZONA_EQ2_B4_C_SHIFT 0 /* EQ2_B4_C - [15:0] */ +#define ARIZONA_EQ2_B4_C_WIDTH 16 /* EQ2_B4_C - [15:0] */ + +/* + * R3638 (0xE36) - EQ2_17 + */ +#define ARIZONA_EQ2_B4_PG_MASK 0xFFFF /* EQ2_B4_PG - [15:0] */ +#define ARIZONA_EQ2_B4_PG_SHIFT 0 /* EQ2_B4_PG - [15:0] */ +#define ARIZONA_EQ2_B4_PG_WIDTH 16 /* EQ2_B4_PG - [15:0] */ + +/* + * R3639 (0xE37) - EQ2_18 + */ +#define ARIZONA_EQ2_B5_A_MASK 0xFFFF /* EQ2_B5_A - [15:0] */ +#define ARIZONA_EQ2_B5_A_SHIFT 0 /* EQ2_B5_A - [15:0] */ +#define ARIZONA_EQ2_B5_A_WIDTH 16 /* EQ2_B5_A - [15:0] */ + +/* + * R3640 (0xE38) - EQ2_19 + */ +#define ARIZONA_EQ2_B5_B_MASK 0xFFFF /* EQ2_B5_B - [15:0] */ +#define ARIZONA_EQ2_B5_B_SHIFT 0 /* EQ2_B5_B - [15:0] */ +#define ARIZONA_EQ2_B5_B_WIDTH 16 /* EQ2_B5_B - [15:0] */ + +/* + * R3641 (0xE39) - EQ2_20 + */ +#define ARIZONA_EQ2_B5_PG_MASK 0xFFFF /* EQ2_B5_PG - [15:0] */ +#define ARIZONA_EQ2_B5_PG_SHIFT 0 /* EQ2_B5_PG - [15:0] */ +#define ARIZONA_EQ2_B5_PG_WIDTH 16 /* EQ2_B5_PG - [15:0] */ + +/* + * R3642 (0xE3A) - EQ2_21 + */ +#define ARIZONA_EQ2_B1_C_MASK 0xFFFF /* EQ2_B1_C - [15:0] */ +#define ARIZONA_EQ2_B1_C_SHIFT 0 /* EQ2_B1_C - [15:0] */ +#define ARIZONA_EQ2_B1_C_WIDTH 16 /* EQ2_B1_C - [15:0] */ + +/* + * R3644 (0xE3C) - EQ3_1 + */ +#define ARIZONA_EQ3_B1_GAIN_MASK 0xF800 /* EQ3_B1_GAIN - [15:11] */ +#define ARIZONA_EQ3_B1_GAIN_SHIFT 11 /* EQ3_B1_GAIN - [15:11] */ +#define ARIZONA_EQ3_B1_GAIN_WIDTH 5 /* EQ3_B1_GAIN - [15:11] */ +#define ARIZONA_EQ3_B2_GAIN_MASK 0x07C0 /* EQ3_B2_GAIN - [10:6] */ +#define ARIZONA_EQ3_B2_GAIN_SHIFT 6 /* EQ3_B2_GAIN - [10:6] */ +#define ARIZONA_EQ3_B2_GAIN_WIDTH 5 /* EQ3_B2_GAIN - [10:6] */ +#define ARIZONA_EQ3_B3_GAIN_MASK 0x003E /* EQ3_B3_GAIN - [5:1] */ +#define ARIZONA_EQ3_B3_GAIN_SHIFT 1 /* EQ3_B3_GAIN - [5:1] */ +#define ARIZONA_EQ3_B3_GAIN_WIDTH 5 /* EQ3_B3_GAIN - [5:1] */ +#define ARIZONA_EQ3_ENA 0x0001 /* EQ3_ENA */ +#define ARIZONA_EQ3_ENA_MASK 0x0001 /* EQ3_ENA */ +#define ARIZONA_EQ3_ENA_SHIFT 0 /* EQ3_ENA */ +#define ARIZONA_EQ3_ENA_WIDTH 1 /* EQ3_ENA */ + +/* + * R3645 (0xE3D) - EQ3_2 + */ +#define ARIZONA_EQ3_B4_GAIN_MASK 0xF800 /* EQ3_B4_GAIN - [15:11] */ +#define ARIZONA_EQ3_B4_GAIN_SHIFT 11 /* EQ3_B4_GAIN - [15:11] */ +#define ARIZONA_EQ3_B4_GAIN_WIDTH 5 /* EQ3_B4_GAIN - [15:11] */ +#define ARIZONA_EQ3_B5_GAIN_MASK 0x07C0 /* EQ3_B5_GAIN - [10:6] */ +#define ARIZONA_EQ3_B5_GAIN_SHIFT 6 /* EQ3_B5_GAIN - [10:6] */ +#define ARIZONA_EQ3_B5_GAIN_WIDTH 5 /* EQ3_B5_GAIN - [10:6] */ +#define ARIZONA_EQ3_B1_MODE 0x0001 /* EQ3_B1_MODE */ +#define ARIZONA_EQ3_B1_MODE_MASK 0x0001 /* EQ3_B1_MODE */ +#define ARIZONA_EQ3_B1_MODE_SHIFT 0 /* EQ3_B1_MODE */ +#define ARIZONA_EQ3_B1_MODE_WIDTH 1 /* EQ3_B1_MODE */ + +/* + * R3646 (0xE3E) - EQ3_3 + */ +#define ARIZONA_EQ3_B1_A_MASK 0xFFFF /* EQ3_B1_A - [15:0] */ +#define ARIZONA_EQ3_B1_A_SHIFT 0 /* EQ3_B1_A - [15:0] */ +#define ARIZONA_EQ3_B1_A_WIDTH 16 /* EQ3_B1_A - [15:0] */ + +/* + * R3647 (0xE3F) - EQ3_4 + */ +#define ARIZONA_EQ3_B1_B_MASK 0xFFFF /* EQ3_B1_B - [15:0] */ +#define ARIZONA_EQ3_B1_B_SHIFT 0 /* EQ3_B1_B - [15:0] */ +#define ARIZONA_EQ3_B1_B_WIDTH 16 /* EQ3_B1_B - [15:0] */ + +/* + * R3648 (0xE40) - EQ3_5 + */ +#define ARIZONA_EQ3_B1_PG_MASK 0xFFFF /* EQ3_B1_PG - [15:0] */ +#define ARIZONA_EQ3_B1_PG_SHIFT 0 /* EQ3_B1_PG - [15:0] */ +#define ARIZONA_EQ3_B1_PG_WIDTH 16 /* EQ3_B1_PG - [15:0] */ + +/* + * R3649 (0xE41) - EQ3_6 + */ +#define ARIZONA_EQ3_B2_A_MASK 0xFFFF /* EQ3_B2_A - [15:0] */ +#define ARIZONA_EQ3_B2_A_SHIFT 0 /* EQ3_B2_A - [15:0] */ +#define ARIZONA_EQ3_B2_A_WIDTH 16 /* EQ3_B2_A - [15:0] */ + +/* + * R3650 (0xE42) - EQ3_7 + */ +#define ARIZONA_EQ3_B2_B_MASK 0xFFFF /* EQ3_B2_B - [15:0] */ +#define ARIZONA_EQ3_B2_B_SHIFT 0 /* EQ3_B2_B - [15:0] */ +#define ARIZONA_EQ3_B2_B_WIDTH 16 /* EQ3_B2_B - [15:0] */ + +/* + * R3651 (0xE43) - EQ3_8 + */ +#define ARIZONA_EQ3_B2_C_MASK 0xFFFF /* EQ3_B2_C - [15:0] */ +#define ARIZONA_EQ3_B2_C_SHIFT 0 /* EQ3_B2_C - [15:0] */ +#define ARIZONA_EQ3_B2_C_WIDTH 16 /* EQ3_B2_C - [15:0] */ + +/* + * R3652 (0xE44) - EQ3_9 + */ +#define ARIZONA_EQ3_B2_PG_MASK 0xFFFF /* EQ3_B2_PG - [15:0] */ +#define ARIZONA_EQ3_B2_PG_SHIFT 0 /* EQ3_B2_PG - [15:0] */ +#define ARIZONA_EQ3_B2_PG_WIDTH 16 /* EQ3_B2_PG - [15:0] */ + +/* + * R3653 (0xE45) - EQ3_10 + */ +#define ARIZONA_EQ3_B3_A_MASK 0xFFFF /* EQ3_B3_A - [15:0] */ +#define ARIZONA_EQ3_B3_A_SHIFT 0 /* EQ3_B3_A - [15:0] */ +#define ARIZONA_EQ3_B3_A_WIDTH 16 /* EQ3_B3_A - [15:0] */ + +/* + * R3654 (0xE46) - EQ3_11 + */ +#define ARIZONA_EQ3_B3_B_MASK 0xFFFF /* EQ3_B3_B - [15:0] */ +#define ARIZONA_EQ3_B3_B_SHIFT 0 /* EQ3_B3_B - [15:0] */ +#define ARIZONA_EQ3_B3_B_WIDTH 16 /* EQ3_B3_B - [15:0] */ + +/* + * R3655 (0xE47) - EQ3_12 + */ +#define ARIZONA_EQ3_B3_C_MASK 0xFFFF /* EQ3_B3_C - [15:0] */ +#define ARIZONA_EQ3_B3_C_SHIFT 0 /* EQ3_B3_C - [15:0] */ +#define ARIZONA_EQ3_B3_C_WIDTH 16 /* EQ3_B3_C - [15:0] */ + +/* + * R3656 (0xE48) - EQ3_13 + */ +#define ARIZONA_EQ3_B3_PG_MASK 0xFFFF /* EQ3_B3_PG - [15:0] */ +#define ARIZONA_EQ3_B3_PG_SHIFT 0 /* EQ3_B3_PG - [15:0] */ +#define ARIZONA_EQ3_B3_PG_WIDTH 16 /* EQ3_B3_PG - [15:0] */ + +/* + * R3657 (0xE49) - EQ3_14 + */ +#define ARIZONA_EQ3_B4_A_MASK 0xFFFF /* EQ3_B4_A - [15:0] */ +#define ARIZONA_EQ3_B4_A_SHIFT 0 /* EQ3_B4_A - [15:0] */ +#define ARIZONA_EQ3_B4_A_WIDTH 16 /* EQ3_B4_A - [15:0] */ + +/* + * R3658 (0xE4A) - EQ3_15 + */ +#define ARIZONA_EQ3_B4_B_MASK 0xFFFF /* EQ3_B4_B - [15:0] */ +#define ARIZONA_EQ3_B4_B_SHIFT 0 /* EQ3_B4_B - [15:0] */ +#define ARIZONA_EQ3_B4_B_WIDTH 16 /* EQ3_B4_B - [15:0] */ + +/* + * R3659 (0xE4B) - EQ3_16 + */ +#define ARIZONA_EQ3_B4_C_MASK 0xFFFF /* EQ3_B4_C - [15:0] */ +#define ARIZONA_EQ3_B4_C_SHIFT 0 /* EQ3_B4_C - [15:0] */ +#define ARIZONA_EQ3_B4_C_WIDTH 16 /* EQ3_B4_C - [15:0] */ + +/* + * R3660 (0xE4C) - EQ3_17 + */ +#define ARIZONA_EQ3_B4_PG_MASK 0xFFFF /* EQ3_B4_PG - [15:0] */ +#define ARIZONA_EQ3_B4_PG_SHIFT 0 /* EQ3_B4_PG - [15:0] */ +#define ARIZONA_EQ3_B4_PG_WIDTH 16 /* EQ3_B4_PG - [15:0] */ + +/* + * R3661 (0xE4D) - EQ3_18 + */ +#define ARIZONA_EQ3_B5_A_MASK 0xFFFF /* EQ3_B5_A - [15:0] */ +#define ARIZONA_EQ3_B5_A_SHIFT 0 /* EQ3_B5_A - [15:0] */ +#define ARIZONA_EQ3_B5_A_WIDTH 16 /* EQ3_B5_A - [15:0] */ + +/* + * R3662 (0xE4E) - EQ3_19 + */ +#define ARIZONA_EQ3_B5_B_MASK 0xFFFF /* EQ3_B5_B - [15:0] */ +#define ARIZONA_EQ3_B5_B_SHIFT 0 /* EQ3_B5_B - [15:0] */ +#define ARIZONA_EQ3_B5_B_WIDTH 16 /* EQ3_B5_B - [15:0] */ + +/* + * R3663 (0xE4F) - EQ3_20 + */ +#define ARIZONA_EQ3_B5_PG_MASK 0xFFFF /* EQ3_B5_PG - [15:0] */ +#define ARIZONA_EQ3_B5_PG_SHIFT 0 /* EQ3_B5_PG - [15:0] */ +#define ARIZONA_EQ3_B5_PG_WIDTH 16 /* EQ3_B5_PG - [15:0] */ + +/* + * R3664 (0xE50) - EQ3_21 + */ +#define ARIZONA_EQ3_B1_C_MASK 0xFFFF /* EQ3_B1_C - [15:0] */ +#define ARIZONA_EQ3_B1_C_SHIFT 0 /* EQ3_B1_C - [15:0] */ +#define ARIZONA_EQ3_B1_C_WIDTH 16 /* EQ3_B1_C - [15:0] */ + +/* + * R3666 (0xE52) - EQ4_1 + */ +#define ARIZONA_EQ4_B1_GAIN_MASK 0xF800 /* EQ4_B1_GAIN - [15:11] */ +#define ARIZONA_EQ4_B1_GAIN_SHIFT 11 /* EQ4_B1_GAIN - [15:11] */ +#define ARIZONA_EQ4_B1_GAIN_WIDTH 5 /* EQ4_B1_GAIN - [15:11] */ +#define ARIZONA_EQ4_B2_GAIN_MASK 0x07C0 /* EQ4_B2_GAIN - [10:6] */ +#define ARIZONA_EQ4_B2_GAIN_SHIFT 6 /* EQ4_B2_GAIN - [10:6] */ +#define ARIZONA_EQ4_B2_GAIN_WIDTH 5 /* EQ4_B2_GAIN - [10:6] */ +#define ARIZONA_EQ4_B3_GAIN_MASK 0x003E /* EQ4_B3_GAIN - [5:1] */ +#define ARIZONA_EQ4_B3_GAIN_SHIFT 1 /* EQ4_B3_GAIN - [5:1] */ +#define ARIZONA_EQ4_B3_GAIN_WIDTH 5 /* EQ4_B3_GAIN - [5:1] */ +#define ARIZONA_EQ4_ENA 0x0001 /* EQ4_ENA */ +#define ARIZONA_EQ4_ENA_MASK 0x0001 /* EQ4_ENA */ +#define ARIZONA_EQ4_ENA_SHIFT 0 /* EQ4_ENA */ +#define ARIZONA_EQ4_ENA_WIDTH 1 /* EQ4_ENA */ + +/* + * R3667 (0xE53) - EQ4_2 + */ +#define ARIZONA_EQ4_B4_GAIN_MASK 0xF800 /* EQ4_B4_GAIN - [15:11] */ +#define ARIZONA_EQ4_B4_GAIN_SHIFT 11 /* EQ4_B4_GAIN - [15:11] */ +#define ARIZONA_EQ4_B4_GAIN_WIDTH 5 /* EQ4_B4_GAIN - [15:11] */ +#define ARIZONA_EQ4_B5_GAIN_MASK 0x07C0 /* EQ4_B5_GAIN - [10:6] */ +#define ARIZONA_EQ4_B5_GAIN_SHIFT 6 /* EQ4_B5_GAIN - [10:6] */ +#define ARIZONA_EQ4_B5_GAIN_WIDTH 5 /* EQ4_B5_GAIN - [10:6] */ +#define ARIZONA_EQ4_B1_MODE 0x0001 /* EQ4_B1_MODE */ +#define ARIZONA_EQ4_B1_MODE_MASK 0x0001 /* EQ4_B1_MODE */ +#define ARIZONA_EQ4_B1_MODE_SHIFT 0 /* EQ4_B1_MODE */ +#define ARIZONA_EQ4_B1_MODE_WIDTH 1 /* EQ4_B1_MODE */ + +/* + * R3668 (0xE54) - EQ4_3 + */ +#define ARIZONA_EQ4_B1_A_MASK 0xFFFF /* EQ4_B1_A - [15:0] */ +#define ARIZONA_EQ4_B1_A_SHIFT 0 /* EQ4_B1_A - [15:0] */ +#define ARIZONA_EQ4_B1_A_WIDTH 16 /* EQ4_B1_A - [15:0] */ + +/* + * R3669 (0xE55) - EQ4_4 + */ +#define ARIZONA_EQ4_B1_B_MASK 0xFFFF /* EQ4_B1_B - [15:0] */ +#define ARIZONA_EQ4_B1_B_SHIFT 0 /* EQ4_B1_B - [15:0] */ +#define ARIZONA_EQ4_B1_B_WIDTH 16 /* EQ4_B1_B - [15:0] */ + +/* + * R3670 (0xE56) - EQ4_5 + */ +#define ARIZONA_EQ4_B1_PG_MASK 0xFFFF /* EQ4_B1_PG - [15:0] */ +#define ARIZONA_EQ4_B1_PG_SHIFT 0 /* EQ4_B1_PG - [15:0] */ +#define ARIZONA_EQ4_B1_PG_WIDTH 16 /* EQ4_B1_PG - [15:0] */ + +/* + * R3671 (0xE57) - EQ4_6 + */ +#define ARIZONA_EQ4_B2_A_MASK 0xFFFF /* EQ4_B2_A - [15:0] */ +#define ARIZONA_EQ4_B2_A_SHIFT 0 /* EQ4_B2_A - [15:0] */ +#define ARIZONA_EQ4_B2_A_WIDTH 16 /* EQ4_B2_A - [15:0] */ + +/* + * R3672 (0xE58) - EQ4_7 + */ +#define ARIZONA_EQ4_B2_B_MASK 0xFFFF /* EQ4_B2_B - [15:0] */ +#define ARIZONA_EQ4_B2_B_SHIFT 0 /* EQ4_B2_B - [15:0] */ +#define ARIZONA_EQ4_B2_B_WIDTH 16 /* EQ4_B2_B - [15:0] */ + +/* + * R3673 (0xE59) - EQ4_8 + */ +#define ARIZONA_EQ4_B2_C_MASK 0xFFFF /* EQ4_B2_C - [15:0] */ +#define ARIZONA_EQ4_B2_C_SHIFT 0 /* EQ4_B2_C - [15:0] */ +#define ARIZONA_EQ4_B2_C_WIDTH 16 /* EQ4_B2_C - [15:0] */ + +/* + * R3674 (0xE5A) - EQ4_9 + */ +#define ARIZONA_EQ4_B2_PG_MASK 0xFFFF /* EQ4_B2_PG - [15:0] */ +#define ARIZONA_EQ4_B2_PG_SHIFT 0 /* EQ4_B2_PG - [15:0] */ +#define ARIZONA_EQ4_B2_PG_WIDTH 16 /* EQ4_B2_PG - [15:0] */ + +/* + * R3675 (0xE5B) - EQ4_10 + */ +#define ARIZONA_EQ4_B3_A_MASK 0xFFFF /* EQ4_B3_A - [15:0] */ +#define ARIZONA_EQ4_B3_A_SHIFT 0 /* EQ4_B3_A - [15:0] */ +#define ARIZONA_EQ4_B3_A_WIDTH 16 /* EQ4_B3_A - [15:0] */ + +/* + * R3676 (0xE5C) - EQ4_11 + */ +#define ARIZONA_EQ4_B3_B_MASK 0xFFFF /* EQ4_B3_B - [15:0] */ +#define ARIZONA_EQ4_B3_B_SHIFT 0 /* EQ4_B3_B - [15:0] */ +#define ARIZONA_EQ4_B3_B_WIDTH 16 /* EQ4_B3_B - [15:0] */ + +/* + * R3677 (0xE5D) - EQ4_12 + */ +#define ARIZONA_EQ4_B3_C_MASK 0xFFFF /* EQ4_B3_C - [15:0] */ +#define ARIZONA_EQ4_B3_C_SHIFT 0 /* EQ4_B3_C - [15:0] */ +#define ARIZONA_EQ4_B3_C_WIDTH 16 /* EQ4_B3_C - [15:0] */ + +/* + * R3678 (0xE5E) - EQ4_13 + */ +#define ARIZONA_EQ4_B3_PG_MASK 0xFFFF /* EQ4_B3_PG - [15:0] */ +#define ARIZONA_EQ4_B3_PG_SHIFT 0 /* EQ4_B3_PG - [15:0] */ +#define ARIZONA_EQ4_B3_PG_WIDTH 16 /* EQ4_B3_PG - [15:0] */ + +/* + * R3679 (0xE5F) - EQ4_14 + */ +#define ARIZONA_EQ4_B4_A_MASK 0xFFFF /* EQ4_B4_A - [15:0] */ +#define ARIZONA_EQ4_B4_A_SHIFT 0 /* EQ4_B4_A - [15:0] */ +#define ARIZONA_EQ4_B4_A_WIDTH 16 /* EQ4_B4_A - [15:0] */ + +/* + * R3680 (0xE60) - EQ4_15 + */ +#define ARIZONA_EQ4_B4_B_MASK 0xFFFF /* EQ4_B4_B - [15:0] */ +#define ARIZONA_EQ4_B4_B_SHIFT 0 /* EQ4_B4_B - [15:0] */ +#define ARIZONA_EQ4_B4_B_WIDTH 16 /* EQ4_B4_B - [15:0] */ + +/* + * R3681 (0xE61) - EQ4_16 + */ +#define ARIZONA_EQ4_B4_C_MASK 0xFFFF /* EQ4_B4_C - [15:0] */ +#define ARIZONA_EQ4_B4_C_SHIFT 0 /* EQ4_B4_C - [15:0] */ +#define ARIZONA_EQ4_B4_C_WIDTH 16 /* EQ4_B4_C - [15:0] */ + +/* + * R3682 (0xE62) - EQ4_17 + */ +#define ARIZONA_EQ4_B4_PG_MASK 0xFFFF /* EQ4_B4_PG - [15:0] */ +#define ARIZONA_EQ4_B4_PG_SHIFT 0 /* EQ4_B4_PG - [15:0] */ +#define ARIZONA_EQ4_B4_PG_WIDTH 16 /* EQ4_B4_PG - [15:0] */ + +/* + * R3683 (0xE63) - EQ4_18 + */ +#define ARIZONA_EQ4_B5_A_MASK 0xFFFF /* EQ4_B5_A - [15:0] */ +#define ARIZONA_EQ4_B5_A_SHIFT 0 /* EQ4_B5_A - [15:0] */ +#define ARIZONA_EQ4_B5_A_WIDTH 16 /* EQ4_B5_A - [15:0] */ + +/* + * R3684 (0xE64) - EQ4_19 + */ +#define ARIZONA_EQ4_B5_B_MASK 0xFFFF /* EQ4_B5_B - [15:0] */ +#define ARIZONA_EQ4_B5_B_SHIFT 0 /* EQ4_B5_B - [15:0] */ +#define ARIZONA_EQ4_B5_B_WIDTH 16 /* EQ4_B5_B - [15:0] */ + +/* + * R3685 (0xE65) - EQ4_20 + */ +#define ARIZONA_EQ4_B5_PG_MASK 0xFFFF /* EQ4_B5_PG - [15:0] */ +#define ARIZONA_EQ4_B5_PG_SHIFT 0 /* EQ4_B5_PG - [15:0] */ +#define ARIZONA_EQ4_B5_PG_WIDTH 16 /* EQ4_B5_PG - [15:0] */ + +/* + * R3686 (0xE66) - EQ4_21 + */ +#define ARIZONA_EQ4_B1_C_MASK 0xFFFF /* EQ4_B1_C - [15:0] */ +#define ARIZONA_EQ4_B1_C_SHIFT 0 /* EQ4_B1_C - [15:0] */ +#define ARIZONA_EQ4_B1_C_WIDTH 16 /* EQ4_B1_C - [15:0] */ + +/* + * R3712 (0xE80) - DRC1 ctrl1 + */ +#define ARIZONA_DRC1_SIG_DET_RMS_MASK 0xF800 /* DRC1_SIG_DET_RMS - [15:11] */ +#define ARIZONA_DRC1_SIG_DET_RMS_SHIFT 11 /* DRC1_SIG_DET_RMS - [15:11] */ +#define ARIZONA_DRC1_SIG_DET_RMS_WIDTH 5 /* DRC1_SIG_DET_RMS - [15:11] */ +#define ARIZONA_DRC1_SIG_DET_PK_MASK 0x0600 /* DRC1_SIG_DET_PK - [10:9] */ +#define ARIZONA_DRC1_SIG_DET_PK_SHIFT 9 /* DRC1_SIG_DET_PK - [10:9] */ +#define ARIZONA_DRC1_SIG_DET_PK_WIDTH 2 /* DRC1_SIG_DET_PK - [10:9] */ +#define ARIZONA_DRC1_NG_ENA 0x0100 /* DRC1_NG_ENA */ +#define ARIZONA_DRC1_NG_ENA_MASK 0x0100 /* DRC1_NG_ENA */ +#define ARIZONA_DRC1_NG_ENA_SHIFT 8 /* DRC1_NG_ENA */ +#define ARIZONA_DRC1_NG_ENA_WIDTH 1 /* DRC1_NG_ENA */ +#define ARIZONA_DRC1_SIG_DET_MODE 0x0080 /* DRC1_SIG_DET_MODE */ +#define ARIZONA_DRC1_SIG_DET_MODE_MASK 0x0080 /* DRC1_SIG_DET_MODE */ +#define ARIZONA_DRC1_SIG_DET_MODE_SHIFT 7 /* DRC1_SIG_DET_MODE */ +#define ARIZONA_DRC1_SIG_DET_MODE_WIDTH 1 /* DRC1_SIG_DET_MODE */ +#define ARIZONA_DRC1_SIG_DET 0x0040 /* DRC1_SIG_DET */ +#define ARIZONA_DRC1_SIG_DET_MASK 0x0040 /* DRC1_SIG_DET */ +#define ARIZONA_DRC1_SIG_DET_SHIFT 6 /* DRC1_SIG_DET */ +#define ARIZONA_DRC1_SIG_DET_WIDTH 1 /* DRC1_SIG_DET */ +#define ARIZONA_DRC1_KNEE2_OP_ENA 0x0020 /* DRC1_KNEE2_OP_ENA */ +#define ARIZONA_DRC1_KNEE2_OP_ENA_MASK 0x0020 /* DRC1_KNEE2_OP_ENA */ +#define ARIZONA_DRC1_KNEE2_OP_ENA_SHIFT 5 /* DRC1_KNEE2_OP_ENA */ +#define ARIZONA_DRC1_KNEE2_OP_ENA_WIDTH 1 /* DRC1_KNEE2_OP_ENA */ +#define ARIZONA_DRC1_QR 0x0010 /* DRC1_QR */ +#define ARIZONA_DRC1_QR_MASK 0x0010 /* DRC1_QR */ +#define ARIZONA_DRC1_QR_SHIFT 4 /* DRC1_QR */ +#define ARIZONA_DRC1_QR_WIDTH 1 /* DRC1_QR */ +#define ARIZONA_DRC1_ANTICLIP 0x0008 /* DRC1_ANTICLIP */ +#define ARIZONA_DRC1_ANTICLIP_MASK 0x0008 /* DRC1_ANTICLIP */ +#define ARIZONA_DRC1_ANTICLIP_SHIFT 3 /* DRC1_ANTICLIP */ +#define ARIZONA_DRC1_ANTICLIP_WIDTH 1 /* DRC1_ANTICLIP */ +#define ARIZONA_DRC1L_ENA 0x0002 /* DRC1L_ENA */ +#define ARIZONA_DRC1L_ENA_MASK 0x0002 /* DRC1L_ENA */ +#define ARIZONA_DRC1L_ENA_SHIFT 1 /* DRC1L_ENA */ +#define ARIZONA_DRC1L_ENA_WIDTH 1 /* DRC1L_ENA */ +#define ARIZONA_DRC1R_ENA 0x0001 /* DRC1R_ENA */ +#define ARIZONA_DRC1R_ENA_MASK 0x0001 /* DRC1R_ENA */ +#define ARIZONA_DRC1R_ENA_SHIFT 0 /* DRC1R_ENA */ +#define ARIZONA_DRC1R_ENA_WIDTH 1 /* DRC1R_ENA */ + +/* + * R3713 (0xE81) - DRC1 ctrl2 + */ +#define ARIZONA_DRC1_ATK_MASK 0x1E00 /* DRC1_ATK - [12:9] */ +#define ARIZONA_DRC1_ATK_SHIFT 9 /* DRC1_ATK - [12:9] */ +#define ARIZONA_DRC1_ATK_WIDTH 4 /* DRC1_ATK - [12:9] */ +#define ARIZONA_DRC1_DCY_MASK 0x01E0 /* DRC1_DCY - [8:5] */ +#define ARIZONA_DRC1_DCY_SHIFT 5 /* DRC1_DCY - [8:5] */ +#define ARIZONA_DRC1_DCY_WIDTH 4 /* DRC1_DCY - [8:5] */ +#define ARIZONA_DRC1_MINGAIN_MASK 0x001C /* DRC1_MINGAIN - [4:2] */ +#define ARIZONA_DRC1_MINGAIN_SHIFT 2 /* DRC1_MINGAIN - [4:2] */ +#define ARIZONA_DRC1_MINGAIN_WIDTH 3 /* DRC1_MINGAIN - [4:2] */ +#define ARIZONA_DRC1_MAXGAIN_MASK 0x0003 /* DRC1_MAXGAIN - [1:0] */ +#define ARIZONA_DRC1_MAXGAIN_SHIFT 0 /* DRC1_MAXGAIN - [1:0] */ +#define ARIZONA_DRC1_MAXGAIN_WIDTH 2 /* DRC1_MAXGAIN - [1:0] */ + +/* + * R3714 (0xE82) - DRC1 ctrl3 + */ +#define ARIZONA_DRC1_NG_MINGAIN_MASK 0xF000 /* DRC1_NG_MINGAIN - [15:12] */ +#define ARIZONA_DRC1_NG_MINGAIN_SHIFT 12 /* DRC1_NG_MINGAIN - [15:12] */ +#define ARIZONA_DRC1_NG_MINGAIN_WIDTH 4 /* DRC1_NG_MINGAIN - [15:12] */ +#define ARIZONA_DRC1_NG_EXP_MASK 0x0C00 /* DRC1_NG_EXP - [11:10] */ +#define ARIZONA_DRC1_NG_EXP_SHIFT 10 /* DRC1_NG_EXP - [11:10] */ +#define ARIZONA_DRC1_NG_EXP_WIDTH 2 /* DRC1_NG_EXP - [11:10] */ +#define ARIZONA_DRC1_QR_THR_MASK 0x0300 /* DRC1_QR_THR - [9:8] */ +#define ARIZONA_DRC1_QR_THR_SHIFT 8 /* DRC1_QR_THR - [9:8] */ +#define ARIZONA_DRC1_QR_THR_WIDTH 2 /* DRC1_QR_THR - [9:8] */ +#define ARIZONA_DRC1_QR_DCY_MASK 0x00C0 /* DRC1_QR_DCY - [7:6] */ +#define ARIZONA_DRC1_QR_DCY_SHIFT 6 /* DRC1_QR_DCY - [7:6] */ +#define ARIZONA_DRC1_QR_DCY_WIDTH 2 /* DRC1_QR_DCY - [7:6] */ +#define ARIZONA_DRC1_HI_COMP_MASK 0x0038 /* DRC1_HI_COMP - [5:3] */ +#define ARIZONA_DRC1_HI_COMP_SHIFT 3 /* DRC1_HI_COMP - [5:3] */ +#define ARIZONA_DRC1_HI_COMP_WIDTH 3 /* DRC1_HI_COMP - [5:3] */ +#define ARIZONA_DRC1_LO_COMP_MASK 0x0007 /* DRC1_LO_COMP - [2:0] */ +#define ARIZONA_DRC1_LO_COMP_SHIFT 0 /* DRC1_LO_COMP - [2:0] */ +#define ARIZONA_DRC1_LO_COMP_WIDTH 3 /* DRC1_LO_COMP - [2:0] */ + +/* + * R3715 (0xE83) - DRC1 ctrl4 + */ +#define ARIZONA_DRC1_KNEE_IP_MASK 0x07E0 /* DRC1_KNEE_IP - [10:5] */ +#define ARIZONA_DRC1_KNEE_IP_SHIFT 5 /* DRC1_KNEE_IP - [10:5] */ +#define ARIZONA_DRC1_KNEE_IP_WIDTH 6 /* DRC1_KNEE_IP - [10:5] */ +#define ARIZONA_DRC1_KNEE_OP_MASK 0x001F /* DRC1_KNEE_OP - [4:0] */ +#define ARIZONA_DRC1_KNEE_OP_SHIFT 0 /* DRC1_KNEE_OP - [4:0] */ +#define ARIZONA_DRC1_KNEE_OP_WIDTH 5 /* DRC1_KNEE_OP - [4:0] */ + +/* + * R3716 (0xE84) - DRC1 ctrl5 + */ +#define ARIZONA_DRC1_KNEE2_IP_MASK 0x03E0 /* DRC1_KNEE2_IP - [9:5] */ +#define ARIZONA_DRC1_KNEE2_IP_SHIFT 5 /* DRC1_KNEE2_IP - [9:5] */ +#define ARIZONA_DRC1_KNEE2_IP_WIDTH 5 /* DRC1_KNEE2_IP - [9:5] */ +#define ARIZONA_DRC1_KNEE2_OP_MASK 0x001F /* DRC1_KNEE2_OP - [4:0] */ +#define ARIZONA_DRC1_KNEE2_OP_SHIFT 0 /* DRC1_KNEE2_OP - [4:0] */ +#define ARIZONA_DRC1_KNEE2_OP_WIDTH 5 /* DRC1_KNEE2_OP - [4:0] */ + +/* + * R3721 (0xE89) - DRC2 ctrl1 + */ +#define ARIZONA_DRC2_SIG_DET_RMS_MASK 0xF800 /* DRC2_SIG_DET_RMS - [15:11] */ +#define ARIZONA_DRC2_SIG_DET_RMS_SHIFT 11 /* DRC2_SIG_DET_RMS - [15:11] */ +#define ARIZONA_DRC2_SIG_DET_RMS_WIDTH 5 /* DRC2_SIG_DET_RMS - [15:11] */ +#define ARIZONA_DRC2_SIG_DET_PK_MASK 0x0600 /* DRC2_SIG_DET_PK - [10:9] */ +#define ARIZONA_DRC2_SIG_DET_PK_SHIFT 9 /* DRC2_SIG_DET_PK - [10:9] */ +#define ARIZONA_DRC2_SIG_DET_PK_WIDTH 2 /* DRC2_SIG_DET_PK - [10:9] */ +#define ARIZONA_DRC2_NG_ENA 0x0100 /* DRC2_NG_ENA */ +#define ARIZONA_DRC2_NG_ENA_MASK 0x0100 /* DRC2_NG_ENA */ +#define ARIZONA_DRC2_NG_ENA_SHIFT 8 /* DRC2_NG_ENA */ +#define ARIZONA_DRC2_NG_ENA_WIDTH 1 /* DRC2_NG_ENA */ +#define ARIZONA_DRC2_SIG_DET_MODE 0x0080 /* DRC2_SIG_DET_MODE */ +#define ARIZONA_DRC2_SIG_DET_MODE_MASK 0x0080 /* DRC2_SIG_DET_MODE */ +#define ARIZONA_DRC2_SIG_DET_MODE_SHIFT 7 /* DRC2_SIG_DET_MODE */ +#define ARIZONA_DRC2_SIG_DET_MODE_WIDTH 1 /* DRC2_SIG_DET_MODE */ +#define ARIZONA_DRC2_SIG_DET 0x0040 /* DRC2_SIG_DET */ +#define ARIZONA_DRC2_SIG_DET_MASK 0x0040 /* DRC2_SIG_DET */ +#define ARIZONA_DRC2_SIG_DET_SHIFT 6 /* DRC2_SIG_DET */ +#define ARIZONA_DRC2_SIG_DET_WIDTH 1 /* DRC2_SIG_DET */ +#define ARIZONA_DRC2_KNEE2_OP_ENA 0x0020 /* DRC2_KNEE2_OP_ENA */ +#define ARIZONA_DRC2_KNEE2_OP_ENA_MASK 0x0020 /* DRC2_KNEE2_OP_ENA */ +#define ARIZONA_DRC2_KNEE2_OP_ENA_SHIFT 5 /* DRC2_KNEE2_OP_ENA */ +#define ARIZONA_DRC2_KNEE2_OP_ENA_WIDTH 1 /* DRC2_KNEE2_OP_ENA */ +#define ARIZONA_DRC2_QR 0x0010 /* DRC2_QR */ +#define ARIZONA_DRC2_QR_MASK 0x0010 /* DRC2_QR */ +#define ARIZONA_DRC2_QR_SHIFT 4 /* DRC2_QR */ +#define ARIZONA_DRC2_QR_WIDTH 1 /* DRC2_QR */ +#define ARIZONA_DRC2_ANTICLIP 0x0008 /* DRC2_ANTICLIP */ +#define ARIZONA_DRC2_ANTICLIP_MASK 0x0008 /* DRC2_ANTICLIP */ +#define ARIZONA_DRC2_ANTICLIP_SHIFT 3 /* DRC2_ANTICLIP */ +#define ARIZONA_DRC2_ANTICLIP_WIDTH 1 /* DRC2_ANTICLIP */ +#define ARIZONA_DRC2L_ENA 0x0002 /* DRC2L_ENA */ +#define ARIZONA_DRC2L_ENA_MASK 0x0002 /* DRC2L_ENA */ +#define ARIZONA_DRC2L_ENA_SHIFT 1 /* DRC2L_ENA */ +#define ARIZONA_DRC2L_ENA_WIDTH 1 /* DRC2L_ENA */ +#define ARIZONA_DRC2R_ENA 0x0001 /* DRC2R_ENA */ +#define ARIZONA_DRC2R_ENA_MASK 0x0001 /* DRC2R_ENA */ +#define ARIZONA_DRC2R_ENA_SHIFT 0 /* DRC2R_ENA */ +#define ARIZONA_DRC2R_ENA_WIDTH 1 /* DRC2R_ENA */ + +/* + * R3722 (0xE8A) - DRC2 ctrl2 + */ +#define ARIZONA_DRC2_ATK_MASK 0x1E00 /* DRC2_ATK - [12:9] */ +#define ARIZONA_DRC2_ATK_SHIFT 9 /* DRC2_ATK - [12:9] */ +#define ARIZONA_DRC2_ATK_WIDTH 4 /* DRC2_ATK - [12:9] */ +#define ARIZONA_DRC2_DCY_MASK 0x01E0 /* DRC2_DCY - [8:5] */ +#define ARIZONA_DRC2_DCY_SHIFT 5 /* DRC2_DCY - [8:5] */ +#define ARIZONA_DRC2_DCY_WIDTH 4 /* DRC2_DCY - [8:5] */ +#define ARIZONA_DRC2_MINGAIN_MASK 0x001C /* DRC2_MINGAIN - [4:2] */ +#define ARIZONA_DRC2_MINGAIN_SHIFT 2 /* DRC2_MINGAIN - [4:2] */ +#define ARIZONA_DRC2_MINGAIN_WIDTH 3 /* DRC2_MINGAIN - [4:2] */ +#define ARIZONA_DRC2_MAXGAIN_MASK 0x0003 /* DRC2_MAXGAIN - [1:0] */ +#define ARIZONA_DRC2_MAXGAIN_SHIFT 0 /* DRC2_MAXGAIN - [1:0] */ +#define ARIZONA_DRC2_MAXGAIN_WIDTH 2 /* DRC2_MAXGAIN - [1:0] */ + +/* + * R3723 (0xE8B) - DRC2 ctrl3 + */ +#define ARIZONA_DRC2_NG_MINGAIN_MASK 0xF000 /* DRC2_NG_MINGAIN - [15:12] */ +#define ARIZONA_DRC2_NG_MINGAIN_SHIFT 12 /* DRC2_NG_MINGAIN - [15:12] */ +#define ARIZONA_DRC2_NG_MINGAIN_WIDTH 4 /* DRC2_NG_MINGAIN - [15:12] */ +#define ARIZONA_DRC2_NG_EXP_MASK 0x0C00 /* DRC2_NG_EXP - [11:10] */ +#define ARIZONA_DRC2_NG_EXP_SHIFT 10 /* DRC2_NG_EXP - [11:10] */ +#define ARIZONA_DRC2_NG_EXP_WIDTH 2 /* DRC2_NG_EXP - [11:10] */ +#define ARIZONA_DRC2_QR_THR_MASK 0x0300 /* DRC2_QR_THR - [9:8] */ +#define ARIZONA_DRC2_QR_THR_SHIFT 8 /* DRC2_QR_THR - [9:8] */ +#define ARIZONA_DRC2_QR_THR_WIDTH 2 /* DRC2_QR_THR - [9:8] */ +#define ARIZONA_DRC2_QR_DCY_MASK 0x00C0 /* DRC2_QR_DCY - [7:6] */ +#define ARIZONA_DRC2_QR_DCY_SHIFT 6 /* DRC2_QR_DCY - [7:6] */ +#define ARIZONA_DRC2_QR_DCY_WIDTH 2 /* DRC2_QR_DCY - [7:6] */ +#define ARIZONA_DRC2_HI_COMP_MASK 0x0038 /* DRC2_HI_COMP - [5:3] */ +#define ARIZONA_DRC2_HI_COMP_SHIFT 3 /* DRC2_HI_COMP - [5:3] */ +#define ARIZONA_DRC2_HI_COMP_WIDTH 3 /* DRC2_HI_COMP - [5:3] */ +#define ARIZONA_DRC2_LO_COMP_MASK 0x0007 /* DRC2_LO_COMP - [2:0] */ +#define ARIZONA_DRC2_LO_COMP_SHIFT 0 /* DRC2_LO_COMP - [2:0] */ +#define ARIZONA_DRC2_LO_COMP_WIDTH 3 /* DRC2_LO_COMP - [2:0] */ + +/* + * R3724 (0xE8C) - DRC2 ctrl4 + */ +#define ARIZONA_DRC2_KNEE_IP_MASK 0x07E0 /* DRC2_KNEE_IP - [10:5] */ +#define ARIZONA_DRC2_KNEE_IP_SHIFT 5 /* DRC2_KNEE_IP - [10:5] */ +#define ARIZONA_DRC2_KNEE_IP_WIDTH 6 /* DRC2_KNEE_IP - [10:5] */ +#define ARIZONA_DRC2_KNEE_OP_MASK 0x001F /* DRC2_KNEE_OP - [4:0] */ +#define ARIZONA_DRC2_KNEE_OP_SHIFT 0 /* DRC2_KNEE_OP - [4:0] */ +#define ARIZONA_DRC2_KNEE_OP_WIDTH 5 /* DRC2_KNEE_OP - [4:0] */ + +/* + * R3725 (0xE8D) - DRC2 ctrl5 + */ +#define ARIZONA_DRC2_KNEE2_IP_MASK 0x03E0 /* DRC2_KNEE2_IP - [9:5] */ +#define ARIZONA_DRC2_KNEE2_IP_SHIFT 5 /* DRC2_KNEE2_IP - [9:5] */ +#define ARIZONA_DRC2_KNEE2_IP_WIDTH 5 /* DRC2_KNEE2_IP - [9:5] */ +#define ARIZONA_DRC2_KNEE2_OP_MASK 0x001F /* DRC2_KNEE2_OP - [4:0] */ +#define ARIZONA_DRC2_KNEE2_OP_SHIFT 0 /* DRC2_KNEE2_OP - [4:0] */ +#define ARIZONA_DRC2_KNEE2_OP_WIDTH 5 /* DRC2_KNEE2_OP - [4:0] */ + +/* + * R3776 (0xEC0) - HPLPF1_1 + */ +#define ARIZONA_LHPF1_MODE 0x0002 /* LHPF1_MODE */ +#define ARIZONA_LHPF1_MODE_MASK 0x0002 /* LHPF1_MODE */ +#define ARIZONA_LHPF1_MODE_SHIFT 1 /* LHPF1_MODE */ +#define ARIZONA_LHPF1_MODE_WIDTH 1 /* LHPF1_MODE */ +#define ARIZONA_LHPF1_ENA 0x0001 /* LHPF1_ENA */ +#define ARIZONA_LHPF1_ENA_MASK 0x0001 /* LHPF1_ENA */ +#define ARIZONA_LHPF1_ENA_SHIFT 0 /* LHPF1_ENA */ +#define ARIZONA_LHPF1_ENA_WIDTH 1 /* LHPF1_ENA */ + +/* + * R3777 (0xEC1) - HPLPF1_2 + */ +#define ARIZONA_LHPF1_COEFF_MASK 0xFFFF /* LHPF1_COEFF - [15:0] */ +#define ARIZONA_LHPF1_COEFF_SHIFT 0 /* LHPF1_COEFF - [15:0] */ +#define ARIZONA_LHPF1_COEFF_WIDTH 16 /* LHPF1_COEFF - [15:0] */ + +/* + * R3780 (0xEC4) - HPLPF2_1 + */ +#define ARIZONA_LHPF2_MODE 0x0002 /* LHPF2_MODE */ +#define ARIZONA_LHPF2_MODE_MASK 0x0002 /* LHPF2_MODE */ +#define ARIZONA_LHPF2_MODE_SHIFT 1 /* LHPF2_MODE */ +#define ARIZONA_LHPF2_MODE_WIDTH 1 /* LHPF2_MODE */ +#define ARIZONA_LHPF2_ENA 0x0001 /* LHPF2_ENA */ +#define ARIZONA_LHPF2_ENA_MASK 0x0001 /* LHPF2_ENA */ +#define ARIZONA_LHPF2_ENA_SHIFT 0 /* LHPF2_ENA */ +#define ARIZONA_LHPF2_ENA_WIDTH 1 /* LHPF2_ENA */ + +/* + * R3781 (0xEC5) - HPLPF2_2 + */ +#define ARIZONA_LHPF2_COEFF_MASK 0xFFFF /* LHPF2_COEFF - [15:0] */ +#define ARIZONA_LHPF2_COEFF_SHIFT 0 /* LHPF2_COEFF - [15:0] */ +#define ARIZONA_LHPF2_COEFF_WIDTH 16 /* LHPF2_COEFF - [15:0] */ + +/* + * R3784 (0xEC8) - HPLPF3_1 + */ +#define ARIZONA_LHPF3_MODE 0x0002 /* LHPF3_MODE */ +#define ARIZONA_LHPF3_MODE_MASK 0x0002 /* LHPF3_MODE */ +#define ARIZONA_LHPF3_MODE_SHIFT 1 /* LHPF3_MODE */ +#define ARIZONA_LHPF3_MODE_WIDTH 1 /* LHPF3_MODE */ +#define ARIZONA_LHPF3_ENA 0x0001 /* LHPF3_ENA */ +#define ARIZONA_LHPF3_ENA_MASK 0x0001 /* LHPF3_ENA */ +#define ARIZONA_LHPF3_ENA_SHIFT 0 /* LHPF3_ENA */ +#define ARIZONA_LHPF3_ENA_WIDTH 1 /* LHPF3_ENA */ + +/* + * R3785 (0xEC9) - HPLPF3_2 + */ +#define ARIZONA_LHPF3_COEFF_MASK 0xFFFF /* LHPF3_COEFF - [15:0] */ +#define ARIZONA_LHPF3_COEFF_SHIFT 0 /* LHPF3_COEFF - [15:0] */ +#define ARIZONA_LHPF3_COEFF_WIDTH 16 /* LHPF3_COEFF - [15:0] */ + +/* + * R3788 (0xECC) - HPLPF4_1 + */ +#define ARIZONA_LHPF4_MODE 0x0002 /* LHPF4_MODE */ +#define ARIZONA_LHPF4_MODE_MASK 0x0002 /* LHPF4_MODE */ +#define ARIZONA_LHPF4_MODE_SHIFT 1 /* LHPF4_MODE */ +#define ARIZONA_LHPF4_MODE_WIDTH 1 /* LHPF4_MODE */ +#define ARIZONA_LHPF4_ENA 0x0001 /* LHPF4_ENA */ +#define ARIZONA_LHPF4_ENA_MASK 0x0001 /* LHPF4_ENA */ +#define ARIZONA_LHPF4_ENA_SHIFT 0 /* LHPF4_ENA */ +#define ARIZONA_LHPF4_ENA_WIDTH 1 /* LHPF4_ENA */ + +/* + * R3789 (0xECD) - HPLPF4_2 + */ +#define ARIZONA_LHPF4_COEFF_MASK 0xFFFF /* LHPF4_COEFF - [15:0] */ +#define ARIZONA_LHPF4_COEFF_SHIFT 0 /* LHPF4_COEFF - [15:0] */ +#define ARIZONA_LHPF4_COEFF_WIDTH 16 /* LHPF4_COEFF - [15:0] */ + +/* + * R3808 (0xEE0) - ASRC_ENABLE + */ +#define ARIZONA_ASRC2L_ENA 0x0008 /* ASRC2L_ENA */ +#define ARIZONA_ASRC2L_ENA_MASK 0x0008 /* ASRC2L_ENA */ +#define ARIZONA_ASRC2L_ENA_SHIFT 3 /* ASRC2L_ENA */ +#define ARIZONA_ASRC2L_ENA_WIDTH 1 /* ASRC2L_ENA */ +#define ARIZONA_ASRC2R_ENA 0x0004 /* ASRC2R_ENA */ +#define ARIZONA_ASRC2R_ENA_MASK 0x0004 /* ASRC2R_ENA */ +#define ARIZONA_ASRC2R_ENA_SHIFT 2 /* ASRC2R_ENA */ +#define ARIZONA_ASRC2R_ENA_WIDTH 1 /* ASRC2R_ENA */ +#define ARIZONA_ASRC1L_ENA 0x0002 /* ASRC1L_ENA */ +#define ARIZONA_ASRC1L_ENA_MASK 0x0002 /* ASRC1L_ENA */ +#define ARIZONA_ASRC1L_ENA_SHIFT 1 /* ASRC1L_ENA */ +#define ARIZONA_ASRC1L_ENA_WIDTH 1 /* ASRC1L_ENA */ +#define ARIZONA_ASRC1R_ENA 0x0001 /* ASRC1R_ENA */ +#define ARIZONA_ASRC1R_ENA_MASK 0x0001 /* ASRC1R_ENA */ +#define ARIZONA_ASRC1R_ENA_SHIFT 0 /* ASRC1R_ENA */ +#define ARIZONA_ASRC1R_ENA_WIDTH 1 /* ASRC1R_ENA */ + +/* + * R3810 (0xEE2) - ASRC_RATE1 + */ +#define ARIZONA_ASRC_RATE1_MASK 0x7800 /* ASRC_RATE1 - [14:11] */ +#define ARIZONA_ASRC_RATE1_SHIFT 11 /* ASRC_RATE1 - [14:11] */ +#define ARIZONA_ASRC_RATE1_WIDTH 4 /* ASRC_RATE1 - [14:11] */ + +/* + * R3811 (0xEE3) - ASRC_RATE2 + */ +#define ARIZONA_ASRC_RATE2_MASK 0x7800 /* ASRC_RATE2 - [14:11] */ +#define ARIZONA_ASRC_RATE2_SHIFT 11 /* ASRC_RATE2 - [14:11] */ +#define ARIZONA_ASRC_RATE2_WIDTH 4 /* ASRC_RATE2 - [14:11] */ + +/* + * R3824 (0xEF0) - ISRC 1 CTRL 1 + */ +#define ARIZONA_ISRC1_FSH_MASK 0x7800 /* ISRC1_FSH - [14:11] */ +#define ARIZONA_ISRC1_FSH_SHIFT 11 /* ISRC1_FSH - [14:11] */ +#define ARIZONA_ISRC1_FSH_WIDTH 4 /* ISRC1_FSH - [14:11] */ +#define ARIZONA_ISRC1_CLK_SEL_MASK 0x0700 /* ISRC1_CLK_SEL - [10:8] */ +#define ARIZONA_ISRC1_CLK_SEL_SHIFT 8 /* ISRC1_CLK_SEL - [10:8] */ +#define ARIZONA_ISRC1_CLK_SEL_WIDTH 3 /* ISRC1_CLK_SEL - [10:8] */ + +/* + * R3825 (0xEF1) - ISRC 1 CTRL 2 + */ +#define ARIZONA_ISRC1_FSL_MASK 0x7800 /* ISRC1_FSL - [14:11] */ +#define ARIZONA_ISRC1_FSL_SHIFT 11 /* ISRC1_FSL - [14:11] */ +#define ARIZONA_ISRC1_FSL_WIDTH 4 /* ISRC1_FSL - [14:11] */ + +/* + * R3826 (0xEF2) - ISRC 1 CTRL 3 + */ +#define ARIZONA_ISRC1_INT0_ENA 0x8000 /* ISRC1_INT0_ENA */ +#define ARIZONA_ISRC1_INT0_ENA_MASK 0x8000 /* ISRC1_INT0_ENA */ +#define ARIZONA_ISRC1_INT0_ENA_SHIFT 15 /* ISRC1_INT0_ENA */ +#define ARIZONA_ISRC1_INT0_ENA_WIDTH 1 /* ISRC1_INT0_ENA */ +#define ARIZONA_ISRC1_INT1_ENA 0x4000 /* ISRC1_INT1_ENA */ +#define ARIZONA_ISRC1_INT1_ENA_MASK 0x4000 /* ISRC1_INT1_ENA */ +#define ARIZONA_ISRC1_INT1_ENA_SHIFT 14 /* ISRC1_INT1_ENA */ +#define ARIZONA_ISRC1_INT1_ENA_WIDTH 1 /* ISRC1_INT1_ENA */ +#define ARIZONA_ISRC1_INT2_ENA 0x2000 /* ISRC1_INT2_ENA */ +#define ARIZONA_ISRC1_INT2_ENA_MASK 0x2000 /* ISRC1_INT2_ENA */ +#define ARIZONA_ISRC1_INT2_ENA_SHIFT 13 /* ISRC1_INT2_ENA */ +#define ARIZONA_ISRC1_INT2_ENA_WIDTH 1 /* ISRC1_INT2_ENA */ +#define ARIZONA_ISRC1_INT3_ENA 0x1000 /* ISRC1_INT3_ENA */ +#define ARIZONA_ISRC1_INT3_ENA_MASK 0x1000 /* ISRC1_INT3_ENA */ +#define ARIZONA_ISRC1_INT3_ENA_SHIFT 12 /* ISRC1_INT3_ENA */ +#define ARIZONA_ISRC1_INT3_ENA_WIDTH 1 /* ISRC1_INT3_ENA */ +#define ARIZONA_ISRC1_DEC0_ENA 0x0200 /* ISRC1_DEC0_ENA */ +#define ARIZONA_ISRC1_DEC0_ENA_MASK 0x0200 /* ISRC1_DEC0_ENA */ +#define ARIZONA_ISRC1_DEC0_ENA_SHIFT 9 /* ISRC1_DEC0_ENA */ +#define ARIZONA_ISRC1_DEC0_ENA_WIDTH 1 /* ISRC1_DEC0_ENA */ +#define ARIZONA_ISRC1_DEC1_ENA 0x0100 /* ISRC1_DEC1_ENA */ +#define ARIZONA_ISRC1_DEC1_ENA_MASK 0x0100 /* ISRC1_DEC1_ENA */ +#define ARIZONA_ISRC1_DEC1_ENA_SHIFT 8 /* ISRC1_DEC1_ENA */ +#define ARIZONA_ISRC1_DEC1_ENA_WIDTH 1 /* ISRC1_DEC1_ENA */ +#define ARIZONA_ISRC1_DEC2_ENA 0x0080 /* ISRC1_DEC2_ENA */ +#define ARIZONA_ISRC1_DEC2_ENA_MASK 0x0080 /* ISRC1_DEC2_ENA */ +#define ARIZONA_ISRC1_DEC2_ENA_SHIFT 7 /* ISRC1_DEC2_ENA */ +#define ARIZONA_ISRC1_DEC2_ENA_WIDTH 1 /* ISRC1_DEC2_ENA */ +#define ARIZONA_ISRC1_DEC3_ENA 0x0040 /* ISRC1_DEC3_ENA */ +#define ARIZONA_ISRC1_DEC3_ENA_MASK 0x0040 /* ISRC1_DEC3_ENA */ +#define ARIZONA_ISRC1_DEC3_ENA_SHIFT 6 /* ISRC1_DEC3_ENA */ +#define ARIZONA_ISRC1_DEC3_ENA_WIDTH 1 /* ISRC1_DEC3_ENA */ +#define ARIZONA_ISRC1_NOTCH_ENA 0x0001 /* ISRC1_NOTCH_ENA */ +#define ARIZONA_ISRC1_NOTCH_ENA_MASK 0x0001 /* ISRC1_NOTCH_ENA */ +#define ARIZONA_ISRC1_NOTCH_ENA_SHIFT 0 /* ISRC1_NOTCH_ENA */ +#define ARIZONA_ISRC1_NOTCH_ENA_WIDTH 1 /* ISRC1_NOTCH_ENA */ + +/* + * R3827 (0xEF3) - ISRC 2 CTRL 1 + */ +#define ARIZONA_ISRC2_FSH_MASK 0x7800 /* ISRC2_FSH - [14:11] */ +#define ARIZONA_ISRC2_FSH_SHIFT 11 /* ISRC2_FSH - [14:11] */ +#define ARIZONA_ISRC2_FSH_WIDTH 4 /* ISRC2_FSH - [14:11] */ +#define ARIZONA_ISRC2_CLK_SEL_MASK 0x0700 /* ISRC2_CLK_SEL - [10:8] */ +#define ARIZONA_ISRC2_CLK_SEL_SHIFT 8 /* ISRC2_CLK_SEL - [10:8] */ +#define ARIZONA_ISRC2_CLK_SEL_WIDTH 3 /* ISRC2_CLK_SEL - [10:8] */ + +/* + * R3828 (0xEF4) - ISRC 2 CTRL 2 + */ +#define ARIZONA_ISRC2_FSL_MASK 0x7800 /* ISRC2_FSL - [14:11] */ +#define ARIZONA_ISRC2_FSL_SHIFT 11 /* ISRC2_FSL - [14:11] */ +#define ARIZONA_ISRC2_FSL_WIDTH 4 /* ISRC2_FSL - [14:11] */ + +/* + * R3829 (0xEF5) - ISRC 2 CTRL 3 + */ +#define ARIZONA_ISRC2_INT0_ENA 0x8000 /* ISRC2_INT0_ENA */ +#define ARIZONA_ISRC2_INT0_ENA_MASK 0x8000 /* ISRC2_INT0_ENA */ +#define ARIZONA_ISRC2_INT0_ENA_SHIFT 15 /* ISRC2_INT0_ENA */ +#define ARIZONA_ISRC2_INT0_ENA_WIDTH 1 /* ISRC2_INT0_ENA */ +#define ARIZONA_ISRC2_INT1_ENA 0x4000 /* ISRC2_INT1_ENA */ +#define ARIZONA_ISRC2_INT1_ENA_MASK 0x4000 /* ISRC2_INT1_ENA */ +#define ARIZONA_ISRC2_INT1_ENA_SHIFT 14 /* ISRC2_INT1_ENA */ +#define ARIZONA_ISRC2_INT1_ENA_WIDTH 1 /* ISRC2_INT1_ENA */ +#define ARIZONA_ISRC2_INT2_ENA 0x2000 /* ISRC2_INT2_ENA */ +#define ARIZONA_ISRC2_INT2_ENA_MASK 0x2000 /* ISRC2_INT2_ENA */ +#define ARIZONA_ISRC2_INT2_ENA_SHIFT 13 /* ISRC2_INT2_ENA */ +#define ARIZONA_ISRC2_INT2_ENA_WIDTH 1 /* ISRC2_INT2_ENA */ +#define ARIZONA_ISRC2_INT3_ENA 0x1000 /* ISRC2_INT3_ENA */ +#define ARIZONA_ISRC2_INT3_ENA_MASK 0x1000 /* ISRC2_INT3_ENA */ +#define ARIZONA_ISRC2_INT3_ENA_SHIFT 12 /* ISRC2_INT3_ENA */ +#define ARIZONA_ISRC2_INT3_ENA_WIDTH 1 /* ISRC2_INT3_ENA */ +#define ARIZONA_ISRC2_DEC0_ENA 0x0200 /* ISRC2_DEC0_ENA */ +#define ARIZONA_ISRC2_DEC0_ENA_MASK 0x0200 /* ISRC2_DEC0_ENA */ +#define ARIZONA_ISRC2_DEC0_ENA_SHIFT 9 /* ISRC2_DEC0_ENA */ +#define ARIZONA_ISRC2_DEC0_ENA_WIDTH 1 /* ISRC2_DEC0_ENA */ +#define ARIZONA_ISRC2_DEC1_ENA 0x0100 /* ISRC2_DEC1_ENA */ +#define ARIZONA_ISRC2_DEC1_ENA_MASK 0x0100 /* ISRC2_DEC1_ENA */ +#define ARIZONA_ISRC2_DEC1_ENA_SHIFT 8 /* ISRC2_DEC1_ENA */ +#define ARIZONA_ISRC2_DEC1_ENA_WIDTH 1 /* ISRC2_DEC1_ENA */ +#define ARIZONA_ISRC2_DEC2_ENA 0x0080 /* ISRC2_DEC2_ENA */ +#define ARIZONA_ISRC2_DEC2_ENA_MASK 0x0080 /* ISRC2_DEC2_ENA */ +#define ARIZONA_ISRC2_DEC2_ENA_SHIFT 7 /* ISRC2_DEC2_ENA */ +#define ARIZONA_ISRC2_DEC2_ENA_WIDTH 1 /* ISRC2_DEC2_ENA */ +#define ARIZONA_ISRC2_DEC3_ENA 0x0040 /* ISRC2_DEC3_ENA */ +#define ARIZONA_ISRC2_DEC3_ENA_MASK 0x0040 /* ISRC2_DEC3_ENA */ +#define ARIZONA_ISRC2_DEC3_ENA_SHIFT 6 /* ISRC2_DEC3_ENA */ +#define ARIZONA_ISRC2_DEC3_ENA_WIDTH 1 /* ISRC2_DEC3_ENA */ +#define ARIZONA_ISRC2_NOTCH_ENA 0x0001 /* ISRC2_NOTCH_ENA */ +#define ARIZONA_ISRC2_NOTCH_ENA_MASK 0x0001 /* ISRC2_NOTCH_ENA */ +#define ARIZONA_ISRC2_NOTCH_ENA_SHIFT 0 /* ISRC2_NOTCH_ENA */ +#define ARIZONA_ISRC2_NOTCH_ENA_WIDTH 1 /* ISRC2_NOTCH_ENA */ + +/* + * R3830 (0xEF6) - ISRC 3 CTRL 1 + */ +#define ARIZONA_ISRC3_FSH_MASK 0x7800 /* ISRC3_FSH - [14:11] */ +#define ARIZONA_ISRC3_FSH_SHIFT 11 /* ISRC3_FSH - [14:11] */ +#define ARIZONA_ISRC3_FSH_WIDTH 4 /* ISRC3_FSH - [14:11] */ +#define ARIZONA_ISRC3_CLK_SEL_MASK 0x0700 /* ISRC3_CLK_SEL - [10:8] */ +#define ARIZONA_ISRC3_CLK_SEL_SHIFT 8 /* ISRC3_CLK_SEL - [10:8] */ +#define ARIZONA_ISRC3_CLK_SEL_WIDTH 3 /* ISRC3_CLK_SEL - [10:8] */ + +/* + * R3831 (0xEF7) - ISRC 3 CTRL 2 + */ +#define ARIZONA_ISRC3_FSL_MASK 0x7800 /* ISRC3_FSL - [14:11] */ +#define ARIZONA_ISRC3_FSL_SHIFT 11 /* ISRC3_FSL - [14:11] */ +#define ARIZONA_ISRC3_FSL_WIDTH 4 /* ISRC3_FSL - [14:11] */ + +/* + * R3832 (0xEF8) - ISRC 3 CTRL 3 + */ +#define ARIZONA_ISRC3_INT0_ENA 0x8000 /* ISRC3_INT0_ENA */ +#define ARIZONA_ISRC3_INT0_ENA_MASK 0x8000 /* ISRC3_INT0_ENA */ +#define ARIZONA_ISRC3_INT0_ENA_SHIFT 15 /* ISRC3_INT0_ENA */ +#define ARIZONA_ISRC3_INT0_ENA_WIDTH 1 /* ISRC3_INT0_ENA */ +#define ARIZONA_ISRC3_INT1_ENA 0x4000 /* ISRC3_INT1_ENA */ +#define ARIZONA_ISRC3_INT1_ENA_MASK 0x4000 /* ISRC3_INT1_ENA */ +#define ARIZONA_ISRC3_INT1_ENA_SHIFT 14 /* ISRC3_INT1_ENA */ +#define ARIZONA_ISRC3_INT1_ENA_WIDTH 1 /* ISRC3_INT1_ENA */ +#define ARIZONA_ISRC3_INT2_ENA 0x2000 /* ISRC3_INT2_ENA */ +#define ARIZONA_ISRC3_INT2_ENA_MASK 0x2000 /* ISRC3_INT2_ENA */ +#define ARIZONA_ISRC3_INT2_ENA_SHIFT 13 /* ISRC3_INT2_ENA */ +#define ARIZONA_ISRC3_INT2_ENA_WIDTH 1 /* ISRC3_INT2_ENA */ +#define ARIZONA_ISRC3_INT3_ENA 0x1000 /* ISRC3_INT3_ENA */ +#define ARIZONA_ISRC3_INT3_ENA_MASK 0x1000 /* ISRC3_INT3_ENA */ +#define ARIZONA_ISRC3_INT3_ENA_SHIFT 12 /* ISRC3_INT3_ENA */ +#define ARIZONA_ISRC3_INT3_ENA_WIDTH 1 /* ISRC3_INT3_ENA */ +#define ARIZONA_ISRC3_DEC0_ENA 0x0200 /* ISRC3_DEC0_ENA */ +#define ARIZONA_ISRC3_DEC0_ENA_MASK 0x0200 /* ISRC3_DEC0_ENA */ +#define ARIZONA_ISRC3_DEC0_ENA_SHIFT 9 /* ISRC3_DEC0_ENA */ +#define ARIZONA_ISRC3_DEC0_ENA_WIDTH 1 /* ISRC3_DEC0_ENA */ +#define ARIZONA_ISRC3_DEC1_ENA 0x0100 /* ISRC3_DEC1_ENA */ +#define ARIZONA_ISRC3_DEC1_ENA_MASK 0x0100 /* ISRC3_DEC1_ENA */ +#define ARIZONA_ISRC3_DEC1_ENA_SHIFT 8 /* ISRC3_DEC1_ENA */ +#define ARIZONA_ISRC3_DEC1_ENA_WIDTH 1 /* ISRC3_DEC1_ENA */ +#define ARIZONA_ISRC3_DEC2_ENA 0x0080 /* ISRC3_DEC2_ENA */ +#define ARIZONA_ISRC3_DEC2_ENA_MASK 0x0080 /* ISRC3_DEC2_ENA */ +#define ARIZONA_ISRC3_DEC2_ENA_SHIFT 7 /* ISRC3_DEC2_ENA */ +#define ARIZONA_ISRC3_DEC2_ENA_WIDTH 1 /* ISRC3_DEC2_ENA */ +#define ARIZONA_ISRC3_DEC3_ENA 0x0040 /* ISRC3_DEC3_ENA */ +#define ARIZONA_ISRC3_DEC3_ENA_MASK 0x0040 /* ISRC3_DEC3_ENA */ +#define ARIZONA_ISRC3_DEC3_ENA_SHIFT 6 /* ISRC3_DEC3_ENA */ +#define ARIZONA_ISRC3_DEC3_ENA_WIDTH 1 /* ISRC3_DEC3_ENA */ +#define ARIZONA_ISRC3_NOTCH_ENA 0x0001 /* ISRC3_NOTCH_ENA */ +#define ARIZONA_ISRC3_NOTCH_ENA_MASK 0x0001 /* ISRC3_NOTCH_ENA */ +#define ARIZONA_ISRC3_NOTCH_ENA_SHIFT 0 /* ISRC3_NOTCH_ENA */ +#define ARIZONA_ISRC3_NOTCH_ENA_WIDTH 1 /* ISRC3_NOTCH_ENA */ + +/* + * R4352 (0x1100) - DSP1 Control 1 + */ +#define ARIZONA_DSP1_RATE_MASK 0x7800 /* DSP1_RATE - [14:11] */ +#define ARIZONA_DSP1_RATE_SHIFT 11 /* DSP1_RATE - [14:11] */ +#define ARIZONA_DSP1_RATE_WIDTH 4 /* DSP1_RATE - [14:11] */ +#define ARIZONA_DSP1_MEM_ENA 0x0010 /* DSP1_MEM_ENA */ +#define ARIZONA_DSP1_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */ +#define ARIZONA_DSP1_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */ +#define ARIZONA_DSP1_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */ +#define ARIZONA_DSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ +#define ARIZONA_DSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ +#define ARIZONA_DSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ +#define ARIZONA_DSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ +#define ARIZONA_DSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ +#define ARIZONA_DSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ +#define ARIZONA_DSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ +#define ARIZONA_DSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ +#define ARIZONA_DSP1_START 0x0001 /* DSP1_START */ +#define ARIZONA_DSP1_START_MASK 0x0001 /* DSP1_START */ +#define ARIZONA_DSP1_START_SHIFT 0 /* DSP1_START */ +#define ARIZONA_DSP1_START_WIDTH 1 /* DSP1_START */ + +/* + * R4353 (0x1101) - DSP1 Clocking 1 + */ +#define ARIZONA_DSP1_CLK_SEL_MASK 0x0007 /* DSP1_CLK_SEL - [2:0] */ +#define ARIZONA_DSP1_CLK_SEL_SHIFT 0 /* DSP1_CLK_SEL - [2:0] */ +#define ARIZONA_DSP1_CLK_SEL_WIDTH 3 /* DSP1_CLK_SEL - [2:0] */ + +/* + * R4356 (0x1104) - DSP1 Status 1 + */ +#define ARIZONA_DSP1_RAM_RDY 0x0001 /* DSP1_RAM_RDY */ +#define ARIZONA_DSP1_RAM_RDY_MASK 0x0001 /* DSP1_RAM_RDY */ +#define ARIZONA_DSP1_RAM_RDY_SHIFT 0 /* DSP1_RAM_RDY */ +#define ARIZONA_DSP1_RAM_RDY_WIDTH 1 /* DSP1_RAM_RDY */ + +/* + * R4357 (0x1105) - DSP1 Status 2 + */ +#define ARIZONA_DSP1_PING_FULL 0x8000 /* DSP1_PING_FULL */ +#define ARIZONA_DSP1_PING_FULL_MASK 0x8000 /* DSP1_PING_FULL */ +#define ARIZONA_DSP1_PING_FULL_SHIFT 15 /* DSP1_PING_FULL */ +#define ARIZONA_DSP1_PING_FULL_WIDTH 1 /* DSP1_PING_FULL */ +#define ARIZONA_DSP1_PONG_FULL 0x4000 /* DSP1_PONG_FULL */ +#define ARIZONA_DSP1_PONG_FULL_MASK 0x4000 /* DSP1_PONG_FULL */ +#define ARIZONA_DSP1_PONG_FULL_SHIFT 14 /* DSP1_PONG_FULL */ +#define ARIZONA_DSP1_PONG_FULL_WIDTH 1 /* DSP1_PONG_FULL */ +#define ARIZONA_DSP1_WDMA_ACTIVE_CHANNELS_MASK 0x00FF /* DSP1_WDMA_ACTIVE_CHANNELS - [7:0] */ +#define ARIZONA_DSP1_WDMA_ACTIVE_CHANNELS_SHIFT 0 /* DSP1_WDMA_ACTIVE_CHANNELS - [7:0] */ +#define ARIZONA_DSP1_WDMA_ACTIVE_CHANNELS_WIDTH 8 /* DSP1_WDMA_ACTIVE_CHANNELS - [7:0] */ + +#endif -- cgit v1.2.3 From 3cc72986947501a6a8fd12330e0963b59ed2f964 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 19 Jun 2012 16:31:53 +0100 Subject: mfd: arizona: Core driver Several forthcoming Wolfson devices are based on a common platform known as Arizona allowing a great deal of reuse of driver code. This patch adds core support for these devices. In order to handle systems which do not use the generic clock API a simple wrapper for the 32kHz clock domain in the devices is provided. Once the generic clock API is widely available this code will be moved over to use that. For simplicity some WM5102 specific code is included in the core driver, the effort involved in splitting the device out isn't worth it. Signed-off-by: Mark Brown --- drivers/mfd/arizona-core.c | 527 ++++++++++++++++++++++++++++++++++++++ drivers/mfd/arizona.h | 33 +++ include/linux/mfd/arizona/core.h | 102 ++++++++ include/linux/mfd/arizona/pdata.h | 119 +++++++++ 4 files changed, 781 insertions(+) create mode 100644 drivers/mfd/arizona-core.c create mode 100644 drivers/mfd/arizona.h create mode 100644 include/linux/mfd/arizona/core.h create mode 100644 include/linux/mfd/arizona/pdata.h (limited to 'include') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c new file mode 100644 index 000000000000..42cb28b2b5c8 --- /dev/null +++ b/drivers/mfd/arizona-core.c @@ -0,0 +1,527 @@ +/* + * Arizona core driver + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown + * + * 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 "arizona.h" + +static const char *wm5102_core_supplies[] = { + "AVDD", + "DBVDD1", + "DCVDD", +}; + +int arizona_clk32k_enable(struct arizona *arizona) +{ + int ret = 0; + + mutex_lock(&arizona->clk_lock); + + arizona->clk32k_ref++; + + if (arizona->clk32k_ref == 1) + ret = regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1, + ARIZONA_CLK_32K_ENA, + ARIZONA_CLK_32K_ENA); + + if (ret != 0) + arizona->clk32k_ref--; + + mutex_unlock(&arizona->clk_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(arizona_clk32k_enable); + +int arizona_clk32k_disable(struct arizona *arizona) +{ + int ret = 0; + + mutex_lock(&arizona->clk_lock); + + BUG_ON(arizona->clk32k_ref <= 0); + + arizona->clk32k_ref--; + + if (arizona->clk32k_ref == 0) + regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1, + ARIZONA_CLK_32K_ENA, 0); + + mutex_unlock(&arizona->clk_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(arizona_clk32k_disable); + +static irqreturn_t arizona_clkgen_err(int irq, void *data) +{ + struct arizona *arizona = data; + + dev_err(arizona->dev, "CLKGEN error\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t arizona_underclocked(int irq, void *data) +{ + struct arizona *arizona = data; + unsigned int val; + int ret; + + ret = regmap_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_8, + &val); + if (ret != 0) { + dev_err(arizona->dev, "Failed to read underclock status: %d\n", + ret); + return IRQ_NONE; + } + + if (val & ARIZONA_AIF3_UNDERCLOCKED_STS) + dev_err(arizona->dev, "AIF3 underclocked\n"); + if (val & ARIZONA_AIF3_UNDERCLOCKED_STS) + dev_err(arizona->dev, "AIF3 underclocked\n"); + if (val & ARIZONA_AIF2_UNDERCLOCKED_STS) + dev_err(arizona->dev, "AIF1 underclocked\n"); + if (val & ARIZONA_ISRC2_UNDERCLOCKED_STS) + dev_err(arizona->dev, "ISRC2 underclocked\n"); + if (val & ARIZONA_ISRC1_UNDERCLOCKED_STS) + dev_err(arizona->dev, "ISRC1 underclocked\n"); + if (val & ARIZONA_FX_UNDERCLOCKED_STS) + dev_err(arizona->dev, "FX underclocked\n"); + if (val & ARIZONA_ASRC_UNDERCLOCKED_STS) + dev_err(arizona->dev, "ASRC underclocked\n"); + if (val & ARIZONA_DAC_UNDERCLOCKED_STS) + dev_err(arizona->dev, "DAC underclocked\n"); + if (val & ARIZONA_ADC_UNDERCLOCKED_STS) + dev_err(arizona->dev, "ADC underclocked\n"); + if (val & ARIZONA_MIXER_UNDERCLOCKED_STS) + dev_err(arizona->dev, "Mixer underclocked\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t arizona_overclocked(int irq, void *data) +{ + struct arizona *arizona = data; + unsigned int val[2]; + int ret; + + ret = regmap_bulk_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_6, + &val[0], 2); + if (ret != 0) { + dev_err(arizona->dev, "Failed to read overclock status: %d\n", + ret); + return IRQ_NONE; + } + + if (val[0] & ARIZONA_PWM_OVERCLOCKED_STS) + dev_err(arizona->dev, "PWM overclocked\n"); + if (val[0] & ARIZONA_FX_CORE_OVERCLOCKED_STS) + dev_err(arizona->dev, "FX core overclocked\n"); + if (val[0] & ARIZONA_DAC_SYS_OVERCLOCKED_STS) + dev_err(arizona->dev, "DAC SYS overclocked\n"); + if (val[0] & ARIZONA_DAC_WARP_OVERCLOCKED_STS) + dev_err(arizona->dev, "DAC WARP overclocked\n"); + if (val[0] & ARIZONA_ADC_OVERCLOCKED_STS) + dev_err(arizona->dev, "ADC overclocked\n"); + if (val[0] & ARIZONA_MIXER_OVERCLOCKED_STS) + dev_err(arizona->dev, "Mixer overclocked\n"); + if (val[0] & ARIZONA_AIF3_SYNC_OVERCLOCKED_STS) + dev_err(arizona->dev, "AIF3 overclocked\n"); + if (val[0] & ARIZONA_AIF2_SYNC_OVERCLOCKED_STS) + dev_err(arizona->dev, "AIF2 overclocked\n"); + if (val[0] & ARIZONA_AIF1_SYNC_OVERCLOCKED_STS) + dev_err(arizona->dev, "AIF1 overclocked\n"); + if (val[0] & ARIZONA_PAD_CTRL_OVERCLOCKED_STS) + dev_err(arizona->dev, "Pad control overclocked\n"); + + if (val[1] & ARIZONA_SLIMBUS_SUBSYS_OVERCLOCKED_STS) + dev_err(arizona->dev, "Slimbus subsystem overclocked\n"); + if (val[1] & ARIZONA_SLIMBUS_ASYNC_OVERCLOCKED_STS) + dev_err(arizona->dev, "Slimbus async overclocked\n"); + if (val[1] & ARIZONA_SLIMBUS_SYNC_OVERCLOCKED_STS) + dev_err(arizona->dev, "Slimbus sync overclocked\n"); + if (val[1] & ARIZONA_ASRC_ASYNC_SYS_OVERCLOCKED_STS) + dev_err(arizona->dev, "ASRC async system overclocked\n"); + if (val[1] & ARIZONA_ASRC_ASYNC_WARP_OVERCLOCKED_STS) + dev_err(arizona->dev, "ASRC async WARP overclocked\n"); + if (val[1] & ARIZONA_ASRC_SYNC_SYS_OVERCLOCKED_STS) + dev_err(arizona->dev, "ASRC sync system overclocked\n"); + if (val[1] & ARIZONA_ASRC_SYNC_WARP_OVERCLOCKED_STS) + dev_err(arizona->dev, "ASRC sync WARP overclocked\n"); + if (val[1] & ARIZONA_ADSP2_1_OVERCLOCKED_STS) + dev_err(arizona->dev, "DSP1 overclocked\n"); + if (val[1] & ARIZONA_ISRC2_OVERCLOCKED_STS) + dev_err(arizona->dev, "ISRC2 overclocked\n"); + if (val[1] & ARIZONA_ISRC1_OVERCLOCKED_STS) + dev_err(arizona->dev, "ISRC1 overclocked\n"); + + return IRQ_HANDLED; +} + +static int arizona_wait_for_boot(struct arizona *arizona) +{ + unsigned int reg; + int ret, i; + + /* + * We can't use an interrupt as we need to runtime resume to do so, + * we won't race with the interrupt handler as it'll be blocked on + * runtime resume. + */ + for (i = 0; i < 5; i++) { + msleep(1); + + ret = regmap_read(arizona->regmap, + ARIZONA_INTERRUPT_RAW_STATUS_5, ®); + if (ret != 0) { + dev_err(arizona->dev, "Failed to read boot state: %d\n", + ret); + return ret; + } + + if (reg & ARIZONA_BOOT_DONE_STS) + break; + } + + if (reg & ARIZONA_BOOT_DONE_STS) { + regmap_write(arizona->regmap, ARIZONA_INTERRUPT_STATUS_5, + ARIZONA_BOOT_DONE_STS); + } else { + dev_err(arizona->dev, "Device boot timed out: %x\n", reg); + return -ETIMEDOUT; + } + + pm_runtime_mark_last_busy(arizona->dev); + + return 0; +} + +#ifdef CONFIG_PM_RUNTIME +static int arizona_runtime_resume(struct device *dev) +{ + struct arizona *arizona = dev_get_drvdata(dev); + int ret; + + if (arizona->pdata.ldoena) + gpio_set_value_cansleep(arizona->pdata.ldoena, 1); + + regcache_cache_only(arizona->regmap, false); + + ret = arizona_wait_for_boot(arizona); + if (ret != 0) + return ret; + + regcache_sync(arizona->regmap); + + return 0; +} + +static int arizona_runtime_suspend(struct device *dev) +{ + struct arizona *arizona = dev_get_drvdata(dev); + + if (arizona->pdata.ldoena) { + gpio_set_value_cansleep(arizona->pdata.ldoena, 0); + regcache_cache_only(arizona->regmap, true); + regcache_mark_dirty(arizona->regmap); + } + + return 0; +} +#endif + +const struct dev_pm_ops arizona_pm_ops = { + SET_RUNTIME_PM_OPS(arizona_runtime_suspend, + arizona_runtime_resume, + NULL) +}; +EXPORT_SYMBOL_GPL(arizona_pm_ops); + +static struct mfd_cell early_devs[] = { + { .name = "arizona-ldo1" }, +}; + +static struct mfd_cell wm5102_devs[] = { + { .name = "arizona-extcon" }, + { .name = "arizona-gpio" }, + { .name = "arizona-micsupp" }, + { .name = "arizona-pwm" }, + { .name = "wm5102-codec" }, +}; + +int __devinit arizona_dev_init(struct arizona *arizona) +{ + struct device *dev = arizona->dev; + const char *type_name; + unsigned int reg, val; + int ret, i; + + dev_set_drvdata(arizona->dev, arizona); + mutex_init(&arizona->clk_lock); + + if (dev_get_platdata(arizona->dev)) + memcpy(&arizona->pdata, dev_get_platdata(arizona->dev), + sizeof(arizona->pdata)); + + regcache_cache_only(arizona->regmap, true); + + switch (arizona->type) { + case WM5102: + for (i = 0; i < ARRAY_SIZE(wm5102_core_supplies); i++) + arizona->core_supplies[i].supply + = wm5102_core_supplies[i]; + arizona->num_core_supplies = ARRAY_SIZE(wm5102_core_supplies); + break; + default: + dev_err(arizona->dev, "Unknown device type %d\n", + arizona->type); + return -EINVAL; + } + + ret = mfd_add_devices(arizona->dev, -1, early_devs, + ARRAY_SIZE(early_devs), NULL, 0); + if (ret != 0) { + dev_err(dev, "Failed to add early children: %d\n", ret); + return ret; + } + + ret = devm_regulator_bulk_get(dev, arizona->num_core_supplies, + arizona->core_supplies); + if (ret != 0) { + dev_err(dev, "Failed to request core supplies: %d\n", + ret); + goto err_early; + } + + ret = regulator_bulk_enable(arizona->num_core_supplies, + arizona->core_supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable core supplies: %d\n", + ret); + goto err_early; + } + + if (arizona->pdata.reset) { + /* Start out with /RESET low to put the chip into reset */ + ret = gpio_request_one(arizona->pdata.reset, + GPIOF_DIR_OUT | GPIOF_INIT_LOW, + "arizona /RESET"); + if (ret != 0) { + dev_err(dev, "Failed to request /RESET: %d\n", ret); + goto err_enable; + } + + gpio_set_value_cansleep(arizona->pdata.reset, 1); + } + + if (arizona->pdata.ldoena) { + ret = gpio_request_one(arizona->pdata.ldoena, + GPIOF_DIR_OUT | GPIOF_INIT_HIGH, + "arizona LDOENA"); + if (ret != 0) { + dev_err(dev, "Failed to request LDOENA: %d\n", ret); + goto err_reset; + } + } + + regcache_cache_only(arizona->regmap, false); + + ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, ®); + if (ret != 0) { + dev_err(dev, "Failed to read ID register: %d\n", ret); + goto err_ldoena; + } + + ret = regmap_read(arizona->regmap, ARIZONA_DEVICE_REVISION, + &arizona->rev); + if (ret != 0) { + dev_err(dev, "Failed to read revision register: %d\n", ret); + goto err_ldoena; + } + arizona->rev &= ARIZONA_DEVICE_REVISION_MASK; + + switch (reg) { + case 0x5102: + type_name = "WM5102"; + if (arizona->type != WM5102) { + dev_err(arizona->dev, "WM5102 registered as %d\n", + arizona->type); + arizona->type = WM5102; + } + ret = wm5102_patch(arizona); + break; + + default: + dev_err(arizona->dev, "Unknown device ID %x\n", reg); + goto err_ldoena; + } + + dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A'); + + if (ret != 0) + dev_err(arizona->dev, "Failed to apply patch: %d\n", ret); + + /* If we have a /RESET GPIO we'll already be reset */ + if (!arizona->pdata.reset) { + ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0); + if (ret != 0) { + dev_err(dev, "Failed to reset device: %d\n", ret); + goto err_ldoena; + } + } + + arizona_wait_for_boot(arizona); + + for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) { + if (!arizona->pdata.gpio_defaults[i]) + continue; + + regmap_write(arizona->regmap, ARIZONA_GPIO1_CTRL + i, + arizona->pdata.gpio_defaults[i]); + } + + pm_runtime_set_autosuspend_delay(arizona->dev, 100); + pm_runtime_use_autosuspend(arizona->dev); + pm_runtime_enable(arizona->dev); + + /* Chip default */ + if (!arizona->pdata.clk32k_src) + arizona->pdata.clk32k_src = ARIZONA_32KZ_MCLK2; + + switch (arizona->pdata.clk32k_src) { + case ARIZONA_32KZ_MCLK1: + case ARIZONA_32KZ_MCLK2: + regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1, + ARIZONA_CLK_32K_SRC_MASK, + arizona->pdata.clk32k_src - 1); + break; + case ARIZONA_32KZ_NONE: + regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1, + ARIZONA_CLK_32K_SRC_MASK, 2); + break; + default: + dev_err(arizona->dev, "Invalid 32kHz clock source: %d\n", + arizona->pdata.clk32k_src); + ret = -EINVAL; + goto err_ldoena; + } + + for (i = 0; i < ARIZONA_MAX_INPUT; i++) { + /* Default for both is 0 so noop with defaults */ + val = arizona->pdata.dmic_ref[i] + << ARIZONA_IN1_DMIC_SUP_SHIFT; + val |= arizona->pdata.inmode[i] << ARIZONA_IN1_MODE_SHIFT; + + regmap_update_bits(arizona->regmap, + ARIZONA_IN1L_CONTROL + (i * 8), + ARIZONA_IN1_DMIC_SUP_MASK | + ARIZONA_IN1_MODE_MASK, val); + } + + for (i = 0; i < ARIZONA_MAX_OUTPUT; i++) { + /* Default is 0 so noop with defaults */ + if (arizona->pdata.out_mono[i]) + val = ARIZONA_OUT1_MONO; + else + val = 0; + + regmap_update_bits(arizona->regmap, + ARIZONA_OUTPUT_PATH_CONFIG_1L + (i * 8), + ARIZONA_OUT1_MONO, val); + } + + BUILD_BUG_ON(ARIZONA_MAX_PDM_SPK > 1); + for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) { + if (arizona->pdata.spk_mute[i]) + regmap_update_bits(arizona->regmap, + ARIZONA_PDM_SPK1_CTRL_1, + ARIZONA_SPK1_MUTE_ENDIAN_MASK | + ARIZONA_SPK1_MUTE_SEQ1_MASK, + arizona->pdata.spk_mute[i]); + + if (arizona->pdata.spk_fmt[i]) + regmap_update_bits(arizona->regmap, + ARIZONA_PDM_SPK1_CTRL_2, + ARIZONA_SPK1_FMT_MASK, + arizona->pdata.spk_fmt[i]); + } + + /* Set up for interrupts */ + ret = arizona_irq_init(arizona); + if (ret != 0) + goto err_ldoena; + + arizona_request_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, "CLKGEN error", + arizona_clkgen_err, arizona); + arizona_request_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, "Overclocked", + arizona_overclocked, arizona); + arizona_request_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, "Underclocked", + arizona_underclocked, arizona); + + switch (arizona->type) { + case WM5102: + ret = mfd_add_devices(arizona->dev, -1, wm5102_devs, + ARRAY_SIZE(wm5102_devs), NULL, 0); + break; + } + + if (ret != 0) { + dev_err(arizona->dev, "Failed to add subdevices: %d\n", ret); + goto err_irq; + } + + return 0; + +err_irq: + arizona_irq_exit(arizona); +err_ldoena: + if (arizona->pdata.ldoena) { + gpio_set_value_cansleep(arizona->pdata.ldoena, 0); + gpio_free(arizona->pdata.ldoena); + } +err_reset: + if (arizona->pdata.reset) { + gpio_set_value_cansleep(arizona->pdata.reset, 1); + gpio_free(arizona->pdata.reset); + } +err_enable: + regulator_bulk_disable(ARRAY_SIZE(arizona->core_supplies), + arizona->core_supplies); +err_early: + mfd_remove_devices(dev); + return ret; +} +EXPORT_SYMBOL_GPL(arizona_dev_init); + +int __devexit arizona_dev_exit(struct arizona *arizona) +{ + mfd_remove_devices(arizona->dev); + arizona_free_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, arizona); + arizona_free_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, arizona); + arizona_free_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, arizona); + pm_runtime_disable(arizona->dev); + arizona_irq_exit(arizona); + return 0; +} +EXPORT_SYMBOL_GPL(arizona_dev_exit); diff --git a/drivers/mfd/arizona.h b/drivers/mfd/arizona.h new file mode 100644 index 000000000000..1c9f333a9c17 --- /dev/null +++ b/drivers/mfd/arizona.h @@ -0,0 +1,33 @@ +/* + * wm5102.h -- WM5102 MFD internals + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown + * + * 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 _WM5102_H +#define _WM5102_H + +#include +#include + +struct wm_arizona; + +extern const struct regmap_config wm5102_i2c_regmap; +extern const struct regmap_config wm5102_spi_regmap; +extern const struct dev_pm_ops arizona_pm_ops; + +extern const struct regmap_irq_chip wm5102_aod; +extern const struct regmap_irq_chip wm5102_irq; + +int arizona_dev_init(struct arizona *arizona); +int arizona_dev_exit(struct arizona *arizona); +int arizona_irq_init(struct arizona *arizona); +int arizona_irq_exit(struct arizona *arizona); + +#endif diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h new file mode 100644 index 000000000000..0157d845c2ff --- /dev/null +++ b/include/linux/mfd/arizona/core.h @@ -0,0 +1,102 @@ +/* + * Arizona MFD internals + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown + * + * 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 _WM_ARIZONA_CORE_H +#define _WM_ARIZONA_CORE_H + +#include +#include +#include +#include + +#define ARIZONA_MAX_CORE_SUPPLIES 3 + +enum arizona_type { + WM5102 = 1, +}; + +#define ARIZONA_IRQ_GP1 0 +#define ARIZONA_IRQ_GP2 1 +#define ARIZONA_IRQ_GP3 2 +#define ARIZONA_IRQ_GP4 3 +#define ARIZONA_IRQ_GP5_FALL 4 +#define ARIZONA_IRQ_GP5_RISE 5 +#define ARIZONA_IRQ_JD_FALL 6 +#define ARIZONA_IRQ_JD_RISE 7 +#define ARIZONA_IRQ_DSP1_RAM_RDY 8 +#define ARIZONA_IRQ_DSP_IRQ1 9 +#define ARIZONA_IRQ_DSP_IRQ2 10 +#define ARIZONA_IRQ_SPK_SHUTDOWN_WARN 11 +#define ARIZONA_IRQ_SPK_SHUTDOWN 12 +#define ARIZONA_IRQ_MICDET 13 +#define ARIZONA_IRQ_HPDET 14 +#define ARIZONA_IRQ_WSEQ_DONE 15 +#define ARIZONA_IRQ_DRC2_SIG_DET 16 +#define ARIZONA_IRQ_DRC1_SIG_DET 17 +#define ARIZONA_IRQ_ASRC2_LOCK 18 +#define ARIZONA_IRQ_ASRC1_LOCK 19 +#define ARIZONA_IRQ_UNDERCLOCKED 20 +#define ARIZONA_IRQ_OVERCLOCKED 21 +#define ARIZONA_IRQ_FLL2_LOCK 22 +#define ARIZONA_IRQ_FLL1_LOCK 23 +#define ARIZONA_IRQ_CLKGEN_ERR 24 +#define ARIZONA_IRQ_CLKGEN_ERR_ASYNC 25 +#define ARIZONA_IRQ_ASRC_CFG_ERR 26 +#define ARIZONA_IRQ_AIF3_ERR 27 +#define ARIZONA_IRQ_AIF2_ERR 28 +#define ARIZONA_IRQ_AIF1_ERR 29 +#define ARIZONA_IRQ_CTRLIF_ERR 30 +#define ARIZONA_IRQ_MIXER_DROPPED_SAMPLES 31 +#define ARIZONA_IRQ_ASYNC_CLK_ENA_LOW 32 +#define ARIZONA_IRQ_SYSCLK_ENA_LOW 33 +#define ARIZONA_IRQ_ISRC1_CFG_ERR 34 +#define ARIZONA_IRQ_ISRC2_CFG_ERR 35 +#define ARIZONA_IRQ_BOOT_DONE 36 +#define ARIZONA_IRQ_DCS_DAC_DONE 37 +#define ARIZONA_IRQ_DCS_HP_DONE 38 +#define ARIZONA_IRQ_FLL2_CLOCK_OK 39 +#define ARIZONA_IRQ_FLL1_CLOCK_OK 40 + +#define ARIZONA_NUM_IRQ 41 + +struct arizona { + struct regmap *regmap; + struct device *dev; + + enum arizona_type type; + unsigned int rev; + + int num_core_supplies; + struct regulator_bulk_data core_supplies[ARIZONA_MAX_CORE_SUPPLIES]; + + struct arizona_pdata pdata; + + int irq; + struct irq_domain *virq; + struct regmap_irq_chip_data *aod_irq_chip; + struct regmap_irq_chip_data *irq_chip; + + struct mutex clk_lock; + int clk32k_ref; +}; + +int arizona_clk32k_enable(struct arizona *arizona); +int arizona_clk32k_disable(struct arizona *arizona); + +int arizona_request_irq(struct arizona *arizona, int irq, char *name, + irq_handler_t handler, void *data); +void arizona_free_irq(struct arizona *arizona, int irq, void *data); +int arizona_set_irq_wake(struct arizona *arizona, int irq, int on); + +int wm5102_patch(struct arizona *arizona); + +#endif diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h new file mode 100644 index 000000000000..fa2cb9885d62 --- /dev/null +++ b/include/linux/mfd/arizona/pdata.h @@ -0,0 +1,119 @@ +/* + * Platform data for Arizona devices + * + * Copyright 2012 Wolfson Microelectronics. PLC. + * + * 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_PDATA_H +#define _ARIZONA_PDATA_H + +#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_FN - [6:0] */ +#define ARIZONA_GPN_FN_SHIFT 0 /* GPN_FN - [6:0] */ +#define ARIZONA_GPN_FN_WIDTH 7 /* GPN_FN - [6:0] */ + +#define ARIZONA_MAX_GPIO 5 + +#define ARIZONA_32KZ_MCLK1 1 +#define ARIZONA_32KZ_MCLK2 2 +#define ARIZONA_32KZ_NONE 3 + +#define ARIZONA_MAX_INPUT 3 + +#define ARIZONA_DMIC_MICVDD 0 +#define ARIZONA_DMIC_MICBIAS1 1 +#define ARIZONA_DMIC_MICBIAS2 2 +#define ARIZONA_DMIC_MICBIAS3 3 + +#define ARIZONA_INMODE_DIFF 0 +#define ARIZONA_INMODE_SE 1 +#define ARIZONA_INMODE_DMIC 2 + +#define ARIZONA_MAX_OUTPUT 5 + +#define ARIZONA_MAX_PDM_SPK 1 + +struct regulator_init_data; + +struct arizona_micd_config { + unsigned int src; + unsigned int bias; + bool gpio; +}; + +struct arizona_pdata { + int reset; /** GPIO controlling /RESET, if any */ + int ldoena; /** GPIO controlling LODENA, if any */ + + /** Regulator configuration for MICVDD */ + struct regulator_init_data *micvdd; + + /** Regulator configuration for LDO1 */ + struct regulator_init_data *ldo1; + + /** If a direct 32kHz clock is provided on an MCLK specify it here */ + int clk32k_src; + + bool irq_active_high; /** IRQ polarity */ + + /* Base GPIO */ + int gpio_base; + + /** Pin state for GPIO pins */ + int gpio_defaults[ARIZONA_MAX_GPIO]; + + /** GPIO for mic detection polarity */ + int micd_pol_gpio; + + /** Headset polarity configurations */ + struct arizona_micd_config *micd_configs; + int num_micd_configs; + + /** Reference voltage for DMIC inputs */ + int dmic_ref[ARIZONA_MAX_INPUT]; + + /** Mode of input structures */ + int inmode[ARIZONA_MAX_INPUT]; + + /** Mode for outputs */ + bool out_mono[ARIZONA_MAX_OUTPUT]; + + /** PDM speaker mute setting */ + unsigned int spk_mute[ARIZONA_MAX_PDM_SPK]; + + /** PDM speaker format */ + unsigned int spk_fmt[ARIZONA_MAX_PDM_SPK]; +}; + +#endif -- cgit v1.2.3 From ee85f543710dd56ce526cb44e39191f32972e5ad Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Sat, 23 Jun 2012 10:23:48 +0800 Subject: ACPI/PM: specify lowest allowed state for device sleep state Lower device sleep state can save more power, but has more exit latency too. Sometimes, to satisfy some power QoS and other requirement, we need to constrain the lowest device sleep state. In this patch, a parameter to specify lowest allowed state for acpi_pm_device_sleep_state is added. So that the caller can enforce the constraint via the parameter. This is needed by PCIe D3cold support, where the lowest power state allowed may be D3_HOT instead of default D3_COLD. CC: Len Brown CC: linux-acpi@vger.kernel.org Reviewed-by: Rafael J. Wysocki Signed-off-by: Huang Ying Signed-off-by: Bjorn Helgaas --- drivers/acpi/sleep.c | 24 +++++++++++++++++++----- drivers/pci/pci-acpi.c | 3 ++- drivers/pnp/pnpacpi/core.c | 4 ++-- include/acpi/acpi_bus.h | 6 +++--- 4 files changed, 26 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 88561029cca8..1cc02ca2af2a 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -716,8 +716,9 @@ int acpi_suspend(u32 acpi_state) * @dev: device to examine; its driver model wakeup flags control * whether it should be able to wake up the system * @d_min_p: used to store the upper limit of allowed states range - * Return value: preferred power state of the device on success, -ENODEV on - * failure (ie. if there's no 'struct acpi_device' for @dev) + * @d_max_in: specify the lowest allowed states + * Return value: preferred power state of the device on success, -ENODEV + * (ie. if there's no 'struct acpi_device' for @dev) or -EINVAL on failure * * Find the lowest power (highest number) ACPI device power state that * device @dev can be in while the system is in the sleep state represented @@ -732,13 +733,15 @@ int acpi_suspend(u32 acpi_state) * via @wake. */ -int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p) +int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in) { acpi_handle handle = DEVICE_ACPI_HANDLE(dev); struct acpi_device *adev; char acpi_method[] = "_SxD"; unsigned long long d_min, d_max; + if (d_max_in < ACPI_STATE_D0 || d_max_in > ACPI_STATE_D3) + return -EINVAL; if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &adev))) { printk(KERN_DEBUG "ACPI handle has no context!\n"); return -ENODEV; @@ -746,8 +749,10 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p) acpi_method[2] = '0' + acpi_target_sleep_state; /* - * If the sleep state is S0, we will return D3, but if the device has - * _S0W, we will use the value from _S0W + * If the sleep state is S0, the lowest limit from ACPI is D3, + * but if the device has _S0W, we will use the value from _S0W + * as the lowest limit from ACPI. Finally, we will constrain + * the lowest limit with the specified one. */ d_min = ACPI_STATE_D0; d_max = ACPI_STATE_D3; @@ -791,8 +796,17 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p) } } + if (d_max_in < d_min) + return -EINVAL; if (d_min_p) *d_min_p = d_min; + /* constrain d_max with specified lowest limit (max number) */ + if (d_max > d_max_in) { + for (d_max = d_max_in; d_max > d_min; d_max--) { + if (adev->power.states[d_max].flags.valid) + break; + } + } return d_max; } #endif /* CONFIG_PM */ diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 61e2fefeedab..a9efebc586b4 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -189,7 +189,8 @@ static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev) { int acpi_state; - acpi_state = acpi_pm_device_sleep_state(&pdev->dev, NULL); + acpi_state = acpi_pm_device_sleep_state(&pdev->dev, NULL, + ACPI_STATE_D3); if (acpi_state < 0) return PCI_POWER_ERROR; diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c index d21e8f59c84e..507a8e2b9a4c 100644 --- a/drivers/pnp/pnpacpi/core.c +++ b/drivers/pnp/pnpacpi/core.c @@ -170,8 +170,8 @@ 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); - + int power_state = acpi_pm_device_sleep_state(&dev->dev, NULL, + ACPI_STATE_D3); if (power_state < 0) power_state = (state.event == PM_EVENT_ON) ? ACPI_STATE_D0 : ACPI_STATE_D3; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 9e6e1c6eb60a..16bd68504ff7 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -414,13 +414,13 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int state); int acpi_disable_wakeup_device_power(struct acpi_device *dev); #ifdef CONFIG_PM -int acpi_pm_device_sleep_state(struct device *, int *); +int acpi_pm_device_sleep_state(struct device *, int *, int); #else -static inline int acpi_pm_device_sleep_state(struct device *d, int *p) +static inline int acpi_pm_device_sleep_state(struct device *d, int *p, int m) { if (p) *p = ACPI_STATE_D0; - return ACPI_STATE_D3; + return (m >= ACPI_STATE_D0 && m <= ACPI_STATE_D3) ? m : ACPI_STATE_D0; } #endif -- cgit v1.2.3 From 448bd857d48e69b33ef323739dc6d8ca20d4cda7 Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Sat, 23 Jun 2012 10:23:51 +0800 Subject: PCI/PM: add PCIe runtime D3cold support This patch adds runtime D3cold support and corresponding ACPI platform support. This patch only enables runtime D3cold support; it does not enable D3cold support during system suspend/hibernate. D3cold is the deepest power saving state for a PCIe device, where its main power is removed. While it is in D3cold, you can't access the device at all, not even its configuration space (which is still accessible in D3hot). Therefore the PCI PM registers can not be used to transition into/out of the D3cold state; that must be done by platform logic such as ACPI _PR3. To support wakeup from D3cold, a system may provide auxiliary power, which allows a device to request wakeup using a Beacon or the sideband WAKE# signal. WAKE# is usually connected to platform logic such as ACPI GPE. This is quite different from other power saving states, where devices request wakeup via a PME message on the PCIe link. Some devices, such as those in plug-in slots, have no direct platform logic. For example, there is usually no ACPI _PR3 for them. D3cold support for these devices can be done via the PCIe Downstream Port leading to the device. When the PCIe port is powered on/off, the device is powered on/off too. Wakeup events from the device will be notified to the corresponding PCIe port. For more information about PCIe D3cold and corresponding ACPI support, please refer to: - PCI Express Base Specification Revision 2.0 - Advanced Configuration and Power Interface Specification Revision 5.0 [bhelgaas: changelog] Reviewed-by: Rafael J. Wysocki Originally-by: Zheng Yan Signed-off-by: Huang Ying Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-acpi.c | 23 +++++++-- drivers/pci/pci-driver.c | 10 +++- drivers/pci/pci-sysfs.c | 29 +++++++++++ drivers/pci/pci.c | 114 +++++++++++++++++++++++++++++++++++++---- drivers/pci/pci.h | 1 + drivers/pci/pcie/portdrv_pci.c | 44 ++++++++++++++-- include/linux/pci.h | 16 ++++-- 7 files changed, 215 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index a9efebc586b4..e1658afef873 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -48,6 +48,12 @@ static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context) if (event != ACPI_NOTIFY_DEVICE_WAKE || !pci_dev) return; + if (pci_dev->current_state == PCI_D3cold) { + pci_wakeup_event(pci_dev); + pm_runtime_resume(&pci_dev->dev); + return; + } + if (!pci_dev->pm_cap || !pci_dev->pme_support || pci_check_pme_status(pci_dev)) { if (pci_dev->pme_poll) @@ -187,10 +193,13 @@ acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev) static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev) { - int acpi_state; + int acpi_state, d_max; - acpi_state = acpi_pm_device_sleep_state(&pdev->dev, NULL, - ACPI_STATE_D3); + if (pdev->no_d3cold) + d_max = ACPI_STATE_D3_HOT; + else + d_max = ACPI_STATE_D3_COLD; + acpi_state = acpi_pm_device_sleep_state(&pdev->dev, NULL, d_max); if (acpi_state < 0) return PCI_POWER_ERROR; @@ -297,7 +306,13 @@ static void acpi_pci_propagate_run_wake(struct pci_bus *bus, bool enable) static int acpi_pci_run_wake(struct pci_dev *dev, bool enable) { - if (dev->pme_interrupt) + /* + * Per PCI Express Base Specification Revision 2.0 section + * 5.3.3.2 Link Wakeup, platform support is needed for D3cold + * waking up to power on the main link even if there is PME + * support for D3cold + */ + if (dev->pme_interrupt && !dev->runtime_d3cold) return 0; if (!acpi_pm_device_run_wake(&dev->dev, enable)) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index bf0cee629b60..ca2e4c79a588 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -1019,10 +1019,13 @@ static int pci_pm_runtime_suspend(struct device *dev) if (!pm || !pm->runtime_suspend) return -ENOSYS; + pci_dev->no_d3cold = false; error = pm->runtime_suspend(dev); suspend_report_result(pm->runtime_suspend, error); if (error) return error; + if (!pci_dev->d3cold_allowed) + pci_dev->no_d3cold = true; pci_fixup_device(pci_fixup_suspend, pci_dev); @@ -1044,6 +1047,7 @@ static int pci_pm_runtime_suspend(struct device *dev) static int pci_pm_runtime_resume(struct device *dev) { + int rc; struct pci_dev *pci_dev = to_pci_dev(dev); const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; @@ -1054,7 +1058,11 @@ static int pci_pm_runtime_resume(struct device *dev) __pci_enable_wake(pci_dev, PCI_D0, true, false); pci_fixup_device(pci_fixup_resume, pci_dev); - return pm->runtime_resume(dev); + rc = pm->runtime_resume(dev); + + pci_dev->runtime_d3cold = false; + + return rc; } static int pci_pm_runtime_idle(struct device *dev) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 86c63fe45d11..1426db0c0607 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "pci.h" static int sysfs_initialized; /* = 0 */ @@ -378,6 +379,31 @@ dev_bus_rescan_store(struct device *dev, struct device_attribute *attr, #endif +#if defined(CONFIG_PM_RUNTIME) && defined(CONFIG_ACPI) +static ssize_t d3cold_allowed_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pci_dev *pdev = to_pci_dev(dev); + unsigned long val; + + if (strict_strtoul(buf, 0, &val) < 0) + return -EINVAL; + + pdev->d3cold_allowed = !!val; + pm_runtime_resume(dev); + + return count; +} + +static ssize_t d3cold_allowed_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + return sprintf (buf, "%u\n", pdev->d3cold_allowed); +} +#endif + struct device_attribute pci_dev_attrs[] = { __ATTR_RO(resource), __ATTR_RO(vendor), @@ -401,6 +427,9 @@ struct device_attribute pci_dev_attrs[] = { #ifdef CONFIG_HOTPLUG __ATTR(remove, (S_IWUSR|S_IWGRP), NULL, remove_store), __ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, dev_rescan_store), +#endif +#if defined(CONFIG_PM_RUNTIME) && defined(CONFIG_ACPI) + __ATTR(d3cold_allowed, 0644, d3cold_allowed_show, d3cold_allowed_store), #endif __ATTR_NULL, }; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 9eae64b17954..8effb9b23eec 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -622,7 +622,8 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) dev_info(&dev->dev, "Refused to change power state, " "currently in D%d\n", dev->current_state); - /* According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT + /* + * According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT * INTERFACE SPECIFICATION, REV. 1.2", a device transitioning * from D3hot to D0 _may_ perform an internal reset, thereby * going to "D0 Uninitialized" rather than "D0 Initialized". @@ -654,6 +655,16 @@ void pci_update_current_state(struct pci_dev *dev, pci_power_t state) if (dev->pm_cap) { u16 pmcsr; + /* + * Configuration space is not accessible for device in + * D3cold, so just keep or set D3cold for safety + */ + if (dev->current_state == PCI_D3cold) + return; + if (state == PCI_D3cold) { + dev->current_state = PCI_D3cold; + return; + } pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK); } else { @@ -694,8 +705,50 @@ static int pci_platform_power_transition(struct pci_dev *dev, pci_power_t state) */ static void __pci_start_power_transition(struct pci_dev *dev, pci_power_t state) { - if (state == PCI_D0) + if (state == PCI_D0) { pci_platform_power_transition(dev, PCI_D0); + /* + * Mandatory power management transition delays, see + * PCI Express Base Specification Revision 2.0 Section + * 6.6.1: Conventional Reset. Do not delay for + * devices powered on/off by corresponding bridge, + * because have already delayed for the bridge. + */ + if (dev->runtime_d3cold) { + msleep(dev->d3cold_delay); + /* + * When powering on a bridge from D3cold, the + * whole hierarchy may be powered on into + * D0uninitialized state, resume them to give + * them a chance to suspend again + */ + pci_wakeup_bus(dev->subordinate); + } + } +} + +/** + * __pci_dev_set_current_state - Set current state of a PCI device + * @dev: Device to handle + * @data: pointer to state to be set + */ +static int __pci_dev_set_current_state(struct pci_dev *dev, void *data) +{ + pci_power_t state = *(pci_power_t *)data; + + dev->current_state = state; + return 0; +} + +/** + * __pci_bus_set_current_state - Walk given bus and set current state of devices + * @bus: Top bus of the subtree to walk. + * @state: state to be set + */ +static void __pci_bus_set_current_state(struct pci_bus *bus, pci_power_t state) +{ + if (bus) + pci_walk_bus(bus, __pci_dev_set_current_state, &state); } /** @@ -707,8 +760,15 @@ static void __pci_start_power_transition(struct pci_dev *dev, pci_power_t state) */ int __pci_complete_power_transition(struct pci_dev *dev, pci_power_t state) { - return state >= PCI_D0 ? - pci_platform_power_transition(dev, state) : -EINVAL; + int ret; + + if (state < PCI_D0) + return -EINVAL; + ret = pci_platform_power_transition(dev, state); + /* Power off the bridge may power off the whole hierarchy */ + if (!ret && state == PCI_D3cold) + __pci_bus_set_current_state(dev->subordinate, PCI_D3cold); + return ret; } EXPORT_SYMBOL_GPL(__pci_complete_power_transition); @@ -732,8 +792,8 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) int error; /* bound the state we're entering */ - if (state > PCI_D3hot) - state = PCI_D3hot; + if (state > PCI_D3cold) + state = PCI_D3cold; else if (state < PCI_D0) state = PCI_D0; else if ((state == PCI_D1 || state == PCI_D2) && pci_no_d1d2(dev)) @@ -748,10 +808,15 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) /* This device is quirked not to be put into D3, so don't put it in D3 */ - if (state == PCI_D3hot && (dev->dev_flags & PCI_DEV_FLAGS_NO_D3)) + if (state >= PCI_D3hot && (dev->dev_flags & PCI_DEV_FLAGS_NO_D3)) return 0; - error = pci_raw_set_power_state(dev, state); + /* + * To put device in D3cold, we put device into D3hot in native + * way, then put device into D3cold with platform ops + */ + error = pci_raw_set_power_state(dev, state > PCI_D3hot ? + PCI_D3hot : state); if (!__pci_complete_power_transition(dev, state)) error = 0; @@ -1497,6 +1562,28 @@ void pci_pme_wakeup_bus(struct pci_bus *bus) pci_walk_bus(bus, pci_pme_wakeup, (void *)true); } +/** + * pci_wakeup - Wake up a PCI device + * @dev: Device to handle. + * @ign: ignored parameter + */ +static int pci_wakeup(struct pci_dev *pci_dev, void *ign) +{ + pci_wakeup_event(pci_dev); + pm_request_resume(&pci_dev->dev); + return 0; +} + +/** + * pci_wakeup_bus - Walk given bus and wake up devices on it + * @bus: Top bus of the subtree to walk. + */ +void pci_wakeup_bus(struct pci_bus *bus) +{ + if (bus) + pci_walk_bus(bus, pci_wakeup, NULL); +} + /** * pci_pme_capable - check the capability of PCI device to generate PME# * @dev: PCI device to handle. @@ -1754,6 +1841,10 @@ int pci_prepare_to_sleep(struct pci_dev *dev) if (target_state == PCI_POWER_ERROR) return -EIO; + /* D3cold during system suspend/hibernate is not supported */ + if (target_state > PCI_D3hot) + target_state = PCI_D3hot; + pci_enable_wake(dev, target_state, device_may_wakeup(&dev->dev)); error = pci_set_power_state(dev, target_state); @@ -1791,12 +1882,16 @@ int pci_finish_runtime_suspend(struct pci_dev *dev) if (target_state == PCI_POWER_ERROR) return -EIO; + dev->runtime_d3cold = target_state == PCI_D3cold; + __pci_enable_wake(dev, target_state, true, pci_dev_run_wake(dev)); error = pci_set_power_state(dev, target_state); - if (error) + if (error) { __pci_enable_wake(dev, target_state, true, false); + dev->runtime_d3cold = false; + } return error; } @@ -1866,6 +1961,7 @@ void pci_pm_init(struct pci_dev *dev) dev->pm_cap = pm; dev->d3_delay = PCI_PM_D3_WAIT; + dev->d3cold_delay = PCI_PM_D3COLD_WAIT; dev->d1_support = false; dev->d2_support = false; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index e4943479b234..5cd3dce7a245 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -70,6 +70,7 @@ extern void pci_update_current_state(struct pci_dev *dev, pci_power_t state); extern void pci_disable_enabled_device(struct pci_dev *dev); extern int pci_finish_runtime_suspend(struct pci_dev *dev); extern int __pci_pme_wakeup(struct pci_dev *dev, void *ign); +extern void pci_wakeup_bus(struct pci_bus *bus); extern void pci_pm_init(struct pci_dev *dev); extern void platform_pci_wakeup_init(struct pci_dev *dev); extern void pci_allocate_cap_save_buffers(struct pci_dev *dev); diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 7c576b9aa01d..3a7eefcb270a 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -101,12 +101,48 @@ static int pcie_port_resume_noirq(struct device *dev) } #ifdef CONFIG_PM_RUNTIME -static int pcie_port_runtime_pm(struct device *dev) +struct d3cold_info { + bool no_d3cold; + unsigned int d3cold_delay; +}; + +static int pci_dev_d3cold_info(struct pci_dev *pdev, void *data) +{ + struct d3cold_info *info = data; + + info->d3cold_delay = max_t(unsigned int, pdev->d3cold_delay, + info->d3cold_delay); + if (pdev->no_d3cold) + info->no_d3cold = true; + return 0; +} + +static int pcie_port_runtime_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct d3cold_info d3cold_info = { + .no_d3cold = false, + .d3cold_delay = PCI_PM_D3_WAIT, + }; + + /* + * If any subordinate device disable D3cold, we should not put + * the port into D3cold. The D3cold delay of port should be + * the max of that of all subordinate devices. + */ + pci_walk_bus(pdev->subordinate, pci_dev_d3cold_info, &d3cold_info); + pdev->no_d3cold = d3cold_info.no_d3cold; + pdev->d3cold_delay = d3cold_info.d3cold_delay; + return 0; +} + +static int pcie_port_runtime_resume(struct device *dev) { return 0; } #else -#define pcie_port_runtime_pm NULL +#define pcie_port_runtime_suspend NULL +#define pcie_port_runtime_resume NULL #endif static const struct dev_pm_ops pcie_portdrv_pm_ops = { @@ -117,8 +153,8 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = { .poweroff = pcie_port_device_suspend, .restore = pcie_port_device_resume, .resume_noirq = pcie_port_resume_noirq, - .runtime_suspend = pcie_port_runtime_pm, - .runtime_resume = pcie_port_runtime_pm, + .runtime_suspend = pcie_port_runtime_suspend, + .runtime_resume = pcie_port_runtime_resume, }; #define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops) diff --git a/include/linux/pci.h b/include/linux/pci.h index d8c379dba6ad..002cfd3e33ca 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -132,9 +132,10 @@ static inline const char *pci_power_name(pci_power_t state) return pci_power_names[1 + (int) state]; } -#define PCI_PM_D2_DELAY 200 -#define PCI_PM_D3_WAIT 10 -#define PCI_PM_BUS_WAIT 50 +#define PCI_PM_D2_DELAY 200 +#define PCI_PM_D3_WAIT 10 +#define PCI_PM_D3COLD_WAIT 100 +#define PCI_PM_BUS_WAIT 50 /** The pci_channel state describes connectivity between the CPU and * the pci device. If some PCI bus between here and the pci device @@ -278,11 +279,18 @@ struct pci_dev { unsigned int pme_poll:1; /* Poll device's PME status bit */ unsigned int d1_support:1; /* Low power state D1 is supported */ unsigned int d2_support:1; /* Low power state D2 is supported */ - unsigned int no_d1d2:1; /* Only allow D0 and D3 */ + unsigned int no_d1d2:1; /* D1 and D2 are forbidden */ + unsigned int no_d3cold:1; /* D3cold is forbidden */ + unsigned int d3cold_allowed:1; /* D3cold is allowed by user */ unsigned int mmio_always_on:1; /* disallow turning off io/mem decoding during bar sizing */ unsigned int wakeup_prepared:1; + unsigned int runtime_d3cold:1; /* whether go through runtime + D3cold, not set for devices + powered on/off by the + corresponding bridge */ unsigned int d3_delay; /* D3->D0 transition time in ms */ + unsigned int d3cold_delay; /* D3cold->D0 transition time in ms */ #ifdef CONFIG_PCIEASPM struct pcie_link_state *link_state; /* ASPM link state. */ -- cgit v1.2.3 From a91a5ac6858fbf7477131e1210cb3e897b668e6f Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 4 Jun 2012 20:40:53 -0700 Subject: mempool: add @gfp_mask to mempool_create_node() mempool_create_node() currently assumes %GFP_KERNEL. Its only user, blk_init_free_list(), is about to be updated to use other allocation flags - add @gfp_mask argument to the function. Signed-off-by: Tejun Heo Cc: Andrew Morton Cc: Hugh Dickins Signed-off-by: Jens Axboe --- block/blk-core.c | 4 ++-- include/linux/mempool.h | 3 ++- mm/mempool.c | 12 +++++++----- 3 files changed, 11 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index 93eb3e4f88ce..64f9a8668253 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -531,8 +531,8 @@ static int blk_init_free_list(struct request_queue *q) init_waitqueue_head(&rl->wait[BLK_RW_ASYNC]); rl->rq_pool = mempool_create_node(BLKDEV_MIN_RQ, mempool_alloc_slab, - mempool_free_slab, request_cachep, q->node); - + mempool_free_slab, request_cachep, + GFP_KERNEL, q->node); if (!rl->rq_pool) return -ENOMEM; diff --git a/include/linux/mempool.h b/include/linux/mempool.h index 7c08052e3321..39ed62ab5b8a 100644 --- a/include/linux/mempool.h +++ b/include/linux/mempool.h @@ -26,7 +26,8 @@ typedef struct mempool_s { extern mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn, mempool_free_t *free_fn, void *pool_data); extern mempool_t *mempool_create_node(int min_nr, mempool_alloc_t *alloc_fn, - mempool_free_t *free_fn, void *pool_data, int nid); + mempool_free_t *free_fn, void *pool_data, + gfp_t gfp_mask, int nid); extern int mempool_resize(mempool_t *pool, int new_min_nr, gfp_t gfp_mask); extern void mempool_destroy(mempool_t *pool); diff --git a/mm/mempool.c b/mm/mempool.c index d9049811f352..54990476c049 100644 --- a/mm/mempool.c +++ b/mm/mempool.c @@ -63,19 +63,21 @@ EXPORT_SYMBOL(mempool_destroy); mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn, mempool_free_t *free_fn, void *pool_data) { - return mempool_create_node(min_nr,alloc_fn,free_fn, pool_data,-1); + return mempool_create_node(min_nr,alloc_fn,free_fn, pool_data, + GFP_KERNEL, NUMA_NO_NODE); } EXPORT_SYMBOL(mempool_create); mempool_t *mempool_create_node(int min_nr, mempool_alloc_t *alloc_fn, - mempool_free_t *free_fn, void *pool_data, int node_id) + mempool_free_t *free_fn, void *pool_data, + gfp_t gfp_mask, int node_id) { mempool_t *pool; - pool = kmalloc_node(sizeof(*pool), GFP_KERNEL | __GFP_ZERO, node_id); + pool = kmalloc_node(sizeof(*pool), gfp_mask | __GFP_ZERO, node_id); if (!pool) return NULL; pool->elements = kmalloc_node(min_nr * sizeof(void *), - GFP_KERNEL, node_id); + gfp_mask, node_id); if (!pool->elements) { kfree(pool); return NULL; @@ -93,7 +95,7 @@ mempool_t *mempool_create_node(int min_nr, mempool_alloc_t *alloc_fn, while (pool->curr_nr < pool->min_nr) { void *element; - element = pool->alloc(GFP_KERNEL, pool->pool_data); + element = pool->alloc(gfp_mask, pool->pool_data); if (unlikely(!element)) { mempool_destroy(pool); return NULL; -- cgit v1.2.3 From 86072d8112595ea1b6beeb33f578e7c2839e014e Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 4 Jun 2012 20:40:54 -0700 Subject: block: drop custom queue draining used by scsi_transport_{iscsi|fc} iscsi_remove_host() uses bsg_remove_queue() which implements custom queue draining. fc_bsg_remove() open-codes mostly identical logic. The draining logic isn't correct in that blk_stop_queue() doesn't prevent new requests from being queued - it just stops processing, so nothing prevents new requests to be queued after the logic determines that the queue is drained. blk_cleanup_queue() now implements proper queue draining and these custom draining logics aren't necessary. Drop them and use bsg_unregister_queue() + blk_cleanup_queue() instead. Signed-off-by: Tejun Heo Reviewed-by: Mike Christie Acked-by: Vivek Goyal Cc: James Bottomley Cc: James Smart Signed-off-by: Jens Axboe --- block/bsg-lib.c | 53 ------------------------------------- drivers/scsi/scsi_transport_fc.c | 38 -------------------------- drivers/scsi/scsi_transport_iscsi.c | 2 +- include/linux/bsg-lib.h | 1 - 4 files changed, 1 insertion(+), 93 deletions(-) (limited to 'include') diff --git a/block/bsg-lib.c b/block/bsg-lib.c index 7ad49c88f6b1..deee61fbb741 100644 --- a/block/bsg-lib.c +++ b/block/bsg-lib.c @@ -243,56 +243,3 @@ int bsg_setup_queue(struct device *dev, struct request_queue *q, return 0; } EXPORT_SYMBOL_GPL(bsg_setup_queue); - -/** - * bsg_remove_queue - Deletes the bsg dev from the q - * @q: the request_queue that is to be torn down. - * - * Notes: - * Before unregistering the queue empty any requests that are blocked - */ -void bsg_remove_queue(struct request_queue *q) -{ - struct request *req; /* block request */ - int counts; /* totals for request_list count and starved */ - - if (!q) - return; - - /* Stop taking in new requests */ - spin_lock_irq(q->queue_lock); - blk_stop_queue(q); - - /* drain all requests in the queue */ - while (1) { - /* need the lock to fetch a request - * this may fetch the same reqeust as the previous pass - */ - req = blk_fetch_request(q); - /* save requests in use and starved */ - counts = q->rq.count[0] + q->rq.count[1] + - q->rq.starved[0] + q->rq.starved[1]; - spin_unlock_irq(q->queue_lock); - /* any requests still outstanding? */ - if (counts == 0) - break; - - /* This may be the same req as the previous iteration, - * always send the blk_end_request_all after a prefetch. - * It is not okay to not end the request because the - * prefetch started the request. - */ - if (req) { - /* return -ENXIO to indicate that this queue is - * going away - */ - req->errors = -ENXIO; - blk_end_request_all(req, -ENXIO); - } - - msleep(200); /* allow bsg to possibly finish */ - spin_lock_irq(q->queue_lock); - } - bsg_unregister_queue(q); -} -EXPORT_SYMBOL_GPL(bsg_remove_queue); diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 579760420d53..a9617ad05f33 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -4130,45 +4130,7 @@ fc_bsg_rportadd(struct Scsi_Host *shost, struct fc_rport *rport) static void fc_bsg_remove(struct request_queue *q) { - struct request *req; /* block request */ - int counts; /* totals for request_list count and starved */ - if (q) { - /* Stop taking in new requests */ - spin_lock_irq(q->queue_lock); - blk_stop_queue(q); - - /* drain all requests in the queue */ - while (1) { - /* need the lock to fetch a request - * this may fetch the same reqeust as the previous pass - */ - req = blk_fetch_request(q); - /* save requests in use and starved */ - counts = q->rq.count[0] + q->rq.count[1] + - q->rq.starved[0] + q->rq.starved[1]; - spin_unlock_irq(q->queue_lock); - /* any requests still outstanding? */ - if (counts == 0) - break; - - /* This may be the same req as the previous iteration, - * always send the blk_end_request_all after a prefetch. - * It is not okay to not end the request because the - * prefetch started the request. - */ - if (req) { - /* return -ENXIO to indicate that this queue is - * going away - */ - req->errors = -ENXIO; - blk_end_request_all(req, -ENXIO); - } - - msleep(200); /* allow bsg to possibly finish */ - spin_lock_irq(q->queue_lock); - } - bsg_unregister_queue(q); blk_cleanup_queue(q); } diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 1cf640e575da..c737a16b0a1d 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -575,7 +575,7 @@ static int iscsi_remove_host(struct transport_container *tc, struct iscsi_cls_host *ihost = shost->shost_data; if (ihost->bsg_q) { - bsg_remove_queue(ihost->bsg_q); + bsg_unregister_queue(ihost->bsg_q); blk_cleanup_queue(ihost->bsg_q); } return 0; diff --git a/include/linux/bsg-lib.h b/include/linux/bsg-lib.h index f55ab8cdc106..4d0fb3df2f4a 100644 --- a/include/linux/bsg-lib.h +++ b/include/linux/bsg-lib.h @@ -67,7 +67,6 @@ void bsg_job_done(struct bsg_job *job, int result, int bsg_setup_queue(struct device *dev, struct request_queue *q, char *name, bsg_job_fn *job_fn, int dd_job_size); void bsg_request_fn(struct request_queue *q); -void bsg_remove_queue(struct request_queue *q); void bsg_goose_queue(struct request_queue *q); #endif -- cgit v1.2.3 From 8a5ecdd42862bf87ceab00bf2a15d7eabf58c02d Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 4 Jun 2012 20:40:58 -0700 Subject: block: add q->nr_rqs[] and move q->rq.elvpriv to q->nr_rqs_elvpriv Add q->nr_rqs[] which currently behaves the same as q->rq.count[] and move q->rq.elvpriv to q->nr_rqs_elvpriv. blk_drain_queue() is updated to use q->nr_rqs[] instead of q->rq.count[]. These counters separates queue-wide request statistics from the request list and allow implementation of per-queue request allocation. While at it, properly indent fields of struct request_list. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal Signed-off-by: Jens Axboe --- block/blk-core.c | 13 +++++++------ include/linux/blkdev.h | 11 ++++++----- 2 files changed, 13 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index 71894e143b91..a2648153691f 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -387,7 +387,7 @@ void blk_drain_queue(struct request_queue *q, bool drain_all) if (!list_empty(&q->queue_head) && q->request_fn) __blk_run_queue(q); - drain |= q->rq.elvpriv; + drain |= q->nr_rqs_elvpriv; /* * Unfortunately, requests are queued at and tracked from @@ -397,7 +397,7 @@ void blk_drain_queue(struct request_queue *q, bool drain_all) if (drain_all) { drain |= !list_empty(&q->queue_head); for (i = 0; i < 2; i++) { - drain |= q->rq.count[i]; + drain |= q->nr_rqs[i]; drain |= q->in_flight[i]; drain |= !list_empty(&q->flush_queue[i]); } @@ -526,7 +526,6 @@ static int blk_init_free_list(struct request_queue *q) rl->count[BLK_RW_SYNC] = rl->count[BLK_RW_ASYNC] = 0; rl->starved[BLK_RW_SYNC] = rl->starved[BLK_RW_ASYNC] = 0; - rl->elvpriv = 0; init_waitqueue_head(&rl->wait[BLK_RW_SYNC]); init_waitqueue_head(&rl->wait[BLK_RW_ASYNC]); @@ -791,9 +790,10 @@ static void freed_request(struct request_queue *q, unsigned int flags) struct request_list *rl = &q->rq; int sync = rw_is_sync(flags); + q->nr_rqs[sync]--; rl->count[sync]--; if (flags & REQ_ELVPRIV) - rl->elvpriv--; + q->nr_rqs_elvpriv--; __freed_request(q, sync); @@ -902,6 +902,7 @@ static struct request *__get_request(struct request_queue *q, int rw_flags, if (rl->count[is_sync] >= (3 * q->nr_requests / 2)) return NULL; + q->nr_rqs[is_sync]++; rl->count[is_sync]++; rl->starved[is_sync] = 0; @@ -917,7 +918,7 @@ static struct request *__get_request(struct request_queue *q, int rw_flags, */ if (blk_rq_should_init_elevator(bio) && !blk_queue_bypass(q)) { rw_flags |= REQ_ELVPRIV; - rl->elvpriv++; + q->nr_rqs_elvpriv++; if (et->icq_cache && ioc) icq = ioc_lookup_icq(ioc, q); } @@ -978,7 +979,7 @@ fail_elvpriv: rq->elv.icq = NULL; spin_lock_irq(q->queue_lock); - rl->elvpriv--; + q->nr_rqs_elvpriv--; spin_unlock_irq(q->queue_lock); goto out; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 07954b05b86c..7e44ed93f84b 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -51,11 +51,10 @@ struct request_list { * count[], starved[], and wait[] are indexed by * BLK_RW_SYNC/BLK_RW_ASYNC */ - int count[2]; - int starved[2]; - int elvpriv; - mempool_t *rq_pool; - wait_queue_head_t wait[2]; + int count[2]; + int starved[2]; + mempool_t *rq_pool; + wait_queue_head_t wait[2]; }; /* @@ -282,6 +281,8 @@ struct request_queue { struct list_head queue_head; struct request *last_merge; struct elevator_queue *elevator; + int nr_rqs[2]; /* # allocated [a]sync rqs */ + int nr_rqs_elvpriv; /* # allocated rqs w/ elvpriv */ /* * the queue request freelist, one for reads and one for writes -- cgit v1.2.3 From 5b788ce3e2acac9bf109743b1281d77347cf2101 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 4 Jun 2012 20:40:59 -0700 Subject: block: prepare for multiple request_lists Request allocation is about to be made per-blkg meaning that there'll be multiple request lists. * Make queue full state per request_list. blk_*queue_full() functions are renamed to blk_*rl_full() and takes @rl instead of @q. * Rename blk_init_free_list() to blk_init_rl() and make it take @rl instead of @q. Also add @gfp_mask parameter. * Add blk_exit_rl() instead of destroying rl directly from blk_release_queue(). * Add request_list->q and make request alloc/free functions - blk_free_request(), [__]freed_request(), __get_request() - take @rl instead of @q. This patch doesn't introduce any functional difference. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal Signed-off-by: Jens Axboe --- block/blk-core.c | 56 ++++++++++++++++++++++++++++---------------------- block/blk-sysfs.c | 12 +++++------ block/blk.h | 3 +++ include/linux/blkdev.h | 32 ++++++++++++++++------------- 4 files changed, 57 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index a2648153691f..f392a2edf462 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -517,13 +517,13 @@ void blk_cleanup_queue(struct request_queue *q) } EXPORT_SYMBOL(blk_cleanup_queue); -static int blk_init_free_list(struct request_queue *q) +int blk_init_rl(struct request_list *rl, struct request_queue *q, + gfp_t gfp_mask) { - struct request_list *rl = &q->rq; - if (unlikely(rl->rq_pool)) return 0; + rl->q = q; rl->count[BLK_RW_SYNC] = rl->count[BLK_RW_ASYNC] = 0; rl->starved[BLK_RW_SYNC] = rl->starved[BLK_RW_ASYNC] = 0; init_waitqueue_head(&rl->wait[BLK_RW_SYNC]); @@ -531,13 +531,19 @@ static int blk_init_free_list(struct request_queue *q) rl->rq_pool = mempool_create_node(BLKDEV_MIN_RQ, mempool_alloc_slab, mempool_free_slab, request_cachep, - GFP_KERNEL, q->node); + gfp_mask, q->node); if (!rl->rq_pool) return -ENOMEM; return 0; } +void blk_exit_rl(struct request_list *rl) +{ + if (rl->rq_pool) + mempool_destroy(rl->rq_pool); +} + struct request_queue *blk_alloc_queue(gfp_t gfp_mask) { return blk_alloc_queue_node(gfp_mask, -1); @@ -679,7 +685,7 @@ blk_init_allocated_queue(struct request_queue *q, request_fn_proc *rfn, if (!q) return NULL; - if (blk_init_free_list(q)) + if (blk_init_rl(&q->rq, q, GFP_KERNEL)) return NULL; q->request_fn = rfn; @@ -721,15 +727,15 @@ bool blk_get_queue(struct request_queue *q) } EXPORT_SYMBOL(blk_get_queue); -static inline void blk_free_request(struct request_queue *q, struct request *rq) +static inline void blk_free_request(struct request_list *rl, struct request *rq) { if (rq->cmd_flags & REQ_ELVPRIV) { - elv_put_request(q, rq); + elv_put_request(rl->q, rq); if (rq->elv.icq) put_io_context(rq->elv.icq->ioc); } - mempool_free(rq, q->rq.rq_pool); + mempool_free(rq, rl->rq_pool); } /* @@ -766,9 +772,9 @@ static void ioc_set_batching(struct request_queue *q, struct io_context *ioc) ioc->last_waited = jiffies; } -static void __freed_request(struct request_queue *q, int sync) +static void __freed_request(struct request_list *rl, int sync) { - struct request_list *rl = &q->rq; + struct request_queue *q = rl->q; if (rl->count[sync] < queue_congestion_off_threshold(q)) blk_clear_queue_congested(q, sync); @@ -777,7 +783,7 @@ static void __freed_request(struct request_queue *q, int sync) if (waitqueue_active(&rl->wait[sync])) wake_up(&rl->wait[sync]); - blk_clear_queue_full(q, sync); + blk_clear_rl_full(rl, sync); } } @@ -785,9 +791,9 @@ static void __freed_request(struct request_queue *q, int sync) * A request has just been released. Account for it, update the full and * congestion status, wake up any waiters. Called under q->queue_lock. */ -static void freed_request(struct request_queue *q, unsigned int flags) +static void freed_request(struct request_list *rl, unsigned int flags) { - struct request_list *rl = &q->rq; + struct request_queue *q = rl->q; int sync = rw_is_sync(flags); q->nr_rqs[sync]--; @@ -795,10 +801,10 @@ static void freed_request(struct request_queue *q, unsigned int flags) if (flags & REQ_ELVPRIV) q->nr_rqs_elvpriv--; - __freed_request(q, sync); + __freed_request(rl, sync); if (unlikely(rl->starved[sync ^ 1])) - __freed_request(q, sync ^ 1); + __freed_request(rl, sync ^ 1); } /* @@ -838,7 +844,7 @@ static struct io_context *rq_ioc(struct bio *bio) /** * __get_request - get a free request - * @q: request_queue to allocate request from + * @rl: request list to allocate from * @rw_flags: RW and SYNC flags * @bio: bio to allocate request for (can be %NULL) * @gfp_mask: allocation mask @@ -850,11 +856,11 @@ static struct io_context *rq_ioc(struct bio *bio) * Returns %NULL on failure, with @q->queue_lock held. * Returns !%NULL on success, with @q->queue_lock *not held*. */ -static struct request *__get_request(struct request_queue *q, int rw_flags, +static struct request *__get_request(struct request_list *rl, int rw_flags, struct bio *bio, gfp_t gfp_mask) { + struct request_queue *q = rl->q; struct request *rq; - struct request_list *rl = &q->rq; struct elevator_type *et = q->elevator->type; struct io_context *ioc = rq_ioc(bio); struct io_cq *icq = NULL; @@ -876,9 +882,9 @@ static struct request *__get_request(struct request_queue *q, int rw_flags, * This process will be allowed to complete a batch of * requests, others will be blocked. */ - if (!blk_queue_full(q, is_sync)) { + if (!blk_rl_full(rl, is_sync)) { ioc_set_batching(q, ioc); - blk_set_queue_full(q, is_sync); + blk_set_rl_full(rl, is_sync); } else { if (may_queue != ELV_MQUEUE_MUST && !ioc_batching(q, ioc)) { @@ -928,7 +934,7 @@ static struct request *__get_request(struct request_queue *q, int rw_flags, spin_unlock_irq(q->queue_lock); /* allocate and init request */ - rq = mempool_alloc(q->rq.rq_pool, gfp_mask); + rq = mempool_alloc(rl->rq_pool, gfp_mask); if (!rq) goto fail_alloc; @@ -992,7 +998,7 @@ fail_alloc: * queue, but this is pretty rare. */ spin_lock_irq(q->queue_lock); - freed_request(q, rw_flags); + freed_request(rl, rw_flags); /* * in the very unlikely event that allocation failed and no @@ -1029,7 +1035,7 @@ static struct request *get_request(struct request_queue *q, int rw_flags, struct request_list *rl = &q->rq; struct request *rq; retry: - rq = __get_request(q, rw_flags, bio, gfp_mask); + rq = __get_request(&q->rq, rw_flags, bio, gfp_mask); if (rq) return rq; @@ -1229,8 +1235,8 @@ void __blk_put_request(struct request_queue *q, struct request *req) BUG_ON(!list_empty(&req->queuelist)); BUG_ON(!hlist_unhashed(&req->hash)); - blk_free_request(q, req); - freed_request(q, flags); + blk_free_request(&q->rq, req); + freed_request(&q->rq, flags); } } EXPORT_SYMBOL_GPL(__blk_put_request); diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index aa41b47c22d2..234ce7c082fa 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -66,16 +66,16 @@ queue_requests_store(struct request_queue *q, const char *page, size_t count) blk_clear_queue_congested(q, BLK_RW_ASYNC); if (rl->count[BLK_RW_SYNC] >= q->nr_requests) { - blk_set_queue_full(q, BLK_RW_SYNC); + blk_set_rl_full(rl, BLK_RW_SYNC); } else { - blk_clear_queue_full(q, BLK_RW_SYNC); + blk_clear_rl_full(rl, BLK_RW_SYNC); wake_up(&rl->wait[BLK_RW_SYNC]); } if (rl->count[BLK_RW_ASYNC] >= q->nr_requests) { - blk_set_queue_full(q, BLK_RW_ASYNC); + blk_set_rl_full(rl, BLK_RW_ASYNC); } else { - blk_clear_queue_full(q, BLK_RW_ASYNC); + blk_clear_rl_full(rl, BLK_RW_ASYNC); wake_up(&rl->wait[BLK_RW_ASYNC]); } spin_unlock_irq(q->queue_lock); @@ -476,7 +476,6 @@ static void blk_release_queue(struct kobject *kobj) { struct request_queue *q = container_of(kobj, struct request_queue, kobj); - struct request_list *rl = &q->rq; blk_sync_queue(q); @@ -489,8 +488,7 @@ static void blk_release_queue(struct kobject *kobj) elevator_exit(q->elevator); } - if (rl->rq_pool) - mempool_destroy(rl->rq_pool); + blk_exit_rl(&q->rq); if (q->queue_tags) __blk_queue_free_tags(q); diff --git a/block/blk.h b/block/blk.h index 85f6ae42f7d3..a134231fd22a 100644 --- a/block/blk.h +++ b/block/blk.h @@ -18,6 +18,9 @@ static inline void __blk_get_queue(struct request_queue *q) kobject_get(&q->kobj); } +int blk_init_rl(struct request_list *rl, struct request_queue *q, + gfp_t gfp_mask); +void blk_exit_rl(struct request_list *rl); void init_request_from_bio(struct request *req, struct bio *bio); void blk_rq_bio_prep(struct request_queue *q, struct request *rq, struct bio *bio); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 7e44ed93f84b..f2385ee7c7b2 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -46,7 +46,12 @@ struct blkcg_gq; struct request; typedef void (rq_end_io_fn)(struct request *, int); +#define BLK_RL_SYNCFULL (1U << 0) +#define BLK_RL_ASYNCFULL (1U << 1) + struct request_list { + struct request_queue *q; /* the queue this rl belongs to */ + /* * count[], starved[], and wait[] are indexed by * BLK_RW_SYNC/BLK_RW_ASYNC @@ -55,6 +60,7 @@ struct request_list { int starved[2]; mempool_t *rq_pool; wait_queue_head_t wait[2]; + unsigned int flags; }; /* @@ -562,27 +568,25 @@ static inline bool rq_is_sync(struct request *rq) return rw_is_sync(rq->cmd_flags); } -static inline int blk_queue_full(struct request_queue *q, int sync) +static inline bool blk_rl_full(struct request_list *rl, bool sync) { - if (sync) - return test_bit(QUEUE_FLAG_SYNCFULL, &q->queue_flags); - return test_bit(QUEUE_FLAG_ASYNCFULL, &q->queue_flags); + unsigned int flag = sync ? BLK_RL_SYNCFULL : BLK_RL_ASYNCFULL; + + return rl->flags & flag; } -static inline void blk_set_queue_full(struct request_queue *q, int sync) +static inline void blk_set_rl_full(struct request_list *rl, bool sync) { - if (sync) - queue_flag_set(QUEUE_FLAG_SYNCFULL, q); - else - queue_flag_set(QUEUE_FLAG_ASYNCFULL, q); + unsigned int flag = sync ? BLK_RL_SYNCFULL : BLK_RL_ASYNCFULL; + + rl->flags |= flag; } -static inline void blk_clear_queue_full(struct request_queue *q, int sync) +static inline void blk_clear_rl_full(struct request_list *rl, bool sync) { - if (sync) - queue_flag_clear(QUEUE_FLAG_SYNCFULL, q); - else - queue_flag_clear(QUEUE_FLAG_ASYNCFULL, q); + unsigned int flag = sync ? BLK_RL_SYNCFULL : BLK_RL_ASYNCFULL; + + rl->flags &= ~flag; } -- cgit v1.2.3 From 721002ec1dd55a52425455826af49cf8853b2d4f Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 22 Jun 2012 17:02:45 +0530 Subject: usb: otg: utils: rename function name in OTG utils _transceiver() in otg.c is replaced with _phy. usb_set_transceiver is replaced with usb_add_phy to make it similar to other usb standard function names like usb_add_hcd. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/power/ab8500_charger.c | 6 +++--- drivers/power/isp1704_charger.c | 6 +++--- drivers/power/pda_power.c | 6 +++--- drivers/power/twl4030_charger.c | 6 +++--- drivers/usb/chipidea/udc.c | 8 ++++---- drivers/usb/gadget/fsl_udc_core.c | 2 +- drivers/usb/gadget/mv_udc_core.c | 2 +- drivers/usb/gadget/omap_udc.c | 6 +++--- drivers/usb/gadget/pxa25x_udc.c | 6 +++--- drivers/usb/gadget/pxa27x_udc.c | 4 ++-- drivers/usb/gadget/s3c-hsudc.c | 4 ++-- drivers/usb/host/ehci-fsl.c | 6 +++--- drivers/usb/host/ehci-msm.c | 6 +++--- drivers/usb/host/ehci-mv.c | 6 +++--- drivers/usb/host/ehci-tegra.c | 6 +++--- drivers/usb/host/ohci-omap.c | 6 +++--- drivers/usb/musb/am35x.c | 4 ++-- drivers/usb/musb/blackfin.c | 4 ++-- drivers/usb/musb/da8xx.c | 4 ++-- drivers/usb/musb/davinci.c | 6 +++--- drivers/usb/musb/musb_core.c | 2 +- drivers/usb/musb/musb_dsps.c | 6 +++--- drivers/usb/musb/omap2430.c | 4 ++-- drivers/usb/musb/tusb6010.c | 6 +++--- drivers/usb/musb/ux500.c | 4 ++-- drivers/usb/otg/ab8500-usb.c | 4 ++-- drivers/usb/otg/fsl_otg.c | 6 +++--- drivers/usb/otg/gpio_vbus.c | 4 ++-- drivers/usb/otg/isp1301_omap.c | 4 ++-- drivers/usb/otg/msm_otg.c | 6 +++--- drivers/usb/otg/mv_otg.c | 6 +++--- drivers/usb/otg/nop-usb-xceiv.c | 4 ++-- drivers/usb/otg/otg.c | 32 ++++++++++++++++---------------- drivers/usb/otg/twl4030-usb.c | 2 +- drivers/usb/otg/twl6030-usb.c | 2 +- include/linux/usb/otg.h | 10 +++++----- 36 files changed, 103 insertions(+), 103 deletions(-) (limited to 'include') diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index d2303d0b7c75..cf5ffc4d1048 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -2517,7 +2517,7 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev) dev_err(di->dev, "%s mask and set failed\n", __func__); usb_unregister_notifier(di->usb_phy, &di->nb); - usb_put_transceiver(di->usb_phy); + usb_put_phy(di->usb_phy); /* Delete the work queue */ destroy_workqueue(di->charger_wq); @@ -2688,7 +2688,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) goto free_ac; } - di->usb_phy = usb_get_transceiver(); + di->usb_phy = usb_get_phy(); if (!di->usb_phy) { dev_err(di->dev, "failed to get usb transceiver\n"); ret = -EINVAL; @@ -2747,7 +2747,7 @@ free_irq: free_irq(irq, di); } put_usb_phy: - usb_put_transceiver(di->usb_phy); + usb_put_phy(di->usb_phy); free_usb: power_supply_unregister(&di->usb_chg.psy); free_ac: diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c index e5ccd2979773..50773ae6f72e 100644 --- a/drivers/power/isp1704_charger.c +++ b/drivers/power/isp1704_charger.c @@ -415,7 +415,7 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev) if (!isp) return -ENOMEM; - isp->phy = usb_get_transceiver(); + isp->phy = usb_get_phy(); if (!isp->phy) goto fail0; @@ -475,7 +475,7 @@ fail2: power_supply_unregister(&isp->psy); fail1: isp1704_charger_set_power(isp, 0); - usb_put_transceiver(isp->phy); + usb_put_phy(isp->phy); fail0: kfree(isp); @@ -490,7 +490,7 @@ static int __devexit isp1704_charger_remove(struct platform_device *pdev) usb_unregister_notifier(isp->phy, &isp->nb); power_supply_unregister(&isp->psy); - usb_put_transceiver(isp->phy); + usb_put_phy(isp->phy); isp1704_charger_set_power(isp, 0); kfree(isp); diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c index 214468f4444a..e0f206b0775b 100644 --- a/drivers/power/pda_power.c +++ b/drivers/power/pda_power.c @@ -321,7 +321,7 @@ static int pda_power_probe(struct platform_device *pdev) } #ifdef CONFIG_USB_OTG_UTILS - transceiver = usb_get_transceiver(); + transceiver = usb_get_phy(); if (transceiver && !pdata->is_usb_online) { pdata->is_usb_online = otg_is_usb_online; } @@ -409,7 +409,7 @@ usb_supply_failed: free_irq(ac_irq->start, &pda_psy_ac); #ifdef CONFIG_USB_OTG_UTILS if (transceiver) - usb_put_transceiver(transceiver); + usb_put_phy(transceiver); #endif ac_irq_failed: if (pdata->is_ac_online) @@ -444,7 +444,7 @@ static int pda_power_remove(struct platform_device *pdev) power_supply_unregister(&pda_psy_ac); #ifdef CONFIG_USB_OTG_UTILS if (transceiver) - usb_put_transceiver(transceiver); + usb_put_phy(transceiver); #endif if (ac_draw) { regulator_put(ac_draw); diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index fdad850c77d3..fcddd115cc08 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -479,7 +479,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) INIT_WORK(&bci->work, twl4030_bci_usb_work); - bci->transceiver = usb_get_transceiver(); + bci->transceiver = usb_get_phy(); if (bci->transceiver != NULL) { bci->usb_nb.notifier_call = twl4030_bci_usb_ncb; usb_register_notifier(bci->transceiver, &bci->usb_nb); @@ -509,7 +509,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) fail_unmask_interrupts: if (bci->transceiver != NULL) { usb_unregister_notifier(bci->transceiver, &bci->usb_nb); - usb_put_transceiver(bci->transceiver); + usb_put_phy(bci->transceiver); } free_irq(bci->irq_bci, bci); fail_bci_irq: @@ -540,7 +540,7 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) if (bci->transceiver != NULL) { usb_unregister_notifier(bci->transceiver, &bci->usb_nb); - usb_put_transceiver(bci->transceiver); + usb_put_phy(bci->transceiver); } free_irq(bci->irq_bci, bci); free_irq(bci->irq_chg, bci); diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 51f96942dc5e..4468f2c2dddd 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1687,7 +1687,7 @@ static int udc_start(struct ci13xxx *udc) udc->gadget.ep0 = &udc->ep0in->ep; - udc->transceiver = usb_get_transceiver(); + udc->transceiver = usb_get_phy(); if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) { if (udc->transceiver == NULL) { @@ -1731,7 +1731,7 @@ static int udc_start(struct ci13xxx *udc) remove_trans: if (udc->transceiver) { otg_set_peripheral(udc->transceiver->otg, &udc->gadget); - usb_put_transceiver(udc->transceiver); + usb_put_phy(udc->transceiver); } dev_err(dev, "error = %i\n", retval); @@ -1741,7 +1741,7 @@ unreg_device: device_unregister(&udc->gadget.dev); put_transceiver: if (udc->transceiver) - usb_put_transceiver(udc->transceiver); + usb_put_phy(udc->transceiver); free_pools: dma_pool_destroy(udc->td_pool); free_qh_pool: @@ -1774,7 +1774,7 @@ static void udc_stop(struct ci13xxx *udc) if (udc->transceiver) { otg_set_peripheral(udc->transceiver->otg, NULL); - usb_put_transceiver(udc->transceiver); + usb_put_phy(udc->transceiver); } dbg_remove_files(&udc->gadget.dev); device_unregister(&udc->gadget.dev); diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 28316858208b..d7038509b956 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -2455,7 +2455,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) #ifdef CONFIG_USB_OTG if (pdata->operating_mode == FSL_USB2_DR_OTG) { - udc_controller->transceiver = usb_get_transceiver(); + udc_controller->transceiver = usb_get_phy(); if (!udc_controller->transceiver) { ERR("Can't find OTG driver!\n"); ret = -ENODEV; diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index dbcd1329495e..5d779955d5a6 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -2180,7 +2180,7 @@ static int __devinit mv_udc_probe(struct platform_device *dev) #ifdef CONFIG_USB_OTG_UTILS if (pdata->mode == MV_USB_MODE_OTG) - udc->transceiver = usb_get_transceiver(); + udc->transceiver = usb_get_phy(); #endif udc->clknum = pdata->clknum; diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 7ba32469c5bd..74b9bb8099e7 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -2865,7 +2865,7 @@ static int __init omap_udc_probe(struct platform_device *pdev) * use it. Except for OTG, we don't _need_ to talk to one; * but not having one probably means no VBUS detection. */ - xceiv = usb_get_transceiver(); + xceiv = usb_get_phy(); if (xceiv) type = xceiv->label; else if (config->otg) { @@ -3011,7 +3011,7 @@ cleanup1: cleanup0: if (xceiv) - usb_put_transceiver(xceiv); + usb_put_phy(xceiv); if (cpu_is_omap16xx() || cpu_is_omap24xx() || cpu_is_omap7xx()) { clk_disable(hhc_clk); @@ -3041,7 +3041,7 @@ static int __exit omap_udc_remove(struct platform_device *pdev) pullup_disable(udc); if (udc->transceiver) { - usb_put_transceiver(udc->transceiver); + usb_put_phy(udc->transceiver); udc->transceiver = NULL; } omap_writew(0, UDC_SYSCON1); diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index d7c8cb3bf759..a658e446caba 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -2159,7 +2159,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) dev->dev = &pdev->dev; dev->mach = pdev->dev.platform_data; - dev->transceiver = usb_get_transceiver(); + dev->transceiver = usb_get_phy(); if (gpio_is_valid(dev->mach->gpio_pullup)) { if ((retval = gpio_request(dev->mach->gpio_pullup, @@ -2238,7 +2238,7 @@ lubbock_fail0: gpio_free(dev->mach->gpio_pullup); err_gpio_pullup: if (dev->transceiver) { - usb_put_transceiver(dev->transceiver); + usb_put_phy(dev->transceiver); dev->transceiver = NULL; } clk_put(dev->clk); @@ -2280,7 +2280,7 @@ static int __exit pxa25x_udc_remove(struct platform_device *pdev) clk_put(dev->clk); if (dev->transceiver) { - usb_put_transceiver(dev->transceiver); + usb_put_phy(dev->transceiver); dev->transceiver = NULL; } diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 98acb3ab9e17..b982304a49c1 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -2464,7 +2464,7 @@ static int __init pxa_udc_probe(struct platform_device *pdev) udc->dev = &pdev->dev; udc->mach = pdev->dev.platform_data; - udc->transceiver = usb_get_transceiver(); + udc->transceiver = usb_get_phy(); gpio = udc->mach->gpio_pullup; if (gpio_is_valid(gpio)) { @@ -2543,7 +2543,7 @@ static int __exit pxa_udc_remove(struct platform_device *_dev) if (gpio_is_valid(gpio)) gpio_free(gpio); - usb_put_transceiver(udc->transceiver); + usb_put_phy(udc->transceiver); udc->transceiver = NULL; platform_set_drvdata(_dev, NULL); diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 36c6836eeb0f..9ad33395f564 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -1282,7 +1282,7 @@ static int __devinit s3c_hsudc_probe(struct platform_device *pdev) hsudc->dev = dev; hsudc->pd = pdev->dev.platform_data; - hsudc->transceiver = usb_get_transceiver(); + hsudc->transceiver = usb_get_phy(); for (i = 0; i < ARRAY_SIZE(hsudc->supplies); i++) hsudc->supplies[i].supply = s3c_hsudc_supply_names[i]; @@ -1386,7 +1386,7 @@ err_remap: release_mem_region(res->start, resource_size(res)); err_res: if (hsudc->transceiver) - usb_put_transceiver(hsudc->transceiver); + usb_put_phy(hsudc->transceiver); regulator_bulk_free(ARRAY_SIZE(hsudc->supplies), hsudc->supplies); err_supplies: diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 43362577b54a..0e8976a0ed51 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -142,7 +142,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, if (pdata->operating_mode == FSL_USB2_DR_OTG) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); - ehci->transceiver = usb_get_transceiver(); + ehci->transceiver = usb_get_phy(); dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, transceiver=0x%p\n", hcd, ehci, ehci->transceiver); @@ -150,7 +150,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, retval = otg_set_host(ehci->transceiver->otg, &ehci_to_hcd(ehci)->self); if (retval) { - usb_put_transceiver(ehci->transceiver); + usb_put_phy(ehci->transceiver); goto err4; } } else { @@ -194,7 +194,7 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd, if (ehci->transceiver) { otg_set_host(ehci->transceiver->otg, NULL); - usb_put_transceiver(ehci->transceiver); + usb_put_phy(ehci->transceiver); } usb_remove_hcd(hcd); diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 9803a55fd5f4..7badd5db398c 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -145,7 +145,7 @@ static int ehci_msm_probe(struct platform_device *pdev) * powering up VBUS, mapping of registers address space and power * management. */ - phy = usb_get_transceiver(); + phy = usb_get_phy(); if (!phy) { dev_err(&pdev->dev, "unable to find transceiver\n"); ret = -ENODEV; @@ -169,7 +169,7 @@ static int ehci_msm_probe(struct platform_device *pdev) return 0; put_transceiver: - usb_put_transceiver(phy); + usb_put_phy(phy); unmap: iounmap(hcd->regs); put_hcd: @@ -187,7 +187,7 @@ static int __devexit ehci_msm_remove(struct platform_device *pdev) pm_runtime_set_suspended(&pdev->dev); otg_set_host(phy->otg, NULL); - usb_put_transceiver(phy); + usb_put_phy(phy); usb_put_hcd(hcd); diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index a936bbcff8f4..24f838fe25ac 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -253,7 +253,7 @@ static int mv_ehci_probe(struct platform_device *pdev) ehci_mv->mode = pdata->mode; if (ehci_mv->mode == MV_USB_MODE_OTG) { #ifdef CONFIG_USB_OTG_UTILS - ehci_mv->otg = usb_get_transceiver(); + ehci_mv->otg = usb_get_phy(); if (!ehci_mv->otg) { dev_err(&pdev->dev, "unable to find transceiver\n"); @@ -303,7 +303,7 @@ err_set_vbus: #ifdef CONFIG_USB_OTG_UTILS err_put_transceiver: if (ehci_mv->otg) - usb_put_transceiver(ehci_mv->otg); + usb_put_phy(ehci_mv->otg); #endif err_disable_clk: mv_ehci_disable(ehci_mv); @@ -333,7 +333,7 @@ static int mv_ehci_remove(struct platform_device *pdev) if (ehci_mv->otg) { otg_set_host(ehci_mv->otg->otg, NULL); - usb_put_transceiver(ehci_mv->otg); + usb_put_phy(ehci_mv->otg); } if (ehci_mv->mode == MV_USB_MODE_HOST) { diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 68548236ec42..ee17d19b1b82 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -749,7 +749,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) #ifdef CONFIG_USB_OTG_UTILS if (pdata->operating_mode == TEGRA_USB_OTG) { - tegra->transceiver = usb_get_transceiver(); + tegra->transceiver = usb_get_phy(); if (tegra->transceiver) otg_set_host(tegra->transceiver->otg, &hcd->self); } @@ -775,7 +775,7 @@ fail: #ifdef CONFIG_USB_OTG_UTILS if (tegra->transceiver) { otg_set_host(tegra->transceiver->otg, NULL); - usb_put_transceiver(tegra->transceiver); + usb_put_phy(tegra->transceiver); } #endif tegra_usb_phy_close(tegra->phy); @@ -810,7 +810,7 @@ static int tegra_ehci_remove(struct platform_device *pdev) #ifdef CONFIG_USB_OTG_UTILS if (tegra->transceiver) { otg_set_host(tegra->transceiver->otg, NULL); - usb_put_transceiver(tegra->transceiver); + usb_put_phy(tegra->transceiver); } #endif diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 9ce35d0d9d5d..c2c1f55889a4 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -211,14 +211,14 @@ static int ohci_omap_init(struct usb_hcd *hcd) #ifdef CONFIG_USB_OTG if (need_transceiver) { - ohci->transceiver = usb_get_transceiver(); + ohci->transceiver = usb_get_phy(); if (ohci->transceiver) { int status = otg_set_host(ohci->transceiver->otg, &ohci_to_hcd(ohci)->self); dev_dbg(hcd->self.controller, "init %s transceiver, status %d\n", ohci->transceiver->label, status); if (status) { - usb_put_transceiver(ohci->transceiver); + usb_put_phy(ohci->transceiver); return status; } } else { @@ -405,7 +405,7 @@ usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev) usb_remove_hcd(hcd); if (ohci->transceiver) { (void) otg_set_host(ohci->transceiver->otg, 0); - usb_put_transceiver(ohci->transceiver); + usb_put_phy(ohci->transceiver); } if (machine_is_omap_osk()) gpio_free(9); diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 9f3eda91ea4d..a75989bbb3d4 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -364,7 +364,7 @@ static int am35x_musb_init(struct musb *musb) return -ENODEV; usb_nop_xceiv_register(); - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) return -ENODEV; @@ -406,7 +406,7 @@ static int am35x_musb_exit(struct musb *musb) if (data->set_phy_power) data->set_phy_power(0); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return 0; diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index a087ed6c3be9..522a4a263df8 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -415,7 +415,7 @@ static int bfin_musb_init(struct musb *musb) gpio_direction_output(musb->config->gpio_vrsel, 0); usb_nop_xceiv_register(); - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) { gpio_free(musb->config->gpio_vrsel); return -ENODEV; @@ -440,7 +440,7 @@ static int bfin_musb_exit(struct musb *musb) { gpio_free(musb->config->gpio_vrsel); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return 0; } diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 8bd9566f3fbb..61868d604b28 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -425,7 +425,7 @@ static int da8xx_musb_init(struct musb *musb) goto fail; usb_nop_xceiv_register(); - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) goto fail; @@ -458,7 +458,7 @@ static int da8xx_musb_exit(struct musb *musb) phy_off(); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return 0; diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 768b4b55c816..441f776366f3 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -384,7 +384,7 @@ static int davinci_musb_init(struct musb *musb) u32 revision; usb_nop_xceiv_register(); - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) goto unregister; @@ -443,7 +443,7 @@ static int davinci_musb_init(struct musb *musb) return 0; fail: - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); unregister: usb_nop_xceiv_unregister(); return -ENODEV; @@ -493,7 +493,7 @@ static int davinci_musb_exit(struct musb *musb) phy_off(); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return 0; diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index db3dff854b71..26f1befb4896 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1909,7 +1909,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) /* The musb_platform_init() call: * - adjusts musb->mregs and musb->isr if needed, * - may initialize an integrated tranceiver - * - initializes musb->xceiv, usually by otg_get_transceiver() + * - initializes musb->xceiv, usually by otg_get_phy() * - stops powering VBUS * * There are various transceiver configurations. Blackfin, diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 23db42db761a..716c113608f4 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -376,7 +376,7 @@ static int dsps_musb_init(struct musb *musb) /* NOP driver needs change if supporting dual instance */ usb_nop_xceiv_register(); - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) return -ENODEV; @@ -409,7 +409,7 @@ static int dsps_musb_init(struct musb *musb) return 0; err0: - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return status; } @@ -430,7 +430,7 @@ static int dsps_musb_exit(struct musb *musb) data->set_phy_power(0); /* NOP driver needs change if supporting dual instance */ - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return 0; diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index c7785e81254c..e16dbbf7f305 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -292,7 +292,7 @@ static int omap2430_musb_init(struct musb *musb) * up through ULPI. TWL4030-family PMICs include one, * which needs a driver, drivers aren't always needed. */ - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; @@ -391,7 +391,7 @@ static int omap2430_musb_exit(struct musb *musb) cancel_work_sync(&musb->otg_notifier_work); omap2430_low_level_exit(musb); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); return 0; } diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index de1355946a83..a004736186f1 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -1078,7 +1078,7 @@ static int tusb_musb_init(struct musb *musb) int ret; usb_nop_xceiv_register(); - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) return -ENODEV; @@ -1130,7 +1130,7 @@ done: if (sync) iounmap(sync); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); } return ret; @@ -1146,7 +1146,7 @@ static int tusb_musb_exit(struct musb *musb) iounmap(musb->sync_va); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return 0; } diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index aa09dd417b94..53006b113b12 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -37,7 +37,7 @@ struct ux500_glue { static int ux500_musb_init(struct musb *musb) { - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; @@ -48,7 +48,7 @@ static int ux500_musb_init(struct musb *musb) static int ux500_musb_exit(struct musb *musb) { - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); return 0; } diff --git a/drivers/usb/otg/ab8500-usb.c b/drivers/usb/otg/ab8500-usb.c index a84af677dc59..672e28c81437 100644 --- a/drivers/usb/otg/ab8500-usb.c +++ b/drivers/usb/otg/ab8500-usb.c @@ -529,7 +529,7 @@ static int __devinit ab8500_usb_probe(struct platform_device *pdev) if (err < 0) goto fail0; - err = usb_set_transceiver(&ab->phy); + err = usb_add_phy(&ab->phy); if (err) { dev_err(&pdev->dev, "Can't register transceiver\n"); goto fail1; @@ -556,7 +556,7 @@ static int __devexit ab8500_usb_remove(struct platform_device *pdev) cancel_work_sync(&ab->phy_dis_work); - usb_set_transceiver(NULL); + usb_add_phy(NULL); ab8500_usb_host_phy_dis(ab); ab8500_usb_peri_phy_dis(ab); diff --git a/drivers/usb/otg/fsl_otg.c b/drivers/usb/otg/fsl_otg.c index be4a63e8302f..73561edd81de 100644 --- a/drivers/usb/otg/fsl_otg.c +++ b/drivers/usb/otg/fsl_otg.c @@ -806,7 +806,7 @@ static int fsl_otg_conf(struct platform_device *pdev) fsl_otg_dev = fsl_otg_tc; /* Store the otg transceiver */ - status = usb_set_transceiver(&fsl_otg_tc->phy); + status = usb_add_phy(&fsl_otg_tc->phy); if (status) { pr_warn(FSL_OTG_NAME ": unable to register OTG transceiver.\n"); goto err; @@ -824,7 +824,7 @@ err: int usb_otg_start(struct platform_device *pdev) { struct fsl_otg *p_otg; - struct usb_phy *otg_trans = usb_get_transceiver(); + struct usb_phy *otg_trans = usb_get_phy(); struct otg_fsm *fsm; int status; struct resource *res; @@ -1134,7 +1134,7 @@ static int __devexit fsl_otg_remove(struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; - usb_set_transceiver(NULL); + usb_add_phy(NULL); free_irq(fsl_otg_dev->irq, fsl_otg_dev); iounmap((void *)usb_dr_regs); diff --git a/drivers/usb/otg/gpio_vbus.c b/drivers/usb/otg/gpio_vbus.c index bde6298a9693..9b3c264cdb56 100644 --- a/drivers/usb/otg/gpio_vbus.c +++ b/drivers/usb/otg/gpio_vbus.c @@ -320,7 +320,7 @@ static int __init gpio_vbus_probe(struct platform_device *pdev) } /* only active when a gadget is registered */ - err = usb_set_transceiver(&gpio_vbus->phy); + err = usb_add_phy(&gpio_vbus->phy); if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); @@ -354,7 +354,7 @@ static int __exit gpio_vbus_remove(struct platform_device *pdev) cancel_delayed_work_sync(&gpio_vbus->work); regulator_put(gpio_vbus->vbus_draw); - usb_set_transceiver(NULL); + usb_add_phy(NULL); free_irq(gpio_vbus->irq, pdev); if (gpio_is_valid(pdata->gpio_pullup)) diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index 33cd709b084e..b74df3fec56f 100644 --- a/drivers/usb/otg/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c @@ -1611,7 +1611,7 @@ isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id) dev_dbg(&i2c->dev, "scheduled timer, %d min\n", TIMER_MINUTES); #endif - status = usb_set_transceiver(&isp->phy); + status = usb_add_phy(&isp->phy); if (status < 0) dev_err(&i2c->dev, "can't register transceiver, %d\n", status); @@ -1650,7 +1650,7 @@ subsys_initcall(isp_init); static void __exit isp_exit(void) { if (the_transceiver) - usb_set_transceiver(NULL); + usb_add_phy(NULL); i2c_del_driver(&isp1301_driver); } module_exit(isp_exit); diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index 1d0347c247d1..dd606c010035 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -1555,9 +1555,9 @@ static int __init msm_otg_probe(struct platform_device *pdev) phy->otg->set_host = msm_otg_set_host; phy->otg->set_peripheral = msm_otg_set_peripheral; - ret = usb_set_transceiver(&motg->phy); + ret = usb_add_phy(&motg->phy); if (ret) { - dev_err(&pdev->dev, "usb_set_transceiver failed\n"); + dev_err(&pdev->dev, "usb_add_phy failed\n"); goto free_irq; } @@ -1624,7 +1624,7 @@ static int __devexit msm_otg_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 0); pm_runtime_disable(&pdev->dev); - usb_set_transceiver(NULL); + usb_add_phy(NULL); free_irq(motg->irq, motg); /* diff --git a/drivers/usb/otg/mv_otg.c b/drivers/usb/otg/mv_otg.c index 6cc6c3ffbb83..18e90fe1fbd1 100644 --- a/drivers/usb/otg/mv_otg.c +++ b/drivers/usb/otg/mv_otg.c @@ -690,7 +690,7 @@ int mv_otg_remove(struct platform_device *pdev) for (clk_i = 0; clk_i <= mvotg->clknum; clk_i++) clk_put(mvotg->clk[clk_i]); - usb_set_transceiver(NULL); + usb_add_phy(NULL); platform_set_drvdata(pdev, NULL); kfree(mvotg->phy.otg); @@ -853,7 +853,7 @@ static int mv_otg_probe(struct platform_device *pdev) goto err_disable_clk; } - retval = usb_set_transceiver(&mvotg->phy); + retval = usb_add_phy(&mvotg->phy); if (retval < 0) { dev_err(&pdev->dev, "can't register transceiver, %d\n", retval); @@ -880,7 +880,7 @@ static int mv_otg_probe(struct platform_device *pdev) return 0; err_set_transceiver: - usb_set_transceiver(NULL); + usb_add_phy(NULL); err_free_irq: free_irq(mvotg->irq, mvotg); err_disable_clk: diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index 58b26df6afd1..33000dae7200 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -117,7 +117,7 @@ static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev) nop->phy.otg->set_host = nop_set_host; nop->phy.otg->set_peripheral = nop_set_peripheral; - err = usb_set_transceiver(&nop->phy); + err = usb_add_phy(&nop->phy); if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); @@ -139,7 +139,7 @@ static int __devexit nop_usb_xceiv_remove(struct platform_device *pdev) { struct nop_usb_xceiv *nop = platform_get_drvdata(pdev); - usb_set_transceiver(NULL); + usb_add_phy(NULL); platform_set_drvdata(pdev, NULL); kfree(nop->phy.otg); diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index 801e597a1541..300a995cfdbe 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -18,53 +18,53 @@ static struct usb_phy *phy; /** - * usb_get_transceiver - find the (single) USB transceiver + * usb_get_phy - find the (single) USB PHY * - * Returns the transceiver driver, after getting a refcount to it; or - * null if there is no such transceiver. The caller is responsible for - * calling usb_put_transceiver() to release that count. + * Returns the phy driver, after getting a refcount to it; or + * null if there is no such phy. The caller is responsible for + * calling usb_put_phy() to release that count. * * For use by USB host and peripheral drivers. */ -struct usb_phy *usb_get_transceiver(void) +struct usb_phy *usb_get_phy(void) { if (phy) get_device(phy->dev); return phy; } -EXPORT_SYMBOL(usb_get_transceiver); +EXPORT_SYMBOL(usb_get_phy); /** - * usb_put_transceiver - release the (single) USB transceiver - * @x: the transceiver returned by usb_get_transceiver() + * usb_put_phy - release the (single) USB PHY + * @x: the phy returned by usb_get_phy() * - * Releases a refcount the caller received from usb_get_transceiver(). + * Releases a refcount the caller received from usb_get_phy(). * * For use by USB host and peripheral drivers. */ -void usb_put_transceiver(struct usb_phy *x) +void usb_put_phy(struct usb_phy *x) { if (x) put_device(x->dev); } -EXPORT_SYMBOL(usb_put_transceiver); +EXPORT_SYMBOL(usb_put_phy); /** - * usb_set_transceiver - declare the (single) USB transceiver - * @x: the USB transceiver to be used; or NULL + * usb_add_phy - declare the (single) USB PHY + * @x: the USB phy to be used; or NULL * - * This call is exclusively for use by transceiver drivers, which + * This call is exclusively for use by phy drivers, which * coordinate the activities of drivers for host and peripheral * controllers, and in some cases for VBUS current regulation. */ -int usb_set_transceiver(struct usb_phy *x) +int usb_add_phy(struct usb_phy *x) { if (phy && x) return -EBUSY; phy = x; return 0; } -EXPORT_SYMBOL(usb_set_transceiver); +EXPORT_SYMBOL(usb_add_phy); const char *otg_state_string(enum usb_otg_state state) { diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index 02979306bf83..01022c891e26 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -633,7 +633,7 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev) kfree(twl); return err; } - usb_set_transceiver(&twl->phy); + usb_add_phy(&twl->phy); platform_set_drvdata(pdev, twl); if (device_create_file(&pdev->dev, &dev_attr_vbus)) diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index d2a9a8e691b9..a8be20878eb9 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -443,7 +443,7 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) kfree(twl); return err; } - usb_set_transceiver(&twl->phy); + usb_add_phy(&twl->phy); platform_set_drvdata(pdev, twl); if (device_create_file(&pdev->dev, &dev_attr_vbus)) diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 38ab3f46346f..0e739c810525 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -121,7 +121,7 @@ struct usb_phy { /* for board-specific init logic */ -extern int usb_set_transceiver(struct usb_phy *); +extern int usb_add_phy(struct usb_phy *); #if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE)) /* sometimes transceivers are accessed only through e.g. ULPI */ @@ -172,16 +172,16 @@ usb_phy_shutdown(struct usb_phy *x) /* for usb host and peripheral controller drivers */ #ifdef CONFIG_USB_OTG_UTILS -extern struct usb_phy *usb_get_transceiver(void); -extern void usb_put_transceiver(struct usb_phy *); +extern struct usb_phy *usb_get_phy(void); +extern void usb_put_phy(struct usb_phy *); extern const char *otg_state_string(enum usb_otg_state state); #else -static inline struct usb_phy *usb_get_transceiver(void) +static inline struct usb_phy *usb_get_phy(void) { return NULL; } -static inline void usb_put_transceiver(struct usb_phy *x) +static inline void usb_put_phy(struct usb_phy *x) { } -- cgit v1.2.3 From 662dca54ca67c92b7aa14b9a2ec54acacf33ce45 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 22 Jun 2012 17:02:46 +0530 Subject: usb: otg: support for multiple transceivers by a single controller Add a linked list for keeping multiple PHY instances with different types so that we can have separate USB2 and USB3 PHYs on one single board. _get_phy_ has been changed so that the controller gets the transceiver by type. _remove_phy_ has been added to let the phy be removed from the phy list. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/power/ab8500_charger.c | 2 +- drivers/power/isp1704_charger.c | 2 +- drivers/power/pda_power.c | 2 +- drivers/power/twl4030_charger.c | 2 +- drivers/usb/chipidea/udc.c | 2 +- drivers/usb/gadget/fsl_udc_core.c | 2 +- drivers/usb/gadget/mv_udc_core.c | 2 +- drivers/usb/gadget/omap_udc.c | 2 +- drivers/usb/gadget/pxa25x_udc.c | 2 +- drivers/usb/gadget/pxa27x_udc.c | 2 +- drivers/usb/gadget/s3c-hsudc.c | 2 +- drivers/usb/host/ehci-fsl.c | 2 +- drivers/usb/host/ehci-msm.c | 2 +- drivers/usb/host/ehci-mv.c | 2 +- drivers/usb/host/ehci-tegra.c | 2 +- drivers/usb/host/ohci-omap.c | 2 +- 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/omap2430.c | 2 +- drivers/usb/musb/tusb6010.c | 2 +- drivers/usb/musb/ux500.c | 2 +- drivers/usb/otg/ab8500-usb.c | 4 +- drivers/usb/otg/fsl_otg.c | 6 +-- drivers/usb/otg/gpio_vbus.c | 4 +- drivers/usb/otg/isp1301_omap.c | 4 +- drivers/usb/otg/msm_otg.c | 4 +- drivers/usb/otg/mv_otg.c | 6 +-- drivers/usb/otg/nop-usb-xceiv.c | 4 +- drivers/usb/otg/otg.c | 96 ++++++++++++++++++++++++++++++++++----- drivers/usb/otg/twl4030-usb.c | 2 +- drivers/usb/otg/twl6030-usb.c | 2 +- include/linux/usb/otg.h | 29 ++++++++++-- 35 files changed, 152 insertions(+), 57 deletions(-) (limited to 'include') diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index cf5ffc4d1048..6bd6f1c41967 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -2688,7 +2688,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) goto free_ac; } - di->usb_phy = usb_get_phy(); + di->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2); if (!di->usb_phy) { dev_err(di->dev, "failed to get usb transceiver\n"); ret = -EINVAL; diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c index 50773ae6f72e..090e5f9e72c9 100644 --- a/drivers/power/isp1704_charger.c +++ b/drivers/power/isp1704_charger.c @@ -415,7 +415,7 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev) if (!isp) return -ENOMEM; - isp->phy = usb_get_phy(); + isp->phy = usb_get_phy(USB_PHY_TYPE_USB2); if (!isp->phy) goto fail0; diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c index e0f206b0775b..7602d49e4d81 100644 --- a/drivers/power/pda_power.c +++ b/drivers/power/pda_power.c @@ -321,7 +321,7 @@ static int pda_power_probe(struct platform_device *pdev) } #ifdef CONFIG_USB_OTG_UTILS - transceiver = usb_get_phy(); + transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (transceiver && !pdata->is_usb_online) { pdata->is_usb_online = otg_is_usb_online; } diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index fcddd115cc08..13f9db2e8538 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -479,7 +479,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) INIT_WORK(&bci->work, twl4030_bci_usb_work); - bci->transceiver = usb_get_phy(); + bci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (bci->transceiver != NULL) { bci->usb_nb.notifier_call = twl4030_bci_usb_ncb; usb_register_notifier(bci->transceiver, &bci->usb_nb); diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 4468f2c2dddd..a06d28b119f5 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1687,7 +1687,7 @@ static int udc_start(struct ci13xxx *udc) udc->gadget.ep0 = &udc->ep0in->ep; - udc->transceiver = usb_get_phy(); + udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) { if (udc->transceiver == NULL) { diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index d7038509b956..0808820ba495 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -2455,7 +2455,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) #ifdef CONFIG_USB_OTG if (pdata->operating_mode == FSL_USB2_DR_OTG) { - udc_controller->transceiver = usb_get_phy(); + udc_controller->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (!udc_controller->transceiver) { ERR("Can't find OTG driver!\n"); ret = -ENODEV; diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 5d779955d5a6..75ff41a5c959 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -2180,7 +2180,7 @@ static int __devinit mv_udc_probe(struct platform_device *dev) #ifdef CONFIG_USB_OTG_UTILS if (pdata->mode == MV_USB_MODE_OTG) - udc->transceiver = usb_get_phy(); + udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); #endif udc->clknum = pdata->clknum; diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 74b9bb8099e7..cf8bf26f12ed 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -2865,7 +2865,7 @@ static int __init omap_udc_probe(struct platform_device *pdev) * use it. Except for OTG, we don't _need_ to talk to one; * but not having one probably means no VBUS detection. */ - xceiv = usb_get_phy(); + xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (xceiv) type = xceiv->label; else if (config->otg) { diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index a658e446caba..cc0b1e63dcab 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -2159,7 +2159,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) dev->dev = &pdev->dev; dev->mach = pdev->dev.platform_data; - dev->transceiver = usb_get_phy(); + dev->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (gpio_is_valid(dev->mach->gpio_pullup)) { if ((retval = gpio_request(dev->mach->gpio_pullup, diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index b982304a49c1..8f744aab9628 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -2464,7 +2464,7 @@ static int __init pxa_udc_probe(struct platform_device *pdev) udc->dev = &pdev->dev; udc->mach = pdev->dev.platform_data; - udc->transceiver = usb_get_phy(); + udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); gpio = udc->mach->gpio_pullup; if (gpio_is_valid(gpio)) { diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 9ad33395f564..22326f274466 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -1282,7 +1282,7 @@ static int __devinit s3c_hsudc_probe(struct platform_device *pdev) hsudc->dev = dev; hsudc->pd = pdev->dev.platform_data; - hsudc->transceiver = usb_get_phy(); + hsudc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); for (i = 0; i < ARRAY_SIZE(hsudc->supplies); i++) hsudc->supplies[i].supply = s3c_hsudc_supply_names[i]; diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 0e8976a0ed51..ba290589d858 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -142,7 +142,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, if (pdata->operating_mode == FSL_USB2_DR_OTG) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); - ehci->transceiver = usb_get_phy(); + ehci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, transceiver=0x%p\n", hcd, ehci, ehci->transceiver); diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 7badd5db398c..c7615fb93dbb 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -145,7 +145,7 @@ static int ehci_msm_probe(struct platform_device *pdev) * powering up VBUS, mapping of registers address space and power * management. */ - phy = usb_get_phy(); + phy = usb_get_phy(USB_PHY_TYPE_USB2); if (!phy) { dev_err(&pdev->dev, "unable to find transceiver\n"); ret = -ENODEV; diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index 24f838fe25ac..ef7aa0df40a6 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -253,7 +253,7 @@ static int mv_ehci_probe(struct platform_device *pdev) ehci_mv->mode = pdata->mode; if (ehci_mv->mode == MV_USB_MODE_OTG) { #ifdef CONFIG_USB_OTG_UTILS - ehci_mv->otg = usb_get_phy(); + ehci_mv->otg = usb_get_phy(USB_PHY_TYPE_USB2); if (!ehci_mv->otg) { dev_err(&pdev->dev, "unable to find transceiver\n"); diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index ee17d19b1b82..14df2f5cf6ae 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -749,7 +749,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) #ifdef CONFIG_USB_OTG_UTILS if (pdata->operating_mode == TEGRA_USB_OTG) { - tegra->transceiver = usb_get_phy(); + tegra->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (tegra->transceiver) otg_set_host(tegra->transceiver->otg, &hcd->self); } diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index c2c1f55889a4..92a77dfd1930 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -211,7 +211,7 @@ static int ohci_omap_init(struct usb_hcd *hcd) #ifdef CONFIG_USB_OTG if (need_transceiver) { - ohci->transceiver = usb_get_phy(); + ohci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (ohci->transceiver) { int status = otg_set_host(ohci->transceiver->otg, &ohci_to_hcd(ohci)->self); diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index a75989bbb3d4..4a8cbf0e8d51 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -364,7 +364,7 @@ static int am35x_musb_init(struct musb *musb) return -ENODEV; usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) return -ENODEV; diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 522a4a263df8..452940986d6d 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -415,7 +415,7 @@ static int bfin_musb_init(struct musb *musb) gpio_direction_output(musb->config->gpio_vrsel, 0); usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) { gpio_free(musb->config->gpio_vrsel); return -ENODEV; diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 61868d604b28..d731c80c4fef 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -425,7 +425,7 @@ static int da8xx_musb_init(struct musb *musb) goto fail; usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) goto fail; diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 441f776366f3..582268de3fa2 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -384,7 +384,7 @@ static int davinci_musb_init(struct musb *musb) u32 revision; usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) goto unregister; diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 716c113608f4..92603e498e61 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -376,7 +376,7 @@ static int dsps_musb_init(struct musb *musb) /* NOP driver needs change if supporting dual instance */ usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) return -ENODEV; diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index e16dbbf7f305..e279cf32772e 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -292,7 +292,7 @@ static int omap2430_musb_init(struct musb *musb) * up through ULPI. TWL4030-family PMICs include one, * which needs a driver, drivers aren't always needed. */ - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index a004736186f1..8ddf3d5f7cdc 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -1078,7 +1078,7 @@ static int tusb_musb_init(struct musb *musb) int ret; usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) return -ENODEV; diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index 53006b113b12..46cf80a8cacd 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -37,7 +37,7 @@ struct ux500_glue { static int ux500_musb_init(struct musb *musb) { - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; diff --git a/drivers/usb/otg/ab8500-usb.c b/drivers/usb/otg/ab8500-usb.c index 672e28c81437..ae8ad561f083 100644 --- a/drivers/usb/otg/ab8500-usb.c +++ b/drivers/usb/otg/ab8500-usb.c @@ -529,7 +529,7 @@ static int __devinit ab8500_usb_probe(struct platform_device *pdev) if (err < 0) goto fail0; - err = usb_add_phy(&ab->phy); + err = usb_add_phy(&ab->phy, USB_PHY_TYPE_USB2); if (err) { dev_err(&pdev->dev, "Can't register transceiver\n"); goto fail1; @@ -556,7 +556,7 @@ static int __devexit ab8500_usb_remove(struct platform_device *pdev) cancel_work_sync(&ab->phy_dis_work); - usb_add_phy(NULL); + usb_remove_phy(&ab->phy); ab8500_usb_host_phy_dis(ab); ab8500_usb_peri_phy_dis(ab); diff --git a/drivers/usb/otg/fsl_otg.c b/drivers/usb/otg/fsl_otg.c index 73561edd81de..23c798cb2d7f 100644 --- a/drivers/usb/otg/fsl_otg.c +++ b/drivers/usb/otg/fsl_otg.c @@ -806,7 +806,7 @@ static int fsl_otg_conf(struct platform_device *pdev) fsl_otg_dev = fsl_otg_tc; /* Store the otg transceiver */ - status = usb_add_phy(&fsl_otg_tc->phy); + status = usb_add_phy(&fsl_otg_tc->phy, USB_PHY_TYPE_USB2); if (status) { pr_warn(FSL_OTG_NAME ": unable to register OTG transceiver.\n"); goto err; @@ -824,7 +824,7 @@ err: int usb_otg_start(struct platform_device *pdev) { struct fsl_otg *p_otg; - struct usb_phy *otg_trans = usb_get_phy(); + struct usb_phy *otg_trans = usb_get_phy(USB_PHY_TYPE_USB2); struct otg_fsm *fsm; int status; struct resource *res; @@ -1134,7 +1134,7 @@ static int __devexit fsl_otg_remove(struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; - usb_add_phy(NULL); + usb_remove_phy(&fsl_otg_dev->phy); free_irq(fsl_otg_dev->irq, fsl_otg_dev); iounmap((void *)usb_dr_regs); diff --git a/drivers/usb/otg/gpio_vbus.c b/drivers/usb/otg/gpio_vbus.c index 9b3c264cdb56..a67ffe22179a 100644 --- a/drivers/usb/otg/gpio_vbus.c +++ b/drivers/usb/otg/gpio_vbus.c @@ -320,7 +320,7 @@ static int __init gpio_vbus_probe(struct platform_device *pdev) } /* only active when a gadget is registered */ - err = usb_add_phy(&gpio_vbus->phy); + err = usb_add_phy(&gpio_vbus->phy, USB_PHY_TYPE_USB2); if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); @@ -354,7 +354,7 @@ static int __exit gpio_vbus_remove(struct platform_device *pdev) cancel_delayed_work_sync(&gpio_vbus->work); regulator_put(gpio_vbus->vbus_draw); - usb_add_phy(NULL); + usb_remove_phy(&gpio_vbus->phy); free_irq(gpio_vbus->irq, pdev); if (gpio_is_valid(pdata->gpio_pullup)) diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index b74df3fec56f..75cea4ab0985 100644 --- a/drivers/usb/otg/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c @@ -1611,7 +1611,7 @@ isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id) dev_dbg(&i2c->dev, "scheduled timer, %d min\n", TIMER_MINUTES); #endif - status = usb_add_phy(&isp->phy); + status = usb_add_phy(&isp->phy, USB_PHY_TYPE_USB2); if (status < 0) dev_err(&i2c->dev, "can't register transceiver, %d\n", status); @@ -1650,7 +1650,7 @@ subsys_initcall(isp_init); static void __exit isp_exit(void) { if (the_transceiver) - usb_add_phy(NULL); + usb_remove_phy(&the_transceiver->phy); i2c_del_driver(&isp1301_driver); } module_exit(isp_exit); diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index dd606c010035..9f5fc906041a 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -1555,7 +1555,7 @@ static int __init msm_otg_probe(struct platform_device *pdev) phy->otg->set_host = msm_otg_set_host; phy->otg->set_peripheral = msm_otg_set_peripheral; - ret = usb_add_phy(&motg->phy); + ret = usb_add_phy(&motg->phy, USB_PHY_TYPE_USB2); if (ret) { dev_err(&pdev->dev, "usb_add_phy failed\n"); goto free_irq; @@ -1624,7 +1624,7 @@ static int __devexit msm_otg_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 0); pm_runtime_disable(&pdev->dev); - usb_add_phy(NULL); + usb_remove_phy(phy); free_irq(motg->irq, motg); /* diff --git a/drivers/usb/otg/mv_otg.c b/drivers/usb/otg/mv_otg.c index 18e90fe1fbd1..3f124e8f5792 100644 --- a/drivers/usb/otg/mv_otg.c +++ b/drivers/usb/otg/mv_otg.c @@ -690,7 +690,7 @@ int mv_otg_remove(struct platform_device *pdev) for (clk_i = 0; clk_i <= mvotg->clknum; clk_i++) clk_put(mvotg->clk[clk_i]); - usb_add_phy(NULL); + usb_remove_phy(&mvotg->phy); platform_set_drvdata(pdev, NULL); kfree(mvotg->phy.otg); @@ -853,7 +853,7 @@ static int mv_otg_probe(struct platform_device *pdev) goto err_disable_clk; } - retval = usb_add_phy(&mvotg->phy); + retval = usb_add_phy(&mvotg->phy, USB_PHY_TYPE_USB2); if (retval < 0) { dev_err(&pdev->dev, "can't register transceiver, %d\n", retval); @@ -880,7 +880,7 @@ static int mv_otg_probe(struct platform_device *pdev) return 0; err_set_transceiver: - usb_add_phy(NULL); + usb_remove_phy(&mvotg->phy); err_free_irq: free_irq(mvotg->irq, mvotg); err_disable_clk: diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index 33000dae7200..803f958f4133 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -117,7 +117,7 @@ static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev) nop->phy.otg->set_host = nop_set_host; nop->phy.otg->set_peripheral = nop_set_peripheral; - err = usb_add_phy(&nop->phy); + err = usb_add_phy(&nop->phy, USB_PHY_TYPE_USB2); if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); @@ -139,7 +139,7 @@ static int __devexit nop_usb_xceiv_remove(struct platform_device *pdev) { struct nop_usb_xceiv *nop = platform_get_drvdata(pdev); - usb_add_phy(NULL); + usb_remove_phy(&nop->phy); platform_set_drvdata(pdev, NULL); kfree(nop->phy.otg); diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index 300a995cfdbe..a23065820ead 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -11,14 +11,32 @@ #include #include +#include #include #include -static struct usb_phy *phy; +static LIST_HEAD(phy_list); +static DEFINE_SPINLOCK(phy_lock); + +static struct usb_phy *__usb_find_phy(struct list_head *list, + enum usb_phy_type type) +{ + struct usb_phy *phy = NULL; + + list_for_each_entry(phy, list, head) { + if (phy->type != type) + continue; + + return phy; + } + + return ERR_PTR(-ENODEV); +} /** - * usb_get_phy - find the (single) USB PHY + * usb_get_phy - find the USB PHY + * @type - the type of the phy the controller requires * * Returns the phy driver, after getting a refcount to it; or * null if there is no such phy. The caller is responsible for @@ -26,16 +44,30 @@ static struct usb_phy *phy; * * For use by USB host and peripheral drivers. */ -struct usb_phy *usb_get_phy(void) +struct usb_phy *usb_get_phy(enum usb_phy_type type) { - if (phy) - get_device(phy->dev); + struct usb_phy *phy = NULL; + unsigned long flags; + + spin_lock_irqsave(&phy_lock, flags); + + phy = __usb_find_phy(&phy_list, type); + if (IS_ERR(phy)) { + pr_err("unable to find transceiver of type %s\n", + usb_phy_type_string(type)); + return phy; + } + + get_device(phy->dev); + + spin_unlock_irqrestore(&phy_lock, flags); + return phy; } EXPORT_SYMBOL(usb_get_phy); /** - * usb_put_phy - release the (single) USB PHY + * usb_put_phy - release the USB PHY * @x: the phy returned by usb_get_phy() * * Releases a refcount the caller received from usb_get_phy(). @@ -50,22 +82,62 @@ void usb_put_phy(struct usb_phy *x) EXPORT_SYMBOL(usb_put_phy); /** - * usb_add_phy - declare the (single) USB PHY + * usb_add_phy - declare the USB PHY * @x: the USB phy to be used; or NULL + * @type - the type of this PHY * * This call is exclusively for use by phy drivers, which * coordinate the activities of drivers for host and peripheral * controllers, and in some cases for VBUS current regulation. */ -int usb_add_phy(struct usb_phy *x) +int usb_add_phy(struct usb_phy *x, enum usb_phy_type type) { - if (phy && x) - return -EBUSY; - phy = x; - return 0; + int ret = 0; + unsigned long flags; + struct usb_phy *phy; + + if (x && x->type != USB_PHY_TYPE_UNDEFINED) { + dev_err(x->dev, "not accepting initialized PHY %s\n", x->label); + return -EINVAL; + } + + spin_lock_irqsave(&phy_lock, flags); + + list_for_each_entry(phy, &phy_list, head) { + if (phy->type == type) { + ret = -EBUSY; + dev_err(x->dev, "transceiver type %s already exists\n", + usb_phy_type_string(type)); + goto out; + } + } + + x->type = type; + list_add_tail(&x->head, &phy_list); + +out: + spin_unlock_irqrestore(&phy_lock, flags); + return ret; } EXPORT_SYMBOL(usb_add_phy); +/** + * usb_remove_phy - remove the OTG PHY + * @x: the USB OTG PHY to be removed; + * + * This reverts the effects of usb_add_phy + */ +void usb_remove_phy(struct usb_phy *x) +{ + unsigned long flags; + + spin_lock_irqsave(&phy_lock, flags); + if (x) + list_del(&x->head); + spin_unlock_irqrestore(&phy_lock, flags); +} +EXPORT_SYMBOL(usb_remove_phy); + const char *otg_state_string(enum usb_otg_state state) { switch (state) { diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index 01022c891e26..25a09fabbe35 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -633,7 +633,7 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev) kfree(twl); return err; } - usb_add_phy(&twl->phy); + usb_add_phy(&twl->phy, USB_PHY_TYPE_USB2); platform_set_drvdata(pdev, twl); if (device_create_file(&pdev->dev, &dev_attr_vbus)) diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index a8be20878eb9..dbee00aea755 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -443,7 +443,7 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) kfree(twl); return err; } - usb_add_phy(&twl->phy); + usb_add_phy(&twl->phy, USB_PHY_TYPE_USB2); platform_set_drvdata(pdev, twl); if (device_create_file(&pdev->dev, &dev_attr_vbus)) diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 0e739c810525..1def65fb57d0 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -43,6 +43,13 @@ enum usb_phy_events { USB_EVENT_ENUMERATED, /* gadget driver enumerated */ }; +/* associate a type with PHY */ +enum usb_phy_type { + USB_PHY_TYPE_UNDEFINED, + USB_PHY_TYPE_USB2, + USB_PHY_TYPE_USB3, +}; + struct usb_phy; /* for transceivers connected thru an ULPI interface, the user must @@ -89,6 +96,7 @@ struct usb_phy { const char *label; unsigned int flags; + enum usb_phy_type type; enum usb_otg_state state; enum usb_phy_events last_event; @@ -105,6 +113,9 @@ struct usb_phy { u16 port_status; u16 port_change; + /* to support controllers that have multiple transceivers */ + struct list_head head; + /* initialize/shutdown the OTG controller */ int (*init)(struct usb_phy *x); void (*shutdown)(struct usb_phy *x); @@ -121,7 +132,8 @@ struct usb_phy { /* for board-specific init logic */ -extern int usb_add_phy(struct usb_phy *); +extern int usb_add_phy(struct usb_phy *, enum usb_phy_type type); +extern void usb_remove_phy(struct usb_phy *); #if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE)) /* sometimes transceivers are accessed only through e.g. ULPI */ @@ -172,11 +184,11 @@ usb_phy_shutdown(struct usb_phy *x) /* for usb host and peripheral controller drivers */ #ifdef CONFIG_USB_OTG_UTILS -extern struct usb_phy *usb_get_phy(void); +extern struct usb_phy *usb_get_phy(enum usb_phy_type type); extern void usb_put_phy(struct usb_phy *); extern const char *otg_state_string(enum usb_otg_state state); #else -static inline struct usb_phy *usb_get_phy(void) +static inline struct usb_phy *usb_get_phy(enum usb_phy_type type) { return NULL; } @@ -276,4 +288,15 @@ usb_unregister_notifier(struct usb_phy *x, struct notifier_block *nb) /* for OTG controller drivers (and maybe other stuff) */ extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num); +static inline const char *usb_phy_type_string(enum usb_phy_type type) +{ + switch (type) { + case USB_PHY_TYPE_USB2: + return "USB2 PHY"; + case USB_PHY_TYPE_USB3: + return "USB3 PHY"; + default: + return "UNKNOWN PHY TYPE"; + } +} #endif /* __LINUX_USB_OTG_H */ -- cgit v1.2.3 From 410219dcd2ba8d8b4bcfa9c232f35bf505bc021a Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 22 Jun 2012 17:02:47 +0530 Subject: usb: otg: utils: devres: Add API's to associate a device with the phy Used devres API's to associate the phy with a device so that on driver detach, release function is invoked on the devres data(usb_phy) and devres data(usb_phy) is released. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/usb/otg/otg.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/usb/otg.h | 13 +++++++++++ 2 files changed, 75 insertions(+) (limited to 'include') diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index a23065820ead..0fa4d8c1b1e8 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -34,6 +35,48 @@ static struct usb_phy *__usb_find_phy(struct list_head *list, return ERR_PTR(-ENODEV); } +static void devm_usb_phy_release(struct device *dev, void *res) +{ + struct usb_phy *phy = *(struct usb_phy **)res; + + usb_put_phy(phy); +} + +static int devm_usb_phy_match(struct device *dev, void *res, void *match_data) +{ + return res == match_data; +} + +/** + * devm_usb_get_phy - find the USB PHY + * @dev - device that requests this phy + * @type - the type of the phy the controller requires + * + * Gets the phy using usb_get_phy(), and associates a device with it using + * devres. On driver detach, release function is invoked on the devres data, + * then, devres data is freed. + * + * For use by USB host and peripheral drivers. + */ +struct usb_phy *devm_usb_get_phy(struct device *dev, enum usb_phy_type type) +{ + struct usb_phy **ptr, *phy; + + ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return NULL; + + phy = usb_get_phy(type); + if (phy) { + *ptr = phy; + devres_add(dev, ptr); + } else + devres_free(ptr); + + return phy; +} +EXPORT_SYMBOL(devm_usb_get_phy); + /** * usb_get_phy - find the USB PHY * @type - the type of the phy the controller requires @@ -66,6 +109,25 @@ struct usb_phy *usb_get_phy(enum usb_phy_type type) } EXPORT_SYMBOL(usb_get_phy); +/** + * devm_usb_put_phy - release the USB PHY + * @dev - device that wants to release this phy + * @phy - the phy returned by devm_usb_get_phy() + * + * destroys the devres associated with this phy and invokes usb_put_phy + * to release the phy. + * + * For use by USB host and peripheral drivers. + */ +void devm_usb_put_phy(struct device *dev, struct usb_phy *phy) +{ + int r; + + r = devres_destroy(dev, devm_usb_phy_release, devm_usb_phy_match, phy); + dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n"); +} +EXPORT_SYMBOL(devm_usb_put_phy); + /** * usb_put_phy - release the USB PHY * @x: the phy returned by usb_get_phy() diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 1def65fb57d0..0cb2ec2e50c0 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -185,7 +185,10 @@ usb_phy_shutdown(struct usb_phy *x) /* for usb host and peripheral controller drivers */ #ifdef CONFIG_USB_OTG_UTILS extern struct usb_phy *usb_get_phy(enum usb_phy_type type); +extern struct usb_phy *devm_usb_get_phy(struct device *dev, + enum usb_phy_type type); extern void usb_put_phy(struct usb_phy *); +extern void devm_usb_put_phy(struct device *dev, struct usb_phy *x); extern const char *otg_state_string(enum usb_otg_state state); #else static inline struct usb_phy *usb_get_phy(enum usb_phy_type type) @@ -193,10 +196,20 @@ static inline struct usb_phy *usb_get_phy(enum usb_phy_type type) return NULL; } +static inline struct usb_phy *devm_usb_get_phy(struct device *dev, + enum usb_phy_type type) +{ + return NULL; +} + static inline void usb_put_phy(struct usb_phy *x) { } +static inline void devm_usb_put_phy(struct device *dev, struct usb_phy *x) +{ +} + static inline const char *otg_state_string(enum usb_otg_state state) { return NULL; -- cgit v1.2.3 From c9721438c009adf8e81d376839ed037c53b9b8d9 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 22 Jun 2012 17:40:52 +0530 Subject: usb: musb: twl: use mailbox API to send VBUS or ID events The atomic notifier from twl4030/twl6030 to notifiy VBUS and ID events, is replaced by a direct call to omap musb blue. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/usb/musb/omap2430.c | 94 +++++++++++++++++++++++++++---------------- drivers/usb/otg/twl4030-usb.c | 46 ++++++++++----------- drivers/usb/otg/twl6030-usb.c | 47 ++++++++++------------ include/linux/usb/musb-omap.h | 30 ++++++++++++++ 4 files changed, 133 insertions(+), 84 deletions(-) create mode 100644 include/linux/usb/musb-omap.h (limited to 'include') diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index f40c8053a291..063687085d1e 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "musb_core.h" #include "omap2430.h" @@ -41,11 +42,13 @@ struct omap2430_glue { struct device *dev; struct platform_device *musb; - u8 xceiv_event; + enum omap_musb_vbus_id_status status; struct work_struct omap_musb_mailbox_work; }; #define glue_to_musb(g) platform_get_drvdata(g->musb) +struct omap2430_glue *_glue; + static struct timer_list musb_idle_timer; static void musb_do_idle(unsigned long _musb) @@ -225,54 +228,58 @@ static inline void omap2430_low_level_init(struct musb *musb) musb_writel(musb->mregs, OTG_FORCESTDBY, l); } -static int musb_otg_notifications(struct notifier_block *nb, - unsigned long event, void *unused) +void omap_musb_mailbox(enum omap_musb_vbus_id_status status) { - struct musb *musb = container_of(nb, struct musb, nb); - struct device *dev = musb->controller; - struct omap2430_glue *glue = dev_get_drvdata(dev->parent); + struct omap2430_glue *glue = _glue; + struct musb *musb = glue_to_musb(glue); - glue->xceiv_event = event; - schedule_work(&glue->omap_musb_mailbox_work); + glue->status = status; + if (!musb) { + dev_err(glue->dev, "musb core is not yet ready\n"); + return; + } - return NOTIFY_OK; + schedule_work(&glue->omap_musb_mailbox_work); } +EXPORT_SYMBOL_GPL(omap_musb_mailbox); -static void omap_musb_mailbox_work(struct work_struct *data_notifier_work) +static void omap_musb_set_mailbox(struct omap2430_glue *glue) { - struct omap2430_glue *glue = container_of(data_notifier_work, - struct omap2430_glue, omap_musb_mailbox_work); struct musb *musb = glue_to_musb(glue); struct device *dev = musb->controller; struct musb_hdrc_platform_data *pdata = dev->platform_data; struct omap_musb_board_data *data = pdata->board_data; - switch (glue->xceiv_event) { - case USB_EVENT_ID: - dev_dbg(musb->controller, "ID GND\n"); + switch (glue->status) { + case OMAP_MUSB_ID_GROUND: + dev_dbg(dev, "ID GND\n"); + musb->xceiv->last_event = USB_EVENT_ID; if (!is_otg_enabled(musb) || musb->gadget_driver) { - pm_runtime_get_sync(musb->controller); + pm_runtime_get_sync(dev); usb_phy_init(musb->xceiv); omap2430_musb_set_vbus(musb, 1); } break; - case USB_EVENT_VBUS: - dev_dbg(musb->controller, "VBUS Connect\n"); + case OMAP_MUSB_VBUS_VALID: + dev_dbg(dev, "VBUS Connect\n"); + musb->xceiv->last_event = USB_EVENT_VBUS; if (musb->gadget_driver) - pm_runtime_get_sync(musb->controller); + pm_runtime_get_sync(dev); usb_phy_init(musb->xceiv); break; - case USB_EVENT_NONE: - dev_dbg(musb->controller, "VBUS Disconnect\n"); + case OMAP_MUSB_ID_FLOAT: + case OMAP_MUSB_VBUS_OFF: + dev_dbg(dev, "VBUS Disconnect\n"); + musb->xceiv->last_event = USB_EVENT_NONE; if (is_otg_enabled(musb) || is_peripheral_enabled(musb)) if (musb->gadget_driver) { - pm_runtime_mark_last_busy(musb->controller); - pm_runtime_put_autosuspend(musb->controller); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); } if (data->interface_type == MUSB_INTERFACE_UTMI) { @@ -282,15 +289,24 @@ static void omap_musb_mailbox_work(struct work_struct *data_notifier_work) usb_phy_shutdown(musb->xceiv); break; default: - dev_dbg(musb->controller, "ID float\n"); + dev_dbg(dev, "ID float\n"); } } + +static void omap_musb_mailbox_work(struct work_struct *mailbox_work) +{ + struct omap2430_glue *glue = container_of(mailbox_work, + struct omap2430_glue, omap_musb_mailbox_work); + omap_musb_set_mailbox(glue); +} + static int omap2430_musb_init(struct musb *musb) { u32 l; int status = 0; struct device *dev = musb->controller; + struct omap2430_glue *glue = dev_get_drvdata(dev->parent); struct musb_hdrc_platform_data *plat = dev->platform_data; struct omap_musb_board_data *data = plat->board_data; @@ -330,14 +346,11 @@ static int omap2430_musb_init(struct musb *musb) musb_readl(musb->mregs, OTG_INTERFSEL), musb_readl(musb->mregs, OTG_SIMENABLE)); - musb->nb.notifier_call = musb_otg_notifications; - status = usb_register_notifier(musb->xceiv, &musb->nb); - - if (status) - dev_dbg(musb->controller, "notification register failed\n"); - setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb); + if (glue->status != OMAP_MUSB_UNKNOWN) + omap_musb_set_mailbox(glue); + pm_runtime_put_noidle(musb->controller); return 0; @@ -350,12 +363,13 @@ static void omap2430_musb_enable(struct musb *musb) u8 devctl; unsigned long timeout = jiffies + msecs_to_jiffies(1000); struct device *dev = musb->controller; + struct omap2430_glue *glue = dev_get_drvdata(dev->parent); struct musb_hdrc_platform_data *pdata = dev->platform_data; struct omap_musb_board_data *data = pdata->board_data; - switch (musb->xceiv->last_event) { + switch (glue->status) { - case USB_EVENT_ID: + case OMAP_MUSB_ID_GROUND: usb_phy_init(musb->xceiv); if (data->interface_type != MUSB_INTERFACE_UTMI) break; @@ -374,7 +388,7 @@ static void omap2430_musb_enable(struct musb *musb) } break; - case USB_EVENT_VBUS: + case OMAP_MUSB_VBUS_VALID: usb_phy_init(musb->xceiv); break; @@ -385,7 +399,10 @@ static void omap2430_musb_enable(struct musb *musb) static void omap2430_musb_disable(struct musb *musb) { - if (musb->xceiv->last_event) + struct device *dev = musb->controller; + struct omap2430_glue *glue = dev_get_drvdata(dev->parent); + + if (glue->status != OMAP_MUSB_UNKNOWN) usb_phy_shutdown(musb->xceiv); } @@ -439,11 +456,18 @@ static int __devinit omap2430_probe(struct platform_device *pdev) glue->dev = &pdev->dev; glue->musb = musb; + glue->status = OMAP_MUSB_UNKNOWN; pdata->platform_ops = &omap2430_ops; platform_set_drvdata(pdev, glue); + /* + * REVISIT if we ever have two instances of the wrapper, we will be + * in big trouble + */ + _glue = glue; + INIT_WORK(&glue->omap_musb_mailbox_work, omap_musb_mailbox_work); ret = platform_device_add_resources(musb, pdev->resource, @@ -552,7 +576,7 @@ static int __init omap2430_init(void) { return platform_driver_register(&omap2430_driver); } -module_init(omap2430_init); +subsys_initcall(omap2430_init); static void __exit omap2430_exit(void) { diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index 25a09fabbe35..a7b809e217ea 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -33,11 +33,11 @@ #include #include #include +#include #include #include #include #include -#include #include /* Register defines */ @@ -159,7 +159,7 @@ struct twl4030_usb { enum twl4030_usb_mode usb_mode; int irq; - u8 linkstat; + enum omap_musb_vbus_id_status linkstat; bool vbus_supplied; u8 asleep; bool irq_enabled; @@ -246,10 +246,11 @@ twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits) /*-------------------------------------------------------------------------*/ -static enum usb_phy_events twl4030_usb_linkstat(struct twl4030_usb *twl) +static enum omap_musb_vbus_id_status + twl4030_usb_linkstat(struct twl4030_usb *twl) { int status; - int linkstat = USB_EVENT_NONE; + enum omap_musb_vbus_id_status linkstat = OMAP_MUSB_UNKNOWN; struct usb_otg *otg = twl->phy.otg; twl->vbus_supplied = false; @@ -273,24 +274,24 @@ static enum usb_phy_events twl4030_usb_linkstat(struct twl4030_usb *twl) twl->vbus_supplied = true; if (status & BIT(2)) - linkstat = USB_EVENT_ID; + linkstat = OMAP_MUSB_ID_GROUND; else - linkstat = USB_EVENT_VBUS; - } else - linkstat = USB_EVENT_NONE; + linkstat = OMAP_MUSB_VBUS_VALID; + } else { + if (twl->linkstat != OMAP_MUSB_UNKNOWN) + linkstat = OMAP_MUSB_VBUS_OFF; + } dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n", status, status, linkstat); - twl->phy.last_event = linkstat; - /* REVISIT this assumes host and peripheral controllers * are registered, and that both are active... */ spin_lock_irq(&twl->lock); twl->linkstat = linkstat; - if (linkstat == USB_EVENT_ID) { + if (linkstat == OMAP_MUSB_ID_GROUND) { otg->default_a = true; twl->phy.state = OTG_STATE_A_IDLE; } else { @@ -501,10 +502,10 @@ static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL); static irqreturn_t twl4030_usb_irq(int irq, void *_twl) { struct twl4030_usb *twl = _twl; - int status; + enum omap_musb_vbus_id_status status; status = twl4030_usb_linkstat(twl); - if (status >= 0) { + if (status > 0) { /* FIXME add a set_power() method so that B-devices can * configure the charger appropriately. It's not always * correct to consume VBUS power, and how much current to @@ -516,13 +517,13 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) * USB_LINK_VBUS state. musb_hdrc won't care until it * starts to handle softconnect right. */ - if (status == USB_EVENT_NONE) + if (status == OMAP_MUSB_VBUS_OFF || + status == OMAP_MUSB_ID_FLOAT) twl4030_phy_suspend(twl, 0); else twl4030_phy_resume(twl); - atomic_notifier_call_chain(&twl->phy.notifier, status, - twl->phy.otg->gadget); + omap_musb_mailbox(twl->linkstat); } sysfs_notify(&twl->dev->kobj, NULL, "vbus"); @@ -531,11 +532,12 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) static void twl4030_usb_phy_init(struct twl4030_usb *twl) { - int status; + enum omap_musb_vbus_id_status status; status = twl4030_usb_linkstat(twl); - if (status >= 0) { - if (status == USB_EVENT_NONE) { + if (status > 0) { + if (status == OMAP_MUSB_VBUS_OFF || + status == OMAP_MUSB_ID_FLOAT) { __twl4030_phy_power(twl, 0); twl->asleep = 1; } else { @@ -543,8 +545,7 @@ static void twl4030_usb_phy_init(struct twl4030_usb *twl) twl->asleep = 0; } - atomic_notifier_call_chain(&twl->phy.notifier, status, - twl->phy.otg->gadget); + omap_musb_mailbox(twl->linkstat); } sysfs_notify(&twl->dev->kobj, NULL, "vbus"); } @@ -613,6 +614,7 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev) twl->usb_mode = pdata->usb_mode; twl->vbus_supplied = false; twl->asleep = 1; + twl->linkstat = OMAP_MUSB_UNKNOWN; twl->phy.dev = twl->dev; twl->phy.label = "twl4030"; @@ -639,8 +641,6 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev) if (device_create_file(&pdev->dev, &dev_attr_vbus)) dev_warn(&pdev->dev, "could not create sysfs file\n"); - ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier); - /* Our job is to use irqs and status from the power module * to keep the transceiver disabled when nothing's connected. * diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index dbee00aea755..6c758836cfb1 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -26,10 +26,10 @@ #include #include #include +#include #include #include #include -#include #include #include @@ -100,7 +100,7 @@ struct twl6030_usb { int irq1; int irq2; - u8 linkstat; + enum omap_musb_vbus_id_status linkstat; u8 asleep; bool irq_enabled; bool vbus_enable; @@ -147,7 +147,7 @@ static int twl6030_phy_init(struct usb_phy *x) dev = twl->dev; pdata = dev->platform_data; - if (twl->linkstat == USB_EVENT_ID) + if (twl->linkstat == OMAP_MUSB_ID_GROUND) pdata->phy_power(twl->dev, 1, 1); else pdata->phy_power(twl->dev, 0, 1); @@ -235,13 +235,13 @@ static ssize_t twl6030_usb_vbus_show(struct device *dev, spin_lock_irqsave(&twl->lock, flags); switch (twl->linkstat) { - case USB_EVENT_VBUS: + case OMAP_MUSB_VBUS_VALID: ret = snprintf(buf, PAGE_SIZE, "vbus\n"); break; - case USB_EVENT_ID: + case OMAP_MUSB_ID_GROUND: ret = snprintf(buf, PAGE_SIZE, "id\n"); break; - case USB_EVENT_NONE: + case OMAP_MUSB_VBUS_OFF: ret = snprintf(buf, PAGE_SIZE, "none\n"); break; default: @@ -257,7 +257,7 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl) { struct twl6030_usb *twl = _twl; struct usb_otg *otg = twl->phy.otg; - int status; + enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN; u8 vbus_state, hw_state; hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); @@ -268,22 +268,20 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl) if (vbus_state & VBUS_DET) { regulator_enable(twl->usb3v3); twl->asleep = 1; - status = USB_EVENT_VBUS; + status = OMAP_MUSB_VBUS_VALID; otg->default_a = false; twl->phy.state = OTG_STATE_B_IDLE; twl->linkstat = status; - twl->phy.last_event = status; - atomic_notifier_call_chain(&twl->phy.notifier, - status, otg->gadget); + omap_musb_mailbox(status); } else { - status = USB_EVENT_NONE; - twl->linkstat = status; - twl->phy.last_event = status; - atomic_notifier_call_chain(&twl->phy.notifier, - status, otg->gadget); - if (twl->asleep) { - regulator_disable(twl->usb3v3); - twl->asleep = 0; + if (twl->linkstat != OMAP_MUSB_UNKNOWN) { + status = OMAP_MUSB_VBUS_OFF; + twl->linkstat = status; + omap_musb_mailbox(status); + if (twl->asleep) { + regulator_disable(twl->usb3v3); + twl->asleep = 0; + } } } } @@ -296,7 +294,7 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl) { struct twl6030_usb *twl = _twl; struct usb_otg *otg = twl->phy.otg; - int status = USB_EVENT_NONE; + enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN; u8 hw_state; hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); @@ -308,13 +306,11 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl) twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_CLR, 0x1); twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_SET, 0x10); - status = USB_EVENT_ID; + status = OMAP_MUSB_ID_GROUND; otg->default_a = true; twl->phy.state = OTG_STATE_A_IDLE; twl->linkstat = status; - twl->phy.last_event = status; - atomic_notifier_call_chain(&twl->phy.notifier, status, - otg->gadget); + omap_musb_mailbox(status); } else { twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_CLR, 0x10); @@ -419,6 +415,7 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) twl->irq1 = platform_get_irq(pdev, 0); twl->irq2 = platform_get_irq(pdev, 1); twl->features = pdata->features; + twl->linkstat = OMAP_MUSB_UNKNOWN; twl->phy.dev = twl->dev; twl->phy.label = "twl6030"; @@ -449,8 +446,6 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) if (device_create_file(&pdev->dev, &dev_attr_vbus)) dev_warn(&pdev->dev, "could not create sysfs file\n"); - ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier); - INIT_WORK(&twl->set_vbus_work, otg_set_vbus_work); twl->irq_enabled = true; diff --git a/include/linux/usb/musb-omap.h b/include/linux/usb/musb-omap.h new file mode 100644 index 000000000000..7774c5986f07 --- /dev/null +++ b/include/linux/usb/musb-omap.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2011-2012 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 __MUSB_OMAP_H__ +#define __MUSB_OMAP_H__ + +enum omap_musb_vbus_id_status { + OMAP_MUSB_UNKNOWN = 0, + OMAP_MUSB_ID_GROUND, + OMAP_MUSB_ID_FLOAT, + OMAP_MUSB_VBUS_VALID, + OMAP_MUSB_VBUS_OFF, +}; + +#if (defined(CONFIG_USB_MUSB_OMAP2PLUS) || \ + defined(CONFIG_USB_MUSB_OMAP2PLUS_MODULE)) +void omap_musb_mailbox(enum omap_musb_vbus_id_status status); +#else +static inline void omap_musb_mailbox(enum omap_musb_vbus_id_status status) +{ +} +#endif + +#endif /* __MUSB_OMAP_H__ */ -- cgit v1.2.3 From 74416e1e07660798379ce10a210bf4fd35b84f9f Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Wed, 30 May 2012 14:18:41 -0600 Subject: driver core: Add iommu_group tracking to struct device IOMMU groups allow IOMMU drivers to represent DMA visibility and isolation of devices. Multiple devices may be grouped together for the purposes of DMA. Placing a pointer on struct device enable easy access for things like streaming DMA programming and drivers like VFIO. Signed-off-by: Alex Williamson Acked-by: Greg Kroah-Hartman Acked-by: Benjamin Herrenschmidt Signed-off-by: Joerg Roedel --- include/linux/device.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/device.h b/include/linux/device.h index 161d96241b1b..d0e4d99405ae 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -36,6 +36,7 @@ struct subsys_private; struct bus_type; struct device_node; struct iommu_ops; +struct iommu_group; struct bus_attribute { struct attribute attr; @@ -687,6 +688,7 @@ struct device { const struct attribute_group **groups; /* optional groups */ void (*release)(struct device *dev); + struct iommu_group *iommu_group; }; /* Get the wakeup routines, which depend on struct device */ -- cgit v1.2.3 From d72e31c9374627068df29da8085ca18c92ae35d3 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Wed, 30 May 2012 14:18:53 -0600 Subject: iommu: IOMMU Groups IOMMU device groups are currently a rather vague associative notion with assembly required by the user or user level driver provider to do anything useful. This patch intends to grow the IOMMU group concept into something a bit more consumable. To do this, we first create an object representing the group, struct iommu_group. This structure is allocated (iommu_group_alloc) and filled (iommu_group_add_device) by the iommu driver. The iommu driver is free to add devices to the group using it's own set of policies. This allows inclusion of devices based on physical hardware or topology limitations of the platform, as well as soft requirements, such as multi-function trust levels or peer-to-peer protection of the interconnects. Each device may only belong to a single iommu group, which is linked from struct device.iommu_group. IOMMU groups are maintained using kobject reference counting, allowing for automatic removal of empty, unreferenced groups. It is the responsibility of the iommu driver to remove devices from the group (iommu_group_remove_device). IOMMU groups also include a userspace representation in sysfs under /sys/kernel/iommu_groups. When allocated, each group is given a dynamically assign ID (int). The ID is managed by the core IOMMU group code to support multiple heterogeneous iommu drivers, which could potentially collide in group naming/numbering. This also keeps group IDs to small, easily managed values. A directory is created under /sys/kernel/iommu_groups for each group. A further subdirectory named "devices" contains links to each device within the group. The iommu_group file in the device's sysfs directory, which formerly contained a group number when read, is now a link to the iommu group. Example: $ ls -l /sys/kernel/iommu_groups/26/devices/ total 0 lrwxrwxrwx. 1 root root 0 Apr 17 12:57 0000:00:1e.0 -> ../../../../devices/pci0000:00/0000:00:1e.0 lrwxrwxrwx. 1 root root 0 Apr 17 12:57 0000:06:0d.0 -> ../../../../devices/pci0000:00/0000:00:1e.0/0000:06:0d.0 lrwxrwxrwx. 1 root root 0 Apr 17 12:57 0000:06:0d.1 -> ../../../../devices/pci0000:00/0000:00:1e.0/0000:06:0d.1 $ ls -l /sys/kernel/iommu_groups/26/devices/*/iommu_group [truncating perms/owner/timestamp] /sys/kernel/iommu_groups/26/devices/0000:00:1e.0/iommu_group -> ../../../kernel/iommu_groups/26 /sys/kernel/iommu_groups/26/devices/0000:06:0d.0/iommu_group -> ../../../../kernel/iommu_groups/26 /sys/kernel/iommu_groups/26/devices/0000:06:0d.1/iommu_group -> ../../../../kernel/iommu_groups/26 Groups also include several exported functions for use by user level driver providers, for example VFIO. These include: iommu_group_get(): Acquires a reference to a group from a device iommu_group_put(): Releases reference iommu_group_for_each_dev(): Iterates over group devices using callback iommu_group_[un]register_notifier(): Allows notification of device add and remove operations relevant to the group iommu_group_id(): Return the group number This patch also extends the IOMMU API to allow attaching groups to domains. This is currently a simple wrapper for iterating through devices within a group, but it's expected that the IOMMU API may eventually make groups a more integral part of domains. Groups intentionally do not try to manage group ownership. A user level driver provider must independently acquire ownership for each device within a group before making use of the group as a whole. This may change in the future if group usage becomes more pervasive across both DMA and IOMMU ops. Groups intentionally do not provide a mechanism for driver locking or otherwise manipulating driver matching/probing of devices within the group. Such interfaces are generic to devices and beyond the scope of IOMMU groups. If implemented, user level providers have ready access via iommu_group_for_each_dev and group notifiers. iommu_device_group() is removed here as it has no users. The replacement is: group = iommu_group_get(dev); id = iommu_group_id(group); iommu_group_put(group); AMD-Vi & Intel VT-d support re-added in following patches. Signed-off-by: Alex Williamson Acked-by: Benjamin Herrenschmidt Signed-off-by: Joerg Roedel --- .../ABI/testing/sysfs-kernel-iommu_groups | 14 + drivers/iommu/amd_iommu.c | 21 - drivers/iommu/intel-iommu.c | 49 -- drivers/iommu/iommu.c | 578 +++++++++++++++++++-- include/linux/iommu.h | 104 +++- 5 files changed, 663 insertions(+), 103 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-kernel-iommu_groups (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-kernel-iommu_groups b/Documentation/ABI/testing/sysfs-kernel-iommu_groups new file mode 100644 index 000000000000..9b31556cfdda --- /dev/null +++ b/Documentation/ABI/testing/sysfs-kernel-iommu_groups @@ -0,0 +1,14 @@ +What: /sys/kernel/iommu_groups/ +Date: May 2012 +KernelVersion: v3.5 +Contact: Alex Williamson +Description: /sys/kernel/iommu_groups/ contains a number of sub- + directories, each representing an IOMMU group. The + name of the sub-directory matches the iommu_group_id() + for the group, which is an integer value. Within each + subdirectory is another directory named "devices" with + links to the sysfs devices contained in this group. + The group directory also optionally contains a "name" + file if the IOMMU driver has chosen to register a more + common name for the group. +Users: diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index a2e418cba0ff..55283d6291c8 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -3227,26 +3227,6 @@ static int amd_iommu_domain_has_cap(struct iommu_domain *domain, return 0; } -static int amd_iommu_device_group(struct device *dev, unsigned int *groupid) -{ - struct iommu_dev_data *dev_data = dev->archdata.iommu; - struct pci_dev *pdev = to_pci_dev(dev); - u16 devid; - - if (!dev_data) - return -ENODEV; - - if (pdev->is_virtfn || !iommu_group_mf) - devid = dev_data->devid; - else - devid = calc_devid(pdev->bus->number, - PCI_DEVFN(PCI_SLOT(pdev->devfn), 0)); - - *groupid = amd_iommu_alias_table[devid]; - - return 0; -} - static struct iommu_ops amd_iommu_ops = { .domain_init = amd_iommu_domain_init, .domain_destroy = amd_iommu_domain_destroy, @@ -3256,7 +3236,6 @@ static struct iommu_ops amd_iommu_ops = { .unmap = amd_iommu_unmap, .iova_to_phys = amd_iommu_iova_to_phys, .domain_has_cap = amd_iommu_domain_has_cap, - .device_group = amd_iommu_device_group, .pgsize_bitmap = AMD_IOMMU_PGSIZES, }; diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index b12af2ff8c54..c62f2df25221 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -4090,54 +4090,6 @@ static int intel_iommu_domain_has_cap(struct iommu_domain *domain, return 0; } -/* - * Group numbers are arbitrary. Device with the same group number - * indicate the iommu cannot differentiate between them. To avoid - * tracking used groups we just use the seg|bus|devfn of the lowest - * level we're able to differentiate devices - */ -static int intel_iommu_device_group(struct device *dev, unsigned int *groupid) -{ - struct pci_dev *pdev = to_pci_dev(dev); - struct pci_dev *bridge; - union { - struct { - u8 devfn; - u8 bus; - u16 segment; - } pci; - u32 group; - } id; - - if (iommu_no_mapping(dev)) - return -ENODEV; - - id.pci.segment = pci_domain_nr(pdev->bus); - id.pci.bus = pdev->bus->number; - id.pci.devfn = pdev->devfn; - - if (!device_to_iommu(id.pci.segment, id.pci.bus, id.pci.devfn)) - return -ENODEV; - - bridge = pci_find_upstream_pcie_bridge(pdev); - if (bridge) { - if (pci_is_pcie(bridge)) { - id.pci.bus = bridge->subordinate->number; - id.pci.devfn = 0; - } else { - id.pci.bus = bridge->bus->number; - id.pci.devfn = bridge->devfn; - } - } - - if (!pdev->is_virtfn && iommu_group_mf) - id.pci.devfn = PCI_DEVFN(PCI_SLOT(id.pci.devfn), 0); - - *groupid = id.group; - - return 0; -} - static struct iommu_ops intel_iommu_ops = { .domain_init = intel_iommu_domain_init, .domain_destroy = intel_iommu_domain_destroy, @@ -4147,7 +4099,6 @@ static struct iommu_ops intel_iommu_ops = { .unmap = intel_iommu_unmap, .iova_to_phys = intel_iommu_iova_to_phys, .domain_has_cap = intel_iommu_domain_has_cap, - .device_group = intel_iommu_device_group, .pgsize_bitmap = INTEL_IOMMU_PGSIZES, }; diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 8b9ded88e6f5..0e928acd7dcf 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -26,60 +26,535 @@ #include #include #include +#include +#include +#include + +static struct kset *iommu_group_kset; +static struct ida iommu_group_ida; +static struct mutex iommu_group_mutex; + +struct iommu_group { + struct kobject kobj; + struct kobject *devices_kobj; + struct list_head devices; + struct mutex mutex; + struct blocking_notifier_head notifier; + void *iommu_data; + void (*iommu_data_release)(void *iommu_data); + char *name; + int id; +}; + +struct iommu_device { + struct list_head list; + struct device *dev; + char *name; +}; + +struct iommu_group_attribute { + struct attribute attr; + ssize_t (*show)(struct iommu_group *group, char *buf); + ssize_t (*store)(struct iommu_group *group, + const char *buf, size_t count); +}; + +#define IOMMU_GROUP_ATTR(_name, _mode, _show, _store) \ +struct iommu_group_attribute iommu_group_attr_##_name = \ + __ATTR(_name, _mode, _show, _store) -static ssize_t show_iommu_group(struct device *dev, - struct device_attribute *attr, char *buf) +#define to_iommu_group_attr(_attr) \ + container_of(_attr, struct iommu_group_attribute, attr) +#define to_iommu_group(_kobj) \ + container_of(_kobj, struct iommu_group, kobj) + +static ssize_t iommu_group_attr_show(struct kobject *kobj, + struct attribute *__attr, char *buf) { - unsigned int groupid; + struct iommu_group_attribute *attr = to_iommu_group_attr(__attr); + struct iommu_group *group = to_iommu_group(kobj); + ssize_t ret = -EIO; - if (iommu_device_group(dev, &groupid)) - return 0; + if (attr->show) + ret = attr->show(group, buf); + return ret; +} + +static ssize_t iommu_group_attr_store(struct kobject *kobj, + struct attribute *__attr, + const char *buf, size_t count) +{ + struct iommu_group_attribute *attr = to_iommu_group_attr(__attr); + struct iommu_group *group = to_iommu_group(kobj); + ssize_t ret = -EIO; - return sprintf(buf, "%u", groupid); + if (attr->store) + ret = attr->store(group, buf, count); + return ret; } -static DEVICE_ATTR(iommu_group, S_IRUGO, show_iommu_group, NULL); -static int add_iommu_group(struct device *dev, void *data) +static const struct sysfs_ops iommu_group_sysfs_ops = { + .show = iommu_group_attr_show, + .store = iommu_group_attr_store, +}; + +static int iommu_group_create_file(struct iommu_group *group, + struct iommu_group_attribute *attr) +{ + return sysfs_create_file(&group->kobj, &attr->attr); +} + +static void iommu_group_remove_file(struct iommu_group *group, + struct iommu_group_attribute *attr) +{ + sysfs_remove_file(&group->kobj, &attr->attr); +} + +static ssize_t iommu_group_show_name(struct iommu_group *group, char *buf) +{ + return sprintf(buf, "%s\n", group->name); +} + +static IOMMU_GROUP_ATTR(name, S_IRUGO, iommu_group_show_name, NULL); + +static void iommu_group_release(struct kobject *kobj) +{ + struct iommu_group *group = to_iommu_group(kobj); + + if (group->iommu_data_release) + group->iommu_data_release(group->iommu_data); + + mutex_lock(&iommu_group_mutex); + ida_remove(&iommu_group_ida, group->id); + mutex_unlock(&iommu_group_mutex); + + kfree(group->name); + kfree(group); +} + +static struct kobj_type iommu_group_ktype = { + .sysfs_ops = &iommu_group_sysfs_ops, + .release = iommu_group_release, +}; + +/** + * iommu_group_alloc - Allocate a new group + * @name: Optional name to associate with group, visible in sysfs + * + * This function is called by an iommu driver to allocate a new iommu + * group. The iommu group represents the minimum granularity of the iommu. + * Upon successful return, the caller holds a reference to the supplied + * group in order to hold the group until devices are added. Use + * iommu_group_put() to release this extra reference count, allowing the + * group to be automatically reclaimed once it has no devices or external + * references. + */ +struct iommu_group *iommu_group_alloc(void) { - unsigned int groupid; + struct iommu_group *group; + int ret; + + group = kzalloc(sizeof(*group), GFP_KERNEL); + if (!group) + return ERR_PTR(-ENOMEM); + + group->kobj.kset = iommu_group_kset; + mutex_init(&group->mutex); + INIT_LIST_HEAD(&group->devices); + BLOCKING_INIT_NOTIFIER_HEAD(&group->notifier); + + mutex_lock(&iommu_group_mutex); + +again: + if (unlikely(0 == ida_pre_get(&iommu_group_ida, GFP_KERNEL))) { + kfree(group); + mutex_unlock(&iommu_group_mutex); + return ERR_PTR(-ENOMEM); + } + + if (-EAGAIN == ida_get_new(&iommu_group_ida, &group->id)) + goto again; + + mutex_unlock(&iommu_group_mutex); - if (iommu_device_group(dev, &groupid) == 0) - return device_create_file(dev, &dev_attr_iommu_group); + ret = kobject_init_and_add(&group->kobj, &iommu_group_ktype, + NULL, "%d", group->id); + if (ret) { + mutex_lock(&iommu_group_mutex); + ida_remove(&iommu_group_ida, group->id); + mutex_unlock(&iommu_group_mutex); + kfree(group); + return ERR_PTR(ret); + } + + group->devices_kobj = kobject_create_and_add("devices", &group->kobj); + if (!group->devices_kobj) { + kobject_put(&group->kobj); /* triggers .release & free */ + return ERR_PTR(-ENOMEM); + } + + /* + * The devices_kobj holds a reference on the group kobject, so + * as long as that exists so will the group. We can therefore + * use the devices_kobj for reference counting. + */ + kobject_put(&group->kobj); + + return group; +} +EXPORT_SYMBOL_GPL(iommu_group_alloc); + +/** + * iommu_group_get_iommudata - retrieve iommu_data registered for a group + * @group: the group + * + * iommu drivers can store data in the group for use when doing iommu + * operations. This function provides a way to retrieve it. Caller + * should hold a group reference. + */ +void *iommu_group_get_iommudata(struct iommu_group *group) +{ + return group->iommu_data; +} +EXPORT_SYMBOL_GPL(iommu_group_get_iommudata); + +/** + * iommu_group_set_iommudata - set iommu_data for a group + * @group: the group + * @iommu_data: new data + * @release: release function for iommu_data + * + * iommu drivers can store data in the group for use when doing iommu + * operations. This function provides a way to set the data after + * the group has been allocated. Caller should hold a group reference. + */ +void iommu_group_set_iommudata(struct iommu_group *group, void *iommu_data, + void (*release)(void *iommu_data)) +{ + group->iommu_data = iommu_data; + group->iommu_data_release = release; +} +EXPORT_SYMBOL_GPL(iommu_group_set_iommudata); + +/** + * iommu_group_set_name - set name for a group + * @group: the group + * @name: name + * + * Allow iommu driver to set a name for a group. When set it will + * appear in a name attribute file under the group in sysfs. + */ +int iommu_group_set_name(struct iommu_group *group, const char *name) +{ + int ret; + + if (group->name) { + iommu_group_remove_file(group, &iommu_group_attr_name); + kfree(group->name); + group->name = NULL; + if (!name) + return 0; + } + + group->name = kstrdup(name, GFP_KERNEL); + if (!group->name) + return -ENOMEM; + + ret = iommu_group_create_file(group, &iommu_group_attr_name); + if (ret) { + kfree(group->name); + group->name = NULL; + return ret; + } return 0; } +EXPORT_SYMBOL_GPL(iommu_group_set_name); -static int remove_iommu_group(struct device *dev) +/** + * iommu_group_add_device - add a device to an iommu group + * @group: the group into which to add the device (reference should be held) + * @dev: the device + * + * This function is called by an iommu driver to add a device into a + * group. Adding a device increments the group reference count. + */ +int iommu_group_add_device(struct iommu_group *group, struct device *dev) { - unsigned int groupid; + int ret, i = 0; + struct iommu_device *device; + + device = kzalloc(sizeof(*device), GFP_KERNEL); + if (!device) + return -ENOMEM; + + device->dev = dev; - if (iommu_device_group(dev, &groupid) == 0) - device_remove_file(dev, &dev_attr_iommu_group); + ret = sysfs_create_link(&dev->kobj, &group->kobj, "iommu_group"); + if (ret) { + kfree(device); + return ret; + } + + device->name = kasprintf(GFP_KERNEL, "%s", kobject_name(&dev->kobj)); +rename: + if (!device->name) { + sysfs_remove_link(&dev->kobj, "iommu_group"); + kfree(device); + return -ENOMEM; + } + ret = sysfs_create_link_nowarn(group->devices_kobj, + &dev->kobj, device->name); + if (ret) { + kfree(device->name); + if (ret == -EEXIST && i >= 0) { + /* + * Account for the slim chance of collision + * and append an instance to the name. + */ + device->name = kasprintf(GFP_KERNEL, "%s.%d", + kobject_name(&dev->kobj), i++); + goto rename; + } + + sysfs_remove_link(&dev->kobj, "iommu_group"); + kfree(device); + return ret; + } + + kobject_get(group->devices_kobj); + + dev->iommu_group = group; + + mutex_lock(&group->mutex); + list_add_tail(&device->list, &group->devices); + mutex_unlock(&group->mutex); + + /* Notify any listeners about change to group. */ + blocking_notifier_call_chain(&group->notifier, + IOMMU_GROUP_NOTIFY_ADD_DEVICE, dev); return 0; } +EXPORT_SYMBOL_GPL(iommu_group_add_device); -static int iommu_device_notifier(struct notifier_block *nb, - unsigned long action, void *data) +/** + * iommu_group_remove_device - remove a device from it's current group + * @dev: device to be removed + * + * This function is called by an iommu driver to remove the device from + * it's current group. This decrements the iommu group reference count. + */ +void iommu_group_remove_device(struct device *dev) +{ + struct iommu_group *group = dev->iommu_group; + struct iommu_device *tmp_device, *device = NULL; + + /* Pre-notify listeners that a device is being removed. */ + blocking_notifier_call_chain(&group->notifier, + IOMMU_GROUP_NOTIFY_DEL_DEVICE, dev); + + mutex_lock(&group->mutex); + list_for_each_entry(tmp_device, &group->devices, list) { + if (tmp_device->dev == dev) { + device = tmp_device; + list_del(&device->list); + break; + } + } + mutex_unlock(&group->mutex); + + if (!device) + return; + + sysfs_remove_link(group->devices_kobj, device->name); + sysfs_remove_link(&dev->kobj, "iommu_group"); + + kfree(device->name); + kfree(device); + dev->iommu_group = NULL; + kobject_put(group->devices_kobj); +} +EXPORT_SYMBOL_GPL(iommu_group_remove_device); + +/** + * iommu_group_for_each_dev - iterate over each device in the group + * @group: the group + * @data: caller opaque data to be passed to callback function + * @fn: caller supplied callback function + * + * This function is called by group users to iterate over group devices. + * Callers should hold a reference count to the group during callback. + * The group->mutex is held across callbacks, which will block calls to + * iommu_group_add/remove_device. + */ +int iommu_group_for_each_dev(struct iommu_group *group, void *data, + int (*fn)(struct device *, void *)) +{ + struct iommu_device *device; + int ret = 0; + + mutex_lock(&group->mutex); + list_for_each_entry(device, &group->devices, list) { + ret = fn(device->dev, data); + if (ret) + break; + } + mutex_unlock(&group->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(iommu_group_for_each_dev); + +/** + * iommu_group_get - Return the group for a device and increment reference + * @dev: get the group that this device belongs to + * + * This function is called by iommu drivers and users to get the group + * for the specified device. If found, the group is returned and the group + * reference in incremented, else NULL. + */ +struct iommu_group *iommu_group_get(struct device *dev) +{ + struct iommu_group *group = dev->iommu_group; + + if (group) + kobject_get(group->devices_kobj); + + return group; +} +EXPORT_SYMBOL_GPL(iommu_group_get); + +/** + * iommu_group_put - Decrement group reference + * @group: the group to use + * + * This function is called by iommu drivers and users to release the + * iommu group. Once the reference count is zero, the group is released. + */ +void iommu_group_put(struct iommu_group *group) +{ + if (group) + kobject_put(group->devices_kobj); +} +EXPORT_SYMBOL_GPL(iommu_group_put); + +/** + * iommu_group_register_notifier - Register a notifier for group changes + * @group: the group to watch + * @nb: notifier block to signal + * + * This function allows iommu group users to track changes in a group. + * See include/linux/iommu.h for actions sent via this notifier. Caller + * should hold a reference to the group throughout notifier registration. + */ +int iommu_group_register_notifier(struct iommu_group *group, + struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&group->notifier, nb); +} +EXPORT_SYMBOL_GPL(iommu_group_register_notifier); + +/** + * iommu_group_unregister_notifier - Unregister a notifier + * @group: the group to watch + * @nb: notifier block to signal + * + * Unregister a previously registered group notifier block. + */ +int iommu_group_unregister_notifier(struct iommu_group *group, + struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&group->notifier, nb); +} +EXPORT_SYMBOL_GPL(iommu_group_unregister_notifier); + +/** + * iommu_group_id - Return ID for a group + * @group: the group to ID + * + * Return the unique ID for the group matching the sysfs group number. + */ +int iommu_group_id(struct iommu_group *group) +{ + return group->id; +} +EXPORT_SYMBOL_GPL(iommu_group_id); + +static int add_iommu_group(struct device *dev, void *data) +{ + struct iommu_ops *ops = data; + + if (!ops->add_device) + return -ENODEV; + + WARN_ON(dev->iommu_group); + + ops->add_device(dev); + + return 0; +} + +static int iommu_bus_notifier(struct notifier_block *nb, + unsigned long action, void *data) { struct device *dev = data; + struct iommu_ops *ops = dev->bus->iommu_ops; + struct iommu_group *group; + unsigned long group_action = 0; + + /* + * ADD/DEL call into iommu driver ops if provided, which may + * result in ADD/DEL notifiers to group->notifier + */ + if (action == BUS_NOTIFY_ADD_DEVICE) { + if (ops->add_device) + return ops->add_device(dev); + } else if (action == BUS_NOTIFY_DEL_DEVICE) { + if (ops->remove_device && dev->iommu_group) { + ops->remove_device(dev); + return 0; + } + } - if (action == BUS_NOTIFY_ADD_DEVICE) - return add_iommu_group(dev, NULL); - else if (action == BUS_NOTIFY_DEL_DEVICE) - return remove_iommu_group(dev); + /* + * Remaining BUS_NOTIFYs get filtered and republished to the + * group, if anyone is listening + */ + group = iommu_group_get(dev); + if (!group) + return 0; + switch (action) { + case BUS_NOTIFY_BIND_DRIVER: + group_action = IOMMU_GROUP_NOTIFY_BIND_DRIVER; + break; + case BUS_NOTIFY_BOUND_DRIVER: + group_action = IOMMU_GROUP_NOTIFY_BOUND_DRIVER; + break; + case BUS_NOTIFY_UNBIND_DRIVER: + group_action = IOMMU_GROUP_NOTIFY_UNBIND_DRIVER; + break; + case BUS_NOTIFY_UNBOUND_DRIVER: + group_action = IOMMU_GROUP_NOTIFY_UNBOUND_DRIVER; + break; + } + + if (group_action) + blocking_notifier_call_chain(&group->notifier, + group_action, dev); + + iommu_group_put(group); return 0; } -static struct notifier_block iommu_device_nb = { - .notifier_call = iommu_device_notifier, +static struct notifier_block iommu_bus_nb = { + .notifier_call = iommu_bus_notifier, }; static void iommu_bus_init(struct bus_type *bus, struct iommu_ops *ops) { - bus_register_notifier(bus, &iommu_device_nb); - bus_for_each_dev(bus, NULL, NULL, add_iommu_group); + bus_register_notifier(bus, &iommu_bus_nb); + bus_for_each_dev(bus, NULL, ops, add_iommu_group); } /** @@ -192,6 +667,45 @@ void iommu_detach_device(struct iommu_domain *domain, struct device *dev) } EXPORT_SYMBOL_GPL(iommu_detach_device); +/* + * IOMMU groups are really the natrual working unit of the IOMMU, but + * the IOMMU API works on domains and devices. Bridge that gap by + * iterating over the devices in a group. Ideally we'd have a single + * device which represents the requestor ID of the group, but we also + * allow IOMMU drivers to create policy defined minimum sets, where + * the physical hardware may be able to distiguish members, but we + * wish to group them at a higher level (ex. untrusted multi-function + * PCI devices). Thus we attach each device. + */ +static int iommu_group_do_attach_device(struct device *dev, void *data) +{ + struct iommu_domain *domain = data; + + return iommu_attach_device(domain, dev); +} + +int iommu_attach_group(struct iommu_domain *domain, struct iommu_group *group) +{ + return iommu_group_for_each_dev(group, domain, + iommu_group_do_attach_device); +} +EXPORT_SYMBOL_GPL(iommu_attach_group); + +static int iommu_group_do_detach_device(struct device *dev, void *data) +{ + struct iommu_domain *domain = data; + + iommu_detach_device(domain, dev); + + return 0; +} + +void iommu_detach_group(struct iommu_domain *domain, struct iommu_group *group) +{ + iommu_group_for_each_dev(group, domain, iommu_group_do_detach_device); +} +EXPORT_SYMBOL_GPL(iommu_detach_group); + phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, unsigned long iova) { @@ -336,11 +850,15 @@ size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) } EXPORT_SYMBOL_GPL(iommu_unmap); -int iommu_device_group(struct device *dev, unsigned int *groupid) +static int __init iommu_init(void) { - if (iommu_present(dev->bus) && dev->bus->iommu_ops->device_group) - return dev->bus->iommu_ops->device_group(dev, groupid); + iommu_group_kset = kset_create_and_add("iommu_groups", + NULL, kernel_kobj); + ida_init(&iommu_group_ida); + mutex_init(&iommu_group_mutex); - return -ENODEV; + BUG_ON(!iommu_group_kset); + + return 0; } -EXPORT_SYMBOL_GPL(iommu_device_group); +subsys_initcall(iommu_init); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 450293f6d68b..a71df92be992 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -26,6 +26,7 @@ #define IOMMU_CACHE (4) /* DMA cache coherency */ struct iommu_ops; +struct iommu_group; struct bus_type; struct device; struct iommu_domain; @@ -60,6 +61,8 @@ struct iommu_domain { * @iova_to_phys: translate iova to physical address * @domain_has_cap: domain capabilities query * @commit: commit iommu domain + * @add_device: add device to iommu grouping + * @remove_device: remove device from iommu grouping * @pgsize_bitmap: bitmap of supported page sizes */ struct iommu_ops { @@ -75,10 +78,18 @@ struct iommu_ops { unsigned long iova); int (*domain_has_cap)(struct iommu_domain *domain, unsigned long cap); - int (*device_group)(struct device *dev, unsigned int *groupid); + int (*add_device)(struct device *dev); + void (*remove_device)(struct device *dev); unsigned long pgsize_bitmap; }; +#define IOMMU_GROUP_NOTIFY_ADD_DEVICE 1 /* Device added */ +#define IOMMU_GROUP_NOTIFY_DEL_DEVICE 2 /* Pre Device removed */ +#define IOMMU_GROUP_NOTIFY_BIND_DRIVER 3 /* Pre Driver bind */ +#define IOMMU_GROUP_NOTIFY_BOUND_DRIVER 4 /* Post Driver bind */ +#define IOMMU_GROUP_NOTIFY_UNBIND_DRIVER 5 /* Pre Driver unbind */ +#define IOMMU_GROUP_NOTIFY_UNBOUND_DRIVER 6 /* Post Driver unbind */ + extern int bus_set_iommu(struct bus_type *bus, struct iommu_ops *ops); extern bool iommu_present(struct bus_type *bus); extern struct iommu_domain *iommu_domain_alloc(struct bus_type *bus); @@ -97,7 +108,29 @@ extern int iommu_domain_has_cap(struct iommu_domain *domain, unsigned long cap); extern void iommu_set_fault_handler(struct iommu_domain *domain, iommu_fault_handler_t handler, void *token); -extern int iommu_device_group(struct device *dev, unsigned int *groupid); + +extern int iommu_attach_group(struct iommu_domain *domain, + struct iommu_group *group); +extern void iommu_detach_group(struct iommu_domain *domain, + struct iommu_group *group); +extern struct iommu_group *iommu_group_alloc(void); +extern void *iommu_group_get_iommudata(struct iommu_group *group); +extern void iommu_group_set_iommudata(struct iommu_group *group, + void *iommu_data, + void (*release)(void *iommu_data)); +extern int iommu_group_set_name(struct iommu_group *group, const char *name); +extern int iommu_group_add_device(struct iommu_group *group, + struct device *dev); +extern void iommu_group_remove_device(struct device *dev); +extern int iommu_group_for_each_dev(struct iommu_group *group, void *data, + int (*fn)(struct device *, void *)); +extern struct iommu_group *iommu_group_get(struct device *dev); +extern void iommu_group_put(struct iommu_group *group); +extern int iommu_group_register_notifier(struct iommu_group *group, + struct notifier_block *nb); +extern int iommu_group_unregister_notifier(struct iommu_group *group, + struct notifier_block *nb); +extern int iommu_group_id(struct iommu_group *group); /** * report_iommu_fault() - report about an IOMMU fault to the IOMMU framework @@ -142,6 +175,7 @@ static inline int report_iommu_fault(struct iommu_domain *domain, #else /* CONFIG_IOMMU_API */ struct iommu_ops {}; +struct iommu_group {}; static inline bool iommu_present(struct bus_type *bus) { @@ -197,11 +231,75 @@ static inline void iommu_set_fault_handler(struct iommu_domain *domain, { } -static inline int iommu_device_group(struct device *dev, unsigned int *groupid) +int iommu_attach_group(struct iommu_domain *domain, struct iommu_group *group) +{ + return -ENODEV; +} + +void iommu_detach_group(struct iommu_domain *domain, struct iommu_group *group) +{ +} + +struct iommu_group *iommu_group_alloc(void) +{ + return ERR_PTR(-ENODEV); +} + +void *iommu_group_get_iommudata(struct iommu_group *group) +{ + return NULL; +} + +void iommu_group_set_iommudata(struct iommu_group *group, void *iommu_data, + void (*release)(void *iommu_data)) +{ +} + +int iommu_group_set_name(struct iommu_group *group, const char *name) +{ + return -ENODEV; +} + +int iommu_group_add_device(struct iommu_group *group, struct device *dev) +{ + return -ENODEV; +} + +void iommu_group_remove_device(struct device *dev) +{ +} + +int iommu_group_for_each_dev(struct iommu_group *group, void *data, + int (*fn)(struct device *, void *)) +{ + return -ENODEV; +} + +struct iommu_group *iommu_group_get(struct device *dev) +{ + return NULL; +} + +void iommu_group_put(struct iommu_group *group) +{ +} + +int iommu_group_register_notifier(struct iommu_group *group, + struct notifier_block *nb) { return -ENODEV; } +int iommu_group_unregister_notifier(struct iommu_group *group, + struct notifier_block *nb) +{ + return 0; +} + +int iommu_group_id(struct iommu_group *group) +{ + return -ENODEV; +} #endif /* CONFIG_IOMMU_API */ #endif /* __LINUX_IOMMU_H */ -- cgit v1.2.3 From 4e0ee78f2af96676c9dca898c13250f62c513058 Mon Sep 17 00:00:00 2001 From: Hiroshi Doyu Date: Mon, 25 Jun 2012 14:23:54 +0300 Subject: iommu: Add DMA window parser, of_get_dma_window() This code was based on: "arch/microblaze/kernel/prom_parse.c" "arch/powerpc/kernel/prom_parse.c" Can replace "of_parse_dma_window()" in the above. This supports different formats flexibly. "prefix" can be configured if any. "busno" and "index" are optionally specified. Set NULL and 0 if not used. Signed-off-by: Hiroshi DOYU Acked-by: Stephen Warren Signed-off-by: Joerg Roedel --- drivers/iommu/Kconfig | 4 +++ drivers/iommu/Makefile | 1 + drivers/iommu/of_iommu.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/of_iommu.h | 21 +++++++++++ 4 files changed, 116 insertions(+) create mode 100644 drivers/iommu/of_iommu.c create mode 100644 include/linux/of_iommu.h (limited to 'include') diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index 340893727538..4826af62a9de 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -13,6 +13,10 @@ menuconfig IOMMU_SUPPORT if IOMMU_SUPPORT +config OF_IOMMU + def_bool y + depends on OF + # MSM IOMMU support config MSM_IOMMU bool "MSM IOMMU Support" diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile index 76e54ef796de..14a4d5fc94fa 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_IOMMU_API) += iommu.o +obj-$(CONFIG_OF_IOMMU) += of_iommu.o obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c new file mode 100644 index 000000000000..ee249bc959f8 --- /dev/null +++ b/drivers/iommu/of_iommu.c @@ -0,0 +1,90 @@ +/* + * OF helpers for IOMMU + * + * Copyright (c) 2012, 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include + +/** + * of_get_dma_window - Parse *dma-window property and returns 0 if found. + * + * @dn: device node + * @prefix: prefix for property name if any + * @index: index to start to parse + * @busno: Returns busno if supported. Otherwise pass NULL + * @addr: Returns address that DMA starts + * @size: Returns the range that DMA can handle + * + * This supports different formats flexibly. "prefix" can be + * configured if any. "busno" and "index" are optionally + * specified. Set 0(or NULL) if not used. + */ +int of_get_dma_window(struct device_node *dn, const char *prefix, int index, + unsigned long *busno, dma_addr_t *addr, size_t *size) +{ + const __be32 *dma_window, *end; + int bytes, cur_index = 0; + char propname[NAME_MAX], addrname[NAME_MAX], sizename[NAME_MAX]; + + if (!dn || !addr || !size) + return -EINVAL; + + if (!prefix) + prefix = ""; + + snprintf(propname, sizeof(propname), "%sdma-window", prefix); + snprintf(addrname, sizeof(addrname), "%s#dma-address-cells", prefix); + snprintf(sizename, sizeof(sizename), "%s#dma-size-cells", prefix); + + dma_window = of_get_property(dn, propname, &bytes); + if (!dma_window) + return -ENODEV; + end = dma_window + bytes / sizeof(*dma_window); + + while (dma_window < end) { + u32 cells; + const void *prop; + + /* busno is one cell if supported */ + if (busno) + *busno = be32_to_cpup(dma_window++); + + prop = of_get_property(dn, addrname, NULL); + if (!prop) + prop = of_get_property(dn, "#address-cells", NULL); + + cells = prop ? be32_to_cpup(prop) : of_n_addr_cells(dn); + if (!cells) + return -EINVAL; + *addr = of_read_number(dma_window, cells); + dma_window += cells; + + prop = of_get_property(dn, sizename, NULL); + cells = prop ? be32_to_cpup(prop) : of_n_size_cells(dn); + if (!cells) + return -EINVAL; + *size = of_read_number(dma_window, cells); + dma_window += cells; + + if (cur_index++ == index) + break; + } + return 0; +} +EXPORT_SYMBOL_GPL(of_get_dma_window); diff --git a/include/linux/of_iommu.h b/include/linux/of_iommu.h new file mode 100644 index 000000000000..51a560f34bca --- /dev/null +++ b/include/linux/of_iommu.h @@ -0,0 +1,21 @@ +#ifndef __OF_IOMMU_H +#define __OF_IOMMU_H + +#ifdef CONFIG_OF_IOMMU + +extern int of_get_dma_window(struct device_node *dn, const char *prefix, + int index, unsigned long *busno, dma_addr_t *addr, + size_t *size); + +#else + +static inline int of_get_dma_window(struct device_node *dn, const char *prefix, + int index, unsigned long *busno, dma_addr_t *addr, + size_t *size) +{ + return -EINVAL; +} + +#endif /* CONFIG_OF_IOMMU */ + +#endif /* __OF_IOMMU_H */ -- cgit v1.2.3 From 2bd237b8a45eb107d6304496fbd5b4a34471fbd3 Mon Sep 17 00:00:00 2001 From: Benoît Thébaudeau Date: Mon, 18 Jun 2012 15:02:44 -0300 Subject: [media] media: gpio-ir-recv: add map name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make it possible for gpio-ir-recv users to choose a map name. Cc: Ravi Kumar V Signed-off-by: Benoît Thébaudeau Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/gpio-ir-recv.c | 2 +- include/media/gpio-ir-recv.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c index b41e13c393cd..15e346e0bcd0 100644 --- a/drivers/media/rc/gpio-ir-recv.c +++ b/drivers/media/rc/gpio-ir-recv.c @@ -93,7 +93,7 @@ static int __devinit gpio_ir_recv_probe(struct platform_device *pdev) rcdev->input_id.version = 0x0100; rcdev->dev.parent = &pdev->dev; rcdev->driver_name = GPIO_IR_DRIVER_NAME; - rcdev->map_name = RC_MAP_EMPTY; + rcdev->map_name = pdata->map_name ?: RC_MAP_EMPTY; gpio_dev->rcdev = rcdev; gpio_dev->gpio_nr = pdata->gpio_nr; diff --git a/include/media/gpio-ir-recv.h b/include/media/gpio-ir-recv.h index 67797bf5d432..91546f35b7e1 100644 --- a/include/media/gpio-ir-recv.h +++ b/include/media/gpio-ir-recv.h @@ -16,6 +16,7 @@ struct gpio_ir_recv_platform_data { int gpio_nr; bool active_low; + const char *map_name; }; #endif /* __GPIO_IR_RECV_H__ */ -- cgit v1.2.3 From 023b515e5b304122f3802abaa68d1da46fdf48b8 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 19 Jun 2012 09:54:54 +0200 Subject: uas: task mgmt & error handling Add task management support, wind up in abort and device reset error handlers. Cancel all in-flight urbs in bus reset handler. Signed-off-by: Gerd Hoffmann Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/uas.c | 160 +++++++++++++++++++++++++++++++++++++--------- include/linux/usb/uas.h | 40 ++++++++++++ 2 files changed, 171 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 875829a56183..638cd64f9610 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -43,7 +43,8 @@ struct uas_dev_info { struct usb_device *udev; struct usb_anchor sense_urbs; struct usb_anchor data_urbs; - int qdepth; + int qdepth, resetting; + struct response_ui response; unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe; unsigned use_streams:1; unsigned uas_sense_old:1; @@ -68,6 +69,7 @@ enum { struct uas_cmd_info { unsigned int state; unsigned int stream; + unsigned int aborted; struct urb *cmd_urb; struct urb *data_in_urb; struct urb *data_out_urb; @@ -222,16 +224,24 @@ static void uas_stat_cmplt(struct urb *urb) return; } + if (devinfo->resetting) { + usb_free_urb(urb); + return; + } + tag = be16_to_cpup(&iu->tag) - 1; if (tag == 0) cmnd = devinfo->cmnd; else cmnd = scsi_host_find_tag(shost, tag - 1); if (!cmnd) { - usb_free_urb(urb); - return; + if (iu->iu_id != IU_ID_RESPONSE) { + usb_free_urb(urb); + return; + } + } else { + cmdinfo = (void *)&cmnd->SCp; } - cmdinfo = (void *)&cmnd->SCp; switch (iu->iu_id) { case IU_ID_STATUS: @@ -260,6 +270,10 @@ static void uas_stat_cmplt(struct urb *urb) case IU_ID_WRITE_READY: uas_xfer_data(urb, cmnd, SUBMIT_DATA_OUT_URB); break; + case IU_ID_RESPONSE: + /* store results for uas_eh_task_mgmt() */ + memcpy(&devinfo->response, iu, sizeof(devinfo->response)); + break; default: scmd_printk(KERN_ERR, cmnd, "Bogus IU (%d) received on status pipe\n", iu->iu_id); @@ -287,6 +301,9 @@ static void uas_data_cmplt(struct urb *urb) } else { sdb->resid = sdb->length - urb->actual_length; } + if (cmdinfo->aborted) { + return; + } uas_try_complete(cmnd, __func__); } @@ -377,6 +394,51 @@ static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp, return NULL; } +static int uas_submit_task_urb(struct scsi_cmnd *cmnd, gfp_t gfp, + u8 function, u16 stream_id) +{ + struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; + struct usb_device *udev = devinfo->udev; + struct urb *urb = usb_alloc_urb(0, gfp); + struct task_mgmt_iu *iu; + int err = -ENOMEM; + + if (!urb) + goto err; + + iu = kzalloc(sizeof(*iu), gfp); + if (!iu) + goto err; + + iu->iu_id = IU_ID_TASK_MGMT; + iu->tag = cpu_to_be16(stream_id); + int_to_scsilun(cmnd->device->lun, &iu->lun); + + iu->function = function; + switch (function) { + case TMF_ABORT_TASK: + if (blk_rq_tagged(cmnd->request)) + iu->task_tag = cpu_to_be16(cmnd->request->tag + 2); + else + iu->task_tag = cpu_to_be16(1); + break; + } + + usb_fill_bulk_urb(urb, udev, devinfo->cmd_pipe, iu, sizeof(*iu), + usb_free_urb, NULL); + urb->transfer_flags |= URB_FREE_BUFFER; + + err = usb_submit_urb(urb, gfp); + if (err) + goto err; + + return 0; + +err: + usb_free_urb(urb); + return err; +} + /* * Why should I request the Status IU before sending the Command IU? Spec * says to, but also says the device may receive them in any order. Seems @@ -502,6 +564,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, cmdinfo->state = SUBMIT_STATUS_URB | ALLOC_CMD_URB | SUBMIT_CMD_URB; + cmdinfo->aborted = 0; switch (cmnd->sc_data_direction) { case DMA_FROM_DEVICE: @@ -537,34 +600,66 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, static DEF_SCSI_QCMD(uas_queuecommand) -static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) +static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, + const char *fname, u8 function) { - uas_log_cmd_state(cmnd, __func__); + struct Scsi_Host *shost = cmnd->device->host; + struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; + u16 tag = 9999; /* FIXME */ -/* XXX: Send ABORT TASK Task Management command */ - return FAILED; + memset(&devinfo->response, 0, sizeof(devinfo->response)); + if (uas_submit_sense_urb(shost, GFP_NOIO, tag)) { + shost_printk(KERN_INFO, shost, + "%s: %s: submit sense urb failed\n", + __func__, fname); + return FAILED; + } + if (uas_submit_task_urb(cmnd, GFP_NOIO, function, tag)) { + shost_printk(KERN_INFO, shost, + "%s: %s: submit task mgmt urb failed\n", + __func__, fname); + return FAILED; + } + if (0 == usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 3000)) { + shost_printk(KERN_INFO, shost, + "%s: %s timed out\n", __func__, fname); + return FAILED; + } + if (be16_to_cpu(devinfo->response.tag) != tag) { + shost_printk(KERN_INFO, shost, + "%s: %s failed (wrong tag %d/%d)\n", __func__, + fname, be16_to_cpu(devinfo->response.tag), tag); + return FAILED; + } + if (devinfo->response.response_code != RC_TMF_COMPLETE) { + shost_printk(KERN_INFO, shost, + "%s: %s failed (rc 0x%x)\n", __func__, + fname, devinfo->response.response_code); + return FAILED; + } + return SUCCESS; } -static int uas_eh_device_reset_handler(struct scsi_cmnd *cmnd) +static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) { - struct scsi_device *sdev = cmnd->device; - sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__, - cmnd->request->tag); + struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; + int ret; -/* XXX: Send LOGICAL UNIT RESET Task Management command */ - return FAILED; + uas_log_cmd_state(cmnd, __func__); + cmdinfo->aborted = 1; + ret = uas_eh_task_mgmt(cmnd, "ABORT TASK", TMF_ABORT_TASK); + if (cmdinfo->state & DATA_IN_URB_INFLIGHT) + usb_kill_urb(cmdinfo->data_in_urb); + if (cmdinfo->state & DATA_OUT_URB_INFLIGHT) + usb_kill_urb(cmdinfo->data_out_urb); + return ret; } -static int uas_eh_target_reset_handler(struct scsi_cmnd *cmnd) +static int uas_eh_device_reset_handler(struct scsi_cmnd *cmnd) { - struct scsi_device *sdev = cmnd->device; - sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__, - cmnd->request->tag); - -/* XXX: Can we reset just the one USB interface? - * Would calling usb_set_interface() have the right effect? - */ - return FAILED; + sdev_printk(KERN_INFO, cmnd->device, "%s\n", __func__); + return uas_eh_task_mgmt(cmnd, "LOGICAL UNIT RESET", + TMF_LOGICAL_UNIT_RESET); } static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) @@ -572,14 +667,21 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) struct scsi_device *sdev = cmnd->device; struct uas_dev_info *devinfo = sdev->hostdata; struct usb_device *udev = devinfo->udev; + int err; - sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__, - cmnd->request->tag); + devinfo->resetting = 1; + usb_kill_anchored_urbs(&devinfo->sense_urbs); + usb_kill_anchored_urbs(&devinfo->data_urbs); + err = usb_reset_device(udev); + devinfo->resetting = 0; - if (usb_reset_device(udev)) - return SUCCESS; + if (err) { + shost_printk(KERN_INFO, sdev->host, "%s FAILED\n", __func__); + return FAILED; + } - return FAILED; + shost_printk(KERN_INFO, sdev->host, "%s success\n", __func__); + return SUCCESS; } static int uas_slave_alloc(struct scsi_device *sdev) @@ -604,7 +706,6 @@ static struct scsi_host_template uas_host_template = { .slave_configure = uas_slave_configure, .eh_abort_handler = uas_eh_abort_handler, .eh_device_reset_handler = uas_eh_device_reset_handler, - .eh_target_reset_handler = uas_eh_target_reset_handler, .eh_bus_reset_handler = uas_eh_bus_reset_handler, .can_queue = 65536, /* Is there a limit on the _host_ ? */ .this_id = -1, @@ -766,6 +867,7 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) devinfo->intf = intf; devinfo->udev = udev; + devinfo->resetting = 0; init_usb_anchor(&devinfo->sense_urbs); init_usb_anchor(&devinfo->data_urbs); uas_configure_endpoints(devinfo); diff --git a/include/linux/usb/uas.h b/include/linux/usb/uas.h index 9a988e413694..5499ab5c94bd 100644 --- a/include/linux/usb/uas.h +++ b/include/linux/usb/uas.h @@ -20,6 +20,28 @@ enum { IU_ID_WRITE_READY = 0x07, }; +enum { + TMF_ABORT_TASK = 0x01, + TMF_ABORT_TASK_SET = 0x02, + TMF_CLEAR_TASK_SET = 0x04, + TMF_LOGICAL_UNIT_RESET = 0x08, + TMF_I_T_NEXUS_RESET = 0x10, + TMF_CLEAR_ACA = 0x40, + TMF_QUERY_TASK = 0x80, + TMF_QUERY_TASK_SET = 0x81, + TMF_QUERY_ASYNC_EVENT = 0x82, +}; + +enum { + RC_TMF_COMPLETE = 0x00, + RC_INVALID_INFO_UNIT = 0x02, + RC_TMF_NOT_SUPPORTED = 0x04, + RC_TMF_FAILED = 0x05, + RC_TMF_SUCCEEDED = 0x08, + RC_INCORRECT_LUN = 0x09, + RC_OVERLAPPED_TAG = 0x0a, +}; + struct command_iu { __u8 iu_id; __u8 rsvd1; @@ -32,6 +54,16 @@ struct command_iu { __u8 cdb[16]; /* XXX: Overflow-checking tools may misunderstand */ }; +struct task_mgmt_iu { + __u8 iu_id; + __u8 rsvd1; + __be16 tag; + __u8 function; + __u8 rsvd2; + __be16 task_tag; + struct scsi_lun lun; +}; + /* * Also used for the Read Ready and Write Ready IUs since they have the * same first four bytes @@ -47,6 +79,14 @@ struct sense_iu { __u8 sense[SCSI_SENSE_BUFFERSIZE]; }; +struct response_ui { + __u8 iu_id; + __u8 rsvd1; + __be16 tag; + __be16 add_response_info; + __u8 response_code; +}; + struct usb_pipe_usage_descriptor { __u8 bLength; __u8 bDescriptorType; -- cgit v1.2.3 From 2b6f2c3520124e8bad4bffa71f5b98e602b9cf03 Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Mon, 25 Jun 2012 21:30:57 -0600 Subject: PCI: pull pcibios_setup() up into core Currently, all of the architectures implement their own pcibios_setup() routine. Most of the implementations do nothing so this patch introduces a generic (__weak) routine in the core that can be used by all architectures as a default. If necessary, it can be overridden by architecture-specific code. Signed-off-by: Myron Stowe Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 12 ++++++++++++ include/linux/pci.h | 1 + 2 files changed, 13 insertions(+) (limited to 'include') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c01..c87d518acace 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2665,6 +2665,18 @@ static void __pci_set_master(struct pci_dev *dev, bool enable) dev->is_busmaster = enable; } +/** + * pcibios_setup - process "pci=" kernel boot arguments + * @str: string used to pass in "pci=" kernel boot arguments + * + * Process kernel boot arguments. This is the default implementation. + * Architecture specific implementations can override this as necessary. + */ +char * __weak __init pcibios_setup(char *str) +{ + return str; +} + /** * pcibios_set_master - enable PCI bus-mastering for device dev * @dev: the PCI device to enable diff --git a/include/linux/pci.h b/include/linux/pci.h index d8c379dba6ad..f91143e86f85 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -642,6 +642,7 @@ extern int no_pci_devices(void); void pcibios_fixup_bus(struct pci_bus *); int __must_check pcibios_enable_device(struct pci_dev *, int mask); +/* Architecture specific versions may override this (weak) */ char *pcibios_setup(char *str); /* Used only when drivers/pci/setup.c is used */ -- cgit v1.2.3 From a051661ca6d134c18599498b185b667859d4339b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 26 Jun 2012 15:05:44 -0700 Subject: blkcg: implement per-blkg request allocation Currently, request_queue has one request_list to allocate requests from regardless of blkcg of the IO being issued. When the unified request pool is used up, cfq proportional IO limits become meaningless - whoever grabs the next request being freed wins the race regardless of the configured weights. This can be easily demonstrated by creating a blkio cgroup w/ very low weight, put a program which can issue a lot of random direct IOs there and running a sequential IO from a different cgroup. As soon as the request pool is used up, the sequential IO bandwidth crashes. This patch implements per-blkg request_list. Each blkg has its own request_list and any IO allocates its request from the matching blkg making blkcgs completely isolated in terms of request allocation. * Root blkcg uses the request_list embedded in each request_queue, which was renamed to @q->root_rl from @q->rq. While making blkcg rl handling a bit harier, this enables avoiding most overhead for root blkcg. * Queue fullness is properly per request_list but bdi isn't blkcg aware yet, so congestion state currently just follows the root blkcg. As writeback isn't aware of blkcg yet, this works okay for async congestion but readahead may get the wrong signals. It's better than blkcg completely collapsing with shared request_list but needs to be improved with future changes. * After this change, each block cgroup gets a full request pool making resource consumption of each cgroup higher. This makes allowing non-root users to create cgroups less desirable; however, note that allowing non-root users to directly manage cgroups is already severely broken regardless of this patch - each block cgroup consumes kernel memory and skews IO weight (IO weights are not hierarchical). v2: queue-sysfs.txt updated and patch description udpated as suggested by Vivek. v3: blk_get_rl() wasn't checking error return from blkg_lookup_create() and may cause oops on lookup failure. Fix it by falling back to root_rl on blkg lookup failures. This problem was spotted by Rakesh Iyer . v4: Updated to accomodate 458f27a982 "block: Avoid missed wakeup in request waitqueue". blk_drain_queue() now wakes up waiters on all blkg->rl on the target queue. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal Cc: Wu Fengguang Signed-off-by: Jens Axboe --- Documentation/block/queue-sysfs.txt | 7 +++ block/blk-cgroup.c | 51 ++++++++++++++++-- block/blk-cgroup.h | 102 ++++++++++++++++++++++++++++++++++++ block/blk-core.c | 42 +++++++++++---- block/blk-sysfs.c | 32 ++++++----- include/linux/blkdev.h | 12 +++-- 6 files changed, 216 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/Documentation/block/queue-sysfs.txt b/Documentation/block/queue-sysfs.txt index d8147b336c35..6518a55273e7 100644 --- a/Documentation/block/queue-sysfs.txt +++ b/Documentation/block/queue-sysfs.txt @@ -38,6 +38,13 @@ read or write requests. Note that the total allocated number may be twice this amount, since it applies only to reads or writes (not the accumulated sum). +To avoid priority inversion through request starvation, a request +queue maintains a separate request pool per each cgroup when +CONFIG_BLK_CGROUP is enabled, and this parameter applies to each such +per-block-cgroup request pool. IOW, if there are N block cgroups, +each request queue may have upto N request pools, each independently +regulated by nr_requests. + read_ahead_kb (RW) ------------------ Maximum number of kilobytes to read-ahead for filesystems on this block diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 63b31ebae6e2..f3b44a65fc7a 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -63,6 +63,7 @@ static void blkg_free(struct blkcg_gq *blkg) kfree(pd); } + blk_exit_rl(&blkg->rl); kfree(blkg); } @@ -90,6 +91,13 @@ static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q, blkg->blkcg = blkcg; blkg->refcnt = 1; + /* root blkg uses @q->root_rl, init rl only for !root blkgs */ + if (blkcg != &blkcg_root) { + if (blk_init_rl(&blkg->rl, q, gfp_mask)) + goto err_free; + blkg->rl.blkg = blkg; + } + for (i = 0; i < BLKCG_MAX_POLS; i++) { struct blkcg_policy *pol = blkcg_policy[i]; struct blkg_policy_data *pd; @@ -99,10 +107,8 @@ static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q, /* alloc per-policy data and attach it to blkg */ pd = kzalloc_node(pol->pd_size, gfp_mask, q->node); - if (!pd) { - blkg_free(blkg); - return NULL; - } + if (!pd) + goto err_free; blkg->pd[i] = pd; pd->blkg = blkg; @@ -113,6 +119,10 @@ static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q, } return blkg; + +err_free: + blkg_free(blkg); + return NULL; } static struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg, @@ -300,6 +310,38 @@ void __blkg_release(struct blkcg_gq *blkg) } EXPORT_SYMBOL_GPL(__blkg_release); +/* + * The next function used by blk_queue_for_each_rl(). It's a bit tricky + * because the root blkg uses @q->root_rl instead of its own rl. + */ +struct request_list *__blk_queue_next_rl(struct request_list *rl, + struct request_queue *q) +{ + struct list_head *ent; + struct blkcg_gq *blkg; + + /* + * Determine the current blkg list_head. The first entry is + * root_rl which is off @q->blkg_list and mapped to the head. + */ + if (rl == &q->root_rl) { + ent = &q->blkg_list; + } else { + blkg = container_of(rl, struct blkcg_gq, rl); + ent = &blkg->q_node; + } + + /* walk to the next list_head, skip root blkcg */ + ent = ent->next; + if (ent == &q->root_blkg->q_node) + ent = ent->next; + if (ent == &q->blkg_list) + return NULL; + + blkg = container_of(ent, struct blkcg_gq, q_node); + return &blkg->rl; +} + static int blkcg_reset_stats(struct cgroup *cgroup, struct cftype *cftype, u64 val) { @@ -750,6 +792,7 @@ int blkcg_activate_policy(struct request_queue *q, goto out_unlock; } q->root_blkg = blkg; + q->root_rl.blkg = blkg; list_for_each_entry(blkg, &q->blkg_list, q_node) cnt++; diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index e74cce1fbac9..24597309e23d 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -17,6 +17,7 @@ #include #include #include +#include /* Max limits for throttle policy */ #define THROTL_IOPS_MAX UINT_MAX @@ -93,6 +94,8 @@ struct blkcg_gq { struct list_head q_node; struct hlist_node blkcg_node; struct blkcg *blkcg; + /* request allocation list for this blkcg-q pair */ + struct request_list rl; /* reference count */ int refcnt; @@ -250,6 +253,95 @@ static inline void blkg_put(struct blkcg_gq *blkg) __blkg_release(blkg); } +/** + * blk_get_rl - get request_list to use + * @q: request_queue of interest + * @bio: bio which will be attached to the allocated request (may be %NULL) + * + * The caller wants to allocate a request from @q to use for @bio. Find + * the request_list to use and obtain a reference on it. Should be called + * under queue_lock. This function is guaranteed to return non-%NULL + * request_list. + */ +static inline struct request_list *blk_get_rl(struct request_queue *q, + struct bio *bio) +{ + struct blkcg *blkcg; + struct blkcg_gq *blkg; + + rcu_read_lock(); + + blkcg = bio_blkcg(bio); + + /* bypass blkg lookup and use @q->root_rl directly for root */ + if (blkcg == &blkcg_root) + goto root_rl; + + /* + * Try to use blkg->rl. blkg lookup may fail under memory pressure + * or if either the blkcg or queue is going away. Fall back to + * root_rl in such cases. + */ + blkg = blkg_lookup_create(blkcg, q); + if (unlikely(IS_ERR(blkg))) + goto root_rl; + + blkg_get(blkg); + rcu_read_unlock(); + return &blkg->rl; +root_rl: + rcu_read_unlock(); + return &q->root_rl; +} + +/** + * blk_put_rl - put request_list + * @rl: request_list to put + * + * Put the reference acquired by blk_get_rl(). Should be called under + * queue_lock. + */ +static inline void blk_put_rl(struct request_list *rl) +{ + /* root_rl may not have blkg set */ + if (rl->blkg && rl->blkg->blkcg != &blkcg_root) + blkg_put(rl->blkg); +} + +/** + * blk_rq_set_rl - associate a request with a request_list + * @rq: request of interest + * @rl: target request_list + * + * Associate @rq with @rl so that accounting and freeing can know the + * request_list @rq came from. + */ +static inline void blk_rq_set_rl(struct request *rq, struct request_list *rl) +{ + rq->rl = rl; +} + +/** + * blk_rq_rl - return the request_list a request came from + * @rq: request of interest + * + * Return the request_list @rq is allocated from. + */ +static inline struct request_list *blk_rq_rl(struct request *rq) +{ + return rq->rl; +} + +struct request_list *__blk_queue_next_rl(struct request_list *rl, + struct request_queue *q); +/** + * blk_queue_for_each_rl - iterate through all request_lists of a request_queue + * + * Should be used under queue_lock. + */ +#define blk_queue_for_each_rl(rl, q) \ + for ((rl) = &(q)->root_rl; (rl); (rl) = __blk_queue_next_rl((rl), (q))) + /** * blkg_stat_add - add a value to a blkg_stat * @stat: target blkg_stat @@ -392,6 +484,7 @@ static inline void blkcg_deactivate_policy(struct request_queue *q, 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, struct blkcg_policy *pol) { return NULL; } static inline struct blkcg_gq *pd_to_blkg(struct blkg_policy_data *pd) { return NULL; } @@ -399,5 +492,14 @@ static inline char *blkg_path(struct blkcg_gq *blkg) { return NULL; } static inline void blkg_get(struct blkcg_gq *blkg) { } static inline void blkg_put(struct blkcg_gq *blkg) { } +static inline struct request_list *blk_get_rl(struct request_queue *q, + struct bio *bio) { return &q->root_rl; } +static inline void blk_put_rl(struct request_list *rl) { } +static inline void blk_rq_set_rl(struct request *rq, struct request_list *rl) { } +static inline struct request_list *blk_rq_rl(struct request *rq) { return &rq->q->root_rl; } + +#define blk_queue_for_each_rl(rl, q) \ + for ((rl) = &(q)->root_rl; (rl); (rl) = NULL) + #endif /* CONFIG_BLK_CGROUP */ #endif /* _BLK_CGROUP_H */ diff --git a/block/blk-core.c b/block/blk-core.c index f392a2edf462..dd134d834d58 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -416,9 +416,14 @@ void blk_drain_queue(struct request_queue *q, bool drain_all) * left with hung waiters. We need to wake up those waiters. */ if (q->request_fn) { + struct request_list *rl; + spin_lock_irq(q->queue_lock); - for (i = 0; i < ARRAY_SIZE(q->rq.wait); i++) - wake_up_all(&q->rq.wait[i]); + + blk_queue_for_each_rl(rl, q) + for (i = 0; i < ARRAY_SIZE(rl->wait); i++) + wake_up_all(&rl->wait[i]); + spin_unlock_irq(q->queue_lock); } } @@ -685,7 +690,7 @@ blk_init_allocated_queue(struct request_queue *q, request_fn_proc *rfn, if (!q) return NULL; - if (blk_init_rl(&q->rq, q, GFP_KERNEL)) + if (blk_init_rl(&q->root_rl, q, GFP_KERNEL)) return NULL; q->request_fn = rfn; @@ -776,7 +781,12 @@ static void __freed_request(struct request_list *rl, int sync) { struct request_queue *q = rl->q; - if (rl->count[sync] < queue_congestion_off_threshold(q)) + /* + * bdi isn't aware of blkcg yet. As all async IOs end up root + * blkcg anyway, just use root blkcg state. + */ + if (rl == &q->root_rl && + rl->count[sync] < queue_congestion_off_threshold(q)) blk_clear_queue_congested(q, sync); if (rl->count[sync] + 1 <= q->nr_requests) { @@ -897,7 +907,12 @@ static struct request *__get_request(struct request_list *rl, int rw_flags, } } } - blk_set_queue_congested(q, is_sync); + /* + * bdi isn't aware of blkcg yet. As all async IOs end up + * root blkcg anyway, just use root blkcg state. + */ + if (rl == &q->root_rl) + blk_set_queue_congested(q, is_sync); } /* @@ -939,6 +954,7 @@ static struct request *__get_request(struct request_list *rl, int rw_flags, goto fail_alloc; blk_rq_init(q, rq); + blk_rq_set_rl(rq, rl); rq->cmd_flags = rw_flags | REQ_ALLOCED; /* init elvpriv */ @@ -1032,15 +1048,19 @@ static struct request *get_request(struct request_queue *q, int rw_flags, { const bool is_sync = rw_is_sync(rw_flags) != 0; DEFINE_WAIT(wait); - struct request_list *rl = &q->rq; + struct request_list *rl; struct request *rq; + + rl = blk_get_rl(q, bio); /* transferred to @rq on success */ retry: - rq = __get_request(&q->rq, rw_flags, bio, gfp_mask); + rq = __get_request(rl, rw_flags, bio, gfp_mask); if (rq) return rq; - if (!(gfp_mask & __GFP_WAIT) || unlikely(blk_queue_dead(q))) + if (!(gfp_mask & __GFP_WAIT) || unlikely(blk_queue_dead(q))) { + blk_put_rl(rl); return NULL; + } /* wait on @rl and retry */ prepare_to_wait_exclusive(&rl->wait[is_sync], &wait, @@ -1231,12 +1251,14 @@ void __blk_put_request(struct request_queue *q, struct request *req) */ if (req->cmd_flags & REQ_ALLOCED) { unsigned int flags = req->cmd_flags; + struct request_list *rl = blk_rq_rl(req); BUG_ON(!list_empty(&req->queuelist)); BUG_ON(!hlist_unhashed(&req->hash)); - blk_free_request(&q->rq, req); - freed_request(&q->rq, flags); + blk_free_request(rl, req); + freed_request(rl, flags); + blk_put_rl(rl); } } EXPORT_SYMBOL_GPL(__blk_put_request); diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 234ce7c082fa..9628b291f960 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -40,7 +40,7 @@ static ssize_t queue_requests_show(struct request_queue *q, char *page) static ssize_t queue_requests_store(struct request_queue *q, const char *page, size_t count) { - struct request_list *rl = &q->rq; + struct request_list *rl; unsigned long nr; int ret; @@ -55,6 +55,9 @@ queue_requests_store(struct request_queue *q, const char *page, size_t count) q->nr_requests = nr; blk_queue_congestion_threshold(q); + /* congestion isn't cgroup aware and follows root blkcg for now */ + rl = &q->root_rl; + if (rl->count[BLK_RW_SYNC] >= queue_congestion_on_threshold(q)) blk_set_queue_congested(q, BLK_RW_SYNC); else if (rl->count[BLK_RW_SYNC] < queue_congestion_off_threshold(q)) @@ -65,19 +68,22 @@ queue_requests_store(struct request_queue *q, const char *page, size_t count) else if (rl->count[BLK_RW_ASYNC] < queue_congestion_off_threshold(q)) blk_clear_queue_congested(q, BLK_RW_ASYNC); - if (rl->count[BLK_RW_SYNC] >= q->nr_requests) { - blk_set_rl_full(rl, BLK_RW_SYNC); - } else { - blk_clear_rl_full(rl, BLK_RW_SYNC); - wake_up(&rl->wait[BLK_RW_SYNC]); + blk_queue_for_each_rl(rl, q) { + if (rl->count[BLK_RW_SYNC] >= q->nr_requests) { + blk_set_rl_full(rl, BLK_RW_SYNC); + } else { + blk_clear_rl_full(rl, BLK_RW_SYNC); + wake_up(&rl->wait[BLK_RW_SYNC]); + } + + if (rl->count[BLK_RW_ASYNC] >= q->nr_requests) { + blk_set_rl_full(rl, BLK_RW_ASYNC); + } else { + blk_clear_rl_full(rl, BLK_RW_ASYNC); + wake_up(&rl->wait[BLK_RW_ASYNC]); + } } - if (rl->count[BLK_RW_ASYNC] >= q->nr_requests) { - blk_set_rl_full(rl, BLK_RW_ASYNC); - } else { - blk_clear_rl_full(rl, BLK_RW_ASYNC); - wake_up(&rl->wait[BLK_RW_ASYNC]); - } spin_unlock_irq(q->queue_lock); return ret; } @@ -488,7 +494,7 @@ static void blk_release_queue(struct kobject *kobj) elevator_exit(q->elevator); } - blk_exit_rl(&q->rq); + blk_exit_rl(&q->root_rl); if (q->queue_tags) __blk_queue_free_tags(q); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index f2385ee7c7b2..3816ce8a08fc 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -51,7 +51,9 @@ typedef void (rq_end_io_fn)(struct request *, int); struct request_list { struct request_queue *q; /* the queue this rl belongs to */ - +#ifdef CONFIG_BLK_CGROUP + struct blkcg_gq *blkg; /* blkg this request pool belongs to */ +#endif /* * count[], starved[], and wait[] are indexed by * BLK_RW_SYNC/BLK_RW_ASYNC @@ -143,6 +145,7 @@ struct request { struct hd_struct *part; unsigned long start_time; #ifdef CONFIG_BLK_CGROUP + struct request_list *rl; /* rl this rq is alloced from */ unsigned long long start_time_ns; unsigned long long io_start_time_ns; /* when passed to hardware */ #endif @@ -291,9 +294,12 @@ struct request_queue { int nr_rqs_elvpriv; /* # allocated rqs w/ elvpriv */ /* - * the queue request freelist, one for reads and one for writes + * If blkcg is not used, @q->root_rl serves all requests. If blkcg + * is used, root blkg allocates from @q->root_rl and all other + * blkgs from their own blkg->rl. Which one to use should be + * determined using bio_request_list(). */ - struct request_list rq; + struct request_list root_rl; request_fn_proc *request_fn; make_request_fn *make_request_fn; -- cgit v1.2.3 From a46af4ebf9ffec35eea0390e89935197b833dc61 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 25 Jun 2012 12:19:03 -0400 Subject: USB: EHCI: define extension registers like normal ones This patch (as1562) cleans up the definitions of the EHCI extended registers to be consistent with the definitions of the standard registers. This makes the code look a lot nicer, with no functional change. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 15 +++++---------- drivers/usb/host/ehci-hub.c | 26 ++++++++------------------ include/linux/usb/ehci_def.h | 28 +++++++++++++++++++++------- 3 files changed, 34 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 800be38c78b4..c49fc1e7895d 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -203,11 +203,9 @@ static int handshake (struct ehci_hcd *ehci, void __iomem *ptr, /* check TDI/ARC silicon is in host mode */ static int tdi_in_host_mode (struct ehci_hcd *ehci) { - u32 __iomem *reg_ptr; u32 tmp; - reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + USBMODE); - tmp = ehci_readl(ehci, reg_ptr); + tmp = ehci_readl(ehci, &ehci->regs->usbmode); return (tmp & 3) == USBMODE_CM_HC; } @@ -303,11 +301,9 @@ static int handshake_on_error_set_halt(struct ehci_hcd *ehci, void __iomem *ptr, /* put TDI/ARC silicon into EHCI mode */ static void tdi_reset (struct ehci_hcd *ehci) { - u32 __iomem *reg_ptr; u32 tmp; - reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + USBMODE); - tmp = ehci_readl(ehci, reg_ptr); + tmp = ehci_readl(ehci, &ehci->regs->usbmode); tmp |= USBMODE_CM_HC; /* The default byte access to MMR space is LE after * controller reset. Set the required endian mode @@ -315,7 +311,7 @@ static void tdi_reset (struct ehci_hcd *ehci) */ if (ehci_big_endian_mmio(ehci)) tmp |= USBMODE_BE; - ehci_writel(ehci, tmp, reg_ptr); + ehci_writel(ehci, tmp, &ehci->regs->usbmode); } /* reset a non-running (STS_HALT == 1) controller */ @@ -339,9 +335,8 @@ static int ehci_reset (struct ehci_hcd *ehci) if (ehci->has_hostpc) { ehci_writel(ehci, USBMODE_EX_HC | USBMODE_EX_VBPS, - (u32 __iomem *)(((u8 *)ehci->regs) + USBMODE_EX)); - ehci_writel(ehci, TXFIFO_DEFAULT, - (u32 __iomem *)(((u8 *)ehci->regs) + TXFILLTUNING)); + &ehci->regs->usbmode_ex); + ehci_writel(ehci, TXFIFO_DEFAULT, &ehci->regs->txfill_tuning); } if (retval) return retval; diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index dd5eef6af6df..db05e358677a 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -149,10 +149,8 @@ static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, if (ehci->has_hostpc) { port = HCS_N_PORTS(ehci->hcs_params); while (port--) { - u32 __iomem *hostpc_reg; + u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port]; - hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs - + HOSTPC0 + 4 * port); temp = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, temp & ~HOSTPC_PHCD, hostpc_reg); } @@ -185,10 +183,8 @@ static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, if (ehci->has_hostpc) { port = HCS_N_PORTS(ehci->hcs_params); while (port--) { - u32 __iomem *hostpc_reg; + u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port]; - hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs - + HOSTPC0 + 4 * port); temp = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, temp | HOSTPC_PHCD, hostpc_reg); } @@ -285,11 +281,9 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) port = HCS_N_PORTS(ehci->hcs_params); while (port--) { - u32 __iomem *hostpc_reg; + u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port]; u32 t3; - hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs - + HOSTPC0 + 4 * port); t3 = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg); t3 = ehci_readl(ehci, hostpc_reg); @@ -388,10 +382,9 @@ static int ehci_bus_resume (struct usb_hcd *hcd) i = HCS_N_PORTS(ehci->hcs_params); while (i--) { if (test_bit(i, &ehci->bus_suspended)) { - u32 __iomem *hostpc_reg; + u32 __iomem *hostpc_reg = + &ehci->regs->hostpc[i]; - hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs - + HOSTPC0 + 4 * i); temp = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, temp & ~HOSTPC_PHCD, hostpc_reg); @@ -667,7 +660,7 @@ static int ehci_hub_control ( int ports = HCS_N_PORTS (ehci->hcs_params); u32 __iomem *status_reg = &ehci->regs->port_status[ (wIndex & 0xff) - 1]; - u32 __iomem *hostpc_reg = NULL; + u32 __iomem *hostpc_reg = &ehci->regs->hostpc[(wIndex & 0xff) - 1]; u32 temp, temp1, status; unsigned long flags; int retval = 0; @@ -680,9 +673,6 @@ static int ehci_hub_control ( * power, "this is the one", etc. EHCI spec supports this. */ - if (ehci->has_hostpc) - hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs - + HOSTPC0 + 4 * ((wIndex & 0xff) - 1)); spin_lock_irqsave (&ehci->lock, flags); switch (typeReq) { case ClearHubFeature: @@ -734,7 +724,7 @@ static int ehci_hub_control ( goto error; /* clear phy low-power mode before resume */ - if (hostpc_reg) { + if (ehci->has_hostpc) { temp1 = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, temp1 & ~HOSTPC_PHCD, hostpc_reg); @@ -984,7 +974,7 @@ static int ehci_hub_control ( temp &= ~PORT_WKCONN_E; temp |= PORT_WKDISC_E | PORT_WKOC_E; ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); - if (hostpc_reg) { + if (ehci->has_hostpc) { spin_unlock_irqrestore(&ehci->lock, flags); msleep(5);/* 5ms for HCD enter low pwr mode */ spin_lock_irqsave(&ehci->lock, flags); diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h index 7cc95ee3606b..de4b9ed5d5dd 100644 --- a/include/linux/usb/ehci_def.h +++ b/include/linux/usb/ehci_def.h @@ -111,7 +111,13 @@ struct ehci_regs { /* ASYNCLISTADDR: offset 0x18 */ u32 async_next; /* address of next async queue head */ - u32 reserved[9]; + u32 reserved1[2]; + + /* TXFILLTUNING: offset 0x24 */ + u32 txfill_tuning; /* TX FIFO Tuning register */ +#define TXFIFO_DEFAULT (8<<16) /* FIFO burst threshold 8 */ + + u32 reserved2[6]; /* CONFIGFLAG: offset 0x40 */ u32 configured_flag; @@ -155,26 +161,34 @@ struct ehci_regs { #define PORT_CSC (1<<1) /* connect status change */ #define PORT_CONNECT (1<<0) /* device connected */ #define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) -}; -#define USBMODE 0x68 /* USB Device mode */ + u32 reserved3[9]; + + /* USBMODE: offset 0x68 */ + u32 usbmode; /* USB Device mode */ #define USBMODE_SDIS (1<<3) /* Stream disable */ #define USBMODE_BE (1<<2) /* BE/LE endianness select */ #define USBMODE_CM_HC (3<<0) /* host controller mode */ #define USBMODE_CM_IDLE (0<<0) /* idle state */ + u32 reserved4[7]; + /* Moorestown has some non-standard registers, partially due to the fact that * its EHCI controller has both TT and LPM support. HOSTPCx are extensions to * PORTSCx */ -#define HOSTPC0 0x84 /* HOSTPC extension */ + /* HOSTPC: offset 0x84 */ + u32 hostpc[0]; /* HOSTPC extension */ #define HOSTPC_PHCD (1<<22) /* Phy clock disable */ #define HOSTPC_PSPD (3<<25) /* Port speed detection */ -#define USBMODE_EX 0xc8 /* USB Device mode extension */ + + u32 reserved5[17]; + + /* USBMODE_EX: offset 0xc8 */ + u32 usbmode_ex; /* USB Device mode extension */ #define USBMODE_EX_VBPS (1<<5) /* VBus Power Select On */ #define USBMODE_EX_HC (3<<0) /* host controller mode */ -#define TXFILLTUNING 0x24 /* TX FIFO Tuning register */ -#define TXFIFO_DEFAULT (8<<16) /* FIFO burst threshold 8 */ +}; /* Appendix C, Debug port ... intended for use with special "debug devices" * that can help if there's no serial console. (nonstandard enumeration.) -- cgit v1.2.3 From 38ced28b21efff18fd5e5c98a92830e8f0031cee Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Jun 2012 10:55:57 -0300 Subject: edac: allow specifying the error count with fake_inject In order to test if the error counters are properly incremented, add a way to specify how many errors were generated by a trace. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/edac_mc_sysfs.c | 15 +++++++++++++-- include/linux/edac.h | 1 + 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index a2bf7e9dd6de..ed0bc07b8503 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -812,18 +812,24 @@ static ssize_t edac_fake_inject_write(struct file *file, struct device *dev = file->private_data; struct mem_ctl_info *mci = to_mci(dev); static enum hw_event_mc_err_type type; + u16 errcount = mci->fake_inject_count; + + if (!errcount) + errcount = 1; type = mci->fake_inject_ue ? HW_EVENT_ERR_UNCORRECTED : HW_EVENT_ERR_CORRECTED; printk(KERN_DEBUG - "Generating a %s fake error to %d.%d.%d to test core handling. NOTE: this won't test the driver-specific decoding logic.\n", + "Generating %d %s fake error%s to %d.%d.%d to test core handling. NOTE: this won't test the driver-specific decoding logic.\n", + errcount, (type == HW_EVENT_ERR_UNCORRECTED) ? "UE" : "CE", + errcount > 1 ? "s" : "", mci->fake_inject_layer[0], mci->fake_inject_layer[1], mci->fake_inject_layer[2] ); - edac_mc_handle_error(type, mci, 1, 0, 0, 0, + edac_mc_handle_error(type, mci, errcount, 0, 0, 0, mci->fake_inject_layer[0], mci->fake_inject_layer[1], mci->fake_inject_layer[2], @@ -944,6 +950,11 @@ int edac_create_debug_nodes(struct mem_ctl_info *mci) if (!d) goto nomem; + d = debugfs_create_u16("fake_inject_count", S_IRUGO | S_IWUSR, parent, + &mci->fake_inject_count); + if (!d) + goto nomem; + d = debugfs_create_file("fake_inject", S_IWUSR, parent, &mci->dev, &debug_fake_inject_fops); diff --git a/include/linux/edac.h b/include/linux/edac.h index 6677af853e30..bab9f8473dc1 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -665,6 +665,7 @@ struct mem_ctl_info { struct dentry *debugfs; u8 fake_inject_layer[EDAC_MAX_LAYERS]; u32 fake_inject_ue; + u16 fake_inject_count; #endif }; -- cgit v1.2.3 From e7b52ffd45a6d834473f43b349e7d86593d763c7 Mon Sep 17 00:00:00 2001 From: Alex Shi Date: Thu, 28 Jun 2012 09:02:17 +0800 Subject: x86/flush_tlb: try flush_tlb_single one by one in flush_tlb_range x86 has no flush_tlb_range support in instruction level. Currently the flush_tlb_range just implemented by flushing all page table. That is not the best solution for all scenarios. In fact, if we just use 'invlpg' to flush few lines from TLB, we can get the performance gain from later remain TLB lines accessing. But the 'invlpg' instruction costs much of time. Its execution time can compete with cr3 rewriting, and even a bit more on SNB CPU. So, on a 512 4KB TLB entries CPU, the balance points is at: (512 - X) * 100ns(assumed TLB refill cost) = X(TLB flush entries) * 100ns(assumed invlpg cost) Here, X is 256, that is 1/2 of 512 entries. But with the mysterious CPU pre-fetcher and page miss handler Unit, the assumed TLB refill cost is far lower then 100ns in sequential access. And 2 HT siblings in one core makes the memory access more faster if they are accessing the same memory. So, in the patch, I just do the change when the target entries is less than 1/16 of whole active tlb entries. Actually, I have no data support for the percentage '1/16', so any suggestions are welcomed. As to hugetlb, guess due to smaller page table, and smaller active TLB entries, I didn't see benefit via my benchmark, so no optimizing now. My micro benchmark show in ideal scenarios, the performance improves 70 percent in reading. And in worst scenario, the reading/writing performance is similar with unpatched 3.4-rc4 kernel. Here is the reading data on my 2P * 4cores *HT NHM EP machine, with THP 'always': multi thread testing, '-t' paramter is thread number: with patch unpatched 3.4-rc4 ./mprotect -t 1 14ns 24ns ./mprotect -t 2 13ns 22ns ./mprotect -t 4 12ns 19ns ./mprotect -t 8 14ns 16ns ./mprotect -t 16 28ns 26ns ./mprotect -t 32 54ns 51ns ./mprotect -t 128 200ns 199ns Single process with sequencial flushing and memory accessing: with patch unpatched 3.4-rc4 ./mprotect 7ns 11ns ./mprotect -p 4096 -l 8 -n 10240 21ns 21ns [ hpa: http://lkml.kernel.org/r/1B4B44D9196EFF41AE41FDA404FC0A100BFF94@SHSMSX101.ccr.corp.intel.com has additional performance numbers. ] Signed-off-by: Alex Shi Link: http://lkml.kernel.org/r/1340845344-27557-3-git-send-email-alex.shi@intel.com Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/paravirt.h | 5 +- arch/x86/include/asm/paravirt_types.h | 3 +- arch/x86/include/asm/tlbflush.h | 23 ++++----- arch/x86/include/asm/uv/uv.h | 5 +- arch/x86/mm/tlb.c | 97 +++++++++++++++++++++++++++++------ arch/x86/platform/uv/tlb_uv.c | 6 +-- arch/x86/xen/mmu.c | 12 ++--- include/trace/events/xen.h | 12 +++-- 8 files changed, 114 insertions(+), 49 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/paravirt.h b/arch/x86/include/asm/paravirt.h index 6cbbabf52707..7e2c2a635737 100644 --- a/arch/x86/include/asm/paravirt.h +++ b/arch/x86/include/asm/paravirt.h @@ -397,9 +397,10 @@ static inline void __flush_tlb_single(unsigned long addr) static inline void flush_tlb_others(const struct cpumask *cpumask, struct mm_struct *mm, - unsigned long va) + unsigned long start, + unsigned long end) { - PVOP_VCALL3(pv_mmu_ops.flush_tlb_others, cpumask, mm, va); + PVOP_VCALL4(pv_mmu_ops.flush_tlb_others, cpumask, mm, start, end); } static inline int paravirt_pgd_alloc(struct mm_struct *mm) diff --git a/arch/x86/include/asm/paravirt_types.h b/arch/x86/include/asm/paravirt_types.h index 8e8b9a4987ee..600a5fcac9cd 100644 --- a/arch/x86/include/asm/paravirt_types.h +++ b/arch/x86/include/asm/paravirt_types.h @@ -250,7 +250,8 @@ struct pv_mmu_ops { void (*flush_tlb_single)(unsigned long addr); void (*flush_tlb_others)(const struct cpumask *cpus, struct mm_struct *mm, - unsigned long va); + unsigned long start, + unsigned long end); /* Hooks for allocating and freeing a pagetable top-level */ int (*pgd_alloc)(struct mm_struct *mm); diff --git a/arch/x86/include/asm/tlbflush.h b/arch/x86/include/asm/tlbflush.h index 36a1a2ab87d2..33608d96d68b 100644 --- a/arch/x86/include/asm/tlbflush.h +++ b/arch/x86/include/asm/tlbflush.h @@ -73,14 +73,10 @@ static inline void __flush_tlb_one(unsigned long addr) * - flush_tlb_page(vma, vmaddr) flushes one page * - flush_tlb_range(vma, start, end) flushes a range of pages * - flush_tlb_kernel_range(start, end) flushes a range of kernel pages - * - flush_tlb_others(cpumask, mm, va) flushes TLBs on other cpus + * - flush_tlb_others(cpumask, mm, start, end) flushes TLBs on other cpus * * ..but the i386 has somewhat limited tlb flushing capabilities, * and page-granular flushes are available only on i486 and up. - * - * x86-64 can only flush individual pages or full VMs. For a range flush - * we always do the full VM. Might be worth trying if for a small - * range a few INVLPGs in a row are a win. */ #ifndef CONFIG_SMP @@ -111,7 +107,8 @@ static inline void flush_tlb_range(struct vm_area_struct *vma, static inline void native_flush_tlb_others(const struct cpumask *cpumask, struct mm_struct *mm, - unsigned long va) + unsigned long start, + unsigned long end) { } @@ -129,17 +126,14 @@ extern void flush_tlb_all(void); extern void flush_tlb_current_task(void); extern void flush_tlb_mm(struct mm_struct *); extern void flush_tlb_page(struct vm_area_struct *, unsigned long); +extern void flush_tlb_range(struct vm_area_struct *vma, + unsigned long start, unsigned long end); #define flush_tlb() flush_tlb_current_task() -static inline void flush_tlb_range(struct vm_area_struct *vma, - unsigned long start, unsigned long end) -{ - flush_tlb_mm(vma->vm_mm); -} - void native_flush_tlb_others(const struct cpumask *cpumask, - struct mm_struct *mm, unsigned long va); + struct mm_struct *mm, + unsigned long start, unsigned long end); #define TLBSTATE_OK 1 #define TLBSTATE_LAZY 2 @@ -159,7 +153,8 @@ static inline void reset_lazy_tlbstate(void) #endif /* SMP */ #ifndef CONFIG_PARAVIRT -#define flush_tlb_others(mask, mm, va) native_flush_tlb_others(mask, mm, va) +#define flush_tlb_others(mask, mm, start, end) \ + native_flush_tlb_others(mask, mm, start, end) #endif static inline void flush_tlb_kernel_range(unsigned long start, diff --git a/arch/x86/include/asm/uv/uv.h b/arch/x86/include/asm/uv/uv.h index 3bb9491b7659..b47c2a82ff15 100644 --- a/arch/x86/include/asm/uv/uv.h +++ b/arch/x86/include/asm/uv/uv.h @@ -15,7 +15,8 @@ extern void uv_nmi_init(void); extern void uv_system_init(void); extern const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask, struct mm_struct *mm, - unsigned long va, + unsigned long start, + unsigned end, unsigned int cpu); #else /* X86_UV */ @@ -26,7 +27,7 @@ static inline void uv_cpu_init(void) { } static inline void uv_system_init(void) { } static inline const struct cpumask * uv_flush_tlb_others(const struct cpumask *cpumask, struct mm_struct *mm, - unsigned long va, unsigned int cpu) + unsigned long start, unsigned long end, unsigned int cpu) { return cpumask; } #endif /* X86_UV */ diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c index 5e57e113b72c..3b91c981a27f 100644 --- a/arch/x86/mm/tlb.c +++ b/arch/x86/mm/tlb.c @@ -41,7 +41,8 @@ DEFINE_PER_CPU_SHARED_ALIGNED(struct tlb_state, cpu_tlbstate) union smp_flush_state { struct { struct mm_struct *flush_mm; - unsigned long flush_va; + unsigned long flush_start; + unsigned long flush_end; raw_spinlock_t tlbstate_lock; DECLARE_BITMAP(flush_cpumask, NR_CPUS); }; @@ -156,10 +157,19 @@ void smp_invalidate_interrupt(struct pt_regs *regs) if (f->flush_mm == this_cpu_read(cpu_tlbstate.active_mm)) { if (this_cpu_read(cpu_tlbstate.state) == TLBSTATE_OK) { - if (f->flush_va == TLB_FLUSH_ALL) + if (f->flush_end == TLB_FLUSH_ALL + || !cpu_has_invlpg) local_flush_tlb(); - else - __flush_tlb_one(f->flush_va); + else if (!f->flush_end) + __flush_tlb_single(f->flush_start); + else { + unsigned long addr; + addr = f->flush_start; + while (addr < f->flush_end) { + __flush_tlb_single(addr); + addr += PAGE_SIZE; + } + } } else leave_mm(cpu); } @@ -172,7 +182,8 @@ out: } static void flush_tlb_others_ipi(const struct cpumask *cpumask, - struct mm_struct *mm, unsigned long va) + struct mm_struct *mm, unsigned long start, + unsigned long end) { unsigned int sender; union smp_flush_state *f; @@ -185,7 +196,8 @@ static void flush_tlb_others_ipi(const struct cpumask *cpumask, raw_spin_lock(&f->tlbstate_lock); f->flush_mm = mm; - f->flush_va = va; + f->flush_start = start; + f->flush_end = end; if (cpumask_andnot(to_cpumask(f->flush_cpumask), cpumask, cpumask_of(smp_processor_id()))) { /* * We have to send the IPI only to @@ -199,24 +211,26 @@ static void flush_tlb_others_ipi(const struct cpumask *cpumask, } f->flush_mm = NULL; - f->flush_va = 0; + f->flush_start = 0; + f->flush_end = 0; if (nr_cpu_ids > NUM_INVALIDATE_TLB_VECTORS) raw_spin_unlock(&f->tlbstate_lock); } void native_flush_tlb_others(const struct cpumask *cpumask, - struct mm_struct *mm, unsigned long va) + struct mm_struct *mm, unsigned long start, + unsigned long end) { if (is_uv_system()) { unsigned int cpu; cpu = smp_processor_id(); - cpumask = uv_flush_tlb_others(cpumask, mm, va, cpu); + cpumask = uv_flush_tlb_others(cpumask, mm, start, end, cpu); if (cpumask) - flush_tlb_others_ipi(cpumask, mm, va); + flush_tlb_others_ipi(cpumask, mm, start, end); return; } - flush_tlb_others_ipi(cpumask, mm, va); + flush_tlb_others_ipi(cpumask, mm, start, end); } static void __cpuinit calculate_tlb_offset(void) @@ -282,7 +296,7 @@ void flush_tlb_current_task(void) local_flush_tlb(); if (cpumask_any_but(mm_cpumask(mm), smp_processor_id()) < nr_cpu_ids) - flush_tlb_others(mm_cpumask(mm), mm, TLB_FLUSH_ALL); + flush_tlb_others(mm_cpumask(mm), mm, 0UL, TLB_FLUSH_ALL); preempt_enable(); } @@ -297,12 +311,63 @@ void flush_tlb_mm(struct mm_struct *mm) leave_mm(smp_processor_id()); } if (cpumask_any_but(mm_cpumask(mm), smp_processor_id()) < nr_cpu_ids) - flush_tlb_others(mm_cpumask(mm), mm, TLB_FLUSH_ALL); + flush_tlb_others(mm_cpumask(mm), mm, 0UL, TLB_FLUSH_ALL); + + preempt_enable(); +} + +#define FLUSHALL_BAR 16 + +void flush_tlb_range(struct vm_area_struct *vma, + unsigned long start, unsigned long end) +{ + struct mm_struct *mm; + + if (!cpu_has_invlpg || vma->vm_flags & VM_HUGETLB) { + flush_tlb_mm(vma->vm_mm); + return; + } + + preempt_disable(); + mm = vma->vm_mm; + if (current->active_mm == mm) { + if (current->mm) { + unsigned long addr, vmflag = vma->vm_flags; + unsigned act_entries, tlb_entries = 0; + + if (vmflag & VM_EXEC) + tlb_entries = tlb_lli_4k[ENTRIES]; + else + tlb_entries = tlb_lld_4k[ENTRIES]; + + act_entries = tlb_entries > mm->total_vm ? + mm->total_vm : tlb_entries; + if ((end - start)/PAGE_SIZE > act_entries/FLUSHALL_BAR) + local_flush_tlb(); + else { + for (addr = start; addr < end; + addr += PAGE_SIZE) + __flush_tlb_single(addr); + + if (cpumask_any_but(mm_cpumask(mm), + smp_processor_id()) < nr_cpu_ids) + flush_tlb_others(mm_cpumask(mm), mm, + start, end); + preempt_enable(); + return; + } + } else { + leave_mm(smp_processor_id()); + } + } + if (cpumask_any_but(mm_cpumask(mm), smp_processor_id()) < nr_cpu_ids) + flush_tlb_others(mm_cpumask(mm), mm, 0UL, TLB_FLUSH_ALL); preempt_enable(); } -void flush_tlb_page(struct vm_area_struct *vma, unsigned long va) + +void flush_tlb_page(struct vm_area_struct *vma, unsigned long start) { struct mm_struct *mm = vma->vm_mm; @@ -310,13 +375,13 @@ void flush_tlb_page(struct vm_area_struct *vma, unsigned long va) if (current->active_mm == mm) { if (current->mm) - __flush_tlb_one(va); + __flush_tlb_one(start); else leave_mm(smp_processor_id()); } if (cpumask_any_but(mm_cpumask(mm), smp_processor_id()) < nr_cpu_ids) - flush_tlb_others(mm_cpumask(mm), mm, va); + flush_tlb_others(mm_cpumask(mm), mm, start, 0UL); preempt_enable(); } diff --git a/arch/x86/platform/uv/tlb_uv.c b/arch/x86/platform/uv/tlb_uv.c index 59880afa851f..f1bef8e1d633 100644 --- a/arch/x86/platform/uv/tlb_uv.c +++ b/arch/x86/platform/uv/tlb_uv.c @@ -1068,8 +1068,8 @@ static int set_distrib_bits(struct cpumask *flush_mask, struct bau_control *bcp, * done. The returned pointer is valid till preemption is re-enabled. */ const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask, - struct mm_struct *mm, unsigned long va, - unsigned int cpu) + struct mm_struct *mm, unsigned long start, + unsigned end, unsigned int cpu) { int locals = 0; int remotes = 0; @@ -1112,7 +1112,7 @@ const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask, record_send_statistics(stat, locals, hubs, remotes, bau_desc); - bau_desc->payload.address = va; + bau_desc->payload.address = start; bau_desc->payload.sending_cpu = cpu; /* * uv_flush_send_and_wait returns 0 if all cpu's were messaged, diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c index 3a73785631ce..39ed56789f68 100644 --- a/arch/x86/xen/mmu.c +++ b/arch/x86/xen/mmu.c @@ -1244,7 +1244,8 @@ static void xen_flush_tlb_single(unsigned long addr) } static void xen_flush_tlb_others(const struct cpumask *cpus, - struct mm_struct *mm, unsigned long va) + struct mm_struct *mm, unsigned long start, + unsigned long end) { struct { struct mmuext_op op; @@ -1256,7 +1257,7 @@ static void xen_flush_tlb_others(const struct cpumask *cpus, } *args; struct multicall_space mcs; - trace_xen_mmu_flush_tlb_others(cpus, mm, va); + trace_xen_mmu_flush_tlb_others(cpus, mm, start, end); if (cpumask_empty(cpus)) return; /* nothing to do */ @@ -1269,11 +1270,10 @@ static void xen_flush_tlb_others(const struct cpumask *cpus, cpumask_and(to_cpumask(args->mask), cpus, cpu_online_mask); cpumask_clear_cpu(smp_processor_id(), to_cpumask(args->mask)); - if (va == TLB_FLUSH_ALL) { - args->op.cmd = MMUEXT_TLB_FLUSH_MULTI; - } else { + args->op.cmd = MMUEXT_TLB_FLUSH_MULTI; + if (start != TLB_FLUSH_ALL && (end - start) <= PAGE_SIZE) { args->op.cmd = MMUEXT_INVLPG_MULTI; - args->op.arg1.linear_addr = va; + args->op.arg1.linear_addr = start; } MULTI_mmuext_op(mcs.mc, &args->op, 1, NULL, DOMID_SELF); diff --git a/include/trace/events/xen.h b/include/trace/events/xen.h index 92f1a796829e..15ba03bdd7c6 100644 --- a/include/trace/events/xen.h +++ b/include/trace/events/xen.h @@ -397,18 +397,20 @@ TRACE_EVENT(xen_mmu_flush_tlb_single, TRACE_EVENT(xen_mmu_flush_tlb_others, TP_PROTO(const struct cpumask *cpus, struct mm_struct *mm, - unsigned long addr), - TP_ARGS(cpus, mm, addr), + unsigned long addr, unsigned long end), + TP_ARGS(cpus, mm, addr, end), TP_STRUCT__entry( __field(unsigned, ncpus) __field(struct mm_struct *, mm) __field(unsigned long, addr) + __field(unsigned long, end) ), TP_fast_assign(__entry->ncpus = cpumask_weight(cpus); __entry->mm = mm; - __entry->addr = addr), - TP_printk("ncpus %d mm %p addr %lx", - __entry->ncpus, __entry->mm, __entry->addr) + __entry->addr = addr, + __entry->end = end), + TP_printk("ncpus %d mm %p addr %lx, end %lx", + __entry->ncpus, __entry->mm, __entry->addr, __entry->end) ); TRACE_EVENT(xen_mmu_write_cr3, -- cgit v1.2.3 From c4211f42d3e66875298a5e26a75109878c80f15b Mon Sep 17 00:00:00 2001 From: Alex Shi Date: Thu, 28 Jun 2012 09:02:19 +0800 Subject: x86/tlb: add tlb_flushall_shift for specific CPU Testing show different CPU type(micro architectures and NUMA mode) has different balance points between the TLB flush all and multiple invlpg. And there also has cases the tlb flush change has no any help. This patch give a interface to let x86 vendor developers have a chance to set different shift for different CPU type. like some machine in my hands, balance points is 16 entries on Romely-EP; while it is at 8 entries on Bloomfield NHM-EP; and is 256 on IVB mobile CPU. but on model 15 core2 Xeon using invlpg has nothing help. For untested machine, do a conservative optimization, same as NHM CPU. Signed-off-by: Alex Shi Link: http://lkml.kernel.org/r/1340845344-27557-5-git-send-email-alex.shi@intel.com Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/processor.h | 2 ++ arch/x86/kernel/cpu/common.c | 14 ++++++++++++-- arch/x86/kernel/cpu/intel.c | 34 ++++++++++++++++++++++++++++++++++ arch/x86/mm/tlb.c | 7 +++---- include/asm-generic/tlb.h | 3 ++- 5 files changed, 53 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 39b2bd48dfbc..d048cad9bcad 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -72,6 +72,8 @@ extern u16 __read_mostly tlb_lli_4m[NR_INFO]; extern u16 __read_mostly tlb_lld_4k[NR_INFO]; extern u16 __read_mostly tlb_lld_2m[NR_INFO]; extern u16 __read_mostly tlb_lld_4m[NR_INFO]; +extern s8 __read_mostly tlb_flushall_shift; + /* * CPU type and hardware bug flags. Kept separately for each CPU. * Members of this structure are referenced in head.S, so think twice diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index b2016df00813..7595552600b8 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -459,16 +459,26 @@ u16 __read_mostly tlb_lld_4k[NR_INFO]; u16 __read_mostly tlb_lld_2m[NR_INFO]; u16 __read_mostly tlb_lld_4m[NR_INFO]; +/* + * tlb_flushall_shift shows the balance point in replacing cr3 write + * with multiple 'invlpg'. It will do this replacement when + * flush_tlb_lines <= active_lines/2^tlb_flushall_shift. + * If tlb_flushall_shift is -1, means the replacement will be disabled. + */ +s8 __read_mostly tlb_flushall_shift = -1; + void __cpuinit cpu_detect_tlb(struct cpuinfo_x86 *c) { if (this_cpu->c_detect_tlb) this_cpu->c_detect_tlb(c); printk(KERN_INFO "Last level iTLB entries: 4KB %d, 2MB %d, 4MB %d\n" \ - "Last level dTLB entries: 4KB %d, 2MB %d, 4MB %d\n", + "Last level dTLB entries: 4KB %d, 2MB %d, 4MB %d\n" \ + "tlb_flushall_shift is 0x%x\n", tlb_lli_4k[ENTRIES], tlb_lli_2m[ENTRIES], tlb_lli_4m[ENTRIES], tlb_lld_4k[ENTRIES], - tlb_lld_2m[ENTRIES], tlb_lld_4m[ENTRIES]); + tlb_lld_2m[ENTRIES], tlb_lld_4m[ENTRIES], + tlb_flushall_shift); } void __cpuinit detect_ht(struct cpuinfo_x86 *c) diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c index ed0d512cf51b..0a4ce2980a5a 100644 --- a/arch/x86/kernel/cpu/intel.c +++ b/arch/x86/kernel/cpu/intel.c @@ -610,6 +610,39 @@ static void __cpuinit intel_tlb_lookup(const unsigned char desc) } } +static void __cpuinit intel_tlb_flushall_shift_set(struct cpuinfo_x86 *c) +{ + if (!cpu_has_invlpg) { + tlb_flushall_shift = -1; + return; + } + switch ((c->x86 << 8) + c->x86_model) { + case 0x60f: /* original 65 nm celeron/pentium/core2/xeon, "Merom"/"Conroe" */ + case 0x616: /* single-core 65 nm celeron/core2solo "Merom-L"/"Conroe-L" */ + case 0x617: /* current 45 nm celeron/core2/xeon "Penryn"/"Wolfdale" */ + case 0x61d: /* six-core 45 nm xeon "Dunnington" */ + tlb_flushall_shift = -1; + break; + case 0x61a: /* 45 nm nehalem, "Bloomfield" */ + case 0x61e: /* 45 nm nehalem, "Lynnfield" */ + case 0x625: /* 32 nm nehalem, "Clarkdale" */ + case 0x62c: /* 32 nm nehalem, "Gulftown" */ + case 0x62e: /* 45 nm nehalem-ex, "Beckton" */ + case 0x62f: /* 32 nm Xeon E7 */ + tlb_flushall_shift = 6; + break; + case 0x62a: /* SandyBridge */ + case 0x62d: /* SandyBridge, "Romely-EP" */ + tlb_flushall_shift = 5; + break; + case 0x63a: /* Ivybridge */ + tlb_flushall_shift = 1; + break; + default: + tlb_flushall_shift = 6; + } +} + static void __cpuinit intel_detect_tlb(struct cpuinfo_x86 *c) { int i, j, n; @@ -630,6 +663,7 @@ static void __cpuinit intel_detect_tlb(struct cpuinfo_x86 *c) for (j = 1 ; j < 16 ; j++) intel_tlb_lookup(desc[j]); } + intel_tlb_flushall_shift_set(c); } static const struct cpu_dev __cpuinitconst intel_cpu_dev = { diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c index 184a02a4d871..2939f2f9edbb 100644 --- a/arch/x86/mm/tlb.c +++ b/arch/x86/mm/tlb.c @@ -316,8 +316,6 @@ void flush_tlb_mm(struct mm_struct *mm) preempt_enable(); } -#define FLUSHALL_BAR 16 - #ifdef CONFIG_TRANSPARENT_HUGEPAGE static inline unsigned long has_large_page(struct mm_struct *mm, unsigned long start, unsigned long end) @@ -352,7 +350,7 @@ void flush_tlb_range(struct vm_area_struct *vma, { struct mm_struct *mm; - if (!cpu_has_invlpg || vma->vm_flags & VM_HUGETLB) { + if (vma->vm_flags & VM_HUGETLB || tlb_flushall_shift == -1) { flush_all: flush_tlb_mm(vma->vm_mm); return; @@ -373,7 +371,8 @@ flush_all: act_entries = tlb_entries > mm->total_vm ? mm->total_vm : tlb_entries; - if ((end - start)/PAGE_SIZE > act_entries/FLUSHALL_BAR) + if ((end - start) >> PAGE_SHIFT > + act_entries >> tlb_flushall_shift) local_flush_tlb(); else { if (has_large_page(mm, start, end)) { diff --git a/include/asm-generic/tlb.h b/include/asm-generic/tlb.h index f96a5b58a975..75e888b3cfd2 100644 --- a/include/asm-generic/tlb.h +++ b/include/asm-generic/tlb.h @@ -113,7 +113,8 @@ static inline int tlb_fast_mode(struct mmu_gather *tlb) void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, bool fullmm); void tlb_flush_mmu(struct mmu_gather *tlb); -void tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, unsigned long end); +void tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, + unsigned long end); int __tlb_remove_page(struct mmu_gather *tlb, struct page *page); /* tlb_remove_page -- cgit v1.2.3 From 597e1c3580b7cfd95bb0f3167e2b297bf8a5a3ae Mon Sep 17 00:00:00 2001 From: Alex Shi Date: Thu, 28 Jun 2012 09:02:21 +0800 Subject: mm/mmu_gather: enable tlb flush range in generic mmu_gather This patch enabled the tlb flush range support in generic mmu layer. Most of arch has self tlb flush range support, like ARM/IA64 etc. X86 arch has no this support in hardware yet. But another instruction 'invlpg' can implement this function in some degree. So, enable this feather in generic layer for x86 now. and maybe useful for other archs in further. Generic mmu_gather struct is protected by micro HAVE_GENERIC_MMU_GATHER. Other archs that has flush range supported own self mmu_gather struct. So, now this change is safe for them. In future we may unify this struct and related functions on multiple archs. Thanks for Peter Zijlstra time and time reminder for multiple architecture code safe! Signed-off-by: Alex Shi Link: http://lkml.kernel.org/r/1340845344-27557-7-git-send-email-alex.shi@intel.com Signed-off-by: H. Peter Anvin --- include/asm-generic/tlb.h | 2 ++ mm/memory.c | 9 +++++++++ 2 files changed, 11 insertions(+) (limited to 'include') diff --git a/include/asm-generic/tlb.h b/include/asm-generic/tlb.h index 75e888b3cfd2..ed6642ad03e0 100644 --- a/include/asm-generic/tlb.h +++ b/include/asm-generic/tlb.h @@ -86,6 +86,8 @@ struct mmu_gather { #ifdef CONFIG_HAVE_RCU_TABLE_FREE struct mmu_table_batch *batch; #endif + unsigned long start; + unsigned long end; unsigned int need_flush : 1, /* Did free PTEs */ fast_mode : 1; /* No batching */ diff --git a/mm/memory.c b/mm/memory.c index 1b7dc662bf9f..32c99433cfdf 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -206,6 +206,8 @@ void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, bool fullmm) tlb->mm = mm; tlb->fullmm = fullmm; + tlb->start = -1UL; + tlb->end = 0; tlb->need_flush = 0; tlb->fast_mode = (num_possible_cpus() == 1); tlb->local.next = NULL; @@ -248,6 +250,8 @@ void tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, unsigned long e { struct mmu_gather_batch *batch, *next; + tlb->start = start; + tlb->end = end; tlb_flush_mmu(tlb); /* keep the page table cache within bounds */ @@ -1204,6 +1208,11 @@ again: */ if (force_flush) { force_flush = 0; + +#ifdef HAVE_GENERIC_MMU_GATHER + tlb->start = addr; + tlb->end = end; +#endif tlb_flush_mmu(tlb); if (addr != end) goto again; -- cgit v1.2.3 From 0df8ad71a281893bf0bb46b715397ec4f106d394 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 25 Jun 2012 11:20:13 +0300 Subject: OMAPDSS: remove enum omap_dss_overlay_managers We have two almost the same enums: omap_channel and omap_dss_overlay_managers. omap_channel is used almost everywhere, and omap_channel assigns explicit values to the enum values which are needed for proper operation. omap_dss_overlay_managers is only used in one place, so it's easy to remove it, which is what this patch does. Signed-off-by: Tomi Valkeinen --- drivers/video/omap2/dss/overlay.c | 6 +++--- include/video/omapdss.h | 7 ------- 2 files changed, 3 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c index b0ba60f88dd2..c492bb074d0f 100644 --- a/drivers/video/omap2/dss/overlay.c +++ b/drivers/video/omap2/dss/overlay.c @@ -530,10 +530,10 @@ void dss_recheck_connections(struct omap_dss_device *dssdev, bool force) struct omap_overlay_manager *lcd2_mgr = NULL; struct omap_overlay_manager *mgr = NULL; - lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD); - tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_TV); + lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_LCD); + tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_DIGIT); if (dss_has_feature(FEAT_MGR_LCD2)) - lcd2_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD2); + lcd2_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_LCD2); if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) { if (!lcd2_mgr->device || force) { diff --git a/include/video/omapdss.h b/include/video/omapdss.h index c8e59b4a3364..ae6954836ef3 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -167,13 +167,6 @@ enum omap_dss_audio_state { OMAP_DSS_AUDIO_PLAYING, }; -/* XXX perhaps this should be removed */ -enum omap_dss_overlay_managers { - OMAP_DSS_OVL_MGR_LCD, - OMAP_DSS_OVL_MGR_TV, - OMAP_DSS_OVL_MGR_LCD2, -}; - enum omap_dss_rotation_type { OMAP_DSS_ROT_DMA = 1 << 0, OMAP_DSS_ROT_VRFB = 1 << 1, -- cgit v1.2.3 From 46028e6d10cbf9ccd5fb49aa0c23a430f314144c Mon Sep 17 00:00:00 2001 From: Wanpeng Li Date: Fri, 15 Jun 2012 16:52:29 +0800 Subject: mm: cleanup on the comments of zone_reclaim_stat Signed-off-by: Wanpeng Li Acked-by: Minchan Kim Signed-off-by: Jiri Kosina --- include/linux/mmzone.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 4871e31ae277..54631776dff2 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -316,7 +316,7 @@ enum zone_type { struct zone_reclaim_stat { /* * The pageout code in vmscan.c keeps track of how many of the - * mem/swap backed and file backed pages are refeferenced. + * mem/swap backed and file backed pages are referenced. * The higher the rotated/scanned ratio, the more valuable * that cache is. * -- cgit v1.2.3 From dccd2304cc907c4b4d2920eeb24b055320fe942e Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Sun, 24 Jun 2012 12:46:05 +0100 Subject: ARM: 7430/1: sizes.h: move from asm-generic to sizes.h is used throughout the AMBA code and drivers, so the header should be available to everyone in order to driver AMBA/PrimeCell peripherals behind a PCI bridge where the host can be any platform (I'm doing it under x86). At this step includes , to allow a grace period for both in-tree and out-of-tree drivers. Signed-off-by: Alessandro Rubini Acked-by: Giancarlo Asnaghi Acked-by: Linus Walleij Cc: Alan Cox Signed-off-by: Russell King --- include/asm-generic/sizes.h | 49 ++------------------------------------------- include/linux/sizes.h | 47 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 47 deletions(-) create mode 100644 include/linux/sizes.h (limited to 'include') diff --git a/include/asm-generic/sizes.h b/include/asm-generic/sizes.h index ea5d4ef81061..1dcfad9629ef 100644 --- a/include/asm-generic/sizes.h +++ b/include/asm-generic/sizes.h @@ -1,47 +1,2 @@ -/* - * linux/include/asm-generic/sizes.h - * - * 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 __ASM_GENERIC_SIZES_H__ -#define __ASM_GENERIC_SIZES_H__ - -#define SZ_1 0x00000001 -#define SZ_2 0x00000002 -#define SZ_4 0x00000004 -#define SZ_8 0x00000008 -#define SZ_16 0x00000010 -#define SZ_32 0x00000020 -#define SZ_64 0x00000040 -#define SZ_128 0x00000080 -#define SZ_256 0x00000100 -#define SZ_512 0x00000200 - -#define SZ_1K 0x00000400 -#define SZ_2K 0x00000800 -#define SZ_4K 0x00001000 -#define SZ_8K 0x00002000 -#define SZ_16K 0x00004000 -#define SZ_32K 0x00008000 -#define SZ_64K 0x00010000 -#define SZ_128K 0x00020000 -#define SZ_256K 0x00040000 -#define SZ_512K 0x00080000 - -#define SZ_1M 0x00100000 -#define SZ_2M 0x00200000 -#define SZ_4M 0x00400000 -#define SZ_8M 0x00800000 -#define SZ_16M 0x01000000 -#define SZ_32M 0x02000000 -#define SZ_64M 0x04000000 -#define SZ_128M 0x08000000 -#define SZ_256M 0x10000000 -#define SZ_512M 0x20000000 - -#define SZ_1G 0x40000000 -#define SZ_2G 0x80000000 - -#endif /* __ASM_GENERIC_SIZES_H__ */ +/* This is a placeholder, to be removed over time */ +#include diff --git a/include/linux/sizes.h b/include/linux/sizes.h new file mode 100644 index 000000000000..ce3e8150c174 --- /dev/null +++ b/include/linux/sizes.h @@ -0,0 +1,47 @@ +/* + * include/linux/sizes.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __LINUX_SIZES_H__ +#define __LINUX_SIZES_H__ + +#define SZ_1 0x00000001 +#define SZ_2 0x00000002 +#define SZ_4 0x00000004 +#define SZ_8 0x00000008 +#define SZ_16 0x00000010 +#define SZ_32 0x00000020 +#define SZ_64 0x00000040 +#define SZ_128 0x00000080 +#define SZ_256 0x00000100 +#define SZ_512 0x00000200 + +#define SZ_1K 0x00000400 +#define SZ_2K 0x00000800 +#define SZ_4K 0x00001000 +#define SZ_8K 0x00002000 +#define SZ_16K 0x00004000 +#define SZ_32K 0x00008000 +#define SZ_64K 0x00010000 +#define SZ_128K 0x00020000 +#define SZ_256K 0x00040000 +#define SZ_512K 0x00080000 + +#define SZ_1M 0x00100000 +#define SZ_2M 0x00200000 +#define SZ_4M 0x00400000 +#define SZ_8M 0x00800000 +#define SZ_16M 0x01000000 +#define SZ_32M 0x02000000 +#define SZ_64M 0x04000000 +#define SZ_128M 0x08000000 +#define SZ_256M 0x10000000 +#define SZ_512M 0x20000000 + +#define SZ_1G 0x40000000 +#define SZ_2G 0x80000000 + +#endif /* __LINUX_SIZES_H__ */ -- cgit v1.2.3 From bfeea1dc1c9238f9e5a17a265489ecd0d19f66bb Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 20 Jun 2012 09:58:35 -0400 Subject: SUNRPC: Don't decode beyond the end of the RPC reply message Now that xdr_inline_decode() will automatically cross into the page buffers, we need to ensure that it doesn't exceed the total reply message length. This patch sets up a counter that tracks the number of words remaining in the reply message, and ensures that xdr_inline_decode, xdr_read_pages and xdr_enter_page respect the end of message boundary. Signed-off-by: Trond Myklebust --- include/linux/sunrpc/xdr.h | 1 + net/sunrpc/xdr.c | 28 +++++++++++++++++++++------- 2 files changed, 22 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index af70af333546..f1e7f884d1c3 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -205,6 +205,7 @@ struct xdr_stream { struct kvec *iov; /* pointer to the current kvec */ struct kvec scratch; /* Scratch buffer */ struct page **page_ptr; /* pointer to the current page */ + unsigned int nwords; /* Remaining decode buffer length */ }; /* diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 21d041cf7f65..5643feb6c645 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -630,12 +630,15 @@ void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p) xdr->buf = buf; xdr->scratch.iov_base = NULL; xdr->scratch.iov_len = 0; + xdr->nwords = XDR_QUADLEN(buf->len); if (buf->head[0].iov_len != 0) xdr_set_iov(xdr, buf->head, buf->len); else if (buf->page_len != 0) xdr_set_page_base(xdr, 0, buf->len); - if (p != NULL && p > xdr->p && xdr->end >= p) + if (p != NULL && p > xdr->p && xdr->end >= p) { + xdr->nwords -= p - xdr->p; xdr->p = p; + } } EXPORT_SYMBOL_GPL(xdr_init_decode); @@ -660,12 +663,14 @@ EXPORT_SYMBOL_GPL(xdr_init_decode_pages); static __be32 * __xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) { + unsigned int nwords = XDR_QUADLEN(nbytes); __be32 *p = xdr->p; - __be32 *q = p + XDR_QUADLEN(nbytes); + __be32 *q = p + nwords; - if (unlikely(q > xdr->end || q < p)) + if (unlikely(nwords > xdr->nwords || q > xdr->end || q < p)) return NULL; xdr->p = q; + xdr->nwords -= nwords; return p; } @@ -746,9 +751,16 @@ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len) struct xdr_buf *buf = xdr->buf; struct kvec *iov; ssize_t shift; + unsigned int nwords = XDR_QUADLEN(len); unsigned int end; int padding; + if (xdr->nwords == 0) + return; + if (nwords > xdr->nwords) { + nwords = xdr->nwords; + len = nwords << 2; + } /* Realign pages to current pointer position */ iov = buf->head; shift = iov->iov_len + (char *)iov->iov_base - (char *)xdr->p; @@ -758,15 +770,15 @@ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len) /* Truncate page data and move it into the tail */ if (buf->page_len > len) xdr_shrink_pagelen(buf, buf->page_len - len); - padding = (XDR_QUADLEN(len) << 2) - len; + padding = (nwords << 2) - len; xdr->iov = iov = buf->tail; /* Compute remaining message length. */ end = iov->iov_len; shift = buf->buflen - buf->len; - if (shift < end) + if (end > shift + padding) end -= shift; - else if (shift > 0) - end = 0; + else + end = padding; /* * Position current pointer at beginning of tail, and * set remaining message length. @@ -774,6 +786,7 @@ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len) xdr->p = (__be32 *)((char *)iov->iov_base + padding); xdr->end = (__be32 *)((char *)iov->iov_base + end); xdr->page_ptr = NULL; + xdr->nwords = XDR_QUADLEN(end - padding); } EXPORT_SYMBOL_GPL(xdr_read_pages); @@ -795,6 +808,7 @@ void xdr_enter_page(struct xdr_stream *xdr, unsigned int len) * set remaining message length. */ xdr_set_page_base(xdr, 0, len); + xdr->nwords += XDR_QUADLEN(xdr->buf->page_len); } EXPORT_SYMBOL_GPL(xdr_enter_page); -- cgit v1.2.3 From c337d3655ce85e12c7c476cb81406521926cacd2 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 21 Jun 2012 17:05:37 -0400 Subject: SUNRPC: xdr_read_pages should return the amount of XDR encoded page data Callers of xdr_read_pages() will want to know exactly how much XDR data is encoded in the pages after the data realignment. Signed-off-by: Trond Myklebust --- include/linux/sunrpc/xdr.h | 2 +- net/sunrpc/xdr.c | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index f1e7f884d1c3..2489bce1fb88 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -223,7 +223,7 @@ extern void xdr_init_decode_pages(struct xdr_stream *xdr, struct xdr_buf *buf, struct page **pages, unsigned int len); extern void xdr_set_scratch_buffer(struct xdr_stream *xdr, void *buf, size_t buflen); extern __be32 *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes); -extern void xdr_read_pages(struct xdr_stream *xdr, unsigned int len); +extern unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len); extern void xdr_enter_page(struct xdr_stream *xdr, unsigned int len); extern int xdr_process_buf(struct xdr_buf *buf, unsigned int offset, unsigned int len, int (*actor)(struct scatterlist *, void *), void *data); diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 5643feb6c645..80d518644cf8 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -745,8 +745,10 @@ EXPORT_SYMBOL_GPL(xdr_inline_decode); * Moves data beyond the current pointer position from the XDR head[] buffer * into the page list. Any data that lies beyond current position + "len" * bytes is moved into the XDR tail[]. + * + * Returns the number of XDR encoded bytes now contained in the pages */ -void xdr_read_pages(struct xdr_stream *xdr, unsigned int len) +unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len) { struct xdr_buf *buf = xdr->buf; struct kvec *iov; @@ -756,7 +758,7 @@ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len) int padding; if (xdr->nwords == 0) - return; + return 0; if (nwords > xdr->nwords) { nwords = xdr->nwords; len = nwords << 2; @@ -787,6 +789,7 @@ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len) xdr->end = (__be32 *)((char *)iov->iov_base + end); xdr->page_ptr = NULL; xdr->nwords = XDR_QUADLEN(end - padding); + return len; } EXPORT_SYMBOL_GPL(xdr_read_pages); @@ -802,7 +805,7 @@ EXPORT_SYMBOL_GPL(xdr_read_pages); */ void xdr_enter_page(struct xdr_stream *xdr, unsigned int len) { - xdr_read_pages(xdr, len); + len = xdr_read_pages(xdr, len); /* * Position current pointer at beginning of tail, and * set remaining message length. -- cgit v1.2.3 From 4517d526c8aa31b5c14165ef180cc19518ff0a35 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 21 Jun 2012 17:14:46 -0400 Subject: SUNRPC: Add the helper xdr_stream_pos Add a helper to report the current offset from the start of the xdr_stream. Signed-off-by: Trond Myklebust --- include/linux/sunrpc/xdr.h | 1 + net/sunrpc/xdr.c | 10 ++++++++++ 2 files changed, 11 insertions(+) (limited to 'include') diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index 2489bce1fb88..647faf2289a7 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -218,6 +218,7 @@ extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes); extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int base, unsigned int len); +extern unsigned int xdr_stream_pos(const struct xdr_stream *xdr); extern void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p); extern void xdr_init_decode_pages(struct xdr_stream *xdr, struct xdr_buf *buf, struct page **pages, unsigned int len); diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 80d518644cf8..95980a5cd0a8 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -454,6 +454,16 @@ xdr_shift_buf(struct xdr_buf *buf, size_t len) } EXPORT_SYMBOL_GPL(xdr_shift_buf); +/** + * xdr_stream_pos - Return the current offset from the start of the xdr_stream + * @xdr: pointer to struct xdr_stream + */ +unsigned int xdr_stream_pos(const struct xdr_stream *xdr) +{ + return (unsigned int)(XDR_QUADLEN(xdr->buf->len) - xdr->nwords) << 2; +} +EXPORT_SYMBOL_GPL(xdr_stream_pos); + /** * xdr_init_encode - Initialize a struct xdr_stream for sending data. * @xdr: pointer to xdr_stream struct -- cgit v1.2.3 From 140150dbb1f9cf3ef963fb55505f994d74ff3276 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 5 Jun 2012 15:20:25 -0400 Subject: SUNRPC: Remove unused function xdr_encode_pages Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 4 +--- include/linux/sunrpc/xdr.h | 2 -- net/sunrpc/xdr.c | 28 ---------------------------- 3 files changed, 1 insertion(+), 33 deletions(-) (limited to 'include') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 15fc7e4664ed..5a7b3723cc6f 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2766,9 +2766,7 @@ static int nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry) * * In the case of WRITE, we also want to put the GETATTR after * the operation -- in this case because we want to make sure - * we get the post-operation mtime and size. This means that - * we can't use xdr_encode_pages() as written: we need a variant - * of it which would leave room in the 'tail' iovec. + * we get the post-operation mtime and size. * * Both of these changes to the XDR layer would in fact be quite * minor, but I decided to leave them for a subsequent patch. diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index 647faf2289a7..63988990bd36 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -104,8 +104,6 @@ __be32 *xdr_decode_string_inplace(__be32 *p, char **sp, unsigned int *lenp, __be32 *xdr_encode_netobj(__be32 *p, const struct xdr_netobj *); __be32 *xdr_decode_netobj(__be32 *p, struct xdr_netobj *); -void xdr_encode_pages(struct xdr_buf *, struct page **, unsigned int, - unsigned int); void xdr_inline_pages(struct xdr_buf *, unsigned int, struct page **, unsigned int, unsigned int); void xdr_terminate_string(struct xdr_buf *, const u32); diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 2e3694eccd82..d65d380571bc 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -128,34 +128,6 @@ xdr_terminate_string(struct xdr_buf *buf, const u32 len) } EXPORT_SYMBOL_GPL(xdr_terminate_string); -void -xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base, - unsigned int len) -{ - struct kvec *tail = xdr->tail; - u32 *p; - - xdr->pages = pages; - xdr->page_base = base; - xdr->page_len = len; - - p = (u32 *)xdr->head[0].iov_base + XDR_QUADLEN(xdr->head[0].iov_len); - tail->iov_base = p; - tail->iov_len = 0; - - if (len & 3) { - unsigned int pad = 4 - (len & 3); - - *p = 0; - tail->iov_base = (char *)p + (len & 3); - tail->iov_len = pad; - len += pad; - } - xdr->buflen += len; - xdr->len += len; -} -EXPORT_SYMBOL_GPL(xdr_encode_pages); - void xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset, struct page **pages, unsigned int base, unsigned int len) -- cgit v1.2.3 From 2f2c63bc221c5fcded24de2704575d0abf96b910 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 8 Jun 2012 11:56:09 -0400 Subject: NFS: Cleanup - only store the write verifier in struct nfs_page The 'committed' field is not needed once we have put the struct nfs_page on the right list. Also correct the type of the verifier: it is not an array of __be32, but simply an 8 byte long opaque array. Signed-off-by: Trond Myklebust --- fs/nfs/nfs3xdr.c | 12 +++++++----- fs/nfs/nfs4filelayout.c | 6 +++--- fs/nfs/nfs4xdr.c | 12 ++++++++---- fs/nfs/write.c | 4 ++-- include/linux/nfs_page.h | 2 +- include/linux/nfs_xdr.h | 6 +++++- 6 files changed, 26 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index 5013bdd85ab9..6cbe89400dfc 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c @@ -325,14 +325,14 @@ static void encode_createverf3(struct xdr_stream *xdr, const __be32 *verifier) memcpy(p, verifier, NFS3_CREATEVERFSIZE); } -static int decode_writeverf3(struct xdr_stream *xdr, __be32 *verifier) +static int decode_writeverf3(struct xdr_stream *xdr, struct nfs_write_verifier *verifier) { __be32 *p; p = xdr_inline_decode(xdr, NFS3_WRITEVERFSIZE); if (unlikely(p == NULL)) goto out_overflow; - memcpy(verifier, p, NFS3_WRITEVERFSIZE); + memcpy(verifier->data, p, NFS3_WRITEVERFSIZE); return 0; out_overflow: print_overflow_msg(__func__, xdr); @@ -1668,20 +1668,22 @@ static int decode_write3resok(struct xdr_stream *xdr, { __be32 *p; - p = xdr_inline_decode(xdr, 4 + 4 + NFS3_WRITEVERFSIZE); + p = xdr_inline_decode(xdr, 4 + 4); if (unlikely(p == NULL)) goto out_overflow; result->count = be32_to_cpup(p++); result->verf->committed = be32_to_cpup(p++); if (unlikely(result->verf->committed > NFS_FILE_SYNC)) goto out_badvalue; - memcpy(result->verf->verifier, p, NFS3_WRITEVERFSIZE); + if (decode_writeverf3(xdr, &result->verf->verifier)) + goto out_eio; return result->count; out_badvalue: dprintk("NFS: bad stable_how value: %u\n", result->verf->committed); return -EIO; out_overflow: print_overflow_msg(__func__, xdr); +out_eio: return -EIO; } @@ -2314,7 +2316,7 @@ static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req, goto out; if (status != NFS3_OK) goto out_status; - error = decode_writeverf3(xdr, result->verf->verifier); + error = decode_writeverf3(xdr, &result->verf->verifier); out: return error; out_status: diff --git a/fs/nfs/nfs4filelayout.c b/fs/nfs/nfs4filelayout.c index e1340293872c..85b70639921b 100644 --- a/fs/nfs/nfs4filelayout.c +++ b/fs/nfs/nfs4filelayout.c @@ -351,9 +351,9 @@ static void prepare_to_resend_writes(struct nfs_commit_data *data) struct nfs_page *first = nfs_list_entry(data->pages.next); data->task.tk_status = 0; - memcpy(data->verf.verifier, first->wb_verf.verifier, - sizeof(first->wb_verf.verifier)); - data->verf.verifier[0]++; /* ensure verifier mismatch */ + memcpy(&data->verf.verifier, &first->wb_verf, + sizeof(data->verf.verifier)); + data->verf.verifier.data[0]++; /* ensure verifier mismatch */ } static int filelayout_commit_done_cb(struct rpc_task *task, diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 1e2c47b3889d..610ebccbde5d 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -4158,13 +4158,18 @@ static int decode_verifier(struct xdr_stream *xdr, void *verifier) return decode_opaque_fixed(xdr, verifier, NFS4_VERIFIER_SIZE); } +static int decode_write_verifier(struct xdr_stream *xdr, struct nfs_write_verifier *verifier) +{ + return decode_opaque_fixed(xdr, verifier->data, NFS4_VERIFIER_SIZE); +} + static int decode_commit(struct xdr_stream *xdr, struct nfs_commitres *res) { int status; status = decode_op_hdr(xdr, OP_COMMIT); if (!status) - status = decode_verifier(xdr, res->verf->verifier); + status = decode_write_verifier(xdr, &res->verf->verifier); return status; } @@ -5192,13 +5197,12 @@ static int decode_write(struct xdr_stream *xdr, struct nfs_writeres *res) if (status) return status; - p = xdr_inline_decode(xdr, 16); + p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) goto out_overflow; res->count = be32_to_cpup(p++); res->verf->committed = be32_to_cpup(p++); - memcpy(res->verf->verifier, p, NFS4_VERIFIER_SIZE); - return 0; + return decode_write_verifier(xdr, &res->verf->verifier); out_overflow: print_overflow_msg(__func__, xdr); return -EIO; diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 4d6861c0dc14..ee929e5e1f7b 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -620,7 +620,7 @@ static void nfs_write_completion(struct nfs_pgio_header *hdr) goto next; } if (test_bit(NFS_IOHDR_NEED_COMMIT, &hdr->flags)) { - memcpy(&req->wb_verf, hdr->verf, sizeof(req->wb_verf)); + memcpy(&req->wb_verf, &hdr->verf->verifier, sizeof(req->wb_verf)); nfs_mark_request_commit(req, hdr->lseg, &cinfo); goto next; } @@ -1547,7 +1547,7 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data) /* Okay, COMMIT succeeded, apparently. Check the verifier * returned by the server against all stored verfs. */ - if (!memcmp(req->wb_verf.verifier, data->verf.verifier, sizeof(data->verf.verifier))) { + if (!memcmp(&req->wb_verf, &data->verf.verifier, sizeof(req->wb_verf))) { /* We have a match */ nfs_inode_remove_request(req); dprintk(" OK\n"); diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index 88d166b555e8..880805774f9f 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -42,7 +42,7 @@ struct nfs_page { wb_bytes; /* Length of request */ struct kref wb_kref; /* reference count */ unsigned long wb_flags; - struct nfs_writeverf wb_verf; /* Commit cookie */ + struct nfs_write_verifier wb_verf; /* Commit cookie */ }; struct nfs_pageio_descriptor; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 8aadd90b808a..5c0014d1c969 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -514,9 +514,13 @@ struct nfs_writeargs { struct nfs4_sequence_args seq_args; }; +struct nfs_write_verifier { + char data[8]; +}; + struct nfs_writeverf { + struct nfs_write_verifier verifier; enum nfs3_stable_how committed; - __be32 verifier[2]; }; struct nfs_writeres { -- cgit v1.2.3 From ff6331e25e3e02de17deef9a1e96334dad29e097 Mon Sep 17 00:00:00 2001 From: Chandrabhanu Mahapatra Date: Tue, 19 Jun 2012 15:08:16 +0530 Subject: OMAPDSS: Add support for LCD3 channel OMAP5 Display Subsystem (DSS) architecture comes with a additional LCD3 channel with its own dedicated overlay manager. The current patch adds LCD3 channel and basic register support for LCD3 channel. It adds register addresses for various Display Controller (DISPC) registers like DISPC_DEFAULT_COLOR, DISPC_TIMING_H, DISPC_DIVISORo, etc. Signed-off-by: Chandrabhanu Mahapatra Signed-off-by: Tomi Valkeinen --- drivers/video/omap2/dss/dispc.h | 26 ++++++++++++++++++++++++++ include/video/omapdss.h | 1 + 2 files changed, 27 insertions(+) (limited to 'include') diff --git a/drivers/video/omap2/dss/dispc.h b/drivers/video/omap2/dss/dispc.h index f278080e1063..420c980dcbdd 100644 --- a/drivers/video/omap2/dss/dispc.h +++ b/drivers/video/omap2/dss/dispc.h @@ -118,6 +118,8 @@ static inline u16 DISPC_DEFAULT_COLOR(enum omap_channel channel) return 0x0050; case OMAP_DSS_CHANNEL_LCD2: return 0x03AC; + case OMAP_DSS_CHANNEL_LCD3: + return 0x0814; default: BUG(); return 0; @@ -133,6 +135,8 @@ static inline u16 DISPC_TRANS_COLOR(enum omap_channel channel) return 0x0058; case OMAP_DSS_CHANNEL_LCD2: return 0x03B0; + case OMAP_DSS_CHANNEL_LCD3: + return 0x0818; default: BUG(); return 0; @@ -149,6 +153,8 @@ static inline u16 DISPC_TIMING_H(enum omap_channel channel) return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x0400; + case OMAP_DSS_CHANNEL_LCD3: + return 0x0840; default: BUG(); return 0; @@ -165,6 +171,8 @@ static inline u16 DISPC_TIMING_V(enum omap_channel channel) return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x0404; + case OMAP_DSS_CHANNEL_LCD3: + return 0x0844; default: BUG(); return 0; @@ -181,6 +189,8 @@ static inline u16 DISPC_POL_FREQ(enum omap_channel channel) return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x0408; + case OMAP_DSS_CHANNEL_LCD3: + return 0x083C; default: BUG(); return 0; @@ -197,6 +207,8 @@ static inline u16 DISPC_DIVISORo(enum omap_channel channel) return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x040C; + case OMAP_DSS_CHANNEL_LCD3: + return 0x0838; default: BUG(); return 0; @@ -213,6 +225,8 @@ static inline u16 DISPC_SIZE_MGR(enum omap_channel channel) return 0x0078; case OMAP_DSS_CHANNEL_LCD2: return 0x03CC; + case OMAP_DSS_CHANNEL_LCD3: + return 0x0834; default: BUG(); return 0; @@ -229,6 +243,8 @@ static inline u16 DISPC_DATA_CYCLE1(enum omap_channel channel) return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03C0; + case OMAP_DSS_CHANNEL_LCD3: + return 0x0828; default: BUG(); return 0; @@ -245,6 +261,8 @@ static inline u16 DISPC_DATA_CYCLE2(enum omap_channel channel) return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03C4; + case OMAP_DSS_CHANNEL_LCD3: + return 0x082C; default: BUG(); return 0; @@ -261,6 +279,8 @@ static inline u16 DISPC_DATA_CYCLE3(enum omap_channel channel) return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03C8; + case OMAP_DSS_CHANNEL_LCD3: + return 0x0830; default: BUG(); return 0; @@ -277,6 +297,8 @@ static inline u16 DISPC_CPR_COEF_R(enum omap_channel channel) return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03BC; + case OMAP_DSS_CHANNEL_LCD3: + return 0x0824; default: BUG(); return 0; @@ -293,6 +315,8 @@ static inline u16 DISPC_CPR_COEF_G(enum omap_channel channel) return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03B8; + case OMAP_DSS_CHANNEL_LCD3: + return 0x0820; default: BUG(); return 0; @@ -309,6 +333,8 @@ static inline u16 DISPC_CPR_COEF_B(enum omap_channel channel) return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03B4; + case OMAP_DSS_CHANNEL_LCD3: + return 0x081C; default: BUG(); return 0; diff --git a/include/video/omapdss.h b/include/video/omapdss.h index ae6954836ef3..23887b358a49 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -75,6 +75,7 @@ enum omap_channel { OMAP_DSS_CHANNEL_LCD = 0, OMAP_DSS_CHANNEL_DIGIT = 1, OMAP_DSS_CHANNEL_LCD2 = 2, + OMAP_DSS_CHANNEL_LCD3 = 3, }; enum omap_color_mode { -- cgit v1.2.3 From e86d456a23f3ecbb97704e63899ecfd6ec54b8d8 Mon Sep 17 00:00:00 2001 From: Chandrabhanu Mahapatra Date: Fri, 29 Jun 2012 10:43:13 +0530 Subject: OMAPDSS: Add LCD3 overlay manager and Clock and IRQ support The support for LCD3 manager has been added into the manager module. LCD3 panel has registers as DISPC_CONTROL3 and DISPC_CONFIG3 just like those in LCD and LCD2 panels. These registers control the Display Controller (DISPC) module for LCD3 output. The three LCDs support Display Serial Interface (DSI), Remote Frame Buffer Interface (RFBI) and Parallel CMOS Output Interface (DPI). These LCDs can be connected through parallel output interface using DISPC and RFBI or DPI. For serial interface DSS uses DSI. The LCD3 panel, just like LCD and LCD2 panels, has a clock switch in DSS_CTRL register which has been enabled. The clock switch chooses between DSS_CLK and DPLL_DSI1_C_CLK1 as source for LCD3_CLK. New IRQs as DISPC_IRQ_VSYNC3, DISPC_IRQ_FRAMEDONE3, DISPC_IRQ_ACBIAS_COUNT_STAT3 and DISPC_IRQ_SYNC_LOST3 have been added specific to the new manager. Signed-off-by: Chandrabhanu Mahapatra Signed-off-by: Tomi Valkeinen --- drivers/video/omap2/dss/dispc.c | 48 ++++++++++++++++++++++++++++++++-- drivers/video/omap2/dss/dispc.h | 2 ++ drivers/video/omap2/dss/dss.c | 12 ++++++--- drivers/video/omap2/dss/dss_features.h | 5 ++-- drivers/video/omap2/dss/manager.c | 4 +++ drivers/video/omap2/dss/overlay.c | 12 ++++++++- include/video/omapdss.h | 4 +++ 7 files changed, 78 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index eb1c9be20d0a..d19665e74e72 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -191,6 +191,23 @@ static const struct { [DISPC_MGR_FLD_FIFOHANDCHECK] = { DISPC_CONFIG2, 16, 16 }, }, }, + [OMAP_DSS_CHANNEL_LCD3] = { + .name = "LCD3", + .vsync_irq = DISPC_IRQ_VSYNC3, + .framedone_irq = DISPC_IRQ_FRAMEDONE3, + .sync_lost_irq = DISPC_IRQ_SYNC_LOST3, + .reg_desc = { + [DISPC_MGR_FLD_ENABLE] = { DISPC_CONTROL3, 0, 0 }, + [DISPC_MGR_FLD_STNTFT] = { DISPC_CONTROL3, 3, 3 }, + [DISPC_MGR_FLD_GO] = { DISPC_CONTROL3, 5, 5 }, + [DISPC_MGR_FLD_TFTDATALINES] = { DISPC_CONTROL3, 9, 8 }, + [DISPC_MGR_FLD_STALLMODE] = { DISPC_CONTROL3, 11, 11 }, + [DISPC_MGR_FLD_TCKENABLE] = { DISPC_CONFIG3, 10, 10 }, + [DISPC_MGR_FLD_TCKSELECTION] = { DISPC_CONFIG3, 11, 11 }, + [DISPC_MGR_FLD_CPR] = { DISPC_CONFIG3, 15, 15 }, + [DISPC_MGR_FLD_FIFOHANDCHECK] = { DISPC_CONFIG3, 16, 16 }, + }, + }, }; static void _omap_dispc_set_irqs(void); @@ -239,6 +256,10 @@ static void dispc_save_context(void) SR(CONTROL2); SR(CONFIG2); } + if (dss_has_feature(FEAT_MGR_LCD3)) { + SR(CONTROL3); + SR(CONFIG3); + } for (i = 0; i < dss_feat_get_num_mgrs(); i++) { SR(DEFAULT_COLOR(i)); @@ -352,6 +373,8 @@ static void dispc_restore_context(void) RR(GLOBAL_ALPHA); if (dss_has_feature(FEAT_MGR_LCD2)) RR(CONFIG2); + if (dss_has_feature(FEAT_MGR_LCD3)) + RR(CONFIG3); for (i = 0; i < dss_feat_get_num_mgrs(); i++) { RR(DEFAULT_COLOR(i)); @@ -437,6 +460,8 @@ static void dispc_restore_context(void) RR(CONTROL); if (dss_has_feature(FEAT_MGR_LCD2)) RR(CONTROL2); + if (dss_has_feature(FEAT_MGR_LCD3)) + RR(CONTROL3); /* clear spurious SYNC_LOST_DIGIT interrupts */ dispc_write_reg(DISPC_IRQSTATUS, DISPC_IRQ_SYNC_LOST_DIGIT); @@ -476,7 +501,8 @@ void dispc_runtime_put(void) static inline bool dispc_mgr_is_lcd(enum omap_channel channel) { if (channel == OMAP_DSS_CHANNEL_LCD || - channel == OMAP_DSS_CHANNEL_LCD2) + channel == OMAP_DSS_CHANNEL_LCD2 || + channel == OMAP_DSS_CHANNEL_LCD3) return true; else return false; @@ -867,6 +893,15 @@ void dispc_ovl_set_channel_out(enum omap_plane plane, enum omap_channel channel) chan = 0; chan2 = 1; break; + case OMAP_DSS_CHANNEL_LCD3: + if (dss_has_feature(FEAT_MGR_LCD3)) { + chan = 0; + chan2 = 2; + } else { + BUG(); + return; + } + break; default: BUG(); return; @@ -902,7 +937,14 @@ static enum omap_channel dispc_ovl_get_channel_out(enum omap_plane plane) val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); - if (dss_has_feature(FEAT_MGR_LCD2)) { + if (dss_has_feature(FEAT_MGR_LCD3)) { + if (FLD_GET(val, 31, 30) == 0) + channel = FLD_GET(val, shift, shift); + else if (FLD_GET(val, 31, 30) == 1) + channel = OMAP_DSS_CHANNEL_LCD2; + else + channel = OMAP_DSS_CHANNEL_LCD3; + } else if (dss_has_feature(FEAT_MGR_LCD2)) { if (FLD_GET(val, 31, 30) == 0) channel = FLD_GET(val, shift, shift); else @@ -3587,6 +3629,8 @@ static void _omap_dispc_initialize_irq(void) dispc.irq_error_mask = DISPC_IRQ_MASK_ERROR; if (dss_has_feature(FEAT_MGR_LCD2)) dispc.irq_error_mask |= DISPC_IRQ_SYNC_LOST2; + if (dss_has_feature(FEAT_MGR_LCD3)) + dispc.irq_error_mask |= DISPC_IRQ_SYNC_LOST3; if (dss_feat_get_num_ovls() > 3) dispc.irq_error_mask |= DISPC_IRQ_VID3_FIFO_UNDERFLOW; diff --git a/drivers/video/omap2/dss/dispc.h b/drivers/video/omap2/dss/dispc.h index 420c980dcbdd..92d8a9be86fc 100644 --- a/drivers/video/omap2/dss/dispc.h +++ b/drivers/video/omap2/dss/dispc.h @@ -36,6 +36,8 @@ #define DISPC_CONTROL2 0x0238 #define DISPC_CONFIG2 0x0620 #define DISPC_DIVISOR 0x0804 +#define DISPC_CONTROL3 0x0848 +#define DISPC_CONFIG3 0x084C /* DISPC overlay registers */ #define DISPC_OVL_BA0(n) (DISPC_OVL_BASE(n) + \ diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c index d2b57197b292..fc0c3ce802e1 100644 --- a/drivers/video/omap2/dss/dss.c +++ b/drivers/video/omap2/dss/dss.c @@ -388,7 +388,8 @@ void dss_select_lcd_clk_source(enum omap_channel channel, dsi_wait_pll_hsdiv_dispc_active(dsidev); break; case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC: - BUG_ON(channel != OMAP_DSS_CHANNEL_LCD2); + BUG_ON(channel != OMAP_DSS_CHANNEL_LCD2 && + channel != OMAP_DSS_CHANNEL_LCD3); b = 1; dsidev = dsi_get_dsidev_from_id(1); dsi_wait_pll_hsdiv_dispc_active(dsidev); @@ -398,10 +399,12 @@ void dss_select_lcd_clk_source(enum omap_channel channel, return; } - pos = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 12; + pos = channel == OMAP_DSS_CHANNEL_LCD ? 0 : + (channel == OMAP_DSS_CHANNEL_LCD2 ? 12 : 19); REG_FLD_MOD(DSS_CONTROL, b, pos, pos); /* LCDx_CLK_SWITCH */ - ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 1; + ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 : + (channel == OMAP_DSS_CHANNEL_LCD2 ? 1 : 2); dss.lcd_clk_source[ix] = clk_src; } @@ -418,7 +421,8 @@ enum omap_dss_clk_source dss_get_dsi_clk_source(int dsi_module) enum omap_dss_clk_source dss_get_lcd_clk_source(enum omap_channel channel) { if (dss_has_feature(FEAT_LCD_CLK_SRC)) { - int ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 1; + int ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 : + (channel == OMAP_DSS_CHANNEL_LCD2 ? 1 : 2); return dss.lcd_clk_source[ix]; } else { /* LCD_CLK source is the same as DISPC_FCLK source for diff --git a/drivers/video/omap2/dss/dss_features.h b/drivers/video/omap2/dss/dss_features.h index bdf469f080e7..996ffcbfed58 100644 --- a/drivers/video/omap2/dss/dss_features.h +++ b/drivers/video/omap2/dss/dss_features.h @@ -24,9 +24,9 @@ #include "ti_hdmi.h" #endif -#define MAX_DSS_MANAGERS 3 +#define MAX_DSS_MANAGERS 4 #define MAX_DSS_OVERLAYS 4 -#define MAX_DSS_LCD_MANAGERS 2 +#define MAX_DSS_LCD_MANAGERS 3 #define MAX_NUM_DSI 2 /* DSS has feature id */ @@ -36,6 +36,7 @@ enum dss_feat_id { FEAT_PCKFREEENABLE, FEAT_FUNCGATED, FEAT_MGR_LCD2, + FEAT_MGR_LCD3, FEAT_LINEBUFFERSPLIT, FEAT_ROWREPEATENABLE, FEAT_RESIZECONF, diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c index bb602a2d57c1..a51eb060c142 100644 --- a/drivers/video/omap2/dss/manager.c +++ b/drivers/video/omap2/dss/manager.c @@ -541,6 +541,10 @@ int dss_init_overlay_managers(struct platform_device *pdev) mgr->name = "lcd2"; mgr->id = OMAP_DSS_CHANNEL_LCD2; break; + case 3: + mgr->name = "lcd3"; + mgr->id = OMAP_DSS_CHANNEL_LCD3; + break; } mgr->set_device = &dss_mgr_set_device; diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c index c492bb074d0f..ec69325a118a 100644 --- a/drivers/video/omap2/dss/overlay.c +++ b/drivers/video/omap2/dss/overlay.c @@ -528,14 +528,24 @@ void dss_recheck_connections(struct omap_dss_device *dssdev, bool force) struct omap_overlay_manager *lcd_mgr; struct omap_overlay_manager *tv_mgr; struct omap_overlay_manager *lcd2_mgr = NULL; + struct omap_overlay_manager *lcd3_mgr = NULL; struct omap_overlay_manager *mgr = NULL; lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_LCD); tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_DIGIT); + if (dss_has_feature(FEAT_MGR_LCD3)) + lcd3_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_LCD3); if (dss_has_feature(FEAT_MGR_LCD2)) lcd2_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_LCD2); - if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) { + if (dssdev->channel == OMAP_DSS_CHANNEL_LCD3) { + if (!lcd3_mgr->device || force) { + if (lcd3_mgr->device) + lcd3_mgr->unset_device(lcd3_mgr); + lcd3_mgr->set_device(lcd3_mgr, dssdev); + mgr = lcd3_mgr; + } + } else if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) { if (!lcd2_mgr->device || force) { if (lcd2_mgr->device) lcd2_mgr->unset_device(lcd2_mgr); diff --git a/include/video/omapdss.h b/include/video/omapdss.h index 23887b358a49..117de0e695f4 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -48,6 +48,10 @@ #define DISPC_IRQ_FRAMEDONEWB (1 << 23) #define DISPC_IRQ_FRAMEDONETV (1 << 24) #define DISPC_IRQ_WBBUFFEROVERFLOW (1 << 25) +#define DISPC_IRQ_FRAMEDONE3 (1 << 26) +#define DISPC_IRQ_VSYNC3 (1 << 27) +#define DISPC_IRQ_ACBIAS_COUNT_STAT3 (1 << 28) +#define DISPC_IRQ_SYNC_LOST3 (1 << 29) struct omap_dss_device; struct omap_overlay_manager; -- cgit v1.2.3 From 5ae9eaa6dbeccab781cd9312371fad801a5ba1a2 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 21 Jun 2012 09:41:10 +0530 Subject: OMAPDSS: Remove passive matrix LCD support (part 2) Remove OMAP_DSS_LCD_TFT as a omap_panel_config flag. We don't support passive matrix displays any more. Remove this flag from all the panel drivers. Force the display_type to OMAP_DSS_LCD_DISPLAY_TFT in the interface drivers. Signed-off-by: Archit Taneja --- drivers/video/omap2/displays/panel-acx565akm.c | 4 +- drivers/video/omap2/displays/panel-generic-dpi.c | 61 +++++++++------------- .../omap2/displays/panel-lgphilips-lb035q02.c | 3 +- drivers/video/omap2/displays/panel-n8x0.c | 1 - .../omap2/displays/panel-nec-nl8048hl11-01b.c | 5 +- drivers/video/omap2/displays/panel-picodlp.c | 4 +- .../video/omap2/displays/panel-sharp-ls037v7dw01.c | 3 +- drivers/video/omap2/displays/panel-taal.c | 1 - drivers/video/omap2/displays/panel-tfp410.c | 1 - .../video/omap2/displays/panel-tpo-td043mtea1.c | 4 +- drivers/video/omap2/dss/display.c | 4 -- drivers/video/omap2/dss/dpi.c | 8 +-- drivers/video/omap2/dss/hdmi_panel.c | 3 +- include/video/omapdss.h | 2 - 14 files changed, 37 insertions(+), 67 deletions(-) (limited to 'include') diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c index ad741c3d1ae1..9dc21ddc93da 100644 --- a/drivers/video/omap2/displays/panel-acx565akm.c +++ b/drivers/video/omap2/displays/panel-acx565akm.c @@ -498,8 +498,8 @@ static int acx_panel_probe(struct omap_dss_device *dssdev) struct backlight_properties props; dev_dbg(&dssdev->dev, "%s\n", __func__); - dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS; + dssdev->panel.config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; + /* FIXME AC bias ? */ dssdev->panel.timings = acx_panel_timings; diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c index e42f9dc22123..c17ba743792a 100644 --- a/drivers/video/omap2/displays/panel-generic-dpi.c +++ b/drivers/video/omap2/displays/panel-generic-dpi.c @@ -76,8 +76,8 @@ static struct panel_config generic_dpi_panels[] = { }, .acbi = 0x0, .acb = 0x0, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | + OMAP_DSS_LCD_IEO, .power_on_delay = 50, .power_off_delay = 100, .name = "sharp_lq", @@ -101,8 +101,7 @@ static struct panel_config generic_dpi_panels[] = { }, .acbi = 0x0, .acb = 0x28, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .power_on_delay = 50, .power_off_delay = 100, .name = "sharp_ls", @@ -126,9 +125,9 @@ static struct panel_config generic_dpi_panels[] = { }, .acbi = 0x0, .acb = 0x0, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC | - OMAP_DSS_LCD_ONOFF, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | + OMAP_DSS_LCD_IPC | + OMAP_DSS_LCD_ONOFF, .power_on_delay = 0, .power_off_delay = 0, .name = "toppoly_tdo35s", @@ -152,8 +151,7 @@ static struct panel_config generic_dpi_panels[] = { }, .acbi = 0x0, .acb = 0x0, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .power_on_delay = 0, .power_off_delay = 0, .name = "samsung_lte430wq_f0c", @@ -177,8 +175,7 @@ static struct panel_config generic_dpi_panels[] = { }, .acbi = 0x0, .acb = 0x0, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .power_on_delay = 0, .power_off_delay = 0, .name = "seiko_70wvw1tz3", @@ -202,8 +199,8 @@ static struct panel_config generic_dpi_panels[] = { }, .acbi = 0x0, .acb = 0x0, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | + OMAP_DSS_LCD_IEO, .power_on_delay = 0, .power_off_delay = 0, .name = "powertip_ph480272t", @@ -227,8 +224,7 @@ static struct panel_config generic_dpi_panels[] = { }, .acbi = 0x0, .acb = 0x28, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .power_on_delay = 0, .power_off_delay = 0, .name = "innolux_at070tn83", @@ -250,8 +246,7 @@ static struct panel_config generic_dpi_panels[] = { .vfp = 2, .vbp = 7, }, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .name = "nec_nl2432dr22-11b", }, @@ -271,8 +266,6 @@ static struct panel_config generic_dpi_panels[] = { .vfp = 1, .vbp = 1, }, - .config = OMAP_DSS_LCD_TFT, - .name = "h4", }, @@ -292,8 +285,7 @@ static struct panel_config generic_dpi_panels[] = { .vfp = 2, .vbp = 2, }, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .name = "apollon", }, @@ -313,8 +305,7 @@ static struct panel_config generic_dpi_panels[] = { .vfp = 13, .vbp = 29, }, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .name = "focaltech_etm070003dh6", }, @@ -336,8 +327,8 @@ static struct panel_config generic_dpi_panels[] = { }, .acbi = 0x0, .acb = 0x0, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | + OMAP_DSS_LCD_IPC, .power_on_delay = 0, .power_off_delay = 0, .name = "microtips_umsh_8173md", @@ -359,8 +350,6 @@ static struct panel_config generic_dpi_panels[] = { .vfp = 4, .vbp = 2, }, - .config = OMAP_DSS_LCD_TFT, - .name = "ortustech_com43h4m10xtc", }, @@ -381,8 +370,8 @@ static struct panel_config generic_dpi_panels[] = { .vbp = 23, }, .acb = 0x0, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | + OMAP_DSS_LCD_IEO, .name = "innolux_at080tn52", }, @@ -402,7 +391,6 @@ static struct panel_config generic_dpi_panels[] = { .vfp = 26, .vbp = 1, }, - .config = OMAP_DSS_LCD_TFT, .name = "mitsubishi_aa084sb01", }, /* EDT ET0500G0DH6 */ @@ -420,7 +408,6 @@ static struct panel_config generic_dpi_panels[] = { .vfp = 35, .vbp = 10, }, - .config = OMAP_DSS_LCD_TFT, .name = "edt_et0500g0dh6", }, @@ -440,8 +427,8 @@ static struct panel_config generic_dpi_panels[] = { .vfp = 10, .vbp = 33, }, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | + OMAP_DSS_LCD_IPC, .name = "primeview_pd050vl1", }, @@ -461,8 +448,8 @@ static struct panel_config generic_dpi_panels[] = { .vfp = 10, .vbp = 33, }, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | + OMAP_DSS_LCD_IPC, .name = "primeview_pm070wl4", }, @@ -482,8 +469,8 @@ static struct panel_config generic_dpi_panels[] = { .vfp = 1, .vbp = 23, }, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | + OMAP_DSS_LCD_IPC, .name = "primeview_pd104slf", }, }; diff --git a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c index 0841cc2b3f77..3b1877ce7983 100644 --- a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c +++ b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c @@ -82,8 +82,7 @@ static int lb035q02_panel_probe(struct omap_dss_device *dssdev) struct lb035q02_data *ld; int r; - dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS; + dssdev->panel.config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; dssdev->panel.timings = lb035q02_timings; ld = kzalloc(sizeof(*ld), GFP_KERNEL); diff --git a/drivers/video/omap2/displays/panel-n8x0.c b/drivers/video/omap2/displays/panel-n8x0.c index 4a34cdc1371b..e6c115373c00 100644 --- a/drivers/video/omap2/displays/panel-n8x0.c +++ b/drivers/video/omap2/displays/panel-n8x0.c @@ -473,7 +473,6 @@ static int n8x0_panel_probe(struct omap_dss_device *dssdev) mutex_init(&ddata->lock); - dssdev->panel.config = OMAP_DSS_LCD_TFT; dssdev->panel.timings.x_res = 800; dssdev->panel.timings.y_res = 480; dssdev->ctrl.pixel_size = 16; diff --git a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c index 8b38b39213f4..531440875054 100644 --- a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c +++ b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c @@ -116,9 +116,8 @@ static int nec_8048_panel_probe(struct omap_dss_device *dssdev) struct backlight_properties props; int r; - dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_RF | - OMAP_DSS_LCD_ONOFF; + dssdev->panel.config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | + OMAP_DSS_LCD_RF | OMAP_DSS_LCD_ONOFF; dssdev->panel.timings = nec_8048_panel_timings; necd = kzalloc(sizeof(*necd), GFP_KERNEL); diff --git a/drivers/video/omap2/displays/panel-picodlp.c b/drivers/video/omap2/displays/panel-picodlp.c index 98ebdaddab5a..6563d85e00e4 100644 --- a/drivers/video/omap2/displays/panel-picodlp.c +++ b/drivers/video/omap2/displays/panel-picodlp.c @@ -414,8 +414,8 @@ static int picodlp_panel_probe(struct omap_dss_device *dssdev) struct i2c_client *picodlp_i2c_client; int r = 0, picodlp_adapter_id; - dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_ONOFF | - OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IVS; + dssdev->panel.config = OMAP_DSS_LCD_ONOFF | OMAP_DSS_LCD_IHS | + OMAP_DSS_LCD_IVS; dssdev->panel.acb = 0x0; dssdev->panel.timings = pico_ls_timings; diff --git a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c index ba38b3ad17d6..d71386a864f5 100644 --- a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c +++ b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c @@ -86,8 +86,7 @@ static int sharp_ls_panel_probe(struct omap_dss_device *dssdev) struct sharp_data *sd; int r; - dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS; + dssdev->panel.config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; dssdev->panel.acb = 0x28; dssdev->panel.timings = sharp_ls_timings; diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c index 901576eb5a84..3f5acc7771da 100644 --- a/drivers/video/omap2/displays/panel-taal.c +++ b/drivers/video/omap2/displays/panel-taal.c @@ -882,7 +882,6 @@ static int taal_probe(struct omap_dss_device *dssdev) goto err; } - dssdev->panel.config = OMAP_DSS_LCD_TFT; dssdev->panel.timings = panel_config->timings; dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888; diff --git a/drivers/video/omap2/displays/panel-tfp410.c b/drivers/video/omap2/displays/panel-tfp410.c index bff306e041ca..ab5b1d43ca10 100644 --- a/drivers/video/omap2/displays/panel-tfp410.c +++ b/drivers/video/omap2/displays/panel-tfp410.c @@ -95,7 +95,6 @@ static int tfp410_probe(struct omap_dss_device *dssdev) return -ENOMEM; dssdev->panel.timings = tfp410_default_timings; - dssdev->panel.config = OMAP_DSS_LCD_TFT; ddata->dssdev = dssdev; mutex_init(&ddata->lock); diff --git a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c index 4b6448b3c31f..d9585c895052 100644 --- a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c +++ b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c @@ -423,8 +423,8 @@ static int tpo_td043_probe(struct omap_dss_device *dssdev) return -ENODEV; } - dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IHS | - OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IPC; + dssdev->panel.config = OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IPC; dssdev->panel.timings = tpo_td043_timings; dssdev->ctrl.pixel_size = 24; diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c index 249010630370..25be51e22827 100644 --- a/drivers/video/omap2/dss/display.c +++ b/drivers/video/omap2/dss/display.c @@ -327,10 +327,6 @@ bool dss_use_replication(struct omap_dss_device *dssdev, if (mode != OMAP_DSS_COLOR_RGB12U && mode != OMAP_DSS_COLOR_RGB16) return false; - if (dssdev->type == OMAP_DISPLAY_TYPE_DPI && - (dssdev->panel.config & OMAP_DSS_LCD_TFT) == 0) - return false; - switch (dssdev->type) { case OMAP_DISPLAY_TYPE_DPI: bpp = dssdev->phy.dpi.data_lines; diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c index 09dd2435df11..e6904b2b9fec 100644 --- a/drivers/video/omap2/dss/dpi.c +++ b/drivers/video/omap2/dss/dpi.c @@ -160,15 +160,11 @@ static int dpi_set_mode(struct omap_dss_device *dssdev) static void dpi_basic_init(struct omap_dss_device *dssdev) { - bool is_tft; - - is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; - dispc_mgr_set_io_pad_mode(DSS_IO_PAD_MODE_BYPASS); dispc_mgr_enable_stallmode(dssdev->manager->id, false); - dispc_mgr_set_lcd_display_type(dssdev->manager->id, is_tft ? - OMAP_DSS_LCD_DISPLAY_TFT : OMAP_DSS_LCD_DISPLAY_STN); + dispc_mgr_set_lcd_display_type(dssdev->manager->id, + OMAP_DSS_LCD_DISPLAY_TFT); dispc_mgr_set_tft_data_lines(dssdev->manager->id, dssdev->phy.dpi.data_lines); } diff --git a/drivers/video/omap2/dss/hdmi_panel.c b/drivers/video/omap2/dss/hdmi_panel.c index 1179e3c4b1c7..788ebf001cb0 100644 --- a/drivers/video/omap2/dss/hdmi_panel.c +++ b/drivers/video/omap2/dss/hdmi_panel.c @@ -43,8 +43,7 @@ static int hdmi_panel_probe(struct omap_dss_device *dssdev) { DSSDBG("ENTER hdmi_panel_probe\n"); - dssdev->panel.config = OMAP_DSS_LCD_TFT | - OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; + dssdev->panel.config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; dssdev->panel.timings = (struct omap_video_timings){640, 480, 25175, 96, 16, 48, 2 , 11, 31}; diff --git a/include/video/omapdss.h b/include/video/omapdss.h index 117de0e695f4..12bc2dfdcdc4 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -133,8 +133,6 @@ enum omap_panel_config { OMAP_DSS_LCD_IEO = 1<<3, OMAP_DSS_LCD_RF = 1<<4, OMAP_DSS_LCD_ONOFF = 1<<5, - - OMAP_DSS_LCD_TFT = 1<<20, }; enum omap_dss_venc_type { -- cgit v1.2.3 From d21f43bc392911acf01b7f2090615df4ca09ac7d Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 21 Jun 2012 09:45:11 +0530 Subject: OMAPDSS: Remove passive matrix LCD support (part 3) Remove omap_lcd_display_type enum The enum omap_lcd_display_type is used to configure the lcd display type in DISPC. Remove this enum and always set display type to TFT by creating function dss_mgr_set_lcd_type_tft(). Signed-off-by: Archit Taneja --- drivers/video/omap2/dss/dispc.c | 21 ++------------------- drivers/video/omap2/dss/dpi.c | 4 ++-- drivers/video/omap2/dss/dsi.c | 9 +++++---- drivers/video/omap2/dss/dss.h | 3 +-- drivers/video/omap2/dss/rfbi.c | 3 +-- drivers/video/omap2/dss/sdi.c | 3 +-- include/video/omapdss.h | 5 ----- 7 files changed, 12 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index 6e25624b3faf..c017d22cad48 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -2490,26 +2490,9 @@ void dispc_mgr_enable_fifohandcheck(enum omap_channel channel, bool enable) } -void dispc_mgr_set_lcd_display_type(enum omap_channel channel, - enum omap_lcd_display_type type) +void dispc_mgr_set_lcd_type_tft(enum omap_channel channel) { - int mode; - - switch (type) { - case OMAP_DSS_LCD_DISPLAY_STN: - mode = 0; - break; - - case OMAP_DSS_LCD_DISPLAY_TFT: - mode = 1; - break; - - default: - BUG(); - return; - } - - mgr_fld_write(channel, DISPC_MGR_FLD_STNTFT, mode); + mgr_fld_write(channel, DISPC_MGR_FLD_STNTFT, 1); } void dispc_set_loadmode(enum omap_dss_load_mode mode) diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c index e6904b2b9fec..45fb1a673a2a 100644 --- a/drivers/video/omap2/dss/dpi.c +++ b/drivers/video/omap2/dss/dpi.c @@ -163,8 +163,8 @@ static void dpi_basic_init(struct omap_dss_device *dssdev) dispc_mgr_set_io_pad_mode(DSS_IO_PAD_MODE_BYPASS); dispc_mgr_enable_stallmode(dssdev->manager->id, false); - dispc_mgr_set_lcd_display_type(dssdev->manager->id, - OMAP_DSS_LCD_DISPLAY_TFT); + dispc_mgr_set_lcd_type_tft(dssdev->manager->id); + dispc_mgr_set_tft_data_lines(dssdev->manager->id, dssdev->phy.dpi.data_lines); } diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index aa3b81088a23..06b578036497 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c @@ -4380,10 +4380,11 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev) dss_mgr_set_timings(dssdev->manager, &dssdev->panel.timings); } - dispc_mgr_set_lcd_display_type(dssdev->manager->id, - OMAP_DSS_LCD_DISPLAY_TFT); - dispc_mgr_set_tft_data_lines(dssdev->manager->id, - dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt)); + dispc_mgr_set_lcd_type_tft(dssdev->manager->id); + + dispc_mgr_set_tft_data_lines(dssdev->manager->id, + dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt)); + return 0; } diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h index 88e1c12195e5..692d6501b4e8 100644 --- a/drivers/video/omap2/dss/dss.h +++ b/drivers/video/omap2/dss/dss.h @@ -420,8 +420,7 @@ bool dispc_mgr_is_channel_enabled(enum omap_channel channel); void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode); void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable); void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines); -void dispc_mgr_set_lcd_display_type(enum omap_channel channel, - enum omap_lcd_display_type type); +void dispc_mgr_set_lcd_type_tft(enum omap_channel channel); void dispc_mgr_set_timings(enum omap_channel channel, struct omap_video_timings *timings); void dispc_mgr_set_pol_freq(enum omap_channel channel, diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c index 7985fa12b9b4..539d709c6c0e 100644 --- a/drivers/video/omap2/dss/rfbi.c +++ b/drivers/video/omap2/dss/rfbi.c @@ -885,8 +885,7 @@ int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev) goto err1; } - dispc_mgr_set_lcd_display_type(dssdev->manager->id, - OMAP_DSS_LCD_DISPLAY_TFT); + dispc_mgr_set_lcd_type_tft(dssdev->manager->id); dispc_mgr_set_io_pad_mode(DSS_IO_PAD_MODE_RFBI); dispc_mgr_enable_stallmode(dssdev->manager->id, true); diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c index d07ecc44603e..1a369decad38 100644 --- a/drivers/video/omap2/dss/sdi.c +++ b/drivers/video/omap2/dss/sdi.c @@ -40,8 +40,7 @@ static void sdi_basic_init(struct omap_dss_device *dssdev) dispc_mgr_set_io_pad_mode(DSS_IO_PAD_MODE_BYPASS); dispc_mgr_enable_stallmode(dssdev->manager->id, false); - dispc_mgr_set_lcd_display_type(dssdev->manager->id, - OMAP_DSS_LCD_DISPLAY_TFT); + dispc_mgr_set_lcd_type_tft(dssdev->manager->id); dispc_mgr_set_tft_data_lines(dssdev->manager->id, 24); dispc_lcd_enable_signal_polarity(1); diff --git a/include/video/omapdss.h b/include/video/omapdss.h index 12bc2dfdcdc4..115bbd849806 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -104,11 +104,6 @@ enum omap_color_mode { OMAP_DSS_COLOR_XRGB16_1555 = 1 << 18, /* xRGB16 - 1555 */ }; -enum omap_lcd_display_type { - OMAP_DSS_LCD_DISPLAY_STN, - OMAP_DSS_LCD_DISPLAY_TFT, -}; - enum omap_dss_load_mode { OMAP_DSS_LOAD_CLUT_AND_FRAME = 0, OMAP_DSS_LOAD_CLUT_ONLY = 1, -- cgit v1.2.3 From a8d5e41cef43bd740ca7c56ff316bdee30040a91 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Mon, 25 Jun 2012 12:26:38 +0530 Subject: OMAPDSS: Add some new fields to omap_video_timings Some panel timing related fields are contained in omap_panel_config in the form of flags. The fields are: - Hsync logic level - Vsync logic level - Data driven on rising/falling edge of pixel clock - Output enable/Data enable logic level - HSYNC/VSYNC driven on rising/falling edge of pixel clock Out of these parameters, Hsync and Vsync logic levels are a part of the timings in the Xorg modeline configuration. So it makes sense to move the to omap_video_timings. The rest aren't a part of modeline, but it still makes sense to move these since they are related to panel timings. These fields stored in omap_panel_config in dssdev are configured for LCD panels, and the corresponding LCD managers in the DISPC_POL_FREQo registers. Add the above fields in omap_video_timings. Represent their state via new enums. Add these parameters to the omap_video_timings instances in the panel drivers. Keep the corresponding IVS, IHS, IPC, IEO, RF and ONOFF flags in omap_panel_config for now. The struct will be removed later. Signed-off-by: Archit Taneja --- drivers/video/omap2/displays/panel-acx565akm.c | 7 ++ drivers/video/omap2/displays/panel-generic-dpi.c | 114 +++++++++++++++++++++ .../omap2/displays/panel-lgphilips-lb035q02.c | 6 ++ .../omap2/displays/panel-nec-nl8048hl11-01b.c | 6 ++ drivers/video/omap2/displays/panel-picodlp.c | 6 ++ .../video/omap2/displays/panel-sharp-ls037v7dw01.c | 6 ++ drivers/video/omap2/displays/panel-tfp410.c | 6 ++ .../video/omap2/displays/panel-tpo-td043mtea1.c | 6 ++ drivers/video/omap2/dss/hdmi_panel.c | 5 +- drivers/video/omap2/dss/sdi.c | 3 + include/video/omapdss.h | 22 ++++ 11 files changed, 186 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c index 9dc21ddc93da..acfaa40b932e 100644 --- a/drivers/video/omap2/displays/panel-acx565akm.c +++ b/drivers/video/omap2/displays/panel-acx565akm.c @@ -487,6 +487,13 @@ static struct omap_video_timings acx_panel_timings = { .vfp = 3, .vsw = 3, .vbp = 4, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }; static int acx_panel_probe(struct omap_dss_device *dssdev) diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c index 0534926dfc74..6cdd97c64330 100644 --- a/drivers/video/omap2/displays/panel-generic-dpi.c +++ b/drivers/video/omap2/displays/panel-generic-dpi.c @@ -69,6 +69,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 11, .vfp = 3, .vbp = 2, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_LOW, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO, @@ -92,6 +98,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 1, .vfp = 1, .vbp = 1, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .power_on_delay = 50, @@ -114,6 +126,12 @@ static struct panel_config generic_dpi_panels[] = { .vfp = 4, .vsw = 2, .vbp = 2, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC | @@ -138,6 +156,12 @@ static struct panel_config generic_dpi_panels[] = { .vfp = 4, .vsw = 10, .vbp = 12 - 10, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .power_on_delay = 0, @@ -160,6 +184,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 2, .vfp = 4, .vbp = 11, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .power_on_delay = 0, @@ -182,6 +212,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 10, .vfp = 2, .vbp = 2, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_LOW, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO, @@ -205,6 +241,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 3, .vfp = 12, .vbp = 25, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .power_on_delay = 0, @@ -227,6 +269,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 1, .vfp = 2, .vbp = 7, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .name = "nec_nl2432dr22-11b", @@ -247,6 +295,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 1, .vfp = 1, .vbp = 1, + + .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH, + .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .name = "h4", }, @@ -266,6 +320,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 10, .vfp = 2, .vbp = 2, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, @@ -286,6 +346,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 3, .vfp = 13, .vbp = 29, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .name = "focaltech_etm070003dh6", @@ -306,6 +372,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 23, .vfp = 1, .vbp = 1, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, @@ -329,6 +401,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 10, .vfp = 4, .vbp = 2, + + .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH, + .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .name = "ortustech_com43h4m10xtc", }, @@ -348,6 +426,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 10, .vfp = 12, .vbp = 23, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_LOW, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO, @@ -369,6 +453,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 1, .vfp = 26, .vbp = 1, + + .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH, + .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .name = "mitsubishi_aa084sb01", }, @@ -386,6 +476,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 2, .vfp = 35, .vbp = 10, + + .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH, + .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .name = "edt_et0500g0dh6", }, @@ -405,6 +501,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 2, .vfp = 10, .vbp = 33, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, @@ -426,6 +528,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 2, .vfp = 10, .vbp = 33, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, @@ -447,6 +555,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 4, .vfp = 1, .vbp = 23, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, diff --git a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c index 3b1877ce7983..fc57ed63b08b 100644 --- a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c +++ b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c @@ -40,6 +40,12 @@ static struct omap_video_timings lb035q02_timings = { .vsw = 2, .vfp = 4, .vbp = 18, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }; static int lb035q02_panel_power_on(struct omap_dss_device *dssdev) diff --git a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c index 531440875054..9eab0dda2f33 100644 --- a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c +++ b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c @@ -76,6 +76,12 @@ static struct omap_video_timings nec_8048_panel_timings = { .vfp = 3, .vsw = 1, .vbp = 4, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, }; static int nec_8048_bl_update_status(struct backlight_device *bl) diff --git a/drivers/video/omap2/displays/panel-picodlp.c b/drivers/video/omap2/displays/panel-picodlp.c index 118e76b7b440..9363cf4ab740 100644 --- a/drivers/video/omap2/displays/panel-picodlp.c +++ b/drivers/video/omap2/displays/panel-picodlp.c @@ -69,6 +69,12 @@ static struct omap_video_timings pico_ls_timings = { .vsw = 2, .vfp = 3, .vbp = 14, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, }; static inline struct picodlp_panel_data diff --git a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c index 4285849d27b3..6b7b43c808e2 100644 --- a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c +++ b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c @@ -44,6 +44,12 @@ static struct omap_video_timings sharp_ls_timings = { .vsw = 1, .vfp = 1, .vbp = 1, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }; static int sharp_ls_bl_update_status(struct backlight_device *bl) diff --git a/drivers/video/omap2/displays/panel-tfp410.c b/drivers/video/omap2/displays/panel-tfp410.c index ab5b1d43ca10..40cc0cfa5d17 100644 --- a/drivers/video/omap2/displays/panel-tfp410.c +++ b/drivers/video/omap2/displays/panel-tfp410.c @@ -39,6 +39,12 @@ static const struct omap_video_timings tfp410_default_timings = { .vfp = 3, .vsw = 4, .vbp = 7, + + .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH, + .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }; struct panel_drv_data { diff --git a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c index d9585c895052..e627c6c5c551 100644 --- a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c +++ b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c @@ -267,6 +267,12 @@ static const struct omap_video_timings tpo_td043_timings = { .vsw = 1, .vfp = 39, .vbp = 34, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }; static int tpo_td043_power_on(struct tpo_td043_device *tpo_td043) diff --git a/drivers/video/omap2/dss/hdmi_panel.c b/drivers/video/omap2/dss/hdmi_panel.c index 788ebf001cb0..5bf3a84d89cd 100644 --- a/drivers/video/omap2/dss/hdmi_panel.c +++ b/drivers/video/omap2/dss/hdmi_panel.c @@ -45,7 +45,10 @@ static int hdmi_panel_probe(struct omap_dss_device *dssdev) dssdev->panel.config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; - dssdev->panel.timings = (struct omap_video_timings){640, 480, 25175, 96, 16, 48, 2 , 11, 31}; + dssdev->panel.timings = (struct omap_video_timings) + { 640, 480, 25175, 96, 16, 48, 2, 11, 31, + OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW, + }; DSSDBG("hdmi_panel_probe x_res= %d y_res = %d\n", dssdev->panel.timings.x_res, diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c index 5268fdba955d..4f43537d0214 100644 --- a/drivers/video/omap2/dss/sdi.c +++ b/drivers/video/omap2/dss/sdi.c @@ -80,6 +80,9 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) /* 15.5.9.1.2 */ dssdev->panel.config |= OMAP_DSS_LCD_RF | OMAP_DSS_LCD_ONOFF; + dssdev->panel.timings.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; + dssdev->panel.timings.sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; + dispc_mgr_set_pol_freq(dssdev->manager->id, dssdev->panel.config); r = dss_calc_clock_div(t->pixel_clock * 1000, &dss_cinfo, &dispc_cinfo); diff --git a/include/video/omapdss.h b/include/video/omapdss.h index 115bbd849806..be6590dc66a5 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -130,6 +130,17 @@ enum omap_panel_config { OMAP_DSS_LCD_ONOFF = 1<<5, }; +enum omap_dss_signal_level { + OMAPDSS_SIG_ACTIVE_HIGH = 0, + OMAPDSS_SIG_ACTIVE_LOW = 1, +}; + +enum omap_dss_signal_edge { + OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, + OMAPDSS_DRIVE_SIG_RISING_EDGE, + OMAPDSS_DRIVE_SIG_FALLING_EDGE, +}; + enum omap_dss_venc_type { OMAP_DSS_VENC_TYPE_COMPOSITE, OMAP_DSS_VENC_TYPE_SVIDEO, @@ -337,6 +348,17 @@ struct omap_video_timings { u16 vfp; /* Vertical front porch */ /* Unit: line clocks */ u16 vbp; /* Vertical back porch */ + + /* Vsync logic level */ + enum omap_dss_signal_level vsync_level; + /* Hsync logic level */ + enum omap_dss_signal_level hsync_level; + /* Pixel clock edge to drive LCD data */ + enum omap_dss_signal_edge data_pclk_edge; + /* Data enable logic level */ + enum omap_dss_signal_level de_level; + /* Pixel clock edges to drive HSYNC and VSYNC signals */ + enum omap_dss_signal_edge sync_pclk_edge; }; #ifdef CONFIG_OMAP2_DSS_VENC -- cgit v1.2.3 From 07fb51c6bda74210b57a06e6dc901a6b0f04c09a Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Fri, 29 Jun 2012 12:13:32 +0530 Subject: OMAPDSS: Remove omap_panel_config enum from omap_dss_device omap_panel_config contains fields which are finally written to DISPC_POL_FREQo registers. These are now held by omap_video_timings and are set when the manager timings are applied. Remove the omap_panel_config enum, and remove all it's references from panel or interface drivers. Signed-off-by: Archit Taneja --- drivers/video/omap2/displays/panel-acx565akm.c | 1 - drivers/video/omap2/displays/panel-generic-dpi.c | 29 ---------------------- .../omap2/displays/panel-lgphilips-lb035q02.c | 1 - .../omap2/displays/panel-nec-nl8048hl11-01b.c | 2 -- drivers/video/omap2/displays/panel-picodlp.c | 2 -- .../video/omap2/displays/panel-sharp-ls037v7dw01.c | 1 - .../video/omap2/displays/panel-tpo-td043mtea1.c | 2 -- drivers/video/omap2/dss/hdmi_panel.c | 2 -- drivers/video/omap2/dss/sdi.c | 2 -- include/video/omapdss.h | 11 -------- 10 files changed, 53 deletions(-) (limited to 'include') diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c index acfaa40b932e..eaeed4340e04 100644 --- a/drivers/video/omap2/displays/panel-acx565akm.c +++ b/drivers/video/omap2/displays/panel-acx565akm.c @@ -505,7 +505,6 @@ static int acx_panel_probe(struct omap_dss_device *dssdev) struct backlight_properties props; dev_dbg(&dssdev->dev, "%s\n", __func__); - dssdev->panel.config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; /* FIXME AC bias ? */ dssdev->panel.timings = acx_panel_timings; diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c index 6cdd97c64330..bc5af2500eb9 100644 --- a/drivers/video/omap2/displays/panel-generic-dpi.c +++ b/drivers/video/omap2/displays/panel-generic-dpi.c @@ -40,8 +40,6 @@ struct panel_config { struct omap_video_timings timings; - enum omap_panel_config config; - int power_on_delay; int power_off_delay; @@ -76,8 +74,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_LOW, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | - OMAP_DSS_LCD_IEO, .power_on_delay = 50, .power_off_delay = 100, .name = "sharp_lq", @@ -105,7 +101,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_HIGH, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .power_on_delay = 50, .power_off_delay = 100, .name = "sharp_ls", @@ -133,9 +128,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_HIGH, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | - OMAP_DSS_LCD_IPC | - OMAP_DSS_LCD_ONOFF, .power_on_delay = 0, .power_off_delay = 0, .name = "toppoly_tdo35s", @@ -163,7 +155,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_HIGH, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .power_on_delay = 0, .power_off_delay = 0, .name = "samsung_lte430wq_f0c", @@ -191,7 +182,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_HIGH, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .power_on_delay = 0, .power_off_delay = 0, .name = "seiko_70wvw1tz3", @@ -219,8 +209,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_LOW, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | - OMAP_DSS_LCD_IEO, .power_on_delay = 0, .power_off_delay = 0, .name = "powertip_ph480272t", @@ -248,7 +236,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_HIGH, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .power_on_delay = 0, .power_off_delay = 0, .name = "innolux_at070tn83", @@ -276,7 +263,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_HIGH, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .name = "nec_nl2432dr22-11b", }, @@ -327,8 +313,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_HIGH, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, - .name = "apollon", }, /* FocalTech ETM070003DH6 */ @@ -353,7 +337,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_HIGH, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .name = "focaltech_etm070003dh6", }, @@ -379,8 +362,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_HIGH, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | - OMAP_DSS_LCD_IPC, .power_on_delay = 0, .power_off_delay = 0, .name = "microtips_umsh_8173md", @@ -433,9 +414,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_LOW, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | - OMAP_DSS_LCD_IEO, - .name = "innolux_at080tn52", }, @@ -508,8 +486,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_HIGH, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | - OMAP_DSS_LCD_IPC, .name = "primeview_pd050vl1", }, @@ -535,8 +511,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_HIGH, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | - OMAP_DSS_LCD_IPC, .name = "primeview_pm070wl4", }, @@ -562,8 +536,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_HIGH, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | - OMAP_DSS_LCD_IPC, .name = "primeview_pd104slf", }, }; @@ -653,7 +625,6 @@ static int generic_dpi_panel_probe(struct omap_dss_device *dssdev) if (!panel_config) return -EINVAL; - dssdev->panel.config = panel_config->config; dssdev->panel.timings = panel_config->timings; drv_data = kzalloc(sizeof(*drv_data), GFP_KERNEL); diff --git a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c index fc57ed63b08b..802807798846 100644 --- a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c +++ b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c @@ -88,7 +88,6 @@ static int lb035q02_panel_probe(struct omap_dss_device *dssdev) struct lb035q02_data *ld; int r; - dssdev->panel.config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; dssdev->panel.timings = lb035q02_timings; ld = kzalloc(sizeof(*ld), GFP_KERNEL); diff --git a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c index 9eab0dda2f33..b122b0f31c43 100644 --- a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c +++ b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c @@ -122,8 +122,6 @@ static int nec_8048_panel_probe(struct omap_dss_device *dssdev) struct backlight_properties props; int r; - dssdev->panel.config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | - OMAP_DSS_LCD_RF | OMAP_DSS_LCD_ONOFF; dssdev->panel.timings = nec_8048_panel_timings; necd = kzalloc(sizeof(*necd), GFP_KERNEL); diff --git a/drivers/video/omap2/displays/panel-picodlp.c b/drivers/video/omap2/displays/panel-picodlp.c index 9363cf4ab740..2d35bd388860 100644 --- a/drivers/video/omap2/displays/panel-picodlp.c +++ b/drivers/video/omap2/displays/panel-picodlp.c @@ -420,8 +420,6 @@ static int picodlp_panel_probe(struct omap_dss_device *dssdev) struct i2c_client *picodlp_i2c_client; int r = 0, picodlp_adapter_id; - dssdev->panel.config = OMAP_DSS_LCD_ONOFF | OMAP_DSS_LCD_IHS | - OMAP_DSS_LCD_IVS; dssdev->panel.timings = pico_ls_timings; picod = kzalloc(sizeof(struct picodlp_data), GFP_KERNEL); diff --git a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c index 6b7b43c808e2..bd86ba9ccf76 100644 --- a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c +++ b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c @@ -92,7 +92,6 @@ static int sharp_ls_panel_probe(struct omap_dss_device *dssdev) struct sharp_data *sd; int r; - dssdev->panel.config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; dssdev->panel.timings = sharp_ls_timings; sd = kzalloc(sizeof(*sd), GFP_KERNEL); diff --git a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c index e627c6c5c551..fa7baa650ae0 100644 --- a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c +++ b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c @@ -429,8 +429,6 @@ static int tpo_td043_probe(struct omap_dss_device *dssdev) return -ENODEV; } - dssdev->panel.config = OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IPC; dssdev->panel.timings = tpo_td043_timings; dssdev->ctrl.pixel_size = 24; diff --git a/drivers/video/omap2/dss/hdmi_panel.c b/drivers/video/omap2/dss/hdmi_panel.c index 5bf3a84d89cd..723a13788476 100644 --- a/drivers/video/omap2/dss/hdmi_panel.c +++ b/drivers/video/omap2/dss/hdmi_panel.c @@ -43,8 +43,6 @@ static int hdmi_panel_probe(struct omap_dss_device *dssdev) { DSSDBG("ENTER hdmi_panel_probe\n"); - dssdev->panel.config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; - dssdev->panel.timings = (struct omap_video_timings) { 640, 480, 25175, 96, 16, 48, 2, 11, 31, OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW, diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c index 59992a3ed0ee..0fcd4d7e202e 100644 --- a/drivers/video/omap2/dss/sdi.c +++ b/drivers/video/omap2/dss/sdi.c @@ -78,8 +78,6 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) sdi_basic_init(dssdev); /* 15.5.9.1.2 */ - dssdev->panel.config |= OMAP_DSS_LCD_RF | OMAP_DSS_LCD_ONOFF; - dssdev->panel.timings.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; dssdev->panel.timings.sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; diff --git a/include/video/omapdss.h b/include/video/omapdss.h index be6590dc66a5..14f261b584fa 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -121,15 +121,6 @@ enum omap_rfbi_te_mode { OMAP_DSS_RFBI_TE_MODE_2 = 2, }; -enum omap_panel_config { - OMAP_DSS_LCD_IVS = 1<<0, - OMAP_DSS_LCD_IHS = 1<<1, - OMAP_DSS_LCD_IPC = 1<<2, - OMAP_DSS_LCD_IEO = 1<<3, - OMAP_DSS_LCD_RF = 1<<4, - OMAP_DSS_LCD_ONOFF = 1<<5, -}; - enum omap_dss_signal_level { OMAPDSS_SIG_ACTIVE_HIGH = 0, OMAPDSS_SIG_ACTIVE_LOW = 1, @@ -572,8 +563,6 @@ struct omap_dss_device { /* Unit: line clocks */ int acb; /* ac-bias pin frequency */ - enum omap_panel_config config; - enum omap_dss_dsi_pixel_format dsi_pix_fmt; enum omap_dss_dsi_mode dsi_mode; struct omap_dss_dsi_videomode_data dsi_vm_data; -- cgit v1.2.3 From 23c8f88e8a140c8435658c369b26c7b60d8fe3c0 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 28 Jun 2012 11:15:51 +0530 Subject: OMAPDSS: Add interlace parameter to omap_video_timings Add a parameter called interlace which tells whether the timings are in interlaced or progressive mode. This aligns the omap_video_timings struct with the Xorg modeline configuration. It also removes the hack needed to write to divide the manager height by 2 if the connected interface is VENC. Signed-off-by: Archit Taneja --- drivers/video/omap2/dss/dispc.c | 6 +----- drivers/video/omap2/dss/hdmi_panel.c | 1 + drivers/video/omap2/dss/venc.c | 4 ++++ include/video/omapdss.h | 2 ++ 4 files changed, 8 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index 957c04962793..d200f446840f 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -2754,11 +2754,7 @@ void dispc_mgr_set_timings(enum omap_channel channel, DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt); } else { - enum dss_hdmi_venc_clk_source_select source; - - source = dss_get_hdmi_venc_clk_source(); - - if (source == DSS_VENC_TV_CLK) + if (t.interlace == true) t.y_res /= 2; } diff --git a/drivers/video/omap2/dss/hdmi_panel.c b/drivers/video/omap2/dss/hdmi_panel.c index 723a13788476..e10844faadf9 100644 --- a/drivers/video/omap2/dss/hdmi_panel.c +++ b/drivers/video/omap2/dss/hdmi_panel.c @@ -46,6 +46,7 @@ static int hdmi_panel_probe(struct omap_dss_device *dssdev) dssdev->panel.timings = (struct omap_video_timings) { 640, 480, 25175, 96, 16, 48, 2, 11, 31, OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW, + false, }; DSSDBG("hdmi_panel_probe x_res= %d y_res = %d\n", diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c index 416d478803e5..3a220877461a 100644 --- a/drivers/video/omap2/dss/venc.c +++ b/drivers/video/omap2/dss/venc.c @@ -272,6 +272,8 @@ const struct omap_video_timings omap_dss_pal_timings = { .vsw = 5, .vfp = 5, .vbp = 41, + + .interlace = true, }; EXPORT_SYMBOL(omap_dss_pal_timings); @@ -285,6 +287,8 @@ const struct omap_video_timings omap_dss_ntsc_timings = { .vsw = 6, .vfp = 6, .vbp = 31, + + .interlace = true, }; EXPORT_SYMBOL(omap_dss_ntsc_timings); diff --git a/include/video/omapdss.h b/include/video/omapdss.h index 14f261b584fa..d8ab94485c97 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -344,6 +344,8 @@ struct omap_video_timings { enum omap_dss_signal_level vsync_level; /* Hsync logic level */ enum omap_dss_signal_level hsync_level; + /* Interlaced or Progressive timings */ + bool interlace; /* Pixel clock edge to drive LCD data */ enum omap_dss_signal_edge data_pclk_edge; /* Data enable logic level */ -- cgit v1.2.3 From bd5a7b11a0bfd172b4cd6ef3e01e6beb1753c3f1 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Tue, 26 Jun 2012 12:38:31 +0530 Subject: OMAPDSS: DSI: Fix HSYNC, VSYNC and DE polarities between DISPC and DSI For DSI operation in videomode, DISPC logic levels for the signals HSYNC, VSYNC and DE need to be specified to DSI via the fields VP_HSYNC_POL, VP_VSYNC_POL and VP_DE_POL in DSI_CTRL registers. This information is completely internal to DSS as logic levels for the above signals hold no meaning on the DSI bus. Hence a DSI panel driver should be totally oblivious of these fields. Fix the logic levels/polarities in the DISPC and DSI registers to a default value. This is done by overriding these fields in omap_video_timings struct filled by the panel driver for DISPC, and use the equivalent default values when programming DSI_CTRL registers. Also, remove the redundant polarity related fields in omap_dss_dsi_videomode_data. Signed-off-by: Archit Taneja --- drivers/video/omap2/dss/dsi.c | 44 +++++++++++++++++++++++++------------------ include/video/omapdss.h | 3 --- 2 files changed, 26 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index 06b578036497..e0d43b275e3e 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c @@ -3631,17 +3631,14 @@ static void dsi_config_vp_num_line_buffers(struct omap_dss_device *dssdev) static void dsi_config_vp_sync_events(struct omap_dss_device *dssdev) { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - int de_pol = dssdev->panel.dsi_vm_data.vp_de_pol; - int hsync_pol = dssdev->panel.dsi_vm_data.vp_hsync_pol; - int vsync_pol = dssdev->panel.dsi_vm_data.vp_vsync_pol; bool vsync_end = dssdev->panel.dsi_vm_data.vp_vsync_end; bool hsync_end = dssdev->panel.dsi_vm_data.vp_hsync_end; u32 r; r = dsi_read_reg(dsidev, DSI_CTRL); - r = FLD_MOD(r, de_pol, 9, 9); /* VP_DE_POL */ - r = FLD_MOD(r, hsync_pol, 10, 10); /* VP_HSYNC_POL */ - r = FLD_MOD(r, vsync_pol, 11, 11); /* VP_VSYNC_POL */ + r = FLD_MOD(r, 1, 9, 9); /* VP_DE_POL */ + r = FLD_MOD(r, 1, 10, 10); /* VP_HSYNC_POL */ + r = FLD_MOD(r, 1, 11, 11); /* VP_VSYNC_POL */ r = FLD_MOD(r, 1, 15, 15); /* VP_VSYNC_START */ r = FLD_MOD(r, vsync_end, 16, 16); /* VP_VSYNC_END */ r = FLD_MOD(r, 1, 17, 17); /* VP_HSYNC_START */ @@ -4343,22 +4340,22 @@ EXPORT_SYMBOL(omap_dsi_update); static int dsi_display_init_dispc(struct omap_dss_device *dssdev) { int r; + struct omap_video_timings timings; if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_CMD_MODE) { u16 dw, dh; u32 irq; - struct omap_video_timings timings = { - .hsw = 1, - .hfp = 1, - .hbp = 1, - .vsw = 1, - .vfp = 0, - .vbp = 0, - }; dssdev->driver->get_resolution(dssdev, &dw, &dh); + timings.x_res = dw; timings.y_res = dh; + timings.hsw = 1; + timings.hfp = 1; + timings.hbp = 1; + timings.vsw = 1; + timings.vfp = 0; + timings.vbp = 0; irq = dispc_mgr_get_framedone_irq(dssdev->manager->id); @@ -4371,15 +4368,26 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev) dispc_mgr_enable_stallmode(dssdev->manager->id, true); dispc_mgr_enable_fifohandcheck(dssdev->manager->id, 1); - - dss_mgr_set_timings(dssdev->manager, &timings); } else { + timings = dssdev->panel.timings; + dispc_mgr_enable_stallmode(dssdev->manager->id, false); dispc_mgr_enable_fifohandcheck(dssdev->manager->id, 0); - - dss_mgr_set_timings(dssdev->manager, &dssdev->panel.timings); } + /* + * override interlace, logic level and edge related parameters in + * omap_video_timings with default values + */ + timings.interlace = false; + timings.hsync_level = OMAPDSS_SIG_ACTIVE_HIGH; + timings.vsync_level = OMAPDSS_SIG_ACTIVE_HIGH; + timings.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; + timings.de_level = OMAPDSS_SIG_ACTIVE_HIGH; + timings.sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES; + + dss_mgr_set_timings(dssdev->manager, &timings); + dispc_mgr_set_lcd_type_tft(dssdev->manager->id); dispc_mgr_set_tft_data_lines(dssdev->manager->id, diff --git a/include/video/omapdss.h b/include/video/omapdss.h index d8ab94485c97..a6267a2d292b 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -261,9 +261,6 @@ struct omap_dss_dsi_videomode_data { int hfp_blanking_mode; /* Video port sync events */ - int vp_de_pol; - int vp_hsync_pol; - int vp_vsync_pol; bool vp_vsync_end; bool vp_hsync_end; -- cgit v1.2.3 From de50ada55b6b83b54b817911ec42dc590e1c1738 Mon Sep 17 00:00:00 2001 From: Holger Macht Date: Mon, 25 Jun 2012 16:13:02 +0800 Subject: [SCSI] add wrapper to access and set scsi_bus_type in struct acpi_bus_type For being able to bind ata devices against acpi devices, scsi_bus_type needs to be set as bus in struct acpi_bus_type. So add wrapper to scsi_lib to accomplish that. Signed-off-by: Holger Macht Signed-off-by: Lin Ming Signed-off-by: Jeff Garzik --- drivers/scsi/scsi_lib.c | 17 +++++++++++++++++ include/scsi/scsi.h | 10 ++++++++++ 2 files changed, 27 insertions(+) (limited to 'include') diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 6dfb9785d345..08f1e297c735 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -68,6 +68,23 @@ static struct scsi_host_sg_pool scsi_sg_pools[] = { struct kmem_cache *scsi_sdb_cache; +#ifdef CONFIG_ACPI +#include + +int scsi_register_acpi_bus_type(struct acpi_bus_type *bus) +{ + bus->bus = &scsi_bus_type; + 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/scsi/scsi.h b/include/scsi/scsi.h index f34a5a87af38..4527b3a13321 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h @@ -214,6 +214,16 @@ scsi_command_size(const unsigned char *cmnd) scsi_varlen_cdb_length(cmnd) : COMMAND_SIZE(cmnd[0]); } +#ifdef CONFIG_ACPI +struct acpi_bus_type; + +extern int +scsi_register_acpi_bus_type(struct acpi_bus_type *bus); + +extern void +scsi_unregister_acpi_bus_type(struct acpi_bus_type *bus); +#endif + /* * SCSI Architecture Model (SAM) Status codes. Taken from SAM-3 draft * T10/1561-D Revision 4 Draft dated 7th November 2002. -- cgit v1.2.3 From 30dcf76acc695cbd2fa919e294670fe9552e16e7 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 25 Jun 2012 16:13:04 +0800 Subject: libata: migrate ACPI code over to new bindings Now that we have the ability to directly glue the ACPI namespace to the driver model in libata, we don't need the custom code to handle the same thing. Remove it and migrate the functions over to the new code. Signed-off-by: Matthew Garrett Signed-off-by: Holger Macht Signed-off-by: Lin Ming Signed-off-by: Jeff Garzik --- drivers/ata/libata-acpi.c | 161 +++++++++++----------------------------------- drivers/ata/libata-core.c | 3 - drivers/ata/libata-pmp.c | 4 -- drivers/ata/libata.h | 5 -- drivers/ata/pata_acpi.c | 4 +- include/linux/libata.h | 7 +- 6 files changed, 40 insertions(+), 144 deletions(-) (limited to 'include') diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index b3025a7d71e3..c6f0101fd253 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -47,7 +47,14 @@ static void ata_acpi_clear_gtf(struct ata_device *dev) dev->gtf_cache = NULL; } -static acpi_handle ap_acpi_handle(struct ata_port *ap) +/** + * 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; @@ -64,8 +71,16 @@ static acpi_handle ap_acpi_handle(struct ata_port *ap) return acpi_get_child(DEVICE_ACPI_HANDLE(ap->host->dev), ap->port_no); } +EXPORT_SYMBOL(ata_ap_acpi_handle); -static acpi_handle dev_acpi_handle(struct ata_device *dev) +/** + * ata_dev_acpi_handle - provide the acpi_handle for an ata_device + * @dev: the acpi_device returned will correspond to this port + * + * 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 + */ +acpi_handle ata_dev_acpi_handle(struct ata_device *dev) { acpi_integer adr; struct ata_port *ap = dev->link->ap; @@ -77,66 +92,9 @@ static acpi_handle dev_acpi_handle(struct ata_device *dev) 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(ap_acpi_handle(ap), dev->devno); -} - -/** - * ata_acpi_associate_sata_port - associate SATA port with ACPI objects - * @ap: target SATA port - * - * Look up ACPI objects associated with @ap and initialize acpi_handle - * fields of @ap, the port and devices accordingly. - * - * LOCKING: - * EH context. - * - * RETURNS: - * 0 on success, -errno on failure. - */ -void ata_acpi_associate_sata_port(struct ata_port *ap) -{ - WARN_ON(!(ap->flags & ATA_FLAG_ACPI_SATA)); - - if (!sata_pmp_attached(ap)) { - u64 adr = SATA_ADR(ap->port_no, NO_PORT_MULT); - - ap->link.device->acpi_handle = - acpi_get_child(ap->host->acpi_handle, adr); - } else { - struct ata_link *link; - - ap->link.device->acpi_handle = NULL; - - ata_for_each_link(link, ap, EDGE) { - u64 adr = SATA_ADR(ap->port_no, link->pmp); - - link->device->acpi_handle = - acpi_get_child(ap->host->acpi_handle, adr); - } - } -} - -static void ata_acpi_associate_ide_port(struct ata_port *ap) -{ - int max_devices, i; - - ap->acpi_handle = acpi_get_child(ap->host->acpi_handle, ap->port_no); - if (!ap->acpi_handle) - return; - - max_devices = 1; - if (ap->flags & ATA_FLAG_SLAVE_POSS) - max_devices++; - - for (i = 0; i < max_devices; i++) { - struct ata_device *dev = &ap->link.device[i]; - - dev->acpi_handle = acpi_get_child(ap->acpi_handle, i); - } - - if (ata_acpi_gtm(ap, &ap->__acpi_init_gtm) == 0) - ap->pflags |= ATA_PFLAG_INIT_GTM_VALID; + return acpi_get_child(ata_ap_acpi_handle(ap), dev->devno); } +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) @@ -261,56 +219,6 @@ static const struct acpi_dock_ops ata_acpi_ap_dock_ops = { .uevent = ata_acpi_ap_uevent, }; -/** - * ata_acpi_associate - associate ATA host with ACPI objects - * @host: target ATA host - * - * Look up ACPI objects associated with @host and initialize - * acpi_handle fields of @host, its ports and devices accordingly. - * - * LOCKING: - * EH context. - * - * RETURNS: - * 0 on success, -errno on failure. - */ -void ata_acpi_associate(struct ata_host *host) -{ - int i, j; - - if (!is_pci_dev(host->dev) || libata_noacpi) - return; - - host->acpi_handle = DEVICE_ACPI_HANDLE(host->dev); - if (!host->acpi_handle) - return; - - for (i = 0; i < host->n_ports; i++) { - struct ata_port *ap = host->ports[i]; - - if (host->ports[0]->flags & ATA_FLAG_ACPI_SATA) - ata_acpi_associate_sata_port(ap); - else - ata_acpi_associate_ide_port(ap); - - if (ap->acpi_handle) { - /* we might be on a docking station */ - register_hotplug_dock_device(ap->acpi_handle, - &ata_acpi_ap_dock_ops, ap); - } - - for (j = 0; j < ata_link_max_devices(&ap->link); j++) { - struct ata_device *dev = &ap->link.device[j]; - - if (dev->acpi_handle) { - /* we might be on a docking station */ - register_hotplug_dock_device(dev->acpi_handle, - &ata_acpi_dev_dock_ops, dev); - } - } - } -} - /** * ata_acpi_dissociate - dissociate ATA host from ACPI objects * @host: target ATA host @@ -332,7 +240,7 @@ 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 (ap->acpi_handle && gtm) + if (ata_ap_acpi_handle(ap) && gtm) ata_acpi_stm(ap, gtm); } } @@ -357,7 +265,8 @@ int ata_acpi_gtm(struct ata_port *ap, struct ata_acpi_gtm *gtm) acpi_status status; int rc = 0; - status = acpi_evaluate_object(ap->acpi_handle, "_GTM", NULL, &output); + status = acpi_evaluate_object(ata_ap_acpi_handle(ap), "_GTM", NULL, + &output); rc = -ENOENT; if (status == AE_NOT_FOUND) @@ -427,7 +336,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(ap->acpi_handle, "_STM", &input, NULL); + status = acpi_evaluate_object(ata_ap_acpi_handle(ap), "_STM", &input, + NULL); if (status == AE_NOT_FOUND) return -ENOENT; @@ -484,7 +394,8 @@ static int ata_dev_get_GTF(struct ata_device *dev, struct ata_acpi_gtf **gtf) __func__, ap->port_no); /* _GTF has no input parameters */ - status = acpi_evaluate_object(dev->acpi_handle, "_GTF", NULL, &output); + status = acpi_evaluate_object(ata_dev_acpi_handle(dev), "_GTF", NULL, + &output); out_obj = dev->gtf_cache = output.pointer; if (ACPI_FAILURE(status)) { @@ -850,7 +761,8 @@ static int ata_acpi_push_id(struct ata_device *dev) /* It's OK for _SDD to be missing too. */ swap_buf_le16(dev->id, ATA_ID_WORDS); - status = acpi_evaluate_object(dev->acpi_handle, "_SDD", &input, NULL); + status = acpi_evaluate_object(ata_dev_acpi_handle(dev), "_SDD", &input, + NULL); swap_buf_le16(dev->id, ATA_ID_WORDS); if (status == AE_NOT_FOUND) @@ -900,7 +812,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 (ap->acpi_handle && gtm) { + if (ata_ap_acpi_handle(ap) && gtm) { /* _GTM valid */ /* restore timing parameters */ @@ -941,22 +853,22 @@ void ata_acpi_set_state(struct ata_port *ap, pm_message_t state) { struct ata_device *dev; - if (!ap->acpi_handle || (ap->flags & ATA_FLAG_ACPI_SATA)) + if (!ata_ap_acpi_handle(ap) || (ap->flags & ATA_FLAG_ACPI_SATA)) return; /* channel first and then drives for power on and vica versa for power off */ if (state.event == PM_EVENT_ON) - acpi_bus_set_power(ap->acpi_handle, ACPI_STATE_D0); + acpi_bus_set_power(ata_ap_acpi_handle(ap), ACPI_STATE_D0); ata_for_each_dev(dev, &ap->link, ENABLED) { - if (dev->acpi_handle) - acpi_bus_set_power(dev->acpi_handle, + if (ata_dev_acpi_handle(dev)) + acpi_bus_set_power(ata_dev_acpi_handle(dev), state.event == PM_EVENT_ON ? ACPI_STATE_D0 : ACPI_STATE_D3); } if (state.event != PM_EVENT_ON) - acpi_bus_set_power(ap->acpi_handle, ACPI_STATE_D3); + acpi_bus_set_power(ata_ap_acpi_handle(ap), ACPI_STATE_D3); } /** @@ -981,7 +893,7 @@ int ata_acpi_on_devcfg(struct ata_device *dev) int nr_executed = 0; int rc; - if (!dev->acpi_handle) + if (!ata_dev_acpi_handle(dev)) return 0; /* do we need to do _GTF? */ @@ -1027,7 +939,6 @@ int ata_acpi_on_devcfg(struct ata_device *dev) } ata_dev_warn(dev, "ACPI: failed the second time, disabled\n"); - dev->acpi_handle = NULL; /* We can safely continue if no _GTF command has been executed * and port is not frozen. @@ -1093,7 +1004,7 @@ static int ata_acpi_bind_device(struct ata_port *ap, struct scsi_device *sdev, else ata_dev = &ap->link.device[sdev->id]; - *handle = dev_acpi_handle(ata_dev); + *handle = ata_dev_acpi_handle(ata_dev); if (!*handle) return -ENODEV; diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index bd33b4a7ac13..7705191b5a81 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -6049,9 +6049,6 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) if (rc) goto err_tadd; - /* associate with ACPI nodes */ - ata_acpi_associate(host); - /* set cable, sata_spd_limit and report */ for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c index 21b80c555c60..61c59ee45ce9 100644 --- a/drivers/ata/libata-pmp.c +++ b/drivers/ata/libata-pmp.c @@ -529,8 +529,6 @@ int sata_pmp_attach(struct ata_device *dev) ata_for_each_link(tlink, ap, EDGE) sata_link_init_spd(tlink); - ata_acpi_associate_sata_port(ap); - return 0; fail: @@ -570,8 +568,6 @@ static void sata_pmp_detach(struct ata_device *dev) ap->nr_pmp_links = 0; link->pmp = 0; spin_unlock_irqrestore(ap->lock, flags); - - ata_acpi_associate_sata_port(ap); } /** diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 7f48b602b01b..b0d5294982eb 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -112,9 +112,6 @@ extern void __ata_port_probe(struct ata_port *ap); /* libata-acpi.c */ #ifdef CONFIG_ATA_ACPI extern unsigned int ata_acpi_gtf_filter; - -extern void ata_acpi_associate_sata_port(struct ata_port *ap); -extern void ata_acpi_associate(struct ata_host *host); extern void ata_acpi_dissociate(struct ata_host *host); extern int ata_acpi_on_suspend(struct ata_port *ap); extern void ata_acpi_on_resume(struct ata_port *ap); @@ -124,8 +121,6 @@ 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); #else -static inline void ata_acpi_associate_sata_port(struct ata_port *ap) { } -static inline void ata_acpi_associate(struct ata_host *host) { } static inline void ata_acpi_dissociate(struct ata_host *host) { } static inline int ata_acpi_on_suspend(struct ata_port *ap) { return 0; } static inline void ata_acpi_on_resume(struct ata_port *ap) { } diff --git a/drivers/ata/pata_acpi.c b/drivers/ata/pata_acpi.c index 54145edf50e8..b63ca3b54fb9 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 (ap->acpi_handle == NULL || ata_acpi_gtm(ap, &acpi->gtm) < 0) + if (ata_ap_acpi_handle(ap) == 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 (ap->acpi_handle == NULL) + if (ata_ap_acpi_handle(ap) == NULL) return -ENODEV; acpi = ap->private_data = devm_kzalloc(&pdev->dev, sizeof(struct pata_acpi), GFP_KERNEL); diff --git a/include/linux/libata.h b/include/linux/libata.h index 6e887c742a27..888feef3cda4 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -545,9 +545,6 @@ struct ata_host { struct mutex eh_mutex; struct task_struct *eh_owner; -#ifdef CONFIG_ATA_ACPI - acpi_handle acpi_handle; -#endif struct ata_port *simplex_claimed; /* channel owning the DMA */ struct ata_port *ports[0]; }; @@ -615,7 +612,6 @@ struct ata_device { struct scsi_device *sdev; /* attached SCSI device */ void *private_data; #ifdef CONFIG_ATA_ACPI - acpi_handle acpi_handle; union acpi_object *gtf_cache; unsigned int gtf_filter; #endif @@ -797,7 +793,6 @@ struct ata_port { void *private_data; #ifdef CONFIG_ATA_ACPI - acpi_handle acpi_handle; struct ata_acpi_gtm __acpi_init_gtm; /* use ata_acpi_init_gtm() */ #endif /* owned by EH */ @@ -1114,6 +1109,8 @@ 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 From b1354cbb5bfce28f2e1ed28d77b362dfdfca638d Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Mon, 25 Jun 2012 16:13:08 +0800 Subject: libata: detect Device Attention support Add a new flag ATA_DFLAG_DA to indicate that device supports "Device Attention". Acked-by: Aaron Lu Signed-off-by: Lin Ming Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 3 +++ include/linux/ata.h | 1 + include/linux/libata.h | 2 ++ 3 files changed, 6 insertions(+) (limited to 'include') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 7705191b5a81..c14f88c1f1da 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -2374,6 +2374,9 @@ int ata_dev_configure(struct ata_device *dev) dma_dir_string = ", DMADIR"; } + if (ata_id_has_da(dev->id)) + dev->flags |= ATA_DFLAG_DA; + /* print device info to dmesg */ if (ata_msg_drv(ap) && print_info) ata_dev_info(dev, diff --git a/include/linux/ata.h b/include/linux/ata.h index 32df2b6ef0e0..5713d3ac381a 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -578,6 +578,7 @@ static inline int ata_is_data(u8 prot) ((u64) (id)[(n) + 0]) ) #define ata_id_cdb_intr(id) (((id)[ATA_ID_CONFIG] & 0x60) == 0x20) +#define ata_id_has_da(id) ((id)[77] & (1 << 4)) static inline bool ata_id_has_hipm(const u16 *id) { diff --git a/include/linux/libata.h b/include/linux/libata.h index 888feef3cda4..cc22b943db83 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -161,6 +161,8 @@ enum { ATA_DFLAG_DETACH = (1 << 24), ATA_DFLAG_DETACHED = (1 << 25), + ATA_DFLAG_DA = (1 << 26), /* device supports Device Attention */ + ATA_DEV_UNKNOWN = 0, /* unknown device */ ATA_DEV_ATA = 1, /* ATA device */ ATA_DEV_ATA_UNSUP = 2, /* ATA device (unsupported) */ -- cgit v1.2.3 From 166a2967b45ede2e2e56f3ede3cd32053dc17812 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Mon, 25 Jun 2012 16:13:09 +0800 Subject: libata: tell scsi layer device supports runtime power off If ATA device supports "Device Attention", then tell scsi layer that the device supports runtime power off. Signed-off-by: Aaron Lu Signed-off-by: Lin Ming Signed-off-by: Jeff Garzik --- drivers/ata/libata-acpi.c | 28 ++++++++++++++++++++++++++-- include/scsi/scsi_device.h | 1 + 2 files changed, 27 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index f36284e3290d..f1d6901de37d 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -1000,7 +1000,10 @@ static void ata_acpi_add_pm_notifier(struct ata_device *dev) return; status = acpi_bus_get_device(handle, &acpi_dev); - if (ACPI_SUCCESS(status)) { + if (ACPI_FAILURE(status)) + return; + + if (dev->sdev->can_power_off) { acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, ata_acpi_wake_dev, dev); device_set_run_wake(&dev->sdev->sdev_gendev, true); @@ -1018,7 +1021,10 @@ static void ata_acpi_remove_pm_notifier(struct ata_device *dev) return; status = acpi_bus_get_device(handle, &acpi_dev); - if (ACPI_SUCCESS(status)) { + if (ACPI_FAILURE(status)) + return; + + if (dev->sdev->can_power_off) { device_set_run_wake(&dev->sdev->sdev_gendev, false); acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, ata_acpi_wake_dev); @@ -1102,6 +1108,9 @@ static int ata_acpi_bind_device(struct ata_port *ap, struct scsi_device *sdev, acpi_handle *handle) { struct ata_device *ata_dev; + acpi_status status; + struct acpi_device *acpi_dev; + struct acpi_device_power_state *states; if (ap->flags & ATA_FLAG_ACPI_SATA) ata_dev = &ap->link.device[sdev->channel]; @@ -1113,6 +1122,21 @@ static int ata_acpi_bind_device(struct ata_port *ap, struct scsi_device *sdev, if (!*handle) return -ENODEV; + status = acpi_bus_get_device(*handle, &acpi_dev); + if (ACPI_FAILURE(status)) + return 0; + + /* + * If firmware has _PS3 or _PR3 for this device, + * and this ata ODD device support device attention, + * it means this device can be powered off + */ + states = acpi_dev->power.states; + if ((states[ACPI_STATE_D3_HOT].flags.valid || + states[ACPI_STATE_D3_COLD].flags.explicit_set) && + ata_dev->flags & ATA_DFLAG_DA) + sdev->can_power_off = 1; + return 0; } diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index ba9698852321..aff7525de194 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -153,6 +153,7 @@ struct scsi_device { unsigned no_read_capacity_16:1; /* Avoid READ_CAPACITY_16 cmds */ unsigned try_rc_10_first:1; /* Try READ_CAPACACITY_10 first */ unsigned is_visible:1; /* is the device visible in sysfs */ + unsigned can_power_off:1; /* Device supports runtime power off */ DECLARE_BITMAP(supported_events, SDEV_EVT_MAXBITS); /* supported events */ struct list_head event_list; /* asserted events */ -- cgit v1.2.3 From 011e2a7fd5e9e0c2fdba6b9466d53fc437f8bfaf Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Wed, 20 Jun 2012 15:53:43 -0400 Subject: NFS: Create a have_delegation rpc_op Delegations are a v4 feature, so push them out of the generic code. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/delegation.c | 2 +- fs/nfs/delegation.h | 9 ++------- fs/nfs/dir.c | 2 +- fs/nfs/file.c | 6 +++--- fs/nfs/inode.c | 2 +- fs/nfs/nfs3proc.c | 6 ++++++ fs/nfs/nfs4proc.c | 5 +++-- fs/nfs/proc.c | 6 ++++++ fs/nfs/write.c | 2 +- include/linux/nfs_xdr.h | 1 + 10 files changed, 25 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 9a7a1b488af9..36c7c647a1d0 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -47,7 +47,7 @@ void nfs_mark_delegation_referenced(struct nfs_delegation *delegation) * * Returns one if inode has the indicated delegation, otherwise zero. */ -int nfs_have_delegation(struct inode *inode, fmode_t flags) +int nfs4_have_delegation(struct inode *inode, fmode_t flags) { struct nfs_delegation *delegation; int ret = 0; diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h index 206db5679996..d134fc5fda70 100644 --- a/fs/nfs/delegation.h +++ b/fs/nfs/delegation.h @@ -56,14 +56,9 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl); bool nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode, fmode_t flags); void nfs_mark_delegation_referenced(struct nfs_delegation *delegation); -int nfs_have_delegation(struct inode *inode, fmode_t flags); +int nfs4_have_delegation(struct inode *inode, fmode_t flags); #else -static inline int nfs_have_delegation(struct inode *inode, fmode_t flags) -{ - return 0; -} - static inline int nfs_inode_return_delegation(struct inode *inode) { nfs_wb_all(inode); @@ -73,7 +68,7 @@ static inline int nfs_inode_return_delegation(struct inode *inode) static inline int nfs_have_delegated_attributes(struct inode *inode) { - return nfs_have_delegation(inode, FMODE_READ) && + return NFS_PROTO(inode)->have_delegation(inode, FMODE_READ) && !(NFS_I(inode)->cache_validity & NFS_INO_REVAL_FORCED); } diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index f430057ff3b3..4a3e23aea143 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1144,7 +1144,7 @@ static int nfs_lookup_revalidate(struct dentry *dentry, struct nameidata *nd) goto out_bad; } - if (nfs_have_delegation(inode, FMODE_READ)) + if (NFS_PROTO(dir)->have_delegation(inode, FMODE_READ)) goto out_set_verifier; /* Force a full look up iff the parent directory has changed */ diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 8941ac41c59b..57a22a1533e2 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -178,7 +178,7 @@ nfs_file_flush(struct file *file, fl_owner_t id) * If we're holding a write delegation, then just start the i/o * but don't wait for completion (or send a commit). */ - if (nfs_have_delegation(inode, FMODE_WRITE)) + if (NFS_PROTO(inode)->have_delegation(inode, FMODE_WRITE)) return filemap_fdatawrite(file->f_mapping); /* Flush writes to the server and return any errors */ @@ -677,7 +677,7 @@ do_getlk(struct file *filp, int cmd, struct file_lock *fl, int is_local) } fl->fl_type = saved_type; - if (nfs_have_delegation(inode, FMODE_READ)) + if (NFS_PROTO(inode)->have_delegation(inode, FMODE_READ)) goto out_noconflict; if (is_local) @@ -772,7 +772,7 @@ do_setlk(struct file *filp, int cmd, struct file_lock *fl, int is_local) * This makes locking act as a cache coherency point. */ nfs_sync_mapping(filp->f_mapping); - if (!nfs_have_delegation(inode, FMODE_READ)) { + if (!NFS_PROTO(inode)->have_delegation(inode, FMODE_READ)) { if (is_time_granular(&NFS_SERVER(inode)->time_delta)) __nfs_revalidate_inode(NFS_SERVER(inode), inode); else diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index f7296983eba6..0f0b928ef252 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -1457,7 +1457,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) invalid &= ~NFS_INO_INVALID_DATA; - if (!nfs_have_delegation(inode, FMODE_READ) || + if (!NFS_PROTO(inode)->have_delegation(inode, FMODE_READ) || (save_cache_validity & NFS_INO_REVAL_FORCED)) nfsi->cache_validity |= invalid; diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 2292a0fd2bff..08f832634ef9 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -877,6 +877,11 @@ nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl) return nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl); } +static int nfs3_have_delegation(struct inode *inode, fmode_t flags) +{ + return 0; +} + const struct nfs_rpc_ops nfs_v3_clientops = { .version = 3, /* protocol version */ .dentry_ops = &nfs_dentry_operations, @@ -921,5 +926,6 @@ const struct nfs_rpc_ops nfs_v3_clientops = { .lock = nfs3_proc_lock, .clear_acl_cache = nfs3_forget_cached_acls, .close_context = nfs_close_context, + .have_delegation = nfs3_have_delegation, .init_client = nfs_init_client, }; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index e9a8ad2df7af..86f428bb5e07 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -294,7 +294,7 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc case 0: return 0; case -NFS4ERR_OPENMODE: - if (inode && nfs_have_delegation(inode, FMODE_READ)) { + if (inode && nfs4_have_delegation(inode, FMODE_READ)) { nfs_inode_return_delegation(inode); exception->retry = 1; return 0; @@ -3466,7 +3466,7 @@ bool nfs4_write_need_cache_consistency_data(const struct nfs_write_data *data) /* Otherwise, request attributes if and only if we don't hold * a delegation */ - return nfs_have_delegation(hdr->inode, FMODE_READ) == 0; + return nfs4_have_delegation(hdr->inode, FMODE_READ) == 0; } static void nfs4_proc_write_setup(struct nfs_write_data *data, struct rpc_message *msg) @@ -6804,6 +6804,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .clear_acl_cache = nfs4_zap_acl_attr, .close_context = nfs4_close_context, .open_context = nfs4_atomic_open, + .have_delegation = nfs4_have_delegation, .init_client = nfs4_init_client, }; diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 617c7419a08e..4aed3ddf9bba 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -734,6 +734,11 @@ out_einval: return -EINVAL; } +static int nfs_have_delegation(struct inode *inode, fmode_t flags) +{ + return 0; +} + const struct nfs_rpc_ops nfs_v2_clientops = { .version = 2, /* protocol version */ .dentry_ops = &nfs_dentry_operations, @@ -777,5 +782,6 @@ const struct nfs_rpc_ops nfs_v2_clientops = { .lock = nfs_proc_lock, .lock_check_bounds = nfs_lock_check_bounds, .close_context = nfs_close_context, + .have_delegation = nfs_have_delegation, .init_client = nfs_init_client, }; diff --git a/fs/nfs/write.c b/fs/nfs/write.c index ee929e5e1f7b..f163355b9618 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -410,7 +410,7 @@ static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req) nfs_lock_request(req); spin_lock(&inode->i_lock); - if (!nfsi->npages && nfs_have_delegation(inode, FMODE_WRITE)) + if (!nfsi->npages && NFS_PROTO(inode)->have_delegation(inode, FMODE_WRITE)) inode->i_version++; set_bit(PG_MAPPED, &req->wb_flags); SetPagePrivate(req->wb_page); diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 5c0014d1c969..8787f77c64b3 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1422,6 +1422,7 @@ struct nfs_rpc_ops { struct nfs_open_context *ctx, int open_flags, struct iattr *iattr); + int (*have_delegation)(struct inode *, fmode_t); struct nfs_client * (*init_client) (struct nfs_client *, const struct rpc_timeout *, const char *, rpc_authflavor_t); -- cgit v1.2.3 From 57ec14c55dee2733330327499d16e40f8c23219e Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Wed, 20 Jun 2012 15:53:44 -0400 Subject: NFS: Create a return_delegation rpc op Delegations are a v4 feature, so push return_delegation out of the generic client by creating a new rpc_op and renaming the old function to be in the nfs v4 "namespace" Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/delegation.c | 2 +- fs/nfs/delegation.h | 8 +------- fs/nfs/dir.c | 8 ++++---- fs/nfs/inode.c | 2 +- fs/nfs/nfs3proc.c | 7 +++++++ fs/nfs/nfs4proc.c | 7 ++++--- fs/nfs/proc.c | 7 +++++++ fs/nfs/unlink.c | 2 +- include/linux/nfs_xdr.h | 1 + 9 files changed, 27 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 36c7c647a1d0..81c5eec3cf38 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -388,7 +388,7 @@ void nfs_inode_return_delegation_noreclaim(struct inode *inode) * * Returns zero on success, or a negative errno value. */ -int nfs_inode_return_delegation(struct inode *inode) +int nfs4_inode_return_delegation(struct inode *inode) { struct nfs_server *server = NFS_SERVER(inode); struct nfs_inode *nfsi = NFS_I(inode); diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h index d134fc5fda70..1f3ccd934635 100644 --- a/fs/nfs/delegation.h +++ b/fs/nfs/delegation.h @@ -33,7 +33,7 @@ enum { int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); -int nfs_inode_return_delegation(struct inode *inode); +int nfs4_inode_return_delegation(struct inode *inode); int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid); void nfs_inode_return_delegation_noreclaim(struct inode *inode); @@ -58,12 +58,6 @@ bool nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode, fmode_ void nfs_mark_delegation_referenced(struct nfs_delegation *delegation); int nfs4_have_delegation(struct inode *inode, fmode_t flags); -#else -static inline int nfs_inode_return_delegation(struct inode *inode) -{ - nfs_wb_all(inode); - return 0; -} #endif static inline int nfs_have_delegated_attributes(struct inode *inode) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 4a3e23aea143..68e451f59305 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1778,7 +1778,7 @@ static int nfs_safe_remove(struct dentry *dentry) } if (inode != NULL) { - nfs_inode_return_delegation(inode); + NFS_PROTO(inode)->return_delegation(inode); error = NFS_PROTO(dir)->remove(dir, &dentry->d_name); /* The VFS may want to delete this inode */ if (error == 0) @@ -1906,7 +1906,7 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) old_dentry->d_parent->d_name.name, old_dentry->d_name.name, dentry->d_parent->d_name.name, dentry->d_name.name); - nfs_inode_return_delegation(inode); + NFS_PROTO(inode)->return_delegation(inode); d_drop(dentry); error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name); @@ -1990,9 +1990,9 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, } } - nfs_inode_return_delegation(old_inode); + NFS_PROTO(old_inode)->return_delegation(old_inode); if (new_inode != NULL) - nfs_inode_return_delegation(new_inode); + NFS_PROTO(new_inode)->return_delegation(new_inode); error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name, new_dir, &new_dentry->d_name); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 0f0b928ef252..28c9ebbe78a6 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -430,7 +430,7 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr) * Return any delegations if we're going to change ACLs */ if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0) - nfs_inode_return_delegation(inode); + NFS_PROTO(inode)->return_delegation(inode); error = NFS_PROTO(inode)->setattr(dentry, fattr, attr); if (error == 0) nfs_refresh_inode(inode, fattr); diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 08f832634ef9..4749a32e54be 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -882,6 +882,12 @@ static int nfs3_have_delegation(struct inode *inode, fmode_t flags) return 0; } +static int nfs3_return_delegation(struct inode *inode) +{ + nfs_wb_all(inode); + return 0; +} + const struct nfs_rpc_ops nfs_v3_clientops = { .version = 3, /* protocol version */ .dentry_ops = &nfs_dentry_operations, @@ -927,5 +933,6 @@ const struct nfs_rpc_ops nfs_v3_clientops = { .clear_acl_cache = nfs3_forget_cached_acls, .close_context = nfs_close_context, .have_delegation = nfs3_have_delegation, + .return_delegation = nfs3_return_delegation, .init_client = nfs_init_client, }; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 86f428bb5e07..035f7a0829ec 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -295,7 +295,7 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc return 0; case -NFS4ERR_OPENMODE: if (inode && nfs4_have_delegation(inode, FMODE_READ)) { - nfs_inode_return_delegation(inode); + nfs4_inode_return_delegation(inode); exception->retry = 1; return 0; } @@ -1065,7 +1065,7 @@ static void nfs4_return_incompatible_delegation(struct inode *inode, fmode_t fmo return; } rcu_read_unlock(); - nfs_inode_return_delegation(inode); + nfs4_inode_return_delegation(inode); } static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata) @@ -3870,7 +3870,7 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl i = buf_to_pages_noslab(buf, buflen, arg.acl_pages, &arg.acl_pgbase); if (i < 0) return i; - nfs_inode_return_delegation(inode); + nfs4_inode_return_delegation(inode); ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1); /* @@ -6805,6 +6805,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .close_context = nfs4_close_context, .open_context = nfs4_atomic_open, .have_delegation = nfs4_have_delegation, + .return_delegation = nfs4_inode_return_delegation, .init_client = nfs4_init_client, }; diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 4aed3ddf9bba..16632930abd2 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -739,6 +739,12 @@ static int nfs_have_delegation(struct inode *inode, fmode_t flags) return 0; } +static int nfs_return_delegation(struct inode *inode) +{ + nfs_wb_all(inode); + return 0; +} + const struct nfs_rpc_ops nfs_v2_clientops = { .version = 2, /* protocol version */ .dentry_ops = &nfs_dentry_operations, @@ -783,5 +789,6 @@ const struct nfs_rpc_ops nfs_v2_clientops = { .lock_check_bounds = nfs_lock_check_bounds, .close_context = nfs_close_context, .have_delegation = nfs_have_delegation, + .return_delegation = nfs_return_delegation, .init_client = nfs_init_client, }; diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index 3210a03342f9..13cea637eff8 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c @@ -501,7 +501,7 @@ nfs_sillyrename(struct inode *dir, struct dentry *dentry) (unsigned long long)NFS_FILEID(dentry->d_inode)); /* Return delegation in anticipation of the rename */ - nfs_inode_return_delegation(dentry->d_inode); + NFS_PROTO(dentry->d_inode)->return_delegation(dentry->d_inode); sdentry = NULL; do { diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 8787f77c64b3..62235be07fb8 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1423,6 +1423,7 @@ struct nfs_rpc_ops { int open_flags, struct iattr *iattr); int (*have_delegation)(struct inode *, fmode_t); + int (*return_delegation)(struct inode *); struct nfs_client * (*init_client) (struct nfs_client *, const struct rpc_timeout *, const char *, rpc_authflavor_t); -- cgit v1.2.3 From cdb7ecedec766861e7c4cc35a203518f92023bff Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Wed, 20 Jun 2012 15:53:45 -0400 Subject: NFS: Create a free_client rpc_op NFS v4 needs a way to shut down callbacks and sessions, but v2 and v3 don't. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 18 +++++++++--------- fs/nfs/internal.h | 1 + fs/nfs/nfs3proc.c | 1 + fs/nfs/nfs4_fs.h | 2 ++ fs/nfs/nfs4proc.c | 1 + fs/nfs/proc.c | 1 + include/linux/nfs_xdr.h | 1 + 7 files changed, 16 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index bf0f896284a8..82cb8a386a8f 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -242,6 +242,12 @@ static void nfs4_shutdown_client(struct nfs_client *clp) kfree(clp->cl_implid); } +void nfs4_free_client(struct nfs_client *clp) +{ + nfs4_shutdown_client(clp); + nfs_free_client(clp); +} + /* idr_remove_all is not needed as all id's are removed by nfs_put_client */ void nfs_cleanup_cb_ident_idr(struct net *net) { @@ -272,10 +278,6 @@ static void nfs4_destroy_server(struct nfs_server *server) } #else -static void nfs4_shutdown_client(struct nfs_client *clp) -{ -} - void nfs_cleanup_cb_ident_idr(struct net *net) { } @@ -293,12 +295,10 @@ static void pnfs_init_server(struct nfs_server *server) /* * Destroy a shared client record */ -static void nfs_free_client(struct nfs_client *clp) +void nfs_free_client(struct nfs_client *clp) { dprintk("--> nfs_free_client(%u)\n", clp->rpc_ops->version); - nfs4_shutdown_client(clp); - nfs_fscache_release_client_cookie(clp); /* -EIO all pending I/O */ @@ -335,7 +335,7 @@ void nfs_put_client(struct nfs_client *clp) BUG_ON(!list_empty(&clp->cl_superblocks)); - nfs_free_client(clp); + clp->rpc_ops->free_client(clp); } } EXPORT_SYMBOL_GPL(nfs_put_client); @@ -574,7 +574,7 @@ nfs_get_client(const struct nfs_client_initdata *cl_init, if (clp) { spin_unlock(&nn->nfs_client_lock); if (new) - nfs_free_client(new); + new->rpc_ops->free_client(new); return nfs_found_client(cl_init, clp); } if (new) { diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 18f99ef71343..93b732523342 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -151,6 +151,7 @@ extern void nfs_clients_init(struct net *net); extern void nfs_cleanup_cb_ident_idr(struct net *); extern void nfs_put_client(struct nfs_client *); +extern void nfs_free_client(struct nfs_client *); extern struct nfs_client *nfs4_find_client_ident(struct net *, int); extern struct nfs_client * nfs4_find_client_sessionid(struct net *, const struct sockaddr *, diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 4749a32e54be..4ccb34bf1732 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -935,4 +935,5 @@ const struct nfs_rpc_ops nfs_v3_clientops = { .have_delegation = nfs3_have_delegation, .return_delegation = nfs3_return_delegation, .init_client = nfs_init_client, + .free_client = nfs_free_client, }; diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index cc5900ac61b5..9889ee476e37 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -301,6 +301,8 @@ extern const u32 nfs4_pathconf_bitmap[2]; extern const u32 nfs4_fsinfo_bitmap[3]; extern const u32 nfs4_fs_locations_bitmap[2]; +void nfs4_free_client(struct nfs_client *); + /* nfs4renewd.c */ extern void nfs4_schedule_state_renewal(struct nfs_client *); extern void nfs4_renewd_prepare_shutdown(struct nfs_server *); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 035f7a0829ec..f301c53926b2 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -6807,6 +6807,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .have_delegation = nfs4_have_delegation, .return_delegation = nfs4_inode_return_delegation, .init_client = nfs4_init_client, + .free_client = nfs4_free_client, }; static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = { diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 16632930abd2..53620bf10969 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -791,4 +791,5 @@ const struct nfs_rpc_ops nfs_v2_clientops = { .have_delegation = nfs_have_delegation, .return_delegation = nfs_return_delegation, .init_client = nfs_init_client, + .free_client = nfs_free_client, }; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 62235be07fb8..e61dc7235d5d 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1427,6 +1427,7 @@ struct nfs_rpc_ops { struct nfs_client * (*init_client) (struct nfs_client *, const struct rpc_timeout *, const char *, rpc_authflavor_t); + void (*free_client) (struct nfs_client *); }; /* -- cgit v1.2.3 From 6663ee7f8187708143255c057bc132bbc84c1894 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Wed, 20 Jun 2012 15:53:46 -0400 Subject: NFS: Create an alloc_client rpc_op This gives NFS v4 a way to set up callbacks and sessions without v2 or v3 having to do them as well. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 40 ++++++++++++++++++++++++++-------------- fs/nfs/internal.h | 1 + fs/nfs/nfs3proc.c | 1 + fs/nfs/nfs4_fs.h | 2 ++ fs/nfs/nfs4proc.c | 1 + fs/nfs/proc.c | 1 + include/linux/nfs_xdr.h | 2 ++ 7 files changed, 34 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 82cb8a386a8f..254719c4a575 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -147,7 +147,7 @@ struct nfs_client_initdata { * Since these are allocated/deallocated very rarely, we don't * bother putting them in a slab cache... */ -static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) +struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) { struct nfs_client *clp; struct rpc_cred *cred; @@ -177,18 +177,6 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_ clp->cl_proto = cl_init->proto; clp->cl_net = get_net(cl_init->net); -#ifdef CONFIG_NFS_V4 - err = nfs_get_cb_ident_idr(clp, cl_init->minorversion); - if (err) - goto error_cleanup; - - spin_lock_init(&clp->cl_lock); - INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state); - rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); - clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED; - clp->cl_minorversion = cl_init->minorversion; - clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion]; -#endif cred = rpc_lookup_machine_cred("*"); if (!IS_ERR(cred)) clp->cl_machine_cred = cred; @@ -218,6 +206,30 @@ static void nfs4_shutdown_session(struct nfs_client *clp) } #endif /* CONFIG_NFS_V4_1 */ +struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init) +{ + int err; + struct nfs_client *clp = nfs_alloc_client(cl_init); + if (IS_ERR(clp)) + return clp; + + err = nfs_get_cb_ident_idr(clp, cl_init->minorversion); + if (err) + goto error; + + spin_lock_init(&clp->cl_lock); + INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state); + rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); + clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED; + clp->cl_minorversion = cl_init->minorversion; + clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion]; + return clp; + +error: + kfree(clp); + return ERR_PTR(err); +} + /* * Destroy the NFS4 callback service */ @@ -588,7 +600,7 @@ nfs_get_client(const struct nfs_client_initdata *cl_init, spin_unlock(&nn->nfs_client_lock); - new = nfs_alloc_client(cl_init); + new = cl_init->rpc_ops->alloc_client(cl_init); } while (!IS_ERR(new)); dprintk("<-- nfs_get_client() Failed to find %s (%ld)\n", diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 93b732523342..633af813984d 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -148,6 +148,7 @@ extern void nfs_umount(const struct nfs_mount_request *info); /* client.c */ extern const struct rpc_program nfs_program; extern void nfs_clients_init(struct net *net); +extern struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *); extern void nfs_cleanup_cb_ident_idr(struct net *); extern void nfs_put_client(struct nfs_client *); diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 4ccb34bf1732..77c7aac228bb 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -934,6 +934,7 @@ const struct nfs_rpc_ops nfs_v3_clientops = { .close_context = nfs_close_context, .have_delegation = nfs3_have_delegation, .return_delegation = nfs3_return_delegation, + .alloc_client = nfs_alloc_client, .init_client = nfs_init_client, .free_client = nfs_free_client, }; diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 9889ee476e37..a0be2d1af04b 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -303,6 +303,8 @@ extern const u32 nfs4_fs_locations_bitmap[2]; void nfs4_free_client(struct nfs_client *); +struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *); + /* nfs4renewd.c */ extern void nfs4_schedule_state_renewal(struct nfs_client *); extern void nfs4_renewd_prepare_shutdown(struct nfs_server *); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index f301c53926b2..7f39e7ecde6c 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -6806,6 +6806,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .open_context = nfs4_atomic_open, .have_delegation = nfs4_have_delegation, .return_delegation = nfs4_inode_return_delegation, + .alloc_client = nfs4_alloc_client, .init_client = nfs4_init_client, .free_client = nfs4_free_client, }; diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 53620bf10969..99a002515dfe 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -790,6 +790,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = { .close_context = nfs_close_context, .have_delegation = nfs_have_delegation, .return_delegation = nfs_return_delegation, + .alloc_client = nfs_alloc_client, .init_client = nfs_init_client, .free_client = nfs_free_client, }; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index e61dc7235d5d..4d62b774ddaf 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1353,6 +1353,7 @@ struct nfs_renamedata { struct nfs_access_entry; struct nfs_client; struct rpc_timeout; +struct nfs_client_initdata; /* * RPC procedure vector for NFSv2/NFSv3 demuxing @@ -1424,6 +1425,7 @@ struct nfs_rpc_ops { struct iattr *iattr); int (*have_delegation)(struct inode *, fmode_t); int (*return_delegation)(struct inode *); + 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); -- cgit v1.2.3 From 1abb50886afe8a126705c93dab2b50c1252a9c19 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Wed, 20 Jun 2012 15:53:47 -0400 Subject: NFS: Create an read_pageio_init() function pNFS needs to select a read function based on the layout driver currently in use, so I let each NFS version decide how to best handle initializing reads. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/internal.h | 2 +- fs/nfs/nfs3proc.c | 1 + fs/nfs/nfs4proc.c | 1 + fs/nfs/pnfs.c | 11 +++++------ fs/nfs/pnfs.h | 6 +++--- fs/nfs/proc.c | 1 + fs/nfs/read.c | 16 +++------------- include/linux/nfs_xdr.h | 3 +++ 8 files changed, 18 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 633af813984d..b3121123b40d 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -306,7 +306,7 @@ extern int nfs_initiate_read(struct rpc_clnt *clnt, extern void nfs_read_prepare(struct rpc_task *task, void *calldata); extern int nfs_generic_pagein(struct nfs_pageio_descriptor *desc, struct nfs_pgio_header *hdr); -extern void nfs_pageio_init_read_mds(struct nfs_pageio_descriptor *pgio, +extern void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode, const struct nfs_pgio_completion_ops *compl_ops); extern void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio); diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 77c7aac228bb..9864d05432da 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -921,6 +921,7 @@ const struct nfs_rpc_ops nfs_v3_clientops = { .pathconf = nfs3_proc_pathconf, .decode_dirent = nfs3_decode_dirent, .read_setup = nfs3_proc_read_setup, + .read_pageio_init = nfs_pageio_init_read, .read_rpc_prepare = nfs3_proc_read_rpc_prepare, .read_done = nfs3_read_done, .write_setup = nfs3_proc_write_setup, diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 7f39e7ecde6c..f99cf71f4e36 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -6792,6 +6792,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .set_capabilities = nfs4_server_capabilities, .decode_dirent = nfs4_decode_dirent, .read_setup = nfs4_proc_read_setup, + .read_pageio_init = pnfs_pageio_init_read, .read_rpc_prepare = nfs4_proc_read_rpc_prepare, .read_done = nfs4_read_done, .write_setup = nfs4_proc_write_setup, diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index bbc49caa7a82..9c830603a16c 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1209,7 +1209,7 @@ pnfs_generic_pg_init_write(struct nfs_pageio_descriptor *pgio, struct nfs_page * } EXPORT_SYMBOL_GPL(pnfs_generic_pg_init_write); -bool +void pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode, const struct nfs_pgio_completion_ops *compl_ops) { @@ -1217,10 +1217,9 @@ pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode, struct pnfs_layoutdriver_type *ld = server->pnfs_curr_ld; if (ld == NULL) - return false; - nfs_pageio_init(pgio, inode, ld->pg_read_ops, compl_ops, - server->rsize, 0); - return true; + nfs_pageio_init_read(pgio, inode, compl_ops); + else + nfs_pageio_init(pgio, inode, ld->pg_read_ops, compl_ops, server->rsize, 0); } bool @@ -1427,7 +1426,7 @@ int pnfs_read_done_resend_to_mds(struct inode *inode, LIST_HEAD(failed); /* Resend all requests through the MDS */ - nfs_pageio_init_read_mds(&pgio, inode, compl_ops); + nfs_pageio_init_read(&pgio, inode, compl_ops); while (!list_empty(head)) { struct nfs_page *req = nfs_list_entry(head->next); diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index 64f90d845f6a..80ee8919dd5e 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -178,7 +178,7 @@ extern int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp); void get_layout_hdr(struct pnfs_layout_hdr *lo); void put_lseg(struct pnfs_layout_segment *lseg); -bool pnfs_pageio_init_read(struct nfs_pageio_descriptor *, struct inode *, +void pnfs_pageio_init_read(struct nfs_pageio_descriptor *, struct inode *, const struct nfs_pgio_completion_ops *); bool pnfs_pageio_init_write(struct nfs_pageio_descriptor *, struct inode *, int, const struct nfs_pgio_completion_ops *); @@ -438,10 +438,10 @@ static inline void unset_pnfs_layoutdriver(struct nfs_server *s) { } -static inline bool pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode, +static inline void pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode, const struct nfs_pgio_completion_ops *compl_ops) { - return false; + nfs_pageio_init_read(pgio, inode, compl_ops); } static inline bool pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, int ioflags, diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 99a002515dfe..6fea6e107bc3 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -778,6 +778,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = { .pathconf = nfs_proc_pathconf, .decode_dirent = nfs2_decode_dirent, .read_setup = nfs_proc_read_setup, + .read_pageio_init = nfs_pageio_init_read, .read_rpc_prepare = nfs_proc_read_rpc_prepare, .read_done = nfs_read_done, .write_setup = nfs_proc_write_setup, diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 86ced7836214..6267b873bbcb 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -20,8 +20,6 @@ #include #include -#include "pnfs.h" - #include "nfs4_fs.h" #include "internal.h" #include "iostat.h" @@ -108,7 +106,7 @@ int nfs_return_empty_page(struct page *page) return 0; } -void nfs_pageio_init_read_mds(struct nfs_pageio_descriptor *pgio, +void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode, const struct nfs_pgio_completion_ops *compl_ops) { @@ -123,14 +121,6 @@ void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio) } EXPORT_SYMBOL_GPL(nfs_pageio_reset_read_mds); -void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, - struct inode *inode, - const struct nfs_pgio_completion_ops *compl_ops) -{ - if (!pnfs_pageio_init_read(pgio, inode, compl_ops)) - nfs_pageio_init_read_mds(pgio, inode, compl_ops); -} - int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode, struct page *page) { @@ -149,7 +139,7 @@ int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode, if (len < PAGE_CACHE_SIZE) zero_user_segment(page, len, PAGE_CACHE_SIZE); - nfs_pageio_init_read(&pgio, inode, &nfs_async_read_completion_ops); + NFS_PROTO(inode)->read_pageio_init(&pgio, inode, &nfs_async_read_completion_ops); nfs_pageio_add_request(&pgio, new); nfs_pageio_complete(&pgio); NFS_I(inode)->read_io += pgio.pg_bytes_written; @@ -652,7 +642,7 @@ int nfs_readpages(struct file *filp, struct address_space *mapping, if (ret == 0) goto read_complete; /* all pages were read */ - nfs_pageio_init_read(&pgio, inode, &nfs_async_read_completion_ops); + NFS_PROTO(inode)->read_pageio_init(&pgio, inode, &nfs_async_read_completion_ops); ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc); diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 4d62b774ddaf..e00b8b3c334e 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1354,6 +1354,7 @@ struct nfs_access_entry; struct nfs_client; struct rpc_timeout; struct nfs_client_initdata; +struct nfs_pageio_descriptor; /* * RPC procedure vector for NFSv2/NFSv3 demuxing @@ -1407,6 +1408,8 @@ struct nfs_rpc_ops { int (*set_capabilities)(struct nfs_server *, struct nfs_fh *); int (*decode_dirent)(struct xdr_stream *, struct nfs_entry *, int); void (*read_setup) (struct nfs_read_data *, struct rpc_message *); + void (*read_pageio_init)(struct nfs_pageio_descriptor *, struct inode *, + const struct nfs_pgio_completion_ops *); void (*read_rpc_prepare)(struct rpc_task *, struct nfs_read_data *); int (*read_done) (struct rpc_task *, struct nfs_read_data *); void (*write_setup) (struct nfs_write_data *, struct rpc_message *); -- cgit v1.2.3 From 57208fa7e51ca16cd68de8e8bf482f16b06d3ea1 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Wed, 20 Jun 2012 15:53:48 -0400 Subject: NFS: Create an write_pageio_init() function pNFS needs to select a write function based on the layout driver currently in use, so I let each NFS version decide how to best handle initializing writes. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/internal.h | 2 +- fs/nfs/nfs3proc.c | 1 + fs/nfs/nfs4proc.c | 1 + fs/nfs/pnfs.c | 11 +++++------ fs/nfs/pnfs.h | 6 +++--- fs/nfs/proc.c | 1 + fs/nfs/write.c | 18 ++++++------------ include/linux/nfs_xdr.h | 2 ++ 8 files changed, 20 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index b3121123b40d..7edc172c371e 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -320,7 +320,7 @@ extern struct nfs_write_header *nfs_writehdr_alloc(void); extern void nfs_writehdr_free(struct nfs_pgio_header *hdr); extern int nfs_generic_flush(struct nfs_pageio_descriptor *desc, struct nfs_pgio_header *hdr); -extern void nfs_pageio_init_write_mds(struct nfs_pageio_descriptor *pgio, +extern void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, int ioflags, const struct nfs_pgio_completion_ops *compl_ops); extern void nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio); diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 9864d05432da..f3344f7f46a9 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -925,6 +925,7 @@ const struct nfs_rpc_ops nfs_v3_clientops = { .read_rpc_prepare = nfs3_proc_read_rpc_prepare, .read_done = nfs3_read_done, .write_setup = nfs3_proc_write_setup, + .write_pageio_init = nfs_pageio_init_write, .write_rpc_prepare = nfs3_proc_write_rpc_prepare, .write_done = nfs3_write_done, .commit_setup = nfs3_proc_commit_setup, diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index f99cf71f4e36..7d387cb8ceb5 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -6796,6 +6796,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .read_rpc_prepare = nfs4_proc_read_rpc_prepare, .read_done = nfs4_read_done, .write_setup = nfs4_proc_write_setup, + .write_pageio_init = pnfs_pageio_init_write, .write_rpc_prepare = nfs4_proc_write_rpc_prepare, .write_done = nfs4_write_done, .commit_setup = nfs4_proc_commit_setup, diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 9c830603a16c..2617831afd39 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1222,7 +1222,7 @@ pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode, nfs_pageio_init(pgio, inode, ld->pg_read_ops, compl_ops, server->rsize, 0); } -bool +void pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, int ioflags, const struct nfs_pgio_completion_ops *compl_ops) @@ -1231,10 +1231,9 @@ pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, struct pnfs_layoutdriver_type *ld = server->pnfs_curr_ld; if (ld == NULL) - return false; - nfs_pageio_init(pgio, inode, ld->pg_write_ops, compl_ops, - server->wsize, ioflags); - return true; + nfs_pageio_init_write(pgio, inode, ioflags, compl_ops); + else + nfs_pageio_init(pgio, inode, ld->pg_write_ops, compl_ops, server->wsize, ioflags); } bool @@ -1271,7 +1270,7 @@ int pnfs_write_done_resend_to_mds(struct inode *inode, LIST_HEAD(failed); /* Resend all requests through the MDS */ - nfs_pageio_init_write_mds(&pgio, inode, FLUSH_STABLE, compl_ops); + nfs_pageio_init_write(&pgio, inode, FLUSH_STABLE, compl_ops); while (!list_empty(head)) { struct nfs_page *req = nfs_list_entry(head->next); diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index 80ee8919dd5e..592beb02c955 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -180,7 +180,7 @@ void put_lseg(struct pnfs_layout_segment *lseg); void pnfs_pageio_init_read(struct nfs_pageio_descriptor *, struct inode *, const struct nfs_pgio_completion_ops *); -bool pnfs_pageio_init_write(struct nfs_pageio_descriptor *, struct inode *, +void pnfs_pageio_init_write(struct nfs_pageio_descriptor *, struct inode *, int, const struct nfs_pgio_completion_ops *); void set_pnfs_layoutdriver(struct nfs_server *, const struct nfs_fh *, u32); @@ -444,10 +444,10 @@ static inline void pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, str nfs_pageio_init_read(pgio, inode, compl_ops); } -static inline bool pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, int ioflags, +static inline void pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, int ioflags, const struct nfs_pgio_completion_ops *compl_ops) { - return false; + nfs_pageio_init_write(pgio, inode, ioflags, compl_ops); } static inline int diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 6fea6e107bc3..cf6499742b10 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -782,6 +782,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = { .read_rpc_prepare = nfs_proc_read_rpc_prepare, .read_done = nfs_read_done, .write_setup = nfs_proc_write_setup, + .write_pageio_init = nfs_pageio_init_write, .write_rpc_prepare = nfs_proc_write_rpc_prepare, .write_done = nfs_write_done, .commit_setup = nfs_proc_commit_setup, diff --git a/fs/nfs/write.c b/fs/nfs/write.c index f163355b9618..c11fb0025f0b 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -336,8 +336,10 @@ static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc struct nfs_pageio_descriptor pgio; int err; - nfs_pageio_init_write(&pgio, page->mapping->host, wb_priority(wbc), - &nfs_async_write_completion_ops); + NFS_PROTO(page->mapping->host)->write_pageio_init(&pgio, + page->mapping->host, + wb_priority(wbc), + &nfs_async_write_completion_ops); err = nfs_do_writepage(page, wbc, &pgio); nfs_pageio_complete(&pgio); if (err < 0) @@ -380,8 +382,7 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc) nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGES); - nfs_pageio_init_write(&pgio, inode, wb_priority(wbc), - &nfs_async_write_completion_ops); + NFS_PROTO(inode)->write_pageio_init(&pgio, inode, wb_priority(wbc), &nfs_async_write_completion_ops); err = write_cache_pages(mapping, wbc, nfs_writepages_callback, &pgio); nfs_pageio_complete(&pgio); @@ -1202,7 +1203,7 @@ static const struct nfs_pageio_ops nfs_pageio_write_ops = { .pg_doio = nfs_generic_pg_writepages, }; -void nfs_pageio_init_write_mds(struct nfs_pageio_descriptor *pgio, +void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, int ioflags, const struct nfs_pgio_completion_ops *compl_ops) { @@ -1217,13 +1218,6 @@ void nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio) } EXPORT_SYMBOL_GPL(nfs_pageio_reset_write_mds); -void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, - struct inode *inode, int ioflags, - const struct nfs_pgio_completion_ops *compl_ops) -{ - if (!pnfs_pageio_init_write(pgio, inode, ioflags, compl_ops)) - nfs_pageio_init_write_mds(pgio, inode, ioflags, compl_ops); -} void nfs_write_prepare(struct rpc_task *task, void *calldata) { diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index e00b8b3c334e..8ed8ec628290 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1413,6 +1413,8 @@ struct nfs_rpc_ops { void (*read_rpc_prepare)(struct rpc_task *, struct nfs_read_data *); int (*read_done) (struct rpc_task *, struct nfs_read_data *); void (*write_setup) (struct nfs_write_data *, struct rpc_message *); + void (*write_pageio_init)(struct nfs_pageio_descriptor *, struct inode *, int, + const struct nfs_pgio_completion_ops *); void (*write_rpc_prepare)(struct rpc_task *, struct nfs_write_data *); int (*write_done) (struct rpc_task *, struct nfs_write_data *); void (*commit_setup) (struct nfs_commit_data *, struct rpc_message *); -- cgit v1.2.3 From b37d2a3a75cb0e72e18c29336cb2095b63dabfc8 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 29 Jun 2012 07:47:19 -0300 Subject: [media] i2c: Export an unlocked flavor of i2c_transfer Some drivers (in particular for TV cards) need exclusive access to their I2C buses for specific operations. Export an unlocked flavor of i2c_transfer to give them full control. The unlocked flavor has the following limitations: * Obviously, caller must hold the i2c adapter lock. * No debug messages are logged. We don't want to log messages while holding a rt_mutex. * No check is done on the existence of adap->algo->master_xfer. It is thus the caller's responsibility to ensure that the function is OK to call. Signed-off-by: Jean Delvare Signed-off-by: Mauro Carvalho Chehab --- drivers/i2c/i2c-core.c | 44 +++++++++++++++++++++++++++++++++----------- include/linux/i2c.h | 3 +++ 2 files changed, 36 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index feb7dc359186..ccc6445979c4 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1294,6 +1294,37 @@ module_exit(i2c_exit); * ---------------------------------------------------- */ +/** + * __i2c_transfer - unlocked flavor of i2c_transfer + * @adap: Handle to I2C bus + * @msgs: One or more messages to execute before STOP is issued to + * terminate the operation; each message begins with a START. + * @num: Number of messages to be executed. + * + * Returns negative errno, else the number of messages executed. + * + * Adapter lock must be held when calling this function. No debug logging + * takes place. adap->algo->master_xfer existence isn't checked. + */ +int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + unsigned long orig_jiffies; + int ret, try; + + /* Retry automatically on arbitration loss */ + orig_jiffies = jiffies; + for (ret = 0, try = 0; try <= adap->retries; try++) { + ret = adap->algo->master_xfer(adap, msgs, num); + if (ret != -EAGAIN) + break; + if (time_after(jiffies, orig_jiffies + adap->timeout)) + break; + } + + return ret; +} +EXPORT_SYMBOL(__i2c_transfer); + /** * i2c_transfer - execute a single or combined I2C message * @adap: Handle to I2C bus @@ -1308,8 +1339,7 @@ module_exit(i2c_exit); */ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { - unsigned long orig_jiffies; - int ret, try; + int ret; /* REVISIT the fault reporting model here is weak: * @@ -1347,15 +1377,7 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) i2c_lock_adapter(adap); } - /* Retry automatically on arbitration loss */ - orig_jiffies = jiffies; - for (ret = 0, try = 0; try <= adap->retries; try++) { - ret = adap->algo->master_xfer(adap, msgs, num); - if (ret != -EAGAIN) - break; - if (time_after(jiffies, orig_jiffies + adap->timeout)) - break; - } + ret = __i2c_transfer(adap, msgs, num); i2c_unlock_adapter(adap); return ret; diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 195d8b3d9cfb..a121c012309a 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -68,6 +68,9 @@ extern int i2c_master_recv(const struct i2c_client *client, char *buf, */ extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); +/* Unlocked flavor */ +extern int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num); /* This is the very generalized SMBus access routine. You probably do not want to use this, though; one of the functions below may be much easier, -- cgit v1.2.3 From baedee177e6c553af455865718971d9a9c75e537 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 17 Jun 2012 16:40:36 +0200 Subject: firewire: core: add is_local sysfs device attribute Making this information available in sysfs allows to differentiate between controllers in the local and remote Linux PCs, and thus is useful for servers that are started with udev rules. Signed-off-by: Clemens Ladisch Signed-off-by: Stefan Richter --- drivers/firewire/core-device.c | 9 +++++++++ include/linux/firewire.h | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index 4d460ef87161..7a05fd24d68b 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -398,6 +398,14 @@ static ssize_t guid_show(struct device *dev, return ret; } +static ssize_t is_local_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fw_device *device = fw_device(dev); + + return sprintf(buf, "%u\n", device->is_local); +} + static int units_sprintf(char *buf, const u32 *directory) { struct fw_csr_iterator ci; @@ -447,6 +455,7 @@ static ssize_t units_show(struct device *dev, static struct device_attribute fw_device_attributes[] = { __ATTR_RO(config_rom), __ATTR_RO(guid), + __ATTR_RO(is_local), __ATTR_RO(units), __ATTR_NULL, }; diff --git a/include/linux/firewire.h b/include/linux/firewire.h index d77f60c6d1ed..cb2445e2e10e 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -138,7 +138,7 @@ struct fw_card { struct fw_attribute_group { struct attribute_group *groups[2]; struct attribute_group group; - struct attribute *attrs[12]; + struct attribute *attrs[13]; }; enum fw_device_state { -- cgit v1.2.3 From 8ec4cf5303e03941fa5fd91bbb9c85bd4ae88c47 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 25 Jun 2012 14:52:49 +0200 Subject: iio:adc: Add AD7265/AD7266 support This patch adds support for the Analog Devices AD7265 and AD7266 Analog-to-Digital converters. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 10 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ad7266.c | 536 +++++++++++++++++++++++++++++++++++ include/linux/platform_data/ad7266.h | 54 ++++ 4 files changed, 601 insertions(+) create mode 100644 drivers/iio/adc/ad7266.c create mode 100644 include/linux/platform_data/ad7266.h (limited to 'include') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 4f7f584cfd61..8a78b4f3ef58 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -3,6 +3,16 @@ # menu "Analog to digital converters" +config AD7266 + tristate "Analog Devices AD7265/AD7266 ADC driver" + depends on SPI_MASTER + select IIO_BUFFER + select IIO_TRIGGER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices AD7265 and AD7266 + ADCs. + config AT91_ADC tristate "Atmel AT91 ADC" depends on ARCH_AT91 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 175c8d41ea99..52eec254c38c 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -2,4 +2,5 @@ # Makefile for IIO ADC drivers # +obj-$(CONFIG_AD7266) += ad7266.o obj-$(CONFIG_AT91_ADC) += at91_adc.o diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c new file mode 100644 index 000000000000..5c3f1ba5a06d --- /dev/null +++ b/drivers/iio/adc/ad7266.c @@ -0,0 +1,536 @@ +/* + * AD7266/65 SPI ADC driver + * + * Copyright 2012 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +struct ad7266_state { + struct spi_device *spi; + struct regulator *reg; + unsigned long vref_uv; + + struct spi_transfer single_xfer[3]; + struct spi_message single_msg; + + enum ad7266_range range; + enum ad7266_mode mode; + bool fixed_addr; + struct gpio gpios[3]; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + * The buffer needs to be large enough to hold two samples (4 bytes) and + * the naturally aligned timestamp (8 bytes). + */ + uint8_t data[ALIGN(4, sizeof(s64)) + sizeof(s64)] ____cacheline_aligned; +}; + +static int ad7266_wakeup(struct ad7266_state *st) +{ + /* Any read with >= 2 bytes will wake the device */ + return spi_read(st->spi, st->data, 2); +} + +static int ad7266_powerdown(struct ad7266_state *st) +{ + /* Any read with < 2 bytes will powerdown the device */ + return spi_read(st->spi, st->data, 1); +} + +static int ad7266_preenable(struct iio_dev *indio_dev) +{ + struct ad7266_state *st = iio_priv(indio_dev); + int ret; + + ret = ad7266_wakeup(st); + if (ret) + return ret; + + ret = iio_sw_buffer_preenable(indio_dev); + if (ret) + ad7266_powerdown(st); + + return ret; +} + +static int ad7266_postdisable(struct iio_dev *indio_dev) +{ + struct ad7266_state *st = iio_priv(indio_dev); + return ad7266_powerdown(st); +} + +static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = { + .preenable = &ad7266_preenable, + .postenable = &iio_triggered_buffer_postenable, + .predisable = &iio_triggered_buffer_predisable, + .postdisable = &ad7266_postdisable, +}; + +static irqreturn_t ad7266_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct iio_buffer *buffer = indio_dev->buffer; + struct ad7266_state *st = iio_priv(indio_dev); + int ret; + + ret = spi_read(st->spi, st->data, 4); + if (ret == 0) { + if (indio_dev->scan_timestamp) + ((s64 *)st->data)[1] = pf->timestamp; + iio_push_to_buffer(buffer, (u8 *)st->data, pf->timestamp); + } + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static void ad7266_select_input(struct ad7266_state *st, unsigned int nr) +{ + unsigned int i; + + if (st->fixed_addr) + return; + + switch (st->mode) { + case AD7266_MODE_SINGLE_ENDED: + nr >>= 1; + break; + case AD7266_MODE_PSEUDO_DIFF: + nr |= 1; + break; + case AD7266_MODE_DIFF: + nr &= ~1; + break; + } + + for (i = 0; i < 3; ++i) + gpio_set_value(st->gpios[i].gpio, (bool)(nr & BIT(i))); +} + +static int ad7266_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad7266_state *st = iio_priv(indio_dev); + unsigned int nr = find_first_bit(scan_mask, indio_dev->masklength); + + ad7266_select_input(st, nr); + + return 0; +} + +static int ad7266_read_single(struct ad7266_state *st, int *val, + unsigned int address) +{ + int ret; + + ad7266_select_input(st, address); + + ret = spi_sync(st->spi, &st->single_msg); + *val = be16_to_cpu(st->data[address % 2]); + + return ret; +} + +static int ad7266_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long m) +{ + struct ad7266_state *st = iio_priv(indio_dev); + unsigned long scale_uv; + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + + ret = ad7266_read_single(st, val, chan->address); + if (ret) + return ret; + + *val = (*val >> 2) & 0xfff; + if (chan->scan_type.sign == 's') + *val = sign_extend32(*val, 11); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + scale_uv = (st->vref_uv * 100); + if (st->mode == AD7266_MODE_DIFF) + scale_uv *= 2; + if (st->range == AD7266_RANGE_2VREF) + scale_uv *= 2; + + scale_uv >>= chan->scan_type.realbits; + *val = scale_uv / 100000; + *val2 = (scale_uv % 100000) * 10; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OFFSET: + if (st->range == AD7266_RANGE_2VREF && + st->mode != AD7266_MODE_DIFF) + *val = 2048; + else + *val = 0; + return IIO_VAL_INT; + } + return -EINVAL; +} + +#define AD7266_CHAN(_chan, _sign) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_chan), \ + .address = (_chan), \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT \ + | IIO_CHAN_INFO_SCALE_SHARED_BIT \ + | IIO_CHAN_INFO_OFFSET_SHARED_BIT, \ + .scan_index = (_chan), \ + .scan_type = { \ + .sign = (_sign), \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 2, \ + .endianness = IIO_BE, \ + }, \ +} + +#define AD7266_DECLARE_SINGLE_ENDED_CHANNELS(_name, _sign) \ +const struct iio_chan_spec ad7266_channels_##_name[] = { \ + AD7266_CHAN(0, (_sign)), \ + AD7266_CHAN(1, (_sign)), \ + AD7266_CHAN(2, (_sign)), \ + AD7266_CHAN(3, (_sign)), \ + AD7266_CHAN(4, (_sign)), \ + AD7266_CHAN(5, (_sign)), \ + AD7266_CHAN(6, (_sign)), \ + AD7266_CHAN(7, (_sign)), \ + AD7266_CHAN(8, (_sign)), \ + AD7266_CHAN(9, (_sign)), \ + AD7266_CHAN(10, (_sign)), \ + AD7266_CHAN(11, (_sign)), \ + IIO_CHAN_SOFT_TIMESTAMP(13), \ +} + +#define AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(_name, _sign) \ +const struct iio_chan_spec ad7266_channels_##_name##_fixed[] = { \ + AD7266_CHAN(0, (_sign)), \ + AD7266_CHAN(1, (_sign)), \ + IIO_CHAN_SOFT_TIMESTAMP(2), \ +} + +static AD7266_DECLARE_SINGLE_ENDED_CHANNELS(u, 'u'); +static AD7266_DECLARE_SINGLE_ENDED_CHANNELS(s, 's'); +static AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(u, 'u'); +static AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(s, 's'); + +#define AD7266_CHAN_DIFF(_chan, _sign) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_chan) * 2, \ + .channel2 = (_chan) * 2 + 1, \ + .address = (_chan), \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT \ + | IIO_CHAN_INFO_SCALE_SHARED_BIT \ + | IIO_CHAN_INFO_OFFSET_SHARED_BIT, \ + .scan_index = (_chan), \ + .scan_type = { \ + .sign = _sign, \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 2, \ + .endianness = IIO_BE, \ + }, \ + .differential = 1, \ +} + +#define AD7266_DECLARE_DIFF_CHANNELS(_name, _sign) \ +const struct iio_chan_spec ad7266_channels_diff_##_name[] = { \ + AD7266_CHAN_DIFF(0, (_sign)), \ + AD7266_CHAN_DIFF(1, (_sign)), \ + AD7266_CHAN_DIFF(2, (_sign)), \ + AD7266_CHAN_DIFF(3, (_sign)), \ + AD7266_CHAN_DIFF(4, (_sign)), \ + AD7266_CHAN_DIFF(5, (_sign)), \ + IIO_CHAN_SOFT_TIMESTAMP(6), \ +} + +static AD7266_DECLARE_DIFF_CHANNELS(s, 's'); +static AD7266_DECLARE_DIFF_CHANNELS(u, 'u'); + +#define AD7266_DECLARE_DIFF_CHANNELS_FIXED(_name, _sign) \ +const struct iio_chan_spec ad7266_channels_diff_fixed_##_name[] = { \ + AD7266_CHAN_DIFF(0, (_sign)), \ + AD7266_CHAN_DIFF(1, (_sign)), \ + IIO_CHAN_SOFT_TIMESTAMP(2), \ +} + +static AD7266_DECLARE_DIFF_CHANNELS_FIXED(s, 's'); +static AD7266_DECLARE_DIFF_CHANNELS_FIXED(u, 'u'); + +static const struct iio_info ad7266_info = { + .read_raw = &ad7266_read_raw, + .update_scan_mode = &ad7266_update_scan_mode, + .driver_module = THIS_MODULE, +}; + +static unsigned long ad7266_available_scan_masks[] = { + 0x003, + 0x00c, + 0x030, + 0x0c0, + 0x300, + 0xc00, + 0x000, +}; + +static unsigned long ad7266_available_scan_masks_diff[] = { + 0x003, + 0x00c, + 0x030, + 0x000, +}; + +static unsigned long ad7266_available_scan_masks_fixed[] = { + 0x003, + 0x000, +}; + +struct ad7266_chan_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; + unsigned long *scan_masks; +}; + +#define AD7266_CHAN_INFO_INDEX(_differential, _signed, _fixed) \ + (((_differential) << 2) | ((_signed) << 1) | ((_fixed) << 0)) + +static const struct ad7266_chan_info ad7266_chan_infos[] = { + [AD7266_CHAN_INFO_INDEX(0, 0, 0)] = { + .channels = ad7266_channels_u, + .num_channels = ARRAY_SIZE(ad7266_channels_u), + .scan_masks = ad7266_available_scan_masks, + }, + [AD7266_CHAN_INFO_INDEX(0, 0, 1)] = { + .channels = ad7266_channels_u_fixed, + .num_channels = ARRAY_SIZE(ad7266_channels_u_fixed), + .scan_masks = ad7266_available_scan_masks_fixed, + }, + [AD7266_CHAN_INFO_INDEX(0, 1, 0)] = { + .channels = ad7266_channels_s, + .num_channels = ARRAY_SIZE(ad7266_channels_s), + .scan_masks = ad7266_available_scan_masks, + }, + [AD7266_CHAN_INFO_INDEX(0, 1, 1)] = { + .channels = ad7266_channels_s_fixed, + .num_channels = ARRAY_SIZE(ad7266_channels_s_fixed), + .scan_masks = ad7266_available_scan_masks_fixed, + }, + [AD7266_CHAN_INFO_INDEX(1, 0, 0)] = { + .channels = ad7266_channels_diff_u, + .num_channels = ARRAY_SIZE(ad7266_channels_diff_u), + .scan_masks = ad7266_available_scan_masks_diff, + }, + [AD7266_CHAN_INFO_INDEX(1, 0, 1)] = { + .channels = ad7266_channels_diff_fixed_u, + .num_channels = ARRAY_SIZE(ad7266_channels_diff_fixed_u), + .scan_masks = ad7266_available_scan_masks_fixed, + }, + [AD7266_CHAN_INFO_INDEX(1, 1, 0)] = { + .channels = ad7266_channels_diff_s, + .num_channels = ARRAY_SIZE(ad7266_channels_diff_s), + .scan_masks = ad7266_available_scan_masks_diff, + }, + [AD7266_CHAN_INFO_INDEX(1, 1, 1)] = { + .channels = ad7266_channels_diff_fixed_s, + .num_channels = ARRAY_SIZE(ad7266_channels_diff_fixed_s), + .scan_masks = ad7266_available_scan_masks_fixed, + }, +}; + +static void __devinit ad7266_init_channels(struct iio_dev *indio_dev) +{ + struct ad7266_state *st = iio_priv(indio_dev); + bool is_differential, is_signed; + const struct ad7266_chan_info *chan_info; + int i; + + is_differential = st->mode != AD7266_MODE_SINGLE_ENDED; + is_signed = (st->range == AD7266_RANGE_2VREF) | + (st->mode == AD7266_MODE_DIFF); + + i = AD7266_CHAN_INFO_INDEX(is_differential, is_signed, st->fixed_addr); + chan_info = &ad7266_chan_infos[i]; + + indio_dev->channels = chan_info->channels; + indio_dev->num_channels = chan_info->num_channels; + indio_dev->available_scan_masks = chan_info->scan_masks; + indio_dev->masklength = chan_info->num_channels - 1; +} + +static const char * const ad7266_gpio_labels[] = { + "AD0", "AD1", "AD2", +}; + +static int __devinit ad7266_probe(struct spi_device *spi) +{ + struct ad7266_platform_data *pdata = spi->dev.platform_data; + struct iio_dev *indio_dev; + struct ad7266_state *st; + unsigned int i; + int ret; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->reg = regulator_get(&spi->dev, "vref"); + if (!IS_ERR_OR_NULL(st->reg)) { + ret = regulator_enable(st->reg); + if (ret) + goto error_put_reg; + + st->vref_uv = regulator_get_voltage(st->reg); + } else { + /* Use internal reference */ + st->vref_uv = 2500000; + } + + if (pdata) { + st->fixed_addr = pdata->fixed_addr; + st->mode = pdata->mode; + st->range = pdata->range; + + if (!st->fixed_addr) { + for (i = 0; i < ARRAY_SIZE(st->gpios); ++i) { + st->gpios[i].gpio = pdata->addr_gpios[i]; + st->gpios[i].flags = GPIOF_OUT_INIT_LOW; + st->gpios[i].label = ad7266_gpio_labels[i]; + } + ret = gpio_request_array(st->gpios, + ARRAY_SIZE(st->gpios)); + if (ret) + goto error_disable_reg; + } + } else { + st->fixed_addr = true; + st->range = AD7266_RANGE_VREF; + st->mode = AD7266_MODE_DIFF; + } + + spi_set_drvdata(spi, indio_dev); + st->spi = spi; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ad7266_info; + + ad7266_init_channels(indio_dev); + + /* wakeup */ + st->single_xfer[0].rx_buf = &st->data; + st->single_xfer[0].len = 2; + st->single_xfer[0].cs_change = 1; + /* conversion */ + st->single_xfer[1].rx_buf = &st->data; + st->single_xfer[1].len = 4; + st->single_xfer[1].cs_change = 1; + /* powerdown */ + st->single_xfer[2].tx_buf = &st->data; + st->single_xfer[2].len = 1; + + spi_message_init(&st->single_msg); + spi_message_add_tail(&st->single_xfer[0], &st->single_msg); + spi_message_add_tail(&st->single_xfer[1], &st->single_msg); + spi_message_add_tail(&st->single_xfer[2], &st->single_msg); + + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + &ad7266_trigger_handler, &iio_triggered_buffer_setup_ops); + if (ret) + goto error_free_gpios; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_buffer_cleanup; + + return 0; + +error_buffer_cleanup: + iio_triggered_buffer_cleanup(indio_dev); +error_free_gpios: + if (!st->fixed_addr) + gpio_free_array(st->gpios, ARRAY_SIZE(st->gpios)); +error_disable_reg: + if (!IS_ERR_OR_NULL(st->reg)) + regulator_disable(st->reg); +error_put_reg: + if (!IS_ERR_OR_NULL(st->reg)) + regulator_put(st->reg); + + iio_device_free(indio_dev); + + return ret; +} + +static int __devexit ad7266_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad7266_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + if (!st->fixed_addr) + gpio_free_array(st->gpios, ARRAY_SIZE(st->gpios)); + if (!IS_ERR_OR_NULL(st->reg)) { + regulator_disable(st->reg); + regulator_put(st->reg); + } + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad7266_id[] = { + {"ad7265", 0}, + {"ad7266", 0}, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7266_id); + +static struct spi_driver ad7266_driver = { + .driver = { + .name = "ad7266", + .owner = THIS_MODULE, + }, + .probe = ad7266_probe, + .remove = __devexit_p(ad7266_remove), + .id_table = ad7266_id, +}; +module_spi_driver(ad7266_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Analog Devices AD7266/65 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/ad7266.h b/include/linux/platform_data/ad7266.h new file mode 100644 index 000000000000..eabfdcb26992 --- /dev/null +++ b/include/linux/platform_data/ad7266.h @@ -0,0 +1,54 @@ +/* + * AD7266/65 SPI ADC driver + * + * Copyright 2012 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#ifndef __IIO_ADC_AD7266_H__ +#define __IIO_ADC_AD7266_H__ + +/** + * enum ad7266_range - AD7266 reference voltage range + * @AD7266_RANGE_VREF: Device is configured for input range 0V - VREF + * (RANGE pin set to low) + * @AD7266_RANGE_2VREF: Device is configured for input range 0V - 2VREF + * (RANGE pin set to high) + */ +enum ad7266_range { + AD7266_RANGE_VREF, + AD7266_RANGE_2VREF, +}; + +/** + * enum ad7266_mode - AD7266 sample mode + * @AD7266_MODE_DIFF: Device is configured for full differential mode + * (SGL/DIFF pin set to low, AD0 pin set to low) + * @AD7266_MODE_PSEUDO_DIFF: Device is configured for pseudo differential mode + * (SGL/DIFF pin set to low, AD0 pin set to high) + * @AD7266_MODE_SINGLE_ENDED: Device is configured for single-ended mode + * (SGL/DIFF pin set to high) + */ +enum ad7266_mode { + AD7266_MODE_DIFF, + AD7266_MODE_PSEUDO_DIFF, + AD7266_MODE_SINGLE_ENDED, +}; + +/** + * struct ad7266_platform_data - Platform data for the AD7266 driver + * @range: Reference voltage range the device is configured for + * @mode: Sample mode the device is configured for + * @fixed_addr: Whether the address pins are hard-wired + * @addr_gpios: GPIOs used for controlling the address pins, only used if + * fixed_addr is set to false. + */ +struct ad7266_platform_data { + enum ad7266_range range; + enum ad7266_mode mode; + bool fixed_addr; + unsigned int addr_gpios[3]; +}; + +#endif -- cgit v1.2.3 From 314be14bb89369b2164125b0ec3b24d85b407b62 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 1 May 2012 21:04:24 +0100 Subject: iio: Rename _st_ functions to loose the bit that meant the staging version. These were originally introduced when the plan was to have parallel IIO cores in and out of staging with a slow move between them. Now we have reached the point where the whole core has moved, they need clearing up! Signed-off-by: Jonathan Cameron --- drivers/iio/inkern.c | 33 +++++++++++++++------------------ drivers/staging/iio/iio_hwmon.c | 12 ++++++------ include/linux/iio/consumer.h | 34 +++++++++++++++++----------------- 3 files changed, 38 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index d4760bd1e9b1..9a46ca61ef02 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -92,8 +92,7 @@ error_ret: EXPORT_SYMBOL_GPL(iio_map_array_unregister); static const struct iio_chan_spec -*iio_chan_spec_from_name(const struct iio_dev *indio_dev, - const char *name) +*iio_chan_spec_from_name(const struct iio_dev *indio_dev, const char *name) { int i; const struct iio_chan_spec *chan = NULL; @@ -108,8 +107,7 @@ static const struct iio_chan_spec } -struct iio_channel *iio_st_channel_get(const char *name, - const char *channel_name) +struct iio_channel *iio_channel_get(const char *name, const char *channel_name) { struct iio_map_internal *c_i = NULL, *c = NULL; struct iio_channel *channel; @@ -145,16 +143,16 @@ struct iio_channel *iio_st_channel_get(const char *name, return channel; } -EXPORT_SYMBOL_GPL(iio_st_channel_get); +EXPORT_SYMBOL_GPL(iio_channel_get); -void iio_st_channel_release(struct iio_channel *channel) +void iio_channel_release(struct iio_channel *channel) { iio_device_put(channel->indio_dev); kfree(channel); } -EXPORT_SYMBOL_GPL(iio_st_channel_release); +EXPORT_SYMBOL_GPL(iio_channel_release); -struct iio_channel *iio_st_channel_get_all(const char *name) +struct iio_channel *iio_channel_get_all(const char *name) { struct iio_channel *chans; struct iio_map_internal *c = NULL; @@ -217,9 +215,9 @@ error_ret: return ERR_PTR(ret); } -EXPORT_SYMBOL_GPL(iio_st_channel_get_all); +EXPORT_SYMBOL_GPL(iio_channel_get_all); -void iio_st_channel_release_all(struct iio_channel *channels) +void iio_channel_release_all(struct iio_channel *channels) { struct iio_channel *chan = &channels[0]; @@ -229,9 +227,9 @@ void iio_st_channel_release_all(struct iio_channel *channels) } kfree(channels); } -EXPORT_SYMBOL_GPL(iio_st_channel_release_all); +EXPORT_SYMBOL_GPL(iio_channel_release_all); -int iio_st_read_channel_raw(struct iio_channel *chan, int *val) +int iio_read_channel_raw(struct iio_channel *chan, int *val) { int val2, ret; @@ -248,9 +246,9 @@ err_unlock: return ret; } -EXPORT_SYMBOL_GPL(iio_st_read_channel_raw); +EXPORT_SYMBOL_GPL(iio_read_channel_raw); -int iio_st_read_channel_scale(struct iio_channel *chan, int *val, int *val2) +int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2) { int ret; @@ -269,10 +267,9 @@ err_unlock: return ret; } -EXPORT_SYMBOL_GPL(iio_st_read_channel_scale); +EXPORT_SYMBOL_GPL(iio_read_channel_scale); -int iio_st_get_channel_type(struct iio_channel *chan, - enum iio_chan_type *type) +int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type) { int ret = 0; /* Need to verify underlying driver has not gone away */ @@ -289,4 +286,4 @@ err_unlock: return ret; } -EXPORT_SYMBOL_GPL(iio_st_get_channel_type); +EXPORT_SYMBOL_GPL(iio_get_channel_type); diff --git a/drivers/staging/iio/iio_hwmon.c b/drivers/staging/iio/iio_hwmon.c index b03554fee443..27d27ec9521f 100644 --- a/drivers/staging/iio/iio_hwmon.c +++ b/drivers/staging/iio/iio_hwmon.c @@ -51,12 +51,12 @@ static ssize_t iio_hwmon_read_val(struct device *dev, * No locking between this pair, so theoretically possible * the scale has changed. */ - ret = iio_st_read_channel_raw(&state->channels[sattr->index], + ret = iio_read_channel_raw(&state->channels[sattr->index], &val); if (ret < 0) return ret; - ret = iio_st_read_channel_scale(&state->channels[sattr->index], + ret = iio_read_channel_scale(&state->channels[sattr->index], &scaleint, &scalepart); if (ret < 0) return ret; @@ -106,7 +106,7 @@ static int __devinit iio_hwmon_probe(struct platform_device *pdev) goto error_ret; } - st->channels = iio_st_channel_get_all(dev_name(&pdev->dev)); + st->channels = iio_channel_get_all(dev_name(&pdev->dev)); if (IS_ERR(st->channels)) { ret = PTR_ERR(st->channels); goto error_free_state; @@ -130,7 +130,7 @@ static int __devinit iio_hwmon_probe(struct platform_device *pdev) } sysfs_attr_init(&a->dev_attr.attr); - ret = iio_st_get_channel_type(&st->channels[i], &type); + ret = iio_get_channel_type(&st->channels[i], &type); if (ret < 0) { kfree(a); goto error_free_attrs; @@ -186,7 +186,7 @@ error_free_attrs: iio_hwmon_free_attrs(st); kfree(st->attrs); error_release_channels: - iio_st_channel_release_all(st->channels); + iio_channel_release_all(st->channels); error_free_state: kfree(st); error_ret: @@ -201,7 +201,7 @@ static int __devexit iio_hwmon_remove(struct platform_device *pdev) sysfs_remove_group(&pdev->dev.kobj, &st->attr_group); iio_hwmon_free_attrs(st); kfree(st->attrs); - iio_st_channel_release_all(st->channels); + iio_channel_release_all(st->channels); return 0; } diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h index 1a15e560a5a1..e2657e6d4d26 100644 --- a/include/linux/iio/consumer.h +++ b/include/linux/iio/consumer.h @@ -33,17 +33,17 @@ struct iio_channel { * side. This typically describes the channels use within * the consumer. E.g. 'battery_voltage' */ -struct iio_channel *iio_st_channel_get(const char *name, - const char *consumer_channel); +struct iio_channel *iio_channel_get(const char *name, + const char *consumer_channel); /** - * iio_st_channel_release() - release channels obtained via iio_st_channel_get + * iio_channel_release() - release channels obtained via iio_channel_get * @chan: The channel to be released. */ -void iio_st_channel_release(struct iio_channel *chan); +void iio_channel_release(struct iio_channel *chan); /** - * iio_st_channel_get_all() - get all channels associated with a client + * iio_channel_get_all() - get all channels associated with a client * @name: name of consumer device. * * Returns an array of iio_channel structures terminated with one with @@ -51,37 +51,37 @@ void iio_st_channel_release(struct iio_channel *chan); * This function is used by fairly generic consumers to get all the * channels registered as having this consumer. */ -struct iio_channel *iio_st_channel_get_all(const char *name); +struct iio_channel *iio_channel_get_all(const char *name); /** - * iio_st_channel_release_all() - reverse iio_st_get_all + * iio_channel_release_all() - reverse iio_channel_get_all * @chan: Array of channels to be released. */ -void iio_st_channel_release_all(struct iio_channel *chan); +void iio_channel_release_all(struct iio_channel *chan); /** - * iio_st_read_channel_raw() - read from a given channel + * iio_read_channel_raw() - read from a given channel * @channel: The channel being queried. * @val: Value read back. * * Note raw reads from iio channels are in adc counts and hence * scale will need to be applied if standard units required. */ -int iio_st_read_channel_raw(struct iio_channel *chan, - int *val); +int iio_read_channel_raw(struct iio_channel *chan, + int *val); /** - * iio_st_get_channel_type() - get the type of a channel + * iio_get_channel_type() - get the type of a channel * @channel: The channel being queried. * @type: The type of the channel. * * returns the enum iio_chan_type of the channel */ -int iio_st_get_channel_type(struct iio_channel *channel, - enum iio_chan_type *type); +int iio_get_channel_type(struct iio_channel *channel, + enum iio_chan_type *type); /** - * iio_st_read_channel_scale() - read the scale value for a channel + * iio_read_channel_scale() - read the scale value for a channel * @channel: The channel being queried. * @val: First part of value read back. * @val2: Second part of value read back. @@ -90,7 +90,7 @@ int iio_st_get_channel_type(struct iio_channel *channel, * as IIO_VAL_INT_PLUS_MICRO telling us we have a value of val * + val2/1e6 */ -int iio_st_read_channel_scale(struct iio_channel *chan, int *val, - int *val2); +int iio_read_channel_scale(struct iio_channel *chan, int *val, + int *val2); #endif -- cgit v1.2.3 From 8f5879b20be7f918cdc4b3d831cfd8f3dc02c74c Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sat, 5 May 2012 10:39:22 +0100 Subject: IIO: Add a modifier for sqrt(x^2+y^2) There will probably be a number of such modifiers eventually but this one is used in the adis16204 accelerometer driver. Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 1 + include/linux/iio/types.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index a5a446beb2fa..e42749ec5c3c 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -70,6 +70,7 @@ static const char * const iio_modifier_names[] = { [IIO_MOD_X] = "x", [IIO_MOD_Y] = "y", [IIO_MOD_Z] = "z", + [IIO_MOD_ROOT_SUM_SQUARED_X_Y] = "sqrt(x^2+y^2)", [IIO_MOD_LIGHT_BOTH] = "both", [IIO_MOD_LIGHT_IR] = "ir", }; diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index d086736a9033..210559ddf8a3 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -44,6 +44,7 @@ enum iio_modifier { IIO_MOD_X_OR_Y_OR_Z, IIO_MOD_LIGHT_BOTH, IIO_MOD_LIGHT_IR, + IIO_MOD_ROOT_SUM_SQUARED_X_Y, }; #define IIO_VAL_INT 1 -- cgit v1.2.3 From cf82cb8128496955a38fa62e1819ceb1d596e2eb Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sat, 5 May 2012 10:56:41 +0100 Subject: IIO: Add a modifier for x^2+y^2+z^2 There will probably be a number of such modifiers eventually but this one is used in the adis16240 accelerometer driver. Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 1 + include/linux/iio/types.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index e42749ec5c3c..bb3c692e49b8 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -71,6 +71,7 @@ static const char * const iio_modifier_names[] = { [IIO_MOD_Y] = "y", [IIO_MOD_Z] = "z", [IIO_MOD_ROOT_SUM_SQUARED_X_Y] = "sqrt(x^2+y^2)", + [IIO_MOD_SUM_SQUARED_X_Y_Z] = "x^2+y^2+z^2", [IIO_MOD_LIGHT_BOTH] = "both", [IIO_MOD_LIGHT_IR] = "ir", }; diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index 210559ddf8a3..e25040173346 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -45,6 +45,7 @@ enum iio_modifier { IIO_MOD_LIGHT_BOTH, IIO_MOD_LIGHT_IR, IIO_MOD_ROOT_SUM_SQUARED_X_Y, + IIO_MOD_SUM_SQUARED_X_Y_Z, }; #define IIO_VAL_INT 1 -- cgit v1.2.3 From 48afb3112e6373a292e54d675e986a5da14c0516 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 26 May 2012 16:58:15 +0100 Subject: dmaengine: PL08x: remove circular_buffer boolean from channel data Circular buffers are not handled in this way; we have a separate API call now to setup circular buffers. So lets not mislead people with this bool. Acked-by: Linus Walleij Tested-by: Linus Walleij Signed-off-by: Russell King --- drivers/dma/amba-pl08x.c | 7 ------- include/linux/amba/pl08x.h | 4 ---- 2 files changed, 11 deletions(-) (limited to 'include') diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 9c5bae6f85b5..5821125f0458 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -1701,13 +1701,6 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x, return -ENOMEM; } } - if (chan->cd->circular_buffer) { - dev_err(&pl08x->adev->dev, - "channel %s: circular buffers not supported\n", - chan->name); - kfree(chan); - continue; - } dev_dbg(&pl08x->adev->dev, "initialize virtual channel \"%s\"\n", chan->name); diff --git a/include/linux/amba/pl08x.h b/include/linux/amba/pl08x.h index 02549017212a..0f5b34d668b6 100644 --- a/include/linux/amba/pl08x.h +++ b/include/linux/amba/pl08x.h @@ -51,9 +51,6 @@ enum { * can be the address of a FIFO register for burst requests for example. * This can be left undefined if the PrimeCell API is used for configuring * this. - * @circular_buffer: whether the buffer passed in is circular and - * shall simply be looped round round (like a record baby round - * round round round) * @single: the device connected to this channel will request single DMA * transfers, not bursts. (Bursts are default.) * @periph_buses: the device connected to this channel is accessible via @@ -66,7 +63,6 @@ struct pl08x_channel_data { u32 muxval; u32 cctl; dma_addr_t addr; - bool circular_buffer; bool single; u8 periph_buses; }; -- cgit v1.2.3 From aeea1808fe752e917b966961bde3e9603f206dec Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 17 May 2012 15:01:28 +0100 Subject: dmaengine: PL08x: clean up get_signal/put_signal Try to avoid dereferencing the DMA engine's channel struct in these platform helpers; instead, pass a pointer to the channel data into get_signal(), and the returned signal number to put_signal(). Tested-by: Linus Walleij Signed-off-by: Russell King --- drivers/dma/amba-pl08x.c | 4 ++-- include/linux/amba/pl08x.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 5821125f0458..cc08c8c01a9e 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -874,7 +874,7 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan, * Can the platform allow us to use this channel? */ if (plchan->slave && pl08x->pd->get_signal) { - ret = pl08x->pd->get_signal(plchan); + ret = pl08x->pd->get_signal(plchan->cd); if (ret < 0) { dev_dbg(&pl08x->adev->dev, "unable to use physical channel %d for transfer on %s due to platform restrictions\n", @@ -909,7 +909,7 @@ static void release_phy_channel(struct pl08x_dma_chan *plchan) struct pl08x_driver_data *pl08x = plchan->host; if ((plchan->phychan->signal >= 0) && pl08x->pd->put_signal) { - pl08x->pd->put_signal(plchan); + pl08x->pd->put_signal(plchan->cd, plchan->phychan->signal); plchan->phychan->signal = -1; } pl08x_put_phy_channel(pl08x, plchan->phychan); diff --git a/include/linux/amba/pl08x.h b/include/linux/amba/pl08x.h index 0f5b34d668b6..88765a62c8f2 100644 --- a/include/linux/amba/pl08x.h +++ b/include/linux/amba/pl08x.h @@ -225,8 +225,8 @@ struct pl08x_platform_data { const struct pl08x_channel_data *slave_channels; unsigned int num_slave_channels; struct pl08x_channel_data memcpy_channel; - int (*get_signal)(struct pl08x_dma_chan *); - void (*put_signal)(struct pl08x_dma_chan *); + int (*get_signal)(const struct pl08x_channel_data *); + void (*put_signal)(const struct pl08x_channel_data *, int); u8 lli_buses; u8 mem_buses; }; -- cgit v1.2.3 From b23f204c8dbbed8e501442c47d7639aac21a3d84 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 16 May 2012 10:48:44 +0100 Subject: dmaengine: PL08x: move private data structures into amba-pl08x.c Move the driver private data structures into the driver itself, rather than having them exposed to everyone in a header file. Acked-by: Linus Walleij Tested-by: Linus Walleij Signed-off-by: Russell King --- drivers/dma/amba-pl08x.c | 136 +++++++++++++++++++++++++++++++++++++++++++ include/linux/amba/pl08x.h | 141 +-------------------------------------------- 2 files changed, 138 insertions(+), 139 deletions(-) (limited to 'include') diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index cc08c8c01a9e..94949909e4e9 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -90,6 +90,7 @@ #define DRIVER_NAME "pl08xdmac" static struct amba_driver pl08x_amba_driver; +struct pl08x_driver_data; /** * struct vendor_data - vendor-specific config parameters for PL08x derivatives @@ -118,6 +119,141 @@ struct pl08x_lli { u32 cctl; }; +/** + * struct pl08x_bus_data - information of source or destination + * busses for a transfer + * @addr: current address + * @maxwidth: the maximum width of a transfer on this bus + * @buswidth: the width of this bus in bytes: 1, 2 or 4 + */ +struct pl08x_bus_data { + dma_addr_t addr; + u8 maxwidth; + u8 buswidth; +}; + +/** + * struct pl08x_phy_chan - holder for the physical channels + * @id: physical index to this channel + * @lock: a lock to use when altering an instance of this struct + * @signal: the physical signal (aka channel) serving this physical channel + * right now + * @serving: the virtual channel currently being served by this physical + * channel + */ +struct pl08x_phy_chan { + unsigned int id; + void __iomem *base; + spinlock_t lock; + int signal; + struct pl08x_dma_chan *serving; +}; + +/** + * struct pl08x_sg - structure containing data per sg + * @src_addr: src address of sg + * @dst_addr: dst address of sg + * @len: transfer len in bytes + * @node: node for txd's dsg_list + */ +struct pl08x_sg { + dma_addr_t src_addr; + dma_addr_t dst_addr; + size_t len; + struct list_head node; +}; + +/** + * struct pl08x_txd - wrapper for struct dma_async_tx_descriptor + * @tx: async tx descriptor + * @node: node for txd list for channels + * @dsg_list: list of children sg's + * @direction: direction of transfer + * @llis_bus: DMA memory address (physical) start for the LLIs + * @llis_va: virtual memory address start for the LLIs + * @cctl: control reg values for current txd + * @ccfg: config reg values for current txd + */ +struct pl08x_txd { + struct dma_async_tx_descriptor tx; + struct list_head node; + struct list_head dsg_list; + enum dma_transfer_direction direction; + dma_addr_t llis_bus; + struct pl08x_lli *llis_va; + /* Default cctl value for LLIs */ + u32 cctl; + /* + * Settings to be put into the physical channel when we + * trigger this txd. Other registers are in llis_va[0]. + */ + u32 ccfg; +}; + +/** + * struct pl08x_dma_chan_state - holds the PL08x specific virtual channel + * states + * @PL08X_CHAN_IDLE: the channel is idle + * @PL08X_CHAN_RUNNING: the channel has allocated a physical transport + * channel and is running a transfer on it + * @PL08X_CHAN_PAUSED: the channel has allocated a physical transport + * channel, but the transfer is currently paused + * @PL08X_CHAN_WAITING: the channel is waiting for a physical transport + * channel to become available (only pertains to memcpy channels) + */ +enum pl08x_dma_chan_state { + PL08X_CHAN_IDLE, + PL08X_CHAN_RUNNING, + PL08X_CHAN_PAUSED, + PL08X_CHAN_WAITING, +}; + +/** + * struct pl08x_dma_chan - this structure wraps a DMA ENGINE channel + * @chan: wrappped abstract channel + * @phychan: the physical channel utilized by this channel, if there is one + * @phychan_hold: if non-zero, hold on to the physical channel even if we + * have no pending entries + * @tasklet: tasklet scheduled by the IRQ to handle actual work etc + * @name: name of channel + * @cd: channel platform data + * @runtime_addr: address for RX/TX according to the runtime config + * @runtime_direction: current direction of this channel according to + * runtime config + * @pend_list: queued transactions pending on this channel + * @at: active transaction on this channel + * @lock: a lock for this channel data + * @host: a pointer to the host (internal use) + * @state: whether the channel is idle, paused, running etc + * @slave: whether this channel is a device (slave) or for memcpy + * @device_fc: Flow Controller Settings for ccfg register. Only valid for slave + * channels. Fill with 'true' if peripheral should be flow controller. Direction + * will be selected at Runtime. + * @waiting: a TX descriptor on this channel which is waiting for a physical + * channel to become available + */ +struct pl08x_dma_chan { + struct dma_chan chan; + struct pl08x_phy_chan *phychan; + int phychan_hold; + struct tasklet_struct tasklet; + char *name; + const struct pl08x_channel_data *cd; + dma_addr_t src_addr; + dma_addr_t dst_addr; + u32 src_cctl; + u32 dst_cctl; + enum dma_transfer_direction runtime_direction; + struct list_head pend_list; + struct pl08x_txd *at; + spinlock_t lock; + struct pl08x_driver_data *host; + enum pl08x_dma_chan_state state; + bool slave; + bool device_fc; + struct pl08x_txd *waiting; +}; + /** * struct pl08x_driver_data - the local state holder for the PL08x * @slave: slave engine for this instance diff --git a/include/linux/amba/pl08x.h b/include/linux/amba/pl08x.h index 88765a62c8f2..48d02bf66ec9 100644 --- a/include/linux/amba/pl08x.h +++ b/include/linux/amba/pl08x.h @@ -21,8 +21,9 @@ #include #include -struct pl08x_lli; struct pl08x_driver_data; +struct pl08x_phy_chan; +struct pl08x_txd; /* Bitmasks for selecting AHB ports for DMA transfers */ enum { @@ -67,144 +68,6 @@ struct pl08x_channel_data { u8 periph_buses; }; -/** - * Struct pl08x_bus_data - information of source or destination - * busses for a transfer - * @addr: current address - * @maxwidth: the maximum width of a transfer on this bus - * @buswidth: the width of this bus in bytes: 1, 2 or 4 - */ -struct pl08x_bus_data { - dma_addr_t addr; - u8 maxwidth; - u8 buswidth; -}; - -/** - * struct pl08x_phy_chan - holder for the physical channels - * @id: physical index to this channel - * @lock: a lock to use when altering an instance of this struct - * @signal: the physical signal (aka channel) serving this physical channel - * right now - * @serving: the virtual channel currently being served by this physical - * channel - * @locked: channel unavailable for the system, e.g. dedicated to secure - * world - */ -struct pl08x_phy_chan { - unsigned int id; - void __iomem *base; - spinlock_t lock; - int signal; - struct pl08x_dma_chan *serving; - bool locked; -}; - -/** - * struct pl08x_sg - structure containing data per sg - * @src_addr: src address of sg - * @dst_addr: dst address of sg - * @len: transfer len in bytes - * @node: node for txd's dsg_list - */ -struct pl08x_sg { - dma_addr_t src_addr; - dma_addr_t dst_addr; - size_t len; - struct list_head node; -}; - -/** - * struct pl08x_txd - wrapper for struct dma_async_tx_descriptor - * @tx: async tx descriptor - * @node: node for txd list for channels - * @dsg_list: list of children sg's - * @direction: direction of transfer - * @llis_bus: DMA memory address (physical) start for the LLIs - * @llis_va: virtual memory address start for the LLIs - * @cctl: control reg values for current txd - * @ccfg: config reg values for current txd - */ -struct pl08x_txd { - struct dma_async_tx_descriptor tx; - struct list_head node; - struct list_head dsg_list; - enum dma_transfer_direction direction; - dma_addr_t llis_bus; - struct pl08x_lli *llis_va; - /* Default cctl value for LLIs */ - u32 cctl; - /* - * Settings to be put into the physical channel when we - * trigger this txd. Other registers are in llis_va[0]. - */ - u32 ccfg; -}; - -/** - * struct pl08x_dma_chan_state - holds the PL08x specific virtual channel - * states - * @PL08X_CHAN_IDLE: the channel is idle - * @PL08X_CHAN_RUNNING: the channel has allocated a physical transport - * channel and is running a transfer on it - * @PL08X_CHAN_PAUSED: the channel has allocated a physical transport - * channel, but the transfer is currently paused - * @PL08X_CHAN_WAITING: the channel is waiting for a physical transport - * channel to become available (only pertains to memcpy channels) - */ -enum pl08x_dma_chan_state { - PL08X_CHAN_IDLE, - PL08X_CHAN_RUNNING, - PL08X_CHAN_PAUSED, - PL08X_CHAN_WAITING, -}; - -/** - * struct pl08x_dma_chan - this structure wraps a DMA ENGINE channel - * @chan: wrappped abstract channel - * @phychan: the physical channel utilized by this channel, if there is one - * @phychan_hold: if non-zero, hold on to the physical channel even if we - * have no pending entries - * @tasklet: tasklet scheduled by the IRQ to handle actual work etc - * @name: name of channel - * @cd: channel platform data - * @runtime_addr: address for RX/TX according to the runtime config - * @runtime_direction: current direction of this channel according to - * runtime config - * @pend_list: queued transactions pending on this channel - * @at: active transaction on this channel - * @lock: a lock for this channel data - * @host: a pointer to the host (internal use) - * @state: whether the channel is idle, paused, running etc - * @slave: whether this channel is a device (slave) or for memcpy - * @device_fc: Flow Controller Settings for ccfg register. Only valid for slave - * channels. Fill with 'true' if peripheral should be flow controller. Direction - * will be selected at Runtime. - * @waiting: a TX descriptor on this channel which is waiting for a physical - * channel to become available - */ -struct pl08x_dma_chan { - struct dma_chan chan; - struct pl08x_phy_chan *phychan; - int phychan_hold; - struct tasklet_struct tasklet; - char *name; - const struct pl08x_channel_data *cd; - dma_addr_t src_addr; - dma_addr_t dst_addr; - u32 src_cctl; - u32 dst_cctl; - enum dma_transfer_direction runtime_direction; - struct list_head pend_list; - struct pl08x_txd *at; - spinlock_t lock; - struct pl08x_driver_data *host; - enum pl08x_dma_chan_state state; - bool slave; - bool device_fc; - struct pl08x_txd *waiting; -}; - /** * struct pl08x_platform_data - the platform configuration for the PL08x * PrimeCells. -- cgit v1.2.3 From 550ec36f507177470a394c4dfffcaf986ca25818 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 28 May 2012 10:18:55 +0100 Subject: dmaengine: PL08x: constify channel names and bus_id strings Acked-by: Linus Walleij Tested-by: Linus Walleij Signed-off-by: Russell King --- drivers/dma/amba-pl08x.c | 2 +- include/linux/amba/pl08x.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 94949909e4e9..775efef0349c 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -237,7 +237,7 @@ struct pl08x_dma_chan { struct pl08x_phy_chan *phychan; int phychan_hold; struct tasklet_struct tasklet; - char *name; + const char *name; const struct pl08x_channel_data *cd; dma_addr_t src_addr; dma_addr_t dst_addr; diff --git a/include/linux/amba/pl08x.h b/include/linux/amba/pl08x.h index 48d02bf66ec9..158ce2634b01 100644 --- a/include/linux/amba/pl08x.h +++ b/include/linux/amba/pl08x.h @@ -58,7 +58,7 @@ enum { * these buses (use PL08X_AHB1 | PL08X_AHB2). */ struct pl08x_channel_data { - char *bus_id; + const char *bus_id; int min_signal; int max_signal; u32 muxval; -- cgit v1.2.3 From dc8d5f8de12146c8732d926a30e5f064d76061e0 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 16 May 2012 12:20:55 +0100 Subject: dmaengine: PL08x: get rid of unnecessary checks in dma_slave_config Get rid of the unnecessary checks in dma_slave_config utilizing the DMA direction. This allows us to move the computation of cctl to the prepare function. Acked-by: Linus Walleij Tested-by: Linus Walleij Signed-off-by: Russell King --- arch/arm/mach-spear3xx/spear300.c | 26 -------------------- arch/arm/mach-spear3xx/spear310.c | 26 -------------------- arch/arm/mach-spear3xx/spear320.c | 26 -------------------- arch/arm/mach-spear3xx/spear3xx.c | 3 ++- arch/arm/mach-spear6xx/spear6xx.c | 51 ++------------------------------------- drivers/dma/amba-pl08x.c | 41 ++++++++++--------------------- include/linux/amba/pl08x.h | 5 ++-- 7 files changed, 20 insertions(+), 158 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-spear3xx/spear300.c b/arch/arm/mach-spear3xx/spear300.c index 0f882ecb7d81..6ec300549960 100644 --- a/arch/arm/mach-spear3xx/spear300.c +++ b/arch/arm/mach-spear3xx/spear300.c @@ -120,182 +120,156 @@ struct pl08x_channel_data spear300_dma_info[] = { .min_signal = 2, .max_signal = 2, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart0_tx", .min_signal = 3, .max_signal = 3, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp0_rx", .min_signal = 8, .max_signal = 8, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp0_tx", .min_signal = 9, .max_signal = 9, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "i2c_rx", .min_signal = 10, .max_signal = 10, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "i2c_tx", .min_signal = 11, .max_signal = 11, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "irda", .min_signal = 12, .max_signal = 12, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "adc", .min_signal = 13, .max_signal = 13, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "to_jpeg", .min_signal = 14, .max_signal = 14, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "from_jpeg", .min_signal = 15, .max_signal = 15, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras0_rx", .min_signal = 0, .max_signal = 0, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras0_tx", .min_signal = 1, .max_signal = 1, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras1_rx", .min_signal = 2, .max_signal = 2, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras1_tx", .min_signal = 3, .max_signal = 3, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras2_rx", .min_signal = 4, .max_signal = 4, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras2_tx", .min_signal = 5, .max_signal = 5, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras3_rx", .min_signal = 6, .max_signal = 6, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras3_tx", .min_signal = 7, .max_signal = 7, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras4_rx", .min_signal = 8, .max_signal = 8, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras4_tx", .min_signal = 9, .max_signal = 9, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras5_rx", .min_signal = 10, .max_signal = 10, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras5_tx", .min_signal = 11, .max_signal = 11, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras6_rx", .min_signal = 12, .max_signal = 12, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras6_tx", .min_signal = 13, .max_signal = 13, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras7_rx", .min_signal = 14, .max_signal = 14, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras7_tx", .min_signal = 15, .max_signal = 15, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, }; diff --git a/arch/arm/mach-spear3xx/spear310.c b/arch/arm/mach-spear3xx/spear310.c index bbcf4571d361..1d0e435b9045 100644 --- a/arch/arm/mach-spear3xx/spear310.c +++ b/arch/arm/mach-spear3xx/spear310.c @@ -205,182 +205,156 @@ struct pl08x_channel_data spear310_dma_info[] = { .min_signal = 2, .max_signal = 2, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart0_tx", .min_signal = 3, .max_signal = 3, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp0_rx", .min_signal = 8, .max_signal = 8, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp0_tx", .min_signal = 9, .max_signal = 9, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "i2c_rx", .min_signal = 10, .max_signal = 10, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "i2c_tx", .min_signal = 11, .max_signal = 11, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "irda", .min_signal = 12, .max_signal = 12, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "adc", .min_signal = 13, .max_signal = 13, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "to_jpeg", .min_signal = 14, .max_signal = 14, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "from_jpeg", .min_signal = 15, .max_signal = 15, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart1_rx", .min_signal = 0, .max_signal = 0, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart1_tx", .min_signal = 1, .max_signal = 1, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart2_rx", .min_signal = 2, .max_signal = 2, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart2_tx", .min_signal = 3, .max_signal = 3, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart3_rx", .min_signal = 4, .max_signal = 4, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart3_tx", .min_signal = 5, .max_signal = 5, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart4_rx", .min_signal = 6, .max_signal = 6, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart4_tx", .min_signal = 7, .max_signal = 7, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart5_rx", .min_signal = 8, .max_signal = 8, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart5_tx", .min_signal = 9, .max_signal = 9, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras5_rx", .min_signal = 10, .max_signal = 10, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras5_tx", .min_signal = 11, .max_signal = 11, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras6_rx", .min_signal = 12, .max_signal = 12, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras6_tx", .min_signal = 13, .max_signal = 13, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras7_rx", .min_signal = 14, .max_signal = 14, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras7_tx", .min_signal = 15, .max_signal = 15, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, }; diff --git a/arch/arm/mach-spear3xx/spear320.c b/arch/arm/mach-spear3xx/spear320.c index 88d483bcd66a..fd823c624575 100644 --- a/arch/arm/mach-spear3xx/spear320.c +++ b/arch/arm/mach-spear3xx/spear320.c @@ -213,182 +213,156 @@ struct pl08x_channel_data spear320_dma_info[] = { .min_signal = 2, .max_signal = 2, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart0_tx", .min_signal = 3, .max_signal = 3, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp0_rx", .min_signal = 8, .max_signal = 8, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp0_tx", .min_signal = 9, .max_signal = 9, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "i2c0_rx", .min_signal = 10, .max_signal = 10, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "i2c0_tx", .min_signal = 11, .max_signal = 11, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "irda", .min_signal = 12, .max_signal = 12, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "adc", .min_signal = 13, .max_signal = 13, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "to_jpeg", .min_signal = 14, .max_signal = 14, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "from_jpeg", .min_signal = 15, .max_signal = 15, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp1_rx", .min_signal = 0, .max_signal = 0, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ssp1_tx", .min_signal = 1, .max_signal = 1, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ssp2_rx", .min_signal = 2, .max_signal = 2, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ssp2_tx", .min_signal = 3, .max_signal = 3, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "uart1_rx", .min_signal = 4, .max_signal = 4, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "uart1_tx", .min_signal = 5, .max_signal = 5, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "uart2_rx", .min_signal = 6, .max_signal = 6, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "uart2_tx", .min_signal = 7, .max_signal = 7, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "i2c1_rx", .min_signal = 8, .max_signal = 8, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "i2c1_tx", .min_signal = 9, .max_signal = 9, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "i2c2_rx", .min_signal = 10, .max_signal = 10, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "i2c2_tx", .min_signal = 11, .max_signal = 11, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "i2s_rx", .min_signal = 12, .max_signal = 12, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "i2s_tx", .min_signal = 13, .max_signal = 13, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "rs485_rx", .min_signal = 14, .max_signal = 14, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "rs485_tx", .min_signal = 15, .max_signal = 15, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, }; diff --git a/arch/arm/mach-spear3xx/spear3xx.c b/arch/arm/mach-spear3xx/spear3xx.c index 0f41bd1c47c3..d6cd8403fe66 100644 --- a/arch/arm/mach-spear3xx/spear3xx.c +++ b/arch/arm/mach-spear3xx/spear3xx.c @@ -46,7 +46,8 @@ struct pl022_ssp_controller pl022_plat_data = { struct pl08x_platform_data pl080_plat_data = { .memcpy_channel = { .bus_id = "memcpy", - .cctl = (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT | \ + .cctl_memcpy = + (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT | \ PL080_BSIZE_16 << PL080_CONTROL_DB_SIZE_SHIFT | \ PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT | \ PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT | \ diff --git a/arch/arm/mach-spear6xx/spear6xx.c b/arch/arm/mach-spear6xx/spear6xx.c index 2e2e3596583e..b59ae5369e7b 100644 --- a/arch/arm/mach-spear6xx/spear6xx.c +++ b/arch/arm/mach-spear6xx/spear6xx.c @@ -36,336 +36,288 @@ static struct pl08x_channel_data spear600_dma_info[] = { .min_signal = 0, .max_signal = 0, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp1_tx", .min_signal = 1, .max_signal = 1, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart0_rx", .min_signal = 2, .max_signal = 2, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart0_tx", .min_signal = 3, .max_signal = 3, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart1_rx", .min_signal = 4, .max_signal = 4, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart1_tx", .min_signal = 5, .max_signal = 5, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp2_rx", .min_signal = 6, .max_signal = 6, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ssp2_tx", .min_signal = 7, .max_signal = 7, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ssp0_rx", .min_signal = 8, .max_signal = 8, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp0_tx", .min_signal = 9, .max_signal = 9, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "i2c_rx", .min_signal = 10, .max_signal = 10, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "i2c_tx", .min_signal = 11, .max_signal = 11, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "irda", .min_signal = 12, .max_signal = 12, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "adc", .min_signal = 13, .max_signal = 13, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "to_jpeg", .min_signal = 14, .max_signal = 14, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "from_jpeg", .min_signal = 15, .max_signal = 15, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras0_rx", .min_signal = 0, .max_signal = 0, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras0_tx", .min_signal = 1, .max_signal = 1, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras1_rx", .min_signal = 2, .max_signal = 2, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras1_tx", .min_signal = 3, .max_signal = 3, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras2_rx", .min_signal = 4, .max_signal = 4, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras2_tx", .min_signal = 5, .max_signal = 5, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras3_rx", .min_signal = 6, .max_signal = 6, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras3_tx", .min_signal = 7, .max_signal = 7, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras4_rx", .min_signal = 8, .max_signal = 8, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras4_tx", .min_signal = 9, .max_signal = 9, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras5_rx", .min_signal = 10, .max_signal = 10, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras5_tx", .min_signal = 11, .max_signal = 11, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras6_rx", .min_signal = 12, .max_signal = 12, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras6_tx", .min_signal = 13, .max_signal = 13, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras7_rx", .min_signal = 14, .max_signal = 14, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras7_tx", .min_signal = 15, .max_signal = 15, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ext0_rx", .min_signal = 0, .max_signal = 0, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext0_tx", .min_signal = 1, .max_signal = 1, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext1_rx", .min_signal = 2, .max_signal = 2, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext1_tx", .min_signal = 3, .max_signal = 3, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext2_rx", .min_signal = 4, .max_signal = 4, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext2_tx", .min_signal = 5, .max_signal = 5, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext3_rx", .min_signal = 6, .max_signal = 6, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext3_tx", .min_signal = 7, .max_signal = 7, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext4_rx", .min_signal = 8, .max_signal = 8, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext4_tx", .min_signal = 9, .max_signal = 9, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext5_rx", .min_signal = 10, .max_signal = 10, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext5_tx", .min_signal = 11, .max_signal = 11, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext6_rx", .min_signal = 12, .max_signal = 12, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext6_tx", .min_signal = 13, .max_signal = 13, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext7_rx", .min_signal = 14, .max_signal = 14, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext7_tx", .min_signal = 15, .max_signal = 15, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, }; @@ -373,7 +325,8 @@ static struct pl08x_channel_data spear600_dma_info[] = { struct pl08x_platform_data pl080_plat_data = { .memcpy_channel = { .bus_id = "memcpy", - .cctl = (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT | \ + .cctl_memcpy = + (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT | \ PL080_BSIZE_16 << PL080_CONTROL_DB_SIZE_SHIFT | \ PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT | \ PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT | \ diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 50b9a839e9c3..f7397789e4e8 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -235,8 +235,6 @@ struct pl08x_dma_chan { const char *name; const struct pl08x_channel_data *cd; struct dma_slave_config cfg; - u32 src_cctl; - u32 dst_cctl; struct list_head pend_list; struct pl08x_txd *at; spinlock_t lock; @@ -1235,30 +1233,15 @@ static int dma_set_runtime_config(struct dma_chan *chan, struct dma_slave_config *config) { struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); - struct pl08x_driver_data *pl08x = plchan->host; - u32 src_cctl, dst_cctl; if (!plchan->slave) return -EINVAL; - dst_cctl = pl08x_get_cctl(plchan, config->dst_addr_width, - config->dst_maxburst); - if (dst_cctl == ~0 && config->direction == DMA_MEM_TO_DEV) { - dev_err(&pl08x->adev->dev, - "bad runtime_config: alien address width (M2D)\n"); + /* Reject definitely invalid configurations */ + if (config->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES || + config->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) return -EINVAL; - } - src_cctl = pl08x_get_cctl(plchan, config->src_addr_width, - config->src_maxburst); - if (src_cctl == ~0 && config->direction == DMA_DEV_TO_MEM) { - dev_err(&pl08x->adev->dev, - "bad runtime_config: alien address width (D2M)\n"); - return -EINVAL; - } - - plchan->dst_cctl = dst_cctl; - plchan->src_cctl = src_cctl; plchan->cfg = *config; return 0; @@ -1407,7 +1390,7 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy( /* Set platform data for m2m */ txd->ccfg |= PL080_FLOW_MEM2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT; - txd->cctl = pl08x->pd->memcpy_channel.cctl & + txd->cctl = pl08x->pd->memcpy_channel.cctl_memcpy & ~(PL080_CONTROL_DST_AHB2 | PL080_CONTROL_SRC_AHB2); /* Both to be incremented or the code will break */ @@ -1434,10 +1417,11 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( struct pl08x_txd *txd; struct pl08x_sg *dsg; struct scatterlist *sg; + enum dma_slave_buswidth addr_width; dma_addr_t slave_addr; int ret, tmp; u8 src_buses, dst_buses; - u32 cctl; + u32 maxburst, cctl; dev_dbg(&pl08x->adev->dev, "%s prepare transaction of %d bytes from %s\n", __func__, sg_dma_len(sgl), plchan->name); @@ -1456,13 +1440,17 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( txd->direction = direction; if (direction == DMA_MEM_TO_DEV) { - cctl = plchan->dst_cctl | PL080_CONTROL_SRC_INCR; + cctl = PL080_CONTROL_SRC_INCR; slave_addr = plchan->cfg.dst_addr; + addr_width = plchan->cfg.dst_addr_width; + maxburst = plchan->cfg.dst_maxburst; src_buses = pl08x->mem_buses; dst_buses = plchan->cd->periph_buses; } else if (direction == DMA_DEV_TO_MEM) { - cctl = plchan->src_cctl | PL080_CONTROL_DST_INCR; + cctl = PL080_CONTROL_DST_INCR; slave_addr = plchan->cfg.src_addr; + addr_width = plchan->cfg.src_addr_width; + maxburst = plchan->cfg.src_maxburst; src_buses = plchan->cd->periph_buses; dst_buses = pl08x->mem_buses; } else { @@ -1472,6 +1460,7 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( return NULL; } + cctl |= pl08x_get_cctl(plchan, addr_width, maxburst); if (cctl == ~0) { pl08x_free_txd(pl08x, txd); dev_err(&pl08x->adev->dev, @@ -1774,14 +1763,10 @@ static irqreturn_t pl08x_irq(int irq, void *dev) static void pl08x_dma_slave_init(struct pl08x_dma_chan *chan) { - u32 cctl = pl08x_cctl(chan->cd->cctl); - chan->slave = true; chan->name = chan->cd->bus_id; chan->cfg.src_addr = chan->cd->addr; chan->cfg.dst_addr = chan->cd->addr; - chan->src_cctl = cctl; - chan->dst_cctl = cctl; } /* diff --git a/include/linux/amba/pl08x.h b/include/linux/amba/pl08x.h index 158ce2634b01..2a5f64a11b77 100644 --- a/include/linux/amba/pl08x.h +++ b/include/linux/amba/pl08x.h @@ -47,7 +47,8 @@ enum { * devices with static assignments * @muxval: a number usually used to poke into some mux regiser to * mux in the signal to this channel - * @cctl_opt: default options for the channel control register + * @cctl_memcpy: options for the channel control register for memcpy + * *** not used for slave channels *** * @addr: source/target address in physical memory for this DMA channel, * can be the address of a FIFO register for burst requests for example. * This can be left undefined if the PrimeCell API is used for configuring @@ -62,7 +63,7 @@ struct pl08x_channel_data { int min_signal; int max_signal; u32 muxval; - u32 cctl; + u32 cctl_memcpy; dma_addr_t addr; bool single; u8 periph_buses; -- cgit v1.2.3 From a618e89f1e6fb3cdfc8ef0ad54a0d57830bf8881 Mon Sep 17 00:00:00 2001 From: Glauber Costa Date: Thu, 14 Jun 2012 16:17:21 +0400 Subject: slab: rename gfpflags to allocflags A consistent name with slub saves us an acessor function. In both caches, this field represents the same thing. We would like to use it from the mem_cgroup code. Signed-off-by: Glauber Costa Acked-by: Christoph Lameter CC: Pekka Enberg Signed-off-by: Pekka Enberg --- include/linux/slab_def.h | 2 +- mm/slab.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/slab_def.h b/include/linux/slab_def.h index 1d93f27d81de..0c634fa376c9 100644 --- a/include/linux/slab_def.h +++ b/include/linux/slab_def.h @@ -39,7 +39,7 @@ struct kmem_cache { unsigned int gfporder; /* force GFP flags, e.g. GFP_DMA */ - gfp_t gfpflags; + gfp_t allocflags; size_t colour; /* cache colouring range */ unsigned int colour_off; /* colour offset */ diff --git a/mm/slab.c b/mm/slab.c index dd607a8e6706..bb7965253159 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -1771,7 +1771,7 @@ static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid) flags |= __GFP_COMP; #endif - flags |= cachep->gfpflags; + flags |= cachep->allocflags; if (cachep->flags & SLAB_RECLAIM_ACCOUNT) flags |= __GFP_RECLAIMABLE; @@ -2482,9 +2482,9 @@ kmem_cache_create (const char *name, size_t size, size_t align, cachep->colour = left_over / cachep->colour_off; cachep->slab_size = slab_size; cachep->flags = flags; - cachep->gfpflags = 0; + cachep->allocflags = 0; if (CONFIG_ZONE_DMA_FLAG && (flags & SLAB_CACHE_DMA)) - cachep->gfpflags |= GFP_DMA; + cachep->allocflags |= GFP_DMA; cachep->size = size; cachep->reciprocal_buffer_size = reciprocal_value(size); @@ -2831,9 +2831,9 @@ static void kmem_flagcheck(struct kmem_cache *cachep, gfp_t flags) { if (CONFIG_ZONE_DMA_FLAG) { if (flags & GFP_DMA) - BUG_ON(!(cachep->gfpflags & GFP_DMA)); + BUG_ON(!(cachep->allocflags & GFP_DMA)); else - BUG_ON(cachep->gfpflags & GFP_DMA); + BUG_ON(cachep->allocflags & GFP_DMA); } } -- cgit v1.2.3 From 5b063b87deba33ed1676db9d16c52ede662132d8 Mon Sep 17 00:00:00 2001 From: Alexandre Pereira da Silva Date: Thu, 14 Jun 2012 10:10:32 -0300 Subject: spi/pl022: cleanup pl022 header documentation Remove the unused fields from struct ssp_config_chip that was removed before Add bus_id documentation to pl022_ssp_controller Signed-off-by: Alexandre Pereira da Silva Signed-off-by: Roland Stigge --- include/linux/amba/pl022.h | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/amba/pl022.h b/include/linux/amba/pl022.h index 76dd1b199a1b..fe1d7b283cb6 100644 --- a/include/linux/amba/pl022.h +++ b/include/linux/amba/pl022.h @@ -231,6 +231,7 @@ enum ssp_chip_select { struct dma_chan; /** * struct pl022_ssp_master - device.platform_data for SPI controller devices. + * @bus_id: identifier for this bus * @num_chipselect: chipselects are used to distinguish individual * SPI slaves, and are numbered from zero to num_chipselects - 1. * each slave has a chipselect signal, but it's common that not @@ -259,19 +260,13 @@ struct pl022_ssp_controller { * struct ssp_config_chip - spi_board_info.controller_data for SPI * slave devices, copied to spi_device.controller_data. * - * @lbm: used for test purpose to internally connect RX and TX * @iface: Interface type(Motorola, TI, Microwire, Universal) * @hierarchy: sets whether interface is master or slave * @slave_tx_disable: SSPTXD is disconnected (in slave mode only) * @clk_freq: Tune freq parameters of SSP(when in master mode) - * @endian_rx: Endianess of Data in Rx FIFO - * @endian_tx: Endianess of Data in Tx FIFO - * @data_size: Width of data element(4 to 32 bits) * @com_mode: communication mode: polling, Interrupt or DMA * @rx_lev_trig: Rx FIFO watermark level (for IT & DMA mode) * @tx_lev_trig: Tx FIFO watermark level (for IT & DMA mode) - * @clk_phase: Motorola SPI interface Clock phase - * @clk_pol: Motorola SPI interface Clock polarity * @ctrl_len: Microwire interface: Control length * @wait_state: Microwire interface: Wait state * @duplex: Microwire interface: Full/Half duplex @@ -279,8 +274,6 @@ struct pl022_ssp_controller { * before sampling the incoming line * @cs_control: function pointer to board-specific function to * assert/deassert I/O port to control HW generation of devices chip-select. - * @dma_xfer_type: Type of DMA xfer (Mem-to-periph or Periph-to-Periph) - * @dma_config: DMA configuration for SSP controller and peripheral */ struct pl022_config_chip { enum ssp_interface iface; -- cgit v1.2.3 From 68cb2b559278858ef9f3a7722f95df88797c7840 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 2 Jul 2012 15:20:37 +0200 Subject: ALSA: Convert to new pm_ops for PCI drivers Straightforward conversion to the new pm_ops from the legacy suspend/resume ops. Since we change vx222, vx_core and vxpocket have to be converted, too. Signed-off-by: Takashi Iwai --- include/sound/cs46xx.h | 3 +-- include/sound/trident.h | 3 +-- include/sound/vx_core.h | 2 +- include/sound/ymfpci.h | 3 +-- sound/drivers/vx/vx_core.c | 2 +- sound/pci/ali5451/ali5451.c | 24 +++++++++++++++--------- sound/pci/als300.c | 24 +++++++++++++++--------- sound/pci/als4000.c | 25 +++++++++++++++---------- sound/pci/atiixp.c | 24 +++++++++++++++--------- sound/pci/atiixp_modem.c | 25 +++++++++++++++---------- sound/pci/azt3328.c | 25 +++++++++++++++---------- sound/pci/ca0106/ca0106_main.c | 24 +++++++++++++++--------- sound/pci/cmipci.c | 24 +++++++++++++++--------- sound/pci/cs4281.c | 24 +++++++++++++++--------- sound/pci/cs46xx/cs46xx.c | 5 +++-- sound/pci/cs46xx/cs46xx_lib.c | 14 +++++++++----- sound/pci/cs5535audio/cs5535audio.c | 5 +++-- sound/pci/cs5535audio/cs5535audio.h | 5 +---- sound/pci/cs5535audio/cs5535audio_pm.c | 13 ++++++++----- sound/pci/ctxfi/ctatc.c | 4 ++-- sound/pci/ctxfi/ctatc.h | 2 +- sound/pci/ctxfi/cthardware.h | 2 +- sound/pci/ctxfi/cthw20k1.c | 4 ++-- sound/pci/ctxfi/cthw20k2.c | 4 ++-- sound/pci/ctxfi/xfi.c | 22 +++++++++++++--------- sound/pci/echoaudio/echoaudio.c | 22 +++++++++++++--------- sound/pci/emu10k1/emu10k1.c | 26 ++++++++++++++++---------- sound/pci/ens1370.c | 25 +++++++++++++++---------- sound/pci/es1938.c | 24 +++++++++++++++--------- sound/pci/es1968.c | 24 +++++++++++++++--------- sound/pci/fm801.c | 26 ++++++++++++++++---------- sound/pci/hda/hda_codec.c | 2 +- sound/pci/hda/hda_codec.h | 2 +- sound/pci/hda/hda_intel.c | 29 ++++++++++++++++++----------- sound/pci/hda/patch_analog.c | 2 +- sound/pci/hda/patch_cirrus.c | 2 +- sound/pci/hda/patch_conexant.c | 2 +- sound/pci/hda/patch_realtek.c | 2 +- sound/pci/hda/patch_sigmatel.c | 2 +- sound/pci/hda/patch_via.c | 2 +- sound/pci/ice1712/ice1724.c | 26 ++++++++++++++++---------- sound/pci/intel8x0.c | 24 +++++++++++++++--------- sound/pci/intel8x0m.c | 24 +++++++++++++++--------- sound/pci/maestro3.c | 24 +++++++++++++++--------- sound/pci/nm256/nm256.c | 24 +++++++++++++++--------- sound/pci/oxygen/oxygen.c | 5 +++-- sound/pci/oxygen/oxygen.h | 3 +-- sound/pci/oxygen/oxygen_lib.c | 17 ++++++++++------- sound/pci/oxygen/virtuoso.c | 5 +++-- sound/pci/riptide/riptide.c | 26 ++++++++++++++++---------- sound/pci/sis7019.c | 25 +++++++++++++++---------- sound/pci/trident/trident.c | 5 +++-- sound/pci/trident/trident_main.c | 14 +++++++++----- sound/pci/via82xx.c | 24 +++++++++++++++--------- sound/pci/via82xx_modem.c | 24 +++++++++++++++--------- sound/pci/vx222/vx222.c | 26 ++++++++++++++++---------- sound/pci/ymfpci/ymfpci.c | 5 +++-- sound/pci/ymfpci/ymfpci_main.c | 14 +++++++++----- sound/pcmcia/vx/vxpocket.c | 2 +- 59 files changed, 496 insertions(+), 325 deletions(-) (limited to 'include') diff --git a/include/sound/cs46xx.h b/include/sound/cs46xx.h index e3005a674a24..34a2dd1614fa 100644 --- a/include/sound/cs46xx.h +++ b/include/sound/cs46xx.h @@ -1730,8 +1730,7 @@ int snd_cs46xx_create(struct snd_card *card, struct pci_dev *pci, int external_amp, int thinkpad, struct snd_cs46xx **rcodec); -int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state); -int snd_cs46xx_resume(struct pci_dev *pci); +extern const struct dev_pm_ops snd_cs46xx_pm; int snd_cs46xx_pcm(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm); int snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm); diff --git a/include/sound/trident.h b/include/sound/trident.h index 9f191a0a1e19..06f0478103db 100644 --- a/include/sound/trident.h +++ b/include/sound/trident.h @@ -430,8 +430,7 @@ void snd_trident_free_voice(struct snd_trident * trident, struct snd_trident_voi void snd_trident_start_voice(struct snd_trident * trident, unsigned int voice); void snd_trident_stop_voice(struct snd_trident * trident, unsigned int voice); void snd_trident_write_voice_regs(struct snd_trident * trident, struct snd_trident_voice *voice); -int snd_trident_suspend(struct pci_dev *pci, pm_message_t state); -int snd_trident_resume(struct pci_dev *pci); +extern const struct dev_pm_ops snd_trident_pm; /* TLB memory allocation */ struct snd_util_memblk *snd_trident_alloc_pages(struct snd_trident *trident, diff --git a/include/sound/vx_core.h b/include/sound/vx_core.h index 5456343ebe4c..4f67c762cd74 100644 --- a/include/sound/vx_core.h +++ b/include/sound/vx_core.h @@ -341,7 +341,7 @@ int vx_change_frequency(struct vx_core *chip); /* * PM */ -int snd_vx_suspend(struct vx_core *card, pm_message_t state); +int snd_vx_suspend(struct vx_core *card); int snd_vx_resume(struct vx_core *card); /* diff --git a/include/sound/ymfpci.h b/include/sound/ymfpci.h index 41199664666b..238f118de6e1 100644 --- a/include/sound/ymfpci.h +++ b/include/sound/ymfpci.h @@ -377,8 +377,7 @@ int snd_ymfpci_create(struct snd_card *card, struct snd_ymfpci ** rcodec); void snd_ymfpci_free_gameport(struct snd_ymfpci *chip); -int snd_ymfpci_suspend(struct pci_dev *pci, pm_message_t state); -int snd_ymfpci_resume(struct pci_dev *pci); +extern const struct dev_pm_ops snd_ymfpci_pm; int snd_ymfpci_pcm(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm); int snd_ymfpci_pcm2(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm); diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c index b8e515999bc2..de5055a3b0d0 100644 --- a/sound/drivers/vx/vx_core.c +++ b/sound/drivers/vx/vx_core.c @@ -725,7 +725,7 @@ EXPORT_SYMBOL(snd_vx_dsp_load); /* * suspend */ -int snd_vx_suspend(struct vx_core *chip, pm_message_t state) +int snd_vx_suspend(struct vx_core *chip) { unsigned int i; diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index 9dfc27bf6cc6..ee895f3c8605 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -1884,9 +1884,10 @@ static int __devinit snd_ali_mixer(struct snd_ali * codec) } #ifdef CONFIG_PM -static int ali_suspend(struct pci_dev *pci, pm_message_t state) +static int ali_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_ali *chip = card->private_data; struct snd_ali_image *im; int i, j; @@ -1929,13 +1930,14 @@ static int ali_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int ali_resume(struct pci_dev *pci) +static int ali_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_ali *chip = card->private_data; struct snd_ali_image *im; int i, j; @@ -1982,6 +1984,11 @@ static int ali_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +static SIMPLE_DEV_PM_OPS(ali_pm, ali_suspend, ali_resume); +#define ALI_PM_OPS &ali_pm +#else +#define ALI_PM_OPS NULL #endif /* CONFIG_PM */ static int snd_ali_free(struct snd_ali * codec) @@ -2299,10 +2306,9 @@ static struct pci_driver ali5451_driver = { .id_table = snd_ali_ids, .probe = snd_ali_probe, .remove = __devexit_p(snd_ali_remove), -#ifdef CONFIG_PM - .suspend = ali_suspend, - .resume = ali_resume, -#endif + .driver = { + .pm = ALI_PM_OPS, + }, }; module_pci_driver(ali5451_driver); diff --git a/sound/pci/als300.c b/sound/pci/als300.c index 59d65388faf5..68c4469c6d19 100644 --- a/sound/pci/als300.c +++ b/sound/pci/als300.c @@ -766,9 +766,10 @@ static int __devinit snd_als300_create(struct snd_card *card, } #ifdef CONFIG_PM -static int snd_als300_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_als300_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_als300 *chip = card->private_data; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); @@ -777,13 +778,14 @@ static int snd_als300_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int snd_als300_resume(struct pci_dev *pci) +static int snd_als300_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_als300 *chip = card->private_data; pci_set_power_state(pci, PCI_D0); @@ -802,6 +804,11 @@ static int snd_als300_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +static SIMPLE_DEV_PM_OPS(snd_als300_pm, snd_als300_suspend, snd_als300_resume); +#define SND_ALS300_PM_OPS &snd_als300_pm +#else +#define SND_ALS300_PM_OPS NULL #endif static int __devinit snd_als300_probe(struct pci_dev *pci, @@ -857,10 +864,9 @@ static struct pci_driver als300_driver = { .id_table = snd_als300_ids, .probe = snd_als300_probe, .remove = __devexit_p(snd_als300_remove), -#ifdef CONFIG_PM - .suspend = snd_als300_suspend, - .resume = snd_als300_resume, -#endif + .driver = { + .pm = SND_ALS300_PM_OPS, + }, }; module_pci_driver(als300_driver); diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index 7d7f2598c748..0eeca49c5754 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -988,9 +988,10 @@ static void __devexit snd_card_als4000_remove(struct pci_dev *pci) } #ifdef CONFIG_PM -static int snd_als4000_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_als4000_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_card_als4000 *acard = card->private_data; struct snd_sb *chip = acard->chip; @@ -1001,13 +1002,14 @@ static int snd_als4000_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int snd_als4000_resume(struct pci_dev *pci) +static int snd_als4000_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_card_als4000 *acard = card->private_data; struct snd_sb *chip = acard->chip; @@ -1033,18 +1035,21 @@ static int snd_als4000_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } -#endif /* CONFIG_PM */ +static SIMPLE_DEV_PM_OPS(snd_als4000_pm, snd_als4000_suspend, snd_als4000_resume); +#define SND_ALS4000_PM_OPS &snd_als4000_pm +#else +#define SND_ALS4000_PM_OPS NULL +#endif /* CONFIG_PM */ static struct pci_driver als4000_driver = { .name = KBUILD_MODNAME, .id_table = snd_als4000_ids, .probe = snd_card_als4000_probe, .remove = __devexit_p(snd_card_als4000_remove), -#ifdef CONFIG_PM - .suspend = snd_als4000_suspend, - .resume = snd_als4000_resume, -#endif + .driver = { + .pm = SND_ALS4000_PM_OPS, + }, }; module_pci_driver(als4000_driver); diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 156a94f8a123..31020d2a868b 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -1462,9 +1462,10 @@ static int __devinit snd_atiixp_mixer_new(struct atiixp *chip, int clock, /* * power management */ -static int snd_atiixp_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_atiixp_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct atiixp *chip = card->private_data; int i; @@ -1484,13 +1485,14 @@ static int snd_atiixp_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int snd_atiixp_resume(struct pci_dev *pci) +static int snd_atiixp_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct atiixp *chip = card->private_data; int i; @@ -1526,6 +1528,11 @@ static int snd_atiixp_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +static SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume); +#define SND_ATIIXP_PM_OPS &snd_atiixp_pm +#else +#define SND_ATIIXP_PM_OPS NULL #endif /* CONFIG_PM */ @@ -1705,10 +1712,9 @@ static struct pci_driver atiixp_driver = { .id_table = snd_atiixp_ids, .probe = snd_atiixp_probe, .remove = __devexit_p(snd_atiixp_remove), -#ifdef CONFIG_PM - .suspend = snd_atiixp_suspend, - .resume = snd_atiixp_resume, -#endif + .driver = { + .pm = SND_ATIIXP_PM_OPS, + }, }; module_pci_driver(atiixp_driver); diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index 30a4fd96ce73..79e204ec623f 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -1117,9 +1117,10 @@ static int __devinit snd_atiixp_mixer_new(struct atiixp_modem *chip, int clock) /* * power management */ -static int snd_atiixp_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_atiixp_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct atiixp_modem *chip = card->private_data; int i; @@ -1133,13 +1134,14 @@ static int snd_atiixp_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int snd_atiixp_resume(struct pci_dev *pci) +static int snd_atiixp_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct atiixp_modem *chip = card->private_data; int i; @@ -1162,8 +1164,12 @@ static int snd_atiixp_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } -#endif /* CONFIG_PM */ +static SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume); +#define SND_ATIIXP_PM_OPS &snd_atiixp_pm +#else +#define SND_ATIIXP_PM_OPS NULL +#endif /* CONFIG_PM */ #ifdef CONFIG_PROC_FS /* @@ -1336,10 +1342,9 @@ static struct pci_driver atiixp_modem_driver = { .id_table = snd_atiixp_ids, .probe = snd_atiixp_probe, .remove = __devexit_p(snd_atiixp_remove), -#ifdef CONFIG_PM - .suspend = snd_atiixp_suspend, - .resume = snd_atiixp_resume, -#endif + .driver = { + .pm = SND_ATIIXP_PM_OPS, + }, }; module_pci_driver(atiixp_modem_driver); diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index f0b4d7493af5..4dddd871548b 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -2794,9 +2794,10 @@ snd_azf3328_resume_ac97(const struct snd_azf3328 *chip) } static int -snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state) +snd_azf3328_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_azf3328 *chip = card->private_data; u16 *saved_regs_ctrl_u16; @@ -2824,14 +2825,15 @@ snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } static int -snd_azf3328_resume(struct pci_dev *pci) +snd_azf3328_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); const struct snd_azf3328 *chip = card->private_data; pci_set_power_state(pci, PCI_D0); @@ -2859,18 +2861,21 @@ snd_azf3328_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } -#endif /* CONFIG_PM */ +static SIMPLE_DEV_PM_OPS(snd_azf3328_pm, snd_azf3328_suspend, snd_azf3328_resume); +#define SND_AZF3328_PM_OPS &snd_azf3328_pm +#else +#define SND_AZF3328_PM_OPS NULL +#endif /* CONFIG_PM */ static struct pci_driver azf3328_driver = { .name = KBUILD_MODNAME, .id_table = snd_azf3328_ids, .probe = snd_azf3328_probe, .remove = __devexit_p(snd_azf3328_remove), -#ifdef CONFIG_PM - .suspend = snd_azf3328_suspend, - .resume = snd_azf3328_resume, -#endif + .driver = { + .pm = SND_AZF3328_PM_OPS, + }, }; module_pci_driver(azf3328_driver); diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index e76d68a7081f..83277b747b36 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -1872,9 +1872,10 @@ static void __devexit snd_ca0106_remove(struct pci_dev *pci) } #ifdef CONFIG_PM -static int snd_ca0106_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_ca0106_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_ca0106 *chip = card->private_data; int i; @@ -1889,13 +1890,14 @@ static int snd_ca0106_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int snd_ca0106_resume(struct pci_dev *pci) +static int snd_ca0106_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_ca0106 *chip = card->private_data; int i; @@ -1922,6 +1924,11 @@ static int snd_ca0106_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +static SIMPLE_DEV_PM_OPS(snd_ca0106_pm, snd_ca0106_suspend, snd_ca0106_resume); +#define SND_CA0106_PM_OPS &snd_ca0106_pm +#else +#define SND_CA0106_PM_OPS NULL #endif // PCI IDs @@ -1937,10 +1944,9 @@ static struct pci_driver ca0106_driver = { .id_table = snd_ca0106_ids, .probe = snd_ca0106_probe, .remove = __devexit_p(snd_ca0106_remove), -#ifdef CONFIG_PM - .suspend = snd_ca0106_suspend, - .resume = snd_ca0106_resume, -#endif + .driver = { + .pm = SND_CA0106_PM_OPS, + }, }; module_pci_driver(ca0106_driver); diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 3815bd4c6779..b7d6f2b886ef 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -3338,9 +3338,10 @@ static unsigned char saved_mixers[] = { SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, }; -static int snd_cmipci_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_cmipci_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct cmipci *cm = card->private_data; int i; @@ -3361,13 +3362,14 @@ static int snd_cmipci_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int snd_cmipci_resume(struct pci_dev *pci) +static int snd_cmipci_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct cmipci *cm = card->private_data; int i; @@ -3396,6 +3398,11 @@ static int snd_cmipci_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +static SIMPLE_DEV_PM_OPS(snd_cmipci_pm, snd_cmipci_suspend, snd_cmipci_resume); +#define SND_CMIPCI_PM_OPS &snd_cmipci_pm +#else +#define SND_CMIPCI_PM_OPS NULL #endif /* CONFIG_PM */ static struct pci_driver cmipci_driver = { @@ -3403,10 +3410,9 @@ static struct pci_driver cmipci_driver = { .id_table = snd_cmipci_ids, .probe = snd_cmipci_probe, .remove = __devexit_p(snd_cmipci_remove), -#ifdef CONFIG_PM - .suspend = snd_cmipci_suspend, - .resume = snd_cmipci_resume, -#endif + .driver = { + .pm = SND_CMIPCI_PM_OPS, + }, }; module_pci_driver(cmipci_driver); diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index 33506ee569bd..45a8317085f4 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -1997,9 +1997,10 @@ static int saved_regs[SUSPEND_REGISTERS] = { #define CLKCR1_CKRA 0x00010000L -static int cs4281_suspend(struct pci_dev *pci, pm_message_t state) +static int cs4281_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct cs4281 *chip = card->private_data; u32 ulCLK; unsigned int i; @@ -2040,13 +2041,14 @@ static int cs4281_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int cs4281_resume(struct pci_dev *pci) +static int cs4281_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct cs4281 *chip = card->private_data; unsigned int i; u32 ulCLK; @@ -2082,6 +2084,11 @@ static int cs4281_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +static SIMPLE_DEV_PM_OPS(cs4281_pm, cs4281_suspend, cs4281_resume); +#define CS4281_PM_OPS &cs4281_pm +#else +#define CS4281_PM_OPS NULL #endif /* CONFIG_PM */ static struct pci_driver cs4281_driver = { @@ -2089,10 +2096,9 @@ static struct pci_driver cs4281_driver = { .id_table = snd_cs4281_ids, .probe = snd_cs4281_probe, .remove = __devexit_p(snd_cs4281_remove), -#ifdef CONFIG_PM - .suspend = cs4281_suspend, - .resume = cs4281_resume, -#endif + .driver = { + .pm = CS4281_PM_OPS, + }, }; module_pci_driver(cs4281_driver); diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c index 6cc7404e0e8f..00e03bc9a762 100644 --- a/sound/pci/cs46xx/cs46xx.c +++ b/sound/pci/cs46xx/cs46xx.c @@ -167,8 +167,9 @@ static struct pci_driver cs46xx_driver = { .probe = snd_card_cs46xx_probe, .remove = __devexit_p(snd_card_cs46xx_remove), #ifdef CONFIG_PM - .suspend = snd_cs46xx_suspend, - .resume = snd_cs46xx_resume, + .driver = { + .pm = &snd_cs46xx_pm, + }, #endif }; diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 4fa53161b094..28b9747becc9 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -3599,9 +3599,10 @@ static unsigned int saved_regs[] = { BA1_CVOL, }; -int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_cs46xx_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_cs46xx *chip = card->private_data; int i, amp_saved; @@ -3628,13 +3629,14 @@ int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -int snd_cs46xx_resume(struct pci_dev *pci) +static int snd_cs46xx_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_cs46xx *chip = card->private_data; int amp_saved; #ifdef CONFIG_SND_CS46XX_NEW_DSP @@ -3707,6 +3709,8 @@ int snd_cs46xx_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +SIMPLE_DEV_PM_OPS(snd_cs46xx_pm, snd_cs46xx_suspend, snd_cs46xx_resume); #endif /* CONFIG_PM */ diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c index 2c9697cf0a1a..51f64ba5facf 100644 --- a/sound/pci/cs5535audio/cs5535audio.c +++ b/sound/pci/cs5535audio/cs5535audio.c @@ -400,8 +400,9 @@ static struct pci_driver cs5535audio_driver = { .probe = snd_cs5535audio_probe, .remove = __devexit_p(snd_cs5535audio_remove), #ifdef CONFIG_PM - .suspend = snd_cs5535audio_suspend, - .resume = snd_cs5535audio_resume, + .driver = { + .pm = &snd_cs5535audio_pm, + }, #endif }; diff --git a/sound/pci/cs5535audio/cs5535audio.h b/sound/pci/cs5535audio/cs5535audio.h index 51966d782a3c..bb3cc641130c 100644 --- a/sound/pci/cs5535audio/cs5535audio.h +++ b/sound/pci/cs5535audio/cs5535audio.h @@ -94,10 +94,7 @@ struct cs5535audio { struct cs5535audio_dma dmas[NUM_CS5535AUDIO_DMAS]; }; -#ifdef CONFIG_PM -int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state); -int snd_cs5535audio_resume(struct pci_dev *pci); -#endif +extern const struct dev_pm_ops snd_cs5535audio_pm; #ifdef CONFIG_OLPC void __devinit olpc_prequirks(struct snd_card *card, diff --git a/sound/pci/cs5535audio/cs5535audio_pm.c b/sound/pci/cs5535audio/cs5535audio_pm.c index 185b00088320..6c34def5986d 100644 --- a/sound/pci/cs5535audio/cs5535audio_pm.c +++ b/sound/pci/cs5535audio/cs5535audio_pm.c @@ -55,9 +55,10 @@ static void snd_cs5535audio_stop_hardware(struct cs5535audio *cs5535au) } -int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_cs5535audio_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct cs5535audio *cs5535au = card->private_data; int i; @@ -77,13 +78,14 @@ int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state) return -EIO; } pci_disable_device(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -int snd_cs5535audio_resume(struct pci_dev *pci) +static int snd_cs5535audio_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct cs5535audio *cs5535au = card->private_data; u32 tmp; int timeout; @@ -129,3 +131,4 @@ int snd_cs5535audio_resume(struct pci_dev *pci) return 0; } +SIMPLE_DEV_PM_OPS(snd_cs5535audio_pm, snd_cs5535audio_suspend, snd_cs5535audio_resume); diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index d8a4423539ce..8e40262d4117 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -1537,7 +1537,7 @@ static void atc_connect_resources(struct ct_atc *atc) } #ifdef CONFIG_PM -static int atc_suspend(struct ct_atc *atc, pm_message_t state) +static int atc_suspend(struct ct_atc *atc) { int i; struct hw *hw = atc->hw; @@ -1553,7 +1553,7 @@ static int atc_suspend(struct ct_atc *atc, pm_message_t state) atc_release_resources(atc); - hw->suspend(hw, state); + hw->suspend(hw); return 0; } diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h index 3a0def656af0..653e813ad142 100644 --- a/sound/pci/ctxfi/ctatc.h +++ b/sound/pci/ctxfi/ctatc.h @@ -144,7 +144,7 @@ struct ct_atc { struct ct_timer *timer; #ifdef CONFIG_PM - int (*suspend)(struct ct_atc *atc, pm_message_t state); + int (*suspend)(struct ct_atc *atc); int (*resume)(struct ct_atc *atc); #define NUM_PCMS (NUM_CTALSADEVS - 1) struct snd_pcm *pcms[NUM_PCMS]; diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h index 908315bec3b4..c56fe533b3f3 100644 --- a/sound/pci/ctxfi/cthardware.h +++ b/sound/pci/ctxfi/cthardware.h @@ -73,7 +73,7 @@ struct hw { int (*card_stop)(struct hw *hw); int (*pll_init)(struct hw *hw, unsigned int rsr); #ifdef CONFIG_PM - int (*suspend)(struct hw *hw, pm_message_t state); + int (*suspend)(struct hw *hw); int (*resume)(struct hw *hw, struct card_conf *info); #endif int (*is_adc_source_selected)(struct hw *hw, enum ADCSRC source); diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index a7df19791f5a..dc1969bc67d4 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -2086,7 +2086,7 @@ static int hw_card_init(struct hw *hw, struct card_conf *info) } #ifdef CONFIG_PM -static int hw_suspend(struct hw *hw, pm_message_t state) +static int hw_suspend(struct hw *hw) { struct pci_dev *pci = hw->pci; @@ -2099,7 +2099,7 @@ static int hw_suspend(struct hw *hw, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index d6c54b524bfa..9d1231dc4ae2 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -2202,7 +2202,7 @@ static int hw_card_init(struct hw *hw, struct card_conf *info) } #ifdef CONFIG_PM -static int hw_suspend(struct hw *hw, pm_message_t state) +static int hw_suspend(struct hw *hw) { struct pci_dev *pci = hw->pci; @@ -2210,7 +2210,7 @@ static int hw_suspend(struct hw *hw, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c index 75aa2c338410..e002183ef8b2 100644 --- a/sound/pci/ctxfi/xfi.c +++ b/sound/pci/ctxfi/xfi.c @@ -126,21 +126,26 @@ static void __devexit ct_card_remove(struct pci_dev *pci) } #ifdef CONFIG_PM -static int ct_card_suspend(struct pci_dev *pci, pm_message_t state) +static int ct_card_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct snd_card *card = dev_get_drvdata(dev); struct ct_atc *atc = card->private_data; - return atc->suspend(atc, state); + return atc->suspend(atc); } -static int ct_card_resume(struct pci_dev *pci) +static int ct_card_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct snd_card *card = dev_get_drvdata(dev); struct ct_atc *atc = card->private_data; return atc->resume(atc); } + +static SIMPLE_DEV_PM_OPS(ct_card_pm, ct_card_suspend, ct_card_resume); +#define CT_CARD_PM_OPS &ct_card_pm +#else +#define CT_CARD_PM_OPS NULL #endif static struct pci_driver ct_driver = { @@ -148,10 +153,9 @@ static struct pci_driver ct_driver = { .id_table = ct_pci_dev_ids, .probe = ct_card_probe, .remove = __devexit_p(ct_card_remove), -#ifdef CONFIG_PM - .suspend = ct_card_suspend, - .resume = ct_card_resume, -#endif + .driver = { + .pm = CT_CARD_PM_OPS, + }, }; module_pci_driver(ct_driver); diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 0f8eda1dafdb..0ff754f180d0 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -2205,9 +2205,10 @@ ctl_error: #if defined(CONFIG_PM) -static int snd_echo_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_echo_suspend(struct device *dev) { - struct echoaudio *chip = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct echoaudio *chip = dev_get_drvdata(dev); DE_INIT(("suspend start\n")); snd_pcm_suspend_all(chip->analog_pcm); @@ -2242,9 +2243,10 @@ static int snd_echo_suspend(struct pci_dev *pci, pm_message_t state) -static int snd_echo_resume(struct pci_dev *pci) +static int snd_echo_resume(struct device *dev) { - struct echoaudio *chip = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct echoaudio *chip = dev_get_drvdata(dev); struct comm_page *commpage, *commpage_bak; u32 pipe_alloc_mask; int err; @@ -2307,10 +2309,13 @@ static int snd_echo_resume(struct pci_dev *pci) return 0; } +static SIMPLE_DEV_PM_OPS(snd_echo_pm, snd_echo_suspend, snd_echo_resume); +#define SND_ECHO_PM_OPS &snd_echo_pm +#else +#define SND_ECHO_PM_OPS NULL #endif /* CONFIG_PM */ - static void __devexit snd_echo_remove(struct pci_dev *pci) { struct echoaudio *chip; @@ -2333,10 +2338,9 @@ static struct pci_driver echo_driver = { .id_table = snd_echo_ids, .probe = snd_echo_probe, .remove = __devexit_p(snd_echo_remove), -#ifdef CONFIG_PM - .suspend = snd_echo_suspend, - .resume = snd_echo_resume, -#endif /* CONFIG_PM */ + .driver = { + .pm = SND_ECHO_PM_OPS, + }, }; module_pci_driver(echo_driver); diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index 7fdbbe4d9965..ddac4e6d660d 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -207,9 +207,10 @@ static void __devexit snd_card_emu10k1_remove(struct pci_dev *pci) #ifdef CONFIG_PM -static int snd_emu10k1_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_emu10k1_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_emu10k1 *emu = card->private_data; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); @@ -231,13 +232,14 @@ static int snd_emu10k1_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int snd_emu10k1_resume(struct pci_dev *pci) +static int snd_emu10k1_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_emu10k1 *emu = card->private_data; pci_set_power_state(pci, PCI_D0); @@ -261,17 +263,21 @@ static int snd_emu10k1_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } -#endif + +static SIMPLE_DEV_PM_OPS(snd_emu10k1_pm, snd_emu10k1_suspend, snd_emu10k1_resume); +#define SND_EMU10K1_PM_OPS &snd_emu10k1_pm +#else +#define SND_EMU10K1_PM_OPS NULL +#endif /* CONFIG_PM */ static struct pci_driver emu10k1_driver = { .name = KBUILD_MODNAME, .id_table = snd_emu10k1_ids, .probe = snd_card_emu10k1_probe, .remove = __devexit_p(snd_card_emu10k1_remove), -#ifdef CONFIG_PM - .suspend = snd_emu10k1_suspend, - .resume = snd_emu10k1_resume, -#endif + .driver = { + .pm = SND_EMU10K1_PM_OPS, + }, }; module_pci_driver(emu10k1_driver); diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 3821c81d1c99..f7e6f73186e1 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -2033,9 +2033,10 @@ static void snd_ensoniq_chip_init(struct ensoniq *ensoniq) } #ifdef CONFIG_PM -static int snd_ensoniq_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_ensoniq_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct ensoniq *ensoniq = card->private_data; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); @@ -2058,13 +2059,14 @@ static int snd_ensoniq_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int snd_ensoniq_resume(struct pci_dev *pci) +static int snd_ensoniq_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct ensoniq *ensoniq = card->private_data; pci_set_power_state(pci, PCI_D0); @@ -2087,8 +2089,12 @@ static int snd_ensoniq_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } -#endif /* CONFIG_PM */ +static SIMPLE_DEV_PM_OPS(snd_ensoniq_pm, snd_ensoniq_suspend, snd_ensoniq_resume); +#define SND_ENSONIQ_PM_OPS &snd_ensoniq_pm +#else +#define SND_ENSONIQ_PM_OPS NULL +#endif /* CONFIG_PM */ static int __devinit snd_ensoniq_create(struct snd_card *card, struct pci_dev *pci, @@ -2493,10 +2499,9 @@ static struct pci_driver ens137x_driver = { .id_table = snd_audiopci_ids, .probe = snd_audiopci_probe, .remove = __devexit_p(snd_audiopci_remove), -#ifdef CONFIG_PM - .suspend = snd_ensoniq_suspend, - .resume = snd_ensoniq_resume, -#endif + .driver = { + .pm = SND_ENSONIQ_PM_OPS, + }, }; module_pci_driver(ens137x_driver); diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 82c8d8c5c52a..227dff70069f 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -1474,9 +1474,10 @@ static unsigned char saved_regs[SAVED_REG_SIZE+1] = { }; -static int es1938_suspend(struct pci_dev *pci, pm_message_t state) +static int es1938_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct es1938 *chip = card->private_data; unsigned char *s, *d; @@ -1494,13 +1495,14 @@ static int es1938_suspend(struct pci_dev *pci, pm_message_t state) } pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int es1938_resume(struct pci_dev *pci) +static int es1938_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct es1938 *chip = card->private_data; unsigned char *s, *d; @@ -1534,6 +1536,11 @@ static int es1938_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +static SIMPLE_DEV_PM_OPS(es1938_pm, es1938_suspend, es1938_resume); +#define ES1938_PM_OPS &es1938_pm +#else +#define ES1938_PM_OPS NULL #endif /* CONFIG_PM */ #ifdef SUPPORT_JOYSTICK @@ -1887,10 +1894,9 @@ static struct pci_driver es1938_driver = { .id_table = snd_es1938_ids, .probe = snd_es1938_probe, .remove = __devexit_p(snd_es1938_remove), -#ifdef CONFIG_PM - .suspend = es1938_suspend, - .resume = es1938_resume, -#endif + .driver = { + .pm = ES1938_PM_OPS, + }, }; module_pci_driver(es1938_driver); diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 52b5c0bf90c1..fb4c90b99c00 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -2381,9 +2381,10 @@ static void snd_es1968_start_irq(struct es1968 *chip) /* * PM support */ -static int es1968_suspend(struct pci_dev *pci, pm_message_t state) +static int es1968_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct es1968 *chip = card->private_data; if (! chip->do_pm) @@ -2398,13 +2399,14 @@ static int es1968_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int es1968_resume(struct pci_dev *pci) +static int es1968_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct es1968 *chip = card->private_data; struct esschan *es; @@ -2454,6 +2456,11 @@ static int es1968_resume(struct pci_dev *pci) chip->in_suspend = 0; return 0; } + +static SIMPLE_DEV_PM_OPS(es1968_pm, es1968_suspend, es1968_resume); +#define ES1968_PM_OPS &es1968_pm +#else +#define ES1968_PM_OPS NULL #endif /* CONFIG_PM */ #ifdef SUPPORT_JOYSTICK @@ -2903,10 +2910,9 @@ static struct pci_driver es1968_driver = { .id_table = snd_es1968_ids, .probe = snd_es1968_probe, .remove = __devexit_p(snd_es1968_remove), -#ifdef CONFIG_PM - .suspend = es1968_suspend, - .resume = es1968_resume, -#endif + .driver = { + .pm = ES1968_PM_OPS, + }, }; module_pci_driver(es1968_driver); diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index b32e8024ea86..522c8706f244 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -1369,9 +1369,10 @@ static unsigned char saved_regs[] = { FM801_CODEC_CTRL, FM801_I2S_MODE, FM801_VOLUME, FM801_GEN_CTRL, }; -static int snd_fm801_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_fm801_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct fm801 *chip = card->private_data; int i; @@ -1385,13 +1386,14 @@ static int snd_fm801_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int snd_fm801_resume(struct pci_dev *pci) +static int snd_fm801_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct fm801 *chip = card->private_data; int i; @@ -1414,17 +1416,21 @@ static int snd_fm801_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } -#endif + +static SIMPLE_DEV_PM_OPS(snd_fm801_pm, snd_fm801_suspend, snd_fm801_resume); +#define SND_FM801_PM_OPS &snd_fm801_pm +#else +#define SND_FM801_PM_OPS NULL +#endif /* CONFIG_PM */ static struct pci_driver fm801_driver = { .name = KBUILD_MODNAME, .id_table = snd_fm801_ids, .probe = snd_card_fm801_probe, .remove = __devexit_p(snd_card_fm801_remove), -#ifdef CONFIG_PM - .suspend = snd_fm801_suspend, - .resume = snd_fm801_resume, -#endif + .driver = { + .pm = SND_FM801_PM_OPS, + }, }; module_pci_driver(fm801_driver); diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 51cb2a2e4fce..f4c274c5144b 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3545,7 +3545,7 @@ static inline void hda_exec_init_verbs(struct hda_codec *codec) {} static void hda_call_codec_suspend(struct hda_codec *codec) { if (codec->patch_ops.suspend) - codec->patch_ops.suspend(codec, PMSG_SUSPEND); + codec->patch_ops.suspend(codec); hda_cleanup_all_streams(codec); hda_set_power_state(codec, codec->afg ? codec->afg : codec->mfg, diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 2fdaadbb4326..5aab408dcb6a 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -703,7 +703,7 @@ struct hda_codec_ops { void (*set_power_state)(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state); #ifdef CONFIG_PM - int (*suspend)(struct hda_codec *codec, pm_message_t state); + int (*suspend)(struct hda_codec *codec); int (*resume)(struct hda_codec *codec); #endif #ifdef CONFIG_SND_HDA_POWER_SAVE diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 7757536b9d5f..a69ec7448714 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2403,9 +2403,10 @@ static void azx_power_notify(struct hda_bus *bus) * power management */ -static int azx_suspend(struct pci_dev *pci, pm_message_t state) +static int azx_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct azx *chip = card->private_data; struct azx_pcm *p; @@ -2424,13 +2425,14 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_msi(chip->pci); pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int azx_resume(struct pci_dev *pci) +static int azx_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct azx *chip = card->private_data; pci_set_power_state(pci, PCI_D0); @@ -2455,6 +2457,12 @@ static int azx_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } +static SIMPLE_DEV_PM_OPS(azx_pm, azx_suspend, azx_resume); +#define AZX_PM_OPS &azx_pm +#else +#define azx_suspend(dev) +#define azx_resume(dev) +#define AZX_PM_OPS NULL #endif /* CONFIG_PM */ @@ -2521,13 +2529,13 @@ static void azx_vs_set_state(struct pci_dev *pci, disabled ? "Disabling" : "Enabling", pci_name(chip->pci)); if (disabled) { - azx_suspend(pci, PMSG_FREEZE); + azx_suspend(&pci->dev); chip->disabled = true; snd_hda_lock_devices(chip->bus); } else { snd_hda_unlock_devices(chip->bus); chip->disabled = false; - azx_resume(pci); + azx_resume(&pci->dev); } } } @@ -3398,10 +3406,9 @@ static struct pci_driver azx_driver = { .id_table = azx_ids, .probe = azx_probe, .remove = __devexit_p(azx_remove), -#ifdef CONFIG_PM - .suspend = azx_suspend, - .resume = azx_resume, -#endif + .driver = { + .pm = AZX_PM_OPS, + }, }; module_pci_driver(azx_driver); diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index d8b2d6dee986..0208fa121e5a 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -642,7 +642,7 @@ static void ad198x_free(struct hda_codec *codec) } #ifdef CONFIG_PM -static int ad198x_suspend(struct hda_codec *codec, pm_message_t state) +static int ad198x_suspend(struct hda_codec *codec) { ad198x_shutup(codec); return 0; diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 9647ed4d7929..0c4c1a61b378 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -1892,7 +1892,7 @@ static int cs421x_parse_auto_config(struct hda_codec *codec) Manage PDREF, when transitioning to D3hot (DAC,ADC) -> D3, PDREF=1, AFG->D3 */ -static int cs421x_suspend(struct hda_codec *codec, pm_message_t state) +static int cs421x_suspend(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; unsigned int coef; diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 2bf99fc1cbf2..14361184ae1e 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -554,7 +554,7 @@ static int conexant_build_controls(struct hda_codec *codec) } #ifdef CONFIG_SND_HDA_POWER_SAVE -static int conexant_suspend(struct hda_codec *codec, pm_message_t state) +static int conexant_suspend(struct hda_codec *codec) { snd_hda_shutup_pins(codec); return 0; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 5ccf10a4d593..ab2c729b88ea 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2300,7 +2300,7 @@ static void alc_power_eapd(struct hda_codec *codec) alc_auto_setup_eapd(codec, false); } -static int alc_suspend(struct hda_codec *codec, pm_message_t state) +static int alc_suspend(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; alc_shutup(codec); diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 07675282015a..a1596a3b171c 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4997,7 +4997,7 @@ static int stac92xx_resume(struct hda_codec *codec) return 0; } -static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state) +static int stac92xx_suspend(struct hda_codec *codec) { stac92xx_shutup(codec); return 0; diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 82b368068e08..90645560ed39 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1748,7 +1748,7 @@ static void via_unsol_event(struct hda_codec *codec, } #ifdef CONFIG_PM -static int via_suspend(struct hda_codec *codec, pm_message_t state) +static int via_suspend(struct hda_codec *codec) { struct via_spec *spec = codec->spec; vt1708_stop_hp_work(spec); diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index a01a00d1cf4d..bed9f34f4efe 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -2793,9 +2793,10 @@ static void __devexit snd_vt1724_remove(struct pci_dev *pci) } #ifdef CONFIG_PM -static int snd_vt1724_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_vt1724_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_ice1712 *ice = card->private_data; if (!ice->pm_suspend_enabled) @@ -2820,13 +2821,14 @@ static int snd_vt1724_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int snd_vt1724_resume(struct pci_dev *pci) +static int snd_vt1724_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_ice1712 *ice = card->private_data; if (!ice->pm_suspend_enabled) @@ -2871,17 +2873,21 @@ static int snd_vt1724_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } -#endif + +static SIMPLE_DEV_PM_OPS(snd_vt1724_pm, snd_vt1724_suspend, snd_vt1724_resume); +#define SND_VT1724_PM_OPS &snd_vt1724_pm +#else +#define SND_VT1724_PM_OPS NULL +#endif /* CONFIG_PM */ static struct pci_driver vt1724_driver = { .name = KBUILD_MODNAME, .id_table = snd_vt1724_ids, .probe = snd_vt1724_probe, .remove = __devexit_p(snd_vt1724_remove), -#ifdef CONFIG_PM - .suspend = snd_vt1724_suspend, - .resume = snd_vt1724_resume, -#endif + .driver = { + .pm = SND_VT1724_PM_OPS, + }, }; module_pci_driver(vt1724_driver); diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index f4e2dd4da8cf..cd553f592e2d 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -2624,9 +2624,10 @@ static int snd_intel8x0_free(struct intel8x0 *chip) /* * power management */ -static int intel8x0_suspend(struct pci_dev *pci, pm_message_t state) +static int intel8x0_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct intel8x0 *chip = card->private_data; int i; @@ -2658,13 +2659,14 @@ static int intel8x0_suspend(struct pci_dev *pci, pm_message_t state) /* The call below may disable built-in speaker on some laptops * after S2RAM. So, don't touch it. */ - /* pci_set_power_state(pci, pci_choose_state(pci, state)); */ + /* pci_set_power_state(pci, PCI_D3hot); */ return 0; } -static int intel8x0_resume(struct pci_dev *pci) +static int intel8x0_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct intel8x0 *chip = card->private_data; int i; @@ -2734,6 +2736,11 @@ static int intel8x0_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +static SIMPLE_DEV_PM_OPS(intel8x0_pm, intel8x0_suspend, intel8x0_resume); +#define INTEL8X0_PM_OPS &intel8x0_pm +#else +#define INTEL8X0_PM_OPS NULL #endif /* CONFIG_PM */ #define INTEL8X0_TESTBUF_SIZE 32768 /* enough large for one shot */ @@ -3343,10 +3350,9 @@ static struct pci_driver intel8x0_driver = { .id_table = snd_intel8x0_ids, .probe = snd_intel8x0_probe, .remove = __devexit_p(snd_intel8x0_remove), -#ifdef CONFIG_PM - .suspend = intel8x0_suspend, - .resume = intel8x0_resume, -#endif + .driver = { + .pm = INTEL8X0_PM_OPS, + }, }; module_pci_driver(intel8x0_driver); diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index fc27a6a69e77..da44bb3f8e7a 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -1012,9 +1012,10 @@ static int snd_intel8x0m_free(struct intel8x0m *chip) /* * power management */ -static int intel8x0m_suspend(struct pci_dev *pci, pm_message_t state) +static int intel8x0m_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct intel8x0m *chip = card->private_data; int i; @@ -1028,13 +1029,14 @@ static int intel8x0m_suspend(struct pci_dev *pci, pm_message_t state) } pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int intel8x0m_resume(struct pci_dev *pci) +static int intel8x0m_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct intel8x0m *chip = card->private_data; pci_set_power_state(pci, PCI_D0); @@ -1060,6 +1062,11 @@ static int intel8x0m_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +static SIMPLE_DEV_PM_OPS(intel8x0m_pm, intel8x0m_suspend, intel8x0m_resume); +#define INTEL8X0M_PM_OPS &intel8x0m_pm +#else +#define INTEL8X0M_PM_OPS NULL #endif /* CONFIG_PM */ #ifdef CONFIG_PROC_FS @@ -1329,10 +1336,9 @@ static struct pci_driver intel8x0m_driver = { .id_table = snd_intel8x0m_ids, .probe = snd_intel8x0m_probe, .remove = __devexit_p(snd_intel8x0m_remove), -#ifdef CONFIG_PM - .suspend = intel8x0m_suspend, - .resume = intel8x0m_resume, -#endif + .driver = { + .pm = INTEL8X0M_PM_OPS, + }, }; module_pci_driver(intel8x0m_driver); diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index deef21399586..36008a943aa3 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -2459,9 +2459,10 @@ static int snd_m3_free(struct snd_m3 *chip) * APM support */ #ifdef CONFIG_PM -static int m3_suspend(struct pci_dev *pci, pm_message_t state) +static int m3_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_m3 *chip = card->private_data; int i, dsp_index; @@ -2489,13 +2490,14 @@ static int m3_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int m3_resume(struct pci_dev *pci) +static int m3_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_m3 *chip = card->private_data; int i, dsp_index; @@ -2546,6 +2548,11 @@ static int m3_resume(struct pci_dev *pci) chip->in_suspend = 0; return 0; } + +static SIMPLE_DEV_PM_OPS(m3_pm, m3_suspend, m3_resume); +#define M3_PM_OPS &m3_pm +#else +#define M3_PM_OPS NULL #endif /* CONFIG_PM */ #ifdef CONFIG_SND_MAESTRO3_INPUT @@ -2842,10 +2849,9 @@ static struct pci_driver m3_driver = { .id_table = snd_m3_ids, .probe = snd_m3_probe, .remove = __devexit_p(snd_m3_remove), -#ifdef CONFIG_PM - .suspend = m3_suspend, - .resume = m3_resume, -#endif + .driver = { + .pm = M3_PM_OPS, + }, }; module_pci_driver(m3_driver); diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 8159b05ee94d..465cff25b146 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -1382,9 +1382,10 @@ snd_nm256_peek_for_sig(struct nm256 *chip) * APM event handler, so the card is properly reinitialized after a power * event. */ -static int nm256_suspend(struct pci_dev *pci, pm_message_t state) +static int nm256_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct nm256 *chip = card->private_data; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); @@ -1393,13 +1394,14 @@ static int nm256_suspend(struct pci_dev *pci, pm_message_t state) chip->coeffs_current = 0; pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int nm256_resume(struct pci_dev *pci) +static int nm256_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct nm256 *chip = card->private_data; int i; @@ -1434,6 +1436,11 @@ static int nm256_resume(struct pci_dev *pci) chip->in_resume = 0; return 0; } + +static SIMPLE_DEV_PM_OPS(nm256_pm, nm256_suspend, nm256_resume); +#define NM256_PM_OPS &nm256_pm +#else +#define NM256_PM_OPS NULL #endif /* CONFIG_PM */ static int snd_nm256_free(struct nm256 *chip) @@ -1747,10 +1754,9 @@ static struct pci_driver nm256_driver = { .id_table = snd_nm256_ids, .probe = snd_nm256_probe, .remove = __devexit_p(snd_nm256_remove), -#ifdef CONFIG_PM - .suspend = nm256_suspend, - .resume = nm256_resume, -#endif + .driver = { + .pm = NM256_PM_OPS, + }, }; module_pci_driver(nm256_driver); diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c index 610275bfbaeb..37520a2b4dcf 100644 --- a/sound/pci/oxygen/oxygen.c +++ b/sound/pci/oxygen/oxygen.c @@ -873,8 +873,9 @@ static struct pci_driver oxygen_driver = { .probe = generic_oxygen_probe, .remove = __devexit_p(oxygen_pci_remove), #ifdef CONFIG_PM - .suspend = oxygen_pci_suspend, - .resume = oxygen_pci_resume, + .driver = { + .pm = &oxygen_pci_pm, + }, #endif }; diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h index f53897a708b4..7112a89fb8bd 100644 --- a/sound/pci/oxygen/oxygen.h +++ b/sound/pci/oxygen/oxygen.h @@ -162,8 +162,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, ); void oxygen_pci_remove(struct pci_dev *pci); #ifdef CONFIG_PM -int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state); -int oxygen_pci_resume(struct pci_dev *pci); +extern const struct dev_pm_ops oxygen_pci_pm; #endif void oxygen_pci_shutdown(struct pci_dev *pci); diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index 92e2d67f16a1..ab8738e21ad1 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -727,9 +727,10 @@ void oxygen_pci_remove(struct pci_dev *pci) EXPORT_SYMBOL(oxygen_pci_remove); #ifdef CONFIG_PM -int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state) +static int oxygen_pci_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct oxygen *chip = card->private_data; unsigned int i, saved_interrupt_mask; @@ -756,10 +757,9 @@ int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -EXPORT_SYMBOL(oxygen_pci_suspend); static const u32 registers_to_restore[OXYGEN_IO_SIZE / 32] = { 0xffffffff, 0x00ff077f, 0x00011d08, 0x007f00ff, @@ -787,9 +787,10 @@ static void oxygen_restore_ac97(struct oxygen *chip, unsigned int codec) chip->saved_ac97_registers[codec][i]); } -int oxygen_pci_resume(struct pci_dev *pci) +static int oxygen_pci_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct oxygen *chip = card->private_data; unsigned int i; @@ -820,7 +821,9 @@ int oxygen_pci_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } -EXPORT_SYMBOL(oxygen_pci_resume); + +SIMPLE_DEV_PM_OPS(oxygen_pci_pm, oxygen_pci_suspend, oxygen_pci_resume); +EXPORT_SYMBOL(oxygen_pci_pm); #endif /* CONFIG_PM */ void oxygen_pci_shutdown(struct pci_dev *pci) diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c index 19962c6d38c3..d3b606b69f3b 100644 --- a/sound/pci/oxygen/virtuoso.c +++ b/sound/pci/oxygen/virtuoso.c @@ -94,8 +94,9 @@ static struct pci_driver xonar_driver = { .probe = xonar_probe, .remove = __devexit_p(oxygen_pci_remove), #ifdef CONFIG_PM - .suspend = oxygen_pci_suspend, - .resume = oxygen_pci_resume, + .driver = { + .pm = &oxygen_pci_pm, + }, #endif .shutdown = oxygen_pci_shutdown, }; diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index cbeb3f77350c..760ee467cd9a 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -1151,9 +1151,10 @@ static void riptide_handleirq(unsigned long dev_id) } #ifdef CONFIG_PM -static int riptide_suspend(struct pci_dev *pci, pm_message_t state) +static int riptide_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_riptide *chip = card->private_data; chip->in_suspend = 1; @@ -1162,13 +1163,14 @@ static int riptide_suspend(struct pci_dev *pci, pm_message_t state) snd_ac97_suspend(chip->ac97); pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int riptide_resume(struct pci_dev *pci) +static int riptide_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_riptide *chip = card->private_data; pci_set_power_state(pci, PCI_D0); @@ -1186,7 +1188,12 @@ static int riptide_resume(struct pci_dev *pci) chip->in_suspend = 0; return 0; } -#endif + +static SIMPLE_DEV_PM_OPS(riptide_pm, riptide_suspend, riptide_resume); +#define RIPTIDE_PM_OPS &riptide_pm +#else +#define RIPTIDE_PM_OPS NULL +#endif /* CONFIG_PM */ static int try_to_load_firmware(struct cmdif *cif, struct snd_riptide *chip) { @@ -2180,10 +2187,9 @@ static struct pci_driver driver = { .id_table = snd_riptide_ids, .probe = snd_card_riptide_probe, .remove = __devexit_p(snd_card_riptide_remove), -#ifdef CONFIG_PM - .suspend = riptide_suspend, - .resume = riptide_resume, -#endif + .driver = { + .pm = RIPTIDE_PM_OPS, + }, }; #ifdef SUPPORT_JOYSTICK diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c index 1552642765d6..512434efcc31 100644 --- a/sound/pci/sis7019.c +++ b/sound/pci/sis7019.c @@ -1209,9 +1209,10 @@ static int sis_chip_init(struct sis7019 *sis) } #ifdef CONFIG_PM -static int sis_suspend(struct pci_dev *pci, pm_message_t state) +static int sis_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct sis7019 *sis = card->private_data; void __iomem *ioaddr = sis->ioaddr; int i; @@ -1241,13 +1242,14 @@ static int sis_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int sis_resume(struct pci_dev *pci) +static int sis_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct sis7019 *sis = card->private_data; void __iomem *ioaddr = sis->ioaddr; int i; @@ -1298,6 +1300,11 @@ error: snd_card_disconnect(card); return -EIO; } + +static SIMPLE_DEV_PM_OPS(sis_pm, sis_suspend, sis_resume); +#define SIS_PM_OPS &sis_pm +#else +#define SIS_PM_OPS NULL #endif /* CONFIG_PM */ static int sis_alloc_suspend(struct sis7019 *sis) @@ -1481,11 +1488,9 @@ static struct pci_driver sis7019_driver = { .id_table = snd_sis7019_ids, .probe = snd_sis7019_probe, .remove = __devexit_p(snd_sis7019_remove), - -#ifdef CONFIG_PM - .suspend = sis_suspend, - .resume = sis_resume, -#endif + .driver = { + .pm = SIS_PM_OPS, + }, }; module_pci_driver(sis7019_driver); diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c index 611983ec7321..f61346a555bb 100644 --- a/sound/pci/trident/trident.c +++ b/sound/pci/trident/trident.c @@ -178,8 +178,9 @@ static struct pci_driver trident_driver = { .probe = snd_trident_probe, .remove = __devexit_p(snd_trident_remove), #ifdef CONFIG_PM - .suspend = snd_trident_suspend, - .resume = snd_trident_resume, + .driver = { + .pm = &snd_trident_pm, + }, #endif }; diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index 61d3c0e8d4ce..b4430c093bad 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -3920,9 +3920,10 @@ static void snd_trident_clear_voices(struct snd_trident * trident, unsigned shor } #ifdef CONFIG_PM -int snd_trident_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_trident_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_trident *trident = card->private_data; trident->in_suspend = 1; @@ -3936,13 +3937,14 @@ int snd_trident_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -int snd_trident_resume(struct pci_dev *pci) +static int snd_trident_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_trident *trident = card->private_data; pci_set_power_state(pci, PCI_D0); @@ -3979,4 +3981,6 @@ int snd_trident_resume(struct pci_dev *pci) trident->in_suspend = 0; return 0; } + +SIMPLE_DEV_PM_OPS(snd_trident_pm, snd_trident_suspend, snd_trident_resume); #endif /* CONFIG_PM */ diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index b5afab48943e..0eb7245dd362 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -2242,9 +2242,10 @@ static int snd_via82xx_chip_init(struct via82xx *chip) /* * power management */ -static int snd_via82xx_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_via82xx_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct via82xx *chip = card->private_data; int i; @@ -2265,13 +2266,14 @@ static int snd_via82xx_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int snd_via82xx_resume(struct pci_dev *pci) +static int snd_via82xx_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct via82xx *chip = card->private_data; int i; @@ -2306,6 +2308,11 @@ static int snd_via82xx_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +static SIMPLE_DEV_PM_OPS(snd_via82xx_pm, snd_via82xx_suspend, snd_via82xx_resume); +#define SND_VIA82XX_PM_OPS &snd_via82xx_pm +#else +#define SND_VIA82XX_PM_OPS NULL #endif /* CONFIG_PM */ static int snd_via82xx_free(struct via82xx *chip) @@ -2624,10 +2631,9 @@ static struct pci_driver via82xx_driver = { .id_table = snd_via82xx_ids, .probe = snd_via82xx_probe, .remove = __devexit_p(snd_via82xx_remove), -#ifdef CONFIG_PM - .suspend = snd_via82xx_suspend, - .resume = snd_via82xx_resume, -#endif + .driver = { + .pm = SND_VIA82XX_PM_OPS, + }, }; module_pci_driver(via82xx_driver); diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index 59fd47ed0a31..e886bc16999d 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -1023,9 +1023,10 @@ static int snd_via82xx_chip_init(struct via82xx_modem *chip) /* * power management */ -static int snd_via82xx_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_via82xx_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct via82xx_modem *chip = card->private_data; int i; @@ -1039,13 +1040,14 @@ static int snd_via82xx_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int snd_via82xx_resume(struct pci_dev *pci) +static int snd_via82xx_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct via82xx_modem *chip = card->private_data; int i; @@ -1069,6 +1071,11 @@ static int snd_via82xx_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +static SIMPLE_DEV_PM_OPS(snd_via82xx_pm, snd_via82xx_suspend, snd_via82xx_resume); +#define SND_VIA82XX_PM_OPS &snd_via82xx_pm +#else +#define SND_VIA82XX_PM_OPS NULL #endif /* CONFIG_PM */ static int snd_via82xx_free(struct via82xx_modem *chip) @@ -1228,10 +1235,9 @@ static struct pci_driver via82xx_modem_driver = { .id_table = snd_via82xx_modem_ids, .probe = snd_via82xx_probe, .remove = __devexit_p(snd_via82xx_remove), -#ifdef CONFIG_PM - .suspend = snd_via82xx_suspend, - .resume = snd_via82xx_resume, -#endif + .driver = { + .pm = SND_VIA82XX_PM_OPS, + }, }; module_pci_driver(via82xx_modem_driver); diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c index 1ea1f656a5dc..b89e7a86e9d8 100644 --- a/sound/pci/vx222/vx222.c +++ b/sound/pci/vx222/vx222.c @@ -258,22 +258,24 @@ static void __devexit snd_vx222_remove(struct pci_dev *pci) } #ifdef CONFIG_PM -static int snd_vx222_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_vx222_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_vx222 *vx = card->private_data; int err; - err = snd_vx_suspend(&vx->core, state); + err = snd_vx_suspend(&vx->core); pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return err; } -static int snd_vx222_resume(struct pci_dev *pci) +static int snd_vx222_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_vx222 *vx = card->private_data; pci_set_power_state(pci, PCI_D0); @@ -287,6 +289,11 @@ static int snd_vx222_resume(struct pci_dev *pci) pci_set_master(pci); return snd_vx_resume(&vx->core); } + +static SIMPLE_DEV_PM_OPS(snd_vx222_pm, snd_vx222_suspend, snd_vx222_resume); +#define SND_VX222_PM_OPS &snd_vx222_pm +#else +#define SND_VX222_PM_OPS NULL #endif static struct pci_driver vx222_driver = { @@ -294,10 +301,9 @@ static struct pci_driver vx222_driver = { .id_table = snd_vx222_ids, .probe = snd_vx222_probe, .remove = __devexit_p(snd_vx222_remove), -#ifdef CONFIG_PM - .suspend = snd_vx222_suspend, - .resume = snd_vx222_resume, -#endif + .driver = { + .pm = SND_VX222_PM_OPS, + }, }; module_pci_driver(vx222_driver); diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c index 9a1d01d653a7..7e20ddb9123a 100644 --- a/sound/pci/ymfpci/ymfpci.c +++ b/sound/pci/ymfpci/ymfpci.c @@ -356,8 +356,9 @@ static struct pci_driver ymfpci_driver = { .probe = snd_card_ymfpci_probe, .remove = __devexit_p(snd_card_ymfpci_remove), #ifdef CONFIG_PM - .suspend = snd_ymfpci_suspend, - .resume = snd_ymfpci_resume, + .driver = { + .pm = &snd_ymfpci_pm, + }, #endif }; diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index a8159b81e9c4..c706901d6ff6 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -2302,9 +2302,10 @@ static int saved_regs_index[] = { }; #define YDSXGR_NUM_SAVED_REGS ARRAY_SIZE(saved_regs_index) -int snd_ymfpci_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_ymfpci_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_ymfpci *chip = card->private_data; unsigned int i; @@ -2326,13 +2327,14 @@ int snd_ymfpci_suspend(struct pci_dev *pci, pm_message_t state) snd_ymfpci_disable_dsp(chip); pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -int snd_ymfpci_resume(struct pci_dev *pci) +static int snd_ymfpci_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_ymfpci *chip = card->private_data; unsigned int i; @@ -2370,6 +2372,8 @@ int snd_ymfpci_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +SIMPLE_DEV_PM_OPS(snd_ymfpci_pm, snd_ymfpci_suspend, snd_ymfpci_resume); #endif /* CONFIG_PM */ int __devinit snd_ymfpci_create(struct snd_card *card, diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c index 512f0b472375..8f9350475c7b 100644 --- a/sound/pcmcia/vx/vxpocket.c +++ b/sound/pcmcia/vx/vxpocket.c @@ -260,7 +260,7 @@ static int vxp_suspend(struct pcmcia_device *link) snd_printdd(KERN_DEBUG "SUSPEND\n"); if (chip) { snd_printdd(KERN_DEBUG "snd_vx_suspend calling\n"); - snd_vx_suspend(chip, PMSG_SUSPEND); + snd_vx_suspend(chip); } return 0; -- cgit v1.2.3 From 81fcb170852d58d7ebd8101a8ef970c82056426e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 2 Jul 2012 16:37:05 +0200 Subject: ALSA: Move some headers to local directories from include/sound This is a bit clean up of public sound header directory. Some header files in include/sound aren't really necessary to be located there but can be moved to their local directories gracefully. Signed-off-by: Takashi Iwai --- include/sound/cs46xx.h | 1744 ------------------------------ include/sound/cs46xx_dsp_scb_types.h | 1213 --------------------- include/sound/cs46xx_dsp_spos.h | 234 ---- include/sound/cs46xx_dsp_task_types.h | 252 ----- include/sound/trident.h | 444 -------- include/sound/ymfpci.h | 389 ------- sound/pci/cs46xx/cs46xx.c | 2 +- sound/pci/cs46xx/cs46xx.h | 1744 ++++++++++++++++++++++++++++++ sound/pci/cs46xx/cs46xx_dsp_scb_types.h | 1213 +++++++++++++++++++++ sound/pci/cs46xx/cs46xx_dsp_spos.h | 234 ++++ sound/pci/cs46xx/cs46xx_dsp_task_types.h | 252 +++++ sound/pci/cs46xx/cs46xx_lib.c | 2 +- sound/pci/cs46xx/dsp_spos.c | 2 +- sound/pci/cs46xx/dsp_spos_scb_lib.c | 2 +- sound/pci/trident/trident.c | 2 +- sound/pci/trident/trident.h | 444 ++++++++ sound/pci/trident/trident_main.c | 2 +- sound/pci/trident/trident_memory.c | 2 +- sound/pci/ymfpci/ymfpci.c | 2 +- sound/pci/ymfpci/ymfpci.h | 389 +++++++ sound/pci/ymfpci/ymfpci_main.c | 2 +- 21 files changed, 4285 insertions(+), 4285 deletions(-) delete mode 100644 include/sound/cs46xx.h delete mode 100644 include/sound/cs46xx_dsp_scb_types.h delete mode 100644 include/sound/cs46xx_dsp_spos.h delete mode 100644 include/sound/cs46xx_dsp_task_types.h delete mode 100644 include/sound/trident.h delete mode 100644 include/sound/ymfpci.h create mode 100644 sound/pci/cs46xx/cs46xx.h create mode 100644 sound/pci/cs46xx/cs46xx_dsp_scb_types.h create mode 100644 sound/pci/cs46xx/cs46xx_dsp_spos.h create mode 100644 sound/pci/cs46xx/cs46xx_dsp_task_types.h create mode 100644 sound/pci/trident/trident.h create mode 100644 sound/pci/ymfpci/ymfpci.h (limited to 'include') diff --git a/include/sound/cs46xx.h b/include/sound/cs46xx.h deleted file mode 100644 index 34a2dd1614fa..000000000000 --- a/include/sound/cs46xx.h +++ /dev/null @@ -1,1744 +0,0 @@ -#ifndef __SOUND_CS46XX_H -#define __SOUND_CS46XX_H - -/* - * Copyright (c) by Jaroslav Kysela , - * Cirrus Logic, Inc. - * Definitions for Cirrus Logic CS46xx chips - * - * - * 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 "pcm.h" -#include "pcm-indirect.h" -#include "rawmidi.h" -#include "ac97_codec.h" -#include "cs46xx_dsp_spos.h" - -/* - * Direct registers - */ - -/* - * The following define the offsets of the registers accessed via base address - * register zero on the CS46xx part. - */ -#define BA0_HISR 0x00000000 -#define BA0_HSR0 0x00000004 -#define BA0_HICR 0x00000008 -#define BA0_DMSR 0x00000100 -#define BA0_HSAR 0x00000110 -#define BA0_HDAR 0x00000114 -#define BA0_HDMR 0x00000118 -#define BA0_HDCR 0x0000011C -#define BA0_PFMC 0x00000200 -#define BA0_PFCV1 0x00000204 -#define BA0_PFCV2 0x00000208 -#define BA0_PCICFG00 0x00000300 -#define BA0_PCICFG04 0x00000304 -#define BA0_PCICFG08 0x00000308 -#define BA0_PCICFG0C 0x0000030C -#define BA0_PCICFG10 0x00000310 -#define BA0_PCICFG14 0x00000314 -#define BA0_PCICFG18 0x00000318 -#define BA0_PCICFG1C 0x0000031C -#define BA0_PCICFG20 0x00000320 -#define BA0_PCICFG24 0x00000324 -#define BA0_PCICFG28 0x00000328 -#define BA0_PCICFG2C 0x0000032C -#define BA0_PCICFG30 0x00000330 -#define BA0_PCICFG34 0x00000334 -#define BA0_PCICFG38 0x00000338 -#define BA0_PCICFG3C 0x0000033C -#define BA0_CLKCR1 0x00000400 -#define BA0_CLKCR2 0x00000404 -#define BA0_PLLM 0x00000408 -#define BA0_PLLCC 0x0000040C -#define BA0_FRR 0x00000410 -#define BA0_CFL1 0x00000414 -#define BA0_CFL2 0x00000418 -#define BA0_SERMC1 0x00000420 -#define BA0_SERMC2 0x00000424 -#define BA0_SERC1 0x00000428 -#define BA0_SERC2 0x0000042C -#define BA0_SERC3 0x00000430 -#define BA0_SERC4 0x00000434 -#define BA0_SERC5 0x00000438 -#define BA0_SERBSP 0x0000043C -#define BA0_SERBST 0x00000440 -#define BA0_SERBCM 0x00000444 -#define BA0_SERBAD 0x00000448 -#define BA0_SERBCF 0x0000044C -#define BA0_SERBWP 0x00000450 -#define BA0_SERBRP 0x00000454 -#ifndef NO_CS4612 -#define BA0_ASER_FADDR 0x00000458 -#endif -#define BA0_ACCTL 0x00000460 -#define BA0_ACSTS 0x00000464 -#define BA0_ACOSV 0x00000468 -#define BA0_ACCAD 0x0000046C -#define BA0_ACCDA 0x00000470 -#define BA0_ACISV 0x00000474 -#define BA0_ACSAD 0x00000478 -#define BA0_ACSDA 0x0000047C -#define BA0_JSPT 0x00000480 -#define BA0_JSCTL 0x00000484 -#define BA0_JSC1 0x00000488 -#define BA0_JSC2 0x0000048C -#define BA0_MIDCR 0x00000490 -#define BA0_MIDSR 0x00000494 -#define BA0_MIDWP 0x00000498 -#define BA0_MIDRP 0x0000049C -#define BA0_JSIO 0x000004A0 -#ifndef NO_CS4612 -#define BA0_ASER_MASTER 0x000004A4 -#endif -#define BA0_CFGI 0x000004B0 -#define BA0_SSVID 0x000004B4 -#define BA0_GPIOR 0x000004B8 -#ifndef NO_CS4612 -#define BA0_EGPIODR 0x000004BC -#define BA0_EGPIOPTR 0x000004C0 -#define BA0_EGPIOTR 0x000004C4 -#define BA0_EGPIOWR 0x000004C8 -#define BA0_EGPIOSR 0x000004CC -#define BA0_SERC6 0x000004D0 -#define BA0_SERC7 0x000004D4 -#define BA0_SERACC 0x000004D8 -#define BA0_ACCTL2 0x000004E0 -#define BA0_ACSTS2 0x000004E4 -#define BA0_ACOSV2 0x000004E8 -#define BA0_ACCAD2 0x000004EC -#define BA0_ACCDA2 0x000004F0 -#define BA0_ACISV2 0x000004F4 -#define BA0_ACSAD2 0x000004F8 -#define BA0_ACSDA2 0x000004FC -#define BA0_IOTAC0 0x00000500 -#define BA0_IOTAC1 0x00000504 -#define BA0_IOTAC2 0x00000508 -#define BA0_IOTAC3 0x0000050C -#define BA0_IOTAC4 0x00000510 -#define BA0_IOTAC5 0x00000514 -#define BA0_IOTAC6 0x00000518 -#define BA0_IOTAC7 0x0000051C -#define BA0_IOTAC8 0x00000520 -#define BA0_IOTAC9 0x00000524 -#define BA0_IOTAC10 0x00000528 -#define BA0_IOTAC11 0x0000052C -#define BA0_IOTFR0 0x00000540 -#define BA0_IOTFR1 0x00000544 -#define BA0_IOTFR2 0x00000548 -#define BA0_IOTFR3 0x0000054C -#define BA0_IOTFR4 0x00000550 -#define BA0_IOTFR5 0x00000554 -#define BA0_IOTFR6 0x00000558 -#define BA0_IOTFR7 0x0000055C -#define BA0_IOTFIFO 0x00000580 -#define BA0_IOTRRD 0x00000584 -#define BA0_IOTFP 0x00000588 -#define BA0_IOTCR 0x0000058C -#define BA0_DPCID 0x00000590 -#define BA0_DPCIA 0x00000594 -#define BA0_DPCIC 0x00000598 -#define BA0_PCPCIR 0x00000600 -#define BA0_PCPCIG 0x00000604 -#define BA0_PCPCIEN 0x00000608 -#define BA0_EPCIPMC 0x00000610 -#endif - -/* - * The following define the offsets of the registers and memories accessed via - * base address register one on the CS46xx part. - */ -#define BA1_SP_DMEM0 0x00000000 -#define BA1_SP_DMEM1 0x00010000 -#define BA1_SP_PMEM 0x00020000 -#define BA1_SP_REG 0x00030000 -#define BA1_SPCR 0x00030000 -#define BA1_DREG 0x00030004 -#define BA1_DSRWP 0x00030008 -#define BA1_TWPR 0x0003000C -#define BA1_SPWR 0x00030010 -#define BA1_SPIR 0x00030014 -#define BA1_FGR1 0x00030020 -#define BA1_SPCS 0x00030028 -#define BA1_SDSR 0x0003002C -#define BA1_FRMT 0x00030030 -#define BA1_FRCC 0x00030034 -#define BA1_FRSC 0x00030038 -#define BA1_OMNI_MEM 0x000E0000 - - -/* - * The following defines are for the flags in the host interrupt status - * register. - */ -#define HISR_VC_MASK 0x0000FFFF -#define HISR_VC0 0x00000001 -#define HISR_VC1 0x00000002 -#define HISR_VC2 0x00000004 -#define HISR_VC3 0x00000008 -#define HISR_VC4 0x00000010 -#define HISR_VC5 0x00000020 -#define HISR_VC6 0x00000040 -#define HISR_VC7 0x00000080 -#define HISR_VC8 0x00000100 -#define HISR_VC9 0x00000200 -#define HISR_VC10 0x00000400 -#define HISR_VC11 0x00000800 -#define HISR_VC12 0x00001000 -#define HISR_VC13 0x00002000 -#define HISR_VC14 0x00004000 -#define HISR_VC15 0x00008000 -#define HISR_INT0 0x00010000 -#define HISR_INT1 0x00020000 -#define HISR_DMAI 0x00040000 -#define HISR_FROVR 0x00080000 -#define HISR_MIDI 0x00100000 -#ifdef NO_CS4612 -#define HISR_RESERVED 0x0FE00000 -#else -#define HISR_SBINT 0x00200000 -#define HISR_RESERVED 0x0FC00000 -#endif -#define HISR_H0P 0x40000000 -#define HISR_INTENA 0x80000000 - -/* - * The following defines are for the flags in the host signal register 0. - */ -#define HSR0_VC_MASK 0xFFFFFFFF -#define HSR0_VC16 0x00000001 -#define HSR0_VC17 0x00000002 -#define HSR0_VC18 0x00000004 -#define HSR0_VC19 0x00000008 -#define HSR0_VC20 0x00000010 -#define HSR0_VC21 0x00000020 -#define HSR0_VC22 0x00000040 -#define HSR0_VC23 0x00000080 -#define HSR0_VC24 0x00000100 -#define HSR0_VC25 0x00000200 -#define HSR0_VC26 0x00000400 -#define HSR0_VC27 0x00000800 -#define HSR0_VC28 0x00001000 -#define HSR0_VC29 0x00002000 -#define HSR0_VC30 0x00004000 -#define HSR0_VC31 0x00008000 -#define HSR0_VC32 0x00010000 -#define HSR0_VC33 0x00020000 -#define HSR0_VC34 0x00040000 -#define HSR0_VC35 0x00080000 -#define HSR0_VC36 0x00100000 -#define HSR0_VC37 0x00200000 -#define HSR0_VC38 0x00400000 -#define HSR0_VC39 0x00800000 -#define HSR0_VC40 0x01000000 -#define HSR0_VC41 0x02000000 -#define HSR0_VC42 0x04000000 -#define HSR0_VC43 0x08000000 -#define HSR0_VC44 0x10000000 -#define HSR0_VC45 0x20000000 -#define HSR0_VC46 0x40000000 -#define HSR0_VC47 0x80000000 - -/* - * The following defines are for the flags in the host interrupt control - * register. - */ -#define HICR_IEV 0x00000001 -#define HICR_CHGM 0x00000002 - -/* - * The following defines are for the flags in the DMA status register. - */ -#define DMSR_HP 0x00000001 -#define DMSR_HR 0x00000002 -#define DMSR_SP 0x00000004 -#define DMSR_SR 0x00000008 - -/* - * The following defines are for the flags in the host DMA source address - * register. - */ -#define HSAR_HOST_ADDR_MASK 0xFFFFFFFF -#define HSAR_DSP_ADDR_MASK 0x0000FFFF -#define HSAR_MEMID_MASK 0x000F0000 -#define HSAR_MEMID_SP_DMEM0 0x00000000 -#define HSAR_MEMID_SP_DMEM1 0x00010000 -#define HSAR_MEMID_SP_PMEM 0x00020000 -#define HSAR_MEMID_SP_DEBUG 0x00030000 -#define HSAR_MEMID_OMNI_MEM 0x000E0000 -#define HSAR_END 0x40000000 -#define HSAR_ERR 0x80000000 - -/* - * The following defines are for the flags in the host DMA destination address - * register. - */ -#define HDAR_HOST_ADDR_MASK 0xFFFFFFFF -#define HDAR_DSP_ADDR_MASK 0x0000FFFF -#define HDAR_MEMID_MASK 0x000F0000 -#define HDAR_MEMID_SP_DMEM0 0x00000000 -#define HDAR_MEMID_SP_DMEM1 0x00010000 -#define HDAR_MEMID_SP_PMEM 0x00020000 -#define HDAR_MEMID_SP_DEBUG 0x00030000 -#define HDAR_MEMID_OMNI_MEM 0x000E0000 -#define HDAR_END 0x40000000 -#define HDAR_ERR 0x80000000 - -/* - * The following defines are for the flags in the host DMA control register. - */ -#define HDMR_AC_MASK 0x0000F000 -#define HDMR_AC_8_16 0x00001000 -#define HDMR_AC_M_S 0x00002000 -#define HDMR_AC_B_L 0x00004000 -#define HDMR_AC_S_U 0x00008000 - -/* - * The following defines are for the flags in the host DMA control register. - */ -#define HDCR_COUNT_MASK 0x000003FF -#define HDCR_DONE 0x00004000 -#define HDCR_OPT 0x00008000 -#define HDCR_WBD 0x00400000 -#define HDCR_WBS 0x00800000 -#define HDCR_DMS_MASK 0x07000000 -#define HDCR_DMS_LINEAR 0x00000000 -#define HDCR_DMS_16_DWORDS 0x01000000 -#define HDCR_DMS_32_DWORDS 0x02000000 -#define HDCR_DMS_64_DWORDS 0x03000000 -#define HDCR_DMS_128_DWORDS 0x04000000 -#define HDCR_DMS_256_DWORDS 0x05000000 -#define HDCR_DMS_512_DWORDS 0x06000000 -#define HDCR_DMS_1024_DWORDS 0x07000000 -#define HDCR_DH 0x08000000 -#define HDCR_SMS_MASK 0x70000000 -#define HDCR_SMS_LINEAR 0x00000000 -#define HDCR_SMS_16_DWORDS 0x10000000 -#define HDCR_SMS_32_DWORDS 0x20000000 -#define HDCR_SMS_64_DWORDS 0x30000000 -#define HDCR_SMS_128_DWORDS 0x40000000 -#define HDCR_SMS_256_DWORDS 0x50000000 -#define HDCR_SMS_512_DWORDS 0x60000000 -#define HDCR_SMS_1024_DWORDS 0x70000000 -#define HDCR_SH 0x80000000 -#define HDCR_COUNT_SHIFT 0 - -/* - * The following defines are for the flags in the performance monitor control - * register. - */ -#define PFMC_C1SS_MASK 0x0000001F -#define PFMC_C1EV 0x00000020 -#define PFMC_C1RS 0x00008000 -#define PFMC_C2SS_MASK 0x001F0000 -#define PFMC_C2EV 0x00200000 -#define PFMC_C2RS 0x80000000 -#define PFMC_C1SS_SHIFT 0 -#define PFMC_C2SS_SHIFT 16 -#define PFMC_BUS_GRANT 0 -#define PFMC_GRANT_AFTER_REQ 1 -#define PFMC_TRANSACTION 2 -#define PFMC_DWORD_TRANSFER 3 -#define PFMC_SLAVE_READ 4 -#define PFMC_SLAVE_WRITE 5 -#define PFMC_PREEMPTION 6 -#define PFMC_DISCONNECT_RETRY 7 -#define PFMC_INTERRUPT 8 -#define PFMC_BUS_OWNERSHIP 9 -#define PFMC_TRANSACTION_LAG 10 -#define PFMC_PCI_CLOCK 11 -#define PFMC_SERIAL_CLOCK 12 -#define PFMC_SP_CLOCK 13 - -/* - * The following defines are for the flags in the performance counter value 1 - * register. - */ -#define PFCV1_PC1V_MASK 0xFFFFFFFF -#define PFCV1_PC1V_SHIFT 0 - -/* - * The following defines are for the flags in the performance counter value 2 - * register. - */ -#define PFCV2_PC2V_MASK 0xFFFFFFFF -#define PFCV2_PC2V_SHIFT 0 - -/* - * The following defines are for the flags in the clock control register 1. - */ -#define CLKCR1_OSCS 0x00000001 -#define CLKCR1_OSCP 0x00000002 -#define CLKCR1_PLLSS_MASK 0x0000000C -#define CLKCR1_PLLSS_SERIAL 0x00000000 -#define CLKCR1_PLLSS_CRYSTAL 0x00000004 -#define CLKCR1_PLLSS_PCI 0x00000008 -#define CLKCR1_PLLSS_RESERVED 0x0000000C -#define CLKCR1_PLLP 0x00000010 -#define CLKCR1_SWCE 0x00000020 -#define CLKCR1_PLLOS 0x00000040 - -/* - * The following defines are for the flags in the clock control register 2. - */ -#define CLKCR2_PDIVS_MASK 0x0000000F -#define CLKCR2_PDIVS_1 0x00000001 -#define CLKCR2_PDIVS_2 0x00000002 -#define CLKCR2_PDIVS_4 0x00000004 -#define CLKCR2_PDIVS_7 0x00000007 -#define CLKCR2_PDIVS_8 0x00000008 -#define CLKCR2_PDIVS_16 0x00000000 - -/* - * The following defines are for the flags in the PLL multiplier register. - */ -#define PLLM_MASK 0x000000FF -#define PLLM_SHIFT 0 - -/* - * The following defines are for the flags in the PLL capacitor coefficient - * register. - */ -#define PLLCC_CDR_MASK 0x00000007 -#ifndef NO_CS4610 -#define PLLCC_CDR_240_350_MHZ 0x00000000 -#define PLLCC_CDR_184_265_MHZ 0x00000001 -#define PLLCC_CDR_144_205_MHZ 0x00000002 -#define PLLCC_CDR_111_160_MHZ 0x00000003 -#define PLLCC_CDR_87_123_MHZ 0x00000004 -#define PLLCC_CDR_67_96_MHZ 0x00000005 -#define PLLCC_CDR_52_74_MHZ 0x00000006 -#define PLLCC_CDR_45_58_MHZ 0x00000007 -#endif -#ifndef NO_CS4612 -#define PLLCC_CDR_271_398_MHZ 0x00000000 -#define PLLCC_CDR_227_330_MHZ 0x00000001 -#define PLLCC_CDR_167_239_MHZ 0x00000002 -#define PLLCC_CDR_150_215_MHZ 0x00000003 -#define PLLCC_CDR_107_154_MHZ 0x00000004 -#define PLLCC_CDR_98_140_MHZ 0x00000005 -#define PLLCC_CDR_73_104_MHZ 0x00000006 -#define PLLCC_CDR_63_90_MHZ 0x00000007 -#endif -#define PLLCC_LPF_MASK 0x000000F8 -#ifndef NO_CS4610 -#define PLLCC_LPF_23850_60000_KHZ 0x00000000 -#define PLLCC_LPF_7960_26290_KHZ 0x00000008 -#define PLLCC_LPF_4160_10980_KHZ 0x00000018 -#define PLLCC_LPF_1740_4580_KHZ 0x00000038 -#define PLLCC_LPF_724_1910_KHZ 0x00000078 -#define PLLCC_LPF_317_798_KHZ 0x000000F8 -#endif -#ifndef NO_CS4612 -#define PLLCC_LPF_25580_64530_KHZ 0x00000000 -#define PLLCC_LPF_14360_37270_KHZ 0x00000008 -#define PLLCC_LPF_6100_16020_KHZ 0x00000018 -#define PLLCC_LPF_2540_6690_KHZ 0x00000038 -#define PLLCC_LPF_1050_2780_KHZ 0x00000078 -#define PLLCC_LPF_450_1160_KHZ 0x000000F8 -#endif - -/* - * The following defines are for the flags in the feature reporting register. - */ -#define FRR_FAB_MASK 0x00000003 -#define FRR_MASK_MASK 0x0000001C -#ifdef NO_CS4612 -#define FRR_CFOP_MASK 0x000000E0 -#else -#define FRR_CFOP_MASK 0x00000FE0 -#endif -#define FRR_CFOP_NOT_DVD 0x00000020 -#define FRR_CFOP_A3D 0x00000040 -#define FRR_CFOP_128_PIN 0x00000080 -#ifndef NO_CS4612 -#define FRR_CFOP_CS4280 0x00000800 -#endif -#define FRR_FAB_SHIFT 0 -#define FRR_MASK_SHIFT 2 -#define FRR_CFOP_SHIFT 5 - -/* - * The following defines are for the flags in the configuration load 1 - * register. - */ -#define CFL1_CLOCK_SOURCE_MASK 0x00000003 -#define CFL1_CLOCK_SOURCE_CS423X 0x00000000 -#define CFL1_CLOCK_SOURCE_AC97 0x00000001 -#define CFL1_CLOCK_SOURCE_CRYSTAL 0x00000002 -#define CFL1_CLOCK_SOURCE_DUAL_AC97 0x00000003 -#define CFL1_VALID_DATA_MASK 0x000000FF - -/* - * The following defines are for the flags in the configuration load 2 - * register. - */ -#define CFL2_VALID_DATA_MASK 0x000000FF - -/* - * The following defines are for the flags in the serial port master control - * register 1. - */ -#define SERMC1_MSPE 0x00000001 -#define SERMC1_PTC_MASK 0x0000000E -#define SERMC1_PTC_CS423X 0x00000000 -#define SERMC1_PTC_AC97 0x00000002 -#define SERMC1_PTC_DAC 0x00000004 -#define SERMC1_PLB 0x00000010 -#define SERMC1_XLB 0x00000020 - -/* - * The following defines are for the flags in the serial port master control - * register 2. - */ -#define SERMC2_LROE 0x00000001 -#define SERMC2_MCOE 0x00000002 -#define SERMC2_MCDIV 0x00000004 - -/* - * The following defines are for the flags in the serial port 1 configuration - * register. - */ -#define SERC1_SO1EN 0x00000001 -#define SERC1_SO1F_MASK 0x0000000E -#define SERC1_SO1F_CS423X 0x00000000 -#define SERC1_SO1F_AC97 0x00000002 -#define SERC1_SO1F_DAC 0x00000004 -#define SERC1_SO1F_SPDIF 0x00000006 - -/* - * The following defines are for the flags in the serial port 2 configuration - * register. - */ -#define SERC2_SI1EN 0x00000001 -#define SERC2_SI1F_MASK 0x0000000E -#define SERC2_SI1F_CS423X 0x00000000 -#define SERC2_SI1F_AC97 0x00000002 -#define SERC2_SI1F_ADC 0x00000004 -#define SERC2_SI1F_SPDIF 0x00000006 - -/* - * The following defines are for the flags in the serial port 3 configuration - * register. - */ -#define SERC3_SO2EN 0x00000001 -#define SERC3_SO2F_MASK 0x00000006 -#define SERC3_SO2F_DAC 0x00000000 -#define SERC3_SO2F_SPDIF 0x00000002 - -/* - * The following defines are for the flags in the serial port 4 configuration - * register. - */ -#define SERC4_SO3EN 0x00000001 -#define SERC4_SO3F_MASK 0x00000006 -#define SERC4_SO3F_DAC 0x00000000 -#define SERC4_SO3F_SPDIF 0x00000002 - -/* - * The following defines are for the flags in the serial port 5 configuration - * register. - */ -#define SERC5_SI2EN 0x00000001 -#define SERC5_SI2F_MASK 0x00000006 -#define SERC5_SI2F_ADC 0x00000000 -#define SERC5_SI2F_SPDIF 0x00000002 - -/* - * The following defines are for the flags in the serial port backdoor sample - * pointer register. - */ -#define SERBSP_FSP_MASK 0x0000000F -#define SERBSP_FSP_SHIFT 0 - -/* - * The following defines are for the flags in the serial port backdoor status - * register. - */ -#define SERBST_RRDY 0x00000001 -#define SERBST_WBSY 0x00000002 - -/* - * The following defines are for the flags in the serial port backdoor command - * register. - */ -#define SERBCM_RDC 0x00000001 -#define SERBCM_WRC 0x00000002 - -/* - * The following defines are for the flags in the serial port backdoor address - * register. - */ -#ifdef NO_CS4612 -#define SERBAD_FAD_MASK 0x000000FF -#else -#define SERBAD_FAD_MASK 0x000001FF -#endif -#define SERBAD_FAD_SHIFT 0 - -/* - * The following defines are for the flags in the serial port backdoor - * configuration register. - */ -#define SERBCF_HBP 0x00000001 - -/* - * The following defines are for the flags in the serial port backdoor write - * port register. - */ -#define SERBWP_FWD_MASK 0x000FFFFF -#define SERBWP_FWD_SHIFT 0 - -/* - * The following defines are for the flags in the serial port backdoor read - * port register. - */ -#define SERBRP_FRD_MASK 0x000FFFFF -#define SERBRP_FRD_SHIFT 0 - -/* - * The following defines are for the flags in the async FIFO address register. - */ -#ifndef NO_CS4612 -#define ASER_FADDR_A1_MASK 0x000001FF -#define ASER_FADDR_EN1 0x00008000 -#define ASER_FADDR_A2_MASK 0x01FF0000 -#define ASER_FADDR_EN2 0x80000000 -#define ASER_FADDR_A1_SHIFT 0 -#define ASER_FADDR_A2_SHIFT 16 -#endif - -/* - * The following defines are for the flags in the AC97 control register. - */ -#define ACCTL_RSTN 0x00000001 -#define ACCTL_ESYN 0x00000002 -#define ACCTL_VFRM 0x00000004 -#define ACCTL_DCV 0x00000008 -#define ACCTL_CRW 0x00000010 -#define ACCTL_ASYN 0x00000020 -#ifndef NO_CS4612 -#define ACCTL_TC 0x00000040 -#endif - -/* - * The following defines are for the flags in the AC97 status register. - */ -#define ACSTS_CRDY 0x00000001 -#define ACSTS_VSTS 0x00000002 -#ifndef NO_CS4612 -#define ACSTS_WKUP 0x00000004 -#endif - -/* - * The following defines are for the flags in the AC97 output slot valid - * register. - */ -#define ACOSV_SLV3 0x00000001 -#define ACOSV_SLV4 0x00000002 -#define ACOSV_SLV5 0x00000004 -#define ACOSV_SLV6 0x00000008 -#define ACOSV_SLV7 0x00000010 -#define ACOSV_SLV8 0x00000020 -#define ACOSV_SLV9 0x00000040 -#define ACOSV_SLV10 0x00000080 -#define ACOSV_SLV11 0x00000100 -#define ACOSV_SLV12 0x00000200 - -/* - * The following defines are for the flags in the AC97 command address - * register. - */ -#define ACCAD_CI_MASK 0x0000007F -#define ACCAD_CI_SHIFT 0 - -/* - * The following defines are for the flags in the AC97 command data register. - */ -#define ACCDA_CD_MASK 0x0000FFFF -#define ACCDA_CD_SHIFT 0 - -/* - * The following defines are for the flags in the AC97 input slot valid - * register. - */ -#define ACISV_ISV3 0x00000001 -#define ACISV_ISV4 0x00000002 -#define ACISV_ISV5 0x00000004 -#define ACISV_ISV6 0x00000008 -#define ACISV_ISV7 0x00000010 -#define ACISV_ISV8 0x00000020 -#define ACISV_ISV9 0x00000040 -#define ACISV_ISV10 0x00000080 -#define ACISV_ISV11 0x00000100 -#define ACISV_ISV12 0x00000200 - -/* - * The following defines are for the flags in the AC97 status address - * register. - */ -#define ACSAD_SI_MASK 0x0000007F -#define ACSAD_SI_SHIFT 0 - -/* - * The following defines are for the flags in the AC97 status data register. - */ -#define ACSDA_SD_MASK 0x0000FFFF -#define ACSDA_SD_SHIFT 0 - -/* - * The following defines are for the flags in the joystick poll/trigger - * register. - */ -#define JSPT_CAX 0x00000001 -#define JSPT_CAY 0x00000002 -#define JSPT_CBX 0x00000004 -#define JSPT_CBY 0x00000008 -#define JSPT_BA1 0x00000010 -#define JSPT_BA2 0x00000020 -#define JSPT_BB1 0x00000040 -#define JSPT_BB2 0x00000080 - -/* - * The following defines are for the flags in the joystick control register. - */ -#define JSCTL_SP_MASK 0x00000003 -#define JSCTL_SP_SLOW 0x00000000 -#define JSCTL_SP_MEDIUM_SLOW 0x00000001 -#define JSCTL_SP_MEDIUM_FAST 0x00000002 -#define JSCTL_SP_FAST 0x00000003 -#define JSCTL_ARE 0x00000004 - -/* - * The following defines are for the flags in the joystick coordinate pair 1 - * readback register. - */ -#define JSC1_Y1V_MASK 0x0000FFFF -#define JSC1_X1V_MASK 0xFFFF0000 -#define JSC1_Y1V_SHIFT 0 -#define JSC1_X1V_SHIFT 16 - -/* - * The following defines are for the flags in the joystick coordinate pair 2 - * readback register. - */ -#define JSC2_Y2V_MASK 0x0000FFFF -#define JSC2_X2V_MASK 0xFFFF0000 -#define JSC2_Y2V_SHIFT 0 -#define JSC2_X2V_SHIFT 16 - -/* - * The following defines are for the flags in the MIDI control register. - */ -#define MIDCR_TXE 0x00000001 /* Enable transmitting. */ -#define MIDCR_RXE 0x00000002 /* Enable receiving. */ -#define MIDCR_RIE 0x00000004 /* Interrupt upon tx ready. */ -#define MIDCR_TIE 0x00000008 /* Interrupt upon rx ready. */ -#define MIDCR_MLB 0x00000010 /* Enable midi loopback. */ -#define MIDCR_MRST 0x00000020 /* Reset interface. */ - -/* - * The following defines are for the flags in the MIDI status register. - */ -#define MIDSR_TBF 0x00000001 /* Tx FIFO is full. */ -#define MIDSR_RBE 0x00000002 /* Rx FIFO is empty. */ - -/* - * The following defines are for the flags in the MIDI write port register. - */ -#define MIDWP_MWD_MASK 0x000000FF -#define MIDWP_MWD_SHIFT 0 - -/* - * The following defines are for the flags in the MIDI read port register. - */ -#define MIDRP_MRD_MASK 0x000000FF -#define MIDRP_MRD_SHIFT 0 - -/* - * The following defines are for the flags in the joystick GPIO register. - */ -#define JSIO_DAX 0x00000001 -#define JSIO_DAY 0x00000002 -#define JSIO_DBX 0x00000004 -#define JSIO_DBY 0x00000008 -#define JSIO_AXOE 0x00000010 -#define JSIO_AYOE 0x00000020 -#define JSIO_BXOE 0x00000040 -#define JSIO_BYOE 0x00000080 - -/* - * The following defines are for the flags in the master async/sync serial - * port enable register. - */ -#ifndef NO_CS4612 -#define ASER_MASTER_ME 0x00000001 -#endif - -/* - * The following defines are for the flags in the configuration interface - * register. - */ -#define CFGI_CLK 0x00000001 -#define CFGI_DOUT 0x00000002 -#define CFGI_DIN_EEN 0x00000004 -#define CFGI_EELD 0x00000008 - -/* - * The following defines are for the flags in the subsystem ID and vendor ID - * register. - */ -#define SSVID_VID_MASK 0x0000FFFF -#define SSVID_SID_MASK 0xFFFF0000 -#define SSVID_VID_SHIFT 0 -#define SSVID_SID_SHIFT 16 - -/* - * The following defines are for the flags in the GPIO pin interface register. - */ -#define GPIOR_VOLDN 0x00000001 -#define GPIOR_VOLUP 0x00000002 -#define GPIOR_SI2D 0x00000004 -#define GPIOR_SI2OE 0x00000008 - -/* - * The following defines are for the flags in the extended GPIO pin direction - * register. - */ -#ifndef NO_CS4612 -#define EGPIODR_GPOE0 0x00000001 -#define EGPIODR_GPOE1 0x00000002 -#define EGPIODR_GPOE2 0x00000004 -#define EGPIODR_GPOE3 0x00000008 -#define EGPIODR_GPOE4 0x00000010 -#define EGPIODR_GPOE5 0x00000020 -#define EGPIODR_GPOE6 0x00000040 -#define EGPIODR_GPOE7 0x00000080 -#define EGPIODR_GPOE8 0x00000100 -#endif - -/* - * The following defines are for the flags in the extended GPIO pin polarity/ - * type register. - */ -#ifndef NO_CS4612 -#define EGPIOPTR_GPPT0 0x00000001 -#define EGPIOPTR_GPPT1 0x00000002 -#define EGPIOPTR_GPPT2 0x00000004 -#define EGPIOPTR_GPPT3 0x00000008 -#define EGPIOPTR_GPPT4 0x00000010 -#define EGPIOPTR_GPPT5 0x00000020 -#define EGPIOPTR_GPPT6 0x00000040 -#define EGPIOPTR_GPPT7 0x00000080 -#define EGPIOPTR_GPPT8 0x00000100 -#endif - -/* - * The following defines are for the flags in the extended GPIO pin sticky - * register. - */ -#ifndef NO_CS4612 -#define EGPIOTR_GPS0 0x00000001 -#define EGPIOTR_GPS1 0x00000002 -#define EGPIOTR_GPS2 0x00000004 -#define EGPIOTR_GPS3 0x00000008 -#define EGPIOTR_GPS4 0x00000010 -#define EGPIOTR_GPS5 0x00000020 -#define EGPIOTR_GPS6 0x00000040 -#define EGPIOTR_GPS7 0x00000080 -#define EGPIOTR_GPS8 0x00000100 -#endif - -/* - * The following defines are for the flags in the extended GPIO ping wakeup - * register. - */ -#ifndef NO_CS4612 -#define EGPIOWR_GPW0 0x00000001 -#define EGPIOWR_GPW1 0x00000002 -#define EGPIOWR_GPW2 0x00000004 -#define EGPIOWR_GPW3 0x00000008 -#define EGPIOWR_GPW4 0x00000010 -#define EGPIOWR_GPW5 0x00000020 -#define EGPIOWR_GPW6 0x00000040 -#define EGPIOWR_GPW7 0x00000080 -#define EGPIOWR_GPW8 0x00000100 -#endif - -/* - * The following defines are for the flags in the extended GPIO pin status - * register. - */ -#ifndef NO_CS4612 -#define EGPIOSR_GPS0 0x00000001 -#define EGPIOSR_GPS1 0x00000002 -#define EGPIOSR_GPS2 0x00000004 -#define EGPIOSR_GPS3 0x00000008 -#define EGPIOSR_GPS4 0x00000010 -#define EGPIOSR_GPS5 0x00000020 -#define EGPIOSR_GPS6 0x00000040 -#define EGPIOSR_GPS7 0x00000080 -#define EGPIOSR_GPS8 0x00000100 -#endif - -/* - * The following defines are for the flags in the serial port 6 configuration - * register. - */ -#ifndef NO_CS4612 -#define SERC6_ASDO2EN 0x00000001 -#endif - -/* - * The following defines are for the flags in the serial port 7 configuration - * register. - */ -#ifndef NO_CS4612 -#define SERC7_ASDI2EN 0x00000001 -#define SERC7_POSILB 0x00000002 -#define SERC7_SIPOLB 0x00000004 -#define SERC7_SOSILB 0x00000008 -#define SERC7_SISOLB 0x00000010 -#endif - -/* - * The following defines are for the flags in the serial port AC link - * configuration register. - */ -#ifndef NO_CS4612 -#define SERACC_CHIP_TYPE_MASK 0x00000001 -#define SERACC_CHIP_TYPE_1_03 0x00000000 -#define SERACC_CHIP_TYPE_2_0 0x00000001 -#define SERACC_TWO_CODECS 0x00000002 -#define SERACC_MDM 0x00000004 -#define SERACC_HSP 0x00000008 -#define SERACC_ODT 0x00000010 /* only CS4630 */ -#endif - -/* - * The following defines are for the flags in the AC97 control register 2. - */ -#ifndef NO_CS4612 -#define ACCTL2_RSTN 0x00000001 -#define ACCTL2_ESYN 0x00000002 -#define ACCTL2_VFRM 0x00000004 -#define ACCTL2_DCV 0x00000008 -#define ACCTL2_CRW 0x00000010 -#define ACCTL2_ASYN 0x00000020 -#endif - -/* - * The following defines are for the flags in the AC97 status register 2. - */ -#ifndef NO_CS4612 -#define ACSTS2_CRDY 0x00000001 -#define ACSTS2_VSTS 0x00000002 -#endif - -/* - * The following defines are for the flags in the AC97 output slot valid - * register 2. - */ -#ifndef NO_CS4612 -#define ACOSV2_SLV3 0x00000001 -#define ACOSV2_SLV4 0x00000002 -#define ACOSV2_SLV5 0x00000004 -#define ACOSV2_SLV6 0x00000008 -#define ACOSV2_SLV7 0x00000010 -#define ACOSV2_SLV8 0x00000020 -#define ACOSV2_SLV9 0x00000040 -#define ACOSV2_SLV10 0x00000080 -#define ACOSV2_SLV11 0x00000100 -#define ACOSV2_SLV12 0x00000200 -#endif - -/* - * The following defines are for the flags in the AC97 command address - * register 2. - */ -#ifndef NO_CS4612 -#define ACCAD2_CI_MASK 0x0000007F -#define ACCAD2_CI_SHIFT 0 -#endif - -/* - * The following defines are for the flags in the AC97 command data register - * 2. - */ -#ifndef NO_CS4612 -#define ACCDA2_CD_MASK 0x0000FFFF -#define ACCDA2_CD_SHIFT 0 -#endif - -/* - * The following defines are for the flags in the AC97 input slot valid - * register 2. - */ -#ifndef NO_CS4612 -#define ACISV2_ISV3 0x00000001 -#define ACISV2_ISV4 0x00000002 -#define ACISV2_ISV5 0x00000004 -#define ACISV2_ISV6 0x00000008 -#define ACISV2_ISV7 0x00000010 -#define ACISV2_ISV8 0x00000020 -#define ACISV2_ISV9 0x00000040 -#define ACISV2_ISV10 0x00000080 -#define ACISV2_ISV11 0x00000100 -#define ACISV2_ISV12 0x00000200 -#endif - -/* - * The following defines are for the flags in the AC97 status address - * register 2. - */ -#ifndef NO_CS4612 -#define ACSAD2_SI_MASK 0x0000007F -#define ACSAD2_SI_SHIFT 0 -#endif - -/* - * The following defines are for the flags in the AC97 status data register 2. - */ -#ifndef NO_CS4612 -#define ACSDA2_SD_MASK 0x0000FFFF -#define ACSDA2_SD_SHIFT 0 -#endif - -/* - * The following defines are for the flags in the I/O trap address and control - * registers (all 12). - */ -#ifndef NO_CS4612 -#define IOTAC_SA_MASK 0x0000FFFF -#define IOTAC_MSK_MASK 0x000F0000 -#define IOTAC_IODC_MASK 0x06000000 -#define IOTAC_IODC_16_BIT 0x00000000 -#define IOTAC_IODC_10_BIT 0x02000000 -#define IOTAC_IODC_12_BIT 0x04000000 -#define IOTAC_WSPI 0x08000000 -#define IOTAC_RSPI 0x10000000 -#define IOTAC_WSE 0x20000000 -#define IOTAC_WE 0x40000000 -#define IOTAC_RE 0x80000000 -#define IOTAC_SA_SHIFT 0 -#define IOTAC_MSK_SHIFT 16 -#endif - -/* - * The following defines are for the flags in the I/O trap fast read registers - * (all 8). - */ -#ifndef NO_CS4612 -#define IOTFR_D_MASK 0x0000FFFF -#define IOTFR_A_MASK 0x000F0000 -#define IOTFR_R_MASK 0x0F000000 -#define IOTFR_ALL 0x40000000 -#define IOTFR_VL 0x80000000 -#define IOTFR_D_SHIFT 0 -#define IOTFR_A_SHIFT 16 -#define IOTFR_R_SHIFT 24 -#endif - -/* - * The following defines are for the flags in the I/O trap FIFO register. - */ -#ifndef NO_CS4612 -#define IOTFIFO_BA_MASK 0x00003FFF -#define IOTFIFO_S_MASK 0x00FF0000 -#define IOTFIFO_OF 0x40000000 -#define IOTFIFO_SPIOF 0x80000000 -#define IOTFIFO_BA_SHIFT 0 -#define IOTFIFO_S_SHIFT 16 -#endif - -/* - * The following defines are for the flags in the I/O trap retry read data - * register. - */ -#ifndef NO_CS4612 -#define IOTRRD_D_MASK 0x0000FFFF -#define IOTRRD_RDV 0x80000000 -#define IOTRRD_D_SHIFT 0 -#endif - -/* - * The following defines are for the flags in the I/O trap FIFO pointer - * register. - */ -#ifndef NO_CS4612 -#define IOTFP_CA_MASK 0x00003FFF -#define IOTFP_PA_MASK 0x3FFF0000 -#define IOTFP_CA_SHIFT 0 -#define IOTFP_PA_SHIFT 16 -#endif - -/* - * The following defines are for the flags in the I/O trap control register. - */ -#ifndef NO_CS4612 -#define IOTCR_ITD 0x00000001 -#define IOTCR_HRV 0x00000002 -#define IOTCR_SRV 0x00000004 -#define IOTCR_DTI 0x00000008 -#define IOTCR_DFI 0x00000010 -#define IOTCR_DDP 0x00000020 -#define IOTCR_JTE 0x00000040 -#define IOTCR_PPE 0x00000080 -#endif - -/* - * The following defines are for the flags in the direct PCI data register. - */ -#ifndef NO_CS4612 -#define DPCID_D_MASK 0xFFFFFFFF -#define DPCID_D_SHIFT 0 -#endif - -/* - * The following defines are for the flags in the direct PCI address register. - */ -#ifndef NO_CS4612 -#define DPCIA_A_MASK 0xFFFFFFFF -#define DPCIA_A_SHIFT 0 -#endif - -/* - * The following defines are for the flags in the direct PCI command register. - */ -#ifndef NO_CS4612 -#define DPCIC_C_MASK 0x0000000F -#define DPCIC_C_IOREAD 0x00000002 -#define DPCIC_C_IOWRITE 0x00000003 -#define DPCIC_BE_MASK 0x000000F0 -#endif - -/* - * The following defines are for the flags in the PC/PCI request register. - */ -#ifndef NO_CS4612 -#define PCPCIR_RDC_MASK 0x00000007 -#define PCPCIR_C_MASK 0x00007000 -#define PCPCIR_REQ 0x00008000 -#define PCPCIR_RDC_SHIFT 0 -#define PCPCIR_C_SHIFT 12 -#endif - -/* - * The following defines are for the flags in the PC/PCI grant register. - */ -#ifndef NO_CS4612 -#define PCPCIG_GDC_MASK 0x00000007 -#define PCPCIG_VL 0x00008000 -#define PCPCIG_GDC_SHIFT 0 -#endif - -/* - * The following defines are for the flags in the PC/PCI master enable - * register. - */ -#ifndef NO_CS4612 -#define PCPCIEN_EN 0x00000001 -#endif - -/* - * The following defines are for the flags in the extended PCI power - * management control register. - */ -#ifndef NO_CS4612 -#define EPCIPMC_GWU 0x00000001 -#define EPCIPMC_FSPC 0x00000002 -#endif - -/* - * The following defines are for the flags in the SP control register. - */ -#define SPCR_RUN 0x00000001 -#define SPCR_STPFR 0x00000002 -#define SPCR_RUNFR 0x00000004 -#define SPCR_TICK 0x00000008 -#define SPCR_DRQEN 0x00000020 -#define SPCR_RSTSP 0x00000040 -#define SPCR_OREN 0x00000080 -#ifndef NO_CS4612 -#define SPCR_PCIINT 0x00000100 -#define SPCR_OINTD 0x00000200 -#define SPCR_CRE 0x00008000 -#endif - -/* - * The following defines are for the flags in the debug index register. - */ -#define DREG_REGID_MASK 0x0000007F -#define DREG_DEBUG 0x00000080 -#define DREG_RGBK_MASK 0x00000700 -#define DREG_TRAP 0x00000800 -#if !defined(NO_CS4612) -#if !defined(NO_CS4615) -#define DREG_TRAPX 0x00001000 -#endif -#endif -#define DREG_REGID_SHIFT 0 -#define DREG_RGBK_SHIFT 8 -#define DREG_RGBK_REGID_MASK 0x0000077F -#define DREG_REGID_R0 0x00000010 -#define DREG_REGID_R1 0x00000011 -#define DREG_REGID_R2 0x00000012 -#define DREG_REGID_R3 0x00000013 -#define DREG_REGID_R4 0x00000014 -#define DREG_REGID_R5 0x00000015 -#define DREG_REGID_R6 0x00000016 -#define DREG_REGID_R7 0x00000017 -#define DREG_REGID_R8 0x00000018 -#define DREG_REGID_R9 0x00000019 -#define DREG_REGID_RA 0x0000001A -#define DREG_REGID_RB 0x0000001B -#define DREG_REGID_RC 0x0000001C -#define DREG_REGID_RD 0x0000001D -#define DREG_REGID_RE 0x0000001E -#define DREG_REGID_RF 0x0000001F -#define DREG_REGID_RA_BUS_LOW 0x00000020 -#define DREG_REGID_RA_BUS_HIGH 0x00000038 -#define DREG_REGID_YBUS_LOW 0x00000050 -#define DREG_REGID_YBUS_HIGH 0x00000058 -#define DREG_REGID_TRAP_0 0x00000100 -#define DREG_REGID_TRAP_1 0x00000101 -#define DREG_REGID_TRAP_2 0x00000102 -#define DREG_REGID_TRAP_3 0x00000103 -#define DREG_REGID_TRAP_4 0x00000104 -#define DREG_REGID_TRAP_5 0x00000105 -#define DREG_REGID_TRAP_6 0x00000106 -#define DREG_REGID_TRAP_7 0x00000107 -#define DREG_REGID_INDIRECT_ADDRESS 0x0000010E -#define DREG_REGID_TOP_OF_STACK 0x0000010F -#if !defined(NO_CS4612) -#if !defined(NO_CS4615) -#define DREG_REGID_TRAP_8 0x00000110 -#define DREG_REGID_TRAP_9 0x00000111 -#define DREG_REGID_TRAP_10 0x00000112 -#define DREG_REGID_TRAP_11 0x00000113 -#define DREG_REGID_TRAP_12 0x00000114 -#define DREG_REGID_TRAP_13 0x00000115 -#define DREG_REGID_TRAP_14 0x00000116 -#define DREG_REGID_TRAP_15 0x00000117 -#define DREG_REGID_TRAP_16 0x00000118 -#define DREG_REGID_TRAP_17 0x00000119 -#define DREG_REGID_TRAP_18 0x0000011A -#define DREG_REGID_TRAP_19 0x0000011B -#define DREG_REGID_TRAP_20 0x0000011C -#define DREG_REGID_TRAP_21 0x0000011D -#define DREG_REGID_TRAP_22 0x0000011E -#define DREG_REGID_TRAP_23 0x0000011F -#endif -#endif -#define DREG_REGID_RSA0_LOW 0x00000200 -#define DREG_REGID_RSA0_HIGH 0x00000201 -#define DREG_REGID_RSA1_LOW 0x00000202 -#define DREG_REGID_RSA1_HIGH 0x00000203 -#define DREG_REGID_RSA2 0x00000204 -#define DREG_REGID_RSA3 0x00000205 -#define DREG_REGID_RSI0_LOW 0x00000206 -#define DREG_REGID_RSI0_HIGH 0x00000207 -#define DREG_REGID_RSI1 0x00000208 -#define DREG_REGID_RSI2 0x00000209 -#define DREG_REGID_SAGUSTATUS 0x0000020A -#define DREG_REGID_RSCONFIG01_LOW 0x0000020B -#define DREG_REGID_RSCONFIG01_HIGH 0x0000020C -#define DREG_REGID_RSCONFIG23_LOW 0x0000020D -#define DREG_REGID_RSCONFIG23_HIGH 0x0000020E -#define DREG_REGID_RSDMA01E 0x0000020F -#define DREG_REGID_RSDMA23E 0x00000210 -#define DREG_REGID_RSD0_LOW 0x00000211 -#define DREG_REGID_RSD0_HIGH 0x00000212 -#define DREG_REGID_RSD1_LOW 0x00000213 -#define DREG_REGID_RSD1_HIGH 0x00000214 -#define DREG_REGID_RSD2_LOW 0x00000215 -#define DREG_REGID_RSD2_HIGH 0x00000216 -#define DREG_REGID_RSD3_LOW 0x00000217 -#define DREG_REGID_RSD3_HIGH 0x00000218 -#define DREG_REGID_SRAR_HIGH 0x0000021A -#define DREG_REGID_SRAR_LOW 0x0000021B -#define DREG_REGID_DMA_STATE 0x0000021C -#define DREG_REGID_CURRENT_DMA_STREAM 0x0000021D -#define DREG_REGID_NEXT_DMA_STREAM 0x0000021E -#define DREG_REGID_CPU_STATUS 0x00000300 -#define DREG_REGID_MAC_MODE 0x00000301 -#define DREG_REGID_STACK_AND_REPEAT 0x00000302 -#define DREG_REGID_INDEX0 0x00000304 -#define DREG_REGID_INDEX1 0x00000305 -#define DREG_REGID_DMA_STATE_0_3 0x00000400 -#define DREG_REGID_DMA_STATE_4_7 0x00000404 -#define DREG_REGID_DMA_STATE_8_11 0x00000408 -#define DREG_REGID_DMA_STATE_12_15 0x0000040C -#define DREG_REGID_DMA_STATE_16_19 0x00000410 -#define DREG_REGID_DMA_STATE_20_23 0x00000414 -#define DREG_REGID_DMA_STATE_24_27 0x00000418 -#define DREG_REGID_DMA_STATE_28_31 0x0000041C -#define DREG_REGID_DMA_STATE_32_35 0x00000420 -#define DREG_REGID_DMA_STATE_36_39 0x00000424 -#define DREG_REGID_DMA_STATE_40_43 0x00000428 -#define DREG_REGID_DMA_STATE_44_47 0x0000042C -#define DREG_REGID_DMA_STATE_48_51 0x00000430 -#define DREG_REGID_DMA_STATE_52_55 0x00000434 -#define DREG_REGID_DMA_STATE_56_59 0x00000438 -#define DREG_REGID_DMA_STATE_60_63 0x0000043C -#define DREG_REGID_DMA_STATE_64_67 0x00000440 -#define DREG_REGID_DMA_STATE_68_71 0x00000444 -#define DREG_REGID_DMA_STATE_72_75 0x00000448 -#define DREG_REGID_DMA_STATE_76_79 0x0000044C -#define DREG_REGID_DMA_STATE_80_83 0x00000450 -#define DREG_REGID_DMA_STATE_84_87 0x00000454 -#define DREG_REGID_DMA_STATE_88_91 0x00000458 -#define DREG_REGID_DMA_STATE_92_95 0x0000045C -#define DREG_REGID_TRAP_SELECT 0x00000500 -#define DREG_REGID_TRAP_WRITE_0 0x00000500 -#define DREG_REGID_TRAP_WRITE_1 0x00000501 -#define DREG_REGID_TRAP_WRITE_2 0x00000502 -#define DREG_REGID_TRAP_WRITE_3 0x00000503 -#define DREG_REGID_TRAP_WRITE_4 0x00000504 -#define DREG_REGID_TRAP_WRITE_5 0x00000505 -#define DREG_REGID_TRAP_WRITE_6 0x00000506 -#define DREG_REGID_TRAP_WRITE_7 0x00000507 -#if !defined(NO_CS4612) -#if !defined(NO_CS4615) -#define DREG_REGID_TRAP_WRITE_8 0x00000510 -#define DREG_REGID_TRAP_WRITE_9 0x00000511 -#define DREG_REGID_TRAP_WRITE_10 0x00000512 -#define DREG_REGID_TRAP_WRITE_11 0x00000513 -#define DREG_REGID_TRAP_WRITE_12 0x00000514 -#define DREG_REGID_TRAP_WRITE_13 0x00000515 -#define DREG_REGID_TRAP_WRITE_14 0x00000516 -#define DREG_REGID_TRAP_WRITE_15 0x00000517 -#define DREG_REGID_TRAP_WRITE_16 0x00000518 -#define DREG_REGID_TRAP_WRITE_17 0x00000519 -#define DREG_REGID_TRAP_WRITE_18 0x0000051A -#define DREG_REGID_TRAP_WRITE_19 0x0000051B -#define DREG_REGID_TRAP_WRITE_20 0x0000051C -#define DREG_REGID_TRAP_WRITE_21 0x0000051D -#define DREG_REGID_TRAP_WRITE_22 0x0000051E -#define DREG_REGID_TRAP_WRITE_23 0x0000051F -#endif -#endif -#define DREG_REGID_MAC0_ACC0_LOW 0x00000600 -#define DREG_REGID_MAC0_ACC1_LOW 0x00000601 -#define DREG_REGID_MAC0_ACC2_LOW 0x00000602 -#define DREG_REGID_MAC0_ACC3_LOW 0x00000603 -#define DREG_REGID_MAC1_ACC0_LOW 0x00000604 -#define DREG_REGID_MAC1_ACC1_LOW 0x00000605 -#define DREG_REGID_MAC1_ACC2_LOW 0x00000606 -#define DREG_REGID_MAC1_ACC3_LOW 0x00000607 -#define DREG_REGID_MAC0_ACC0_MID 0x00000608 -#define DREG_REGID_MAC0_ACC1_MID 0x00000609 -#define DREG_REGID_MAC0_ACC2_MID 0x0000060A -#define DREG_REGID_MAC0_ACC3_MID 0x0000060B -#define DREG_REGID_MAC1_ACC0_MID 0x0000060C -#define DREG_REGID_MAC1_ACC1_MID 0x0000060D -#define DREG_REGID_MAC1_ACC2_MID 0x0000060E -#define DREG_REGID_MAC1_ACC3_MID 0x0000060F -#define DREG_REGID_MAC0_ACC0_HIGH 0x00000610 -#define DREG_REGID_MAC0_ACC1_HIGH 0x00000611 -#define DREG_REGID_MAC0_ACC2_HIGH 0x00000612 -#define DREG_REGID_MAC0_ACC3_HIGH 0x00000613 -#define DREG_REGID_MAC1_ACC0_HIGH 0x00000614 -#define DREG_REGID_MAC1_ACC1_HIGH 0x00000615 -#define DREG_REGID_MAC1_ACC2_HIGH 0x00000616 -#define DREG_REGID_MAC1_ACC3_HIGH 0x00000617 -#define DREG_REGID_RSHOUT_LOW 0x00000620 -#define DREG_REGID_RSHOUT_MID 0x00000628 -#define DREG_REGID_RSHOUT_HIGH 0x00000630 - -/* - * The following defines are for the flags in the DMA stream requestor write - */ -#define DSRWP_DSR_MASK 0x0000000F -#define DSRWP_DSR_BG_RQ 0x00000001 -#define DSRWP_DSR_PRIORITY_MASK 0x00000006 -#define DSRWP_DSR_PRIORITY_0 0x00000000 -#define DSRWP_DSR_PRIORITY_1 0x00000002 -#define DSRWP_DSR_PRIORITY_2 0x00000004 -#define DSRWP_DSR_PRIORITY_3 0x00000006 -#define DSRWP_DSR_RQ_PENDING 0x00000008 - -/* - * The following defines are for the flags in the trap write port register. - */ -#define TWPR_TW_MASK 0x0000FFFF -#define TWPR_TW_SHIFT 0 - -/* - * The following defines are for the flags in the stack pointer write - * register. - */ -#define SPWR_STKP_MASK 0x0000000F -#define SPWR_STKP_SHIFT 0 - -/* - * The following defines are for the flags in the SP interrupt register. - */ -#define SPIR_FRI 0x00000001 -#define SPIR_DOI 0x00000002 -#define SPIR_GPI2 0x00000004 -#define SPIR_GPI3 0x00000008 -#define SPIR_IP0 0x00000010 -#define SPIR_IP1 0x00000020 -#define SPIR_IP2 0x00000040 -#define SPIR_IP3 0x00000080 - -/* - * The following defines are for the flags in the functional group 1 register. - */ -#define FGR1_F1S_MASK 0x0000FFFF -#define FGR1_F1S_SHIFT 0 - -/* - * The following defines are for the flags in the SP clock status register. - */ -#define SPCS_FRI 0x00000001 -#define SPCS_DOI 0x00000002 -#define SPCS_GPI2 0x00000004 -#define SPCS_GPI3 0x00000008 -#define SPCS_IP0 0x00000010 -#define SPCS_IP1 0x00000020 -#define SPCS_IP2 0x00000040 -#define SPCS_IP3 0x00000080 -#define SPCS_SPRUN 0x00000100 -#define SPCS_SLEEP 0x00000200 -#define SPCS_FG 0x00000400 -#define SPCS_ORUN 0x00000800 -#define SPCS_IRQ 0x00001000 -#define SPCS_FGN_MASK 0x0000E000 -#define SPCS_FGN_SHIFT 13 - -/* - * The following defines are for the flags in the SP DMA requestor status - * register. - */ -#define SDSR_DCS_MASK 0x000000FF -#define SDSR_DCS_SHIFT 0 -#define SDSR_DCS_NONE 0x00000007 - -/* - * The following defines are for the flags in the frame timer register. - */ -#define FRMT_FTV_MASK 0x0000FFFF -#define FRMT_FTV_SHIFT 0 - -/* - * The following defines are for the flags in the frame timer current count - * register. - */ -#define FRCC_FCC_MASK 0x0000FFFF -#define FRCC_FCC_SHIFT 0 - -/* - * The following defines are for the flags in the frame timer save count - * register. - */ -#define FRSC_FCS_MASK 0x0000FFFF -#define FRSC_FCS_SHIFT 0 - -/* - * The following define the various flags stored in the scatter/gather - * descriptors. - */ -#define DMA_SG_NEXT_ENTRY_MASK 0x00000FF8 -#define DMA_SG_SAMPLE_END_MASK 0x0FFF0000 -#define DMA_SG_SAMPLE_END_FLAG 0x10000000 -#define DMA_SG_LOOP_END_FLAG 0x20000000 -#define DMA_SG_SIGNAL_END_FLAG 0x40000000 -#define DMA_SG_SIGNAL_PAGE_FLAG 0x80000000 -#define DMA_SG_NEXT_ENTRY_SHIFT 3 -#define DMA_SG_SAMPLE_END_SHIFT 16 - -/* - * The following define the offsets of the fields within the on-chip generic - * DMA requestor. - */ -#define DMA_RQ_CONTROL1 0x00000000 -#define DMA_RQ_CONTROL2 0x00000004 -#define DMA_RQ_SOURCE_ADDR 0x00000008 -#define DMA_RQ_DESTINATION_ADDR 0x0000000C -#define DMA_RQ_NEXT_PAGE_ADDR 0x00000010 -#define DMA_RQ_NEXT_PAGE_SGDESC 0x00000014 -#define DMA_RQ_LOOP_START_ADDR 0x00000018 -#define DMA_RQ_POST_LOOP_ADDR 0x0000001C -#define DMA_RQ_PAGE_MAP_ADDR 0x00000020 - -/* - * The following defines are for the flags in the first control word of the - * on-chip generic DMA requestor. - */ -#define DMA_RQ_C1_COUNT_MASK 0x000003FF -#define DMA_RQ_C1_DESTINATION_SCATTER 0x00001000 -#define DMA_RQ_C1_SOURCE_GATHER 0x00002000 -#define DMA_RQ_C1_DONE_FLAG 0x00004000 -#define DMA_RQ_C1_OPTIMIZE_STATE 0x00008000 -#define DMA_RQ_C1_SAMPLE_END_STATE_MASK 0x00030000 -#define DMA_RQ_C1_FULL_PAGE 0x00000000 -#define DMA_RQ_C1_BEFORE_SAMPLE_END 0x00010000 -#define DMA_RQ_C1_PAGE_MAP_ERROR 0x00020000 -#define DMA_RQ_C1_AT_SAMPLE_END 0x00030000 -#define DMA_RQ_C1_LOOP_END_STATE_MASK 0x000C0000 -#define DMA_RQ_C1_NOT_LOOP_END 0x00000000 -#define DMA_RQ_C1_BEFORE_LOOP_END 0x00040000 -#define DMA_RQ_C1_2PAGE_LOOP_BEGIN 0x00080000 -#define DMA_RQ_C1_LOOP_BEGIN 0x000C0000 -#define DMA_RQ_C1_PAGE_MAP_MASK 0x00300000 -#define DMA_RQ_C1_PM_NONE_PENDING 0x00000000 -#define DMA_RQ_C1_PM_NEXT_PENDING 0x00100000 -#define DMA_RQ_C1_PM_RESERVED 0x00200000 -#define DMA_RQ_C1_PM_LOOP_NEXT_PENDING 0x00300000 -#define DMA_RQ_C1_WRITEBACK_DEST_FLAG 0x00400000 -#define DMA_RQ_C1_WRITEBACK_SRC_FLAG 0x00800000 -#define DMA_RQ_C1_DEST_SIZE_MASK 0x07000000 -#define DMA_RQ_C1_DEST_LINEAR 0x00000000 -#define DMA_RQ_C1_DEST_MOD16 0x01000000 -#define DMA_RQ_C1_DEST_MOD32 0x02000000 -#define DMA_RQ_C1_DEST_MOD64 0x03000000 -#define DMA_RQ_C1_DEST_MOD128 0x04000000 -#define DMA_RQ_C1_DEST_MOD256 0x05000000 -#define DMA_RQ_C1_DEST_MOD512 0x06000000 -#define DMA_RQ_C1_DEST_MOD1024 0x07000000 -#define DMA_RQ_C1_DEST_ON_HOST 0x08000000 -#define DMA_RQ_C1_SOURCE_SIZE_MASK 0x70000000 -#define DMA_RQ_C1_SOURCE_LINEAR 0x00000000 -#define DMA_RQ_C1_SOURCE_MOD16 0x10000000 -#define DMA_RQ_C1_SOURCE_MOD32 0x20000000 -#define DMA_RQ_C1_SOURCE_MOD64 0x30000000 -#define DMA_RQ_C1_SOURCE_MOD128 0x40000000 -#define DMA_RQ_C1_SOURCE_MOD256 0x50000000 -#define DMA_RQ_C1_SOURCE_MOD512 0x60000000 -#define DMA_RQ_C1_SOURCE_MOD1024 0x70000000 -#define DMA_RQ_C1_SOURCE_ON_HOST 0x80000000 -#define DMA_RQ_C1_COUNT_SHIFT 0 - -/* - * The following defines are for the flags in the second control word of the - * on-chip generic DMA requestor. - */ -#define DMA_RQ_C2_VIRTUAL_CHANNEL_MASK 0x0000003F -#define DMA_RQ_C2_VIRTUAL_SIGNAL_MASK 0x00000300 -#define DMA_RQ_C2_NO_VIRTUAL_SIGNAL 0x00000000 -#define DMA_RQ_C2_SIGNAL_EVERY_DMA 0x00000100 -#define DMA_RQ_C2_SIGNAL_SOURCE_PINGPONG 0x00000200 -#define DMA_RQ_C2_SIGNAL_DEST_PINGPONG 0x00000300 -#define DMA_RQ_C2_AUDIO_CONVERT_MASK 0x0000F000 -#define DMA_RQ_C2_AC_NONE 0x00000000 -#define DMA_RQ_C2_AC_8_TO_16_BIT 0x00001000 -#define DMA_RQ_C2_AC_MONO_TO_STEREO 0x00002000 -#define DMA_RQ_C2_AC_ENDIAN_CONVERT 0x00004000 -#define DMA_RQ_C2_AC_SIGNED_CONVERT 0x00008000 -#define DMA_RQ_C2_LOOP_END_MASK 0x0FFF0000 -#define DMA_RQ_C2_LOOP_MASK 0x30000000 -#define DMA_RQ_C2_NO_LOOP 0x00000000 -#define DMA_RQ_C2_ONE_PAGE_LOOP 0x10000000 -#define DMA_RQ_C2_TWO_PAGE_LOOP 0x20000000 -#define DMA_RQ_C2_MULTI_PAGE_LOOP 0x30000000 -#define DMA_RQ_C2_SIGNAL_LOOP_BACK 0x40000000 -#define DMA_RQ_C2_SIGNAL_POST_BEGIN_PAGE 0x80000000 -#define DMA_RQ_C2_VIRTUAL_CHANNEL_SHIFT 0 -#define DMA_RQ_C2_LOOP_END_SHIFT 16 - -/* - * The following defines are for the flags in the source and destination words - * of the on-chip generic DMA requestor. - */ -#define DMA_RQ_SD_ADDRESS_MASK 0x0000FFFF -#define DMA_RQ_SD_MEMORY_ID_MASK 0x000F0000 -#define DMA_RQ_SD_SP_PARAM_ADDR 0x00000000 -#define DMA_RQ_SD_SP_SAMPLE_ADDR 0x00010000 -#define DMA_RQ_SD_SP_PROGRAM_ADDR 0x00020000 -#define DMA_RQ_SD_SP_DEBUG_ADDR 0x00030000 -#define DMA_RQ_SD_OMNIMEM_ADDR 0x000E0000 -#define DMA_RQ_SD_END_FLAG 0x40000000 -#define DMA_RQ_SD_ERROR_FLAG 0x80000000 -#define DMA_RQ_SD_ADDRESS_SHIFT 0 - -/* - * The following defines are for the flags in the page map address word of the - * on-chip generic DMA requestor. - */ -#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_MASK 0x00000FF8 -#define DMA_RQ_PMA_PAGE_TABLE_MASK 0xFFFFF000 -#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_SHIFT 3 -#define DMA_RQ_PMA_PAGE_TABLE_SHIFT 12 - -#define BA1_VARIDEC_BUF_1 0x000 - -#define BA1_PDTC 0x0c0 /* BA1_PLAY_DMA_TRANSACTION_COUNT_REG */ -#define BA1_PFIE 0x0c4 /* BA1_PLAY_FORMAT_&_INTERRUPT_ENABLE_REG */ -#define BA1_PBA 0x0c8 /* BA1_PLAY_BUFFER_ADDRESS */ -#define BA1_PVOL 0x0f8 /* BA1_PLAY_VOLUME_REG */ -#define BA1_PSRC 0x288 /* BA1_PLAY_SAMPLE_RATE_CORRECTION_REG */ -#define BA1_PCTL 0x2a4 /* BA1_PLAY_CONTROL_REG */ -#define BA1_PPI 0x2b4 /* BA1_PLAY_PHASE_INCREMENT_REG */ - -#define BA1_CCTL 0x064 /* BA1_CAPTURE_CONTROL_REG */ -#define BA1_CIE 0x104 /* BA1_CAPTURE_INTERRUPT_ENABLE_REG */ -#define BA1_CBA 0x10c /* BA1_CAPTURE_BUFFER_ADDRESS */ -#define BA1_CSRC 0x2c8 /* BA1_CAPTURE_SAMPLE_RATE_CORRECTION_REG */ -#define BA1_CCI 0x2d8 /* BA1_CAPTURE_COEFFICIENT_INCREMENT_REG */ -#define BA1_CD 0x2e0 /* BA1_CAPTURE_DELAY_REG */ -#define BA1_CPI 0x2f4 /* BA1_CAPTURE_PHASE_INCREMENT_REG */ -#define BA1_CVOL 0x2f8 /* BA1_CAPTURE_VOLUME_REG */ - -#define BA1_CFG1 0x134 /* BA1_CAPTURE_FRAME_GROUP_1_REG */ -#define BA1_CFG2 0x138 /* BA1_CAPTURE_FRAME_GROUP_2_REG */ -#define BA1_CCST 0x13c /* BA1_CAPTURE_CONSTANT_REG */ -#define BA1_CSPB 0x340 /* BA1_CAPTURE_SPB_ADDRESS */ - -/* - * - */ - -#define CS46XX_MODE_OUTPUT (1<<0) /* MIDI UART - output */ -#define CS46XX_MODE_INPUT (1<<1) /* MIDI UART - input */ - -/* - * - */ - -#define SAVE_REG_MAX 0x10 -#define POWER_DOWN_ALL 0x7f0f - -/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */ -#define MAX_NR_AC97 4 -#define CS46XX_PRIMARY_CODEC_INDEX 0 -#define CS46XX_SECONDARY_CODEC_INDEX 1 -#define CS46XX_SECONDARY_CODEC_OFFSET 0x80 -#define CS46XX_DSP_CAPTURE_CHANNEL 1 - -/* capture */ -#define CS46XX_DSP_CAPTURE_CHANNEL 1 - -/* mixer */ -#define CS46XX_MIXER_SPDIF_INPUT_ELEMENT 1 -#define CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT 2 - - -struct snd_cs46xx_pcm { - struct snd_dma_buffer hw_buf; - - unsigned int ctl; - unsigned int shift; /* Shift count to trasform frames in bytes */ - struct snd_pcm_indirect pcm_rec; - struct snd_pcm_substream *substream; - - struct dsp_pcm_channel_descriptor * pcm_channel; - - int pcm_channel_id; /* Fron Rear, Center Lfe ... */ -}; - -struct snd_cs46xx_region { - char name[24]; - unsigned long base; - void __iomem *remap_addr; - unsigned long size; - struct resource *resource; -}; - -struct snd_cs46xx { - int irq; - unsigned long ba0_addr; - unsigned long ba1_addr; - union { - struct { - struct snd_cs46xx_region ba0; - struct snd_cs46xx_region data0; - struct snd_cs46xx_region data1; - struct snd_cs46xx_region pmem; - struct snd_cs46xx_region reg; - } name; - struct snd_cs46xx_region idx[5]; - } region; - - unsigned int mode; - - struct { - struct snd_dma_buffer hw_buf; - - unsigned int ctl; - unsigned int shift; /* Shift count to trasform frames in bytes */ - struct snd_pcm_indirect pcm_rec; - struct snd_pcm_substream *substream; - } capt; - - - int nr_ac97_codecs; - struct snd_ac97_bus *ac97_bus; - struct snd_ac97 *ac97[MAX_NR_AC97]; - - struct pci_dev *pci; - struct snd_card *card; - struct snd_pcm *pcm; - - struct snd_rawmidi *rmidi; - struct snd_rawmidi_substream *midi_input; - struct snd_rawmidi_substream *midi_output; - - spinlock_t reg_lock; - unsigned int midcr; - unsigned int uartm; - - int amplifier; - void (*amplifier_ctrl)(struct snd_cs46xx *, int); - void (*active_ctrl)(struct snd_cs46xx *, int); - void (*mixer_init)(struct snd_cs46xx *); - - int acpi_port; - struct snd_kcontrol *eapd_switch; /* for amplifier hack */ - int accept_valid; /* accept mmap valid (for OSS) */ - int in_suspend; - - struct gameport *gameport; - -#ifdef CONFIG_SND_CS46XX_NEW_DSP - struct mutex spos_mutex; - - struct dsp_spos_instance * dsp_spos_instance; - - struct snd_pcm *pcm_rear; - struct snd_pcm *pcm_center_lfe; - struct snd_pcm *pcm_iec958; -#else /* for compatibility */ - struct snd_cs46xx_pcm *playback_pcm; - unsigned int play_ctl; -#endif - -#ifdef CONFIG_PM - u32 *saved_regs; -#endif -}; - -int snd_cs46xx_create(struct snd_card *card, - struct pci_dev *pci, - int external_amp, int thinkpad, - struct snd_cs46xx **rcodec); -extern const struct dev_pm_ops snd_cs46xx_pm; - -int snd_cs46xx_pcm(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm); -int snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm); -int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm); -int snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm); -int snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device); -int snd_cs46xx_midi(struct snd_cs46xx *chip, int device, struct snd_rawmidi **rmidi); -int snd_cs46xx_start_dsp(struct snd_cs46xx *chip); -int snd_cs46xx_gameport(struct snd_cs46xx *chip); - -#endif /* __SOUND_CS46XX_H */ diff --git a/include/sound/cs46xx_dsp_scb_types.h b/include/sound/cs46xx_dsp_scb_types.h deleted file mode 100644 index 080857ad0ca2..000000000000 --- a/include/sound/cs46xx_dsp_scb_types.h +++ /dev/null @@ -1,1213 +0,0 @@ -/* - * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards - * Copyright (c) by 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 - * - * - * NOTE: comments are copy/paste from cwcemb80.lst - * provided by Tom Woller at Cirrus (my only - * documentation about the SP OS running inside - * the DSP) - */ - -#ifndef __CS46XX_DSP_SCB_TYPES_H__ -#define __CS46XX_DSP_SCB_TYPES_H__ - -#include - -#ifndef ___DSP_DUAL_16BIT_ALLOC -#if defined(__LITTLE_ENDIAN) -#define ___DSP_DUAL_16BIT_ALLOC(a,b) u16 a; u16 b; -#elif defined(__BIG_ENDIAN) -#define ___DSP_DUAL_16BIT_ALLOC(a,b) u16 b; u16 a; -#else -#error Not __LITTLE_ENDIAN and not __BIG_ENDIAN, then what ??? -#endif -#endif - -/* This structs are used internally by the SP */ - -struct dsp_basic_dma_req { - /* DMA Requestor Word 0 (DCW) fields: - - 31 [30-28]27 [26:24] 23 22 21 20 [19:18] [17:16] 15 14 13 12 11 10 9 8 7 6 [5:0] - _______________________________________________________________________________________ - |S| SBT |D| DBT |wb|wb| | | LS | SS |Opt|Do|SSG|DSG| | | | | | | Dword | - |H|_____ |H|_________|S_|D |__|__|______|_______|___|ne|__ |__ |__|__|_|_|_|_|_Count -1| - */ - u32 dcw; /* DMA Control Word */ - u32 dmw; /* DMA Mode Word */ - u32 saw; /* Source Address Word */ - u32 daw; /* Destination Address Word */ -}; - -struct dsp_scatter_gather_ext { - u32 npaw; /* Next-Page Address Word */ - - /* DMA Requestor Word 5 (NPCW) fields: - - 31-30 29 28 [27:16] [15:12] [11:3] [2:0] - _________________________________________________________________________________________ - |SV |LE|SE| Sample-end byte offset | | Page-map entry offset for next | | - |page|__|__| ___________________________|_________|__page, if !sample-end___________|____| - */ - u32 npcw; /* Next-Page Control Word */ - u32 lbaw; /* Loop-Begin Address Word */ - u32 nplbaw; /* Next-Page after Loop-Begin Address Word */ - u32 sgaw; /* Scatter/Gather Address Word */ -}; - -struct dsp_volume_control { - ___DSP_DUAL_16BIT_ALLOC( - rightTarg, /* Target volume for left & right channels */ - leftTarg - ) - ___DSP_DUAL_16BIT_ALLOC( - rightVol, /* Current left & right channel volumes */ - leftVol - ) -}; - -/* Generic stream control block (SCB) structure definition */ -struct dsp_generic_scb { - /* For streaming I/O, the DSP should never alter any words in the DMA - requestor or the scatter/gather extension. Only ad hoc DMA request - streams are free to alter the requestor (currently only occur in the - DOS-based MIDI controller and in debugger-inserted code). - - If an SCB does not have any associated DMA requestor, these 9 ints - may be freed for use by other tasks, but the pointer to the SCB must - still be such that the insOrd:nextSCB appear at offset 9 from the - SCB pointer. - - Basic (non scatter/gather) DMA requestor (4 ints) - */ - - /* Initialized by the host, only modified by DMA - R/O for the DSP task */ - struct dsp_basic_dma_req basic_req; /* Optional */ - - /* Scatter/gather DMA requestor extension (5 ints) - Initialized by the host, only modified by DMA - DSP task never needs to even read these. - */ - struct dsp_scatter_gather_ext sg_ext; /* Optional */ - - /* Sublist pointer & next stream control block (SCB) link. - Initialized & modified by the host R/O for the DSP task - */ - ___DSP_DUAL_16BIT_ALLOC( - next_scb, /* REQUIRED */ - sub_list_ptr /* REQUIRED */ - ) - - /* Pointer to this tasks parameter block & stream function pointer - Initialized by the host R/O for the DSP task */ - ___DSP_DUAL_16BIT_ALLOC( - entry_point, /* REQUIRED */ - this_spb /* REQUIRED */ - ) - - /* rsConfig register for stream buffer (rsDMA reg. - is loaded from basicReq.daw for incoming streams, or - basicReq.saw, for outgoing streams) - - 31 30 29 [28:24] [23:16] 15 14 13 12 11 10 9 8 7 6 5 4 [3:0] - ______________________________________________________________________________ - |DMA |D|maxDMAsize| streamNum|dir|p| | | | | | |ds |shr 1|rev Cy | mod | - |prio |_|__________|__________|___|_|__|__|__|__|_|_|___|_____|_______|_______| - 31 30 29 [28:24] [23:16] 15 14 13 12 11 10 9 8 7 6 5 4 [3:0] - - - Initialized by the host R/O for the DSP task - */ - u32 strm_rs_config; /* REQUIRED */ - // - /* On mixer input streams: indicates mixer input stream configuration - On Tees, this is copied from the stream being snooped - - Stream sample pointer & MAC-unit mode for this stream - - Initialized by the host Updated by the DSP task - */ - u32 strm_buf_ptr; /* REQUIRED */ - - /* On mixer input streams: points to next mixer input and is updated by the - mixer subroutine in the "parent" DSP task - (least-significant 16 bits are preserved, unused) - - On Tees, the pointer is copied from the stream being snooped on - initialization, and, subsequently, it is copied into the - stream being snooped. - - On wavetable/3D voices: the strmBufPtr will use all 32 bits to allow for - fractional phase accumulation - - Fractional increment per output sample in the input sample buffer - - (Not used on mixer input streams & redefined on Tees) - On wavetable/3D voices: this 32-bit word specifies the integer.fractional - increment per output sample. - */ - u32 strmPhiIncr; - - - /* Standard stereo volume control - Initialized by the host (host updates target volumes) - - Current volumes update by the DSP task - On mixer input streams: required & updated by the mixer subroutine in the - "parent" DSP task - - On Tees, both current & target volumes are copied up on initialization, - and, subsequently, the target volume is copied up while the current - volume is copied down. - - These two 32-bit words are redefined for wavetable & 3-D voices. - */ - struct dsp_volume_control vol_ctrl_t; /* Optional */ -}; - - -struct dsp_spos_control_block { - /* WARNING: Certain items in this structure are modified by the host - Any dword that can be modified by the host, must not be - modified by the SP as the host can only do atomic dword - writes, and to do otherwise, even a read modify write, - may lead to corrupted data on the SP. - - This rule does not apply to one off boot time initialisation prior to starting the SP - */ - - - ___DSP_DUAL_16BIT_ALLOC( - /* First element on the Hyper forground task tree */ - hfg_tree_root_ptr, /* HOST */ - /* First 3 dwords are written by the host and read-only on the DSP */ - hfg_stack_base /* HOST */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - /* Point to this data structure to enable easy access */ - spos_cb_ptr, /* SP */ - prev_task_tree_ptr /* SP && HOST */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - /* Currently Unused */ - xxinterval_timer_period, - /* Enable extension of SPOS data structure */ - HFGSPB_ptr - ) - - - ___DSP_DUAL_16BIT_ALLOC( - xxnum_HFG_ticks_thisInterval, - /* Modified by the DSP */ - xxnum_tntervals - ) - - - /* Set by DSP upon encountering a trap (breakpoint) or a spurious - interrupt. The host must clear this dword after reading it - upon receiving spInt1. */ - ___DSP_DUAL_16BIT_ALLOC( - spurious_int_flag, /* (Host & SP) Nature of the spurious interrupt */ - trap_flag /* (Host & SP) Nature of detected Trap */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - unused2, - invalid_IP_flag /* (Host & SP ) Indicate detection of invalid instruction pointer */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - /* pointer to forground task tree header for use in next task search */ - fg_task_tree_hdr_ptr, /* HOST */ - /* Data structure for controlling synchronous link update */ - hfg_sync_update_ptr /* HOST */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - begin_foreground_FCNT, /* SP */ - /* Place holder for holding sleep timing */ - last_FCNT_before_sleep /* SP */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - unused7, /* SP */ - next_task_treePtr /* SP */ - ) - - u32 unused5; - - ___DSP_DUAL_16BIT_ALLOC( - active_flags, /* SP */ - /* State flags, used to assist control of execution of Hyper Forground */ - HFG_flags /* SP */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - unused9, - unused8 - ) - - /* Space for saving enough context so that we can set up enough - to save some more context. - */ - u32 rFE_save_for_invalid_IP; - u32 r32_save_for_spurious_int; - u32 r32_save_for_trap; - u32 r32_save_for_HFG; -}; - -/* SPB for MIX_TO_OSTREAM algorithm family */ -struct dsp_mix2_ostream_spb -{ - /* 16b.16b integer.frac approximation to the - number of 3 sample triplets to output each - frame. (approximation must be floor, to - insure that the fractional error is always - positive) - */ - u32 outTripletsPerFrame; - - /* 16b.16b integer.frac accumulated number of - output triplets since the start of group - */ - u32 accumOutTriplets; -}; - -/* SCB for Timing master algorithm */ -struct dsp_timing_master_scb { - /* First 12 dwords from generic_scb_t */ - struct dsp_basic_dma_req basic_req; /* Optional */ - struct dsp_scatter_gather_ext sg_ext; /* Optional */ - ___DSP_DUAL_16BIT_ALLOC( - next_scb, /* REQUIRED */ - sub_list_ptr /* REQUIRED */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - entry_point, /* REQUIRED */ - this_spb /* REQUIRED */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - /* Initial values are 0000:xxxx */ - reserved, - extra_sample_accum - ) - - - /* Initial values are xxxx:0000 - hi: Current CODEC output FIFO pointer - (0 to 0x0f) - lo: Flag indicating that the CODEC - FIFO is sync'd (host clears to - resynchronize the FIFO pointer - upon start/restart) - */ - ___DSP_DUAL_16BIT_ALLOC( - codec_FIFO_syncd, - codec_FIFO_ptr - ) - - /* Init. 8000:0005 for 44.1k - 8000:0001 for 48k - hi: Fractional sample accumulator 0.16b - lo: Number of frames remaining to be - processed in the current group of - frames - */ - ___DSP_DUAL_16BIT_ALLOC( - frac_samp_accum_qm1, - TM_frms_left_in_group - ) - - /* Init. 0001:0005 for 44.1k - 0000:0001 for 48k - hi: Fractional sample correction factor 0.16b - to be added every frameGroupLength frames - to correct for truncation error in - nsamp_per_frm_q15 - lo: Number of frames in the group - */ - ___DSP_DUAL_16BIT_ALLOC( - frac_samp_correction_qm1, - TM_frm_group_length - ) - - /* Init. 44.1k*65536/8k = 0x00058333 for 44.1k - 48k*65536/8k = 0x00060000 for 48k - 16b.16b integer.frac approximation to the - number of samples to output each frame. - (approximation must be floor, to insure */ - u32 nsamp_per_frm_q15; -}; - -/* SCB for CODEC output algorithm */ -struct dsp_codec_output_scb { - /* First 13 dwords from generic_scb_t */ - struct dsp_basic_dma_req basic_req; /* Optional */ - struct dsp_scatter_gather_ext sg_ext; /* Optional */ - ___DSP_DUAL_16BIT_ALLOC( - next_scb, /* REQUIRED */ - sub_list_ptr /* REQUIRED */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - entry_point, /* REQUIRED */ - this_spb /* REQUIRED */ - ) - - u32 strm_rs_config; /* REQUIRED */ - - u32 strm_buf_ptr; /* REQUIRED */ - - /* NOTE: The CODEC output task reads samples from the first task on its - sublist at the stream buffer pointer (init. to lag DMA destination - address word). After the required number of samples is transferred, - the CODEC output task advances sub_list_ptr->strm_buf_ptr past the samples - consumed. - */ - - /* Init. 0000:0010 for SDout - 0060:0010 for SDout2 - 0080:0010 for SDout3 - hi: Base IO address of FIFO to which - the left-channel samples are to - be written. - lo: Displacement for the base IO - address for left-channel to obtain - the base IO address for the FIFO - to which the right-channel samples - are to be written. - */ - ___DSP_DUAL_16BIT_ALLOC( - left_chan_base_IO_addr, - right_chan_IO_disp - ) - - - /* Init: 0x0080:0004 for non-AC-97 - Init: 0x0080:0000 for AC-97 - hi: Exponential volume change rate - for input stream - lo: Positive shift count to shift the - 16-bit input sample to obtain the - 32-bit output word - */ - ___DSP_DUAL_16BIT_ALLOC( - CO_scale_shift_count, - CO_exp_vol_change_rate - ) - - /* Pointer to SCB at end of input chain */ - ___DSP_DUAL_16BIT_ALLOC( - reserved, - last_sub_ptr - ) -}; - -/* SCB for CODEC input algorithm */ -struct dsp_codec_input_scb { - /* First 13 dwords from generic_scb_t */ - struct dsp_basic_dma_req basic_req; /* Optional */ - struct dsp_scatter_gather_ext sg_ext; /* Optional */ - ___DSP_DUAL_16BIT_ALLOC( - next_scb, /* REQUIRED */ - sub_list_ptr /* REQUIRED */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - entry_point, /* REQUIRED */ - this_spb /* REQUIRED */ - ) - - u32 strm_rs_config; /* REQUIRED */ - u32 strm_buf_ptr; /* REQUIRED */ - - /* NOTE: The CODEC input task reads samples from the hardware FIFO - sublist at the DMA source address word (sub_list_ptr->basic_req.saw). - After the required number of samples is transferred, the CODEC - output task advances sub_list_ptr->basic_req.saw past the samples - consumed. SPuD must initialize the sub_list_ptr->basic_req.saw - to point half-way around from the initial sub_list_ptr->strm_nuf_ptr - to allow for lag/lead. - */ - - /* Init. 0000:0010 for SDout - 0060:0010 for SDout2 - 0080:0010 for SDout3 - hi: Base IO address of FIFO to which - the left-channel samples are to - be written. - lo: Displacement for the base IO - address for left-channel to obtain - the base IO address for the FIFO - to which the right-channel samples - are to be written. - */ - ___DSP_DUAL_16BIT_ALLOC( - rightChanINdisp, - left_chan_base_IN_addr - ) - /* Init. ?:fffc - lo: Negative shift count to shift the - 32-bit input dword to obtain the - 16-bit sample msb-aligned (count - is negative to shift left) - */ - ___DSP_DUAL_16BIT_ALLOC( - scaleShiftCount, - reserver1 - ) - - u32 reserved2; -}; - - -struct dsp_pcm_serial_input_scb { - /* First 13 dwords from generic_scb_t */ - struct dsp_basic_dma_req basic_req; /* Optional */ - struct dsp_scatter_gather_ext sg_ext; /* Optional */ - ___DSP_DUAL_16BIT_ALLOC( - next_scb, /* REQUIRED */ - sub_list_ptr /* REQUIRED */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - entry_point, /* REQUIRED */ - this_spb /* REQUIRED */ - ) - - u32 strm_buf_ptr; /* REQUIRED */ - u32 strm_rs_config; /* REQUIRED */ - - /* Init. Ptr to CODEC input SCB - hi: Pointer to the SCB containing the - input buffer to which CODEC input - samples are written - lo: Flag indicating the link to the CODEC - input task is to be initialized - */ - ___DSP_DUAL_16BIT_ALLOC( - init_codec_input_link, - codec_input_buf_scb - ) - - /* Initialized by the host (host updates target volumes) */ - struct dsp_volume_control psi_vol_ctrl; - -}; - -struct dsp_src_task_scb { - ___DSP_DUAL_16BIT_ALLOC( - frames_left_in_gof, - gofs_left_in_sec - ) - - ___DSP_DUAL_16BIT_ALLOC( - const2_thirds, - num_extra_tnput_samples - ) - - ___DSP_DUAL_16BIT_ALLOC( - cor_per_gof, - correction_per_sec - ) - - ___DSP_DUAL_16BIT_ALLOC( - output_buf_producer_ptr, - junk_DMA_MID - ) - - ___DSP_DUAL_16BIT_ALLOC( - gof_length, - gofs_per_sec - ) - - u32 input_buf_strm_config; - - ___DSP_DUAL_16BIT_ALLOC( - reserved_for_SRC_use, - input_buf_consumer_ptr - ) - - u32 accum_phi; - - ___DSP_DUAL_16BIT_ALLOC( - exp_src_vol_change_rate, - input_buf_producer_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - src_next_scb, - src_sub_list_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - src_entry_point, - src_this_sbp - ) - - u32 src_strm_rs_config; - u32 src_strm_buf_ptr; - - u32 phiIncr6int_26frac; - - struct dsp_volume_control src_vol_ctrl; -}; - -struct dsp_decimate_by_pow2_scb { - /* decimationFactor = 2, 4, or 8 (larger factors waste too much memory - when compared to cascading decimators) - */ - ___DSP_DUAL_16BIT_ALLOC( - dec2_coef_base_ptr, - dec2_coef_increment - ) - - /* coefIncrement = 128 / decimationFactor (for our ROM filter) - coefBasePtr = 0x8000 (for our ROM filter) - */ - ___DSP_DUAL_16BIT_ALLOC( - dec2_in_samples_per_out_triplet, - dec2_extra_in_samples - ) - /* extraInSamples: # of accumulated, unused input samples (init. to 0) - inSamplesPerOutTriplet = 3 * decimationFactor - */ - - ___DSP_DUAL_16BIT_ALLOC( - dec2_const2_thirds, - dec2_half_num_taps_mp5 - ) - /* halfNumTapsM5: (1/2 number of taps in decimation filter) minus 5 - const2thirds: constant 2/3 in 16Q0 format (sign.15) - */ - - ___DSP_DUAL_16BIT_ALLOC( - dec2_output_buf_producer_ptr, - dec2_junkdma_mid - ) - - u32 dec2_reserved2; - - u32 dec2_input_nuf_strm_config; - /* inputBufStrmConfig: rsConfig for the input buffer to the decimator - (buffer size = decimationFactor * 32 dwords) - */ - - ___DSP_DUAL_16BIT_ALLOC( - dec2_phi_incr, - dec2_input_buf_consumer_ptr - ) - /* inputBufConsumerPtr: Input buffer read pointer (into SRC filter) - phiIncr = decimationFactor * 4 - */ - - u32 dec2_reserved3; - - ___DSP_DUAL_16BIT_ALLOC( - dec2_exp_vol_change_rate, - dec2_input_buf_producer_ptr - ) - /* inputBufProducerPtr: Input buffer write pointer - expVolChangeRate: Exponential volume change rate for possible - future mixer on input streams - */ - - ___DSP_DUAL_16BIT_ALLOC( - dec2_next_scb, - dec2_sub_list_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - dec2_entry_point, - dec2_this_spb - ) - - u32 dec2_strm_rs_config; - u32 dec2_strm_buf_ptr; - - u32 dec2_reserved4; - - struct dsp_volume_control dec2_vol_ctrl; /* Not used! */ -}; - -struct dsp_vari_decimate_scb { - ___DSP_DUAL_16BIT_ALLOC( - vdec_frames_left_in_gof, - vdec_gofs_left_in_sec - ) - - ___DSP_DUAL_16BIT_ALLOC( - vdec_const2_thirds, - vdec_extra_in_samples - ) - /* extraInSamples: # of accumulated, unused input samples (init. to 0) - const2thirds: constant 2/3 in 16Q0 format (sign.15) */ - - ___DSP_DUAL_16BIT_ALLOC( - vdec_cor_per_gof, - vdec_correction_per_sec - ) - - ___DSP_DUAL_16BIT_ALLOC( - vdec_output_buf_producer_ptr, - vdec_input_buf_consumer_ptr - ) - /* inputBufConsumerPtr: Input buffer read pointer (into SRC filter) */ - ___DSP_DUAL_16BIT_ALLOC( - vdec_gof_length, - vdec_gofs_per_sec - ) - - u32 vdec_input_buf_strm_config; - /* inputBufStrmConfig: rsConfig for the input buffer to the decimator - (buffer size = 64 dwords) */ - u32 vdec_coef_increment; - /* coefIncrement = - 128.0 / decimationFactor (as a 32Q15 number) */ - - u32 vdec_accumphi; - /* accumPhi: accumulated fractional phase increment (6.26) */ - - ___DSP_DUAL_16BIT_ALLOC( - vdec_exp_vol_change_rate, - vdec_input_buf_producer_ptr - ) - /* inputBufProducerPtr: Input buffer write pointer - expVolChangeRate: Exponential volume change rate for possible - future mixer on input streams */ - - ___DSP_DUAL_16BIT_ALLOC( - vdec_next_scb, - vdec_sub_list_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - vdec_entry_point, - vdec_this_spb - ) - - u32 vdec_strm_rs_config; - u32 vdec_strm_buf_ptr; - - u32 vdec_phi_incr_6int_26frac; - - struct dsp_volume_control vdec_vol_ctrl; -}; - - -/* SCB for MIX_TO_OSTREAM algorithm family */ -struct dsp_mix2_ostream_scb { - /* First 13 dwords from generic_scb_t */ - struct dsp_basic_dma_req basic_req; /* Optional */ - struct dsp_scatter_gather_ext sg_ext; /* Optional */ - ___DSP_DUAL_16BIT_ALLOC( - next_scb, /* REQUIRED */ - sub_list_ptr /* REQUIRED */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - entry_point, /* REQUIRED */ - this_spb /* REQUIRED */ - ) - - u32 strm_rs_config; /* REQUIRED */ - u32 strm_buf_ptr; /* REQUIRED */ - - - /* hi: Number of mixed-down input triplets - computed since start of group - lo: Number of frames remaining to be - processed in the current group of - frames - */ - ___DSP_DUAL_16BIT_ALLOC( - frames_left_in_group, - accum_input_triplets - ) - - /* hi: Exponential volume change rate - for mixer on input streams - lo: Number of frames in the group - */ - ___DSP_DUAL_16BIT_ALLOC( - frame_group_length, - exp_vol_change_rate - ) - - ___DSP_DUAL_16BIT_ALLOC( - const_FFFF, - const_zero - ) -}; - - -/* SCB for S16_MIX algorithm */ -struct dsp_mix_only_scb { - /* First 13 dwords from generic_scb_t */ - struct dsp_basic_dma_req basic_req; /* Optional */ - struct dsp_scatter_gather_ext sg_ext; /* Optional */ - ___DSP_DUAL_16BIT_ALLOC( - next_scb, /* REQUIRED */ - sub_list_ptr /* REQUIRED */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - entry_point, /* REQUIRED */ - this_spb /* REQUIRED */ - ) - - u32 strm_rs_config; /* REQUIRED */ - u32 strm_buf_ptr; /* REQUIRED */ - - u32 reserved; - struct dsp_volume_control vol_ctrl; -}; - -/* SCB for the async. CODEC input algorithm */ -struct dsp_async_codec_input_scb { - u32 io_free2; - - u32 io_current_total; - u32 io_previous_total; - - u16 io_count; - u16 io_count_limit; - - u16 o_fifo_base_addr; - u16 ost_mo_format; - /* 1 = stereo; 0 = mono - xxx for ASER 1 (not allowed); 118 for ASER2 */ - - u32 ostrm_rs_config; - u32 ostrm_buf_ptr; - - ___DSP_DUAL_16BIT_ALLOC( - io_sclks_per_lr_clk, - io_io_enable - ) - - u32 io_free4; - - ___DSP_DUAL_16BIT_ALLOC( - io_next_scb, - io_sub_list_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - io_entry_point, - io_this_spb - ) - - u32 istrm_rs_config; - u32 istrm_buf_ptr; - - /* Init. 0000:8042: for ASER1 - 0000:8044: for ASER2 */ - ___DSP_DUAL_16BIT_ALLOC( - io_stat_reg_addr, - iofifo_pointer - ) - - /* Init 1 stero:100 ASER1 - Init 0 mono:110 ASER2 - */ - ___DSP_DUAL_16BIT_ALLOC( - ififo_base_addr, - ist_mo_format - ) - - u32 i_free; -}; - - -/* SCB for the SP/DIF CODEC input and output */ -struct dsp_spdifiscb { - ___DSP_DUAL_16BIT_ALLOC( - status_ptr, - status_start_ptr - ) - - u32 current_total; - u32 previous_total; - - ___DSP_DUAL_16BIT_ALLOC( - count, - count_limit - ) - - u32 status_data; - - ___DSP_DUAL_16BIT_ALLOC( - status, - free4 - ) - - u32 free3; - - ___DSP_DUAL_16BIT_ALLOC( - free2, - bit_count - ) - - u32 temp_status; - - ___DSP_DUAL_16BIT_ALLOC( - next_SCB, - sub_list_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - entry_point, - this_spb - ) - - u32 strm_rs_config; - u32 strm_buf_ptr; - - ___DSP_DUAL_16BIT_ALLOC( - stat_reg_addr, - fifo_pointer - ) - - ___DSP_DUAL_16BIT_ALLOC( - fifo_base_addr, - st_mo_format - ) - - u32 free1; -}; - - -/* SCB for the SP/DIF CODEC input and output */ -struct dsp_spdifoscb { - - u32 free2; - - u32 free3[4]; - - /* Need to be here for compatibility with AsynchFGTxCode */ - u32 strm_rs_config; - - u32 strm_buf_ptr; - - ___DSP_DUAL_16BIT_ALLOC( - status, - free5 - ) - - u32 free4; - - ___DSP_DUAL_16BIT_ALLOC( - next_scb, - sub_list_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - entry_point, - this_spb - ) - - u32 free6[2]; - - ___DSP_DUAL_16BIT_ALLOC( - stat_reg_addr, - fifo_pointer - ) - - ___DSP_DUAL_16BIT_ALLOC( - fifo_base_addr, - st_mo_format - ) - - u32 free1; -}; - - -struct dsp_asynch_fg_rx_scb { - ___DSP_DUAL_16BIT_ALLOC( - bot_buf_mask, - buf_Mask - ) - - ___DSP_DUAL_16BIT_ALLOC( - max, - min - ) - - ___DSP_DUAL_16BIT_ALLOC( - old_producer_pointer, - hfg_scb_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - delta, - adjust_count - ) - - u32 unused2[5]; - - ___DSP_DUAL_16BIT_ALLOC( - sibling_ptr, - child_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - code_ptr, - this_ptr - ) - - u32 strm_rs_config; - - u32 strm_buf_ptr; - - u32 unused_phi_incr; - - ___DSP_DUAL_16BIT_ALLOC( - right_targ, - left_targ - ) - - ___DSP_DUAL_16BIT_ALLOC( - right_vol, - left_vol - ) -}; - - -struct dsp_asynch_fg_tx_scb { - ___DSP_DUAL_16BIT_ALLOC( - not_buf_mask, - buf_mask - ) - - ___DSP_DUAL_16BIT_ALLOC( - max, - min - ) - - ___DSP_DUAL_16BIT_ALLOC( - unused1, - hfg_scb_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - delta, - adjust_count - ) - - u32 accum_phi; - - ___DSP_DUAL_16BIT_ALLOC( - unused2, - const_one_third - ) - - u32 unused3[3]; - - ___DSP_DUAL_16BIT_ALLOC( - sibling_ptr, - child_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - codePtr, - this_ptr - ) - - u32 strm_rs_config; - - u32 strm_buf_ptr; - - u32 phi_incr; - - ___DSP_DUAL_16BIT_ALLOC( - unused_right_targ, - unused_left_targ - ) - - ___DSP_DUAL_16BIT_ALLOC( - unused_right_vol, - unused_left_vol - ) -}; - - -struct dsp_output_snoop_scb { - /* First 13 dwords from generic_scb_t */ - struct dsp_basic_dma_req basic_req; /* Optional */ - struct dsp_scatter_gather_ext sg_ext; /* Optional */ - ___DSP_DUAL_16BIT_ALLOC( - next_scb, /* REQUIRED */ - sub_list_ptr /* REQUIRED */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - entry_point, /* REQUIRED */ - this_spb /* REQUIRED */ - ) - - u32 strm_rs_config; /* REQUIRED */ - u32 strm_buf_ptr; /* REQUIRED */ - - ___DSP_DUAL_16BIT_ALLOC( - init_snoop_input_link, - snoop_child_input_scb - ) - - u32 snoop_input_buf_ptr; - - ___DSP_DUAL_16BIT_ALLOC( - reserved, - input_scb - ) -}; - -struct dsp_spio_write_scb { - ___DSP_DUAL_16BIT_ALLOC( - address1, - address2 - ) - - u32 data1; - - u32 data2; - - ___DSP_DUAL_16BIT_ALLOC( - address3, - address4 - ) - - u32 data3; - - u32 data4; - - ___DSP_DUAL_16BIT_ALLOC( - unused1, - data_ptr - ) - - u32 unused2[2]; - - ___DSP_DUAL_16BIT_ALLOC( - sibling_ptr, - child_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - entry_point, - this_ptr - ) - - u32 unused3[5]; -}; - -struct dsp_magic_snoop_task { - u32 i0; - u32 i1; - - u32 strm_buf_ptr1; - - u16 i2; - u16 snoop_scb; - - u32 i3; - u32 i4; - u32 i5; - u32 i6; - - u32 i7; - - ___DSP_DUAL_16BIT_ALLOC( - next_scb, - sub_list_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - entry_point, - this_ptr - ) - - u32 strm_buf_config; - u32 strm_buf_ptr2; - - u32 i8; - - struct dsp_volume_control vdec_vol_ctrl; -}; - - -struct dsp_filter_scb { - ___DSP_DUAL_16BIT_ALLOC( - a0_right, /* 0x00 */ - a0_left - ) - ___DSP_DUAL_16BIT_ALLOC( - a1_right, /* 0x01 */ - a1_left - ) - ___DSP_DUAL_16BIT_ALLOC( - a2_right, /* 0x02 */ - a2_left - ) - ___DSP_DUAL_16BIT_ALLOC( - output_buf_ptr, /* 0x03 */ - init - ) - - ___DSP_DUAL_16BIT_ALLOC( - filter_unused3, /* 0x04 */ - filter_unused2 - ) - - u32 prev_sample_output1; /* 0x05 */ - u32 prev_sample_output2; /* 0x06 */ - u32 prev_sample_input1; /* 0x07 */ - u32 prev_sample_input2; /* 0x08 */ - - ___DSP_DUAL_16BIT_ALLOC( - next_scb_ptr, /* 0x09 */ - sub_list_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - entry_point, /* 0x0A */ - spb_ptr - ) - - u32 strm_rs_config; /* 0x0B */ - u32 strm_buf_ptr; /* 0x0C */ - - ___DSP_DUAL_16BIT_ALLOC( - b0_right, /* 0x0D */ - b0_left - ) - ___DSP_DUAL_16BIT_ALLOC( - b1_right, /* 0x0E */ - b1_left - ) - ___DSP_DUAL_16BIT_ALLOC( - b2_right, /* 0x0F */ - b2_left - ) -}; -#endif /* __DSP_SCB_TYPES_H__ */ diff --git a/include/sound/cs46xx_dsp_spos.h b/include/sound/cs46xx_dsp_spos.h deleted file mode 100644 index 8008c59288a6..000000000000 --- a/include/sound/cs46xx_dsp_spos.h +++ /dev/null @@ -1,234 +0,0 @@ -/* - * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards - * Copyright (c) by 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 - * - */ - -#ifndef __CS46XX_DSP_SPOS_H__ -#define __CS46XX_DSP_SPOS_H__ - -#include "cs46xx_dsp_scb_types.h" -#include "cs46xx_dsp_task_types.h" - -#define SYMBOL_CONSTANT 0x0 -#define SYMBOL_SAMPLE 0x1 -#define SYMBOL_PARAMETER 0x2 -#define SYMBOL_CODE 0x3 - -#define SEGTYPE_SP_PROGRAM 0x00000001 -#define SEGTYPE_SP_PARAMETER 0x00000002 -#define SEGTYPE_SP_SAMPLE 0x00000003 -#define SEGTYPE_SP_COEFFICIENT 0x00000004 - -#define DSP_SPOS_UU 0x0deadul /* unused */ -#define DSP_SPOS_DC 0x0badul /* don't care */ -#define DSP_SPOS_DC_DC 0x0bad0badul /* don't care */ -#define DSP_SPOS_UUUU 0xdeadc0edul /* unused */ -#define DSP_SPOS_UUHI 0xdeadul -#define DSP_SPOS_UULO 0xc0edul -#define DSP_SPOS_DCDC 0x0badf1d0ul /* don't care */ -#define DSP_SPOS_DCDCHI 0x0badul -#define DSP_SPOS_DCDCLO 0xf1d0ul - -#define DSP_MAX_TASK_NAME 60 -#define DSP_MAX_SYMBOL_NAME 100 -#define DSP_MAX_SCB_NAME 60 -#define DSP_MAX_SCB_DESC 200 -#define DSP_MAX_TASK_DESC 50 - -#define DSP_MAX_PCM_CHANNELS 32 -#define DSP_MAX_SRC_NR 14 - -#define DSP_PCM_MAIN_CHANNEL 1 -#define DSP_PCM_REAR_CHANNEL 2 -#define DSP_PCM_CENTER_LFE_CHANNEL 3 -#define DSP_PCM_S71_CHANNEL 4 /* surround 7.1 */ -#define DSP_IEC958_CHANNEL 5 - -#define DSP_SPDIF_STATUS_OUTPUT_ENABLED 1 -#define DSP_SPDIF_STATUS_PLAYBACK_OPEN 2 -#define DSP_SPDIF_STATUS_HW_ENABLED 4 -#define DSP_SPDIF_STATUS_INPUT_CTRL_ENABLED 8 - -struct dsp_symbol_entry { - u32 address; - char symbol_name[DSP_MAX_SYMBOL_NAME]; - int symbol_type; - - /* initialized by driver */ - struct dsp_module_desc * module; - int deleted; -}; - -struct dsp_symbol_desc { - int nsymbols; - - struct dsp_symbol_entry *symbols; - - /* initialized by driver */ - int highest_frag_index; -}; - -struct dsp_segment_desc { - int segment_type; - u32 offset; - u32 size; - u32 * data; -}; - -struct dsp_module_desc { - char * module_name; - struct dsp_symbol_desc symbol_table; - int nsegments; - struct dsp_segment_desc * segments; - - /* initialized by driver */ - u32 overlay_begin_address; - u32 load_address; - int nfixups; -}; - -struct dsp_scb_descriptor { - char scb_name[DSP_MAX_SCB_NAME]; - u32 address; - int index; - u32 *data; - - struct dsp_scb_descriptor * sub_list_ptr; - struct dsp_scb_descriptor * next_scb_ptr; - struct dsp_scb_descriptor * parent_scb_ptr; - - struct dsp_symbol_entry * task_entry; - struct dsp_symbol_entry * scb_symbol; - - struct snd_info_entry *proc_info; - int ref_count; - - u16 volume[2]; - unsigned int deleted :1; - unsigned int updated :1; - unsigned int volume_set :1; -}; - -struct dsp_task_descriptor { - char task_name[DSP_MAX_TASK_NAME]; - int size; - u32 address; - int index; - u32 *data; -}; - -struct dsp_pcm_channel_descriptor { - int active; - int src_slot; - int pcm_slot; - u32 sample_rate; - u32 unlinked; - struct dsp_scb_descriptor * pcm_reader_scb; - struct dsp_scb_descriptor * src_scb; - struct dsp_scb_descriptor * mixer_scb; - - void * private_data; -}; - -struct dsp_spos_instance { - struct dsp_symbol_desc symbol_table; /* currently available loaded symbols in SP */ - - int nmodules; - struct dsp_module_desc * modules; /* modules loaded into SP */ - - struct dsp_segment_desc code; - - /* Main PCM playback mixer */ - struct dsp_scb_descriptor * master_mix_scb; - u16 dac_volume_right; - u16 dac_volume_left; - - /* Rear/surround PCM playback mixer */ - struct dsp_scb_descriptor * rear_mix_scb; - - /* Center/LFE mixer */ - struct dsp_scb_descriptor * center_lfe_mix_scb; - - int npcm_channels; - int nsrc_scb; - struct dsp_pcm_channel_descriptor pcm_channels[DSP_MAX_PCM_CHANNELS]; - int src_scb_slots[DSP_MAX_SRC_NR]; - - /* cache this symbols */ - struct dsp_symbol_entry * null_algorithm; /* used by PCMreaderSCB's */ - struct dsp_symbol_entry * s16_up; /* used by SRCtaskSCB's */ - - /* proc fs */ - struct snd_card *snd_card; - struct snd_info_entry * proc_dsp_dir; - struct snd_info_entry * proc_sym_info_entry; - struct snd_info_entry * proc_modules_info_entry; - struct snd_info_entry * proc_parameter_dump_info_entry; - struct snd_info_entry * proc_sample_dump_info_entry; - - /* SCB's descriptors */ - int nscb; - int scb_highest_frag_index; - struct dsp_scb_descriptor scbs[DSP_MAX_SCB_DESC]; - struct snd_info_entry * proc_scb_info_entry; - struct dsp_scb_descriptor * the_null_scb; - - /* Task's descriptors */ - int ntask; - struct dsp_task_descriptor tasks[DSP_MAX_TASK_DESC]; - struct snd_info_entry * proc_task_info_entry; - - /* SPDIF status */ - int spdif_status_out; - int spdif_status_in; - u16 spdif_input_volume_right; - u16 spdif_input_volume_left; - /* spdif channel status, - left right and user validity bits */ - unsigned int spdif_csuv_default; - unsigned int spdif_csuv_stream; - - /* SPDIF input sample rate converter */ - struct dsp_scb_descriptor * spdif_in_src; - /* SPDIF input asynch. receiver */ - struct dsp_scb_descriptor * asynch_rx_scb; - - /* Capture record mixer SCB */ - struct dsp_scb_descriptor * record_mixer_scb; - - /* CODEC input SCB */ - struct dsp_scb_descriptor * codec_in_scb; - - /* reference snooper */ - struct dsp_scb_descriptor * ref_snoop_scb; - - /* SPDIF output PCM reference */ - struct dsp_scb_descriptor * spdif_pcm_input_scb; - - /* asynch TX task */ - struct dsp_scb_descriptor * asynch_tx_scb; - - /* record sources */ - struct dsp_scb_descriptor * pcm_input; - struct dsp_scb_descriptor * adc_input; - - int spdif_in_sample_rate; -}; - -#endif /* __DSP_SPOS_H__ */ diff --git a/include/sound/cs46xx_dsp_task_types.h b/include/sound/cs46xx_dsp_task_types.h deleted file mode 100644 index 5cf920bfda27..000000000000 --- a/include/sound/cs46xx_dsp_task_types.h +++ /dev/null @@ -1,252 +0,0 @@ -/* - * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards - * Copyright (c) by 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 - * - * - * NOTE: comments are copy/paste from cwcemb80.lst - * provided by Tom Woller at Cirrus (my only - * documentation about the SP OS running inside - * the DSP) - */ - -#ifndef __CS46XX_DSP_TASK_TYPES_H__ -#define __CS46XX_DSP_TASK_TYPES_H__ - -#include "cs46xx_dsp_scb_types.h" - -/********************************************************************************************* -Example hierarchy of stream control blocks in the SP - -hfgTree -Ptr____Call (c) - \ - -------+------ ------------- ------------- ------------- ----- -| SBlaster IF |______\| Foreground |___\| Middlegr'nd |___\| Background |___\| Nul | -| |Goto /| tree header |g /| tree header |g /| tree header |g /| SCB |r - -------------- (g) ------------- ------------- ------------- ----- - |c |c |c |c - | | | | - \/ ------------- ------------- ------------- - | Foreground |_\ | Middlegr'nd |_\ | Background |_\ - | tree |g/ | tree |g/ | tree |g/ - ------------- ------------- ------------- - |c |c |c - | | | - \/ \/ \/ - -*********************************************************************************************/ - -#define HFG_FIRST_EXECUTE_MODE 0x0001 -#define HFG_FIRST_EXECUTE_MODE_BIT 0 -#define HFG_CONTEXT_SWITCH_MODE 0x0002 -#define HFG_CONTEXT_SWITCH_MODE_BIT 1 - -#define MAX_FG_STACK_SIZE 32 /* THESE NEED TO BE COMPUTED PROPERLY */ -#define MAX_MG_STACK_SIZE 16 -#define MAX_BG_STACK_SIZE 9 -#define MAX_HFG_STACK_SIZE 4 - -#define SLEEP_ACTIVE_INCREMENT 0 /* Enable task tree thread to go to sleep - This should only ever be used on the Background thread */ -#define STANDARD_ACTIVE_INCREMENT 1 /* Task tree thread normal operation */ -#define SUSPEND_ACTIVE_INCREMENT 2 /* Cause execution to suspend in the task tree thread - This should only ever be used on the Background thread */ - -#define HOSTFLAGS_DISABLE_BG_SLEEP 0 /* Host-controlled flag that determines whether we go to sleep - at the end of BG */ - -/* Minimal context save area for Hyper Forground */ -struct dsp_hf_save_area { - u32 r10_save; - u32 r54_save; - u32 r98_save; - - ___DSP_DUAL_16BIT_ALLOC( - status_save, - ind_save - ) - - ___DSP_DUAL_16BIT_ALLOC( - rci1_save, - rci0_save - ) - - u32 r32_save; - u32 r76_save; - u32 rsd2_save; - - ___DSP_DUAL_16BIT_ALLOC( - rsi2_save, /* See TaskTreeParameterBlock for - remainder of registers */ - rsa2Save - ) - /* saved as part of HFG context */ -}; - - -/* Task link data structure */ -struct dsp_tree_link { - ___DSP_DUAL_16BIT_ALLOC( - /* Pointer to sibling task control block */ - next_scb, - /* Pointer to child task control block */ - sub_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - /* Pointer to code entry point */ - entry_point, - /* Pointer to local data */ - this_spb - ) -}; - - -struct dsp_task_tree_data { - ___DSP_DUAL_16BIT_ALLOC( - /* Initial tock count; controls task tree execution rate */ - tock_count_limit, - /* Tock down counter */ - tock_count - ) - - /* Add to ActiveCount when TockCountLimit reached: - Subtract on task tree termination */ - ___DSP_DUAL_16BIT_ALLOC( - active_tncrement, - /* Number of pending activations for task tree */ - active_count - ) - - ___DSP_DUAL_16BIT_ALLOC( - /* BitNumber to enable modification of correct bit in ActiveTaskFlags */ - active_bit, - /* Pointer to OS location for indicating current activity on task level */ - active_task_flags_ptr - ) - - /* Data structure for controlling movement of memory blocks:- - currently unused */ - ___DSP_DUAL_16BIT_ALLOC( - mem_upd_ptr, - /* Data structure for controlling synchronous link update */ - link_upd_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - /* Save area for remainder of full context. */ - save_area, - /* Address of start of local stack for data storage */ - data_stack_base_ptr - ) - -}; - - -struct dsp_interval_timer_data -{ - /* These data items have the same relative locations to those */ - ___DSP_DUAL_16BIT_ALLOC( - interval_timer_period, - itd_unused - ) - - /* used for this data in the SPOS control block for SPOS 1.0 */ - ___DSP_DUAL_16BIT_ALLOC( - num_FG_ticks_this_interval, - num_intervals - ) -}; - - -/* This structure contains extra storage for the task tree - Currently, this additional data is related only to a full context save */ -struct dsp_task_tree_context_block { - /* Up to 10 values are saved onto the stack. 8 for the task tree, 1 for - The access to the context switch (call or interrupt), and 1 spare that - users should never use. This last may be required by the system */ - ___DSP_DUAL_16BIT_ALLOC( - stack1, - stack0 - ) - ___DSP_DUAL_16BIT_ALLOC( - stack3, - stack2 - ) - ___DSP_DUAL_16BIT_ALLOC( - stack5, - stack4 - ) - ___DSP_DUAL_16BIT_ALLOC( - stack7, - stack6 - ) - ___DSP_DUAL_16BIT_ALLOC( - stack9, - stack8 - ) - - u32 saverfe; - - /* Value may be overwriten by stack save algorithm. - Retain the size of the stack data saved here if used */ - ___DSP_DUAL_16BIT_ALLOC( - reserved1, - stack_size - ) - u32 saverba; /* (HFG) */ - u32 saverdc; - u32 savers_config_23; /* (HFG) */ - u32 savers_DMA23; /* (HFG) */ - u32 saversa0; - u32 saversi0; - u32 saversa1; - u32 saversi1; - u32 saversa3; - u32 saversd0; - u32 saversd1; - u32 saversd3; - u32 savers_config01; - u32 savers_DMA01; - u32 saveacc0hl; - u32 saveacc1hl; - u32 saveacc0xacc1x; - u32 saveacc2hl; - u32 saveacc3hl; - u32 saveacc2xacc3x; - u32 saveaux0hl; - u32 saveaux1hl; - u32 saveaux0xaux1x; - u32 saveaux2hl; - u32 saveaux3hl; - u32 saveaux2xaux3x; - u32 savershouthl; - u32 savershoutxmacmode; -}; - - -struct dsp_task_tree_control_block { - struct dsp_hf_save_area context; - struct dsp_tree_link links; - struct dsp_task_tree_data data; - struct dsp_task_tree_context_block context_blk; - struct dsp_interval_timer_data int_timer; -}; - - -#endif /* __DSP_TASK_TYPES_H__ */ diff --git a/include/sound/trident.h b/include/sound/trident.h deleted file mode 100644 index 06f0478103db..000000000000 --- a/include/sound/trident.h +++ /dev/null @@ -1,444 +0,0 @@ -#ifndef __SOUND_TRIDENT_H -#define __SOUND_TRIDENT_H - -/* - * audio@tridentmicro.com - * Fri Feb 19 15:55:28 MST 1999 - * Definitions for Trident 4DWave DX/NX chips - * - * - * 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 "pcm.h" -#include "mpu401.h" -#include "ac97_codec.h" -#include "util_mem.h" - -#define TRIDENT_DEVICE_ID_DX ((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_DX) -#define TRIDENT_DEVICE_ID_NX ((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_NX) -#define TRIDENT_DEVICE_ID_SI7018 ((PCI_VENDOR_ID_SI<<16)|PCI_DEVICE_ID_SI_7018) - -#define SNDRV_TRIDENT_VOICE_TYPE_PCM 0 -#define SNDRV_TRIDENT_VOICE_TYPE_SYNTH 1 -#define SNDRV_TRIDENT_VOICE_TYPE_MIDI 2 - -#define SNDRV_TRIDENT_VFLG_RUNNING (1<<0) - -/* TLB code constants */ -#define SNDRV_TRIDENT_PAGE_SIZE 4096 -#define SNDRV_TRIDENT_PAGE_SHIFT 12 -#define SNDRV_TRIDENT_PAGE_MASK ((1<port + (x)) - -#define ID_4DWAVE_DX 0x2000 -#define ID_4DWAVE_NX 0x2001 - -/* Bank definitions */ - -#define T4D_BANK_A 0 -#define T4D_BANK_B 1 -#define T4D_NUM_BANKS 2 - -/* Register definitions */ - -/* Global registers */ - -enum global_control_bits { - CHANNEL_IDX = 0x0000003f, - OVERRUN_IE = 0x00000400, /* interrupt enable: capture overrun */ - UNDERRUN_IE = 0x00000800, /* interrupt enable: playback underrun */ - ENDLP_IE = 0x00001000, /* interrupt enable: end of buffer */ - MIDLP_IE = 0x00002000, /* interrupt enable: middle buffer */ - ETOG_IE = 0x00004000, /* interrupt enable: envelope toggling */ - EDROP_IE = 0x00008000, /* interrupt enable: envelope drop */ - BANK_B_EN = 0x00010000, /* SiS: enable bank B (64 channels) */ - PCMIN_B_MIX = 0x00020000, /* SiS: PCM IN B mixing enable */ - I2S_OUT_ASSIGN = 0x00040000, /* SiS: I2S Out contains surround PCM */ - SPDIF_OUT_ASSIGN= 0x00080000, /* SiS: 0=S/PDIF L/R | 1=PCM Out FIFO */ - MAIN_OUT_ASSIGN = 0x00100000, /* SiS: 0=PCM Out FIFO | 1=MMC Out buffer */ -}; - -enum miscint_bits { - PB_UNDERRUN_IRQ = 0x00000001, REC_OVERRUN_IRQ = 0x00000002, - SB_IRQ = 0x00000004, MPU401_IRQ = 0x00000008, - OPL3_IRQ = 0x00000010, ADDRESS_IRQ = 0x00000020, - ENVELOPE_IRQ = 0x00000040, PB_UNDERRUN = 0x00000100, - REC_OVERRUN = 0x00000200, MIXER_UNDERFLOW = 0x00000400, - MIXER_OVERFLOW = 0x00000800, NX_SB_IRQ_DISABLE = 0x00001000, - ST_TARGET_REACHED = 0x00008000, - PB_24K_MODE = 0x00010000, ST_IRQ_EN = 0x00800000, - ACGPIO_IRQ = 0x01000000 -}; - -/* T2 legacy dma control registers. */ -#define LEGACY_DMAR0 0x00 // ADR0 -#define LEGACY_DMAR4 0x04 // CNT0 -#define LEGACY_DMAR6 0x06 // CNT0 - High bits -#define LEGACY_DMAR11 0x0b // MOD -#define LEGACY_DMAR15 0x0f // MMR - -#define T4D_START_A 0x80 -#define T4D_STOP_A 0x84 -#define T4D_DLY_A 0x88 -#define T4D_SIGN_CSO_A 0x8c -#define T4D_CSPF_A 0x90 -#define T4D_CSPF_B 0xbc -#define T4D_CEBC_A 0x94 -#define T4D_AINT_A 0x98 -#define T4D_AINTEN_A 0x9c -#define T4D_LFO_GC_CIR 0xa0 -#define T4D_MUSICVOL_WAVEVOL 0xa8 -#define T4D_SBDELTA_DELTA_R 0xac -#define T4D_MISCINT 0xb0 -#define T4D_START_B 0xb4 -#define T4D_STOP_B 0xb8 -#define T4D_SBBL_SBCL 0xc0 -#define T4D_SBCTRL_SBE2R_SBDD 0xc4 -#define T4D_STIMER 0xc8 -#define T4D_AINT_B 0xd8 -#define T4D_AINTEN_B 0xdc -#define T4D_RCI 0x70 - -/* MPU-401 UART */ -#define T4D_MPU401_BASE 0x20 -#define T4D_MPUR0 0x20 -#define T4D_MPUR1 0x21 -#define T4D_MPUR2 0x22 -#define T4D_MPUR3 0x23 - -/* S/PDIF Registers */ -#define NX_SPCTRL_SPCSO 0x24 -#define NX_SPLBA 0x28 -#define NX_SPESO 0x2c -#define NX_SPCSTATUS 0x64 - -/* Joystick */ -#define GAMEPORT_GCR 0x30 -#define GAMEPORT_MODE_ADC 0x80 -#define GAMEPORT_LEGACY 0x31 -#define GAMEPORT_AXES 0x34 - -/* NX Specific Registers */ -#define NX_TLBC 0x6c - -/* Channel Registers */ - -#define CH_START 0xe0 - -#define CH_DX_CSO_ALPHA_FMS 0xe0 -#define CH_DX_ESO_DELTA 0xe8 -#define CH_DX_FMC_RVOL_CVOL 0xec - -#define CH_NX_DELTA_CSO 0xe0 -#define CH_NX_DELTA_ESO 0xe8 -#define CH_NX_ALPHA_FMS_FMC_RVOL_CVOL 0xec - -#define CH_LBA 0xe4 -#define CH_GVSEL_PAN_VOL_CTRL_EC 0xf0 -#define CH_EBUF1 0xf4 -#define CH_EBUF2 0xf8 - -/* AC-97 Registers */ - -#define DX_ACR0_AC97_W 0x40 -#define DX_ACR1_AC97_R 0x44 -#define DX_ACR2_AC97_COM_STAT 0x48 - -#define NX_ACR0_AC97_COM_STAT 0x40 -#define NX_ACR1_AC97_W 0x44 -#define NX_ACR2_AC97_R_PRIMARY 0x48 -#define NX_ACR3_AC97_R_SECONDARY 0x4c - -#define SI_AC97_WRITE 0x40 -#define SI_AC97_READ 0x44 -#define SI_SERIAL_INTF_CTRL 0x48 -#define SI_AC97_GPIO 0x4c -#define SI_ASR0 0x50 -#define SI_SPDIF_CS 0x70 -#define SI_GPIO 0x7c - -enum trident_nx_ac97_bits { - /* ACR1-3 */ - NX_AC97_BUSY_WRITE = 0x0800, - NX_AC97_BUSY_READ = 0x0800, - NX_AC97_BUSY_DATA = 0x0400, - NX_AC97_WRITE_SECONDARY = 0x0100, - /* ACR0 */ - NX_AC97_SECONDARY_READY = 0x0040, - NX_AC97_SECONDARY_RECORD = 0x0020, - NX_AC97_SURROUND_OUTPUT = 0x0010, - NX_AC97_PRIMARY_READY = 0x0008, - NX_AC97_PRIMARY_RECORD = 0x0004, - NX_AC97_PCM_OUTPUT = 0x0002, - NX_AC97_WARM_RESET = 0x0001 -}; - -enum trident_dx_ac97_bits { - DX_AC97_BUSY_WRITE = 0x8000, - DX_AC97_BUSY_READ = 0x8000, - DX_AC97_READY = 0x0010, - DX_AC97_RECORD = 0x0008, - DX_AC97_PLAYBACK = 0x0002 -}; - -enum sis7018_ac97_bits { - SI_AC97_BUSY_WRITE = 0x00008000, - SI_AC97_AUDIO_BUSY = 0x00004000, - SI_AC97_MODEM_BUSY = 0x00002000, - SI_AC97_BUSY_READ = 0x00008000, - SI_AC97_SECONDARY = 0x00000080, -}; - -enum serial_intf_ctrl_bits { - WARM_RESET = 0x00000001, - COLD_RESET = 0x00000002, - I2S_CLOCK = 0x00000004, - PCM_SEC_AC97 = 0x00000008, - AC97_DBL_RATE = 0x00000010, - SPDIF_EN = 0x00000020, - I2S_OUTPUT_EN = 0x00000040, - I2S_INPUT_EN = 0x00000080, - PCMIN = 0x00000100, - LINE1IN = 0x00000200, - MICIN = 0x00000400, - LINE2IN = 0x00000800, - HEAD_SET_IN = 0x00001000, - GPIOIN = 0x00002000, - /* 7018 spec says id = 01 but the demo board routed to 10 - SECONDARY_ID= 0x00004000, */ - SECONDARY_ID = 0x00004000, - PCMOUT = 0x00010000, - SURROUT = 0x00020000, - CENTEROUT = 0x00040000, - LFEOUT = 0x00080000, - LINE1OUT = 0x00100000, - LINE2OUT = 0x00200000, - GPIOOUT = 0x00400000, - SI_AC97_PRIMARY_READY = 0x01000000, - SI_AC97_SECONDARY_READY = 0x02000000, - SI_AC97_POWERDOWN = 0x04000000, -}; - -/* PCM defaults */ - -#define T4D_DEFAULT_PCM_VOL 10 /* 0 - 255 */ -#define T4D_DEFAULT_PCM_PAN 0 /* 0 - 127 */ -#define T4D_DEFAULT_PCM_RVOL 127 /* 0 - 127 */ -#define T4D_DEFAULT_PCM_CVOL 127 /* 0 - 127 */ - -struct snd_trident; -struct snd_trident_voice; -struct snd_trident_pcm_mixer; - -struct snd_trident_port { - struct snd_midi_channel_set * chset; - struct snd_trident * trident; - int mode; /* operation mode */ - int client; /* sequencer client number */ - int port; /* sequencer port number */ - unsigned int midi_has_voices: 1; -}; - -struct snd_trident_memblk_arg { - short first_page, last_page; -}; - -struct snd_trident_tlb { - unsigned int * entries; /* 16k-aligned TLB table */ - dma_addr_t entries_dmaaddr; /* 16k-aligned PCI address to TLB table */ - unsigned long * shadow_entries; /* shadow entries with virtual addresses */ - struct snd_dma_buffer buffer; - struct snd_util_memhdr * memhdr; /* page allocation list */ - struct snd_dma_buffer silent_page; -}; - -struct snd_trident_voice { - unsigned int number; - unsigned int use: 1, - pcm: 1, - synth:1, - midi: 1; - unsigned int flags; - unsigned char client; - unsigned char port; - unsigned char index; - - struct snd_trident_sample_ops *sample_ops; - - /* channel parameters */ - unsigned int CSO; /* 24 bits (16 on DX) */ - unsigned int ESO; /* 24 bits (16 on DX) */ - unsigned int LBA; /* 30 bits */ - unsigned short EC; /* 12 bits */ - unsigned short Alpha; /* 12 bits */ - unsigned short Delta; /* 16 bits */ - unsigned short Attribute; /* 16 bits - SiS 7018 */ - unsigned short Vol; /* 12 bits (6.6) */ - unsigned char Pan; /* 7 bits (1.4.2) */ - unsigned char GVSel; /* 1 bit */ - unsigned char RVol; /* 7 bits (5.2) */ - unsigned char CVol; /* 7 bits (5.2) */ - unsigned char FMC; /* 2 bits */ - unsigned char CTRL; /* 4 bits */ - unsigned char FMS; /* 4 bits */ - unsigned char LFO; /* 8 bits */ - - unsigned int negCSO; /* nonzero - use negative CSO */ - - struct snd_util_memblk *memblk; /* memory block if TLB enabled */ - - /* PCM data */ - - struct snd_trident *trident; - struct snd_pcm_substream *substream; - struct snd_trident_voice *extra; /* extra PCM voice (acts as interrupt generator) */ - unsigned int running: 1, - capture: 1, - spdif: 1, - foldback: 1, - isync: 1, - isync2: 1, - isync3: 1; - int foldback_chan; /* foldback subdevice number */ - unsigned int stimer; /* global sample timer (to detect spurious interrupts) */ - unsigned int spurious_threshold; /* spurious threshold */ - unsigned int isync_mark; - unsigned int isync_max; - unsigned int isync_ESO; - - /* --- */ - - void *private_data; - void (*private_free)(struct snd_trident_voice *voice); -}; - -struct snd_4dwave { - int seq_client; - - struct snd_trident_port seq_ports[4]; - struct snd_trident_voice voices[64]; - - int ChanSynthCount; /* number of allocated synth channels */ - int max_size; /* maximum synth memory size in bytes */ - int current_size; /* current allocated synth mem in bytes */ -}; - -struct snd_trident_pcm_mixer { - struct snd_trident_voice *voice; /* active voice */ - unsigned short vol; /* front volume */ - unsigned char pan; /* pan control */ - unsigned char rvol; /* rear volume */ - unsigned char cvol; /* center volume */ - unsigned char pad; -}; - -struct snd_trident { - int irq; - - unsigned int device; /* device ID */ - - unsigned char bDMAStart; - - unsigned long port; - unsigned long midi_port; - - unsigned int spurious_irq_count; - unsigned int spurious_irq_max_delta; - - struct snd_trident_tlb tlb; /* TLB entries for NX cards */ - - unsigned char spdif_ctrl; - unsigned char spdif_pcm_ctrl; - unsigned int spdif_bits; - unsigned int spdif_pcm_bits; - struct snd_kcontrol *spdif_pcm_ctl; /* S/PDIF settings */ - unsigned int ac97_ctrl; - - unsigned int ChanMap[2]; /* allocation map for hardware channels */ - - int ChanPCM; /* max number of PCM channels */ - int ChanPCMcnt; /* actual number of PCM channels */ - - unsigned int ac97_detect: 1; /* 1 = AC97 in detection phase */ - unsigned int in_suspend: 1; /* 1 during suspend/resume */ - - struct snd_4dwave synth; /* synth specific variables */ - - spinlock_t event_lock; - spinlock_t voice_alloc; - - struct snd_dma_device dma_dev; - - struct pci_dev *pci; - struct snd_card *card; - struct snd_pcm *pcm; /* ADC/DAC PCM */ - struct snd_pcm *foldback; /* Foldback PCM */ - struct snd_pcm *spdif; /* SPDIF PCM */ - struct snd_rawmidi *rmidi; - - struct snd_ac97_bus *ac97_bus; - struct snd_ac97 *ac97; - struct snd_ac97 *ac97_sec; - - unsigned int musicvol_wavevol; - struct snd_trident_pcm_mixer pcm_mixer[32]; - struct snd_kcontrol *ctl_vol; /* front volume */ - struct snd_kcontrol *ctl_pan; /* pan */ - struct snd_kcontrol *ctl_rvol; /* rear volume */ - struct snd_kcontrol *ctl_cvol; /* center volume */ - - spinlock_t reg_lock; - - struct gameport *gameport; -}; - -int snd_trident_create(struct snd_card *card, - struct pci_dev *pci, - int pcm_streams, - int pcm_spdif_device, - int max_wavetable_size, - struct snd_trident ** rtrident); -int snd_trident_create_gameport(struct snd_trident *trident); - -int snd_trident_pcm(struct snd_trident * trident, int device, struct snd_pcm **rpcm); -int snd_trident_foldback_pcm(struct snd_trident * trident, int device, struct snd_pcm **rpcm); -int snd_trident_spdif_pcm(struct snd_trident * trident, int device, struct snd_pcm **rpcm); -int snd_trident_attach_synthesizer(struct snd_trident * trident); -struct snd_trident_voice *snd_trident_alloc_voice(struct snd_trident * trident, int type, - int client, int port); -void snd_trident_free_voice(struct snd_trident * trident, struct snd_trident_voice *voice); -void snd_trident_start_voice(struct snd_trident * trident, unsigned int voice); -void snd_trident_stop_voice(struct snd_trident * trident, unsigned int voice); -void snd_trident_write_voice_regs(struct snd_trident * trident, struct snd_trident_voice *voice); -extern const struct dev_pm_ops snd_trident_pm; - -/* TLB memory allocation */ -struct snd_util_memblk *snd_trident_alloc_pages(struct snd_trident *trident, - struct snd_pcm_substream *substream); -int snd_trident_free_pages(struct snd_trident *trident, struct snd_util_memblk *blk); -struct snd_util_memblk *snd_trident_synth_alloc(struct snd_trident *trident, unsigned int size); -int snd_trident_synth_free(struct snd_trident *trident, struct snd_util_memblk *blk); -int snd_trident_synth_copy_from_user(struct snd_trident *trident, struct snd_util_memblk *blk, - int offset, const char __user *data, int size); - -#endif /* __SOUND_TRIDENT_H */ diff --git a/include/sound/ymfpci.h b/include/sound/ymfpci.h deleted file mode 100644 index 238f118de6e1..000000000000 --- a/include/sound/ymfpci.h +++ /dev/null @@ -1,389 +0,0 @@ -#ifndef __SOUND_YMFPCI_H -#define __SOUND_YMFPCI_H - -/* - * Copyright (c) by Jaroslav Kysela - * Definitions for Yahama YMF724/740/744/754 chips - * - * - * 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 "pcm.h" -#include "rawmidi.h" -#include "ac97_codec.h" -#include "timer.h" -#include - -/* - * Direct registers - */ - -#define YMFREG(chip, reg) (chip->port + YDSXGR_##reg) - -#define YDSXGR_INTFLAG 0x0004 -#define YDSXGR_ACTIVITY 0x0006 -#define YDSXGR_GLOBALCTRL 0x0008 -#define YDSXGR_ZVCTRL 0x000A -#define YDSXGR_TIMERCTRL 0x0010 -#define YDSXGR_TIMERCOUNT 0x0012 -#define YDSXGR_SPDIFOUTCTRL 0x0018 -#define YDSXGR_SPDIFOUTSTATUS 0x001C -#define YDSXGR_EEPROMCTRL 0x0020 -#define YDSXGR_SPDIFINCTRL 0x0034 -#define YDSXGR_SPDIFINSTATUS 0x0038 -#define YDSXGR_DSPPROGRAMDL 0x0048 -#define YDSXGR_DLCNTRL 0x004C -#define YDSXGR_GPIOININTFLAG 0x0050 -#define YDSXGR_GPIOININTENABLE 0x0052 -#define YDSXGR_GPIOINSTATUS 0x0054 -#define YDSXGR_GPIOOUTCTRL 0x0056 -#define YDSXGR_GPIOFUNCENABLE 0x0058 -#define YDSXGR_GPIOTYPECONFIG 0x005A -#define YDSXGR_AC97CMDDATA 0x0060 -#define YDSXGR_AC97CMDADR 0x0062 -#define YDSXGR_PRISTATUSDATA 0x0064 -#define YDSXGR_PRISTATUSADR 0x0066 -#define YDSXGR_SECSTATUSDATA 0x0068 -#define YDSXGR_SECSTATUSADR 0x006A -#define YDSXGR_SECCONFIG 0x0070 -#define YDSXGR_LEGACYOUTVOL 0x0080 -#define YDSXGR_LEGACYOUTVOLL 0x0080 -#define YDSXGR_LEGACYOUTVOLR 0x0082 -#define YDSXGR_NATIVEDACOUTVOL 0x0084 -#define YDSXGR_NATIVEDACOUTVOLL 0x0084 -#define YDSXGR_NATIVEDACOUTVOLR 0x0086 -#define YDSXGR_ZVOUTVOL 0x0088 -#define YDSXGR_ZVOUTVOLL 0x0088 -#define YDSXGR_ZVOUTVOLR 0x008A -#define YDSXGR_SECADCOUTVOL 0x008C -#define YDSXGR_SECADCOUTVOLL 0x008C -#define YDSXGR_SECADCOUTVOLR 0x008E -#define YDSXGR_PRIADCOUTVOL 0x0090 -#define YDSXGR_PRIADCOUTVOLL 0x0090 -#define YDSXGR_PRIADCOUTVOLR 0x0092 -#define YDSXGR_LEGACYLOOPVOL 0x0094 -#define YDSXGR_LEGACYLOOPVOLL 0x0094 -#define YDSXGR_LEGACYLOOPVOLR 0x0096 -#define YDSXGR_NATIVEDACLOOPVOL 0x0098 -#define YDSXGR_NATIVEDACLOOPVOLL 0x0098 -#define YDSXGR_NATIVEDACLOOPVOLR 0x009A -#define YDSXGR_ZVLOOPVOL 0x009C -#define YDSXGR_ZVLOOPVOLL 0x009E -#define YDSXGR_ZVLOOPVOLR 0x009E -#define YDSXGR_SECADCLOOPVOL 0x00A0 -#define YDSXGR_SECADCLOOPVOLL 0x00A0 -#define YDSXGR_SECADCLOOPVOLR 0x00A2 -#define YDSXGR_PRIADCLOOPVOL 0x00A4 -#define YDSXGR_PRIADCLOOPVOLL 0x00A4 -#define YDSXGR_PRIADCLOOPVOLR 0x00A6 -#define YDSXGR_NATIVEADCINVOL 0x00A8 -#define YDSXGR_NATIVEADCINVOLL 0x00A8 -#define YDSXGR_NATIVEADCINVOLR 0x00AA -#define YDSXGR_NATIVEDACINVOL 0x00AC -#define YDSXGR_NATIVEDACINVOLL 0x00AC -#define YDSXGR_NATIVEDACINVOLR 0x00AE -#define YDSXGR_BUF441OUTVOL 0x00B0 -#define YDSXGR_BUF441OUTVOLL 0x00B0 -#define YDSXGR_BUF441OUTVOLR 0x00B2 -#define YDSXGR_BUF441LOOPVOL 0x00B4 -#define YDSXGR_BUF441LOOPVOLL 0x00B4 -#define YDSXGR_BUF441LOOPVOLR 0x00B6 -#define YDSXGR_SPDIFOUTVOL 0x00B8 -#define YDSXGR_SPDIFOUTVOLL 0x00B8 -#define YDSXGR_SPDIFOUTVOLR 0x00BA -#define YDSXGR_SPDIFLOOPVOL 0x00BC -#define YDSXGR_SPDIFLOOPVOLL 0x00BC -#define YDSXGR_SPDIFLOOPVOLR 0x00BE -#define YDSXGR_ADCSLOTSR 0x00C0 -#define YDSXGR_RECSLOTSR 0x00C4 -#define YDSXGR_ADCFORMAT 0x00C8 -#define YDSXGR_RECFORMAT 0x00CC -#define YDSXGR_P44SLOTSR 0x00D0 -#define YDSXGR_STATUS 0x0100 -#define YDSXGR_CTRLSELECT 0x0104 -#define YDSXGR_MODE 0x0108 -#define YDSXGR_SAMPLECOUNT 0x010C -#define YDSXGR_NUMOFSAMPLES 0x0110 -#define YDSXGR_CONFIG 0x0114 -#define YDSXGR_PLAYCTRLSIZE 0x0140 -#define YDSXGR_RECCTRLSIZE 0x0144 -#define YDSXGR_EFFCTRLSIZE 0x0148 -#define YDSXGR_WORKSIZE 0x014C -#define YDSXGR_MAPOFREC 0x0150 -#define YDSXGR_MAPOFEFFECT 0x0154 -#define YDSXGR_PLAYCTRLBASE 0x0158 -#define YDSXGR_RECCTRLBASE 0x015C -#define YDSXGR_EFFCTRLBASE 0x0160 -#define YDSXGR_WORKBASE 0x0164 -#define YDSXGR_DSPINSTRAM 0x1000 -#define YDSXGR_CTRLINSTRAM 0x4000 - -#define YDSXG_AC97READCMD 0x8000 -#define YDSXG_AC97WRITECMD 0x0000 - -#define PCIR_DSXG_LEGACY 0x40 -#define PCIR_DSXG_ELEGACY 0x42 -#define PCIR_DSXG_CTRL 0x48 -#define PCIR_DSXG_PWRCTRL1 0x4a -#define PCIR_DSXG_PWRCTRL2 0x4e -#define PCIR_DSXG_FMBASE 0x60 -#define PCIR_DSXG_SBBASE 0x62 -#define PCIR_DSXG_MPU401BASE 0x64 -#define PCIR_DSXG_JOYBASE 0x66 - -#define YDSXG_DSPLENGTH 0x0080 -#define YDSXG_CTRLLENGTH 0x3000 - -#define YDSXG_DEFAULT_WORK_SIZE 0x0400 - -#define YDSXG_PLAYBACK_VOICES 64 -#define YDSXG_CAPTURE_VOICES 2 -#define YDSXG_EFFECT_VOICES 5 - -#define YMFPCI_LEGACY_SBEN (1 << 0) /* soundblaster enable */ -#define YMFPCI_LEGACY_FMEN (1 << 1) /* OPL3 enable */ -#define YMFPCI_LEGACY_JPEN (1 << 2) /* joystick enable */ -#define YMFPCI_LEGACY_MEN (1 << 3) /* MPU401 enable */ -#define YMFPCI_LEGACY_MIEN (1 << 4) /* MPU RX irq enable */ -#define YMFPCI_LEGACY_IOBITS (1 << 5) /* i/o bits range, 0 = 16bit, 1 =10bit */ -#define YMFPCI_LEGACY_SDMA (3 << 6) /* SB DMA select */ -#define YMFPCI_LEGACY_SBIRQ (7 << 8) /* SB IRQ select */ -#define YMFPCI_LEGACY_MPUIRQ (7 << 11) /* MPU IRQ select */ -#define YMFPCI_LEGACY_SIEN (1 << 14) /* serialized IRQ */ -#define YMFPCI_LEGACY_LAD (1 << 15) /* legacy audio disable */ - -#define YMFPCI_LEGACY2_FMIO (3 << 0) /* OPL3 i/o address (724/740) */ -#define YMFPCI_LEGACY2_SBIO (3 << 2) /* SB i/o address (724/740) */ -#define YMFPCI_LEGACY2_MPUIO (3 << 4) /* MPU401 i/o address (724/740) */ -#define YMFPCI_LEGACY2_JSIO (3 << 6) /* joystick i/o address (724/740) */ -#define YMFPCI_LEGACY2_MAIM (1 << 8) /* MPU401 ack intr mask */ -#define YMFPCI_LEGACY2_SMOD (3 << 11) /* SB DMA mode */ -#define YMFPCI_LEGACY2_SBVER (3 << 13) /* SB version select */ -#define YMFPCI_LEGACY2_IMOD (1 << 15) /* legacy IRQ mode */ -/* SIEN:IMOD 0:0 = legacy irq, 0:1 = INTA, 1:0 = serialized IRQ */ - -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) -#define SUPPORT_JOYSTICK -#endif - -/* - * - */ - -struct snd_ymfpci_playback_bank { - u32 format; - u32 loop_default; - u32 base; /* 32-bit address */ - u32 loop_start; /* 32-bit offset */ - u32 loop_end; /* 32-bit offset */ - u32 loop_frac; /* 8-bit fraction - loop_start */ - u32 delta_end; /* pitch delta end */ - u32 lpfK_end; - u32 eg_gain_end; - u32 left_gain_end; - u32 right_gain_end; - u32 eff1_gain_end; - u32 eff2_gain_end; - u32 eff3_gain_end; - u32 lpfQ; - u32 status; - u32 num_of_frames; - u32 loop_count; - u32 start; - u32 start_frac; - u32 delta; - u32 lpfK; - u32 eg_gain; - u32 left_gain; - u32 right_gain; - u32 eff1_gain; - u32 eff2_gain; - u32 eff3_gain; - u32 lpfD1; - u32 lpfD2; - }; - -struct snd_ymfpci_capture_bank { - u32 base; /* 32-bit address */ - u32 loop_end; /* 32-bit offset */ - u32 start; /* 32-bit offset */ - u32 num_of_loops; /* counter */ -}; - -struct snd_ymfpci_effect_bank { - u32 base; /* 32-bit address */ - u32 loop_end; /* 32-bit offset */ - u32 start; /* 32-bit offset */ - u32 temp; -}; - -struct snd_ymfpci_pcm; -struct snd_ymfpci; - -enum snd_ymfpci_voice_type { - YMFPCI_PCM, - YMFPCI_SYNTH, - YMFPCI_MIDI -}; - -struct snd_ymfpci_voice { - struct snd_ymfpci *chip; - int number; - unsigned int use: 1, - pcm: 1, - synth: 1, - midi: 1; - struct snd_ymfpci_playback_bank *bank; - dma_addr_t bank_addr; - void (*interrupt)(struct snd_ymfpci *chip, struct snd_ymfpci_voice *voice); - struct snd_ymfpci_pcm *ypcm; -}; - -enum snd_ymfpci_pcm_type { - PLAYBACK_VOICE, - CAPTURE_REC, - CAPTURE_AC97, - EFFECT_DRY_LEFT, - EFFECT_DRY_RIGHT, - EFFECT_EFF1, - EFFECT_EFF2, - EFFECT_EFF3 -}; - -struct snd_ymfpci_pcm { - struct snd_ymfpci *chip; - enum snd_ymfpci_pcm_type type; - struct snd_pcm_substream *substream; - struct snd_ymfpci_voice *voices[2]; /* playback only */ - unsigned int running: 1, - use_441_slot: 1, - output_front: 1, - output_rear: 1, - swap_rear: 1; - unsigned int update_pcm_vol; - u32 period_size; /* cached from runtime->period_size */ - u32 buffer_size; /* cached from runtime->buffer_size */ - u32 period_pos; - u32 last_pos; - u32 capture_bank_number; - u32 shift; -}; - -struct snd_ymfpci { - int irq; - - unsigned int device_id; /* PCI device ID */ - unsigned char rev; /* PCI revision */ - unsigned long reg_area_phys; - void __iomem *reg_area_virt; - struct resource *res_reg_area; - struct resource *fm_res; - struct resource *mpu_res; - - unsigned short old_legacy_ctrl; -#ifdef SUPPORT_JOYSTICK - struct gameport *gameport; -#endif - - struct snd_dma_buffer work_ptr; - - unsigned int bank_size_playback; - unsigned int bank_size_capture; - unsigned int bank_size_effect; - unsigned int work_size; - - void *bank_base_playback; - void *bank_base_capture; - void *bank_base_effect; - void *work_base; - dma_addr_t bank_base_playback_addr; - dma_addr_t bank_base_capture_addr; - dma_addr_t bank_base_effect_addr; - dma_addr_t work_base_addr; - struct snd_dma_buffer ac3_tmp_base; - - u32 *ctrl_playback; - struct snd_ymfpci_playback_bank *bank_playback[YDSXG_PLAYBACK_VOICES][2]; - struct snd_ymfpci_capture_bank *bank_capture[YDSXG_CAPTURE_VOICES][2]; - struct snd_ymfpci_effect_bank *bank_effect[YDSXG_EFFECT_VOICES][2]; - - int start_count; - - u32 active_bank; - struct snd_ymfpci_voice voices[64]; - int src441_used; - - struct snd_ac97_bus *ac97_bus; - struct snd_ac97 *ac97; - struct snd_rawmidi *rawmidi; - struct snd_timer *timer; - unsigned int timer_ticks; - - struct pci_dev *pci; - struct snd_card *card; - struct snd_pcm *pcm; - struct snd_pcm *pcm2; - struct snd_pcm *pcm_spdif; - struct snd_pcm *pcm_4ch; - struct snd_pcm_substream *capture_substream[YDSXG_CAPTURE_VOICES]; - struct snd_pcm_substream *effect_substream[YDSXG_EFFECT_VOICES]; - struct snd_kcontrol *ctl_vol_recsrc; - struct snd_kcontrol *ctl_vol_adcrec; - struct snd_kcontrol *ctl_vol_spdifrec; - unsigned short spdif_bits, spdif_pcm_bits; - struct snd_kcontrol *spdif_pcm_ctl; - int mode_dup4ch; - int rear_opened; - int spdif_opened; - struct snd_ymfpci_pcm_mixer { - u16 left; - u16 right; - struct snd_kcontrol *ctl; - } pcm_mixer[32]; - - spinlock_t reg_lock; - spinlock_t voice_lock; - wait_queue_head_t interrupt_sleep; - atomic_t interrupt_sleep_count; - struct snd_info_entry *proc_entry; - const struct firmware *dsp_microcode; - const struct firmware *controller_microcode; - -#ifdef CONFIG_PM - u32 *saved_regs; - u32 saved_ydsxgr_mode; - u16 saved_dsxg_legacy; - u16 saved_dsxg_elegacy; -#endif -}; - -int snd_ymfpci_create(struct snd_card *card, - struct pci_dev *pci, - unsigned short old_legacy_ctrl, - struct snd_ymfpci ** rcodec); -void snd_ymfpci_free_gameport(struct snd_ymfpci *chip); - -extern const struct dev_pm_ops snd_ymfpci_pm; - -int snd_ymfpci_pcm(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm); -int snd_ymfpci_pcm2(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm); -int snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm); -int snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm); -int snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch); -int snd_ymfpci_timer(struct snd_ymfpci *chip, int device); - -#endif /* __SOUND_YMFPCI_H */ diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c index 00e03bc9a762..1e007c736a8b 100644 --- a/sound/pci/cs46xx/cs46xx.c +++ b/sound/pci/cs46xx/cs46xx.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include "cs46xx.h" #include MODULE_AUTHOR("Jaroslav Kysela "); diff --git a/sound/pci/cs46xx/cs46xx.h b/sound/pci/cs46xx/cs46xx.h new file mode 100644 index 000000000000..29d8a8da1ba7 --- /dev/null +++ b/sound/pci/cs46xx/cs46xx.h @@ -0,0 +1,1744 @@ +#ifndef __SOUND_CS46XX_H +#define __SOUND_CS46XX_H + +/* + * Copyright (c) by Jaroslav Kysela , + * Cirrus Logic, Inc. + * Definitions for Cirrus Logic CS46xx chips + * + * + * 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 "cs46xx_dsp_spos.h" + +/* + * Direct registers + */ + +/* + * The following define the offsets of the registers accessed via base address + * register zero on the CS46xx part. + */ +#define BA0_HISR 0x00000000 +#define BA0_HSR0 0x00000004 +#define BA0_HICR 0x00000008 +#define BA0_DMSR 0x00000100 +#define BA0_HSAR 0x00000110 +#define BA0_HDAR 0x00000114 +#define BA0_HDMR 0x00000118 +#define BA0_HDCR 0x0000011C +#define BA0_PFMC 0x00000200 +#define BA0_PFCV1 0x00000204 +#define BA0_PFCV2 0x00000208 +#define BA0_PCICFG00 0x00000300 +#define BA0_PCICFG04 0x00000304 +#define BA0_PCICFG08 0x00000308 +#define BA0_PCICFG0C 0x0000030C +#define BA0_PCICFG10 0x00000310 +#define BA0_PCICFG14 0x00000314 +#define BA0_PCICFG18 0x00000318 +#define BA0_PCICFG1C 0x0000031C +#define BA0_PCICFG20 0x00000320 +#define BA0_PCICFG24 0x00000324 +#define BA0_PCICFG28 0x00000328 +#define BA0_PCICFG2C 0x0000032C +#define BA0_PCICFG30 0x00000330 +#define BA0_PCICFG34 0x00000334 +#define BA0_PCICFG38 0x00000338 +#define BA0_PCICFG3C 0x0000033C +#define BA0_CLKCR1 0x00000400 +#define BA0_CLKCR2 0x00000404 +#define BA0_PLLM 0x00000408 +#define BA0_PLLCC 0x0000040C +#define BA0_FRR 0x00000410 +#define BA0_CFL1 0x00000414 +#define BA0_CFL2 0x00000418 +#define BA0_SERMC1 0x00000420 +#define BA0_SERMC2 0x00000424 +#define BA0_SERC1 0x00000428 +#define BA0_SERC2 0x0000042C +#define BA0_SERC3 0x00000430 +#define BA0_SERC4 0x00000434 +#define BA0_SERC5 0x00000438 +#define BA0_SERBSP 0x0000043C +#define BA0_SERBST 0x00000440 +#define BA0_SERBCM 0x00000444 +#define BA0_SERBAD 0x00000448 +#define BA0_SERBCF 0x0000044C +#define BA0_SERBWP 0x00000450 +#define BA0_SERBRP 0x00000454 +#ifndef NO_CS4612 +#define BA0_ASER_FADDR 0x00000458 +#endif +#define BA0_ACCTL 0x00000460 +#define BA0_ACSTS 0x00000464 +#define BA0_ACOSV 0x00000468 +#define BA0_ACCAD 0x0000046C +#define BA0_ACCDA 0x00000470 +#define BA0_ACISV 0x00000474 +#define BA0_ACSAD 0x00000478 +#define BA0_ACSDA 0x0000047C +#define BA0_JSPT 0x00000480 +#define BA0_JSCTL 0x00000484 +#define BA0_JSC1 0x00000488 +#define BA0_JSC2 0x0000048C +#define BA0_MIDCR 0x00000490 +#define BA0_MIDSR 0x00000494 +#define BA0_MIDWP 0x00000498 +#define BA0_MIDRP 0x0000049C +#define BA0_JSIO 0x000004A0 +#ifndef NO_CS4612 +#define BA0_ASER_MASTER 0x000004A4 +#endif +#define BA0_CFGI 0x000004B0 +#define BA0_SSVID 0x000004B4 +#define BA0_GPIOR 0x000004B8 +#ifndef NO_CS4612 +#define BA0_EGPIODR 0x000004BC +#define BA0_EGPIOPTR 0x000004C0 +#define BA0_EGPIOTR 0x000004C4 +#define BA0_EGPIOWR 0x000004C8 +#define BA0_EGPIOSR 0x000004CC +#define BA0_SERC6 0x000004D0 +#define BA0_SERC7 0x000004D4 +#define BA0_SERACC 0x000004D8 +#define BA0_ACCTL2 0x000004E0 +#define BA0_ACSTS2 0x000004E4 +#define BA0_ACOSV2 0x000004E8 +#define BA0_ACCAD2 0x000004EC +#define BA0_ACCDA2 0x000004F0 +#define BA0_ACISV2 0x000004F4 +#define BA0_ACSAD2 0x000004F8 +#define BA0_ACSDA2 0x000004FC +#define BA0_IOTAC0 0x00000500 +#define BA0_IOTAC1 0x00000504 +#define BA0_IOTAC2 0x00000508 +#define BA0_IOTAC3 0x0000050C +#define BA0_IOTAC4 0x00000510 +#define BA0_IOTAC5 0x00000514 +#define BA0_IOTAC6 0x00000518 +#define BA0_IOTAC7 0x0000051C +#define BA0_IOTAC8 0x00000520 +#define BA0_IOTAC9 0x00000524 +#define BA0_IOTAC10 0x00000528 +#define BA0_IOTAC11 0x0000052C +#define BA0_IOTFR0 0x00000540 +#define BA0_IOTFR1 0x00000544 +#define BA0_IOTFR2 0x00000548 +#define BA0_IOTFR3 0x0000054C +#define BA0_IOTFR4 0x00000550 +#define BA0_IOTFR5 0x00000554 +#define BA0_IOTFR6 0x00000558 +#define BA0_IOTFR7 0x0000055C +#define BA0_IOTFIFO 0x00000580 +#define BA0_IOTRRD 0x00000584 +#define BA0_IOTFP 0x00000588 +#define BA0_IOTCR 0x0000058C +#define BA0_DPCID 0x00000590 +#define BA0_DPCIA 0x00000594 +#define BA0_DPCIC 0x00000598 +#define BA0_PCPCIR 0x00000600 +#define BA0_PCPCIG 0x00000604 +#define BA0_PCPCIEN 0x00000608 +#define BA0_EPCIPMC 0x00000610 +#endif + +/* + * The following define the offsets of the registers and memories accessed via + * base address register one on the CS46xx part. + */ +#define BA1_SP_DMEM0 0x00000000 +#define BA1_SP_DMEM1 0x00010000 +#define BA1_SP_PMEM 0x00020000 +#define BA1_SP_REG 0x00030000 +#define BA1_SPCR 0x00030000 +#define BA1_DREG 0x00030004 +#define BA1_DSRWP 0x00030008 +#define BA1_TWPR 0x0003000C +#define BA1_SPWR 0x00030010 +#define BA1_SPIR 0x00030014 +#define BA1_FGR1 0x00030020 +#define BA1_SPCS 0x00030028 +#define BA1_SDSR 0x0003002C +#define BA1_FRMT 0x00030030 +#define BA1_FRCC 0x00030034 +#define BA1_FRSC 0x00030038 +#define BA1_OMNI_MEM 0x000E0000 + + +/* + * The following defines are for the flags in the host interrupt status + * register. + */ +#define HISR_VC_MASK 0x0000FFFF +#define HISR_VC0 0x00000001 +#define HISR_VC1 0x00000002 +#define HISR_VC2 0x00000004 +#define HISR_VC3 0x00000008 +#define HISR_VC4 0x00000010 +#define HISR_VC5 0x00000020 +#define HISR_VC6 0x00000040 +#define HISR_VC7 0x00000080 +#define HISR_VC8 0x00000100 +#define HISR_VC9 0x00000200 +#define HISR_VC10 0x00000400 +#define HISR_VC11 0x00000800 +#define HISR_VC12 0x00001000 +#define HISR_VC13 0x00002000 +#define HISR_VC14 0x00004000 +#define HISR_VC15 0x00008000 +#define HISR_INT0 0x00010000 +#define HISR_INT1 0x00020000 +#define HISR_DMAI 0x00040000 +#define HISR_FROVR 0x00080000 +#define HISR_MIDI 0x00100000 +#ifdef NO_CS4612 +#define HISR_RESERVED 0x0FE00000 +#else +#define HISR_SBINT 0x00200000 +#define HISR_RESERVED 0x0FC00000 +#endif +#define HISR_H0P 0x40000000 +#define HISR_INTENA 0x80000000 + +/* + * The following defines are for the flags in the host signal register 0. + */ +#define HSR0_VC_MASK 0xFFFFFFFF +#define HSR0_VC16 0x00000001 +#define HSR0_VC17 0x00000002 +#define HSR0_VC18 0x00000004 +#define HSR0_VC19 0x00000008 +#define HSR0_VC20 0x00000010 +#define HSR0_VC21 0x00000020 +#define HSR0_VC22 0x00000040 +#define HSR0_VC23 0x00000080 +#define HSR0_VC24 0x00000100 +#define HSR0_VC25 0x00000200 +#define HSR0_VC26 0x00000400 +#define HSR0_VC27 0x00000800 +#define HSR0_VC28 0x00001000 +#define HSR0_VC29 0x00002000 +#define HSR0_VC30 0x00004000 +#define HSR0_VC31 0x00008000 +#define HSR0_VC32 0x00010000 +#define HSR0_VC33 0x00020000 +#define HSR0_VC34 0x00040000 +#define HSR0_VC35 0x00080000 +#define HSR0_VC36 0x00100000 +#define HSR0_VC37 0x00200000 +#define HSR0_VC38 0x00400000 +#define HSR0_VC39 0x00800000 +#define HSR0_VC40 0x01000000 +#define HSR0_VC41 0x02000000 +#define HSR0_VC42 0x04000000 +#define HSR0_VC43 0x08000000 +#define HSR0_VC44 0x10000000 +#define HSR0_VC45 0x20000000 +#define HSR0_VC46 0x40000000 +#define HSR0_VC47 0x80000000 + +/* + * The following defines are for the flags in the host interrupt control + * register. + */ +#define HICR_IEV 0x00000001 +#define HICR_CHGM 0x00000002 + +/* + * The following defines are for the flags in the DMA status register. + */ +#define DMSR_HP 0x00000001 +#define DMSR_HR 0x00000002 +#define DMSR_SP 0x00000004 +#define DMSR_SR 0x00000008 + +/* + * The following defines are for the flags in the host DMA source address + * register. + */ +#define HSAR_HOST_ADDR_MASK 0xFFFFFFFF +#define HSAR_DSP_ADDR_MASK 0x0000FFFF +#define HSAR_MEMID_MASK 0x000F0000 +#define HSAR_MEMID_SP_DMEM0 0x00000000 +#define HSAR_MEMID_SP_DMEM1 0x00010000 +#define HSAR_MEMID_SP_PMEM 0x00020000 +#define HSAR_MEMID_SP_DEBUG 0x00030000 +#define HSAR_MEMID_OMNI_MEM 0x000E0000 +#define HSAR_END 0x40000000 +#define HSAR_ERR 0x80000000 + +/* + * The following defines are for the flags in the host DMA destination address + * register. + */ +#define HDAR_HOST_ADDR_MASK 0xFFFFFFFF +#define HDAR_DSP_ADDR_MASK 0x0000FFFF +#define HDAR_MEMID_MASK 0x000F0000 +#define HDAR_MEMID_SP_DMEM0 0x00000000 +#define HDAR_MEMID_SP_DMEM1 0x00010000 +#define HDAR_MEMID_SP_PMEM 0x00020000 +#define HDAR_MEMID_SP_DEBUG 0x00030000 +#define HDAR_MEMID_OMNI_MEM 0x000E0000 +#define HDAR_END 0x40000000 +#define HDAR_ERR 0x80000000 + +/* + * The following defines are for the flags in the host DMA control register. + */ +#define HDMR_AC_MASK 0x0000F000 +#define HDMR_AC_8_16 0x00001000 +#define HDMR_AC_M_S 0x00002000 +#define HDMR_AC_B_L 0x00004000 +#define HDMR_AC_S_U 0x00008000 + +/* + * The following defines are for the flags in the host DMA control register. + */ +#define HDCR_COUNT_MASK 0x000003FF +#define HDCR_DONE 0x00004000 +#define HDCR_OPT 0x00008000 +#define HDCR_WBD 0x00400000 +#define HDCR_WBS 0x00800000 +#define HDCR_DMS_MASK 0x07000000 +#define HDCR_DMS_LINEAR 0x00000000 +#define HDCR_DMS_16_DWORDS 0x01000000 +#define HDCR_DMS_32_DWORDS 0x02000000 +#define HDCR_DMS_64_DWORDS 0x03000000 +#define HDCR_DMS_128_DWORDS 0x04000000 +#define HDCR_DMS_256_DWORDS 0x05000000 +#define HDCR_DMS_512_DWORDS 0x06000000 +#define HDCR_DMS_1024_DWORDS 0x07000000 +#define HDCR_DH 0x08000000 +#define HDCR_SMS_MASK 0x70000000 +#define HDCR_SMS_LINEAR 0x00000000 +#define HDCR_SMS_16_DWORDS 0x10000000 +#define HDCR_SMS_32_DWORDS 0x20000000 +#define HDCR_SMS_64_DWORDS 0x30000000 +#define HDCR_SMS_128_DWORDS 0x40000000 +#define HDCR_SMS_256_DWORDS 0x50000000 +#define HDCR_SMS_512_DWORDS 0x60000000 +#define HDCR_SMS_1024_DWORDS 0x70000000 +#define HDCR_SH 0x80000000 +#define HDCR_COUNT_SHIFT 0 + +/* + * The following defines are for the flags in the performance monitor control + * register. + */ +#define PFMC_C1SS_MASK 0x0000001F +#define PFMC_C1EV 0x00000020 +#define PFMC_C1RS 0x00008000 +#define PFMC_C2SS_MASK 0x001F0000 +#define PFMC_C2EV 0x00200000 +#define PFMC_C2RS 0x80000000 +#define PFMC_C1SS_SHIFT 0 +#define PFMC_C2SS_SHIFT 16 +#define PFMC_BUS_GRANT 0 +#define PFMC_GRANT_AFTER_REQ 1 +#define PFMC_TRANSACTION 2 +#define PFMC_DWORD_TRANSFER 3 +#define PFMC_SLAVE_READ 4 +#define PFMC_SLAVE_WRITE 5 +#define PFMC_PREEMPTION 6 +#define PFMC_DISCONNECT_RETRY 7 +#define PFMC_INTERRUPT 8 +#define PFMC_BUS_OWNERSHIP 9 +#define PFMC_TRANSACTION_LAG 10 +#define PFMC_PCI_CLOCK 11 +#define PFMC_SERIAL_CLOCK 12 +#define PFMC_SP_CLOCK 13 + +/* + * The following defines are for the flags in the performance counter value 1 + * register. + */ +#define PFCV1_PC1V_MASK 0xFFFFFFFF +#define PFCV1_PC1V_SHIFT 0 + +/* + * The following defines are for the flags in the performance counter value 2 + * register. + */ +#define PFCV2_PC2V_MASK 0xFFFFFFFF +#define PFCV2_PC2V_SHIFT 0 + +/* + * The following defines are for the flags in the clock control register 1. + */ +#define CLKCR1_OSCS 0x00000001 +#define CLKCR1_OSCP 0x00000002 +#define CLKCR1_PLLSS_MASK 0x0000000C +#define CLKCR1_PLLSS_SERIAL 0x00000000 +#define CLKCR1_PLLSS_CRYSTAL 0x00000004 +#define CLKCR1_PLLSS_PCI 0x00000008 +#define CLKCR1_PLLSS_RESERVED 0x0000000C +#define CLKCR1_PLLP 0x00000010 +#define CLKCR1_SWCE 0x00000020 +#define CLKCR1_PLLOS 0x00000040 + +/* + * The following defines are for the flags in the clock control register 2. + */ +#define CLKCR2_PDIVS_MASK 0x0000000F +#define CLKCR2_PDIVS_1 0x00000001 +#define CLKCR2_PDIVS_2 0x00000002 +#define CLKCR2_PDIVS_4 0x00000004 +#define CLKCR2_PDIVS_7 0x00000007 +#define CLKCR2_PDIVS_8 0x00000008 +#define CLKCR2_PDIVS_16 0x00000000 + +/* + * The following defines are for the flags in the PLL multiplier register. + */ +#define PLLM_MASK 0x000000FF +#define PLLM_SHIFT 0 + +/* + * The following defines are for the flags in the PLL capacitor coefficient + * register. + */ +#define PLLCC_CDR_MASK 0x00000007 +#ifndef NO_CS4610 +#define PLLCC_CDR_240_350_MHZ 0x00000000 +#define PLLCC_CDR_184_265_MHZ 0x00000001 +#define PLLCC_CDR_144_205_MHZ 0x00000002 +#define PLLCC_CDR_111_160_MHZ 0x00000003 +#define PLLCC_CDR_87_123_MHZ 0x00000004 +#define PLLCC_CDR_67_96_MHZ 0x00000005 +#define PLLCC_CDR_52_74_MHZ 0x00000006 +#define PLLCC_CDR_45_58_MHZ 0x00000007 +#endif +#ifndef NO_CS4612 +#define PLLCC_CDR_271_398_MHZ 0x00000000 +#define PLLCC_CDR_227_330_MHZ 0x00000001 +#define PLLCC_CDR_167_239_MHZ 0x00000002 +#define PLLCC_CDR_150_215_MHZ 0x00000003 +#define PLLCC_CDR_107_154_MHZ 0x00000004 +#define PLLCC_CDR_98_140_MHZ 0x00000005 +#define PLLCC_CDR_73_104_MHZ 0x00000006 +#define PLLCC_CDR_63_90_MHZ 0x00000007 +#endif +#define PLLCC_LPF_MASK 0x000000F8 +#ifndef NO_CS4610 +#define PLLCC_LPF_23850_60000_KHZ 0x00000000 +#define PLLCC_LPF_7960_26290_KHZ 0x00000008 +#define PLLCC_LPF_4160_10980_KHZ 0x00000018 +#define PLLCC_LPF_1740_4580_KHZ 0x00000038 +#define PLLCC_LPF_724_1910_KHZ 0x00000078 +#define PLLCC_LPF_317_798_KHZ 0x000000F8 +#endif +#ifndef NO_CS4612 +#define PLLCC_LPF_25580_64530_KHZ 0x00000000 +#define PLLCC_LPF_14360_37270_KHZ 0x00000008 +#define PLLCC_LPF_6100_16020_KHZ 0x00000018 +#define PLLCC_LPF_2540_6690_KHZ 0x00000038 +#define PLLCC_LPF_1050_2780_KHZ 0x00000078 +#define PLLCC_LPF_450_1160_KHZ 0x000000F8 +#endif + +/* + * The following defines are for the flags in the feature reporting register. + */ +#define FRR_FAB_MASK 0x00000003 +#define FRR_MASK_MASK 0x0000001C +#ifdef NO_CS4612 +#define FRR_CFOP_MASK 0x000000E0 +#else +#define FRR_CFOP_MASK 0x00000FE0 +#endif +#define FRR_CFOP_NOT_DVD 0x00000020 +#define FRR_CFOP_A3D 0x00000040 +#define FRR_CFOP_128_PIN 0x00000080 +#ifndef NO_CS4612 +#define FRR_CFOP_CS4280 0x00000800 +#endif +#define FRR_FAB_SHIFT 0 +#define FRR_MASK_SHIFT 2 +#define FRR_CFOP_SHIFT 5 + +/* + * The following defines are for the flags in the configuration load 1 + * register. + */ +#define CFL1_CLOCK_SOURCE_MASK 0x00000003 +#define CFL1_CLOCK_SOURCE_CS423X 0x00000000 +#define CFL1_CLOCK_SOURCE_AC97 0x00000001 +#define CFL1_CLOCK_SOURCE_CRYSTAL 0x00000002 +#define CFL1_CLOCK_SOURCE_DUAL_AC97 0x00000003 +#define CFL1_VALID_DATA_MASK 0x000000FF + +/* + * The following defines are for the flags in the configuration load 2 + * register. + */ +#define CFL2_VALID_DATA_MASK 0x000000FF + +/* + * The following defines are for the flags in the serial port master control + * register 1. + */ +#define SERMC1_MSPE 0x00000001 +#define SERMC1_PTC_MASK 0x0000000E +#define SERMC1_PTC_CS423X 0x00000000 +#define SERMC1_PTC_AC97 0x00000002 +#define SERMC1_PTC_DAC 0x00000004 +#define SERMC1_PLB 0x00000010 +#define SERMC1_XLB 0x00000020 + +/* + * The following defines are for the flags in the serial port master control + * register 2. + */ +#define SERMC2_LROE 0x00000001 +#define SERMC2_MCOE 0x00000002 +#define SERMC2_MCDIV 0x00000004 + +/* + * The following defines are for the flags in the serial port 1 configuration + * register. + */ +#define SERC1_SO1EN 0x00000001 +#define SERC1_SO1F_MASK 0x0000000E +#define SERC1_SO1F_CS423X 0x00000000 +#define SERC1_SO1F_AC97 0x00000002 +#define SERC1_SO1F_DAC 0x00000004 +#define SERC1_SO1F_SPDIF 0x00000006 + +/* + * The following defines are for the flags in the serial port 2 configuration + * register. + */ +#define SERC2_SI1EN 0x00000001 +#define SERC2_SI1F_MASK 0x0000000E +#define SERC2_SI1F_CS423X 0x00000000 +#define SERC2_SI1F_AC97 0x00000002 +#define SERC2_SI1F_ADC 0x00000004 +#define SERC2_SI1F_SPDIF 0x00000006 + +/* + * The following defines are for the flags in the serial port 3 configuration + * register. + */ +#define SERC3_SO2EN 0x00000001 +#define SERC3_SO2F_MASK 0x00000006 +#define SERC3_SO2F_DAC 0x00000000 +#define SERC3_SO2F_SPDIF 0x00000002 + +/* + * The following defines are for the flags in the serial port 4 configuration + * register. + */ +#define SERC4_SO3EN 0x00000001 +#define SERC4_SO3F_MASK 0x00000006 +#define SERC4_SO3F_DAC 0x00000000 +#define SERC4_SO3F_SPDIF 0x00000002 + +/* + * The following defines are for the flags in the serial port 5 configuration + * register. + */ +#define SERC5_SI2EN 0x00000001 +#define SERC5_SI2F_MASK 0x00000006 +#define SERC5_SI2F_ADC 0x00000000 +#define SERC5_SI2F_SPDIF 0x00000002 + +/* + * The following defines are for the flags in the serial port backdoor sample + * pointer register. + */ +#define SERBSP_FSP_MASK 0x0000000F +#define SERBSP_FSP_SHIFT 0 + +/* + * The following defines are for the flags in the serial port backdoor status + * register. + */ +#define SERBST_RRDY 0x00000001 +#define SERBST_WBSY 0x00000002 + +/* + * The following defines are for the flags in the serial port backdoor command + * register. + */ +#define SERBCM_RDC 0x00000001 +#define SERBCM_WRC 0x00000002 + +/* + * The following defines are for the flags in the serial port backdoor address + * register. + */ +#ifdef NO_CS4612 +#define SERBAD_FAD_MASK 0x000000FF +#else +#define SERBAD_FAD_MASK 0x000001FF +#endif +#define SERBAD_FAD_SHIFT 0 + +/* + * The following defines are for the flags in the serial port backdoor + * configuration register. + */ +#define SERBCF_HBP 0x00000001 + +/* + * The following defines are for the flags in the serial port backdoor write + * port register. + */ +#define SERBWP_FWD_MASK 0x000FFFFF +#define SERBWP_FWD_SHIFT 0 + +/* + * The following defines are for the flags in the serial port backdoor read + * port register. + */ +#define SERBRP_FRD_MASK 0x000FFFFF +#define SERBRP_FRD_SHIFT 0 + +/* + * The following defines are for the flags in the async FIFO address register. + */ +#ifndef NO_CS4612 +#define ASER_FADDR_A1_MASK 0x000001FF +#define ASER_FADDR_EN1 0x00008000 +#define ASER_FADDR_A2_MASK 0x01FF0000 +#define ASER_FADDR_EN2 0x80000000 +#define ASER_FADDR_A1_SHIFT 0 +#define ASER_FADDR_A2_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the AC97 control register. + */ +#define ACCTL_RSTN 0x00000001 +#define ACCTL_ESYN 0x00000002 +#define ACCTL_VFRM 0x00000004 +#define ACCTL_DCV 0x00000008 +#define ACCTL_CRW 0x00000010 +#define ACCTL_ASYN 0x00000020 +#ifndef NO_CS4612 +#define ACCTL_TC 0x00000040 +#endif + +/* + * The following defines are for the flags in the AC97 status register. + */ +#define ACSTS_CRDY 0x00000001 +#define ACSTS_VSTS 0x00000002 +#ifndef NO_CS4612 +#define ACSTS_WKUP 0x00000004 +#endif + +/* + * The following defines are for the flags in the AC97 output slot valid + * register. + */ +#define ACOSV_SLV3 0x00000001 +#define ACOSV_SLV4 0x00000002 +#define ACOSV_SLV5 0x00000004 +#define ACOSV_SLV6 0x00000008 +#define ACOSV_SLV7 0x00000010 +#define ACOSV_SLV8 0x00000020 +#define ACOSV_SLV9 0x00000040 +#define ACOSV_SLV10 0x00000080 +#define ACOSV_SLV11 0x00000100 +#define ACOSV_SLV12 0x00000200 + +/* + * The following defines are for the flags in the AC97 command address + * register. + */ +#define ACCAD_CI_MASK 0x0000007F +#define ACCAD_CI_SHIFT 0 + +/* + * The following defines are for the flags in the AC97 command data register. + */ +#define ACCDA_CD_MASK 0x0000FFFF +#define ACCDA_CD_SHIFT 0 + +/* + * The following defines are for the flags in the AC97 input slot valid + * register. + */ +#define ACISV_ISV3 0x00000001 +#define ACISV_ISV4 0x00000002 +#define ACISV_ISV5 0x00000004 +#define ACISV_ISV6 0x00000008 +#define ACISV_ISV7 0x00000010 +#define ACISV_ISV8 0x00000020 +#define ACISV_ISV9 0x00000040 +#define ACISV_ISV10 0x00000080 +#define ACISV_ISV11 0x00000100 +#define ACISV_ISV12 0x00000200 + +/* + * The following defines are for the flags in the AC97 status address + * register. + */ +#define ACSAD_SI_MASK 0x0000007F +#define ACSAD_SI_SHIFT 0 + +/* + * The following defines are for the flags in the AC97 status data register. + */ +#define ACSDA_SD_MASK 0x0000FFFF +#define ACSDA_SD_SHIFT 0 + +/* + * The following defines are for the flags in the joystick poll/trigger + * register. + */ +#define JSPT_CAX 0x00000001 +#define JSPT_CAY 0x00000002 +#define JSPT_CBX 0x00000004 +#define JSPT_CBY 0x00000008 +#define JSPT_BA1 0x00000010 +#define JSPT_BA2 0x00000020 +#define JSPT_BB1 0x00000040 +#define JSPT_BB2 0x00000080 + +/* + * The following defines are for the flags in the joystick control register. + */ +#define JSCTL_SP_MASK 0x00000003 +#define JSCTL_SP_SLOW 0x00000000 +#define JSCTL_SP_MEDIUM_SLOW 0x00000001 +#define JSCTL_SP_MEDIUM_FAST 0x00000002 +#define JSCTL_SP_FAST 0x00000003 +#define JSCTL_ARE 0x00000004 + +/* + * The following defines are for the flags in the joystick coordinate pair 1 + * readback register. + */ +#define JSC1_Y1V_MASK 0x0000FFFF +#define JSC1_X1V_MASK 0xFFFF0000 +#define JSC1_Y1V_SHIFT 0 +#define JSC1_X1V_SHIFT 16 + +/* + * The following defines are for the flags in the joystick coordinate pair 2 + * readback register. + */ +#define JSC2_Y2V_MASK 0x0000FFFF +#define JSC2_X2V_MASK 0xFFFF0000 +#define JSC2_Y2V_SHIFT 0 +#define JSC2_X2V_SHIFT 16 + +/* + * The following defines are for the flags in the MIDI control register. + */ +#define MIDCR_TXE 0x00000001 /* Enable transmitting. */ +#define MIDCR_RXE 0x00000002 /* Enable receiving. */ +#define MIDCR_RIE 0x00000004 /* Interrupt upon tx ready. */ +#define MIDCR_TIE 0x00000008 /* Interrupt upon rx ready. */ +#define MIDCR_MLB 0x00000010 /* Enable midi loopback. */ +#define MIDCR_MRST 0x00000020 /* Reset interface. */ + +/* + * The following defines are for the flags in the MIDI status register. + */ +#define MIDSR_TBF 0x00000001 /* Tx FIFO is full. */ +#define MIDSR_RBE 0x00000002 /* Rx FIFO is empty. */ + +/* + * The following defines are for the flags in the MIDI write port register. + */ +#define MIDWP_MWD_MASK 0x000000FF +#define MIDWP_MWD_SHIFT 0 + +/* + * The following defines are for the flags in the MIDI read port register. + */ +#define MIDRP_MRD_MASK 0x000000FF +#define MIDRP_MRD_SHIFT 0 + +/* + * The following defines are for the flags in the joystick GPIO register. + */ +#define JSIO_DAX 0x00000001 +#define JSIO_DAY 0x00000002 +#define JSIO_DBX 0x00000004 +#define JSIO_DBY 0x00000008 +#define JSIO_AXOE 0x00000010 +#define JSIO_AYOE 0x00000020 +#define JSIO_BXOE 0x00000040 +#define JSIO_BYOE 0x00000080 + +/* + * The following defines are for the flags in the master async/sync serial + * port enable register. + */ +#ifndef NO_CS4612 +#define ASER_MASTER_ME 0x00000001 +#endif + +/* + * The following defines are for the flags in the configuration interface + * register. + */ +#define CFGI_CLK 0x00000001 +#define CFGI_DOUT 0x00000002 +#define CFGI_DIN_EEN 0x00000004 +#define CFGI_EELD 0x00000008 + +/* + * The following defines are for the flags in the subsystem ID and vendor ID + * register. + */ +#define SSVID_VID_MASK 0x0000FFFF +#define SSVID_SID_MASK 0xFFFF0000 +#define SSVID_VID_SHIFT 0 +#define SSVID_SID_SHIFT 16 + +/* + * The following defines are for the flags in the GPIO pin interface register. + */ +#define GPIOR_VOLDN 0x00000001 +#define GPIOR_VOLUP 0x00000002 +#define GPIOR_SI2D 0x00000004 +#define GPIOR_SI2OE 0x00000008 + +/* + * The following defines are for the flags in the extended GPIO pin direction + * register. + */ +#ifndef NO_CS4612 +#define EGPIODR_GPOE0 0x00000001 +#define EGPIODR_GPOE1 0x00000002 +#define EGPIODR_GPOE2 0x00000004 +#define EGPIODR_GPOE3 0x00000008 +#define EGPIODR_GPOE4 0x00000010 +#define EGPIODR_GPOE5 0x00000020 +#define EGPIODR_GPOE6 0x00000040 +#define EGPIODR_GPOE7 0x00000080 +#define EGPIODR_GPOE8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO pin polarity/ + * type register. + */ +#ifndef NO_CS4612 +#define EGPIOPTR_GPPT0 0x00000001 +#define EGPIOPTR_GPPT1 0x00000002 +#define EGPIOPTR_GPPT2 0x00000004 +#define EGPIOPTR_GPPT3 0x00000008 +#define EGPIOPTR_GPPT4 0x00000010 +#define EGPIOPTR_GPPT5 0x00000020 +#define EGPIOPTR_GPPT6 0x00000040 +#define EGPIOPTR_GPPT7 0x00000080 +#define EGPIOPTR_GPPT8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO pin sticky + * register. + */ +#ifndef NO_CS4612 +#define EGPIOTR_GPS0 0x00000001 +#define EGPIOTR_GPS1 0x00000002 +#define EGPIOTR_GPS2 0x00000004 +#define EGPIOTR_GPS3 0x00000008 +#define EGPIOTR_GPS4 0x00000010 +#define EGPIOTR_GPS5 0x00000020 +#define EGPIOTR_GPS6 0x00000040 +#define EGPIOTR_GPS7 0x00000080 +#define EGPIOTR_GPS8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO ping wakeup + * register. + */ +#ifndef NO_CS4612 +#define EGPIOWR_GPW0 0x00000001 +#define EGPIOWR_GPW1 0x00000002 +#define EGPIOWR_GPW2 0x00000004 +#define EGPIOWR_GPW3 0x00000008 +#define EGPIOWR_GPW4 0x00000010 +#define EGPIOWR_GPW5 0x00000020 +#define EGPIOWR_GPW6 0x00000040 +#define EGPIOWR_GPW7 0x00000080 +#define EGPIOWR_GPW8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO pin status + * register. + */ +#ifndef NO_CS4612 +#define EGPIOSR_GPS0 0x00000001 +#define EGPIOSR_GPS1 0x00000002 +#define EGPIOSR_GPS2 0x00000004 +#define EGPIOSR_GPS3 0x00000008 +#define EGPIOSR_GPS4 0x00000010 +#define EGPIOSR_GPS5 0x00000020 +#define EGPIOSR_GPS6 0x00000040 +#define EGPIOSR_GPS7 0x00000080 +#define EGPIOSR_GPS8 0x00000100 +#endif + +/* + * The following defines are for the flags in the serial port 6 configuration + * register. + */ +#ifndef NO_CS4612 +#define SERC6_ASDO2EN 0x00000001 +#endif + +/* + * The following defines are for the flags in the serial port 7 configuration + * register. + */ +#ifndef NO_CS4612 +#define SERC7_ASDI2EN 0x00000001 +#define SERC7_POSILB 0x00000002 +#define SERC7_SIPOLB 0x00000004 +#define SERC7_SOSILB 0x00000008 +#define SERC7_SISOLB 0x00000010 +#endif + +/* + * The following defines are for the flags in the serial port AC link + * configuration register. + */ +#ifndef NO_CS4612 +#define SERACC_CHIP_TYPE_MASK 0x00000001 +#define SERACC_CHIP_TYPE_1_03 0x00000000 +#define SERACC_CHIP_TYPE_2_0 0x00000001 +#define SERACC_TWO_CODECS 0x00000002 +#define SERACC_MDM 0x00000004 +#define SERACC_HSP 0x00000008 +#define SERACC_ODT 0x00000010 /* only CS4630 */ +#endif + +/* + * The following defines are for the flags in the AC97 control register 2. + */ +#ifndef NO_CS4612 +#define ACCTL2_RSTN 0x00000001 +#define ACCTL2_ESYN 0x00000002 +#define ACCTL2_VFRM 0x00000004 +#define ACCTL2_DCV 0x00000008 +#define ACCTL2_CRW 0x00000010 +#define ACCTL2_ASYN 0x00000020 +#endif + +/* + * The following defines are for the flags in the AC97 status register 2. + */ +#ifndef NO_CS4612 +#define ACSTS2_CRDY 0x00000001 +#define ACSTS2_VSTS 0x00000002 +#endif + +/* + * The following defines are for the flags in the AC97 output slot valid + * register 2. + */ +#ifndef NO_CS4612 +#define ACOSV2_SLV3 0x00000001 +#define ACOSV2_SLV4 0x00000002 +#define ACOSV2_SLV5 0x00000004 +#define ACOSV2_SLV6 0x00000008 +#define ACOSV2_SLV7 0x00000010 +#define ACOSV2_SLV8 0x00000020 +#define ACOSV2_SLV9 0x00000040 +#define ACOSV2_SLV10 0x00000080 +#define ACOSV2_SLV11 0x00000100 +#define ACOSV2_SLV12 0x00000200 +#endif + +/* + * The following defines are for the flags in the AC97 command address + * register 2. + */ +#ifndef NO_CS4612 +#define ACCAD2_CI_MASK 0x0000007F +#define ACCAD2_CI_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the AC97 command data register + * 2. + */ +#ifndef NO_CS4612 +#define ACCDA2_CD_MASK 0x0000FFFF +#define ACCDA2_CD_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the AC97 input slot valid + * register 2. + */ +#ifndef NO_CS4612 +#define ACISV2_ISV3 0x00000001 +#define ACISV2_ISV4 0x00000002 +#define ACISV2_ISV5 0x00000004 +#define ACISV2_ISV6 0x00000008 +#define ACISV2_ISV7 0x00000010 +#define ACISV2_ISV8 0x00000020 +#define ACISV2_ISV9 0x00000040 +#define ACISV2_ISV10 0x00000080 +#define ACISV2_ISV11 0x00000100 +#define ACISV2_ISV12 0x00000200 +#endif + +/* + * The following defines are for the flags in the AC97 status address + * register 2. + */ +#ifndef NO_CS4612 +#define ACSAD2_SI_MASK 0x0000007F +#define ACSAD2_SI_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the AC97 status data register 2. + */ +#ifndef NO_CS4612 +#define ACSDA2_SD_MASK 0x0000FFFF +#define ACSDA2_SD_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the I/O trap address and control + * registers (all 12). + */ +#ifndef NO_CS4612 +#define IOTAC_SA_MASK 0x0000FFFF +#define IOTAC_MSK_MASK 0x000F0000 +#define IOTAC_IODC_MASK 0x06000000 +#define IOTAC_IODC_16_BIT 0x00000000 +#define IOTAC_IODC_10_BIT 0x02000000 +#define IOTAC_IODC_12_BIT 0x04000000 +#define IOTAC_WSPI 0x08000000 +#define IOTAC_RSPI 0x10000000 +#define IOTAC_WSE 0x20000000 +#define IOTAC_WE 0x40000000 +#define IOTAC_RE 0x80000000 +#define IOTAC_SA_SHIFT 0 +#define IOTAC_MSK_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the I/O trap fast read registers + * (all 8). + */ +#ifndef NO_CS4612 +#define IOTFR_D_MASK 0x0000FFFF +#define IOTFR_A_MASK 0x000F0000 +#define IOTFR_R_MASK 0x0F000000 +#define IOTFR_ALL 0x40000000 +#define IOTFR_VL 0x80000000 +#define IOTFR_D_SHIFT 0 +#define IOTFR_A_SHIFT 16 +#define IOTFR_R_SHIFT 24 +#endif + +/* + * The following defines are for the flags in the I/O trap FIFO register. + */ +#ifndef NO_CS4612 +#define IOTFIFO_BA_MASK 0x00003FFF +#define IOTFIFO_S_MASK 0x00FF0000 +#define IOTFIFO_OF 0x40000000 +#define IOTFIFO_SPIOF 0x80000000 +#define IOTFIFO_BA_SHIFT 0 +#define IOTFIFO_S_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the I/O trap retry read data + * register. + */ +#ifndef NO_CS4612 +#define IOTRRD_D_MASK 0x0000FFFF +#define IOTRRD_RDV 0x80000000 +#define IOTRRD_D_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the I/O trap FIFO pointer + * register. + */ +#ifndef NO_CS4612 +#define IOTFP_CA_MASK 0x00003FFF +#define IOTFP_PA_MASK 0x3FFF0000 +#define IOTFP_CA_SHIFT 0 +#define IOTFP_PA_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the I/O trap control register. + */ +#ifndef NO_CS4612 +#define IOTCR_ITD 0x00000001 +#define IOTCR_HRV 0x00000002 +#define IOTCR_SRV 0x00000004 +#define IOTCR_DTI 0x00000008 +#define IOTCR_DFI 0x00000010 +#define IOTCR_DDP 0x00000020 +#define IOTCR_JTE 0x00000040 +#define IOTCR_PPE 0x00000080 +#endif + +/* + * The following defines are for the flags in the direct PCI data register. + */ +#ifndef NO_CS4612 +#define DPCID_D_MASK 0xFFFFFFFF +#define DPCID_D_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the direct PCI address register. + */ +#ifndef NO_CS4612 +#define DPCIA_A_MASK 0xFFFFFFFF +#define DPCIA_A_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the direct PCI command register. + */ +#ifndef NO_CS4612 +#define DPCIC_C_MASK 0x0000000F +#define DPCIC_C_IOREAD 0x00000002 +#define DPCIC_C_IOWRITE 0x00000003 +#define DPCIC_BE_MASK 0x000000F0 +#endif + +/* + * The following defines are for the flags in the PC/PCI request register. + */ +#ifndef NO_CS4612 +#define PCPCIR_RDC_MASK 0x00000007 +#define PCPCIR_C_MASK 0x00007000 +#define PCPCIR_REQ 0x00008000 +#define PCPCIR_RDC_SHIFT 0 +#define PCPCIR_C_SHIFT 12 +#endif + +/* + * The following defines are for the flags in the PC/PCI grant register. + */ +#ifndef NO_CS4612 +#define PCPCIG_GDC_MASK 0x00000007 +#define PCPCIG_VL 0x00008000 +#define PCPCIG_GDC_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the PC/PCI master enable + * register. + */ +#ifndef NO_CS4612 +#define PCPCIEN_EN 0x00000001 +#endif + +/* + * The following defines are for the flags in the extended PCI power + * management control register. + */ +#ifndef NO_CS4612 +#define EPCIPMC_GWU 0x00000001 +#define EPCIPMC_FSPC 0x00000002 +#endif + +/* + * The following defines are for the flags in the SP control register. + */ +#define SPCR_RUN 0x00000001 +#define SPCR_STPFR 0x00000002 +#define SPCR_RUNFR 0x00000004 +#define SPCR_TICK 0x00000008 +#define SPCR_DRQEN 0x00000020 +#define SPCR_RSTSP 0x00000040 +#define SPCR_OREN 0x00000080 +#ifndef NO_CS4612 +#define SPCR_PCIINT 0x00000100 +#define SPCR_OINTD 0x00000200 +#define SPCR_CRE 0x00008000 +#endif + +/* + * The following defines are for the flags in the debug index register. + */ +#define DREG_REGID_MASK 0x0000007F +#define DREG_DEBUG 0x00000080 +#define DREG_RGBK_MASK 0x00000700 +#define DREG_TRAP 0x00000800 +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_TRAPX 0x00001000 +#endif +#endif +#define DREG_REGID_SHIFT 0 +#define DREG_RGBK_SHIFT 8 +#define DREG_RGBK_REGID_MASK 0x0000077F +#define DREG_REGID_R0 0x00000010 +#define DREG_REGID_R1 0x00000011 +#define DREG_REGID_R2 0x00000012 +#define DREG_REGID_R3 0x00000013 +#define DREG_REGID_R4 0x00000014 +#define DREG_REGID_R5 0x00000015 +#define DREG_REGID_R6 0x00000016 +#define DREG_REGID_R7 0x00000017 +#define DREG_REGID_R8 0x00000018 +#define DREG_REGID_R9 0x00000019 +#define DREG_REGID_RA 0x0000001A +#define DREG_REGID_RB 0x0000001B +#define DREG_REGID_RC 0x0000001C +#define DREG_REGID_RD 0x0000001D +#define DREG_REGID_RE 0x0000001E +#define DREG_REGID_RF 0x0000001F +#define DREG_REGID_RA_BUS_LOW 0x00000020 +#define DREG_REGID_RA_BUS_HIGH 0x00000038 +#define DREG_REGID_YBUS_LOW 0x00000050 +#define DREG_REGID_YBUS_HIGH 0x00000058 +#define DREG_REGID_TRAP_0 0x00000100 +#define DREG_REGID_TRAP_1 0x00000101 +#define DREG_REGID_TRAP_2 0x00000102 +#define DREG_REGID_TRAP_3 0x00000103 +#define DREG_REGID_TRAP_4 0x00000104 +#define DREG_REGID_TRAP_5 0x00000105 +#define DREG_REGID_TRAP_6 0x00000106 +#define DREG_REGID_TRAP_7 0x00000107 +#define DREG_REGID_INDIRECT_ADDRESS 0x0000010E +#define DREG_REGID_TOP_OF_STACK 0x0000010F +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_REGID_TRAP_8 0x00000110 +#define DREG_REGID_TRAP_9 0x00000111 +#define DREG_REGID_TRAP_10 0x00000112 +#define DREG_REGID_TRAP_11 0x00000113 +#define DREG_REGID_TRAP_12 0x00000114 +#define DREG_REGID_TRAP_13 0x00000115 +#define DREG_REGID_TRAP_14 0x00000116 +#define DREG_REGID_TRAP_15 0x00000117 +#define DREG_REGID_TRAP_16 0x00000118 +#define DREG_REGID_TRAP_17 0x00000119 +#define DREG_REGID_TRAP_18 0x0000011A +#define DREG_REGID_TRAP_19 0x0000011B +#define DREG_REGID_TRAP_20 0x0000011C +#define DREG_REGID_TRAP_21 0x0000011D +#define DREG_REGID_TRAP_22 0x0000011E +#define DREG_REGID_TRAP_23 0x0000011F +#endif +#endif +#define DREG_REGID_RSA0_LOW 0x00000200 +#define DREG_REGID_RSA0_HIGH 0x00000201 +#define DREG_REGID_RSA1_LOW 0x00000202 +#define DREG_REGID_RSA1_HIGH 0x00000203 +#define DREG_REGID_RSA2 0x00000204 +#define DREG_REGID_RSA3 0x00000205 +#define DREG_REGID_RSI0_LOW 0x00000206 +#define DREG_REGID_RSI0_HIGH 0x00000207 +#define DREG_REGID_RSI1 0x00000208 +#define DREG_REGID_RSI2 0x00000209 +#define DREG_REGID_SAGUSTATUS 0x0000020A +#define DREG_REGID_RSCONFIG01_LOW 0x0000020B +#define DREG_REGID_RSCONFIG01_HIGH 0x0000020C +#define DREG_REGID_RSCONFIG23_LOW 0x0000020D +#define DREG_REGID_RSCONFIG23_HIGH 0x0000020E +#define DREG_REGID_RSDMA01E 0x0000020F +#define DREG_REGID_RSDMA23E 0x00000210 +#define DREG_REGID_RSD0_LOW 0x00000211 +#define DREG_REGID_RSD0_HIGH 0x00000212 +#define DREG_REGID_RSD1_LOW 0x00000213 +#define DREG_REGID_RSD1_HIGH 0x00000214 +#define DREG_REGID_RSD2_LOW 0x00000215 +#define DREG_REGID_RSD2_HIGH 0x00000216 +#define DREG_REGID_RSD3_LOW 0x00000217 +#define DREG_REGID_RSD3_HIGH 0x00000218 +#define DREG_REGID_SRAR_HIGH 0x0000021A +#define DREG_REGID_SRAR_LOW 0x0000021B +#define DREG_REGID_DMA_STATE 0x0000021C +#define DREG_REGID_CURRENT_DMA_STREAM 0x0000021D +#define DREG_REGID_NEXT_DMA_STREAM 0x0000021E +#define DREG_REGID_CPU_STATUS 0x00000300 +#define DREG_REGID_MAC_MODE 0x00000301 +#define DREG_REGID_STACK_AND_REPEAT 0x00000302 +#define DREG_REGID_INDEX0 0x00000304 +#define DREG_REGID_INDEX1 0x00000305 +#define DREG_REGID_DMA_STATE_0_3 0x00000400 +#define DREG_REGID_DMA_STATE_4_7 0x00000404 +#define DREG_REGID_DMA_STATE_8_11 0x00000408 +#define DREG_REGID_DMA_STATE_12_15 0x0000040C +#define DREG_REGID_DMA_STATE_16_19 0x00000410 +#define DREG_REGID_DMA_STATE_20_23 0x00000414 +#define DREG_REGID_DMA_STATE_24_27 0x00000418 +#define DREG_REGID_DMA_STATE_28_31 0x0000041C +#define DREG_REGID_DMA_STATE_32_35 0x00000420 +#define DREG_REGID_DMA_STATE_36_39 0x00000424 +#define DREG_REGID_DMA_STATE_40_43 0x00000428 +#define DREG_REGID_DMA_STATE_44_47 0x0000042C +#define DREG_REGID_DMA_STATE_48_51 0x00000430 +#define DREG_REGID_DMA_STATE_52_55 0x00000434 +#define DREG_REGID_DMA_STATE_56_59 0x00000438 +#define DREG_REGID_DMA_STATE_60_63 0x0000043C +#define DREG_REGID_DMA_STATE_64_67 0x00000440 +#define DREG_REGID_DMA_STATE_68_71 0x00000444 +#define DREG_REGID_DMA_STATE_72_75 0x00000448 +#define DREG_REGID_DMA_STATE_76_79 0x0000044C +#define DREG_REGID_DMA_STATE_80_83 0x00000450 +#define DREG_REGID_DMA_STATE_84_87 0x00000454 +#define DREG_REGID_DMA_STATE_88_91 0x00000458 +#define DREG_REGID_DMA_STATE_92_95 0x0000045C +#define DREG_REGID_TRAP_SELECT 0x00000500 +#define DREG_REGID_TRAP_WRITE_0 0x00000500 +#define DREG_REGID_TRAP_WRITE_1 0x00000501 +#define DREG_REGID_TRAP_WRITE_2 0x00000502 +#define DREG_REGID_TRAP_WRITE_3 0x00000503 +#define DREG_REGID_TRAP_WRITE_4 0x00000504 +#define DREG_REGID_TRAP_WRITE_5 0x00000505 +#define DREG_REGID_TRAP_WRITE_6 0x00000506 +#define DREG_REGID_TRAP_WRITE_7 0x00000507 +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_REGID_TRAP_WRITE_8 0x00000510 +#define DREG_REGID_TRAP_WRITE_9 0x00000511 +#define DREG_REGID_TRAP_WRITE_10 0x00000512 +#define DREG_REGID_TRAP_WRITE_11 0x00000513 +#define DREG_REGID_TRAP_WRITE_12 0x00000514 +#define DREG_REGID_TRAP_WRITE_13 0x00000515 +#define DREG_REGID_TRAP_WRITE_14 0x00000516 +#define DREG_REGID_TRAP_WRITE_15 0x00000517 +#define DREG_REGID_TRAP_WRITE_16 0x00000518 +#define DREG_REGID_TRAP_WRITE_17 0x00000519 +#define DREG_REGID_TRAP_WRITE_18 0x0000051A +#define DREG_REGID_TRAP_WRITE_19 0x0000051B +#define DREG_REGID_TRAP_WRITE_20 0x0000051C +#define DREG_REGID_TRAP_WRITE_21 0x0000051D +#define DREG_REGID_TRAP_WRITE_22 0x0000051E +#define DREG_REGID_TRAP_WRITE_23 0x0000051F +#endif +#endif +#define DREG_REGID_MAC0_ACC0_LOW 0x00000600 +#define DREG_REGID_MAC0_ACC1_LOW 0x00000601 +#define DREG_REGID_MAC0_ACC2_LOW 0x00000602 +#define DREG_REGID_MAC0_ACC3_LOW 0x00000603 +#define DREG_REGID_MAC1_ACC0_LOW 0x00000604 +#define DREG_REGID_MAC1_ACC1_LOW 0x00000605 +#define DREG_REGID_MAC1_ACC2_LOW 0x00000606 +#define DREG_REGID_MAC1_ACC3_LOW 0x00000607 +#define DREG_REGID_MAC0_ACC0_MID 0x00000608 +#define DREG_REGID_MAC0_ACC1_MID 0x00000609 +#define DREG_REGID_MAC0_ACC2_MID 0x0000060A +#define DREG_REGID_MAC0_ACC3_MID 0x0000060B +#define DREG_REGID_MAC1_ACC0_MID 0x0000060C +#define DREG_REGID_MAC1_ACC1_MID 0x0000060D +#define DREG_REGID_MAC1_ACC2_MID 0x0000060E +#define DREG_REGID_MAC1_ACC3_MID 0x0000060F +#define DREG_REGID_MAC0_ACC0_HIGH 0x00000610 +#define DREG_REGID_MAC0_ACC1_HIGH 0x00000611 +#define DREG_REGID_MAC0_ACC2_HIGH 0x00000612 +#define DREG_REGID_MAC0_ACC3_HIGH 0x00000613 +#define DREG_REGID_MAC1_ACC0_HIGH 0x00000614 +#define DREG_REGID_MAC1_ACC1_HIGH 0x00000615 +#define DREG_REGID_MAC1_ACC2_HIGH 0x00000616 +#define DREG_REGID_MAC1_ACC3_HIGH 0x00000617 +#define DREG_REGID_RSHOUT_LOW 0x00000620 +#define DREG_REGID_RSHOUT_MID 0x00000628 +#define DREG_REGID_RSHOUT_HIGH 0x00000630 + +/* + * The following defines are for the flags in the DMA stream requestor write + */ +#define DSRWP_DSR_MASK 0x0000000F +#define DSRWP_DSR_BG_RQ 0x00000001 +#define DSRWP_DSR_PRIORITY_MASK 0x00000006 +#define DSRWP_DSR_PRIORITY_0 0x00000000 +#define DSRWP_DSR_PRIORITY_1 0x00000002 +#define DSRWP_DSR_PRIORITY_2 0x00000004 +#define DSRWP_DSR_PRIORITY_3 0x00000006 +#define DSRWP_DSR_RQ_PENDING 0x00000008 + +/* + * The following defines are for the flags in the trap write port register. + */ +#define TWPR_TW_MASK 0x0000FFFF +#define TWPR_TW_SHIFT 0 + +/* + * The following defines are for the flags in the stack pointer write + * register. + */ +#define SPWR_STKP_MASK 0x0000000F +#define SPWR_STKP_SHIFT 0 + +/* + * The following defines are for the flags in the SP interrupt register. + */ +#define SPIR_FRI 0x00000001 +#define SPIR_DOI 0x00000002 +#define SPIR_GPI2 0x00000004 +#define SPIR_GPI3 0x00000008 +#define SPIR_IP0 0x00000010 +#define SPIR_IP1 0x00000020 +#define SPIR_IP2 0x00000040 +#define SPIR_IP3 0x00000080 + +/* + * The following defines are for the flags in the functional group 1 register. + */ +#define FGR1_F1S_MASK 0x0000FFFF +#define FGR1_F1S_SHIFT 0 + +/* + * The following defines are for the flags in the SP clock status register. + */ +#define SPCS_FRI 0x00000001 +#define SPCS_DOI 0x00000002 +#define SPCS_GPI2 0x00000004 +#define SPCS_GPI3 0x00000008 +#define SPCS_IP0 0x00000010 +#define SPCS_IP1 0x00000020 +#define SPCS_IP2 0x00000040 +#define SPCS_IP3 0x00000080 +#define SPCS_SPRUN 0x00000100 +#define SPCS_SLEEP 0x00000200 +#define SPCS_FG 0x00000400 +#define SPCS_ORUN 0x00000800 +#define SPCS_IRQ 0x00001000 +#define SPCS_FGN_MASK 0x0000E000 +#define SPCS_FGN_SHIFT 13 + +/* + * The following defines are for the flags in the SP DMA requestor status + * register. + */ +#define SDSR_DCS_MASK 0x000000FF +#define SDSR_DCS_SHIFT 0 +#define SDSR_DCS_NONE 0x00000007 + +/* + * The following defines are for the flags in the frame timer register. + */ +#define FRMT_FTV_MASK 0x0000FFFF +#define FRMT_FTV_SHIFT 0 + +/* + * The following defines are for the flags in the frame timer current count + * register. + */ +#define FRCC_FCC_MASK 0x0000FFFF +#define FRCC_FCC_SHIFT 0 + +/* + * The following defines are for the flags in the frame timer save count + * register. + */ +#define FRSC_FCS_MASK 0x0000FFFF +#define FRSC_FCS_SHIFT 0 + +/* + * The following define the various flags stored in the scatter/gather + * descriptors. + */ +#define DMA_SG_NEXT_ENTRY_MASK 0x00000FF8 +#define DMA_SG_SAMPLE_END_MASK 0x0FFF0000 +#define DMA_SG_SAMPLE_END_FLAG 0x10000000 +#define DMA_SG_LOOP_END_FLAG 0x20000000 +#define DMA_SG_SIGNAL_END_FLAG 0x40000000 +#define DMA_SG_SIGNAL_PAGE_FLAG 0x80000000 +#define DMA_SG_NEXT_ENTRY_SHIFT 3 +#define DMA_SG_SAMPLE_END_SHIFT 16 + +/* + * The following define the offsets of the fields within the on-chip generic + * DMA requestor. + */ +#define DMA_RQ_CONTROL1 0x00000000 +#define DMA_RQ_CONTROL2 0x00000004 +#define DMA_RQ_SOURCE_ADDR 0x00000008 +#define DMA_RQ_DESTINATION_ADDR 0x0000000C +#define DMA_RQ_NEXT_PAGE_ADDR 0x00000010 +#define DMA_RQ_NEXT_PAGE_SGDESC 0x00000014 +#define DMA_RQ_LOOP_START_ADDR 0x00000018 +#define DMA_RQ_POST_LOOP_ADDR 0x0000001C +#define DMA_RQ_PAGE_MAP_ADDR 0x00000020 + +/* + * The following defines are for the flags in the first control word of the + * on-chip generic DMA requestor. + */ +#define DMA_RQ_C1_COUNT_MASK 0x000003FF +#define DMA_RQ_C1_DESTINATION_SCATTER 0x00001000 +#define DMA_RQ_C1_SOURCE_GATHER 0x00002000 +#define DMA_RQ_C1_DONE_FLAG 0x00004000 +#define DMA_RQ_C1_OPTIMIZE_STATE 0x00008000 +#define DMA_RQ_C1_SAMPLE_END_STATE_MASK 0x00030000 +#define DMA_RQ_C1_FULL_PAGE 0x00000000 +#define DMA_RQ_C1_BEFORE_SAMPLE_END 0x00010000 +#define DMA_RQ_C1_PAGE_MAP_ERROR 0x00020000 +#define DMA_RQ_C1_AT_SAMPLE_END 0x00030000 +#define DMA_RQ_C1_LOOP_END_STATE_MASK 0x000C0000 +#define DMA_RQ_C1_NOT_LOOP_END 0x00000000 +#define DMA_RQ_C1_BEFORE_LOOP_END 0x00040000 +#define DMA_RQ_C1_2PAGE_LOOP_BEGIN 0x00080000 +#define DMA_RQ_C1_LOOP_BEGIN 0x000C0000 +#define DMA_RQ_C1_PAGE_MAP_MASK 0x00300000 +#define DMA_RQ_C1_PM_NONE_PENDING 0x00000000 +#define DMA_RQ_C1_PM_NEXT_PENDING 0x00100000 +#define DMA_RQ_C1_PM_RESERVED 0x00200000 +#define DMA_RQ_C1_PM_LOOP_NEXT_PENDING 0x00300000 +#define DMA_RQ_C1_WRITEBACK_DEST_FLAG 0x00400000 +#define DMA_RQ_C1_WRITEBACK_SRC_FLAG 0x00800000 +#define DMA_RQ_C1_DEST_SIZE_MASK 0x07000000 +#define DMA_RQ_C1_DEST_LINEAR 0x00000000 +#define DMA_RQ_C1_DEST_MOD16 0x01000000 +#define DMA_RQ_C1_DEST_MOD32 0x02000000 +#define DMA_RQ_C1_DEST_MOD64 0x03000000 +#define DMA_RQ_C1_DEST_MOD128 0x04000000 +#define DMA_RQ_C1_DEST_MOD256 0x05000000 +#define DMA_RQ_C1_DEST_MOD512 0x06000000 +#define DMA_RQ_C1_DEST_MOD1024 0x07000000 +#define DMA_RQ_C1_DEST_ON_HOST 0x08000000 +#define DMA_RQ_C1_SOURCE_SIZE_MASK 0x70000000 +#define DMA_RQ_C1_SOURCE_LINEAR 0x00000000 +#define DMA_RQ_C1_SOURCE_MOD16 0x10000000 +#define DMA_RQ_C1_SOURCE_MOD32 0x20000000 +#define DMA_RQ_C1_SOURCE_MOD64 0x30000000 +#define DMA_RQ_C1_SOURCE_MOD128 0x40000000 +#define DMA_RQ_C1_SOURCE_MOD256 0x50000000 +#define DMA_RQ_C1_SOURCE_MOD512 0x60000000 +#define DMA_RQ_C1_SOURCE_MOD1024 0x70000000 +#define DMA_RQ_C1_SOURCE_ON_HOST 0x80000000 +#define DMA_RQ_C1_COUNT_SHIFT 0 + +/* + * The following defines are for the flags in the second control word of the + * on-chip generic DMA requestor. + */ +#define DMA_RQ_C2_VIRTUAL_CHANNEL_MASK 0x0000003F +#define DMA_RQ_C2_VIRTUAL_SIGNAL_MASK 0x00000300 +#define DMA_RQ_C2_NO_VIRTUAL_SIGNAL 0x00000000 +#define DMA_RQ_C2_SIGNAL_EVERY_DMA 0x00000100 +#define DMA_RQ_C2_SIGNAL_SOURCE_PINGPONG 0x00000200 +#define DMA_RQ_C2_SIGNAL_DEST_PINGPONG 0x00000300 +#define DMA_RQ_C2_AUDIO_CONVERT_MASK 0x0000F000 +#define DMA_RQ_C2_AC_NONE 0x00000000 +#define DMA_RQ_C2_AC_8_TO_16_BIT 0x00001000 +#define DMA_RQ_C2_AC_MONO_TO_STEREO 0x00002000 +#define DMA_RQ_C2_AC_ENDIAN_CONVERT 0x00004000 +#define DMA_RQ_C2_AC_SIGNED_CONVERT 0x00008000 +#define DMA_RQ_C2_LOOP_END_MASK 0x0FFF0000 +#define DMA_RQ_C2_LOOP_MASK 0x30000000 +#define DMA_RQ_C2_NO_LOOP 0x00000000 +#define DMA_RQ_C2_ONE_PAGE_LOOP 0x10000000 +#define DMA_RQ_C2_TWO_PAGE_LOOP 0x20000000 +#define DMA_RQ_C2_MULTI_PAGE_LOOP 0x30000000 +#define DMA_RQ_C2_SIGNAL_LOOP_BACK 0x40000000 +#define DMA_RQ_C2_SIGNAL_POST_BEGIN_PAGE 0x80000000 +#define DMA_RQ_C2_VIRTUAL_CHANNEL_SHIFT 0 +#define DMA_RQ_C2_LOOP_END_SHIFT 16 + +/* + * The following defines are for the flags in the source and destination words + * of the on-chip generic DMA requestor. + */ +#define DMA_RQ_SD_ADDRESS_MASK 0x0000FFFF +#define DMA_RQ_SD_MEMORY_ID_MASK 0x000F0000 +#define DMA_RQ_SD_SP_PARAM_ADDR 0x00000000 +#define DMA_RQ_SD_SP_SAMPLE_ADDR 0x00010000 +#define DMA_RQ_SD_SP_PROGRAM_ADDR 0x00020000 +#define DMA_RQ_SD_SP_DEBUG_ADDR 0x00030000 +#define DMA_RQ_SD_OMNIMEM_ADDR 0x000E0000 +#define DMA_RQ_SD_END_FLAG 0x40000000 +#define DMA_RQ_SD_ERROR_FLAG 0x80000000 +#define DMA_RQ_SD_ADDRESS_SHIFT 0 + +/* + * The following defines are for the flags in the page map address word of the + * on-chip generic DMA requestor. + */ +#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_MASK 0x00000FF8 +#define DMA_RQ_PMA_PAGE_TABLE_MASK 0xFFFFF000 +#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_SHIFT 3 +#define DMA_RQ_PMA_PAGE_TABLE_SHIFT 12 + +#define BA1_VARIDEC_BUF_1 0x000 + +#define BA1_PDTC 0x0c0 /* BA1_PLAY_DMA_TRANSACTION_COUNT_REG */ +#define BA1_PFIE 0x0c4 /* BA1_PLAY_FORMAT_&_INTERRUPT_ENABLE_REG */ +#define BA1_PBA 0x0c8 /* BA1_PLAY_BUFFER_ADDRESS */ +#define BA1_PVOL 0x0f8 /* BA1_PLAY_VOLUME_REG */ +#define BA1_PSRC 0x288 /* BA1_PLAY_SAMPLE_RATE_CORRECTION_REG */ +#define BA1_PCTL 0x2a4 /* BA1_PLAY_CONTROL_REG */ +#define BA1_PPI 0x2b4 /* BA1_PLAY_PHASE_INCREMENT_REG */ + +#define BA1_CCTL 0x064 /* BA1_CAPTURE_CONTROL_REG */ +#define BA1_CIE 0x104 /* BA1_CAPTURE_INTERRUPT_ENABLE_REG */ +#define BA1_CBA 0x10c /* BA1_CAPTURE_BUFFER_ADDRESS */ +#define BA1_CSRC 0x2c8 /* BA1_CAPTURE_SAMPLE_RATE_CORRECTION_REG */ +#define BA1_CCI 0x2d8 /* BA1_CAPTURE_COEFFICIENT_INCREMENT_REG */ +#define BA1_CD 0x2e0 /* BA1_CAPTURE_DELAY_REG */ +#define BA1_CPI 0x2f4 /* BA1_CAPTURE_PHASE_INCREMENT_REG */ +#define BA1_CVOL 0x2f8 /* BA1_CAPTURE_VOLUME_REG */ + +#define BA1_CFG1 0x134 /* BA1_CAPTURE_FRAME_GROUP_1_REG */ +#define BA1_CFG2 0x138 /* BA1_CAPTURE_FRAME_GROUP_2_REG */ +#define BA1_CCST 0x13c /* BA1_CAPTURE_CONSTANT_REG */ +#define BA1_CSPB 0x340 /* BA1_CAPTURE_SPB_ADDRESS */ + +/* + * + */ + +#define CS46XX_MODE_OUTPUT (1<<0) /* MIDI UART - output */ +#define CS46XX_MODE_INPUT (1<<1) /* MIDI UART - input */ + +/* + * + */ + +#define SAVE_REG_MAX 0x10 +#define POWER_DOWN_ALL 0x7f0f + +/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */ +#define MAX_NR_AC97 4 +#define CS46XX_PRIMARY_CODEC_INDEX 0 +#define CS46XX_SECONDARY_CODEC_INDEX 1 +#define CS46XX_SECONDARY_CODEC_OFFSET 0x80 +#define CS46XX_DSP_CAPTURE_CHANNEL 1 + +/* capture */ +#define CS46XX_DSP_CAPTURE_CHANNEL 1 + +/* mixer */ +#define CS46XX_MIXER_SPDIF_INPUT_ELEMENT 1 +#define CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT 2 + + +struct snd_cs46xx_pcm { + struct snd_dma_buffer hw_buf; + + unsigned int ctl; + unsigned int shift; /* Shift count to trasform frames in bytes */ + struct snd_pcm_indirect pcm_rec; + struct snd_pcm_substream *substream; + + struct dsp_pcm_channel_descriptor * pcm_channel; + + int pcm_channel_id; /* Fron Rear, Center Lfe ... */ +}; + +struct snd_cs46xx_region { + char name[24]; + unsigned long base; + void __iomem *remap_addr; + unsigned long size; + struct resource *resource; +}; + +struct snd_cs46xx { + int irq; + unsigned long ba0_addr; + unsigned long ba1_addr; + union { + struct { + struct snd_cs46xx_region ba0; + struct snd_cs46xx_region data0; + struct snd_cs46xx_region data1; + struct snd_cs46xx_region pmem; + struct snd_cs46xx_region reg; + } name; + struct snd_cs46xx_region idx[5]; + } region; + + unsigned int mode; + + struct { + struct snd_dma_buffer hw_buf; + + unsigned int ctl; + unsigned int shift; /* Shift count to trasform frames in bytes */ + struct snd_pcm_indirect pcm_rec; + struct snd_pcm_substream *substream; + } capt; + + + int nr_ac97_codecs; + struct snd_ac97_bus *ac97_bus; + struct snd_ac97 *ac97[MAX_NR_AC97]; + + struct pci_dev *pci; + struct snd_card *card; + struct snd_pcm *pcm; + + struct snd_rawmidi *rmidi; + struct snd_rawmidi_substream *midi_input; + struct snd_rawmidi_substream *midi_output; + + spinlock_t reg_lock; + unsigned int midcr; + unsigned int uartm; + + int amplifier; + void (*amplifier_ctrl)(struct snd_cs46xx *, int); + void (*active_ctrl)(struct snd_cs46xx *, int); + void (*mixer_init)(struct snd_cs46xx *); + + int acpi_port; + struct snd_kcontrol *eapd_switch; /* for amplifier hack */ + int accept_valid; /* accept mmap valid (for OSS) */ + int in_suspend; + + struct gameport *gameport; + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + struct mutex spos_mutex; + + struct dsp_spos_instance * dsp_spos_instance; + + struct snd_pcm *pcm_rear; + struct snd_pcm *pcm_center_lfe; + struct snd_pcm *pcm_iec958; +#else /* for compatibility */ + struct snd_cs46xx_pcm *playback_pcm; + unsigned int play_ctl; +#endif + +#ifdef CONFIG_PM + u32 *saved_regs; +#endif +}; + +int snd_cs46xx_create(struct snd_card *card, + struct pci_dev *pci, + int external_amp, int thinkpad, + struct snd_cs46xx **rcodec); +extern const struct dev_pm_ops snd_cs46xx_pm; + +int snd_cs46xx_pcm(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm); +int snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm); +int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm); +int snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm); +int snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device); +int snd_cs46xx_midi(struct snd_cs46xx *chip, int device, struct snd_rawmidi **rmidi); +int snd_cs46xx_start_dsp(struct snd_cs46xx *chip); +int snd_cs46xx_gameport(struct snd_cs46xx *chip); + +#endif /* __SOUND_CS46XX_H */ diff --git a/sound/pci/cs46xx/cs46xx_dsp_scb_types.h b/sound/pci/cs46xx/cs46xx_dsp_scb_types.h new file mode 100644 index 000000000000..080857ad0ca2 --- /dev/null +++ b/sound/pci/cs46xx/cs46xx_dsp_scb_types.h @@ -0,0 +1,1213 @@ +/* + * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards + * Copyright (c) by 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 + * + * + * NOTE: comments are copy/paste from cwcemb80.lst + * provided by Tom Woller at Cirrus (my only + * documentation about the SP OS running inside + * the DSP) + */ + +#ifndef __CS46XX_DSP_SCB_TYPES_H__ +#define __CS46XX_DSP_SCB_TYPES_H__ + +#include + +#ifndef ___DSP_DUAL_16BIT_ALLOC +#if defined(__LITTLE_ENDIAN) +#define ___DSP_DUAL_16BIT_ALLOC(a,b) u16 a; u16 b; +#elif defined(__BIG_ENDIAN) +#define ___DSP_DUAL_16BIT_ALLOC(a,b) u16 b; u16 a; +#else +#error Not __LITTLE_ENDIAN and not __BIG_ENDIAN, then what ??? +#endif +#endif + +/* This structs are used internally by the SP */ + +struct dsp_basic_dma_req { + /* DMA Requestor Word 0 (DCW) fields: + + 31 [30-28]27 [26:24] 23 22 21 20 [19:18] [17:16] 15 14 13 12 11 10 9 8 7 6 [5:0] + _______________________________________________________________________________________ + |S| SBT |D| DBT |wb|wb| | | LS | SS |Opt|Do|SSG|DSG| | | | | | | Dword | + |H|_____ |H|_________|S_|D |__|__|______|_______|___|ne|__ |__ |__|__|_|_|_|_|_Count -1| + */ + u32 dcw; /* DMA Control Word */ + u32 dmw; /* DMA Mode Word */ + u32 saw; /* Source Address Word */ + u32 daw; /* Destination Address Word */ +}; + +struct dsp_scatter_gather_ext { + u32 npaw; /* Next-Page Address Word */ + + /* DMA Requestor Word 5 (NPCW) fields: + + 31-30 29 28 [27:16] [15:12] [11:3] [2:0] + _________________________________________________________________________________________ + |SV |LE|SE| Sample-end byte offset | | Page-map entry offset for next | | + |page|__|__| ___________________________|_________|__page, if !sample-end___________|____| + */ + u32 npcw; /* Next-Page Control Word */ + u32 lbaw; /* Loop-Begin Address Word */ + u32 nplbaw; /* Next-Page after Loop-Begin Address Word */ + u32 sgaw; /* Scatter/Gather Address Word */ +}; + +struct dsp_volume_control { + ___DSP_DUAL_16BIT_ALLOC( + rightTarg, /* Target volume for left & right channels */ + leftTarg + ) + ___DSP_DUAL_16BIT_ALLOC( + rightVol, /* Current left & right channel volumes */ + leftVol + ) +}; + +/* Generic stream control block (SCB) structure definition */ +struct dsp_generic_scb { + /* For streaming I/O, the DSP should never alter any words in the DMA + requestor or the scatter/gather extension. Only ad hoc DMA request + streams are free to alter the requestor (currently only occur in the + DOS-based MIDI controller and in debugger-inserted code). + + If an SCB does not have any associated DMA requestor, these 9 ints + may be freed for use by other tasks, but the pointer to the SCB must + still be such that the insOrd:nextSCB appear at offset 9 from the + SCB pointer. + + Basic (non scatter/gather) DMA requestor (4 ints) + */ + + /* Initialized by the host, only modified by DMA + R/O for the DSP task */ + struct dsp_basic_dma_req basic_req; /* Optional */ + + /* Scatter/gather DMA requestor extension (5 ints) + Initialized by the host, only modified by DMA + DSP task never needs to even read these. + */ + struct dsp_scatter_gather_ext sg_ext; /* Optional */ + + /* Sublist pointer & next stream control block (SCB) link. + Initialized & modified by the host R/O for the DSP task + */ + ___DSP_DUAL_16BIT_ALLOC( + next_scb, /* REQUIRED */ + sub_list_ptr /* REQUIRED */ + ) + + /* Pointer to this tasks parameter block & stream function pointer + Initialized by the host R/O for the DSP task */ + ___DSP_DUAL_16BIT_ALLOC( + entry_point, /* REQUIRED */ + this_spb /* REQUIRED */ + ) + + /* rsConfig register for stream buffer (rsDMA reg. + is loaded from basicReq.daw for incoming streams, or + basicReq.saw, for outgoing streams) + + 31 30 29 [28:24] [23:16] 15 14 13 12 11 10 9 8 7 6 5 4 [3:0] + ______________________________________________________________________________ + |DMA |D|maxDMAsize| streamNum|dir|p| | | | | | |ds |shr 1|rev Cy | mod | + |prio |_|__________|__________|___|_|__|__|__|__|_|_|___|_____|_______|_______| + 31 30 29 [28:24] [23:16] 15 14 13 12 11 10 9 8 7 6 5 4 [3:0] + + + Initialized by the host R/O for the DSP task + */ + u32 strm_rs_config; /* REQUIRED */ + // + /* On mixer input streams: indicates mixer input stream configuration + On Tees, this is copied from the stream being snooped + + Stream sample pointer & MAC-unit mode for this stream + + Initialized by the host Updated by the DSP task + */ + u32 strm_buf_ptr; /* REQUIRED */ + + /* On mixer input streams: points to next mixer input and is updated by the + mixer subroutine in the "parent" DSP task + (least-significant 16 bits are preserved, unused) + + On Tees, the pointer is copied from the stream being snooped on + initialization, and, subsequently, it is copied into the + stream being snooped. + + On wavetable/3D voices: the strmBufPtr will use all 32 bits to allow for + fractional phase accumulation + + Fractional increment per output sample in the input sample buffer + + (Not used on mixer input streams & redefined on Tees) + On wavetable/3D voices: this 32-bit word specifies the integer.fractional + increment per output sample. + */ + u32 strmPhiIncr; + + + /* Standard stereo volume control + Initialized by the host (host updates target volumes) + + Current volumes update by the DSP task + On mixer input streams: required & updated by the mixer subroutine in the + "parent" DSP task + + On Tees, both current & target volumes are copied up on initialization, + and, subsequently, the target volume is copied up while the current + volume is copied down. + + These two 32-bit words are redefined for wavetable & 3-D voices. + */ + struct dsp_volume_control vol_ctrl_t; /* Optional */ +}; + + +struct dsp_spos_control_block { + /* WARNING: Certain items in this structure are modified by the host + Any dword that can be modified by the host, must not be + modified by the SP as the host can only do atomic dword + writes, and to do otherwise, even a read modify write, + may lead to corrupted data on the SP. + + This rule does not apply to one off boot time initialisation prior to starting the SP + */ + + + ___DSP_DUAL_16BIT_ALLOC( + /* First element on the Hyper forground task tree */ + hfg_tree_root_ptr, /* HOST */ + /* First 3 dwords are written by the host and read-only on the DSP */ + hfg_stack_base /* HOST */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + /* Point to this data structure to enable easy access */ + spos_cb_ptr, /* SP */ + prev_task_tree_ptr /* SP && HOST */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + /* Currently Unused */ + xxinterval_timer_period, + /* Enable extension of SPOS data structure */ + HFGSPB_ptr + ) + + + ___DSP_DUAL_16BIT_ALLOC( + xxnum_HFG_ticks_thisInterval, + /* Modified by the DSP */ + xxnum_tntervals + ) + + + /* Set by DSP upon encountering a trap (breakpoint) or a spurious + interrupt. The host must clear this dword after reading it + upon receiving spInt1. */ + ___DSP_DUAL_16BIT_ALLOC( + spurious_int_flag, /* (Host & SP) Nature of the spurious interrupt */ + trap_flag /* (Host & SP) Nature of detected Trap */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + unused2, + invalid_IP_flag /* (Host & SP ) Indicate detection of invalid instruction pointer */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + /* pointer to forground task tree header for use in next task search */ + fg_task_tree_hdr_ptr, /* HOST */ + /* Data structure for controlling synchronous link update */ + hfg_sync_update_ptr /* HOST */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + begin_foreground_FCNT, /* SP */ + /* Place holder for holding sleep timing */ + last_FCNT_before_sleep /* SP */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + unused7, /* SP */ + next_task_treePtr /* SP */ + ) + + u32 unused5; + + ___DSP_DUAL_16BIT_ALLOC( + active_flags, /* SP */ + /* State flags, used to assist control of execution of Hyper Forground */ + HFG_flags /* SP */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + unused9, + unused8 + ) + + /* Space for saving enough context so that we can set up enough + to save some more context. + */ + u32 rFE_save_for_invalid_IP; + u32 r32_save_for_spurious_int; + u32 r32_save_for_trap; + u32 r32_save_for_HFG; +}; + +/* SPB for MIX_TO_OSTREAM algorithm family */ +struct dsp_mix2_ostream_spb +{ + /* 16b.16b integer.frac approximation to the + number of 3 sample triplets to output each + frame. (approximation must be floor, to + insure that the fractional error is always + positive) + */ + u32 outTripletsPerFrame; + + /* 16b.16b integer.frac accumulated number of + output triplets since the start of group + */ + u32 accumOutTriplets; +}; + +/* SCB for Timing master algorithm */ +struct dsp_timing_master_scb { + /* First 12 dwords from generic_scb_t */ + struct dsp_basic_dma_req basic_req; /* Optional */ + struct dsp_scatter_gather_ext sg_ext; /* Optional */ + ___DSP_DUAL_16BIT_ALLOC( + next_scb, /* REQUIRED */ + sub_list_ptr /* REQUIRED */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + entry_point, /* REQUIRED */ + this_spb /* REQUIRED */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + /* Initial values are 0000:xxxx */ + reserved, + extra_sample_accum + ) + + + /* Initial values are xxxx:0000 + hi: Current CODEC output FIFO pointer + (0 to 0x0f) + lo: Flag indicating that the CODEC + FIFO is sync'd (host clears to + resynchronize the FIFO pointer + upon start/restart) + */ + ___DSP_DUAL_16BIT_ALLOC( + codec_FIFO_syncd, + codec_FIFO_ptr + ) + + /* Init. 8000:0005 for 44.1k + 8000:0001 for 48k + hi: Fractional sample accumulator 0.16b + lo: Number of frames remaining to be + processed in the current group of + frames + */ + ___DSP_DUAL_16BIT_ALLOC( + frac_samp_accum_qm1, + TM_frms_left_in_group + ) + + /* Init. 0001:0005 for 44.1k + 0000:0001 for 48k + hi: Fractional sample correction factor 0.16b + to be added every frameGroupLength frames + to correct for truncation error in + nsamp_per_frm_q15 + lo: Number of frames in the group + */ + ___DSP_DUAL_16BIT_ALLOC( + frac_samp_correction_qm1, + TM_frm_group_length + ) + + /* Init. 44.1k*65536/8k = 0x00058333 for 44.1k + 48k*65536/8k = 0x00060000 for 48k + 16b.16b integer.frac approximation to the + number of samples to output each frame. + (approximation must be floor, to insure */ + u32 nsamp_per_frm_q15; +}; + +/* SCB for CODEC output algorithm */ +struct dsp_codec_output_scb { + /* First 13 dwords from generic_scb_t */ + struct dsp_basic_dma_req basic_req; /* Optional */ + struct dsp_scatter_gather_ext sg_ext; /* Optional */ + ___DSP_DUAL_16BIT_ALLOC( + next_scb, /* REQUIRED */ + sub_list_ptr /* REQUIRED */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + entry_point, /* REQUIRED */ + this_spb /* REQUIRED */ + ) + + u32 strm_rs_config; /* REQUIRED */ + + u32 strm_buf_ptr; /* REQUIRED */ + + /* NOTE: The CODEC output task reads samples from the first task on its + sublist at the stream buffer pointer (init. to lag DMA destination + address word). After the required number of samples is transferred, + the CODEC output task advances sub_list_ptr->strm_buf_ptr past the samples + consumed. + */ + + /* Init. 0000:0010 for SDout + 0060:0010 for SDout2 + 0080:0010 for SDout3 + hi: Base IO address of FIFO to which + the left-channel samples are to + be written. + lo: Displacement for the base IO + address for left-channel to obtain + the base IO address for the FIFO + to which the right-channel samples + are to be written. + */ + ___DSP_DUAL_16BIT_ALLOC( + left_chan_base_IO_addr, + right_chan_IO_disp + ) + + + /* Init: 0x0080:0004 for non-AC-97 + Init: 0x0080:0000 for AC-97 + hi: Exponential volume change rate + for input stream + lo: Positive shift count to shift the + 16-bit input sample to obtain the + 32-bit output word + */ + ___DSP_DUAL_16BIT_ALLOC( + CO_scale_shift_count, + CO_exp_vol_change_rate + ) + + /* Pointer to SCB at end of input chain */ + ___DSP_DUAL_16BIT_ALLOC( + reserved, + last_sub_ptr + ) +}; + +/* SCB for CODEC input algorithm */ +struct dsp_codec_input_scb { + /* First 13 dwords from generic_scb_t */ + struct dsp_basic_dma_req basic_req; /* Optional */ + struct dsp_scatter_gather_ext sg_ext; /* Optional */ + ___DSP_DUAL_16BIT_ALLOC( + next_scb, /* REQUIRED */ + sub_list_ptr /* REQUIRED */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + entry_point, /* REQUIRED */ + this_spb /* REQUIRED */ + ) + + u32 strm_rs_config; /* REQUIRED */ + u32 strm_buf_ptr; /* REQUIRED */ + + /* NOTE: The CODEC input task reads samples from the hardware FIFO + sublist at the DMA source address word (sub_list_ptr->basic_req.saw). + After the required number of samples is transferred, the CODEC + output task advances sub_list_ptr->basic_req.saw past the samples + consumed. SPuD must initialize the sub_list_ptr->basic_req.saw + to point half-way around from the initial sub_list_ptr->strm_nuf_ptr + to allow for lag/lead. + */ + + /* Init. 0000:0010 for SDout + 0060:0010 for SDout2 + 0080:0010 for SDout3 + hi: Base IO address of FIFO to which + the left-channel samples are to + be written. + lo: Displacement for the base IO + address for left-channel to obtain + the base IO address for the FIFO + to which the right-channel samples + are to be written. + */ + ___DSP_DUAL_16BIT_ALLOC( + rightChanINdisp, + left_chan_base_IN_addr + ) + /* Init. ?:fffc + lo: Negative shift count to shift the + 32-bit input dword to obtain the + 16-bit sample msb-aligned (count + is negative to shift left) + */ + ___DSP_DUAL_16BIT_ALLOC( + scaleShiftCount, + reserver1 + ) + + u32 reserved2; +}; + + +struct dsp_pcm_serial_input_scb { + /* First 13 dwords from generic_scb_t */ + struct dsp_basic_dma_req basic_req; /* Optional */ + struct dsp_scatter_gather_ext sg_ext; /* Optional */ + ___DSP_DUAL_16BIT_ALLOC( + next_scb, /* REQUIRED */ + sub_list_ptr /* REQUIRED */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + entry_point, /* REQUIRED */ + this_spb /* REQUIRED */ + ) + + u32 strm_buf_ptr; /* REQUIRED */ + u32 strm_rs_config; /* REQUIRED */ + + /* Init. Ptr to CODEC input SCB + hi: Pointer to the SCB containing the + input buffer to which CODEC input + samples are written + lo: Flag indicating the link to the CODEC + input task is to be initialized + */ + ___DSP_DUAL_16BIT_ALLOC( + init_codec_input_link, + codec_input_buf_scb + ) + + /* Initialized by the host (host updates target volumes) */ + struct dsp_volume_control psi_vol_ctrl; + +}; + +struct dsp_src_task_scb { + ___DSP_DUAL_16BIT_ALLOC( + frames_left_in_gof, + gofs_left_in_sec + ) + + ___DSP_DUAL_16BIT_ALLOC( + const2_thirds, + num_extra_tnput_samples + ) + + ___DSP_DUAL_16BIT_ALLOC( + cor_per_gof, + correction_per_sec + ) + + ___DSP_DUAL_16BIT_ALLOC( + output_buf_producer_ptr, + junk_DMA_MID + ) + + ___DSP_DUAL_16BIT_ALLOC( + gof_length, + gofs_per_sec + ) + + u32 input_buf_strm_config; + + ___DSP_DUAL_16BIT_ALLOC( + reserved_for_SRC_use, + input_buf_consumer_ptr + ) + + u32 accum_phi; + + ___DSP_DUAL_16BIT_ALLOC( + exp_src_vol_change_rate, + input_buf_producer_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + src_next_scb, + src_sub_list_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + src_entry_point, + src_this_sbp + ) + + u32 src_strm_rs_config; + u32 src_strm_buf_ptr; + + u32 phiIncr6int_26frac; + + struct dsp_volume_control src_vol_ctrl; +}; + +struct dsp_decimate_by_pow2_scb { + /* decimationFactor = 2, 4, or 8 (larger factors waste too much memory + when compared to cascading decimators) + */ + ___DSP_DUAL_16BIT_ALLOC( + dec2_coef_base_ptr, + dec2_coef_increment + ) + + /* coefIncrement = 128 / decimationFactor (for our ROM filter) + coefBasePtr = 0x8000 (for our ROM filter) + */ + ___DSP_DUAL_16BIT_ALLOC( + dec2_in_samples_per_out_triplet, + dec2_extra_in_samples + ) + /* extraInSamples: # of accumulated, unused input samples (init. to 0) + inSamplesPerOutTriplet = 3 * decimationFactor + */ + + ___DSP_DUAL_16BIT_ALLOC( + dec2_const2_thirds, + dec2_half_num_taps_mp5 + ) + /* halfNumTapsM5: (1/2 number of taps in decimation filter) minus 5 + const2thirds: constant 2/3 in 16Q0 format (sign.15) + */ + + ___DSP_DUAL_16BIT_ALLOC( + dec2_output_buf_producer_ptr, + dec2_junkdma_mid + ) + + u32 dec2_reserved2; + + u32 dec2_input_nuf_strm_config; + /* inputBufStrmConfig: rsConfig for the input buffer to the decimator + (buffer size = decimationFactor * 32 dwords) + */ + + ___DSP_DUAL_16BIT_ALLOC( + dec2_phi_incr, + dec2_input_buf_consumer_ptr + ) + /* inputBufConsumerPtr: Input buffer read pointer (into SRC filter) + phiIncr = decimationFactor * 4 + */ + + u32 dec2_reserved3; + + ___DSP_DUAL_16BIT_ALLOC( + dec2_exp_vol_change_rate, + dec2_input_buf_producer_ptr + ) + /* inputBufProducerPtr: Input buffer write pointer + expVolChangeRate: Exponential volume change rate for possible + future mixer on input streams + */ + + ___DSP_DUAL_16BIT_ALLOC( + dec2_next_scb, + dec2_sub_list_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + dec2_entry_point, + dec2_this_spb + ) + + u32 dec2_strm_rs_config; + u32 dec2_strm_buf_ptr; + + u32 dec2_reserved4; + + struct dsp_volume_control dec2_vol_ctrl; /* Not used! */ +}; + +struct dsp_vari_decimate_scb { + ___DSP_DUAL_16BIT_ALLOC( + vdec_frames_left_in_gof, + vdec_gofs_left_in_sec + ) + + ___DSP_DUAL_16BIT_ALLOC( + vdec_const2_thirds, + vdec_extra_in_samples + ) + /* extraInSamples: # of accumulated, unused input samples (init. to 0) + const2thirds: constant 2/3 in 16Q0 format (sign.15) */ + + ___DSP_DUAL_16BIT_ALLOC( + vdec_cor_per_gof, + vdec_correction_per_sec + ) + + ___DSP_DUAL_16BIT_ALLOC( + vdec_output_buf_producer_ptr, + vdec_input_buf_consumer_ptr + ) + /* inputBufConsumerPtr: Input buffer read pointer (into SRC filter) */ + ___DSP_DUAL_16BIT_ALLOC( + vdec_gof_length, + vdec_gofs_per_sec + ) + + u32 vdec_input_buf_strm_config; + /* inputBufStrmConfig: rsConfig for the input buffer to the decimator + (buffer size = 64 dwords) */ + u32 vdec_coef_increment; + /* coefIncrement = - 128.0 / decimationFactor (as a 32Q15 number) */ + + u32 vdec_accumphi; + /* accumPhi: accumulated fractional phase increment (6.26) */ + + ___DSP_DUAL_16BIT_ALLOC( + vdec_exp_vol_change_rate, + vdec_input_buf_producer_ptr + ) + /* inputBufProducerPtr: Input buffer write pointer + expVolChangeRate: Exponential volume change rate for possible + future mixer on input streams */ + + ___DSP_DUAL_16BIT_ALLOC( + vdec_next_scb, + vdec_sub_list_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + vdec_entry_point, + vdec_this_spb + ) + + u32 vdec_strm_rs_config; + u32 vdec_strm_buf_ptr; + + u32 vdec_phi_incr_6int_26frac; + + struct dsp_volume_control vdec_vol_ctrl; +}; + + +/* SCB for MIX_TO_OSTREAM algorithm family */ +struct dsp_mix2_ostream_scb { + /* First 13 dwords from generic_scb_t */ + struct dsp_basic_dma_req basic_req; /* Optional */ + struct dsp_scatter_gather_ext sg_ext; /* Optional */ + ___DSP_DUAL_16BIT_ALLOC( + next_scb, /* REQUIRED */ + sub_list_ptr /* REQUIRED */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + entry_point, /* REQUIRED */ + this_spb /* REQUIRED */ + ) + + u32 strm_rs_config; /* REQUIRED */ + u32 strm_buf_ptr; /* REQUIRED */ + + + /* hi: Number of mixed-down input triplets + computed since start of group + lo: Number of frames remaining to be + processed in the current group of + frames + */ + ___DSP_DUAL_16BIT_ALLOC( + frames_left_in_group, + accum_input_triplets + ) + + /* hi: Exponential volume change rate + for mixer on input streams + lo: Number of frames in the group + */ + ___DSP_DUAL_16BIT_ALLOC( + frame_group_length, + exp_vol_change_rate + ) + + ___DSP_DUAL_16BIT_ALLOC( + const_FFFF, + const_zero + ) +}; + + +/* SCB for S16_MIX algorithm */ +struct dsp_mix_only_scb { + /* First 13 dwords from generic_scb_t */ + struct dsp_basic_dma_req basic_req; /* Optional */ + struct dsp_scatter_gather_ext sg_ext; /* Optional */ + ___DSP_DUAL_16BIT_ALLOC( + next_scb, /* REQUIRED */ + sub_list_ptr /* REQUIRED */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + entry_point, /* REQUIRED */ + this_spb /* REQUIRED */ + ) + + u32 strm_rs_config; /* REQUIRED */ + u32 strm_buf_ptr; /* REQUIRED */ + + u32 reserved; + struct dsp_volume_control vol_ctrl; +}; + +/* SCB for the async. CODEC input algorithm */ +struct dsp_async_codec_input_scb { + u32 io_free2; + + u32 io_current_total; + u32 io_previous_total; + + u16 io_count; + u16 io_count_limit; + + u16 o_fifo_base_addr; + u16 ost_mo_format; + /* 1 = stereo; 0 = mono + xxx for ASER 1 (not allowed); 118 for ASER2 */ + + u32 ostrm_rs_config; + u32 ostrm_buf_ptr; + + ___DSP_DUAL_16BIT_ALLOC( + io_sclks_per_lr_clk, + io_io_enable + ) + + u32 io_free4; + + ___DSP_DUAL_16BIT_ALLOC( + io_next_scb, + io_sub_list_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + io_entry_point, + io_this_spb + ) + + u32 istrm_rs_config; + u32 istrm_buf_ptr; + + /* Init. 0000:8042: for ASER1 + 0000:8044: for ASER2 */ + ___DSP_DUAL_16BIT_ALLOC( + io_stat_reg_addr, + iofifo_pointer + ) + + /* Init 1 stero:100 ASER1 + Init 0 mono:110 ASER2 + */ + ___DSP_DUAL_16BIT_ALLOC( + ififo_base_addr, + ist_mo_format + ) + + u32 i_free; +}; + + +/* SCB for the SP/DIF CODEC input and output */ +struct dsp_spdifiscb { + ___DSP_DUAL_16BIT_ALLOC( + status_ptr, + status_start_ptr + ) + + u32 current_total; + u32 previous_total; + + ___DSP_DUAL_16BIT_ALLOC( + count, + count_limit + ) + + u32 status_data; + + ___DSP_DUAL_16BIT_ALLOC( + status, + free4 + ) + + u32 free3; + + ___DSP_DUAL_16BIT_ALLOC( + free2, + bit_count + ) + + u32 temp_status; + + ___DSP_DUAL_16BIT_ALLOC( + next_SCB, + sub_list_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + entry_point, + this_spb + ) + + u32 strm_rs_config; + u32 strm_buf_ptr; + + ___DSP_DUAL_16BIT_ALLOC( + stat_reg_addr, + fifo_pointer + ) + + ___DSP_DUAL_16BIT_ALLOC( + fifo_base_addr, + st_mo_format + ) + + u32 free1; +}; + + +/* SCB for the SP/DIF CODEC input and output */ +struct dsp_spdifoscb { + + u32 free2; + + u32 free3[4]; + + /* Need to be here for compatibility with AsynchFGTxCode */ + u32 strm_rs_config; + + u32 strm_buf_ptr; + + ___DSP_DUAL_16BIT_ALLOC( + status, + free5 + ) + + u32 free4; + + ___DSP_DUAL_16BIT_ALLOC( + next_scb, + sub_list_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + entry_point, + this_spb + ) + + u32 free6[2]; + + ___DSP_DUAL_16BIT_ALLOC( + stat_reg_addr, + fifo_pointer + ) + + ___DSP_DUAL_16BIT_ALLOC( + fifo_base_addr, + st_mo_format + ) + + u32 free1; +}; + + +struct dsp_asynch_fg_rx_scb { + ___DSP_DUAL_16BIT_ALLOC( + bot_buf_mask, + buf_Mask + ) + + ___DSP_DUAL_16BIT_ALLOC( + max, + min + ) + + ___DSP_DUAL_16BIT_ALLOC( + old_producer_pointer, + hfg_scb_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + delta, + adjust_count + ) + + u32 unused2[5]; + + ___DSP_DUAL_16BIT_ALLOC( + sibling_ptr, + child_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + code_ptr, + this_ptr + ) + + u32 strm_rs_config; + + u32 strm_buf_ptr; + + u32 unused_phi_incr; + + ___DSP_DUAL_16BIT_ALLOC( + right_targ, + left_targ + ) + + ___DSP_DUAL_16BIT_ALLOC( + right_vol, + left_vol + ) +}; + + +struct dsp_asynch_fg_tx_scb { + ___DSP_DUAL_16BIT_ALLOC( + not_buf_mask, + buf_mask + ) + + ___DSP_DUAL_16BIT_ALLOC( + max, + min + ) + + ___DSP_DUAL_16BIT_ALLOC( + unused1, + hfg_scb_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + delta, + adjust_count + ) + + u32 accum_phi; + + ___DSP_DUAL_16BIT_ALLOC( + unused2, + const_one_third + ) + + u32 unused3[3]; + + ___DSP_DUAL_16BIT_ALLOC( + sibling_ptr, + child_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + codePtr, + this_ptr + ) + + u32 strm_rs_config; + + u32 strm_buf_ptr; + + u32 phi_incr; + + ___DSP_DUAL_16BIT_ALLOC( + unused_right_targ, + unused_left_targ + ) + + ___DSP_DUAL_16BIT_ALLOC( + unused_right_vol, + unused_left_vol + ) +}; + + +struct dsp_output_snoop_scb { + /* First 13 dwords from generic_scb_t */ + struct dsp_basic_dma_req basic_req; /* Optional */ + struct dsp_scatter_gather_ext sg_ext; /* Optional */ + ___DSP_DUAL_16BIT_ALLOC( + next_scb, /* REQUIRED */ + sub_list_ptr /* REQUIRED */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + entry_point, /* REQUIRED */ + this_spb /* REQUIRED */ + ) + + u32 strm_rs_config; /* REQUIRED */ + u32 strm_buf_ptr; /* REQUIRED */ + + ___DSP_DUAL_16BIT_ALLOC( + init_snoop_input_link, + snoop_child_input_scb + ) + + u32 snoop_input_buf_ptr; + + ___DSP_DUAL_16BIT_ALLOC( + reserved, + input_scb + ) +}; + +struct dsp_spio_write_scb { + ___DSP_DUAL_16BIT_ALLOC( + address1, + address2 + ) + + u32 data1; + + u32 data2; + + ___DSP_DUAL_16BIT_ALLOC( + address3, + address4 + ) + + u32 data3; + + u32 data4; + + ___DSP_DUAL_16BIT_ALLOC( + unused1, + data_ptr + ) + + u32 unused2[2]; + + ___DSP_DUAL_16BIT_ALLOC( + sibling_ptr, + child_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + entry_point, + this_ptr + ) + + u32 unused3[5]; +}; + +struct dsp_magic_snoop_task { + u32 i0; + u32 i1; + + u32 strm_buf_ptr1; + + u16 i2; + u16 snoop_scb; + + u32 i3; + u32 i4; + u32 i5; + u32 i6; + + u32 i7; + + ___DSP_DUAL_16BIT_ALLOC( + next_scb, + sub_list_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + entry_point, + this_ptr + ) + + u32 strm_buf_config; + u32 strm_buf_ptr2; + + u32 i8; + + struct dsp_volume_control vdec_vol_ctrl; +}; + + +struct dsp_filter_scb { + ___DSP_DUAL_16BIT_ALLOC( + a0_right, /* 0x00 */ + a0_left + ) + ___DSP_DUAL_16BIT_ALLOC( + a1_right, /* 0x01 */ + a1_left + ) + ___DSP_DUAL_16BIT_ALLOC( + a2_right, /* 0x02 */ + a2_left + ) + ___DSP_DUAL_16BIT_ALLOC( + output_buf_ptr, /* 0x03 */ + init + ) + + ___DSP_DUAL_16BIT_ALLOC( + filter_unused3, /* 0x04 */ + filter_unused2 + ) + + u32 prev_sample_output1; /* 0x05 */ + u32 prev_sample_output2; /* 0x06 */ + u32 prev_sample_input1; /* 0x07 */ + u32 prev_sample_input2; /* 0x08 */ + + ___DSP_DUAL_16BIT_ALLOC( + next_scb_ptr, /* 0x09 */ + sub_list_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + entry_point, /* 0x0A */ + spb_ptr + ) + + u32 strm_rs_config; /* 0x0B */ + u32 strm_buf_ptr; /* 0x0C */ + + ___DSP_DUAL_16BIT_ALLOC( + b0_right, /* 0x0D */ + b0_left + ) + ___DSP_DUAL_16BIT_ALLOC( + b1_right, /* 0x0E */ + b1_left + ) + ___DSP_DUAL_16BIT_ALLOC( + b2_right, /* 0x0F */ + b2_left + ) +}; +#endif /* __DSP_SCB_TYPES_H__ */ diff --git a/sound/pci/cs46xx/cs46xx_dsp_spos.h b/sound/pci/cs46xx/cs46xx_dsp_spos.h new file mode 100644 index 000000000000..8008c59288a6 --- /dev/null +++ b/sound/pci/cs46xx/cs46xx_dsp_spos.h @@ -0,0 +1,234 @@ +/* + * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards + * Copyright (c) by 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 + * + */ + +#ifndef __CS46XX_DSP_SPOS_H__ +#define __CS46XX_DSP_SPOS_H__ + +#include "cs46xx_dsp_scb_types.h" +#include "cs46xx_dsp_task_types.h" + +#define SYMBOL_CONSTANT 0x0 +#define SYMBOL_SAMPLE 0x1 +#define SYMBOL_PARAMETER 0x2 +#define SYMBOL_CODE 0x3 + +#define SEGTYPE_SP_PROGRAM 0x00000001 +#define SEGTYPE_SP_PARAMETER 0x00000002 +#define SEGTYPE_SP_SAMPLE 0x00000003 +#define SEGTYPE_SP_COEFFICIENT 0x00000004 + +#define DSP_SPOS_UU 0x0deadul /* unused */ +#define DSP_SPOS_DC 0x0badul /* don't care */ +#define DSP_SPOS_DC_DC 0x0bad0badul /* don't care */ +#define DSP_SPOS_UUUU 0xdeadc0edul /* unused */ +#define DSP_SPOS_UUHI 0xdeadul +#define DSP_SPOS_UULO 0xc0edul +#define DSP_SPOS_DCDC 0x0badf1d0ul /* don't care */ +#define DSP_SPOS_DCDCHI 0x0badul +#define DSP_SPOS_DCDCLO 0xf1d0ul + +#define DSP_MAX_TASK_NAME 60 +#define DSP_MAX_SYMBOL_NAME 100 +#define DSP_MAX_SCB_NAME 60 +#define DSP_MAX_SCB_DESC 200 +#define DSP_MAX_TASK_DESC 50 + +#define DSP_MAX_PCM_CHANNELS 32 +#define DSP_MAX_SRC_NR 14 + +#define DSP_PCM_MAIN_CHANNEL 1 +#define DSP_PCM_REAR_CHANNEL 2 +#define DSP_PCM_CENTER_LFE_CHANNEL 3 +#define DSP_PCM_S71_CHANNEL 4 /* surround 7.1 */ +#define DSP_IEC958_CHANNEL 5 + +#define DSP_SPDIF_STATUS_OUTPUT_ENABLED 1 +#define DSP_SPDIF_STATUS_PLAYBACK_OPEN 2 +#define DSP_SPDIF_STATUS_HW_ENABLED 4 +#define DSP_SPDIF_STATUS_INPUT_CTRL_ENABLED 8 + +struct dsp_symbol_entry { + u32 address; + char symbol_name[DSP_MAX_SYMBOL_NAME]; + int symbol_type; + + /* initialized by driver */ + struct dsp_module_desc * module; + int deleted; +}; + +struct dsp_symbol_desc { + int nsymbols; + + struct dsp_symbol_entry *symbols; + + /* initialized by driver */ + int highest_frag_index; +}; + +struct dsp_segment_desc { + int segment_type; + u32 offset; + u32 size; + u32 * data; +}; + +struct dsp_module_desc { + char * module_name; + struct dsp_symbol_desc symbol_table; + int nsegments; + struct dsp_segment_desc * segments; + + /* initialized by driver */ + u32 overlay_begin_address; + u32 load_address; + int nfixups; +}; + +struct dsp_scb_descriptor { + char scb_name[DSP_MAX_SCB_NAME]; + u32 address; + int index; + u32 *data; + + struct dsp_scb_descriptor * sub_list_ptr; + struct dsp_scb_descriptor * next_scb_ptr; + struct dsp_scb_descriptor * parent_scb_ptr; + + struct dsp_symbol_entry * task_entry; + struct dsp_symbol_entry * scb_symbol; + + struct snd_info_entry *proc_info; + int ref_count; + + u16 volume[2]; + unsigned int deleted :1; + unsigned int updated :1; + unsigned int volume_set :1; +}; + +struct dsp_task_descriptor { + char task_name[DSP_MAX_TASK_NAME]; + int size; + u32 address; + int index; + u32 *data; +}; + +struct dsp_pcm_channel_descriptor { + int active; + int src_slot; + int pcm_slot; + u32 sample_rate; + u32 unlinked; + struct dsp_scb_descriptor * pcm_reader_scb; + struct dsp_scb_descriptor * src_scb; + struct dsp_scb_descriptor * mixer_scb; + + void * private_data; +}; + +struct dsp_spos_instance { + struct dsp_symbol_desc symbol_table; /* currently available loaded symbols in SP */ + + int nmodules; + struct dsp_module_desc * modules; /* modules loaded into SP */ + + struct dsp_segment_desc code; + + /* Main PCM playback mixer */ + struct dsp_scb_descriptor * master_mix_scb; + u16 dac_volume_right; + u16 dac_volume_left; + + /* Rear/surround PCM playback mixer */ + struct dsp_scb_descriptor * rear_mix_scb; + + /* Center/LFE mixer */ + struct dsp_scb_descriptor * center_lfe_mix_scb; + + int npcm_channels; + int nsrc_scb; + struct dsp_pcm_channel_descriptor pcm_channels[DSP_MAX_PCM_CHANNELS]; + int src_scb_slots[DSP_MAX_SRC_NR]; + + /* cache this symbols */ + struct dsp_symbol_entry * null_algorithm; /* used by PCMreaderSCB's */ + struct dsp_symbol_entry * s16_up; /* used by SRCtaskSCB's */ + + /* proc fs */ + struct snd_card *snd_card; + struct snd_info_entry * proc_dsp_dir; + struct snd_info_entry * proc_sym_info_entry; + struct snd_info_entry * proc_modules_info_entry; + struct snd_info_entry * proc_parameter_dump_info_entry; + struct snd_info_entry * proc_sample_dump_info_entry; + + /* SCB's descriptors */ + int nscb; + int scb_highest_frag_index; + struct dsp_scb_descriptor scbs[DSP_MAX_SCB_DESC]; + struct snd_info_entry * proc_scb_info_entry; + struct dsp_scb_descriptor * the_null_scb; + + /* Task's descriptors */ + int ntask; + struct dsp_task_descriptor tasks[DSP_MAX_TASK_DESC]; + struct snd_info_entry * proc_task_info_entry; + + /* SPDIF status */ + int spdif_status_out; + int spdif_status_in; + u16 spdif_input_volume_right; + u16 spdif_input_volume_left; + /* spdif channel status, + left right and user validity bits */ + unsigned int spdif_csuv_default; + unsigned int spdif_csuv_stream; + + /* SPDIF input sample rate converter */ + struct dsp_scb_descriptor * spdif_in_src; + /* SPDIF input asynch. receiver */ + struct dsp_scb_descriptor * asynch_rx_scb; + + /* Capture record mixer SCB */ + struct dsp_scb_descriptor * record_mixer_scb; + + /* CODEC input SCB */ + struct dsp_scb_descriptor * codec_in_scb; + + /* reference snooper */ + struct dsp_scb_descriptor * ref_snoop_scb; + + /* SPDIF output PCM reference */ + struct dsp_scb_descriptor * spdif_pcm_input_scb; + + /* asynch TX task */ + struct dsp_scb_descriptor * asynch_tx_scb; + + /* record sources */ + struct dsp_scb_descriptor * pcm_input; + struct dsp_scb_descriptor * adc_input; + + int spdif_in_sample_rate; +}; + +#endif /* __DSP_SPOS_H__ */ diff --git a/sound/pci/cs46xx/cs46xx_dsp_task_types.h b/sound/pci/cs46xx/cs46xx_dsp_task_types.h new file mode 100644 index 000000000000..5cf920bfda27 --- /dev/null +++ b/sound/pci/cs46xx/cs46xx_dsp_task_types.h @@ -0,0 +1,252 @@ +/* + * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards + * Copyright (c) by 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 + * + * + * NOTE: comments are copy/paste from cwcemb80.lst + * provided by Tom Woller at Cirrus (my only + * documentation about the SP OS running inside + * the DSP) + */ + +#ifndef __CS46XX_DSP_TASK_TYPES_H__ +#define __CS46XX_DSP_TASK_TYPES_H__ + +#include "cs46xx_dsp_scb_types.h" + +/********************************************************************************************* +Example hierarchy of stream control blocks in the SP + +hfgTree +Ptr____Call (c) + \ + -------+------ ------------- ------------- ------------- ----- +| SBlaster IF |______\| Foreground |___\| Middlegr'nd |___\| Background |___\| Nul | +| |Goto /| tree header |g /| tree header |g /| tree header |g /| SCB |r + -------------- (g) ------------- ------------- ------------- ----- + |c |c |c |c + | | | | + \/ ------------- ------------- ------------- + | Foreground |_\ | Middlegr'nd |_\ | Background |_\ + | tree |g/ | tree |g/ | tree |g/ + ------------- ------------- ------------- + |c |c |c + | | | + \/ \/ \/ + +*********************************************************************************************/ + +#define HFG_FIRST_EXECUTE_MODE 0x0001 +#define HFG_FIRST_EXECUTE_MODE_BIT 0 +#define HFG_CONTEXT_SWITCH_MODE 0x0002 +#define HFG_CONTEXT_SWITCH_MODE_BIT 1 + +#define MAX_FG_STACK_SIZE 32 /* THESE NEED TO BE COMPUTED PROPERLY */ +#define MAX_MG_STACK_SIZE 16 +#define MAX_BG_STACK_SIZE 9 +#define MAX_HFG_STACK_SIZE 4 + +#define SLEEP_ACTIVE_INCREMENT 0 /* Enable task tree thread to go to sleep + This should only ever be used on the Background thread */ +#define STANDARD_ACTIVE_INCREMENT 1 /* Task tree thread normal operation */ +#define SUSPEND_ACTIVE_INCREMENT 2 /* Cause execution to suspend in the task tree thread + This should only ever be used on the Background thread */ + +#define HOSTFLAGS_DISABLE_BG_SLEEP 0 /* Host-controlled flag that determines whether we go to sleep + at the end of BG */ + +/* Minimal context save area for Hyper Forground */ +struct dsp_hf_save_area { + u32 r10_save; + u32 r54_save; + u32 r98_save; + + ___DSP_DUAL_16BIT_ALLOC( + status_save, + ind_save + ) + + ___DSP_DUAL_16BIT_ALLOC( + rci1_save, + rci0_save + ) + + u32 r32_save; + u32 r76_save; + u32 rsd2_save; + + ___DSP_DUAL_16BIT_ALLOC( + rsi2_save, /* See TaskTreeParameterBlock for + remainder of registers */ + rsa2Save + ) + /* saved as part of HFG context */ +}; + + +/* Task link data structure */ +struct dsp_tree_link { + ___DSP_DUAL_16BIT_ALLOC( + /* Pointer to sibling task control block */ + next_scb, + /* Pointer to child task control block */ + sub_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + /* Pointer to code entry point */ + entry_point, + /* Pointer to local data */ + this_spb + ) +}; + + +struct dsp_task_tree_data { + ___DSP_DUAL_16BIT_ALLOC( + /* Initial tock count; controls task tree execution rate */ + tock_count_limit, + /* Tock down counter */ + tock_count + ) + + /* Add to ActiveCount when TockCountLimit reached: + Subtract on task tree termination */ + ___DSP_DUAL_16BIT_ALLOC( + active_tncrement, + /* Number of pending activations for task tree */ + active_count + ) + + ___DSP_DUAL_16BIT_ALLOC( + /* BitNumber to enable modification of correct bit in ActiveTaskFlags */ + active_bit, + /* Pointer to OS location for indicating current activity on task level */ + active_task_flags_ptr + ) + + /* Data structure for controlling movement of memory blocks:- + currently unused */ + ___DSP_DUAL_16BIT_ALLOC( + mem_upd_ptr, + /* Data structure for controlling synchronous link update */ + link_upd_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + /* Save area for remainder of full context. */ + save_area, + /* Address of start of local stack for data storage */ + data_stack_base_ptr + ) + +}; + + +struct dsp_interval_timer_data +{ + /* These data items have the same relative locations to those */ + ___DSP_DUAL_16BIT_ALLOC( + interval_timer_period, + itd_unused + ) + + /* used for this data in the SPOS control block for SPOS 1.0 */ + ___DSP_DUAL_16BIT_ALLOC( + num_FG_ticks_this_interval, + num_intervals + ) +}; + + +/* This structure contains extra storage for the task tree + Currently, this additional data is related only to a full context save */ +struct dsp_task_tree_context_block { + /* Up to 10 values are saved onto the stack. 8 for the task tree, 1 for + The access to the context switch (call or interrupt), and 1 spare that + users should never use. This last may be required by the system */ + ___DSP_DUAL_16BIT_ALLOC( + stack1, + stack0 + ) + ___DSP_DUAL_16BIT_ALLOC( + stack3, + stack2 + ) + ___DSP_DUAL_16BIT_ALLOC( + stack5, + stack4 + ) + ___DSP_DUAL_16BIT_ALLOC( + stack7, + stack6 + ) + ___DSP_DUAL_16BIT_ALLOC( + stack9, + stack8 + ) + + u32 saverfe; + + /* Value may be overwriten by stack save algorithm. + Retain the size of the stack data saved here if used */ + ___DSP_DUAL_16BIT_ALLOC( + reserved1, + stack_size + ) + u32 saverba; /* (HFG) */ + u32 saverdc; + u32 savers_config_23; /* (HFG) */ + u32 savers_DMA23; /* (HFG) */ + u32 saversa0; + u32 saversi0; + u32 saversa1; + u32 saversi1; + u32 saversa3; + u32 saversd0; + u32 saversd1; + u32 saversd3; + u32 savers_config01; + u32 savers_DMA01; + u32 saveacc0hl; + u32 saveacc1hl; + u32 saveacc0xacc1x; + u32 saveacc2hl; + u32 saveacc3hl; + u32 saveacc2xacc3x; + u32 saveaux0hl; + u32 saveaux1hl; + u32 saveaux0xaux1x; + u32 saveaux2hl; + u32 saveaux3hl; + u32 saveaux2xaux3x; + u32 savershouthl; + u32 savershoutxmacmode; +}; + + +struct dsp_task_tree_control_block { + struct dsp_hf_save_area context; + struct dsp_tree_link links; + struct dsp_task_tree_data data; + struct dsp_task_tree_context_block context_blk; + struct dsp_interval_timer_data int_timer; +}; + + +#endif /* __DSP_TASK_TYPES_H__ */ diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 28b9747becc9..f75f5ffdfdfb 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -61,7 +61,7 @@ #include #include #include -#include +#include "cs46xx.h" #include diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c index e377287192aa..56fec0bc0efb 100644 --- a/sound/pci/cs46xx/dsp_spos.c +++ b/sound/pci/cs46xx/dsp_spos.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include "cs46xx.h" #include "cs46xx_lib.h" #include "dsp_spos.h" diff --git a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c index 00b148a10239..c2c695b07f8c 100644 --- a/sound/pci/cs46xx/dsp_spos_scb_lib.c +++ b/sound/pci/cs46xx/dsp_spos_scb_lib.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include "cs46xx.h" #include "cs46xx_lib.h" #include "dsp_spos.h" diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c index f61346a555bb..d36e6ca147e1 100644 --- a/sound/pci/trident/trident.c +++ b/sound/pci/trident/trident.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include "trident.h" #include MODULE_AUTHOR("Jaroslav Kysela , "); diff --git a/sound/pci/trident/trident.h b/sound/pci/trident/trident.h new file mode 100644 index 000000000000..5f110eb56e47 --- /dev/null +++ b/sound/pci/trident/trident.h @@ -0,0 +1,444 @@ +#ifndef __SOUND_TRIDENT_H +#define __SOUND_TRIDENT_H + +/* + * audio@tridentmicro.com + * Fri Feb 19 15:55:28 MST 1999 + * Definitions for Trident 4DWave DX/NX chips + * + * + * 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 TRIDENT_DEVICE_ID_DX ((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_DX) +#define TRIDENT_DEVICE_ID_NX ((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_NX) +#define TRIDENT_DEVICE_ID_SI7018 ((PCI_VENDOR_ID_SI<<16)|PCI_DEVICE_ID_SI_7018) + +#define SNDRV_TRIDENT_VOICE_TYPE_PCM 0 +#define SNDRV_TRIDENT_VOICE_TYPE_SYNTH 1 +#define SNDRV_TRIDENT_VOICE_TYPE_MIDI 2 + +#define SNDRV_TRIDENT_VFLG_RUNNING (1<<0) + +/* TLB code constants */ +#define SNDRV_TRIDENT_PAGE_SIZE 4096 +#define SNDRV_TRIDENT_PAGE_SHIFT 12 +#define SNDRV_TRIDENT_PAGE_MASK ((1<port + (x)) + +#define ID_4DWAVE_DX 0x2000 +#define ID_4DWAVE_NX 0x2001 + +/* Bank definitions */ + +#define T4D_BANK_A 0 +#define T4D_BANK_B 1 +#define T4D_NUM_BANKS 2 + +/* Register definitions */ + +/* Global registers */ + +enum global_control_bits { + CHANNEL_IDX = 0x0000003f, + OVERRUN_IE = 0x00000400, /* interrupt enable: capture overrun */ + UNDERRUN_IE = 0x00000800, /* interrupt enable: playback underrun */ + ENDLP_IE = 0x00001000, /* interrupt enable: end of buffer */ + MIDLP_IE = 0x00002000, /* interrupt enable: middle buffer */ + ETOG_IE = 0x00004000, /* interrupt enable: envelope toggling */ + EDROP_IE = 0x00008000, /* interrupt enable: envelope drop */ + BANK_B_EN = 0x00010000, /* SiS: enable bank B (64 channels) */ + PCMIN_B_MIX = 0x00020000, /* SiS: PCM IN B mixing enable */ + I2S_OUT_ASSIGN = 0x00040000, /* SiS: I2S Out contains surround PCM */ + SPDIF_OUT_ASSIGN= 0x00080000, /* SiS: 0=S/PDIF L/R | 1=PCM Out FIFO */ + MAIN_OUT_ASSIGN = 0x00100000, /* SiS: 0=PCM Out FIFO | 1=MMC Out buffer */ +}; + +enum miscint_bits { + PB_UNDERRUN_IRQ = 0x00000001, REC_OVERRUN_IRQ = 0x00000002, + SB_IRQ = 0x00000004, MPU401_IRQ = 0x00000008, + OPL3_IRQ = 0x00000010, ADDRESS_IRQ = 0x00000020, + ENVELOPE_IRQ = 0x00000040, PB_UNDERRUN = 0x00000100, + REC_OVERRUN = 0x00000200, MIXER_UNDERFLOW = 0x00000400, + MIXER_OVERFLOW = 0x00000800, NX_SB_IRQ_DISABLE = 0x00001000, + ST_TARGET_REACHED = 0x00008000, + PB_24K_MODE = 0x00010000, ST_IRQ_EN = 0x00800000, + ACGPIO_IRQ = 0x01000000 +}; + +/* T2 legacy dma control registers. */ +#define LEGACY_DMAR0 0x00 // ADR0 +#define LEGACY_DMAR4 0x04 // CNT0 +#define LEGACY_DMAR6 0x06 // CNT0 - High bits +#define LEGACY_DMAR11 0x0b // MOD +#define LEGACY_DMAR15 0x0f // MMR + +#define T4D_START_A 0x80 +#define T4D_STOP_A 0x84 +#define T4D_DLY_A 0x88 +#define T4D_SIGN_CSO_A 0x8c +#define T4D_CSPF_A 0x90 +#define T4D_CSPF_B 0xbc +#define T4D_CEBC_A 0x94 +#define T4D_AINT_A 0x98 +#define T4D_AINTEN_A 0x9c +#define T4D_LFO_GC_CIR 0xa0 +#define T4D_MUSICVOL_WAVEVOL 0xa8 +#define T4D_SBDELTA_DELTA_R 0xac +#define T4D_MISCINT 0xb0 +#define T4D_START_B 0xb4 +#define T4D_STOP_B 0xb8 +#define T4D_SBBL_SBCL 0xc0 +#define T4D_SBCTRL_SBE2R_SBDD 0xc4 +#define T4D_STIMER 0xc8 +#define T4D_AINT_B 0xd8 +#define T4D_AINTEN_B 0xdc +#define T4D_RCI 0x70 + +/* MPU-401 UART */ +#define T4D_MPU401_BASE 0x20 +#define T4D_MPUR0 0x20 +#define T4D_MPUR1 0x21 +#define T4D_MPUR2 0x22 +#define T4D_MPUR3 0x23 + +/* S/PDIF Registers */ +#define NX_SPCTRL_SPCSO 0x24 +#define NX_SPLBA 0x28 +#define NX_SPESO 0x2c +#define NX_SPCSTATUS 0x64 + +/* Joystick */ +#define GAMEPORT_GCR 0x30 +#define GAMEPORT_MODE_ADC 0x80 +#define GAMEPORT_LEGACY 0x31 +#define GAMEPORT_AXES 0x34 + +/* NX Specific Registers */ +#define NX_TLBC 0x6c + +/* Channel Registers */ + +#define CH_START 0xe0 + +#define CH_DX_CSO_ALPHA_FMS 0xe0 +#define CH_DX_ESO_DELTA 0xe8 +#define CH_DX_FMC_RVOL_CVOL 0xec + +#define CH_NX_DELTA_CSO 0xe0 +#define CH_NX_DELTA_ESO 0xe8 +#define CH_NX_ALPHA_FMS_FMC_RVOL_CVOL 0xec + +#define CH_LBA 0xe4 +#define CH_GVSEL_PAN_VOL_CTRL_EC 0xf0 +#define CH_EBUF1 0xf4 +#define CH_EBUF2 0xf8 + +/* AC-97 Registers */ + +#define DX_ACR0_AC97_W 0x40 +#define DX_ACR1_AC97_R 0x44 +#define DX_ACR2_AC97_COM_STAT 0x48 + +#define NX_ACR0_AC97_COM_STAT 0x40 +#define NX_ACR1_AC97_W 0x44 +#define NX_ACR2_AC97_R_PRIMARY 0x48 +#define NX_ACR3_AC97_R_SECONDARY 0x4c + +#define SI_AC97_WRITE 0x40 +#define SI_AC97_READ 0x44 +#define SI_SERIAL_INTF_CTRL 0x48 +#define SI_AC97_GPIO 0x4c +#define SI_ASR0 0x50 +#define SI_SPDIF_CS 0x70 +#define SI_GPIO 0x7c + +enum trident_nx_ac97_bits { + /* ACR1-3 */ + NX_AC97_BUSY_WRITE = 0x0800, + NX_AC97_BUSY_READ = 0x0800, + NX_AC97_BUSY_DATA = 0x0400, + NX_AC97_WRITE_SECONDARY = 0x0100, + /* ACR0 */ + NX_AC97_SECONDARY_READY = 0x0040, + NX_AC97_SECONDARY_RECORD = 0x0020, + NX_AC97_SURROUND_OUTPUT = 0x0010, + NX_AC97_PRIMARY_READY = 0x0008, + NX_AC97_PRIMARY_RECORD = 0x0004, + NX_AC97_PCM_OUTPUT = 0x0002, + NX_AC97_WARM_RESET = 0x0001 +}; + +enum trident_dx_ac97_bits { + DX_AC97_BUSY_WRITE = 0x8000, + DX_AC97_BUSY_READ = 0x8000, + DX_AC97_READY = 0x0010, + DX_AC97_RECORD = 0x0008, + DX_AC97_PLAYBACK = 0x0002 +}; + +enum sis7018_ac97_bits { + SI_AC97_BUSY_WRITE = 0x00008000, + SI_AC97_AUDIO_BUSY = 0x00004000, + SI_AC97_MODEM_BUSY = 0x00002000, + SI_AC97_BUSY_READ = 0x00008000, + SI_AC97_SECONDARY = 0x00000080, +}; + +enum serial_intf_ctrl_bits { + WARM_RESET = 0x00000001, + COLD_RESET = 0x00000002, + I2S_CLOCK = 0x00000004, + PCM_SEC_AC97 = 0x00000008, + AC97_DBL_RATE = 0x00000010, + SPDIF_EN = 0x00000020, + I2S_OUTPUT_EN = 0x00000040, + I2S_INPUT_EN = 0x00000080, + PCMIN = 0x00000100, + LINE1IN = 0x00000200, + MICIN = 0x00000400, + LINE2IN = 0x00000800, + HEAD_SET_IN = 0x00001000, + GPIOIN = 0x00002000, + /* 7018 spec says id = 01 but the demo board routed to 10 + SECONDARY_ID= 0x00004000, */ + SECONDARY_ID = 0x00004000, + PCMOUT = 0x00010000, + SURROUT = 0x00020000, + CENTEROUT = 0x00040000, + LFEOUT = 0x00080000, + LINE1OUT = 0x00100000, + LINE2OUT = 0x00200000, + GPIOOUT = 0x00400000, + SI_AC97_PRIMARY_READY = 0x01000000, + SI_AC97_SECONDARY_READY = 0x02000000, + SI_AC97_POWERDOWN = 0x04000000, +}; + +/* PCM defaults */ + +#define T4D_DEFAULT_PCM_VOL 10 /* 0 - 255 */ +#define T4D_DEFAULT_PCM_PAN 0 /* 0 - 127 */ +#define T4D_DEFAULT_PCM_RVOL 127 /* 0 - 127 */ +#define T4D_DEFAULT_PCM_CVOL 127 /* 0 - 127 */ + +struct snd_trident; +struct snd_trident_voice; +struct snd_trident_pcm_mixer; + +struct snd_trident_port { + struct snd_midi_channel_set * chset; + struct snd_trident * trident; + int mode; /* operation mode */ + int client; /* sequencer client number */ + int port; /* sequencer port number */ + unsigned int midi_has_voices: 1; +}; + +struct snd_trident_memblk_arg { + short first_page, last_page; +}; + +struct snd_trident_tlb { + unsigned int * entries; /* 16k-aligned TLB table */ + dma_addr_t entries_dmaaddr; /* 16k-aligned PCI address to TLB table */ + unsigned long * shadow_entries; /* shadow entries with virtual addresses */ + struct snd_dma_buffer buffer; + struct snd_util_memhdr * memhdr; /* page allocation list */ + struct snd_dma_buffer silent_page; +}; + +struct snd_trident_voice { + unsigned int number; + unsigned int use: 1, + pcm: 1, + synth:1, + midi: 1; + unsigned int flags; + unsigned char client; + unsigned char port; + unsigned char index; + + struct snd_trident_sample_ops *sample_ops; + + /* channel parameters */ + unsigned int CSO; /* 24 bits (16 on DX) */ + unsigned int ESO; /* 24 bits (16 on DX) */ + unsigned int LBA; /* 30 bits */ + unsigned short EC; /* 12 bits */ + unsigned short Alpha; /* 12 bits */ + unsigned short Delta; /* 16 bits */ + unsigned short Attribute; /* 16 bits - SiS 7018 */ + unsigned short Vol; /* 12 bits (6.6) */ + unsigned char Pan; /* 7 bits (1.4.2) */ + unsigned char GVSel; /* 1 bit */ + unsigned char RVol; /* 7 bits (5.2) */ + unsigned char CVol; /* 7 bits (5.2) */ + unsigned char FMC; /* 2 bits */ + unsigned char CTRL; /* 4 bits */ + unsigned char FMS; /* 4 bits */ + unsigned char LFO; /* 8 bits */ + + unsigned int negCSO; /* nonzero - use negative CSO */ + + struct snd_util_memblk *memblk; /* memory block if TLB enabled */ + + /* PCM data */ + + struct snd_trident *trident; + struct snd_pcm_substream *substream; + struct snd_trident_voice *extra; /* extra PCM voice (acts as interrupt generator) */ + unsigned int running: 1, + capture: 1, + spdif: 1, + foldback: 1, + isync: 1, + isync2: 1, + isync3: 1; + int foldback_chan; /* foldback subdevice number */ + unsigned int stimer; /* global sample timer (to detect spurious interrupts) */ + unsigned int spurious_threshold; /* spurious threshold */ + unsigned int isync_mark; + unsigned int isync_max; + unsigned int isync_ESO; + + /* --- */ + + void *private_data; + void (*private_free)(struct snd_trident_voice *voice); +}; + +struct snd_4dwave { + int seq_client; + + struct snd_trident_port seq_ports[4]; + struct snd_trident_voice voices[64]; + + int ChanSynthCount; /* number of allocated synth channels */ + int max_size; /* maximum synth memory size in bytes */ + int current_size; /* current allocated synth mem in bytes */ +}; + +struct snd_trident_pcm_mixer { + struct snd_trident_voice *voice; /* active voice */ + unsigned short vol; /* front volume */ + unsigned char pan; /* pan control */ + unsigned char rvol; /* rear volume */ + unsigned char cvol; /* center volume */ + unsigned char pad; +}; + +struct snd_trident { + int irq; + + unsigned int device; /* device ID */ + + unsigned char bDMAStart; + + unsigned long port; + unsigned long midi_port; + + unsigned int spurious_irq_count; + unsigned int spurious_irq_max_delta; + + struct snd_trident_tlb tlb; /* TLB entries for NX cards */ + + unsigned char spdif_ctrl; + unsigned char spdif_pcm_ctrl; + unsigned int spdif_bits; + unsigned int spdif_pcm_bits; + struct snd_kcontrol *spdif_pcm_ctl; /* S/PDIF settings */ + unsigned int ac97_ctrl; + + unsigned int ChanMap[2]; /* allocation map for hardware channels */ + + int ChanPCM; /* max number of PCM channels */ + int ChanPCMcnt; /* actual number of PCM channels */ + + unsigned int ac97_detect: 1; /* 1 = AC97 in detection phase */ + unsigned int in_suspend: 1; /* 1 during suspend/resume */ + + struct snd_4dwave synth; /* synth specific variables */ + + spinlock_t event_lock; + spinlock_t voice_alloc; + + struct snd_dma_device dma_dev; + + struct pci_dev *pci; + struct snd_card *card; + struct snd_pcm *pcm; /* ADC/DAC PCM */ + struct snd_pcm *foldback; /* Foldback PCM */ + struct snd_pcm *spdif; /* SPDIF PCM */ + struct snd_rawmidi *rmidi; + + struct snd_ac97_bus *ac97_bus; + struct snd_ac97 *ac97; + struct snd_ac97 *ac97_sec; + + unsigned int musicvol_wavevol; + struct snd_trident_pcm_mixer pcm_mixer[32]; + struct snd_kcontrol *ctl_vol; /* front volume */ + struct snd_kcontrol *ctl_pan; /* pan */ + struct snd_kcontrol *ctl_rvol; /* rear volume */ + struct snd_kcontrol *ctl_cvol; /* center volume */ + + spinlock_t reg_lock; + + struct gameport *gameport; +}; + +int snd_trident_create(struct snd_card *card, + struct pci_dev *pci, + int pcm_streams, + int pcm_spdif_device, + int max_wavetable_size, + struct snd_trident ** rtrident); +int snd_trident_create_gameport(struct snd_trident *trident); + +int snd_trident_pcm(struct snd_trident * trident, int device, struct snd_pcm **rpcm); +int snd_trident_foldback_pcm(struct snd_trident * trident, int device, struct snd_pcm **rpcm); +int snd_trident_spdif_pcm(struct snd_trident * trident, int device, struct snd_pcm **rpcm); +int snd_trident_attach_synthesizer(struct snd_trident * trident); +struct snd_trident_voice *snd_trident_alloc_voice(struct snd_trident * trident, int type, + int client, int port); +void snd_trident_free_voice(struct snd_trident * trident, struct snd_trident_voice *voice); +void snd_trident_start_voice(struct snd_trident * trident, unsigned int voice); +void snd_trident_stop_voice(struct snd_trident * trident, unsigned int voice); +void snd_trident_write_voice_regs(struct snd_trident * trident, struct snd_trident_voice *voice); +extern const struct dev_pm_ops snd_trident_pm; + +/* TLB memory allocation */ +struct snd_util_memblk *snd_trident_alloc_pages(struct snd_trident *trident, + struct snd_pcm_substream *substream); +int snd_trident_free_pages(struct snd_trident *trident, struct snd_util_memblk *blk); +struct snd_util_memblk *snd_trident_synth_alloc(struct snd_trident *trident, unsigned int size); +int snd_trident_synth_free(struct snd_trident *trident, struct snd_util_memblk *blk); +int snd_trident_synth_copy_from_user(struct snd_trident *trident, struct snd_util_memblk *blk, + int offset, const char __user *data, int size); + +#endif /* __SOUND_TRIDENT_H */ diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index b4430c093bad..94011dcae731 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -41,7 +41,7 @@ #include #include #include -#include +#include "trident.h" #include #include diff --git a/sound/pci/trident/trident_memory.c b/sound/pci/trident/trident_memory.c index f9779e23fe57..3102a579660b 100644 --- a/sound/pci/trident/trident_memory.c +++ b/sound/pci/trident/trident_memory.c @@ -29,7 +29,7 @@ #include #include -#include +#include "trident.h" /* page arguments of these two macros are Trident page (4096 bytes), not like * aligned pages in others diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c index 7e20ddb9123a..4810356b97ba 100644 --- a/sound/pci/ymfpci/ymfpci.c +++ b/sound/pci/ymfpci/ymfpci.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include "ymfpci.h" #include #include #include diff --git a/sound/pci/ymfpci/ymfpci.h b/sound/pci/ymfpci/ymfpci.h new file mode 100644 index 000000000000..bddc4052286b --- /dev/null +++ b/sound/pci/ymfpci/ymfpci.h @@ -0,0 +1,389 @@ +#ifndef __SOUND_YMFPCI_H +#define __SOUND_YMFPCI_H + +/* + * Copyright (c) by Jaroslav Kysela + * Definitions for Yahama YMF724/740/744/754 chips + * + * + * 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 + +/* + * Direct registers + */ + +#define YMFREG(chip, reg) (chip->port + YDSXGR_##reg) + +#define YDSXGR_INTFLAG 0x0004 +#define YDSXGR_ACTIVITY 0x0006 +#define YDSXGR_GLOBALCTRL 0x0008 +#define YDSXGR_ZVCTRL 0x000A +#define YDSXGR_TIMERCTRL 0x0010 +#define YDSXGR_TIMERCOUNT 0x0012 +#define YDSXGR_SPDIFOUTCTRL 0x0018 +#define YDSXGR_SPDIFOUTSTATUS 0x001C +#define YDSXGR_EEPROMCTRL 0x0020 +#define YDSXGR_SPDIFINCTRL 0x0034 +#define YDSXGR_SPDIFINSTATUS 0x0038 +#define YDSXGR_DSPPROGRAMDL 0x0048 +#define YDSXGR_DLCNTRL 0x004C +#define YDSXGR_GPIOININTFLAG 0x0050 +#define YDSXGR_GPIOININTENABLE 0x0052 +#define YDSXGR_GPIOINSTATUS 0x0054 +#define YDSXGR_GPIOOUTCTRL 0x0056 +#define YDSXGR_GPIOFUNCENABLE 0x0058 +#define YDSXGR_GPIOTYPECONFIG 0x005A +#define YDSXGR_AC97CMDDATA 0x0060 +#define YDSXGR_AC97CMDADR 0x0062 +#define YDSXGR_PRISTATUSDATA 0x0064 +#define YDSXGR_PRISTATUSADR 0x0066 +#define YDSXGR_SECSTATUSDATA 0x0068 +#define YDSXGR_SECSTATUSADR 0x006A +#define YDSXGR_SECCONFIG 0x0070 +#define YDSXGR_LEGACYOUTVOL 0x0080 +#define YDSXGR_LEGACYOUTVOLL 0x0080 +#define YDSXGR_LEGACYOUTVOLR 0x0082 +#define YDSXGR_NATIVEDACOUTVOL 0x0084 +#define YDSXGR_NATIVEDACOUTVOLL 0x0084 +#define YDSXGR_NATIVEDACOUTVOLR 0x0086 +#define YDSXGR_ZVOUTVOL 0x0088 +#define YDSXGR_ZVOUTVOLL 0x0088 +#define YDSXGR_ZVOUTVOLR 0x008A +#define YDSXGR_SECADCOUTVOL 0x008C +#define YDSXGR_SECADCOUTVOLL 0x008C +#define YDSXGR_SECADCOUTVOLR 0x008E +#define YDSXGR_PRIADCOUTVOL 0x0090 +#define YDSXGR_PRIADCOUTVOLL 0x0090 +#define YDSXGR_PRIADCOUTVOLR 0x0092 +#define YDSXGR_LEGACYLOOPVOL 0x0094 +#define YDSXGR_LEGACYLOOPVOLL 0x0094 +#define YDSXGR_LEGACYLOOPVOLR 0x0096 +#define YDSXGR_NATIVEDACLOOPVOL 0x0098 +#define YDSXGR_NATIVEDACLOOPVOLL 0x0098 +#define YDSXGR_NATIVEDACLOOPVOLR 0x009A +#define YDSXGR_ZVLOOPVOL 0x009C +#define YDSXGR_ZVLOOPVOLL 0x009E +#define YDSXGR_ZVLOOPVOLR 0x009E +#define YDSXGR_SECADCLOOPVOL 0x00A0 +#define YDSXGR_SECADCLOOPVOLL 0x00A0 +#define YDSXGR_SECADCLOOPVOLR 0x00A2 +#define YDSXGR_PRIADCLOOPVOL 0x00A4 +#define YDSXGR_PRIADCLOOPVOLL 0x00A4 +#define YDSXGR_PRIADCLOOPVOLR 0x00A6 +#define YDSXGR_NATIVEADCINVOL 0x00A8 +#define YDSXGR_NATIVEADCINVOLL 0x00A8 +#define YDSXGR_NATIVEADCINVOLR 0x00AA +#define YDSXGR_NATIVEDACINVOL 0x00AC +#define YDSXGR_NATIVEDACINVOLL 0x00AC +#define YDSXGR_NATIVEDACINVOLR 0x00AE +#define YDSXGR_BUF441OUTVOL 0x00B0 +#define YDSXGR_BUF441OUTVOLL 0x00B0 +#define YDSXGR_BUF441OUTVOLR 0x00B2 +#define YDSXGR_BUF441LOOPVOL 0x00B4 +#define YDSXGR_BUF441LOOPVOLL 0x00B4 +#define YDSXGR_BUF441LOOPVOLR 0x00B6 +#define YDSXGR_SPDIFOUTVOL 0x00B8 +#define YDSXGR_SPDIFOUTVOLL 0x00B8 +#define YDSXGR_SPDIFOUTVOLR 0x00BA +#define YDSXGR_SPDIFLOOPVOL 0x00BC +#define YDSXGR_SPDIFLOOPVOLL 0x00BC +#define YDSXGR_SPDIFLOOPVOLR 0x00BE +#define YDSXGR_ADCSLOTSR 0x00C0 +#define YDSXGR_RECSLOTSR 0x00C4 +#define YDSXGR_ADCFORMAT 0x00C8 +#define YDSXGR_RECFORMAT 0x00CC +#define YDSXGR_P44SLOTSR 0x00D0 +#define YDSXGR_STATUS 0x0100 +#define YDSXGR_CTRLSELECT 0x0104 +#define YDSXGR_MODE 0x0108 +#define YDSXGR_SAMPLECOUNT 0x010C +#define YDSXGR_NUMOFSAMPLES 0x0110 +#define YDSXGR_CONFIG 0x0114 +#define YDSXGR_PLAYCTRLSIZE 0x0140 +#define YDSXGR_RECCTRLSIZE 0x0144 +#define YDSXGR_EFFCTRLSIZE 0x0148 +#define YDSXGR_WORKSIZE 0x014C +#define YDSXGR_MAPOFREC 0x0150 +#define YDSXGR_MAPOFEFFECT 0x0154 +#define YDSXGR_PLAYCTRLBASE 0x0158 +#define YDSXGR_RECCTRLBASE 0x015C +#define YDSXGR_EFFCTRLBASE 0x0160 +#define YDSXGR_WORKBASE 0x0164 +#define YDSXGR_DSPINSTRAM 0x1000 +#define YDSXGR_CTRLINSTRAM 0x4000 + +#define YDSXG_AC97READCMD 0x8000 +#define YDSXG_AC97WRITECMD 0x0000 + +#define PCIR_DSXG_LEGACY 0x40 +#define PCIR_DSXG_ELEGACY 0x42 +#define PCIR_DSXG_CTRL 0x48 +#define PCIR_DSXG_PWRCTRL1 0x4a +#define PCIR_DSXG_PWRCTRL2 0x4e +#define PCIR_DSXG_FMBASE 0x60 +#define PCIR_DSXG_SBBASE 0x62 +#define PCIR_DSXG_MPU401BASE 0x64 +#define PCIR_DSXG_JOYBASE 0x66 + +#define YDSXG_DSPLENGTH 0x0080 +#define YDSXG_CTRLLENGTH 0x3000 + +#define YDSXG_DEFAULT_WORK_SIZE 0x0400 + +#define YDSXG_PLAYBACK_VOICES 64 +#define YDSXG_CAPTURE_VOICES 2 +#define YDSXG_EFFECT_VOICES 5 + +#define YMFPCI_LEGACY_SBEN (1 << 0) /* soundblaster enable */ +#define YMFPCI_LEGACY_FMEN (1 << 1) /* OPL3 enable */ +#define YMFPCI_LEGACY_JPEN (1 << 2) /* joystick enable */ +#define YMFPCI_LEGACY_MEN (1 << 3) /* MPU401 enable */ +#define YMFPCI_LEGACY_MIEN (1 << 4) /* MPU RX irq enable */ +#define YMFPCI_LEGACY_IOBITS (1 << 5) /* i/o bits range, 0 = 16bit, 1 =10bit */ +#define YMFPCI_LEGACY_SDMA (3 << 6) /* SB DMA select */ +#define YMFPCI_LEGACY_SBIRQ (7 << 8) /* SB IRQ select */ +#define YMFPCI_LEGACY_MPUIRQ (7 << 11) /* MPU IRQ select */ +#define YMFPCI_LEGACY_SIEN (1 << 14) /* serialized IRQ */ +#define YMFPCI_LEGACY_LAD (1 << 15) /* legacy audio disable */ + +#define YMFPCI_LEGACY2_FMIO (3 << 0) /* OPL3 i/o address (724/740) */ +#define YMFPCI_LEGACY2_SBIO (3 << 2) /* SB i/o address (724/740) */ +#define YMFPCI_LEGACY2_MPUIO (3 << 4) /* MPU401 i/o address (724/740) */ +#define YMFPCI_LEGACY2_JSIO (3 << 6) /* joystick i/o address (724/740) */ +#define YMFPCI_LEGACY2_MAIM (1 << 8) /* MPU401 ack intr mask */ +#define YMFPCI_LEGACY2_SMOD (3 << 11) /* SB DMA mode */ +#define YMFPCI_LEGACY2_SBVER (3 << 13) /* SB version select */ +#define YMFPCI_LEGACY2_IMOD (1 << 15) /* legacy IRQ mode */ +/* SIEN:IMOD 0:0 = legacy irq, 0:1 = INTA, 1:0 = serialized IRQ */ + +#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#define SUPPORT_JOYSTICK +#endif + +/* + * + */ + +struct snd_ymfpci_playback_bank { + u32 format; + u32 loop_default; + u32 base; /* 32-bit address */ + u32 loop_start; /* 32-bit offset */ + u32 loop_end; /* 32-bit offset */ + u32 loop_frac; /* 8-bit fraction - loop_start */ + u32 delta_end; /* pitch delta end */ + u32 lpfK_end; + u32 eg_gain_end; + u32 left_gain_end; + u32 right_gain_end; + u32 eff1_gain_end; + u32 eff2_gain_end; + u32 eff3_gain_end; + u32 lpfQ; + u32 status; + u32 num_of_frames; + u32 loop_count; + u32 start; + u32 start_frac; + u32 delta; + u32 lpfK; + u32 eg_gain; + u32 left_gain; + u32 right_gain; + u32 eff1_gain; + u32 eff2_gain; + u32 eff3_gain; + u32 lpfD1; + u32 lpfD2; + }; + +struct snd_ymfpci_capture_bank { + u32 base; /* 32-bit address */ + u32 loop_end; /* 32-bit offset */ + u32 start; /* 32-bit offset */ + u32 num_of_loops; /* counter */ +}; + +struct snd_ymfpci_effect_bank { + u32 base; /* 32-bit address */ + u32 loop_end; /* 32-bit offset */ + u32 start; /* 32-bit offset */ + u32 temp; +}; + +struct snd_ymfpci_pcm; +struct snd_ymfpci; + +enum snd_ymfpci_voice_type { + YMFPCI_PCM, + YMFPCI_SYNTH, + YMFPCI_MIDI +}; + +struct snd_ymfpci_voice { + struct snd_ymfpci *chip; + int number; + unsigned int use: 1, + pcm: 1, + synth: 1, + midi: 1; + struct snd_ymfpci_playback_bank *bank; + dma_addr_t bank_addr; + void (*interrupt)(struct snd_ymfpci *chip, struct snd_ymfpci_voice *voice); + struct snd_ymfpci_pcm *ypcm; +}; + +enum snd_ymfpci_pcm_type { + PLAYBACK_VOICE, + CAPTURE_REC, + CAPTURE_AC97, + EFFECT_DRY_LEFT, + EFFECT_DRY_RIGHT, + EFFECT_EFF1, + EFFECT_EFF2, + EFFECT_EFF3 +}; + +struct snd_ymfpci_pcm { + struct snd_ymfpci *chip; + enum snd_ymfpci_pcm_type type; + struct snd_pcm_substream *substream; + struct snd_ymfpci_voice *voices[2]; /* playback only */ + unsigned int running: 1, + use_441_slot: 1, + output_front: 1, + output_rear: 1, + swap_rear: 1; + unsigned int update_pcm_vol; + u32 period_size; /* cached from runtime->period_size */ + u32 buffer_size; /* cached from runtime->buffer_size */ + u32 period_pos; + u32 last_pos; + u32 capture_bank_number; + u32 shift; +}; + +struct snd_ymfpci { + int irq; + + unsigned int device_id; /* PCI device ID */ + unsigned char rev; /* PCI revision */ + unsigned long reg_area_phys; + void __iomem *reg_area_virt; + struct resource *res_reg_area; + struct resource *fm_res; + struct resource *mpu_res; + + unsigned short old_legacy_ctrl; +#ifdef SUPPORT_JOYSTICK + struct gameport *gameport; +#endif + + struct snd_dma_buffer work_ptr; + + unsigned int bank_size_playback; + unsigned int bank_size_capture; + unsigned int bank_size_effect; + unsigned int work_size; + + void *bank_base_playback; + void *bank_base_capture; + void *bank_base_effect; + void *work_base; + dma_addr_t bank_base_playback_addr; + dma_addr_t bank_base_capture_addr; + dma_addr_t bank_base_effect_addr; + dma_addr_t work_base_addr; + struct snd_dma_buffer ac3_tmp_base; + + u32 *ctrl_playback; + struct snd_ymfpci_playback_bank *bank_playback[YDSXG_PLAYBACK_VOICES][2]; + struct snd_ymfpci_capture_bank *bank_capture[YDSXG_CAPTURE_VOICES][2]; + struct snd_ymfpci_effect_bank *bank_effect[YDSXG_EFFECT_VOICES][2]; + + int start_count; + + u32 active_bank; + struct snd_ymfpci_voice voices[64]; + int src441_used; + + struct snd_ac97_bus *ac97_bus; + struct snd_ac97 *ac97; + struct snd_rawmidi *rawmidi; + struct snd_timer *timer; + unsigned int timer_ticks; + + struct pci_dev *pci; + struct snd_card *card; + struct snd_pcm *pcm; + struct snd_pcm *pcm2; + struct snd_pcm *pcm_spdif; + struct snd_pcm *pcm_4ch; + struct snd_pcm_substream *capture_substream[YDSXG_CAPTURE_VOICES]; + struct snd_pcm_substream *effect_substream[YDSXG_EFFECT_VOICES]; + struct snd_kcontrol *ctl_vol_recsrc; + struct snd_kcontrol *ctl_vol_adcrec; + struct snd_kcontrol *ctl_vol_spdifrec; + unsigned short spdif_bits, spdif_pcm_bits; + struct snd_kcontrol *spdif_pcm_ctl; + int mode_dup4ch; + int rear_opened; + int spdif_opened; + struct snd_ymfpci_pcm_mixer { + u16 left; + u16 right; + struct snd_kcontrol *ctl; + } pcm_mixer[32]; + + spinlock_t reg_lock; + spinlock_t voice_lock; + wait_queue_head_t interrupt_sleep; + atomic_t interrupt_sleep_count; + struct snd_info_entry *proc_entry; + const struct firmware *dsp_microcode; + const struct firmware *controller_microcode; + +#ifdef CONFIG_PM + u32 *saved_regs; + u32 saved_ydsxgr_mode; + u16 saved_dsxg_legacy; + u16 saved_dsxg_elegacy; +#endif +}; + +int snd_ymfpci_create(struct snd_card *card, + struct pci_dev *pci, + unsigned short old_legacy_ctrl, + struct snd_ymfpci ** rcodec); +void snd_ymfpci_free_gameport(struct snd_ymfpci *chip); + +extern const struct dev_pm_ops snd_ymfpci_pm; + +int snd_ymfpci_pcm(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm); +int snd_ymfpci_pcm2(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm); +int snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm); +int snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm); +int snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch); +int snd_ymfpci_timer(struct snd_ymfpci *chip, int device); + +#endif /* __SOUND_YMFPCI_H */ diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index c706901d6ff6..62b23635b754 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include "ymfpci.h" #include #include -- cgit v1.2.3 From 36c1ed821bd11fb9a3f99a060b1553c114dc2d07 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 15 Jun 2012 15:07:24 -0400 Subject: KVM: Guard mmu_notifier specific code with CONFIG_MMU_NOTIFIER In order to avoid compilation failure when KVM is not compiled in, guard the mmu_notifier specific sections with both CONFIG_MMU_NOTIFIER and KVM_ARCH_WANT_MMU_NOTIFIER, like it is being done in the rest of the KVM code. Signed-off-by: Marc Zyngier Signed-off-by: Christoffer Dall Signed-off-by: Marcelo Tosatti --- include/linux/kvm_host.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index c7f77876c9b3..e3c86f8c86c9 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -306,7 +306,7 @@ struct kvm { struct hlist_head irq_ack_notifier_list; #endif -#ifdef KVM_ARCH_WANT_MMU_NOTIFIER +#if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER) struct mmu_notifier mmu_notifier; unsigned long mmu_notifier_seq; long mmu_notifier_count; @@ -780,7 +780,7 @@ struct kvm_stats_debugfs_item { extern struct kvm_stats_debugfs_item debugfs_entries[]; extern struct dentry *kvm_debugfs_dir; -#ifdef KVM_ARCH_WANT_MMU_NOTIFIER +#if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER) static inline int mmu_notifier_retry(struct kvm_vcpu *vcpu, unsigned long mmu_seq) { if (unlikely(vcpu->kvm->mmu_notifier_count)) -- cgit v1.2.3 From 5d589b092ab212bbcc27828167e1c036e7fc77d2 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 23 May 2012 21:22:40 +0800 Subject: pinctrl: remove pinctrl_remove_gpio_range The gpio ranges will be automatically removed when the pinctrl driver is unregistered. Acked-by: Stephen Warren Signed-off-by: Dong Aisheng Signed-off-by: Linus Walleij --- drivers/pinctrl/core.c | 19 +++++-------------- drivers/pinctrl/pinctrl-tegra.c | 1 - drivers/pinctrl/pinctrl-u300.c | 2 -- include/linux/pinctrl/pinctrl.h | 2 -- 4 files changed, 5 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 50d9c289cffd..902428dfb37e 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -332,20 +332,6 @@ void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev, } EXPORT_SYMBOL_GPL(pinctrl_add_gpio_range); -/** - * pinctrl_remove_gpio_range() - remove a range of GPIOs fro a pin controller - * @pctldev: pin controller device to remove the range from - * @range: the GPIO range to remove - */ -void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev, - struct pinctrl_gpio_range *range) -{ - mutex_lock(&pinctrl_mutex); - list_del(&range->node); - mutex_unlock(&pinctrl_mutex); -} -EXPORT_SYMBOL_GPL(pinctrl_remove_gpio_range); - /** * pinctrl_get_group_selector() - returns the group selector for a group * @pctldev: the pin controller handling the group @@ -1480,6 +1466,7 @@ EXPORT_SYMBOL_GPL(pinctrl_register); */ void pinctrl_unregister(struct pinctrl_dev *pctldev) { + struct pinctrl_gpio_range *range, *n; if (pctldev == NULL) return; @@ -1495,6 +1482,10 @@ void pinctrl_unregister(struct pinctrl_dev *pctldev) /* Destroy descriptor tree */ pinctrl_free_pindescs(pctldev, pctldev->desc->pins, pctldev->desc->npins); + /* remove gpio ranges map */ + list_for_each_entry_safe(range, n, &pctldev->gpio_ranges, node) + list_del(&range->node); + kfree(pctldev); mutex_unlock(&pinctrl_mutex); diff --git a/drivers/pinctrl/pinctrl-tegra.c b/drivers/pinctrl/pinctrl-tegra.c index b6934867d8d3..07228b17a370 100644 --- a/drivers/pinctrl/pinctrl-tegra.c +++ b/drivers/pinctrl/pinctrl-tegra.c @@ -764,7 +764,6 @@ int __devexit tegra_pinctrl_remove(struct platform_device *pdev) { struct tegra_pmx *pmx = platform_get_drvdata(pdev); - pinctrl_remove_gpio_range(pmx->pctl, &tegra_pinctrl_gpio_range); pinctrl_unregister(pmx->pctl); return 0; diff --git a/drivers/pinctrl/pinctrl-u300.c b/drivers/pinctrl/pinctrl-u300.c index 6cd697a079f9..5f43f9ae36d3 100644 --- a/drivers/pinctrl/pinctrl-u300.c +++ b/drivers/pinctrl/pinctrl-u300.c @@ -1175,8 +1175,6 @@ static int __devexit u300_pmx_remove(struct platform_device *pdev) struct u300_pmx *upmx = platform_get_drvdata(pdev); int i; - for (i = 0; i < ARRAY_SIZE(u300_gpio_ranges); i++) - pinctrl_remove_gpio_range(upmx->pctl, &u300_gpio_ranges[i]); pinctrl_unregister(upmx->pctl); iounmap(upmx->virtbase); release_mem_region(upmx->phybase, upmx->physize); diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index 3b894a668d32..170a588a55b6 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -131,8 +131,6 @@ extern void pinctrl_unregister(struct pinctrl_dev *pctldev); extern bool pin_is_valid(struct pinctrl_dev *pctldev, int pin); extern void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range); -extern void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev, - struct pinctrl_gpio_range *range); extern const char *pinctrl_dev_get_name(struct pinctrl_dev *pctldev); extern void *pinctrl_dev_get_drvdata(struct pinctrl_dev *pctldev); #else -- cgit v1.2.3 From 3e5e00b654997aa2c3998d30f7213b9611eb23d7 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 23 May 2012 21:22:41 +0800 Subject: pinctrl: add pinctrl_add_gpio_ranges function Often GPIO ranges are added in batch, so create a special function for that. Acked-by: Stephen Warren Signed-off-by: Dong Aisheng Signed-off-by: Linus Walleij --- drivers/pinctrl/core.c | 11 +++++++++++ include/linux/pinctrl/pinctrl.h | 3 +++ 2 files changed, 14 insertions(+) (limited to 'include') diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 902428dfb37e..fb7f3bebdc69 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -332,6 +332,17 @@ void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev, } EXPORT_SYMBOL_GPL(pinctrl_add_gpio_range); +void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *ranges, + unsigned nranges) +{ + int i; + + for (i = 0; i < nranges; i++) + pinctrl_add_gpio_range(pctldev, &ranges[i]); +} +EXPORT_SYMBOL_GPL(pinctrl_add_gpio_ranges); + /** * pinctrl_get_group_selector() - returns the group selector for a group * @pctldev: the pin controller handling the group diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index 170a588a55b6..69393a662532 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -131,6 +131,9 @@ extern void pinctrl_unregister(struct pinctrl_dev *pctldev); extern bool pin_is_valid(struct pinctrl_dev *pctldev, int pin); extern void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range); +extern void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *ranges, + unsigned nranges); extern const char *pinctrl_dev_get_name(struct pinctrl_dev *pctldev); extern void *pinctrl_dev_get_drvdata(struct pinctrl_dev *pctldev); #else -- cgit v1.2.3 From cab7faca5e446b84e829d57d2095035d72edba09 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Wed, 27 Jun 2012 09:53:47 +0200 Subject: Input: MT - Include win8 support The newly released HID protocol for win8 multitouch devices is capable of transmitting more information about each touch. In particular, it includes details useful for touch alignment. This patch completes the MT protocol with the ABS_MT_TOOL_X/Y events, and documents how to map win8 devices. Cc: Stephane Chatty Cc: Benjamin Tissoires Cc: Peter Hutterer Acked-by: Chase Douglas Signed-off-by: Henrik Rydberg --- Documentation/input/multi-touch-protocol.txt | 118 +++++++++++++++++++++------ include/linux/input.h | 8 +- 2 files changed, 97 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/Documentation/input/multi-touch-protocol.txt b/Documentation/input/multi-touch-protocol.txt index 543101c5bf26..2c179613f81b 100644 --- a/Documentation/input/multi-touch-protocol.txt +++ b/Documentation/input/multi-touch-protocol.txt @@ -162,26 +162,48 @@ are divided into categories, to allow for partial implementation. The minimum set consists of ABS_MT_POSITION_X and ABS_MT_POSITION_Y, which allows for multiple contacts to be tracked. If the device supports it, the ABS_MT_TOUCH_MAJOR and ABS_MT_WIDTH_MAJOR may be used to provide the size -of the contact area and approaching contact, respectively. +of the contact area and approaching tool, respectively. The TOUCH and WIDTH parameters have a geometrical interpretation; imagine looking through a window at someone gently holding a finger against the glass. You will see two regions, one inner region consisting of the part of the finger actually touching the glass, and one outer region formed by -the perimeter of the finger. The diameter of the inner region is the -ABS_MT_TOUCH_MAJOR, the diameter of the outer region is -ABS_MT_WIDTH_MAJOR. Now imagine the person pressing the finger harder -against the glass. The inner region will increase, and in general, the -ratio ABS_MT_TOUCH_MAJOR / ABS_MT_WIDTH_MAJOR, which is always smaller than -unity, is related to the contact pressure. For pressure-based devices, +the perimeter of the finger. The center of the touching region (a) is +ABS_MT_POSITION_X/Y and the center of the approaching finger (b) is +ABS_MT_TOOL_X/Y. The touch diameter is ABS_MT_TOUCH_MAJOR and the finger +diameter is ABS_MT_WIDTH_MAJOR. Now imagine the person pressing the finger +harder against the glass. The touch region will increase, and in general, +the ratio ABS_MT_TOUCH_MAJOR / ABS_MT_WIDTH_MAJOR, which is always smaller +than unity, is related to the contact pressure. For pressure-based devices, ABS_MT_PRESSURE may be used to provide the pressure on the contact area instead. Devices capable of contact hovering can use ABS_MT_DISTANCE to indicate the distance between the contact and the surface. -In addition to the MAJOR parameters, the oval shape of the contact can be -described by adding the MINOR parameters, such that MAJOR and MINOR are the -major and minor axis of an ellipse. Finally, the orientation of the oval -shape can be describe with the ORIENTATION parameter. + + Linux MT Win8 + __________ _______________________ + / \ | | + / \ | | + / ____ \ | | + / / \ \ | | + \ \ a \ \ | a | + \ \____/ \ | | + \ \ | | + \ b \ | b | + \ \ | | + \ \ | | + \ \ | | + \ / | | + \ / | | + \ / | | + \__________/ |_______________________| + + +In addition to the MAJOR parameters, the oval shape of the touch and finger +regions can be described by adding the MINOR parameters, such that MAJOR +and MINOR are the major and minor axis of an ellipse. The orientation of +the touch ellipse can be described with the ORIENTATION parameter, and the +direction of the finger ellipse is given by the vector (a - b). For type A devices, further specification of the touch shape is possible via ABS_MT_BLOB_ID. @@ -224,7 +246,7 @@ tool. Omit if circular [4]. The above four values can be used to derive additional information about the contact. The ratio ABS_MT_TOUCH_MAJOR / ABS_MT_WIDTH_MAJOR approximates the notion of pressure. The fingers of the hand and the palm all have -different characteristic widths [1]. +different characteristic widths. ABS_MT_PRESSURE @@ -240,17 +262,24 @@ the contact is hovering above the surface. ABS_MT_ORIENTATION -The orientation of the ellipse. The value should describe a signed quarter -of a revolution clockwise around the touch center. The signed value range -is arbitrary, but zero should be returned for a finger aligned along the Y -axis of the surface, a negative value when finger is turned to the left, and -a positive value when finger turned to the right. When completely aligned with -the X axis, the range max should be returned. Orientation can be omitted -if the touching object is circular, or if the information is not available -in the kernel driver. Partial orientation support is possible if the device -can distinguish between the two axis, but not (uniquely) any values in -between. In such cases, the range of ABS_MT_ORIENTATION should be [0, 1] -[4]. +The orientation of the touching ellipse. The value should describe a signed +quarter of a revolution clockwise around the touch center. The signed value +range is arbitrary, but zero should be returned for an ellipse aligned with +the Y axis of the surface, a negative value when the ellipse is turned to +the left, and a positive value when the ellipse is turned to the +right. When completely aligned with the X axis, the range max should be +returned. + +Touch ellipsis are symmetrical by default. For devices capable of true 360 +degree orientation, the reported orientation must exceed the range max to +indicate more than a quarter of a revolution. For an upside-down finger, +range max * 2 should be returned. + +Orientation can be omitted if the touch area is circular, or if the +information is not available in the kernel driver. Partial orientation +support is possible if the device can distinguish between the two axis, but +not (uniquely) any values in between. In such cases, the range of +ABS_MT_ORIENTATION should be [0, 1] [4]. ABS_MT_POSITION_X @@ -260,6 +289,23 @@ ABS_MT_POSITION_Y The surface Y coordinate of the center of the touching ellipse. +ABS_MT_TOOL_X + +The surface X coordinate of the center of the approaching tool. Omit if +the device cannot distinguish between the intended touch point and the +tool itself. + +ABS_MT_TOOL_Y + +The surface Y coordinate of the center of the approaching tool. Omit if the +device cannot distinguish between the intended touch point and the tool +itself. + +The four position values can be used to separate the position of the touch +from the position of the tool. If both positions are present, the major +tool axis points towards the touch point [1]. Otherwise, the tool axes are +aligned with the touch axes. + ABS_MT_TOOL_TYPE The type of approaching tool. A lot of kernel drivers cannot distinguish @@ -305,6 +351,28 @@ The range of ABS_MT_ORIENTATION should be set to [0, 1], to indicate that the device can distinguish between a finger along the Y axis (0) and a finger along the X axis (1). +For win8 devices with both T and C coordinates, the position mapping is + + ABS_MT_POSITION_X := T_X + ABS_MT_POSITION_Y := T_Y + ABS_MT_TOOL_X := C_X + ABS_MT_TOOL_X := C_Y + +Unfortunately, there is not enough information to specify both the touching +ellipse and the tool ellipse, so one has to resort to approximations. One +simple scheme, which is compatible with earlier usage, is: + + ABS_MT_TOUCH_MAJOR := min(X, Y) + ABS_MT_TOUCH_MINOR := + ABS_MT_ORIENTATION := + ABS_MT_WIDTH_MAJOR := min(X, Y) + distance(T, C) + ABS_MT_WIDTH_MINOR := min(X, Y) + +Rationale: We have no information about the orientation of the touching +ellipse, so approximate it with an inscribed circle instead. The tool +ellipse should align with the the vector (T - C), so the diameter must +increase with distance(T, C). Finally, assume that the touch diameter is +equal to the tool thickness, and we arrive at the formulas above. Finger Tracking --------------- @@ -338,9 +406,7 @@ subsequent events of the same type refer to different fingers. For example usage of the type A protocol, see the bcm5974 driver. For example usage of the type B protocol, see the hid-egalax driver. -[1] With the extension ABS_MT_APPROACH_X and ABS_MT_APPROACH_Y, the -difference between the contact position and the approaching tool position -could be used to derive tilt. +[1] Also, the difference (TOOL_X - POSITION_X) can be used to model tilt. [2] The list can of course be extended. [3] The mtdev project: http://bitmath.org/code/mtdev/. [4] See the section on event computation. diff --git a/include/linux/input.h b/include/linux/input.h index a81671453575..cacb95343a7e 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -806,18 +806,20 @@ struct input_keymap_entry { #define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */ #define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */ #define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */ -#define ABS_MT_POSITION_X 0x35 /* Center X ellipse position */ -#define ABS_MT_POSITION_Y 0x36 /* Center Y ellipse position */ +#define ABS_MT_POSITION_X 0x35 /* Center X touch position */ +#define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */ #define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */ #define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */ #define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */ #define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */ #define ABS_MT_DISTANCE 0x3b /* Contact hover distance */ +#define ABS_MT_TOOL_X 0x3c /* Center X tool position */ +#define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */ #ifdef __KERNEL__ /* Implementation details, userspace should not care about these */ #define ABS_MT_FIRST ABS_MT_TOUCH_MAJOR -#define ABS_MT_LAST ABS_MT_DISTANCE +#define ABS_MT_LAST ABS_MT_TOOL_Y #endif #define ABS_MAX 0x3f -- cgit v1.2.3 From 1464189f8c2a5341722437ef916786afaf241c44 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 5 Jul 2012 12:15:01 +0100 Subject: ALSA: pcm: Make constraints lists const They aren't modified by the core so the drivers can declare them const. Signed-off-by: Mark Brown Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 2 +- sound/core/pcm_lib.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 68372bc1e11b..e91e6047ca6f 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -810,7 +810,7 @@ int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_pa int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, unsigned int cond, snd_pcm_hw_param_t var, - struct snd_pcm_hw_constraint_list *l); + const struct snd_pcm_hw_constraint_list *l); int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, unsigned int cond, snd_pcm_hw_param_t var, diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 8f312fa6c282..7ae671923393 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1250,10 +1250,10 @@ static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params, int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, unsigned int cond, snd_pcm_hw_param_t var, - struct snd_pcm_hw_constraint_list *l) + const struct snd_pcm_hw_constraint_list *l) { return snd_pcm_hw_rule_add(runtime, cond, var, - snd_pcm_hw_rule_list, l, + snd_pcm_hw_rule_list, (void *)l, var, -1); } -- cgit v1.2.3 From 2b719d7baf490e24ce7d817c6337b7c87fda84c1 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 2 May 2012 09:40:03 -0300 Subject: [media] v4l: drop v4l2_buffer.input and V4L2_BUF_FLAG_INPUT Remove input field in struct v4l2_buffer and flag V4L2_BUF_FLAG_INPUT which tells the former is valid. The flag is used by no driver currently. Also change the documentation accordingly. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/compat.xml | 12 ++++++++++++ Documentation/DocBook/media/v4l/io.xml | 19 +++++-------------- Documentation/DocBook/media/v4l/vidioc-qbuf.xml | 9 +++------ drivers/media/video/cpia2/cpia2_v4l.c | 2 +- drivers/media/video/v4l2-compat-ioctl32.c | 11 +++++------ drivers/media/video/videobuf-core.c | 16 ---------------- drivers/media/video/videobuf2-core.c | 5 ++--- include/linux/videodev2.h | 3 +-- 8 files changed, 29 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index ea42ef824948..a8b374930405 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2458,6 +2458,18 @@ details. +
+ V4L2 in Linux 3.5 + + + Replaced input in + v4l2_buffer by + reserved2 and removed + V4L2_BUF_FLAG_INPUT. + + +
+
Relation of V4L2 to other Linux multimedia APIs diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml index fd6aca2922b6..1885cc0755cb 100644 --- a/Documentation/DocBook/media/v4l/io.xml +++ b/Documentation/DocBook/media/v4l/io.xml @@ -683,14 +683,12 @@ memory, set by the application. See for details. __u32 - input + reserved2 - Some video capture drivers support rapid and -synchronous video input changes, a function useful for example in -video surveillance applications. For this purpose applications set the -V4L2_BUF_FLAG_INPUT flag, and this field to the -number of a video input as in &v4l2-input; field -index. + A place holder for future extensions and custom +(driver defined) buffer types +V4L2_BUF_TYPE_PRIVATE and higher. Applications +should set this to 0. __u32 @@ -921,13 +919,6 @@ previous key frame. The timecode field is valid. Drivers set or clear this flag when the VIDIOC_DQBUF ioctl is called. - - - V4L2_BUF_FLAG_INPUT - 0x0200 - The input field is valid. -Applications set or clear this flag before calling the -VIDIOC_QBUF ioctl. V4L2_BUF_FLAG_PREPARED diff --git a/Documentation/DocBook/media/v4l/vidioc-qbuf.xml b/Documentation/DocBook/media/v4l/vidioc-qbuf.xml index 9caa49af580f..77ff5be0809d 100644 --- a/Documentation/DocBook/media/v4l/vidioc-qbuf.xml +++ b/Documentation/DocBook/media/v4l/vidioc-qbuf.xml @@ -71,12 +71,9 @@ initialize the bytesused, field and timestamp fields, see for details. -Applications must also set flags to 0. If a driver -supports capturing from specific video inputs and you want to specify a video -input, then flags should be set to -V4L2_BUF_FLAG_INPUT and the field -input must be initialized to the desired input. -The reserved field must be set to 0. When using +Applications must also set flags to 0. +The reserved2 and +reserved fields must be set to 0. When using the multi-planar API, the m.planes field must contain a userspace pointer to a filled-in array of &v4l2-plane; and the length diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c index 55e92902a76c..a62a7b739991 100644 --- a/drivers/media/video/cpia2/cpia2_v4l.c +++ b/drivers/media/video/cpia2/cpia2_v4l.c @@ -932,7 +932,7 @@ static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) buf->sequence = cam->buffers[buf->index].seq; buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer; buf->length = cam->frame_size; - buf->input = 0; + buf->reserved2 = 0; buf->reserved = 0; memset(&buf->timecode, 0, sizeof(buf->timecode)); diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index 5327ad3a6390..658ba46ee9d2 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -327,7 +327,7 @@ struct v4l2_buffer32 { compat_caddr_t planes; } m; __u32 length; - __u32 input; + __u32 reserved2; __u32 reserved; }; @@ -387,8 +387,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user get_user(kp->index, &up->index) || get_user(kp->type, &up->type) || get_user(kp->flags, &up->flags) || - get_user(kp->memory, &up->memory) || - get_user(kp->input, &up->input)) + get_user(kp->memory, &up->memory) return -EFAULT; if (V4L2_TYPE_IS_OUTPUT(kp->type)) @@ -472,8 +471,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user put_user(kp->index, &up->index) || put_user(kp->type, &up->type) || put_user(kp->flags, &up->flags) || - put_user(kp->memory, &up->memory) || - put_user(kp->input, &up->input)) + put_user(kp->memory, &up->memory) return -EFAULT; if (put_user(kp->bytesused, &up->bytesused) || @@ -482,7 +480,8 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user put_user(kp->timestamp.tv_usec, &up->timestamp.tv_usec) || copy_to_user(&up->timecode, &kp->timecode, sizeof(struct v4l2_timecode)) || put_user(kp->sequence, &up->sequence) || - put_user(kp->reserved, &up->reserved)) + put_user(kp->reserved2, &up->reserved2) || + put_user(kp->reserved, &up->reserved) return -EFAULT; if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) { diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index ffdf59cfe405..bf7a326b1cdc 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -359,11 +359,6 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b, break; } - if (vb->input != UNSET) { - b->flags |= V4L2_BUF_FLAG_INPUT; - b->input = vb->input; - } - b->field = vb->field; b->timestamp = vb->ts; b->bytesused = vb->size; @@ -402,7 +397,6 @@ int __videobuf_mmap_setup(struct videobuf_queue *q, break; q->bufs[i]->i = i; - q->bufs[i]->input = UNSET; q->bufs[i]->memory = memory; q->bufs[i]->bsize = bsize; switch (memory) { @@ -566,16 +560,6 @@ int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b) goto done; } - if (b->flags & V4L2_BUF_FLAG_INPUT) { - if (b->input >= q->inputs) { - dprintk(1, "qbuf: wrong input.\n"); - goto done; - } - buf->input = b->input; - } else { - buf->input = UNSET; - } - switch (b->memory) { case V4L2_MEMORY_MMAP: if (0 == buf->baddr) { diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index 9d4e9edbd2e7..ec24718b3ed0 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -336,9 +336,9 @@ static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) struct vb2_queue *q = vb->vb2_queue; int ret; - /* Copy back data such as timestamp, flags, input, etc. */ + /* Copy back data such as timestamp, flags, etc. */ memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m)); - b->input = vb->v4l2_buf.input; + b->reserved2 = vb->v4l2_buf.reserved2; b->reserved = vb->v4l2_buf.reserved; if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) { @@ -860,7 +860,6 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b, vb->v4l2_buf.field = b->field; vb->v4l2_buf.timestamp = b->timestamp; - vb->v4l2_buf.input = b->input; vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_STATE_FLAGS; return 0; diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index f79d0cc565ab..a61edb353273 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -657,7 +657,7 @@ struct v4l2_buffer { struct v4l2_plane *planes; } m; __u32 length; - __u32 input; + __u32 reserved2; __u32 reserved; }; @@ -671,7 +671,6 @@ struct v4l2_buffer { /* Buffer is ready, but the data contained within is corrupted. */ #define V4L2_BUF_FLAG_ERROR 0x0040 #define V4L2_BUF_FLAG_TIMECODE 0x0100 /* timecode field is valid */ -#define V4L2_BUF_FLAG_INPUT 0x0200 /* input field is valid */ #define V4L2_BUF_FLAG_PREPARED 0x0400 /* Buffer is prepared for queuing */ /* Cache handling flags */ #define V4L2_BUF_FLAG_NO_CACHE_INVALIDATE 0x0800 -- cgit v1.2.3 From 31361fc4632f20e3a108f56b1a1a9c9bf2dfc07c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 5 Jul 2012 18:01:55 -0300 Subject: [media] videobuf-core.h: remove input fields Now that the input fields got removed from the userspace API, there's no sense to keep there at the VB struct. Remove it. Cc: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- include/media/videobuf-core.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/media/videobuf-core.h b/include/media/videobuf-core.h index 90ed895e217d..8c6e825940e5 100644 --- a/include/media/videobuf-core.h +++ b/include/media/videobuf-core.h @@ -72,7 +72,6 @@ struct videobuf_buffer { unsigned int height; unsigned int bytesperline; /* use only if != 0 */ unsigned long size; - unsigned int input; enum v4l2_field field; enum videobuf_state state; struct list_head stream; /* QBUF/DQBUF list */ @@ -142,7 +141,6 @@ struct videobuf_queue { wait_queue_head_t wait; /* wait if queue is empty */ enum v4l2_buf_type type; - unsigned int inputs; /* for V4L2_BUF_FLAG_INPUT */ unsigned int msize; enum v4l2_field field; enum v4l2_field last; /* for field=V4L2_FIELD_ALTERNATE */ -- cgit v1.2.3 From b5ab5e24e960b9f780a4cc96815cfd4b0d412720 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Wed, 30 May 2012 22:01:25 +0300 Subject: remoteproc: maintain a generic child device for each rproc For each registered rproc, maintain a generic remoteproc device whose parent is the low level platform-specific device (commonly a pdev, but it may certainly be any other type of device too). With this in hand, the resulting device hierarchy might then look like: omap-rproc.0 | - remoteproc0 <---- new ! | - virtio0 | - virtio1 | - rpmsg0 | - rpmsg1 | - rpmsg2 Where: - omap-rproc.0 is the low level device that's bound to the driver which invokes rproc_register() - remoteproc0 is the result of this patch, and will be added by the remoteproc framework when rproc_register() is invoked - virtio0 and virtio1 are vdevs that are registered by remoteproc when it realizes that they are supported by the firmware of the physical remote processor represented by omap-rproc.0 - rpmsg0, rpmsg1 and rpmsg2 are rpmsg devices that represent rpmsg channels, and are registerd by the rpmsg bus when it gets notified about their existence Technically, this patch: - changes 'struct rproc' to contain this generic remoteproc.x device - creates a new "remoteproc" type, to which this new generic remoteproc.x device belong to. - adds a super simple enumeration method for the indices of the remoteproc.x devices - updates all dev_* messaging to use the generic remoteproc.x device instead of the low level platform-specific device - updates all dma_* allocations to use the parent of remoteproc.x (where the platform-specific memory pools, most commonly CMA, are to be found) Adding this generic device has several merits: - we can now add remoteproc runtime PM support simply by hooking onto the new "remoteproc" type - all remoteproc log messages will now carry a common name prefix instead of having a platform-specific one - having a device as part of the rproc struct makes it possible to simplify refcounting (see subsequent patch) Thanks to Stephen Boyd for suggesting and discussing these ideas in one of the remoteproc review threads and to Fernando Guzman Lugo for trying them out with the (upcoming) runtime PM support for remoteproc. Cc: Fernando Guzman Lugo Reviewed-by: Stephen Boyd Signed-off-by: Ohad Ben-Cohen --- drivers/remoteproc/omap_remoteproc.c | 17 ++-- drivers/remoteproc/remoteproc_core.c | 135 ++++++++++++++++++++++---------- drivers/remoteproc/remoteproc_debugfs.c | 4 +- drivers/remoteproc/remoteproc_virtio.c | 13 +-- drivers/rpmsg/virtio_rpmsg_bus.c | 3 +- include/linux/remoteproc.h | 6 +- 6 files changed, 117 insertions(+), 61 deletions(-) (limited to 'include') diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index 69425c4e86f3..b5e6d2981741 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -66,7 +66,7 @@ static int omap_rproc_mbox_callback(struct notifier_block *this, { mbox_msg_t msg = (mbox_msg_t) data; struct omap_rproc *oproc = container_of(this, struct omap_rproc, nb); - struct device *dev = oproc->rproc->dev; + struct device *dev = oproc->rproc->dev.parent; const char *name = oproc->rproc->name; dev_dbg(dev, "mbox msg: 0x%x\n", msg); @@ -92,12 +92,13 @@ static int omap_rproc_mbox_callback(struct notifier_block *this, static void omap_rproc_kick(struct rproc *rproc, int vqid) { struct omap_rproc *oproc = rproc->priv; + struct device *dev = rproc->dev.parent; int ret; /* send the index of the triggered virtqueue in the mailbox payload */ ret = omap_mbox_msg_send(oproc->mbox, vqid); if (ret) - dev_err(rproc->dev, "omap_mbox_msg_send failed: %d\n", ret); + dev_err(dev, "omap_mbox_msg_send failed: %d\n", ret); } /* @@ -110,7 +111,8 @@ static void omap_rproc_kick(struct rproc *rproc, int vqid) static int omap_rproc_start(struct rproc *rproc) { struct omap_rproc *oproc = rproc->priv; - struct platform_device *pdev = to_platform_device(rproc->dev); + struct device *dev = rproc->dev.parent; + struct platform_device *pdev = to_platform_device(dev); struct omap_rproc_pdata *pdata = pdev->dev.platform_data; int ret; @@ -120,7 +122,7 @@ static int omap_rproc_start(struct rproc *rproc) oproc->mbox = omap_mbox_get(pdata->mbox_name, &oproc->nb); if (IS_ERR(oproc->mbox)) { ret = PTR_ERR(oproc->mbox); - dev_err(rproc->dev, "omap_mbox_get failed: %d\n", ret); + dev_err(dev, "omap_mbox_get failed: %d\n", ret); return ret; } @@ -133,13 +135,13 @@ static int omap_rproc_start(struct rproc *rproc) */ ret = omap_mbox_msg_send(oproc->mbox, RP_MBOX_ECHO_REQUEST); if (ret) { - dev_err(rproc->dev, "omap_mbox_get failed: %d\n", ret); + dev_err(dev, "omap_mbox_get failed: %d\n", ret); goto put_mbox; } ret = pdata->device_enable(pdev); if (ret) { - dev_err(rproc->dev, "omap_device_enable failed: %d\n", ret); + dev_err(dev, "omap_device_enable failed: %d\n", ret); goto put_mbox; } @@ -153,7 +155,8 @@ put_mbox: /* power off the remote processor */ static int omap_rproc_stop(struct rproc *rproc) { - struct platform_device *pdev = to_platform_device(rproc->dev); + struct device *dev = rproc->dev.parent; + struct platform_device *pdev = to_platform_device(dev); struct omap_rproc_pdata *pdata = pdev->dev.platform_data; struct omap_rproc *oproc = rproc->priv; int ret; diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 288d4175bbf6..25f937843836 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -66,6 +67,9 @@ typedef int (*rproc_handle_resources_t)(struct rproc *rproc, struct resource_table *table, int len); typedef int (*rproc_handle_resource_t)(struct rproc *rproc, void *, int avail); +/* Unique indices for remoteproc devices */ +static DEFINE_IDA(rproc_dev_index); + /* * This is the IOMMU fault handler we register with the IOMMU API * (when relevant; not all remote processors access memory through @@ -92,7 +96,7 @@ static int rproc_iommu_fault(struct iommu_domain *domain, struct device *dev, static int rproc_enable_iommu(struct rproc *rproc) { struct iommu_domain *domain; - struct device *dev = rproc->dev; + struct device *dev = rproc->dev.parent; int ret; /* @@ -137,7 +141,7 @@ free_domain: static void rproc_disable_iommu(struct rproc *rproc) { struct iommu_domain *domain = rproc->domain; - struct device *dev = rproc->dev; + struct device *dev = rproc->dev.parent; if (!domain) return; @@ -217,7 +221,7 @@ static void *rproc_da_to_va(struct rproc *rproc, u64 da, int len) static int rproc_load_segments(struct rproc *rproc, const u8 *elf_data, size_t len) { - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; struct elf32_hdr *ehdr; struct elf32_phdr *phdr; int i, ret = 0; @@ -282,7 +286,7 @@ rproc_load_segments(struct rproc *rproc, const u8 *elf_data, size_t len) int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) { struct rproc *rproc = rvdev->rproc; - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; struct rproc_vring *rvring = &rvdev->vring[i]; dma_addr_t dma; void *va; @@ -301,9 +305,9 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) * this call will also configure the IOMMU for us * TODO: let the rproc know the da of this vring */ - va = dma_alloc_coherent(dev, size, &dma, GFP_KERNEL); + va = dma_alloc_coherent(dev->parent, size, &dma, GFP_KERNEL); if (!va) { - dev_err(dev, "dma_alloc_coherent failed\n"); + dev_err(dev->parent, "dma_alloc_coherent failed\n"); return -EINVAL; } @@ -316,7 +320,7 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) ret = idr_get_new(&rproc->notifyids, rvring, ¬ifyid); if (ret) { dev_err(dev, "idr_get_new failed: %d\n", ret); - dma_free_coherent(dev, size, va, dma); + dma_free_coherent(dev->parent, size, va, dma); return ret; } @@ -334,7 +338,7 @@ static int rproc_parse_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i) { struct rproc *rproc = rvdev->rproc; - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; struct fw_rsc_vdev_vring *vring = &rsc->vring[i]; struct rproc_vring *rvring = &rvdev->vring[i]; @@ -366,7 +370,7 @@ void rproc_free_vring(struct rproc_vring *rvring) int size = PAGE_ALIGN(vring_size(rvring->len, rvring->align)); struct rproc *rproc = rvring->rvdev->rproc; - dma_free_coherent(rproc->dev, size, rvring->va, rvring->dma); + dma_free_coherent(rproc->dev.parent, size, rvring->va, rvring->dma); idr_remove(&rproc->notifyids, rvring->notifyid); } @@ -400,14 +404,14 @@ void rproc_free_vring(struct rproc_vring *rvring) static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, int avail) { - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; struct rproc_vdev *rvdev; int i, ret; /* make sure resource isn't truncated */ if (sizeof(*rsc) + rsc->num_of_vrings * sizeof(struct fw_rsc_vdev_vring) + rsc->config_len > avail) { - dev_err(rproc->dev, "vdev rsc is truncated\n"); + dev_err(dev, "vdev rsc is truncated\n"); return -EINVAL; } @@ -476,12 +480,12 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc, int avail) { struct rproc_mem_entry *trace; - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; void *ptr; char name[15]; if (sizeof(*rsc) > avail) { - dev_err(rproc->dev, "trace rsc is truncated\n"); + dev_err(dev, "trace rsc is truncated\n"); return -EINVAL; } @@ -558,6 +562,7 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc, int avail) { struct rproc_mem_entry *mapping; + struct device *dev = &rproc->dev; int ret; /* no point in handling this resource without a valid iommu domain */ @@ -565,25 +570,25 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc, return -EINVAL; if (sizeof(*rsc) > avail) { - dev_err(rproc->dev, "devmem rsc is truncated\n"); + dev_err(dev, "devmem rsc is truncated\n"); return -EINVAL; } /* make sure reserved bytes are zeroes */ if (rsc->reserved) { - dev_err(rproc->dev, "devmem rsc has non zero reserved bytes\n"); + dev_err(dev, "devmem rsc has non zero reserved bytes\n"); return -EINVAL; } mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); if (!mapping) { - dev_err(rproc->dev, "kzalloc mapping failed\n"); + dev_err(dev, "kzalloc mapping failed\n"); return -ENOMEM; } ret = iommu_map(rproc->domain, rsc->da, rsc->pa, rsc->len, rsc->flags); if (ret) { - dev_err(rproc->dev, "failed to map devmem: %d\n", ret); + dev_err(dev, "failed to map devmem: %d\n", ret); goto out; } @@ -598,7 +603,7 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc, mapping->len = rsc->len; list_add_tail(&mapping->node, &rproc->mappings); - dev_dbg(rproc->dev, "mapped devmem pa 0x%x, da 0x%x, len 0x%x\n", + dev_dbg(dev, "mapped devmem pa 0x%x, da 0x%x, len 0x%x\n", rsc->pa, rsc->da, rsc->len); return 0; @@ -630,13 +635,13 @@ static int rproc_handle_carveout(struct rproc *rproc, struct fw_rsc_carveout *rsc, int avail) { struct rproc_mem_entry *carveout, *mapping; - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; dma_addr_t dma; void *va; int ret; if (sizeof(*rsc) > avail) { - dev_err(rproc->dev, "carveout rsc is truncated\n"); + dev_err(dev, "carveout rsc is truncated\n"); return -EINVAL; } @@ -662,9 +667,9 @@ static int rproc_handle_carveout(struct rproc *rproc, goto free_mapping; } - va = dma_alloc_coherent(dev, rsc->len, &dma, GFP_KERNEL); + va = dma_alloc_coherent(dev->parent, rsc->len, &dma, GFP_KERNEL); if (!va) { - dev_err(dev, "failed to dma alloc carveout: %d\n", rsc->len); + dev_err(dev->parent, "dma_alloc_coherent err: %d\n", rsc->len); ret = -ENOMEM; goto free_carv; } @@ -735,7 +740,7 @@ static int rproc_handle_carveout(struct rproc *rproc, return 0; dma_free: - dma_free_coherent(dev, rsc->len, va, dma); + dma_free_coherent(dev->parent, rsc->len, va, dma); free_carv: kfree(carveout); free_mapping: @@ -758,7 +763,7 @@ static rproc_handle_resource_t rproc_handle_rsc[] = { static int rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len) { - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; rproc_handle_resource_t handler; int ret = 0, i; @@ -797,7 +802,7 @@ rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len static int rproc_handle_virtio_rsc(struct rproc *rproc, struct resource_table *table, int len) { - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; int ret = 0, i; for (i = 0; i < table->num; i++) { @@ -850,7 +855,7 @@ rproc_find_rsc_table(struct rproc *rproc, const u8 *elf_data, size_t len, struct elf32_hdr *ehdr; struct elf32_shdr *shdr; const char *name_table; - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; struct resource_table *table = NULL; int i; @@ -916,7 +921,7 @@ rproc_find_rsc_table(struct rproc *rproc, const u8 *elf_data, size_t len, static void rproc_resource_cleanup(struct rproc *rproc) { struct rproc_mem_entry *entry, *tmp; - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; /* clean up debugfs trace entries */ list_for_each_entry_safe(entry, tmp, &rproc->traces, node) { @@ -928,7 +933,7 @@ static void rproc_resource_cleanup(struct rproc *rproc) /* clean up carveout allocations */ list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) { - dma_free_coherent(dev, entry->len, entry->va, entry->dma); + dma_free_coherent(dev->parent, entry->len, entry->va, entry->dma); list_del(&entry->node); kfree(entry); } @@ -953,7 +958,7 @@ static void rproc_resource_cleanup(struct rproc *rproc) static int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw) { const char *name = rproc->firmware; - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; struct elf32_hdr *ehdr; char class; @@ -1014,7 +1019,7 @@ static int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw) */ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw) { - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; const char *name = rproc->firmware; struct elf32_hdr *ehdr; struct resource_table *table; @@ -1138,7 +1143,7 @@ int rproc_boot(struct rproc *rproc) return -EINVAL; } - dev = rproc->dev; + dev = &rproc->dev; ret = mutex_lock_interruptible(&rproc->lock); if (ret) { @@ -1154,7 +1159,7 @@ int rproc_boot(struct rproc *rproc) } /* prevent underlying implementation from being removed */ - if (!try_module_get(dev->driver->owner)) { + if (!try_module_get(dev->parent->driver->owner)) { dev_err(dev, "%s: can't get owner\n", __func__); ret = -EINVAL; goto unlock_mutex; @@ -1181,7 +1186,7 @@ int rproc_boot(struct rproc *rproc) downref_rproc: if (ret) { - module_put(dev->driver->owner); + module_put(dev->parent->driver->owner); atomic_dec(&rproc->power); } unlock_mutex: @@ -1215,7 +1220,7 @@ EXPORT_SYMBOL(rproc_boot); */ void rproc_shutdown(struct rproc *rproc) { - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; int ret; ret = mutex_lock_interruptible(&rproc->lock); @@ -1248,7 +1253,7 @@ void rproc_shutdown(struct rproc *rproc) out: mutex_unlock(&rproc->lock); if (!ret) - module_put(dev->driver->owner); + module_put(dev->parent->driver->owner); } EXPORT_SYMBOL(rproc_shutdown); @@ -1271,7 +1276,7 @@ void rproc_release(struct kref *kref) { struct rproc *rproc = container_of(kref, struct rproc, refcount); - dev_info(rproc->dev, "removing %s\n", rproc->name); + dev_info(&rproc->dev, "removing %s\n", rproc->name); rproc_delete_debug_dir(rproc); @@ -1403,13 +1408,17 @@ EXPORT_SYMBOL(rproc_put); */ int rproc_register(struct rproc *rproc) { - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; int ret = 0; + ret = device_add(dev); + if (ret < 0) + return ret; + /* expose to rproc_get_by_name users */ klist_add_tail(&rproc->node, &rprocs); - dev_info(rproc->dev, "%s is available\n", rproc->name); + dev_info(dev, "%s is available\n", rproc->name); dev_info(dev, "Note: remoteproc is still under development and considered experimental.\n"); dev_info(dev, "THE BINARY FORMAT IS NOT YET FINALIZED, and backward compatibility isn't yet guaranteed.\n"); @@ -1441,6 +1450,33 @@ int rproc_register(struct rproc *rproc) } EXPORT_SYMBOL(rproc_register); +/** + * rproc_type_release() - release a remote processor instance + * @dev: the rproc's device + * + * This function should _never_ be called directly. + * + * It will be called by the driver core when no one holds a valid pointer + * to @dev anymore. + */ +static void rproc_type_release(struct device *dev) +{ + struct rproc *rproc = container_of(dev, struct rproc, dev); + + idr_remove_all(&rproc->notifyids); + idr_destroy(&rproc->notifyids); + + if (rproc->index >= 0) + ida_simple_remove(&rproc_dev_index, rproc->index); + + kfree(rproc); +} + +static struct device_type rproc_type = { + .name = "remoteproc", + .release = rproc_type_release, +}; + /** * rproc_alloc() - allocate a remote processor handle * @dev: the underlying device @@ -1479,12 +1515,25 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, return NULL; } - rproc->dev = dev; rproc->name = name; rproc->ops = ops; rproc->firmware = firmware; rproc->priv = &rproc[1]; + device_initialize(&rproc->dev); + rproc->dev.parent = dev; + rproc->dev.type = &rproc_type; + + /* Assign a unique device index and name */ + rproc->index = ida_simple_get(&rproc_dev_index, 0, 0, GFP_KERNEL); + if (rproc->index < 0) { + dev_err(dev, "ida_simple_get failed: %d\n", rproc->index); + put_device(&rproc->dev); + return NULL; + } + + dev_set_name(&rproc->dev, "remoteproc%d", rproc->index); + atomic_set(&rproc->power, 0); kref_init(&rproc->refcount); @@ -1516,10 +1565,7 @@ EXPORT_SYMBOL(rproc_alloc); */ void rproc_free(struct rproc *rproc) { - idr_remove_all(&rproc->notifyids); - idr_destroy(&rproc->notifyids); - - kfree(rproc); + put_device(&rproc->dev); } EXPORT_SYMBOL(rproc_free); @@ -1560,6 +1606,8 @@ int rproc_unregister(struct rproc *rproc) /* the rproc is downref'ed as soon as it's removed from the klist */ klist_del(&rproc->node); + device_del(&rproc->dev); + /* the rproc will only be released after its refcount drops to zero */ kref_put(&rproc->refcount, rproc_release); @@ -1570,6 +1618,7 @@ EXPORT_SYMBOL(rproc_unregister); static int __init remoteproc_init(void) { rproc_init_debugfs(); + return 0; } module_init(remoteproc_init); diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c index 85d31a69e117..03833850f214 100644 --- a/drivers/remoteproc/remoteproc_debugfs.c +++ b/drivers/remoteproc/remoteproc_debugfs.c @@ -124,7 +124,7 @@ struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc, tfile = debugfs_create_file(name, 0400, rproc->dbg_dir, trace, &trace_rproc_ops); if (!tfile) { - dev_err(rproc->dev, "failed to create debugfs trace entry\n"); + dev_err(&rproc->dev, "failed to create debugfs trace entry\n"); return NULL; } @@ -141,7 +141,7 @@ void rproc_delete_debug_dir(struct rproc *rproc) void rproc_create_debug_dir(struct rproc *rproc) { - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; if (!rproc_dbg) return; diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c index 26a7144e7f3b..b6621831a58a 100644 --- a/drivers/remoteproc/remoteproc_virtio.c +++ b/drivers/remoteproc/remoteproc_virtio.c @@ -36,7 +36,7 @@ static void rproc_virtio_notify(struct virtqueue *vq) struct rproc *rproc = rvring->rvdev->rproc; int notifyid = rvring->notifyid; - dev_dbg(rproc->dev, "kicking vq index: %d\n", notifyid); + dev_dbg(&rproc->dev, "kicking vq index: %d\n", notifyid); rproc->ops->kick(rproc, notifyid); } @@ -57,7 +57,7 @@ irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int notifyid) { struct rproc_vring *rvring; - dev_dbg(rproc->dev, "vq index %d is interrupted\n", notifyid); + dev_dbg(&rproc->dev, "vq index %d is interrupted\n", notifyid); rvring = idr_find(&rproc->notifyids, notifyid); if (!rvring || !rvring->vq) @@ -74,6 +74,7 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, { struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); struct rproc *rproc = vdev_to_rproc(vdev); + struct device *dev = &rproc->dev; struct rproc_vring *rvring; struct virtqueue *vq; void *addr; @@ -95,7 +96,7 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, size = vring_size(len, rvring->align); memset(addr, 0, size); - dev_dbg(rproc->dev, "vring%d: va %p qsz %d notifyid %d\n", + dev_dbg(dev, "vring%d: va %p qsz %d notifyid %d\n", id, addr, len, rvring->notifyid); /* @@ -105,7 +106,7 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, vq = vring_new_virtqueue(len, rvring->align, vdev, false, addr, rproc_virtio_notify, callback, name); if (!vq) { - dev_err(rproc->dev, "vring_new_virtqueue %s failed\n", name); + dev_err(dev, "vring_new_virtqueue %s failed\n", name); rproc_free_vring(rvring); return ERR_PTR(-ENOMEM); } @@ -152,7 +153,7 @@ static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs, /* now that the vqs are all set, boot the remote processor */ ret = rproc_boot(rproc); if (ret) { - dev_err(rproc->dev, "rproc_boot() failed %d\n", ret); + dev_err(&rproc->dev, "rproc_boot() failed %d\n", ret); goto error; } @@ -254,7 +255,7 @@ static void rproc_vdev_release(struct device *dev) int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id) { struct rproc *rproc = rvdev->rproc; - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; struct virtio_device *vdev = &rvdev->vdev; int ret; diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index 75506ec2840e..691e52ec48d9 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -911,7 +911,8 @@ static int rpmsg_probe(struct virtio_device *vdev) vrp->svq = vqs[1]; /* allocate coherent memory for the buffers */ - bufs_va = dma_alloc_coherent(vdev->dev.parent, RPMSG_TOTAL_BUF_SPACE, + bufs_va = dma_alloc_coherent(vdev->dev.parent->parent, + RPMSG_TOTAL_BUF_SPACE, &vrp->bufs_dma, GFP_KERNEL); if (!bufs_va) goto vqs_del; diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index f1ffabb978d3..7f806dcf5278 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -369,7 +369,7 @@ enum rproc_state { * @firmware: name of firmware file to be loaded * @priv: private data which belongs to the platform-specific rproc module * @ops: platform-specific start/stop rproc handlers - * @dev: underlying device + * @dev: virtual device for refcounting and common remoteproc behavior * @refcount: refcount of users that have a valid pointer to this rproc * @power: refcount of users who need this rproc powered up * @state: state of the device @@ -383,6 +383,7 @@ enum rproc_state { * @bootaddr: address of first instruction to boot rproc with (optional) * @rvdevs: list of remote virtio devices * @notifyids: idr for dynamically assigning rproc-wide unique notify ids + * @index: index of this rproc device */ struct rproc { struct klist_node node; @@ -391,7 +392,7 @@ struct rproc { const char *firmware; void *priv; const struct rproc_ops *ops; - struct device *dev; + struct device dev; struct kref refcount; atomic_t power; unsigned int state; @@ -405,6 +406,7 @@ struct rproc { u32 bootaddr; struct list_head rvdevs; struct idr notifyids; + int index; }; /* we currently support only two vrings per rvdev */ -- cgit v1.2.3 From 7183a2a799b81490354973117ecd810c23cdc668 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Wed, 30 May 2012 22:02:24 +0300 Subject: remoteproc: remove the now-redundant kref Now that every rproc instance contains a device, we don't need a kref anymore to maintain the refcount of the rproc instances: that's what device are good with! This patch removes the now-redundant kref, and switches to {get, put}_device instead of kref_{get, put}. We also don't need the kref's release function anymore, and instead, we just utilize the class's release handler (which is now responsible for all memory de-allocations). Cc: Stephen Boyd Cc: Fernando Guzman Lugo Signed-off-by: Ohad Ben-Cohen --- drivers/remoteproc/remoteproc_core.c | 50 ++++++++-------------------------- drivers/remoteproc/remoteproc_virtio.c | 8 +++--- include/linux/remoteproc.h | 3 -- 3 files changed, 15 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 25f937843836..aa713aade30e 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -1257,42 +1257,12 @@ out: } EXPORT_SYMBOL(rproc_shutdown); -/** - * rproc_release() - completely deletes the existence of a remote processor - * @kref: the rproc's kref - * - * This function should _never_ be called directly. - * - * The only reasonable location to use it is as an argument when kref_put'ing - * @rproc's refcount. - * - * This way it will be called when no one holds a valid pointer to this @rproc - * anymore (and obviously after it is removed from the rprocs klist). - * - * Note: this function is not static because rproc_vdev_release() needs it when - * it decrements @rproc's refcount. - */ -void rproc_release(struct kref *kref) -{ - struct rproc *rproc = container_of(kref, struct rproc, refcount); - - dev_info(&rproc->dev, "removing %s\n", rproc->name); - - rproc_delete_debug_dir(rproc); - - /* - * At this point no one holds a reference to rproc anymore, - * so we can directly unroll rproc_alloc() - */ - rproc_free(rproc); -} - /* will be called when an rproc is added to the rprocs klist */ static void klist_rproc_get(struct klist_node *n) { struct rproc *rproc = container_of(n, struct rproc, node); - kref_get(&rproc->refcount); + get_device(&rproc->dev); } /* will be called when an rproc is removed from the rprocs klist */ @@ -1300,7 +1270,7 @@ static void klist_rproc_put(struct klist_node *n) { struct rproc *rproc = container_of(n, struct rproc, node); - kref_put(&rproc->refcount, rproc_release); + put_device(&rproc->dev); } static struct rproc *next_rproc(struct klist_iter *i) @@ -1342,7 +1312,7 @@ struct rproc *rproc_get_by_name(const char *name) klist_iter_init(&rprocs, &i); while ((rproc = next_rproc(&i)) != NULL) if (!strcmp(rproc->name, name)) { - kref_get(&rproc->refcount); + get_device(&rproc->dev); break; } klist_iter_exit(&i); @@ -1355,7 +1325,7 @@ struct rproc *rproc_get_by_name(const char *name) ret = rproc_boot(rproc); if (ret < 0) { - kref_put(&rproc->refcount, rproc_release); + put_device(&rproc->dev); return NULL; } @@ -1382,7 +1352,7 @@ void rproc_put(struct rproc *rproc) rproc_shutdown(rproc); /* downref rproc's refcount */ - kref_put(&rproc->refcount, rproc_release); + put_device(&rproc->dev); } EXPORT_SYMBOL(rproc_put); @@ -1463,6 +1433,10 @@ static void rproc_type_release(struct device *dev) { struct rproc *rproc = container_of(dev, struct rproc, dev); + dev_info(&rproc->dev, "releasing %s\n", rproc->name); + + rproc_delete_debug_dir(rproc); + idr_remove_all(&rproc->notifyids); idr_destroy(&rproc->notifyids); @@ -1536,8 +1510,6 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, atomic_set(&rproc->power, 0); - kref_init(&rproc->refcount); - mutex_init(&rproc->lock); idr_init(&rproc->notifyids); @@ -1608,8 +1580,8 @@ int rproc_unregister(struct rproc *rproc) device_del(&rproc->dev); - /* the rproc will only be released after its refcount drops to zero */ - kref_put(&rproc->refcount, rproc_release); + /* unroll rproc_alloc. TODO: we may want to let the users do that */ + put_device(&rproc->dev); return 0; } diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c index b6621831a58a..3541b4492f64 100644 --- a/drivers/remoteproc/remoteproc_virtio.c +++ b/drivers/remoteproc/remoteproc_virtio.c @@ -225,7 +225,7 @@ static struct virtio_config_ops rproc_virtio_config_ops = { /* * This function is called whenever vdev is released, and is responsible - * to decrement the remote processor's refcount taken when vdev was + * to decrement the remote processor's refcount which was taken when vdev was * added. * * Never call this function directly; it will be called by the driver @@ -240,7 +240,7 @@ static void rproc_vdev_release(struct device *dev) list_del(&rvdev->node); kfree(rvdev); - kref_put(&rproc->refcount, rproc_release); + put_device(&rproc->dev); } /** @@ -272,11 +272,11 @@ int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id) * Therefore we must increment the rproc refcount here, and decrement * it _only_ when the vdev is released. */ - kref_get(&rproc->refcount); + get_device(&rproc->dev); ret = register_virtio_device(vdev); if (ret) { - kref_put(&rproc->refcount, rproc_release); + put_device(&rproc->dev); dev_err(dev, "failed to register vdev: %d\n", ret); goto out; } diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 7f806dcf5278..cbe8a51a21de 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -36,7 +36,6 @@ #define REMOTEPROC_H #include -#include #include #include #include @@ -370,7 +369,6 @@ enum rproc_state { * @priv: private data which belongs to the platform-specific rproc module * @ops: platform-specific start/stop rproc handlers * @dev: virtual device for refcounting and common remoteproc behavior - * @refcount: refcount of users that have a valid pointer to this rproc * @power: refcount of users who need this rproc powered up * @state: state of the device * @lock: lock which protects concurrent manipulations of the rproc @@ -393,7 +391,6 @@ struct rproc { void *priv; const struct rproc_ops *ops; struct device dev; - struct kref refcount; atomic_t power; unsigned int state; struct mutex lock; -- cgit v1.2.3 From 40e575b1d0b34b38519d361c10bdf8e0c688957b Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Mon, 2 Jul 2012 20:20:53 +0300 Subject: remoteproc: remove the get_by_name/put API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove rproc_get_by_name() and rproc_put(), and the associated remoteproc infrastructure that supports it (i.e. klist and friends), because: 1. No one uses them 2. Using them is highly discouraged, and any potential user will be deeply scrutinized and encouraged to move. If a user, that absolutely can't live with the direct boot/shutdown model, does show up one day, then bringing this functionality back is going to be trivial. At this point though, keeping this functionality around is way too much of a maintenance burden. Cc: Sjur Brændeland Cc: Loic Pallardy Cc: Ludovic BARRE Cc: Michal Simek Cc: Fernando Guzman Lugo Cc: Suman Anna Cc: Mark Grosen Acked-by: Stephen Boyd Signed-off-by: Ohad Ben-Cohen --- Documentation/remoteproc.txt | 27 +------- drivers/remoteproc/remoteproc_core.c | 130 ----------------------------------- include/linux/remoteproc.h | 3 - 3 files changed, 1 insertion(+), 159 deletions(-) (limited to 'include') diff --git a/Documentation/remoteproc.txt b/Documentation/remoteproc.txt index ad6ded4bca5c..f33c3bbbc867 100644 --- a/Documentation/remoteproc.txt +++ b/Documentation/remoteproc.txt @@ -36,8 +36,7 @@ cost. Note: to use this function you should already have a valid rproc handle. There are several ways to achieve that cleanly (devres, pdata, the way remoteproc_rpmsg.c does this, or, if this becomes prevalent, we - might also consider using dev_archdata for this). See also - rproc_get_by_name() below. + might also consider using dev_archdata for this). void rproc_shutdown(struct rproc *rproc) - Power off a remote processor (previously booted with rproc_boot()). @@ -51,30 +50,6 @@ cost. which means that the @rproc handle stays valid even after rproc_shutdown() returns, and users can still use it with a subsequent rproc_boot(), if needed. - - don't call rproc_shutdown() to unroll rproc_get_by_name(), exactly - because rproc_shutdown() _does not_ decrement the refcount of @rproc. - To decrement the refcount of @rproc, use rproc_put() (but _only_ if - you acquired @rproc using rproc_get_by_name()). - - struct rproc *rproc_get_by_name(const char *name) - - Find an rproc handle using the remote processor's name, and then - boot it. If it's already powered on, then just immediately return - (successfully). Returns the rproc handle on success, and NULL on failure. - This function increments the remote processor's refcount, so always - use rproc_put() to decrement it back once rproc isn't needed anymore. - Note: currently rproc_get_by_name() and rproc_put() are not used anymore - by the rpmsg bus and its drivers. We need to scrutinize the use cases - that still need them, and see if we can migrate them to use the non - name-based boot/shutdown interface. - - void rproc_put(struct rproc *rproc) - - Decrement @rproc's power refcount and shut it down if it reaches zero - (essentially by just calling rproc_shutdown), and then decrement @rproc's - validity refcount too. - After this function returns, @rproc may _not_ be used anymore, and its - handle should be considered invalid. - This function should be called _iff_ the @rproc handle was grabbed by - calling rproc_get_by_name(). 3. Typical usage diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index c85db123ba0a..0c77c4fcf436 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include @@ -44,25 +43,6 @@ #include "remoteproc_internal.h" -static void klist_rproc_get(struct klist_node *n); -static void klist_rproc_put(struct klist_node *n); - -/* - * klist of the available remote processors. - * - * We need this in order to support name-based lookups (needed by the - * rproc_get_by_name()). - * - * That said, we don't use rproc_get_by_name() at this point. - * The use cases that do require its existence should be - * scrutinized, and hopefully migrated to rproc_boot() using device-based - * binding. - * - * If/when this materializes, we could drop the klist (and the by_name - * API). - */ -static DEFINE_KLIST(rprocs, klist_rproc_get, klist_rproc_put); - typedef int (*rproc_handle_resources_t)(struct rproc *rproc, struct resource_table *table, int len); typedef int (*rproc_handle_resource_t)(struct rproc *rproc, void *, int avail); @@ -1217,10 +1197,6 @@ EXPORT_SYMBOL(rproc_boot); * which means that the @rproc handle stays valid even after rproc_shutdown() * returns, and users can still use it with a subsequent rproc_boot(), if * needed. - * - don't call rproc_shutdown() to unroll rproc_get_by_name(), exactly - * because rproc_shutdown() _does not_ decrement the refcount of @rproc. - * To decrement the refcount of @rproc, use rproc_put() (but _only_ if - * you acquired @rproc using rproc_get_by_name()). */ void rproc_shutdown(struct rproc *rproc) { @@ -1261,105 +1237,6 @@ out: } EXPORT_SYMBOL(rproc_shutdown); -/* will be called when an rproc is added to the rprocs klist */ -static void klist_rproc_get(struct klist_node *n) -{ - struct rproc *rproc = container_of(n, struct rproc, node); - - get_device(&rproc->dev); -} - -/* will be called when an rproc is removed from the rprocs klist */ -static void klist_rproc_put(struct klist_node *n) -{ - struct rproc *rproc = container_of(n, struct rproc, node); - - put_device(&rproc->dev); -} - -static struct rproc *next_rproc(struct klist_iter *i) -{ - struct klist_node *n; - - n = klist_next(i); - if (!n) - return NULL; - - return container_of(n, struct rproc, node); -} - -/** - * rproc_get_by_name() - find a remote processor by name and boot it - * @name: name of the remote processor - * - * Finds an rproc handle using the remote processor's name, and then - * boot it. If it's already powered on, then just immediately return - * (successfully). - * - * Returns the rproc handle on success, and NULL on failure. - * - * This function increments the remote processor's refcount, so always - * use rproc_put() to decrement it back once rproc isn't needed anymore. - * - * Note: currently this function (and its counterpart rproc_put()) are not - * being used. We need to scrutinize the use cases - * that still need them, and see if we can migrate them to use the non - * name-based boot/shutdown interface. - */ -struct rproc *rproc_get_by_name(const char *name) -{ - struct rproc *rproc; - struct klist_iter i; - int ret; - - /* find the remote processor, and upref its refcount */ - klist_iter_init(&rprocs, &i); - while ((rproc = next_rproc(&i)) != NULL) - if (!strcmp(rproc->name, name)) { - get_device(&rproc->dev); - break; - } - klist_iter_exit(&i); - - /* can't find this rproc ? */ - if (!rproc) { - pr_err("can't find remote processor %s\n", name); - return NULL; - } - - ret = rproc_boot(rproc); - if (ret < 0) { - put_device(&rproc->dev); - return NULL; - } - - return rproc; -} -EXPORT_SYMBOL(rproc_get_by_name); - -/** - * rproc_put() - decrement the refcount of a remote processor, and shut it down - * @rproc: the remote processor - * - * This function tries to shutdown @rproc, and it then decrements its - * refcount. - * - * After this function returns, @rproc may _not_ be used anymore, and its - * handle should be considered invalid. - * - * This function should be called _iff_ the @rproc handle was grabbed by - * calling rproc_get_by_name(). - */ -void rproc_put(struct rproc *rproc) -{ - /* try to power off the remote processor */ - rproc_shutdown(rproc); - - /* downref rproc's refcount */ - put_device(&rproc->dev); -} -EXPORT_SYMBOL(rproc_put); - /** * rproc_register() - register a remote processor * @rproc: the remote processor handle to register @@ -1389,9 +1266,6 @@ int rproc_register(struct rproc *rproc) if (ret < 0) return ret; - /* expose to rproc_get_by_name users */ - klist_add_tail(&rproc->node, &rprocs); - dev_info(dev, "%s is available\n", rproc->name); dev_info(dev, "Note: remoteproc is still under development and considered experimental.\n"); @@ -1417,7 +1291,6 @@ int rproc_register(struct rproc *rproc) if (ret < 0) { dev_err(dev, "request_firmware_nowait failed: %d\n", ret); complete_all(&rproc->firmware_loading_complete); - klist_remove(&rproc->node); } return ret; @@ -1573,9 +1446,6 @@ int rproc_unregister(struct rproc *rproc) list_for_each_entry_safe(rvdev, tmp, &rproc->rvdevs, node) rproc_remove_virtio_dev(rvdev); - /* the rproc is downref'ed as soon as it's removed from the klist */ - klist_del(&rproc->node); - device_del(&rproc->dev); return 0; diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index cbe8a51a21de..b88d6af5ba52 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -449,9 +449,6 @@ struct rproc_vdev { unsigned long gfeatures; }; -struct rproc *rproc_get_by_name(const char *name); -void rproc_put(struct rproc *rproc); - struct rproc *rproc_alloc(struct device *dev, const char *name, const struct rproc_ops *ops, const char *firmware, int len); -- cgit v1.2.3 From 160e7c840fe85836040c43e0058d5afced470c85 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Wed, 4 Jul 2012 16:25:06 +0300 Subject: remoteproc: adopt the driver core's alloc/add/del/put naming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To make remoteproc's API more intuitive for developers, we adopt the driver core's naming, i.e. alloc -> add -> del -> put. We'll also add register/unregister when their first user shows up. Otherwise - there's no functional change here. Suggested by Russell King . Cc: Russell King Cc: Fernando Guzman Lugo Cc: Sjur Brændeland Reviewed-by: Linus Walleij Acked-by: Stephen Boyd Signed-off-by: Ohad Ben-Cohen --- Documentation/remoteproc.txt | 18 +++++++++--------- drivers/remoteproc/omap_remoteproc.c | 8 ++++---- drivers/remoteproc/remoteproc_core.c | 32 ++++++++++++++++---------------- include/linux/remoteproc.h | 6 +++--- 4 files changed, 32 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/Documentation/remoteproc.txt b/Documentation/remoteproc.txt index f33c3bbbc867..23a09b884bc7 100644 --- a/Documentation/remoteproc.txt +++ b/Documentation/remoteproc.txt @@ -90,21 +90,21 @@ int dummy_rproc_example(struct rproc *my_rproc) This function should be used by rproc implementations during initialization of the remote processor. After creating an rproc handle using this function, and when ready, - implementations should then call rproc_register() to complete + implementations should then call rproc_add() to complete the registration of the remote processor. On success, the new rproc is returned, and on failure, NULL. Note: _never_ directly deallocate @rproc, even if it was not registered - yet. Instead, when you need to unroll rproc_alloc(), use rproc_free(). + yet. Instead, when you need to unroll rproc_alloc(), use rproc_put(). - void rproc_free(struct rproc *rproc) + void rproc_put(struct rproc *rproc) - Free an rproc handle that was allocated by rproc_alloc. This function essentially unrolls rproc_alloc(), by decrementing the rproc's refcount. It doesn't directly free rproc; that would happen only if there are no other references to rproc and its refcount now dropped to zero. - int rproc_register(struct rproc *rproc) + int rproc_add(struct rproc *rproc) - Register @rproc with the remoteproc framework, after it has been allocated with rproc_alloc(). This is called by the platform-specific rproc implementation, whenever @@ -117,15 +117,15 @@ int dummy_rproc_example(struct rproc *my_rproc) of registering this remote processor, additional virtio drivers might get probed. - int rproc_unregister(struct rproc *rproc) - - Unroll rproc_register(). + int rproc_del(struct rproc *rproc) + - Unroll rproc_add(). This function should be called when the platform specific rproc implementation decides to remove the rproc device. it should - _only_ be called if a previous invocation of rproc_register() + _only_ be called if a previous invocation of rproc_add() has completed successfully. - After rproc_unregister() returns, @rproc is still valid, and its - last refcount should be decremented by calling rproc_free(). + After rproc_del() returns, @rproc is still valid, and its + last refcount should be decremented by calling rproc_put(). Returns 0 on success and -EINVAL if @rproc isn't valid. diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index 4f2fe8fd7fe6..02bae3a5264f 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -199,14 +199,14 @@ static int __devinit omap_rproc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, rproc); - ret = rproc_register(rproc); + ret = rproc_add(rproc); if (ret) goto free_rproc; return 0; free_rproc: - rproc_free(rproc); + rproc_put(rproc); return ret; } @@ -214,8 +214,8 @@ static int __devexit omap_rproc_remove(struct platform_device *pdev) { struct rproc *rproc = platform_get_drvdata(pdev); - rproc_unregister(rproc); - rproc_free(rproc); + rproc_del(rproc); + rproc_put(rproc); return 0; } diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 0c77c4fcf436..25fd9733d5df 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -1101,7 +1101,7 @@ static void rproc_fw_config_virtio(const struct firmware *fw, void *context) out: release_firmware(fw); - /* allow rproc_unregister() contexts, if any, to proceed */ + /* allow rproc_del() contexts, if any, to proceed */ complete_all(&rproc->firmware_loading_complete); } @@ -1238,7 +1238,7 @@ out: EXPORT_SYMBOL(rproc_shutdown); /** - * rproc_register() - register a remote processor + * rproc_add() - register a remote processor * @rproc: the remote processor handle to register * * Registers @rproc with the remoteproc framework, after it has been @@ -1257,7 +1257,7 @@ EXPORT_SYMBOL(rproc_shutdown); * of registering this remote processor, additional virtio drivers might be * probed. */ -int rproc_register(struct rproc *rproc) +int rproc_add(struct rproc *rproc) { struct device *dev = &rproc->dev; int ret = 0; @@ -1274,7 +1274,7 @@ int rproc_register(struct rproc *rproc) /* create debugfs entries */ rproc_create_debug_dir(rproc); - /* rproc_unregister() calls must wait until async loader completes */ + /* rproc_del() calls must wait until async loader completes */ init_completion(&rproc->firmware_loading_complete); /* @@ -1295,7 +1295,7 @@ int rproc_register(struct rproc *rproc) return ret; } -EXPORT_SYMBOL(rproc_register); +EXPORT_SYMBOL(rproc_add); /** * rproc_type_release() - release a remote processor instance @@ -1343,13 +1343,13 @@ static struct device_type rproc_type = { * of the remote processor. * * After creating an rproc handle using this function, and when ready, - * implementations should then call rproc_register() to complete + * implementations should then call rproc_add() to complete * the registration of the remote processor. * * On success the new rproc is returned, and on failure, NULL. * * Note: _never_ directly deallocate @rproc, even if it was not registered - * yet. Instead, when you need to unroll rproc_alloc(), use rproc_free(). + * yet. Instead, when you need to unroll rproc_alloc(), use rproc_put(). */ struct rproc *rproc_alloc(struct device *dev, const char *name, const struct rproc_ops *ops, @@ -1403,7 +1403,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, EXPORT_SYMBOL(rproc_alloc); /** - * rproc_free() - unroll rproc_alloc() + * rproc_put() - unroll rproc_alloc() * @rproc: the remote processor handle * * This function decrements the rproc dev refcount. @@ -1411,28 +1411,28 @@ EXPORT_SYMBOL(rproc_alloc); * If no one holds any reference to rproc anymore, then its refcount would * now drop to zero, and it would be freed. */ -void rproc_free(struct rproc *rproc) +void rproc_put(struct rproc *rproc) { put_device(&rproc->dev); } -EXPORT_SYMBOL(rproc_free); +EXPORT_SYMBOL(rproc_put); /** - * rproc_unregister() - unregister a remote processor + * rproc_del() - unregister a remote processor * @rproc: rproc handle to unregister * * This function should be called when the platform specific rproc * implementation decides to remove the rproc device. it should - * _only_ be called if a previous invocation of rproc_register() + * _only_ be called if a previous invocation of rproc_add() * has completed successfully. * - * After rproc_unregister() returns, @rproc isn't freed yet, because + * After rproc_del() returns, @rproc isn't freed yet, because * of the outstanding reference created by rproc_alloc. To decrement that - * one last refcount, one still needs to call rproc_free(). + * one last refcount, one still needs to call rproc_put(). * * Returns 0 on success and -EINVAL if @rproc isn't valid. */ -int rproc_unregister(struct rproc *rproc) +int rproc_del(struct rproc *rproc) { struct rproc_vdev *rvdev, *tmp; @@ -1450,7 +1450,7 @@ int rproc_unregister(struct rproc *rproc) return 0; } -EXPORT_SYMBOL(rproc_unregister); +EXPORT_SYMBOL(rproc_del); static int __init remoteproc_init(void) { diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index b88d6af5ba52..eea3ac86b2b7 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -452,9 +452,9 @@ struct rproc_vdev { struct rproc *rproc_alloc(struct device *dev, const char *name, const struct rproc_ops *ops, const char *firmware, int len); -void rproc_free(struct rproc *rproc); -int rproc_register(struct rproc *rproc); -int rproc_unregister(struct rproc *rproc); +void rproc_put(struct rproc *rproc); +int rproc_add(struct rproc *rproc); +int rproc_del(struct rproc *rproc); int rproc_boot(struct rproc *rproc); void rproc_shutdown(struct rproc *rproc); -- cgit v1.2.3 From e27947c767f5bed15048f4e4dad3e2eb69133697 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 23 May 2012 14:35:23 -0500 Subject: libceph: define and use an explicit CONNECTED state There is no state explicitly defined when a ceph connection is fully operational. So define one. It's set when the connection sequence completes successfully, and is cleared when the connection gets closed. Be a little more careful when examining the old state when a socket disconnect event is reported. Signed-off-by: Alex Elder Reviewed-by: Sage Weil --- include/linux/ceph/messenger.h | 1 + net/ceph/messenger.c | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index cc6f9bdcf466..002d504df3b7 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -120,6 +120,7 @@ struct ceph_msg_pos { */ #define CONNECTING 1 #define NEGOTIATING 2 +#define CONNECTED 5 #define STANDBY 8 /* no outgoing messages, socket closed. we keep * the ceph_connection around to maintain shared * state with the peer. */ diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 500207bad5d6..83bcf977e9b9 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -463,6 +463,7 @@ void ceph_con_close(struct ceph_connection *con) ceph_pr_addr(&con->peer_addr.in_addr)); clear_bit(NEGOTIATING, &con->state); clear_bit(CONNECTING, &con->state); + clear_bit(CONNECTED, &con->state); clear_bit(STANDBY, &con->state); /* avoid connect_seq bump */ set_bit(CLOSED, &con->state); @@ -1564,6 +1565,7 @@ static int process_connect(struct ceph_connection *con) } clear_bit(NEGOTIATING, &con->state); clear_bit(CONNECTING, &con->state); + set_bit(CONNECTED, &con->state); con->peer_global_seq = le32_to_cpu(con->in_reply.global_seq); con->connect_seq++; con->peer_features = server_feat; @@ -2114,6 +2116,7 @@ more: prepare_read_ack(con); break; case CEPH_MSGR_TAG_CLOSE: + clear_bit(CONNECTED, &con->state); set_bit(CLOSED, &con->state); /* fixme */ goto out; default: @@ -2190,11 +2193,13 @@ static void con_work(struct work_struct *work) mutex_lock(&con->mutex); restart: if (test_and_clear_bit(SOCK_CLOSED, &con->flags)) { - if (test_and_clear_bit(CONNECTING, &con->state)) { + if (test_and_clear_bit(CONNECTED, &con->state)) + con->error_msg = "socket closed"; + else if (test_and_clear_bit(CONNECTING, &con->state)) { clear_bit(NEGOTIATING, &con->state); con->error_msg = "connection failed"; } else { - con->error_msg = "socket closed"; + con->error_msg = "unrecognized con state"; } goto fault; } -- cgit v1.2.3 From 261030215d970c62f799e6e508e3c68fc7ec2aa9 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 21 Jun 2012 12:49:23 -0700 Subject: libceph: drop declaration of ceph_con_get() For some reason the declaration of ceph_con_get() and ceph_con_put() did not get deleted in this commit: d59315ca libceph: drop ceph_con_get/put helpers and nref member Clean that up. Signed-off-by: Alex Elder --- include/linux/ceph/messenger.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index 002d504df3b7..dd4ef1f8ec93 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -244,8 +244,6 @@ extern void ceph_msg_revoke(struct ceph_msg *msg); extern void ceph_msg_revoke_incoming(struct ceph_msg *msg); extern void ceph_con_keepalive(struct ceph_connection *con); -extern struct ceph_connection *ceph_con_get(struct ceph_connection *con); -extern void ceph_con_put(struct ceph_connection *con); extern struct ceph_msg *ceph_msg_new(int type, int front_len, gfp_t flags, bool can_fail); -- cgit v1.2.3 From b7a9e5dd40f17a48a72f249b8bbc989b63bae5fd Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 27 Jun 2012 12:24:08 -0700 Subject: libceph: set peer name on con_open, not init The peer name may change on each open attempt, even when the connection is reused. Signed-off-by: Sage Weil --- fs/ceph/mds_client.c | 7 ++++--- include/linux/ceph/messenger.h | 4 ++-- net/ceph/messenger.c | 12 +++++++----- net/ceph/mon_client.c | 4 ++-- net/ceph/osd_client.c | 10 ++++++---- 5 files changed, 21 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index ecd7f15741c1..5ac6434185ae 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -394,8 +394,7 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc, s->s_seq = 0; mutex_init(&s->s_mutex); - ceph_con_init(&s->s_con, s, &mds_con_ops, &mdsc->fsc->client->msgr, - CEPH_ENTITY_TYPE_MDS, mds); + ceph_con_init(&s->s_con, s, &mds_con_ops, &mdsc->fsc->client->msgr); spin_lock_init(&s->s_gen_ttl_lock); s->s_cap_gen = 0; @@ -437,7 +436,8 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc, mdsc->sessions[mds] = s; atomic_inc(&s->s_ref); /* one ref to sessions[], one to caller */ - ceph_con_open(&s->s_con, ceph_mdsmap_get_addr(mdsc->mdsmap, mds)); + ceph_con_open(&s->s_con, CEPH_ENTITY_TYPE_MDS, mds, + ceph_mdsmap_get_addr(mdsc->mdsmap, mds)); return s; @@ -2529,6 +2529,7 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc, session->s_seq = 0; ceph_con_open(&session->s_con, + CEPH_ENTITY_TYPE_MDS, mds, ceph_mdsmap_get_addr(mdsc->mdsmap, mds)); /* replay unsafe requests */ diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index dd4ef1f8ec93..478f814f2100 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -232,9 +232,9 @@ extern void ceph_messenger_init(struct ceph_messenger *msgr, extern void ceph_con_init(struct ceph_connection *con, void *private, const struct ceph_connection_operations *ops, - struct ceph_messenger *msgr, __u8 entity_type, - __u64 entity_num); + struct ceph_messenger *msgr); extern void ceph_con_open(struct ceph_connection *con, + __u8 entity_type, __u64 entity_num, struct ceph_entity_addr *addr); extern bool ceph_con_opened(struct ceph_connection *con); extern void ceph_con_close(struct ceph_connection *con); diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index dcc50e4cd5cd..ae082d95fc72 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -523,12 +523,17 @@ EXPORT_SYMBOL(ceph_con_close); /* * Reopen a closed connection, with a new peer address. */ -void ceph_con_open(struct ceph_connection *con, struct ceph_entity_addr *addr) +void ceph_con_open(struct ceph_connection *con, + __u8 entity_type, __u64 entity_num, + struct ceph_entity_addr *addr) { dout("con_open %p %s\n", con, ceph_pr_addr(&addr->in_addr)); set_bit(OPENING, &con->state); WARN_ON(!test_and_clear_bit(CLOSED, &con->state)); + con->peer_name.type = (__u8) entity_type; + con->peer_name.num = cpu_to_le64(entity_num); + memcpy(&con->peer_addr, addr, sizeof(*addr)); con->delay = 0; /* reset backoff memory */ queue_con(con); @@ -548,7 +553,7 @@ bool ceph_con_opened(struct ceph_connection *con) */ void ceph_con_init(struct ceph_connection *con, void *private, const struct ceph_connection_operations *ops, - struct ceph_messenger *msgr, __u8 entity_type, __u64 entity_num) + struct ceph_messenger *msgr) { dout("con_init %p\n", con); memset(con, 0, sizeof(*con)); @@ -558,9 +563,6 @@ void ceph_con_init(struct ceph_connection *con, void *private, con_sock_state_init(con); - con->peer_name.type = (__u8) entity_type; - con->peer_name.num = cpu_to_le64(entity_num); - mutex_init(&con->mutex); INIT_LIST_HEAD(&con->out_queue); INIT_LIST_HEAD(&con->out_sent); diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c index e9db3de20b2e..bcc80a0e2a98 100644 --- a/net/ceph/mon_client.c +++ b/net/ceph/mon_client.c @@ -143,11 +143,11 @@ static int __open_session(struct ceph_mon_client *monc) monc->want_next_osdmap = !!monc->want_next_osdmap; ceph_con_init(&monc->con, monc, &mon_con_ops, - &monc->client->msgr, - CEPH_ENTITY_TYPE_MON, monc->cur_mon); + &monc->client->msgr); dout("open_session mon%d opening\n", monc->cur_mon); ceph_con_open(&monc->con, + CEPH_ENTITY_TYPE_MON, monc->cur_mon, &monc->monmap->mon_inst[monc->cur_mon].addr); /* initiatiate authentication handshake */ diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index db2da54f7336..c2527113d2ae 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -639,8 +639,7 @@ static struct ceph_osd *create_osd(struct ceph_osd_client *osdc, int onum) INIT_LIST_HEAD(&osd->o_osd_lru); osd->o_incarnation = 1; - ceph_con_init(&osd->o_con, osd, &osd_con_ops, &osdc->client->msgr, - CEPH_ENTITY_TYPE_OSD, onum); + ceph_con_init(&osd->o_con, osd, &osd_con_ops, &osdc->client->msgr); INIT_LIST_HEAD(&osd->o_keepalive_item); return osd; @@ -750,7 +749,8 @@ static int __reset_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd) ret = -EAGAIN; } else { ceph_con_close(&osd->o_con); - ceph_con_open(&osd->o_con, &osdc->osdmap->osd_addr[osd->o_osd]); + ceph_con_open(&osd->o_con, CEPH_ENTITY_TYPE_OSD, osd->o_osd, + &osdc->osdmap->osd_addr[osd->o_osd]); osd->o_incarnation++; } return ret; @@ -1005,7 +1005,9 @@ static int __map_request(struct ceph_osd_client *osdc, dout("map_request osd %p is osd%d\n", req->r_osd, o); __insert_osd(osdc, req->r_osd); - ceph_con_open(&req->r_osd->o_con, &osdc->osdmap->osd_addr[o]); + ceph_con_open(&req->r_osd->o_con, + CEPH_ENTITY_TYPE_OSD, o, + &osdc->osdmap->osd_addr[o]); } if (req->r_osd) { -- cgit v1.2.3 From 74a7f08448adea6cb47cd9b260c98ff168117e92 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 15 Jun 2012 11:50:25 -0600 Subject: devicetree: add helper inline for retrieving a node's full name The pattern (np ? np->full_name : "") is rather common in the kernel, but can also make for quite long lines. This patch adds a new inline function, of_node_full_name() so that the test for a valid node pointer doesn't need to be open coded at all call sites. Signed-off-by: Grant Likely Cc: Paul Mundt Cc: Benjamin Herrenschmidt Cc: Thomas Gleixner Signed-off-by: Rob Herring --- arch/microblaze/pci/pci-common.c | 6 ++---- arch/powerpc/kernel/pci-common.c | 6 ++---- arch/powerpc/kernel/vio.c | 5 ++--- arch/powerpc/platforms/cell/iommu.c | 3 +-- arch/powerpc/platforms/pseries/iommu.c | 2 +- arch/sparc/kernel/of_device_64.c | 2 +- drivers/of/base.c | 2 +- drivers/of/irq.c | 2 +- include/linux/of.h | 10 ++++++++++ kernel/irq/irqdomain.c | 8 ++++---- 10 files changed, 25 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/arch/microblaze/pci/pci-common.c b/arch/microblaze/pci/pci-common.c index ed22bfc5db14..ca8f6e769960 100644 --- a/arch/microblaze/pci/pci-common.c +++ b/arch/microblaze/pci/pci-common.c @@ -249,8 +249,7 @@ int pci_read_irq_line(struct pci_dev *pci_dev) } else { pr_debug(" Got one, spec %d cells (0x%08x 0x%08x...) on %s\n", oirq.size, oirq.specifier[0], oirq.specifier[1], - oirq.controller ? oirq.controller->full_name : - ""); + of_node_full_name(oirq.controller)); virq = irq_create_of_mapping(oirq.controller, oirq.specifier, oirq.size); @@ -1493,8 +1492,7 @@ static void __devinit pcibios_scan_phb(struct pci_controller *hose) struct pci_bus *bus; struct device_node *node = hose->dn; - pr_debug("PCI: Scanning PHB %s\n", - node ? node->full_name : ""); + pr_debug("PCI: Scanning PHB %s\n", of_node_full_name(node)); pcibios_setup_phb_resources(hose, &resources); diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 8e78e93c8185..886c254fd565 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -248,8 +248,7 @@ static int pci_read_irq_line(struct pci_dev *pci_dev) } else { pr_debug(" Got one, spec %d cells (0x%08x 0x%08x...) on %s\n", oirq.size, oirq.specifier[0], oirq.specifier[1], - oirq.controller ? oirq.controller->full_name : - ""); + of_node_full_name(oirq.controller)); virq = irq_create_of_mapping(oirq.controller, oirq.specifier, oirq.size); @@ -1628,8 +1627,7 @@ void __devinit pcibios_scan_phb(struct pci_controller *hose) struct device_node *node = hose->dn; int mode; - pr_debug("PCI: Scanning PHB %s\n", - node ? node->full_name : ""); + pr_debug("PCI: Scanning PHB %s\n", of_node_full_name(node)); /* Get some IO space for the new PHB */ pcibios_setup_phb_io_space(hose); diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c index cb87301ccd55..63f72ede4341 100644 --- a/arch/powerpc/kernel/vio.c +++ b/arch/powerpc/kernel/vio.c @@ -1296,8 +1296,7 @@ static void __devinit vio_dev_release(struct device *dev) struct iommu_table *tbl = get_iommu_table_base(dev); if (tbl) - iommu_free_table(tbl, dev->of_node ? - dev->of_node->full_name : dev_name(dev)); + iommu_free_table(tbl, of_node_full_name(dev->of_node)); of_node_put(dev->of_node); kfree(to_vio_dev(dev)); } @@ -1509,7 +1508,7 @@ static ssize_t devspec_show(struct device *dev, { struct device_node *of_node = dev->of_node; - return sprintf(buf, "%s\n", of_node ? of_node->full_name : "none"); + return sprintf(buf, "%s\n", of_node_full_name(of_node)); } static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c index b9f509a34c01..b6732004c882 100644 --- a/arch/powerpc/platforms/cell/iommu.c +++ b/arch/powerpc/platforms/cell/iommu.c @@ -552,8 +552,7 @@ static struct iommu_table *cell_get_iommu_table(struct device *dev) iommu = cell_iommu_for_node(dev_to_node(dev)); if (iommu == NULL || list_empty(&iommu->windows)) { printk(KERN_ERR "iommu: missing iommu for %s (node %d)\n", - dev->of_node ? dev->of_node->full_name : "?", - dev_to_node(dev)); + of_node_full_name(dev->of_node), dev_to_node(dev)); return NULL; } window = list_entry(iommu->windows.next, struct iommu_window, list); diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index 2d311c0caf8e..6b58a395dff6 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -1051,7 +1051,7 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev) if (!pdn || !PCI_DN(pdn)) { printk(KERN_WARNING "pci_dma_dev_setup_pSeriesLP: " "no DMA window found for pci dev=%s dn=%s\n", - pci_name(dev), dn? dn->full_name : ""); + pci_name(dev), of_node_full_name(dn)); return; } pr_debug(" parent is %s\n", pdn->full_name); diff --git a/arch/sparc/kernel/of_device_64.c b/arch/sparc/kernel/of_device_64.c index 7a3be6f6737a..7bbdc26d9512 100644 --- a/arch/sparc/kernel/of_device_64.c +++ b/arch/sparc/kernel/of_device_64.c @@ -580,7 +580,7 @@ static unsigned int __init build_one_device_irq(struct platform_device *op, printk("%s: Apply [%s:%x] imap --> [%s:%x]\n", op->dev.of_node->full_name, pp->full_name, this_orig_irq, - (iret ? iret->full_name : "NULL"), irq); + of_node_full_name(iret), irq); if (!iret) break; diff --git a/drivers/of/base.c b/drivers/of/base.c index 85757952f12d..9ec0a2f1b028 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1173,7 +1173,7 @@ static void of_alias_add(struct alias_prop *ap, struct device_node *np, ap->stem[stem_len] = 0; list_add_tail(&ap->link, &aliases_lookup); pr_debug("adding DT alias:%s: stem=%s id=%i node=%s\n", - ap->alias, ap->stem, ap->id, np ? np->full_name : NULL); + ap->alias, ap->stem, ap->id, of_node_full_name(np)); } /** diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 9cf00602f566..ff8ab7b27373 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -255,7 +255,7 @@ int of_irq_map_raw(struct device_node *parent, const __be32 *intspec, skiplevel: /* Iterate again with new parent */ - pr_debug(" -> new parent: %s\n", newpar ? newpar->full_name : "<>"); + pr_debug(" -> new parent: %s\n", of_node_full_name(newpar)); of_node_put(ipar); ipar = newpar; newpar = NULL; diff --git a/include/linux/of.h b/include/linux/of.h index 2ec1083af7ff..1012377cae92 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -163,6 +163,11 @@ static inline int of_node_to_nid(struct device_node *np) { return -1; } #define of_node_to_nid of_node_to_nid #endif +static inline const char* of_node_full_name(struct device_node *np) +{ + return np ? np->full_name : ""; +} + extern struct device_node *of_find_node_by_name(struct device_node *from, const char *name); #define for_each_node_by_name(dn, name) \ @@ -303,6 +308,11 @@ const char *of_prop_next_string(struct property *prop, const char *cur); #else /* CONFIG_OF */ +static inline const char* of_node_full_name(struct device_node *np) +{ + return ""; +} + static inline bool of_have_populated_dt(void) { return false; diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 41c1564103f1..38c5eb839c92 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -448,7 +448,7 @@ unsigned int irq_create_mapping(struct irq_domain *domain, } pr_debug("irq %lu on domain %s mapped to virtual irq %u\n", - hwirq, domain->of_node ? domain->of_node->full_name : "null", virq); + hwirq, of_node_full_name(domain->of_node), virq); return virq; } @@ -477,7 +477,7 @@ unsigned int irq_create_of_mapping(struct device_node *controller, return intspec[0]; #endif pr_warning("no irq domain found for %s !\n", - controller->full_name); + of_node_full_name(controller)); return 0; } @@ -725,8 +725,8 @@ static int virq_debug_show(struct seq_file *m, void *private) data = irq_desc_get_chip_data(desc); seq_printf(m, data ? "0x%p " : " %p ", data); - if (desc->irq_data.domain && desc->irq_data.domain->of_node) - p = desc->irq_data.domain->of_node->full_name; + if (desc->irq_data.domain) + p = of_node_full_name(desc->irq_data.domain->of_node); else p = none; seq_printf(m, "%s\n", p); -- cgit v1.2.3 From 19181bc50e1b8e92a7a3b3d78637c6dc5c0b5a1b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 4 Jul 2012 09:18:02 +0200 Subject: usbdevfs: Add a USBDEVFS_GET_CAPABILITIES ioctl There are a few (new) usbdevfs capabilities which an application cannot discover in any other way then checking the kernel version. There are 3 problems with this: 1) It is just not very pretty. 2) Given the tendency of enterprise distros to backport stuff it is not reliable. 3) As discussed in length on the mailinglist, USBDEVFS_URB_BULK_CONTINUATION does not work as it should when combined with USBDEVFS_URB_SHORT_NOT_OK (which is its intended use) on devices attached to an XHCI controller. So the availability of these features can be host controller dependent, making depending on them based on the kernel version not a good idea. This patch besides adding the new ioctl also adds flags for the following existing capabilities: USBDEVFS_CAP_ZERO_PACKET, available since 2.6.31 USBDEVFS_CAP_BULK_CONTINUATION, available since 2.6.32, except for XHCI USBDEVFS_CAP_NO_PACKET_SIZE_LIM, available since 3.3 Note that this patch only does not advertise the USBDEVFS_URB_BULK_CONTINUATION cap for XHCI controllers, bulk transfers with this flag set will still be accepted when submitted to XHCI controllers. Returning -EINVAL for them would break existing apps, and in most cases the troublesome scenario wrt USBDEVFS_URB_SHORT_NOT_OK urbs on XHCI controllers will never get hit, so this would break working use cases. The disadvantage of not returning -EINVAL is that cases were it is causing real trouble may go undetected / the cause of the trouble may be unclear, but this is the best we can do. Signed-off-by: Hans de Goede Acked-by: Alan Stern Acked-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 17 +++++++++++++++++ drivers/usb/host/xhci.c | 2 ++ include/linux/usb.h | 5 +++++ include/linux/usbdevice_fs.h | 7 +++++++ 4 files changed, 31 insertions(+) (limited to 'include') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 62679bc031fb..0b387c1a8b7e 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1824,6 +1824,20 @@ static int proc_release_port(struct dev_state *ps, void __user *arg) return usb_hub_release_port(ps->dev, portnum, ps); } +static int proc_get_capabilities(struct dev_state *ps, void __user *arg) +{ + __u32 caps; + + caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM; + if (!ps->dev->bus->no_stop_on_short) + caps |= USBDEVFS_CAP_BULK_CONTINUATION; + + if (put_user(caps, (__u32 __user *)arg)) + return -EFAULT; + + return 0; +} + /* * NOTE: All requests here that have interface numbers as parameters * are assuming that somehow the configuration has been prevented from @@ -1994,6 +2008,9 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, snoop(&dev->dev, "%s: RELEASE_PORT\n", __func__); ret = proc_release_port(ps, p); break; + case USBDEVFS_GET_CAPABILITIES: + ret = proc_get_capabilities(ps, p); + break; } usb_unlock_device(dev); if (ret >= 0) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index a979cd0dbe0f..7648b2d4b268 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -4450,6 +4450,8 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) /* Accept arbitrarily long scatter-gather lists */ hcd->self.sg_tablesize = ~0; + /* XHCI controllers don't stop the ep queue on short packets :| */ + hcd->self.no_stop_on_short = 1; if (usb_hcd_is_primary_hcd(hcd)) { xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL); diff --git a/include/linux/usb.h b/include/linux/usb.h index f717fbdaee8e..d4f9de1acd45 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -331,6 +331,11 @@ struct usb_bus { u8 otg_port; /* 0, or number of OTG/HNP port */ unsigned is_b_host:1; /* true during some HNP roleswitches */ unsigned b_hnp_enable:1; /* OTG: did A-Host enable HNP? */ + unsigned no_stop_on_short:1; /* + * Quirk: some controllers don't stop + * the ep queue on a short transfer + * with the URB_SHORT_NOT_OK flag set. + */ unsigned sg_tablesize; /* 0 or largest number of sg list entries */ int devnum_next; /* Next open device number in diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h index 15591d2ea400..07b2ceaaad70 100644 --- a/include/linux/usbdevice_fs.h +++ b/include/linux/usbdevice_fs.h @@ -125,6 +125,11 @@ struct usbdevfs_hub_portinfo { char port [127]; /* e.g. port 3 connects to device 27 */ }; +/* Device capability flags */ +#define USBDEVFS_CAP_ZERO_PACKET 0x01 +#define USBDEVFS_CAP_BULK_CONTINUATION 0x02 +#define USBDEVFS_CAP_NO_PACKET_SIZE_LIM 0x04 + #ifdef __KERNEL__ #ifdef CONFIG_COMPAT #include @@ -204,4 +209,6 @@ struct usbdevfs_ioctl32 { #define USBDEVFS_CONNECT _IO('U', 23) #define USBDEVFS_CLAIM_PORT _IOR('U', 24, unsigned int) #define USBDEVFS_RELEASE_PORT _IOR('U', 25, unsigned int) +#define USBDEVFS_GET_CAPABILITIES _IOR('U', 26, __u32) + #endif /* _LINUX_USBDEVICE_FS_H */ -- cgit v1.2.3 From 3d97ff63f8997761f12c8fbe8082996c6eeaba1a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 4 Jul 2012 09:18:03 +0200 Subject: usbdevfs: Use scatter-gather lists for large bulk transfers When using urb->transfer_buffer we need to allocate physical contiguous buffers for the entire transfer, which is pretty much guaranteed to fail with large transfers. Currently userspace works around this by breaking large transfers into multiple urbs. For large bulk transfers this leads to all kind of complications. This patch makes it possible for userspace to reliable submit large bulk transfers to scatter-gather capable host controllers in one go, by using a scatterlist to break the transfer up in managable chunks. Signed-off-by: Hans de Goede Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 152 ++++++++++++++++++++++++++++++++++--------- include/linux/usbdevice_fs.h | 1 + 2 files changed, 122 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 0b387c1a8b7e..ebb8a9de8b5f 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,7 @@ #define USB_MAXBUS 64 #define USB_DEVICE_MAX USB_MAXBUS * 128 +#define USB_SG_SIZE 16384 /* split-size for large txs */ /* Mutual exclusion for removal, open, and release */ DEFINE_MUTEX(usbfs_mutex); @@ -285,9 +287,16 @@ static struct async *alloc_async(unsigned int numisoframes) static void free_async(struct async *as) { + int i; + put_pid(as->pid); if (as->cred) put_cred(as->cred); + for (i = 0; i < as->urb->num_sgs; i++) { + if (sg_page(&as->urb->sg[i])) + kfree(sg_virt(&as->urb->sg[i])); + } + kfree(as->urb->sg); kfree(as->urb->transfer_buffer); kfree(as->urb->setup_packet); usb_free_urb(as->urb); @@ -388,6 +397,53 @@ static void snoop_urb(struct usb_device *udev, } } +static void snoop_urb_data(struct urb *urb, unsigned len) +{ + int i, size; + + if (!usbfs_snoop) + return; + + if (urb->num_sgs == 0) { + print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1, + urb->transfer_buffer, len, 1); + return; + } + + for (i = 0; i < urb->num_sgs && len; i++) { + size = (len > USB_SG_SIZE) ? USB_SG_SIZE : len; + print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1, + sg_virt(&urb->sg[i]), size, 1); + len -= size; + } +} + +static int copy_urb_data_to_user(u8 __user *userbuffer, struct urb *urb) +{ + unsigned i, len, size; + + if (urb->number_of_packets > 0) /* Isochronous */ + len = urb->transfer_buffer_length; + else /* Non-Isoc */ + len = urb->actual_length; + + if (urb->num_sgs == 0) { + if (copy_to_user(userbuffer, urb->transfer_buffer, len)) + return -EFAULT; + return 0; + } + + for (i = 0; i < urb->num_sgs && len; i++) { + size = (len > USB_SG_SIZE) ? USB_SG_SIZE : len; + if (copy_to_user(userbuffer, sg_virt(&urb->sg[i]), size)) + return -EFAULT; + userbuffer += size; + len -= size; + } + + return 0; +} + #define AS_CONTINUATION 1 #define AS_UNLINK 2 @@ -454,9 +510,10 @@ static void async_completed(struct urb *urb) } snoop(&urb->dev->dev, "urb complete\n"); snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length, - as->status, COMPLETE, - ((urb->transfer_flags & URB_DIR_MASK) == USB_DIR_OUT) ? - NULL : urb->transfer_buffer, urb->actual_length); + as->status, COMPLETE, NULL, 0); + if ((urb->transfer_flags & URB_DIR_MASK) == USB_DIR_IN) + snoop_urb_data(urb, urb->actual_length); + if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET && as->status != -ENOENT) cancel_bulk_urbs(ps, as->bulk_addr); @@ -1114,8 +1171,8 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, struct async *as = NULL; struct usb_ctrlrequest *dr = NULL; unsigned int u, totlen, isofrmlen; - int ret, ifnum = -1; - int is_in; + int i, ret, is_in, num_sgs = 0, ifnum = -1; + void *buf; if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP | USBDEVFS_URB_SHORT_NOT_OK | @@ -1199,6 +1256,9 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, goto interrupt_urb; } uurb->number_of_packets = 0; + num_sgs = DIV_ROUND_UP(uurb->buffer_length, USB_SG_SIZE); + if (num_sgs == 1 || num_sgs > ps->dev->bus->sg_tablesize) + num_sgs = 0; break; case USBDEVFS_URB_TYPE_INTERRUPT: @@ -1255,26 +1315,67 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, ret = -ENOMEM; goto error; } - u += sizeof(struct async) + sizeof(struct urb) + uurb->buffer_length; + + u += sizeof(struct async) + sizeof(struct urb) + uurb->buffer_length + + num_sgs * sizeof(struct scatterlist); ret = usbfs_increase_memory_usage(u); if (ret) goto error; as->mem_usage = u; - if (uurb->buffer_length > 0) { + if (num_sgs) { + as->urb->sg = kmalloc(num_sgs * sizeof(struct scatterlist), + GFP_KERNEL); + if (!as->urb->sg) { + ret = -ENOMEM; + goto error; + } + as->urb->num_sgs = num_sgs; + sg_init_table(as->urb->sg, as->urb->num_sgs); + + totlen = uurb->buffer_length; + for (i = 0; i < as->urb->num_sgs; i++) { + u = (totlen > USB_SG_SIZE) ? USB_SG_SIZE : totlen; + buf = kmalloc(u, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto error; + } + sg_set_buf(&as->urb->sg[i], buf, u); + + if (!is_in) { + if (copy_from_user(buf, uurb->buffer, u)) { + ret = -EFAULT; + goto error; + } + } + totlen -= u; + } + } else if (uurb->buffer_length > 0) { as->urb->transfer_buffer = kmalloc(uurb->buffer_length, GFP_KERNEL); if (!as->urb->transfer_buffer) { ret = -ENOMEM; goto error; } - /* Isochronous input data may end up being discontiguous - * if some of the packets are short. Clear the buffer so - * that the gaps don't leak kernel data to userspace. - */ - if (is_in && uurb->type == USBDEVFS_URB_TYPE_ISO) + + if (!is_in) { + if (copy_from_user(as->urb->transfer_buffer, + uurb->buffer, + uurb->buffer_length)) { + ret = -EFAULT; + goto error; + } + } else if (uurb->type == USBDEVFS_URB_TYPE_ISO) { + /* + * Isochronous input data may end up being + * discontiguous if some of the packets are short. + * Clear the buffer so that the gaps don't leak + * kernel data to userspace. + */ memset(as->urb->transfer_buffer, 0, uurb->buffer_length); + } } as->urb->dev = ps->dev; as->urb->pipe = (uurb->type << 30) | @@ -1328,17 +1429,12 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, as->pid = get_pid(task_pid(current)); as->cred = get_current_cred(); security_task_getsecid(current, &as->secid); - if (!is_in && uurb->buffer_length > 0) { - if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, - uurb->buffer_length)) { - ret = -EFAULT; - goto error; - } - } snoop_urb(ps->dev, as->userurb, as->urb->pipe, as->urb->transfer_buffer_length, 0, SUBMIT, - is_in ? NULL : as->urb->transfer_buffer, - uurb->buffer_length); + NULL, 0); + if (!is_in) + snoop_urb_data(as->urb, as->urb->transfer_buffer_length); + async_newpending(as); if (usb_endpoint_xfer_bulk(&ep->desc)) { @@ -1433,11 +1529,7 @@ static int processcompl(struct async *as, void __user * __user *arg) unsigned int i; if (as->userbuffer && urb->actual_length) { - if (urb->number_of_packets > 0) /* Isochronous */ - i = urb->transfer_buffer_length; - else /* Non-Isoc */ - i = urb->actual_length; - if (copy_to_user(as->userbuffer, urb->transfer_buffer, i)) + if (copy_urb_data_to_user(as->userbuffer, urb)) goto err_out; } if (put_user(as->status, &userurb->status)) @@ -1605,11 +1697,7 @@ static int processcompl_compat(struct async *as, void __user * __user *arg) unsigned int i; if (as->userbuffer && urb->actual_length) { - if (urb->number_of_packets > 0) /* Isochronous */ - i = urb->transfer_buffer_length; - else /* Non-Isoc */ - i = urb->actual_length; - if (copy_to_user(as->userbuffer, urb->transfer_buffer, i)) + if (copy_urb_data_to_user(as->userbuffer, urb)) return -EFAULT; } if (put_user(as->status, &userurb->status)) @@ -1831,6 +1919,8 @@ static int proc_get_capabilities(struct dev_state *ps, void __user *arg) caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM; if (!ps->dev->bus->no_stop_on_short) caps |= USBDEVFS_CAP_BULK_CONTINUATION; + if (ps->dev->bus->sg_tablesize) + caps |= USBDEVFS_CAP_BULK_SCATTER_GATHER; if (put_user(caps, (__u32 __user *)arg)) return -EFAULT; diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h index 07b2ceaaad70..3b74666be027 100644 --- a/include/linux/usbdevice_fs.h +++ b/include/linux/usbdevice_fs.h @@ -129,6 +129,7 @@ struct usbdevfs_hub_portinfo { #define USBDEVFS_CAP_ZERO_PACKET 0x01 #define USBDEVFS_CAP_BULK_CONTINUATION 0x02 #define USBDEVFS_CAP_NO_PACKET_SIZE_LIM 0x04 +#define USBDEVFS_CAP_BULK_SCATTER_GATHER 0x08 #ifdef __KERNEL__ #ifdef CONFIG_COMPAT -- cgit v1.2.3 From 77c4400f2f0fd8384ab5cbe41d81ccc664896b2d Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Fri, 29 Jun 2012 17:48:53 +0800 Subject: USB: Chipidea: rename struct ci13xxx_udc_driver to struct ci13xxx_platform_data This patch rename struct ci13xxx_udc_driver and var with the type. ci13xxx_platform_data reflect it's passed from platfrom driver. Signed-off-by: Richard Zhao Reviewed-by: Felipe Balbi Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci.h | 4 ++-- drivers/usb/chipidea/ci13xxx_msm.c | 6 +++--- drivers/usb/chipidea/ci13xxx_pci.c | 20 ++++++++++---------- drivers/usb/chipidea/core.c | 12 ++++++------ drivers/usb/chipidea/host.c | 2 +- drivers/usb/chipidea/udc.c | 24 ++++++++++++------------ include/linux/usb/chipidea.h | 2 +- 7 files changed, 35 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 50911f8490d4..0b093308548d 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -125,7 +125,7 @@ struct hw_bank { * @remote_wakeup: host-enabled remote wakeup * @suspended: suspended by host * @test_mode: the selected test mode - * @udc_driver: platform specific information supplied by parent device + * @platdata: platform specific information supplied by parent device * @vbus_active: is VBUS active * @transceiver: pointer to USB PHY, if any * @hcd: pointer to usb_hcd for ehci host driver @@ -158,7 +158,7 @@ struct ci13xxx { u8 suspended; u8 test_mode; - struct ci13xxx_udc_driver *udc_driver; + struct ci13xxx_platform_data *platdata; int vbus_active; struct usb_phy *transceiver; struct usb_hcd *hcd; diff --git a/drivers/usb/chipidea/ci13xxx_msm.c b/drivers/usb/chipidea/ci13xxx_msm.c index 11a7befdc4ef..12c0dd6a300c 100644 --- a/drivers/usb/chipidea/ci13xxx_msm.c +++ b/drivers/usb/chipidea/ci13xxx_msm.c @@ -45,7 +45,7 @@ static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event) } } -static struct ci13xxx_udc_driver ci13xxx_msm_udc_driver = { +static struct ci13xxx_platform_data ci13xxx_msm_platdata = { .name = "ci13xxx_msm", .flags = CI13XXX_REGS_SHARED | CI13XXX_REQUIRE_TRANSCEIVER | @@ -75,8 +75,8 @@ static int __devinit ci13xxx_msm_probe(struct platform_device *pdev) goto put_platform; } - ret = platform_device_add_data(plat_ci, &ci13xxx_msm_udc_driver, - sizeof(ci13xxx_msm_udc_driver)); + ret = platform_device_add_data(plat_ci, &ci13xxx_msm_platdata, + sizeof(ci13xxx_msm_platdata)); if (ret) goto put_platform; diff --git a/drivers/usb/chipidea/ci13xxx_pci.c b/drivers/usb/chipidea/ci13xxx_pci.c index e3dab27f5c75..cdcac3a0e94d 100644 --- a/drivers/usb/chipidea/ci13xxx_pci.c +++ b/drivers/usb/chipidea/ci13xxx_pci.c @@ -23,17 +23,17 @@ /****************************************************************************** * PCI block *****************************************************************************/ -struct ci13xxx_udc_driver pci_driver = { +struct ci13xxx_platform_data pci_platdata = { .name = UDC_DRIVER_NAME, .capoffset = DEF_CAPOFFSET, }; -struct ci13xxx_udc_driver langwell_pci_driver = { +struct ci13xxx_platform_data langwell_pci_platdata = { .name = UDC_DRIVER_NAME, .capoffset = 0, }; -struct ci13xxx_udc_driver penwell_pci_driver = { +struct ci13xxx_platform_data penwell_pci_platdata = { .name = UDC_DRIVER_NAME, .capoffset = 0, .power_budget = 200, @@ -51,12 +51,12 @@ struct ci13xxx_udc_driver penwell_pci_driver = { static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - struct ci13xxx_udc_driver *driver = (void *)id->driver_data; + struct ci13xxx_platform_data *platdata = (void *)id->driver_data; struct platform_device *plat_ci; struct resource res[3]; int retval = 0, nres = 2; - if (!driver) { + if (!platdata) { dev_err(&pdev->dev, "device doesn't provide driver data\n"); return -ENODEV; } @@ -95,7 +95,7 @@ static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev, goto put_platform; } - retval = platform_device_add_data(plat_ci, driver, sizeof(*driver)); + retval = platform_device_add_data(plat_ci, platdata, sizeof(*platdata)); if (retval) goto put_platform; @@ -147,19 +147,19 @@ static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev) static DEFINE_PCI_DEVICE_TABLE(ci13xxx_pci_id_table) = { { PCI_DEVICE(0x153F, 0x1004), - .driver_data = (kernel_ulong_t)&pci_driver, + .driver_data = (kernel_ulong_t)&pci_platdata, }, { PCI_DEVICE(0x153F, 0x1006), - .driver_data = (kernel_ulong_t)&pci_driver, + .driver_data = (kernel_ulong_t)&pci_platdata, }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0811), - .driver_data = (kernel_ulong_t)&langwell_pci_driver, + .driver_data = (kernel_ulong_t)&langwell_pci_platdata, }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0829), - .driver_data = (kernel_ulong_t)&penwell_pci_driver, + .driver_data = (kernel_ulong_t)&penwell_pci_platdata, }, { 0, 0, 0, 0, 0, 0, 0 /* end: all zeroes */ } }; diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 15e03b308f8a..9a883bd5e113 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -179,7 +179,7 @@ static int hw_device_init(struct ci13xxx *ci, void __iomem *base) ci->hw_bank.abs = base; ci->hw_bank.cap = ci->hw_bank.abs; - ci->hw_bank.cap += ci->udc_driver->capoffset; + ci->hw_bank.cap += ci->platdata->capoffset; ci->hw_bank.op = ci->hw_bank.cap + ioread8(ci->hw_bank.cap); hw_alloc_regmap(ci, false); @@ -227,11 +227,11 @@ int hw_device_reset(struct ci13xxx *ci, u32 mode) udelay(10); /* not RTOS friendly */ - if (ci->udc_driver->notify_event) - ci->udc_driver->notify_event(ci, + if (ci->platdata->notify_event) + ci->platdata->notify_event(ci, CI13XXX_CONTROLLER_RESET_EVENT); - if (ci->udc_driver->flags & CI13XXX_DISABLE_STREAMING) + if (ci->platdata->flags & CI13XXX_DISABLE_STREAMING) hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); /* USBMODE should be configured step by step */ @@ -364,7 +364,7 @@ static int __devinit ci_hdrc_probe(struct platform_device *pdev) } ci->dev = dev; - ci->udc_driver = dev->platform_data; + ci->platdata = dev->platform_data; ret = hw_device_init(ci, base); if (ret < 0) { @@ -419,7 +419,7 @@ static int __devinit ci_hdrc_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, ci); - ret = request_irq(ci->irq, ci_irq, IRQF_SHARED, ci->udc_driver->name, + ret = request_irq(ci->irq, ci_irq, IRQF_SHARED, ci->platdata->name, ci); if (ret) goto stop; diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index 9eacd21c0cd9..4a4fdb8c65f8 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -116,7 +116,7 @@ static int host_start(struct ci13xxx *ci) hcd->regs = ci->hw_bank.abs; hcd->has_tt = 1; - hcd->power_budget = ci->udc_driver->power_budget; + hcd->power_budget = ci->platdata->power_budget; ehci = hcd_to_ehci(hcd); ehci->caps = ci->hw_bank.cap; diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 80e71021f186..3094c85dc0b5 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1363,7 +1363,7 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active) unsigned long flags; int gadget_ready = 0; - if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS)) + if (!(udc->platdata->flags & CI13XXX_PULLUP_ON_VBUS)) return -EOPNOTSUPP; spin_lock_irqsave(&udc->lock, flags); @@ -1379,8 +1379,8 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active) hw_device_state(udc, udc->ep0out->qh.dma); } else { hw_device_state(udc, 0); - if (udc->udc_driver->notify_event) - udc->udc_driver->notify_event(udc, + if (udc->platdata->notify_event) + udc->platdata->notify_event(udc, CI13XXX_CONTROLLER_STOPPED_EVENT); _gadget_stop_activity(&udc->gadget); pm_runtime_put_sync(&_gadget->dev); @@ -1515,9 +1515,9 @@ static int ci13xxx_start(struct usb_gadget *gadget, udc->driver = driver; pm_runtime_get_sync(&udc->gadget.dev); - if (udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) { + if (udc->platdata->flags & CI13XXX_PULLUP_ON_VBUS) { if (udc->vbus_active) { - if (udc->udc_driver->flags & CI13XXX_REGS_SHARED) + if (udc->platdata->flags & CI13XXX_REGS_SHARED) hw_device_reset(udc, USBMODE_CM_DC); } else { pm_runtime_put_sync(&udc->gadget.dev); @@ -1545,11 +1545,11 @@ static int ci13xxx_stop(struct usb_gadget *gadget, spin_lock_irqsave(&udc->lock, flags); - if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) || + if (!(udc->platdata->flags & CI13XXX_PULLUP_ON_VBUS) || udc->vbus_active) { hw_device_state(udc, 0); - if (udc->udc_driver->notify_event) - udc->udc_driver->notify_event(udc, + if (udc->platdata->notify_event) + udc->platdata->notify_event(udc, CI13XXX_CONTROLLER_STOPPED_EVENT); udc->driver = NULL; spin_unlock_irqrestore(&udc->lock, flags); @@ -1582,7 +1582,7 @@ static irqreturn_t udc_irq(struct ci13xxx *udc) spin_lock(&udc->lock); - if (udc->udc_driver->flags & CI13XXX_REGS_SHARED) { + if (udc->platdata->flags & CI13XXX_REGS_SHARED) { if (hw_read(udc, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DC) { spin_unlock(&udc->lock); @@ -1654,7 +1654,7 @@ static int udc_start(struct ci13xxx *udc) udc->gadget.speed = USB_SPEED_UNKNOWN; udc->gadget.max_speed = USB_SPEED_HIGH; udc->gadget.is_otg = 0; - udc->gadget.name = udc->udc_driver->name; + udc->gadget.name = udc->platdata->name; INIT_LIST_HEAD(&udc->gadget.ep_list); @@ -1687,14 +1687,14 @@ static int udc_start(struct ci13xxx *udc) udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) { + if (udc->platdata->flags & CI13XXX_REQUIRE_TRANSCEIVER) { if (udc->transceiver == NULL) { retval = -ENODEV; goto free_pools; } } - if (!(udc->udc_driver->flags & CI13XXX_REGS_SHARED)) { + if (!(udc->platdata->flags & CI13XXX_REGS_SHARED)) { retval = hw_device_reset(udc, USBMODE_CM_DC); if (retval) goto put_transceiver; diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index edb90d6cfd12..d4cf970656fb 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -6,7 +6,7 @@ #define __LINUX_USB_CHIPIDEA_H struct ci13xxx; -struct ci13xxx_udc_driver { +struct ci13xxx_platform_data { const char *name; /* offset of the capability registers */ uintptr_t capoffset; -- cgit v1.2.3 From efcc3c61b9b1e4f764e14c48c553e6d477f40512 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 5 Jul 2012 17:24:19 +0100 Subject: ASoC: dapm: Allow routes to be deleted at runtime Since we're now relying on DAPM for things like enabling clocks when we reparent the clocks for widgets we need to either use conditional routes (which are expensive) or remove routes at runtime. Add a route removal API to support this use case. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc-dapm.h | 2 ++ sound/soc/soc-dapm.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 05559e571d44..abe373d57adc 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -374,6 +374,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm); 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); +int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_route *route, int num); int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 19fda1339510..4ba47aab9801 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2264,6 +2264,59 @@ err: return ret; } +static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_route *route) +{ + struct snd_soc_dapm_path *path, *p; + const char *sink; + const char *source; + char prefixed_sink[80]; + char prefixed_source[80]; + + if (route->control) { + dev_err(dapm->dev, + "Removal of routes with controls not supported\n"); + return -EINVAL; + } + + if (dapm->codec && dapm->codec->name_prefix) { + snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s", + dapm->codec->name_prefix, route->sink); + sink = prefixed_sink; + snprintf(prefixed_source, sizeof(prefixed_source), "%s %s", + dapm->codec->name_prefix, route->source); + source = prefixed_source; + } else { + sink = route->sink; + source = route->source; + } + + path = NULL; + list_for_each_entry(p, &dapm->card->paths, list) { + if (strcmp(p->source->name, source) != 0) + continue; + if (strcmp(p->sink->name, sink) != 0) + continue; + path = p; + break; + } + + if (path) { + dapm_mark_dirty(path->source, "Route removed"); + dapm_mark_dirty(path->sink, "Route removed"); + + list_del(&path->list); + list_del(&path->list_sink); + list_del(&path->list_source); + kfree(path); + } else { + dev_warn(dapm->dev, "Route %s->%s does not exist\n", + source, sink); + } + + return 0; +} + /** * snd_soc_dapm_add_routes - Add routes between DAPM widgets * @dapm: DAPM context @@ -2298,6 +2351,30 @@ int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, } EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes); +/** + * snd_soc_dapm_del_routes - Remove routes between DAPM widgets + * @dapm: DAPM context + * @route: audio routes + * @num: number of routes + * + * Removes routes from the DAPM context. + */ +int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_route *route, int num) +{ + int i, ret = 0; + + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); + for (i = 0; i < num; i++) { + snd_soc_dapm_del_route(dapm, route); + route++; + } + mutex_unlock(&dapm->card->dapm_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_del_routes); + static int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route) { -- cgit v1.2.3 From 7078daa020db5ba501fa3de64b8653c221a640b4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 14 May 2012 04:34:34 -0300 Subject: [media] uvcvideo: Document the struct uvc_xu_control_query query field Several developers have reported that the lack of macros for the struct uvc_xu_control_query query field in uvcvideo.h was confusing and forced them to look at the driver source code to find out what applications were supposed to pass in that field. Add a comment to the header to clarify the expected usage of the query field. Reported-by: Paulo Assis Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- include/linux/uvcvideo.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/uvcvideo.h b/include/linux/uvcvideo.h index f46a53f060d7..3b081862b9e8 100644 --- a/include/linux/uvcvideo.h +++ b/include/linux/uvcvideo.h @@ -58,7 +58,8 @@ struct uvc_xu_control_mapping { struct uvc_xu_control_query { __u8 unit; __u8 selector; - __u8 query; + __u8 query; /* Video Class-Specific Request Code, */ + /* defined in linux/usb/video.h A.8. */ __u16 size; __u8 __user *data; }; -- cgit v1.2.3 From ebc04047b398d415627f82653c4e722e8fc2c083 Mon Sep 17 00:00:00 2001 From: Benoît Thébaudeau Date: Thu, 28 Jun 2012 12:12:13 -0300 Subject: [PATCH] media: add Analog Devices ADV7393 video encoder driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add ADV7393 I²C-based video encoder driver. This driver has been tested on custom hardware. It has been tested for composite output. It is derived from the ADV7343 driver. Signed-off-by: Benoît Thébaudeau Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 9 + drivers/media/video/Makefile | 1 + drivers/media/video/adv7393.c | 487 +++++++++++++++++++++++++++++++++++++ drivers/media/video/adv7393_regs.h | 188 ++++++++++++++ include/media/adv7393.h | 28 +++ include/media/v4l2-chip-ident.h | 3 + 6 files changed, 716 insertions(+) create mode 100644 drivers/media/video/adv7393.c create mode 100644 drivers/media/video/adv7393_regs.h create mode 100644 include/media/adv7393.h (limited to 'include') diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index da363c44fab2..c128fac0ce2c 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -462,6 +462,15 @@ config VIDEO_ADV7343 To compile this driver as a module, choose M here: the module will be called adv7343. +config VIDEO_ADV7393 + tristate "ADV7393 video encoder" + depends on I2C + help + Support for Analog Devices I2C bus based ADV7393 encoder. + + To compile this driver as a module, choose M here: the + module will be called adv7393. + config VIDEO_AK881X tristate "AK8813/AK8814 video encoders" depends on I2C diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index d209de0e0ca8..b7da9faa3b0a 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o +obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o obj-$(CONFIG_VIDEO_VS6624) += vs6624.o obj-$(CONFIG_VIDEO_BT819) += bt819.o diff --git a/drivers/media/video/adv7393.c b/drivers/media/video/adv7393.c new file mode 100644 index 000000000000..3dc6098c7267 --- /dev/null +++ b/drivers/media/video/adv7393.c @@ -0,0 +1,487 @@ +/* + * adv7393 - ADV7393 Video Encoder Driver + * + * The encoder hardware does not support SECAM. + * + * Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/ + * Benoît Thébaudeau + * + * Based on ADV7343 driver, + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "adv7393_regs.h" + +MODULE_DESCRIPTION("ADV7393 video encoder driver"); +MODULE_LICENSE("GPL"); + +static bool debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Debug level 0-1"); + +struct adv7393_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + u8 reg00; + u8 reg01; + u8 reg02; + u8 reg35; + u8 reg80; + u8 reg82; + u32 output; + v4l2_std_id std; +}; + +static inline struct adv7393_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7393_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct adv7393_state, hdl)->sd; +} + +static inline int adv7393_write(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +static const u8 adv7393_init_reg_val[] = { + ADV7393_SOFT_RESET, ADV7393_SOFT_RESET_DEFAULT, + ADV7393_POWER_MODE_REG, ADV7393_POWER_MODE_REG_DEFAULT, + + ADV7393_HD_MODE_REG1, ADV7393_HD_MODE_REG1_DEFAULT, + ADV7393_HD_MODE_REG2, ADV7393_HD_MODE_REG2_DEFAULT, + ADV7393_HD_MODE_REG3, ADV7393_HD_MODE_REG3_DEFAULT, + ADV7393_HD_MODE_REG4, ADV7393_HD_MODE_REG4_DEFAULT, + ADV7393_HD_MODE_REG5, ADV7393_HD_MODE_REG5_DEFAULT, + ADV7393_HD_MODE_REG6, ADV7393_HD_MODE_REG6_DEFAULT, + ADV7393_HD_MODE_REG7, ADV7393_HD_MODE_REG7_DEFAULT, + + ADV7393_SD_MODE_REG1, ADV7393_SD_MODE_REG1_DEFAULT, + ADV7393_SD_MODE_REG2, ADV7393_SD_MODE_REG2_DEFAULT, + ADV7393_SD_MODE_REG3, ADV7393_SD_MODE_REG3_DEFAULT, + ADV7393_SD_MODE_REG4, ADV7393_SD_MODE_REG4_DEFAULT, + ADV7393_SD_MODE_REG5, ADV7393_SD_MODE_REG5_DEFAULT, + ADV7393_SD_MODE_REG6, ADV7393_SD_MODE_REG6_DEFAULT, + ADV7393_SD_MODE_REG7, ADV7393_SD_MODE_REG7_DEFAULT, + ADV7393_SD_MODE_REG8, ADV7393_SD_MODE_REG8_DEFAULT, + + ADV7393_SD_TIMING_REG0, ADV7393_SD_TIMING_REG0_DEFAULT, + + ADV7393_SD_HUE_ADJUST, ADV7393_SD_HUE_ADJUST_DEFAULT, + ADV7393_SD_CGMS_WSS0, ADV7393_SD_CGMS_WSS0_DEFAULT, + ADV7393_SD_BRIGHTNESS_WSS, ADV7393_SD_BRIGHTNESS_WSS_DEFAULT, +}; + +/* + * 2^32 + * FSC(reg) = FSC (HZ) * -------- + * 27000000 + */ +static const struct adv7393_std_info stdinfo[] = { + { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_NTSC, 705268427, V4L2_STD_NTSC_443, + }, { + /* FSC(Hz) = 3,579,545.45 Hz */ + SD_STD_NTSC, 569408542, V4L2_STD_NTSC, + }, { + /* FSC(Hz) = 3,575,611.00 Hz */ + SD_STD_PAL_M, 568782678, V4L2_STD_PAL_M, + }, { + /* FSC(Hz) = 3,582,056.00 Hz */ + SD_STD_PAL_N, 569807903, V4L2_STD_PAL_Nc, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_PAL_N, 705268427, V4L2_STD_PAL_N, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_PAL_M, 705268427, V4L2_STD_PAL_60, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_PAL_BDGHI, 705268427, V4L2_STD_PAL, + }, +}; + +static int adv7393_setstd(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7393_state *state = to_state(sd); + const struct adv7393_std_info *std_info; + int num_std; + u8 reg; + u32 val; + int err = 0; + int i; + + num_std = ARRAY_SIZE(stdinfo); + + for (i = 0; i < num_std; i++) { + if (stdinfo[i].stdid & std) + break; + } + + if (i == num_std) { + v4l2_dbg(1, debug, sd, + "Invalid std or std is not supported: %llx\n", + (unsigned long long)std); + return -EINVAL; + } + + std_info = &stdinfo[i]; + + /* Set the standard */ + val = state->reg80 & ~SD_STD_MASK; + val |= std_info->standard_val3; + err = adv7393_write(sd, ADV7393_SD_MODE_REG1, val); + if (err < 0) + goto setstd_exit; + + state->reg80 = val; + + /* Configure the input mode register */ + val = state->reg01 & ~INPUT_MODE_MASK; + val |= SD_INPUT_MODE; + err = adv7393_write(sd, ADV7393_MODE_SELECT_REG, val); + if (err < 0) + goto setstd_exit; + + state->reg01 = val; + + /* Program the sub carrier frequency registers */ + val = std_info->fsc_val; + for (reg = ADV7393_FSC_REG0; reg <= ADV7393_FSC_REG3; reg++) { + err = adv7393_write(sd, reg, val); + if (err < 0) + goto setstd_exit; + val >>= 8; + } + + val = state->reg82; + + /* Pedestal settings */ + if (std & (V4L2_STD_NTSC | V4L2_STD_NTSC_443)) + val |= SD_PEDESTAL_EN; + else + val &= SD_PEDESTAL_DI; + + err = adv7393_write(sd, ADV7393_SD_MODE_REG2, val); + if (err < 0) + goto setstd_exit; + + state->reg82 = val; + +setstd_exit: + if (err != 0) + v4l2_err(sd, "Error setting std, write failed\n"); + + return err; +} + +static int adv7393_setoutput(struct v4l2_subdev *sd, u32 output_type) +{ + struct adv7393_state *state = to_state(sd); + u8 val; + int err = 0; + + if (output_type > ADV7393_SVIDEO_ID) { + v4l2_dbg(1, debug, sd, + "Invalid output type or output type not supported:%d\n", + output_type); + return -EINVAL; + } + + /* Enable Appropriate DAC */ + val = state->reg00 & 0x03; + + if (output_type == ADV7393_COMPOSITE_ID) + val |= ADV7393_COMPOSITE_POWER_VALUE; + else if (output_type == ADV7393_COMPONENT_ID) + val |= ADV7393_COMPONENT_POWER_VALUE; + else + val |= ADV7393_SVIDEO_POWER_VALUE; + + err = adv7393_write(sd, ADV7393_POWER_MODE_REG, val); + if (err < 0) + goto setoutput_exit; + + state->reg00 = val; + + /* Enable YUV output */ + val = state->reg02 | YUV_OUTPUT_SELECT; + err = adv7393_write(sd, ADV7393_MODE_REG0, val); + if (err < 0) + goto setoutput_exit; + + state->reg02 = val; + + /* configure SD DAC Output 1 bit */ + val = state->reg82; + if (output_type == ADV7393_COMPONENT_ID) + val &= SD_DAC_OUT1_DI; + else + val |= SD_DAC_OUT1_EN; + err = adv7393_write(sd, ADV7393_SD_MODE_REG2, val); + if (err < 0) + goto setoutput_exit; + + state->reg82 = val; + + /* configure ED/HD Color DAC Swap bit to zero */ + val = state->reg35 & HD_DAC_SWAP_DI; + err = adv7393_write(sd, ADV7393_HD_MODE_REG6, val); + if (err < 0) + goto setoutput_exit; + + state->reg35 = val; + +setoutput_exit: + if (err != 0) + v4l2_err(sd, "Error setting output, write failed\n"); + + return err; +} + +static int adv7393_log_status(struct v4l2_subdev *sd) +{ + struct adv7393_state *state = to_state(sd); + + v4l2_info(sd, "Standard: %llx\n", (unsigned long long)state->std); + v4l2_info(sd, "Output: %s\n", (state->output == 0) ? "Composite" : + ((state->output == 1) ? "Component" : "S-Video")); + return 0; +} + +static int adv7393_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + return adv7393_write(sd, ADV7393_SD_BRIGHTNESS_WSS, + ctrl->val & SD_BRIGHTNESS_VALUE_MASK); + + case V4L2_CID_HUE: + return adv7393_write(sd, ADV7393_SD_HUE_ADJUST, + ctrl->val - ADV7393_HUE_MIN); + + case V4L2_CID_GAIN: + return adv7393_write(sd, ADV7393_DAC123_OUTPUT_LEVEL, + ctrl->val); + } + return -EINVAL; +} + +static int adv7393_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7393, 0); +} + +static const struct v4l2_ctrl_ops adv7393_ctrl_ops = { + .s_ctrl = adv7393_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops adv7393_core_ops = { + .log_status = adv7393_log_status, + .g_chip_ident = adv7393_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, +}; + +static int adv7393_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7393_state *state = to_state(sd); + int err = 0; + + if (state->std == std) + return 0; + + err = adv7393_setstd(sd, std); + if (!err) + state->std = std; + + return err; +} + +static int adv7393_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct adv7393_state *state = to_state(sd); + int err = 0; + + if (state->output == output) + return 0; + + err = adv7393_setoutput(sd, output); + if (!err) + state->output = output; + + return err; +} + +static const struct v4l2_subdev_video_ops adv7393_video_ops = { + .s_std_output = adv7393_s_std_output, + .s_routing = adv7393_s_routing, +}; + +static const struct v4l2_subdev_ops adv7393_ops = { + .core = &adv7393_core_ops, + .video = &adv7393_video_ops, +}; + +static int adv7393_initialize(struct v4l2_subdev *sd) +{ + struct adv7393_state *state = to_state(sd); + int err = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(adv7393_init_reg_val); i += 2) { + + err = adv7393_write(sd, adv7393_init_reg_val[i], + adv7393_init_reg_val[i+1]); + if (err) { + v4l2_err(sd, "Error initializing\n"); + return err; + } + } + + /* Configure for default video standard */ + err = adv7393_setoutput(sd, state->output); + if (err < 0) { + v4l2_err(sd, "Error setting output during init\n"); + return -EINVAL; + } + + err = adv7393_setstd(sd, state->std); + if (err < 0) { + v4l2_err(sd, "Error setting std during init\n"); + return -EINVAL; + } + + return err; +} + +static int adv7393_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv7393_state *state; + int err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct adv7393_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + + state->reg00 = ADV7393_POWER_MODE_REG_DEFAULT; + state->reg01 = 0x00; + state->reg02 = 0x20; + state->reg35 = ADV7393_HD_MODE_REG6_DEFAULT; + state->reg80 = ADV7393_SD_MODE_REG1_DEFAULT; + state->reg82 = ADV7393_SD_MODE_REG2_DEFAULT; + + state->output = ADV7393_COMPOSITE_ID; + state->std = V4L2_STD_NTSC; + + v4l2_i2c_subdev_init(&state->sd, client, &adv7393_ops); + + v4l2_ctrl_handler_init(&state->hdl, 3); + v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops, + V4L2_CID_BRIGHTNESS, ADV7393_BRIGHTNESS_MIN, + ADV7393_BRIGHTNESS_MAX, 1, + ADV7393_BRIGHTNESS_DEF); + v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops, + V4L2_CID_HUE, ADV7393_HUE_MIN, + ADV7393_HUE_MAX, 1, + ADV7393_HUE_DEF); + v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops, + V4L2_CID_GAIN, ADV7393_GAIN_MIN, + ADV7393_GAIN_MAX, 1, + ADV7393_GAIN_DEF); + state->sd.ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + v4l2_ctrl_handler_setup(&state->hdl); + + err = adv7393_initialize(&state->sd); + if (err) { + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + } + return err; +} + +static int adv7393_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7393_state *state = to_state(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + + return 0; +} + +static const struct i2c_device_id adv7393_id[] = { + {"adv7393", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, adv7393_id); + +static struct i2c_driver adv7393_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "adv7393", + }, + .probe = adv7393_probe, + .remove = adv7393_remove, + .id_table = adv7393_id, +}; +module_i2c_driver(adv7393_driver); diff --git a/drivers/media/video/adv7393_regs.h b/drivers/media/video/adv7393_regs.h new file mode 100644 index 000000000000..78968330f0be --- /dev/null +++ b/drivers/media/video/adv7393_regs.h @@ -0,0 +1,188 @@ +/* + * ADV7393 encoder related structure and register definitions + * + * Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/ + * Benoît Thébaudeau + * + * Based on ADV7343 driver, + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 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 ADV7393_REGS_H +#define ADV7393_REGS_H + +struct adv7393_std_info { + u32 standard_val3; + u32 fsc_val; + v4l2_std_id stdid; +}; + +/* Register offset macros */ +#define ADV7393_POWER_MODE_REG (0x00) +#define ADV7393_MODE_SELECT_REG (0x01) +#define ADV7393_MODE_REG0 (0x02) + +#define ADV7393_DAC123_OUTPUT_LEVEL (0x0B) + +#define ADV7393_SOFT_RESET (0x17) + +#define ADV7393_HD_MODE_REG1 (0x30) +#define ADV7393_HD_MODE_REG2 (0x31) +#define ADV7393_HD_MODE_REG3 (0x32) +#define ADV7393_HD_MODE_REG4 (0x33) +#define ADV7393_HD_MODE_REG5 (0x34) +#define ADV7393_HD_MODE_REG6 (0x35) + +#define ADV7393_HD_MODE_REG7 (0x39) + +#define ADV7393_SD_MODE_REG1 (0x80) +#define ADV7393_SD_MODE_REG2 (0x82) +#define ADV7393_SD_MODE_REG3 (0x83) +#define ADV7393_SD_MODE_REG4 (0x84) +#define ADV7393_SD_MODE_REG5 (0x86) +#define ADV7393_SD_MODE_REG6 (0x87) +#define ADV7393_SD_MODE_REG7 (0x88) +#define ADV7393_SD_MODE_REG8 (0x89) + +#define ADV7393_SD_TIMING_REG0 (0x8A) + +#define ADV7393_FSC_REG0 (0x8C) +#define ADV7393_FSC_REG1 (0x8D) +#define ADV7393_FSC_REG2 (0x8E) +#define ADV7393_FSC_REG3 (0x8F) + +#define ADV7393_SD_CGMS_WSS0 (0x99) + +#define ADV7393_SD_HUE_ADJUST (0xA0) +#define ADV7393_SD_BRIGHTNESS_WSS (0xA1) + +/* Default values for the registers */ +#define ADV7393_POWER_MODE_REG_DEFAULT (0x10) +#define ADV7393_HD_MODE_REG1_DEFAULT (0x3C) /* Changed Default + 720p EAV/SAV code*/ +#define ADV7393_HD_MODE_REG2_DEFAULT (0x01) /* Changed Pixel data + valid */ +#define ADV7393_HD_MODE_REG3_DEFAULT (0x00) /* Color delay 0 clks */ +#define ADV7393_HD_MODE_REG4_DEFAULT (0xEC) /* Changed */ +#define ADV7393_HD_MODE_REG5_DEFAULT (0x08) +#define ADV7393_HD_MODE_REG6_DEFAULT (0x00) +#define ADV7393_HD_MODE_REG7_DEFAULT (0x00) +#define ADV7393_SOFT_RESET_DEFAULT (0x02) +#define ADV7393_COMPOSITE_POWER_VALUE (0x10) +#define ADV7393_COMPONENT_POWER_VALUE (0x1C) +#define ADV7393_SVIDEO_POWER_VALUE (0x0C) +#define ADV7393_SD_HUE_ADJUST_DEFAULT (0x80) +#define ADV7393_SD_BRIGHTNESS_WSS_DEFAULT (0x00) + +#define ADV7393_SD_CGMS_WSS0_DEFAULT (0x10) + +#define ADV7393_SD_MODE_REG1_DEFAULT (0x10) +#define ADV7393_SD_MODE_REG2_DEFAULT (0xC9) +#define ADV7393_SD_MODE_REG3_DEFAULT (0x00) +#define ADV7393_SD_MODE_REG4_DEFAULT (0x00) +#define ADV7393_SD_MODE_REG5_DEFAULT (0x02) +#define ADV7393_SD_MODE_REG6_DEFAULT (0x8C) +#define ADV7393_SD_MODE_REG7_DEFAULT (0x14) +#define ADV7393_SD_MODE_REG8_DEFAULT (0x00) + +#define ADV7393_SD_TIMING_REG0_DEFAULT (0x0C) + +/* Bit masks for Mode Select Register */ +#define INPUT_MODE_MASK (0x70) +#define SD_INPUT_MODE (0x00) +#define HD_720P_INPUT_MODE (0x10) +#define HD_1080I_INPUT_MODE (0x10) + +/* Bit masks for Mode Register 0 */ +#define TEST_PATTERN_BLACK_BAR_EN (0x04) +#define YUV_OUTPUT_SELECT (0x20) +#define RGB_OUTPUT_SELECT (0xDF) + +/* Bit masks for SD brightness/WSS */ +#define SD_BRIGHTNESS_VALUE_MASK (0x7F) +#define SD_BLANK_WSS_DATA_MASK (0x80) + +/* Bit masks for soft reset register */ +#define SOFT_RESET (0x02) + +/* Bit masks for HD Mode Register 1 */ +#define OUTPUT_STD_MASK (0x03) +#define OUTPUT_STD_SHIFT (0) +#define OUTPUT_STD_EIA0_2 (0x00) +#define OUTPUT_STD_EIA0_1 (0x01) +#define OUTPUT_STD_FULL (0x02) +#define EMBEDDED_SYNC (0x04) +#define EXTERNAL_SYNC (0xFB) +#define STD_MODE_MASK (0x1F) +#define STD_MODE_SHIFT (3) +#define STD_MODE_720P (0x05) +#define STD_MODE_720P_25 (0x08) +#define STD_MODE_720P_30 (0x07) +#define STD_MODE_720P_50 (0x06) +#define STD_MODE_1080I (0x0D) +#define STD_MODE_1080I_25 (0x0E) +#define STD_MODE_1080P_24 (0x11) +#define STD_MODE_1080P_25 (0x10) +#define STD_MODE_1080P_30 (0x0F) +#define STD_MODE_525P (0x00) +#define STD_MODE_625P (0x03) + +/* Bit masks for SD Mode Register 1 */ +#define SD_STD_MASK (0x03) +#define SD_STD_NTSC (0x00) +#define SD_STD_PAL_BDGHI (0x01) +#define SD_STD_PAL_M (0x02) +#define SD_STD_PAL_N (0x03) +#define SD_LUMA_FLTR_MASK (0x07) +#define SD_LUMA_FLTR_SHIFT (2) +#define SD_CHROMA_FLTR_MASK (0x07) +#define SD_CHROMA_FLTR_SHIFT (5) + +/* Bit masks for SD Mode Register 2 */ +#define SD_PRPB_SSAF_EN (0x01) +#define SD_PRPB_SSAF_DI (0xFE) +#define SD_DAC_OUT1_EN (0x02) +#define SD_DAC_OUT1_DI (0xFD) +#define SD_PEDESTAL_EN (0x08) +#define SD_PEDESTAL_DI (0xF7) +#define SD_SQUARE_PIXEL_EN (0x10) +#define SD_SQUARE_PIXEL_DI (0xEF) +#define SD_PIXEL_DATA_VALID (0x40) +#define SD_ACTIVE_EDGE_EN (0x80) +#define SD_ACTIVE_EDGE_DI (0x7F) + +/* Bit masks for HD Mode Register 6 */ +#define HD_PRPB_SYNC_EN (0x04) +#define HD_PRPB_SYNC_DI (0xFB) +#define HD_DAC_SWAP_EN (0x08) +#define HD_DAC_SWAP_DI (0xF7) +#define HD_GAMMA_CURVE_A (0xEF) +#define HD_GAMMA_CURVE_B (0x10) +#define HD_GAMMA_EN (0x20) +#define HD_GAMMA_DI (0xDF) +#define HD_ADPT_FLTR_MODEA (0xBF) +#define HD_ADPT_FLTR_MODEB (0x40) +#define HD_ADPT_FLTR_EN (0x80) +#define HD_ADPT_FLTR_DI (0x7F) + +#define ADV7393_BRIGHTNESS_MAX (63) +#define ADV7393_BRIGHTNESS_MIN (-64) +#define ADV7393_BRIGHTNESS_DEF (0) +#define ADV7393_HUE_MAX (127) +#define ADV7393_HUE_MIN (-128) +#define ADV7393_HUE_DEF (0) +#define ADV7393_GAIN_MAX (64) +#define ADV7393_GAIN_MIN (-64) +#define ADV7393_GAIN_DEF (0) + +#endif diff --git a/include/media/adv7393.h b/include/media/adv7393.h new file mode 100644 index 000000000000..b28edf351842 --- /dev/null +++ b/include/media/adv7393.h @@ -0,0 +1,28 @@ +/* + * ADV7393 header file + * + * Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/ + * Benoît Thébaudeau + * + * Based on ADV7343 driver, + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 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 ADV7393_H +#define ADV7393_H + +#define ADV7393_COMPOSITE_ID (0) +#define ADV7393_COMPONENT_ID (1) +#define ADV7393_SVIDEO_ID (2) + +#endif /* End of #ifndef ADV7393_H */ diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index 7395c815939d..58f914a40b20 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -180,6 +180,9 @@ enum { /* module adv7343: just ident 7343 */ V4L2_IDENT_ADV7343 = 7343, + /* module adv7393: just ident 7393 */ + V4L2_IDENT_ADV7393 = 7393, + /* module saa7706h: just ident 7706 */ V4L2_IDENT_SAA7706H = 7706, -- cgit v1.2.3 From 4a085168b59ec0fb18eb7fa023dcc47f4db14655 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 22 Jun 2012 06:38:06 -0300 Subject: [media] v4l2-ioctl: remove v4l_(i2c_)print_ioctl v4l_i2c_print_ioctl wasn't used and v4l_print_ioctl could be replaced by v4l_printk_ioctl. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 4 ++-- drivers/media/video/sn9c102/sn9c102.h | 2 +- drivers/media/video/uvc/uvc_v4l2.c | 2 +- drivers/media/video/v4l2-ioctl.c | 34 +++++++----------------------- include/media/v4l2-ioctl.h | 20 +++--------------- 5 files changed, 15 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index cbe40806bd71..f344aed32a93 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -957,7 +957,7 @@ static long pvr2_v4l2_ioctl(struct file *file, long ret = -EINVAL; if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) - v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw), cmd); + v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw), cmd); if (!pvr2_hdw_dev_ok(hdw)) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, @@ -990,7 +990,7 @@ static long pvr2_v4l2_ioctl(struct file *file, pvr2_trace(PVR2_TRACE_V4LIOCTL, "pvr2_v4l2_do_ioctl failure, ret=%ld" " command was:", ret); - v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw), + v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw), cmd); } } diff --git a/drivers/media/video/sn9c102/sn9c102.h b/drivers/media/video/sn9c102/sn9c102.h index 22ea211ab54f..2bc153e869be 100644 --- a/drivers/media/video/sn9c102/sn9c102.h +++ b/drivers/media/video/sn9c102/sn9c102.h @@ -182,7 +182,7 @@ do { \ # define V4LDBG(level, name, cmd) \ do { \ if (debug >= (level)) \ - v4l_print_ioctl(name, cmd); \ + v4l_printk_ioctl(name, cmd); \ } while (0) # define KDBG(level, fmt, args...) \ do { \ diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 759bef8897e9..f00db3060e0e 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -1051,7 +1051,7 @@ static long uvc_v4l2_ioctl(struct file *file, { if (uvc_trace_param & UVC_TRACE_IOCTL) { uvc_printk(KERN_DEBUG, "uvc_v4l2_ioctl("); - v4l_printk_ioctl(cmd); + v4l_printk_ioctl(NULL, cmd); printk(")\n"); } diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 273c6d7bef65..fd6436edde70 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -28,27 +28,6 @@ #include #include -#define dbgarg(cmd, fmt, arg...) \ - do { \ - if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { \ - printk(KERN_DEBUG "%s: ", vfd->name); \ - v4l_printk_ioctl(cmd); \ - printk(" " fmt, ## arg); \ - } \ - } while (0) - -#define dbgarg2(fmt, arg...) \ - do { \ - if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) \ - printk(KERN_DEBUG "%s: " fmt, vfd->name, ## arg);\ - } while (0) - -#define dbgarg3(fmt, arg...) \ - do { \ - if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) \ - printk(KERN_CONT "%s: " fmt, vfd->name, ## arg);\ - } while (0) - /* Zero out the end of the struct pointed to by p. Everything after, but * not including, the specified field is cleared. */ #define CLEAR_AFTER_FIELD(p, field) \ @@ -1956,10 +1935,13 @@ bool v4l2_is_known_ioctl(unsigned int cmd) /* Common ioctl debug function. This function can be used by external ioctl messages as well as internal V4L ioctl */ -void v4l_printk_ioctl(unsigned int cmd) +void v4l_printk_ioctl(const char *prefix, unsigned int cmd) { const char *dir, *type; + if (prefix) + printk(KERN_DEBUG "%s: ", prefix); + switch (_IOC_TYPE(cmd)) { case 'd': type = "v4l2_int"; @@ -2003,8 +1985,8 @@ static long __video_do_ioctl(struct file *file, long ret = -ENOTTY; if (ops == NULL) { - printk(KERN_WARNING "videodev: \"%s\" has no ioctl_ops.\n", - vfd->name); + pr_warn("%s: has no ioctl_ops.\n", + video_device_node_name(vfd)); return ret; } @@ -2034,7 +2016,7 @@ static long __video_do_ioctl(struct file *file, write_only = _IOC_DIR(cmd) == _IOC_WRITE; if (write_only && debug > V4L2_DEBUG_IOCTL) { - v4l_print_ioctl(vfd->name, cmd); + v4l_printk_ioctl(video_device_node_name(vfd), cmd); pr_cont(": "); info->debug(arg, write_only); } @@ -2062,7 +2044,7 @@ done: video_device_node_name(vfd), ret); return ret; } - v4l_print_ioctl(vfd->name, cmd); + v4l_printk_ioctl(video_device_node_name(vfd), cmd); if (ret < 0) pr_cont(": error %ld\n", ret); else if (debug == V4L2_DEBUG_IOCTL) diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index d8b76f7392f8..dfd984f10d42 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -295,28 +295,14 @@ struct v4l2_ioctl_ops { #define V4L2_DEBUG_IOCTL 0x01 #define V4L2_DEBUG_IOCTL_ARG 0x02 -/* Use this macro for non-I2C drivers. Pass the driver name as the first arg. */ -#define v4l_print_ioctl(name, cmd) \ - do { \ - printk(KERN_DEBUG "%s: ", name); \ - v4l_printk_ioctl(cmd); \ - } while (0) - -/* Use this macro in I2C drivers where 'client' is the struct i2c_client - pointer */ -#define v4l_i2c_print_ioctl(client, cmd) \ - do { \ - v4l_client_printk(KERN_DEBUG, client, ""); \ - v4l_printk_ioctl(cmd); \ - } while (0) - /* Video standard functions */ extern const char *v4l2_norm_to_name(v4l2_std_id id); extern void v4l2_video_std_frame_period(int id, struct v4l2_fract *frameperiod); extern int v4l2_video_std_construct(struct v4l2_standard *vs, int id, const char *name); -/* Prints the ioctl in a human-readable format */ -extern void v4l_printk_ioctl(unsigned int cmd); +/* Prints the ioctl in a human-readable format. If prefix != NULL, + then do printk(KERN_DEBUG "%s: ", prefix) first. */ +extern void v4l_printk_ioctl(const char *prefix, unsigned int cmd); /* names for fancy debug output */ extern const char *v4l2_field_names[]; -- cgit v1.2.3 From 5a5adf6b669cf1a3dd2af419cd68a4c491f384a3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 22 Jun 2012 07:29:35 -0300 Subject: [media] v4l2-dev/ioctl.c: add vb2_queue support to video_device This prepares struct video_device for easier integration with vb2. It also introduces a new lock that protects the vb2_queue. It is up to the driver to use it or not. And the driver can associate an owner filehandle with the queue to check whether queuing requests are permitted for the calling filehandle. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-dev.c | 16 +++++----------- drivers/media/video/v4l2-ioctl.c | 31 +++++++++++++++++++++++-------- include/media/v4l2-dev.h | 3 +++ include/media/v4l2-ioctl.h | 5 +++++ include/media/videobuf2-core.h | 13 +++++++++++++ 5 files changed, 49 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index c2122e53f051..b82778174eef 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -348,20 +348,14 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) int ret = -ENODEV; if (vdev->fops->unlocked_ioctl) { - bool locked = false; + struct mutex *lock = v4l2_ioctl_get_lock(vdev, cmd); - if (vdev->lock) { - /* always lock unless the cmd is marked as "don't use lock" */ - locked = !v4l2_is_known_ioctl(cmd) || - !test_bit(_IOC_NR(cmd), vdev->disable_locking); - - if (locked && mutex_lock_interruptible(vdev->lock)) - return -ERESTARTSYS; - } + if (lock && mutex_lock_interruptible(lock)) + return -ERESTARTSYS; if (video_is_registered(vdev)) ret = vdev->fops->unlocked_ioctl(filp, cmd, arg); - if (locked) - mutex_unlock(vdev->lock); + if (lock) + mutex_unlock(lock); } else if (vdev->fops->ioctl) { /* This code path is a replacement for the BKL. It is a major * hack but it will have to do for those drivers that are not diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index fd6436edde70..70e0efb127a6 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -27,6 +27,7 @@ #include #include #include +#include /* Zero out the end of the struct pointed to by p. Everything after, but * not including, the specified field is cleared. */ @@ -1817,6 +1818,8 @@ struct v4l2_ioctl_info { #define INFO_FL_STD (1 << 2) /* This is ioctl has its own function */ #define INFO_FL_FUNC (1 << 3) +/* Queuing ioctl */ +#define INFO_FL_QUEUE (1 << 4) /* Zero struct from after the field to the end */ #define INFO_FL_CLEAR(v4l2_struct, field) \ ((offsetof(struct v4l2_struct, field) + \ @@ -1846,15 +1849,15 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)), IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, INFO_FL_CLEAR(v4l2_format, type)), IOCTL_INFO_FNC(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_CLEAR(v4l2_buffer, length)), + IOCTL_INFO_FNC(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE), + IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)), IOCTL_INFO_STD(VIDIOC_G_FBUF, vidioc_g_fbuf, v4l_print_framebuffer, 0), IOCTL_INFO_STD(VIDIOC_S_FBUF, vidioc_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO), IOCTL_INFO_STD(VIDIOC_OVERLAY, vidioc_overlay, v4l_print_u32, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, 0), - IOCTL_INFO_FNC(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, 0), - IOCTL_INFO_FNC(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO), + IOCTL_INFO_FNC(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE), + IOCTL_INFO_FNC(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE), + IOCTL_INFO_FNC(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE), + IOCTL_INFO_FNC(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE), IOCTL_INFO_FNC(VIDIOC_G_PARM, v4l_g_parm, v4l_print_streamparm, INFO_FL_CLEAR(v4l2_streamparm, type)), IOCTL_INFO_FNC(VIDIOC_S_PARM, v4l_s_parm, v4l_print_streamparm, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_G_STD, v4l_g_std, v4l_print_std, 0), @@ -1918,8 +1921,8 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_FNC(VIDIOC_DQEVENT, v4l_dqevent, v4l_print_event, 0), IOCTL_INFO_FNC(VIDIOC_SUBSCRIBE_EVENT, v4l_subscribe_event, v4l_print_event_subscription, 0), IOCTL_INFO_FNC(VIDIOC_UNSUBSCRIBE_EVENT, v4l_unsubscribe_event, v4l_print_event_subscription, 0), - IOCTL_INFO_FNC(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, 0), + IOCTL_INFO_FNC(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE), + IOCTL_INFO_FNC(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE), IOCTL_INFO_STD(VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings, v4l_print_enum_dv_timings, 0), IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, 0), IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, 0), @@ -1933,6 +1936,18 @@ bool v4l2_is_known_ioctl(unsigned int cmd) return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd; } +struct mutex *v4l2_ioctl_get_lock(struct video_device *vdev, unsigned cmd) +{ + if (_IOC_NR(cmd) >= V4L2_IOCTLS) + return vdev->lock; + if (test_bit(_IOC_NR(cmd), vdev->disable_locking)) + return NULL; + if (vdev->queue && vdev->queue->lock && + (v4l2_ioctls[_IOC_NR(cmd)].flags & INFO_FL_QUEUE)) + return vdev->queue->lock; + return vdev->lock; +} + /* Common ioctl debug function. This function can be used by external ioctl messages as well as internal V4L ioctl */ void v4l_printk_ioctl(const char *prefix, unsigned int cmd) diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index a056e6ee1b68..5c416cdc88d5 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -100,6 +100,9 @@ struct video_device /* Control handler associated with this device node. May be NULL. */ struct v4l2_ctrl_handler *ctrl_handler; + /* vb2_queue associated with this device node. May be NULL. */ + struct vb2_queue *queue; + /* Priority state. If NULL, then v4l2_dev->prio will be used. */ struct v4l2_prio_state *prio; diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index dfd984f10d42..19e93523c2d8 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -304,6 +304,11 @@ extern int v4l2_video_std_construct(struct v4l2_standard *vs, then do printk(KERN_DEBUG "%s: ", prefix) first. */ extern void v4l_printk_ioctl(const char *prefix, unsigned int cmd); +/* Internal use only: get the mutex (if any) that we need to lock for the + given command. */ +struct video_device; +extern struct mutex *v4l2_ioctl_get_lock(struct video_device *vdev, unsigned cmd); + /* names for fancy debug output */ extern const char *v4l2_field_names[]; extern const char *v4l2_type_names[]; diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index a15d1f1b319e..924e95e0a0a6 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -244,12 +244,23 @@ struct vb2_ops { void (*buf_queue)(struct vb2_buffer *vb); }; +struct v4l2_fh; + /** * struct vb2_queue - a videobuf queue * * @type: queue type (see V4L2_BUF_TYPE_* in linux/videodev2.h * @io_modes: supported io methods (see vb2_io_modes enum) * @io_flags: additional io flags (see vb2_fileio_flags enum) + * @lock: pointer to a mutex that protects the vb2_queue struct. The + * driver can set this to a mutex to let the v4l2 core serialize + * the queuing ioctls. If the driver wants to handle locking + * itself, then this should be set to NULL. This lock is not used + * by the videobuf2 core API. + * @owner: The filehandle that 'owns' the buffers, i.e. the filehandle + * that called reqbufs, create_buffers or started fileio. + * This field is not used by the videobuf2 core API, but it allows + * drivers to easily associate an owner filehandle with the queue. * @ops: driver-specific callbacks * @mem_ops: memory allocator specific callbacks * @drv_priv: driver private data @@ -273,6 +284,8 @@ struct vb2_queue { enum v4l2_buf_type type; unsigned int io_modes; unsigned int io_flags; + struct mutex *lock; + struct v4l2_fh *owner; const struct vb2_ops *ops; const struct vb2_mem_ops *mem_ops; -- cgit v1.2.3 From 4c1ffcaad5070ea5bca9b8057bdd7b4925237bc0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 2 Jul 2012 05:59:18 -0300 Subject: [media] videobuf2-core: add helper functions Add helper functions to make it easier to adapt drivers to vb2. These helpers take care of core locking and check if the filehandle is the owner of the queue. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/videobuf2-core.c | 257 +++++++++++++++++++++++++++++++++++ include/media/videobuf2-core.h | 41 ++++++ 2 files changed, 298 insertions(+) (limited to 'include') diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index ed38eb748357..4e0290ab5071 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -2125,6 +2125,263 @@ size_t vb2_write(struct vb2_queue *q, char __user *data, size_t count, } EXPORT_SYMBOL_GPL(vb2_write); + +/* + * The following functions are not part of the vb2 core API, but are helper + * functions that plug into struct v4l2_ioctl_ops, struct v4l2_file_operations + * and struct vb2_ops. + * They contain boilerplate code that most if not all drivers have to do + * and so they simplify the driver code. + */ + +/* The queue is busy if there is a owner and you are not that owner. */ +static inline bool vb2_queue_is_busy(struct video_device *vdev, struct file *file) +{ + return vdev->queue->owner && vdev->queue->owner != file->private_data; +} + +/* vb2 ioctl helpers */ + +int vb2_ioctl_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct video_device *vdev = video_devdata(file); + int res = __verify_memory_type(vdev->queue, p->memory, p->type); + + if (res) + return res; + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + res = __reqbufs(vdev->queue, p); + /* If count == 0, then the owner has released all buffers and he + is no longer owner of the queue. Otherwise we have a new owner. */ + if (res == 0) + vdev->queue->owner = p->count ? file->private_data : NULL; + return res; +} +EXPORT_SYMBOL_GPL(vb2_ioctl_reqbufs); + +int vb2_ioctl_create_bufs(struct file *file, void *priv, + struct v4l2_create_buffers *p) +{ + struct video_device *vdev = video_devdata(file); + int res = __verify_memory_type(vdev->queue, p->memory, p->format.type); + + p->index = vdev->queue->num_buffers; + /* If count == 0, then just check if memory and type are valid. + Any -EBUSY result from __verify_memory_type can be mapped to 0. */ + if (p->count == 0) + return res != -EBUSY ? res : 0; + if (res) + return res; + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + res = __create_bufs(vdev->queue, p); + if (res == 0) + vdev->queue->owner = file->private_data; + return res; +} +EXPORT_SYMBOL_GPL(vb2_ioctl_create_bufs); + +int vb2_ioctl_prepare_buf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct video_device *vdev = video_devdata(file); + + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + return vb2_prepare_buf(vdev->queue, p); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_prepare_buf); + +int vb2_ioctl_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct video_device *vdev = video_devdata(file); + + /* No need to call vb2_queue_is_busy(), anyone can query buffers. */ + return vb2_querybuf(vdev->queue, p); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_querybuf); + +int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct video_device *vdev = video_devdata(file); + + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + return vb2_qbuf(vdev->queue, p); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_qbuf); + +int vb2_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct video_device *vdev = video_devdata(file); + + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + return vb2_dqbuf(vdev->queue, p, file->f_flags & O_NONBLOCK); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_dqbuf); + +int vb2_ioctl_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct video_device *vdev = video_devdata(file); + + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + return vb2_streamon(vdev->queue, i); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_streamon); + +int vb2_ioctl_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct video_device *vdev = video_devdata(file); + + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + return vb2_streamoff(vdev->queue, i); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_streamoff); + +/* v4l2_file_operations helpers */ + +int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *vdev = video_devdata(file); + + return vb2_mmap(vdev->queue, vma); +} +EXPORT_SYMBOL_GPL(vb2_fop_mmap); + +int vb2_fop_release(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + + if (file->private_data == vdev->queue->owner) { + vb2_queue_release(vdev->queue); + vdev->queue->owner = NULL; + } + return v4l2_fh_release(file); +} +EXPORT_SYMBOL_GPL(vb2_fop_release); + +ssize_t vb2_fop_write(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct video_device *vdev = video_devdata(file); + struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock; + bool must_lock = !test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) && lock; + int err = -EBUSY; + + if (must_lock && mutex_lock_interruptible(lock)) + return -ERESTARTSYS; + if (vb2_queue_is_busy(vdev, file)) + goto exit; + err = vb2_write(vdev->queue, buf, count, ppos, + file->f_flags & O_NONBLOCK); + if (err >= 0) + vdev->queue->owner = file->private_data; +exit: + if (must_lock) + mutex_unlock(lock); + return err; +} +EXPORT_SYMBOL_GPL(vb2_fop_write); + +ssize_t vb2_fop_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct video_device *vdev = video_devdata(file); + struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock; + bool must_lock = !test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) && vdev->lock; + int err = -EBUSY; + + if (must_lock && mutex_lock_interruptible(lock)) + return -ERESTARTSYS; + if (vb2_queue_is_busy(vdev, file)) + goto exit; + err = vb2_read(vdev->queue, buf, count, ppos, + file->f_flags & O_NONBLOCK); + if (err >= 0) + vdev->queue->owner = file->private_data; +exit: + if (must_lock) + mutex_unlock(lock); + return err; +} +EXPORT_SYMBOL_GPL(vb2_fop_read); + +unsigned int vb2_fop_poll(struct file *file, poll_table *wait) +{ + struct video_device *vdev = video_devdata(file); + struct vb2_queue *q = vdev->queue; + struct mutex *lock = q->lock ? q->lock : vdev->lock; + unsigned long req_events = poll_requested_events(wait); + unsigned res; + void *fileio; + /* Yuck. We really need to get rid of this flag asap. If it is + set, then the core took the serialization lock before calling + poll(). This is being phased out, but for now we have to handle + this case. */ + bool locked = test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags); + bool must_lock = false; + + /* Try to be smart: only lock if polling might start fileio, + otherwise locking will only introduce unwanted delays. */ + if (q->num_buffers == 0 && q->fileio == NULL) { + if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) && + (req_events & (POLLIN | POLLRDNORM))) + must_lock = true; + else if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) && + (req_events & (POLLOUT | POLLWRNORM))) + must_lock = true; + } + + /* If locking is needed, but this helper doesn't know how, then you + shouldn't be using this helper but you should write your own. */ + WARN_ON(must_lock && !locked && !lock); + + if (must_lock && !locked && lock && mutex_lock_interruptible(lock)) + return POLLERR; + + fileio = q->fileio; + + res = vb2_poll(vdev->queue, file, wait); + + /* If fileio was started, then we have a new queue owner. */ + if (must_lock && !fileio && q->fileio) + q->owner = file->private_data; + if (must_lock && !locked && lock) + mutex_unlock(lock); + return res; +} +EXPORT_SYMBOL_GPL(vb2_fop_poll); + +#ifndef CONFIG_MMU +unsigned long vb2_fop_get_unmapped_area(struct file *file, unsigned long addr, + unsigned long len, unsigned long pgoff, unsigned long flags) +{ + struct video_device *vdev = video_devdata(file); + + return vb2_get_unmapped_area(vdev->queue, addr, len, pgoff, flags); +} +EXPORT_SYMBOL_GPL(vb2_fop_get_unmapped_area); +#endif + +/* vb2_ops helpers. Only use if vq->lock is non-NULL. */ + +void vb2_ops_wait_prepare(struct vb2_queue *vq) +{ + mutex_unlock(vq->lock); +} +EXPORT_SYMBOL_GPL(vb2_ops_wait_prepare); + +void vb2_ops_wait_finish(struct vb2_queue *vq) +{ + mutex_lock(vq->lock); +} +EXPORT_SYMBOL_GPL(vb2_ops_wait_finish); + MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2"); MODULE_AUTHOR("Pawel Osciak , Marek Szyprowski"); MODULE_LICENSE("GPL"); diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 924e95e0a0a6..8dd9b6cc296b 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -417,4 +417,45 @@ vb2_plane_size(struct vb2_buffer *vb, unsigned int plane_no) return 0; } +/* + * The following functions are not part of the vb2 core API, but are simple + * helper functions that you can use in your struct v4l2_file_operations, + * struct v4l2_ioctl_ops and struct vb2_ops. They will serialize if vb2_queue->lock + * or video_device->lock is set, and they will set and test vb2_queue->owner + * to check if the calling filehandle is permitted to do the queuing operation. + */ + +/* struct v4l2_ioctl_ops helpers */ + +int vb2_ioctl_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p); +int vb2_ioctl_create_bufs(struct file *file, void *priv, + struct v4l2_create_buffers *p); +int vb2_ioctl_prepare_buf(struct file *file, void *priv, + struct v4l2_buffer *p); +int vb2_ioctl_querybuf(struct file *file, void *priv, struct v4l2_buffer *p); +int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p); +int vb2_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p); +int vb2_ioctl_streamon(struct file *file, void *priv, enum v4l2_buf_type i); +int vb2_ioctl_streamoff(struct file *file, void *priv, enum v4l2_buf_type i); + +/* struct v4l2_file_operations helpers */ + +int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma); +int vb2_fop_release(struct file *file); +ssize_t vb2_fop_write(struct file *file, char __user *buf, + size_t count, loff_t *ppos); +ssize_t vb2_fop_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos); +unsigned int vb2_fop_poll(struct file *file, poll_table *wait); +#ifndef CONFIG_MMU +unsigned long vb2_fop_get_unmapped_area(struct file *file, unsigned long addr, + unsigned long len, unsigned long pgoff, unsigned long flags); +#endif + +/* struct vb2_ops helpers, only use if vq->lock is non-NULL. */ + +void vb2_ops_wait_prepare(struct vb2_queue *vq); +void vb2_ops_wait_finish(struct vb2_queue *vq); + #endif /* _MEDIA_VIDEOBUF2_CORE_H */ -- cgit v1.2.3 From 500c3201e2aed201f2de0468dfeb3ceb98a9f981 Mon Sep 17 00:00:00 2001 From: "Du, Changbin" Date: Tue, 3 Jul 2012 06:27:19 -0300 Subject: [media] media: gpio-ir-recv: add allowed_protos for platform data It's better to give platform code a chance to specify the allowed protocols to use. [mchehab@redhat.com: fix merge conflict with a patch that made half of this change] Signed-off-by: Du, Changbin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/gpio-ir-recv.c | 5 ++++- include/media/gpio-ir-recv.h | 7 ++++--- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c index 59fe60cd1e02..04cb272db16a 100644 --- a/drivers/media/rc/gpio-ir-recv.c +++ b/drivers/media/rc/gpio-ir-recv.c @@ -84,7 +84,6 @@ static int __devinit gpio_ir_recv_probe(struct platform_device *pdev) rcdev->priv = gpio_dev; rcdev->driver_type = RC_DRIVER_IR_RAW; - rcdev->allowed_protos = RC_TYPE_ALL; rcdev->input_name = GPIO_IR_DEVICE_NAME; rcdev->input_phys = GPIO_IR_DEVICE_NAME "/input0"; rcdev->input_id.bustype = BUS_HOST; @@ -93,6 +92,10 @@ static int __devinit gpio_ir_recv_probe(struct platform_device *pdev) rcdev->input_id.version = 0x0100; rcdev->dev.parent = &pdev->dev; rcdev->driver_name = GPIO_IR_DRIVER_NAME; + if (pdata->allowed_protos) + rcdev->allowed_protos = pdata->allowed_protos; + else + rcdev->allowed_protos = RC_TYPE_ALL; rcdev->map_name = pdata->map_name ?: RC_MAP_EMPTY; gpio_dev->rcdev = rcdev; diff --git a/include/media/gpio-ir-recv.h b/include/media/gpio-ir-recv.h index 91546f35b7e1..0142736a59db 100644 --- a/include/media/gpio-ir-recv.h +++ b/include/media/gpio-ir-recv.h @@ -14,9 +14,10 @@ #define __GPIO_IR_RECV_H__ struct gpio_ir_recv_platform_data { - int gpio_nr; - bool active_low; - const char *map_name; + int gpio_nr; + bool active_low; + u64 allowed_protos; + const char *map_name; }; #endif /* __GPIO_IR_RECV_H__ */ -- cgit v1.2.3 From f5e3bcc504c3c35cc6e06a9ee42efed7c274066b Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 29 Jun 2012 14:48:36 +0100 Subject: tty: localise the lock The termios and other changes mean the other protections needed on the driver tty arrays should be adequate. Turn it all back on. This contains pieces folded in from the fixes made to the original patches | From: Geert Uytterhoeven (fix m68k) | From: Paul Gortmaker (fix cris) | From: Jiri Kosina (lockdep) | From: Eric Dumazet (lockdep) Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/amiserial.c | 14 ++++----- drivers/tty/cyclades.c | 2 +- drivers/tty/n_r3964.c | 10 +++---- drivers/tty/pty.c | 25 +++++++++------- drivers/tty/serial/crisv10.c | 8 ++--- drivers/tty/synclink.c | 4 +-- drivers/tty/synclink_gt.c | 4 +-- drivers/tty/synclinkmp.c | 4 +-- drivers/tty/tty_io.c | 67 ++++++++++++++++++++++++----------------- drivers/tty/tty_ldisc.c | 67 +++++++++++++++++++++++------------------ drivers/tty/tty_mutex.c | 71 ++++++++++++++++++++++++++++++++++---------- drivers/tty/tty_port.c | 6 ++-- include/linux/tty.h | 23 ++++++++------ net/bluetooth/rfcomm/tty.c | 4 +-- 14 files changed, 190 insertions(+), 119 deletions(-) (limited to 'include') diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 6cc4358f68c1..35819e312624 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1033,7 +1033,7 @@ static int get_serial_info(struct tty_struct *tty, struct serial_state *state, if (!retinfo) return -EFAULT; memset(&tmp, 0, sizeof(tmp)); - tty_lock(); + tty_lock(tty); tmp.line = tty->index; tmp.port = state->port; tmp.flags = state->tport.flags; @@ -1042,7 +1042,7 @@ static int get_serial_info(struct tty_struct *tty, struct serial_state *state, tmp.close_delay = state->tport.close_delay; tmp.closing_wait = state->tport.closing_wait; tmp.custom_divisor = state->custom_divisor; - tty_unlock(); + tty_unlock(tty); if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) return -EFAULT; return 0; @@ -1059,12 +1059,12 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) return -EFAULT; - tty_lock(); + tty_lock(tty); change_spd = ((new_serial.flags ^ port->flags) & ASYNC_SPD_MASK) || new_serial.custom_divisor != state->custom_divisor; if (new_serial.irq || new_serial.port != state->port || new_serial.xmit_fifo_size != state->xmit_fifo_size) { - tty_unlock(); + tty_unlock(tty); return -EINVAL; } @@ -1074,7 +1074,7 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, (new_serial.xmit_fifo_size != state->xmit_fifo_size) || ((new_serial.flags & ~ASYNC_USR_MASK) != (port->flags & ~ASYNC_USR_MASK))) { - tty_unlock(); + tty_unlock(tty); return -EPERM; } port->flags = ((port->flags & ~ASYNC_USR_MASK) | @@ -1084,7 +1084,7 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, } if (new_serial.baud_base < 9600) { - tty_unlock(); + tty_unlock(tty); return -EINVAL; } @@ -1116,7 +1116,7 @@ check_and_exit: } } else retval = startup(tty, state); - tty_unlock(); + tty_unlock(tty); return retval; } diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index cff546839db9..69e9ca2dd4b3 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -1599,7 +1599,7 @@ static int cy_open(struct tty_struct *tty, struct file *filp) * If the port is the middle of closing, bail out now */ if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(info->port.close_wait, + wait_event_interruptible_tty(tty, info->port.close_wait, !(info->port.flags & ASYNC_CLOSING)); return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; } diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c index 5c6c31459a2f..1e6405070ce6 100644 --- a/drivers/tty/n_r3964.c +++ b/drivers/tty/n_r3964.c @@ -1065,7 +1065,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, TRACE_L("read()"); - tty_lock(); + tty_lock(tty); pClient = findClient(pInfo, task_pid(current)); if (pClient) { @@ -1077,7 +1077,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, goto unlock; } /* block until there is a message: */ - wait_event_interruptible_tty(pInfo->read_wait, + wait_event_interruptible_tty(tty, pInfo->read_wait, (pMsg = remove_msg(pInfo, pClient))); } @@ -1107,7 +1107,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, } ret = -EPERM; unlock: - tty_unlock(); + tty_unlock(tty); return ret; } @@ -1156,7 +1156,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file, pHeader->locks = 0; pHeader->owner = NULL; - tty_lock(); + tty_lock(tty); pClient = findClient(pInfo, task_pid(current)); if (pClient) { @@ -1175,7 +1175,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file, add_tx_queue(pInfo, pHeader); trigger_transmit(pInfo); - tty_unlock(); + tty_unlock(tty); return 0; } diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index b50fc1c01415..d8558834ce57 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -47,6 +47,7 @@ static void pty_close(struct tty_struct *tty, struct file *filp) wake_up_interruptible(&tty->read_wait); wake_up_interruptible(&tty->write_wait); tty->packet = 0; + /* Review - krefs on tty_link ?? */ if (!tty->link) return; tty->link->packet = 0; @@ -62,9 +63,9 @@ static void pty_close(struct tty_struct *tty, struct file *filp) mutex_unlock(&devpts_mutex); } #endif - tty_unlock(); + tty_unlock(tty); tty_vhangup(tty->link); - tty_lock(); + tty_lock(tty); } } @@ -615,26 +616,27 @@ static int ptmx_open(struct inode *inode, struct file *filp) return retval; /* find a device that is not in use. */ - tty_lock(); + mutex_lock(&devpts_mutex); index = devpts_new_index(inode); - tty_unlock(); if (index < 0) { retval = index; goto err_file; } + mutex_unlock(&devpts_mutex); + mutex_lock(&tty_mutex); - mutex_lock(&devpts_mutex); tty = tty_init_dev(ptm_driver, index); - mutex_unlock(&devpts_mutex); - tty_lock(); - mutex_unlock(&tty_mutex); if (IS_ERR(tty)) { retval = PTR_ERR(tty); goto out; } + /* The tty returned here is locked so we can safely + drop the mutex */ + mutex_unlock(&tty_mutex); + set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ tty_add_file(tty, filp); @@ -647,16 +649,17 @@ static int ptmx_open(struct inode *inode, struct file *filp) if (retval) goto err_release; - tty_unlock(); + tty_unlock(tty); return 0; err_release: - tty_unlock(); + tty_unlock(tty); tty_release(inode, filp); return retval; out: + mutex_unlock(&tty_mutex); devpts_kill_index(inode, index); - tty_unlock(); err_file: + mutex_unlock(&devpts_mutex); tty_free_file(filp); return retval; } diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 80b6b1b1f725..7264d4d26717 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -3976,7 +3976,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(info->close_wait, + wait_event_interruptible_tty(tty, info->close_wait, !(info->flags & ASYNC_CLOSING)); #ifdef SERIAL_DO_RESTART if (info->flags & ASYNC_HUP_NOTIFY) @@ -4052,9 +4052,9 @@ block_til_ready(struct tty_struct *tty, struct file * filp, printk("block_til_ready blocking: ttyS%d, count = %d\n", info->line, info->count); #endif - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); remove_wait_queue(&info->open_wait, &wait); @@ -4115,7 +4115,7 @@ rs_open(struct tty_struct *tty, struct file * filp) */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(info->close_wait, + wait_event_interruptible_tty(tty, info->close_wait, !(info->flags & ASYNC_CLOSING)); #ifdef SERIAL_DO_RESTART return ((info->flags & ASYNC_HUP_NOTIFY) ? diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index 593d40ad0a6b..5ed0daae6564 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -3338,9 +3338,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, printk("%s(%d):block_til_ready blocking on %s count=%d\n", __FILE__,__LINE__, tty->driver->name, port->count ); - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index aa1debf97cc7..45b43f11ca39 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -3336,9 +3336,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, } DBGINFO(("%s block_til_ready wait\n", tty->driver->name)); - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index a3dddc12d2fe..4a1e4f07765b 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -3357,9 +3357,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, printk("%s(%d):%s block_til_ready() count=%d\n", __FILE__,__LINE__, tty->driver->name, port->count ); - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index ac96f74573d0..ca7c25d9f6d5 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -185,6 +185,7 @@ void free_tty_struct(struct tty_struct *tty) put_device(tty->dev); kfree(tty->write_buf); tty_buffer_free_all(tty); + tty->magic = 0xDEADDEAD; kfree(tty); } @@ -573,7 +574,7 @@ void __tty_hangup(struct tty_struct *tty) } spin_unlock(&redirect_lock); - tty_lock(); + tty_lock(tty); /* some functions below drop BTM, so we need this bit */ set_bit(TTY_HUPPING, &tty->flags); @@ -666,7 +667,7 @@ void __tty_hangup(struct tty_struct *tty) clear_bit(TTY_HUPPING, &tty->flags); tty_ldisc_enable(tty); - tty_unlock(); + tty_unlock(tty); if (f) fput(f); @@ -1103,12 +1104,12 @@ void tty_write_message(struct tty_struct *tty, char *msg) { if (tty) { mutex_lock(&tty->atomic_write_lock); - tty_lock(); + tty_lock(tty); if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) { - tty_unlock(); + tty_unlock(tty); tty->ops->write(tty, msg, strlen(msg)); } else - tty_unlock(); + tty_unlock(tty); tty_write_unlock(tty); } return; @@ -1403,6 +1404,7 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) } initialize_tty_struct(tty, driver, idx); + tty_lock(tty); retval = tty_driver_install_tty(driver, tty); if (retval < 0) goto err_deinit_tty; @@ -1418,9 +1420,11 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) retval = tty_ldisc_setup(tty, tty->link); if (retval) goto err_release_tty; + /* Return the tty locked so that it cannot vanish under the caller */ return tty; err_deinit_tty: + tty_unlock(tty); deinitialize_tty_struct(tty); free_tty_struct(tty); err_module_put: @@ -1429,6 +1433,7 @@ err_module_put: /* call the tty release_tty routine to clean out this slot */ err_release_tty: + tty_unlock(tty); printk_ratelimited(KERN_INFO "tty_init_dev: ldisc open failed, " "clearing slot %d\n", idx); release_tty(tty, idx); @@ -1631,7 +1636,7 @@ int tty_release(struct inode *inode, struct file *filp) if (tty_paranoia_check(tty, inode, __func__)) return 0; - tty_lock(); + tty_lock(tty); check_tty_count(tty, __func__); __tty_fasync(-1, filp, 0); @@ -1640,10 +1645,11 @@ int tty_release(struct inode *inode, struct file *filp) pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER); devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0; + /* Review: parallel close */ o_tty = tty->link; if (tty_release_checks(tty, o_tty, idx)) { - tty_unlock(); + tty_unlock(tty); return 0; } @@ -1655,7 +1661,7 @@ int tty_release(struct inode *inode, struct file *filp) if (tty->ops->close) tty->ops->close(tty, filp); - tty_unlock(); + tty_unlock(tty); /* * Sanity check: if tty->count is going to zero, there shouldn't be * any waiters on tty->read_wait or tty->write_wait. We test the @@ -1678,7 +1684,7 @@ int tty_release(struct inode *inode, struct file *filp) opens on /dev/tty */ mutex_lock(&tty_mutex); - tty_lock(); + tty_lock_pair(tty, o_tty); tty_closing = tty->count <= 1; o_tty_closing = o_tty && (o_tty->count <= (pty_master ? 1 : 0)); @@ -1709,7 +1715,7 @@ int tty_release(struct inode *inode, struct file *filp) printk(KERN_WARNING "%s: %s: read/write wait queue active!\n", __func__, tty_name(tty, buf)); - tty_unlock(); + tty_unlock_pair(tty, o_tty); mutex_unlock(&tty_mutex); schedule(); } @@ -1772,7 +1778,7 @@ int tty_release(struct inode *inode, struct file *filp) /* check whether both sides are closing ... */ if (!tty_closing || (o_tty && !o_tty_closing)) { - tty_unlock(); + tty_unlock_pair(tty, o_tty); return 0; } @@ -1785,14 +1791,16 @@ int tty_release(struct inode *inode, struct file *filp) tty_ldisc_release(tty, o_tty); /* * The release_tty function takes care of the details of clearing - * the slots and preserving the termios structure. + * the slots and preserving the termios structure. The tty_unlock_pair + * should be safe as we keep a kref while the tty is locked (so the + * unlock never unlocks a freed tty). */ release_tty(tty, idx); + tty_unlock_pair(tty, o_tty); /* Make this pty number available for reallocation */ if (devpts) devpts_kill_index(inode, idx); - tty_unlock(); return 0; } @@ -1896,6 +1904,9 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp, * Locking: tty_mutex protects tty, tty_lookup_driver and tty_init_dev. * tty->count should protect the rest. * ->siglock protects ->signal/->sighand + * + * Note: the tty_unlock/lock cases without a ref are only safe due to + * tty_mutex */ static int tty_open(struct inode *inode, struct file *filp) @@ -1919,8 +1930,7 @@ retry_open: retval = 0; mutex_lock(&tty_mutex); - tty_lock(); - + /* This is protected by the tty_mutex */ tty = tty_open_current_tty(device, filp); if (IS_ERR(tty)) { retval = PTR_ERR(tty); @@ -1941,17 +1951,19 @@ retry_open: } if (tty) { + tty_lock(tty); retval = tty_reopen(tty); - if (retval) + if (retval < 0) { + tty_unlock(tty); tty = ERR_PTR(retval); - } else + } + } else /* Returns with the tty_lock held for now */ tty = tty_init_dev(driver, index); mutex_unlock(&tty_mutex); if (driver) tty_driver_kref_put(driver); if (IS_ERR(tty)) { - tty_unlock(); retval = PTR_ERR(tty); goto err_file; } @@ -1980,7 +1992,7 @@ retry_open: printk(KERN_DEBUG "%s: error %d in opening %s...\n", __func__, retval, tty->name); #endif - tty_unlock(); /* need to call tty_release without BTM */ + tty_unlock(tty); /* need to call tty_release without BTM */ tty_release(inode, filp); if (retval != -ERESTARTSYS) return retval; @@ -1992,17 +2004,15 @@ retry_open: /* * Need to reset f_op in case a hangup happened. */ - tty_lock(); if (filp->f_op == &hung_up_tty_fops) filp->f_op = &tty_fops; - tty_unlock(); goto retry_open; } - tty_unlock(); + tty_unlock(tty); mutex_lock(&tty_mutex); - tty_lock(); + tty_lock(tty); spin_lock_irq(¤t->sighand->siglock); if (!noctty && current->signal->leader && @@ -2010,11 +2020,10 @@ retry_open: tty->session == NULL) __proc_set_tty(current, tty); spin_unlock_irq(¤t->sighand->siglock); - tty_unlock(); + tty_unlock(tty); mutex_unlock(&tty_mutex); return 0; err_unlock: - tty_unlock(); mutex_unlock(&tty_mutex); /* after locks to avoid deadlock */ if (!IS_ERR_OR_NULL(driver)) @@ -2097,10 +2106,13 @@ out: static int tty_fasync(int fd, struct file *filp, int on) { + struct tty_struct *tty = file_tty(filp); int retval; - tty_lock(); + + tty_lock(tty); retval = __tty_fasync(fd, filp, on); - tty_unlock(); + tty_unlock(tty); + return retval; } @@ -2937,6 +2949,7 @@ void initialize_tty_struct(struct tty_struct *tty, tty->pgrp = NULL; tty->overrun_time = jiffies; tty_buffer_init(tty); + mutex_init(&tty->legacy_mutex); mutex_init(&tty->termios_mutex); mutex_init(&tty->ldisc_mutex); init_waitqueue_head(&tty->write_wait); diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 9911eb6b34cd..ba8be396a621 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -568,7 +568,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) if (IS_ERR(new_ldisc)) return PTR_ERR(new_ldisc); - tty_lock(); + tty_lock(tty); /* * We need to look at the tty locking here for pty/tty pairs * when both sides try to change in parallel. @@ -582,12 +582,12 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) */ if (tty->ldisc->ops->num == ldisc) { - tty_unlock(); + tty_unlock(tty); tty_ldisc_put(new_ldisc); return 0; } - tty_unlock(); + tty_unlock(tty); /* * Problem: What do we do if this blocks ? * We could deadlock here @@ -595,7 +595,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) tty_wait_until_sent(tty, 0); - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); /* @@ -605,10 +605,10 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) { mutex_unlock(&tty->ldisc_mutex); - tty_unlock(); + tty_unlock(tty); wait_event(tty_ldisc_wait, test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0); - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); } @@ -623,7 +623,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) o_ldisc = tty->ldisc; - tty_unlock(); + tty_unlock(tty); /* * Make sure we don't change while someone holds a * reference to the line discipline. The TTY_LDISC bit @@ -650,7 +650,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) retval = tty_ldisc_wait_idle(tty, 5 * HZ); - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); /* handle wait idle failure locked */ @@ -665,7 +665,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) clear_bit(TTY_LDISC_CHANGING, &tty->flags); mutex_unlock(&tty->ldisc_mutex); tty_ldisc_put(new_ldisc); - tty_unlock(); + tty_unlock(tty); return -EIO; } @@ -708,7 +708,7 @@ enable: if (o_work) schedule_work(&o_tty->buf.work); mutex_unlock(&tty->ldisc_mutex); - tty_unlock(); + tty_unlock(tty); return retval; } @@ -816,11 +816,11 @@ void tty_ldisc_hangup(struct tty_struct *tty) * need to wait for another function taking the BTM */ clear_bit(TTY_LDISC, &tty->flags); - tty_unlock(); + tty_unlock(tty); cancel_work_sync(&tty->buf.work); mutex_unlock(&tty->ldisc_mutex); retry: - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); /* At this point we have a closed ldisc and we want to @@ -831,7 +831,7 @@ retry: if (atomic_read(&tty->ldisc->users) != 1) { char cur_n[TASK_COMM_LEN], tty_n[64]; long timeout = 3 * HZ; - tty_unlock(); + tty_unlock(tty); while (tty_ldisc_wait_idle(tty, timeout) == -EBUSY) { timeout = MAX_SCHEDULE_TIMEOUT; @@ -894,6 +894,23 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *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 + */ + tty_ldisc_close(tty, tty->ldisc); + tty_ldisc_put(tty->ldisc); + /* Force an oops if we mess this up */ + tty->ldisc = NULL; + + /* Ensure the next open requests the N_TTY ldisc */ + tty_set_termios_ldisc(tty, N_TTY); + mutex_unlock(&tty->ldisc_mutex); +} + /** * tty_ldisc_release - release line discipline * @tty: tty being shut down @@ -912,27 +929,19 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) * race with the set_ldisc code path. */ - tty_unlock(); + tty_unlock_pair(tty, o_tty); tty_ldisc_halt(tty); tty_ldisc_flush_works(tty); - tty_lock(); - - mutex_lock(&tty->ldisc_mutex); - /* - * Now kill off the ldisc - */ - tty_ldisc_close(tty, tty->ldisc); - tty_ldisc_put(tty->ldisc); - /* Force an oops if we mess this up */ - tty->ldisc = NULL; + if (o_tty) { + tty_ldisc_halt(o_tty); + tty_ldisc_flush_works(o_tty); + } + tty_lock_pair(tty, o_tty); - /* Ensure the next open requests the N_TTY ldisc */ - tty_set_termios_ldisc(tty, N_TTY); - mutex_unlock(&tty->ldisc_mutex); - /* This will need doing differently if we need to lock */ + tty_ldisc_kill(tty); if (o_tty) - tty_ldisc_release(o_tty, NULL); + tty_ldisc_kill(o_tty); /* And the memory resources remaining (buffers, termios) will be disposed of when the kref hits zero */ diff --git a/drivers/tty/tty_mutex.c b/drivers/tty/tty_mutex.c index 9ff986c32a21..67feac9e6ebb 100644 --- a/drivers/tty/tty_mutex.c +++ b/drivers/tty/tty_mutex.c @@ -4,29 +4,70 @@ #include #include -/* - * The 'big tty mutex' - * - * This mutex is taken and released by tty_lock() and tty_unlock(), - * replacing the older big kernel lock. - * It can no longer be taken recursively, and does not get - * released implicitly while sleeping. - * - * Don't use in new code. - */ -static DEFINE_MUTEX(big_tty_mutex); +/* Legacy tty mutex glue */ + +enum { + TTY_MUTEX_NORMAL, + TTY_MUTEX_NESTED, +}; /* * Getting the big tty mutex. */ -void __lockfunc tty_lock(void) + +static void __lockfunc tty_lock_nested(struct tty_struct *tty, + unsigned int subclass) { - mutex_lock(&big_tty_mutex); + if (tty->magic != TTY_MAGIC) { + printk(KERN_ERR "L Bad %p\n", tty); + WARN_ON(1); + return; + } + tty_kref_get(tty); + mutex_lock_nested(&tty->legacy_mutex, subclass); +} + +void __lockfunc tty_lock(struct tty_struct *tty) +{ + return tty_lock_nested(tty, TTY_MUTEX_NORMAL); } EXPORT_SYMBOL(tty_lock); -void __lockfunc tty_unlock(void) +void __lockfunc tty_unlock(struct tty_struct *tty) { - mutex_unlock(&big_tty_mutex); + if (tty->magic != TTY_MAGIC) { + printk(KERN_ERR "U Bad %p\n", tty); + WARN_ON(1); + return; + } + mutex_unlock(&tty->legacy_mutex); + tty_kref_put(tty); } EXPORT_SYMBOL(tty_unlock); + +/* + * Getting the big tty mutex for a pair of ttys with lock ordering + * On a non pty/tty pair tty2 can be NULL which is just fine. + */ +void __lockfunc tty_lock_pair(struct tty_struct *tty, + struct tty_struct *tty2) +{ + if (tty < tty2) { + tty_lock(tty); + tty_lock_nested(tty2, TTY_MUTEX_NESTED); + } else { + if (tty2 && tty2 != tty) + tty_lock(tty2); + tty_lock_nested(tty, TTY_MUTEX_NESTED); + } +} +EXPORT_SYMBOL(tty_lock_pair); + +void __lockfunc tty_unlock_pair(struct tty_struct *tty, + struct tty_struct *tty2) +{ + tty_unlock(tty); + if (tty2 && tty2 != tty) + tty_unlock(tty2); +} +EXPORT_SYMBOL(tty_unlock_pair); diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 4e9d2b291f4a..a3ba776c574c 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -239,7 +239,7 @@ int tty_port_block_til_ready(struct tty_port *port, /* block if port is in the process of being closed */ if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { - wait_event_interruptible_tty(port->close_wait, + wait_event_interruptible_tty(tty, port->close_wait, !(port->flags & ASYNC_CLOSING)); if (port->flags & ASYNC_HUP_NOTIFY) return -EAGAIN; @@ -305,9 +305,9 @@ int tty_port_block_til_ready(struct tty_port *port, retval = -ERESTARTSYS; break; } - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } finish_wait(&port->open_wait, &wait); diff --git a/include/linux/tty.h b/include/linux/tty.h index 40b18d7ad929..7f9d7df9b131 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -268,6 +268,7 @@ struct tty_struct { struct mutex ldisc_mutex; struct tty_ldisc *ldisc; + struct mutex legacy_mutex; struct mutex termios_mutex; spinlock_t ctrl_lock; /* Termios values are protected by the termios mutex */ @@ -610,8 +611,12 @@ extern long vt_compat_ioctl(struct tty_struct *tty, /* tty_mutex.c */ /* functions for preparation of BKL removal */ -extern void __lockfunc tty_lock(void) __acquires(tty_lock); -extern void __lockfunc tty_unlock(void) __releases(tty_lock); +extern void __lockfunc tty_lock(struct tty_struct *tty); +extern void __lockfunc tty_unlock(struct tty_struct *tty); +extern void __lockfunc tty_lock_pair(struct tty_struct *tty, + struct tty_struct *tty2); +extern void __lockfunc tty_unlock_pair(struct tty_struct *tty, + struct tty_struct *tty2); /* * this shall be called only from where BTM is held (like close) @@ -626,9 +631,9 @@ extern void __lockfunc tty_unlock(void) __releases(tty_lock); static inline void tty_wait_until_sent_from_close(struct tty_struct *tty, long timeout) { - tty_unlock(); /* tty->ops->close holds the BTM, drop it while waiting */ + tty_unlock(tty); /* tty->ops->close holds the BTM, drop it while waiting */ tty_wait_until_sent(tty, timeout); - tty_lock(); + tty_lock(tty); } /* @@ -643,16 +648,16 @@ static inline void tty_wait_until_sent_from_close(struct tty_struct *tty, * * Do not use in new code. */ -#define wait_event_interruptible_tty(wq, condition) \ +#define wait_event_interruptible_tty(tty, wq, condition) \ ({ \ int __ret = 0; \ if (!(condition)) { \ - __wait_event_interruptible_tty(wq, condition, __ret); \ + __wait_event_interruptible_tty(tty, wq, condition, __ret); \ } \ __ret; \ }) -#define __wait_event_interruptible_tty(wq, condition, ret) \ +#define __wait_event_interruptible_tty(tty, wq, condition, ret) \ do { \ DEFINE_WAIT(__wait); \ \ @@ -661,9 +666,9 @@ do { \ if (condition) \ break; \ if (!signal_pending(current)) { \ - tty_unlock(); \ + tty_unlock(tty); \ schedule(); \ - tty_lock(); \ + tty_lock(tty); \ continue; \ } \ ret = -ERESTARTSYS; \ diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index d1820ff14aee..aa5d73b786ac 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -710,9 +710,9 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp) break; } - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); remove_wait_queue(&dev->wait, &wait); -- cgit v1.2.3 From c133482300113b3b71fa4a1fd2118531e765b36a Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sun, 20 May 2012 11:17:12 -0300 Subject: [media] V4L: Remove "_ACTIVE" from the selection target name definitions This patch drops the _ACTIVE part from the selection target names as a prerequisite to unify the selection target names across the subdev and regular video node API. The meaning of V4L2_SEL_TGT_*_ACTIVE and V4L2_SUBDEV_SEL_TGT_*_ACTUAL selection targets is logically the same. Different names add to confusion where both APIs are used in a single driver or an application. For some system configurations different names may lead to interoperability issues. For backwards compatibility V4L2_SEL_TGT_CROP_ACTIVE and V4L2_SEL_TGT_COMPOSE_ACTIVE are defined as aliases to V4L2_SEL_TGT_CROP and V4L2_SEL_TGT_COMPOSE. These aliases will be removed after deprecation period, according to Documentation/feature-removal-schedule.txt. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/selection-api.xml | 24 +++++++++++----------- .../DocBook/media/v4l/vidioc-g-selection.xml | 15 +++++++------- drivers/media/video/s5p-fimc/fimc-capture.c | 14 ++++++------- drivers/media/video/s5p-fimc/fimc-lite.c | 4 ++-- drivers/media/video/s5p-jpeg/jpeg-core.c | 4 ++-- drivers/media/video/s5p-tv/mixer_video.c | 8 ++++---- include/linux/videodev2.h | 8 ++++++-- 7 files changed, 41 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/selection-api.xml b/Documentation/DocBook/media/v4l/selection-api.xml index b299e4779354..ac013e50e0bd 100644 --- a/Documentation/DocBook/media/v4l/selection-api.xml +++ b/Documentation/DocBook/media/v4l/selection-api.xml @@ -91,7 +91,7 @@ top/left corner at position (0,0) . The rectangle's coordinates are expressed in pixels. The top left corner, width and height of the source rectangle, that is -the area actually sampled, is given by the V4L2_SEL_TGT_CROP_ACTIVE +the area actually sampled, is given by the V4L2_SEL_TGT_CROP target. It uses the same coordinate system as V4L2_SEL_TGT_CROP_BOUNDS . The active cropping area must lie completely inside the capture boundaries. The driver may further adjust the @@ -111,7 +111,7 @@ height are equal to the image size set by VIDIOC_S_FMT . The part of a buffer into which the image is inserted by the hardware is -controlled by the V4L2_SEL_TGT_COMPOSE_ACTIVE target. +controlled by the V4L2_SEL_TGT_COMPOSE target. The rectangle's coordinates are also expressed in the same coordinate system as the bounds rectangle. The composing rectangle must lie completely inside bounds rectangle. The driver must adjust the composing rectangle to fit to the @@ -125,7 +125,7 @@ bounding rectangle. The part of a buffer that is modified by the hardware is given by V4L2_SEL_TGT_COMPOSE_PADDED . It contains all pixels -defined using V4L2_SEL_TGT_COMPOSE_ACTIVE plus all +defined using V4L2_SEL_TGT_COMPOSE plus all padding data modified by hardware during insertion process. All pixels outside this rectangle must not be changed by the hardware. The content of pixels that lie inside the padded area but outside active area is @@ -153,7 +153,7 @@ specified using VIDIOC_S_FMT ioctl. The top left corner, width and height of the source rectangle, that is the area from which image date are processed by the hardware, is given by the - V4L2_SEL_TGT_CROP_ACTIVE . Its coordinates are expressed + V4L2_SEL_TGT_CROP . Its coordinates are expressed in in the same coordinate system as the bounds rectangle. The active cropping area must lie completely inside the crop boundaries and the driver may further adjust the requested size and/or position according to hardware @@ -165,7 +165,7 @@ bounding rectangle. The part of a video signal or graphics display where the image is inserted by the hardware is controlled by -V4L2_SEL_TGT_COMPOSE_ACTIVE target. The rectangle's coordinates +V4L2_SEL_TGT_COMPOSE target. The rectangle's coordinates are expressed in pixels. The composing rectangle must lie completely inside the bounds rectangle. The driver must adjust the area to fit to the bounding limits. Moreover, the driver can perform other adjustments according to @@ -184,7 +184,7 @@ such a padded area is driver-dependent feature not covered by this document. Driver developers are encouraged to keep padded rectangle equal to active one. The padded target is accessed by the V4L2_SEL_TGT_COMPOSE_PADDED identifier. It must contain all pixels from the -V4L2_SEL_TGT_COMPOSE_ACTIVE target. +V4L2_SEL_TGT_COMPOSE target.
@@ -193,8 +193,8 @@ V4L2_SEL_TGT_COMPOSE_ACTIVE target. Scaling control An application can detect if scaling is performed by comparing the width -and the height of rectangles obtained using V4L2_SEL_TGT_CROP_ACTIVE - and V4L2_SEL_TGT_COMPOSE_ACTIVE targets. If +and the height of rectangles obtained using V4L2_SEL_TGT_CROP + and V4L2_SEL_TGT_COMPOSE targets. If these are not equal then the scaling is applied. The application can compute the scaling ratios using these values. @@ -252,7 +252,7 @@ area) ret = ioctl(fd, &VIDIOC-G-SELECTION;, &sel); if (ret) exit(-1); - sel.target = V4L2_SEL_TGT_CROP_ACTIVE; + sel.target = V4L2_SEL_TGT_CROP; ret = ioctl(fd, &VIDIOC-S-SELECTION;, &sel); if (ret) exit(-1); @@ -281,7 +281,7 @@ area) r.left = sel.r.width / 4; r.top = sel.r.height / 4; sel.r = r; - sel.target = V4L2_SEL_TGT_COMPOSE_ACTIVE; + sel.target = V4L2_SEL_TGT_COMPOSE; sel.flags = V4L2_SEL_FLAG_LE; ret = ioctl(fd, &VIDIOC-S-SELECTION;, &sel); if (ret) @@ -298,11 +298,11 @@ V4L2_BUF_TYPE_VIDEO_OUTPUT for other devices &v4l2-selection; compose = { .type = V4L2_BUF_TYPE_VIDEO_OUTPUT, - .target = V4L2_SEL_TGT_COMPOSE_ACTIVE, + .target = V4L2_SEL_TGT_COMPOSE, }; &v4l2-selection; crop = { .type = V4L2_BUF_TYPE_VIDEO_OUTPUT, - .target = V4L2_SEL_TGT_CROP_ACTIVE, + .target = V4L2_SEL_TGT_CROP, }; double hscale, vscale; diff --git a/Documentation/DocBook/media/v4l/vidioc-g-selection.xml b/Documentation/DocBook/media/v4l/vidioc-g-selection.xml index bb04eff75f45..6376e57ff576 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-selection.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-selection.xml @@ -65,8 +65,8 @@ Do not use multiplanar buffers. Use V4L2_BUF_TYPE_VIDEO_CAPTURE . Use V4L2_BUF_TYPE_VIDEO_OUTPUT instead of V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE . The next step is setting the value of &v4l2-selection; target field -to V4L2_SEL_TGT_CROP_ACTIVE ( -V4L2_SEL_TGT_COMPOSE_ACTIVE ). Please refer to table V4L2_SEL_TGT_CROP ( +V4L2_SEL_TGT_COMPOSE ). Please refer to table or for additional targets. The flags and reserved fields of &v4l2-selection; are ignored and they must be filled @@ -86,8 +86,8 @@ use multiplanar buffers. Use V4L2_BUF_TYPE_VIDEO_CAPTURE . Use V4L2_BUF_TYPE_VIDEO_OUTPUT instead of V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE . The next step is setting the value of &v4l2-selection; target to -V4L2_SEL_TGT_CROP_ACTIVE ( -V4L2_SEL_TGT_COMPOSE_ACTIVE ). Please refer to table V4L2_SEL_TGT_CROP ( +V4L2_SEL_TGT_COMPOSE ). Please refer to table or for additional targets. The &v4l2-rect; r rectangle need to be set to the desired active area. Field &v4l2-selection; reserved @@ -161,7 +161,7 @@ exist no rectangle that satisfies the constraints. &cs-def; - V4L2_SEL_TGT_CROP_ACTIVE + V4L2_SEL_TGT_CROP 0x0000 The area that is currently cropped by hardware. @@ -176,7 +176,7 @@ exist no rectangle that satisfies the constraints. Limits for the cropping rectangle. - V4L2_SEL_TGT_COMPOSE_ACTIVE + V4L2_SEL_TGT_COMPOSE 0x0100 The area to which data is composed by hardware. @@ -193,7 +193,8 @@ exist no rectangle that satisfies the constraints. V4L2_SEL_TGT_COMPOSE_PADDED 0x0103 - The active area and all padding pixels that are inserted or modified by hardware. + The active area and all padding pixels that are inserted or + modified by hardware. diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 62ce5399c4cf..a3cd78d33913 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -655,7 +655,7 @@ static void fimc_capture_try_selection(struct fimc_ctx *ctx, r->left = r->top = 0; return; } - if (target == V4L2_SEL_TGT_COMPOSE_ACTIVE) { + if (target == V4L2_SEL_TGT_COMPOSE) { if (ctx->rotation != 90 && ctx->rotation != 270) align_h = 1; max_sc_h = min(SCALER_MAX_HRATIO, 1 << (ffs(sink->width) - 3)); @@ -682,7 +682,7 @@ static void fimc_capture_try_selection(struct fimc_ctx *ctx, rotate ? sink->f_height : sink->f_width); max_h = min_t(u32, FIMC_CAMIF_MAX_HEIGHT, sink->f_height); - if (target == V4L2_SEL_TGT_COMPOSE_ACTIVE) { + if (target == V4L2_SEL_TGT_COMPOSE) { min_w = min_t(u32, max_w, sink->f_width / max_sc_h); min_h = min_t(u32, max_h, sink->f_height / max_sc_v); if (rotate) { @@ -1147,9 +1147,9 @@ static int fimc_cap_g_selection(struct file *file, void *fh, s->r.height = f->o_height; return 0; - case V4L2_SEL_TGT_COMPOSE_ACTIVE: + case V4L2_SEL_TGT_COMPOSE: f = &ctx->d_frame; - case V4L2_SEL_TGT_CROP_ACTIVE: + case V4L2_SEL_TGT_CROP: s->r.left = f->offs_h; s->r.top = f->offs_v; s->r.width = f->width; @@ -1185,9 +1185,9 @@ static int fimc_cap_s_selection(struct file *file, void *fh, if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) return -EINVAL; - if (s->target == V4L2_SEL_TGT_COMPOSE_ACTIVE) + if (s->target == V4L2_SEL_TGT_COMPOSE) f = &ctx->d_frame; - else if (s->target == V4L2_SEL_TGT_CROP_ACTIVE) + else if (s->target == V4L2_SEL_TGT_CROP) f = &ctx->s_frame; else return -EINVAL; @@ -1483,7 +1483,7 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd, return -EINVAL; mutex_lock(&fimc->lock); - fimc_capture_try_selection(ctx, r, V4L2_SEL_TGT_CROP_ACTIVE); + fimc_capture_try_selection(ctx, r, V4L2_SEL_TGT_CROP); switch (sel->target) { case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: diff --git a/drivers/media/video/s5p-fimc/fimc-lite.c b/drivers/media/video/s5p-fimc/fimc-lite.c index 400d701aef04..52ede56e0758 100644 --- a/drivers/media/video/s5p-fimc/fimc-lite.c +++ b/drivers/media/video/s5p-fimc/fimc-lite.c @@ -871,7 +871,7 @@ static int fimc_lite_g_selection(struct file *file, void *fh, sel->r.height = f->f_height; return 0; - case V4L2_SEL_TGT_COMPOSE_ACTIVE: + case V4L2_SEL_TGT_COMPOSE: sel->r = f->rect; return 0; } @@ -888,7 +888,7 @@ static int fimc_lite_s_selection(struct file *file, void *fh, unsigned long flags; if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || - sel->target != V4L2_SEL_TGT_COMPOSE_ACTIVE) + sel->target != V4L2_SEL_TGT_COMPOSE) return -EINVAL; fimc_lite_try_compose(fimc, &rect); diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.c b/drivers/media/video/s5p-jpeg/jpeg-core.c index e40e79b33df6..95f23024b17d 100644 --- a/drivers/media/video/s5p-jpeg/jpeg-core.c +++ b/drivers/media/video/s5p-jpeg/jpeg-core.c @@ -824,10 +824,10 @@ static int s5p_jpeg_g_selection(struct file *file, void *priv, /* For JPEG blob active == default == bounds */ switch (s->target) { - case V4L2_SEL_TGT_CROP_ACTIVE: + case V4L2_SEL_TGT_CROP: case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_COMPOSE_ACTIVE: + case V4L2_SEL_TGT_COMPOSE: case V4L2_SEL_TGT_COMPOSE_DEFAULT: s->r.width = ctx->out_q.w; s->r.height = ctx->out_q.h; diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c index 33fde2a763ec..6c74b05d1f95 100644 --- a/drivers/media/video/s5p-tv/mixer_video.c +++ b/drivers/media/video/s5p-tv/mixer_video.c @@ -367,7 +367,7 @@ static int mxr_g_selection(struct file *file, void *fh, return -EINVAL; switch (s->target) { - case V4L2_SEL_TGT_CROP_ACTIVE: + case V4L2_SEL_TGT_CROP: s->r.left = geo->src.x_offset; s->r.top = geo->src.y_offset; s->r.width = geo->src.width; @@ -380,7 +380,7 @@ static int mxr_g_selection(struct file *file, void *fh, s->r.width = geo->src.full_width; s->r.height = geo->src.full_height; break; - case V4L2_SEL_TGT_COMPOSE_ACTIVE: + case V4L2_SEL_TGT_COMPOSE: case V4L2_SEL_TGT_COMPOSE_PADDED: s->r.left = geo->dst.x_offset; s->r.top = geo->dst.y_offset; @@ -449,11 +449,11 @@ static int mxr_s_selection(struct file *file, void *fh, res.height = geo->dst.full_height; break; - case V4L2_SEL_TGT_CROP_ACTIVE: + case V4L2_SEL_TGT_CROP: target = &geo->src; stage = MXR_GEOMETRY_CROP; break; - case V4L2_SEL_TGT_COMPOSE_ACTIVE: + case V4L2_SEL_TGT_COMPOSE: case V4L2_SEL_TGT_COMPOSE_PADDED: target = &geo->dst; stage = MXR_GEOMETRY_COMPOSE; diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index a61edb353273..ac1ad33ba3e0 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -767,13 +767,13 @@ struct v4l2_crop { /* Selection targets */ /* Current cropping area */ -#define V4L2_SEL_TGT_CROP_ACTIVE 0x0000 +#define V4L2_SEL_TGT_CROP 0x0000 /* Default cropping area */ #define V4L2_SEL_TGT_CROP_DEFAULT 0x0001 /* Cropping bounds */ #define V4L2_SEL_TGT_CROP_BOUNDS 0x0002 /* Current composing area */ -#define V4L2_SEL_TGT_COMPOSE_ACTIVE 0x0100 +#define V4L2_SEL_TGT_COMPOSE 0x0100 /* Default composing area */ #define V4L2_SEL_TGT_COMPOSE_DEFAULT 0x0101 /* Composing bounds */ @@ -781,6 +781,10 @@ struct v4l2_crop { /* Current composing area plus all padding pixels */ #define V4L2_SEL_TGT_COMPOSE_PADDED 0x0103 +/* Backward compatibility definitions */ +#define V4L2_SEL_TGT_CROP_ACTIVE V4L2_SEL_TGT_CROP +#define V4L2_SEL_TGT_COMPOSE_ACTIVE V4L2_SEL_TGT_COMPOSE + /** * struct v4l2_selection - selection info * @type: buffer type (do not use *_MPLANE types) -- cgit v1.2.3 From 1ec0ed083988ae433305d7f4158fda8c3a1a23b9 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 17 May 2012 17:50:45 -0300 Subject: [media] v4l: Remove "_ACTUAL" from subdev selection API target definition names The string "_ACTUAL" does not say anything more about the target names. Drop it. V4L2 selection API was changed by "V4L: Remove "_ACTIVE" from the selection target name definitions" by Sylwester Nawrocki. This patch does the same for the V4L2 subdev API. Signed-off-by: Sakari Ailus Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/dev-subdev.xml | 28 +++++++++++----------- .../media/v4l/vidioc-subdev-g-selection.xml | 12 +++++----- drivers/media/video/omap3isp/ispccdc.c | 4 ++-- drivers/media/video/omap3isp/isppreview.c | 4 ++-- drivers/media/video/omap3isp/ispresizer.c | 4 ++-- drivers/media/video/smiapp/smiapp-core.c | 22 ++++++++--------- drivers/media/video/v4l2-subdev.c | 4 ++-- include/linux/v4l2-subdev.h | 9 +++++-- 8 files changed, 46 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/dev-subdev.xml b/Documentation/DocBook/media/v4l/dev-subdev.xml index 4afcbbec5eda..e88d5ea8f826 100644 --- a/Documentation/DocBook/media/v4l/dev-subdev.xml +++ b/Documentation/DocBook/media/v4l/dev-subdev.xml @@ -289,9 +289,9 @@ &v4l2-rect; by the coordinates of the top left corner and the rectangle size. Both the coordinates and sizes are expressed in pixels. - As for pad formats, drivers store try and active - rectangles for the selection targets of ACTUAL type . + As for pad formats, drivers store try and active rectangles for + the selection targets . On sink pads, cropping is applied relative to the current pad format. The pad format represents the image size as @@ -308,7 +308,7 @@ Scaling support is optional. When supported by a subdev, the crop rectangle on the subdev's sink pad is scaled to the size configured using the &VIDIOC-SUBDEV-S-SELECTION; IOCTL - using V4L2_SUBDEV_SEL_COMPOSE_ACTUAL + using V4L2_SUBDEV_SEL_TGT_COMPOSE selection target on the same pad. If the subdev supports scaling but not composing, the top and left values are not used and must always be set to zero. @@ -333,22 +333,22 @@ Types of selection targets
- ACTUAL targets + Actual targets - ACTUAL targets reflect the actual hardware configuration - at any point of time. There is a BOUNDS target - corresponding to every ACTUAL. + Actual targets (without a postfix) reflect the actual + hardware configuration at any point of time. There is a BOUNDS + target corresponding to every actual target.
BOUNDS targets - BOUNDS targets is the smallest rectangle that contains - all valid ACTUAL rectangles. It may not be possible to set the - ACTUAL rectangle as large as the BOUNDS rectangle, however. - This may be because e.g. a sensor's pixel array is not - rectangular but cross-shaped or round. The maximum size may - also be smaller than the BOUNDS rectangle. + BOUNDS targets is the smallest rectangle that contains all + valid actual rectangles. It may not be possible to set the actual + rectangle as large as the BOUNDS rectangle, however. This may be + because e.g. a sensor's pixel array is not rectangular but + cross-shaped or round. The maximum size may also be smaller than the + BOUNDS rectangle.
diff --git a/Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml b/Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml index 208e9f0da3f3..4c44808ab25c 100644 --- a/Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml +++ b/Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml @@ -72,10 +72,10 @@
Types of selection targets - There are two types of selection targets: actual and bounds. - The ACTUAL targets are the targets which configure the hardware. - The BOUNDS target will return a rectangle that contain all - possible ACTUAL rectangles. + There are two types of selection targets: actual and bounds. The + actual targets are the targets which configure the hardware. The BOUNDS + target will return a rectangle that contain all possible actual + rectangles.
@@ -93,7 +93,7 @@ &cs-def; - V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL + V4L2_SUBDEV_SEL_TGT_CROP 0x0000 Actual crop. Defines the cropping performed by the processing step. @@ -104,7 +104,7 @@ Bounds of the crop rectangle. - V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL + V4L2_SUBDEV_SEL_TGT_COMPOSE 0x0100 Actual compose rectangle. Used to configure scaling on sink pads and composition on source pads. diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c index 7e32331b60fb..f19774f8396a 100644 --- a/drivers/media/video/omap3isp/ispccdc.c +++ b/drivers/media/video/omap3isp/ispccdc.c @@ -2024,7 +2024,7 @@ static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ccdc_try_crop(ccdc, format, &sel->r); break; - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_CROP: sel->r = *__ccdc_get_crop(ccdc, fh, sel->which); break; @@ -2052,7 +2052,7 @@ static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL || + if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP || sel->pad != CCDC_PAD_SOURCE_OF) return -EINVAL; diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c index 8a4935ecc655..1086f6a9ff76 100644 --- a/drivers/media/video/omap3isp/isppreview.c +++ b/drivers/media/video/omap3isp/isppreview.c @@ -1960,7 +1960,7 @@ static int preview_get_selection(struct v4l2_subdev *sd, preview_try_crop(prev, format, &sel->r); break; - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_CROP: sel->r = *__preview_get_crop(prev, fh, sel->which); break; @@ -1988,7 +1988,7 @@ static int preview_set_selection(struct v4l2_subdev *sd, struct isp_prev_device *prev = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL || + if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP || sel->pad != PREV_PAD_SINK) return -EINVAL; diff --git a/drivers/media/video/omap3isp/ispresizer.c b/drivers/media/video/omap3isp/ispresizer.c index 14041c9c8643..945665295571 100644 --- a/drivers/media/video/omap3isp/ispresizer.c +++ b/drivers/media/video/omap3isp/ispresizer.c @@ -1259,7 +1259,7 @@ static int resizer_get_selection(struct v4l2_subdev *sd, resizer_calc_ratios(res, &sel->r, format_source, &ratio); break; - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_CROP: sel->r = *__resizer_get_crop(res, fh, sel->which); resizer_calc_ratios(res, &sel->r, format_source, &ratio); break; @@ -1293,7 +1293,7 @@ static int resizer_set_selection(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *format_sink, *format_source; struct resizer_ratio ratio; - if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL || + if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP || sel->pad != RESZ_PAD_SINK) return -EINVAL; diff --git a/drivers/media/video/smiapp/smiapp-core.c b/drivers/media/video/smiapp/smiapp-core.c index e8c93c89265a..37622bb6c667 100644 --- a/drivers/media/video/smiapp/smiapp-core.c +++ b/drivers/media/video/smiapp/smiapp-core.c @@ -1630,7 +1630,7 @@ static void smiapp_propagate(struct v4l2_subdev *subdev, smiapp_get_crop_compose(subdev, fh, crops, &comp, which); switch (target) { - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_CROP: comp->width = crops[SMIAPP_PAD_SINK]->width; comp->height = crops[SMIAPP_PAD_SINK]->height; if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { @@ -1646,7 +1646,7 @@ static void smiapp_propagate(struct v4l2_subdev *subdev, } } /* Fall through */ - case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_COMPOSE: *crops[SMIAPP_PAD_SRC] = *comp; break; default: @@ -1722,7 +1722,7 @@ static int smiapp_set_format(struct v4l2_subdev *subdev, if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) ssd->sink_fmt = *crops[ssd->sink_pad]; smiapp_propagate(subdev, fh, fmt->which, - V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL); + V4L2_SUBDEV_SEL_TGT_CROP); mutex_unlock(&sensor->mutex); @@ -1957,7 +1957,7 @@ static int smiapp_set_compose(struct v4l2_subdev *subdev, *comp = sel->r; smiapp_propagate(subdev, fh, sel->which, - V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL); + V4L2_SUBDEV_SEL_TGT_COMPOSE); if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) return smiapp_update_mode(sensor); @@ -1973,7 +1973,7 @@ static int __smiapp_sel_supported(struct v4l2_subdev *subdev, /* We only implement crop in three places. */ switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_CROP: case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: if (ssd == sensor->pixel_array && sel->pad == SMIAPP_PA_PAD_SRC) @@ -1987,7 +1987,7 @@ static int __smiapp_sel_supported(struct v4l2_subdev *subdev, == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) return 0; return -EINVAL; - case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_COMPOSE: case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: if (sel->pad == ssd->source_pad) return -EINVAL; @@ -2050,7 +2050,7 @@ static int smiapp_set_crop(struct v4l2_subdev *subdev, if (ssd != sensor->pixel_array && sel->pad == SMIAPP_PAD_SINK) smiapp_propagate(subdev, fh, sel->which, - V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL); + V4L2_SUBDEV_SEL_TGT_CROP); return 0; } @@ -2096,11 +2096,11 @@ static int __smiapp_get_selection(struct v4l2_subdev *subdev, sel->r = *comp; } break; - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_CROP: case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: sel->r = *crops[sel->pad]; break; - case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_COMPOSE: sel->r = *comp; break; } @@ -2147,10 +2147,10 @@ static int smiapp_set_selection(struct v4l2_subdev *subdev, sel->r.height); switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_CROP: ret = smiapp_set_crop(subdev, fh, sel); break; - case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_COMPOSE: ret = smiapp_set_compose(subdev, fh, sel); break; default: diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index db6e859b93d4..cd86f0c3ec74 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -245,7 +245,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(&sel, 0, sizeof(sel)); sel.which = crop->which; sel.pad = crop->pad; - sel.target = V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL; + sel.target = V4L2_SUBDEV_SEL_TGT_CROP; rval = v4l2_subdev_call( sd, pad, get_selection, subdev_fh, &sel); @@ -274,7 +274,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(&sel, 0, sizeof(sel)); sel.which = crop->which; sel.pad = crop->pad; - sel.target = V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL; + sel.target = V4L2_SUBDEV_SEL_TGT_CROP; sel.r = crop->rect; rval = v4l2_subdev_call( diff --git a/include/linux/v4l2-subdev.h b/include/linux/v4l2-subdev.h index 812019ee1e06..3cbe6889fcb5 100644 --- a/include/linux/v4l2-subdev.h +++ b/include/linux/v4l2-subdev.h @@ -128,14 +128,19 @@ struct v4l2_subdev_frame_interval_enum { #define V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG (1 << 2) /* active cropping area */ -#define V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL 0x0000 +#define V4L2_SUBDEV_SEL_TGT_CROP 0x0000 /* cropping bounds */ #define V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS 0x0002 /* current composing area */ -#define V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL 0x0100 +#define V4L2_SUBDEV_SEL_TGT_COMPOSE 0x0100 /* composing bounds */ #define V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS 0x0102 +/* backward compatibility definitions */ +#define V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL \ + V4L2_SUBDEV_SEL_TGT_CROP +#define V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL \ + V4L2_SUBDEV_SEL_TGT_COMPOSE /** * struct v4l2_subdev_selection - selection info -- cgit v1.2.3 From 5689b28890f4a7c4e12290dbf2c29a9d23047335 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 18 May 2012 09:31:18 -0300 Subject: [media] v4l: Unify selection targets across V4L2 and V4L2 subdev interfaces Change the users of V4L2_SUBDEV_SEL_TGT_* targets to use V4L2_SEL_TGT_* instead. The common definitions are moved to a new header file, include/linux/v4l2-common.h. Signed-off-by: Sakari Ailus Signed-off-by: Sylwester Nawrocki Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/ispccdc.c | 6 +-- drivers/media/video/omap3isp/isppreview.c | 6 +-- drivers/media/video/omap3isp/ispresizer.c | 6 +-- drivers/media/video/s5p-fimc/fimc-capture.c | 18 ++++----- drivers/media/video/s5p-fimc/fimc-lite.c | 11 +++--- drivers/media/video/smiapp/smiapp-core.c | 30 +++++++-------- drivers/media/video/v4l2-subdev.c | 4 +- include/linux/v4l2-common.h | 57 +++++++++++++++++++++++++++++ include/linux/v4l2-subdev.h | 19 ++-------- include/linux/videodev2.h | 25 ++----------- 10 files changed, 103 insertions(+), 79 deletions(-) create mode 100644 include/linux/v4l2-common.h (limited to 'include') diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c index f19774f8396a..82df7a06dc36 100644 --- a/drivers/media/video/omap3isp/ispccdc.c +++ b/drivers/media/video/omap3isp/ispccdc.c @@ -2014,7 +2014,7 @@ static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, return -EINVAL; switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_BOUNDS: sel->r.left = 0; sel->r.top = 0; sel->r.width = INT_MAX; @@ -2024,7 +2024,7 @@ static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ccdc_try_crop(ccdc, format, &sel->r); break; - case V4L2_SUBDEV_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP: sel->r = *__ccdc_get_crop(ccdc, fh, sel->which); break; @@ -2052,7 +2052,7 @@ static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP || + if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != CCDC_PAD_SOURCE_OF) return -EINVAL; diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c index 1086f6a9ff76..6fa70f4e8ea4 100644 --- a/drivers/media/video/omap3isp/isppreview.c +++ b/drivers/media/video/omap3isp/isppreview.c @@ -1949,7 +1949,7 @@ static int preview_get_selection(struct v4l2_subdev *sd, return -EINVAL; switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_BOUNDS: sel->r.left = 0; sel->r.top = 0; sel->r.width = INT_MAX; @@ -1960,7 +1960,7 @@ static int preview_get_selection(struct v4l2_subdev *sd, preview_try_crop(prev, format, &sel->r); break; - case V4L2_SUBDEV_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP: sel->r = *__preview_get_crop(prev, fh, sel->which); break; @@ -1988,7 +1988,7 @@ static int preview_set_selection(struct v4l2_subdev *sd, struct isp_prev_device *prev = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP || + if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != PREV_PAD_SINK) return -EINVAL; diff --git a/drivers/media/video/omap3isp/ispresizer.c b/drivers/media/video/omap3isp/ispresizer.c index 945665295571..ae17d917f77b 100644 --- a/drivers/media/video/omap3isp/ispresizer.c +++ b/drivers/media/video/omap3isp/ispresizer.c @@ -1249,7 +1249,7 @@ static int resizer_get_selection(struct v4l2_subdev *sd, sel->which); switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_BOUNDS: sel->r.left = 0; sel->r.top = 0; sel->r.width = INT_MAX; @@ -1259,7 +1259,7 @@ static int resizer_get_selection(struct v4l2_subdev *sd, resizer_calc_ratios(res, &sel->r, format_source, &ratio); break; - case V4L2_SUBDEV_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP: sel->r = *__resizer_get_crop(res, fh, sel->which); resizer_calc_ratios(res, &sel->r, format_source, &ratio); break; @@ -1293,7 +1293,7 @@ static int resizer_set_selection(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *format_sink, *format_source; struct resizer_ratio ratio; - if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP || + if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != RESZ_PAD_SINK) return -EINVAL; diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index a3cd78d33913..521e3715b9ee 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -1429,9 +1429,9 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd, mutex_lock(&fimc->lock); switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: f = &ctx->d_frame; - case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_BOUNDS: r->width = f->o_width; r->height = f->o_height; r->left = 0; @@ -1439,10 +1439,10 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd, mutex_unlock(&fimc->lock); return 0; - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SEL_TGT_CROP: try_sel = v4l2_subdev_get_try_crop(fh, sel->pad); break; - case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + case V4L2_SEL_TGT_COMPOSE: try_sel = v4l2_subdev_get_try_compose(fh, sel->pad); f = &ctx->d_frame; break; @@ -1486,9 +1486,9 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd, fimc_capture_try_selection(ctx, r, V4L2_SEL_TGT_CROP); switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: f = &ctx->d_frame; - case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_BOUNDS: r->width = f->o_width; r->height = f->o_height; r->left = 0; @@ -1496,10 +1496,10 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd, mutex_unlock(&fimc->lock); return 0; - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SEL_TGT_CROP: try_sel = v4l2_subdev_get_try_crop(fh, sel->pad); break; - case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + case V4L2_SEL_TGT_COMPOSE: try_sel = v4l2_subdev_get_try_compose(fh, sel->pad); f = &ctx->d_frame; break; @@ -1515,7 +1515,7 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd, set_frame_crop(f, r->left, r->top, r->width, r->height); set_bit(ST_CAPT_APPLY_CFG, &fimc->state); spin_unlock_irqrestore(&fimc->slock, flags); - if (sel->target == V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL) + if (sel->target == V4L2_SEL_TGT_COMPOSE) ctx->state |= FIMC_COMPOSE; } diff --git a/drivers/media/video/s5p-fimc/fimc-lite.c b/drivers/media/video/s5p-fimc/fimc-lite.c index 52ede56e0758..8785089c4460 100644 --- a/drivers/media/video/s5p-fimc/fimc-lite.c +++ b/drivers/media/video/s5p-fimc/fimc-lite.c @@ -1086,9 +1086,9 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd, struct fimc_lite *fimc = v4l2_get_subdevdata(sd); struct flite_frame *f = &fimc->inp_frame; - if ((sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL && - sel->target != V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS) || - sel->pad != FLITE_SD_PAD_SINK) + if ((sel->target != V4L2_SEL_TGT_CROP && + sel->target != V4L2_SEL_TGT_CROP_BOUNDS) || + sel->pad != FLITE_SD_PAD_SINK) return -EINVAL; if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { @@ -1097,7 +1097,7 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd, } mutex_lock(&fimc->lock); - if (sel->target == V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL) { + if (sel->target == V4L2_SEL_TGT_CROP) { sel->r = f->rect; } else { sel->r.left = 0; @@ -1122,8 +1122,7 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd, struct flite_frame *f = &fimc->inp_frame; int ret = 0; - if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL || - sel->pad != FLITE_SD_PAD_SINK) + if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != FLITE_SD_PAD_SINK) return -EINVAL; mutex_lock(&fimc->lock); diff --git a/drivers/media/video/smiapp/smiapp-core.c b/drivers/media/video/smiapp/smiapp-core.c index 37622bb6c667..9bbb5d3f003b 100644 --- a/drivers/media/video/smiapp/smiapp-core.c +++ b/drivers/media/video/smiapp/smiapp-core.c @@ -1630,7 +1630,7 @@ static void smiapp_propagate(struct v4l2_subdev *subdev, smiapp_get_crop_compose(subdev, fh, crops, &comp, which); switch (target) { - case V4L2_SUBDEV_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP: comp->width = crops[SMIAPP_PAD_SINK]->width; comp->height = crops[SMIAPP_PAD_SINK]->height; if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { @@ -1646,7 +1646,7 @@ static void smiapp_propagate(struct v4l2_subdev *subdev, } } /* Fall through */ - case V4L2_SUBDEV_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE: *crops[SMIAPP_PAD_SRC] = *comp; break; default: @@ -1722,7 +1722,7 @@ static int smiapp_set_format(struct v4l2_subdev *subdev, if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) ssd->sink_fmt = *crops[ssd->sink_pad]; smiapp_propagate(subdev, fh, fmt->which, - V4L2_SUBDEV_SEL_TGT_CROP); + V4L2_SEL_TGT_CROP); mutex_unlock(&sensor->mutex); @@ -1957,7 +1957,7 @@ static int smiapp_set_compose(struct v4l2_subdev *subdev, *comp = sel->r; smiapp_propagate(subdev, fh, sel->which, - V4L2_SUBDEV_SEL_TGT_COMPOSE); + V4L2_SEL_TGT_COMPOSE); if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) return smiapp_update_mode(sensor); @@ -1973,8 +1973,8 @@ static int __smiapp_sel_supported(struct v4l2_subdev *subdev, /* We only implement crop in three places. */ switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_CROP: - case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP_BOUNDS: if (ssd == sensor->pixel_array && sel->pad == SMIAPP_PA_PAD_SRC) return 0; @@ -1987,8 +1987,8 @@ static int __smiapp_sel_supported(struct v4l2_subdev *subdev, == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) return 0; return -EINVAL; - case V4L2_SUBDEV_SEL_TGT_COMPOSE: - case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: if (sel->pad == ssd->source_pad) return -EINVAL; if (ssd == sensor->binner) @@ -2050,7 +2050,7 @@ static int smiapp_set_crop(struct v4l2_subdev *subdev, if (ssd != sensor->pixel_array && sel->pad == SMIAPP_PAD_SINK) smiapp_propagate(subdev, fh, sel->which, - V4L2_SUBDEV_SEL_TGT_CROP); + V4L2_SEL_TGT_CROP); return 0; } @@ -2084,7 +2084,7 @@ static int __smiapp_get_selection(struct v4l2_subdev *subdev, } switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_BOUNDS: if (ssd == sensor->pixel_array) { sel->r.width = sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; @@ -2096,11 +2096,11 @@ static int __smiapp_get_selection(struct v4l2_subdev *subdev, sel->r = *comp; } break; - case V4L2_SUBDEV_SEL_TGT_CROP: - case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: sel->r = *crops[sel->pad]; break; - case V4L2_SUBDEV_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE: sel->r = *comp; break; } @@ -2147,10 +2147,10 @@ static int smiapp_set_selection(struct v4l2_subdev *subdev, sel->r.height); switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP: ret = smiapp_set_crop(subdev, fh, sel); break; - case V4L2_SUBDEV_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE: ret = smiapp_set_compose(subdev, fh, sel); break; default: diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index cd86f0c3ec74..9182f81deb5b 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -245,7 +245,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(&sel, 0, sizeof(sel)); sel.which = crop->which; sel.pad = crop->pad; - sel.target = V4L2_SUBDEV_SEL_TGT_CROP; + sel.target = V4L2_SEL_TGT_CROP; rval = v4l2_subdev_call( sd, pad, get_selection, subdev_fh, &sel); @@ -274,7 +274,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(&sel, 0, sizeof(sel)); sel.which = crop->which; sel.pad = crop->pad; - sel.target = V4L2_SUBDEV_SEL_TGT_CROP; + sel.target = V4L2_SEL_TGT_CROP; sel.r = crop->rect; rval = v4l2_subdev_call( diff --git a/include/linux/v4l2-common.h b/include/linux/v4l2-common.h new file mode 100644 index 000000000000..e85bf15b5994 --- /dev/null +++ b/include/linux/v4l2-common.h @@ -0,0 +1,57 @@ +/* + * include/linux/v4l2-common.h + * + * Common V4L2 and V4L2 subdev definitions. + * + * Users are advised to #include this file either through videodev2.h + * (V4L2) or through v4l2-subdev.h (V4L2 subdev) rather than to refer + * to this file directly. + * + * Copyright (C) 2012 Nokia Corporation + * Contact: Sakari Ailus + * + * 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 __V4L2_COMMON__ +#define __V4L2_COMMON__ + +/* Selection target definitions */ + +/* Current cropping area */ +#define V4L2_SEL_TGT_CROP 0x0000 +/* Default cropping area */ +#define V4L2_SEL_TGT_CROP_DEFAULT 0x0001 +/* Cropping bounds */ +#define V4L2_SEL_TGT_CROP_BOUNDS 0x0002 +/* Current composing area */ +#define V4L2_SEL_TGT_COMPOSE 0x0100 +/* Default composing area */ +#define V4L2_SEL_TGT_COMPOSE_DEFAULT 0x0101 +/* Composing bounds */ +#define V4L2_SEL_TGT_COMPOSE_BOUNDS 0x0102 +/* Current composing area plus all padding pixels */ +#define V4L2_SEL_TGT_COMPOSE_PADDED 0x0103 + +/* Backward compatibility definitions */ +#define V4L2_SEL_TGT_CROP_ACTIVE V4L2_SEL_TGT_CROP +#define V4L2_SEL_TGT_COMPOSE_ACTIVE V4L2_SEL_TGT_COMPOSE +#define V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL \ + V4L2_SEL_TGT_CROP +#define V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL \ + V4L2_SEL_TGT_COMPOSE + +#endif /* __V4L2_COMMON__ */ diff --git a/include/linux/v4l2-subdev.h b/include/linux/v4l2-subdev.h index 3cbe6889fcb5..1d7d45739260 100644 --- a/include/linux/v4l2-subdev.h +++ b/include/linux/v4l2-subdev.h @@ -25,6 +25,7 @@ #include #include +#include #include /** @@ -127,27 +128,13 @@ struct v4l2_subdev_frame_interval_enum { #define V4L2_SUBDEV_SEL_FLAG_SIZE_LE (1 << 1) #define V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG (1 << 2) -/* active cropping area */ -#define V4L2_SUBDEV_SEL_TGT_CROP 0x0000 -/* cropping bounds */ -#define V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS 0x0002 -/* current composing area */ -#define V4L2_SUBDEV_SEL_TGT_COMPOSE 0x0100 -/* composing bounds */ -#define V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS 0x0102 - -/* backward compatibility definitions */ -#define V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL \ - V4L2_SUBDEV_SEL_TGT_CROP -#define V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL \ - V4L2_SUBDEV_SEL_TGT_COMPOSE - /** * struct v4l2_subdev_selection - selection info * * @which: either V4L2_SUBDEV_FORMAT_ACTIVE or V4L2_SUBDEV_FORMAT_TRY * @pad: pad number, as reported by the media API - * @target: selection target, used to choose one of possible rectangles + * @target: Selection target, used to choose one of possible rectangles, + * defined in v4l2-common.h; V4L2_SEL_TGT_* . * @flags: constraint flags * @r: coordinates of the selection window * @reserved: for future use, set to zero for now diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index ac1ad33ba3e0..7fdb8710c831 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -64,6 +64,7 @@ #include #include #include +#include /* * Common stuff for both V4L1 and V4L2 @@ -764,31 +765,11 @@ struct v4l2_crop { #define V4L2_SEL_FLAG_GE 0x00000001 #define V4L2_SEL_FLAG_LE 0x00000002 -/* Selection targets */ - -/* Current cropping area */ -#define V4L2_SEL_TGT_CROP 0x0000 -/* Default cropping area */ -#define V4L2_SEL_TGT_CROP_DEFAULT 0x0001 -/* Cropping bounds */ -#define V4L2_SEL_TGT_CROP_BOUNDS 0x0002 -/* Current composing area */ -#define V4L2_SEL_TGT_COMPOSE 0x0100 -/* Default composing area */ -#define V4L2_SEL_TGT_COMPOSE_DEFAULT 0x0101 -/* Composing bounds */ -#define V4L2_SEL_TGT_COMPOSE_BOUNDS 0x0102 -/* Current composing area plus all padding pixels */ -#define V4L2_SEL_TGT_COMPOSE_PADDED 0x0103 - -/* Backward compatibility definitions */ -#define V4L2_SEL_TGT_CROP_ACTIVE V4L2_SEL_TGT_CROP -#define V4L2_SEL_TGT_COMPOSE_ACTIVE V4L2_SEL_TGT_COMPOSE - /** * struct v4l2_selection - selection info * @type: buffer type (do not use *_MPLANE types) - * @target: selection target, used to choose one of possible rectangles + * @target: Selection target, used to choose one of possible rectangles; + * defined in v4l2-common.h; V4L2_SEL_TGT_* . * @flags: constraints flags * @r: coordinates of selection window * @reserved: for future use, rounds structure size to 64 bytes, set to zero -- cgit v1.2.3 From 563df3d0bc2ca103e5ddb76c8b7b3386ed2da0d6 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 13 Jun 2012 16:01:10 -0300 Subject: [media] v4l: Unify selection flags Unify flags on the selection interfaces on V4L2 and V4L2 subdev. Flags are very similar to targets in this case: there are more similarities than differences between the two interfaces. Signed-off-by: Sakari Ailus Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/ispccdc.c | 2 +- drivers/media/video/omap3isp/isppreview.c | 2 +- drivers/media/video/smiapp/smiapp-core.c | 10 +++++----- include/linux/v4l2-common.h | 20 +++++++++++++++++--- include/linux/v4l2-subdev.h | 6 +----- include/linux/videodev2.h | 6 +----- 6 files changed, 26 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c index 82df7a06dc36..f1220d3d4970 100644 --- a/drivers/media/video/omap3isp/ispccdc.c +++ b/drivers/media/video/omap3isp/ispccdc.c @@ -2064,7 +2064,7 @@ static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, * pad. If the KEEP_CONFIG flag is set, just return the current crop * rectangle. */ - if (sel->flags & V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG) { + if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) { sel->r = *__ccdc_get_crop(ccdc, fh, sel->which); return 0; } diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c index 6fa70f4e8ea4..99d5cc4fd623 100644 --- a/drivers/media/video/omap3isp/isppreview.c +++ b/drivers/media/video/omap3isp/isppreview.c @@ -2000,7 +2000,7 @@ static int preview_set_selection(struct v4l2_subdev *sd, * pad. If the KEEP_CONFIG flag is set, just return the current crop * rectangle. */ - if (sel->flags & V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG) { + if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) { sel->r = *__preview_get_crop(prev, fh, sel->which); return 0; } diff --git a/drivers/media/video/smiapp/smiapp-core.c b/drivers/media/video/smiapp/smiapp-core.c index 9bbb5d3f003b..8a5d4f7932a0 100644 --- a/drivers/media/video/smiapp/smiapp-core.c +++ b/drivers/media/video/smiapp/smiapp-core.c @@ -38,9 +38,9 @@ #include "smiapp.h" -#define SMIAPP_ALIGN_DIM(dim, flags) \ - ((flags) & V4L2_SUBDEV_SEL_FLAG_SIZE_GE \ - ? ALIGN((dim), 2) \ +#define SMIAPP_ALIGN_DIM(dim, flags) \ + ((flags) & V4L2_SEL_FLAG_GE \ + ? ALIGN((dim), 2) \ : (dim) & ~1) /* @@ -1747,14 +1747,14 @@ static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w, h &= ~1; ask_h &= ~1; - if (flags & V4L2_SUBDEV_SEL_FLAG_SIZE_GE) { + if (flags & V4L2_SEL_FLAG_GE) { if (w < ask_w) val -= SCALING_GOODNESS; if (h < ask_h) val -= SCALING_GOODNESS; } - if (flags & V4L2_SUBDEV_SEL_FLAG_SIZE_LE) { + if (flags & V4L2_SEL_FLAG_LE) { if (w > ask_w) val -= SCALING_GOODNESS; if (h > ask_h) diff --git a/include/linux/v4l2-common.h b/include/linux/v4l2-common.h index e85bf15b5994..0fa8b64c3cdb 100644 --- a/include/linux/v4l2-common.h +++ b/include/linux/v4l2-common.h @@ -29,7 +29,11 @@ #ifndef __V4L2_COMMON__ #define __V4L2_COMMON__ -/* Selection target definitions */ +/* + * + * Selection interface definitions + * + */ /* Current cropping area */ #define V4L2_SEL_TGT_CROP 0x0000 @@ -46,7 +50,7 @@ /* Current composing area plus all padding pixels */ #define V4L2_SEL_TGT_COMPOSE_PADDED 0x0103 -/* Backward compatibility definitions */ +/* Backward compatibility target definitions --- to be removed. */ #define V4L2_SEL_TGT_CROP_ACTIVE V4L2_SEL_TGT_CROP #define V4L2_SEL_TGT_COMPOSE_ACTIVE V4L2_SEL_TGT_COMPOSE #define V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL \ @@ -54,4 +58,14 @@ #define V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL \ V4L2_SEL_TGT_COMPOSE -#endif /* __V4L2_COMMON__ */ +/* Selection flags */ +#define V4L2_SEL_FLAG_GE (1 << 0) +#define V4L2_SEL_FLAG_LE (1 << 1) +#define V4L2_SEL_FLAG_KEEP_CONFIG (1 << 2) + +/* Backward compatibility flag definitions --- to be removed. */ +#define V4L2_SUBDEV_SEL_FLAG_SIZE_GE V4L2_SEL_FLAG_GE +#define V4L2_SUBDEV_SEL_FLAG_SIZE_LE V4L2_SEL_FLAG_LE +#define V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG V4L2_SEL_FLAG_KEEP_CONFIG + +#endif /* __V4L2_COMMON__ */ diff --git a/include/linux/v4l2-subdev.h b/include/linux/v4l2-subdev.h index 1d7d45739260..8c57ee9872bb 100644 --- a/include/linux/v4l2-subdev.h +++ b/include/linux/v4l2-subdev.h @@ -124,10 +124,6 @@ struct v4l2_subdev_frame_interval_enum { __u32 reserved[9]; }; -#define V4L2_SUBDEV_SEL_FLAG_SIZE_GE (1 << 0) -#define V4L2_SUBDEV_SEL_FLAG_SIZE_LE (1 << 1) -#define V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG (1 << 2) - /** * struct v4l2_subdev_selection - selection info * @@ -135,7 +131,7 @@ struct v4l2_subdev_frame_interval_enum { * @pad: pad number, as reported by the media API * @target: Selection target, used to choose one of possible rectangles, * defined in v4l2-common.h; V4L2_SEL_TGT_* . - * @flags: constraint flags + * @flags: constraint flags, defined in v4l2-common.h; V4L2_SEL_FLAG_*. * @r: coordinates of the selection window * @reserved: for future use, set to zero for now * diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 7fdb8710c831..5d78910f926c 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -761,16 +761,12 @@ struct v4l2_crop { struct v4l2_rect c; }; -/* Hints for adjustments of selection rectangle */ -#define V4L2_SEL_FLAG_GE 0x00000001 -#define V4L2_SEL_FLAG_LE 0x00000002 - /** * struct v4l2_selection - selection info * @type: buffer type (do not use *_MPLANE types) * @target: Selection target, used to choose one of possible rectangles; * defined in v4l2-common.h; V4L2_SEL_TGT_* . - * @flags: constraints flags + * @flags: constraints flags, defined in v4l2-common.h; V4L2_SEL_FLAG_*. * @r: coordinates of selection window * @reserved: for future use, rounds structure size to 64 bytes, set to zero * -- cgit v1.2.3 From 6680884a44207efe8ef6bc56b1c932cce2b89994 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Fri, 6 Jul 2012 10:44:19 -0700 Subject: Input: ad7879 - add option to correct xy axis Sebastian Zenker reported that driver swaps x and y samples when the touchscreen leads are connected in accordance with the datasheet specification. Transposed axis can be typically corrected by touch screen calibration however this bug also negatively influences touch pressure measurements. Add an option to correct x and y axis. Signed-off-by: Michael Hennerich Reported-and-tested-by: Sebastian Zenker Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ad7879.c | 5 +++++ include/linux/spi/ad7879.h | 2 ++ 2 files changed, 7 insertions(+) (limited to 'include') diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index e2482b40da51..e60709261951 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -118,6 +118,7 @@ struct ad7879 { unsigned int irq; bool disabled; /* P: input->mutex */ bool suspended; /* P: input->mutex */ + bool swap_xy; u16 conversion_data[AD7879_NR_SENSE]; char phys[32]; u8 first_conversion_delay; @@ -161,6 +162,9 @@ static int ad7879_report(struct ad7879 *ts) z1 = ts->conversion_data[AD7879_SEQ_Z1] & MAX_12BIT; z2 = ts->conversion_data[AD7879_SEQ_Z2] & MAX_12BIT; + if (ts->swap_xy) + swap(x, y); + /* * The samples processed here are already preprocessed by the AD7879. * The preprocessing function consists of a median and an averaging @@ -520,6 +524,7 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, ts->dev = dev; ts->input = input_dev; ts->irq = irq; + ts->swap_xy = pdata->swap_xy; setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts); diff --git a/include/linux/spi/ad7879.h b/include/linux/spi/ad7879.h index 6334cee1a3be..58368be0b4c0 100644 --- a/include/linux/spi/ad7879.h +++ b/include/linux/spi/ad7879.h @@ -12,6 +12,8 @@ struct ad7879_platform_data { u16 y_min, y_max; u16 pressure_min, pressure_max; + bool swap_xy; /* swap x and y axes */ + /* [0..255] 0=OFF Starts at 1=550us and goes * all the way to 9.440ms in steps of 35us. */ -- cgit v1.2.3 From 3bdff937827da04b487f0a0ac6e1f3a9a1296878 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Sun, 1 Jul 2012 00:47:43 +0200 Subject: iio: cleanup buffer.h comments Signed-off-by: Peter Meerwald Signed-off-by: Jonathan Cameron --- include/linux/iio/buffer.h | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h index ad4fb1af0f7d..2a2b6b4d8d05 100644 --- a/include/linux/iio/buffer.h +++ b/include/linux/iio/buffer.h @@ -85,7 +85,7 @@ struct iio_buffer { /** * iio_buffer_init() - Initialize the buffer structure - * @buffer: buffer to be initialized + * @buffer: buffer to be initialized **/ void iio_buffer_init(struct iio_buffer *buffer); @@ -107,8 +107,9 @@ int iio_scan_mask_query(struct iio_dev *indio_dev, /** * iio_scan_mask_set() - set particular bit in the scan mask - * @buffer: the buffer whose scan mask we are interested in - * @bit: the bit to be set. + * @indio_dev IIO device structure + * @buffer: the buffer whose scan mask we are interested in + * @bit: the bit to be set. **/ int iio_scan_mask_set(struct iio_dev *indio_dev, struct iio_buffer *buffer, int bit); @@ -116,8 +117,8 @@ int iio_scan_mask_set(struct iio_dev *indio_dev, /** * iio_push_to_buffer() - push to a registered buffer. * @buffer: IIO buffer structure for device - * @scan: Full scan. - * @timestamp: + * @data: the data to push to the buffer + * @timestamp: timestamp to associate with the data */ int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data, s64 timestamp); @@ -126,7 +127,9 @@ int iio_update_demux(struct iio_dev *indio_dev); /** * iio_buffer_register() - register the buffer with IIO core - * @indio_dev: device with the buffer to be registered + * @indio_dev: device with the buffer to be registered + * @channels: the channel descriptions used to construct buffer + * @num_channels: the number of channels **/ int iio_buffer_register(struct iio_dev *indio_dev, const struct iio_chan_spec *channels, @@ -134,7 +137,7 @@ int iio_buffer_register(struct iio_dev *indio_dev, /** * iio_buffer_unregister() - unregister the buffer from IIO core - * @indio_dev: the device with the buffer to be unregistered + * @indio_dev: the device with the buffer to be unregistered **/ void iio_buffer_unregister(struct iio_dev *indio_dev); -- cgit v1.2.3 From dae8a969d512ee15e08fbec7837b9dab1777896d Mon Sep 17 00:00:00 2001 From: Jonghwa Lee Date: Mon, 25 Jun 2012 10:34:36 +0200 Subject: mfd: Add Maxim 77686 driver This patch is device driver for MAX77686 chip. MAX77686 is PMIC and includes regulator and rtc on it. This driver is core of MAX77686 chip, so provides common support for accessing on-chip devices. It uses irq_domain to manage irq and regmap to read/write data to its register with i2c bus. Signed-off-by: Chiwoong Byun Signed-off-by: Jonghwa Lee Signed-off-by: Myungjoo Ham Signed-off-by: Kyungmin Park Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 13 ++ drivers/mfd/Makefile | 1 + drivers/mfd/max77686-irq.c | 309 +++++++++++++++++++++++++++++++++++ drivers/mfd/max77686.c | 156 ++++++++++++++++++ include/linux/mfd/max77686-private.h | 247 ++++++++++++++++++++++++++++ include/linux/mfd/max77686.h | 117 +++++++++++++ 6 files changed, 843 insertions(+) create mode 100644 drivers/mfd/max77686-irq.c create mode 100644 drivers/mfd/max77686.c create mode 100644 include/linux/mfd/max77686-private.h create mode 100644 include/linux/mfd/max77686.h (limited to 'include') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index e129c820df7d..a7d0c851afc5 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -422,6 +422,19 @@ config PMIC_ADP5520 individual components like LCD backlight, LEDs, GPIOs and Kepad under the corresponding menus. +config MFD_MAX77686 + bool "Maxim Semiconductor MAX77686 PMIC Support" + depends on I2C=y && GENERIC_HARDIRQS + select MFD_CORE + select REGMAP_I2C + select IRQ_DOMAIN + help + Say yes here to support for Maxim Semiconductor MAX77686. + This is a Power Management IC with RTC on chip. + This driver provides common support for accessing the device; + additional drivers must be enabled in order to use the functionality + of the device. + config MFD_MAX77693 bool "Maxim Semiconductor MAX77693 PMIC Support" depends on I2C=y && GENERIC_HARDIRQS diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 75f6ed68a4b9..8ee7a3bf595b 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -78,6 +78,7 @@ obj-$(CONFIG_PMIC_DA9052) += da9052-core.o obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o +obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o max8925-objs := max8925-core.o max8925-i2c.o obj-$(CONFIG_MFD_MAX8925) += max8925.o diff --git a/drivers/mfd/max77686-irq.c b/drivers/mfd/max77686-irq.c new file mode 100644 index 000000000000..fc101220f990 --- /dev/null +++ b/drivers/mfd/max77686-irq.c @@ -0,0 +1,309 @@ +/* + * max77686-irq.c - Interrupt controller support for MAX77686 + * + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * Chiwoong Byun + * + * 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 + * + * This driver is based on max8997-irq.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + MAX77686_DEBUG_IRQ_INFO = 1 << 0, + MAX77686_DEBUG_IRQ_MASK = 1 << 1, + MAX77686_DEBUG_IRQ_INT = 1 << 2, +}; + +static int debug_mask = 0; +module_param(debug_mask, int, 0); +MODULE_PARM_DESC(debug_mask, "Set debug_mask : 0x0=off 0x1=IRQ_INFO 0x2=IRQ_MASK 0x4=IRQ_INI)"); + +static const u8 max77686_mask_reg[] = { + [PMIC_INT1] = MAX77686_REG_INT1MSK, + [PMIC_INT2] = MAX77686_REG_INT2MSK, + [RTC_INT] = MAX77686_RTC_INTM, +}; + +static struct regmap *max77686_get_regmap(struct max77686_dev *max77686, + enum max77686_irq_source src) +{ + switch (src) { + case PMIC_INT1 ... PMIC_INT2: + return max77686->regmap; + case RTC_INT: + return max77686->rtc_regmap; + default: + return ERR_PTR(-EINVAL); + } +} + +struct max77686_irq_data { + int mask; + enum max77686_irq_source group; +}; + +#define DECLARE_IRQ(idx, _group, _mask) \ + [(idx)] = { .group = (_group), .mask = (_mask) } +static const struct max77686_irq_data max77686_irqs[] = { + DECLARE_IRQ(MAX77686_PMICIRQ_PWRONF, PMIC_INT1, 1 << 0), + DECLARE_IRQ(MAX77686_PMICIRQ_PWRONR, PMIC_INT1, 1 << 1), + DECLARE_IRQ(MAX77686_PMICIRQ_JIGONBF, PMIC_INT1, 1 << 2), + DECLARE_IRQ(MAX77686_PMICIRQ_JIGONBR, PMIC_INT1, 1 << 3), + DECLARE_IRQ(MAX77686_PMICIRQ_ACOKBF, PMIC_INT1, 1 << 4), + DECLARE_IRQ(MAX77686_PMICIRQ_ACOKBR, PMIC_INT1, 1 << 5), + DECLARE_IRQ(MAX77686_PMICIRQ_ONKEY1S, PMIC_INT1, 1 << 6), + DECLARE_IRQ(MAX77686_PMICIRQ_MRSTB, PMIC_INT1, 1 << 7), + DECLARE_IRQ(MAX77686_PMICIRQ_140C, PMIC_INT2, 1 << 0), + DECLARE_IRQ(MAX77686_PMICIRQ_120C, PMIC_INT2, 1 << 1), + DECLARE_IRQ(MAX77686_RTCIRQ_RTC60S, RTC_INT, 1 << 0), + DECLARE_IRQ(MAX77686_RTCIRQ_RTCA1, RTC_INT, 1 << 1), + DECLARE_IRQ(MAX77686_RTCIRQ_RTCA2, RTC_INT, 1 << 2), + DECLARE_IRQ(MAX77686_RTCIRQ_SMPL, RTC_INT, 1 << 3), + DECLARE_IRQ(MAX77686_RTCIRQ_RTC1S, RTC_INT, 1 << 4), + DECLARE_IRQ(MAX77686_RTCIRQ_WTSR, RTC_INT, 1 << 5), +}; + +static void max77686_irq_lock(struct irq_data *data) +{ + struct max77686_dev *max77686 = irq_get_chip_data(data->irq); + + if (debug_mask & MAX77686_DEBUG_IRQ_MASK) + pr_info("%s\n", __func__); + + mutex_lock(&max77686->irqlock); +} + +static void max77686_irq_sync_unlock(struct irq_data *data) +{ + struct max77686_dev *max77686 = irq_get_chip_data(data->irq); + int i; + + for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) { + u8 mask_reg = max77686_mask_reg[i]; + struct regmap *map = max77686_get_regmap(max77686, i); + + if (debug_mask & MAX77686_DEBUG_IRQ_MASK) + pr_debug("%s: mask_reg[%d]=0x%x, cur=0x%x\n", + __func__, i, mask_reg, max77686->irq_masks_cur[i]); + + if (mask_reg == MAX77686_REG_INVALID || + IS_ERR_OR_NULL(map)) + continue; + + max77686->irq_masks_cache[i] = max77686->irq_masks_cur[i]; + + regmap_write(map, max77686_mask_reg[i], + max77686->irq_masks_cur[i]); + } + + mutex_unlock(&max77686->irqlock); +} + +static const inline struct max77686_irq_data *to_max77686_irq(int irq) +{ + struct irq_data *data = irq_get_irq_data(irq); + return &max77686_irqs[data->hwirq]; +} + +static void max77686_irq_mask(struct irq_data *data) +{ + struct max77686_dev *max77686 = irq_get_chip_data(data->irq); + const struct max77686_irq_data *irq_data = to_max77686_irq(data->irq); + + max77686->irq_masks_cur[irq_data->group] |= irq_data->mask; + + if (debug_mask & MAX77686_DEBUG_IRQ_MASK) + pr_info("%s: group=%d, cur=0x%x\n", + __func__, irq_data->group, + max77686->irq_masks_cur[irq_data->group]); +} + +static void max77686_irq_unmask(struct irq_data *data) +{ + struct max77686_dev *max77686 = irq_get_chip_data(data->irq); + const struct max77686_irq_data *irq_data = to_max77686_irq(data->irq); + + max77686->irq_masks_cur[irq_data->group] &= ~irq_data->mask; + + if (debug_mask & MAX77686_DEBUG_IRQ_MASK) + pr_info("%s: group=%d, cur=0x%x\n", + __func__, irq_data->group, + max77686->irq_masks_cur[irq_data->group]); +} + +static struct irq_chip max77686_irq_chip = { + .name = "max77686", + .irq_bus_lock = max77686_irq_lock, + .irq_bus_sync_unlock = max77686_irq_sync_unlock, + .irq_mask = max77686_irq_mask, + .irq_unmask = max77686_irq_unmask, +}; + +static irqreturn_t max77686_irq_thread(int irq, void *data) +{ + struct max77686_dev *max77686 = data; + unsigned int irq_reg[MAX77686_IRQ_GROUP_NR] = {}; + unsigned int irq_src; + int ret; + int i, cur_irq; + + ret = regmap_read(max77686->regmap, MAX77686_REG_INTSRC, &irq_src); + if (ret < 0) { + dev_err(max77686->dev, "Failed to read interrupt source: %d\n", + ret); + return IRQ_NONE; + } + + if (debug_mask & MAX77686_DEBUG_IRQ_INT) + pr_info("%s: irq_src=0x%x\n", __func__, irq_src); + + if (irq_src == MAX77686_IRQSRC_PMIC) { + ret = regmap_bulk_read(max77686->rtc_regmap, + MAX77686_REG_INT1, irq_reg, 2); + if (ret < 0) { + dev_err(max77686->dev, "Failed to read interrupt source: %d\n", + ret); + return IRQ_NONE; + } + + if (debug_mask & MAX77686_DEBUG_IRQ_INT) + pr_info("%s: int1=0x%x, int2=0x%x\n", __func__, + irq_reg[PMIC_INT1], irq_reg[PMIC_INT2]); + } + + if (irq_src & MAX77686_IRQSRC_RTC) { + ret = regmap_read(max77686->rtc_regmap, + MAX77686_RTC_INT, &irq_reg[RTC_INT]); + if (ret < 0) { + dev_err(max77686->dev, "Failed to read interrupt source: %d\n", + ret); + return IRQ_NONE; + } + + if (debug_mask & MAX77686_DEBUG_IRQ_INT) + pr_info("%s: rtc int=0x%x\n", __func__, + irq_reg[RTC_INT]); + + } + + for (i = 0; i < MAX77686_IRQ_NR; i++) { + if (irq_reg[max77686_irqs[i].group] & max77686_irqs[i].mask) { + cur_irq = irq_find_mapping(max77686->irq_domain, i); + if (cur_irq) + handle_nested_irq(cur_irq); + } + } + + return IRQ_HANDLED; +} + +static int max77686_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + struct max77686_dev *max77686 = d->host_data; + + irq_set_chip_data(irq, max77686); + irq_set_chip_and_handler(irq, &max77686_irq_chip, handle_edge_irq); + irq_set_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif + return 0; +} + +static struct irq_domain_ops max77686_irq_domain_ops = { + .map = max77686_irq_domain_map, +}; + +int max77686_irq_init(struct max77686_dev *max77686) +{ + struct irq_domain *domain; + int i; + int ret; + int val; + struct regmap *map; + + mutex_init(&max77686->irqlock); + + max77686->irq = gpio_to_irq(max77686->irq_gpio); + + if (debug_mask & MAX77686_DEBUG_IRQ_INT) { + ret = gpio_request(max77686->irq_gpio, "pmic_irq"); + if (ret < 0) { + dev_err(max77686->dev, + "Failed to request gpio %d with ret: %d\n", + max77686->irq_gpio, ret); + return IRQ_NONE; + } + + gpio_direction_input(max77686->irq_gpio); + val = gpio_get_value(max77686->irq_gpio); + gpio_free(max77686->irq_gpio); + pr_info("%s: gpio_irq=%x\n", __func__, val); + } + + /* Mask individual interrupt sources */ + for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) { + max77686->irq_masks_cur[i] = 0xff; + max77686->irq_masks_cache[i] = 0xff; + map = max77686_get_regmap(max77686, i); + + if (IS_ERR_OR_NULL(map)) + continue; + if (max77686_mask_reg[i] == MAX77686_REG_INVALID) + continue; + + regmap_write(map, max77686_mask_reg[i], 0xff); + } + domain = irq_domain_add_linear(NULL, MAX77686_IRQ_NR, + &max77686_irq_domain_ops, max77686); + if (!domain) { + dev_err(max77686->dev, "could not create irq domain\n"); + return -ENODEV; + } + max77686->irq_domain = domain; + + ret = request_threaded_irq(max77686->irq, NULL, max77686_irq_thread, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "max77686-irq", max77686); + + if (ret) + dev_err(max77686->dev, "Failed to request IRQ %d: %d\n", + max77686->irq, ret); + + + if (debug_mask & MAX77686_DEBUG_IRQ_INFO) + pr_info("%s-\n", __func__); + + return 0; +} + +void max77686_irq_exit(struct max77686_dev *max77686) +{ + if (max77686->irq) + free_irq(max77686->irq, max77686); +} diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c new file mode 100644 index 000000000000..11e56017e0b0 --- /dev/null +++ b/drivers/mfd/max77686.c @@ -0,0 +1,156 @@ +/* + * max77686.c - mfd core driver for the Maxim 77686 + * + * Copyright (C) 2012 Samsung Electronics + * Chiwoong Byun + * Jonghwa Lee + * + * 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 + * + * This driver is based on max8997.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define I2C_ADDR_RTC (0x0C >> 1) + +static struct mfd_cell max77686_devs[] = { + { .name = "max77686-pmic", }, + { .name = "max77686-rtc", }, +}; + +static struct regmap_config max77686_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int max77686_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct max77686_dev *max77686; + struct max77686_platform_data *pdata = i2c->dev.platform_data; + unsigned int data; + int ret = 0; + + max77686 = kzalloc(sizeof(struct max77686_dev), GFP_KERNEL); + if (max77686 == NULL) + return -ENOMEM; + + max77686->regmap = regmap_init_i2c(i2c, &max77686_regmap_config); + if (IS_ERR(max77686->regmap)) { + ret = PTR_ERR(max77686->regmap); + dev_err(max77686->dev, "Failed to allocate register map: %d\n", + ret); + kfree(max77686); + return ret; + } + + i2c_set_clientdata(i2c, max77686); + max77686->dev = &i2c->dev; + max77686->i2c = i2c; + max77686->type = id->driver_data; + + if (!pdata) { + ret = -EIO; + goto err; + } + + max77686->wakeup = pdata->wakeup; + max77686->irq_gpio = pdata->irq_gpio; + + mutex_init(&max77686->iolock); + + if (regmap_read(max77686->regmap, + MAX77686_REG_DEVICE_ID, &data) < 0) { + dev_err(max77686->dev, + "device not found on this channel (this is not an error)\n"); + ret = -ENODEV; + goto err; + } else + dev_info(max77686->dev, "device found\n"); + + max77686->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC); + i2c_set_clientdata(max77686->rtc, max77686); + + max77686_irq_init(max77686); + + ret = mfd_add_devices(max77686->dev, -1, max77686_devs, + ARRAY_SIZE(max77686_devs), NULL, 0); + + if (ret < 0) + goto err_mfd; + + return ret; + +err_mfd: + mfd_remove_devices(max77686->dev); + i2c_unregister_device(max77686->rtc); +err: + kfree(max77686); + return ret; +} + +static int max77686_i2c_remove(struct i2c_client *i2c) +{ + struct max77686_dev *max77686 = i2c_get_clientdata(i2c); + + mfd_remove_devices(max77686->dev); + i2c_unregister_device(max77686->rtc); + kfree(max77686); + + return 0; +} + +static const struct i2c_device_id max77686_i2c_id[] = { + { "max77686", TYPE_MAX77686 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max77686_i2c_id); + +static struct i2c_driver max77686_i2c_driver = { + .driver = { + .name = "max77686", + .owner = THIS_MODULE, + }, + .probe = max77686_i2c_probe, + .remove = max77686_i2c_remove, + .id_table = max77686_i2c_id, +}; + +static int __init max77686_i2c_init(void) +{ + return i2c_add_driver(&max77686_i2c_driver); +} +/* init early so consumer devices can complete system boot */ +subsys_initcall(max77686_i2c_init); + +static void __exit max77686_i2c_exit(void) +{ + i2c_del_driver(&max77686_i2c_driver); +} +module_exit(max77686_i2c_exit); + +MODULE_DESCRIPTION("MAXIM 77686 multi-function core driver"); +MODULE_AUTHOR("Chiwoong Byun "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/max77686-private.h b/include/linux/mfd/max77686-private.h new file mode 100644 index 000000000000..962f65e72b71 --- /dev/null +++ b/include/linux/mfd/max77686-private.h @@ -0,0 +1,247 @@ +/* + * max77686.h - Voltage regulator driver for the Maxim 77686 + * + * Copyright (C) 2012 Samsung Electrnoics + * Chiwoong Byun + * + * 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_MFD_MAX77686_PRIV_H +#define __LINUX_MFD_MAX77686_PRIV_H + +#include +#include +#include + +#define MAX77686_REG_INVALID (0xff) + +enum max77686_pmic_reg { + MAX77686_REG_DEVICE_ID = 0x00, + MAX77686_REG_INTSRC = 0x01, + MAX77686_REG_INT1 = 0x02, + MAX77686_REG_INT2 = 0x03, + + MAX77686_REG_INT1MSK = 0x04, + MAX77686_REG_INT2MSK = 0x05, + + MAX77686_REG_STATUS1 = 0x06, + MAX77686_REG_STATUS2 = 0x07, + + MAX77686_REG_PWRON = 0x08, + MAX77686_REG_ONOFF_DELAY = 0x09, + MAX77686_REG_MRSTB = 0x0A, + /* Reserved: 0x0B-0x0F */ + + MAX77686_REG_BUCK1CTRL = 0x10, + MAX77686_REG_BUCK1OUT = 0x11, + MAX77686_REG_BUCK2CTRL1 = 0x12, + MAX77686_REG_BUCK234FREQ = 0x13, + MAX77686_REG_BUCK2DVS1 = 0x14, + MAX77686_REG_BUCK2DVS2 = 0x15, + MAX77686_REG_BUCK2DVS3 = 0x16, + MAX77686_REG_BUCK2DVS4 = 0x17, + MAX77686_REG_BUCK2DVS5 = 0x18, + MAX77686_REG_BUCK2DVS6 = 0x19, + MAX77686_REG_BUCK2DVS7 = 0x1A, + MAX77686_REG_BUCK2DVS8 = 0x1B, + MAX77686_REG_BUCK3CTRL1 = 0x1C, + /* Reserved: 0x1D */ + MAX77686_REG_BUCK3DVS1 = 0x1E, + MAX77686_REG_BUCK3DVS2 = 0x1F, + MAX77686_REG_BUCK3DVS3 = 0x20, + MAX77686_REG_BUCK3DVS4 = 0x21, + MAX77686_REG_BUCK3DVS5 = 0x22, + MAX77686_REG_BUCK3DVS6 = 0x23, + MAX77686_REG_BUCK3DVS7 = 0x24, + MAX77686_REG_BUCK3DVS8 = 0x25, + MAX77686_REG_BUCK4CTRL1 = 0x26, + /* Reserved: 0x27 */ + MAX77686_REG_BUCK4DVS1 = 0x28, + MAX77686_REG_BUCK4DVS2 = 0x29, + MAX77686_REG_BUCK4DVS3 = 0x2A, + MAX77686_REG_BUCK4DVS4 = 0x2B, + MAX77686_REG_BUCK4DVS5 = 0x2C, + MAX77686_REG_BUCK4DVS6 = 0x2D, + MAX77686_REG_BUCK4DVS7 = 0x2E, + MAX77686_REG_BUCK4DVS8 = 0x2F, + MAX77686_REG_BUCK5CTRL = 0x30, + MAX77686_REG_BUCK5OUT = 0x31, + MAX77686_REG_BUCK6CTRL = 0x32, + MAX77686_REG_BUCK6OUT = 0x33, + MAX77686_REG_BUCK7CTRL = 0x34, + MAX77686_REG_BUCK7OUT = 0x35, + MAX77686_REG_BUCK8CTRL = 0x36, + MAX77686_REG_BUCK8OUT = 0x37, + MAX77686_REG_BUCK9CTRL = 0x38, + MAX77686_REG_BUCK9OUT = 0x39, + /* Reserved: 0x3A-0x3F */ + + MAX77686_REG_LDO1CTRL1 = 0x40, + MAX77686_REG_LDO2CTRL1 = 0x41, + MAX77686_REG_LDO3CTRL1 = 0x42, + MAX77686_REG_LDO4CTRL1 = 0x43, + MAX77686_REG_LDO5CTRL1 = 0x44, + MAX77686_REG_LDO6CTRL1 = 0x45, + MAX77686_REG_LDO7CTRL1 = 0x46, + MAX77686_REG_LDO8CTRL1 = 0x47, + MAX77686_REG_LDO9CTRL1 = 0x48, + MAX77686_REG_LDO10CTRL1 = 0x49, + MAX77686_REG_LDO11CTRL1 = 0x4A, + MAX77686_REG_LDO12CTRL1 = 0x4B, + MAX77686_REG_LDO13CTRL1 = 0x4C, + MAX77686_REG_LDO14CTRL1 = 0x4D, + MAX77686_REG_LDO15CTRL1 = 0x4E, + MAX77686_REG_LDO16CTRL1 = 0x4F, + MAX77686_REG_LDO17CTRL1 = 0x50, + MAX77686_REG_LDO18CTRL1 = 0x51, + MAX77686_REG_LDO19CTRL1 = 0x52, + MAX77686_REG_LDO20CTRL1 = 0x53, + MAX77686_REG_LDO21CTRL1 = 0x54, + MAX77686_REG_LDO22CTRL1 = 0x55, + MAX77686_REG_LDO23CTRL1 = 0x56, + MAX77686_REG_LDO24CTRL1 = 0x57, + MAX77686_REG_LDO25CTRL1 = 0x58, + MAX77686_REG_LDO26CTRL1 = 0x59, + /* Reserved: 0x5A-0x5F */ + MAX77686_REG_LDO1CTRL2 = 0x60, + MAX77686_REG_LDO2CTRL2 = 0x61, + MAX77686_REG_LDO3CTRL2 = 0x62, + MAX77686_REG_LDO4CTRL2 = 0x63, + MAX77686_REG_LDO5CTRL2 = 0x64, + MAX77686_REG_LDO6CTRL2 = 0x65, + MAX77686_REG_LDO7CTRL2 = 0x66, + MAX77686_REG_LDO8CTRL2 = 0x67, + MAX77686_REG_LDO9CTRL2 = 0x68, + MAX77686_REG_LDO10CTRL2 = 0x69, + MAX77686_REG_LDO11CTRL2 = 0x6A, + MAX77686_REG_LDO12CTRL2 = 0x6B, + MAX77686_REG_LDO13CTRL2 = 0x6C, + MAX77686_REG_LDO14CTRL2 = 0x6D, + MAX77686_REG_LDO15CTRL2 = 0x6E, + MAX77686_REG_LDO16CTRL2 = 0x6F, + MAX77686_REG_LDO17CTRL2 = 0x70, + MAX77686_REG_LDO18CTRL2 = 0x71, + MAX77686_REG_LDO19CTRL2 = 0x72, + MAX77686_REG_LDO20CTRL2 = 0x73, + MAX77686_REG_LDO21CTRL2 = 0x74, + MAX77686_REG_LDO22CTRL2 = 0x75, + MAX77686_REG_LDO23CTRL2 = 0x76, + MAX77686_REG_LDO24CTRL2 = 0x77, + MAX77686_REG_LDO25CTRL2 = 0x78, + MAX77686_REG_LDO26CTRL2 = 0x79, + /* Reserved: 0x7A-0x7D */ + + MAX77686_REG_BBAT_CHG = 0x7E, + MAX77686_REG_32KHZ = 0x7F, + + MAX77686_REG_PMIC_END = 0x80, +}; + +enum max77686_rtc_reg { + MAX77686_RTC_INT = 0x00, + MAX77686_RTC_INTM = 0x01, + MAX77686_RTC_CONTROLM = 0x02, + MAX77686_RTC_CONTROL = 0x03, + MAX77686_RTC_UPDATE0 = 0x04, + /* Reserved: 0x5 */ + MAX77686_WTSR_SMPL_CNTL = 0x06, + MAX77686_RTC_SEC = 0x07, + MAX77686_RTC_MIN = 0x08, + MAX77686_RTC_HOUR = 0x09, + MAX77686_RTC_WEEKDAY = 0x0A, + MAX77686_RTC_MONTH = 0x0B, + MAX77686_RTC_YEAR = 0x0C, + MAX77686_RTC_DATE = 0x0D, + MAX77686_ALARM1_SEC = 0x0E, + MAX77686_ALARM1_MIN = 0x0F, + MAX77686_ALARM1_HOUR = 0x10, + MAX77686_ALARM1_WEEKDAY = 0x11, + MAX77686_ALARM1_MONTH = 0x12, + MAX77686_ALARM1_YEAR = 0x13, + MAX77686_ALARM1_DATE = 0x14, + MAX77686_ALARM2_SEC = 0x15, + MAX77686_ALARM2_MIN = 0x16, + MAX77686_ALARM2_HOUR = 0x17, + MAX77686_ALARM2_WEEKDAY = 0x18, + MAX77686_ALARM2_MONTH = 0x19, + MAX77686_ALARM2_YEAR = 0x1A, + MAX77686_ALARM2_DATE = 0x1B, +}; + +#define MAX77686_IRQSRC_PMIC (0) +#define MAX77686_IRQSRC_RTC (1 << 0) + +enum max77686_irq_source { + PMIC_INT1 = 0, + PMIC_INT2, + RTC_INT, + + MAX77686_IRQ_GROUP_NR, +}; + +enum max77686_irq { + MAX77686_PMICIRQ_PWRONF, + MAX77686_PMICIRQ_PWRONR, + MAX77686_PMICIRQ_JIGONBF, + MAX77686_PMICIRQ_JIGONBR, + MAX77686_PMICIRQ_ACOKBF, + MAX77686_PMICIRQ_ACOKBR, + MAX77686_PMICIRQ_ONKEY1S, + MAX77686_PMICIRQ_MRSTB, + + MAX77686_PMICIRQ_140C, + MAX77686_PMICIRQ_120C, + + MAX77686_RTCIRQ_RTC60S, + MAX77686_RTCIRQ_RTCA1, + MAX77686_RTCIRQ_RTCA2, + MAX77686_RTCIRQ_SMPL, + MAX77686_RTCIRQ_RTC1S, + MAX77686_RTCIRQ_WTSR, + + MAX77686_IRQ_NR, +}; + +struct max77686_dev { + struct device *dev; + struct i2c_client *i2c; /* 0xcc / PMIC, Battery Control, and FLASH */ + struct i2c_client *rtc; /* slave addr 0x0c */ + struct mutex iolock; + + int type; + + struct regmap *regmap; /* regmap for mfd */ + struct regmap *rtc_regmap; /* regmap for rtc */ + + struct irq_domain *irq_domain; + + int irq; + int irq_gpio; + bool wakeup; + struct mutex irqlock; + int irq_masks_cur[MAX77686_IRQ_GROUP_NR]; + int irq_masks_cache[MAX77686_IRQ_GROUP_NR]; +}; + +enum max77686_types { + TYPE_MAX77686, +}; + +extern int max77686_irq_init(struct max77686_dev *max77686); +extern void max77686_irq_exit(struct max77686_dev *max77686); +extern int max77686_irq_resume(struct max77686_dev *max77686); + +#endif /* __LINUX_MFD_MAX77686_PRIV_H */ diff --git a/include/linux/mfd/max77686.h b/include/linux/mfd/max77686.h new file mode 100644 index 000000000000..fcf312678cac --- /dev/null +++ b/include/linux/mfd/max77686.h @@ -0,0 +1,117 @@ +/* + * max77686.h - Driver for the Maxim 77686 + * + * Copyright (C) 2012 Samsung Electrnoics + * Chiwoong Byun + * + * 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 + * + * This driver is based on max8997.h + * + * MAX77686 has PMIC, RTC devices. + * The devices share the same I2C bus and included in + * this mfd driver. + */ + +#ifndef __LINUX_MFD_MAX77686_H +#define __LINUX_MFD_MAX77686_H + +#include + +/* MAX77686 regulator IDs */ +enum max77686_regulators { + MAX77686_LDO1 = 0, + MAX77686_LDO2, + MAX77686_LDO3, + MAX77686_LDO4, + MAX77686_LDO5, + MAX77686_LDO6, + MAX77686_LDO7, + MAX77686_LDO8, + MAX77686_LDO9, + MAX77686_LDO10, + MAX77686_LDO11, + MAX77686_LDO12, + MAX77686_LDO13, + MAX77686_LDO14, + MAX77686_LDO15, + MAX77686_LDO16, + MAX77686_LDO17, + MAX77686_LDO18, + MAX77686_LDO19, + MAX77686_LDO20, + MAX77686_LDO21, + MAX77686_LDO22, + MAX77686_LDO23, + MAX77686_LDO24, + MAX77686_LDO25, + MAX77686_LDO26, + MAX77686_BUCK1, + MAX77686_BUCK2, + MAX77686_BUCK3, + MAX77686_BUCK4, + MAX77686_BUCK5, + MAX77686_BUCK6, + MAX77686_BUCK7, + MAX77686_BUCK8, + MAX77686_BUCK9, + MAX77686_EN32KHZ_AP, + MAX77686_EN32KHZ_CP, + MAX77686_P32KH, + + MAX77686_REG_MAX, +}; + +struct max77686_regulator_data { + int id; + struct regulator_init_data *initdata; +}; + +enum max77686_opmode { + MAX77686_OPMODE_NORMAL, + MAX77686_OPMODE_LP, + MAX77686_OPMODE_STANDBY, +}; + +struct max77686_opmode_data { + int id; + int mode; +}; + +struct max77686_platform_data { + /* IRQ */ + int irq_gpio; + int ono; + int wakeup; + + /* ---- PMIC ---- */ + struct max77686_regulator_data *regulators; + int num_regulators; + + struct max77686_opmode_data *opmode_data; + + /* + * GPIO-DVS feature is not enabled with the current version of + * MAX77686 driver. Buck2/3/4_voltages[0] is used as the default + * voltage at probe. DVS/SELB gpios are set as OUTPUT-LOW. + */ + int buck234_gpio_dvs[3]; /* GPIO of [0]DVS1, [1]DVS2, [2]DVS3 */ + int buck234_gpio_selb[3]; /* [0]SELB2, [1]SELB3, [2]SELB4 */ + unsigned int buck2_voltage[8]; /* buckx_voltage in uV */ + unsigned int buck3_voltage[8]; + unsigned int buck4_voltage[8]; +}; + +#endif /* __LINUX_MFD_MAX77686_H */ -- cgit v1.2.3 From a7cc37a49876319b2f848290eefe3388dd82286b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 24 May 2012 16:57:46 +0800 Subject: mfd: Remove unused max77693 iolock mutex Now this driver is using regmap APIs, the iolock mutex is not used and can be removed. Signed-off-by: Axel Lin Signed-off-by: Samuel Ortiz --- drivers/mfd/max77693.c | 2 -- include/linux/mfd/max77693-private.h | 1 - 2 files changed, 3 deletions(-) (limited to 'include') diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c index e9e4278722f3..4055bc2d36fb 100644 --- a/drivers/mfd/max77693.c +++ b/drivers/mfd/max77693.c @@ -138,8 +138,6 @@ static int max77693_i2c_probe(struct i2c_client *i2c, max77693->wakeup = pdata->wakeup; - mutex_init(&max77693->iolock); - if (max77693_read_reg(max77693->regmap, MAX77693_PMIC_REG_PMIC_ID2, ®_data) < 0) { dev_err(max77693->dev, "device not found on this channel\n"); diff --git a/include/linux/mfd/max77693-private.h b/include/linux/mfd/max77693-private.h index 68263c5fa53c..1eeae5c07915 100644 --- a/include/linux/mfd/max77693-private.h +++ b/include/linux/mfd/max77693-private.h @@ -190,7 +190,6 @@ struct max77693_dev { struct i2c_client *i2c; /* 0xCC , PMIC, Charger, Flash LED */ struct i2c_client *muic; /* 0x4A , MUIC */ struct i2c_client *haptic; /* 0x90 , Haptic */ - struct mutex iolock; int type; -- cgit v1.2.3 From 06e589efa5b75e6a38a8e8b9c6cd774b5f679cdc Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 20 Jun 2012 13:56:37 +0100 Subject: mfd: Add IRQ domain support for the AB8500 As the AB8500 is an IRQ controller in its own right, here we provide the AB8500 driver with IRQ domain support. This is required if we wish to reference any of its IRQs from a platform's Device Tree. Cc: Naga Radheshy Cc: Mattias Wallin Cc: Daniel Willerud Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 1 + drivers/mfd/ab8500-core.c | 130 ++++++++++++++++++++------------------ include/linux/mfd/abx500/ab8500.h | 5 ++ 3 files changed, 76 insertions(+), 60 deletions(-) (limited to 'include') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index ed488edb2dee..8b56c1998ab2 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -709,6 +709,7 @@ config AB8500_CORE bool "ST-Ericsson AB8500 Mixed Signal Power Management chip" depends on GENERIC_HARDIRQS && ABX500_CORE && MFD_DB8500_PRCMU select MFD_CORE + select IRQ_DOMAIN help Select this option to enable access to AB8500 power management chip. This connects to U8500 either on the SSP/SPI bus (deprecated diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index dac0e2998603..f3af34596439 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -361,7 +362,7 @@ static void ab8500_irq_sync_unlock(struct irq_data *data) static void ab8500_irq_mask(struct irq_data *data) { struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data); - int offset = data->irq - ab8500->irq_base; + int offset = data->hwirq; int index = offset / 8; int mask = 1 << (offset % 8); @@ -371,7 +372,7 @@ static void ab8500_irq_mask(struct irq_data *data) static void ab8500_irq_unmask(struct irq_data *data) { struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data); - int offset = data->irq - ab8500->irq_base; + int offset = data->hwirq; int index = offset / 8; int mask = 1 << (offset % 8); @@ -510,38 +511,51 @@ static irqreturn_t ab8500_irq(int irq, void *dev) return IRQ_HANDLED; } -static int ab8500_irq_init(struct ab8500 *ab8500) +/** + * ab8500_irq_get_virq(): Map an interrupt on a chip to a virtual IRQ + * + * @ab8500: ab8500_irq controller to operate on. + * @irq: index of the interrupt requested in the chip IRQs + * + * Useful for drivers to request their own IRQs. + */ +int ab8500_irq_get_virq(struct ab8500 *ab8500, int irq) { - int base = ab8500->irq_base; - int irq; - int num_irqs; + if (!ab8500) + return -EINVAL; - if (is_ab9540(ab8500)) - num_irqs = AB9540_NR_IRQS; - else if (is_ab8505(ab8500)) - num_irqs = AB8505_NR_IRQS; - else - num_irqs = AB8500_NR_IRQS; + return irq_create_mapping(ab8500->domain, irq); +} +EXPORT_SYMBOL_GPL(ab8500_irq_get_virq); - for (irq = base; irq < base + num_irqs; irq++) { - irq_set_chip_data(irq, ab8500); - irq_set_chip_and_handler(irq, &ab8500_irq_chip, - handle_simple_irq); - irq_set_nested_thread(irq, 1); +static int ab8500_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hwirq) +{ + struct ab8500 *ab8500 = d->host_data; + + if (!ab8500) + return -EINVAL; + + irq_set_chip_data(virq, ab8500); + irq_set_chip_and_handler(virq, &ab8500_irq_chip, + handle_simple_irq); + irq_set_nested_thread(virq, 1); #ifdef CONFIG_ARM - set_irq_flags(irq, IRQF_VALID); + set_irq_flags(virq, IRQF_VALID); #else - irq_set_noprobe(irq); + irq_set_noprobe(virq); #endif - } return 0; } -static void ab8500_irq_remove(struct ab8500 *ab8500) +static struct irq_domain_ops ab8500_irq_ops = { + .map = ab8500_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + +static int ab8500_irq_init(struct ab8500 *ab8500, struct device_node *np) { - int base = ab8500->irq_base; - int irq; int num_irqs; if (is_ab9540(ab8500)) @@ -551,13 +565,22 @@ static void ab8500_irq_remove(struct ab8500 *ab8500) else num_irqs = AB8500_NR_IRQS; - for (irq = base; irq < base + num_irqs; irq++) { -#ifdef CONFIG_ARM - set_irq_flags(irq, 0); -#endif - irq_set_chip_and_handler(irq, NULL, NULL); - irq_set_chip_data(irq, NULL); + if (ab8500->irq_base) { + ab8500->domain = irq_domain_add_legacy( + NULL, num_irqs, ab8500->irq_base, + 0, &ab8500_irq_ops, ab8500); + } + else { + ab8500->domain = irq_domain_add_linear( + np, num_irqs, &ab8500_irq_ops, ab8500); } + + if (!ab8500->domain) { + dev_err(ab8500->dev, "Failed to create irqdomain\n"); + return -ENOSYS; + } + + return 0; } int ab8500_suspend(struct ab8500 *ab8500) @@ -1233,14 +1256,6 @@ static int __devinit ab8500_probe(struct platform_device *pdev) if (plat) ab8500->irq_base = plat->irq_base; - else if (np) - ret = of_property_read_u32(np, "stericsson,irq-base", &ab8500->irq_base); - - if (!ab8500->irq_base) { - dev_info(&pdev->dev, "couldn't find irq-base\n"); - ret = -EINVAL; - goto out_free_ab8500; - } ab8500->dev = &pdev->dev; @@ -1323,7 +1338,7 @@ static int __devinit ab8500_probe(struct platform_device *pdev) AB8500_SWITCH_OFF_STATUS, &value); if (ret < 0) return ret; - dev_info(ab8500->dev, "switch off status: %#x", value); + dev_info(ab8500->dev, "switch off status: %#x\n", value); if (plat && plat->init) plat->init(ab8500); @@ -1352,25 +1367,25 @@ static int __devinit ab8500_probe(struct platform_device *pdev) for (i = 0; i < ab8500->mask_size; i++) ab8500->mask[i] = ab8500->oldmask[i] = 0xff; - if (ab8500->irq_base) { - ret = ab8500_irq_init(ab8500); - if (ret) - goto out_freeoldmask; + ret = ab8500_irq_init(ab8500, np); + if (ret) + goto out_freeoldmask; - /* Activate this feature only in ab9540 */ - /* till tests are done on ab8500 1p2 or later*/ - if (is_ab9540(ab8500)) - ret = request_threaded_irq(ab8500->irq, NULL, + /* Activate this feature only in ab9540 */ + /* till tests are done on ab8500 1p2 or later*/ + if (is_ab9540(ab8500)) { + ret = request_threaded_irq(ab8500->irq, NULL, ab8500_hierarchical_irq, IRQF_ONESHOT | IRQF_NO_SUSPEND, "ab8500", ab8500); - else - ret = request_threaded_irq(ab8500->irq, NULL, + } + else { + ret = request_threaded_irq(ab8500->irq, NULL, ab8500_irq, IRQF_ONESHOT | IRQF_NO_SUSPEND, "ab8500", ab8500); if (ret) - goto out_removeirq; + goto out_freeoldmask; } if (!np) { @@ -1417,15 +1432,11 @@ static int __devinit ab8500_probe(struct platform_device *pdev) &ab8500_attr_group); if (ret) dev_err(ab8500->dev, "error creating sysfs entries\n"); - else - return ret; + + return ret; out_freeirq: - if (ab8500->irq_base) - free_irq(ab8500->irq, ab8500); -out_removeirq: - if (ab8500->irq_base) - ab8500_irq_remove(ab8500); + free_irq(ab8500->irq, ab8500); out_freeoldmask: kfree(ab8500->oldmask); out_freemask: @@ -1444,11 +1455,10 @@ static int __devexit ab8500_remove(struct platform_device *pdev) sysfs_remove_group(&ab8500->dev->kobj, &ab9540_attr_group); else sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group); + mfd_remove_devices(ab8500->dev); - if (ab8500->irq_base) { - free_irq(ab8500->irq, ab8500); - ab8500_irq_remove(ab8500); - } + free_irq(ab8500->irq, ab8500); + kfree(ab8500->oldmask); kfree(ab8500->mask); kfree(ab8500); diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h index 91dd3ef63e99..4ae2cd9584fb 100644 --- a/include/linux/mfd/abx500/ab8500.h +++ b/include/linux/mfd/abx500/ab8500.h @@ -9,6 +9,7 @@ #include #include +#include struct device; @@ -227,6 +228,7 @@ enum ab8500_version { * @irq_lock: genirq bus lock * @transfer_ongoing: 0 if no transfer ongoing * @irq: irq line + * @irq_domain: irq domain * @version: chip version id (e.g. ab8500 or ab9540) * @chip_id: chip revision id * @write: register write @@ -247,6 +249,7 @@ struct ab8500 { atomic_t transfer_ongoing; int irq_base; int irq; + struct irq_domain *domain; enum ab8500_version version; u8 chip_id; @@ -336,4 +339,6 @@ static inline int is_ab8500_2p0(struct ab8500 *ab) return (is_ab8500(ab) && (ab->chip_id == AB8500_CUT2P0)); } +int ab8500_irq_get_virq(struct ab8500 *ab8500, int irq); + #endif /* MFD_AB8500_H */ -- cgit v1.2.3 From c94bb233a9fee3314dc5d9c7de9fa702e91283f2 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 29 Jun 2012 19:01:03 +0200 Subject: mfd: Make MFD core code Device Tree and IRQ domain aware During Device Tree enablement of the ab8500 and db8500-prcmu drivers, a decision was made to omit registration through the MFD API and use Device Tree directly. However, because MFD devices have a different address space and the ab8500 and db8500 both use I2C to communicate, this causes issues with address translation during execution of of_platform_populate(). So the solution is to make the MFD core aware of Device Tree and have it assign the correct node pointers instead. To make this work the MFD core also needs to be awere of IRQ domains, as Device Tree insists on IRQ domain compatibility. So, instead of providing an irq-base via platform code, in the DT case we simply look up the IRQ domain and map to the correct virtual IRQ. Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 1 + drivers/mfd/mfd-core.c | 30 ++++++++++++++++++++++++++---- include/linux/mfd/core.h | 1 + 3 files changed, 28 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 8b56c1998ab2..d43876c6da23 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -7,6 +7,7 @@ menu "Multifunction device drivers" config MFD_CORE tristate + select IRQ_DOMAIN default n config MFD_88PM860X diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index ffc3d48676ae..0c3a01cde2f7 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include int mfd_cell_enable(struct platform_device *pdev) { @@ -76,6 +78,8 @@ static int mfd_add_device(struct device *parent, int id, { struct resource *res; struct platform_device *pdev; + struct device_node *np = NULL; + struct irq_domain *domain = NULL; int ret = -ENOMEM; int r; @@ -89,6 +93,16 @@ static int mfd_add_device(struct device *parent, int id, pdev->dev.parent = parent; + if (parent->of_node && cell->of_compatible) { + for_each_child_of_node(parent->of_node, np) { + if (of_device_is_compatible(np, cell->of_compatible)) { + pdev->dev.of_node = np; + domain = irq_find_host(parent->of_node); + break; + } + } + } + if (cell->pdata_size) { ret = platform_device_add_data(pdev, cell->platform_data, cell->pdata_size); @@ -112,10 +126,18 @@ static int mfd_add_device(struct device *parent, int id, res[r].end = mem_base->start + cell->resources[r].end; } else if (cell->resources[r].flags & IORESOURCE_IRQ) { - res[r].start = irq_base + - cell->resources[r].start; - res[r].end = irq_base + - cell->resources[r].end; + if (domain) { + /* Unable to create mappings for IRQ ranges. */ + WARN_ON(cell->resources[r].start != + cell->resources[r].end); + res[r].start = res[r].end = irq_create_mapping( + domain, cell->resources[r].start); + } else { + res[r].start = irq_base + + cell->resources[r].start; + res[r].end = irq_base + + cell->resources[r].end; + } } else { res[r].parent = cell->resources[r].parent; res[r].start = cell->resources[r].start; diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index 4e76163dd862..99b7eb1961b6 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -36,6 +36,7 @@ struct mfd_cell { /* platform data passed to the sub devices drivers */ void *platform_data; size_t pdata_size; + const char *of_compatible; /* * These resources can be specified relative to the parent device. -- cgit v1.2.3 From b0ab907d325f99054eb2700a8f8c50776ebfeaf9 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 1 Jun 2012 16:33:19 +0100 Subject: mfd: Support for user defined wm8994 irq flags Signed-off-by: Chris Rattray Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8994-irq.c | 10 +++++++++- include/linux/mfd/wm8994/pdata.h | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c index f1837f669755..0aac4aff17a5 100644 --- a/drivers/mfd/wm8994-irq.c +++ b/drivers/mfd/wm8994-irq.c @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -139,6 +140,8 @@ static struct regmap_irq_chip wm8994_irq_chip = { int wm8994_irq_init(struct wm8994 *wm8994) { int ret; + unsigned long irqflags; + struct wm8994_pdata *pdata = wm8994->dev->platform_data; if (!wm8994->irq) { dev_warn(wm8994->dev, @@ -147,8 +150,13 @@ int wm8994_irq_init(struct wm8994 *wm8994) return 0; } + /* select user or default irq flags */ + irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT; + if (pdata->irq_flags) + irqflags = pdata->irq_flags; + ret = regmap_add_irq_chip(wm8994->regmap, wm8994->irq, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + irqflags, wm8994->irq_base, &wm8994_irq_chip, &wm8994->irq_data); if (ret != 0) { diff --git a/include/linux/mfd/wm8994/pdata.h b/include/linux/mfd/wm8994/pdata.h index 893267bb6229..f0361c031927 100644 --- a/include/linux/mfd/wm8994/pdata.h +++ b/include/linux/mfd/wm8994/pdata.h @@ -141,6 +141,7 @@ struct wm8994_pdata { struct wm8994_ldo_pdata ldo[WM8994_NUM_LDO]; int irq_base; /** Base IRQ number for WM8994, required for IRQs */ + unsigned long irq_flags; /** user irq flags */ int num_drc_cfgs; struct wm8994_drc_cfg *drc_cfgs; -- cgit v1.2.3 From 52b461b86a9f6c7a86bdcb858e1bbef089fbe6a0 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 3 Jun 2012 13:37:22 +0100 Subject: mfd: Add regmap cache support for wm8350 Use the most simple possible transformation on the existing code so keep the table sitting around, further patches in this series will delete the existing cache code - the main purpose of this patch is to ensure that we always have a cache for bisection. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8350-core.c | 29 +++++++++++++-------- drivers/mfd/wm8350-i2c.c | 5 ---- drivers/mfd/wm8350-regmap.c | 56 +++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/wm8350/core.h | 7 +++++- 4 files changed, 80 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index 8a9b11ca076a..fadcbbe9e2ba 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -32,9 +32,6 @@ #include #include -#define WM8350_UNLOCK_KEY 0x0013 -#define WM8350_LOCK_KEY 0x0000 - #define WM8350_CLOCK_CONTROL_1 0x28 #define WM8350_AIF_TEST 0x74 @@ -295,15 +292,20 @@ EXPORT_SYMBOL_GPL(wm8350_block_write); */ int wm8350_reg_lock(struct wm8350 *wm8350) { - u16 key = WM8350_LOCK_KEY; int ret; + mutex_lock(®_lock_mutex); + ldbg(__func__); - mutex_lock(&io_mutex); - ret = wm8350_write(wm8350, WM8350_SECURITY, 1, &key); + + ret = wm8350_reg_write(wm8350, WM8350_SECURITY, WM8350_LOCK_KEY); if (ret) dev_err(wm8350->dev, "lock failed\n"); - mutex_unlock(&io_mutex); + + wm8350->unlocked = false; + + mutex_unlock(®_lock_mutex); + return ret; } EXPORT_SYMBOL_GPL(wm8350_reg_lock); @@ -319,15 +321,20 @@ EXPORT_SYMBOL_GPL(wm8350_reg_lock); */ int wm8350_reg_unlock(struct wm8350 *wm8350) { - u16 key = WM8350_UNLOCK_KEY; int ret; + mutex_lock(®_lock_mutex); + ldbg(__func__); - mutex_lock(&io_mutex); - ret = wm8350_write(wm8350, WM8350_SECURITY, 1, &key); + + ret = wm8350_reg_write(wm8350, WM8350_SECURITY, WM8350_UNLOCK_KEY); if (ret) dev_err(wm8350->dev, "unlock failed\n"); - mutex_unlock(&io_mutex); + + wm8350->unlocked = true; + + mutex_unlock(®_lock_mutex); + return ret; } EXPORT_SYMBOL_GPL(wm8350_reg_unlock); diff --git a/drivers/mfd/wm8350-i2c.c b/drivers/mfd/wm8350-i2c.c index a68aceb4e48c..2e57101c8d3d 100644 --- a/drivers/mfd/wm8350-i2c.c +++ b/drivers/mfd/wm8350-i2c.c @@ -23,11 +23,6 @@ #include #include -static const struct regmap_config wm8350_regmap = { - .reg_bits = 8, - .val_bits = 16, -}; - static int wm8350_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { diff --git a/drivers/mfd/wm8350-regmap.c b/drivers/mfd/wm8350-regmap.c index e965139e5cd5..7974cadaa422 100644 --- a/drivers/mfd/wm8350-regmap.c +++ b/drivers/mfd/wm8350-regmap.c @@ -3433,3 +3433,59 @@ const struct wm8350_reg_access wm8350_reg_io_map[] = { { 0x0000, 0x0000, 0x0000 }, /* R254 */ { 0x0000, 0x0000, 0x0000 }, /* R255 */ }; + +static bool wm8350_readable(struct device *dev, unsigned int reg) +{ + return wm8350_reg_io_map[reg].readable; +} + +static bool wm8350_writeable(struct device *dev, unsigned int reg) +{ + struct wm8350 *wm8350 = dev_get_drvdata(dev); + + if (!wm8350->unlocked) { + if ((reg >= WM8350_GPIO_FUNCTION_SELECT_1 && + reg <= WM8350_GPIO_FUNCTION_SELECT_4) || + (reg >= WM8350_BATTERY_CHARGER_CONTROL_1 && + reg <= WM8350_BATTERY_CHARGER_CONTROL_3)) + return false; + } + + return wm8350_reg_io_map[reg].writable; +} + +static bool wm8350_volatile(struct device *dev, unsigned int reg) +{ + return wm8350_reg_io_map[reg].vol; +} + +static bool wm8350_precious(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8350_SYSTEM_INTERRUPTS: + case WM8350_INT_STATUS_1: + case WM8350_INT_STATUS_2: + case WM8350_POWER_UP_INT_STATUS: + case WM8350_UNDER_VOLTAGE_INT_STATUS: + case WM8350_OVER_CURRENT_INT_STATUS: + case WM8350_GPIO_INT_STATUS: + case WM8350_COMPARATOR_INT_STATUS: + return true; + + default: + return false; + } +} + +const struct regmap_config wm8350_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .cache_type = REGCACHE_RBTREE, + + .max_register = WM8350_MAX_REGISTER, + .readable_reg = wm8350_readable, + .writeable_reg = wm8350_writeable, + .volatile_reg = wm8350_volatile, + .precious_reg = wm8350_precious, +}; diff --git a/include/linux/mfd/wm8350/core.h b/include/linux/mfd/wm8350/core.h index 9192b6404a73..cba9bc8f947b 100644 --- a/include/linux/mfd/wm8350/core.h +++ b/include/linux/mfd/wm8350/core.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -66,6 +67,9 @@ #define WM8350_MAX_REGISTER 0xFF +#define WM8350_UNLOCK_KEY 0x0013 +#define WM8350_LOCK_KEY 0x0000 + /* * Field Definitions. */ @@ -582,6 +586,7 @@ #define WM8350_NUM_IRQ_REGS 7 +extern const struct regmap_config wm8350_regmap; struct wm8350_reg_access { u16 readable; /* Mask of readable bits */ u16 writable; /* Mask of writable bits */ @@ -602,7 +607,6 @@ extern const u16 wm8352_mode2_defaults[]; extern const u16 wm8352_mode3_defaults[]; struct wm8350; -struct regmap; struct wm8350_hwmon { struct platform_device *pdev; @@ -615,6 +619,7 @@ struct wm8350 { /* device IO */ struct regmap *regmap; u16 *reg_cache; + bool unlocked; struct mutex auxadc_mutex; struct completion auxadc_done; -- cgit v1.2.3 From 19d57ed5a308472a02e773f33c03ad4cb2ec6a9d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 3 Jun 2012 13:37:24 +0100 Subject: mfd: Remove custom wm8350 cache implementation Since none of the users now reference the cache directly we can happily remove the custom cache code and rely on the regmap cache. For simplicity we don't bother with the register defaults tables but instead read the defaults from the device - regmap is capable of doing this, unlike our old cache infrastructure. This saves a lot of code and allows us to cache the device revision information too. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8350-core.c | 325 +--- drivers/mfd/wm8350-regmap.c | 3166 +-------------------------------------- include/linux/mfd/wm8350/core.h | 19 - 3 files changed, 18 insertions(+), 3492 deletions(-) (limited to 'include') diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index fadcbbe9e2ba..7c1ae24605d9 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -60,181 +60,32 @@ /* * WM8350 Device IO */ -static DEFINE_MUTEX(io_mutex); static DEFINE_MUTEX(reg_lock_mutex); -/* Perform a physical read from the device. - */ -static int wm8350_phys_read(struct wm8350 *wm8350, u8 reg, int num_regs, - u16 *dest) -{ - int i, ret; - int bytes = num_regs * 2; - - dev_dbg(wm8350->dev, "volatile read\n"); - ret = regmap_raw_read(wm8350->regmap, reg, dest, bytes); - - for (i = reg; i < reg + num_regs; i++) { - /* Cache is CPU endian */ - dest[i - reg] = be16_to_cpu(dest[i - reg]); - - /* Mask out non-readable bits */ - dest[i - reg] &= wm8350_reg_io_map[i].readable; - } - - dump(num_regs, dest); - - return ret; -} - -static int wm8350_read(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *dest) -{ - int i; - int end = reg + num_regs; - int ret = 0; - int bytes = num_regs * 2; - - if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) { - dev_err(wm8350->dev, "invalid reg %x\n", - reg + num_regs - 1); - return -EINVAL; - } - - dev_dbg(wm8350->dev, - "%s R%d(0x%2.2x) %d regs\n", __func__, reg, reg, num_regs); - -#if WM8350_BUS_DEBUG - /* we can _safely_ read any register, but warn if read not supported */ - for (i = reg; i < end; i++) { - if (!wm8350_reg_io_map[i].readable) - dev_warn(wm8350->dev, - "reg R%d is not readable\n", i); - } -#endif - - /* if any volatile registers are required, then read back all */ - for (i = reg; i < end; i++) - if (wm8350_reg_io_map[i].vol) - return wm8350_phys_read(wm8350, reg, num_regs, dest); - - /* no volatiles, then cache is good */ - dev_dbg(wm8350->dev, "cache read\n"); - memcpy(dest, &wm8350->reg_cache[reg], bytes); - dump(num_regs, dest); - return ret; -} - -static inline int is_reg_locked(struct wm8350 *wm8350, u8 reg) -{ - if (reg == WM8350_SECURITY || - wm8350->reg_cache[WM8350_SECURITY] == WM8350_UNLOCK_KEY) - return 0; - - if ((reg >= WM8350_GPIO_FUNCTION_SELECT_1 && - reg <= WM8350_GPIO_FUNCTION_SELECT_4) || - (reg >= WM8350_BATTERY_CHARGER_CONTROL_1 && - reg <= WM8350_BATTERY_CHARGER_CONTROL_3)) - return 1; - return 0; -} - -static int wm8350_write(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *src) -{ - int i; - int end = reg + num_regs; - int bytes = num_regs * 2; - - if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) { - dev_err(wm8350->dev, "invalid reg %x\n", - reg + num_regs - 1); - return -EINVAL; - } - - /* it's generally not a good idea to write to RO or locked registers */ - for (i = reg; i < end; i++) { - if (!wm8350_reg_io_map[i].writable) { - dev_err(wm8350->dev, - "attempted write to read only reg R%d\n", i); - return -EINVAL; - } - - if (is_reg_locked(wm8350, i)) { - dev_err(wm8350->dev, - "attempted write to locked reg R%d\n", i); - return -EINVAL; - } - - src[i - reg] &= wm8350_reg_io_map[i].writable; - - wm8350->reg_cache[i] = - (wm8350->reg_cache[i] & ~wm8350_reg_io_map[i].writable) - | src[i - reg]; - - src[i - reg] = cpu_to_be16(src[i - reg]); - } - - /* Actually write it out */ - return regmap_raw_write(wm8350->regmap, reg, src, bytes); -} - /* * Safe read, modify, write methods */ int wm8350_clear_bits(struct wm8350 *wm8350, u16 reg, u16 mask) { - u16 data; - int err; - - mutex_lock(&io_mutex); - err = wm8350_read(wm8350, reg, 1, &data); - if (err) { - dev_err(wm8350->dev, "read from reg R%d failed\n", reg); - goto out; - } - - data &= ~mask; - err = wm8350_write(wm8350, reg, 1, &data); - if (err) - dev_err(wm8350->dev, "write to reg R%d failed\n", reg); -out: - mutex_unlock(&io_mutex); - return err; + return regmap_update_bits(wm8350->regmap, reg, mask, 0); } EXPORT_SYMBOL_GPL(wm8350_clear_bits); int wm8350_set_bits(struct wm8350 *wm8350, u16 reg, u16 mask) { - u16 data; - int err; - - mutex_lock(&io_mutex); - err = wm8350_read(wm8350, reg, 1, &data); - if (err) { - dev_err(wm8350->dev, "read from reg R%d failed\n", reg); - goto out; - } - - data |= mask; - err = wm8350_write(wm8350, reg, 1, &data); - if (err) - dev_err(wm8350->dev, "write to reg R%d failed\n", reg); -out: - mutex_unlock(&io_mutex); - return err; + return regmap_update_bits(wm8350->regmap, reg, mask, mask); } EXPORT_SYMBOL_GPL(wm8350_set_bits); u16 wm8350_reg_read(struct wm8350 *wm8350, int reg) { - u16 data; + unsigned int data; int err; - mutex_lock(&io_mutex); - err = wm8350_read(wm8350, reg, 1, &data); + err = regmap_read(wm8350->regmap, reg, &data); if (err) dev_err(wm8350->dev, "read from reg R%d failed\n", reg); - mutex_unlock(&io_mutex); return data; } EXPORT_SYMBOL_GPL(wm8350_reg_read); @@ -242,13 +93,11 @@ EXPORT_SYMBOL_GPL(wm8350_reg_read); int wm8350_reg_write(struct wm8350 *wm8350, int reg, u16 val) { int ret; - u16 data = val; - mutex_lock(&io_mutex); - ret = wm8350_write(wm8350, reg, 1, &data); + ret = regmap_write(wm8350->regmap, reg, val); + if (ret) dev_err(wm8350->dev, "write to reg R%d failed\n", reg); - mutex_unlock(&io_mutex); return ret; } EXPORT_SYMBOL_GPL(wm8350_reg_write); @@ -258,12 +107,11 @@ int wm8350_block_read(struct wm8350 *wm8350, int start_reg, int regs, { int err = 0; - mutex_lock(&io_mutex); - err = wm8350_read(wm8350, start_reg, regs, dest); + err = regmap_bulk_read(wm8350->regmap, start_reg, dest, regs); if (err) dev_err(wm8350->dev, "block read starting from R%d failed\n", start_reg); - mutex_unlock(&io_mutex); + return err; } EXPORT_SYMBOL_GPL(wm8350_block_read); @@ -273,12 +121,11 @@ int wm8350_block_write(struct wm8350 *wm8350, int start_reg, int regs, { int ret = 0; - mutex_lock(&io_mutex); - ret = wm8350_write(wm8350, start_reg, regs, src); + ret = regmap_bulk_write(wm8350->regmap, start_reg, src, regs); if (ret) dev_err(wm8350->dev, "block write starting at R%d failed\n", start_reg); - mutex_unlock(&io_mutex); + return ret; } EXPORT_SYMBOL_GPL(wm8350_block_write); @@ -401,146 +248,6 @@ static irqreturn_t wm8350_auxadc_irq(int irq, void *irq_data) return IRQ_HANDLED; } -/* - * Cache is always host endian. - */ -static int wm8350_create_cache(struct wm8350 *wm8350, int type, int mode) -{ - int i, ret = 0; - u16 value; - const u16 *reg_map; - - switch (type) { - case 0: - switch (mode) { -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0 - case 0: - reg_map = wm8350_mode0_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_1 - case 1: - reg_map = wm8350_mode1_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_2 - case 2: - reg_map = wm8350_mode2_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_3 - case 3: - reg_map = wm8350_mode3_defaults; - break; -#endif - default: - dev_err(wm8350->dev, - "WM8350 configuration mode %d not supported\n", - mode); - return -EINVAL; - } - break; - - case 1: - switch (mode) { -#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_0 - case 0: - reg_map = wm8351_mode0_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_1 - case 1: - reg_map = wm8351_mode1_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_2 - case 2: - reg_map = wm8351_mode2_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_3 - case 3: - reg_map = wm8351_mode3_defaults; - break; -#endif - default: - dev_err(wm8350->dev, - "WM8351 configuration mode %d not supported\n", - mode); - return -EINVAL; - } - break; - - case 2: - switch (mode) { -#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_0 - case 0: - reg_map = wm8352_mode0_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_1 - case 1: - reg_map = wm8352_mode1_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_2 - case 2: - reg_map = wm8352_mode2_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_3 - case 3: - reg_map = wm8352_mode3_defaults; - break; -#endif - default: - dev_err(wm8350->dev, - "WM8352 configuration mode %d not supported\n", - mode); - return -EINVAL; - } - break; - - default: - dev_err(wm8350->dev, - "WM835x configuration mode %d not supported\n", - mode); - return -EINVAL; - } - - wm8350->reg_cache = - kmalloc(sizeof(u16) * (WM8350_MAX_REGISTER + 1), GFP_KERNEL); - if (wm8350->reg_cache == NULL) - return -ENOMEM; - - /* Read the initial cache state back from the device - this is - * a PMIC so the device many not be in a virgin state and we - * can't rely on the silicon values. - */ - ret = regmap_raw_read(wm8350->regmap, 0, wm8350->reg_cache, - sizeof(u16) * (WM8350_MAX_REGISTER + 1)); - if (ret < 0) { - dev_err(wm8350->dev, - "failed to read initial cache values\n"); - goto out; - } - - /* Mask out uncacheable/unreadable bits and the audio. */ - for (i = 0; i < WM8350_MAX_REGISTER; i++) { - if (wm8350_reg_io_map[i].readable && - (i < WM8350_CLOCK_CONTROL_1 || i > WM8350_AIF_TEST)) { - value = be16_to_cpu(wm8350->reg_cache[i]); - value &= wm8350_reg_io_map[i].readable; - wm8350->reg_cache[i] = value; - } else - wm8350->reg_cache[i] = reg_map[i]; - } - -out: - kfree(wm8350->reg_cache); - return ret; -} - /* * Register a client device. This is non-fatal since there is no need to * fail the entire device init due to a single platform device failing. @@ -688,18 +395,12 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq, goto err; } - ret = wm8350_create_cache(wm8350, mask_rev, mode); - if (ret < 0) { - dev_err(wm8350->dev, "Failed to create register cache\n"); - return ret; - } - mutex_init(&wm8350->auxadc_mutex); init_completion(&wm8350->auxadc_done); ret = wm8350_irq_init(wm8350, irq, pdata); if (ret < 0) - goto err_free; + goto err; if (wm8350->irq_base) { ret = request_threaded_irq(wm8350->irq_base + @@ -737,8 +438,6 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq, err_irq: wm8350_irq_exit(wm8350); -err_free: - kfree(wm8350->reg_cache); err: return ret; } @@ -765,8 +464,6 @@ void wm8350_device_exit(struct wm8350 *wm8350) free_irq(wm8350->irq_base + WM8350_IRQ_AUXADC_DATARDY, wm8350); wm8350_irq_exit(wm8350); - - kfree(wm8350->reg_cache); } EXPORT_SYMBOL_GPL(wm8350_device_exit); diff --git a/drivers/mfd/wm8350-regmap.c b/drivers/mfd/wm8350-regmap.c index 7974cadaa422..9efc64750fb6 100644 --- a/drivers/mfd/wm8350-regmap.c +++ b/drivers/mfd/wm8350-regmap.c @@ -14,3170 +14,18 @@ #include -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8350_mode0_defaults[] = { - 0x17FF, /* R0 - Reset/ID */ - 0x1000, /* R1 - ID */ - 0x0000, /* R2 */ - 0x1002, /* R3 - System Control 1 */ - 0x0004, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 - Power Up Interrupt Status */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 - Power Up Interrupt Status Mask */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3B00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - LOUT1 Volume */ - 0x00E4, /* R105 - ROUT1 Volume */ - 0x00E4, /* R106 - LOUT2 Volume */ - 0x02E4, /* R107 - ROUT2 Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 - AIF Test */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x03FC, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x0FFC, /* R134 - GPIO Configuration (i/o) */ - 0x0FFC, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0013, /* R140 - GPIO Function Select 1 */ - 0x0000, /* R141 - GPIO Function Select 2 */ - 0x0000, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 - Current Sink Driver B */ - 0x0000, /* R175 - CSB Flash control */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x002D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x000E, /* R180 - DCDC1 Control */ - 0x0000, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0000, /* R186 - DCDC3 Control */ - 0x0000, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0000, /* R189 - DCDC4 Control */ - 0x0000, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 - DCDC5 Control */ - 0x0000, /* R193 - DCDC5 Timeouts */ - 0x0000, /* R194 */ - 0x0000, /* R195 - DCDC6 Control */ - 0x0000, /* R196 - DCDC6 Timeouts */ - 0x0006, /* R197 - DCDC6 Low Power */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x001C, /* R200 - LDO1 Control */ - 0x0000, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x001B, /* R203 - LDO2 Control */ - 0x0000, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x001B, /* R206 - LDO3 Control */ - 0x0000, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001B, /* R209 - LDO4 Control */ - 0x0000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 */ - 0x4000, /* R220 - RAM BIST 1 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 */ - 0x0000, /* R227 */ - 0x0000, /* R228 */ - 0x0000, /* R229 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 */ - 0x0000, /* R232 */ - 0x0000, /* R233 */ - 0x0000, /* R234 */ - 0x0000, /* R235 */ - 0x0000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0000, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0000, /* R243 */ - 0x0000, /* R244 */ - 0x0000, /* R245 */ - 0x0000, /* R246 */ - 0x0000, /* R247 */ - 0x0000, /* R248 */ - 0x0000, /* R249 */ - 0x0000, /* R250 */ - 0x0000, /* R251 */ - 0x0000, /* R252 */ - 0x0000, /* R253 */ - 0x0000, /* R254 */ - 0x0000, /* R255 */ -}; -#endif - -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_1 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8350_mode1_defaults[] = { - 0x17FF, /* R0 - Reset/ID */ - 0x1000, /* R1 - ID */ - 0x0000, /* R2 */ - 0x1002, /* R3 - System Control 1 */ - 0x0014, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 - Power Up Interrupt Status */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 - Power Up Interrupt Status Mask */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3B00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - LOUT1 Volume */ - 0x00E4, /* R105 - ROUT1 Volume */ - 0x00E4, /* R106 - LOUT2 Volume */ - 0x02E4, /* R107 - ROUT2 Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 - AIF Test */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x03FC, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x00FB, /* R134 - GPIO Configuration (i/o) */ - 0x04FE, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0312, /* R140 - GPIO Function Select 1 */ - 0x1003, /* R141 - GPIO Function Select 2 */ - 0x1331, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 - Current Sink Driver B */ - 0x0000, /* R175 - CSB Flash control */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x002D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x0062, /* R180 - DCDC1 Control */ - 0x0400, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0026, /* R186 - DCDC3 Control */ - 0x0400, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0062, /* R189 - DCDC4 Control */ - 0x0400, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 - DCDC5 Control */ - 0x0000, /* R193 - DCDC5 Timeouts */ - 0x0000, /* R194 */ - 0x0026, /* R195 - DCDC6 Control */ - 0x0800, /* R196 - DCDC6 Timeouts */ - 0x0006, /* R197 - DCDC6 Low Power */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x0006, /* R200 - LDO1 Control */ - 0x0400, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x0006, /* R203 - LDO2 Control */ - 0x0400, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x001B, /* R206 - LDO3 Control */ - 0x0000, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001B, /* R209 - LDO4 Control */ - 0x0000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 */ - 0x4000, /* R220 - RAM BIST 1 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 */ - 0x0000, /* R227 */ - 0x0000, /* R228 */ - 0x0000, /* R229 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 */ - 0x0000, /* R232 */ - 0x0000, /* R233 */ - 0x0000, /* R234 */ - 0x0000, /* R235 */ - 0x0000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0000, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0000, /* R243 */ - 0x0000, /* R244 */ - 0x0000, /* R245 */ - 0x0000, /* R246 */ - 0x0000, /* R247 */ - 0x0000, /* R248 */ - 0x0000, /* R249 */ - 0x0000, /* R250 */ - 0x0000, /* R251 */ - 0x0000, /* R252 */ - 0x0000, /* R253 */ - 0x0000, /* R254 */ - 0x0000, /* R255 */ -}; -#endif - -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_2 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8350_mode2_defaults[] = { - 0x17FF, /* R0 - Reset/ID */ - 0x1000, /* R1 - ID */ - 0x0000, /* R2 */ - 0x1002, /* R3 - System Control 1 */ - 0x0014, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 - Power Up Interrupt Status */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 - Power Up Interrupt Status Mask */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3B00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - LOUT1 Volume */ - 0x00E4, /* R105 - ROUT1 Volume */ - 0x00E4, /* R106 - LOUT2 Volume */ - 0x02E4, /* R107 - ROUT2 Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 - AIF Test */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x03FC, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x08FB, /* R134 - GPIO Configuration (i/o) */ - 0x0CFE, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0312, /* R140 - GPIO Function Select 1 */ - 0x0003, /* R141 - GPIO Function Select 2 */ - 0x2331, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 - Current Sink Driver B */ - 0x0000, /* R175 - CSB Flash control */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x002D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x000E, /* R180 - DCDC1 Control */ - 0x0400, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x002E, /* R186 - DCDC3 Control */ - 0x0800, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x000E, /* R189 - DCDC4 Control */ - 0x0800, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 - DCDC5 Control */ - 0x0000, /* R193 - DCDC5 Timeouts */ - 0x0000, /* R194 */ - 0x0026, /* R195 - DCDC6 Control */ - 0x0C00, /* R196 - DCDC6 Timeouts */ - 0x0006, /* R197 - DCDC6 Low Power */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x001A, /* R200 - LDO1 Control */ - 0x0800, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x0010, /* R203 - LDO2 Control */ - 0x0800, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x000A, /* R206 - LDO3 Control */ - 0x0C00, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001A, /* R209 - LDO4 Control */ - 0x0800, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 */ - 0x4000, /* R220 - RAM BIST 1 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 */ - 0x0000, /* R227 */ - 0x0000, /* R228 */ - 0x0000, /* R229 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 */ - 0x0000, /* R232 */ - 0x0000, /* R233 */ - 0x0000, /* R234 */ - 0x0000, /* R235 */ - 0x0000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0000, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0000, /* R243 */ - 0x0000, /* R244 */ - 0x0000, /* R245 */ - 0x0000, /* R246 */ - 0x0000, /* R247 */ - 0x0000, /* R248 */ - 0x0000, /* R249 */ - 0x0000, /* R250 */ - 0x0000, /* R251 */ - 0x0000, /* R252 */ - 0x0000, /* R253 */ - 0x0000, /* R254 */ - 0x0000, /* R255 */ -}; -#endif - -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_3 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8350_mode3_defaults[] = { - 0x17FF, /* R0 - Reset/ID */ - 0x1000, /* R1 - ID */ - 0x0000, /* R2 */ - 0x1000, /* R3 - System Control 1 */ - 0x0004, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 - Power Up Interrupt Status */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 - Power Up Interrupt Status Mask */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3B00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - LOUT1 Volume */ - 0x00E4, /* R105 - ROUT1 Volume */ - 0x00E4, /* R106 - LOUT2 Volume */ - 0x02E4, /* R107 - ROUT2 Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 - AIF Test */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x03FC, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x0A7B, /* R134 - GPIO Configuration (i/o) */ - 0x06FE, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x1312, /* R140 - GPIO Function Select 1 */ - 0x1030, /* R141 - GPIO Function Select 2 */ - 0x2231, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 - Current Sink Driver B */ - 0x0000, /* R175 - CSB Flash control */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x002D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x000E, /* R180 - DCDC1 Control */ - 0x0400, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x000E, /* R186 - DCDC3 Control */ - 0x0400, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0026, /* R189 - DCDC4 Control */ - 0x0400, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 - DCDC5 Control */ - 0x0000, /* R193 - DCDC5 Timeouts */ - 0x0000, /* R194 */ - 0x0026, /* R195 - DCDC6 Control */ - 0x0400, /* R196 - DCDC6 Timeouts */ - 0x0006, /* R197 - DCDC6 Low Power */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x001C, /* R200 - LDO1 Control */ - 0x0000, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x001C, /* R203 - LDO2 Control */ - 0x0400, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x001C, /* R206 - LDO3 Control */ - 0x0400, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001F, /* R209 - LDO4 Control */ - 0x0400, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 */ - 0x4000, /* R220 - RAM BIST 1 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 */ - 0x0000, /* R227 */ - 0x0000, /* R228 */ - 0x0000, /* R229 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 */ - 0x0000, /* R232 */ - 0x0000, /* R233 */ - 0x0000, /* R234 */ - 0x0000, /* R235 */ - 0x0000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0000, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0000, /* R243 */ - 0x0000, /* R244 */ - 0x0000, /* R245 */ - 0x0000, /* R246 */ - 0x0000, /* R247 */ - 0x0000, /* R248 */ - 0x0000, /* R249 */ - 0x0000, /* R250 */ - 0x0000, /* R251 */ - 0x0000, /* R252 */ - 0x0000, /* R253 */ - 0x0000, /* R254 */ - 0x0000, /* R255 */ -}; -#endif - -#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_0 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8351_mode0_defaults[] = { - 0x6143, /* R0 - Reset/ID */ - 0x0000, /* R1 - ID */ - 0x0001, /* R2 - Revision */ - 0x1C02, /* R3 - System Control 1 */ - 0x0004, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3A00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - OUT1L Volume */ - 0x00E4, /* R105 - OUT1R Volume */ - 0x00E4, /* R106 - OUT2L Volume */ - 0x02E4, /* R107 - OUT2R Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x0000, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x0FFC, /* R134 - GPIO Configuration (i/o) */ - 0x0FFC, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0013, /* R140 - GPIO Function Select 1 */ - 0x0000, /* R141 - GPIO Function Select 2 */ - 0x0000, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 */ - 0x0000, /* R175 */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x032D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x000E, /* R180 - DCDC1 Control */ - 0x0000, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0000, /* R186 - DCDC3 Control */ - 0x0000, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0000, /* R189 - DCDC4 Control */ - 0x0000, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 */ - 0x0000, /* R193 */ - 0x0000, /* R194 */ - 0x0000, /* R195 */ - 0x0000, /* R196 */ - 0x0006, /* R197 */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x001C, /* R200 - LDO1 Control */ - 0x0000, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x001B, /* R203 - LDO2 Control */ - 0x0000, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x001B, /* R206 - LDO3 Control */ - 0x0000, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001B, /* R209 - LDO4 Control */ - 0x0000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 - Security1 */ - 0x4000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 - Signal overrides */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 - Charger Overides/status */ - 0x0000, /* R227 - misc overrides */ - 0x0000, /* R228 - Supply overrides/status 1 */ - 0x0000, /* R229 - Supply overrides/status 2 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 - comparotor overrides */ - 0x0000, /* R232 */ - 0x0000, /* R233 - State Machine status */ - 0x1200, /* R234 - FLL Test 1 */ - 0x0000, /* R235 */ - 0x8000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0003, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0004, /* R243 */ - 0x0300, /* R244 */ - 0x0000, /* R245 */ - 0x0200, /* R246 */ - 0x0000, /* R247 */ - 0x1000, /* R248 - DCDC1 Test Controls */ - 0x1000, /* R249 */ - 0x1000, /* R250 - DCDC3 Test Controls */ - 0x1000, /* R251 - DCDC4 Test Controls */ -}; -#endif - -#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_1 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8351_mode1_defaults[] = { - 0x6143, /* R0 - Reset/ID */ - 0x0000, /* R1 - ID */ - 0x0001, /* R2 - Revision */ - 0x1C02, /* R3 - System Control 1 */ - 0x0204, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3A00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - OUT1L Volume */ - 0x00E4, /* R105 - OUT1R Volume */ - 0x00E4, /* R106 - OUT2L Volume */ - 0x02E4, /* R107 - OUT2R Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x0000, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x0CFB, /* R134 - GPIO Configuration (i/o) */ - 0x0C1F, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0300, /* R140 - GPIO Function Select 1 */ - 0x1110, /* R141 - GPIO Function Select 2 */ - 0x0013, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 */ - 0x0000, /* R175 */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x032D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x000E, /* R180 - DCDC1 Control */ - 0x0C00, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0026, /* R186 - DCDC3 Control */ - 0x0400, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0062, /* R189 - DCDC4 Control */ - 0x0800, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 */ - 0x0000, /* R193 */ - 0x0000, /* R194 */ - 0x000A, /* R195 */ - 0x1000, /* R196 */ - 0x0006, /* R197 */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x0006, /* R200 - LDO1 Control */ - 0x0000, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x0010, /* R203 - LDO2 Control */ - 0x0C00, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x001F, /* R206 - LDO3 Control */ - 0x0800, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x000A, /* R209 - LDO4 Control */ - 0x0800, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 - Security1 */ - 0x4000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 - Signal overrides */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 - Charger Overides/status */ - 0x0000, /* R227 - misc overrides */ - 0x0000, /* R228 - Supply overrides/status 1 */ - 0x0000, /* R229 - Supply overrides/status 2 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 - comparotor overrides */ - 0x0000, /* R232 */ - 0x0000, /* R233 - State Machine status */ - 0x1200, /* R234 - FLL Test 1 */ - 0x0000, /* R235 */ - 0x8000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0003, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0004, /* R243 */ - 0x0300, /* R244 */ - 0x0000, /* R245 */ - 0x0200, /* R246 */ - 0x1000, /* R247 */ - 0x1000, /* R248 - DCDC1 Test Controls */ - 0x1000, /* R249 */ - 0x1000, /* R250 - DCDC3 Test Controls */ - 0x1000, /* R251 - DCDC4 Test Controls */ -}; -#endif - -#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_2 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8351_mode2_defaults[] = { - 0x6143, /* R0 - Reset/ID */ - 0x0000, /* R1 - ID */ - 0x0001, /* R2 - Revision */ - 0x1C02, /* R3 - System Control 1 */ - 0x0214, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3A00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - OUT1L Volume */ - 0x00E4, /* R105 - OUT1R Volume */ - 0x00E4, /* R106 - OUT2L Volume */ - 0x02E4, /* R107 - OUT2R Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x0110, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x09FA, /* R134 - GPIO Configuration (i/o) */ - 0x0DF6, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x1310, /* R140 - GPIO Function Select 1 */ - 0x0003, /* R141 - GPIO Function Select 2 */ - 0x2000, /* R142 - GPIO Function Select 3 */ - 0x0000, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 */ - 0x0000, /* R175 */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x032D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x001A, /* R180 - DCDC1 Control */ - 0x0800, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0056, /* R186 - DCDC3 Control */ - 0x0400, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0026, /* R189 - DCDC4 Control */ - 0x0C00, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 */ - 0x0000, /* R193 */ - 0x0000, /* R194 */ - 0x0026, /* R195 */ - 0x0C00, /* R196 */ - 0x0006, /* R197 */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x001C, /* R200 - LDO1 Control */ - 0x0400, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x0010, /* R203 - LDO2 Control */ - 0x0C00, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x0015, /* R206 - LDO3 Control */ - 0x0000, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001A, /* R209 - LDO4 Control */ - 0x0000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 - Security1 */ - 0x4000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 - Signal overrides */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 - Charger Overides/status */ - 0x0000, /* R227 - misc overrides */ - 0x0000, /* R228 - Supply overrides/status 1 */ - 0x0000, /* R229 - Supply overrides/status 2 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 - comparotor overrides */ - 0x0000, /* R232 */ - 0x0000, /* R233 - State Machine status */ - 0x1200, /* R234 - FLL Test 1 */ - 0x0000, /* R235 */ - 0x8000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0003, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0004, /* R243 */ - 0x0300, /* R244 */ - 0x0000, /* R245 */ - 0x0200, /* R246 */ - 0x0000, /* R247 */ - 0x1000, /* R248 - DCDC1 Test Controls */ - 0x1000, /* R249 */ - 0x1000, /* R250 - DCDC3 Test Controls */ - 0x1000, /* R251 - DCDC4 Test Controls */ -}; -#endif - -#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_3 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8351_mode3_defaults[] = { - 0x6143, /* R0 - Reset/ID */ - 0x0000, /* R1 - ID */ - 0x0001, /* R2 - Revision */ - 0x1C02, /* R3 - System Control 1 */ - 0x0204, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3A00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - OUT1L Volume */ - 0x00E4, /* R105 - OUT1R Volume */ - 0x00E4, /* R106 - OUT2L Volume */ - 0x02E4, /* R107 - OUT2R Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0010, /* R129 - GPIO Pin pull up Control */ - 0x0000, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x0BFB, /* R134 - GPIO Configuration (i/o) */ - 0x0FFD, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0310, /* R140 - GPIO Function Select 1 */ - 0x0001, /* R141 - GPIO Function Select 2 */ - 0x2300, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 */ - 0x0000, /* R175 */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x032D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x000E, /* R180 - DCDC1 Control */ - 0x0400, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0026, /* R186 - DCDC3 Control */ - 0x0800, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0062, /* R189 - DCDC4 Control */ - 0x1400, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 */ - 0x0000, /* R193 */ - 0x0000, /* R194 */ - 0x0026, /* R195 */ - 0x0400, /* R196 */ - 0x0006, /* R197 */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x0006, /* R200 - LDO1 Control */ - 0x0C00, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x0016, /* R203 - LDO2 Control */ - 0x0000, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x0019, /* R206 - LDO3 Control */ - 0x0000, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001A, /* R209 - LDO4 Control */ - 0x1000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 - Security1 */ - 0x4000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 - Signal overrides */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 - Charger Overides/status */ - 0x0000, /* R227 - misc overrides */ - 0x0000, /* R228 - Supply overrides/status 1 */ - 0x0000, /* R229 - Supply overrides/status 2 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 - comparotor overrides */ - 0x0000, /* R232 */ - 0x0000, /* R233 - State Machine status */ - 0x1200, /* R234 - FLL Test 1 */ - 0x0000, /* R235 */ - 0x8000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0003, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0004, /* R243 */ - 0x0300, /* R244 */ - 0x0000, /* R245 */ - 0x0200, /* R246 */ - 0x0000, /* R247 */ - 0x1000, /* R248 - DCDC1 Test Controls */ - 0x1000, /* R249 */ - 0x1000, /* R250 - DCDC3 Test Controls */ - 0x1000, /* R251 - DCDC4 Test Controls */ -}; -#endif - -#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_0 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8352_mode0_defaults[] = { - 0x6143, /* R0 - Reset/ID */ - 0x0000, /* R1 - ID */ - 0x0002, /* R2 - Revision */ - 0x1C02, /* R3 - System Control 1 */ - 0x0004, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3A00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - OUT1L Volume */ - 0x00E4, /* R105 - OUT1R Volume */ - 0x00E4, /* R106 - OUT2L Volume */ - 0x02E4, /* R107 - OUT2R Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x0000, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x0FFC, /* R134 - GPIO Configuration (i/o) */ - 0x0FFC, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0013, /* R140 - GPIO Function Select 1 */ - 0x0000, /* R141 - GPIO Function Select 2 */ - 0x0000, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 - Current Sink Driver B */ - 0x0000, /* R175 - CSB Flash control */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x032D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x000E, /* R180 - DCDC1 Control */ - 0x0000, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0000, /* R186 - DCDC3 Control */ - 0x0000, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0000, /* R189 - DCDC4 Control */ - 0x0000, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 - DCDC5 Control */ - 0x0000, /* R193 - DCDC5 Timeouts */ - 0x0000, /* R194 */ - 0x0000, /* R195 - DCDC6 Control */ - 0x0000, /* R196 - DCDC6 Timeouts */ - 0x0006, /* R197 - DCDC6 Low Power */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x001C, /* R200 - LDO1 Control */ - 0x0000, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x001B, /* R203 - LDO2 Control */ - 0x0000, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x001B, /* R206 - LDO3 Control */ - 0x0000, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001B, /* R209 - LDO4 Control */ - 0x0000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 - Security1 */ - 0x4000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 - Signal overrides */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 - Charger Overides/status */ - 0x0000, /* R227 - misc overrides */ - 0x0000, /* R228 - Supply overrides/status 1 */ - 0x0000, /* R229 - Supply overrides/status 2 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 - comparotor overrides */ - 0x0000, /* R232 */ - 0x0000, /* R233 - State Machine status */ - 0x1200, /* R234 */ - 0x0000, /* R235 */ - 0x8000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0003, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0004, /* R243 */ - 0x0300, /* R244 */ - 0x0000, /* R245 */ - 0x0200, /* R246 */ - 0x0000, /* R247 */ - 0x1000, /* R248 - DCDC1 Test Controls */ - 0x5000, /* R249 */ - 0x1000, /* R250 - DCDC3 Test Controls */ - 0x1000, /* R251 - DCDC4 Test Controls */ - 0x5100, /* R252 */ - 0x1000, /* R253 - DCDC6 Test Controls */ -}; -#endif - -#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_1 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8352_mode1_defaults[] = { - 0x6143, /* R0 - Reset/ID */ - 0x0000, /* R1 - ID */ - 0x0002, /* R2 - Revision */ - 0x1C02, /* R3 - System Control 1 */ - 0x0204, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3A00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - OUT1L Volume */ - 0x00E4, /* R105 - OUT1R Volume */ - 0x00E4, /* R106 - OUT2L Volume */ - 0x02E4, /* R107 - OUT2R Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x0000, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x0BFB, /* R134 - GPIO Configuration (i/o) */ - 0x0FFF, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0300, /* R140 - GPIO Function Select 1 */ - 0x0000, /* R141 - GPIO Function Select 2 */ - 0x2300, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 - Current Sink Driver B */ - 0x0000, /* R175 - CSB Flash control */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x032D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x0062, /* R180 - DCDC1 Control */ - 0x0400, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0006, /* R186 - DCDC3 Control */ - 0x0800, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0006, /* R189 - DCDC4 Control */ - 0x0C00, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 - DCDC5 Control */ - 0x0000, /* R193 - DCDC5 Timeouts */ - 0x0000, /* R194 */ - 0x0026, /* R195 - DCDC6 Control */ - 0x1000, /* R196 - DCDC6 Timeouts */ - 0x0006, /* R197 - DCDC6 Low Power */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x0002, /* R200 - LDO1 Control */ - 0x0000, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x001A, /* R203 - LDO2 Control */ - 0x0000, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x001F, /* R206 - LDO3 Control */ - 0x0000, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001F, /* R209 - LDO4 Control */ - 0x0000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 - Security1 */ - 0x4000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 - Signal overrides */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 - Charger Overides/status */ - 0x0000, /* R227 - misc overrides */ - 0x0000, /* R228 - Supply overrides/status 1 */ - 0x0000, /* R229 - Supply overrides/status 2 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 - comparotor overrides */ - 0x0000, /* R232 */ - 0x0000, /* R233 - State Machine status */ - 0x1200, /* R234 */ - 0x0000, /* R235 */ - 0x8000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0003, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0004, /* R243 */ - 0x0300, /* R244 */ - 0x0000, /* R245 */ - 0x0200, /* R246 */ - 0x0000, /* R247 */ - 0x1000, /* R248 - DCDC1 Test Controls */ - 0x5000, /* R249 */ - 0x1000, /* R250 - DCDC3 Test Controls */ - 0x1000, /* R251 - DCDC4 Test Controls */ - 0x5100, /* R252 */ - 0x1000, /* R253 - DCDC6 Test Controls */ -}; -#endif - -#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_2 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8352_mode2_defaults[] = { - 0x6143, /* R0 - Reset/ID */ - 0x0000, /* R1 - ID */ - 0x0002, /* R2 - Revision */ - 0x1C02, /* R3 - System Control 1 */ - 0x0204, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3A00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - OUT1L Volume */ - 0x00E4, /* R105 - OUT1R Volume */ - 0x00E4, /* R106 - OUT2L Volume */ - 0x02E4, /* R107 - OUT2R Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x0110, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x09DA, /* R134 - GPIO Configuration (i/o) */ - 0x0DD6, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x1310, /* R140 - GPIO Function Select 1 */ - 0x0033, /* R141 - GPIO Function Select 2 */ - 0x2000, /* R142 - GPIO Function Select 3 */ - 0x0000, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 - Current Sink Driver B */ - 0x0000, /* R175 - CSB Flash control */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x032D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x000E, /* R180 - DCDC1 Control */ - 0x0800, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0056, /* R186 - DCDC3 Control */ - 0x1800, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x000E, /* R189 - DCDC4 Control */ - 0x1000, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 - DCDC5 Control */ - 0x0000, /* R193 - DCDC5 Timeouts */ - 0x0000, /* R194 */ - 0x0026, /* R195 - DCDC6 Control */ - 0x0C00, /* R196 - DCDC6 Timeouts */ - 0x0006, /* R197 - DCDC6 Low Power */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x001C, /* R200 - LDO1 Control */ - 0x0000, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x0006, /* R203 - LDO2 Control */ - 0x0400, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x001C, /* R206 - LDO3 Control */ - 0x1400, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001A, /* R209 - LDO4 Control */ - 0x0000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 - Security1 */ - 0x4000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 - Signal overrides */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 - Charger Overides/status */ - 0x0000, /* R227 - misc overrides */ - 0x0000, /* R228 - Supply overrides/status 1 */ - 0x0000, /* R229 - Supply overrides/status 2 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 - comparotor overrides */ - 0x0000, /* R232 */ - 0x0000, /* R233 - State Machine status */ - 0x1200, /* R234 */ - 0x0000, /* R235 */ - 0x8000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0003, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0004, /* R243 */ - 0x0300, /* R244 */ - 0x0000, /* R245 */ - 0x0200, /* R246 */ - 0x0000, /* R247 */ - 0x1000, /* R248 - DCDC1 Test Controls */ - 0x5000, /* R249 */ - 0x1000, /* R250 - DCDC3 Test Controls */ - 0x1000, /* R251 - DCDC4 Test Controls */ - 0x5100, /* R252 */ - 0x1000, /* R253 - DCDC6 Test Controls */ -}; -#endif - -#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_3 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8352_mode3_defaults[] = { - 0x6143, /* R0 - Reset/ID */ - 0x0000, /* R1 - ID */ - 0x0002, /* R2 - Revision */ - 0x1C02, /* R3 - System Control 1 */ - 0x0204, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3A00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - OUT1L Volume */ - 0x00E4, /* R105 - OUT1R Volume */ - 0x00E4, /* R106 - OUT2L Volume */ - 0x02E4, /* R107 - OUT2R Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0010, /* R129 - GPIO Pin pull up Control */ - 0x0000, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x0BFB, /* R134 - GPIO Configuration (i/o) */ - 0x0FFD, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0310, /* R140 - GPIO Function Select 1 */ - 0x0001, /* R141 - GPIO Function Select 2 */ - 0x2300, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 - Current Sink Driver B */ - 0x0000, /* R175 - CSB Flash control */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x032D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x0006, /* R180 - DCDC1 Control */ - 0x0400, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0050, /* R186 - DCDC3 Control */ - 0x0C00, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x000E, /* R189 - DCDC4 Control */ - 0x0400, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 - DCDC5 Control */ - 0x0000, /* R193 - DCDC5 Timeouts */ - 0x0000, /* R194 */ - 0x0029, /* R195 - DCDC6 Control */ - 0x0800, /* R196 - DCDC6 Timeouts */ - 0x0006, /* R197 - DCDC6 Low Power */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x001D, /* R200 - LDO1 Control */ - 0x1000, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x0017, /* R203 - LDO2 Control */ - 0x1000, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x0006, /* R206 - LDO3 Control */ - 0x1000, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x0010, /* R209 - LDO4 Control */ - 0x1000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 - Security1 */ - 0x4000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 - Signal overrides */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 - Charger Overides/status */ - 0x0000, /* R227 - misc overrides */ - 0x0000, /* R228 - Supply overrides/status 1 */ - 0x0000, /* R229 - Supply overrides/status 2 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 - comparotor overrides */ - 0x0000, /* R232 */ - 0x0000, /* R233 - State Machine status */ - 0x1200, /* R234 */ - 0x0000, /* R235 */ - 0x8000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0003, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0004, /* R243 */ - 0x0300, /* R244 */ - 0x0000, /* R245 */ - 0x0200, /* R246 */ - 0x0000, /* R247 */ - 0x1000, /* R248 - DCDC1 Test Controls */ - 0x5000, /* R249 */ - 0x1000, /* R250 - DCDC3 Test Controls */ - 0x1000, /* R251 - DCDC4 Test Controls */ - 0x5100, /* R252 */ - 0x1000, /* R253 - DCDC6 Test Controls */ -}; -#endif - /* * Access masks. */ -const struct wm8350_reg_access wm8350_reg_io_map[] = { +static const struct wm8350_reg_access { + u16 readable; /* Mask of readable bits */ + u16 writable; /* Mask of writable bits */ + u16 vol; /* Mask of volatile bits */ +} wm8350_reg_io_map[] = { /* read write volatile */ - { 0xFFFF, 0xFFFF, 0xFFFF }, /* R0 - Reset/ID */ - { 0x7CFF, 0x0C00, 0x7FFF }, /* R1 - ID */ + { 0xFFFF, 0xFFFF, 0x0000 }, /* R0 - Reset/ID */ + { 0x7CFF, 0x0C00, 0x0000 }, /* R1 - ID */ { 0x007F, 0x0000, 0x0000 }, /* R2 - ROM Mask ID */ { 0xBE3B, 0xBE3B, 0x8000 }, /* R3 - System Control 1 */ { 0xFEF7, 0xFEF7, 0xF800 }, /* R4 - System Control 2 */ diff --git a/include/linux/mfd/wm8350/core.h b/include/linux/mfd/wm8350/core.h index cba9bc8f947b..509481d9cf19 100644 --- a/include/linux/mfd/wm8350/core.h +++ b/include/linux/mfd/wm8350/core.h @@ -587,24 +587,6 @@ #define WM8350_NUM_IRQ_REGS 7 extern const struct regmap_config wm8350_regmap; -struct wm8350_reg_access { - u16 readable; /* Mask of readable bits */ - u16 writable; /* Mask of writable bits */ - u16 vol; /* Mask of volatile bits */ -}; -extern const struct wm8350_reg_access wm8350_reg_io_map[]; -extern const u16 wm8350_mode0_defaults[]; -extern const u16 wm8350_mode1_defaults[]; -extern const u16 wm8350_mode2_defaults[]; -extern const u16 wm8350_mode3_defaults[]; -extern const u16 wm8351_mode0_defaults[]; -extern const u16 wm8351_mode1_defaults[]; -extern const u16 wm8351_mode2_defaults[]; -extern const u16 wm8351_mode3_defaults[]; -extern const u16 wm8352_mode0_defaults[]; -extern const u16 wm8352_mode1_defaults[]; -extern const u16 wm8352_mode2_defaults[]; -extern const u16 wm8352_mode3_defaults[]; struct wm8350; @@ -618,7 +600,6 @@ struct wm8350 { /* device IO */ struct regmap *regmap; - u16 *reg_cache; bool unlocked; struct mutex auxadc_mutex; -- cgit v1.2.3 From 5261e101198e7ef31a60d3aa97815a49c8b8fa20 Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Mon, 21 May 2012 14:28:21 +0530 Subject: mfd: Update db8500-prmcu hostport_access enable Force the Modem wakeup by asserting the CaWakeReq signal before the hostaccess_req/ack ping-pong sequence. The Awake_req signal is de-asserted asserted at the same time than the hostaccess_req. Return error on failure case so that the client using this can take appropiate steps. Signed-off-by: Arun Murthy Acked-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 45 +++++++++++++++++----------------------- drivers/mfd/dbx500-prcmu-regs.h | 1 + include/linux/mfd/db8500-prcmu.h | 7 +++++-- include/linux/mfd/dbx500-prcmu.h | 7 +++++-- 4 files changed, 30 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index bf5a054a2b91..f4adcabb2a51 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2269,10 +2269,10 @@ int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) /** * prcmu_ac_wake_req - should be called whenever ARM wants to wakeup Modem */ -void prcmu_ac_wake_req(void) +int prcmu_ac_wake_req(void) { u32 val; - u32 status; + int ret = 0; mutex_lock(&mb0_transfer.ac_wake_lock); @@ -2282,39 +2282,32 @@ void prcmu_ac_wake_req(void) atomic_set(&ac_wake_req_state, 1); -retry: - writel((val | PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ), PRCM_HOSTACCESS_REQ); + /* + * Force Modem Wake-up before hostaccess_req ping-pong. + * It prevents Modem to enter in Sleep while acking the hostaccess + * request. The 31us delay has been calculated by HWI. + */ + val |= PRCM_HOSTACCESS_REQ_WAKE_REQ; + writel(val, PRCM_HOSTACCESS_REQ); + + udelay(31); + + val |= PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ; + writel(val, PRCM_HOSTACCESS_REQ); if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work, msecs_to_jiffies(5000))) { +#if defined(CONFIG_DBX500_PRCMU_DEBUG) + db8500_prcmu_debug_dump(__func__, true, true); +#endif pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n", __func__); - goto unlock_and_return; - } - - /* - * The modem can generate an AC_WAKE_ACK, and then still go to sleep. - * As a workaround, we wait, and then check that the modem is indeed - * awake (in terms of the value of the PRCM_MOD_AWAKE_STATUS - * register, which may not be the whole truth). - */ - udelay(400); - status = (readl(PRCM_MOD_AWAKE_STATUS) & BITS(0, 2)); - if (status != (PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE | - PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE)) { - pr_err("prcmu: %s received ack, but modem not awake (0x%X).\n", - __func__, status); - udelay(1200); - writel(val, PRCM_HOSTACCESS_REQ); - if (wait_for_completion_timeout(&mb0_transfer.ac_wake_work, - msecs_to_jiffies(5000))) - goto retry; - pr_crit("prcmu: %s timed out (5 s) waiting for AC_SLEEP_ACK.\n", - __func__); + ret = -EFAULT; } unlock_and_return: mutex_unlock(&mb0_transfer.ac_wake_lock); + return ret; } /** diff --git a/drivers/mfd/dbx500-prcmu-regs.h b/drivers/mfd/dbx500-prcmu-regs.h index 3a0bf91d7780..23108a6e3167 100644 --- a/drivers/mfd/dbx500-prcmu-regs.h +++ b/drivers/mfd/dbx500-prcmu-regs.h @@ -106,6 +106,7 @@ #define PRCM_HOSTACCESS_REQ (_PRCMU_BASE + 0x334) #define PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ 0x1 +#define PRCM_HOSTACCESS_REQ_WAKE_REQ BIT(16) #define ARM_WAKEUP_MODEM 0x1 #define PRCM_ARM_IT1_CLR (_PRCMU_BASE + 0x48C) diff --git a/include/linux/mfd/db8500-prcmu.h b/include/linux/mfd/db8500-prcmu.h index b3a43b1263fe..b82f6ee66a0b 100644 --- a/include/linux/mfd/db8500-prcmu.h +++ b/include/linux/mfd/db8500-prcmu.h @@ -530,7 +530,7 @@ int db8500_prcmu_stop_temp_sense(void); int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size); int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size); -void prcmu_ac_wake_req(void); +int prcmu_ac_wake_req(void); void prcmu_ac_sleep_req(void); void db8500_prcmu_modem_reset(void); @@ -680,7 +680,10 @@ static inline int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) return -ENOSYS; } -static inline void prcmu_ac_wake_req(void) {} +static inline int prcmu_ac_wake_req(void) +{ + return 0; +} static inline void prcmu_ac_sleep_req(void) {} diff --git a/include/linux/mfd/dbx500-prcmu.h b/include/linux/mfd/dbx500-prcmu.h index 5a13f93d8f1c..5b90e94399e1 100644 --- a/include/linux/mfd/dbx500-prcmu.h +++ b/include/linux/mfd/dbx500-prcmu.h @@ -345,7 +345,7 @@ static inline u16 prcmu_get_reset_code(void) return db8500_prcmu_get_reset_code(); } -void prcmu_ac_wake_req(void); +int prcmu_ac_wake_req(void); void prcmu_ac_sleep_req(void); static inline void prcmu_modem_reset(void) { @@ -533,7 +533,10 @@ static inline u16 prcmu_get_reset_code(void) return 0; } -static inline void prcmu_ac_wake_req(void) {} +static inline int prcmu_ac_wake_req(void) +{ + return 0; +} static inline void prcmu_ac_sleep_req(void) {} -- cgit v1.2.3 From 2968ab133ec790134d4347aa4264c2eb064b42e7 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 2 Jul 2012 10:50:19 +0100 Subject: mfd: Attaching a node to new 'struct mfd_cell' of_compatible variable Applying a succinct description to the of_compatible variable recently added to the mfd_cell struct. Also link to the documentation page where more information can be found about compatible properties. Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- include/linux/mfd/core.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index 99b7eb1961b6..3a8435a8058f 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -36,6 +36,10 @@ struct mfd_cell { /* platform data passed to the sub devices drivers */ void *platform_data; size_t pdata_size; + /* + * Device Tree compatible string + * See: Documentation/devicetree/usage-model.txt Chapter 2.2 for details + */ const char *of_compatible; /* -- cgit v1.2.3 From b41511f713ccaef666e450fae8cb18909897fe4e Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Mon, 2 Jul 2012 09:02:55 +0900 Subject: mfd: Add irq domain support for max8997 interrupts Add irq domain support for max8997 interrupts. The reverse mapping method used is linear mapping since the sub-drivers of max8997 such as regulator and charger drivers can use the max8997 irq_domain to get the linux irq number for max8997 interrupts. All uses of irq_base in platform data and max8997 driver private data are removed. Reviwed-by: Mark Brown Acked-by: MyungJoo Ham Acked-by: Grant Likely Signed-off-by: Thomas Abraham Signed-off-by: Chanwoo Choi Signed-off-by: Kyungmin Park Signed-off-by: Samuel Ortiz --- arch/arm/mach-exynos/mach-nuri.c | 4 --- arch/arm/mach-exynos/mach-origen.c | 1 - drivers/mfd/Kconfig | 1 + drivers/mfd/max8997-irq.c | 62 ++++++++++++++++++++++--------------- drivers/mfd/max8997.c | 1 - include/linux/mfd/max8997-private.h | 4 ++- include/linux/mfd/max8997.h | 1 - 7 files changed, 41 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-exynos/mach-nuri.c b/arch/arm/mach-exynos/mach-nuri.c index 656f8fc9addd..acb58f5cee8d 100644 --- a/arch/arm/mach-exynos/mach-nuri.c +++ b/arch/arm/mach-exynos/mach-nuri.c @@ -1067,12 +1067,8 @@ static struct platform_device nuri_max8903_device = { static void __init nuri_power_init(void) { int gpio; - int irq_base = IRQ_GPIO_END + 1; int ta_en = 0; - nuri_max8997_pdata.irq_base = irq_base; - irq_base += MAX8997_IRQ_NR; - gpio = EXYNOS4_GPX0(7); gpio_request(gpio, "AP_PMIC_IRQ"); s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(0xf)); diff --git a/arch/arm/mach-exynos/mach-origen.c b/arch/arm/mach-exynos/mach-origen.c index f5572be9d7bf..3ce403d7cf1c 100644 --- a/arch/arm/mach-exynos/mach-origen.c +++ b/arch/arm/mach-exynos/mach-origen.c @@ -425,7 +425,6 @@ static struct max8997_platform_data __initdata origen_max8997_pdata = { .buck1_gpiodvs = false, .buck2_gpiodvs = false, .buck5_gpiodvs = false, - .irq_base = IRQ_GPIO_END + 1, .ignore_gpiodvs_side_effect = true, .buck125_default_idx = 0x0, diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index b3dee92fd3a8..aaa048a437c0 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -464,6 +464,7 @@ config MFD_MAX8997 bool "Maxim Semiconductor MAX8997/8966 PMIC Support" depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE + select IRQ_DOMAIN help Say yes here to support for Maxim Semiconductor MAX8997/8966. This is a Power Management IC with RTC, Flash, Fuel Gauge, Haptic, diff --git a/drivers/mfd/max8997-irq.c b/drivers/mfd/max8997-irq.c index 09274cf7c33b..43fa61413e93 100644 --- a/drivers/mfd/max8997-irq.c +++ b/drivers/mfd/max8997-irq.c @@ -142,7 +142,8 @@ static void max8997_irq_sync_unlock(struct irq_data *data) static const inline struct max8997_irq_data * irq_to_max8997_irq(struct max8997_dev *max8997, int irq) { - return &max8997_irqs[irq - max8997->irq_base]; + struct irq_data *data = irq_get_irq_data(irq); + return &max8997_irqs[data->hwirq]; } static void max8997_irq_mask(struct irq_data *data) @@ -182,7 +183,7 @@ static irqreturn_t max8997_irq_thread(int irq, void *data) u8 irq_reg[MAX8997_IRQ_GROUP_NR] = {}; u8 irq_src; int ret; - int i; + int i, cur_irq; ret = max8997_read_reg(max8997->i2c, MAX8997_REG_INTSRC, &irq_src); if (ret < 0) { @@ -269,8 +270,11 @@ static irqreturn_t max8997_irq_thread(int irq, void *data) /* Report */ for (i = 0; i < MAX8997_IRQ_NR; i++) { - if (irq_reg[max8997_irqs[i].group] & max8997_irqs[i].mask) - handle_nested_irq(max8997->irq_base + i); + if (irq_reg[max8997_irqs[i].group] & max8997_irqs[i].mask) { + cur_irq = irq_find_mapping(max8997->irq_domain, i); + if (cur_irq) + handle_nested_irq(cur_irq); + } } return IRQ_HANDLED; @@ -278,26 +282,40 @@ static irqreturn_t max8997_irq_thread(int irq, void *data) int max8997_irq_resume(struct max8997_dev *max8997) { - if (max8997->irq && max8997->irq_base) - max8997_irq_thread(max8997->irq_base, max8997); + if (max8997->irq && max8997->irq_domain) + max8997_irq_thread(0, max8997); + return 0; +} + +static int max8997_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + struct max8997_dev *max8997 = d->host_data; + + irq_set_chip_data(irq, max8997); + irq_set_chip_and_handler(irq, &max8997_irq_chip, handle_edge_irq); + irq_set_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif return 0; } +static struct irq_domain_ops max8997_irq_domain_ops = { + .map = max8997_irq_domain_map, +}; + int max8997_irq_init(struct max8997_dev *max8997) { + struct irq_domain *domain; int i; - int cur_irq; int ret; u8 val; if (!max8997->irq) { dev_warn(max8997->dev, "No interrupt specified.\n"); - max8997->irq_base = 0; - return 0; - } - - if (!max8997->irq_base) { - dev_err(max8997->dev, "No interrupt base specified.\n"); return 0; } @@ -327,19 +345,13 @@ int max8997_irq_init(struct max8997_dev *max8997) true : false; } - /* Register with genirq */ - for (i = 0; i < MAX8997_IRQ_NR; i++) { - cur_irq = i + max8997->irq_base; - irq_set_chip_data(cur_irq, max8997); - irq_set_chip_and_handler(cur_irq, &max8997_irq_chip, - handle_edge_irq); - irq_set_nested_thread(cur_irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(cur_irq, IRQF_VALID); -#else - irq_set_noprobe(cur_irq); -#endif + domain = irq_domain_add_linear(NULL, MAX8997_IRQ_NR, + &max8997_irq_domain_ops, max8997); + if (!domain) { + dev_err(max8997->dev, "could not create irq domain\n"); + return -ENODEV; } + max8997->irq_domain = domain; ret = request_threaded_irq(max8997->irq, NULL, max8997_irq_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c index 454f4992cfc3..10b629c245b6 100644 --- a/drivers/mfd/max8997.c +++ b/drivers/mfd/max8997.c @@ -143,7 +143,6 @@ static int max8997_i2c_probe(struct i2c_client *i2c, if (!pdata) goto err; - max8997->irq_base = pdata->irq_base; max8997->ono = pdata->ono; mutex_init(&max8997->iolock); diff --git a/include/linux/mfd/max8997-private.h b/include/linux/mfd/max8997-private.h index 3f4deb62d6b0..830152cfae33 100644 --- a/include/linux/mfd/max8997-private.h +++ b/include/linux/mfd/max8997-private.h @@ -23,6 +23,8 @@ #define __LINUX_MFD_MAX8997_PRIV_H #include +#include +#include #define MAX8997_REG_INVALID (0xff) @@ -325,7 +327,7 @@ struct max8997_dev { int irq; int ono; - int irq_base; + struct irq_domain *irq_domain; struct mutex irqlock; int irq_masks_cur[MAX8997_IRQ_GROUP_NR]; int irq_masks_cache[MAX8997_IRQ_GROUP_NR]; diff --git a/include/linux/mfd/max8997.h b/include/linux/mfd/max8997.h index b40c08cd30bc..328d8e24b533 100644 --- a/include/linux/mfd/max8997.h +++ b/include/linux/mfd/max8997.h @@ -181,7 +181,6 @@ struct max8997_led_platform_data { struct max8997_platform_data { /* IRQ */ - int irq_base; int ono; int wakeup; -- cgit v1.2.3 From 712db99df155eeef7bbab8677d8a02d0eff50d11 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 28 Jun 2012 12:20:21 +0200 Subject: mfd: Add support for enabling tps65910 external 32-kHz oscillator Add flag to platform data to enable external 32-kHz crystal oscillator (or square wave) input. The tps6591x can use either an internal 32-kHz RC oscillator or an external crystal (or square wave) to generate the 32-kHz clock. The default setting depends on the selected boot mode. In boot mode 00 the internal RC oscillator is used at power-on, but the external crystal oscillator (or square wave) can be enabled by clearing the ck32k_ctrl flag in the device control register. Note that there is no way to switch from the external crystal oscillator to the internal RC oscillator. Signed-off-by: Johan Hovold Signed-off-by: Samuel Ortiz --- drivers/mfd/tps65910.c | 21 ++++++++++++++++++++- include/linux/mfd/tps65910.h | 1 + 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index be9e07b77325..b0526b7d6550 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -68,6 +68,25 @@ static const struct regmap_config tps65910_regmap_config = { .cache_type = REGCACHE_RBTREE, }; +static int __devinit tps65910_misc_init(struct tps65910 *tps65910, + struct tps65910_board *pmic_pdata) +{ + struct device *dev = tps65910->dev; + int ret; + + if (pmic_pdata->en_ck32k_xtal) { + ret = tps65910_reg_clear_bits(tps65910, + TPS65910_DEVCTRL, + DEVCTRL_CK32K_CTRL_MASK); + if (ret < 0) { + dev_err(dev, "clear ck32k_ctrl failed: %d\n", ret); + return ret; + } + } + + return 0; +} + static int __devinit tps65910_sleepinit(struct tps65910 *tps65910, struct tps65910_board *pmic_pdata) { @@ -243,7 +262,7 @@ static __devinit int tps65910_i2c_probe(struct i2c_client *i2c, init_data->irq_base = pmic_plat_data->irq_base; tps65910_irq_init(tps65910, init_data->irq, init_data); - + tps65910_misc_init(tps65910, pmic_plat_data); tps65910_sleepinit(tps65910, pmic_plat_data); return ret; diff --git a/include/linux/mfd/tps65910.h b/include/linux/mfd/tps65910.h index dd8dc0a6c462..a989d2b066be 100644 --- a/include/linux/mfd/tps65910.h +++ b/include/linux/mfd/tps65910.h @@ -807,6 +807,7 @@ struct tps65910_board { int irq_base; int vmbch_threshold; int vmbch2_threshold; + bool en_ck32k_xtal; bool en_dev_slp; struct tps65910_sleep_keepon_data *slp_keepon; bool en_gpio_sleep[TPS6591X_MAX_NUM_GPIO]; -- cgit v1.2.3 From 2573f6d36e73e080fc1d9d9ac7dfaf2253a61434 Mon Sep 17 00:00:00 2001 From: "Jett.Zhou" Date: Fri, 6 Jul 2012 10:59:58 +0800 Subject: mfd: Add pre-regulator device for 88pm860x Pre-regulator of 88pm8606 is mainly for support charging based on vbus, it needs to be enabled for charging battery, and will be disabled in some exception condition like over-temp. Add the pre-regulator device init data and resource for mfd subdev. Signed-off-by: Jett.Zhou Signed-off-by: Samuel Ortiz --- drivers/mfd/88pm860x-core.c | 23 +++++++++++++++++++++++ include/linux/mfd/88pm860x.h | 1 + 2 files changed, 24 insertions(+) (limited to 'include') diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index 87bd5ba38d5b..d09918cf1b15 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c @@ -90,6 +90,10 @@ static struct resource charger_resources[] __devinitdata = { {PM8607_IRQ_VCHG, PM8607_IRQ_VCHG, "vchg voltage", IORESOURCE_IRQ,}, }; +static struct resource preg_resources[] __devinitdata = { + {PM8606_ID_PREG, PM8606_ID_PREG, "preg", IORESOURCE_IO,}, +}; + static struct resource rtc_resources[] __devinitdata = { {PM8607_IRQ_RTC, PM8607_IRQ_RTC, "rtc", IORESOURCE_IRQ,}, }; @@ -142,9 +146,19 @@ static struct mfd_cell codec_devs[] = { {"88pm860x-codec", -1,}, }; +static struct regulator_consumer_supply preg_supply[] = { + REGULATOR_SUPPLY("preg", "charger-manager"), +}; + +static struct regulator_init_data preg_init_data = { + .num_consumer_supplies = ARRAY_SIZE(preg_supply), + .consumer_supplies = &preg_supply[0], +}; + static struct mfd_cell power_devs[] = { {"88pm860x-battery", -1,}, {"88pm860x-charger", -1,}, + {"88pm860x-preg", -1,}, }; static struct mfd_cell rtc_devs[] = { @@ -768,6 +782,15 @@ static void __devinit device_power_init(struct pm860x_chip *chip, &charger_resources[0], chip->irq_base); if (ret < 0) dev_err(chip->dev, "Failed to add charger subdev\n"); + + power_devs[2].platform_data = &preg_init_data; + power_devs[2].pdata_size = sizeof(struct regulator_init_data); + power_devs[2].num_resources = ARRAY_SIZE(preg_resources); + power_devs[2].resources = &preg_resources[0], + ret = mfd_add_devices(chip->dev, 0, &power_devs[2], 1, + &preg_resources[0], chip->irq_base); + if (ret < 0) + dev_err(chip->dev, "Failed to add preg subdev\n"); } static void __devinit device_onkey_init(struct pm860x_chip *chip, diff --git a/include/linux/mfd/88pm860x.h b/include/linux/mfd/88pm860x.h index 84d071ade1d8..7b24943779fa 100644 --- a/include/linux/mfd/88pm860x.h +++ b/include/linux/mfd/88pm860x.h @@ -136,6 +136,7 @@ enum { PM8607_ID_LDO13, PM8607_ID_LDO14, PM8607_ID_LDO15, + PM8606_ID_PREG, PM8607_ID_RG_MAX, }; -- cgit v1.2.3 From b8748096111b483de8a544cc220510dff17bbff9 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 6 Jul 2012 15:32:20 +0800 Subject: mfd: Remove unused max77686 iolock mutex Now this driver is using regmap API, the iolock mutex is not used and can be removed. Signed-off-by: Axel Lin Signed-off-by: Samuel Ortiz --- drivers/mfd/max77686.c | 3 --- include/linux/mfd/max77686-private.h | 1 - 2 files changed, 4 deletions(-) (limited to 'include') diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c index 11e56017e0b0..9e7e1d30f25f 100644 --- a/drivers/mfd/max77686.c +++ b/drivers/mfd/max77686.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -79,8 +78,6 @@ static int max77686_i2c_probe(struct i2c_client *i2c, max77686->wakeup = pdata->wakeup; max77686->irq_gpio = pdata->irq_gpio; - mutex_init(&max77686->iolock); - if (regmap_read(max77686->regmap, MAX77686_REG_DEVICE_ID, &data) < 0) { dev_err(max77686->dev, diff --git a/include/linux/mfd/max77686-private.h b/include/linux/mfd/max77686-private.h index 962f65e72b71..d327d4971e4f 100644 --- a/include/linux/mfd/max77686-private.h +++ b/include/linux/mfd/max77686-private.h @@ -219,7 +219,6 @@ struct max77686_dev { struct device *dev; struct i2c_client *i2c; /* 0xcc / PMIC, Battery Control, and FLASH */ struct i2c_client *rtc; /* slave addr 0x0c */ - struct mutex iolock; int type; -- cgit v1.2.3 From 59db96913c9d94fe74002df494eb80e4a5ca4087 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 9 Jul 2012 00:31:36 +0200 Subject: mfd: Move arizona digital core supply management to the regulator API Rather than open coding the enable GPIO control in the MFD core use the API to push the management on to the regulator driver. The immediate advantage is slight for most systems but this will in future allow device configurations where an external regulator is used for DCVDD. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/arizona-core.c | 65 +++++++++++++++++++++------------------- include/linux/mfd/arizona/core.h | 1 + 2 files changed, 36 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 42cb28b2b5c8..c8946a889a78 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -28,7 +29,6 @@ static const char *wm5102_core_supplies[] = { "AVDD", "DBVDD1", - "DCVDD", }; int arizona_clk32k_enable(struct arizona *arizona) @@ -223,8 +223,11 @@ static int arizona_runtime_resume(struct device *dev) struct arizona *arizona = dev_get_drvdata(dev); int ret; - if (arizona->pdata.ldoena) - gpio_set_value_cansleep(arizona->pdata.ldoena, 1); + ret = regulator_enable(arizona->dcvdd); + if (ret != 0) { + dev_err(arizona->dev, "Failed to enable DCVDD: %d\n", ret); + return ret; + } regcache_cache_only(arizona->regmap, false); @@ -241,11 +244,9 @@ static int arizona_runtime_suspend(struct device *dev) { struct arizona *arizona = dev_get_drvdata(dev); - if (arizona->pdata.ldoena) { - gpio_set_value_cansleep(arizona->pdata.ldoena, 0); - regcache_cache_only(arizona->regmap, true); - regcache_mark_dirty(arizona->regmap); - } + regulator_disable(arizona->dcvdd); + regcache_cache_only(arizona->regmap, true); + regcache_mark_dirty(arizona->regmap); return 0; } @@ -314,6 +315,13 @@ int __devinit arizona_dev_init(struct arizona *arizona) goto err_early; } + arizona->dcvdd = devm_regulator_get(arizona->dev, "DCVDD"); + if (IS_ERR(arizona->dcvdd)) { + ret = PTR_ERR(arizona->dcvdd); + dev_err(dev, "Failed to request DCVDD: %d\n", ret); + goto err_early; + } + ret = regulator_bulk_enable(arizona->num_core_supplies, arizona->core_supplies); if (ret != 0) { @@ -322,6 +330,12 @@ int __devinit arizona_dev_init(struct arizona *arizona) goto err_early; } + ret = regulator_enable(arizona->dcvdd); + if (ret != 0) { + dev_err(dev, "Failed to enable DCVDD: %d\n", ret); + goto err_enable; + } + if (arizona->pdata.reset) { /* Start out with /RESET low to put the chip into reset */ ret = gpio_request_one(arizona->pdata.reset, @@ -329,35 +343,25 @@ int __devinit arizona_dev_init(struct arizona *arizona) "arizona /RESET"); if (ret != 0) { dev_err(dev, "Failed to request /RESET: %d\n", ret); - goto err_enable; + goto err_dcvdd; } gpio_set_value_cansleep(arizona->pdata.reset, 1); } - if (arizona->pdata.ldoena) { - ret = gpio_request_one(arizona->pdata.ldoena, - GPIOF_DIR_OUT | GPIOF_INIT_HIGH, - "arizona LDOENA"); - if (ret != 0) { - dev_err(dev, "Failed to request LDOENA: %d\n", ret); - goto err_reset; - } - } - regcache_cache_only(arizona->regmap, false); ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, ®); if (ret != 0) { dev_err(dev, "Failed to read ID register: %d\n", ret); - goto err_ldoena; + goto err_reset; } ret = regmap_read(arizona->regmap, ARIZONA_DEVICE_REVISION, &arizona->rev); if (ret != 0) { dev_err(dev, "Failed to read revision register: %d\n", ret); - goto err_ldoena; + goto err_reset; } arizona->rev &= ARIZONA_DEVICE_REVISION_MASK; @@ -374,7 +378,7 @@ int __devinit arizona_dev_init(struct arizona *arizona) default: dev_err(arizona->dev, "Unknown device ID %x\n", reg); - goto err_ldoena; + goto err_reset; } dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A'); @@ -387,7 +391,7 @@ int __devinit arizona_dev_init(struct arizona *arizona) ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0); if (ret != 0) { dev_err(dev, "Failed to reset device: %d\n", ret); - goto err_ldoena; + goto err_reset; } } @@ -424,7 +428,7 @@ int __devinit arizona_dev_init(struct arizona *arizona) dev_err(arizona->dev, "Invalid 32kHz clock source: %d\n", arizona->pdata.clk32k_src); ret = -EINVAL; - goto err_ldoena; + goto err_reset; } for (i = 0; i < ARIZONA_MAX_INPUT; i++) { @@ -470,7 +474,7 @@ int __devinit arizona_dev_init(struct arizona *arizona) /* Set up for interrupts */ ret = arizona_irq_init(arizona); if (ret != 0) - goto err_ldoena; + goto err_reset; arizona_request_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, "CLKGEN error", arizona_clkgen_err, arizona); @@ -491,20 +495,21 @@ int __devinit arizona_dev_init(struct arizona *arizona) goto err_irq; } +#ifdef CONFIG_PM_RUNTIME + regulator_disable(arizona->dcvdd); +#endif + return 0; err_irq: arizona_irq_exit(arizona); -err_ldoena: - if (arizona->pdata.ldoena) { - gpio_set_value_cansleep(arizona->pdata.ldoena, 0); - gpio_free(arizona->pdata.ldoena); - } err_reset: if (arizona->pdata.reset) { gpio_set_value_cansleep(arizona->pdata.reset, 1); gpio_free(arizona->pdata.reset); } +err_dcvdd: + regulator_disable(arizona->dcvdd); err_enable: regulator_bulk_disable(ARRAY_SIZE(arizona->core_supplies), arizona->core_supplies); diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index 0157d845c2ff..3ef32b4c1136 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h @@ -77,6 +77,7 @@ struct arizona { int num_core_supplies; struct regulator_bulk_data core_supplies[ARIZONA_MAX_CORE_SUPPLIES]; + struct regulator *dcvdd; struct arizona_pdata pdata; -- cgit v1.2.3 From de2233365d5abc94993378330768786de2c606f6 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 5 Jul 2012 20:35:28 +0100 Subject: mfd: Add more arizona register definitions These registers will be used in future devices. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- include/linux/mfd/arizona/registers.h | 163 ++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) (limited to 'include') diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index 989c47d681e0..8f49106d7bda 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -20,6 +20,9 @@ #define ARIZONA_DEVICE_REVISION 0x01 #define ARIZONA_CTRL_IF_SPI_CFG_1 0x08 #define ARIZONA_CTRL_IF_I2C1_CFG_1 0x09 +#define ARIZONA_CTRL_IF_I2C2_CFG_1 0x0A +#define ARIZONA_CTRL_IF_I2C1_CFG_2 0x0B +#define ARIZONA_CTRL_IF_I2C2_CFG_2 0x0C #define ARIZONA_CTRL_IF_STATUS_1 0x0D #define ARIZONA_WRITE_SEQUENCER_CTRL_0 0x16 #define ARIZONA_WRITE_SEQUENCER_CTRL_1 0x17 @@ -80,6 +83,7 @@ #define ARIZONA_FLL1_CONTROL_5 0x175 #define ARIZONA_FLL1_CONTROL_6 0x176 #define ARIZONA_FLL1_LOOP_FILTER_TEST_1 0x177 +#define ARIZONA_FLL1_NCO_TEST_0 0x178 #define ARIZONA_FLL1_SYNCHRONISER_1 0x181 #define ARIZONA_FLL1_SYNCHRONISER_2 0x182 #define ARIZONA_FLL1_SYNCHRONISER_3 0x183 @@ -95,6 +99,7 @@ #define ARIZONA_FLL2_CONTROL_5 0x195 #define ARIZONA_FLL2_CONTROL_6 0x196 #define ARIZONA_FLL2_LOOP_FILTER_TEST_1 0x197 +#define ARIZONA_FLL2_NCO_TEST_0 0x198 #define ARIZONA_FLL2_SYNCHRONISER_1 0x1A1 #define ARIZONA_FLL2_SYNCHRONISER_2 0x1A2 #define ARIZONA_FLL2_SYNCHRONISER_3 0x1A3 @@ -119,6 +124,7 @@ #define ARIZONA_ISOLATION_CONTROL 0x2CB #define ARIZONA_JACK_DETECT_ANALOGUE 0x2D3 #define ARIZONA_INPUT_ENABLES 0x300 +#define ARIZONA_INPUT_ENABLES_STATUS 0x301 #define ARIZONA_INPUT_RATE 0x308 #define ARIZONA_INPUT_VOLUME_RAMP 0x309 #define ARIZONA_IN1L_CONTROL 0x310 @@ -139,8 +145,14 @@ #define ARIZONA_IN3R_CONTROL 0x324 #define ARIZONA_ADC_DIGITAL_VOLUME_3R 0x325 #define ARIZONA_DMIC3R_CONTROL 0x326 +#define ARIZONA_IN4_CONTROL 0x328 +#define ARIZONA_ADC_DIGITAL_VOLUME_4L 0x329 +#define ARIZONA_DMIC4L_CONTROL 0x32A +#define ARIZONA_ADC_DIGITAL_VOLUME_4R 0x32D +#define ARIZONA_DMIC4R_CONTROL 0x32E #define ARIZONA_OUTPUT_ENABLES_1 0x400 #define ARIZONA_OUTPUT_STATUS_1 0x401 +#define ARIZONA_RAW_OUTPUT_STATUS_1 0x406 #define ARIZONA_OUTPUT_RATE_1 0x408 #define ARIZONA_OUTPUT_VOLUME_RAMP 0x409 #define ARIZONA_OUTPUT_PATH_CONFIG_1L 0x410 @@ -166,6 +178,7 @@ #define ARIZONA_OUTPUT_PATH_CONFIG_3R 0x424 #define ARIZONA_DAC_DIGITAL_VOLUME_3R 0x425 #define ARIZONA_DAC_VOLUME_LIMIT_3R 0x426 +#define ARIZONA_NOISE_GATE_SELECT_3R 0x427 #define ARIZONA_OUTPUT_PATH_CONFIG_4L 0x428 #define ARIZONA_DAC_DIGITAL_VOLUME_4L 0x429 #define ARIZONA_OUT_VOLUME_4L 0x42A @@ -182,10 +195,20 @@ #define ARIZONA_DAC_DIGITAL_VOLUME_5R 0x435 #define ARIZONA_DAC_VOLUME_LIMIT_5R 0x436 #define ARIZONA_NOISE_GATE_SELECT_5R 0x437 +#define ARIZONA_OUTPUT_PATH_CONFIG_6L 0x438 +#define ARIZONA_DAC_DIGITAL_VOLUME_6L 0x439 +#define ARIZONA_DAC_VOLUME_LIMIT_6L 0x43A +#define ARIZONA_NOISE_GATE_SELECT_6L 0x43B +#define ARIZONA_OUTPUT_PATH_CONFIG_6R 0x43C +#define ARIZONA_DAC_DIGITAL_VOLUME_6R 0x43D +#define ARIZONA_DAC_VOLUME_LIMIT_6R 0x43E +#define ARIZONA_NOISE_GATE_SELECT_6R 0x43F #define ARIZONA_DAC_AEC_CONTROL_1 0x450 #define ARIZONA_NOISE_GATE_CONTROL 0x458 #define ARIZONA_PDM_SPK1_CTRL_1 0x490 #define ARIZONA_PDM_SPK1_CTRL_2 0x491 +#define ARIZONA_PDM_SPK2_CTRL_1 0x492 +#define ARIZONA_PDM_SPK2_CTRL_2 0x493 #define ARIZONA_DAC_COMP_1 0x4DC #define ARIZONA_DAC_COMP_2 0x4DD #define ARIZONA_DAC_COMP_3 0x4DE @@ -335,6 +358,14 @@ #define ARIZONA_OUT3LMIX_INPUT_3_VOLUME 0x6A5 #define ARIZONA_OUT3LMIX_INPUT_4_SOURCE 0x6A6 #define ARIZONA_OUT3LMIX_INPUT_4_VOLUME 0x6A7 +#define ARIZONA_OUT3RMIX_INPUT_1_SOURCE 0x6A8 +#define ARIZONA_OUT3RMIX_INPUT_1_VOLUME 0x6A9 +#define ARIZONA_OUT3RMIX_INPUT_2_SOURCE 0x6AA +#define ARIZONA_OUT3RMIX_INPUT_2_VOLUME 0x6AB +#define ARIZONA_OUT3RMIX_INPUT_3_SOURCE 0x6AC +#define ARIZONA_OUT3RMIX_INPUT_3_VOLUME 0x6AD +#define ARIZONA_OUT3RMIX_INPUT_4_SOURCE 0x6AE +#define ARIZONA_OUT3RMIX_INPUT_4_VOLUME 0x6AF #define ARIZONA_OUT4LMIX_INPUT_1_SOURCE 0x6B0 #define ARIZONA_OUT4LMIX_INPUT_1_VOLUME 0x6B1 #define ARIZONA_OUT4LMIX_INPUT_2_SOURCE 0x6B2 @@ -367,6 +398,22 @@ #define ARIZONA_OUT5RMIX_INPUT_3_VOLUME 0x6CD #define ARIZONA_OUT5RMIX_INPUT_4_SOURCE 0x6CE #define ARIZONA_OUT5RMIX_INPUT_4_VOLUME 0x6CF +#define ARIZONA_OUT6LMIX_INPUT_1_SOURCE 0x6D0 +#define ARIZONA_OUT6LMIX_INPUT_1_VOLUME 0x6D1 +#define ARIZONA_OUT6LMIX_INPUT_2_SOURCE 0x6D2 +#define ARIZONA_OUT6LMIX_INPUT_2_VOLUME 0x6D3 +#define ARIZONA_OUT6LMIX_INPUT_3_SOURCE 0x6D4 +#define ARIZONA_OUT6LMIX_INPUT_3_VOLUME 0x6D5 +#define ARIZONA_OUT6LMIX_INPUT_4_SOURCE 0x6D6 +#define ARIZONA_OUT6LMIX_INPUT_4_VOLUME 0x6D7 +#define ARIZONA_OUT6RMIX_INPUT_1_SOURCE 0x6D8 +#define ARIZONA_OUT6RMIX_INPUT_1_VOLUME 0x6D9 +#define ARIZONA_OUT6RMIX_INPUT_2_SOURCE 0x6DA +#define ARIZONA_OUT6RMIX_INPUT_2_VOLUME 0x6DB +#define ARIZONA_OUT6RMIX_INPUT_3_SOURCE 0x6DC +#define ARIZONA_OUT6RMIX_INPUT_3_VOLUME 0x6DD +#define ARIZONA_OUT6RMIX_INPUT_4_SOURCE 0x6DE +#define ARIZONA_OUT6RMIX_INPUT_4_VOLUME 0x6DF #define ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE 0x700 #define ARIZONA_AIF1TX1MIX_INPUT_1_VOLUME 0x701 #define ARIZONA_AIF1TX1MIX_INPUT_2_SOURCE 0x702 @@ -645,18 +692,106 @@ #define ARIZONA_DSP1AUX4MIX_INPUT_1_SOURCE 0x968 #define ARIZONA_DSP1AUX5MIX_INPUT_1_SOURCE 0x970 #define ARIZONA_DSP1AUX6MIX_INPUT_1_SOURCE 0x978 +#define ARIZONA_DSP2LMIX_INPUT_1_SOURCE 0x980 +#define ARIZONA_DSP2LMIX_INPUT_1_VOLUME 0x981 +#define ARIZONA_DSP2LMIX_INPUT_2_SOURCE 0x982 +#define ARIZONA_DSP2LMIX_INPUT_2_VOLUME 0x983 +#define ARIZONA_DSP2LMIX_INPUT_3_SOURCE 0x984 +#define ARIZONA_DSP2LMIX_INPUT_3_VOLUME 0x985 +#define ARIZONA_DSP2LMIX_INPUT_4_SOURCE 0x986 +#define ARIZONA_DSP2LMIX_INPUT_4_VOLUME 0x987 +#define ARIZONA_DSP2RMIX_INPUT_1_SOURCE 0x988 +#define ARIZONA_DSP2RMIX_INPUT_1_VOLUME 0x989 +#define ARIZONA_DSP2RMIX_INPUT_2_SOURCE 0x98A +#define ARIZONA_DSP2RMIX_INPUT_2_VOLUME 0x98B +#define ARIZONA_DSP2RMIX_INPUT_3_SOURCE 0x98C +#define ARIZONA_DSP2RMIX_INPUT_3_VOLUME 0x98D +#define ARIZONA_DSP2RMIX_INPUT_4_SOURCE 0x98E +#define ARIZONA_DSP2RMIX_INPUT_4_VOLUME 0x98F +#define ARIZONA_DSP2AUX1MIX_INPUT_1_SOURCE 0x990 +#define ARIZONA_DSP2AUX2MIX_INPUT_1_SOURCE 0x998 +#define ARIZONA_DSP2AUX3MIX_INPUT_1_SOURCE 0x9A0 +#define ARIZONA_DSP2AUX4MIX_INPUT_1_SOURCE 0x9A8 +#define ARIZONA_DSP2AUX5MIX_INPUT_1_SOURCE 0x9B0 +#define ARIZONA_DSP2AUX6MIX_INPUT_1_SOURCE 0x9B8 +#define ARIZONA_DSP3LMIX_INPUT_1_SOURCE 0x9C0 +#define ARIZONA_DSP3LMIX_INPUT_1_VOLUME 0x9C1 +#define ARIZONA_DSP3LMIX_INPUT_2_SOURCE 0x9C2 +#define ARIZONA_DSP3LMIX_INPUT_2_VOLUME 0x9C3 +#define ARIZONA_DSP3LMIX_INPUT_3_SOURCE 0x9C4 +#define ARIZONA_DSP3LMIX_INPUT_3_VOLUME 0x9C5 +#define ARIZONA_DSP3LMIX_INPUT_4_SOURCE 0x9C6 +#define ARIZONA_DSP3LMIX_INPUT_4_VOLUME 0x9C7 +#define ARIZONA_DSP3RMIX_INPUT_1_SOURCE 0x9C8 +#define ARIZONA_DSP3RMIX_INPUT_1_VOLUME 0x9C9 +#define ARIZONA_DSP3RMIX_INPUT_2_SOURCE 0x9CA +#define ARIZONA_DSP3RMIX_INPUT_2_VOLUME 0x9CB +#define ARIZONA_DSP3RMIX_INPUT_3_SOURCE 0x9CC +#define ARIZONA_DSP3RMIX_INPUT_3_VOLUME 0x9CD +#define ARIZONA_DSP3RMIX_INPUT_4_SOURCE 0x9CE +#define ARIZONA_DSP3RMIX_INPUT_4_VOLUME 0x9CF +#define ARIZONA_DSP3AUX1MIX_INPUT_1_SOURCE 0x9D0 +#define ARIZONA_DSP3AUX2MIX_INPUT_1_SOURCE 0x9D8 +#define ARIZONA_DSP3AUX3MIX_INPUT_1_SOURCE 0x9E0 +#define ARIZONA_DSP3AUX4MIX_INPUT_1_SOURCE 0x9E8 +#define ARIZONA_DSP3AUX5MIX_INPUT_1_SOURCE 0x9F0 +#define ARIZONA_DSP3AUX6MIX_INPUT_1_SOURCE 0x9F8 +#define ARIZONA_DSP4LMIX_INPUT_1_SOURCE 0xA00 +#define ARIZONA_DSP4LMIX_INPUT_1_VOLUME 0xA01 +#define ARIZONA_DSP4LMIX_INPUT_2_SOURCE 0xA02 +#define ARIZONA_DSP4LMIX_INPUT_2_VOLUME 0xA03 +#define ARIZONA_DSP4LMIX_INPUT_3_SOURCE 0xA04 +#define ARIZONA_DSP4LMIX_INPUT_3_VOLUME 0xA05 +#define ARIZONA_DSP4LMIX_INPUT_4_SOURCE 0xA06 +#define ARIZONA_DSP4LMIX_INPUT_4_VOLUME 0xA07 +#define ARIZONA_DSP4RMIX_INPUT_1_SOURCE 0xA08 +#define ARIZONA_DSP4RMIX_INPUT_1_VOLUME 0xA09 +#define ARIZONA_DSP4RMIX_INPUT_2_SOURCE 0xA0A +#define ARIZONA_DSP4RMIX_INPUT_2_VOLUME 0xA0B +#define ARIZONA_DSP4RMIX_INPUT_3_SOURCE 0xA0C +#define ARIZONA_DSP4RMIX_INPUT_3_VOLUME 0xA0D +#define ARIZONA_DSP4RMIX_INPUT_4_SOURCE 0xA0E +#define ARIZONA_DSP4RMIX_INPUT_4_VOLUME 0xA0F +#define ARIZONA_DSP4AUX1MIX_INPUT_1_SOURCE 0xA10 +#define ARIZONA_DSP4AUX2MIX_INPUT_1_SOURCE 0xA18 +#define ARIZONA_DSP4AUX3MIX_INPUT_1_SOURCE 0xA20 +#define ARIZONA_DSP4AUX4MIX_INPUT_1_SOURCE 0xA28 +#define ARIZONA_DSP4AUX5MIX_INPUT_1_SOURCE 0xA30 +#define ARIZONA_DSP4AUX6MIX_INPUT_1_SOURCE 0xA38 #define ARIZONA_ASRC1LMIX_INPUT_1_SOURCE 0xA80 #define ARIZONA_ASRC1RMIX_INPUT_1_SOURCE 0xA88 #define ARIZONA_ASRC2LMIX_INPUT_1_SOURCE 0xA90 #define ARIZONA_ASRC2RMIX_INPUT_1_SOURCE 0xA98 #define ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE 0xB00 #define ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE 0xB08 +#define ARIZONA_ISRC1DEC3MIX_INPUT_1_SOURCE 0xB10 +#define ARIZONA_ISRC1DEC4MIX_INPUT_1_SOURCE 0xB18 #define ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE 0xB20 #define ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE 0xB28 +#define ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE 0xB30 +#define ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE 0xB38 #define ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE 0xB40 #define ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE 0xB48 #define ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE 0xB60 #define ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE 0xB68 +#define ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE 0xB30 +#define ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE 0xB38 +#define ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE 0xB40 +#define ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE 0xB48 +#define ARIZONA_ISRC2DEC3MIX_INPUT_1_SOURCE 0xB50 +#define ARIZONA_ISRC2DEC4MIX_INPUT_1_SOURCE 0xB58 +#define ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE 0xB60 +#define ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE 0xB68 +#define ARIZONA_ISRC2INT3MIX_INPUT_1_SOURCE 0xB70 +#define ARIZONA_ISRC2INT4MIX_INPUT_1_SOURCE 0xB78 +#define ARIZONA_ISRC3DEC1MIX_INPUT_1_SOURCE 0xB80 +#define ARIZONA_ISRC3DEC2MIX_INPUT_1_SOURCE 0xB88 +#define ARIZONA_ISRC3DEC3MIX_INPUT_1_SOURCE 0xB90 +#define ARIZONA_ISRC3DEC4MIX_INPUT_1_SOURCE 0xB98 +#define ARIZONA_ISRC3INT1MIX_INPUT_1_SOURCE 0xBA0 +#define ARIZONA_ISRC3INT2MIX_INPUT_1_SOURCE 0xBA8 +#define ARIZONA_ISRC3INT3MIX_INPUT_1_SOURCE 0xBB0 +#define ARIZONA_ISRC3INT4MIX_INPUT_1_SOURCE 0xBB8 #define ARIZONA_GPIO1_CTRL 0xC00 #define ARIZONA_GPIO2_CTRL 0xC01 #define ARIZONA_GPIO3_CTRL 0xC02 @@ -670,6 +805,18 @@ #define ARIZONA_MISC_PAD_CTRL_4 0xC23 #define ARIZONA_MISC_PAD_CTRL_5 0xC24 #define ARIZONA_MISC_PAD_CTRL_6 0xC25 +#define ARIZONA_MISC_PAD_CTRL_7 0xC30 +#define ARIZONA_MISC_PAD_CTRL_8 0xC31 +#define ARIZONA_MISC_PAD_CTRL_9 0xC32 +#define ARIZONA_MISC_PAD_CTRL_10 0xC33 +#define ARIZONA_MISC_PAD_CTRL_11 0xC34 +#define ARIZONA_MISC_PAD_CTRL_12 0xC35 +#define ARIZONA_MISC_PAD_CTRL_13 0xC36 +#define ARIZONA_MISC_PAD_CTRL_14 0xC37 +#define ARIZONA_MISC_PAD_CTRL_15 0xC38 +#define ARIZONA_MISC_PAD_CTRL_16 0xC39 +#define ARIZONA_MISC_PAD_CTRL_17 0xC3A +#define ARIZONA_MISC_PAD_CTRL_18 0xC3B #define ARIZONA_INTERRUPT_STATUS_1 0xD00 #define ARIZONA_INTERRUPT_STATUS_2 0xD01 #define ARIZONA_INTERRUPT_STATUS_3 0xD02 @@ -813,6 +960,7 @@ #define ARIZONA_HPLPF4_1 0xECC #define ARIZONA_HPLPF4_2 0xECD #define ARIZONA_ASRC_ENABLE 0xEE0 +#define ARIZONA_ASRC_STATUS 0xEE1 #define ARIZONA_ASRC_RATE1 0xEE2 #define ARIZONA_ASRC_RATE2 0xEE3 #define ARIZONA_ISRC_1_CTRL_1 0xEF0 @@ -824,10 +972,25 @@ #define ARIZONA_ISRC_3_CTRL_1 0xEF6 #define ARIZONA_ISRC_3_CTRL_2 0xEF7 #define ARIZONA_ISRC_3_CTRL_3 0xEF8 +#define ARIZONA_CLOCK_CONTROL 0xF00 +#define ARIZONA_ANC_SRC 0xF01 +#define ARIZONA_DSP_STATUS 0xF02 #define ARIZONA_DSP1_CONTROL_1 0x1100 #define ARIZONA_DSP1_CLOCKING_1 0x1101 #define ARIZONA_DSP1_STATUS_1 0x1104 #define ARIZONA_DSP1_STATUS_2 0x1105 +#define ARIZONA_DSP2_CONTROL_1 0x1200 +#define ARIZONA_DSP2_CLOCKING_1 0x1201 +#define ARIZONA_DSP2_STATUS_1 0x1204 +#define ARIZONA_DSP2_STATUS_2 0x1205 +#define ARIZONA_DSP3_CONTROL_1 0x1300 +#define ARIZONA_DSP3_CLOCKING_1 0x1301 +#define ARIZONA_DSP3_STATUS_1 0x1304 +#define ARIZONA_DSP3_STATUS_2 0x1305 +#define ARIZONA_DSP4_CONTROL_1 0x1400 +#define ARIZONA_DSP4_CLOCKING_1 0x1401 +#define ARIZONA_DSP4_STATUS_1 0x1404 +#define ARIZONA_DSP4_STATUS_2 0x1405 /* * Field Definitions. -- cgit v1.2.3 From 68602120e496a31d8e3b36d0bfc7d9d2456fb05c Mon Sep 17 00:00:00 2001 From: Sean Hefty Date: Thu, 14 Jun 2012 20:31:39 +0000 Subject: RDMA/cma: Allow user to restrict listens to bound address family Provide an option for the user to specify that listens should only accept connections where the incoming address family matches that of the locally bound address. This is used to support the equivalent of IPV6_V6ONLY socket option, which allows an app to only accept connection requests directed to IPv6 addresses. Signed-off-by: Sean Hefty Signed-off-by: Roland Dreier --- drivers/infiniband/core/cma.c | 35 +++++++++++++++++++++++++++++++---- drivers/infiniband/core/ucma.c | 7 +++++++ include/rdma/rdma_cm.h | 10 ++++++++++ include/rdma/rdma_user_cm.h | 1 + 4 files changed, 49 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 454e7ea111e6..8734a6af35d7 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -99,6 +99,10 @@ struct rdma_bind_list { unsigned short port; }; +enum { + CMA_OPTION_AFONLY, +}; + /* * Device removal can occur at anytime, so we need extra handling to * serialize notifying the user of device removal with other callbacks. @@ -137,6 +141,7 @@ struct rdma_id_private { u32 qkey; u32 qp_num; pid_t owner; + u32 options; u8 srq; u8 tos; u8 reuseaddr; @@ -2104,6 +2109,26 @@ int rdma_set_reuseaddr(struct rdma_cm_id *id, int reuse) } EXPORT_SYMBOL(rdma_set_reuseaddr); +int rdma_set_afonly(struct rdma_cm_id *id, int afonly) +{ + struct rdma_id_private *id_priv; + unsigned long flags; + int ret; + + id_priv = container_of(id, struct rdma_id_private, id); + spin_lock_irqsave(&id_priv->lock, flags); + if (id_priv->state == RDMA_CM_IDLE || id_priv->state == RDMA_CM_ADDR_BOUND) { + id_priv->options |= (1 << CMA_OPTION_AFONLY); + id_priv->afonly = afonly; + ret = 0; + } else { + ret = -EINVAL; + } + spin_unlock_irqrestore(&id_priv->lock, flags); + return ret; +} +EXPORT_SYMBOL(rdma_set_afonly); + static void cma_bind_port(struct rdma_bind_list *bind_list, struct rdma_id_private *id_priv) { @@ -2379,12 +2404,14 @@ int rdma_bind_addr(struct rdma_cm_id *id, struct sockaddr *addr) } memcpy(&id->route.addr.src_addr, addr, ip_addr_size(addr)); - if (addr->sa_family == AF_INET) - id_priv->afonly = 1; + if (!(id_priv->options & (1 << CMA_OPTION_AFONLY))) { + if (addr->sa_family == AF_INET) + id_priv->afonly = 1; #if IS_ENABLED(CONFIG_IPV6) - else if (addr->sa_family == AF_INET6) - id_priv->afonly = init_net.ipv6.sysctl.bindv6only; + else if (addr->sa_family == AF_INET6) + id_priv->afonly = init_net.ipv6.sysctl.bindv6only; #endif + } ret = cma_get_port(id_priv); if (ret) goto err2; diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index 8002ae642cfe..893cb879462c 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -909,6 +909,13 @@ static int ucma_set_option_id(struct ucma_context *ctx, int optname, } ret = rdma_set_reuseaddr(ctx->cm_id, *((int *) optval) ? 1 : 0); break; + case RDMA_OPTION_ID_AFONLY: + if (optlen != sizeof(int)) { + ret = -EINVAL; + break; + } + ret = rdma_set_afonly(ctx->cm_id, *((int *) optval) ? 1 : 0); + break; default: ret = -ENOSYS; } diff --git a/include/rdma/rdma_cm.h b/include/rdma/rdma_cm.h index 51988f808181..ad3a3142383a 100644 --- a/include/rdma/rdma_cm.h +++ b/include/rdma/rdma_cm.h @@ -357,4 +357,14 @@ void rdma_set_service_type(struct rdma_cm_id *id, int tos); */ int rdma_set_reuseaddr(struct rdma_cm_id *id, int reuse); +/** + * rdma_set_afonly - Specify that listens are restricted to the + * bound address family only. + * @id: Communication identifer to configure. + * @afonly: Value indicating if listens are restricted. + * + * Must be set before identifier is in the listening state. + */ +int rdma_set_afonly(struct rdma_cm_id *id, int afonly); + #endif /* RDMA_CM_H */ diff --git a/include/rdma/rdma_user_cm.h b/include/rdma/rdma_user_cm.h index 5348a000c8f3..1ee9239ff8c2 100644 --- a/include/rdma/rdma_user_cm.h +++ b/include/rdma/rdma_user_cm.h @@ -224,6 +224,7 @@ enum { enum { RDMA_OPTION_ID_TOS = 0, RDMA_OPTION_ID_REUSEADDR = 1, + RDMA_OPTION_ID_AFONLY = 2, RDMA_OPTION_IB_PATH = 1 }; -- cgit v1.2.3 From 752a50cab600c6d46c5a1921c6a6d2fb116c8a4b Mon Sep 17 00:00:00 2001 From: Jack Morgenstein Date: Tue, 19 Jun 2012 11:21:33 +0300 Subject: mlx4_core: Pass an invalid PCI id number to VFs Currently, VFs have 0 in their dev->caps.function field. This is a valid pci id (usually of the PF). Instead, pass an invalid PCI id to the VF via QUERY_FW, so that if the value gets accessed in the VF driver, we'll catch the problem. Signed-off-by: Jack Morgenstein Signed-off-by: Roland Dreier --- drivers/net/ethernet/mellanox/mlx4/fw.c | 10 +++++++--- include/linux/mlx4/device.h | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 9c83bb8151ea..4281ce09add8 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -881,11 +881,12 @@ int mlx4_QUERY_FW(struct mlx4_dev *dev) ((fw_ver & 0xffff0000ull) >> 16) | ((fw_ver & 0x0000ffffull) << 16); + MLX4_GET(lg, outbox, QUERY_FW_PPF_ID); + dev->caps.function = lg; + if (mlx4_is_slave(dev)) goto out; - MLX4_GET(lg, outbox, QUERY_FW_PPF_ID); - dev->caps.function = lg; MLX4_GET(cmd_if_rev, outbox, QUERY_FW_CMD_IF_REV_OFFSET); if (cmd_if_rev < MLX4_COMMAND_INTERFACE_MIN_REV || @@ -966,9 +967,12 @@ int mlx4_QUERY_FW_wrapper(struct mlx4_dev *dev, int slave, if (err) return err; - /* for slaves, zero out everything except FW version */ + /* for slaves, set pci PPF ID to invalid and zero out everything + * else except FW version */ outbuf[0] = outbuf[1] = 0; memset(&outbuf[8], 0, QUERY_FW_OUT_SIZE - 8); + outbuf[QUERY_FW_PPF_ID] = MLX4_INVALID_SLAVE_ID; + return 0; } diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 6a8f002b8ed3..8eadf0f14cc5 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -534,6 +534,8 @@ struct mlx4_init_port_param { if (((dev)->caps.port_mask[port] == MLX4_PORT_TYPE_IB) || \ ((dev)->caps.flags & MLX4_DEV_CAP_FLAG_IBOE)) +#define MLX4_INVALID_SLAVE_ID 0xFF + static inline int mlx4_is_master(struct mlx4_dev *dev) { return dev->flags & MLX4_FLAG_MASTER; -- cgit v1.2.3 From aeab97ed1503bedbe14d1e1c5ab7b90253a67664 Mon Sep 17 00:00:00 2001 From: Erez Shitrit Date: Tue, 19 Jun 2012 11:21:38 +0300 Subject: IB/sa: Add GuidInfoRecord query support This query is needed for SRIOV alias GUID support. The query is implemented per the IB Spec definition in section 15.2.5.18 (GuidInfoRecord). Signed-off-by: Erez Shitrit Signed-off-by: Jack Morgenstein Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier --- drivers/infiniband/core/sa_query.c | 133 +++++++++++++++++++++++++++++++++++++ include/rdma/ib_sa.h | 33 +++++++++ 2 files changed, 166 insertions(+) (limited to 'include') diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c index fbbfa24cf572..a8905abc56e4 100644 --- a/drivers/infiniband/core/sa_query.c +++ b/drivers/infiniband/core/sa_query.c @@ -94,6 +94,12 @@ struct ib_sa_path_query { struct ib_sa_query sa_query; }; +struct ib_sa_guidinfo_query { + void (*callback)(int, struct ib_sa_guidinfo_rec *, void *); + void *context; + struct ib_sa_query sa_query; +}; + struct ib_sa_mcmember_query { void (*callback)(int, struct ib_sa_mcmember_rec *, void *); void *context; @@ -347,6 +353,34 @@ static const struct ib_field service_rec_table[] = { .size_bits = 2*64 }, }; +#define GUIDINFO_REC_FIELD(field) \ + .struct_offset_bytes = offsetof(struct ib_sa_guidinfo_rec, field), \ + .struct_size_bytes = sizeof((struct ib_sa_guidinfo_rec *) 0)->field, \ + .field_name = "sa_guidinfo_rec:" #field + +static const struct ib_field guidinfo_rec_table[] = { + { GUIDINFO_REC_FIELD(lid), + .offset_words = 0, + .offset_bits = 0, + .size_bits = 16 }, + { GUIDINFO_REC_FIELD(block_num), + .offset_words = 0, + .offset_bits = 16, + .size_bits = 8 }, + { GUIDINFO_REC_FIELD(res1), + .offset_words = 0, + .offset_bits = 24, + .size_bits = 8 }, + { GUIDINFO_REC_FIELD(res2), + .offset_words = 1, + .offset_bits = 0, + .size_bits = 32 }, + { GUIDINFO_REC_FIELD(guid_info_list), + .offset_words = 2, + .offset_bits = 0, + .size_bits = 512 }, +}; + static void free_sm_ah(struct kref *kref) { struct ib_sa_sm_ah *sm_ah = container_of(kref, struct ib_sa_sm_ah, ref); @@ -945,6 +979,105 @@ err1: return ret; } +/* Support GuidInfoRecord */ +static void ib_sa_guidinfo_rec_callback(struct ib_sa_query *sa_query, + int status, + struct ib_sa_mad *mad) +{ + struct ib_sa_guidinfo_query *query = + container_of(sa_query, struct ib_sa_guidinfo_query, sa_query); + + if (mad) { + struct ib_sa_guidinfo_rec rec; + + ib_unpack(guidinfo_rec_table, ARRAY_SIZE(guidinfo_rec_table), + mad->data, &rec); + query->callback(status, &rec, query->context); + } else + query->callback(status, NULL, query->context); +} + +static void ib_sa_guidinfo_rec_release(struct ib_sa_query *sa_query) +{ + kfree(container_of(sa_query, struct ib_sa_guidinfo_query, sa_query)); +} + +int ib_sa_guid_info_rec_query(struct ib_sa_client *client, + struct ib_device *device, u8 port_num, + struct ib_sa_guidinfo_rec *rec, + ib_sa_comp_mask comp_mask, u8 method, + int timeout_ms, gfp_t gfp_mask, + void (*callback)(int status, + struct ib_sa_guidinfo_rec *resp, + void *context), + void *context, + struct ib_sa_query **sa_query) +{ + struct ib_sa_guidinfo_query *query; + struct ib_sa_device *sa_dev = ib_get_client_data(device, &sa_client); + struct ib_sa_port *port; + struct ib_mad_agent *agent; + struct ib_sa_mad *mad; + int ret; + + if (!sa_dev) + return -ENODEV; + + if (method != IB_MGMT_METHOD_GET && + method != IB_MGMT_METHOD_SET && + method != IB_SA_METHOD_DELETE) { + return -EINVAL; + } + + port = &sa_dev->port[port_num - sa_dev->start_port]; + agent = port->agent; + + query = kmalloc(sizeof *query, gfp_mask); + if (!query) + return -ENOMEM; + + query->sa_query.port = port; + ret = alloc_mad(&query->sa_query, gfp_mask); + if (ret) + goto err1; + + ib_sa_client_get(client); + query->sa_query.client = client; + query->callback = callback; + query->context = context; + + mad = query->sa_query.mad_buf->mad; + init_mad(mad, agent); + + query->sa_query.callback = callback ? ib_sa_guidinfo_rec_callback : NULL; + query->sa_query.release = ib_sa_guidinfo_rec_release; + + mad->mad_hdr.method = method; + mad->mad_hdr.attr_id = cpu_to_be16(IB_SA_ATTR_GUID_INFO_REC); + mad->sa_hdr.comp_mask = comp_mask; + + ib_pack(guidinfo_rec_table, ARRAY_SIZE(guidinfo_rec_table), rec, + mad->data); + + *sa_query = &query->sa_query; + + ret = send_mad(&query->sa_query, timeout_ms, gfp_mask); + if (ret < 0) + goto err2; + + return ret; + +err2: + *sa_query = NULL; + ib_sa_client_put(query->sa_query.client); + free_mad(&query->sa_query); + +err1: + kfree(query); + return ret; +} +EXPORT_SYMBOL(ib_sa_guid_info_rec_query); + static void send_handler(struct ib_mad_agent *agent, struct ib_mad_send_wc *mad_send_wc) { diff --git a/include/rdma/ib_sa.h b/include/rdma/ib_sa.h index d44a56388a3e..8275e539bace 100644 --- a/include/rdma/ib_sa.h +++ b/include/rdma/ib_sa.h @@ -251,6 +251,28 @@ struct ib_sa_service_rec { u64 data64[2]; }; +#define IB_SA_GUIDINFO_REC_LID IB_SA_COMP_MASK(0) +#define IB_SA_GUIDINFO_REC_BLOCK_NUM IB_SA_COMP_MASK(1) +#define IB_SA_GUIDINFO_REC_RES1 IB_SA_COMP_MASK(2) +#define IB_SA_GUIDINFO_REC_RES2 IB_SA_COMP_MASK(3) +#define IB_SA_GUIDINFO_REC_GID0 IB_SA_COMP_MASK(4) +#define IB_SA_GUIDINFO_REC_GID1 IB_SA_COMP_MASK(5) +#define IB_SA_GUIDINFO_REC_GID2 IB_SA_COMP_MASK(6) +#define IB_SA_GUIDINFO_REC_GID3 IB_SA_COMP_MASK(7) +#define IB_SA_GUIDINFO_REC_GID4 IB_SA_COMP_MASK(8) +#define IB_SA_GUIDINFO_REC_GID5 IB_SA_COMP_MASK(9) +#define IB_SA_GUIDINFO_REC_GID6 IB_SA_COMP_MASK(10) +#define IB_SA_GUIDINFO_REC_GID7 IB_SA_COMP_MASK(11) + +struct ib_sa_guidinfo_rec { + __be16 lid; + u8 block_num; + /* reserved */ + u8 res1; + __be32 res2; + u8 guid_info_list[64]; +}; + struct ib_sa_client { atomic_t users; struct completion comp; @@ -385,4 +407,15 @@ int ib_init_ah_from_path(struct ib_device *device, u8 port_num, */ void ib_sa_unpack_path(void *attribute, struct ib_sa_path_rec *rec); +/* Support GuidInfoRecord */ +int ib_sa_guid_info_rec_query(struct ib_sa_client *client, + struct ib_device *device, u8 port_num, + struct ib_sa_guidinfo_rec *rec, + ib_sa_comp_mask comp_mask, u8 method, + int timeout_ms, gfp_t gfp_mask, + void (*callback)(int status, + struct ib_sa_guidinfo_rec *resp, + void *context), + void *context, + struct ib_sa_query **sa_query); #endif /* IB_SA_H */ -- cgit v1.2.3 From 3045f0920367e625bbec7d66fadb444e673515af Mon Sep 17 00:00:00 2001 From: Jack Morgenstein Date: Tue, 19 Jun 2012 11:21:39 +0300 Subject: IB/core: Move CM_xxx_ATTR_ID macros from cm_msgs.h to ib_cm.h These macros will be reused by the mlx4 SRIOV-IB CM paravirtualization code, and there is no reason to have them declared both in the IB core in the mlx4 IB driver. Signed-off-by: Jack Morgenstein Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier --- drivers/infiniband/core/cm_msgs.h | 12 ------------ include/rdma/ib_cm.h | 12 ++++++++++++ 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/core/cm_msgs.h b/drivers/infiniband/core/cm_msgs.h index 7da9b2102341..be068f47e47e 100644 --- a/drivers/infiniband/core/cm_msgs.h +++ b/drivers/infiniband/core/cm_msgs.h @@ -44,18 +44,6 @@ #define IB_CM_CLASS_VERSION 2 /* IB specification 1.2 */ -#define CM_REQ_ATTR_ID cpu_to_be16(0x0010) -#define CM_MRA_ATTR_ID cpu_to_be16(0x0011) -#define CM_REJ_ATTR_ID cpu_to_be16(0x0012) -#define CM_REP_ATTR_ID cpu_to_be16(0x0013) -#define CM_RTU_ATTR_ID cpu_to_be16(0x0014) -#define CM_DREQ_ATTR_ID cpu_to_be16(0x0015) -#define CM_DREP_ATTR_ID cpu_to_be16(0x0016) -#define CM_SIDR_REQ_ATTR_ID cpu_to_be16(0x0017) -#define CM_SIDR_REP_ATTR_ID cpu_to_be16(0x0018) -#define CM_LAP_ATTR_ID cpu_to_be16(0x0019) -#define CM_APR_ATTR_ID cpu_to_be16(0x001A) - enum cm_msg_sequence { CM_MSG_SEQUENCE_REQ, CM_MSG_SEQUENCE_LAP, diff --git a/include/rdma/ib_cm.h b/include/rdma/ib_cm.h index 83f77ac33957..0e3ff30647d5 100644 --- a/include/rdma/ib_cm.h +++ b/include/rdma/ib_cm.h @@ -262,6 +262,18 @@ struct ib_cm_event { void *private_data; }; +#define CM_REQ_ATTR_ID cpu_to_be16(0x0010) +#define CM_MRA_ATTR_ID cpu_to_be16(0x0011) +#define CM_REJ_ATTR_ID cpu_to_be16(0x0012) +#define CM_REP_ATTR_ID cpu_to_be16(0x0013) +#define CM_RTU_ATTR_ID cpu_to_be16(0x0014) +#define CM_DREQ_ATTR_ID cpu_to_be16(0x0015) +#define CM_DREP_ATTR_ID cpu_to_be16(0x0016) +#define CM_SIDR_REQ_ATTR_ID cpu_to_be16(0x0017) +#define CM_SIDR_REP_ATTR_ID cpu_to_be16(0x0018) +#define CM_LAP_ATTR_ID cpu_to_be16(0x0019) +#define CM_APR_ATTR_ID cpu_to_be16(0x001A) + /** * ib_cm_handler - User-defined callback to process communication events. * @cm_id: Communication identifier associated with the reported event. -- cgit v1.2.3 From 039363f38bfe5f6281e9eae5e0518b11577d9d50 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Fri, 6 Jul 2012 15:25:10 -0500 Subject: mm, sl[aou]b: Extract common code for kmem_cache_create() Kmem_cache_create() does a variety of sanity checks but those vary depending on the allocator. Use the strictest tests and put them into a slab_common file. Make the tests conditional on CONFIG_DEBUG_VM. This patch has the effect of adding sanity checks for SLUB and SLOB under CONFIG_DEBUG_VM and removes the checks in SLAB for !CONFIG_DEBUG_VM. Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/slab.h | 4 ++++ mm/Makefile | 3 ++- mm/slab.c | 24 +++++++------------ mm/slab_common.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++ mm/slob.c | 8 +++---- mm/slub.c | 11 +-------- 6 files changed, 87 insertions(+), 31 deletions(-) create mode 100644 mm/slab_common.c (limited to 'include') diff --git a/include/linux/slab.h b/include/linux/slab.h index 0dd2dfa7beca..0cb7c7eb0416 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -130,6 +130,10 @@ int kmem_cache_shrink(struct kmem_cache *); void kmem_cache_free(struct kmem_cache *, void *); unsigned int kmem_cache_size(struct kmem_cache *); +/* Slab internal function */ +struct kmem_cache *__kmem_cache_create(const char *, size_t, size_t, + unsigned long, + void (*)(void *)); /* * Please use this macro to create slab caches. Simply specify the * name of the structure and maybe some flags that are listed above. diff --git a/mm/Makefile b/mm/Makefile index a156285ce88d..ae370783612d 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -16,7 +16,8 @@ obj-y := filemap.o mempool.o oom_kill.o fadvise.o \ readahead.o swap.o truncate.o vmscan.o shmem.o \ prio_tree.o util.o mmzone.o vmstat.o backing-dev.o \ page_isolation.o mm_init.o mmu_context.o percpu.o \ - compaction.o $(mmu-y) + compaction.o slab_common.o $(mmu-y) + obj-y += init-mm.o ifdef CONFIG_NO_BOOTMEM diff --git a/mm/slab.c b/mm/slab.c index 105f188d14a3..10c821e492bf 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -1558,7 +1558,7 @@ void __init kmem_cache_init(void) * bug. */ - sizes[INDEX_AC].cs_cachep = kmem_cache_create(names[INDEX_AC].name, + sizes[INDEX_AC].cs_cachep = __kmem_cache_create(names[INDEX_AC].name, sizes[INDEX_AC].cs_size, ARCH_KMALLOC_MINALIGN, ARCH_KMALLOC_FLAGS|SLAB_PANIC, @@ -1566,7 +1566,7 @@ void __init kmem_cache_init(void) if (INDEX_AC != INDEX_L3) { sizes[INDEX_L3].cs_cachep = - kmem_cache_create(names[INDEX_L3].name, + __kmem_cache_create(names[INDEX_L3].name, sizes[INDEX_L3].cs_size, ARCH_KMALLOC_MINALIGN, ARCH_KMALLOC_FLAGS|SLAB_PANIC, @@ -1584,14 +1584,14 @@ void __init kmem_cache_init(void) * allow tighter packing of the smaller caches. */ if (!sizes->cs_cachep) { - sizes->cs_cachep = kmem_cache_create(names->name, + sizes->cs_cachep = __kmem_cache_create(names->name, sizes->cs_size, ARCH_KMALLOC_MINALIGN, ARCH_KMALLOC_FLAGS|SLAB_PANIC, NULL); } #ifdef CONFIG_ZONE_DMA - sizes->cs_dmacachep = kmem_cache_create( + sizes->cs_dmacachep = __kmem_cache_create( names->name_dma, sizes->cs_size, ARCH_KMALLOC_MINALIGN, @@ -2220,7 +2220,7 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp) } /** - * kmem_cache_create - Create a cache. + * __kmem_cache_create - Create a cache. * @name: A string which is used in /proc/slabinfo to identify this cache. * @size: The size of objects to be created in this cache. * @align: The required alignment for the objects. @@ -2247,7 +2247,7 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp) * as davem. */ struct kmem_cache * -kmem_cache_create (const char *name, size_t size, size_t align, +__kmem_cache_create (const char *name, size_t size, size_t align, unsigned long flags, void (*ctor)(void *)) { size_t left_over, slab_size, ralign; @@ -2388,7 +2388,7 @@ kmem_cache_create (const char *name, size_t size, size_t align, /* Get cache's description obj. */ cachep = kmem_cache_zalloc(&cache_cache, gfp); if (!cachep) - goto oops; + return NULL; cachep->nodelists = (struct kmem_list3 **)&cachep->array[nr_cpu_ids]; cachep->object_size = size; @@ -2445,8 +2445,7 @@ kmem_cache_create (const char *name, size_t size, size_t align, printk(KERN_ERR "kmem_cache_create: couldn't create cache %s.\n", name); kmem_cache_free(&cache_cache, cachep); - cachep = NULL; - goto oops; + return NULL; } slab_size = ALIGN(cachep->num * sizeof(kmem_bufctl_t) + sizeof(struct slab), align); @@ -2504,8 +2503,7 @@ kmem_cache_create (const char *name, size_t size, size_t align, if (setup_cpu_cache(cachep, gfp)) { __kmem_cache_destroy(cachep); - cachep = NULL; - goto oops; + return NULL; } if (flags & SLAB_DEBUG_OBJECTS) { @@ -2521,16 +2519,12 @@ kmem_cache_create (const char *name, size_t size, size_t align, /* cache setup completed, link it into the list */ list_add(&cachep->list, &cache_chain); oops: - if (!cachep && (flags & SLAB_PANIC)) - panic("kmem_cache_create(): failed to create slab `%s'\n", - name); if (slab_is_available()) { mutex_unlock(&cache_chain_mutex); put_online_cpus(); } return cachep; } -EXPORT_SYMBOL(kmem_cache_create); #if DEBUG static void check_irq_off(void) diff --git a/mm/slab_common.c b/mm/slab_common.c new file mode 100644 index 000000000000..80412beb67cc --- /dev/null +++ b/mm/slab_common.c @@ -0,0 +1,68 @@ +/* + * Slab allocator functions that are independent of the allocator strategy + * + * (C) 2012 Christoph Lameter + */ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * kmem_cache_create - Create a cache. + * @name: A string which is used in /proc/slabinfo to identify this cache. + * @size: The size of objects to be created in this cache. + * @align: The required alignment for the objects. + * @flags: SLAB flags + * @ctor: A constructor for the objects. + * + * Returns a ptr to the cache on success, NULL on failure. + * Cannot be called within a interrupt, but can be interrupted. + * The @ctor is run when new pages are allocated by the cache. + * + * The flags are + * + * %SLAB_POISON - Poison the slab with a known test pattern (a5a5a5a5) + * to catch references to uninitialised memory. + * + * %SLAB_RED_ZONE - Insert `Red' zones around the allocated memory to check + * for buffer overruns. + * + * %SLAB_HWCACHE_ALIGN - Align the objects in this cache to a hardware + * cacheline. This can be beneficial if you're counting cycles as closely + * as davem. + */ + +struct kmem_cache *kmem_cache_create(const char *name, size_t size, size_t align, + unsigned long flags, void (*ctor)(void *)) +{ + struct kmem_cache *s = NULL; + +#ifdef CONFIG_DEBUG_VM + if (!name || in_interrupt() || size < sizeof(void *) || + size > KMALLOC_MAX_SIZE) { + printk(KERN_ERR "kmem_cache_create(%s) integrity check" + " failed\n", name); + goto out; + } +#endif + + s = __kmem_cache_create(name, size, align, flags, ctor); + +#ifdef CONFIG_DEBUG_VM +out: +#endif + if (!s && (flags & SLAB_PANIC)) + panic("kmem_cache_create: Failed to create slab '%s'\n", name); + + return s; +} +EXPORT_SYMBOL(kmem_cache_create); diff --git a/mm/slob.c b/mm/slob.c index 95d1c7dd88e0..d63923d549ec 100644 --- a/mm/slob.c +++ b/mm/slob.c @@ -506,7 +506,7 @@ size_t ksize(const void *block) } EXPORT_SYMBOL(ksize); -struct kmem_cache *kmem_cache_create(const char *name, size_t size, +struct kmem_cache *__kmem_cache_create(const char *name, size_t size, size_t align, unsigned long flags, void (*ctor)(void *)) { struct kmem_cache *c; @@ -529,13 +529,11 @@ struct kmem_cache *kmem_cache_create(const char *name, size_t size, c->align = ARCH_SLAB_MINALIGN; if (c->align < align) c->align = align; - } else if (flags & SLAB_PANIC) - panic("Cannot create slab cache %s\n", name); - kmemleak_alloc(c, sizeof(struct kmem_cache), 1, GFP_KERNEL); + kmemleak_alloc(c, sizeof(struct kmem_cache), 1, GFP_KERNEL); + } return c; } -EXPORT_SYMBOL(kmem_cache_create); void kmem_cache_destroy(struct kmem_cache *c) { diff --git a/mm/slub.c b/mm/slub.c index 79fe9c6b93cf..6551cc9a51f8 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -3920,15 +3920,12 @@ static struct kmem_cache *find_mergeable(size_t size, return NULL; } -struct kmem_cache *kmem_cache_create(const char *name, size_t size, +struct kmem_cache *__kmem_cache_create(const char *name, size_t size, size_t align, unsigned long flags, void (*ctor)(void *)) { struct kmem_cache *s; char *n; - if (WARN_ON(!name)) - return NULL; - down_write(&slub_lock); s = find_mergeable(size, align, flags, name, ctor); if (s) { @@ -3972,14 +3969,8 @@ struct kmem_cache *kmem_cache_create(const char *name, size_t size, kfree(n); err: up_write(&slub_lock); - - if (flags & SLAB_PANIC) - panic("Cannot create slabcache %s\n", name); - else - s = NULL; return s; } -EXPORT_SYMBOL(kmem_cache_create); #ifdef CONFIG_SMP /* -- cgit v1.2.3 From 97d06609158e61f6bdf538c4a6788e2de492236f Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Fri, 6 Jul 2012 15:25:11 -0500 Subject: mm, sl[aou]b: Common definition for boot state of the slab allocators All allocators have some sort of support for the bootstrap status. Setup a common definition for the boot states and make all slab allocators use that definition. Reviewed-by: Glauber Costa Reviewed-by: Joonsoo Kim Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/slab.h | 4 ---- mm/slab.c | 45 ++++++++++++++------------------------------- mm/slab.h | 29 +++++++++++++++++++++++++++++ mm/slab_common.c | 9 +++++++++ mm/slob.c | 14 +++++--------- mm/slub.c | 21 +++++---------------- 6 files changed, 62 insertions(+), 60 deletions(-) create mode 100644 mm/slab.h (limited to 'include') diff --git a/include/linux/slab.h b/include/linux/slab.h index 0cb7c7eb0416..0dd2dfa7beca 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -130,10 +130,6 @@ int kmem_cache_shrink(struct kmem_cache *); void kmem_cache_free(struct kmem_cache *, void *); unsigned int kmem_cache_size(struct kmem_cache *); -/* Slab internal function */ -struct kmem_cache *__kmem_cache_create(const char *, size_t, size_t, - unsigned long, - void (*)(void *)); /* * Please use this macro to create slab caches. Simply specify the * name of the structure and maybe some flags that are listed above. diff --git a/mm/slab.c b/mm/slab.c index 10c821e492bf..59a466b85b0f 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -87,6 +87,7 @@ */ #include +#include "slab.h" #include #include #include @@ -565,27 +566,6 @@ static struct kmem_cache cache_cache = { #define BAD_ALIEN_MAGIC 0x01020304ul -/* - * chicken and egg problem: delay the per-cpu array allocation - * until the general caches are up. - */ -static enum { - NONE, - PARTIAL_AC, - PARTIAL_L3, - EARLY, - LATE, - FULL -} g_cpucache_up; - -/* - * used by boot code to determine if it can use slab based allocator - */ -int slab_is_available(void) -{ - return g_cpucache_up >= EARLY; -} - #ifdef CONFIG_LOCKDEP /* @@ -651,7 +631,7 @@ static void init_node_lock_keys(int q) { struct cache_sizes *s = malloc_sizes; - if (g_cpucache_up < LATE) + if (slab_state < UP) return; for (s = malloc_sizes; s->cs_size != ULONG_MAX; s++) { @@ -1649,14 +1629,14 @@ void __init kmem_cache_init(void) } } - g_cpucache_up = EARLY; + slab_state = UP; } void __init kmem_cache_init_late(void) { struct kmem_cache *cachep; - g_cpucache_up = LATE; + slab_state = UP; /* Annotate slab for lockdep -- annotate the malloc caches */ init_lock_keys(); @@ -1668,6 +1648,9 @@ void __init kmem_cache_init_late(void) BUG(); mutex_unlock(&cache_chain_mutex); + /* Done! */ + slab_state = FULL; + /* * Register a cpu startup notifier callback that initializes * cpu_cache_get for all new cpus @@ -1699,7 +1682,7 @@ static int __init cpucache_init(void) start_cpu_timer(cpu); /* Done! */ - g_cpucache_up = FULL; + slab_state = FULL; return 0; } __initcall(cpucache_init); @@ -2167,10 +2150,10 @@ static size_t calculate_slab_order(struct kmem_cache *cachep, static int __init_refok setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp) { - if (g_cpucache_up >= LATE) + if (slab_state >= FULL) return enable_cpucache(cachep, gfp); - if (g_cpucache_up == NONE) { + if (slab_state == DOWN) { /* * Note: the first kmem_cache_create must create the cache * that's used by kmalloc(24), otherwise the creation of @@ -2185,16 +2168,16 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp) */ set_up_list3s(cachep, SIZE_AC); if (INDEX_AC == INDEX_L3) - g_cpucache_up = PARTIAL_L3; + slab_state = PARTIAL_L3; else - g_cpucache_up = PARTIAL_AC; + slab_state = PARTIAL_ARRAYCACHE; } else { cachep->array[smp_processor_id()] = kmalloc(sizeof(struct arraycache_init), gfp); - if (g_cpucache_up == PARTIAL_AC) { + if (slab_state == PARTIAL_ARRAYCACHE) { set_up_list3s(cachep, SIZE_L3); - g_cpucache_up = PARTIAL_L3; + slab_state = PARTIAL_L3; } else { int node; for_each_online_node(node) { diff --git a/mm/slab.h b/mm/slab.h new file mode 100644 index 000000000000..f9a9815cdc82 --- /dev/null +++ b/mm/slab.h @@ -0,0 +1,29 @@ +#ifndef MM_SLAB_H +#define MM_SLAB_H +/* + * Internal slab definitions + */ + +/* + * State of the slab allocator. + * + * This is used to describe the states of the allocator during bootup. + * Allocators use this to gradually bootstrap themselves. Most allocators + * have the problem that the structures used for managing slab caches are + * allocated from slab caches themselves. + */ +enum slab_state { + DOWN, /* No slab functionality yet */ + PARTIAL, /* SLUB: kmem_cache_node available */ + PARTIAL_ARRAYCACHE, /* SLAB: kmalloc size for arraycache available */ + PARTIAL_L3, /* SLAB: kmalloc size for l3 struct available */ + UP, /* Slab caches usable but not all extras yet */ + FULL /* Everything is working */ +}; + +extern enum slab_state slab_state; + +struct kmem_cache *__kmem_cache_create(const char *name, size_t size, + size_t align, unsigned long flags, void (*ctor)(void *)); + +#endif diff --git a/mm/slab_common.c b/mm/slab_common.c index 80412beb67cc..ca1aaf69a1f5 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -16,6 +16,10 @@ #include #include +#include "slab.h" + +enum slab_state slab_state; + /* * kmem_cache_create - Create a cache. * @name: A string which is used in /proc/slabinfo to identify this cache. @@ -66,3 +70,8 @@ out: return s; } EXPORT_SYMBOL(kmem_cache_create); + +int slab_is_available(void) +{ + return slab_state >= UP; +} diff --git a/mm/slob.c b/mm/slob.c index d63923d549ec..0111e0dece93 100644 --- a/mm/slob.c +++ b/mm/slob.c @@ -59,6 +59,8 @@ #include #include +#include "slab.h" + #include #include /* struct reclaim_state */ #include @@ -531,6 +533,7 @@ struct kmem_cache *__kmem_cache_create(const char *name, size_t size, c->align = align; kmemleak_alloc(c, sizeof(struct kmem_cache), 1, GFP_KERNEL); + c->refcount = 1; } return c; } @@ -616,19 +619,12 @@ int kmem_cache_shrink(struct kmem_cache *d) } EXPORT_SYMBOL(kmem_cache_shrink); -static unsigned int slob_ready __read_mostly; - -int slab_is_available(void) -{ - return slob_ready; -} - void __init kmem_cache_init(void) { - slob_ready = 1; + slab_state = UP; } void __init kmem_cache_init_late(void) { - /* Nothing to do */ + slab_state = FULL; } diff --git a/mm/slub.c b/mm/slub.c index 6551cc9a51f8..4c385164d9f7 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -16,6 +16,7 @@ #include #include #include +#include "slab.h" #include #include #include @@ -182,13 +183,6 @@ static int kmem_size = sizeof(struct kmem_cache); static struct notifier_block slab_notifier; #endif -static enum { - DOWN, /* No slab functionality available */ - PARTIAL, /* Kmem_cache_node works */ - UP, /* Everything works but does not show up in sysfs */ - SYSFS /* Sysfs up */ -} slab_state = DOWN; - /* A list of all slab caches on the system */ static DECLARE_RWSEM(slub_lock); static LIST_HEAD(slab_caches); @@ -237,11 +231,6 @@ static inline void stat(const struct kmem_cache *s, enum stat_item si) * Core slab cache functions *******************************************************************/ -int slab_is_available(void) -{ - return slab_state >= UP; -} - static inline struct kmem_cache_node *get_node(struct kmem_cache *s, int node) { return s->node[node]; @@ -5274,7 +5263,7 @@ static int sysfs_slab_add(struct kmem_cache *s) const char *name; int unmergeable; - if (slab_state < SYSFS) + if (slab_state < FULL) /* Defer until later */ return 0; @@ -5319,7 +5308,7 @@ static int sysfs_slab_add(struct kmem_cache *s) static void sysfs_slab_remove(struct kmem_cache *s) { - if (slab_state < SYSFS) + if (slab_state < FULL) /* * Sysfs has not been setup yet so no need to remove the * cache from sysfs. @@ -5347,7 +5336,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name) { struct saved_alias *al; - if (slab_state == SYSFS) { + if (slab_state == FULL) { /* * If we have a leftover link then remove it. */ @@ -5380,7 +5369,7 @@ static int __init slab_sysfs_init(void) return -ENOSYS; } - slab_state = SYSFS; + slab_state = FULL; list_for_each_entry(s, &slab_caches, list) { err = sysfs_slab_add(s); -- cgit v1.2.3 From af97bace2cca58ee7c94bf4d31e820f29688d7a5 Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Mon, 11 Jun 2012 22:56:26 +0200 Subject: i2c-nomadik: move header to The header and driver are only used by arm/mach-u8500 (and potentially arm/mach-nomadik), but the STA2X11 I/O Hub exports on PCIe a number of devices, including i2c-nomadik. This patch allows compilation of the driver under x86. Signed-off-by: Alessandro Rubini Acked-by: Giancarlo Asnaghi Tested-by: Linus Walleij Signed-off-by: Wolfram Sang --- arch/arm/mach-ux500/board-mop500.c | 2 +- arch/arm/mach-ux500/devices-common.h | 2 +- arch/arm/plat-nomadik/include/plat/i2c.h | 39 ------------------------------- drivers/i2c/busses/i2c-nomadik.c | 3 +-- include/linux/platform_data/i2c-nomadik.h | 39 +++++++++++++++++++++++++++++++ 5 files changed, 42 insertions(+), 43 deletions(-) delete mode 100644 arch/arm/plat-nomadik/include/plat/i2c.h create mode 100644 include/linux/platform_data/i2c-nomadik.h (limited to 'include') diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index 1509a3cb5833..f9a964836d3a 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -39,7 +40,6 @@ #include #include -#include #include #include diff --git a/arch/arm/mach-ux500/devices-common.h b/arch/arm/mach-ux500/devices-common.h index 6e4706560266..23cf734b5384 100644 --- a/arch/arm/mach-ux500/devices-common.h +++ b/arch/arm/mach-ux500/devices-common.h @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include struct spi_master_cntlr; diff --git a/arch/arm/plat-nomadik/include/plat/i2c.h b/arch/arm/plat-nomadik/include/plat/i2c.h deleted file mode 100644 index 8ba70ffc31ec..000000000000 --- a/arch/arm/plat-nomadik/include/plat/i2c.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2009 ST-Ericsson - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, as - * published by the Free Software Foundation. - */ -#ifndef __PLAT_I2C_H -#define __PLAT_I2C_H - -enum i2c_freq_mode { - I2C_FREQ_MODE_STANDARD, /* up to 100 Kb/s */ - I2C_FREQ_MODE_FAST, /* up to 400 Kb/s */ - I2C_FREQ_MODE_HIGH_SPEED, /* up to 3.4 Mb/s */ - I2C_FREQ_MODE_FAST_PLUS, /* up to 1 Mb/s */ -}; - -/** - * struct nmk_i2c_controller - client specific controller configuration - * @clk_freq: clock frequency for the operation mode - * @slsu: Slave data setup time in ns. - * The needed setup time for three modes of operation - * are 250ns, 100ns and 10ns respectively thus leading - * to the values of 14, 6, 2 for a 48 MHz i2c clk - * @tft: Tx FIFO Threshold in bytes - * @rft: Rx FIFO Threshold in bytes - * @timeout Slave response timeout(ms) - * @sm: speed mode - */ -struct nmk_i2c_controller { - unsigned long clk_freq; - unsigned short slsu; - unsigned char tft; - unsigned char rft; - int timeout; - enum i2c_freq_mode sm; -}; - -#endif /* __PLAT_I2C_H */ diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index 5267ab93d550..752f1fda371f 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -23,8 +23,7 @@ #include #include #include - -#include +#include #define DRIVER_NAME "nmk-i2c" diff --git a/include/linux/platform_data/i2c-nomadik.h b/include/linux/platform_data/i2c-nomadik.h new file mode 100644 index 000000000000..c2303c3e4803 --- /dev/null +++ b/include/linux/platform_data/i2c-nomadik.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2009 ST-Ericsson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + */ +#ifndef __PDATA_I2C_NOMADIK_H +#define __PDATA_I2C_NOMADIK_H + +enum i2c_freq_mode { + I2C_FREQ_MODE_STANDARD, /* up to 100 Kb/s */ + I2C_FREQ_MODE_FAST, /* up to 400 Kb/s */ + I2C_FREQ_MODE_HIGH_SPEED, /* up to 3.4 Mb/s */ + I2C_FREQ_MODE_FAST_PLUS, /* up to 1 Mb/s */ +}; + +/** + * struct nmk_i2c_controller - client specific controller configuration + * @clk_freq: clock frequency for the operation mode + * @slsu: Slave data setup time in ns. + * The needed setup time for three modes of operation + * are 250ns, 100ns and 10ns respectively thus leading + * to the values of 14, 6, 2 for a 48 MHz i2c clk + * @tft: Tx FIFO Threshold in bytes + * @rft: Rx FIFO Threshold in bytes + * @timeout Slave response timeout(ms) + * @sm: speed mode + */ +struct nmk_i2c_controller { + unsigned long clk_freq; + unsigned short slsu; + unsigned char tft; + unsigned char rft; + int timeout; + enum i2c_freq_mode sm; +}; + +#endif /* __PDATA_I2C_NOMADIK_H */ -- cgit v1.2.3 From 70c6cce040661204986ebbf22224cb24bd77ea71 Mon Sep 17 00:00:00 2001 From: Qiao Zhou Date: Mon, 9 Jul 2012 14:37:32 +0800 Subject: mfd: Support 88pm80x in 80x driver 88PM800 and 88PM805 are two discrete chips used for power management. Hardware designer can use them together or only one of them according to requirement. 88pm80x.c provides common i2c driver handling for both 800 and 805, such as i2c_driver init, regmap init, read/write api etc. 88pm800.c handles specifically for 800, such as chip init, irq init/handle, mfd device register, including rtc, onkey, regulator( to be add later) etc. besides that, 800 has three i2c device, one regular i2c client, two other i2c dummy for gpadc and power purpose. 88pm805.c handles specifically for 805, such as chip init, irq init/handle, mfd device register, including codec, headset/mic detect etc. the i2c operation of both 800 and 805 are via regmap, and 88pm80x-i2c exported a group of r/w bulk r/w and bits set API for facility. Signed-off-by: Qiao Zhou Reviewed-by: Arnd Bergmann Signed-off-by: Samuel Ortiz --- drivers/mfd/88pm800.c | 593 ++++++++++++++++++++++++++++++++++++++++++++ drivers/mfd/88pm805.c | 299 ++++++++++++++++++++++ drivers/mfd/88pm80x.c | 116 +++++++++ drivers/mfd/Kconfig | 24 ++ drivers/mfd/Makefile | 2 + include/linux/mfd/88pm80x.h | 368 +++++++++++++++++++++++++++ 6 files changed, 1402 insertions(+) create mode 100644 drivers/mfd/88pm800.c create mode 100644 drivers/mfd/88pm805.c create mode 100644 drivers/mfd/88pm80x.c create mode 100644 include/linux/mfd/88pm80x.h (limited to 'include') diff --git a/drivers/mfd/88pm800.c b/drivers/mfd/88pm800.c new file mode 100644 index 000000000000..fe479ccfaa19 --- /dev/null +++ b/drivers/mfd/88pm800.c @@ -0,0 +1,593 @@ +/* + * Base driver for Marvell 88PM800 + * + * Copyright (C) 2012 Marvell International Ltd. + * Haojian Zhuang + * Joseph(Yossi) Hanin + * Qiao Zhou + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 + +#define PM800_CHIP_ID (0x00) + +/* Interrupt Registers */ +#define PM800_INT_STATUS1 (0x05) +#define PM800_ONKEY_INT_STS1 (1 << 0) +#define PM800_EXTON_INT_STS1 (1 << 1) +#define PM800_CHG_INT_STS1 (1 << 2) +#define PM800_BAT_INT_STS1 (1 << 3) +#define PM800_RTC_INT_STS1 (1 << 4) +#define PM800_CLASSD_OC_INT_STS1 (1 << 5) + +#define PM800_INT_STATUS2 (0x06) +#define PM800_VBAT_INT_STS2 (1 << 0) +#define PM800_VSYS_INT_STS2 (1 << 1) +#define PM800_VCHG_INT_STS2 (1 << 2) +#define PM800_TINT_INT_STS2 (1 << 3) +#define PM800_GPADC0_INT_STS2 (1 << 4) +#define PM800_TBAT_INT_STS2 (1 << 5) +#define PM800_GPADC2_INT_STS2 (1 << 6) +#define PM800_GPADC3_INT_STS2 (1 << 7) + +#define PM800_INT_STATUS3 (0x07) + +#define PM800_INT_STATUS4 (0x08) +#define PM800_GPIO0_INT_STS4 (1 << 0) +#define PM800_GPIO1_INT_STS4 (1 << 1) +#define PM800_GPIO2_INT_STS4 (1 << 2) +#define PM800_GPIO3_INT_STS4 (1 << 3) +#define PM800_GPIO4_INT_STS4 (1 << 4) + +#define PM800_INT_ENA_1 (0x09) +#define PM800_ONKEY_INT_ENA1 (1 << 0) +#define PM800_EXTON_INT_ENA1 (1 << 1) +#define PM800_CHG_INT_ENA1 (1 << 2) +#define PM800_BAT_INT_ENA1 (1 << 3) +#define PM800_RTC_INT_ENA1 (1 << 4) +#define PM800_CLASSD_OC_INT_ENA1 (1 << 5) + +#define PM800_INT_ENA_2 (0x0A) +#define PM800_VBAT_INT_ENA2 (1 << 0) +#define PM800_VSYS_INT_ENA2 (1 << 1) +#define PM800_VCHG_INT_ENA2 (1 << 2) +#define PM800_TINT_INT_ENA2 (1 << 3) + +#define PM800_INT_ENA_3 (0x0B) +#define PM800_GPADC0_INT_ENA3 (1 << 0) +#define PM800_GPADC1_INT_ENA3 (1 << 1) +#define PM800_GPADC2_INT_ENA3 (1 << 2) +#define PM800_GPADC3_INT_ENA3 (1 << 3) +#define PM800_GPADC4_INT_ENA3 (1 << 4) + +#define PM800_INT_ENA_4 (0x0C) +#define PM800_GPIO0_INT_ENA4 (1 << 0) +#define PM800_GPIO1_INT_ENA4 (1 << 1) +#define PM800_GPIO2_INT_ENA4 (1 << 2) +#define PM800_GPIO3_INT_ENA4 (1 << 3) +#define PM800_GPIO4_INT_ENA4 (1 << 4) + +/* number of INT_ENA & INT_STATUS regs */ +#define PM800_INT_REG_NUM (4) + +/* Interrupt Number in 88PM800 */ +enum { + PM800_IRQ_ONKEY, /*EN1b0 *//*0 */ + PM800_IRQ_EXTON, /*EN1b1 */ + PM800_IRQ_CHG, /*EN1b2 */ + PM800_IRQ_BAT, /*EN1b3 */ + PM800_IRQ_RTC, /*EN1b4 */ + PM800_IRQ_CLASSD, /*EN1b5 *//*5 */ + PM800_IRQ_VBAT, /*EN2b0 */ + PM800_IRQ_VSYS, /*EN2b1 */ + PM800_IRQ_VCHG, /*EN2b2 */ + PM800_IRQ_TINT, /*EN2b3 */ + PM800_IRQ_GPADC0, /*EN3b0 *//*10 */ + PM800_IRQ_GPADC1, /*EN3b1 */ + PM800_IRQ_GPADC2, /*EN3b2 */ + PM800_IRQ_GPADC3, /*EN3b3 */ + PM800_IRQ_GPADC4, /*EN3b4 */ + PM800_IRQ_GPIO0, /*EN4b0 *//*15 */ + PM800_IRQ_GPIO1, /*EN4b1 */ + PM800_IRQ_GPIO2, /*EN4b2 */ + PM800_IRQ_GPIO3, /*EN4b3 */ + PM800_IRQ_GPIO4, /*EN4b4 *//*19 */ + PM800_MAX_IRQ, +}; + +enum { + /* Procida */ + PM800_CHIP_A0 = 0x60, + PM800_CHIP_A1 = 0x61, + PM800_CHIP_B0 = 0x62, + PM800_CHIP_C0 = 0x63, + PM800_CHIP_END = PM800_CHIP_C0, + + /* Make sure to update this to the last stepping */ + PM8XXX_CHIP_END = PM800_CHIP_END +}; + +static const struct i2c_device_id pm80x_id_table[] = { + {"88PM800", CHIP_PM800}, +}; +MODULE_DEVICE_TABLE(i2c, pm80x_id_table); + +static struct resource rtc_resources[] = { + { + .name = "88pm80x-rtc", + .start = PM800_IRQ_RTC, + .end = PM800_IRQ_RTC, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell rtc_devs[] = { + { + .name = "88pm80x-rtc", + .num_resources = ARRAY_SIZE(rtc_resources), + .resources = &rtc_resources[0], + .id = -1, + }, +}; + +static struct resource onkey_resources[] = { + { + .name = "88pm80x-onkey", + .start = PM800_IRQ_ONKEY, + .end = PM800_IRQ_ONKEY, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell onkey_devs[] = { + { + .name = "88pm80x-onkey", + .num_resources = 1, + .resources = &onkey_resources[0], + .id = -1, + }, +}; + +static const struct regmap_irq pm800_irqs[] = { + /* INT0 */ + [PM800_IRQ_ONKEY] = { + .mask = PM800_ONKEY_INT_ENA1, + }, + [PM800_IRQ_EXTON] = { + .mask = PM800_EXTON_INT_ENA1, + }, + [PM800_IRQ_CHG] = { + .mask = PM800_CHG_INT_ENA1, + }, + [PM800_IRQ_BAT] = { + .mask = PM800_BAT_INT_ENA1, + }, + [PM800_IRQ_RTC] = { + .mask = PM800_RTC_INT_ENA1, + }, + [PM800_IRQ_CLASSD] = { + .mask = PM800_CLASSD_OC_INT_ENA1, + }, + /* INT1 */ + [PM800_IRQ_VBAT] = { + .reg_offset = 1, + .mask = PM800_VBAT_INT_ENA2, + }, + [PM800_IRQ_VSYS] = { + .reg_offset = 1, + .mask = PM800_VSYS_INT_ENA2, + }, + [PM800_IRQ_VCHG] = { + .reg_offset = 1, + .mask = PM800_VCHG_INT_ENA2, + }, + [PM800_IRQ_TINT] = { + .reg_offset = 1, + .mask = PM800_TINT_INT_ENA2, + }, + /* INT2 */ + [PM800_IRQ_GPADC0] = { + .reg_offset = 2, + .mask = PM800_GPADC0_INT_ENA3, + }, + [PM800_IRQ_GPADC1] = { + .reg_offset = 2, + .mask = PM800_GPADC1_INT_ENA3, + }, + [PM800_IRQ_GPADC2] = { + .reg_offset = 2, + .mask = PM800_GPADC2_INT_ENA3, + }, + [PM800_IRQ_GPADC3] = { + .reg_offset = 2, + .mask = PM800_GPADC3_INT_ENA3, + }, + [PM800_IRQ_GPADC4] = { + .reg_offset = 2, + .mask = PM800_GPADC4_INT_ENA3, + }, + /* INT3 */ + [PM800_IRQ_GPIO0] = { + .reg_offset = 3, + .mask = PM800_GPIO0_INT_ENA4, + }, + [PM800_IRQ_GPIO1] = { + .reg_offset = 3, + .mask = PM800_GPIO1_INT_ENA4, + }, + [PM800_IRQ_GPIO2] = { + .reg_offset = 3, + .mask = PM800_GPIO2_INT_ENA4, + }, + [PM800_IRQ_GPIO3] = { + .reg_offset = 3, + .mask = PM800_GPIO3_INT_ENA4, + }, + [PM800_IRQ_GPIO4] = { + .reg_offset = 3, + .mask = PM800_GPIO4_INT_ENA4, + }, +}; + +static int __devinit device_gpadc_init(struct pm80x_chip *chip, + struct pm80x_platform_data *pdata) +{ + struct pm80x_subchip *subchip = chip->subchip; + struct regmap *map = subchip->regmap_gpadc; + int data = 0, mask = 0, ret = 0; + + if (!map) { + dev_warn(chip->dev, + "Warning: gpadc regmap is not available!\n"); + return -EINVAL; + } + /* + * initialize GPADC without activating it turn on GPADC + * measurments + */ + ret = regmap_update_bits(map, + PM800_GPADC_MISC_CONFIG2, + PM800_GPADC_MISC_GPFSM_EN, + PM800_GPADC_MISC_GPFSM_EN); + if (ret < 0) + goto out; + /* + * This function configures the ADC as requires for + * CP implementation.CP does not "own" the ADC configuration + * registers and relies on AP. + * Reason: enable automatic ADC measurements needed + * for CP to get VBAT and RF temperature readings. + */ + ret = regmap_update_bits(map, PM800_GPADC_MEAS_EN1, + PM800_MEAS_EN1_VBAT, PM800_MEAS_EN1_VBAT); + if (ret < 0) + goto out; + ret = regmap_update_bits(map, PM800_GPADC_MEAS_EN2, + (PM800_MEAS_EN2_RFTMP | PM800_MEAS_GP0_EN), + (PM800_MEAS_EN2_RFTMP | PM800_MEAS_GP0_EN)); + if (ret < 0) + goto out; + + /* + * the defult of PM800 is GPADC operates at 100Ks/s rate + * and Number of GPADC slots with active current bias prior + * to GPADC sampling = 1 slot for all GPADCs set for + * Temprature mesurmants + */ + mask = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN1 | + PM800_GPADC_GP_BIAS_EN2 | PM800_GPADC_GP_BIAS_EN3); + + if (pdata && (pdata->batt_det == 0)) + data = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN1 | + PM800_GPADC_GP_BIAS_EN2 | PM800_GPADC_GP_BIAS_EN3); + else + data = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN2 | + PM800_GPADC_GP_BIAS_EN3); + + ret = regmap_update_bits(map, PM800_GP_BIAS_ENA1, mask, data); + if (ret < 0) + goto out; + + dev_info(chip->dev, "pm800 device_gpadc_init: Done\n"); + return 0; + +out: + dev_info(chip->dev, "pm800 device_gpadc_init: Failed!\n"); + return ret; +} + +static int __devinit device_irq_init_800(struct pm80x_chip *chip) +{ + struct regmap *map = chip->regmap; + unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + int data, mask, ret = -EINVAL; + + if (!map || !chip->irq) { + dev_err(chip->dev, "incorrect parameters\n"); + return -EINVAL; + } + + /* + * irq_mode defines the way of clearing interrupt. it's read-clear by + * default. + */ + mask = + PM800_WAKEUP2_INV_INT | PM800_WAKEUP2_INT_CLEAR | + PM800_WAKEUP2_INT_MASK; + + data = PM800_WAKEUP2_INT_CLEAR; + ret = regmap_update_bits(map, PM800_WAKEUP2, mask, data); + + if (ret < 0) + goto out; + + ret = + regmap_add_irq_chip(chip->regmap, chip->irq, flags, -1, + chip->regmap_irq_chip, &chip->irq_data); + +out: + return ret; +} + +static void device_irq_exit_800(struct pm80x_chip *chip) +{ + regmap_del_irq_chip(chip->irq, chip->irq_data); +} + +static struct regmap_irq_chip pm800_irq_chip = { + .name = "88pm800", + .irqs = pm800_irqs, + .num_irqs = ARRAY_SIZE(pm800_irqs), + + .num_regs = 4, + .status_base = PM800_INT_STATUS1, + .mask_base = PM800_INT_ENA_1, + .ack_base = PM800_INT_STATUS1, +}; + +static int pm800_pages_init(struct pm80x_chip *chip) +{ + struct pm80x_subchip *subchip; + struct i2c_client *client = chip->client; + + subchip = chip->subchip; + /* PM800 block power: i2c addr 0x31 */ + if (subchip->power_page_addr) { + subchip->power_page = + i2c_new_dummy(client->adapter, subchip->power_page_addr); + subchip->regmap_power = + devm_regmap_init_i2c(subchip->power_page, + &pm80x_regmap_config); + i2c_set_clientdata(subchip->power_page, chip); + } else + dev_info(chip->dev, + "PM800 block power 0x31: No power_page_addr\n"); + + /* PM800 block GPADC: i2c addr 0x32 */ + if (subchip->gpadc_page_addr) { + subchip->gpadc_page = i2c_new_dummy(client->adapter, + subchip->gpadc_page_addr); + subchip->regmap_gpadc = + devm_regmap_init_i2c(subchip->gpadc_page, + &pm80x_regmap_config); + i2c_set_clientdata(subchip->gpadc_page, chip); + } else + dev_info(chip->dev, + "PM800 block GPADC 0x32: No gpadc_page_addr\n"); + + return 0; +} + +static void pm800_pages_exit(struct pm80x_chip *chip) +{ + struct pm80x_subchip *subchip; + + regmap_exit(chip->regmap); + i2c_unregister_device(chip->client); + + subchip = chip->subchip; + if (subchip->power_page) { + regmap_exit(subchip->regmap_power); + i2c_unregister_device(subchip->power_page); + } + if (subchip->gpadc_page) { + regmap_exit(subchip->regmap_gpadc); + i2c_unregister_device(subchip->gpadc_page); + } +} + +static int __devinit device_800_init(struct pm80x_chip *chip, + struct pm80x_platform_data *pdata) +{ + int ret, pmic_id; + + regmap_read(chip->regmap, PM800_CHIP_ID, &ret); + if (ret < 0) { + dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret); + goto out; + } + + pmic_id = ret & PM80X_VERSION_MASK; + + if ((pmic_id >= PM800_CHIP_A0) && (pmic_id <= PM800_CHIP_END)) { + chip->version = ret; + dev_info(chip->dev, + "88PM80x:Marvell 88PM800 (ID:0x%x) detected\n", ret); + } else { + dev_err(chip->dev, + "Failed to detect Marvell 88PM800:ChipID[0x%x]\n", ret); + goto out; + } + + /* + * alarm wake up bit will be clear in device_irq_init(), + * read before that + */ + regmap_read(chip->regmap, PM800_RTC_CONTROL, &ret); + if (ret < 0) { + dev_err(chip->dev, "Failed to read RTC register: %d\n", ret); + goto out; + } + if (ret & PM800_ALARM_WAKEUP) { + if (pdata && pdata->rtc) + pdata->rtc->rtc_wakeup = 1; + } + + ret = device_gpadc_init(chip, pdata); + if (ret < 0) { + dev_err(chip->dev, "[%s]Failed to init gpadc\n", __func__); + goto out; + } + + chip->regmap_irq_chip = &pm800_irq_chip; + + ret = device_irq_init_800(chip); + if (ret < 0) { + dev_err(chip->dev, "[%s]Failed to init pm800 irq\n", __func__); + goto out; + } + + ret = + mfd_add_devices(chip->dev, 0, &onkey_devs[0], + ARRAY_SIZE(onkey_devs), &onkey_resources[0], 0); + if (ret < 0) { + dev_err(chip->dev, "Failed to add onkey subdev\n"); + goto out_dev; + } else + dev_info(chip->dev, "[%s]:Added mfd onkey_devs\n", __func__); + + if (pdata && pdata->rtc) { + rtc_devs[0].platform_data = pdata->rtc; + rtc_devs[0].pdata_size = sizeof(struct pm80x_rtc_pdata); + ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0], + ARRAY_SIZE(rtc_devs), NULL, 0); + if (ret < 0) { + dev_err(chip->dev, "Failed to add rtc subdev\n"); + goto out_dev; + } else + dev_info(chip->dev, + "[%s]:Added mfd rtc_devs\n", __func__); + } + + return 0; +out_dev: + mfd_remove_devices(chip->dev); + device_irq_exit_800(chip); +out: + return ret; +} + +static int __devinit pm800_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct pm80x_chip *chip; + struct pm80x_platform_data *pdata = client->dev.platform_data; + struct pm80x_subchip *subchip; + + ret = pm80x_init(client, id); + if (ret) { + dev_err(&client->dev, "pm800_init fail\n"); + goto out_init; + } + + chip = i2c_get_clientdata(client); + + /* init subchip for PM800 */ + subchip = + devm_kzalloc(&client->dev, sizeof(struct pm80x_subchip), + GFP_KERNEL); + if (!subchip) { + ret = -ENOMEM; + goto err_subchip_alloc; + } + + subchip->power_page_addr = pdata->power_page_addr; + subchip->gpadc_page_addr = pdata->gpadc_page_addr; + chip->subchip = subchip; + + ret = device_800_init(chip, pdata); + if (ret) { + dev_err(chip->dev, "%s id 0x%x failed!\n", __func__, chip->id); + goto err_800_init; + } + + ret = pm800_pages_init(chip); + if (ret) { + dev_err(&client->dev, "pm800_pages_init failed!\n"); + goto err_page_init; + } + + if (pdata->plat_config) + pdata->plat_config(chip, pdata); + +err_page_init: + mfd_remove_devices(chip->dev); + device_irq_exit_800(chip); +err_800_init: + devm_kfree(&client->dev, subchip); +err_subchip_alloc: + pm80x_deinit(client); +out_init: + return ret; +} + +static int __devexit pm800_remove(struct i2c_client *client) +{ + struct pm80x_chip *chip = i2c_get_clientdata(client); + + mfd_remove_devices(chip->dev); + device_irq_exit_800(chip); + + pm800_pages_exit(chip); + devm_kfree(&client->dev, chip->subchip); + + pm80x_deinit(client); + + return 0; +} + +static struct i2c_driver pm800_driver = { + .driver = { + .name = "88PM80X", + .owner = THIS_MODULE, + .pm = &pm80x_pm_ops, + }, + .probe = pm800_probe, + .remove = __devexit_p(pm800_remove), + .id_table = pm80x_id_table, +}; + +static int __init pm800_i2c_init(void) +{ + return i2c_add_driver(&pm800_driver); +} +subsys_initcall(pm800_i2c_init); + +static void __exit pm800_i2c_exit(void) +{ + i2c_del_driver(&pm800_driver); +} +module_exit(pm800_i2c_exit); + +MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM800"); +MODULE_AUTHOR("Qiao Zhou "); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/88pm805.c b/drivers/mfd/88pm805.c new file mode 100644 index 000000000000..d93c3091cb9f --- /dev/null +++ b/drivers/mfd/88pm805.c @@ -0,0 +1,299 @@ +/* + * Base driver for Marvell 88PM805 + * + * Copyright (C) 2012 Marvell International Ltd. + * Haojian Zhuang + * Joseph(Yossi) Hanin + * Qiao Zhou + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 + +#define PM805_CHIP_ID (0x00) + +static const struct i2c_device_id pm80x_id_table[] = { + {"88PM805", CHIP_PM805}, +}; +MODULE_DEVICE_TABLE(i2c, pm80x_id_table); + +/* Interrupt Number in 88PM805 */ +enum { + PM805_IRQ_LDO_OFF, /*0 */ + PM805_IRQ_SRC_DPLL_LOCK, /*1 */ + PM805_IRQ_CLIP_FAULT, + PM805_IRQ_MIC_CONFLICT, + PM805_IRQ_HP2_SHRT, + PM805_IRQ_HP1_SHRT, /*5 */ + PM805_IRQ_FINE_PLL_FAULT, + PM805_IRQ_RAW_PLL_FAULT, + PM805_IRQ_VOLP_BTN_DET, + PM805_IRQ_VOLM_BTN_DET, + PM805_IRQ_SHRT_BTN_DET, /*10 */ + PM805_IRQ_MIC_DET, /*11 */ + + PM805_MAX_IRQ, +}; + +static struct resource codec_resources[] = { + { + /* Headset microphone insertion or removal */ + .name = "micin", + .start = PM805_IRQ_MIC_DET, + .end = PM805_IRQ_MIC_DET, + .flags = IORESOURCE_IRQ, + }, + { + /* Audio short HP1 */ + .name = "audio-short1", + .start = PM805_IRQ_HP1_SHRT, + .end = PM805_IRQ_HP1_SHRT, + .flags = IORESOURCE_IRQ, + }, + { + /* Audio short HP2 */ + .name = "audio-short2", + .start = PM805_IRQ_HP2_SHRT, + .end = PM805_IRQ_HP2_SHRT, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell codec_devs[] = { + { + .name = "88pm80x-codec", + .num_resources = ARRAY_SIZE(codec_resources), + .resources = &codec_resources[0], + .id = -1, + }, +}; + +static struct regmap_irq pm805_irqs[] = { + /* INT0 */ + [PM805_IRQ_LDO_OFF] = { + .mask = PM805_INT1_HP1_SHRT, + }, + [PM805_IRQ_SRC_DPLL_LOCK] = { + .mask = PM805_INT1_HP2_SHRT, + }, + [PM805_IRQ_CLIP_FAULT] = { + .mask = PM805_INT1_MIC_CONFLICT, + }, + [PM805_IRQ_MIC_CONFLICT] = { + .mask = PM805_INT1_CLIP_FAULT, + }, + [PM805_IRQ_HP2_SHRT] = { + .mask = PM805_INT1_LDO_OFF, + }, + [PM805_IRQ_HP1_SHRT] = { + .mask = PM805_INT1_SRC_DPLL_LOCK, + }, + /* INT1 */ + [PM805_IRQ_FINE_PLL_FAULT] = { + .reg_offset = 1, + .mask = PM805_INT2_MIC_DET, + }, + [PM805_IRQ_RAW_PLL_FAULT] = { + .reg_offset = 1, + .mask = PM805_INT2_SHRT_BTN_DET, + }, + [PM805_IRQ_VOLP_BTN_DET] = { + .reg_offset = 1, + .mask = PM805_INT2_VOLM_BTN_DET, + }, + [PM805_IRQ_VOLM_BTN_DET] = { + .reg_offset = 1, + .mask = PM805_INT2_VOLP_BTN_DET, + }, + [PM805_IRQ_SHRT_BTN_DET] = { + .reg_offset = 1, + .mask = PM805_INT2_RAW_PLL_FAULT, + }, + [PM805_IRQ_MIC_DET] = { + .reg_offset = 1, + .mask = PM805_INT2_FINE_PLL_FAULT, + }, +}; + +static int __devinit device_irq_init_805(struct pm80x_chip *chip) +{ + struct regmap *map = chip->regmap; + unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + int data, mask, ret = -EINVAL; + + if (!map || !chip->irq) { + dev_err(chip->dev, "incorrect parameters\n"); + return -EINVAL; + } + + /* + * irq_mode defines the way of clearing interrupt. it's read-clear by + * default. + */ + mask = + PM805_STATUS0_INT_CLEAR | PM805_STATUS0_INV_INT | + PM800_STATUS0_INT_MASK; + + data = PM805_STATUS0_INT_CLEAR; + ret = regmap_update_bits(map, PM805_INT_STATUS0, mask, data); + /* + * PM805_INT_STATUS is under 32K clock domain, so need to + * add proper delay before the next I2C register access. + */ + msleep(1); + + if (ret < 0) + goto out; + + ret = + regmap_add_irq_chip(chip->regmap, chip->irq, flags, -1, + chip->regmap_irq_chip, &chip->irq_data); + +out: + return ret; +} + +static void device_irq_exit_805(struct pm80x_chip *chip) +{ + regmap_del_irq_chip(chip->irq, chip->irq_data); +} + +static struct regmap_irq_chip pm805_irq_chip = { + .name = "88pm805", + .irqs = pm805_irqs, + .num_irqs = ARRAY_SIZE(pm805_irqs), + + .num_regs = 2, + .status_base = PM805_INT_STATUS1, + .mask_base = PM805_INT_MASK1, + .ack_base = PM805_INT_STATUS1, +}; + +static int __devinit device_805_init(struct pm80x_chip *chip) +{ + int ret = 0; + struct regmap *map = chip->regmap; + + if (!map) { + dev_err(chip->dev, "regmap is invalid\n"); + return -EINVAL; + } + + regmap_read(map, PM805_CHIP_ID, &ret); + if (ret < 0) { + dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret); + goto out_irq_init; + } + chip->version = ret; + + chip->regmap_irq_chip = &pm805_irq_chip; + + ret = device_irq_init_805(chip); + if (ret < 0) { + dev_err(chip->dev, "Failed to init pm805 irq!\n"); + goto out_irq_init; + } + + ret = mfd_add_devices(chip->dev, 0, &codec_devs[0], + ARRAY_SIZE(codec_devs), &codec_resources[0], 0); + if (ret < 0) { + dev_err(chip->dev, "Failed to add codec subdev\n"); + goto out_codec; + } else + dev_info(chip->dev, "[%s]:Added mfd codec_devs\n", __func__); + + return 0; + +out_codec: + device_irq_exit_805(chip); +out_irq_init: + return ret; +} + +static int __devinit pm805_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct pm80x_chip *chip; + struct pm80x_platform_data *pdata = client->dev.platform_data; + + ret = pm80x_init(client, id); + if (ret) { + dev_err(&client->dev, "pm805_init fail!\n"); + goto out_init; + } + + chip = i2c_get_clientdata(client); + + ret = device_805_init(chip); + if (ret) { + dev_err(chip->dev, "%s id 0x%x failed!\n", __func__, chip->id); + goto err_805_init; + } + + if (pdata->plat_config) + pdata->plat_config(chip, pdata); + +err_805_init: + pm80x_deinit(client); +out_init: + return ret; +} + +static int __devexit pm805_remove(struct i2c_client *client) +{ + struct pm80x_chip *chip = i2c_get_clientdata(client); + + mfd_remove_devices(chip->dev); + device_irq_exit_805(chip); + + pm80x_deinit(client); + + return 0; +} + +static struct i2c_driver pm805_driver = { + .driver = { + .name = "88PM80X", + .owner = THIS_MODULE, + .pm = &pm80x_pm_ops, + }, + .probe = pm805_probe, + .remove = __devexit_p(pm805_remove), + .id_table = pm80x_id_table, +}; + +static int __init pm805_i2c_init(void) +{ + return i2c_add_driver(&pm805_driver); +} +subsys_initcall(pm805_i2c_init); + +static void __exit pm805_i2c_exit(void) +{ + i2c_del_driver(&pm805_driver); +} +module_exit(pm805_i2c_exit); + +MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM805"); +MODULE_AUTHOR("Qiao Zhou "); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/88pm80x.c b/drivers/mfd/88pm80x.c new file mode 100644 index 000000000000..90aa18a37f79 --- /dev/null +++ b/drivers/mfd/88pm80x.c @@ -0,0 +1,116 @@ +/* + * I2C driver for Marvell 88PM80x + * + * Copyright (C) 2012 Marvell International Ltd. + * Haojian Zhuang + * Joseph(Yossi) Hanin + * Qiao Zhou + * + * 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 + + +const struct regmap_config pm80x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +int __devinit pm80x_init(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pm80x_chip *chip; + struct regmap *map; + int ret = 0; + + chip = + devm_kzalloc(&client->dev, sizeof(struct pm80x_chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + map = devm_regmap_init_i2c(client, &pm80x_regmap_config); + if (IS_ERR(map)) { + ret = PTR_ERR(map); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + ret); + goto err_regmap_init; + } + + chip->id = id->driver_data; + if (chip->id < CHIP_PM800 || chip->id > CHIP_PM805) { + ret = -EINVAL; + goto err_chip_id; + } + + chip->client = client; + chip->regmap = map; + + chip->irq = client->irq; + + chip->dev = &client->dev; + dev_set_drvdata(chip->dev, chip); + i2c_set_clientdata(chip->client, chip); + + device_init_wakeup(&client->dev, 1); + + return 0; + +err_chip_id: + regmap_exit(map); +err_regmap_init: + devm_kfree(&client->dev, chip); + return ret; +} +EXPORT_SYMBOL_GPL(pm80x_init); + +int __devexit pm80x_deinit(struct i2c_client *client) +{ + struct pm80x_chip *chip = i2c_get_clientdata(client); + + regmap_exit(chip->regmap); + devm_kfree(&client->dev, chip); + + return 0; +} +EXPORT_SYMBOL_GPL(pm80x_deinit); + +#ifdef CONFIG_PM_SLEEP +static int pm80x_suspend(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct pm80x_chip *chip = i2c_get_clientdata(client); + + if (chip && chip->wu_flag) + if (device_may_wakeup(chip->dev)) + enable_irq_wake(chip->irq); + + return 0; +} + +static int pm80x_resume(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct pm80x_chip *chip = i2c_get_clientdata(client); + + if (chip && chip->wu_flag) + if (device_may_wakeup(chip->dev)) + disable_irq_wake(chip->irq); + + return 0; +} +#endif + +SIMPLE_DEV_PM_OPS(pm80x_pm_ops, pm80x_suspend, pm80x_resume); +EXPORT_SYMBOL_GPL(pm80x_pm_ops); + +MODULE_DESCRIPTION("I2C Driver for Marvell 88PM80x"); +MODULE_AUTHOR("Qiao Zhou "); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 5c043693f52c..9c3ab2ab7dc7 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -21,6 +21,30 @@ config MFD_88PM860X select individual components like voltage regulators, RTC and battery-charger under the corresponding menus. +config MFD_88PM800 + tristate "Support Marvell 88PM800" + depends on I2C=y && GENERIC_HARDIRQS + select REGMAP_I2C + select REGMAP_IRQ + select MFD_CORE + help + This supports for Marvell 88PM800 Power Management IC. + This includes the I2C driver and the core APIs _only_, you have to + select individual components like voltage regulators, RTC and + battery-charger under the corresponding menus. + +config MFD_88PM805 + tristate "Support Marvell 88PM805" + depends on I2C=y && GENERIC_HARDIRQS + select REGMAP_I2C + select REGMAP_IRQ + select MFD_CORE + help + This supports for Marvell 88PM805 Power Management IC. This includes + the I2C driver and the core APIs _only_, you have to select individual + components like codec device, headset/Mic device under the + corresponding menus. + config MFD_SM501 tristate "Support for Silicon Motion SM501" ---help--- diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index f28885bb103c..09674a99eb60 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -4,6 +4,8 @@ 88pm860x-objs := 88pm860x-core.o 88pm860x-i2c.o obj-$(CONFIG_MFD_88PM860X) += 88pm860x.o +obj-$(CONFIG_MFD_88PM800) += 88pm800.o 88pm80x.o +obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o obj-$(CONFIG_MFD_SM501) += sm501.o obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o diff --git a/include/linux/mfd/88pm80x.h b/include/linux/mfd/88pm80x.h new file mode 100644 index 000000000000..6c126e9714a3 --- /dev/null +++ b/include/linux/mfd/88pm80x.h @@ -0,0 +1,368 @@ +/* + * Marvell 88PM80x Interface + * + * Copyright (C) 2012 Marvell International Ltd. + * Qiao Zhou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_MFD_88PM80X_H +#define __LINUX_MFD_88PM80X_H + +#include +#include +#include +#include + +#define PM80X_VERSION_MASK (0xFF) /* 80X chip ID mask */ +enum { + CHIP_INVALID = 0, + CHIP_PM800, + CHIP_PM805, + CHIP_MAX, +}; + +enum { + PM800_ID_BUCK1 = 0, + PM800_ID_BUCK2, + PM800_ID_BUCK3, + PM800_ID_BUCK4, + PM800_ID_BUCK5, + + PM800_ID_LDO1, + PM800_ID_LDO2, + PM800_ID_LDO3, + PM800_ID_LDO4, + PM800_ID_LDO5, + PM800_ID_LDO6, + PM800_ID_LDO7, + PM800_ID_LDO8, + PM800_ID_LDO9, + PM800_ID_LDO10, + PM800_ID_LDO11, + PM800_ID_LDO12, + PM800_ID_LDO13, + PM800_ID_LDO14, + PM800_ID_LDO15, + PM800_ID_LDO16, + PM800_ID_LDO17, + PM800_ID_LDO18, + PM800_ID_LDO19, + + PM800_ID_RG_MAX, +}; +#define PM800_MAX_REGULATOR PM800_ID_RG_MAX /* 5 Bucks, 19 LDOs */ +#define PM800_NUM_BUCK (5) /*5 Bucks */ +#define PM800_NUM_LDO (19) /*19 Bucks */ + +/* page 0 basic: slave adder 0x60 */ + +#define PM800_STATUS_1 (0x01) +#define PM800_ONKEY_STS1 (1 << 0) +#define PM800_EXTON_STS1 (1 << 1) +#define PM800_CHG_STS1 (1 << 2) +#define PM800_BAT_STS1 (1 << 3) +#define PM800_VBUS_STS1 (1 << 4) +#define PM800_LDO_PGOOD_STS1 (1 << 5) +#define PM800_BUCK_PGOOD_STS1 (1 << 6) + +#define PM800_STATUS_2 (0x02) +#define PM800_RTC_ALARM_STS2 (1 << 0) + +/* Wakeup Registers */ +#define PM800_WAKEUP1 (0x0D) + +#define PM800_WAKEUP2 (0x0E) +#define PM800_WAKEUP2_INV_INT (1 << 0) +#define PM800_WAKEUP2_INT_CLEAR (1 << 1) +#define PM800_WAKEUP2_INT_MASK (1 << 2) + +#define PM800_POWER_UP_LOG (0x10) + +/* Referance and low power registers */ +#define PM800_LOW_POWER1 (0x20) +#define PM800_LOW_POWER2 (0x21) +#define PM800_LOW_POWER_CONFIG3 (0x22) +#define PM800_LOW_POWER_CONFIG4 (0x23) + +/* GPIO register */ +#define PM800_GPIO_0_1_CNTRL (0x30) +#define PM800_GPIO0_VAL (1 << 0) +#define PM800_GPIO0_GPIO_MODE(x) (x << 1) +#define PM800_GPIO1_VAL (1 << 4) +#define PM800_GPIO1_GPIO_MODE(x) (x << 5) + +#define PM800_GPIO_2_3_CNTRL (0x31) +#define PM800_GPIO2_VAL (1 << 0) +#define PM800_GPIO2_GPIO_MODE(x) (x << 1) +#define PM800_GPIO3_VAL (1 << 4) +#define PM800_GPIO3_GPIO_MODE(x) (x << 5) +#define PM800_GPIO3_MODE_MASK 0x1F +#define PM800_GPIO3_HEADSET_MODE PM800_GPIO3_GPIO_MODE(6) + +#define PM800_GPIO_4_CNTRL (0x32) +#define PM800_GPIO4_VAL (1 << 0) +#define PM800_GPIO4_GPIO_MODE(x) (x << 1) + +#define PM800_HEADSET_CNTRL (0x38) +#define PM800_HEADSET_DET_EN (1 << 7) +#define PM800_HSDET_SLP (1 << 1) +/* PWM register */ +#define PM800_PWM1 (0x40) +#define PM800_PWM2 (0x41) +#define PM800_PWM3 (0x42) +#define PM800_PWM4 (0x43) + +/* RTC Registers */ +#define PM800_RTC_CONTROL (0xD0) +#define PM800_RTC_MISC1 (0xE1) +#define PM800_RTC_MISC2 (0xE2) +#define PM800_RTC_MISC3 (0xE3) +#define PM800_RTC_MISC4 (0xE4) +#define PM800_RTC_MISC5 (0xE7) +/* bit definitions of RTC Register 1 (0xD0) */ +#define PM800_ALARM1_EN (1 << 0) +#define PM800_ALARM_WAKEUP (1 << 4) +#define PM800_ALARM (1 << 5) +#define PM800_RTC1_USE_XO (1 << 7) + +/* Regulator Control Registers: BUCK1,BUCK5,LDO1 have DVC */ + +/* buck registers */ +#define PM800_SLEEP_BUCK1 (0x30) + +/* BUCK Sleep Mode Register 1: BUCK[1..4] */ +#define PM800_BUCK_SLP1 (0x5A) +#define PM800_BUCK1_SLP1_SHIFT 0 +#define PM800_BUCK1_SLP1_MASK (0x3 << PM800_BUCK1_SLP1_SHIFT) + +/* page 2 GPADC: slave adder 0x02 */ +#define PM800_GPADC_MEAS_EN1 (0x01) +#define PM800_MEAS_EN1_VBAT (1 << 2) +#define PM800_GPADC_MEAS_EN2 (0x02) +#define PM800_MEAS_EN2_RFTMP (1 << 0) +#define PM800_MEAS_GP0_EN (1 << 2) +#define PM800_MEAS_GP1_EN (1 << 3) +#define PM800_MEAS_GP2_EN (1 << 4) +#define PM800_MEAS_GP3_EN (1 << 5) +#define PM800_MEAS_GP4_EN (1 << 6) + +#define PM800_GPADC_MISC_CONFIG1 (0x05) +#define PM800_GPADC_MISC_CONFIG2 (0x06) +#define PM800_GPADC_MISC_GPFSM_EN (1 << 0) +#define PM800_GPADC_SLOW_MODE(x) (x << 3) + +#define PM800_GPADC_MISC_CONFIG3 (0x09) +#define PM800_GPADC_MISC_CONFIG4 (0x0A) + +#define PM800_GPADC_PREBIAS1 (0x0F) +#define PM800_GPADC0_GP_PREBIAS_TIME(x) (x << 0) +#define PM800_GPADC_PREBIAS2 (0x10) + +#define PM800_GP_BIAS_ENA1 (0x14) +#define PM800_GPADC_GP_BIAS_EN0 (1 << 0) +#define PM800_GPADC_GP_BIAS_EN1 (1 << 1) +#define PM800_GPADC_GP_BIAS_EN2 (1 << 2) +#define PM800_GPADC_GP_BIAS_EN3 (1 << 3) + +#define PM800_GP_BIAS_OUT1 (0x15) +#define PM800_BIAS_OUT_GP0 (1 << 0) +#define PM800_BIAS_OUT_GP1 (1 << 1) +#define PM800_BIAS_OUT_GP2 (1 << 2) +#define PM800_BIAS_OUT_GP3 (1 << 3) + +#define PM800_GPADC0_LOW_TH 0x20 +#define PM800_GPADC1_LOW_TH 0x21 +#define PM800_GPADC2_LOW_TH 0x22 +#define PM800_GPADC3_LOW_TH 0x23 +#define PM800_GPADC4_LOW_TH 0x24 + +#define PM800_GPADC0_UPP_TH 0x30 +#define PM800_GPADC1_UPP_TH 0x31 +#define PM800_GPADC2_UPP_TH 0x32 +#define PM800_GPADC3_UPP_TH 0x33 +#define PM800_GPADC4_UPP_TH 0x34 + +#define PM800_VBBAT_MEAS1 0x40 +#define PM800_VBBAT_MEAS2 0x41 +#define PM800_VBAT_MEAS1 0x42 +#define PM800_VBAT_MEAS2 0x43 +#define PM800_VSYS_MEAS1 0x44 +#define PM800_VSYS_MEAS2 0x45 +#define PM800_VCHG_MEAS1 0x46 +#define PM800_VCHG_MEAS2 0x47 +#define PM800_TINT_MEAS1 0x50 +#define PM800_TINT_MEAS2 0x51 +#define PM800_PMOD_MEAS1 0x52 +#define PM800_PMOD_MEAS2 0x53 + +#define PM800_GPADC0_MEAS1 0x54 +#define PM800_GPADC0_MEAS2 0x55 +#define PM800_GPADC1_MEAS1 0x56 +#define PM800_GPADC1_MEAS2 0x57 +#define PM800_GPADC2_MEAS1 0x58 +#define PM800_GPADC2_MEAS2 0x59 +#define PM800_GPADC3_MEAS1 0x5A +#define PM800_GPADC3_MEAS2 0x5B +#define PM800_GPADC4_MEAS1 0x5C +#define PM800_GPADC4_MEAS2 0x5D + +#define PM800_GPADC4_AVG1 0xA8 +#define PM800_GPADC4_AVG2 0xA9 + +/* 88PM805 Registers */ +#define PM805_MAIN_POWERUP (0x01) +#define PM805_INT_STATUS0 (0x02) /* for ena/dis all interrupts */ + +#define PM805_STATUS0_INT_CLEAR (1 << 0) +#define PM805_STATUS0_INV_INT (1 << 1) +#define PM800_STATUS0_INT_MASK (1 << 2) + +#define PM805_INT_STATUS1 (0x03) + +#define PM805_INT1_HP1_SHRT (1 << 0) +#define PM805_INT1_HP2_SHRT (1 << 1) +#define PM805_INT1_MIC_CONFLICT (1 << 2) +#define PM805_INT1_CLIP_FAULT (1 << 3) +#define PM805_INT1_LDO_OFF (1 << 4) +#define PM805_INT1_SRC_DPLL_LOCK (1 << 5) + +#define PM805_INT_STATUS2 (0x04) + +#define PM805_INT2_MIC_DET (1 << 0) +#define PM805_INT2_SHRT_BTN_DET (1 << 1) +#define PM805_INT2_VOLM_BTN_DET (1 << 2) +#define PM805_INT2_VOLP_BTN_DET (1 << 3) +#define PM805_INT2_RAW_PLL_FAULT (1 << 4) +#define PM805_INT2_FINE_PLL_FAULT (1 << 5) + +#define PM805_INT_MASK1 (0x05) +#define PM805_INT_MASK2 (0x06) +#define PM805_SHRT_BTN_DET (1 << 1) + +/* number of status and int reg in a row */ +#define PM805_INT_REG_NUM (2) + +#define PM805_MIC_DET1 (0x07) +#define PM805_MIC_DET_EN_MIC_DET (1 << 0) +#define PM805_MIC_DET2 (0x08) +#define PM805_MIC_DET_STATUS1 (0x09) + +#define PM805_MIC_DET_STATUS3 (0x0A) +#define PM805_AUTO_SEQ_STATUS1 (0x0B) +#define PM805_AUTO_SEQ_STATUS2 (0x0C) + +#define PM805_ADC_SETTING1 (0x10) +#define PM805_ADC_SETTING2 (0x11) +#define PM805_ADC_SETTING3 (0x11) +#define PM805_ADC_GAIN1 (0x12) +#define PM805_ADC_GAIN2 (0x13) +#define PM805_DMIC_SETTING (0x15) +#define PM805_DWS_SETTING (0x16) +#define PM805_MIC_CONFLICT_STS (0x17) + +#define PM805_PDM_SETTING1 (0x20) +#define PM805_PDM_SETTING2 (0x21) +#define PM805_PDM_SETTING3 (0x22) +#define PM805_PDM_CONTROL1 (0x23) +#define PM805_PDM_CONTROL2 (0x24) +#define PM805_PDM_CONTROL3 (0x25) + +#define PM805_HEADPHONE_SETTING (0x26) +#define PM805_HEADPHONE_GAIN_A2A (0x27) +#define PM805_HEADPHONE_SHORT_STATE (0x28) +#define PM805_EARPHONE_SETTING (0x29) +#define PM805_AUTO_SEQ_SETTING (0x2A) + +struct pm80x_rtc_pdata { + int vrtc; + int rtc_wakeup; +}; + +struct pm80x_subchip { + struct i2c_client *power_page; /* chip client for power page */ + struct i2c_client *gpadc_page; /* chip client for gpadc page */ + struct regmap *regmap_power; + struct regmap *regmap_gpadc; + unsigned short power_page_addr; /* power page I2C address */ + unsigned short gpadc_page_addr; /* gpadc page I2C address */ +}; + +struct pm80x_chip { + struct pm80x_subchip *subchip; + struct device *dev; + struct i2c_client *client; + struct regmap *regmap; + struct regmap_irq_chip *regmap_irq_chip; + struct regmap_irq_chip_data *irq_data; + unsigned char version; + int id; + int irq; + int irq_mode; + unsigned long wu_flag; + spinlock_t lock; +}; + +struct pm80x_platform_data { + struct pm80x_rtc_pdata *rtc; + unsigned short power_page_addr; /* power page I2C address */ + unsigned short gpadc_page_addr; /* gpadc page I2C address */ + int irq_mode; /* Clear interrupt by read/write(0/1) */ + int batt_det; /* enable/disable */ + int (*plat_config)(struct pm80x_chip *chip, + struct pm80x_platform_data *pdata); +}; + +extern const struct dev_pm_ops pm80x_pm_ops; +extern const struct regmap_config pm80x_regmap_config; + +static inline int pm80x_request_irq(struct pm80x_chip *pm80x, int irq, + irq_handler_t handler, unsigned long flags, + const char *name, void *data) +{ + if (!pm80x->irq_data) + return -EINVAL; + return request_threaded_irq(regmap_irq_get_virq(pm80x->irq_data, irq), + NULL, handler, flags, name, data); +} + +static inline void pm80x_free_irq(struct pm80x_chip *pm80x, int irq, void *data) +{ + if (!pm80x->irq_data) + return; + free_irq(regmap_irq_get_virq(pm80x->irq_data, irq), data); +} + +#ifdef CONFIG_PM +static inline int pm80x_dev_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent); + int irq = platform_get_irq(pdev, 0); + + if (device_may_wakeup(dev)) + set_bit((1 << irq), &chip->wu_flag); + + return 0; +} + +static inline int pm80x_dev_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent); + int irq = platform_get_irq(pdev, 0); + + if (device_may_wakeup(dev)) + clear_bit((1 << irq), &chip->wu_flag); + + return 0; +} +#endif + +extern int pm80x_init(struct i2c_client *client, + const struct i2c_device_id *id) __devinit; +extern int pm80x_deinit(struct i2c_client *client) __devexit; +#endif /* __LINUX_MFD_88PM80X_H */ -- cgit v1.2.3 From 5500e3964b8c154dc5af51ebcd7cd4df5d4abfee Mon Sep 17 00:00:00 2001 From: Qiao Zhou Date: Mon, 9 Jul 2012 14:37:33 +0800 Subject: mfd: Add companion chip in 88pm80x in hw design, 800 is mainly for pmic control, while 805 for audio. but there are 3 registers which controls class D speaker property, and they are defined in 800 i2c client domain. so 805 codec driver needs to use 800 i2c client to access class D speaker reg for audio path management. so add this workaround for the purpose to let 805 access 800 i2c in some scenario. Signed-off-by: Qiao Zhou Reviewed-by: Arnd Bergmann Signed-off-by: Samuel Ortiz --- drivers/mfd/88pm80x.c | 28 ++++++++++++++++++++++++++++ include/linux/mfd/88pm80x.h | 1 + 2 files changed, 29 insertions(+) (limited to 'include') diff --git a/drivers/mfd/88pm80x.c b/drivers/mfd/88pm80x.c index 90aa18a37f79..77b865594c29 100644 --- a/drivers/mfd/88pm80x.c +++ b/drivers/mfd/88pm80x.c @@ -18,6 +18,12 @@ #include #include +/* + * workaround: some registers needed by pm805 are defined in pm800, so + * need to use this global variable to maintain the relation between + * pm800 and pm805. would remove it after HW chip fixes the issue. + */ +static struct pm80x_chip *g_pm80x_chip; const struct regmap_config pm80x_regmap_config = { .reg_bits = 8, @@ -61,6 +67,19 @@ int __devinit pm80x_init(struct i2c_client *client, device_init_wakeup(&client->dev, 1); + /* + * workaround: set g_pm80x_chip to the first probed chip. if the + * second chip is probed, just point to the companion to each + * other so that pm805 can access those specific register. would + * remove it after HW chip fixes the issue. + */ + if (!g_pm80x_chip) + g_pm80x_chip = chip; + else { + chip->companion = g_pm80x_chip->client; + g_pm80x_chip->companion = chip->client; + } + return 0; err_chip_id: @@ -75,6 +94,15 @@ int __devexit pm80x_deinit(struct i2c_client *client) { struct pm80x_chip *chip = i2c_get_clientdata(client); + /* + * workaround: clear the dependency between pm800 and pm805. + * would remove it after HW chip fixes the issue. + */ + if (g_pm80x_chip->companion) + g_pm80x_chip->companion = NULL; + else + g_pm80x_chip = NULL; + regmap_exit(chip->regmap); devm_kfree(&client->dev, chip); diff --git a/include/linux/mfd/88pm80x.h b/include/linux/mfd/88pm80x.h index 6c126e9714a3..103f06d1892d 100644 --- a/include/linux/mfd/88pm80x.h +++ b/include/linux/mfd/88pm80x.h @@ -295,6 +295,7 @@ struct pm80x_chip { struct pm80x_subchip *subchip; struct device *dev; struct i2c_client *client; + struct i2c_client *companion; struct regmap *regmap; struct regmap_irq_chip *regmap_irq_chip; struct regmap_irq_chip_data *irq_data; -- cgit v1.2.3 From a232d56e48024c41c87ba884cc0a0fc98e37f5c6 Mon Sep 17 00:00:00 2001 From: Yadwinder Singh Brar Date: Thu, 5 Jul 2012 09:28:24 +0530 Subject: mfd: Remove the clocks from the list of max77686 regulators Remove the clocks from the list of regulators to correct the value of MAX77686_REG_MAX which is used in the regulator driver to represent the no. of regulators present in max77686. Signed-off-by: Yadwinder Singh Brar Signed-off-by: Samuel Ortiz --- include/linux/mfd/max77686.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include') diff --git a/include/linux/mfd/max77686.h b/include/linux/mfd/max77686.h index fcf312678cac..3d7ae4d7fd36 100644 --- a/include/linux/mfd/max77686.h +++ b/include/linux/mfd/max77686.h @@ -67,9 +67,6 @@ enum max77686_regulators { MAX77686_BUCK7, MAX77686_BUCK8, MAX77686_BUCK9, - MAX77686_EN32KHZ_AP, - MAX77686_EN32KHZ_CP, - MAX77686_P32KH, MAX77686_REG_MAX, }; -- cgit v1.2.3 From 929578ab0339fe42bb3ceeaa2e6607189cddf70b Mon Sep 17 00:00:00 2001 From: Keng-Yu Lin Date: Fri, 6 Jul 2012 18:06:11 +0800 Subject: HID: Add suport for the brightness control keys on HP keyboards The keys are found on the keyboards bundled with HP All-In-One machines with USB VID/PID of 04ca:004d and 04f2:1061. Signed-off-by: Keng-Yu Lin Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 9 +++++++++ include/linux/hid.h | 1 + 2 files changed, 10 insertions(+) (limited to 'include') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 132b0019365e..879443bf410f 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -834,6 +834,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel } break; + case HID_UP_HPVENDOR2: + set_bit(EV_REP, input->evbit); + switch (usage->hid & HID_USAGE) { + case 0x003: map_key_clear(KEY_BRIGHTNESSDOWN); break; + case 0x004: map_key_clear(KEY_BRIGHTNESSUP); break; + default: goto ignore; + } + break; + case HID_UP_MSVENDOR: goto ignore; diff --git a/include/linux/hid.h b/include/linux/hid.h index 449fa385703d..42970de1b40c 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -200,6 +200,7 @@ struct hid_item { #define HID_UP_DIGITIZER 0x000d0000 #define HID_UP_PID 0x000f0000 #define HID_UP_HPVENDOR 0xff7f0000 +#define HID_UP_HPVENDOR2 0xff010000 #define HID_UP_MSVENDOR 0xff000000 #define HID_UP_CUSTOM 0x00ff0000 #define HID_UP_LOGIVENDOR 0xffbc0000 -- cgit v1.2.3 From 26c696c678c4ce180599330999e895cded0f625b Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Sat, 7 Jul 2012 22:56:40 +0800 Subject: USB: Chipidea: rename struct ci13xxx variables from udc to ci struct ci13xxx represent the controller, which may be device or host, so name its variables as ci. Signed-off-by: Richard Zhao Reviewed-by: Felipe Balbi Signed-off-by: Alexander Shishkin Reviewed-by: Marek Vasut Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci.h | 26 +- drivers/usb/chipidea/ci13xxx_msm.c | 12 +- drivers/usb/chipidea/debug.c | 146 ++++----- drivers/usb/chipidea/udc.c | 628 ++++++++++++++++++------------------- include/linux/usb/chipidea.h | 2 +- 5 files changed, 407 insertions(+), 407 deletions(-) (limited to 'include') diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 0b093308548d..9655e3569d4c 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -36,7 +36,7 @@ * @name: string description of the endpoint * @qh: queue head for this endpoint * @wedge: is the endpoint wedged - * @udc: pointer to the controller + * @ci: pointer to the controller * @lock: pointer to controller's spinlock * @td_pool: pointer to controller's TD pool */ @@ -54,7 +54,7 @@ struct ci13xxx_ep { int wedge; /* global resources */ - struct ci13xxx *udc; + struct ci13xxx *ci; spinlock_t *lock; struct dma_pool *td_pool; }; @@ -250,9 +250,9 @@ static inline int ffs_nr(u32 x) * * This function returns register contents */ -static inline u32 hw_read(struct ci13xxx *udc, enum ci13xxx_regs reg, u32 mask) +static inline u32 hw_read(struct ci13xxx *ci, enum ci13xxx_regs reg, u32 mask) { - return ioread32(udc->hw_bank.regmap[reg]) & mask; + return ioread32(ci->hw_bank.regmap[reg]) & mask; } /** @@ -261,14 +261,14 @@ static inline u32 hw_read(struct ci13xxx *udc, enum ci13xxx_regs reg, u32 mask) * @mask: bitfield mask * @data: new value */ -static inline void hw_write(struct ci13xxx *udc, enum ci13xxx_regs reg, +static inline void hw_write(struct ci13xxx *ci, enum ci13xxx_regs reg, u32 mask, u32 data) { if (~mask) - data = (ioread32(udc->hw_bank.regmap[reg]) & ~mask) + data = (ioread32(ci->hw_bank.regmap[reg]) & ~mask) | (data & mask); - iowrite32(data, udc->hw_bank.regmap[reg]); + iowrite32(data, ci->hw_bank.regmap[reg]); } /** @@ -278,12 +278,12 @@ static inline void hw_write(struct ci13xxx *udc, enum ci13xxx_regs reg, * * This function returns register contents */ -static inline u32 hw_test_and_clear(struct ci13xxx *udc, enum ci13xxx_regs reg, +static inline u32 hw_test_and_clear(struct ci13xxx *ci, enum ci13xxx_regs reg, u32 mask) { - u32 val = ioread32(udc->hw_bank.regmap[reg]) & mask; + u32 val = ioread32(ci->hw_bank.regmap[reg]) & mask; - iowrite32(val, udc->hw_bank.regmap[reg]); + iowrite32(val, ci->hw_bank.regmap[reg]); return val; } @@ -295,12 +295,12 @@ static inline u32 hw_test_and_clear(struct ci13xxx *udc, enum ci13xxx_regs reg, * * This function returns register contents */ -static inline u32 hw_test_and_write(struct ci13xxx *udc, enum ci13xxx_regs reg, +static inline u32 hw_test_and_write(struct ci13xxx *ci, enum ci13xxx_regs reg, u32 mask, u32 data) { - u32 val = hw_read(udc, reg, ~0); + u32 val = hw_read(ci, reg, ~0); - hw_write(udc, reg, mask, data); + hw_write(ci, reg, mask, data); return (val & mask) >> ffs_nr(mask); } diff --git a/drivers/usb/chipidea/ci13xxx_msm.c b/drivers/usb/chipidea/ci13xxx_msm.c index 12c0dd6a300c..5a2fe5f9b6c3 100644 --- a/drivers/usb/chipidea/ci13xxx_msm.c +++ b/drivers/usb/chipidea/ci13xxx_msm.c @@ -15,11 +15,11 @@ #include "ci.h" -#define MSM_USB_BASE (udc->hw_bank.abs) +#define MSM_USB_BASE (ci->hw_bank.abs) -static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event) +static void ci13xxx_msm_notify_event(struct ci13xxx *ci, unsigned event) { - struct device *dev = udc->gadget.dev.parent; + struct device *dev = ci->gadget.dev.parent; int val; switch (event) { @@ -34,13 +34,13 @@ static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event) * Put the transceiver in non-driving mode. Otherwise host * may not detect soft-disconnection. */ - val = usb_phy_io_read(udc->transceiver, ULPI_FUNC_CTRL); + val = usb_phy_io_read(ci->transceiver, ULPI_FUNC_CTRL); val &= ~ULPI_FUNC_CTRL_OPMODE_MASK; val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; - usb_phy_io_write(udc->transceiver, val, ULPI_FUNC_CTRL); + usb_phy_io_write(ci->transceiver, val, ULPI_FUNC_CTRL); break; default: - dev_dbg(dev, "unknown ci13xxx_udc event\n"); + dev_dbg(dev, "unknown ci13xxx event\n"); break; } } diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c index c4b3e15532db..c6f50a257565 100644 --- a/drivers/usb/chipidea/debug.c +++ b/drivers/usb/chipidea/debug.c @@ -68,15 +68,15 @@ void dbg_interrupt(u32 intmask) * * This function returns number of registers read */ -static size_t hw_register_read(struct ci13xxx *udc, u32 *buf, size_t size) +static size_t hw_register_read(struct ci13xxx *ci, u32 *buf, size_t size) { unsigned i; - if (size > udc->hw_bank.size) - size = udc->hw_bank.size; + if (size > ci->hw_bank.size) + size = ci->hw_bank.size; for (i = 0; i < size; i++) - buf[i] = hw_read(udc, i * sizeof(u32), ~0); + buf[i] = hw_read(ci, i * sizeof(u32), ~0); return size; } @@ -88,18 +88,18 @@ static size_t hw_register_read(struct ci13xxx *udc, u32 *buf, size_t size) * * This function returns an error code */ -static int hw_register_write(struct ci13xxx *udc, u16 addr, u32 data) +static int hw_register_write(struct ci13xxx *ci, u16 addr, u32 data) { /* align */ addr /= sizeof(u32); - if (addr >= udc->hw_bank.size) + if (addr >= ci->hw_bank.size) return -EINVAL; /* align */ addr *= sizeof(u32); - hw_write(udc, addr, ~0, data); + hw_write(ci, addr, ~0, data); return 0; } @@ -110,13 +110,13 @@ static int hw_register_write(struct ci13xxx *udc, u16 addr, u32 data) * * This function returns an error code */ -static int hw_intr_clear(struct ci13xxx *udc, int n) +static int hw_intr_clear(struct ci13xxx *ci, int n) { if (n >= REG_BITS) return -EINVAL; - hw_write(udc, OP_USBINTR, BIT(n), 0); - hw_write(udc, OP_USBSTS, BIT(n), BIT(n)); + hw_write(ci, OP_USBINTR, BIT(n), 0); + hw_write(ci, OP_USBSTS, BIT(n), BIT(n)); return 0; } @@ -127,15 +127,15 @@ static int hw_intr_clear(struct ci13xxx *udc, int n) * * This function returns an error code */ -static int hw_intr_force(struct ci13xxx *udc, int n) +static int hw_intr_force(struct ci13xxx *ci, int n) { if (n >= REG_BITS) return -EINVAL; - hw_write(udc, CAP_TESTMODE, TESTMODE_FORCE, TESTMODE_FORCE); - hw_write(udc, OP_USBINTR, BIT(n), BIT(n)); - hw_write(udc, OP_USBSTS, BIT(n), BIT(n)); - hw_write(udc, CAP_TESTMODE, TESTMODE_FORCE, 0); + hw_write(ci, CAP_TESTMODE, TESTMODE_FORCE, TESTMODE_FORCE); + hw_write(ci, OP_USBINTR, BIT(n), BIT(n)); + hw_write(ci, OP_USBSTS, BIT(n), BIT(n)); + hw_write(ci, CAP_TESTMODE, TESTMODE_FORCE, 0); return 0; } @@ -147,12 +147,12 @@ static int hw_intr_force(struct ci13xxx *udc, int n) static ssize_t show_device(struct device *dev, struct device_attribute *attr, char *buf) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - struct usb_gadget *gadget = &udc->gadget; + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); + struct usb_gadget *gadget = &ci->gadget; int n = 0; if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); + dev_err(ci->dev, "[%s] EINVAL\n", __func__); return 0; } @@ -188,8 +188,8 @@ static DEVICE_ATTR(device, S_IRUSR, show_device, NULL); static ssize_t show_driver(struct device *dev, struct device_attribute *attr, char *buf) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - struct usb_gadget_driver *driver = udc->driver; + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); + struct usb_gadget_driver *driver = ci->driver; int n = 0; if (attr == NULL || buf == NULL) { @@ -412,22 +412,22 @@ static DEVICE_ATTR(events, S_IRUSR | S_IWUSR, show_events, store_events); static ssize_t show_inters(struct device *dev, struct device_attribute *attr, char *buf) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); unsigned long flags; u32 intr; unsigned i, j, n = 0; if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); + dev_err(ci->dev, "[%s] EINVAL\n", __func__); return 0; } - spin_lock_irqsave(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); /*n += scnprintf(buf + n, PAGE_SIZE - n, - "status = %08x\n", hw_read_intr_status(udc)); + "status = %08x\n", hw_read_intr_status(ci)); n += scnprintf(buf + n, PAGE_SIZE - n, - "enable = %08x\n", hw_read_intr_enable(udc));*/ + "enable = %08x\n", hw_read_intr_enable(ci));*/ n += scnprintf(buf + n, PAGE_SIZE - n, "*test = %d\n", isr_statistics.test); @@ -471,7 +471,7 @@ static ssize_t show_inters(struct device *dev, struct device_attribute *attr, n += scnprintf(buf + n, PAGE_SIZE - n, "\n"); } - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); return n; } @@ -485,31 +485,31 @@ static ssize_t show_inters(struct device *dev, struct device_attribute *attr, static ssize_t store_inters(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); unsigned long flags; unsigned en, bit; if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "EINVAL\n"); + dev_err(ci->dev, "EINVAL\n"); goto done; } if (sscanf(buf, "%u %u", &en, &bit) != 2 || en > 1) { - dev_err(udc->dev, "<1|0> : enable|disable interrupt\n"); + dev_err(ci->dev, "<1|0> : enable|disable interrupt\n"); goto done; } - spin_lock_irqsave(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); if (en) { - if (hw_intr_force(udc, bit)) + if (hw_intr_force(ci, bit)) dev_err(dev, "invalid bit number\n"); else isr_statistics.test++; } else { - if (hw_intr_clear(udc, bit)) + if (hw_intr_clear(ci, bit)) dev_err(dev, "invalid bit number\n"); } - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); done: return count; @@ -524,18 +524,18 @@ static DEVICE_ATTR(inters, S_IRUSR | S_IWUSR, show_inters, store_inters); static ssize_t show_port_test(struct device *dev, struct device_attribute *attr, char *buf) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); unsigned long flags; unsigned mode; if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "EINVAL\n"); + dev_err(ci->dev, "EINVAL\n"); return 0; } - spin_lock_irqsave(&udc->lock, flags); - mode = hw_port_test_get(udc); - spin_unlock_irqrestore(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); + mode = hw_port_test_get(ci); + spin_unlock_irqrestore(&ci->lock, flags); return scnprintf(buf, PAGE_SIZE, "mode = %u\n", mode); } @@ -549,24 +549,24 @@ static ssize_t store_port_test(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); unsigned long flags; unsigned mode; if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); + dev_err(ci->dev, "[%s] EINVAL\n", __func__); goto done; } if (sscanf(buf, "%u", &mode) != 1) { - dev_err(udc->dev, ": set port test mode"); + dev_err(ci->dev, ": set port test mode"); goto done; } - spin_lock_irqsave(&udc->lock, flags); - if (hw_port_test_set(udc, mode)) - dev_err(udc->dev, "invalid mode\n"); - spin_unlock_irqrestore(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); + if (hw_port_test_set(ci, mode)) + dev_err(ci->dev, "invalid mode\n"); + spin_unlock_irqrestore(&ci->lock, flags); done: return count; @@ -582,20 +582,20 @@ static DEVICE_ATTR(port_test, S_IRUSR | S_IWUSR, static ssize_t show_qheads(struct device *dev, struct device_attribute *attr, char *buf) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); unsigned long flags; unsigned i, j, n = 0; if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); + dev_err(ci->dev, "[%s] EINVAL\n", __func__); return 0; } - spin_lock_irqsave(&udc->lock, flags); - for (i = 0; i < udc->hw_ep_max/2; i++) { - struct ci13xxx_ep *mEpRx = &udc->ci13xxx_ep[i]; + spin_lock_irqsave(&ci->lock, flags); + for (i = 0; i < ci->hw_ep_max/2; i++) { + struct ci13xxx_ep *mEpRx = &ci->ci13xxx_ep[i]; struct ci13xxx_ep *mEpTx = - &udc->ci13xxx_ep[i + udc->hw_ep_max/2]; + &ci->ci13xxx_ep[i + ci->hw_ep_max/2]; n += scnprintf(buf + n, PAGE_SIZE - n, "EP=%02i: RX=%08X TX=%08X\n", i, (u32)mEpRx->qh.dma, (u32)mEpTx->qh.dma); @@ -606,7 +606,7 @@ static ssize_t show_qheads(struct device *dev, struct device_attribute *attr, *((u32 *)mEpTx->qh.ptr + j)); } } - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); return n; } @@ -621,25 +621,25 @@ static DEVICE_ATTR(qheads, S_IRUSR, show_qheads, NULL); static ssize_t show_registers(struct device *dev, struct device_attribute *attr, char *buf) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); unsigned long flags; u32 *dump; unsigned i, k, n = 0; if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); + dev_err(ci->dev, "[%s] EINVAL\n", __func__); return 0; } dump = kmalloc(sizeof(u32) * DUMP_ENTRIES, GFP_KERNEL); if (!dump) { - dev_err(udc->dev, "%s: out of memory\n", __func__); + dev_err(ci->dev, "%s: out of memory\n", __func__); return 0; } - spin_lock_irqsave(&udc->lock, flags); - k = hw_register_read(udc, dump, DUMP_ENTRIES); - spin_unlock_irqrestore(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); + k = hw_register_read(ci, dump, DUMP_ENTRIES); + spin_unlock_irqrestore(&ci->lock, flags); for (i = 0; i < k; i++) { n += scnprintf(buf + n, PAGE_SIZE - n, @@ -660,24 +660,24 @@ static ssize_t store_registers(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); unsigned long addr, data, flags; if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); + dev_err(ci->dev, "[%s] EINVAL\n", __func__); goto done; } if (sscanf(buf, "%li %li", &addr, &data) != 2) { - dev_err(udc->dev, + dev_err(ci->dev, " : write data to register address\n"); goto done; } - spin_lock_irqsave(&udc->lock, flags); - if (hw_register_write(udc, addr, data)) - dev_err(udc->dev, "invalid address range\n"); - spin_unlock_irqrestore(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); + if (hw_register_write(ci, addr, data)) + dev_err(ci->dev, "invalid address range\n"); + spin_unlock_irqrestore(&ci->lock, flags); done: return count; @@ -693,34 +693,34 @@ static DEVICE_ATTR(registers, S_IRUSR | S_IWUSR, static ssize_t show_requests(struct device *dev, struct device_attribute *attr, char *buf) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); unsigned long flags; struct list_head *ptr = NULL; struct ci13xxx_req *req = NULL; unsigned i, j, n = 0, qSize = sizeof(struct ci13xxx_td)/sizeof(u32); if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); + dev_err(ci->dev, "[%s] EINVAL\n", __func__); return 0; } - spin_lock_irqsave(&udc->lock, flags); - for (i = 0; i < udc->hw_ep_max; i++) - list_for_each(ptr, &udc->ci13xxx_ep[i].qh.queue) + spin_lock_irqsave(&ci->lock, flags); + for (i = 0; i < ci->hw_ep_max; i++) + list_for_each(ptr, &ci->ci13xxx_ep[i].qh.queue) { req = list_entry(ptr, struct ci13xxx_req, queue); n += scnprintf(buf + n, PAGE_SIZE - n, "EP=%02i: TD=%08X %s\n", - i % udc->hw_ep_max/2, (u32)req->dma, - ((i < udc->hw_ep_max/2) ? "RX" : "TX")); + i % ci->hw_ep_max/2, (u32)req->dma, + ((i < ci->hw_ep_max/2) ? "RX" : "TX")); for (j = 0; j < qSize; j++) n += scnprintf(buf + n, PAGE_SIZE - n, " %04X: %08X\n", j, *((u32 *)req->ptr + j)); } - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); return n; } diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 3094c85dc0b5..ba8284e2a237 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -67,11 +67,11 @@ static inline int hw_ep_bit(int num, int dir) return num + (dir ? 16 : 0); } -static inline int ep_to_bit(struct ci13xxx *udc, int n) +static inline int ep_to_bit(struct ci13xxx *ci, int n) { - int fill = 16 - udc->hw_ep_max / 2; + int fill = 16 - ci->hw_ep_max / 2; - if (n >= udc->hw_ep_max / 2) + if (n >= ci->hw_ep_max / 2) n += fill; return n; @@ -84,17 +84,17 @@ static inline int ep_to_bit(struct ci13xxx *udc, int n) * * This function returns an error code */ -static int hw_device_state(struct ci13xxx *udc, u32 dma) +static int hw_device_state(struct ci13xxx *ci, u32 dma) { if (dma) { - hw_write(udc, OP_ENDPTLISTADDR, ~0, dma); + hw_write(ci, OP_ENDPTLISTADDR, ~0, dma); /* interrupt, error, port change, reset, sleep/suspend */ - hw_write(udc, OP_USBINTR, ~0, + hw_write(ci, OP_USBINTR, ~0, USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI); - hw_write(udc, OP_USBCMD, USBCMD_RS, USBCMD_RS); + hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); } else { - hw_write(udc, OP_USBCMD, USBCMD_RS, 0); - hw_write(udc, OP_USBINTR, ~0, 0); + hw_write(ci, OP_USBCMD, USBCMD_RS, 0); + hw_write(ci, OP_USBINTR, ~0, 0); } return 0; } @@ -106,16 +106,16 @@ static int hw_device_state(struct ci13xxx *udc, u32 dma) * * This function returns an error code */ -static int hw_ep_flush(struct ci13xxx *udc, int num, int dir) +static int hw_ep_flush(struct ci13xxx *ci, int num, int dir) { int n = hw_ep_bit(num, dir); do { /* flush any pending transfer */ - hw_write(udc, OP_ENDPTFLUSH, BIT(n), BIT(n)); - while (hw_read(udc, OP_ENDPTFLUSH, BIT(n))) + hw_write(ci, OP_ENDPTFLUSH, BIT(n), BIT(n)); + while (hw_read(ci, OP_ENDPTFLUSH, BIT(n))) cpu_relax(); - } while (hw_read(udc, OP_ENDPTSTAT, BIT(n))); + } while (hw_read(ci, OP_ENDPTSTAT, BIT(n))); return 0; } @@ -127,10 +127,10 @@ static int hw_ep_flush(struct ci13xxx *udc, int num, int dir) * * This function returns an error code */ -static int hw_ep_disable(struct ci13xxx *udc, int num, int dir) +static int hw_ep_disable(struct ci13xxx *ci, int num, int dir) { - hw_ep_flush(udc, num, dir); - hw_write(udc, OP_ENDPTCTRL + num, + hw_ep_flush(ci, num, dir); + hw_write(ci, OP_ENDPTCTRL + num, dir ? ENDPTCTRL_TXE : ENDPTCTRL_RXE, 0); return 0; } @@ -143,7 +143,7 @@ static int hw_ep_disable(struct ci13xxx *udc, int num, int dir) * * This function returns an error code */ -static int hw_ep_enable(struct ci13xxx *udc, int num, int dir, int type) +static int hw_ep_enable(struct ci13xxx *ci, int num, int dir, int type) { u32 mask, data; @@ -166,7 +166,7 @@ static int hw_ep_enable(struct ci13xxx *udc, int num, int dir, int type) mask |= ENDPTCTRL_RXE; /* enable */ data |= ENDPTCTRL_RXE; } - hw_write(udc, OP_ENDPTCTRL + num, mask, data); + hw_write(ci, OP_ENDPTCTRL + num, mask, data); return 0; } @@ -177,11 +177,11 @@ static int hw_ep_enable(struct ci13xxx *udc, int num, int dir, int type) * * This function returns 1 if endpoint halted */ -static int hw_ep_get_halt(struct ci13xxx *udc, int num, int dir) +static int hw_ep_get_halt(struct ci13xxx *ci, int num, int dir) { u32 mask = dir ? ENDPTCTRL_TXS : ENDPTCTRL_RXS; - return hw_read(udc, OP_ENDPTCTRL + num, mask) ? 1 : 0; + return hw_read(ci, OP_ENDPTCTRL + num, mask) ? 1 : 0; } /** @@ -191,10 +191,10 @@ static int hw_ep_get_halt(struct ci13xxx *udc, int num, int dir) * * This function returns setup status */ -static int hw_test_and_clear_setup_status(struct ci13xxx *udc, int n) +static int hw_test_and_clear_setup_status(struct ci13xxx *ci, int n) { - n = ep_to_bit(udc, n); - return hw_test_and_clear(udc, OP_ENDPTSETUPSTAT, BIT(n)); + n = ep_to_bit(ci, n); + return hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(n)); } /** @@ -205,18 +205,18 @@ static int hw_test_and_clear_setup_status(struct ci13xxx *udc, int n) * * This function returns an error code */ -static int hw_ep_prime(struct ci13xxx *udc, int num, int dir, int is_ctrl) +static int hw_ep_prime(struct ci13xxx *ci, int num, int dir, int is_ctrl) { int n = hw_ep_bit(num, dir); - if (is_ctrl && dir == RX && hw_read(udc, OP_ENDPTSETUPSTAT, BIT(num))) + if (is_ctrl && dir == RX && hw_read(ci, OP_ENDPTSETUPSTAT, BIT(num))) return -EAGAIN; - hw_write(udc, OP_ENDPTPRIME, BIT(n), BIT(n)); + hw_write(ci, OP_ENDPTPRIME, BIT(n), BIT(n)); - while (hw_read(udc, OP_ENDPTPRIME, BIT(n))) + while (hw_read(ci, OP_ENDPTPRIME, BIT(n))) cpu_relax(); - if (is_ctrl && dir == RX && hw_read(udc, OP_ENDPTSETUPSTAT, BIT(num))) + if (is_ctrl && dir == RX && hw_read(ci, OP_ENDPTSETUPSTAT, BIT(num))) return -EAGAIN; /* status shoult be tested according with manual but it doesn't work */ @@ -232,7 +232,7 @@ static int hw_ep_prime(struct ci13xxx *udc, int num, int dir, int is_ctrl) * * This function returns an error code */ -static int hw_ep_set_halt(struct ci13xxx *udc, int num, int dir, int value) +static int hw_ep_set_halt(struct ci13xxx *ci, int num, int dir, int value) { if (value != 0 && value != 1) return -EINVAL; @@ -243,9 +243,9 @@ static int hw_ep_set_halt(struct ci13xxx *udc, int num, int dir, int value) u32 mask_xr = dir ? ENDPTCTRL_TXR : ENDPTCTRL_RXR; /* data toggle - reserved for EP0 but it's in ESS */ - hw_write(udc, reg, mask_xs|mask_xr, + hw_write(ci, reg, mask_xs|mask_xr, value ? mask_xs : mask_xr); - } while (value != hw_ep_get_halt(udc, num, dir)); + } while (value != hw_ep_get_halt(ci, num, dir)); return 0; } @@ -255,10 +255,10 @@ static int hw_ep_set_halt(struct ci13xxx *udc, int num, int dir, int value) * * This function returns true if high speed port */ -static int hw_port_is_high_speed(struct ci13xxx *udc) +static int hw_port_is_high_speed(struct ci13xxx *ci) { - return udc->hw_bank.lpm ? hw_read(udc, OP_DEVLC, DEVLC_PSPD) : - hw_read(udc, OP_PORTSC, PORTSC_HSP); + return ci->hw_bank.lpm ? hw_read(ci, OP_DEVLC, DEVLC_PSPD) : + hw_read(ci, OP_PORTSC, PORTSC_HSP); } /** @@ -266,9 +266,9 @@ static int hw_port_is_high_speed(struct ci13xxx *udc) * * This function returns register data */ -static u32 hw_read_intr_enable(struct ci13xxx *udc) +static u32 hw_read_intr_enable(struct ci13xxx *ci) { - return hw_read(udc, OP_USBINTR, ~0); + return hw_read(ci, OP_USBINTR, ~0); } /** @@ -276,9 +276,9 @@ static u32 hw_read_intr_enable(struct ci13xxx *udc) * * This function returns register data */ -static u32 hw_read_intr_status(struct ci13xxx *udc) +static u32 hw_read_intr_status(struct ci13xxx *ci) { - return hw_read(udc, OP_USBSTS, ~0); + return hw_read(ci, OP_USBSTS, ~0); } /** @@ -288,10 +288,10 @@ static u32 hw_read_intr_status(struct ci13xxx *udc) * * This function returns complete status */ -static int hw_test_and_clear_complete(struct ci13xxx *udc, int n) +static int hw_test_and_clear_complete(struct ci13xxx *ci, int n) { - n = ep_to_bit(udc, n); - return hw_test_and_clear(udc, OP_ENDPTCOMPLETE, BIT(n)); + n = ep_to_bit(ci, n); + return hw_test_and_clear(ci, OP_ENDPTCOMPLETE, BIT(n)); } /** @@ -300,11 +300,11 @@ static int hw_test_and_clear_complete(struct ci13xxx *udc, int n) * * This function returns active interrutps */ -static u32 hw_test_and_clear_intr_active(struct ci13xxx *udc) +static u32 hw_test_and_clear_intr_active(struct ci13xxx *ci) { - u32 reg = hw_read_intr_status(udc) & hw_read_intr_enable(udc); + u32 reg = hw_read_intr_status(ci) & hw_read_intr_enable(ci); - hw_write(udc, OP_USBSTS, ~0, reg); + hw_write(ci, OP_USBSTS, ~0, reg); return reg; } @@ -314,9 +314,9 @@ static u32 hw_test_and_clear_intr_active(struct ci13xxx *udc) * * This function returns guard value */ -static int hw_test_and_clear_setup_guard(struct ci13xxx *udc) +static int hw_test_and_clear_setup_guard(struct ci13xxx *ci) { - return hw_test_and_write(udc, OP_USBCMD, USBCMD_SUTW, 0); + return hw_test_and_write(ci, OP_USBCMD, USBCMD_SUTW, 0); } /** @@ -325,9 +325,9 @@ static int hw_test_and_clear_setup_guard(struct ci13xxx *udc) * * This function returns guard value */ -static int hw_test_and_set_setup_guard(struct ci13xxx *udc) +static int hw_test_and_set_setup_guard(struct ci13xxx *ci) { - return hw_test_and_write(udc, OP_USBCMD, USBCMD_SUTW, USBCMD_SUTW); + return hw_test_and_write(ci, OP_USBCMD, USBCMD_SUTW, USBCMD_SUTW); } /** @@ -337,9 +337,9 @@ static int hw_test_and_set_setup_guard(struct ci13xxx *udc) * This function explicitly sets the address, without the "USBADRA" (advance) * feature, which is not supported by older versions of the controller. */ -static void hw_usb_set_address(struct ci13xxx *udc, u8 value) +static void hw_usb_set_address(struct ci13xxx *ci, u8 value) { - hw_write(udc, OP_DEVICEADDR, DEVICEADDR_USBADR, + hw_write(ci, OP_DEVICEADDR, DEVICEADDR_USBADR, value << ffs_nr(DEVICEADDR_USBADR)); } @@ -349,21 +349,21 @@ static void hw_usb_set_address(struct ci13xxx *udc, u8 value) * * This function returns an error code */ -static int hw_usb_reset(struct ci13xxx *udc) +static int hw_usb_reset(struct ci13xxx *ci) { - hw_usb_set_address(udc, 0); + hw_usb_set_address(ci, 0); /* ESS flushes only at end?!? */ - hw_write(udc, OP_ENDPTFLUSH, ~0, ~0); + hw_write(ci, OP_ENDPTFLUSH, ~0, ~0); /* clear setup token semaphores */ - hw_write(udc, OP_ENDPTSETUPSTAT, 0, 0); + hw_write(ci, OP_ENDPTSETUPSTAT, 0, 0); /* clear complete status */ - hw_write(udc, OP_ENDPTCOMPLETE, 0, 0); + hw_write(ci, OP_ENDPTCOMPLETE, 0, 0); /* wait until all bits cleared */ - while (hw_read(udc, OP_ENDPTPRIME, ~0)) + while (hw_read(ci, OP_ENDPTPRIME, ~0)) udelay(10); /* not RTOS friendly */ /* reset all endpoints ? */ @@ -395,7 +395,7 @@ static inline u8 _usb_addr(struct ci13xxx_ep *ep) */ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) { - struct ci13xxx *udc = mEp->udc; + struct ci13xxx *ci = mEp->ci; unsigned i; int ret = 0; unsigned length = mReq->req.length; @@ -418,7 +418,7 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) if (!mReq->req.no_interrupt) mReq->zptr->token |= TD_IOC; } - ret = usb_gadget_map_request(&udc->gadget, &mReq->req, mEp->dir); + ret = usb_gadget_map_request(&ci->gadget, &mReq->req, mEp->dir); if (ret) return ret; @@ -454,13 +454,13 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) else mReqPrev->ptr->next = mReq->dma & TD_ADDR_MASK; wmb(); - if (hw_read(udc, OP_ENDPTPRIME, BIT(n))) + if (hw_read(ci, OP_ENDPTPRIME, BIT(n))) goto done; do { - hw_write(udc, OP_USBCMD, USBCMD_ATDTW, USBCMD_ATDTW); - tmp_stat = hw_read(udc, OP_ENDPTSTAT, BIT(n)); - } while (!hw_read(udc, OP_USBCMD, USBCMD_ATDTW)); - hw_write(udc, OP_USBCMD, USBCMD_ATDTW, 0); + hw_write(ci, OP_USBCMD, USBCMD_ATDTW, USBCMD_ATDTW); + tmp_stat = hw_read(ci, OP_ENDPTSTAT, BIT(n)); + } while (!hw_read(ci, OP_USBCMD, USBCMD_ATDTW)); + hw_write(ci, OP_USBCMD, USBCMD_ATDTW, 0); if (tmp_stat) goto done; } @@ -472,7 +472,7 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) wmb(); /* synchronize before ep prime */ - ret = hw_ep_prime(udc, mEp->num, mEp->dir, + ret = hw_ep_prime(ci, mEp->num, mEp->dir, mEp->type == USB_ENDPOINT_XFER_CONTROL); done: return ret; @@ -502,7 +502,7 @@ static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) mReq->req.status = 0; - usb_gadget_unmap_request(&mEp->udc->gadget, &mReq->req, mEp->dir); + usb_gadget_unmap_request(&mEp->ci->gadget, &mReq->req, mEp->dir); mReq->req.status = mReq->ptr->token & TD_STATUS; if ((TD_STATUS_HALTED & mReq->req.status) != 0) @@ -534,7 +534,7 @@ __acquires(mEp->lock) if (mEp == NULL) return -EINVAL; - hw_ep_flush(mEp->udc, mEp->num, mEp->dir); + hw_ep_flush(mEp->ci, mEp->num, mEp->dir); while (!list_empty(&mEp->qh.queue)) { @@ -563,33 +563,33 @@ __acquires(mEp->lock) static int _gadget_stop_activity(struct usb_gadget *gadget) { struct usb_ep *ep; - struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget); + struct ci13xxx *ci = container_of(gadget, struct ci13xxx, gadget); unsigned long flags; - spin_lock_irqsave(&udc->lock, flags); - udc->gadget.speed = USB_SPEED_UNKNOWN; - udc->remote_wakeup = 0; - udc->suspended = 0; - spin_unlock_irqrestore(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); + ci->gadget.speed = USB_SPEED_UNKNOWN; + ci->remote_wakeup = 0; + ci->suspended = 0; + spin_unlock_irqrestore(&ci->lock, flags); /* flush all endpoints */ gadget_for_each_ep(ep, gadget) { usb_ep_fifo_flush(ep); } - usb_ep_fifo_flush(&udc->ep0out->ep); - usb_ep_fifo_flush(&udc->ep0in->ep); + usb_ep_fifo_flush(&ci->ep0out->ep); + usb_ep_fifo_flush(&ci->ep0in->ep); - if (udc->driver) - udc->driver->disconnect(gadget); + if (ci->driver) + ci->driver->disconnect(gadget); /* make sure to disable all endpoints */ gadget_for_each_ep(ep, gadget) { usb_ep_disable(ep); } - if (udc->status != NULL) { - usb_ep_free_request(&udc->ep0in->ep, udc->status); - udc->status = NULL; + if (ci->status != NULL) { + usb_ep_free_request(&ci->ep0in->ep, ci->status); + ci->status = NULL; } return 0; @@ -600,36 +600,36 @@ static int _gadget_stop_activity(struct usb_gadget *gadget) *****************************************************************************/ /** * isr_reset_handler: USB reset interrupt handler - * @udc: UDC device + * @ci: UDC device * * This function resets USB engine after a bus reset occurred */ -static void isr_reset_handler(struct ci13xxx *udc) -__releases(udc->lock) -__acquires(udc->lock) +static void isr_reset_handler(struct ci13xxx *ci) +__releases(ci->lock) +__acquires(ci->lock) { int retval; dbg_event(0xFF, "BUS RST", 0); - spin_unlock(&udc->lock); - retval = _gadget_stop_activity(&udc->gadget); + spin_unlock(&ci->lock); + retval = _gadget_stop_activity(&ci->gadget); if (retval) goto done; - retval = hw_usb_reset(udc); + retval = hw_usb_reset(ci); if (retval) goto done; - udc->status = usb_ep_alloc_request(&udc->ep0in->ep, GFP_ATOMIC); - if (udc->status == NULL) + ci->status = usb_ep_alloc_request(&ci->ep0in->ep, GFP_ATOMIC); + if (ci->status == NULL) retval = -ENOMEM; done: - spin_lock(&udc->lock); + spin_lock(&ci->lock); if (retval) - dev_err(udc->dev, "error: %i\n", retval); + dev_err(ci->dev, "error: %i\n", retval); } /** @@ -650,17 +650,17 @@ static void isr_get_status_complete(struct usb_ep *ep, struct usb_request *req) /** * isr_get_status_response: get_status request response - * @udc: udc struct + * @ci: ci struct * @setup: setup request packet * * This function returns an error code */ -static int isr_get_status_response(struct ci13xxx *udc, +static int isr_get_status_response(struct ci13xxx *ci, struct usb_ctrlrequest *setup) __releases(mEp->lock) __acquires(mEp->lock) { - struct ci13xxx_ep *mEp = udc->ep0in; + struct ci13xxx_ep *mEp = ci->ep0in; struct usb_request *req = NULL; gfp_t gfp_flags = GFP_ATOMIC; int dir, num, retval; @@ -684,14 +684,14 @@ __acquires(mEp->lock) if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) { /* Assume that device is bus powered for now. */ - *(u16 *)req->buf = udc->remote_wakeup << 1; + *(u16 *)req->buf = ci->remote_wakeup << 1; retval = 0; } else if ((setup->bRequestType & USB_RECIP_MASK) \ == USB_RECIP_ENDPOINT) { dir = (le16_to_cpu(setup->wIndex) & USB_ENDPOINT_DIR_MASK) ? TX : RX; num = le16_to_cpu(setup->wIndex) & USB_ENDPOINT_NUMBER_MASK; - *(u16 *)req->buf = hw_ep_get_halt(udc, num, dir); + *(u16 *)req->buf = hw_ep_get_halt(ci, num, dir); } /* else do nothing; reserved for future use */ @@ -723,39 +723,39 @@ __acquires(mEp->lock) static void isr_setup_status_complete(struct usb_ep *ep, struct usb_request *req) { - struct ci13xxx *udc = req->context; + struct ci13xxx *ci = req->context; unsigned long flags; - if (udc->setaddr) { - hw_usb_set_address(udc, udc->address); - udc->setaddr = false; + if (ci->setaddr) { + hw_usb_set_address(ci, ci->address); + ci->setaddr = false; } - spin_lock_irqsave(&udc->lock, flags); - if (udc->test_mode) - hw_port_test_set(udc, udc->test_mode); - spin_unlock_irqrestore(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); + if (ci->test_mode) + hw_port_test_set(ci, ci->test_mode); + spin_unlock_irqrestore(&ci->lock, flags); } /** * isr_setup_status_phase: queues the status phase of a setup transation - * @udc: udc struct + * @ci: ci struct * * This function returns an error code */ -static int isr_setup_status_phase(struct ci13xxx *udc) +static int isr_setup_status_phase(struct ci13xxx *ci) __releases(mEp->lock) __acquires(mEp->lock) { int retval; struct ci13xxx_ep *mEp; - mEp = (udc->ep0_dir == TX) ? udc->ep0out : udc->ep0in; - udc->status->context = udc; - udc->status->complete = isr_setup_status_complete; + mEp = (ci->ep0_dir == TX) ? ci->ep0out : ci->ep0in; + ci->status->context = ci; + ci->status->complete = isr_setup_status_complete; spin_unlock(mEp->lock); - retval = usb_ep_queue(&mEp->ep, udc->status, GFP_ATOMIC); + retval = usb_ep_queue(&mEp->ep, ci->status, GFP_ATOMIC); spin_lock(mEp->lock); return retval; @@ -790,7 +790,7 @@ __acquires(mEp->lock) spin_unlock(mEp->lock); if ((mEp->type == USB_ENDPOINT_XFER_CONTROL) && mReq->req.length) - mEpTemp = mEp->udc->ep0in; + mEpTemp = mEp->ci->ep0in; mReq->req.complete(&mEpTemp->ep, &mReq->req); spin_lock(mEp->lock); } @@ -806,48 +806,48 @@ __acquires(mEp->lock) /** * isr_tr_complete_handler: transaction complete interrupt handler - * @udc: UDC descriptor + * @ci: UDC descriptor * * This function handles traffic events */ -static void isr_tr_complete_handler(struct ci13xxx *udc) -__releases(udc->lock) -__acquires(udc->lock) +static void isr_tr_complete_handler(struct ci13xxx *ci) +__releases(ci->lock) +__acquires(ci->lock) { unsigned i; u8 tmode = 0; - for (i = 0; i < udc->hw_ep_max; i++) { - struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i]; + for (i = 0; i < ci->hw_ep_max; i++) { + struct ci13xxx_ep *mEp = &ci->ci13xxx_ep[i]; int type, num, dir, err = -EINVAL; struct usb_ctrlrequest req; if (mEp->ep.desc == NULL) continue; /* not configured */ - if (hw_test_and_clear_complete(udc, i)) { + if (hw_test_and_clear_complete(ci, i)) { err = isr_tr_complete_low(mEp); if (mEp->type == USB_ENDPOINT_XFER_CONTROL) { if (err > 0) /* needs status phase */ - err = isr_setup_status_phase(udc); + err = isr_setup_status_phase(ci); if (err < 0) { dbg_event(_usb_addr(mEp), "ERROR", err); - spin_unlock(&udc->lock); + spin_unlock(&ci->lock); if (usb_ep_set_halt(&mEp->ep)) - dev_err(udc->dev, + dev_err(ci->dev, "error: ep_set_halt\n"); - spin_lock(&udc->lock); + spin_lock(&ci->lock); } } } if (mEp->type != USB_ENDPOINT_XFER_CONTROL || - !hw_test_and_clear_setup_status(udc, i)) + !hw_test_and_clear_setup_status(ci, i)) continue; if (i != 0) { - dev_warn(udc->dev, "ctrl traffic at endpoint %d\n", i); + dev_warn(ci->dev, "ctrl traffic at endpoint %d\n", i); continue; } @@ -855,18 +855,18 @@ __acquires(udc->lock) * Flush data and handshake transactions of previous * setup packet. */ - _ep_nuke(udc->ep0out); - _ep_nuke(udc->ep0in); + _ep_nuke(ci->ep0out); + _ep_nuke(ci->ep0in); /* read_setup_packet */ do { - hw_test_and_set_setup_guard(udc); + hw_test_and_set_setup_guard(ci); memcpy(&req, &mEp->qh.ptr->setup, sizeof(req)); - } while (!hw_test_and_clear_setup_guard(udc)); + } while (!hw_test_and_clear_setup_guard(ci)); type = req.bRequestType; - udc->ep0_dir = (type & USB_DIR_IN) ? TX : RX; + ci->ep0_dir = (type & USB_DIR_IN) ? TX : RX; dbg_setup(_usb_addr(mEp), &req); @@ -881,23 +881,23 @@ __acquires(udc->lock) dir = num & USB_ENDPOINT_DIR_MASK; num &= USB_ENDPOINT_NUMBER_MASK; if (dir) /* TX */ - num += udc->hw_ep_max/2; - if (!udc->ci13xxx_ep[num].wedge) { - spin_unlock(&udc->lock); + num += ci->hw_ep_max/2; + if (!ci->ci13xxx_ep[num].wedge) { + spin_unlock(&ci->lock); err = usb_ep_clear_halt( - &udc->ci13xxx_ep[num].ep); - spin_lock(&udc->lock); + &ci->ci13xxx_ep[num].ep); + spin_lock(&ci->lock); if (err) break; } - err = isr_setup_status_phase(udc); + err = isr_setup_status_phase(ci); } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) && le16_to_cpu(req.wValue) == USB_DEVICE_REMOTE_WAKEUP) { if (req.wLength != 0) break; - udc->remote_wakeup = 0; - err = isr_setup_status_phase(udc); + ci->remote_wakeup = 0; + err = isr_setup_status_phase(ci); } else { goto delegate; } @@ -910,7 +910,7 @@ __acquires(udc->lock) if (le16_to_cpu(req.wLength) != 2 || le16_to_cpu(req.wValue) != 0) break; - err = isr_get_status_response(udc, &req); + err = isr_get_status_response(ci, &req); break; case USB_REQ_SET_ADDRESS: if (type != (USB_DIR_OUT|USB_RECIP_DEVICE)) @@ -918,9 +918,9 @@ __acquires(udc->lock) if (le16_to_cpu(req.wLength) != 0 || le16_to_cpu(req.wIndex) != 0) break; - udc->address = (u8)le16_to_cpu(req.wValue); - udc->setaddr = true; - err = isr_setup_status_phase(udc); + ci->address = (u8)le16_to_cpu(req.wValue); + ci->setaddr = true; + err = isr_setup_status_phase(ci); break; case USB_REQ_SET_FEATURE: if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) && @@ -932,20 +932,20 @@ __acquires(udc->lock) dir = num & USB_ENDPOINT_DIR_MASK; num &= USB_ENDPOINT_NUMBER_MASK; if (dir) /* TX */ - num += udc->hw_ep_max/2; + num += ci->hw_ep_max/2; - spin_unlock(&udc->lock); - err = usb_ep_set_halt(&udc->ci13xxx_ep[num].ep); - spin_lock(&udc->lock); + spin_unlock(&ci->lock); + err = usb_ep_set_halt(&ci->ci13xxx_ep[num].ep); + spin_lock(&ci->lock); if (!err) - isr_setup_status_phase(udc); + isr_setup_status_phase(ci); } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE)) { if (req.wLength != 0) break; switch (le16_to_cpu(req.wValue)) { case USB_DEVICE_REMOTE_WAKEUP: - udc->remote_wakeup = 1; - err = isr_setup_status_phase(udc); + ci->remote_wakeup = 1; + err = isr_setup_status_phase(ci); break; case USB_DEVICE_TEST_MODE: tmode = le16_to_cpu(req.wIndex) >> 8; @@ -955,9 +955,9 @@ __acquires(udc->lock) case TEST_SE0_NAK: case TEST_PACKET: case TEST_FORCE_EN: - udc->test_mode = tmode; + ci->test_mode = tmode; err = isr_setup_status_phase( - udc); + ci); break; default: break; @@ -972,21 +972,21 @@ __acquires(udc->lock) default: delegate: if (req.wLength == 0) /* no data phase */ - udc->ep0_dir = TX; + ci->ep0_dir = TX; - spin_unlock(&udc->lock); - err = udc->driver->setup(&udc->gadget, &req); - spin_lock(&udc->lock); + spin_unlock(&ci->lock); + err = ci->driver->setup(&ci->gadget, &req); + spin_lock(&ci->lock); break; } if (err < 0) { dbg_event(_usb_addr(mEp), "ERROR", err); - spin_unlock(&udc->lock); + spin_unlock(&ci->lock); if (usb_ep_set_halt(&mEp->ep)) - dev_err(udc->dev, "error: ep_set_halt\n"); - spin_lock(&udc->lock); + dev_err(ci->dev, "error: ep_set_halt\n"); + spin_lock(&ci->lock); } } } @@ -1016,7 +1016,7 @@ static int ep_enable(struct usb_ep *ep, mEp->ep.desc = desc; if (!list_empty(&mEp->qh.queue)) - dev_warn(mEp->udc->dev, "enabling a non-empty endpoint!\n"); + dev_warn(mEp->ci->dev, "enabling a non-empty endpoint!\n"); mEp->dir = usb_endpoint_dir_in(desc) ? TX : RX; mEp->num = usb_endpoint_num(desc); @@ -1044,7 +1044,7 @@ static int ep_enable(struct usb_ep *ep, * is always enabled */ if (mEp->num) - retval |= hw_ep_enable(mEp->udc, mEp->num, mEp->dir, mEp->type); + retval |= hw_ep_enable(mEp->ci, mEp->num, mEp->dir, mEp->type); spin_unlock_irqrestore(mEp->lock, flags); return retval; @@ -1075,7 +1075,7 @@ static int ep_disable(struct usb_ep *ep) dbg_event(_usb_addr(mEp), "DISABLE", 0); retval |= _ep_nuke(mEp); - retval |= hw_ep_disable(mEp->udc, mEp->num, mEp->dir); + retval |= hw_ep_disable(mEp->ci, mEp->num, mEp->dir); if (mEp->type == USB_ENDPOINT_XFER_CONTROL) mEp->dir = (mEp->dir == TX) ? RX : TX; @@ -1132,7 +1132,7 @@ static void ep_free_request(struct usb_ep *ep, struct usb_request *req) if (ep == NULL || req == NULL) { return; } else if (!list_empty(&mReq->queue)) { - dev_err(mEp->udc->dev, "freeing queued request\n"); + dev_err(mEp->ci->dev, "freeing queued request\n"); return; } @@ -1157,7 +1157,7 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req, { struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req); - struct ci13xxx *udc = mEp->udc; + struct ci13xxx *ci = mEp->ci; int retval = 0; unsigned long flags; @@ -1168,12 +1168,12 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req, if (mEp->type == USB_ENDPOINT_XFER_CONTROL) { if (req->length) - mEp = (udc->ep0_dir == RX) ? - udc->ep0out : udc->ep0in; + mEp = (ci->ep0_dir == RX) ? + ci->ep0out : ci->ep0in; if (!list_empty(&mEp->qh.queue)) { _ep_nuke(mEp); retval = -EOVERFLOW; - dev_warn(mEp->udc->dev, "endpoint ctrl %X nuked\n", + dev_warn(mEp->ci->dev, "endpoint ctrl %X nuked\n", _usb_addr(mEp)); } } @@ -1181,14 +1181,14 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req, /* first nuke then test link, e.g. previous status has not sent */ if (!list_empty(&mReq->queue)) { retval = -EBUSY; - dev_err(mEp->udc->dev, "request already in queue\n"); + dev_err(mEp->ci->dev, "request already in queue\n"); goto done; } if (req->length > 4 * CI13XXX_PAGE_SIZE) { req->length = 4 * CI13XXX_PAGE_SIZE; retval = -EMSGSIZE; - dev_warn(mEp->udc->dev, "request length truncated\n"); + dev_warn(mEp->ci->dev, "request length truncated\n"); } dbg_queue(_usb_addr(mEp), req, retval); @@ -1231,12 +1231,12 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req) dbg_event(_usb_addr(mEp), "DEQUEUE", 0); - hw_ep_flush(mEp->udc, mEp->num, mEp->dir); + hw_ep_flush(mEp->ci, mEp->num, mEp->dir); /* pop request */ list_del_init(&mReq->queue); - usb_gadget_unmap_request(&mEp->udc->gadget, req, mEp->dir); + usb_gadget_unmap_request(&mEp->ci->gadget, req, mEp->dir); req->status = -ECONNRESET; @@ -1278,7 +1278,7 @@ static int ep_set_halt(struct usb_ep *ep, int value) direction = mEp->dir; do { dbg_event(_usb_addr(mEp), "HALT", value); - retval |= hw_ep_set_halt(mEp->udc, mEp->num, mEp->dir, value); + retval |= hw_ep_set_halt(mEp->ci, mEp->num, mEp->dir, value); if (!value) mEp->wedge = 0; @@ -1326,14 +1326,14 @@ static void ep_fifo_flush(struct usb_ep *ep) unsigned long flags; if (ep == NULL) { - dev_err(mEp->udc->dev, "%02X: -EINVAL\n", _usb_addr(mEp)); + dev_err(mEp->ci->dev, "%02X: -EINVAL\n", _usb_addr(mEp)); return; } spin_lock_irqsave(mEp->lock, flags); dbg_event(_usb_addr(mEp), "FFLUSH", 0); - hw_ep_flush(mEp->udc, mEp->num, mEp->dir); + hw_ep_flush(mEp->ci, mEp->num, mEp->dir); spin_unlock_irqrestore(mEp->lock, flags); } @@ -1359,30 +1359,30 @@ static const struct usb_ep_ops usb_ep_ops = { *****************************************************************************/ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active) { - struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget); + struct ci13xxx *ci = container_of(_gadget, struct ci13xxx, gadget); unsigned long flags; int gadget_ready = 0; - if (!(udc->platdata->flags & CI13XXX_PULLUP_ON_VBUS)) + if (!(ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS)) return -EOPNOTSUPP; - spin_lock_irqsave(&udc->lock, flags); - udc->vbus_active = is_active; - if (udc->driver) + spin_lock_irqsave(&ci->lock, flags); + ci->vbus_active = is_active; + if (ci->driver) gadget_ready = 1; - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); if (gadget_ready) { if (is_active) { pm_runtime_get_sync(&_gadget->dev); - hw_device_reset(udc, USBMODE_CM_DC); - hw_device_state(udc, udc->ep0out->qh.dma); + hw_device_reset(ci, USBMODE_CM_DC); + hw_device_state(ci, ci->ep0out->qh.dma); } else { - hw_device_state(udc, 0); - if (udc->platdata->notify_event) - udc->platdata->notify_event(udc, + hw_device_state(ci, 0); + if (ci->platdata->notify_event) + ci->platdata->notify_event(ci, CI13XXX_CONTROLLER_STOPPED_EVENT); - _gadget_stop_activity(&udc->gadget); + _gadget_stop_activity(&ci->gadget); pm_runtime_put_sync(&_gadget->dev); } } @@ -1392,31 +1392,31 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active) static int ci13xxx_wakeup(struct usb_gadget *_gadget) { - struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget); + struct ci13xxx *ci = container_of(_gadget, struct ci13xxx, gadget); unsigned long flags; int ret = 0; - spin_lock_irqsave(&udc->lock, flags); - if (!udc->remote_wakeup) { + spin_lock_irqsave(&ci->lock, flags); + if (!ci->remote_wakeup) { ret = -EOPNOTSUPP; goto out; } - if (!hw_read(udc, OP_PORTSC, PORTSC_SUSP)) { + if (!hw_read(ci, OP_PORTSC, PORTSC_SUSP)) { ret = -EINVAL; goto out; } - hw_write(udc, OP_PORTSC, PORTSC_FPR, PORTSC_FPR); + hw_write(ci, OP_PORTSC, PORTSC_FPR, PORTSC_FPR); out: - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); return ret; } static int ci13xxx_vbus_draw(struct usb_gadget *_gadget, unsigned mA) { - struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget); + struct ci13xxx *ci = container_of(_gadget, struct ci13xxx, gadget); - if (udc->transceiver) - return usb_phy_set_power(udc->transceiver, mA); + if (ci->transceiver) + return usb_phy_set_power(ci->transceiver, mA); return -ENOTSUPP; } @@ -1437,28 +1437,28 @@ static const struct usb_gadget_ops usb_gadget_ops = { .udc_stop = ci13xxx_stop, }; -static int init_eps(struct ci13xxx *udc) +static int init_eps(struct ci13xxx *ci) { int retval = 0, i, j; - for (i = 0; i < udc->hw_ep_max/2; i++) + for (i = 0; i < ci->hw_ep_max/2; i++) for (j = RX; j <= TX; j++) { - int k = i + j * udc->hw_ep_max/2; - struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[k]; + int k = i + j * ci->hw_ep_max/2; + struct ci13xxx_ep *mEp = &ci->ci13xxx_ep[k]; scnprintf(mEp->name, sizeof(mEp->name), "ep%i%s", i, (j == TX) ? "in" : "out"); - mEp->udc = udc; - mEp->lock = &udc->lock; - mEp->td_pool = udc->td_pool; + mEp->ci = ci; + mEp->lock = &ci->lock; + mEp->td_pool = ci->td_pool; mEp->ep.name = mEp->name; mEp->ep.ops = &usb_ep_ops; mEp->ep.maxpacket = CTRL_PAYLOAD_MAX; INIT_LIST_HEAD(&mEp->qh.queue); - mEp->qh.ptr = dma_pool_alloc(udc->qh_pool, GFP_KERNEL, + mEp->qh.ptr = dma_pool_alloc(ci->qh_pool, GFP_KERNEL, &mEp->qh.dma); if (mEp->qh.ptr == NULL) retval = -ENOMEM; @@ -1471,14 +1471,14 @@ static int init_eps(struct ci13xxx *udc) */ if (i == 0) { if (j == RX) - udc->ep0out = mEp; + ci->ep0out = mEp; else - udc->ep0in = mEp; + ci->ep0in = mEp; continue; } - list_add_tail(&mEp->ep.ep_list, &udc->gadget.ep_list); + list_add_tail(&mEp->ep.ep_list, &ci->gadget.ep_list); } return retval; @@ -1494,7 +1494,7 @@ static int init_eps(struct ci13xxx *udc) static int ci13xxx_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver) { - struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget); + struct ci13xxx *ci = container_of(gadget, struct ci13xxx, gadget); unsigned long flags; int retval = -ENOMEM; @@ -1502,35 +1502,35 @@ static int ci13xxx_start(struct usb_gadget *gadget, return -EINVAL; - udc->ep0out->ep.desc = &ctrl_endpt_out_desc; - retval = usb_ep_enable(&udc->ep0out->ep); + ci->ep0out->ep.desc = &ctrl_endpt_out_desc; + retval = usb_ep_enable(&ci->ep0out->ep); if (retval) return retval; - udc->ep0in->ep.desc = &ctrl_endpt_in_desc; - retval = usb_ep_enable(&udc->ep0in->ep); + ci->ep0in->ep.desc = &ctrl_endpt_in_desc; + retval = usb_ep_enable(&ci->ep0in->ep); if (retval) return retval; - spin_lock_irqsave(&udc->lock, flags); - - udc->driver = driver; - pm_runtime_get_sync(&udc->gadget.dev); - if (udc->platdata->flags & CI13XXX_PULLUP_ON_VBUS) { - if (udc->vbus_active) { - if (udc->platdata->flags & CI13XXX_REGS_SHARED) - hw_device_reset(udc, USBMODE_CM_DC); + spin_lock_irqsave(&ci->lock, flags); + + ci->driver = driver; + pm_runtime_get_sync(&ci->gadget.dev); + if (ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS) { + if (ci->vbus_active) { + if (ci->platdata->flags & CI13XXX_REGS_SHARED) + hw_device_reset(ci, USBMODE_CM_DC); } else { - pm_runtime_put_sync(&udc->gadget.dev); + pm_runtime_put_sync(&ci->gadget.dev); goto done; } } - retval = hw_device_state(udc, udc->ep0out->qh.dma); + retval = hw_device_state(ci, ci->ep0out->qh.dma); if (retval) - pm_runtime_put_sync(&udc->gadget.dev); + pm_runtime_put_sync(&ci->gadget.dev); done: - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); return retval; } @@ -1540,25 +1540,25 @@ static int ci13xxx_start(struct usb_gadget *gadget, static int ci13xxx_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver) { - struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget); + struct ci13xxx *ci = container_of(gadget, struct ci13xxx, gadget); unsigned long flags; - spin_lock_irqsave(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); - if (!(udc->platdata->flags & CI13XXX_PULLUP_ON_VBUS) || - udc->vbus_active) { - hw_device_state(udc, 0); - if (udc->platdata->notify_event) - udc->platdata->notify_event(udc, + if (!(ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS) || + ci->vbus_active) { + hw_device_state(ci, 0); + if (ci->platdata->notify_event) + ci->platdata->notify_event(ci, CI13XXX_CONTROLLER_STOPPED_EVENT); - udc->driver = NULL; - spin_unlock_irqrestore(&udc->lock, flags); - _gadget_stop_activity(&udc->gadget); - spin_lock_irqsave(&udc->lock, flags); - pm_runtime_put(&udc->gadget.dev); + ci->driver = NULL; + spin_unlock_irqrestore(&ci->lock, flags); + _gadget_stop_activity(&ci->gadget); + spin_lock_irqsave(&ci->lock, flags); + pm_runtime_put(&ci->gadget.dev); } - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); return 0; } @@ -1567,64 +1567,64 @@ static int ci13xxx_stop(struct usb_gadget *gadget, * BUS block *****************************************************************************/ /** - * udc_irq: udc interrupt handler + * udc_irq: ci interrupt handler * * This function returns IRQ_HANDLED if the IRQ has been handled * It locks access to registers */ -static irqreturn_t udc_irq(struct ci13xxx *udc) +static irqreturn_t udc_irq(struct ci13xxx *ci) { irqreturn_t retval; u32 intr; - if (udc == NULL) + if (ci == NULL) return IRQ_HANDLED; - spin_lock(&udc->lock); + spin_lock(&ci->lock); - if (udc->platdata->flags & CI13XXX_REGS_SHARED) { - if (hw_read(udc, OP_USBMODE, USBMODE_CM) != + if (ci->platdata->flags & CI13XXX_REGS_SHARED) { + if (hw_read(ci, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DC) { - spin_unlock(&udc->lock); + spin_unlock(&ci->lock); return IRQ_NONE; } } - intr = hw_test_and_clear_intr_active(udc); + intr = hw_test_and_clear_intr_active(ci); dbg_interrupt(intr); if (intr) { /* order defines priority - do NOT change it */ if (USBi_URI & intr) - isr_reset_handler(udc); + isr_reset_handler(ci); if (USBi_PCI & intr) { - udc->gadget.speed = hw_port_is_high_speed(udc) ? + ci->gadget.speed = hw_port_is_high_speed(ci) ? USB_SPEED_HIGH : USB_SPEED_FULL; - if (udc->suspended && udc->driver->resume) { - spin_unlock(&udc->lock); - udc->driver->resume(&udc->gadget); - spin_lock(&udc->lock); - udc->suspended = 0; + if (ci->suspended && ci->driver->resume) { + spin_unlock(&ci->lock); + ci->driver->resume(&ci->gadget); + spin_lock(&ci->lock); + ci->suspended = 0; } } if (USBi_UI & intr) - isr_tr_complete_handler(udc); + isr_tr_complete_handler(ci); if (USBi_SLI & intr) { - if (udc->gadget.speed != USB_SPEED_UNKNOWN && - udc->driver->suspend) { - udc->suspended = 1; - spin_unlock(&udc->lock); - udc->driver->suspend(&udc->gadget); - spin_lock(&udc->lock); + if (ci->gadget.speed != USB_SPEED_UNKNOWN && + ci->driver->suspend) { + ci->suspended = 1; + spin_unlock(&ci->lock); + ci->driver->suspend(&ci->gadget); + spin_lock(&ci->lock); } } retval = IRQ_HANDLED; } else { retval = IRQ_NONE; } - spin_unlock(&udc->lock); + spin_unlock(&ci->lock); return retval; } @@ -1641,109 +1641,109 @@ static void udc_release(struct device *dev) /** * udc_start: initialize gadget role - * @udc: chipidea controller + * @ci: chipidea controller */ -static int udc_start(struct ci13xxx *udc) +static int udc_start(struct ci13xxx *ci) { - struct device *dev = udc->dev; + struct device *dev = ci->dev; int retval = 0; - spin_lock_init(&udc->lock); + spin_lock_init(&ci->lock); - udc->gadget.ops = &usb_gadget_ops; - udc->gadget.speed = USB_SPEED_UNKNOWN; - udc->gadget.max_speed = USB_SPEED_HIGH; - udc->gadget.is_otg = 0; - udc->gadget.name = udc->platdata->name; + ci->gadget.ops = &usb_gadget_ops; + ci->gadget.speed = USB_SPEED_UNKNOWN; + ci->gadget.max_speed = USB_SPEED_HIGH; + ci->gadget.is_otg = 0; + ci->gadget.name = ci->platdata->name; - INIT_LIST_HEAD(&udc->gadget.ep_list); + INIT_LIST_HEAD(&ci->gadget.ep_list); - dev_set_name(&udc->gadget.dev, "gadget"); - udc->gadget.dev.dma_mask = dev->dma_mask; - udc->gadget.dev.coherent_dma_mask = dev->coherent_dma_mask; - udc->gadget.dev.parent = dev; - udc->gadget.dev.release = udc_release; + dev_set_name(&ci->gadget.dev, "gadget"); + ci->gadget.dev.dma_mask = dev->dma_mask; + ci->gadget.dev.coherent_dma_mask = dev->coherent_dma_mask; + ci->gadget.dev.parent = dev; + ci->gadget.dev.release = udc_release; /* alloc resources */ - udc->qh_pool = dma_pool_create("ci13xxx_qh", dev, + ci->qh_pool = dma_pool_create("ci13xxx_qh", dev, sizeof(struct ci13xxx_qh), 64, CI13XXX_PAGE_SIZE); - if (udc->qh_pool == NULL) + if (ci->qh_pool == NULL) return -ENOMEM; - udc->td_pool = dma_pool_create("ci13xxx_td", dev, + ci->td_pool = dma_pool_create("ci13xxx_td", dev, sizeof(struct ci13xxx_td), 64, CI13XXX_PAGE_SIZE); - if (udc->td_pool == NULL) { + if (ci->td_pool == NULL) { retval = -ENOMEM; goto free_qh_pool; } - retval = init_eps(udc); + retval = init_eps(ci); if (retval) goto free_pools; - udc->gadget.ep0 = &udc->ep0in->ep; + ci->gadget.ep0 = &ci->ep0in->ep; - udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); + ci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - if (udc->platdata->flags & CI13XXX_REQUIRE_TRANSCEIVER) { - if (udc->transceiver == NULL) { + if (ci->platdata->flags & CI13XXX_REQUIRE_TRANSCEIVER) { + if (ci->transceiver == NULL) { retval = -ENODEV; goto free_pools; } } - if (!(udc->platdata->flags & CI13XXX_REGS_SHARED)) { - retval = hw_device_reset(udc, USBMODE_CM_DC); + if (!(ci->platdata->flags & CI13XXX_REGS_SHARED)) { + retval = hw_device_reset(ci, USBMODE_CM_DC); if (retval) goto put_transceiver; } - retval = device_register(&udc->gadget.dev); + retval = device_register(&ci->gadget.dev); if (retval) { - put_device(&udc->gadget.dev); + put_device(&ci->gadget.dev); goto put_transceiver; } - retval = dbg_create_files(&udc->gadget.dev); + retval = dbg_create_files(&ci->gadget.dev); if (retval) goto unreg_device; - if (!IS_ERR_OR_NULL(udc->transceiver)) { - retval = otg_set_peripheral(udc->transceiver->otg, - &udc->gadget); + if (!IS_ERR_OR_NULL(ci->transceiver)) { + retval = otg_set_peripheral(ci->transceiver->otg, + &ci->gadget); if (retval) goto remove_dbg; } - retval = usb_add_gadget_udc(dev, &udc->gadget); + retval = usb_add_gadget_udc(dev, &ci->gadget); if (retval) goto remove_trans; - pm_runtime_no_callbacks(&udc->gadget.dev); - pm_runtime_enable(&udc->gadget.dev); + pm_runtime_no_callbacks(&ci->gadget.dev); + pm_runtime_enable(&ci->gadget.dev); return retval; remove_trans: - if (!IS_ERR_OR_NULL(udc->transceiver)) { - otg_set_peripheral(udc->transceiver->otg, &udc->gadget); - usb_put_phy(udc->transceiver); + if (!IS_ERR_OR_NULL(ci->transceiver)) { + otg_set_peripheral(ci->transceiver->otg, &ci->gadget); + usb_put_phy(ci->transceiver); } dev_err(dev, "error = %i\n", retval); remove_dbg: - dbg_remove_files(&udc->gadget.dev); + dbg_remove_files(&ci->gadget.dev); unreg_device: - device_unregister(&udc->gadget.dev); + device_unregister(&ci->gadget.dev); put_transceiver: - if (!IS_ERR_OR_NULL(udc->transceiver)) - usb_put_phy(udc->transceiver); + if (!IS_ERR_OR_NULL(ci->transceiver)) + usb_put_phy(ci->transceiver); free_pools: - dma_pool_destroy(udc->td_pool); + dma_pool_destroy(ci->td_pool); free_qh_pool: - dma_pool_destroy(udc->qh_pool); + dma_pool_destroy(ci->qh_pool); return retval; } @@ -1752,32 +1752,32 @@ free_qh_pool: * * No interrupts active, the IRQ has been released */ -static void udc_stop(struct ci13xxx *udc) +static void udc_stop(struct ci13xxx *ci) { int i; - if (udc == NULL) + if (ci == NULL) return; - usb_del_gadget_udc(&udc->gadget); + usb_del_gadget_udc(&ci->gadget); - for (i = 0; i < udc->hw_ep_max; i++) { - struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i]; + for (i = 0; i < ci->hw_ep_max; i++) { + struct ci13xxx_ep *mEp = &ci->ci13xxx_ep[i]; - dma_pool_free(udc->qh_pool, mEp->qh.ptr, mEp->qh.dma); + dma_pool_free(ci->qh_pool, mEp->qh.ptr, mEp->qh.dma); } - dma_pool_destroy(udc->td_pool); - dma_pool_destroy(udc->qh_pool); + dma_pool_destroy(ci->td_pool); + dma_pool_destroy(ci->qh_pool); - if (!IS_ERR_OR_NULL(udc->transceiver)) { - otg_set_peripheral(udc->transceiver->otg, NULL); - usb_put_phy(udc->transceiver); + if (!IS_ERR_OR_NULL(ci->transceiver)) { + otg_set_peripheral(ci->transceiver->otg, NULL); + usb_put_phy(ci->transceiver); } - dbg_remove_files(&udc->gadget.dev); - device_unregister(&udc->gadget.dev); + dbg_remove_files(&ci->gadget.dev); + device_unregister(&ci->gadget.dev); /* my kobject is dynamic, I swear! */ - memset(&udc->gadget, 0, sizeof(udc->gadget)); + memset(&ci->gadget, 0, sizeof(ci->gadget)); } /** diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index d4cf970656fb..d2f7e494f5c0 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -19,7 +19,7 @@ struct ci13xxx_platform_data { #define CI13XXX_CONTROLLER_RESET_EVENT 0 #define CI13XXX_CONTROLLER_STOPPED_EVENT 1 - void (*notify_event) (struct ci13xxx *udc, unsigned event); + void (*notify_event) (struct ci13xxx *ci, unsigned event); }; /* Default offset of capability registers */ -- cgit v1.2.3 From cbc6dc2af39e1395564445fd71cfcc1c70a96277 Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Sat, 7 Jul 2012 22:56:41 +0800 Subject: USB: Chipidea: add unified ci13xxx_{add,remove}_device for platform drivers Platform drivers do the similar things to add/remove ci13xxx device, so create a unified one. Signed-off-by: Richard Zhao Reviewed-by: Felipe Balbi Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci13xxx_msm.c | 34 +++++++----------------------- drivers/usb/chipidea/ci13xxx_pci.c | 34 ++++++------------------------ drivers/usb/chipidea/core.c | 43 ++++++++++++++++++++++++++++++++++++++ include/linux/usb/chipidea.h | 7 +++++++ 4 files changed, 63 insertions(+), 55 deletions(-) (limited to 'include') diff --git a/drivers/usb/chipidea/ci13xxx_msm.c b/drivers/usb/chipidea/ci13xxx_msm.c index 5a2fe5f9b6c3..b01feb3be92e 100644 --- a/drivers/usb/chipidea/ci13xxx_msm.c +++ b/drivers/usb/chipidea/ci13xxx_msm.c @@ -58,43 +58,23 @@ static struct ci13xxx_platform_data ci13xxx_msm_platdata = { static int __devinit ci13xxx_msm_probe(struct platform_device *pdev) { struct platform_device *plat_ci; - int ret; dev_dbg(&pdev->dev, "ci13xxx_msm_probe\n"); - plat_ci = platform_device_alloc("ci_hdrc", -1); - if (!plat_ci) { - dev_err(&pdev->dev, "can't allocate ci_hdrc platform device\n"); - return -ENOMEM; + plat_ci = ci13xxx_add_device(&pdev->dev, + pdev->resource, pdev->num_resources, + &ci13xxx_msm_platdata); + if (IS_ERR(plat_ci)) { + dev_err(&pdev->dev, "ci13xxx_add_device failed!\n"); + return PTR_ERR(plat_ci); } - ret = platform_device_add_resources(plat_ci, pdev->resource, - pdev->num_resources); - if (ret) { - dev_err(&pdev->dev, "can't add resources to platform device\n"); - goto put_platform; - } - - ret = platform_device_add_data(plat_ci, &ci13xxx_msm_platdata, - sizeof(ci13xxx_msm_platdata)); - if (ret) - goto put_platform; - - ret = platform_device_add(plat_ci); - if (ret) - goto put_platform; - platform_set_drvdata(pdev, plat_ci); pm_runtime_no_callbacks(&pdev->dev); pm_runtime_enable(&pdev->dev); return 0; - -put_platform: - platform_device_put(plat_ci); - - return ret; } static int __devexit ci13xxx_msm_remove(struct platform_device *pdev) @@ -102,7 +82,7 @@ static int __devexit ci13xxx_msm_remove(struct platform_device *pdev) struct platform_device *plat_ci = platform_get_drvdata(pdev); pm_runtime_disable(&pdev->dev); - platform_device_unregister(plat_ci); + ci13xxx_remove_device(plat_ci); return 0; } diff --git a/drivers/usb/chipidea/ci13xxx_pci.c b/drivers/usb/chipidea/ci13xxx_pci.c index cdcac3a0e94d..918e14971f2b 100644 --- a/drivers/usb/chipidea/ci13xxx_pci.c +++ b/drivers/usb/chipidea/ci13xxx_pci.c @@ -75,13 +75,6 @@ static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev, pci_set_master(pdev); pci_try_set_mwi(pdev); - plat_ci = platform_device_alloc("ci_hdrc", -1); - if (!plat_ci) { - dev_err(&pdev->dev, "can't allocate ci_hdrc platform device\n"); - retval = -ENOMEM; - goto disable_device; - } - memset(res, 0, sizeof(res)); res[0].start = pci_resource_start(pdev, 0); res[0].end = pci_resource_end(pdev, 0); @@ -89,32 +82,17 @@ static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev, res[1].start = pdev->irq; res[1].flags = IORESOURCE_IRQ; - retval = platform_device_add_resources(plat_ci, res, nres); - if (retval) { - dev_err(&pdev->dev, "can't add resources to platform device\n"); - goto put_platform; + plat_ci = ci13xxx_add_device(&pdev->dev, res, nres, platdata); + if (IS_ERR(plat_ci)) { + dev_err(&pdev->dev, "ci13xxx_add_device failed!\n"); + retval = PTR_ERR(plat_ci); + goto disable_device; } - retval = platform_device_add_data(plat_ci, platdata, sizeof(*platdata)); - if (retval) - goto put_platform; - - dma_set_coherent_mask(&plat_ci->dev, pdev->dev.coherent_dma_mask); - plat_ci->dev.dma_mask = pdev->dev.dma_mask; - plat_ci->dev.dma_parms = pdev->dev.dma_parms; - plat_ci->dev.parent = &pdev->dev; - pci_set_drvdata(pdev, plat_ci); - retval = platform_device_add(plat_ci); - if (retval) - goto put_platform; - return 0; - put_platform: - pci_set_drvdata(pdev, NULL); - platform_device_put(plat_ci); disable_device: pci_disable_device(pdev); done: @@ -133,7 +111,7 @@ static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev) { struct platform_device *plat_ci = pci_get_drvdata(pdev); - platform_device_unregister(plat_ci); + ci13xxx_remove_device(plat_ci); pci_set_drvdata(pdev, NULL); pci_disable_device(pdev); } diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 9a883bd5e113..8b9d06fd0325 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -332,6 +332,49 @@ static irqreturn_t ci_irq(int irq, void *data) return ci->role == CI_ROLE_END ? ret : ci_role(ci)->irq(ci); } +struct platform_device *ci13xxx_add_device(struct device *dev, + struct resource *res, int nres, + struct ci13xxx_platform_data *platdata) +{ + struct platform_device *pdev; + int ret; + + /* FIXME: find a way to choose id */ + pdev = platform_device_alloc("ci_hdrc", -1); + if (!pdev) + return ERR_PTR(-ENOMEM); + + pdev->dev.parent = dev; + pdev->dev.dma_mask = dev->dma_mask; + pdev->dev.dma_parms = dev->dma_parms; + dma_set_coherent_mask(&pdev->dev, dev->coherent_dma_mask); + + ret = platform_device_add_resources(pdev, res, nres); + if (ret) + goto err; + + ret = platform_device_add_data(pdev, platdata, sizeof(*platdata)); + if (ret) + goto err; + + ret = platform_device_add(pdev); + if (ret) + goto err; + + return pdev; + +err: + platform_device_put(pdev); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(ci13xxx_add_device); + +void ci13xxx_remove_device(struct platform_device *pdev) +{ + platform_device_unregister(pdev); +} +EXPORT_SYMBOL_GPL(ci13xxx_remove_device); + static int __devinit ci_hdrc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index d2f7e494f5c0..be078f0bfde2 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -25,4 +25,11 @@ struct ci13xxx_platform_data { /* Default offset of capability registers */ #define DEF_CAPOFFSET 0x100 +/* Add ci13xxx device */ +struct platform_device *ci13xxx_add_device(struct device *dev, + struct resource *res, int nres, + struct ci13xxx_platform_data *platdata); +/* Remove ci13xxx device */ +void ci13xxx_remove_device(struct platform_device *pdev); + #endif -- cgit v1.2.3 From 526c51382d59144a3704970ab111d668e604a10f Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Sat, 7 Jul 2012 22:56:44 +0800 Subject: usb: otg: add notify_connect/notify_disconnect callback This let usb phy driver has a chance to change hw settings when connect status change. Signed-off-by: Richard Zhao Acked-by: Felipe Balbi Tested-by: Subodh Nijsure Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/otg.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'include') diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 0cb2ec2e50c0..45824be0a2f9 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -128,6 +128,9 @@ struct usb_phy { int (*set_suspend)(struct usb_phy *x, int suspend); + /* notify phy connect status change */ + int (*notify_connect)(struct usb_phy *x, int port); + int (*notify_disconnect)(struct usb_phy *x, int port); }; @@ -276,6 +279,24 @@ usb_phy_set_suspend(struct usb_phy *x, int suspend) return 0; } +static inline int +usb_phy_notify_connect(struct usb_phy *x, int port) +{ + if (x->notify_connect) + return x->notify_connect(x, port); + else + return 0; +} + +static inline int +usb_phy_notify_disconnect(struct usb_phy *x, int port) +{ + if (x->notify_disconnect) + return x->notify_disconnect(x, port); + else + return 0; +} + static inline int otg_start_srp(struct usb_otg *otg) { -- cgit v1.2.3 From a2c3d6902f6f9916b5376c44baa8c1d08bf92a27 Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Sat, 7 Jul 2012 22:56:46 +0800 Subject: usb: chipidea: permit driver bindings pass phy pointer Sometimes, the driver bindings may know what phy they use. For example, when using device tree, the usb controller may have a phandler pointing to usb phy. Signed-off-by: Richard Zhao Reviewed-by: Marek Vasut Acked-by: Felipe Balbi Tested-by: Subodh Nijsure Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci.h | 2 ++ drivers/usb/chipidea/core.c | 4 ++++ drivers/usb/chipidea/host.c | 1 + drivers/usb/chipidea/udc.c | 11 +++++++---- include/linux/usb/chipidea.h | 3 +++ 5 files changed, 17 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 9655e3569d4c..d738603a2757 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -160,6 +160,8 @@ struct ci13xxx { struct ci13xxx_platform_data *platdata; int vbus_active; + /* FIXME: some day, we'll not use global phy */ + bool global_phy; struct usb_phy *transceiver; struct usb_hcd *hcd; }; diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 39603d7b7916..1083585fad00 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -419,6 +419,10 @@ static int __devinit ci_hdrc_probe(struct platform_device *pdev) ci->dev = dev; ci->platdata = dev->platform_data; + if (ci->platdata->phy) + ci->transceiver = ci->platdata->phy; + else + ci->global_phy = true; ret = hw_device_init(ci, base); if (ret < 0) { diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index 4a4fdb8c65f8..ebff9f4f56ec 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -117,6 +117,7 @@ static int host_start(struct ci13xxx *ci) hcd->has_tt = 1; hcd->power_budget = ci->platdata->power_budget; + hcd->phy = ci->transceiver; ehci = hcd_to_ehci(hcd); ehci->caps = ci->hw_bank.cap; diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index ba8284e2a237..c7a032a4f0c5 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1685,7 +1685,8 @@ static int udc_start(struct ci13xxx *ci) ci->gadget.ep0 = &ci->ep0in->ep; - ci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); + if (ci->global_phy) + ci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (ci->platdata->flags & CI13XXX_REQUIRE_TRANSCEIVER) { if (ci->transceiver == NULL) { @@ -1729,7 +1730,8 @@ static int udc_start(struct ci13xxx *ci) remove_trans: if (!IS_ERR_OR_NULL(ci->transceiver)) { otg_set_peripheral(ci->transceiver->otg, &ci->gadget); - usb_put_phy(ci->transceiver); + if (ci->global_phy) + usb_put_phy(ci->transceiver); } dev_err(dev, "error = %i\n", retval); @@ -1738,7 +1740,7 @@ remove_dbg: unreg_device: device_unregister(&ci->gadget.dev); put_transceiver: - if (!IS_ERR_OR_NULL(ci->transceiver)) + if (!IS_ERR_OR_NULL(ci->transceiver) && ci->global_phy) usb_put_phy(ci->transceiver); free_pools: dma_pool_destroy(ci->td_pool); @@ -1772,7 +1774,8 @@ static void udc_stop(struct ci13xxx *ci) if (!IS_ERR_OR_NULL(ci->transceiver)) { otg_set_peripheral(ci->transceiver->otg, NULL); - usb_put_phy(ci->transceiver); + if (ci->global_phy) + usb_put_phy(ci->transceiver); } dbg_remove_files(&ci->gadget.dev); device_unregister(&ci->gadget.dev); diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index be078f0bfde2..544825dde823 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -5,12 +5,15 @@ #ifndef __LINUX_USB_CHIPIDEA_H #define __LINUX_USB_CHIPIDEA_H +#include + struct ci13xxx; struct ci13xxx_platform_data { const char *name; /* offset of the capability registers */ uintptr_t capoffset; unsigned power_budget; + struct usb_phy *phy; unsigned long flags; #define CI13XXX_REGS_SHARED BIT(0) #define CI13XXX_REQUIRE_TRANSCEIVER BIT(1) -- cgit v1.2.3 From 939546d1a9f47ed169554c711e1e05965b84ffe1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 9 Jul 2012 10:00:00 +0100 Subject: iio: Add callback to check whether a scan mask is valid This is useful for cases where the number of valid scan masks grows exponentially, but it is rather easy to check whether a mask is valid or not programmatically. An example of such a case is a device with multiple ADCs where each ADC has a upstream MUX, which allows to select from a number of physical channels. +-------+ +-------+ | | | | --- Channel 1 | ADC 1 |---| MUX 1 | --- ... | | | | --- Channel M +-------+ +-------+ . . . . . . . . . +-------+ +-------+ | | | | --- Channel M * N + 1 | ADC N |---| MUX N | --- ... | | | | --- Channel M * N + M +-------+ +-------+ The number of necessary scan masks for this case is (M+1)**N - 1, on the other hand it is easy to check whether subsets for each ADC of the scanmask have only one bit set. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 27 ++++++++++++++++++++------- include/linux/iio/iio.h | 4 ++++ 2 files changed, 24 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 3d8d187eef2a..cc5db36fb75a 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -570,6 +570,15 @@ int iio_sw_buffer_preenable(struct iio_dev *indio_dev) } EXPORT_SYMBOL(iio_sw_buffer_preenable); +static bool iio_validate_scan_mask(struct iio_dev *indio_dev, + const unsigned long *mask) +{ + if (!indio_dev->setup_ops->validate_scan_mask) + return true; + + return indio_dev->setup_ops->validate_scan_mask(indio_dev, mask); +} + /** * iio_scan_mask_set() - set particular bit in the scan mask * @buffer: the buffer whose scan mask we are interested in @@ -589,27 +598,31 @@ int iio_scan_mask_set(struct iio_dev *indio_dev, return -ENOMEM; if (!indio_dev->masklength) { WARN_ON("trying to set scanmask prior to registering buffer\n"); - kfree(trialmask); - return -EINVAL; + goto err_invalid_mask; } bitmap_copy(trialmask, buffer->scan_mask, indio_dev->masklength); set_bit(bit, trialmask); + if (!iio_validate_scan_mask(indio_dev, trialmask)) + goto err_invalid_mask; + if (indio_dev->available_scan_masks) { mask = iio_scan_mask_match(indio_dev->available_scan_masks, indio_dev->masklength, trialmask); - if (!mask) { - kfree(trialmask); - return -EINVAL; - } + if (!mask) + goto err_invalid_mask; } bitmap_copy(buffer->scan_mask, trialmask, indio_dev->masklength); kfree(trialmask); return 0; -}; + +err_invalid_mask: + kfree(trialmask); + return -EINVAL; +} EXPORT_SYMBOL_GPL(iio_scan_mask_set); int iio_scan_mask_query(struct iio_dev *indio_dev, diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 2afbb6f01afc..be82936c4089 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -363,12 +363,16 @@ struct iio_info { * @predisable: [DRIVER] function to run prior to marking buffer * disabled * @postdisable: [DRIVER] function to run after marking buffer disabled + * @validate_scan_mask: [DRIVER] function callback to check whether a given + * scan mask is valid for the device. */ struct iio_buffer_setup_ops { int (*preenable)(struct iio_dev *); int (*postenable)(struct iio_dev *); int (*predisable)(struct iio_dev *); int (*postdisable)(struct iio_dev *); + bool (*validate_scan_mask)(struct iio_dev *indio_dev, + const unsigned long *scan_mask); }; /** -- cgit v1.2.3 From 81636632057cc1bece2531220dd5803036f95ea9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 9 Jul 2012 10:00:00 +0100 Subject: iio: Introduce iio_validate_scan_mask_onehot Add a helper function for validating a scan mask for devices where exactly one channel must be selected during sampling. This is a common case among devices which have scan mask restrictions so it makes sense to provide this function in the core. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 16 ++++++++++++++++ include/linux/iio/buffer.h | 3 +++ 2 files changed, 19 insertions(+) (limited to 'include') diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index cc5db36fb75a..8c1dc9a683fb 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -570,6 +570,22 @@ int iio_sw_buffer_preenable(struct iio_dev *indio_dev) } EXPORT_SYMBOL(iio_sw_buffer_preenable); +/** + * iio_validate_scan_mask_onehot() - Validates that exactly one channel is selected + * @indio_dev: the iio device + * @mask: scan mask to be checked + * + * Return true if exactly one bit is set in the scan mask, false otherwise. It + * can be used for devices where only one channel can be active for sampling at + * a time. + */ +bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev, + const unsigned long *mask) +{ + return bitmap_weight(mask, indio_dev->masklength) == 1; +} +EXPORT_SYMBOL_GPL(iio_validate_scan_mask_onehot); + static bool iio_validate_scan_mask(struct iio_dev *indio_dev, const unsigned long *mask) { diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h index 2a2b6b4d8d05..8ba516fc2ec6 100644 --- a/include/linux/iio/buffer.h +++ b/include/linux/iio/buffer.h @@ -177,6 +177,9 @@ ssize_t iio_buffer_show_enable(struct device *dev, int iio_sw_buffer_preenable(struct iio_dev *indio_dev); +bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev, + const unsigned long *mask); + #else /* CONFIG_IIO_BUFFER */ static inline int iio_buffer_register(struct iio_dev *indio_dev, -- cgit v1.2.3 From 2b28ae1912e5ce5bb0527e352ae6ff04e76183d1 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 9 Jul 2012 13:38:57 -0600 Subject: PCI: reimplement P2P bridge 1K I/O windows (Intel P64H2) 9d265124d051 and 15a260d53f7c added quirks for P2P bridges that support I/O windows that start/end at 1K boundaries, not just the 4K boundaries defined by the PCI spec. For details, see the IOBL_ADR register and the EN1K bit in the CNF register in the Intel 82870P2 (P64H2). These quirks complicate the code that reads P2P bridge windows (pci_read_bridge_io() and pci_cfg_fake_ranges()) because the bridge I/O resource is updated in the HEADER quirk, in pci_read_bridge_io(), in pci_setup_bridge(), and again in the FINAL quirk. This is confusing and makes it impossible to reassign the bridge windows after FINAL quirks are run. This patch adds support for 1K windows in the generic paths, so the HEADER quirk only has to enable this support. The FINAL quirk, which used to undo damage done by pci_setup_bridge(), is no longer needed. This removes "if (!res->start) res->start = ..." from pci_read_bridge_io(); that was part of 9d265124d051 to avoid overwriting the resource filled in by the quirk. Since pci_read_bridge_io() itself now knows about granularity, the quirk no longer updates the resource and this test is no longer needed. Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 25 ++++++++++++++----------- drivers/pci/quirks.c | 39 +-------------------------------------- drivers/pci/setup-bus.c | 11 +++++++++-- include/linux/pci.h | 1 + include/linux/pci_regs.h | 3 ++- 5 files changed, 27 insertions(+), 52 deletions(-) (limited to 'include') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 9c5d2a992999..ef24cf765b2f 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -269,15 +269,23 @@ static void __devinit pci_read_bridge_io(struct pci_bus *child) { struct pci_dev *dev = child->self; u8 io_base_lo, io_limit_lo; - unsigned long base, limit; + unsigned long io_mask, io_granularity, base, limit; struct pci_bus_region region; - struct resource *res, res2; + struct resource *res; + + io_mask = PCI_IO_RANGE_MASK; + io_granularity = 0x1000; + if (dev->io_window_1k) { + /* Support 1K I/O space granularity */ + io_mask = PCI_IO_1K_RANGE_MASK; + io_granularity = 0x400; + } res = child->resource[0]; pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo); pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo); - base = (io_base_lo & PCI_IO_RANGE_MASK) << 8; - limit = (io_limit_lo & PCI_IO_RANGE_MASK) << 8; + base = (io_base_lo & io_mask) << 8; + limit = (io_limit_lo & io_mask) << 8; if ((io_base_lo & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) { u16 io_base_hi, io_limit_hi; @@ -289,14 +297,9 @@ static void __devinit pci_read_bridge_io(struct pci_bus *child) if (base <= limit) { res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO; - res2.flags = res->flags; region.start = base; - region.end = limit + 0xfff; - pcibios_bus_to_resource(dev, &res2, ®ion); - if (!res->start) - res->start = res2.start; - if (!res->end) - res->end = res2.end; + region.end = limit + io_granularity - 1; + pcibios_bus_to_resource(dev, res, ®ion); dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res); } } diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 2a7521677541..356846bd7ffb 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1938,53 +1938,16 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C810, fixup_rev1 static void __devinit quirk_p64h2_1k_io(struct pci_dev *dev) { u16 en1k; - u8 io_base_lo, io_limit_lo; - unsigned long base, limit; - struct resource *res = dev->resource + PCI_BRIDGE_RESOURCES; pci_read_config_word(dev, 0x40, &en1k); if (en1k & 0x200) { dev_info(&dev->dev, "Enable I/O Space to 1KB granularity\n"); - - pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo); - pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo); - base = (io_base_lo & (PCI_IO_RANGE_MASK | 0x0c)) << 8; - limit = (io_limit_lo & (PCI_IO_RANGE_MASK | 0x0c)) << 8; - - if (base <= limit) { - res->start = base; - res->end = limit + 0x3ff; - } + dev->io_window_1k = 1; } } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1460, quirk_p64h2_1k_io); -/* Fix the IOBL_ADR for 1k I/O space granularity on the Intel P64H2 - * The IOBL_ADR gets re-written to 4k boundaries in pci_setup_bridge() - * in drivers/pci/setup-bus.c - */ -static void __devinit quirk_p64h2_1k_io_fix_iobl(struct pci_dev *dev) -{ - u16 en1k, iobl_adr, iobl_adr_1k; - struct resource *res = dev->resource + PCI_BRIDGE_RESOURCES; - - pci_read_config_word(dev, 0x40, &en1k); - - if (en1k & 0x200) { - pci_read_config_word(dev, PCI_IO_BASE, &iobl_adr); - - iobl_adr_1k = iobl_adr | (res->start >> 8) | (res->end & 0xfc00); - - if (iobl_adr != iobl_adr_1k) { - dev_info(&dev->dev, "Fixing P64H2 IOBL_ADR from 0x%x to 0x%x for 1KB granularity\n", - iobl_adr,iobl_adr_1k); - pci_write_config_word(dev, PCI_IO_BASE, iobl_adr_1k); - } - } -} -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1460, quirk_p64h2_1k_io_fix_iobl); - /* Under some circumstances, AER is not linked with extended capabilities. * Force it to be linked by setting the corresponding control bit in the * config space. diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 8fa2d4be88de..dad5425f1f09 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -469,16 +469,23 @@ static void pci_setup_bridge_io(struct pci_bus *bus) struct pci_dev *bridge = bus->self; struct resource *res; struct pci_bus_region region; + unsigned long io_mask; + u8 io_base_lo, io_limit_lo; u32 l, io_upper16; + io_mask = PCI_IO_RANGE_MASK; + if (bridge->io_window_1k) + io_mask = PCI_IO_1K_RANGE_MASK; + /* Set up the top and bottom of the PCI I/O segment for this bus. */ res = bus->resource[0]; pcibios_resource_to_bus(bridge, ®ion, res); if (res->flags & IORESOURCE_IO) { pci_read_config_dword(bridge, PCI_IO_BASE, &l); l &= 0xffff0000; - l |= (region.start >> 8) & 0x00f0; - l |= region.end & 0xf000; + io_base_lo = (region.start >> 8) & io_mask; + io_limit_lo = (region.end >> 8) & io_mask; + l |= ((u32) io_limit_lo << 8) | io_base_lo; /* Set up upper 16 bits of I/O base/limit. */ io_upper16 = (region.end & 0xffff0000) | (region.start >> 16); dev_info(&bridge->dev, " bridge window %pR\n", res); diff --git a/include/linux/pci.h b/include/linux/pci.h index d8c379dba6ad..89b46fd245c6 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -324,6 +324,7 @@ struct pci_dev { unsigned int is_hotplug_bridge:1; unsigned int __aer_firmware_first_valid:1; unsigned int __aer_firmware_first:1; + unsigned int io_window_1k:1; /* Intel P2P bridge 1K I/O windows */ pci_dev_flags_t dev_flags; atomic_t enable_cnt; /* pci_enable_device has been called */ diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index 4b608f543412..88c9ea56e252 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -125,7 +125,8 @@ #define PCI_IO_RANGE_TYPE_MASK 0x0fUL /* I/O bridging type */ #define PCI_IO_RANGE_TYPE_16 0x00 #define PCI_IO_RANGE_TYPE_32 0x01 -#define PCI_IO_RANGE_MASK (~0x0fUL) +#define PCI_IO_RANGE_MASK (~0x0fUL) /* Standard 4K I/O windows */ +#define PCI_IO_1K_RANGE_MASK (~0x03UL) /* Intel 1K I/O windows */ #define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */ #define PCI_MEMORY_BASE 0x20 /* Memory range behind */ #define PCI_MEMORY_LIMIT 0x22 -- cgit v1.2.3 From 2a51da04fef56ec83f790bf0746e90fe40215a92 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 9 Jul 2012 19:33:14 +0100 Subject: mfd: Add support for multiple arizona PDM speaker outputs The registers have stride 2 so we can write the loop properly now. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/arizona-core.c | 5 ++--- include/linux/mfd/arizona/pdata.h | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index ffa011f4677e..b35680dcd8c1 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -462,18 +462,17 @@ int __devinit arizona_dev_init(struct arizona *arizona) ARIZONA_OUT1_MONO, val); } - BUILD_BUG_ON(ARIZONA_MAX_PDM_SPK > 1); for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) { if (arizona->pdata.spk_mute[i]) regmap_update_bits(arizona->regmap, - ARIZONA_PDM_SPK1_CTRL_1, + ARIZONA_PDM_SPK1_CTRL_1 + (i * 2), ARIZONA_SPK1_MUTE_ENDIAN_MASK | ARIZONA_SPK1_MUTE_SEQ1_MASK, arizona->pdata.spk_mute[i]); if (arizona->pdata.spk_fmt[i]) regmap_update_bits(arizona->regmap, - ARIZONA_PDM_SPK1_CTRL_2, + ARIZONA_PDM_SPK1_CTRL_2 + (i * 2), ARIZONA_SPK1_FMT_MASK, arizona->pdata.spk_fmt[i]); } diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index fa2cb9885d62..68ff91aa3888 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -62,7 +62,7 @@ #define ARIZONA_MAX_OUTPUT 5 -#define ARIZONA_MAX_PDM_SPK 1 +#define ARIZONA_MAX_PDM_SPK 2 struct regulator_init_data; -- cgit v1.2.3 From 1faedca9c7bfd3055204b9d10017ce77ad03fc72 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 9 Jul 2012 19:33:15 +0100 Subject: mfd: Add even more arizona register definitions A few more registers used on newer devices. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- include/linux/mfd/arizona/registers.h | 211 +++++++++++++++++++++++++++++++++- 1 file changed, 210 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index 8f49106d7bda..7671a287dfee 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -145,7 +145,7 @@ #define ARIZONA_IN3R_CONTROL 0x324 #define ARIZONA_ADC_DIGITAL_VOLUME_3R 0x325 #define ARIZONA_DMIC3R_CONTROL 0x326 -#define ARIZONA_IN4_CONTROL 0x328 +#define ARIZONA_IN4L_CONTROL 0x328 #define ARIZONA_ADC_DIGITAL_VOLUME_4L 0x329 #define ARIZONA_DMIC4L_CONTROL 0x32A #define ARIZONA_ADC_DIGITAL_VOLUME_4R 0x32D @@ -2129,6 +2129,14 @@ /* * R768 (0x300) - Input Enables */ +#define ARIZONA_IN4L_ENA 0x0080 /* IN4L_ENA */ +#define ARIZONA_IN4L_ENA_MASK 0x0080 /* IN4L_ENA */ +#define ARIZONA_IN4L_ENA_SHIFT 7 /* IN4L_ENA */ +#define ARIZONA_IN4L_ENA_WIDTH 1 /* IN4L_ENA */ +#define ARIZONA_IN4R_ENA 0x0040 /* IN4R_ENA */ +#define ARIZONA_IN4R_ENA_MASK 0x0040 /* IN4R_ENA */ +#define ARIZONA_IN4R_ENA_SHIFT 6 /* IN4R_ENA */ +#define ARIZONA_IN4R_ENA_WIDTH 1 /* IN4R_ENA */ #define ARIZONA_IN3L_ENA 0x0020 /* IN3L_ENA */ #define ARIZONA_IN3L_ENA_MASK 0x0020 /* IN3L_ENA */ #define ARIZONA_IN3L_ENA_SHIFT 5 /* IN3L_ENA */ @@ -2372,9 +2380,71 @@ #define ARIZONA_IN3_DMICR_DLY_SHIFT 0 /* IN3_DMICR_DLY - [5:0] */ #define ARIZONA_IN3_DMICR_DLY_WIDTH 6 /* IN3_DMICR_DLY - [5:0] */ +/* + * R808 (0x328) - IN4 Control + */ +#define ARIZONA_IN4_OSR_MASK 0x6000 /* IN4_OSR - [14:13] */ +#define ARIZONA_IN4_OSR_SHIFT 13 /* IN4_OSR - [14:13] */ +#define ARIZONA_IN4_OSR_WIDTH 2 /* IN4_OSR - [14:13] */ +#define ARIZONA_IN4_DMIC_SUP_MASK 0x1800 /* IN4_DMIC_SUP - [12:11] */ +#define ARIZONA_IN4_DMIC_SUP_SHIFT 11 /* IN4_DMIC_SUP - [12:11] */ +#define ARIZONA_IN4_DMIC_SUP_WIDTH 2 /* IN4_DMIC_SUP - [12:11] */ + +/* + * R809 (0x329) - ADC Digital Volume 4L + */ +#define ARIZONA_IN_VU 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_MASK 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_SHIFT 9 /* IN_VU */ +#define ARIZONA_IN_VU_WIDTH 1 /* IN_VU */ +#define ARIZONA_IN4L_MUTE 0x0100 /* IN4L_MUTE */ +#define ARIZONA_IN4L_MUTE_MASK 0x0100 /* IN4L_MUTE */ +#define ARIZONA_IN4L_MUTE_SHIFT 8 /* IN4L_MUTE */ +#define ARIZONA_IN4L_MUTE_WIDTH 1 /* IN4L_MUTE */ +#define ARIZONA_IN4L_DIG_VOL_MASK 0x00FF /* IN4L_DIG_VOL - [7:0] */ +#define ARIZONA_IN4L_DIG_VOL_SHIFT 0 /* IN4L_DIG_VOL - [7:0] */ +#define ARIZONA_IN4L_DIG_VOL_WIDTH 8 /* IN4L_DIG_VOL - [7:0] */ + +/* + * R810 (0x32A) - DMIC4L Control + */ +#define ARIZONA_IN4L_DMIC_DLY_MASK 0x003F /* IN4L_DMIC_DLY - [5:0] */ +#define ARIZONA_IN4L_DMIC_DLY_SHIFT 0 /* IN4L_DMIC_DLY - [5:0] */ +#define ARIZONA_IN4L_DMIC_DLY_WIDTH 6 /* IN4L_DMIC_DLY - [5:0] */ + +/* + * R813 (0x32D) - ADC Digital Volume 4R + */ +#define ARIZONA_IN_VU 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_MASK 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_SHIFT 9 /* IN_VU */ +#define ARIZONA_IN_VU_WIDTH 1 /* IN_VU */ +#define ARIZONA_IN4R_MUTE 0x0100 /* IN4R_MUTE */ +#define ARIZONA_IN4R_MUTE_MASK 0x0100 /* IN4R_MUTE */ +#define ARIZONA_IN4R_MUTE_SHIFT 8 /* IN4R_MUTE */ +#define ARIZONA_IN4R_MUTE_WIDTH 1 /* IN4R_MUTE */ +#define ARIZONA_IN4R_DIG_VOL_MASK 0x00FF /* IN4R_DIG_VOL - [7:0] */ +#define ARIZONA_IN4R_DIG_VOL_SHIFT 0 /* IN4R_DIG_VOL - [7:0] */ +#define ARIZONA_IN4R_DIG_VOL_WIDTH 8 /* IN4R_DIG_VOL - [7:0] */ + +/* + * R814 (0x32E) - DMIC4R Control + */ +#define ARIZONA_IN4R_DMIC_DLY_MASK 0x003F /* IN4R_DMIC_DLY - [5:0] */ +#define ARIZONA_IN4R_DMIC_DLY_SHIFT 0 /* IN4R_DMIC_DLY - [5:0] */ +#define ARIZONA_IN4R_DMIC_DLY_WIDTH 6 /* IN4R_DMIC_DLY - [5:0] */ + /* * R1024 (0x400) - Output Enables 1 */ +#define ARIZONA_OUT6L_ENA 0x0800 /* OUT6L_ENA */ +#define ARIZONA_OUT6L_ENA_MASK 0x0800 /* OUT6L_ENA */ +#define ARIZONA_OUT6L_ENA_SHIFT 11 /* OUT6L_ENA */ +#define ARIZONA_OUT6L_ENA_WIDTH 1 /* OUT6L_ENA */ +#define ARIZONA_OUT6R_ENA 0x0400 /* OUT6R_ENA */ +#define ARIZONA_OUT6R_ENA_MASK 0x0400 /* OUT6R_ENA */ +#define ARIZONA_OUT6R_ENA_SHIFT 10 /* OUT6R_ENA */ +#define ARIZONA_OUT6R_ENA_WIDTH 1 /* OUT6R_ENA */ #define ARIZONA_OUT5L_ENA 0x0200 /* OUT5L_ENA */ #define ARIZONA_OUT5L_ENA_MASK 0x0200 /* OUT5L_ENA */ #define ARIZONA_OUT5L_ENA_SHIFT 9 /* OUT5L_ENA */ @@ -2876,6 +2946,82 @@ #define ARIZONA_OUT5R_NGATE_SRC_SHIFT 0 /* OUT5R_NGATE_SRC - [11:0] */ #define ARIZONA_OUT5R_NGATE_SRC_WIDTH 12 /* OUT5R_NGATE_SRC - [11:0] */ +/* + * R1080 (0x438) - Output Path Config 6L + */ +#define ARIZONA_OUT6_OSR 0x2000 /* OUT6_OSR */ +#define ARIZONA_OUT6_OSR_MASK 0x2000 /* OUT6_OSR */ +#define ARIZONA_OUT6_OSR_SHIFT 13 /* OUT6_OSR */ +#define ARIZONA_OUT6_OSR_WIDTH 1 /* OUT6_OSR */ +#define ARIZONA_OUT6L_ANC_SRC_MASK 0x0C00 /* OUT6L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT6L_ANC_SRC_SHIFT 10 /* OUT6L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT6L_ANC_SRC_WIDTH 2 /* OUT6L_ANC_SRC - [11:10] */ + +/* + * R1081 (0x439) - DAC Digital Volume 6L + */ +#define ARIZONA_OUT_VU 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_SHIFT 9 /* OUT_VU */ +#define ARIZONA_OUT_VU_WIDTH 1 /* OUT_VU */ +#define ARIZONA_OUT6L_MUTE 0x0100 /* OUT6L_MUTE */ +#define ARIZONA_OUT6L_MUTE_MASK 0x0100 /* OUT6L_MUTE */ +#define ARIZONA_OUT6L_MUTE_SHIFT 8 /* OUT6L_MUTE */ +#define ARIZONA_OUT6L_MUTE_WIDTH 1 /* OUT6L_MUTE */ +#define ARIZONA_OUT6L_VOL_MASK 0x00FF /* OUT6L_VOL - [7:0] */ +#define ARIZONA_OUT6L_VOL_SHIFT 0 /* OUT6L_VOL - [7:0] */ +#define ARIZONA_OUT6L_VOL_WIDTH 8 /* OUT6L_VOL - [7:0] */ + +/* + * R1082 (0x43A) - DAC Volume Limit 6L + */ +#define ARIZONA_OUT6L_VOL_LIM_MASK 0x00FF /* OUT6L_VOL_LIM - [7:0] */ +#define ARIZONA_OUT6L_VOL_LIM_SHIFT 0 /* OUT6L_VOL_LIM - [7:0] */ +#define ARIZONA_OUT6L_VOL_LIM_WIDTH 8 /* OUT6L_VOL_LIM - [7:0] */ + +/* + * R1083 (0x43B) - Noise Gate Select 6L + */ +#define ARIZONA_OUT6L_NGATE_SRC_MASK 0x0FFF /* OUT6L_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT6L_NGATE_SRC_SHIFT 0 /* OUT6L_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT6L_NGATE_SRC_WIDTH 12 /* OUT6L_NGATE_SRC - [11:0] */ + +/* + * R1084 (0x43C) - Output Path Config 6R + */ +#define ARIZONA_OUT6R_ANC_SRC_MASK 0x0C00 /* OUT6R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT6R_ANC_SRC_SHIFT 10 /* OUT6R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT6R_ANC_SRC_WIDTH 2 /* OUT6R_ANC_SRC - [11:10] */ + +/* + * R1085 (0x43D) - DAC Digital Volume 6R + */ +#define ARIZONA_OUT_VU 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_SHIFT 9 /* OUT_VU */ +#define ARIZONA_OUT_VU_WIDTH 1 /* OUT_VU */ +#define ARIZONA_OUT6R_MUTE 0x0100 /* OUT6R_MUTE */ +#define ARIZONA_OUT6R_MUTE_MASK 0x0100 /* OUT6R_MUTE */ +#define ARIZONA_OUT6R_MUTE_SHIFT 8 /* OUT6R_MUTE */ +#define ARIZONA_OUT6R_MUTE_WIDTH 1 /* OUT6R_MUTE */ +#define ARIZONA_OUT6R_VOL_MASK 0x00FF /* OUT6R_VOL - [7:0] */ +#define ARIZONA_OUT6R_VOL_SHIFT 0 /* OUT6R_VOL - [7:0] */ +#define ARIZONA_OUT6R_VOL_WIDTH 8 /* OUT6R_VOL - [7:0] */ + +/* + * R1086 (0x43E) - DAC Volume Limit 6R + */ +#define ARIZONA_OUT6R_VOL_LIM_MASK 0x00FF /* OUT6R_VOL_LIM - [7:0] */ +#define ARIZONA_OUT6R_VOL_LIM_SHIFT 0 /* OUT6R_VOL_LIM - [7:0] */ +#define ARIZONA_OUT6R_VOL_LIM_WIDTH 8 /* OUT6R_VOL_LIM - [7:0] */ + +/* + * R1087 (0x43F) - Noise Gate Select 6R + */ +#define ARIZONA_OUT6R_NGATE_SRC_MASK 0x0FFF /* OUT6R_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT6R_NGATE_SRC_SHIFT 0 /* OUT6R_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT6R_NGATE_SRC_WIDTH 12 /* OUT6R_NGATE_SRC - [11:0] */ + /* * R1104 (0x450) - DAC AEC Control 1 */ @@ -2932,6 +3078,33 @@ #define ARIZONA_SPK1_FMT_SHIFT 0 /* SPK1_FMT */ #define ARIZONA_SPK1_FMT_WIDTH 1 /* SPK1_FMT */ +/* + * R1170 (0x492) - PDM SPK2 CTRL 1 + */ +#define ARIZONA_SPK2R_MUTE 0x2000 /* SPK2R_MUTE */ +#define ARIZONA_SPK2R_MUTE_MASK 0x2000 /* SPK2R_MUTE */ +#define ARIZONA_SPK2R_MUTE_SHIFT 13 /* SPK2R_MUTE */ +#define ARIZONA_SPK2R_MUTE_WIDTH 1 /* SPK2R_MUTE */ +#define ARIZONA_SPK2L_MUTE 0x1000 /* SPK2L_MUTE */ +#define ARIZONA_SPK2L_MUTE_MASK 0x1000 /* SPK2L_MUTE */ +#define ARIZONA_SPK2L_MUTE_SHIFT 12 /* SPK2L_MUTE */ +#define ARIZONA_SPK2L_MUTE_WIDTH 1 /* SPK2L_MUTE */ +#define ARIZONA_SPK2_MUTE_ENDIAN 0x0100 /* SPK2_MUTE_ENDIAN */ +#define ARIZONA_SPK2_MUTE_ENDIAN_MASK 0x0100 /* SPK2_MUTE_ENDIAN */ +#define ARIZONA_SPK2_MUTE_ENDIAN_SHIFT 8 /* SPK2_MUTE_ENDIAN */ +#define ARIZONA_SPK2_MUTE_ENDIAN_WIDTH 1 /* SPK2_MUTE_ENDIAN */ +#define ARIZONA_SPK2_MUTE_SEQ_MASK 0x00FF /* SPK2_MUTE_SEQ - [7:0] */ +#define ARIZONA_SPK2_MUTE_SEQ_SHIFT 0 /* SPK2_MUTE_SEQ - [7:0] */ +#define ARIZONA_SPK2_MUTE_SEQ_WIDTH 8 /* SPK2_MUTE_SEQ - [7:0] */ + +/* + * R1171 (0x493) - PDM SPK2 CTRL 2 + */ +#define ARIZONA_SPK2_FMT 0x0001 /* SPK2_FMT */ +#define ARIZONA_SPK2_FMT_MASK 0x0001 /* SPK2_FMT */ +#define ARIZONA_SPK2_FMT_SHIFT 0 /* SPK2_FMT */ +#define ARIZONA_SPK2_FMT_WIDTH 1 /* SPK2_FMT */ + /* * R1244 (0x4DC) - DAC comp 1 */ @@ -4028,10 +4201,46 @@ /* * R3329 (0xD01) - Interrupt Status 2 */ +#define ARIZONA_DSP4_RAM_RDY_EINT1 0x0800 /* DSP4_RAM_RDY_EINT1 */ +#define ARIZONA_DSP4_RAM_RDY_EINT1_MASK 0x0800 /* DSP4_RAM_RDY_EINT1 */ +#define ARIZONA_DSP4_RAM_RDY_EINT1_SHIFT 11 /* DSP4_RAM_RDY_EINT1 */ +#define ARIZONA_DSP4_RAM_RDY_EINT1_WIDTH 1 /* DSP4_RAM_RDY_EINT1 */ +#define ARIZONA_DSP3_RAM_RDY_EINT1 0x0400 /* DSP3_RAM_RDY_EINT1 */ +#define ARIZONA_DSP3_RAM_RDY_EINT1_MASK 0x0400 /* DSP3_RAM_RDY_EINT1 */ +#define ARIZONA_DSP3_RAM_RDY_EINT1_SHIFT 10 /* DSP3_RAM_RDY_EINT1 */ +#define ARIZONA_DSP3_RAM_RDY_EINT1_WIDTH 1 /* DSP3_RAM_RDY_EINT1 */ +#define ARIZONA_DSP2_RAM_RDY_EINT1 0x0200 /* DSP2_RAM_RDY_EINT1 */ +#define ARIZONA_DSP2_RAM_RDY_EINT1_MASK 0x0200 /* DSP2_RAM_RDY_EINT1 */ +#define ARIZONA_DSP2_RAM_RDY_EINT1_SHIFT 9 /* DSP2_RAM_RDY_EINT1 */ +#define ARIZONA_DSP2_RAM_RDY_EINT1_WIDTH 1 /* DSP2_RAM_RDY_EINT1 */ #define ARIZONA_DSP1_RAM_RDY_EINT1 0x0100 /* DSP1_RAM_RDY_EINT1 */ #define ARIZONA_DSP1_RAM_RDY_EINT1_MASK 0x0100 /* DSP1_RAM_RDY_EINT1 */ #define ARIZONA_DSP1_RAM_RDY_EINT1_SHIFT 8 /* DSP1_RAM_RDY_EINT1 */ #define ARIZONA_DSP1_RAM_RDY_EINT1_WIDTH 1 /* DSP1_RAM_RDY_EINT1 */ +#define ARIZONA_DSP_IRQ8_EINT1 0x0080 /* DSP_IRQ8_EINT1 */ +#define ARIZONA_DSP_IRQ8_EINT1_MASK 0x0080 /* DSP_IRQ8_EINT1 */ +#define ARIZONA_DSP_IRQ8_EINT1_SHIFT 7 /* DSP_IRQ8_EINT1 */ +#define ARIZONA_DSP_IRQ8_EINT1_WIDTH 1 /* DSP_IRQ8_EINT1 */ +#define ARIZONA_DSP_IRQ7_EINT1 0x0040 /* DSP_IRQ7_EINT1 */ +#define ARIZONA_DSP_IRQ7_EINT1_MASK 0x0040 /* DSP_IRQ7_EINT1 */ +#define ARIZONA_DSP_IRQ7_EINT1_SHIFT 6 /* DSP_IRQ7_EINT1 */ +#define ARIZONA_DSP_IRQ7_EINT1_WIDTH 1 /* DSP_IRQ7_EINT1 */ +#define ARIZONA_DSP_IRQ6_EINT1 0x0020 /* DSP_IRQ6_EINT1 */ +#define ARIZONA_DSP_IRQ6_EINT1_MASK 0x0020 /* DSP_IRQ6_EINT1 */ +#define ARIZONA_DSP_IRQ6_EINT1_SHIFT 5 /* DSP_IRQ6_EINT1 */ +#define ARIZONA_DSP_IRQ6_EINT1_WIDTH 1 /* DSP_IRQ6_EINT1 */ +#define ARIZONA_DSP_IRQ5_EINT1 0x0010 /* DSP_IRQ5_EINT1 */ +#define ARIZONA_DSP_IRQ5_EINT1_MASK 0x0010 /* DSP_IRQ5_EINT1 */ +#define ARIZONA_DSP_IRQ5_EINT1_SHIFT 4 /* DSP_IRQ5_EINT1 */ +#define ARIZONA_DSP_IRQ5_EINT1_WIDTH 1 /* DSP_IRQ5_EINT1 */ +#define ARIZONA_DSP_IRQ4_EINT1 0x0008 /* DSP_IRQ4_EINT1 */ +#define ARIZONA_DSP_IRQ4_EINT1_MASK 0x0008 /* DSP_IRQ4_EINT1 */ +#define ARIZONA_DSP_IRQ4_EINT1_SHIFT 3 /* DSP_IRQ4_EINT1 */ +#define ARIZONA_DSP_IRQ4_EINT1_WIDTH 1 /* DSP_IRQ4_EINT1 */ +#define ARIZONA_DSP_IRQ3_EINT1 0x0004 /* DSP_IRQ3_EINT1 */ +#define ARIZONA_DSP_IRQ3_EINT1_MASK 0x0004 /* DSP_IRQ3_EINT1 */ +#define ARIZONA_DSP_IRQ3_EINT1_SHIFT 2 /* DSP_IRQ3_EINT1 */ +#define ARIZONA_DSP_IRQ3_EINT1_WIDTH 1 /* DSP_IRQ3_EINT1 */ #define ARIZONA_DSP_IRQ2_EINT1 0x0002 /* DSP_IRQ2_EINT1 */ #define ARIZONA_DSP_IRQ2_EINT1_MASK 0x0002 /* DSP_IRQ2_EINT1 */ #define ARIZONA_DSP_IRQ2_EINT1_SHIFT 1 /* DSP_IRQ2_EINT1 */ -- cgit v1.2.3 From e95d8aafa5d911bf523bc47fe89f3336eb8a1b51 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 10 Jul 2012 13:35:50 +0000 Subject: of: mtd: nuke useless const qualifier This patch does the following: -const int of_get_nand_ecc_mode(struct device_node *np) +int of_get_nand_ecc_mode(struct device_node *np) because: 1. it is probably just a typo? 2. it causes warnings like this when people assing the returned value to an 'int' variable: include/linux/of_mtd.h:14:18: warning: type qualifiers ignored on functi= on return type [-Wignored-qualifiers] Remove also the unnecessary "extern" qualifier to be consistent with other declarations in this file. Signed-off-by: Artem Bityutskiy Signed-off-by: Rob Herring --- drivers/of/of_mtd.c | 2 +- include/linux/of_mtd.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/of/of_mtd.c b/drivers/of/of_mtd.c index e7cad627a5d1..a27ec94877e4 100644 --- a/drivers/of/of_mtd.c +++ b/drivers/of/of_mtd.c @@ -32,7 +32,7 @@ static const char *nand_ecc_modes[] = { * The function gets ecc mode string from property 'nand-ecc-mode', * and return its index in nand_ecc_modes table, or errno in error case. */ -const int of_get_nand_ecc_mode(struct device_node *np) +int of_get_nand_ecc_mode(struct device_node *np) { const char *pm; int err, i; diff --git a/include/linux/of_mtd.h b/include/linux/of_mtd.h index bae1b6094c63..ed7f267e6389 100644 --- a/include/linux/of_mtd.h +++ b/include/linux/of_mtd.h @@ -11,7 +11,7 @@ #ifdef CONFIG_OF_MTD #include -extern const int of_get_nand_ecc_mode(struct device_node *np); +int of_get_nand_ecc_mode(struct device_node *np); int of_get_nand_bus_width(struct device_node *np); bool of_get_nand_on_flash_bbt(struct device_node *np); #endif -- cgit v1.2.3 From 00f5ce99dc6ee46c3113393cc8fa12173f9bbcd7 Mon Sep 17 00:00:00 2001 From: Jack Morgenstein Date: Tue, 19 Jun 2012 11:21:40 +0300 Subject: mlx4: Use port management change event instead of smp_snoop The port management change event can replace smp_snoop. If the capability bit for this event is set in dev-caps, the event is used (by the driver setting the PORT_MNG_CHG_EVENT bit in the async event mask in the MAP_EQ fw command). In this case, when the driver passes incoming SMP PORT_INFO SET mads to the FW, the FW generates port management change events to signal any changes to the driver. If the FW generates these events, smp_snoop shouldn't be invoked in ib_process_mad(), or duplicate events will occur (once from the FW-generated event, and once from smp_snoop). In the case where the FW does not generate port management change events smp_snoop needs to be invoked to create these events. The flow in smp_snoop has been modified to make use of the same procedures as in the fw-generated-event event case to generate the port management events (LID change, Client-rereg, Pkey change, and/or GID change). Port management change event handling required changing the mlx4_ib_event and mlx4_dispatch_event prototypes; the "param" argument (last argument) had to be changed to unsigned long in order to accomodate passing the EQE pointer. We also needed to move the definition of struct mlx4_eqe from net/mlx4.h to file device.h -- to make it available to the IB driver, to handle port management change events. Signed-off-by: Jack Morgenstein Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier --- drivers/infiniband/hw/mlx4/mad.c | 118 ++++++++++++++++++++------- drivers/infiniband/hw/mlx4/main.c | 29 +++++-- drivers/infiniband/hw/mlx4/mlx4_ib.h | 9 ++ drivers/net/ethernet/mellanox/mlx4/en_main.c | 5 +- drivers/net/ethernet/mellanox/mlx4/eq.c | 22 ++++- drivers/net/ethernet/mellanox/mlx4/fw.c | 1 + drivers/net/ethernet/mellanox/mlx4/intf.c | 5 +- drivers/net/ethernet/mellanox/mlx4/mlx4.h | 63 +------------- include/linux/mlx4/device.h | 99 +++++++++++++++++++++- include/linux/mlx4/driver.h | 3 +- 10 files changed, 249 insertions(+), 105 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c index 84786a9fb64f..58c45fb5bd31 100644 --- a/drivers/infiniband/hw/mlx4/mad.c +++ b/drivers/infiniband/hw/mlx4/mad.c @@ -147,47 +147,49 @@ static void update_sm_ah(struct mlx4_ib_dev *dev, u8 port_num, u16 lid, u8 sl) } /* - * Snoop SM MADs for port info and P_Key table sets, so we can - * synthesize LID change and P_Key change events. + * Snoop SM MADs for port info, GUID info, and P_Key table sets, so we can + * synthesize LID change, Client-Rereg, GID change, and P_Key change events. */ static void smp_snoop(struct ib_device *ibdev, u8 port_num, struct ib_mad *mad, - u16 prev_lid) + u16 prev_lid) { - struct ib_event event; + struct ib_port_info *pinfo; + u16 lid; + struct mlx4_ib_dev *dev = to_mdev(ibdev); if ((mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_LID_ROUTED || mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) && - mad->mad_hdr.method == IB_MGMT_METHOD_SET) { - if (mad->mad_hdr.attr_id == IB_SMP_ATTR_PORT_INFO) { - struct ib_port_info *pinfo = - (struct ib_port_info *) ((struct ib_smp *) mad)->data; - u16 lid = be16_to_cpu(pinfo->lid); + mad->mad_hdr.method == IB_MGMT_METHOD_SET) + switch (mad->mad_hdr.attr_id) { + case IB_SMP_ATTR_PORT_INFO: + pinfo = (struct ib_port_info *) ((struct ib_smp *) mad)->data; + lid = be16_to_cpu(pinfo->lid); - update_sm_ah(to_mdev(ibdev), port_num, + update_sm_ah(dev, port_num, be16_to_cpu(pinfo->sm_lid), pinfo->neighbormtu_mastersmsl & 0xf); - event.device = ibdev; - event.element.port_num = port_num; + if (pinfo->clientrereg_resv_subnetto & 0x80) + mlx4_ib_dispatch_event(dev, port_num, + IB_EVENT_CLIENT_REREGISTER); - if (pinfo->clientrereg_resv_subnetto & 0x80) { - event.event = IB_EVENT_CLIENT_REREGISTER; - ib_dispatch_event(&event); - } + if (prev_lid != lid) + mlx4_ib_dispatch_event(dev, port_num, + IB_EVENT_LID_CHANGE); + break; - if (prev_lid != lid) { - event.event = IB_EVENT_LID_CHANGE; - ib_dispatch_event(&event); - } - } + case IB_SMP_ATTR_PKEY_TABLE: + mlx4_ib_dispatch_event(dev, port_num, + IB_EVENT_PKEY_CHANGE); + break; - if (mad->mad_hdr.attr_id == IB_SMP_ATTR_PKEY_TABLE) { - event.device = ibdev; - event.event = IB_EVENT_PKEY_CHANGE; - event.element.port_num = port_num; - ib_dispatch_event(&event); + case IB_SMP_ATTR_GUID_INFO: + mlx4_ib_dispatch_event(dev, port_num, + IB_EVENT_GID_CHANGE); + break; + default: + break; } - } } static void node_desc_override(struct ib_device *dev, @@ -305,7 +307,8 @@ static int ib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num, return IB_MAD_RESULT_FAILURE; if (!out_mad->mad_hdr.status) { - smp_snoop(ibdev, port_num, in_mad, prev_lid); + if (!(to_mdev(ibdev)->dev->caps.flags & MLX4_DEV_CAP_FLAG_PORT_MNG_CHG_EV)) + smp_snoop(ibdev, port_num, in_mad, prev_lid); node_desc_override(ibdev, out_mad); } @@ -446,3 +449,62 @@ void mlx4_ib_mad_cleanup(struct mlx4_ib_dev *dev) ib_destroy_ah(dev->sm_ah[p]); } } + +void handle_port_mgmt_change_event(struct work_struct *work) +{ + struct ib_event_work *ew = container_of(work, struct ib_event_work, work); + struct mlx4_ib_dev *dev = ew->ib_dev; + struct mlx4_eqe *eqe = &(ew->ib_eqe); + u8 port = eqe->event.port_mgmt_change.port; + u32 changed_attr; + + switch (eqe->subtype) { + case MLX4_DEV_PMC_SUBTYPE_PORT_INFO: + changed_attr = be32_to_cpu(eqe->event.port_mgmt_change.params.port_info.changed_attr); + + /* Update the SM ah - This should be done before handling + the other changed attributes so that MADs can be sent to the SM */ + if (changed_attr & MSTR_SM_CHANGE_MASK) { + u16 lid = be16_to_cpu(eqe->event.port_mgmt_change.params.port_info.mstr_sm_lid); + u8 sl = eqe->event.port_mgmt_change.params.port_info.mstr_sm_sl & 0xf; + update_sm_ah(dev, port, lid, sl); + } + + /* Check if it is a lid change event */ + if (changed_attr & MLX4_EQ_PORT_INFO_LID_CHANGE_MASK) + mlx4_ib_dispatch_event(dev, port, IB_EVENT_LID_CHANGE); + + /* Generate GUID changed event */ + if (changed_attr & MLX4_EQ_PORT_INFO_GID_PFX_CHANGE_MASK) + mlx4_ib_dispatch_event(dev, port, IB_EVENT_GID_CHANGE); + + if (changed_attr & MLX4_EQ_PORT_INFO_CLIENT_REREG_MASK) + mlx4_ib_dispatch_event(dev, port, + IB_EVENT_CLIENT_REREGISTER); + break; + + case MLX4_DEV_PMC_SUBTYPE_PKEY_TABLE: + mlx4_ib_dispatch_event(dev, port, IB_EVENT_PKEY_CHANGE); + break; + case MLX4_DEV_PMC_SUBTYPE_GUID_INFO: + mlx4_ib_dispatch_event(dev, port, IB_EVENT_GID_CHANGE); + break; + default: + pr_warn("Unsupported subtype 0x%x for " + "Port Management Change event\n", eqe->subtype); + } + + kfree(ew); +} + +void mlx4_ib_dispatch_event(struct mlx4_ib_dev *dev, u8 port_num, + enum ib_event_type type) +{ + struct ib_event event; + + event.device = &dev->ib_dev; + event.element.port_num = port_num; + event.event = type; + + ib_dispatch_event(&event); +} diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 5266b49c46ee..4f230c26622d 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -898,7 +898,6 @@ static void update_gids_task(struct work_struct *work) union ib_gid *gids; int err; struct mlx4_dev *dev = gw->dev->dev; - struct ib_event event; mailbox = mlx4_alloc_cmd_mailbox(dev); if (IS_ERR(mailbox)) { @@ -916,10 +915,7 @@ static void update_gids_task(struct work_struct *work) pr_warn("set port command failed\n"); else { memcpy(gw->dev->iboe.gid_table[gw->port - 1], gw->gids, sizeof gw->gids); - event.device = &gw->dev->ib_dev; - event.element.port_num = gw->port; - event.event = IB_EVENT_GID_CHANGE; - ib_dispatch_event(&event); + mlx4_ib_dispatch_event(gw->dev, gw->port, IB_EVENT_GID_CHANGE); } mlx4_free_cmd_mailbox(dev, mailbox); @@ -1383,10 +1379,18 @@ static void mlx4_ib_remove(struct mlx4_dev *dev, void *ibdev_ptr) } static void mlx4_ib_event(struct mlx4_dev *dev, void *ibdev_ptr, - enum mlx4_dev_event event, int port) + enum mlx4_dev_event event, unsigned long param) { struct ib_event ibev; struct mlx4_ib_dev *ibdev = to_mdev((struct ib_device *) ibdev_ptr); + struct mlx4_eqe *eqe = NULL; + struct ib_event_work *ew; + int port = 0; + + if (event == MLX4_DEV_EVENT_PORT_MGMT_CHANGE) + eqe = (struct mlx4_eqe *)param; + else + port = (u8)param; if (port > ibdev->num_ports) return; @@ -1405,6 +1409,19 @@ static void mlx4_ib_event(struct mlx4_dev *dev, void *ibdev_ptr, ibev.event = IB_EVENT_DEVICE_FATAL; break; + case MLX4_DEV_EVENT_PORT_MGMT_CHANGE: + ew = kmalloc(sizeof *ew, GFP_ATOMIC); + if (!ew) { + pr_err("failed to allocate memory for events work\n"); + break; + } + + INIT_WORK(&ew->work, handle_port_mgmt_change_event); + memcpy(&ew->ib_eqe, eqe, sizeof *eqe); + ew->ib_dev = ibdev; + handle_port_mgmt_change_event(&ew->work); + return; + default: return; } diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h index 5f298afaa81f..23bfbf9ee0e0 100644 --- a/drivers/infiniband/hw/mlx4/mlx4_ib.h +++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h @@ -224,6 +224,12 @@ struct mlx4_ib_dev { int eq_added; }; +struct ib_event_work { + struct work_struct work; + struct mlx4_ib_dev *ib_dev; + struct mlx4_eqe ib_eqe; +}; + static inline struct mlx4_ib_dev *to_mdev(struct ib_device *ibdev) { return container_of(ibdev, struct mlx4_ib_dev, ib_dev); @@ -381,4 +387,7 @@ static inline int mlx4_ib_ah_grh_present(struct mlx4_ib_ah *ah) int mlx4_ib_add_mc(struct mlx4_ib_dev *mdev, struct mlx4_ib_qp *mqp, union ib_gid *gid); +void mlx4_ib_dispatch_event(struct mlx4_ib_dev *dev, u8 port_num, + enum ib_event_type type); + #endif /* MLX4_IB_H */ diff --git a/drivers/net/ethernet/mellanox/mlx4/en_main.c b/drivers/net/ethernet/mellanox/mlx4/en_main.c index 69ba57270481..a52922ed85c1 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_main.c @@ -131,7 +131,7 @@ static void *mlx4_en_get_netdev(struct mlx4_dev *dev, void *ctx, u8 port) } static void mlx4_en_event(struct mlx4_dev *dev, void *endev_ptr, - enum mlx4_dev_event event, int port) + enum mlx4_dev_event event, unsigned long port) { struct mlx4_en_dev *mdev = (struct mlx4_en_dev *) endev_ptr; struct mlx4_en_priv *priv; @@ -156,7 +156,8 @@ static void mlx4_en_event(struct mlx4_dev *dev, void *endev_ptr, if (port < 1 || port > dev->caps.num_ports || !mdev->pndev[port]) return; - mlx4_warn(mdev, "Unhandled event %d for port %d\n", event, port); + mlx4_warn(mdev, "Unhandled event %d for port %d\n", event, + (int) port); } } diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c index bce98d9c0039..9b15d0219950 100644 --- a/drivers/net/ethernet/mellanox/mlx4/eq.c +++ b/drivers/net/ethernet/mellanox/mlx4/eq.c @@ -82,6 +82,15 @@ enum { (1ull << MLX4_EVENT_TYPE_FLR_EVENT) | \ (1ull << MLX4_EVENT_TYPE_FATAL_WARNING)) +static u64 get_async_ev_mask(struct mlx4_dev *dev) +{ + u64 async_ev_mask = MLX4_ASYNC_EVENT_MASK; + if (dev->caps.flags & MLX4_DEV_CAP_FLAG_PORT_MNG_CHG_EV) + async_ev_mask |= (1ull << MLX4_EVENT_TYPE_PORT_MNG_CHG_EVENT); + + return async_ev_mask; +} + static void eq_set_ci(struct mlx4_eq *eq, int req_not) { __raw_writel((__force u32) cpu_to_be32((eq->cons_index & 0xffffff) | @@ -473,6 +482,11 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) break; + case MLX4_EVENT_TYPE_PORT_MNG_CHG_EVENT: + mlx4_dispatch_event(dev, MLX4_DEV_EVENT_PORT_MGMT_CHANGE, + (unsigned long) eqe); + break; + case MLX4_EVENT_TYPE_EEC_CATAS_ERROR: case MLX4_EVENT_TYPE_ECC_DETECT: default: @@ -956,7 +970,7 @@ int mlx4_init_eq_table(struct mlx4_dev *dev) priv->eq_table.have_irq = 1; } - err = mlx4_MAP_EQ(dev, MLX4_ASYNC_EVENT_MASK, 0, + err = mlx4_MAP_EQ(dev, get_async_ev_mask(dev), 0, priv->eq_table.eq[dev->caps.num_comp_vectors].eqn); if (err) mlx4_warn(dev, "MAP_EQ for async EQ %d failed (%d)\n", @@ -996,7 +1010,7 @@ void mlx4_cleanup_eq_table(struct mlx4_dev *dev) struct mlx4_priv *priv = mlx4_priv(dev); int i; - mlx4_MAP_EQ(dev, MLX4_ASYNC_EVENT_MASK, 1, + mlx4_MAP_EQ(dev, get_async_ev_mask(dev), 1, priv->eq_table.eq[dev->caps.num_comp_vectors].eqn); mlx4_free_irqs(dev); @@ -1040,7 +1054,7 @@ int mlx4_test_interrupts(struct mlx4_dev *dev) mlx4_cmd_use_polling(dev); /* Map the new eq to handle all asyncronous events */ - err = mlx4_MAP_EQ(dev, MLX4_ASYNC_EVENT_MASK, 0, + err = mlx4_MAP_EQ(dev, get_async_ev_mask(dev), 0, priv->eq_table.eq[i].eqn); if (err) { mlx4_warn(dev, "Failed mapping eq for interrupt test\n"); @@ -1054,7 +1068,7 @@ int mlx4_test_interrupts(struct mlx4_dev *dev) } /* Return to default */ - mlx4_MAP_EQ(dev, MLX4_ASYNC_EVENT_MASK, 0, + mlx4_MAP_EQ(dev, get_async_ev_mask(dev), 0, priv->eq_table.eq[dev->caps.num_comp_vectors].eqn); return err; } diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 4281ce09add8..ee9d6b0b4d20 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -109,6 +109,7 @@ static void dump_dev_cap_flags(struct mlx4_dev *dev, u64 flags) [41] = "Unicast VEP steering support", [42] = "Multicast VEP steering support", [48] = "Counters support", + [59] = "Port management change event support", }; int i; diff --git a/drivers/net/ethernet/mellanox/mlx4/intf.c b/drivers/net/ethernet/mellanox/mlx4/intf.c index b4e9f6f5cc04..116895ac8b35 100644 --- a/drivers/net/ethernet/mellanox/mlx4/intf.c +++ b/drivers/net/ethernet/mellanox/mlx4/intf.c @@ -115,7 +115,8 @@ void mlx4_unregister_interface(struct mlx4_interface *intf) } EXPORT_SYMBOL_GPL(mlx4_unregister_interface); -void mlx4_dispatch_event(struct mlx4_dev *dev, enum mlx4_dev_event type, int port) +void mlx4_dispatch_event(struct mlx4_dev *dev, enum mlx4_dev_event type, + unsigned long param) { struct mlx4_priv *priv = mlx4_priv(dev); struct mlx4_device_context *dev_ctx; @@ -125,7 +126,7 @@ void mlx4_dispatch_event(struct mlx4_dev *dev, enum mlx4_dev_event type, int por list_for_each_entry(dev_ctx, &priv->ctx_list, list) if (dev_ctx->intf->event) - dev_ctx->intf->event(dev, dev_ctx->context, type, port); + dev_ctx->intf->event(dev, dev_ctx->context, type, param); spin_unlock_irqrestore(&priv->ctx_lock, flags); } diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index e5d20220762c..4d11d12b9db4 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -338,66 +338,6 @@ struct mlx4_srq_context { __be64 db_rec_addr; }; -struct mlx4_eqe { - u8 reserved1; - u8 type; - u8 reserved2; - u8 subtype; - union { - u32 raw[6]; - struct { - __be32 cqn; - } __packed comp; - struct { - u16 reserved1; - __be16 token; - u32 reserved2; - u8 reserved3[3]; - u8 status; - __be64 out_param; - } __packed cmd; - struct { - __be32 qpn; - } __packed qp; - struct { - __be32 srqn; - } __packed srq; - struct { - __be32 cqn; - u32 reserved1; - u8 reserved2[3]; - u8 syndrome; - } __packed cq_err; - struct { - u32 reserved1[2]; - __be32 port; - } __packed port_change; - struct { - #define COMM_CHANNEL_BIT_ARRAY_SIZE 4 - u32 reserved; - u32 bit_vec[COMM_CHANNEL_BIT_ARRAY_SIZE]; - } __packed comm_channel_arm; - struct { - u8 port; - u8 reserved[3]; - __be64 mac; - } __packed mac_update; - struct { - u8 port; - } __packed sw_event; - struct { - __be32 slave_id; - } __packed flr_event; - struct { - __be16 current_temperature; - __be16 warning_threshold; - } __packed warming; - } event; - u8 slave_id; - u8 reserved3[2]; - u8 owner; -} __packed; - struct mlx4_eq { struct mlx4_dev *dev; void __iomem *doorbell; @@ -887,7 +827,8 @@ void mlx4_catas_init(void); int mlx4_restart_one(struct pci_dev *pdev); int mlx4_register_device(struct mlx4_dev *dev); void mlx4_unregister_device(struct mlx4_dev *dev); -void mlx4_dispatch_event(struct mlx4_dev *dev, enum mlx4_dev_event type, int port); +void mlx4_dispatch_event(struct mlx4_dev *dev, enum mlx4_dev_event type, + unsigned long param); struct mlx4_dev_cap; struct mlx4_init_hca_param; diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 8eadf0f14cc5..560b2201519f 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -96,7 +96,8 @@ enum { MLX4_DEV_CAP_FLAG_VEP_UC_STEER = 1LL << 41, MLX4_DEV_CAP_FLAG_VEP_MC_STEER = 1LL << 42, MLX4_DEV_CAP_FLAG_COUNTERS = 1LL << 48, - MLX4_DEV_CAP_FLAG_SENSE_SUPPORT = 1LL << 55 + MLX4_DEV_CAP_FLAG_SENSE_SUPPORT = 1LL << 55, + MLX4_DEV_CAP_FLAG_PORT_MNG_CHG_EV = 1LL << 59, }; enum { @@ -138,6 +139,7 @@ enum mlx4_event { MLX4_EVENT_TYPE_COMM_CHANNEL = 0x18, MLX4_EVENT_TYPE_FATAL_WARNING = 0x1b, MLX4_EVENT_TYPE_FLR_EVENT = 0x1c, + MLX4_EVENT_TYPE_PORT_MNG_CHG_EVENT = 0x1d, MLX4_EVENT_TYPE_NONE = 0xff, }; @@ -235,6 +237,24 @@ enum { MLX4_MAX_FAST_REG_PAGES = 511, }; +enum { + MLX4_DEV_PMC_SUBTYPE_GUID_INFO = 0x14, + MLX4_DEV_PMC_SUBTYPE_PORT_INFO = 0x15, + MLX4_DEV_PMC_SUBTYPE_PKEY_TABLE = 0x16, +}; + +/* Port mgmt change event handling */ +enum { + MLX4_EQ_PORT_INFO_MSTR_SM_LID_CHANGE_MASK = 1 << 0, + MLX4_EQ_PORT_INFO_GID_PFX_CHANGE_MASK = 1 << 1, + MLX4_EQ_PORT_INFO_LID_CHANGE_MASK = 1 << 2, + MLX4_EQ_PORT_INFO_CLIENT_REREG_MASK = 1 << 3, + MLX4_EQ_PORT_INFO_MSTR_SM_SL_CHANGE_MASK = 1 << 4, +}; + +#define MSTR_SM_CHANGE_MASK (MLX4_EQ_PORT_INFO_MSTR_SM_SL_CHANGE_MASK | \ + MLX4_EQ_PORT_INFO_MSTR_SM_LID_CHANGE_MASK) + static inline u64 mlx4_fw_ver(u64 major, u64 minor, u64 subminor) { return (major << 32) | (minor << 16) | subminor; @@ -511,6 +531,81 @@ struct mlx4_dev { int num_vfs; }; +struct mlx4_eqe { + u8 reserved1; + u8 type; + u8 reserved2; + u8 subtype; + union { + u32 raw[6]; + struct { + __be32 cqn; + } __packed comp; + struct { + u16 reserved1; + __be16 token; + u32 reserved2; + u8 reserved3[3]; + u8 status; + __be64 out_param; + } __packed cmd; + struct { + __be32 qpn; + } __packed qp; + struct { + __be32 srqn; + } __packed srq; + struct { + __be32 cqn; + u32 reserved1; + u8 reserved2[3]; + u8 syndrome; + } __packed cq_err; + struct { + u32 reserved1[2]; + __be32 port; + } __packed port_change; + struct { + #define COMM_CHANNEL_BIT_ARRAY_SIZE 4 + u32 reserved; + u32 bit_vec[COMM_CHANNEL_BIT_ARRAY_SIZE]; + } __packed comm_channel_arm; + struct { + u8 port; + u8 reserved[3]; + __be64 mac; + } __packed mac_update; + struct { + __be32 slave_id; + } __packed flr_event; + struct { + __be16 current_temperature; + __be16 warning_threshold; + } __packed warming; + struct { + u8 reserved[3]; + u8 port; + union { + struct { + __be16 mstr_sm_lid; + __be16 port_lid; + __be32 changed_attr; + u8 reserved[3]; + u8 mstr_sm_sl; + __be64 gid_prefix; + } __packed port_info; + struct { + __be32 block_ptr; + __be32 tbl_entries_mask; + } __packed tbl_change_info; + } params; + } __packed port_mgmt_change; + } event; + u8 slave_id; + u8 reserved3[2]; + u8 owner; +} __packed; + struct mlx4_init_port_param { int set_guid0; int set_node_guid; @@ -536,6 +631,8 @@ struct mlx4_init_port_param { #define MLX4_INVALID_SLAVE_ID 0xFF +void handle_port_mgmt_change_event(struct work_struct *work); + static inline int mlx4_is_master(struct mlx4_dev *dev) { return dev->flags & MLX4_FLAG_MASTER; diff --git a/include/linux/mlx4/driver.h b/include/linux/mlx4/driver.h index 5f1298b1b5ef..0f509229fb3d 100644 --- a/include/linux/mlx4/driver.h +++ b/include/linux/mlx4/driver.h @@ -42,13 +42,14 @@ enum mlx4_dev_event { MLX4_DEV_EVENT_PORT_UP, MLX4_DEV_EVENT_PORT_DOWN, MLX4_DEV_EVENT_PORT_REINIT, + MLX4_DEV_EVENT_PORT_MGMT_CHANGE, }; struct mlx4_interface { void * (*add) (struct mlx4_dev *dev); void (*remove)(struct mlx4_dev *dev, void *context); void (*event) (struct mlx4_dev *dev, void *context, - enum mlx4_dev_event event, int port); + enum mlx4_dev_event event, unsigned long param); void * (*get_dev)(struct mlx4_dev *dev, void *context, u8 port); struct list_head list; enum mlx4_protocol protocol; -- cgit v1.2.3 From 2aca1172c2f5b27fbc37297574f716c1c15f4153 Mon Sep 17 00:00:00 2001 From: Jack Morgenstein Date: Tue, 19 Jun 2012 11:21:41 +0300 Subject: net/mlx4_core: Initialize IB port capabilities for all slaves With IB SR-IOV, each slave has its own separate copy of the port capabilities flags. For example, the master can run a subnet manager (which causes the IsSM bit to be set in the master's port capabilities) without affecting the port capabilities seen by the slaves (the IsSM bit will be seen as cleared in the slaves). Also add a static inline mlx4_master_func_num() to enhance readability of the code. Signed-off-by: Jack Morgenstein Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier --- drivers/net/ethernet/mellanox/mlx4/main.c | 11 +++++++++++ include/linux/mlx4/device.h | 5 +++++ 2 files changed, 16 insertions(+) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index a0313de122de..83afb1541a74 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -1477,6 +1477,17 @@ static int mlx4_setup_hca(struct mlx4_dev *dev) "with caps = 0\n", port, err); dev->caps.ib_port_def_cap[port] = ib_port_default_caps; + /* initialize per-slave default ib port capabilities */ + if (mlx4_is_master(dev)) { + int i; + for (i = 0; i < dev->num_slaves; i++) { + if (i == mlx4_master_func_num(dev)) + continue; + priv->mfunc.master.slave_state[i].ib_cap_mask[port] = + ib_port_default_caps; + } + } + if (mlx4_is_mfunc(dev)) dev->caps.port_ib_mtu[port] = IB_MTU_2048; else diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 560b2201519f..7fbdc89de495 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -633,6 +633,11 @@ struct mlx4_init_port_param { void handle_port_mgmt_change_event(struct work_struct *work); +static inline int mlx4_master_func_num(struct mlx4_dev *dev) +{ + return dev->caps.function; +} + static inline int mlx4_is_master(struct mlx4_dev *dev) { return dev->flags & MLX4_FLAG_MASTER; -- cgit v1.2.3 From 21cd1fab058671313f7c178b640999fcd0d8de21 Mon Sep 17 00:00:00 2001 From: Jon Brenner Date: Wed, 16 May 2012 10:46:42 -0500 Subject: IIO channel type and modifiers for CCT and RGBC data Add iio channel type and modifiers for Correlated Color Temperature (CCT) and RGBC (red/green/blue/clear) data. Add CCT and RGBC descriptions to documentation. Changes: Revised/condensed RGBC descriptions. Merge and trivial fix done by Jonathan Cameron. Signed-off-by: Jon Brenner Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 5 +++++ .../staging/iio/Documentation/sysfs-bus-iio-light | 23 ++++++++++++++++++++++ include/linux/iio/types.h | 5 +++++ 3 files changed, 33 insertions(+) (limited to 'include') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index bb3c692e49b8..2ec266ef41a3 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -64,6 +64,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_TIMESTAMP] = "timestamp", [IIO_CAPACITANCE] = "capacitance", [IIO_ALTVOLTAGE] = "altvoltage", + [IIO_CCT] = "cct", }; static const char * const iio_modifier_names[] = { @@ -74,6 +75,10 @@ static const char * const iio_modifier_names[] = { [IIO_MOD_SUM_SQUARED_X_Y_Z] = "x^2+y^2+z^2", [IIO_MOD_LIGHT_BOTH] = "both", [IIO_MOD_LIGHT_IR] = "ir", + [IIO_MOD_LIGHT_CLEAR] = "clear", + [IIO_MOD_LIGHT_RED] = "red", + [IIO_MOD_LIGHT_GREEN] = "green", + [IIO_MOD_LIGHT_BLUE] = "blue", }; /* relies on pairs of these shared then separate */ diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light b/drivers/staging/iio/Documentation/sysfs-bus-iio-light index d52be0385dc4..a28919da13e3 100644 --- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-light @@ -82,3 +82,26 @@ Contact: linux-iio@vger.kernel.org Description: This property gets/sets the table of coefficients used in calculating illuminance in lux. + +What: /sys/bus/iio/devices/device[n]/in_intensity_clear[_input|_raw] +What: /sys/bus/iio/devices/device[n]/in_intensity_red[_input|_raw] +What: /sys/bus/iio/devices/device[n]/in_intensity_green[_input|_raw] +What: /sys/bus/iio/devices/device[n]/in_intensity_blue[_input|_raw] +KernelVersion: 3.4.0 +Contact: linux-iio@vger.kernel.org +Description: + This property is supported by sensors that have a RGBC + sensing mode. This value should be the output from a reading + and if expressed in SI units, should include _input. If this + value is not in SI units (irradiance, uW/mm^2), then it should + include _raw. + +What: /sys/bus/iio/devices/device[n]/in_cct0[_input|_raw] +KernelVersion: 3.4.0 +Contact: linux-iio@vger.kernel.org +Description: + This should return the correlated color temperature from the + light sensor. If it comes back in SI units, it should also + include _input else it should include _raw to signify it is not + in SI units. + diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index e25040173346..44e397705d7f 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -27,6 +27,7 @@ enum iio_chan_type { IIO_TIMESTAMP, IIO_CAPACITANCE, IIO_ALTVOLTAGE, + IIO_CCT, }; enum iio_modifier { @@ -46,6 +47,10 @@ enum iio_modifier { IIO_MOD_LIGHT_IR, IIO_MOD_ROOT_SUM_SQUARED_X_Y, IIO_MOD_SUM_SQUARED_X_Y_Z, + IIO_MOD_LIGHT_CLEAR, + IIO_MOD_LIGHT_RED, + IIO_MOD_LIGHT_GREEN, + IIO_MOD_LIGHT_BLUE, }; #define IIO_VAL_INT 1 -- cgit v1.2.3 From 815e972110052e8da68b5b5298ca2cd69cb7c3c0 Mon Sep 17 00:00:00 2001 From: Nicolas Royer Date: Sun, 1 Jul 2012 19:19:43 +0200 Subject: ARM: AT91SAM9G45: add crypto peripherals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nicolas Royer Acked-by: Nicolas Ferre Acked-by: Eric Bénard Tested-by: Eric Bénard Signed-off-by: Herbert Xu --- arch/arm/mach-at91/at91sam9g45.c | 13 ++- arch/arm/mach-at91/at91sam9g45_devices.c | 128 ++++++++++++++++++++++++++ arch/arm/mach-at91/include/mach/at91sam9g45.h | 2 + include/linux/platform_data/atmel-aes.h | 22 +++++ 4 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 include/linux/platform_data/atmel-aes.h (limited to 'include') diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c index 4792682d52b9..da6dc0f29657 100644 --- a/arch/arm/mach-at91/at91sam9g45.c +++ b/arch/arm/mach-at91/at91sam9g45.c @@ -182,6 +182,13 @@ static struct clk adc_op_clk = { .rate_hz = 13200000, }; +/* AES/TDES/SHA clock - Only for sam9m11/sam9g56 */ +static struct clk aestdessha_clk = { + .name = "aestdessha_clk", + .pmc_mask = 1 << AT91SAM9G45_ID_AESTDESSHA, + .type = CLK_TYPE_PERIPHERAL, +}; + static struct clk *periph_clocks[] __initdata = { &pioA_clk, &pioB_clk, @@ -211,6 +218,7 @@ static struct clk *periph_clocks[] __initdata = { &udphs_clk, &mmc1_clk, &adc_op_clk, + &aestdessha_clk, // irq0 }; @@ -231,6 +239,9 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk), CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk), CLKDEV_CON_DEV_ID(NULL, "atmel-trng", &trng_clk), + CLKDEV_CON_DEV_ID(NULL, "atmel_sha", &aestdessha_clk), + CLKDEV_CON_DEV_ID(NULL, "atmel_tdes", &aestdessha_clk), + CLKDEV_CON_DEV_ID(NULL, "atmel_aes", &aestdessha_clk), /* more usart lookup table for DT entries */ CLKDEV_CON_DEV_ID("usart", "ffffee00.serial", &mck), CLKDEV_CON_DEV_ID("usart", "fff8c000.serial", &usart0_clk), @@ -387,7 +398,7 @@ static unsigned int at91sam9g45_default_irq_priority[NR_AIC_IRQS] __initdata = { 3, /* Ethernet */ 0, /* Image Sensor Interface */ 2, /* USB Device High speed port */ - 0, + 0, /* AESTDESSHA Crypto HW Accelerators */ 0, /* Multimedia Card Interface 1 */ 0, 0, /* Advanced Interrupt Controller (IRQ0) */ diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c index 933fc9afe7d0..7102f62b64ef 100644 --- a/arch/arm/mach-at91/at91sam9g45_devices.c +++ b/arch/arm/mach-at91/at91sam9g45_devices.c @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -1830,6 +1831,130 @@ void __init at91_register_uart(unsigned id, unsigned portnr, unsigned pins) {} void __init at91_add_device_serial(void) {} #endif +/* -------------------------------------------------------------------- + * SHA1/SHA256 + * -------------------------------------------------------------------- */ + +#if defined(CONFIG_CRYPTO_DEV_ATMEL_SHA) || defined(CONFIG_CRYPTO_DEV_ATMEL_SHA_MODULE) +static struct resource sha_resources[] = { + { + .start = AT91SAM9G45_BASE_SHA, + .end = AT91SAM9G45_BASE_SHA + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9G45_ID_AESTDESSHA, + .end = AT91SAM9G45_ID_AESTDESSHA, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device at91sam9g45_sha_device = { + .name = "atmel_sha", + .id = -1, + .resource = sha_resources, + .num_resources = ARRAY_SIZE(sha_resources), +}; + +static void __init at91_add_device_sha(void) +{ + platform_device_register(&at91sam9g45_sha_device); +} +#else +static void __init at91_add_device_sha(void) {} +#endif + +/* -------------------------------------------------------------------- + * DES/TDES + * -------------------------------------------------------------------- */ + +#if defined(CONFIG_CRYPTO_DEV_ATMEL_TDES) || defined(CONFIG_CRYPTO_DEV_ATMEL_TDES_MODULE) +static struct resource tdes_resources[] = { + [0] = { + .start = AT91SAM9G45_BASE_TDES, + .end = AT91SAM9G45_BASE_TDES + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9G45_ID_AESTDESSHA, + .end = AT91SAM9G45_ID_AESTDESSHA, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device at91sam9g45_tdes_device = { + .name = "atmel_tdes", + .id = -1, + .resource = tdes_resources, + .num_resources = ARRAY_SIZE(tdes_resources), +}; + +static void __init at91_add_device_tdes(void) +{ + platform_device_register(&at91sam9g45_tdes_device); +} +#else +static void __init at91_add_device_tdes(void) {} +#endif + +/* -------------------------------------------------------------------- + * AES + * -------------------------------------------------------------------- */ + +#if defined(CONFIG_CRYPTO_DEV_ATMEL_AES) || defined(CONFIG_CRYPTO_DEV_ATMEL_AES_MODULE) +static struct aes_platform_data aes_data; +static u64 aes_dmamask = DMA_BIT_MASK(32); + +static struct resource aes_resources[] = { + [0] = { + .start = AT91SAM9G45_BASE_AES, + .end = AT91SAM9G45_BASE_AES + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9G45_ID_AESTDESSHA, + .end = AT91SAM9G45_ID_AESTDESSHA, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device at91sam9g45_aes_device = { + .name = "atmel_aes", + .id = -1, + .dev = { + .dma_mask = &aes_dmamask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &aes_data, + }, + .resource = aes_resources, + .num_resources = ARRAY_SIZE(aes_resources), +}; + +static void __init at91_add_device_aes(void) +{ + struct at_dma_slave *atslave; + struct aes_dma_data *alt_atslave; + + alt_atslave = kzalloc(sizeof(struct aes_dma_data), GFP_KERNEL); + + /* DMA TX slave channel configuration */ + atslave = &alt_atslave->txdata; + atslave->dma_dev = &at_hdmac_device.dev; + atslave->cfg = ATC_FIFOCFG_ENOUGHSPACE | ATC_SRC_H2SEL_HW | + ATC_SRC_PER(AT_DMA_ID_AES_RX); + + /* DMA RX slave channel configuration */ + atslave = &alt_atslave->rxdata; + atslave->dma_dev = &at_hdmac_device.dev; + atslave->cfg = ATC_FIFOCFG_ENOUGHSPACE | ATC_DST_H2SEL_HW | + ATC_DST_PER(AT_DMA_ID_AES_TX); + + aes_data.dma_slave = alt_atslave; + platform_device_register(&at91sam9g45_aes_device); +} +#else +static void __init at91_add_device_aes(void) {} +#endif /* -------------------------------------------------------------------- */ /* @@ -1847,6 +1972,9 @@ static int __init at91_add_standard_devices(void) at91_add_device_trng(); at91_add_device_watchdog(); at91_add_device_tc(); + at91_add_device_sha(); + at91_add_device_tdes(); + at91_add_device_aes(); return 0; } diff --git a/arch/arm/mach-at91/include/mach/at91sam9g45.h b/arch/arm/mach-at91/include/mach/at91sam9g45.h index 3a4da24d5911..8eba1021f533 100644 --- a/arch/arm/mach-at91/include/mach/at91sam9g45.h +++ b/arch/arm/mach-at91/include/mach/at91sam9g45.h @@ -136,6 +136,8 @@ #define AT_DMA_ID_SSC1_RX 8 #define AT_DMA_ID_AC97_TX 9 #define AT_DMA_ID_AC97_RX 10 +#define AT_DMA_ID_AES_TX 11 +#define AT_DMA_ID_AES_RX 12 #define AT_DMA_ID_MCI1 13 #endif diff --git a/include/linux/platform_data/atmel-aes.h b/include/linux/platform_data/atmel-aes.h new file mode 100644 index 000000000000..e7a1949bad26 --- /dev/null +++ b/include/linux/platform_data/atmel-aes.h @@ -0,0 +1,22 @@ +#ifndef __LINUX_ATMEL_AES_H +#define __LINUX_ATMEL_AES_H + +#include + +/** + * struct aes_dma_data - DMA data for AES + */ +struct aes_dma_data { + struct at_dma_slave txdata; + struct at_dma_slave rxdata; +}; + +/** + * struct aes_platform_data - board-specific AES configuration + * @dma_slave: DMA slave interface to use in data transfers. + */ +struct aes_platform_data { + struct aes_dma_data *dma_slave; +}; + +#endif /* __LINUX_ATMEL_AES_H */ -- cgit v1.2.3 From 0cd76dd13bdd2f7f02a2dc931e808e92b191082f Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Jan 2012 19:40:52 +0100 Subject: iommu: Add domain-attribute handlers This patch introduces an extension to the iommu-api to get and set attributes for an iommu_domain. Two functions are introduced for this: * iommu_domain_get_attr() * iommu_domain_set_attr() These functions will be used to make the iommu-api suitable for GART-like IOMMUs and to implement hardware-specifc api-extensions. Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 20 ++++++++++++++++++++ include/linux/iommu.h | 28 +++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 8b9ded88e6f5..c39972d8ded3 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -344,3 +344,23 @@ int iommu_device_group(struct device *dev, unsigned int *groupid) return -ENODEV; } EXPORT_SYMBOL_GPL(iommu_device_group); + +int iommu_domain_get_attr(struct iommu_domain *domain, + enum iommu_attr attr, void *data) +{ + if (!domain->ops->domain_get_attr) + return -EINVAL; + + return domain->ops->domain_get_attr(domain, attr, data); +} +EXPORT_SYMBOL_GPL(iommu_domain_get_attr); + +int iommu_domain_set_attr(struct iommu_domain *domain, + enum iommu_attr attr, void *data) +{ + if (!domain->ops->domain_set_attr) + return -EINVAL; + + return domain->ops->domain_set_attr(domain, attr, data); +} +EXPORT_SYMBOL_GPL(iommu_domain_set_attr); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 450293f6d68b..0eef096183e8 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -47,6 +47,10 @@ struct iommu_domain { #define IOMMU_CAP_CACHE_COHERENCY 0x1 #define IOMMU_CAP_INTR_REMAP 0x2 /* isolates device intrs */ +enum iommu_attr { + DOMAIN_ATTR_MAX, +}; + #ifdef CONFIG_IOMMU_API /** @@ -59,7 +63,8 @@ struct iommu_domain { * @unmap: unmap a physically contiguous memory region from an iommu domain * @iova_to_phys: translate iova to physical address * @domain_has_cap: domain capabilities query - * @commit: commit iommu domain + * @domain_get_attr: Query domain attributes + * @domain_set_attr: Change domain attributes * @pgsize_bitmap: bitmap of supported page sizes */ struct iommu_ops { @@ -76,6 +81,10 @@ struct iommu_ops { int (*domain_has_cap)(struct iommu_domain *domain, unsigned long cap); int (*device_group)(struct device *dev, unsigned int *groupid); + int (*domain_get_attr)(struct iommu_domain *domain, + enum iommu_attr attr, void *data); + int (*domain_set_attr)(struct iommu_domain *domain, + enum iommu_attr attr, void *data); unsigned long pgsize_bitmap; }; @@ -99,6 +108,11 @@ extern void iommu_set_fault_handler(struct iommu_domain *domain, iommu_fault_handler_t handler, void *token); extern int iommu_device_group(struct device *dev, unsigned int *groupid); +extern int iommu_domain_get_attr(struct iommu_domain *domain, enum iommu_attr, + void *data); +extern int iommu_domain_set_attr(struct iommu_domain *domain, enum iommu_attr, + void *data); + /** * report_iommu_fault() - report about an IOMMU fault to the IOMMU framework * @domain: the iommu domain where the fault has happened @@ -202,6 +216,18 @@ static inline int iommu_device_group(struct device *dev, unsigned int *groupid) return -ENODEV; } +static inline int iommu_domain_get_attr(struct iommu_domain *domain, + enum iommu_attr attr, void *data) +{ + return -EINVAL; +} + +static inline int iommu_domain_set_attr(struct iommu_domain *domain, + enum iommu_attr attr, void *data) +{ + return -EINVAL; +} + #endif /* CONFIG_IOMMU_API */ #endif /* __LINUX_IOMMU_H */ -- cgit v1.2.3 From 0ff64f80e075ae036a4c80c7d7752b1e07fed792 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Jan 2012 19:40:53 +0100 Subject: iommu/amd: Implement DOMAIN_ATTR_GEOMETRY attribute Implement the attribute itself and add the code for the AMD IOMMU driver. Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 4 ++++ drivers/iommu/iommu.c | 19 ++++++++++++++++--- include/linux/iommu.h | 8 ++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index a2e418cba0ff..259a6beddece 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -3069,6 +3069,10 @@ static int amd_iommu_domain_init(struct iommu_domain *dom) dom->priv = domain; + dom->geometry.aperture_start = 0; + dom->geometry.aperture_end = ~0ULL; + dom->geometry.force_aperture = true; + return 0; out_free: diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index c39972d8ded3..ed5e0a553ca7 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -348,10 +348,23 @@ EXPORT_SYMBOL_GPL(iommu_device_group); int iommu_domain_get_attr(struct iommu_domain *domain, enum iommu_attr attr, void *data) { - if (!domain->ops->domain_get_attr) - return -EINVAL; + struct iommu_domain_geometry *geometry; + int ret = 0; + + switch (attr) { + case DOMAIN_ATTR_GEOMETRY: + geometry = data; + *geometry = domain->geometry; + + break; + default: + if (!domain->ops->domain_get_attr) + return -EINVAL; - return domain->ops->domain_get_attr(domain, attr, data); + ret = domain->ops->domain_get_attr(domain, attr, data); + } + + return ret; } EXPORT_SYMBOL_GPL(iommu_domain_get_attr); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 0eef096183e8..f7df4aa527f3 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -37,11 +37,18 @@ struct iommu_domain; typedef int (*iommu_fault_handler_t)(struct iommu_domain *, struct device *, unsigned long, int, void *); +struct iommu_domain_geometry { + dma_addr_t aperture_start; /* First address that can be mapped */ + dma_addr_t aperture_end; /* Last address that can be mapped */ + bool force_aperture; /* DMA only allowed in mappable range? */ +}; + struct iommu_domain { struct iommu_ops *ops; void *priv; iommu_fault_handler_t handler; void *handler_token; + struct iommu_domain_geometry geometry; }; #define IOMMU_CAP_CACHE_COHERENCY 0x1 @@ -49,6 +56,7 @@ struct iommu_domain { enum iommu_attr { DOMAIN_ATTR_MAX, + DOMAIN_ATTR_GEOMETRY, }; #ifdef CONFIG_IOMMU_API -- cgit v1.2.3 From c5c4bdf02e518a281b229ae0891b346919e2d291 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 5 Jul 2012 09:41:22 -0700 Subject: USB: Remove unused LPM variable. hub_initiated_lpm_disable_count is not used by any code, so remove it. This commit should be backported to kernels as old as 3.5, that contain the commit 8306095fd2c1100e8244c09bf560f97aca5a311d "USB: Disable USB 3.0 LPM in critical sections." Signed-off-by: Sarah Sharp Cc: stable@vger.kernel.org --- include/linux/usb.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/usb.h b/include/linux/usb.h index d4f9de1acd45..dea2f0de063e 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -561,7 +561,6 @@ struct usb_device { struct usb3_lpm_parameters u1_params; struct usb3_lpm_parameters u2_params; unsigned lpm_disable_count; - unsigned hub_initiated_lpm_disable_count; }; #define to_usb_device(d) container_of(d, struct usb_device, dev) -- cgit v1.2.3 From f74631e3426474183389e55f703797bd965cd356 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 25 Jun 2012 12:08:08 -0700 Subject: USB: Enable Latency Tolerance Messaging (LTM). USB 3.0 devices may optionally support a new feature called Latency Tolerance Messaging. If both the xHCI host controller and the device support LTM, it should be turned on in order to give the system hardware a better clue about the latency tolerance values of its PCI devices. Once a Set Feature request to enable LTM is received, the USB 3.0 device will begin to send LTM updates as its buffers fill or empty, and it can tolerate more or less latency. The USB 3.0 spec, section C.4.2 says that LTM should be disabled just before the device is placed into suspend. Then the device will send an updated LTM notification, so that the system doesn't think it should remain in an active state in order to satisfy the latency requirements of the suspended device. The Set and Clear Feature LTM enable command can only be sent to a configured device. The device will respond with an error if that command is sent while it is in the Default or Addressed state. Make sure to check udev->actconfig in usb_enable_ltm() and usb_disable_ltm(), and don't send those commands when the device is unconfigured. LTM should be enabled once a new configuration is installed in usb_set_configuration(). If we end up sending duplicate Set Feature LTM Enable commands on a switch from one installed configuration to another configuration, that should be harmless. Make sure that LTM is disabled before the device is unconfigured in usb_disable_device(). If no drivers are bound to the device, it doesn't make sense to allow the device to control the latency tolerance of the xHCI host controller. Signed-off-by: Sarah Sharp --- drivers/usb/core/hub.c | 87 ++++++++++++++++++++++++++++++++++++++++++---- drivers/usb/core/message.c | 3 ++ include/linux/usb.h | 3 ++ 3 files changed, 87 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 5c31d2c2f95a..b5bd6bd8fd12 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2610,6 +2610,57 @@ static int check_port_resume_type(struct usb_device *udev, return status; } +static bool usb_device_supports_ltm(struct usb_device *udev) +{ + if (udev->speed != USB_SPEED_SUPER || !udev->bos || !udev->bos->ss_cap) + return false; + return udev->bos->ss_cap->bmAttributes & USB_LTM_SUPPORT; +} + +int usb_disable_ltm(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + /* Check if the roothub and device supports LTM. */ + if (!usb_device_supports_ltm(hcd->self.root_hub) || + !usb_device_supports_ltm(udev)) + return 0; + + /* Clear Feature LTM Enable can only be sent if the device is + * configured. + */ + if (!udev->actconfig) + return 0; + + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, + USB_DEVICE_LTM_ENABLE, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); +} +EXPORT_SYMBOL_GPL(usb_disable_ltm); + +void usb_enable_ltm(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + /* Check if the roothub and device supports LTM. */ + if (!usb_device_supports_ltm(hcd->self.root_hub) || + !usb_device_supports_ltm(udev)) + return; + + /* Set Feature LTM Enable can only be sent if the device is + * configured. + */ + if (!udev->actconfig) + return; + + usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, + USB_DEVICE_LTM_ENABLE, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); +} +EXPORT_SYMBOL_GPL(usb_enable_ltm); + #ifdef CONFIG_USB_SUSPEND /* @@ -2705,6 +2756,11 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) if (udev->usb2_hw_lpm_enabled == 1) usb_set_usb2_hardware_lpm(udev, 0); + if (usb_disable_ltm(udev)) { + dev_err(&udev->dev, "%s Failed to disable LTM before suspend\n.", + __func__); + return -ENOMEM; + } if (usb_unlocked_disable_lpm(udev)) { dev_err(&udev->dev, "%s Failed to disable LPM before suspend\n.", __func__); @@ -2734,7 +2790,8 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) if (udev->usb2_hw_lpm_capable == 1) usb_set_usb2_hardware_lpm(udev, 1); - /* Try to enable USB3 LPM again */ + /* Try to enable USB3 LTM and LPM again */ + usb_enable_ltm(udev); usb_unlocked_enable_lpm(udev); /* System sleep transitions should never fail */ @@ -2935,7 +2992,8 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) if (udev->usb2_hw_lpm_capable == 1) usb_set_usb2_hardware_lpm(udev, 1); - /* Try to enable USB3 LPM */ + /* Try to enable USB3 LTM and LPM */ + usb_enable_ltm(udev); usb_unlocked_enable_lpm(udev); } @@ -3488,6 +3546,15 @@ EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm); void usb_unlocked_enable_lpm(struct usb_device *udev) { } EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm); + +int usb_disable_ltm(struct usb_device *udev) +{ + return 0; +} +EXPORT_SYMBOL_GPL(usb_disable_ltm); + +void usb_enable_ltm(struct usb_device *udev) { } +EXPORT_SYMBOL_GPL(usb_enable_ltm); #endif @@ -4673,15 +4740,22 @@ static int usb_reset_and_verify_device(struct usb_device *udev) } parent_hub = hdev_to_hub(parent_hdev); - /* Disable LPM while we reset the device and reinstall the alt settings. - * Device-initiated LPM settings, and system exit latency settings are - * cleared when the device is reset, so we have to set them up again. + /* Disable LPM and LTM while we reset the device and reinstall the alt + * settings. Device-initiated LPM settings, and system exit latency + * settings are cleared when the device is reset, so we have to set + * them up again. */ ret = usb_unlocked_disable_lpm(udev); if (ret) { dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__); goto re_enumerate; } + ret = usb_disable_ltm(udev); + if (ret) { + dev_err(&udev->dev, "%s Failed to disable LTM\n.", + __func__); + goto re_enumerate; + } set_bit(port1, parent_hub->busy_bits); for (i = 0; i < SET_CONFIG_TRIES; ++i) { @@ -4769,8 +4843,9 @@ static int usb_reset_and_verify_device(struct usb_device *udev) } done: - /* Now that the alt settings are re-installed, enable LPM. */ + /* Now that the alt settings are re-installed, enable LTM and LPM. */ usb_unlocked_enable_lpm(udev); + usb_enable_ltm(udev); return 0; re_enumerate: diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index c0877b7f505a..0ab7da2283e3 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1175,6 +1175,7 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) dev->actconfig->interface[i] = NULL; } usb_unlocked_disable_lpm(dev); + usb_disable_ltm(dev); dev->actconfig = NULL; if (dev->state == USB_STATE_CONFIGURED) usb_set_device_state(dev, USB_STATE_ADDRESS); @@ -1879,6 +1880,8 @@ free_interfaces: /* Now that the interfaces are installed, re-enable LPM. */ usb_unlocked_enable_lpm(dev); + /* Enable LTM if it was turned off by usb_disable_device. */ + usb_enable_ltm(dev); /* Now that all the interfaces are set up, register them * to trigger binding of drivers to interfaces. probe() diff --git a/include/linux/usb.h b/include/linux/usb.h index dea2f0de063e..f29831bad235 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -633,6 +633,9 @@ extern void usb_enable_lpm(struct usb_device *udev); extern int usb_unlocked_disable_lpm(struct usb_device *udev); extern void usb_unlocked_enable_lpm(struct usb_device *udev); +extern int usb_disable_ltm(struct usb_device *udev); +extern void usb_enable_ltm(struct usb_device *udev); + /*-------------------------------------------------------------------------*/ /* for drivers using iso endpoints */ -- cgit v1.2.3 From 024f117c2f3c4bb5df6e6696b709e0f3ed7e5dbb Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 5 Jul 2012 17:17:24 -0700 Subject: USB: Add a sysfs file to show LTM capabilities. USB 3.0 devices can optionally support Latency Tolerance Messaging (LTM). Add a new sysfs file in the device directory to show whether a device is LTM capable. This file will be present for both USB 2.0 and USB 3.0 devices. Signed-off-by: Sarah Sharp --- Documentation/ABI/testing/sysfs-bus-usb | 12 ++++++++++++ drivers/usb/core/hub.c | 7 ------- drivers/usb/core/sysfs.c | 10 ++++++++++ include/linux/usb.h | 8 ++++++++ 4 files changed, 30 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb index 6df4e6f57560..5f75f8f7df34 100644 --- a/Documentation/ABI/testing/sysfs-bus-usb +++ b/Documentation/ABI/testing/sysfs-bus-usb @@ -208,3 +208,15 @@ Description: such as ACPI. This file will read either "removable" or "fixed" if the information is available, and "unknown" otherwise. + +What: /sys/bus/usb/devices/.../ltm_capable +Date: July 2012 +Contact: Sarah Sharp +Description: + USB 3.0 devices may optionally support Latency Tolerance + Messaging (LTM). They indicate their support by setting a bit + in the bmAttributes field of their SuperSpeed BOS descriptors. + If that bit is set for the device, ltm_capable will read "yes". + If the device doesn't support LTM, the file will read "no". + The file will be present for all speeds of USB devices, and will + always read "no" for USB 1.1 and USB 2.0 devices. diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index b5bd6bd8fd12..d739f966b5a8 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2610,13 +2610,6 @@ static int check_port_resume_type(struct usb_device *udev, return status; } -static bool usb_device_supports_ltm(struct usb_device *udev) -{ - if (udev->speed != USB_SPEED_SUPER || !udev->bos || !udev->bos->ss_cap) - return false; - return udev->bos->ss_cap->bmAttributes & USB_LTM_SUPPORT; -} - int usb_disable_ltm(struct usb_device *udev) { struct usb_hcd *hcd = bus_to_hcd(udev->bus); diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 777f03c37725..682e8256b95d 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -253,6 +253,15 @@ show_removable(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR(removable, S_IRUGO, show_removable, NULL); +static ssize_t +show_ltm_capable(struct device *dev, struct device_attribute *attr, char *buf) +{ + if (usb_device_supports_ltm(to_usb_device(dev))) + return sprintf(buf, "%s\n", "yes"); + return sprintf(buf, "%s\n", "no"); +} +static DEVICE_ATTR(ltm_capable, S_IRUGO, show_ltm_capable, NULL); + #ifdef CONFIG_PM static ssize_t @@ -649,6 +658,7 @@ static struct attribute *dev_attrs[] = { &dev_attr_authorized.attr, &dev_attr_remove.attr, &dev_attr_removable.attr, + &dev_attr_ltm_capable.attr, NULL, }; static struct attribute_group dev_attr_grp = { diff --git a/include/linux/usb.h b/include/linux/usb.h index f29831bad235..f8506ed0f97b 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -636,6 +636,14 @@ extern void usb_unlocked_enable_lpm(struct usb_device *udev); extern int usb_disable_ltm(struct usb_device *udev); extern void usb_enable_ltm(struct usb_device *udev); +static inline bool usb_device_supports_ltm(struct usb_device *udev) +{ + if (udev->speed != USB_SPEED_SUPER || !udev->bos || !udev->bos->ss_cap) + return false; + return udev->bos->ss_cap->bmAttributes & USB_LTM_SUPPORT; +} + + /*-------------------------------------------------------------------------*/ /* for drivers using iso endpoints */ -- cgit v1.2.3 From 781d0f46d81e2c26c70649903b503bcfe817efc8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 5 Jul 2012 12:19:19 +0100 Subject: irq_domain: Standardise legacy/linear domain selection A large proportion of interrupt controllers that support legacy mappings do so because non-DT systems need to use fixed IRQ numbers when registering devices via buses but can otherwise use a linear mapping. The interrupt controller itself typically is not affected by the mapping used and best practice is to use a linear mapping where possible so drivers frequently select at runtime depending on if a legacy range has been allocated to them. Standardise this behaviour by providing irq_domain_register_simple() which will allocate a linear mapping unless a positive first_irq is provided in which case it will fall back to a legacy mapping. This helps make best practice for irq_domain adoption clearer. Signed-off-by: Mark Brown Signed-off-by: Grant Likely --- Documentation/IRQ-domain.txt | 5 +++++ include/linux/irqdomain.h | 5 +++++ kernel/irq/irqdomain.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+) (limited to 'include') diff --git a/Documentation/IRQ-domain.txt b/Documentation/IRQ-domain.txt index 27dcaabfb4db..1401cece745a 100644 --- a/Documentation/IRQ-domain.txt +++ b/Documentation/IRQ-domain.txt @@ -93,6 +93,7 @@ Linux IRQ number into the hardware. Most drivers cannot use this mapping. ==== Legacy ==== +irq_domain_add_simple() irq_domain_add_legacy() irq_domain_add_legacy_isa() @@ -115,3 +116,7 @@ The legacy map should only be used if fixed IRQ mappings must be supported. For example, ISA controllers would use the legacy map for mapping Linux IRQs 0-15 so that existing ISA drivers get the correct IRQ numbers. + +Most users of legacy mappings should use irq_domain_add_simple() which +will use a legacy domain only if an IRQ range is supplied by the +system and will otherwise use a linear domain mapping. diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index 5abb533eb8eb..17b60be30fff 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -112,6 +112,11 @@ struct irq_domain { }; #ifdef CONFIG_IRQ_DOMAIN +struct irq_domain *irq_domain_add_simple(struct device_node *of_node, + unsigned int size, + unsigned int first_irq, + const struct irq_domain_ops *ops, + void *host_data); struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, unsigned int size, unsigned int first_irq, diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index d3968e91bfd2..0c51958c6335 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -139,6 +139,36 @@ static unsigned int irq_domain_legacy_revmap(struct irq_domain *domain, return hwirq - first_hwirq + domain->revmap_data.legacy.first_irq; } +/** + * irq_domain_add_simple() - Allocate and register a simple irq_domain. + * @of_node: pointer to interrupt controller's device tree node. + * @size: total number of irqs in mapping + * @first_irq: first number of irq block assigned to the domain + * @ops: map/unmap domain callbacks + * @host_data: Controller private data pointer + * + * Allocates a legacy irq_domain if irq_base is positive or a linear + * domain otherwise. + * + * This is intended to implement the expected behaviour for most + * interrupt controllers which is that a linear mapping should + * normally be used unless the system requires a legacy mapping in + * order to support supplying interrupt numbers during non-DT + * registration of devices. + */ +struct irq_domain *irq_domain_add_simple(struct device_node *of_node, + unsigned int size, + unsigned int first_irq, + const struct irq_domain_ops *ops, + void *host_data) +{ + if (first_irq > 0) + return irq_domain_add_legacy(of_node, size, first_irq, 0, + ops, host_data); + else + return irq_domain_add_linear(of_node, size, ops, host_data); +} + /** * irq_domain_add_legacy() - Allocate and register a legacy revmap irq_domain. * @of_node: pointer to interrupt controller's device tree node. -- cgit v1.2.3 From 98aa468e045a0091a7c34d9f5205a629634fabf4 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sun, 17 Jun 2012 16:17:04 -0600 Subject: irqdomain: Support for static IRQ mapping and association. This adds a new strict mapping API for supporting creation of linux IRQs at existing positions within the domain. The new routines are as follows: For dynamic allocation and insertion to specified ranges: - irq_create_identity_mapping() - irq_create_strict_mappings() These will allocate and associate a range of linux IRQs at the specified location. This can be used by controllers that have their own static linux IRQ definitions to map a hwirq range to, as well as for platforms that wish to establish 1:1 identity mapping between linux and hwirq space. For insertion to specified ranges by platforms that do their own irq_desc management: - irq_domain_associate() - irq_domain_associate_many() These in turn call back in to the domain's ->map() routine, for further processing by the platform. Disassociation of IRQs get handled through irq_dispose_mapping() as normal. With these in place it should be possible to begin migration of legacy IRQ domains to linear ones, without requiring special handling for static vs dynamic IRQ definitions in DT vs non-DT paths. This also makes it possible for domains with static mappings to adopt whichever tree model best fits their needs, rather than simply restricting them to linear revmaps. Signed-off-by: Paul Mundt [grant.likely: Reorganized irq_domain_associate{,_many} to have all logic in one place] [grant.likely: Add error checking for unallocated irq_descs at associate time] Signed-off-by: Grant Likely Cc: Benjamin Herrenschmidt Cc: Thomas Gleixner Cc: Rob Herring --- include/linux/irqdomain.h | 19 ++++++++ kernel/irq/irqdomain.c | 113 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 107 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index 17b60be30fff..eab8a0e60b8e 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -149,12 +149,31 @@ static inline struct irq_domain *irq_domain_add_legacy_isa( extern void irq_domain_remove(struct irq_domain *host); +extern int irq_domain_associate_many(struct irq_domain *domain, + unsigned int irq_base, + irq_hw_number_t hwirq_base, int count); +static inline int irq_domain_associate(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + return irq_domain_associate_many(domain, irq, hwirq, 1); +} + extern unsigned int irq_create_mapping(struct irq_domain *host, irq_hw_number_t hwirq); extern void irq_dispose_mapping(unsigned int virq); extern unsigned int irq_find_mapping(struct irq_domain *host, irq_hw_number_t hwirq); extern unsigned int irq_create_direct_mapping(struct irq_domain *host); +extern int irq_create_strict_mappings(struct irq_domain *domain, + unsigned int irq_base, + irq_hw_number_t hwirq_base, int count); + +static inline int irq_create_identity_mapping(struct irq_domain *host, + irq_hw_number_t hwirq) +{ + return irq_create_strict_mappings(host, hwirq, hwirq, 1); +} + extern void irq_radix_revmap_insert(struct irq_domain *host, unsigned int virq, irq_hw_number_t hwirq); extern unsigned int irq_radix_revmap_lookup(struct irq_domain *host, diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 091732c9dbdc..a07d92446b66 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -410,36 +410,61 @@ static void irq_domain_disassociate_many(struct irq_domain *domain, } } -static int irq_setup_virq(struct irq_domain *domain, unsigned int virq, - irq_hw_number_t hwirq) +int irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base, + irq_hw_number_t hwirq_base, int count) { - struct irq_data *irq_data = irq_get_irq_data(virq); + unsigned int virq = irq_base; + irq_hw_number_t hwirq = hwirq_base; + int i; - irq_data->hwirq = hwirq; - irq_data->domain = domain; - if (domain->ops->map && domain->ops->map(domain, virq, hwirq)) { - pr_err("irq-%i==>hwirq-0x%lx mapping failed\n", virq, hwirq); - irq_data->domain = NULL; - irq_data->hwirq = 0; - return -1; - } + pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__, + of_node_full_name(domain->of_node), irq_base, (int)hwirq_base, count); - switch (domain->revmap_type) { - case IRQ_DOMAIN_MAP_LINEAR: - if (hwirq < domain->revmap_data.linear.size) - domain->revmap_data.linear.revmap[hwirq] = virq; - break; - case IRQ_DOMAIN_MAP_TREE: - mutex_lock(&revmap_trees_mutex); - irq_radix_revmap_insert(domain, virq, hwirq); - mutex_unlock(&revmap_trees_mutex); - break; - } + for (i = 0; i < count; i++) { + struct irq_data *irq_data = irq_get_irq_data(virq + i); + + if (WARN(!irq_data, "error: irq_desc not allocated; " + "irq=%i hwirq=0x%x\n", virq + i, (int)hwirq + i)) + return -EINVAL; + if (WARN(irq_data->domain, "error: irq_desc already associated; " + "irq=%i hwirq=0x%x\n", virq + i, (int)hwirq + i)) + return -EINVAL; + }; + + for (i = 0; i < count; i++, virq++, hwirq++) { + struct irq_data *irq_data = irq_get_irq_data(virq); + + irq_data->hwirq = hwirq; + irq_data->domain = domain; + if (domain->ops->map && domain->ops->map(domain, virq, hwirq)) { + pr_err("irq-%i==>hwirq-0x%lx mapping failed\n", virq, hwirq); + irq_data->domain = NULL; + irq_data->hwirq = 0; + goto err_unmap; + } + + switch (domain->revmap_type) { + case IRQ_DOMAIN_MAP_LINEAR: + if (hwirq < domain->revmap_data.linear.size) + domain->revmap_data.linear.revmap[hwirq] = virq; + break; + case IRQ_DOMAIN_MAP_TREE: + mutex_lock(&revmap_trees_mutex); + irq_radix_revmap_insert(domain, virq, hwirq); + mutex_unlock(&revmap_trees_mutex); + break; + } - irq_clear_status_flags(virq, IRQ_NOREQUEST); + irq_clear_status_flags(virq, IRQ_NOREQUEST); + } return 0; + + err_unmap: + irq_domain_disassociate_many(domain, irq_base, i); + return -EINVAL; } +EXPORT_SYMBOL_GPL(irq_domain_associate_many); /** * irq_create_direct_mapping() - Allocate an irq for direct mapping @@ -472,7 +497,7 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain) } pr_debug("create_direct obtained virq %d\n", virq); - if (irq_setup_virq(domain, virq, virq)) { + if (irq_domain_associate(domain, virq, virq)) { irq_free_desc(virq); return 0; } @@ -533,7 +558,7 @@ unsigned int irq_create_mapping(struct irq_domain *domain, return 0; } - if (irq_setup_virq(domain, virq, hwirq)) { + if (irq_domain_associate(domain, virq, hwirq)) { irq_free_desc(virq); return 0; } @@ -545,6 +570,44 @@ unsigned int irq_create_mapping(struct irq_domain *domain, } EXPORT_SYMBOL_GPL(irq_create_mapping); +/** + * irq_create_strict_mappings() - Map a range of hw irqs to fixed linux irqs + * @domain: domain owning the interrupt range + * @irq_base: beginning of linux IRQ range + * @hwirq_base: beginning of hardware IRQ range + * @count: Number of interrupts to map + * + * This routine is used for allocating and mapping a range of hardware + * irqs to linux irqs where the linux irq numbers are at pre-defined + * locations. For use by controllers that already have static mappings + * to insert in to the domain. + * + * Non-linear users can use irq_create_identity_mapping() for IRQ-at-a-time + * domain insertion. + * + * 0 is returned upon success, while any failure to establish a static + * mapping is treated as an error. + */ +int irq_create_strict_mappings(struct irq_domain *domain, unsigned int irq_base, + irq_hw_number_t hwirq_base, int count) +{ + int ret; + + ret = irq_alloc_descs(irq_base, irq_base, count, + of_node_to_nid(domain->of_node)); + if (unlikely(ret < 0)) + return ret; + + ret = irq_domain_associate_many(domain, irq_base, hwirq_base, count); + if (unlikely(ret < 0)) { + irq_free_descs(irq_base, count); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(irq_create_strict_mappings); + unsigned int irq_create_of_mapping(struct device_node *controller, const u32 *intspec, unsigned int intsize) { -- cgit v1.2.3 From d6b0d1f7058f7cf818138cd7fd116dca3f3576d9 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sun, 3 Jun 2012 22:04:37 -0700 Subject: irqdomain: Eliminate dedicated radix lookup functions In preparation to remove the slow revmap path, eliminate the public radix revmap lookup functions. This simplifies the code and makes the slowpath removal patch a lot simpler. Signed-off-by: Grant Likely Cc: Paul Mundt Cc: Benjamin Herrenschmidt Cc: Thomas Gleixner Cc: Rob Herring --- arch/powerpc/sysdev/xics/icp-hv.c | 2 +- arch/powerpc/sysdev/xics/icp-native.c | 2 +- include/linux/irqdomain.h | 4 --- kernel/irq/irqdomain.c | 60 +---------------------------------- 4 files changed, 3 insertions(+), 65 deletions(-) (limited to 'include') diff --git a/arch/powerpc/sysdev/xics/icp-hv.c b/arch/powerpc/sysdev/xics/icp-hv.c index 253dce98c16e..14469cf9df68 100644 --- a/arch/powerpc/sysdev/xics/icp-hv.c +++ b/arch/powerpc/sysdev/xics/icp-hv.c @@ -111,7 +111,7 @@ static unsigned int icp_hv_get_irq(void) if (vec == XICS_IRQ_SPURIOUS) return NO_IRQ; - irq = irq_radix_revmap_lookup(xics_host, vec); + irq = irq_find_mapping(xics_host, vec); if (likely(irq != NO_IRQ)) { xics_push_cppr(vec); return irq; diff --git a/arch/powerpc/sysdev/xics/icp-native.c b/arch/powerpc/sysdev/xics/icp-native.c index 4c79b6fbee1c..48861d3fcd07 100644 --- a/arch/powerpc/sysdev/xics/icp-native.c +++ b/arch/powerpc/sysdev/xics/icp-native.c @@ -119,7 +119,7 @@ static unsigned int icp_native_get_irq(void) if (vec == XICS_IRQ_SPURIOUS) return NO_IRQ; - irq = irq_radix_revmap_lookup(xics_host, vec); + irq = irq_find_mapping(xics_host, vec); if (likely(irq != NO_IRQ)) { xics_push_cppr(vec); return irq; diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index eab8a0e60b8e..0d5b17bf5e51 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -174,10 +174,6 @@ static inline int irq_create_identity_mapping(struct irq_domain *host, return irq_create_strict_mappings(host, hwirq, hwirq, 1); } -extern void irq_radix_revmap_insert(struct irq_domain *host, unsigned int virq, - irq_hw_number_t hwirq); -extern unsigned int irq_radix_revmap_lookup(struct irq_domain *host, - irq_hw_number_t hwirq); extern unsigned int irq_linear_revmap(struct irq_domain *host, irq_hw_number_t hwirq); diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index a07d92446b66..f540bb1eff84 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -450,7 +450,7 @@ int irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base, break; case IRQ_DOMAIN_MAP_TREE: mutex_lock(&revmap_trees_mutex); - irq_radix_revmap_insert(domain, virq, hwirq); + radix_tree_insert(&domain->revmap_data.tree, hwirq, irq_data); mutex_unlock(&revmap_trees_mutex); break; } @@ -723,64 +723,6 @@ unsigned int irq_find_mapping(struct irq_domain *domain, } EXPORT_SYMBOL_GPL(irq_find_mapping); -/** - * irq_radix_revmap_lookup() - Find a linux irq from a hw irq number. - * @domain: domain owning this hardware interrupt - * @hwirq: hardware irq number in that domain space - * - * This is a fast path, for use by irq controller code that uses radix tree - * revmaps - */ -unsigned int irq_radix_revmap_lookup(struct irq_domain *domain, - irq_hw_number_t hwirq) -{ - struct irq_data *irq_data; - - if (WARN_ON_ONCE(domain->revmap_type != IRQ_DOMAIN_MAP_TREE)) - return irq_find_mapping(domain, hwirq); - - /* - * Freeing an irq can delete nodes along the path to - * do the lookup via call_rcu. - */ - rcu_read_lock(); - irq_data = radix_tree_lookup(&domain->revmap_data.tree, hwirq); - rcu_read_unlock(); - - /* - * If found in radix tree, then fine. - * Else fallback to linear lookup - this should not happen in practice - * as it means that we failed to insert the node in the radix tree. - */ - return irq_data ? irq_data->irq : irq_find_mapping(domain, hwirq); -} -EXPORT_SYMBOL_GPL(irq_radix_revmap_lookup); - -/** - * irq_radix_revmap_insert() - Insert a hw irq to linux irq number mapping. - * @domain: domain owning this hardware interrupt - * @virq: linux irq number - * @hwirq: hardware irq number in that domain space - * - * This is for use by irq controllers that use a radix tree reverse - * mapping for fast lookup. - */ -void irq_radix_revmap_insert(struct irq_domain *domain, unsigned int virq, - irq_hw_number_t hwirq) -{ - struct irq_data *irq_data = irq_get_irq_data(virq); - - if (WARN_ON(domain->revmap_type != IRQ_DOMAIN_MAP_TREE)) - return; - - if (virq) { - mutex_lock(&revmap_trees_mutex); - radix_tree_insert(&domain->revmap_data.tree, hwirq, irq_data); - mutex_unlock(&revmap_trees_mutex); - } -} -EXPORT_SYMBOL_GPL(irq_radix_revmap_insert); - /** * irq_linear_revmap() - Find a linux irq from a hw irq number. * @domain: domain owning this hardware interrupt -- cgit v1.2.3 From 396f2feb05d7cc5549c611c05abfb4108cd1c6d6 Mon Sep 17 00:00:00 2001 From: Jack Morgenstein Date: Tue, 19 Jun 2012 11:21:42 +0300 Subject: mlx4_core: Implement mechanism for reserved Q_Keys The SR-IOV special QP tunneling mechanism uses proxy special QPs (instead of the real special QPs) for MADs on guests. These proxy QPs send their packets to a "tunnel" QP owned by the master. The master then forwards the MAD (after any required paravirtualization) to the real special QP, which sends out the MAD. For security reasons (i.e., to prevent guests from sending MADs to tunnel QPs belonging to other guests), each proxy-tunnel QP pair is assigned a unique, reserved, Q_Key. These Q_Keys are available only for proxy and tunnel QPs -- if the guest tries to use these Q_Keys with other QPs, it will fail. This patch introduces a mechanism for reserving a block of 64K Q_Keys for proxy/tunneling use. The patch introduces also two new fields into mlx4_dev: base_sqpn and base_tunnel_sqpn. In SR-IOV mode, the QP numbers for the "real," proxy, and tunnel sqps are added to the reserved QPN area (so that they will not change). There are 8 special QPs per port in the HCA, and each of them is assigned both a proxy and a tunnel QP, for each VF and for the PF as well in SR-IOV mode. The QPNs for these QPs are arranged as follows: 1. The real SQP numbers (8) 2. The proxy SQPs (8 * (max number of VFs + max number of PFs) 3. The tunnel SQPs (8 * (max number of VFs + max number of PFs) To support these QPs, two new fields are added to struct mlx4_dev: base_sqp: this is the QP number of the first of the real SQPs base_tunnel_sqp: this is the qp number of the first qp in the tunnel sqp region. (On guests, this is the first tunnel sqp of the 8 which are assigned to that guest). In addition, in SR-IOV mode, sqp_start is the number of the first proxy SQP in the proxy SQP region. (In guests, this is the first proxy SQP of the 8 which are assigned to that guest) Note that in non-SR-IOV mode, there are no proxies and no tunnels. In this case, sqp_start is set to sqp_base -- which minimizes code changes. Signed-off-by: Jack Morgenstein Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier --- drivers/net/ethernet/mellanox/mlx4/main.c | 17 +++++++++++++++++ include/linux/mlx4/device.h | 11 +++++++++++ 2 files changed, 28 insertions(+) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 83afb1541a74..81154a16d6b8 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -391,6 +391,23 @@ static int mlx4_how_many_lives_vf(struct mlx4_dev *dev) return ret; } +int mlx4_get_parav_qkey(struct mlx4_dev *dev, u32 qpn, u32 *qkey) +{ + u32 qk = MLX4_RESERVED_QKEY_BASE; + if (qpn >= dev->caps.base_tunnel_sqpn + 8 * MLX4_MFUNC_MAX || + qpn < dev->caps.sqp_start) + return -EINVAL; + + if (qpn >= dev->caps.base_tunnel_sqpn) + /* tunnel qp */ + qk += qpn - dev->caps.base_tunnel_sqpn; + else + qk += qpn - dev->caps.sqp_start; + *qkey = qk; + return 0; +} +EXPORT_SYMBOL(mlx4_get_parav_qkey); + int mlx4_is_slave_active(struct mlx4_dev *dev, int slave) { struct mlx4_priv *priv = mlx4_priv(dev); diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 7fbdc89de495..c30a314e095c 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -56,6 +56,13 @@ enum { MLX4_MAX_PORTS = 2 }; +/* base qkey for use in sriov tunnel-qp/proxy-qp communication. + * These qkeys must not be allowed for general use. This is a 64k range, + * and to test for violation, we use the mask (protect against future chg). + */ +#define MLX4_RESERVED_QKEY_BASE (0xFFFF0000) +#define MLX4_RESERVED_QKEY_MASK (0xFFFF0000) + enum { MLX4_BOARD_ID_LEN = 64 }; @@ -293,6 +300,8 @@ struct mlx4_caps { int max_qp_init_rdma; int max_qp_dest_rdma; int sqp_start; + u32 base_sqpn; + u32 base_tunnel_sqpn; int num_srqs; int max_srq_wqes; int max_srq_sge; @@ -772,4 +781,6 @@ int mlx4_wol_write(struct mlx4_dev *dev, u64 config, int port); int mlx4_counter_alloc(struct mlx4_dev *dev, u32 *idx); void mlx4_counter_free(struct mlx4_dev *dev, u32 idx); +int mlx4_get_parav_qkey(struct mlx4_dev *dev, u32 qpn, u32 *qkey); + #endif /* MLX4_DEVICE_H */ -- cgit v1.2.3 From 6634961c14d38ef64ec284c07aecb03d3dd03b4a Mon Sep 17 00:00:00 2001 From: Jack Morgenstein Date: Tue, 19 Jun 2012 11:21:44 +0300 Subject: mlx4: Put physical GID and P_Key table sizes in mlx4_phys_caps struct and paravirtualize them To allow easy paravirtualization of P_Key and GID table sizes, keep paravirtualized sizes in mlx4_dev->caps, but save the actual physical sizes from FW in struct: mlx4_dev->phys_cap. In addition, in SR-IOV mode, do the following: 1. Reduce reported P_Key table size by 1. This is done to reserve the highest P_Key index for internal use, for declaring an invalid P_Key in P_Key paravirtualization. We require a P_Key index which always contain an invalid P_Key value for this purpose (i.e., one which cannot be modified by the subnet manager). The way to do this is to reduce the P_Key table size reported to the subnet manager by 1, so that it will not attempt to access the P_Key at index #127. 2. Paravirtualize the GID table size to 1. Thus, each guest sees only a single GID (at its paravirtualized index 0). In addition, since we are paravirtualizing the GID table size to 1, we add paravirtualization of the master GID event here (i.e., we do not do ib_dispatch_event() for the GUID change event on the master, since its (only) GUID never changes). Signed-off-by: Jack Morgenstein Signed-off-by: Roland Dreier --- drivers/infiniband/hw/mlx4/mad.c | 10 ++++--- drivers/net/ethernet/mellanox/mlx4/fw.c | 43 +++++++++++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx4/main.c | 32 ++++++++++++++++++++--- drivers/net/ethernet/mellanox/mlx4/mlx4.h | 4 ++- drivers/net/ethernet/mellanox/mlx4/port.c | 11 ++++++-- include/linux/mlx4/device.h | 2 ++ 6 files changed, 92 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c index 58c45fb5bd31..c27141fef1ab 100644 --- a/drivers/infiniband/hw/mlx4/mad.c +++ b/drivers/infiniband/hw/mlx4/mad.c @@ -184,8 +184,10 @@ static void smp_snoop(struct ib_device *ibdev, u8 port_num, struct ib_mad *mad, break; case IB_SMP_ATTR_GUID_INFO: - mlx4_ib_dispatch_event(dev, port_num, - IB_EVENT_GID_CHANGE); + /* paravirtualized master's guid is guid 0 -- does not change */ + if (!mlx4_is_master(dev->dev)) + mlx4_ib_dispatch_event(dev, port_num, + IB_EVENT_GID_CHANGE); break; default: break; @@ -487,7 +489,9 @@ void handle_port_mgmt_change_event(struct work_struct *work) mlx4_ib_dispatch_event(dev, port, IB_EVENT_PKEY_CHANGE); break; case MLX4_DEV_PMC_SUBTYPE_GUID_INFO: - mlx4_ib_dispatch_event(dev, port, IB_EVENT_GID_CHANGE); + /* paravirtualized master's guid is guid 0 -- does not change */ + if (!mlx4_is_master(dev->dev)) + mlx4_ib_dispatch_event(dev, port, IB_EVENT_GID_CHANGE); break; default: pr_warn("Unsupported subtype 0x%x for " diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 5549f6b3bb67..473d63b63b4e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -730,9 +730,12 @@ int mlx4_QUERY_PORT_wrapper(struct mlx4_dev *dev, int slave, { u64 def_mac; u8 port_type; + u16 short_field; int err; #define MLX4_VF_PORT_NO_LINK_SENSE_MASK 0xE0 +#define QUERY_PORT_CUR_MAX_PKEY_OFFSET 0x0c +#define QUERY_PORT_CUR_MAX_GID_OFFSET 0x0e err = mlx4_cmd_box(dev, 0, outbox->dma, vhcr->in_modifier, 0, MLX4_CMD_QUERY_PORT, MLX4_CMD_TIME_CLASS_B, @@ -755,11 +758,51 @@ int mlx4_QUERY_PORT_wrapper(struct mlx4_dev *dev, int slave, MLX4_PUT(outbox->buf, port_type, QUERY_PORT_SUPPORTED_TYPE_OFFSET); + + short_field = 1; /* slave max gids */ + MLX4_PUT(outbox->buf, short_field, + QUERY_PORT_CUR_MAX_GID_OFFSET); + + short_field = dev->caps.pkey_table_len[vhcr->in_modifier]; + MLX4_PUT(outbox->buf, short_field, + QUERY_PORT_CUR_MAX_PKEY_OFFSET); } return err; } +int mlx4_get_slave_pkey_gid_tbl_len(struct mlx4_dev *dev, u8 port, + int *gid_tbl_len, int *pkey_tbl_len) +{ + struct mlx4_cmd_mailbox *mailbox; + u32 *outbox; + u16 field; + int err; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + err = mlx4_cmd_box(dev, 0, mailbox->dma, port, 0, + MLX4_CMD_QUERY_PORT, MLX4_CMD_TIME_CLASS_B, + MLX4_CMD_WRAPPED); + if (err) + goto out; + + outbox = mailbox->buf; + + MLX4_GET(field, outbox, QUERY_PORT_CUR_MAX_GID_OFFSET); + *gid_tbl_len = field; + + MLX4_GET(field, outbox, QUERY_PORT_CUR_MAX_PKEY_OFFSET); + *pkey_tbl_len = field; + +out: + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} +EXPORT_SYMBOL(mlx4_get_slave_pkey_gid_tbl_len); + int mlx4_map_cmd(struct mlx4_dev *dev, u16 op, struct mlx4_icm *icm, u64 virt) { struct mlx4_cmd_mailbox *mailbox; diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 58544b72bacb..5df3ac40a490 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -215,6 +215,10 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) for (i = 1; i <= dev->caps.num_ports; ++i) { dev->caps.vl_cap[i] = dev_cap->max_vl[i]; dev->caps.ib_mtu_cap[i] = dev_cap->ib_mtu[i]; + dev->phys_caps.gid_phys_table_len[i] = dev_cap->max_gids[i]; + dev->phys_caps.pkey_phys_table_len[i] = dev_cap->max_pkeys[i]; + /* set gid and pkey table operating lengths by default + * to non-sriov values */ dev->caps.gid_table_len[i] = dev_cap->max_gids[i]; dev->caps.pkey_table_len[i] = dev_cap->max_pkeys[i]; dev->caps.port_width_cap[i] = dev_cap->max_port_width[i]; @@ -498,8 +502,13 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) return -ENODEV; } - for (i = 1; i <= dev->caps.num_ports; ++i) + for (i = 1; i <= dev->caps.num_ports; ++i) { dev->caps.port_mask[i] = dev->caps.port_type[i]; + if (mlx4_get_slave_pkey_gid_tbl_len(dev, i, + &dev->caps.gid_table_len[i], + &dev->caps.pkey_table_len[i])) + return -ENODEV; + } if (dev->caps.uar_page_size * (dev->caps.num_uars - dev->caps.reserved_uars) > @@ -536,7 +545,7 @@ int mlx4_change_port_types(struct mlx4_dev *dev, for (port = 1; port <= dev->caps.num_ports; port++) { mlx4_CLOSE_PORT(dev, port); dev->caps.port_type[port] = port_types[port - 1]; - err = mlx4_SET_PORT(dev, port); + err = mlx4_SET_PORT(dev, port, -1); if (err) { mlx4_err(dev, "Failed to set port %d, " "aborting\n", port); @@ -722,7 +731,7 @@ static ssize_t set_port_ib_mtu(struct device *dev, mlx4_unregister_device(mdev); for (port = 1; port <= mdev->caps.num_ports; port++) { mlx4_CLOSE_PORT(mdev, port); - err = mlx4_SET_PORT(mdev, port); + err = mlx4_SET_PORT(mdev, port, -1); if (err) { mlx4_err(mdev, "Failed to set port %d, " "aborting\n", port); @@ -1173,6 +1182,17 @@ err: return -EIO; } +static void mlx4_parav_master_pf_caps(struct mlx4_dev *dev) +{ + int i; + + for (i = 1; i <= dev->caps.num_ports; i++) { + dev->caps.gid_table_len[i] = 1; + dev->caps.pkey_table_len[i] = + dev->phys_caps.pkey_phys_table_len[i] - 1; + } +} + static int mlx4_init_hca(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); @@ -1212,6 +1232,9 @@ static int mlx4_init_hca(struct mlx4_dev *dev) goto err_stop_fw; } + if (mlx4_is_master(dev)) + mlx4_parav_master_pf_caps(dev); + profile = default_profile; icm_size = mlx4_make_profile(dev, &profile, &dev_cap, @@ -1500,7 +1523,8 @@ static int mlx4_setup_hca(struct mlx4_dev *dev) else dev->caps.port_ib_mtu[port] = IB_MTU_4096; - err = mlx4_SET_PORT(dev, port); + err = mlx4_SET_PORT(dev, port, mlx4_is_master(dev) ? + dev->caps.pkey_table_len[port] : -1); if (err) { mlx4_err(dev, "Failed to set port %d, aborting\n", port); diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 4d11d12b9db4..cde6e511899f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -969,7 +969,7 @@ int mlx4_change_port_types(struct mlx4_dev *dev, void mlx4_init_mac_table(struct mlx4_dev *dev, struct mlx4_mac_table *table); void mlx4_init_vlan_table(struct mlx4_dev *dev, struct mlx4_vlan_table *table); -int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port); +int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port, int pkey_tbl_sz); /* resource tracker functions*/ int mlx4_get_slave_from_resource_id(struct mlx4_dev *dev, enum mlx4_resource resource_type, @@ -1012,6 +1012,8 @@ int mlx4_QUERY_PORT_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_cmd_info *cmd); int mlx4_get_port_ib_caps(struct mlx4_dev *dev, u8 port, __be32 *caps); +int mlx4_get_slave_pkey_gid_tbl_len(struct mlx4_dev *dev, u8 port, + int *gid_tbl_len, int *pkey_tbl_len); int mlx4_QP_ATTACH_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_vhcr *vhcr, diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c index a8fb52992c64..90dc47542b8b 100644 --- a/drivers/net/ethernet/mellanox/mlx4/port.c +++ b/drivers/net/ethernet/mellanox/mlx4/port.c @@ -726,14 +726,15 @@ int mlx4_SET_PORT_wrapper(struct mlx4_dev *dev, int slave, enum { MLX4_SET_PORT_VL_CAP = 4, /* bits 7:4 */ MLX4_SET_PORT_MTU_CAP = 12, /* bits 15:12 */ + MLX4_CHANGE_PORT_PKEY_TBL_SZ = 20, MLX4_CHANGE_PORT_VL_CAP = 21, MLX4_CHANGE_PORT_MTU_CAP = 22, }; -int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port) +int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port, int pkey_tbl_sz) { struct mlx4_cmd_mailbox *mailbox; - int err, vl_cap; + int err, vl_cap, pkey_tbl_flag = 0; if (dev->caps.port_type[port] == MLX4_PORT_TYPE_ETH) return 0; @@ -746,11 +747,17 @@ int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port) ((__be32 *) mailbox->buf)[1] = dev->caps.ib_port_def_cap[port]; + if (pkey_tbl_sz >= 0 && mlx4_is_master(dev)) { + pkey_tbl_flag = 1; + ((__be16 *) mailbox->buf)[20] = cpu_to_be16(pkey_tbl_sz); + } + /* IB VL CAP enum isn't used by the firmware, just numerical values */ for (vl_cap = 8; vl_cap >= 1; vl_cap >>= 1) { ((__be32 *) mailbox->buf)[0] = cpu_to_be32( (1 << MLX4_CHANGE_PORT_MTU_CAP) | (1 << MLX4_CHANGE_PORT_VL_CAP) | + (pkey_tbl_flag << MLX4_CHANGE_PORT_PKEY_TBL_SZ) | (dev->caps.port_ib_mtu[port] << MLX4_SET_PORT_MTU_CAP) | (vl_cap << MLX4_SET_PORT_VL_CAP)); err = mlx4_cmd(dev, mailbox->dma, port, 0, MLX4_CMD_SET_PORT, diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index c30a314e095c..441caf1a497d 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -268,6 +268,8 @@ static inline u64 mlx4_fw_ver(u64 major, u64 minor, u64 subminor) } struct mlx4_phys_caps { + u32 gid_phys_table_len[MLX4_MAX_PORTS + 1]; + u32 pkey_phys_table_len[MLX4_MAX_PORTS + 1]; u32 num_phys_eqs; }; -- cgit v1.2.3 From d9c2ede63c74048dfddbb129c59ac01176b0ab71 Mon Sep 17 00:00:00 2001 From: Eldad Zack Date: Fri, 6 Jul 2012 21:31:56 +0200 Subject: sunrpc/cache.h: fix coding style Neaten code style in get_int(). Also use sizeof() instead of hard coded number as suggested by Joe Perches . Cc: Joe Perches Signed-off-by: Eldad Zack Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/cache.h | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index f5fd6160dbca..6def1f6cc269 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h @@ -219,11 +219,17 @@ static inline int get_int(char **bpp, int *anint) char buf[50]; char *ep; int rv; - int len = qword_get(bpp, buf, 50); - if (len < 0) return -EINVAL; - if (len ==0) return -ENOENT; + int len = qword_get(bpp, buf, sizeof(buf)); + + if (len < 0) + return -EINVAL; + if (len == 0) + return -ENOENT; + rv = simple_strtol(buf, &ep, 0); - if (*ep) return -EINVAL; + if (*ep) + return -EINVAL; + *anint = rv; return 0; } -- cgit v1.2.3 From bbf43dc888833ac0539e437dbaeb28bfd4fbab9f Mon Sep 17 00:00:00 2001 From: Eldad Zack Date: Fri, 6 Jul 2012 21:31:57 +0200 Subject: sunrpc/cache.h: replace simple_strtoul This patch replaces the usage of simple_strtoul with kstrtoint in get_int(), since the simple_str* family doesn't account for overflow and is deprecated. Also, in this specific case, the long from strtol is silently converted to an int by the caller. As Joe Perches suggested, this patch also removes the redundant temporary variable rv, since kstrtoint() will not write to anint unless it's successful. Cc: Joe Perches Signed-off-by: Eldad Zack Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/cache.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index 6def1f6cc269..af42596a82f9 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h @@ -217,8 +217,6 @@ extern int qword_get(char **bpp, char *dest, int bufsize); static inline int get_int(char **bpp, int *anint) { char buf[50]; - char *ep; - int rv; int len = qword_get(bpp, buf, sizeof(buf)); if (len < 0) @@ -226,11 +224,9 @@ static inline int get_int(char **bpp, int *anint) if (len == 0) return -ENOENT; - rv = simple_strtol(buf, &ep, 0); - if (*ep) + if (kstrtoint(buf, 0, anint)) return -EINVAL; - *anint = rv; return 0; } -- cgit v1.2.3 From 357c3f0a6c7613f7230fcaf1eb16190ed2a4b0af Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Fri, 29 Jun 2012 19:06:32 +0530 Subject: clk: Add support for rate table based dividers Some divider clks do not have any obvious relationship between the divider and the value programmed in the register. For instance, say a value of 1 could signify divide by 6 and a value of 2 could signify divide by 4 etc. Also there are dividers where not all values possible based on the bitfield width are valid. For instance a 3 bit wide bitfield can be used to program a value from 0 to 7. However its possible that only 0 to 4 are valid values. All these cases need the platform code to pass a simple table of divider/value tuple, so the framework knows the exact value to be written based on the divider calculation and can also do better error checking. This patch adds support for such rate table based dividers and as part of the support adds a new registration function 'clk_register_divider_table()' and a new macro for static definition 'DEFINE_CLK_DIVIDER_TABLE'. Signed-off-by: Rajendra Nayak Signed-off-by: Mike Turquette --- drivers/clk/clk-divider.c | 125 +++++++++++++++++++++++++++++++++++++------ include/linux/clk-private.h | 20 ++++++- include/linux/clk-provider.h | 12 +++++ 3 files changed, 139 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index e548c4328f3c..02a4da98176b 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -32,30 +32,69 @@ #define div_mask(d) ((1 << (d->width)) - 1) #define is_power_of_two(i) !(i & ~i) +static unsigned int _get_table_maxdiv(const struct clk_div_table *table) +{ + unsigned int maxdiv = 0; + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div > maxdiv) + maxdiv = clkt->div; + return maxdiv; +} + static unsigned int _get_maxdiv(struct clk_divider *divider) { if (divider->flags & CLK_DIVIDER_ONE_BASED) return div_mask(divider); if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) return 1 << div_mask(divider); + if (divider->table) + return _get_table_maxdiv(divider->table); return div_mask(divider) + 1; } +static unsigned int _get_table_div(const struct clk_div_table *table, + unsigned int val) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->val == val) + return clkt->div; + return 0; +} + static unsigned int _get_div(struct clk_divider *divider, unsigned int val) { if (divider->flags & CLK_DIVIDER_ONE_BASED) return val; if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) return 1 << val; + if (divider->table) + return _get_table_div(divider->table, val); return val + 1; } +static unsigned int _get_table_val(const struct clk_div_table *table, + unsigned int div) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div == div) + return clkt->val; + return 0; +} + static unsigned int _get_val(struct clk_divider *divider, u8 div) { if (divider->flags & CLK_DIVIDER_ONE_BASED) return div; if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) return __ffs(div); + if (divider->table) + return _get_table_val(divider->table, div); return div - 1; } @@ -84,6 +123,26 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw, */ #define MULT_ROUND_UP(r, m) ((r) * (m) + (m) - 1) +static bool _is_valid_table_div(const struct clk_div_table *table, + unsigned int div) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div == div) + return true; + return false; +} + +static bool _is_valid_div(struct clk_divider *divider, unsigned int div) +{ + if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) + return is_power_of_two(div); + if (divider->table) + return _is_valid_table_div(divider->table, div); + return true; +} + static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, unsigned long *best_parent_rate) { @@ -111,8 +170,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, maxdiv = min(ULONG_MAX / rate, maxdiv); for (i = 1; i <= maxdiv; i++) { - if ((divider->flags & CLK_DIVIDER_POWER_OF_TWO) - && (!is_power_of_two(i))) + if (!_is_valid_div(divider, i)) continue; parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), MULT_ROUND_UP(rate, i)); @@ -176,22 +234,11 @@ const struct clk_ops clk_divider_ops = { }; EXPORT_SYMBOL_GPL(clk_divider_ops); -/** - * clk_register_divider - register a divider clock with the clock framework - * @dev: device registering this clock - * @name: name of this clock - * @parent_name: name of clock's parent - * @flags: framework-specific flags - * @reg: register address to adjust divider - * @shift: number of bits to shift the bitfield - * @width: width of the bitfield - * @clk_divider_flags: divider-specific flags for this clock - * @lock: shared register lock for this clock - */ -struct clk *clk_register_divider(struct device *dev, const char *name, +static struct clk *_register_divider(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, - u8 clk_divider_flags, spinlock_t *lock) + u8 clk_divider_flags, const struct clk_div_table *table, + spinlock_t *lock) { struct clk_divider *div; struct clk *clk; @@ -217,6 +264,7 @@ struct clk *clk_register_divider(struct device *dev, const char *name, div->flags = clk_divider_flags; div->lock = lock; div->hw.init = &init; + div->table = table; /* register the clock */ clk = clk_register(dev, &div->hw); @@ -226,3 +274,48 @@ struct clk *clk_register_divider(struct device *dev, const char *name, return clk; } + +/** + * clk_register_divider - register a divider clock with the clock framework + * @dev: device registering this clock + * @name: name of this clock + * @parent_name: name of clock's parent + * @flags: framework-specific flags + * @reg: register address to adjust divider + * @shift: number of bits to shift the bitfield + * @width: width of the bitfield + * @clk_divider_flags: divider-specific flags for this clock + * @lock: shared register lock for this clock + */ +struct clk *clk_register_divider(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_divider_flags, spinlock_t *lock) +{ + return _register_divider(dev, name, parent_name, flags, reg, shift, + width, clk_divider_flags, NULL, lock); +} + +/** + * clk_register_divider_table - register a table based divider clock with + * the clock framework + * @dev: device registering this clock + * @name: name of this clock + * @parent_name: name of clock's parent + * @flags: framework-specific flags + * @reg: register address to adjust divider + * @shift: number of bits to shift the bitfield + * @width: width of the bitfield + * @clk_divider_flags: divider-specific flags for this clock + * @table: array of divider/value pairs ending with a div set to 0 + * @lock: shared register lock for this clock + */ +struct clk *clk_register_divider_table(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_divider_flags, const struct clk_div_table *table, + spinlock_t *lock) +{ + return _register_divider(dev, name, parent_name, flags, reg, shift, + width, clk_divider_flags, table, lock); +} diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h index eb3f84bc5325..cc9972d1429c 100644 --- a/include/linux/clk-private.h +++ b/include/linux/clk-private.h @@ -103,9 +103,9 @@ struct clk { DEFINE_CLK(_name, clk_gate_ops, _flags, \ _name##_parent_names, _name##_parents); -#define DEFINE_CLK_DIVIDER(_name, _parent_name, _parent_ptr, \ +#define _DEFINE_CLK_DIVIDER(_name, _parent_name, _parent_ptr, \ _flags, _reg, _shift, _width, \ - _divider_flags, _lock) \ + _divider_flags, _table, _lock) \ static struct clk _name; \ static const char *_name##_parent_names[] = { \ _parent_name, \ @@ -121,11 +121,27 @@ struct clk { .shift = _shift, \ .width = _width, \ .flags = _divider_flags, \ + .table = _table, \ .lock = _lock, \ }; \ DEFINE_CLK(_name, clk_divider_ops, _flags, \ _name##_parent_names, _name##_parents); +#define DEFINE_CLK_DIVIDER(_name, _parent_name, _parent_ptr, \ + _flags, _reg, _shift, _width, \ + _divider_flags, _lock) \ + _DEFINE_CLK_DIVIDER(_name, _parent_name, _parent_ptr, \ + _flags, _reg, _shift, _width, \ + _divider_flags, NULL, _lock) + +#define DEFINE_CLK_DIVIDER_TABLE(_name, _parent_name, \ + _parent_ptr, _flags, _reg, \ + _shift, _width, _divider_flags, \ + _table, _lock) \ + _DEFINE_CLK_DIVIDER(_name, _parent_name, _parent_ptr, \ + _flags, _reg, _shift, _width, \ + _divider_flags, _table, _lock) \ + #define DEFINE_CLK_MUX(_name, _parent_names, _parents, _flags, \ _reg, _shift, _width, \ _mux_flags, _lock) \ diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 4a0b483986c3..79caee9f1489 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -203,6 +203,11 @@ struct clk *clk_register_gate(struct device *dev, const char *name, void __iomem *reg, u8 bit_idx, u8 clk_gate_flags, spinlock_t *lock); +struct clk_div_table { + unsigned int val; + unsigned int div; +}; + /** * struct clk_divider - adjustable divider clock * @@ -210,6 +215,7 @@ struct clk *clk_register_gate(struct device *dev, const char *name, * @reg: register containing the divider * @shift: shift to the divider bit field * @width: width of the divider bit field + * @table: array of value/divider pairs, last entry should have div = 0 * @lock: register lock * * Clock with an adjustable divider affecting its output frequency. Implements @@ -229,6 +235,7 @@ struct clk_divider { u8 shift; u8 width; u8 flags; + const struct clk_div_table *table; spinlock_t *lock; }; @@ -240,6 +247,11 @@ struct clk *clk_register_divider(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags, spinlock_t *lock); +struct clk *clk_register_divider_table(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_divider_flags, const struct clk_div_table *table, + spinlock_t *lock); /** * struct clk_mux - multiplexer clock -- cgit v1.2.3 From f7d8caadfd2813cbada82ce9041b13c38e8e5282 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Fri, 1 Jun 2012 14:02:47 +0530 Subject: clk: Add CLK_IS_BASIC flag to identify basic clocks Most platforms end up using a mix of basic clock types and some which use clk_hw_foo struct for filling in custom platform information when the clocks don't fit into basic types supported. In platform code, its useful to know if a clock is using a basic type or clk_hw_foo, which helps platforms know if they can safely use to_clk_hw_foo to derive the clk_hw_foo pointer from clk_hw. Mark all basic clocks with a CLK_IS_BASIC flag. Signed-off-by: Rajendra Nayak Signed-off-by: Mike Turquette --- drivers/clk/clk-divider.c | 2 +- drivers/clk/clk-fixed-factor.c | 2 +- drivers/clk/clk-fixed-rate.c | 2 +- drivers/clk/clk-gate.c | 2 +- drivers/clk/clk-mux.c | 2 +- include/linux/clk-private.h | 2 +- include/linux/clk-provider.h | 1 + 7 files changed, 7 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 02a4da98176b..a9204c69148d 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -253,7 +253,7 @@ static struct clk *_register_divider(struct device *dev, const char *name, init.name = name; init.ops = &clk_divider_ops; - init.flags = flags; + init.flags = flags | CLK_IS_BASIC; init.parent_names = (parent_name ? &parent_name: NULL); init.num_parents = (parent_name ? 1 : 0); diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index c8c003e217ad..a4899855c0f6 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -82,7 +82,7 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name, init.name = name; init.ops = &clk_fixed_factor_ops; - init.flags = flags; + init.flags = flags | CLK_IS_BASIC; init.parent_names = &parent_name; init.num_parents = 1; diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c index cbd246229786..7e1464569727 100644 --- a/drivers/clk/clk-fixed-rate.c +++ b/drivers/clk/clk-fixed-rate.c @@ -63,7 +63,7 @@ struct clk *clk_register_fixed_rate(struct device *dev, const char *name, init.name = name; init.ops = &clk_fixed_rate_ops; - init.flags = flags; + init.flags = flags | CLK_IS_BASIC; init.parent_names = (parent_name ? &parent_name: NULL); init.num_parents = (parent_name ? 1 : 0); diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index 578465e04be6..15114febfd92 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -130,7 +130,7 @@ struct clk *clk_register_gate(struct device *dev, const char *name, init.name = name; init.ops = &clk_gate_ops; - init.flags = flags; + init.flags = flags | CLK_IS_BASIC; init.parent_names = (parent_name ? &parent_name: NULL); init.num_parents = (parent_name ? 1 : 0); diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index fd36a8ea73d9..508c032edce4 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -106,7 +106,7 @@ struct clk *clk_register_mux(struct device *dev, const char *name, init.name = name; init.ops = &clk_mux_ops; - init.flags = flags; + init.flags = flags | CLK_IS_BASIC; init.parent_names = parent_names; init.num_parents = num_parents; diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h index cc9972d1429c..9c7f5807824b 100644 --- a/include/linux/clk-private.h +++ b/include/linux/clk-private.h @@ -64,7 +64,7 @@ struct clk { .parent_names = _parent_names, \ .num_parents = ARRAY_SIZE(_parent_names), \ .parents = _parents, \ - .flags = _flags, \ + .flags = _flags | CLK_IS_BASIC, \ } #define DEFINE_CLK_FIXED_RATE(_name, _flags, _rate, \ diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 79caee9f1489..0236f58f3e65 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -25,6 +25,7 @@ #define CLK_SET_RATE_PARENT BIT(2) /* propagate rate change up one level */ #define CLK_IGNORE_UNUSED BIT(3) /* do not gate even if unused */ #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() */ struct clk_hw; -- cgit v1.2.3 From dc4cd941c900fda27f0146ab615122426229de73 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 14 May 2012 15:12:42 +0100 Subject: clk: Constify struct clk_init_data Allow drivers to declare their clk_init_data const, the framework really shouldn't be modifying the data. Signed-off-by: Mark Brown Signed-off-by: Mike Turquette --- include/linux/clk-provider.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 0236f58f3e65..06ad617664a2 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -144,7 +144,7 @@ struct clk_init_data { */ struct clk_hw { struct clk *clk; - struct clk_init_data *init; + const struct clk_init_data *init; }; /* -- cgit v1.2.3 From 50667d63085af108f625c83102430c1d74931d78 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 19 Jun 2012 23:44:25 +0200 Subject: ARM: u300: convert to common clock This converts the U300 clock implementation over to use the common struct clk and moves the implementation down into drivers/clk. Since VCO isn't used in tree it was removed, it's not hard to put it back in if need be. Signed-off-by: Linus Walleij [mturquette@linaro.org: trivial Makefile conflict] Signed-off-by: Mike Turquette --- arch/arm/Kconfig | 2 +- arch/arm/mach-u300/Makefile | 2 +- arch/arm/mach-u300/clock.c | 1504 -------------------------------- arch/arm/mach-u300/clock.h | 50 -- arch/arm/mach-u300/core.c | 21 +- arch/arm/mach-u300/timer.c | 2 +- drivers/clk/Makefile | 3 +- drivers/clk/clk-u300.c | 746 ++++++++++++++++ include/linux/platform_data/clk-u300.h | 1 + 9 files changed, 763 insertions(+), 1568 deletions(-) delete mode 100644 arch/arm/mach-u300/clock.c delete mode 100644 arch/arm/mach-u300/clock.h create mode 100644 drivers/clk/clk-u300.c create mode 100644 include/linux/platform_data/clk-u300.h (limited to 'include') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index a91009c61870..c59853738967 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -888,7 +888,7 @@ config ARCH_U300 select ARM_VIC select GENERIC_CLOCKEVENTS select CLKDEV_LOOKUP - select HAVE_MACH_CLKDEV + select COMMON_CLK select GENERIC_GPIO select ARCH_REQUIRE_GPIOLIB help diff --git a/arch/arm/mach-u300/Makefile b/arch/arm/mach-u300/Makefile index fd3a5c382f47..7e47d37aeb0e 100644 --- a/arch/arm/mach-u300/Makefile +++ b/arch/arm/mach-u300/Makefile @@ -2,7 +2,7 @@ # Makefile for the linux kernel, U300 machine. # -obj-y := core.o clock.o timer.o +obj-y := core.o timer.o obj-m := obj-n := obj- := diff --git a/arch/arm/mach-u300/clock.c b/arch/arm/mach-u300/clock.c deleted file mode 100644 index 5535dd0a78c9..000000000000 --- a/arch/arm/mach-u300/clock.c +++ /dev/null @@ -1,1504 +0,0 @@ -/* - * - * arch/arm/mach-u300/clock.c - * - * - * Copyright (C) 2007-2009 ST-Ericsson AB - * License terms: GNU General Public License (GPL) version 2 - * Define clocks in the app platform. - * Author: Linus Walleij - * Author: Jonas Aaberg - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "clock.h" - -/* - * TODO: - * - move all handling of the CCR register into this file and create - * a spinlock for the CCR register - * - switch to the clkdevice lookup mechanism that maps clocks to - * device ID:s instead when it becomes available in kernel 2.6.29. - * - implement rate get/set for all clocks that need it. - */ - -/* - * Syscon clock I/O registers lock so clock requests don't collide - * NOTE: this is a local lock only used to lock access to clock and - * reset registers in syscon. - */ -static DEFINE_SPINLOCK(syscon_clkreg_lock); -static DEFINE_SPINLOCK(syscon_resetreg_lock); - -/* - * The clocking hierarchy currently looks like this. - * NOTE: the idea is NOT to show how the clocks are routed on the chip! - * The ideas is to show dependencies, so a clock higher up in the - * hierarchy has to be on in order for another clock to be on. Now, - * both CPU and DMA can actually be on top of the hierarchy, and that - * is not modeled currently. Instead we have the backbone AMBA bus on - * top. This bus cannot be programmed in any way but conceptually it - * needs to be active for the bridges and devices to transport data. - * - * Please be aware that a few clocks are hw controlled, which mean that - * the hw itself can turn on/off or change the rate of the clock when - * needed! - * - * AMBA bus - * | - * +- CPU - * +- FSMC NANDIF NAND Flash interface - * +- SEMI Shared Memory interface - * +- ISP Image Signal Processor (U335 only) - * +- CDS (U335 only) - * +- DMA Direct Memory Access Controller - * +- AAIF APP/ACC Inteface (Mobile Scalable Link, MSL) - * +- APEX - * +- VIDEO_ENC AVE2/3 Video Encoder - * +- XGAM Graphics Accelerator Controller - * +- AHB - * | - * +- ahb:0 AHB Bridge - * | | - * | +- ahb:1 INTCON Interrupt controller - * | +- ahb:3 MSPRO Memory Stick Pro controller - * | +- ahb:4 EMIF External Memory interface - * | - * +- fast:0 FAST bridge - * | | - * | +- fast:1 MMCSD MMC/SD card reader controller - * | +- fast:2 I2S0 PCM I2S channel 0 controller - * | +- fast:3 I2S1 PCM I2S channel 1 controller - * | +- fast:4 I2C0 I2C channel 0 controller - * | +- fast:5 I2C1 I2C channel 1 controller - * | +- fast:6 SPI SPI controller - * | +- fast:7 UART1 Secondary UART (U335 only) - * | - * +- slow:0 SLOW bridge - * | - * +- slow:1 SYSCON (not possible to control) - * +- slow:2 WDOG Watchdog - * +- slow:3 UART0 primary UART - * +- slow:4 TIMER_APP Application timer - used in Linux - * +- slow:5 KEYPAD controller - * +- slow:6 GPIO controller - * +- slow:7 RTC controller - * +- slow:8 BT Bus Tracer (not used currently) - * +- slow:9 EH Event Handler (not used currently) - * +- slow:a TIMER_ACC Access style timer (not used currently) - * +- slow:b PPM (U335 only, what is that?) - */ - -/* - * Reset control functions. We remember if a block has been - * taken out of reset and don't remove the reset assertion again - * and vice versa. Currently we only remove resets so the - * enablement function is defined out. - */ -static void syscon_block_reset_enable(struct clk *clk) -{ - u16 val; - unsigned long iflags; - - /* Not all blocks support resetting */ - if (!clk->res_reg || !clk->res_mask) - return; - spin_lock_irqsave(&syscon_resetreg_lock, iflags); - val = readw(clk->res_reg); - val |= clk->res_mask; - writew(val, clk->res_reg); - spin_unlock_irqrestore(&syscon_resetreg_lock, iflags); - clk->reset = true; -} - -static void syscon_block_reset_disable(struct clk *clk) -{ - u16 val; - unsigned long iflags; - - /* Not all blocks support resetting */ - if (!clk->res_reg || !clk->res_mask) - return; - spin_lock_irqsave(&syscon_resetreg_lock, iflags); - val = readw(clk->res_reg); - val &= ~clk->res_mask; - writew(val, clk->res_reg); - spin_unlock_irqrestore(&syscon_resetreg_lock, iflags); - clk->reset = false; -} - -int __clk_get(struct clk *clk) -{ - u16 val; - - /* The MMC and MSPRO clocks need some special set-up */ - if (!strcmp(clk->name, "MCLK")) { - /* Set default MMC clock divisor to 18.9 MHz */ - writew(0x0054U, U300_SYSCON_VBASE + U300_SYSCON_MMF0R); - val = readw(U300_SYSCON_VBASE + U300_SYSCON_MMCR); - /* Disable the MMC feedback clock */ - val &= ~U300_SYSCON_MMCR_MMC_FB_CLK_SEL_ENABLE; - /* Disable MSPRO frequency */ - val &= ~U300_SYSCON_MMCR_MSPRO_FREQSEL_ENABLE; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_MMCR); - } - if (!strcmp(clk->name, "MSPRO")) { - val = readw(U300_SYSCON_VBASE + U300_SYSCON_MMCR); - /* Disable the MMC feedback clock */ - val &= ~U300_SYSCON_MMCR_MMC_FB_CLK_SEL_ENABLE; - /* Enable MSPRO frequency */ - val |= U300_SYSCON_MMCR_MSPRO_FREQSEL_ENABLE; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_MMCR); - } - return 1; -} -EXPORT_SYMBOL(__clk_get); - -void __clk_put(struct clk *clk) -{ -} -EXPORT_SYMBOL(__clk_put); - -static void syscon_clk_disable(struct clk *clk) -{ - unsigned long iflags; - - /* Don't touch the hardware controlled clocks */ - if (clk->hw_ctrld) - return; - - spin_lock_irqsave(&syscon_clkreg_lock, iflags); - writew(clk->clk_val, U300_SYSCON_VBASE + U300_SYSCON_SBCDR); - spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); -} - -static void syscon_clk_enable(struct clk *clk) -{ - unsigned long iflags; - - /* Don't touch the hardware controlled clocks */ - if (clk->hw_ctrld) - return; - - spin_lock_irqsave(&syscon_clkreg_lock, iflags); - writew(clk->clk_val, U300_SYSCON_VBASE + U300_SYSCON_SBCER); - spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); -} - -static u16 syscon_clk_get_rate(void) -{ - u16 val; - unsigned long iflags; - - spin_lock_irqsave(&syscon_clkreg_lock, iflags); - val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); - val &= U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK; - spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); - return val; -} - -#ifdef CONFIG_MACH_U300_USE_I2S_AS_MASTER -static void enable_i2s0_vcxo(void) -{ - u16 val; - unsigned long iflags; - - spin_lock_irqsave(&syscon_clkreg_lock, iflags); - /* Set I2S0 to use the VCXO 26 MHz clock */ - val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); - val |= U300_SYSCON_CCR_TURN_VCXO_ON; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); - val |= U300_SYSCON_CCR_I2S0_USE_VCXO; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); - val = readw(U300_SYSCON_VBASE + U300_SYSCON_CEFR); - val |= U300_SYSCON_CEFR_I2S0_CLK_EN; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CEFR); - spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); -} - -static void enable_i2s1_vcxo(void) -{ - u16 val; - unsigned long iflags; - - spin_lock_irqsave(&syscon_clkreg_lock, iflags); - /* Set I2S1 to use the VCXO 26 MHz clock */ - val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); - val |= U300_SYSCON_CCR_TURN_VCXO_ON; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); - val |= U300_SYSCON_CCR_I2S1_USE_VCXO; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); - val = readw(U300_SYSCON_VBASE + U300_SYSCON_CEFR); - val |= U300_SYSCON_CEFR_I2S1_CLK_EN; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CEFR); - spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); -} - -static void disable_i2s0_vcxo(void) -{ - u16 val; - unsigned long iflags; - - spin_lock_irqsave(&syscon_clkreg_lock, iflags); - /* Disable I2S0 use of the VCXO 26 MHz clock */ - val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); - val &= ~U300_SYSCON_CCR_I2S0_USE_VCXO; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); - /* Deactivate VCXO if no one else is using VCXO */ - if (!(val & U300_SYSCON_CCR_I2S1_USE_VCXO)) - val &= ~U300_SYSCON_CCR_TURN_VCXO_ON; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); - val = readw(U300_SYSCON_VBASE + U300_SYSCON_CEFR); - val &= ~U300_SYSCON_CEFR_I2S0_CLK_EN; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CEFR); - spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); -} - -static void disable_i2s1_vcxo(void) -{ - u16 val; - unsigned long iflags; - - spin_lock_irqsave(&syscon_clkreg_lock, iflags); - /* Disable I2S1 use of the VCXO 26 MHz clock */ - val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); - val &= ~U300_SYSCON_CCR_I2S1_USE_VCXO; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); - /* Deactivate VCXO if no one else is using VCXO */ - if (!(val & U300_SYSCON_CCR_I2S0_USE_VCXO)) - val &= ~U300_SYSCON_CCR_TURN_VCXO_ON; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); - val = readw(U300_SYSCON_VBASE + U300_SYSCON_CEFR); - val &= ~U300_SYSCON_CEFR_I2S0_CLK_EN; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CEFR); - spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); -} -#endif /* CONFIG_MACH_U300_USE_I2S_AS_MASTER */ - - -static void syscon_clk_rate_set_mclk(unsigned long rate) -{ - u16 val; - u32 reg; - unsigned long iflags; - - switch (rate) { - case 18900000: - val = 0x0054; - break; - case 20800000: - val = 0x0044; - break; - case 23100000: - val = 0x0043; - break; - case 26000000: - val = 0x0033; - break; - case 29700000: - val = 0x0032; - break; - case 34700000: - val = 0x0022; - break; - case 41600000: - val = 0x0021; - break; - case 52000000: - val = 0x0011; - break; - case 104000000: - val = 0x0000; - break; - default: - printk(KERN_ERR "Trying to set MCLK to unknown speed! %ld\n", - rate); - return; - } - - spin_lock_irqsave(&syscon_clkreg_lock, iflags); - reg = readw(U300_SYSCON_VBASE + U300_SYSCON_MMF0R) & - ~U300_SYSCON_MMF0R_MASK; - writew(reg | val, U300_SYSCON_VBASE + U300_SYSCON_MMF0R); - spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); -} - -void syscon_clk_rate_set_cpuclk(unsigned long rate) -{ - u16 val; - unsigned long iflags; - - switch (rate) { - case 13000000: - val = U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER; - break; - case 52000000: - val = U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE; - break; - case 104000000: - val = U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH; - break; - case 208000000: - val = U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST; - break; - default: - return; - } - spin_lock_irqsave(&syscon_clkreg_lock, iflags); - val |= readw(U300_SYSCON_VBASE + U300_SYSCON_CCR) & - ~U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK ; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); - spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); -} -EXPORT_SYMBOL(syscon_clk_rate_set_cpuclk); - -void clk_disable(struct clk *clk) -{ - unsigned long iflags; - - spin_lock_irqsave(&clk->lock, iflags); - if (clk->usecount > 0 && !(--clk->usecount)) { - /* some blocks lack clocking registers and cannot be disabled */ - if (clk->disable) - clk->disable(clk); - if (likely((u32)clk->parent)) - clk_disable(clk->parent); - } -#ifdef CONFIG_MACH_U300_USE_I2S_AS_MASTER - if (unlikely(!strcmp(clk->name, "I2S0"))) - disable_i2s0_vcxo(); - if (unlikely(!strcmp(clk->name, "I2S1"))) - disable_i2s1_vcxo(); -#endif - spin_unlock_irqrestore(&clk->lock, iflags); -} -EXPORT_SYMBOL(clk_disable); - -int clk_enable(struct clk *clk) -{ - int ret = 0; - unsigned long iflags; - - spin_lock_irqsave(&clk->lock, iflags); - if (clk->usecount++ == 0) { - if (likely((u32)clk->parent)) - ret = clk_enable(clk->parent); - - if (unlikely(ret != 0)) - clk->usecount--; - else { - /* remove reset line (we never enable reset again) */ - syscon_block_reset_disable(clk); - /* clocks without enable function are always on */ - if (clk->enable) - clk->enable(clk); -#ifdef CONFIG_MACH_U300_USE_I2S_AS_MASTER - if (unlikely(!strcmp(clk->name, "I2S0"))) - enable_i2s0_vcxo(); - if (unlikely(!strcmp(clk->name, "I2S1"))) - enable_i2s1_vcxo(); -#endif - } - } - spin_unlock_irqrestore(&clk->lock, iflags); - return ret; - -} -EXPORT_SYMBOL(clk_enable); - -/* Returns the clock rate in Hz */ -static unsigned long clk_get_rate_cpuclk(struct clk *clk) -{ - u16 val; - - val = syscon_clk_get_rate(); - - switch (val) { - case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: - return 13000000; - case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: - return 52000000; - case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: - return 104000000; - case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: - return 208000000; - default: - break; - } - return clk->rate; -} - -static unsigned long clk_get_rate_ahb_clk(struct clk *clk) -{ - u16 val; - - val = syscon_clk_get_rate(); - - switch (val) { - case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: - return 6500000; - case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: - return 26000000; - case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: - return 52000000; - default: - break; - } - return clk->rate; - -} - -static unsigned long clk_get_rate_emif_clk(struct clk *clk) -{ - u16 val; - - val = syscon_clk_get_rate(); - - switch (val) { - case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: - return 13000000; - case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: - return 52000000; - case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: - return 104000000; - default: - break; - } - return clk->rate; - -} - -static unsigned long clk_get_rate_xgamclk(struct clk *clk) -{ - u16 val; - - val = syscon_clk_get_rate(); - - switch (val) { - case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: - return 6500000; - case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: - return 26000000; - case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: - return 52000000; - default: - break; - } - - return clk->rate; -} - -static unsigned long clk_get_rate_mclk(struct clk *clk) -{ - u16 val; - - val = syscon_clk_get_rate(); - - switch (val) { - case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: - /* - * Here, the 208 MHz PLL gets shut down and the always - * on 13 MHz PLL used for RTC etc kicks into use - * instead. - */ - return 13000000; - case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: - { - /* - * This clock is under program control. The register is - * divided in two nybbles, bit 7-4 gives cycles-1 to count - * high, bit 3-0 gives cycles-1 to count low. Distribute - * these with no more than 1 cycle difference between - * low and high and add low and high to get the actual - * divisor. The base PLL is 208 MHz. Writing 0x00 will - * divide by 1 and 1 so the highest frequency possible - * is 104 MHz. - * - * e.g. 0x54 => - * f = 208 / ((5+1) + (4+1)) = 208 / 11 = 18.9 MHz - */ - u16 val = readw(U300_SYSCON_VBASE + U300_SYSCON_MMF0R) & - U300_SYSCON_MMF0R_MASK; - switch (val) { - case 0x0054: - return 18900000; - case 0x0044: - return 20800000; - case 0x0043: - return 23100000; - case 0x0033: - return 26000000; - case 0x0032: - return 29700000; - case 0x0022: - return 34700000; - case 0x0021: - return 41600000; - case 0x0011: - return 52000000; - case 0x0000: - return 104000000; - default: - break; - } - } - default: - break; - } - - return clk->rate; -} - -static unsigned long clk_get_rate_i2s_i2c_spi(struct clk *clk) -{ - u16 val; - - val = syscon_clk_get_rate(); - - switch (val) { - case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: - return 13000000; - case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: - return 26000000; - default: - break; - } - - return clk->rate; -} - -unsigned long clk_get_rate(struct clk *clk) -{ - if (clk->get_rate) - return clk->get_rate(clk); - else - return clk->rate; -} -EXPORT_SYMBOL(clk_get_rate); - -static unsigned long clk_round_rate_mclk(struct clk *clk, unsigned long rate) -{ - if (rate <= 18900000) - return 18900000; - if (rate <= 20800000) - return 20800000; - if (rate <= 23100000) - return 23100000; - if (rate <= 26000000) - return 26000000; - if (rate <= 29700000) - return 29700000; - if (rate <= 34700000) - return 34700000; - if (rate <= 41600000) - return 41600000; - if (rate <= 52000000) - return 52000000; - return -EINVAL; -} - -static unsigned long clk_round_rate_cpuclk(struct clk *clk, unsigned long rate) -{ - if (rate <= 13000000) - return 13000000; - if (rate <= 52000000) - return 52000000; - if (rate <= 104000000) - return 104000000; - if (rate <= 208000000) - return 208000000; - return -EINVAL; -} - -/* - * This adjusts a requested rate to the closest exact rate - * a certain clock can provide. For a fixed clock it's - * mostly clk->rate. - */ -long clk_round_rate(struct clk *clk, unsigned long rate) -{ - /* TODO: get appropriate switches for EMIFCLK, AHBCLK and MCLK */ - /* Else default to fixed value */ - - if (clk->round_rate) { - return (long) clk->round_rate(clk, rate); - } else { - printk(KERN_ERR "clock: Failed to round rate of %s\n", - clk->name); - } - return (long) clk->rate; -} -EXPORT_SYMBOL(clk_round_rate); - -static int clk_set_rate_mclk(struct clk *clk, unsigned long rate) -{ - syscon_clk_rate_set_mclk(clk_round_rate(clk, rate)); - return 0; -} - -static int clk_set_rate_cpuclk(struct clk *clk, unsigned long rate) -{ - syscon_clk_rate_set_cpuclk(clk_round_rate(clk, rate)); - return 0; -} - -int clk_set_rate(struct clk *clk, unsigned long rate) -{ - /* TODO: set for EMIFCLK and AHBCLK */ - /* Else assume the clock is fixed and fail */ - if (clk->set_rate) { - return clk->set_rate(clk, rate); - } else { - printk(KERN_ERR "clock: Failed to set %s to %ld hz\n", - clk->name, rate); - return -EINVAL; - } -} -EXPORT_SYMBOL(clk_set_rate); - -/* - * Clock definitions. The clock parents are set to respective - * bridge and the clock framework makes sure that the clocks have - * parents activated and are brought out of reset when in use. - * - * Clocks that have hw_ctrld = true are hw controlled, and the hw - * can by itself turn these clocks on and off. - * So in other words, we don't really have to care about them. - */ - -static struct clk amba_clk = { - .name = "AMBA", - .rate = 52000000, /* this varies! */ - .hw_ctrld = true, - .reset = false, - .lock = __SPIN_LOCK_UNLOCKED(amba_clk.lock), -}; - -/* - * These blocks are connected directly to the AMBA bus - * with no bridge. - */ - -static struct clk cpu_clk = { - .name = "CPU", - .parent = &amba_clk, - .rate = 208000000, /* this varies! */ - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - .res_mask = U300_SYSCON_RRR_CPU_RESET_EN, - .set_rate = clk_set_rate_cpuclk, - .get_rate = clk_get_rate_cpuclk, - .round_rate = clk_round_rate_cpuclk, - .lock = __SPIN_LOCK_UNLOCKED(cpu_clk.lock), -}; - -static struct clk nandif_clk = { - .name = "FSMC", - .parent = &amba_clk, - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - .res_mask = U300_SYSCON_RRR_NANDIF_RESET_EN, - .clk_val = U300_SYSCON_SBCER_NANDIF_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(nandif_clk.lock), -}; - -static struct clk semi_clk = { - .name = "SEMI", - .parent = &amba_clk, - .rate = 0, /* FIXME */ - /* It is not possible to reset SEMI */ - .hw_ctrld = false, - .reset = false, - .clk_val = U300_SYSCON_SBCER_SEMI_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(semi_clk.lock), -}; - -#ifdef CONFIG_MACH_U300_BS335 -static struct clk isp_clk = { - .name = "ISP", - .parent = &amba_clk, - .rate = 0, /* FIXME */ - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - .res_mask = U300_SYSCON_RRR_ISP_RESET_EN, - .clk_val = U300_SYSCON_SBCER_ISP_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(isp_clk.lock), -}; - -static struct clk cds_clk = { - .name = "CDS", - .parent = &amba_clk, - .rate = 0, /* FIXME */ - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - .res_mask = U300_SYSCON_RRR_CDS_RESET_EN, - .clk_val = U300_SYSCON_SBCER_CDS_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(cds_clk.lock), -}; -#endif - -static struct clk dma_clk = { - .name = "DMA", - .parent = &amba_clk, - .rate = 52000000, /* this varies! */ - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - .res_mask = U300_SYSCON_RRR_DMAC_RESET_EN, - .clk_val = U300_SYSCON_SBCER_DMAC_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(dma_clk.lock), -}; - -static struct clk aaif_clk = { - .name = "AAIF", - .parent = &amba_clk, - .rate = 52000000, /* this varies! */ - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - .res_mask = U300_SYSCON_RRR_AAIF_RESET_EN, - .clk_val = U300_SYSCON_SBCER_AAIF_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(aaif_clk.lock), -}; - -static struct clk apex_clk = { - .name = "APEX", - .parent = &amba_clk, - .rate = 0, /* FIXME */ - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - .res_mask = U300_SYSCON_RRR_APEX_RESET_EN, - .clk_val = U300_SYSCON_SBCER_APEX_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(apex_clk.lock), -}; - -static struct clk video_enc_clk = { - .name = "VIDEO_ENC", - .parent = &amba_clk, - .rate = 208000000, /* this varies! */ - .hw_ctrld = false, - .reset = false, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - /* This has XGAM in the name but refers to the video encoder */ - .res_mask = U300_SYSCON_RRR_XGAM_VC_SYNC_RESET_EN, - .clk_val = U300_SYSCON_SBCER_VIDEO_ENC_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(video_enc_clk.lock), -}; - -static struct clk xgam_clk = { - .name = "XGAMCLK", - .parent = &amba_clk, - .rate = 52000000, /* this varies! */ - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - .res_mask = U300_SYSCON_RRR_XGAM_RESET_EN, - .clk_val = U300_SYSCON_SBCER_XGAM_CLK_EN, - .get_rate = clk_get_rate_xgamclk, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(xgam_clk.lock), -}; - -/* This clock is used to activate the video encoder */ -static struct clk ahb_clk = { - .name = "AHB", - .parent = &amba_clk, - .rate = 52000000, /* this varies! */ - .hw_ctrld = false, /* This one is set to false due to HW bug */ - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - .res_mask = U300_SYSCON_RRR_AHB_RESET_EN, - .clk_val = U300_SYSCON_SBCER_AHB_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .get_rate = clk_get_rate_ahb_clk, - .lock = __SPIN_LOCK_UNLOCKED(ahb_clk.lock), -}; - - -/* - * Clocks on the AHB bridge - */ - -static struct clk ahb_subsys_clk = { - .name = "AHB_SUBSYS", - .parent = &amba_clk, - .rate = 52000000, /* this varies! */ - .hw_ctrld = true, - .reset = false, - .clk_val = U300_SYSCON_SBCER_AHB_SUBSYS_BRIDGE_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .get_rate = clk_get_rate_ahb_clk, - .lock = __SPIN_LOCK_UNLOCKED(ahb_subsys_clk.lock), -}; - -static struct clk intcon_clk = { - .name = "INTCON", - .parent = &ahb_subsys_clk, - .rate = 52000000, /* this varies! */ - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - .res_mask = U300_SYSCON_RRR_INTCON_RESET_EN, - /* INTCON can be reset but not clock-gated */ - .lock = __SPIN_LOCK_UNLOCKED(intcon_clk.lock), - -}; - -static struct clk mspro_clk = { - .name = "MSPRO", - .parent = &ahb_subsys_clk, - .rate = 0, /* FIXME */ - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - .res_mask = U300_SYSCON_RRR_MSPRO_RESET_EN, - .clk_val = U300_SYSCON_SBCER_MSPRO_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(mspro_clk.lock), -}; - -static struct clk emif_clk = { - .name = "EMIF", - .parent = &ahb_subsys_clk, - .rate = 104000000, /* this varies! */ - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - .res_mask = U300_SYSCON_RRR_EMIF_RESET_EN, - .clk_val = U300_SYSCON_SBCER_EMIF_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .get_rate = clk_get_rate_emif_clk, - .lock = __SPIN_LOCK_UNLOCKED(emif_clk.lock), -}; - - -/* - * Clocks on the FAST bridge - */ -static struct clk fast_clk = { - .name = "FAST_BRIDGE", - .parent = &amba_clk, - .rate = 13000000, /* this varies! */ - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, - .res_mask = U300_SYSCON_RFR_FAST_BRIDGE_RESET_ENABLE, - .clk_val = U300_SYSCON_SBCER_FAST_BRIDGE_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(fast_clk.lock), -}; - -/* - * The MMCI apb_pclk is hardwired to the same terminal as the - * external MCI clock. Thus this will be referenced twice. - */ -static struct clk mmcsd_clk = { - .name = "MCLK", - .parent = &fast_clk, - .rate = 18900000, /* this varies! */ - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, - .res_mask = U300_SYSCON_RFR_MMC_RESET_ENABLE, - .clk_val = U300_SYSCON_SBCER_MMC_CLK_EN, - .get_rate = clk_get_rate_mclk, - .set_rate = clk_set_rate_mclk, - .round_rate = clk_round_rate_mclk, - .disable = syscon_clk_disable, - .enable = syscon_clk_enable, - .lock = __SPIN_LOCK_UNLOCKED(mmcsd_clk.lock), -}; - -static struct clk i2s0_clk = { - .name = "i2s0", - .parent = &fast_clk, - .rate = 26000000, /* this varies! */ - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, - .res_mask = U300_SYSCON_RFR_PCM_I2S0_RESET_ENABLE, - .clk_val = U300_SYSCON_SBCER_I2S0_CORE_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .get_rate = clk_get_rate_i2s_i2c_spi, - .lock = __SPIN_LOCK_UNLOCKED(i2s0_clk.lock), -}; - -static struct clk i2s1_clk = { - .name = "i2s1", - .parent = &fast_clk, - .rate = 26000000, /* this varies! */ - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, - .res_mask = U300_SYSCON_RFR_PCM_I2S1_RESET_ENABLE, - .clk_val = U300_SYSCON_SBCER_I2S1_CORE_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .get_rate = clk_get_rate_i2s_i2c_spi, - .lock = __SPIN_LOCK_UNLOCKED(i2s1_clk.lock), -}; - -static struct clk i2c0_clk = { - .name = "I2C0", - .parent = &fast_clk, - .rate = 26000000, /* this varies! */ - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, - .res_mask = U300_SYSCON_RFR_I2C0_RESET_ENABLE, - .clk_val = U300_SYSCON_SBCER_I2C0_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .get_rate = clk_get_rate_i2s_i2c_spi, - .lock = __SPIN_LOCK_UNLOCKED(i2c0_clk.lock), -}; - -static struct clk i2c1_clk = { - .name = "I2C1", - .parent = &fast_clk, - .rate = 26000000, /* this varies! */ - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, - .res_mask = U300_SYSCON_RFR_I2C1_RESET_ENABLE, - .clk_val = U300_SYSCON_SBCER_I2C1_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .get_rate = clk_get_rate_i2s_i2c_spi, - .lock = __SPIN_LOCK_UNLOCKED(i2c1_clk.lock), -}; - -/* - * The SPI apb_pclk is hardwired to the same terminal as the - * external SPI clock. Thus this will be referenced twice. - */ -static struct clk spi_clk = { - .name = "SPI", - .parent = &fast_clk, - .rate = 26000000, /* this varies! */ - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, - .res_mask = U300_SYSCON_RFR_SPI_RESET_ENABLE, - .clk_val = U300_SYSCON_SBCER_SPI_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .get_rate = clk_get_rate_i2s_i2c_spi, - .lock = __SPIN_LOCK_UNLOCKED(spi_clk.lock), -}; - -#ifdef CONFIG_MACH_U300_BS335 -static struct clk uart1_pclk = { - .name = "UART1_PCLK", - .parent = &fast_clk, - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, - .res_mask = U300_SYSCON_RFR_UART1_RESET_ENABLE, - .clk_val = U300_SYSCON_SBCER_UART1_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(uart1_pclk.lock), -}; - -/* This one is hardwired to PLL13 */ -static struct clk uart1_clk = { - .name = "UART1_CLK", - .rate = 13000000, - .hw_ctrld = true, - .lock = __SPIN_LOCK_UNLOCKED(uart1_clk.lock), -}; -#endif - - -/* - * Clocks on the SLOW bridge - */ -static struct clk slow_clk = { - .name = "SLOW_BRIDGE", - .parent = &amba_clk, - .rate = 13000000, - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, - .res_mask = U300_SYSCON_RSR_SLOW_BRIDGE_RESET_EN, - .clk_val = U300_SYSCON_SBCER_SLOW_BRIDGE_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(slow_clk.lock), -}; - -/* TODO: implement SYSCON clock? */ - -static struct clk wdog_clk = { - .name = "WDOG", - .parent = &slow_clk, - .hw_ctrld = false, - .rate = 32768, - .reset = false, - /* This is always on, cannot be enabled/disabled or reset */ - .lock = __SPIN_LOCK_UNLOCKED(wdog_clk.lock), -}; - -static struct clk uart0_pclk = { - .name = "UART0_PCLK", - .parent = &slow_clk, - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, - .res_mask = U300_SYSCON_RSR_UART_RESET_EN, - .clk_val = U300_SYSCON_SBCER_UART_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(uart0_pclk.lock), -}; - -/* This one is hardwired to PLL13 */ -static struct clk uart0_clk = { - .name = "UART0_CLK", - .parent = &slow_clk, - .rate = 13000000, - .hw_ctrld = true, - .lock = __SPIN_LOCK_UNLOCKED(uart0_clk.lock), -}; - -static struct clk keypad_clk = { - .name = "KEYPAD", - .parent = &slow_clk, - .rate = 32768, - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, - .res_mask = U300_SYSCON_RSR_KEYPAD_RESET_EN, - .clk_val = U300_SYSCON_SBCER_KEYPAD_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(keypad_clk.lock), -}; - -static struct clk gpio_clk = { - .name = "GPIO", - .parent = &slow_clk, - .rate = 13000000, - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, - .res_mask = U300_SYSCON_RSR_GPIO_RESET_EN, - .clk_val = U300_SYSCON_SBCER_GPIO_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(gpio_clk.lock), -}; - -static struct clk rtc_clk = { - .name = "RTC", - .parent = &slow_clk, - .rate = 32768, - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, - .res_mask = U300_SYSCON_RSR_RTC_RESET_EN, - /* This clock is always on, cannot be enabled/disabled */ - .lock = __SPIN_LOCK_UNLOCKED(rtc_clk.lock), -}; - -static struct clk bustr_clk = { - .name = "BUSTR", - .parent = &slow_clk, - .rate = 13000000, - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, - .res_mask = U300_SYSCON_RSR_BTR_RESET_EN, - .clk_val = U300_SYSCON_SBCER_BTR_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(bustr_clk.lock), -}; - -static struct clk evhist_clk = { - .name = "EVHIST", - .parent = &slow_clk, - .rate = 13000000, - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, - .res_mask = U300_SYSCON_RSR_EH_RESET_EN, - .clk_val = U300_SYSCON_SBCER_EH_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(evhist_clk.lock), -}; - -static struct clk timer_clk = { - .name = "TIMER", - .parent = &slow_clk, - .rate = 13000000, - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, - .res_mask = U300_SYSCON_RSR_ACC_TMR_RESET_EN, - .clk_val = U300_SYSCON_SBCER_ACC_TMR_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(timer_clk.lock), -}; - -/* - * There is a binary divider in the hardware that divides - * the 13MHz PLL by 13 down to 1 MHz. - */ -static struct clk app_timer_clk = { - .name = "TIMER_APP", - .parent = &slow_clk, - .rate = 1000000, - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, - .res_mask = U300_SYSCON_RSR_APP_TMR_RESET_EN, - .clk_val = U300_SYSCON_SBCER_APP_TMR_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(app_timer_clk.lock), -}; - -#ifdef CONFIG_MACH_U300_BS335 -static struct clk ppm_clk = { - .name = "PPM", - .parent = &slow_clk, - .rate = 0, /* FIXME */ - .hw_ctrld = true, /* TODO: Look up if it is hw ctrld or not */ - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, - .res_mask = U300_SYSCON_RSR_PPM_RESET_EN, - .clk_val = U300_SYSCON_SBCER_PPM_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(ppm_clk.lock), -}; -#endif - -#define DEF_LOOKUP(devid, clkref) \ - { \ - .dev_id = devid, \ - .clk = clkref, \ - } - -#define DEF_LOOKUP_CON(devid, conid, clkref) \ - { \ - .dev_id = devid, \ - .con_id = conid, \ - .clk = clkref, \ - } - -/* - * Here we only define clocks that are meaningful to - * look up through clockdevice. - */ -static struct clk_lookup lookups[] = { - /* Connected directly to the AMBA bus */ - DEF_LOOKUP("amba", &amba_clk), - DEF_LOOKUP("cpu", &cpu_clk), - DEF_LOOKUP("fsmc-nand", &nandif_clk), - DEF_LOOKUP("semi", &semi_clk), -#ifdef CONFIG_MACH_U300_BS335 - DEF_LOOKUP("isp", &isp_clk), - DEF_LOOKUP("cds", &cds_clk), -#endif - DEF_LOOKUP("dma", &dma_clk), - DEF_LOOKUP("msl", &aaif_clk), - DEF_LOOKUP("apex", &apex_clk), - DEF_LOOKUP("video_enc", &video_enc_clk), - DEF_LOOKUP("xgam", &xgam_clk), - DEF_LOOKUP("ahb", &ahb_clk), - /* AHB bridge clocks */ - DEF_LOOKUP("ahb_subsys", &ahb_subsys_clk), - DEF_LOOKUP("intcon", &intcon_clk), - DEF_LOOKUP_CON("intcon", "apb_pclk", &intcon_clk), - DEF_LOOKUP("mspro", &mspro_clk), - DEF_LOOKUP("pl172", &emif_clk), - DEF_LOOKUP_CON("pl172", "apb_pclk", &emif_clk), - /* FAST bridge clocks */ - DEF_LOOKUP("fast", &fast_clk), - DEF_LOOKUP("mmci", &mmcsd_clk), - DEF_LOOKUP_CON("mmci", "apb_pclk", &mmcsd_clk), - /* - * The .0 and .1 identifiers on these comes from the platform device - * .id field and are assigned when the platform devices are registered. - */ - DEF_LOOKUP("i2s.0", &i2s0_clk), - DEF_LOOKUP("i2s.1", &i2s1_clk), - DEF_LOOKUP("stu300.0", &i2c0_clk), - DEF_LOOKUP("stu300.1", &i2c1_clk), - DEF_LOOKUP("pl022", &spi_clk), - DEF_LOOKUP_CON("pl022", "apb_pclk", &spi_clk), -#ifdef CONFIG_MACH_U300_BS335 - DEF_LOOKUP("uart1", &uart1_clk), - DEF_LOOKUP_CON("uart1", "apb_pclk", &uart1_pclk), -#endif - /* SLOW bridge clocks */ - DEF_LOOKUP("slow", &slow_clk), - DEF_LOOKUP("coh901327_wdog", &wdog_clk), - DEF_LOOKUP("uart0", &uart0_clk), - DEF_LOOKUP_CON("uart0", "apb_pclk", &uart0_pclk), - DEF_LOOKUP("apptimer", &app_timer_clk), - DEF_LOOKUP("coh901461-keypad", &keypad_clk), - DEF_LOOKUP("u300-gpio", &gpio_clk), - DEF_LOOKUP("rtc-coh901331", &rtc_clk), - DEF_LOOKUP("bustr", &bustr_clk), - DEF_LOOKUP("evhist", &evhist_clk), - DEF_LOOKUP("timer", &timer_clk), -#ifdef CONFIG_MACH_U300_BS335 - DEF_LOOKUP("ppm", &ppm_clk), -#endif -}; - -static void __init clk_register(void) -{ - /* Register the lookups */ - clkdev_add_table(lookups, ARRAY_SIZE(lookups)); -} - -#if (defined(CONFIG_DEBUG_FS) && defined(CONFIG_U300_DEBUG)) -/* - * The following makes it possible to view the status (especially - * reference count and reset status) for the clocks in the platform - * by looking into the special file /u300_clocks - */ - -/* A list of all clocks in the platform */ -static struct clk *clks[] = { - /* Top node clock for the AMBA bus */ - &amba_clk, - /* Connected directly to the AMBA bus */ - &cpu_clk, - &nandif_clk, - &semi_clk, -#ifdef CONFIG_MACH_U300_BS335 - &isp_clk, - &cds_clk, -#endif - &dma_clk, - &aaif_clk, - &apex_clk, - &video_enc_clk, - &xgam_clk, - &ahb_clk, - - /* AHB bridge clocks */ - &ahb_subsys_clk, - &intcon_clk, - &mspro_clk, - &emif_clk, - /* FAST bridge clocks */ - &fast_clk, - &mmcsd_clk, - &i2s0_clk, - &i2s1_clk, - &i2c0_clk, - &i2c1_clk, - &spi_clk, -#ifdef CONFIG_MACH_U300_BS335 - &uart1_clk, - &uart1_pclk, -#endif - /* SLOW bridge clocks */ - &slow_clk, - &wdog_clk, - &uart0_clk, - &uart0_pclk, - &app_timer_clk, - &keypad_clk, - &gpio_clk, - &rtc_clk, - &bustr_clk, - &evhist_clk, - &timer_clk, -#ifdef CONFIG_MACH_U300_BS335 - &ppm_clk, -#endif -}; - -static int u300_clocks_show(struct seq_file *s, void *data) -{ - struct clk *clk; - int i; - - seq_printf(s, "CLOCK DEVICE RESET STATE\t" \ - "ACTIVE\tUSERS\tHW CTRL FREQ\n"); - seq_printf(s, "---------------------------------------------" \ - "-----------------------------------------\n"); - for (i = 0; i < ARRAY_SIZE(clks); i++) { - clk = clks[i]; - if (clk != ERR_PTR(-ENOENT)) { - /* Format clock and device name nicely */ - char cdp[33]; - int chars; - - chars = snprintf(&cdp[0], 17, "%s", clk->name); - while (chars < 16) { - cdp[chars] = ' '; - chars++; - } - chars = snprintf(&cdp[16], 17, "%s", clk->dev ? - dev_name(clk->dev) : "N/A"); - while (chars < 16) { - cdp[chars+16] = ' '; - chars++; - } - cdp[32] = '\0'; - if (clk->get_rate || clk->rate != 0) - seq_printf(s, - "%s%s\t%s\t%d\t%s\t%lu Hz\n", - &cdp[0], - clk->reset ? - "ASSERTED" : "RELEASED", - clk->usecount ? "ON" : "OFF", - clk->usecount, - clk->hw_ctrld ? "YES" : "NO ", - clk_get_rate(clk)); - else - seq_printf(s, - "%s%s\t%s\t%d\t%s\t" \ - "(unknown rate)\n", - &cdp[0], - clk->reset ? - "ASSERTED" : "RELEASED", - clk->usecount ? "ON" : "OFF", - clk->usecount, - clk->hw_ctrld ? "YES" : "NO "); - } - } - return 0; -} - -static int u300_clocks_open(struct inode *inode, struct file *file) -{ - return single_open(file, u300_clocks_show, NULL); -} - -static const struct file_operations u300_clocks_operations = { - .open = u300_clocks_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int __init init_clk_read_debugfs(void) -{ - /* Expose a simple debugfs interface to view all clocks */ - (void) debugfs_create_file("u300_clocks", S_IFREG | S_IRUGO, - NULL, NULL, - &u300_clocks_operations); - return 0; -} -/* - * This needs to come in after the core_initcall() for the - * overall clocks, because debugfs is not available until - * the subsystems come up. - */ -module_init(init_clk_read_debugfs); -#endif - -int __init u300_clock_init(void) -{ - u16 val; - - /* - * FIXME: shall all this powermanagement stuff really live here??? - */ - - /* Set system to run at PLL208, max performance, a known state. */ - val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); - val &= ~U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); - /* Wait for the PLL208 to lock if not locked in yet */ - while (!(readw(U300_SYSCON_VBASE + U300_SYSCON_CSR) & - U300_SYSCON_CSR_PLL208_LOCK_IND)); - - /* Power management enable */ - val = readw(U300_SYSCON_VBASE + U300_SYSCON_PMCR); - val |= U300_SYSCON_PMCR_PWR_MGNT_ENABLE; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_PMCR); - - clk_register(); - - /* - * Some of these may be on when we boot the system so make sure they - * are turned OFF. - */ - syscon_block_reset_enable(&timer_clk); - timer_clk.disable(&timer_clk); - - /* - * These shall be turned on by default when we boot the system - * so make sure they are ON. (Adding CPU here is a bit too much.) - * These clocks will be claimed by drivers later. - */ - syscon_block_reset_disable(&semi_clk); - syscon_block_reset_disable(&emif_clk); - clk_enable(&semi_clk); - clk_enable(&emif_clk); - - return 0; -} diff --git a/arch/arm/mach-u300/clock.h b/arch/arm/mach-u300/clock.h deleted file mode 100644 index 4f50ca8f901e..000000000000 --- a/arch/arm/mach-u300/clock.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * arch/arm/mach-u300/include/mach/clock.h - * - * Copyright (C) 2004 - 2005 Nokia corporation - * Written by Tuukka Tikkanen - * Based on clocks.h by Tony Lindgren, Gordon McNutt and RidgeRun, Inc - * Copyright (C) 2007-2009 ST-Ericsson AB - * Adopted to ST-Ericsson U300 platforms by - * Jonas Aaberg - * - * 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_CLOCK_H -#define __MACH_CLOCK_H - -#include - -struct clk { - struct list_head node; - struct module *owner; - struct device *dev; - const char *name; - struct clk *parent; - - spinlock_t lock; - unsigned long rate; - bool reset; - __u16 clk_val; - __s8 usecount; - void __iomem * res_reg; - __u16 res_mask; - - bool hw_ctrld; - - void (*recalc) (struct clk *); - int (*set_rate) (struct clk *, unsigned long); - unsigned long (*get_rate) (struct clk *); - unsigned long (*round_rate) (struct clk *, unsigned long); - void (*init) (struct clk *); - void (*enable) (struct clk *); - void (*disable) (struct clk *); -}; - -int u300_clock_init(void); - -#endif diff --git a/arch/arm/mach-u300/core.c b/arch/arm/mach-u300/core.c index 33339745d432..03acf1883ec7 100644 --- a/arch/arm/mach-u300/core.c +++ b/arch/arm/mach-u300/core.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -44,7 +45,6 @@ #include #include -#include "clock.h" #include "spi.h" #include "i2c.h" #include "u300-gpio.h" @@ -1658,12 +1658,20 @@ void __init u300_init_irq(void) int i; /* initialize clocking early, we want to clock the INTCON */ - u300_clock_init(); + u300_clk_init(U300_SYSCON_VBASE); + + /* Bootstrap EMIF and SEMI clocks */ + clk = clk_get_sys("pl172", NULL); + BUG_ON(IS_ERR(clk)); + clk_prepare_enable(clk); + clk = clk_get_sys("semi", NULL); + BUG_ON(IS_ERR(clk)); + clk_prepare_enable(clk); /* Clock the interrupt controller */ clk = clk_get_sys("intcon", NULL); BUG_ON(IS_ERR(clk)); - clk_enable(clk); + clk_prepare_enable(clk); for (i = 0; i < U300_VIC_IRQS_END; i++) set_bit(i, (unsigned long *) &mask[0]); @@ -1811,13 +1819,6 @@ void __init u300_init_devices(void) /* Check what platform we run and print some status information */ u300_init_check_chip(); - /* Set system to run at PLL208, max performance, a known state. */ - val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); - val &= ~U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); - /* Wait for the PLL208 to lock if not locked in yet */ - while (!(readw(U300_SYSCON_VBASE + U300_SYSCON_CSR) & - U300_SYSCON_CSR_PLL208_LOCK_IND)); /* Initialize SPI device with some board specifics */ u300_spi_init(&pl022_device); diff --git a/arch/arm/mach-u300/timer.c b/arch/arm/mach-u300/timer.c index bc1c7897e82d..56ac06d38ec1 100644 --- a/arch/arm/mach-u300/timer.c +++ b/arch/arm/mach-u300/timer.c @@ -354,7 +354,7 @@ static void __init u300_timer_init(void) /* Clock the interrupt controller */ clk = clk_get_sys("apptimer", NULL); BUG_ON(IS_ERR(clk)); - clk_enable(clk); + clk_prepare_enable(clk); rate = clk_get_rate(clk); setup_sched_clock(u300_read_sched_clock, 32, rate); diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index b679f117f3cc..35d9fb44c814 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -1,10 +1,11 @@ - +# common clock types obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed-rate.o clk-gate.o \ clk-mux.o clk-divider.o clk-fixed-factor.o # SoCs specific obj-$(CONFIG_ARCH_MXS) += mxs/ obj-$(CONFIG_PLAT_SPEAR) += spear/ +obj-$(CONFIG_ARCH_U300) += clk-u300.o # Chip specific obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o diff --git a/drivers/clk/clk-u300.c b/drivers/clk/clk-u300.c new file mode 100644 index 000000000000..a15f7928fb11 --- /dev/null +++ b/drivers/clk/clk-u300.c @@ -0,0 +1,746 @@ +/* + * U300 clock implementation + * Copyright (C) 2007-2012 ST-Ericsson AB + * License terms: GNU General Public License (GPL) version 2 + * Author: Linus Walleij + * Author: Jonas Aaberg + */ +#include +#include +#include +#include +#include +#include +#include + +/* + * The clocking hierarchy currently looks like this. + * NOTE: the idea is NOT to show how the clocks are routed on the chip! + * The ideas is to show dependencies, so a clock higher up in the + * hierarchy has to be on in order for another clock to be on. Now, + * both CPU and DMA can actually be on top of the hierarchy, and that + * is not modeled currently. Instead we have the backbone AMBA bus on + * top. This bus cannot be programmed in any way but conceptually it + * needs to be active for the bridges and devices to transport data. + * + * Please be aware that a few clocks are hw controlled, which mean that + * the hw itself can turn on/off or change the rate of the clock when + * needed! + * + * AMBA bus + * | + * +- CPU + * +- FSMC NANDIF NAND Flash interface + * +- SEMI Shared Memory interface + * +- ISP Image Signal Processor (U335 only) + * +- CDS (U335 only) + * +- DMA Direct Memory Access Controller + * +- AAIF APP/ACC Inteface (Mobile Scalable Link, MSL) + * +- APEX + * +- VIDEO_ENC AVE2/3 Video Encoder + * +- XGAM Graphics Accelerator Controller + * +- AHB + * | + * +- ahb:0 AHB Bridge + * | | + * | +- ahb:1 INTCON Interrupt controller + * | +- ahb:3 MSPRO Memory Stick Pro controller + * | +- ahb:4 EMIF External Memory interface + * | + * +- fast:0 FAST bridge + * | | + * | +- fast:1 MMCSD MMC/SD card reader controller + * | +- fast:2 I2S0 PCM I2S channel 0 controller + * | +- fast:3 I2S1 PCM I2S channel 1 controller + * | +- fast:4 I2C0 I2C channel 0 controller + * | +- fast:5 I2C1 I2C channel 1 controller + * | +- fast:6 SPI SPI controller + * | +- fast:7 UART1 Secondary UART (U335 only) + * | + * +- slow:0 SLOW bridge + * | + * +- slow:1 SYSCON (not possible to control) + * +- slow:2 WDOG Watchdog + * +- slow:3 UART0 primary UART + * +- slow:4 TIMER_APP Application timer - used in Linux + * +- slow:5 KEYPAD controller + * +- slow:6 GPIO controller + * +- slow:7 RTC controller + * +- slow:8 BT Bus Tracer (not used currently) + * +- slow:9 EH Event Handler (not used currently) + * +- slow:a TIMER_ACC Access style timer (not used currently) + * +- slow:b PPM (U335 only, what is that?) + */ + +/* Global syscon virtual base */ +static void __iomem *syscon_vbase; + +/** + * struct clk_syscon - U300 syscon clock + * @hw: corresponding clock hardware entry + * @hw_ctrld: whether this clock is hardware controlled (for refcount etc) + * and does not need any magic pokes to be enabled/disabled + * @reset: state holder, whether this block's reset line is asserted or not + * @res_reg: reset line enable/disable flag register + * @res_bit: bit for resetting or taking this consumer out of reset + * @en_reg: clock line enable/disable flag register + * @en_bit: bit for enabling/disabling this consumer clock line + * @clk_val: magic value to poke in the register to enable/disable + * this one clock + */ +struct clk_syscon { + struct clk_hw hw; + bool hw_ctrld; + bool reset; + void __iomem *res_reg; + u8 res_bit; + void __iomem *en_reg; + u8 en_bit; + u16 clk_val; +}; + +#define to_syscon(_hw) container_of(_hw, struct clk_syscon, hw) + +static DEFINE_SPINLOCK(syscon_resetreg_lock); + +/* + * Reset control functions. We remember if a block has been + * taken out of reset and don't remove the reset assertion again + * and vice versa. Currently we only remove resets so the + * enablement function is defined out. + */ +static void syscon_block_reset_enable(struct clk_syscon *sclk) +{ + unsigned long iflags; + u16 val; + + /* Not all blocks support resetting */ + if (!sclk->res_reg) + return; + spin_lock_irqsave(&syscon_resetreg_lock, iflags); + val = readw(sclk->res_reg); + val |= BIT(sclk->res_bit); + writew(val, sclk->res_reg); + spin_unlock_irqrestore(&syscon_resetreg_lock, iflags); + sclk->reset = true; +} + +static void syscon_block_reset_disable(struct clk_syscon *sclk) +{ + unsigned long iflags; + u16 val; + + /* Not all blocks support resetting */ + if (!sclk->res_reg) + return; + spin_lock_irqsave(&syscon_resetreg_lock, iflags); + val = readw(sclk->res_reg); + val &= ~BIT(sclk->res_bit); + writew(val, sclk->res_reg); + spin_unlock_irqrestore(&syscon_resetreg_lock, iflags); + sclk->reset = false; +} + +static int syscon_clk_prepare(struct clk_hw *hw) +{ + struct clk_syscon *sclk = to_syscon(hw); + + /* If the block is in reset, bring it out */ + if (sclk->reset) + syscon_block_reset_disable(sclk); + return 0; +} + +static void syscon_clk_unprepare(struct clk_hw *hw) +{ + struct clk_syscon *sclk = to_syscon(hw); + + /* Please don't force the console into reset */ + if (sclk->clk_val == U300_SYSCON_SBCER_UART_CLK_EN) + return; + /* When unpreparing, force block into reset */ + if (!sclk->reset) + syscon_block_reset_enable(sclk); +} + +static int syscon_clk_enable(struct clk_hw *hw) +{ + struct clk_syscon *sclk = to_syscon(hw); + + /* Don't touch the hardware controlled clocks */ + if (sclk->hw_ctrld) + return 0; + /* These cannot be controlled */ + if (sclk->clk_val == 0xFFFFU) + return 0; + + writew(sclk->clk_val, syscon_vbase + U300_SYSCON_SBCER); + return 0; +} + +static void syscon_clk_disable(struct clk_hw *hw) +{ + struct clk_syscon *sclk = to_syscon(hw); + + /* Don't touch the hardware controlled clocks */ + if (sclk->hw_ctrld) + return; + if (sclk->clk_val == 0xFFFFU) + return; + /* Please don't disable the console port */ + if (sclk->clk_val == U300_SYSCON_SBCER_UART_CLK_EN) + return; + + writew(sclk->clk_val, syscon_vbase + U300_SYSCON_SBCDR); +} + +static int syscon_clk_is_enabled(struct clk_hw *hw) +{ + struct clk_syscon *sclk = to_syscon(hw); + u16 val; + + /* If no enable register defined, it's always-on */ + if (!sclk->en_reg) + return 1; + + val = readw(sclk->en_reg); + val &= BIT(sclk->en_bit); + + return val ? 1 : 0; +} + +static u16 syscon_get_perf(void) +{ + u16 val; + + val = readw(syscon_vbase + U300_SYSCON_CCR); + val &= U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK; + return val; +} + +static unsigned long +syscon_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_syscon *sclk = to_syscon(hw); + u16 perf = syscon_get_perf(); + + switch(sclk->clk_val) { + case U300_SYSCON_SBCER_FAST_BRIDGE_CLK_EN: + case U300_SYSCON_SBCER_I2C0_CLK_EN: + case U300_SYSCON_SBCER_I2C1_CLK_EN: + case U300_SYSCON_SBCER_MMC_CLK_EN: + case U300_SYSCON_SBCER_SPI_CLK_EN: + /* The FAST clocks have one progression */ + switch(perf) { + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: + return 13000000; + default: + return parent_rate; /* 26 MHz */ + } + case U300_SYSCON_SBCER_DMAC_CLK_EN: + case U300_SYSCON_SBCER_NANDIF_CLK_EN: + case U300_SYSCON_SBCER_XGAM_CLK_EN: + /* AMBA interconnect peripherals */ + switch(perf) { + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: + return 6500000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: + return 26000000; + default: + return parent_rate; /* 52 MHz */ + } + case U300_SYSCON_SBCER_SEMI_CLK_EN: + case U300_SYSCON_SBCER_EMIF_CLK_EN: + /* EMIF speeds */ + switch(perf) { + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: + return 13000000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: + return 52000000; + default: + return 104000000; + } + case U300_SYSCON_SBCER_CPU_CLK_EN: + /* And the fast CPU clock */ + switch(perf) { + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: + return 13000000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: + return 52000000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: + return 104000000; + default: + return parent_rate; /* 208 MHz */ + } + default: + /* + * The SLOW clocks and default just inherit the rate of + * their parent (typically PLL13 13 MHz). + */ + return parent_rate; + } +} + +static long +syscon_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_syscon *sclk = to_syscon(hw); + + if (sclk->clk_val != U300_SYSCON_SBCER_CPU_CLK_EN) + return *prate; + /* We really only support setting the rate of the CPU clock */ + if (rate <= 13000000) + return 13000000; + if (rate <= 52000000) + return 52000000; + if (rate <= 104000000) + return 104000000; + return 208000000; +} + +static int syscon_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_syscon *sclk = to_syscon(hw); + u16 val; + + /* We only support setting the rate of the CPU clock */ + if (sclk->clk_val != U300_SYSCON_SBCER_CPU_CLK_EN) + return -EINVAL; + switch (rate) { + case 13000000: + val = U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER; + break; + case 52000000: + val = U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE; + break; + case 104000000: + val = U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH; + break; + case 208000000: + val = U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST; + break; + default: + return -EINVAL; + } + val |= readw(syscon_vbase + U300_SYSCON_CCR) & + ~U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK ; + writew(val, syscon_vbase + U300_SYSCON_CCR); + return 0; +} + +static const struct clk_ops syscon_clk_ops = { + .prepare = syscon_clk_prepare, + .unprepare = syscon_clk_unprepare, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, + .is_enabled = syscon_clk_is_enabled, + .recalc_rate = syscon_clk_recalc_rate, + .round_rate = syscon_clk_round_rate, + .set_rate = syscon_clk_set_rate, +}; + +static struct clk * __init +syscon_clk_register(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + bool hw_ctrld, + void __iomem *res_reg, u8 res_bit, + void __iomem *en_reg, u8 en_bit, + u16 clk_val) +{ + struct clk *clk; + struct clk_syscon *sclk; + struct clk_init_data init; + + sclk = kzalloc(sizeof(struct clk_syscon), GFP_KERNEL); + if (!sclk) { + pr_err("could not allocate syscon clock %s\n", + name); + return ERR_PTR(-ENOMEM); + } + init.name = name; + init.ops = &syscon_clk_ops; + init.flags = flags; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + sclk->hw.init = &init; + sclk->hw_ctrld = hw_ctrld; + /* Assume the block is in reset at registration */ + sclk->reset = true; + sclk->res_reg = res_reg; + sclk->res_bit = res_bit; + sclk->en_reg = en_reg; + sclk->en_bit = en_bit; + sclk->clk_val = clk_val; + + clk = clk_register(dev, &sclk->hw); + if (IS_ERR(clk)) + kfree(sclk); + + return clk; +} + +/** + * struct clk_mclk - U300 MCLK clock (MMC/SD clock) + * @hw: corresponding clock hardware entry + * @is_mspro: if this is the memory stick clock rather than MMC/SD + */ +struct clk_mclk { + struct clk_hw hw; + bool is_mspro; +}; + +#define to_mclk(_hw) container_of(_hw, struct clk_mclk, hw) + +static int mclk_clk_prepare(struct clk_hw *hw) +{ + struct clk_mclk *mclk = to_mclk(hw); + u16 val; + + /* The MMC and MSPRO clocks need some special set-up */ + if (!mclk->is_mspro) { + /* Set default MMC clock divisor to 18.9 MHz */ + writew(0x0054U, syscon_vbase + U300_SYSCON_MMF0R); + val = readw(syscon_vbase + U300_SYSCON_MMCR); + /* Disable the MMC feedback clock */ + val &= ~U300_SYSCON_MMCR_MMC_FB_CLK_SEL_ENABLE; + /* Disable MSPRO frequency */ + val &= ~U300_SYSCON_MMCR_MSPRO_FREQSEL_ENABLE; + writew(val, syscon_vbase + U300_SYSCON_MMCR); + } else { + val = readw(syscon_vbase + U300_SYSCON_MMCR); + /* Disable the MMC feedback clock */ + val &= ~U300_SYSCON_MMCR_MMC_FB_CLK_SEL_ENABLE; + /* Enable MSPRO frequency */ + val |= U300_SYSCON_MMCR_MSPRO_FREQSEL_ENABLE; + writew(val, syscon_vbase + U300_SYSCON_MMCR); + } + + return 0; +} + +static unsigned long +mclk_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u16 perf = syscon_get_perf(); + + switch (perf) { + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: + /* + * Here, the 208 MHz PLL gets shut down and the always + * on 13 MHz PLL used for RTC etc kicks into use + * instead. + */ + return 13000000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: + { + /* + * This clock is under program control. The register is + * divided in two nybbles, bit 7-4 gives cycles-1 to count + * high, bit 3-0 gives cycles-1 to count low. Distribute + * these with no more than 1 cycle difference between + * low and high and add low and high to get the actual + * divisor. The base PLL is 208 MHz. Writing 0x00 will + * divide by 1 and 1 so the highest frequency possible + * is 104 MHz. + * + * e.g. 0x54 => + * f = 208 / ((5+1) + (4+1)) = 208 / 11 = 18.9 MHz + */ + u16 val = readw(syscon_vbase + U300_SYSCON_MMF0R) & + U300_SYSCON_MMF0R_MASK; + switch (val) { + case 0x0054: + return 18900000; + case 0x0044: + return 20800000; + case 0x0043: + return 23100000; + case 0x0033: + return 26000000; + case 0x0032: + return 29700000; + case 0x0022: + return 34700000; + case 0x0021: + return 41600000; + case 0x0011: + return 52000000; + case 0x0000: + return 104000000; + default: + break; + } + } + default: + break; + } + return parent_rate; +} + +static long +mclk_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + if (rate <= 18900000) + return 18900000; + if (rate <= 20800000) + return 20800000; + if (rate <= 23100000) + return 23100000; + if (rate <= 26000000) + return 26000000; + if (rate <= 29700000) + return 29700000; + if (rate <= 34700000) + return 34700000; + if (rate <= 41600000) + return 41600000; + /* Highest rate */ + return 52000000; +} + +static int mclk_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + u16 val; + u16 reg; + + switch (rate) { + case 18900000: + val = 0x0054; + break; + case 20800000: + val = 0x0044; + break; + case 23100000: + val = 0x0043; + break; + case 26000000: + val = 0x0033; + break; + case 29700000: + val = 0x0032; + break; + case 34700000: + val = 0x0022; + break; + case 41600000: + val = 0x0021; + break; + case 52000000: + val = 0x0011; + break; + case 104000000: + val = 0x0000; + break; + default: + return -EINVAL; + } + + reg = readw(syscon_vbase + U300_SYSCON_MMF0R) & + ~U300_SYSCON_MMF0R_MASK; + writew(reg | val, syscon_vbase + U300_SYSCON_MMF0R); + return 0; +} + +static const struct clk_ops mclk_ops = { + .prepare = mclk_clk_prepare, + .recalc_rate = mclk_clk_recalc_rate, + .round_rate = mclk_clk_round_rate, + .set_rate = mclk_clk_set_rate, +}; + +static struct clk * __init +mclk_clk_register(struct device *dev, const char *name, + const char *parent_name, bool is_mspro) +{ + struct clk *clk; + struct clk_mclk *mclk; + struct clk_init_data init; + + mclk = kzalloc(sizeof(struct clk_mclk), GFP_KERNEL); + if (!mclk) { + pr_err("could not allocate MMC/SD clock %s\n", + name); + return ERR_PTR(-ENOMEM); + } + init.name = "mclk"; + init.ops = &mclk_ops; + init.flags = 0; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + mclk->hw.init = &init; + mclk->is_mspro = is_mspro; + + clk = clk_register(dev, &mclk->hw); + if (IS_ERR(clk)) + kfree(mclk); + + return clk; +} + +void __init u300_clk_init(void __iomem *base) +{ + u16 val; + struct clk *clk; + + syscon_vbase = base; + + /* Set system to run at PLL208, max performance, a known state. */ + val = readw(syscon_vbase + U300_SYSCON_CCR); + val &= ~U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK; + writew(val, syscon_vbase + U300_SYSCON_CCR); + /* Wait for the PLL208 to lock if not locked in yet */ + while (!(readw(syscon_vbase + U300_SYSCON_CSR) & + U300_SYSCON_CSR_PLL208_LOCK_IND)); + + /* Power management enable */ + val = readw(syscon_vbase + U300_SYSCON_PMCR); + val |= U300_SYSCON_PMCR_PWR_MGNT_ENABLE; + writew(val, syscon_vbase + U300_SYSCON_PMCR); + + /* These are always available (RTC and PLL13) */ + clk = clk_register_fixed_rate(NULL, "app_32_clk", NULL, + CLK_IS_ROOT, 32768); + /* The watchdog sits directly on the 32 kHz clock */ + clk_register_clkdev(clk, NULL, "coh901327_wdog"); + clk = clk_register_fixed_rate(NULL, "pll13", NULL, + CLK_IS_ROOT, 13000000); + + /* These derive from PLL208 */ + clk = clk_register_fixed_rate(NULL, "pll208", NULL, + CLK_IS_ROOT, 208000000); + clk = clk_register_fixed_factor(NULL, "app_208_clk", "pll208", + 0, 1, 1); + clk = clk_register_fixed_factor(NULL, "app_104_clk", "pll208", + 0, 1, 2); + clk = clk_register_fixed_factor(NULL, "app_52_clk", "pll208", + 0, 1, 4); + /* The 52 MHz is divided down to 26 MHz */ + clk = clk_register_fixed_factor(NULL, "app_26_clk", "app_52_clk", + 0, 1, 2); + + /* Directly on the AMBA interconnect */ + clk = syscon_clk_register(NULL, "cpu_clk", "app_208_clk", 0, true, + syscon_vbase + U300_SYSCON_RRR, 3, + syscon_vbase + U300_SYSCON_CERR, 3, + U300_SYSCON_SBCER_CPU_CLK_EN); + clk = syscon_clk_register(NULL, "dmac_clk", "app_52_clk", 0, true, + syscon_vbase + U300_SYSCON_RRR, 4, + syscon_vbase + U300_SYSCON_CERR, 4, + U300_SYSCON_SBCER_DMAC_CLK_EN); + clk_register_clkdev(clk, NULL, "dma"); + clk = syscon_clk_register(NULL, "fsmc_clk", "app_52_clk", 0, false, + syscon_vbase + U300_SYSCON_RRR, 6, + syscon_vbase + U300_SYSCON_CERR, 6, + U300_SYSCON_SBCER_NANDIF_CLK_EN); + clk_register_clkdev(clk, NULL, "fsmc-nand"); + clk = syscon_clk_register(NULL, "xgam_clk", "app_52_clk", 0, true, + syscon_vbase + U300_SYSCON_RRR, 8, + syscon_vbase + U300_SYSCON_CERR, 8, + U300_SYSCON_SBCER_XGAM_CLK_EN); + clk_register_clkdev(clk, NULL, "xgam"); + clk = syscon_clk_register(NULL, "semi_clk", "app_104_clk", 0, false, + syscon_vbase + U300_SYSCON_RRR, 9, + syscon_vbase + U300_SYSCON_CERR, 9, + U300_SYSCON_SBCER_SEMI_CLK_EN); + clk_register_clkdev(clk, NULL, "semi"); + + /* AHB bridge clocks */ + clk = syscon_clk_register(NULL, "ahb_subsys_clk", "app_52_clk", 0, true, + syscon_vbase + U300_SYSCON_RRR, 10, + syscon_vbase + U300_SYSCON_CERR, 10, + U300_SYSCON_SBCER_AHB_SUBSYS_BRIDGE_CLK_EN); + clk = syscon_clk_register(NULL, "intcon_clk", "ahb_subsys_clk", 0, false, + syscon_vbase + U300_SYSCON_RRR, 12, + syscon_vbase + U300_SYSCON_CERR, 12, + /* Cannot be enabled, just taken out of reset */ + 0xFFFFU); + clk_register_clkdev(clk, NULL, "intcon"); + clk = syscon_clk_register(NULL, "emif_clk", "ahb_subsys_clk", 0, false, + syscon_vbase + U300_SYSCON_RRR, 5, + syscon_vbase + U300_SYSCON_CERR, 5, + U300_SYSCON_SBCER_EMIF_CLK_EN); + clk_register_clkdev(clk, NULL, "pl172"); + + /* FAST bridge clocks */ + clk = syscon_clk_register(NULL, "fast_clk", "app_26_clk", 0, true, + syscon_vbase + U300_SYSCON_RFR, 0, + syscon_vbase + U300_SYSCON_CEFR, 0, + U300_SYSCON_SBCER_FAST_BRIDGE_CLK_EN); + clk = syscon_clk_register(NULL, "i2c0_p_clk", "fast_clk", 0, false, + syscon_vbase + U300_SYSCON_RFR, 1, + syscon_vbase + U300_SYSCON_CEFR, 1, + U300_SYSCON_SBCER_I2C0_CLK_EN); + clk_register_clkdev(clk, NULL, "stu300.0"); + clk = syscon_clk_register(NULL, "i2c1_p_clk", "fast_clk", 0, false, + syscon_vbase + U300_SYSCON_RFR, 2, + syscon_vbase + U300_SYSCON_CEFR, 2, + U300_SYSCON_SBCER_I2C1_CLK_EN); + clk_register_clkdev(clk, NULL, "stu300.1"); + clk = syscon_clk_register(NULL, "mmc_p_clk", "fast_clk", 0, false, + syscon_vbase + U300_SYSCON_RFR, 5, + syscon_vbase + U300_SYSCON_CEFR, 5, + U300_SYSCON_SBCER_MMC_CLK_EN); + clk_register_clkdev(clk, "apb_pclk", "mmci"); + clk = syscon_clk_register(NULL, "spi_p_clk", "fast_clk", 0, false, + syscon_vbase + U300_SYSCON_RFR, 6, + syscon_vbase + U300_SYSCON_CEFR, 6, + U300_SYSCON_SBCER_SPI_CLK_EN); + /* The SPI has no external clock for the outward bus, uses the pclk */ + clk_register_clkdev(clk, NULL, "pl022"); + clk_register_clkdev(clk, "apb_pclk", "pl022"); + + /* SLOW bridge clocks */ + clk = syscon_clk_register(NULL, "slow_clk", "pll13", 0, true, + syscon_vbase + U300_SYSCON_RSR, 0, + syscon_vbase + U300_SYSCON_CESR, 0, + U300_SYSCON_SBCER_SLOW_BRIDGE_CLK_EN); + clk = syscon_clk_register(NULL, "uart0_clk", "slow_clk", 0, false, + syscon_vbase + U300_SYSCON_RSR, 1, + syscon_vbase + U300_SYSCON_CESR, 1, + U300_SYSCON_SBCER_UART_CLK_EN); + /* Same clock is used for APB and outward bus */ + clk_register_clkdev(clk, NULL, "uart0"); + clk_register_clkdev(clk, "apb_pclk", "uart0"); + clk = syscon_clk_register(NULL, "gpio_clk", "slow_clk", 0, false, + syscon_vbase + U300_SYSCON_RSR, 4, + syscon_vbase + U300_SYSCON_CESR, 4, + U300_SYSCON_SBCER_GPIO_CLK_EN); + clk_register_clkdev(clk, NULL, "u300-gpio"); + clk = syscon_clk_register(NULL, "keypad_clk", "slow_clk", 0, false, + syscon_vbase + U300_SYSCON_RSR, 5, + syscon_vbase + U300_SYSCON_CESR, 6, + U300_SYSCON_SBCER_KEYPAD_CLK_EN); + clk_register_clkdev(clk, NULL, "coh901461-keypad"); + clk = syscon_clk_register(NULL, "rtc_clk", "slow_clk", 0, true, + syscon_vbase + U300_SYSCON_RSR, 6, + /* No clock enable register bit */ + NULL, 0, 0xFFFFU); + clk_register_clkdev(clk, NULL, "rtc-coh901331"); + clk = syscon_clk_register(NULL, "app_tmr_clk", "slow_clk", 0, false, + syscon_vbase + U300_SYSCON_RSR, 7, + syscon_vbase + U300_SYSCON_CESR, 7, + U300_SYSCON_SBCER_APP_TMR_CLK_EN); + clk_register_clkdev(clk, NULL, "apptimer"); + clk = syscon_clk_register(NULL, "acc_tmr_clk", "slow_clk", 0, false, + syscon_vbase + U300_SYSCON_RSR, 8, + syscon_vbase + U300_SYSCON_CESR, 8, + U300_SYSCON_SBCER_ACC_TMR_CLK_EN); + clk_register_clkdev(clk, NULL, "timer"); + + /* Then this special MMC/SD clock */ + clk = mclk_clk_register(NULL, "mmc_clk", "mmc_p_clk", false); + clk_register_clkdev(clk, NULL, "mmci"); +} diff --git a/include/linux/platform_data/clk-u300.h b/include/linux/platform_data/clk-u300.h new file mode 100644 index 000000000000..8429e73911a1 --- /dev/null +++ b/include/linux/platform_data/clk-u300.h @@ -0,0 +1 @@ +void __init u300_clk_init(void __iomem *base); -- cgit v1.2.3 From a613163dff04cbfcb7d66b06ef4a5f65498ee59b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 11 Jun 2012 17:33:12 +0200 Subject: ARM: integrator: convert to common clock This converts the Integrator platform to use common clock and the ICST driver. Since from this point not all ARM reference platforms use the clock, we define CONFIG_PLAT_VERSATILE_CLOCK and select it for all platforms except the Integrator. Open issue: I could not use the .init_early() field of the machine descriptor to initialize the clocks, but had to move them to .init_irq(), so presumably .init_early() is so early that common clock is not up, and .init_machine() is too late since it's needed for the clockevent/clocksource initialization. Any suggestions on how to solve this is very welcome. Cc: Russell King Signed-off-by: Linus Walleij [mturquette@linaro.org: use 'select' instead of versatile Kconfig] Signed-off-by: Mike Turquette --- arch/arm/Kconfig | 7 +- arch/arm/mach-integrator/core.c | 45 ---------- arch/arm/mach-integrator/include/mach/clkdev.h | 26 ------ arch/arm/mach-integrator/integrator_ap.c | 8 +- arch/arm/mach-integrator/integrator_cp.c | 63 +------------- arch/arm/plat-versatile/Kconfig | 3 + arch/arm/plat-versatile/Makefile | 2 +- drivers/clk/versatile/Makefile | 1 + drivers/clk/versatile/clk-integrator.c | 111 +++++++++++++++++++++++++ include/linux/platform_data/clk-integrator.h | 1 + 10 files changed, 131 insertions(+), 136 deletions(-) delete mode 100644 arch/arm/mach-integrator/include/mach/clkdev.h create mode 100644 drivers/clk/versatile/clk-integrator.c create mode 100644 include/linux/platform_data/clk-integrator.h (limited to 'include') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index c59853738967..6f8cf405d3ec 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -254,8 +254,8 @@ config ARCH_INTEGRATOR bool "ARM Ltd. Integrator family" select ARM_AMBA select ARCH_HAS_CPUFREQ - select CLKDEV_LOOKUP - select HAVE_MACH_CLKDEV + select COMMON_CLK + select CLK_VERSATILE select HAVE_TCM select ICST select GENERIC_CLOCKEVENTS @@ -277,6 +277,7 @@ config ARCH_REALVIEW select GENERIC_CLOCKEVENTS select ARCH_WANT_OPTIONAL_GPIOLIB select PLAT_VERSATILE + select PLAT_VERSATILE_CLOCK select PLAT_VERSATILE_CLCD select ARM_TIMER_SP804 select GPIO_PL061 if GPIOLIB @@ -295,6 +296,7 @@ config ARCH_VERSATILE select ARCH_WANT_OPTIONAL_GPIOLIB select NEED_MACH_IO_H if PCI select PLAT_VERSATILE + select PLAT_VERSATILE_CLOCK select PLAT_VERSATILE_CLCD select PLAT_VERSATILE_FPGA_IRQ select ARM_TIMER_SP804 @@ -314,6 +316,7 @@ config ARCH_VEXPRESS select ICST select NO_IOPORT select PLAT_VERSATILE + select PLAT_VERSATILE_CLOCK select PLAT_VERSATILE_CLCD help This enables support for the ARM Ltd Versatile Express boards. diff --git a/arch/arm/mach-integrator/core.c b/arch/arm/mach-integrator/core.c index 2a20bba246fb..ebf680bebdf2 100644 --- a/arch/arm/mach-integrator/core.c +++ b/arch/arm/mach-integrator/core.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include @@ -61,50 +60,6 @@ static struct amba_device *amba_devs[] __initdata = { &kmi1_device, }; -/* - * These are fixed clocks. - */ -static struct clk clk24mhz = { - .rate = 24000000, -}; - -static struct clk uartclk = { - .rate = 14745600, -}; - -static struct clk dummy_apb_pclk; - -static struct clk_lookup lookups[] = { - { /* Bus clock */ - .con_id = "apb_pclk", - .clk = &dummy_apb_pclk, - }, { - /* Integrator/AP timer frequency */ - .dev_id = "ap_timer", - .clk = &clk24mhz, - }, { /* UART0 */ - .dev_id = "uart0", - .clk = &uartclk, - }, { /* UART1 */ - .dev_id = "uart1", - .clk = &uartclk, - }, { /* KMI0 */ - .dev_id = "kmi0", - .clk = &clk24mhz, - }, { /* KMI1 */ - .dev_id = "kmi1", - .clk = &clk24mhz, - }, { /* MMCI - IntegratorCP */ - .dev_id = "mmci", - .clk = &uartclk, - } -}; - -void __init integrator_init_early(void) -{ - clkdev_add_table(lookups, ARRAY_SIZE(lookups)); -} - static int __init integrator_init(void) { int i; diff --git a/arch/arm/mach-integrator/include/mach/clkdev.h b/arch/arm/mach-integrator/include/mach/clkdev.h deleted file mode 100644 index bfe07679faec..000000000000 --- a/arch/arm/mach-integrator/include/mach/clkdev.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef __ASM_MACH_CLKDEV_H -#define __ASM_MACH_CLKDEV_H - -#include -#include - -struct clk { - unsigned long rate; - const struct clk_ops *ops; - struct module *owner; - const struct icst_params *params; - void __iomem *vcoreg; - void *data; -}; - -static inline int __clk_get(struct clk *clk) -{ - return try_module_get(clk->owner); -} - -static inline void __clk_put(struct clk *clk) -{ - module_put(clk->owner); -} - -#endif diff --git a/arch/arm/mach-integrator/integrator_ap.c b/arch/arm/mach-integrator/integrator_ap.c index c857501c5783..7b1055c8e0b9 100644 --- a/arch/arm/mach-integrator/integrator_ap.c +++ b/arch/arm/mach-integrator/integrator_ap.c @@ -33,6 +33,7 @@ #include #include #include +#include #include
+
+ V4L2 in Linux 3.6 + + + Added V4L2_CAP_VIDEO_M2M and V4L2_CAP_VIDEO_M2M_MPLANE capabilities. + + +
+
Relation of V4L2 to other Linux multimedia APIs diff --git a/Documentation/DocBook/media/v4l/vidioc-querycap.xml b/Documentation/DocBook/media/v4l/vidioc-querycap.xml index 4643505cd4ca..f33dd746b66b 100644 --- a/Documentation/DocBook/media/v4l/vidioc-querycap.xml +++ b/Documentation/DocBook/media/v4l/vidioc-querycap.xml @@ -191,6 +191,19 @@ linkend="output">Video Output interface. multi-planar API through the Video Output interface. + + V4L2_CAP_VIDEO_M2M + 0x00004000 + The device supports the single-planar API through the + Video Memory-To-Memory interface. + + + V4L2_CAP_VIDEO_M2M_MPLANE + 0x00008000 + The device supports the + multi-planar API through the + Video Memory-To-Memory interface. + V4L2_CAP_VIDEO_OVERLAY 0x00000004 diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c index 7fdee8fcf3f7..7efe9ad7acc7 100644 --- a/drivers/media/video/mem2mem_testdev.c +++ b/drivers/media/video/mem2mem_testdev.c @@ -431,8 +431,7 @@ static int vidioc_querycap(struct file *file, void *priv, strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1); strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1); strlcpy(cap->bus_info, MEM2MEM_NAME, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT - | V4L2_CAP_STREAMING; + cap->capabilities = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } diff --git a/drivers/media/video/mx2_emmaprp.c b/drivers/media/video/mx2_emmaprp.c index 0bd5815de369..5f8a6f5b98f9 100644 --- a/drivers/media/video/mx2_emmaprp.c +++ b/drivers/media/video/mx2_emmaprp.c @@ -396,9 +396,13 @@ static int vidioc_querycap(struct file *file, void *priv, { strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1); strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT - | V4L2_CAP_STREAMING; - + /* + * This is only a mem-to-mem video device. The capture and output + * device capability flags are left only for backward compatibility + * and are scheduled for removal. + */ + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | + V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; return 0; } diff --git a/drivers/media/video/s5p-fimc/fimc-m2m.c b/drivers/media/video/s5p-fimc/fimc-m2m.c index 41eda2e27488..c587011d80ef 100644 --- a/drivers/media/video/s5p-fimc/fimc-m2m.c +++ b/drivers/media/video/s5p-fimc/fimc-m2m.c @@ -259,7 +259,12 @@ static int fimc_m2m_querycap(struct file *file, void *fh, strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1); strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1); cap->bus_info[0] = 0; - cap->capabilities = V4L2_CAP_STREAMING | + /* + * This is only a mem-to-mem video device. The capture and output + * device capability flags are left only for backward compatibility + * and are scheduled for removal. + */ + cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; return 0; diff --git a/drivers/media/video/s5p-g2d/g2d.c b/drivers/media/video/s5p-g2d/g2d.c index 7c98ee7377ee..7c2200435206 100644 --- a/drivers/media/video/s5p-g2d/g2d.c +++ b/drivers/media/video/s5p-g2d/g2d.c @@ -290,8 +290,13 @@ static int vidioc_querycap(struct file *file, void *priv, strncpy(cap->card, G2D_NAME, sizeof(cap->card) - 1); cap->bus_info[0] = 0; cap->version = KERNEL_VERSION(1, 0, 0); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT - | V4L2_CAP_STREAMING; + /* + * This is only a mem-to-mem video device. The capture and output + * device capability flags are left only for backward compatibility + * and are scheduled for removal. + */ + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | + V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; return 0; } diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.c b/drivers/media/video/s5p-jpeg/jpeg-core.c index 95f23024b17d..813b801238d1 100644 --- a/drivers/media/video/s5p-jpeg/jpeg-core.c +++ b/drivers/media/video/s5p-jpeg/jpeg-core.c @@ -489,9 +489,13 @@ static int s5p_jpeg_querycap(struct file *file, void *priv, sizeof(cap->card)); } cap->bus_info[0] = 0; - cap->capabilities = V4L2_CAP_STREAMING | - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_VIDEO_OUTPUT; + /* + * This is only a mem-to-mem video device. The capture and output + * device capability flags are left only for backward compatibility + * and are scheduled for removal. + */ + cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M | + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT; return 0; } diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c index feea867f318c..c5d567f87d77 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c @@ -220,8 +220,14 @@ static int vidioc_querycap(struct file *file, void *priv, strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1); cap->bus_info[0] = 0; cap->version = KERNEL_VERSION(1, 0, 0); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE | - V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING; + /* + * This is only a mem-to-mem video device. The capture and output + * device capability flags are left only for backward compatibility + * and are scheduled for removal. + */ + cap->capabilities = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_CAPTURE_MPLANE | + V4L2_CAP_VIDEO_OUTPUT_MPLANE; return 0; } diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c index 158b78989b89..aa1c244cf66e 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c @@ -779,9 +779,14 @@ static int vidioc_querycap(struct file *file, void *priv, strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1); cap->bus_info[0] = 0; cap->version = KERNEL_VERSION(1, 0, 0); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE - | V4L2_CAP_VIDEO_OUTPUT_MPLANE - | V4L2_CAP_STREAMING; + /* + * This is only a mem-to-mem video device. The capture and output + * device capability flags are left only for backward compatibility + * and are scheduled for removal. + */ + cap->capabilities = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_CAPTURE_MPLANE | + V4L2_CAP_VIDEO_OUTPUT_MPLANE; return 0; } diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 5d78910f926c..4cf766e6f120 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -274,6 +274,10 @@ struct v4l2_capability { #define V4L2_CAP_VIDEO_CAPTURE_MPLANE 0x00001000 /* Is a video output device that supports multiplanar formats */ #define V4L2_CAP_VIDEO_OUTPUT_MPLANE 0x00002000 +/* Is a video mem-to-mem device that supports multiplanar formats */ +#define V4L2_CAP_VIDEO_M2M_MPLANE 0x00004000 +/* Is a video mem-to-mem device */ +#define V4L2_CAP_VIDEO_M2M 0x00008000 #define V4L2_CAP_TUNER 0x00010000 /* has a tuner */ #define V4L2_CAP_AUDIO 0x00020000 /* has audio support */ -- cgit v1.2.3 From ab7017a3a0a64b953e091619c30413b3721d925d Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Mon, 30 Jul 2012 16:05:16 -0400 Subject: NFS: Add version registering framework This patch adds in the code to track multiple versions of the NFS protocol. I created default structures for v2, v3 and v4 so that each version can continue to work while I convert them into kernel modules. I also removed the const parameter from the rpc_version array so that I can change it at runtime. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/Makefile | 4 +- fs/nfs/client.c | 147 +++++++++++++++++++++++++++++++++++----------- fs/nfs/inode.c | 9 +-- fs/nfs/internal.h | 10 ++-- fs/nfs/nfs.h | 72 +++++++++++++++++++++++ fs/nfs/nfs2super.c | 25 ++++++++ fs/nfs/nfs3super.c | 25 ++++++++ fs/nfs/nfs4_fs.h | 1 + fs/nfs/nfs4client.c | 4 +- fs/nfs/nfs4super.c | 14 ++++- fs/nfs/super.c | 32 ++++++---- include/linux/nfs_fs_sb.h | 1 + 12 files changed, 283 insertions(+), 61 deletions(-) create mode 100644 fs/nfs/nfs.h create mode 100644 fs/nfs/nfs2super.c create mode 100644 fs/nfs/nfs3super.c (limited to 'include') diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index 0b96c2038346..66dd3075e5db 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -9,8 +9,8 @@ nfs-y := client.o dir.o file.o getroot.o inode.o super.o \ write.o namespace.o mount_clnt.o \ dns_resolve.o cache_lib.o nfs-$(CONFIG_ROOT_NFS) += nfsroot.o -nfs-$(CONFIG_NFS_V2) += proc.o nfs2xdr.o -nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o +nfs-$(CONFIG_NFS_V2) += nfs2super.o proc.o nfs2xdr.o +nfs-$(CONFIG_NFS_V3) += nfs3super.o nfs3proc.o nfs3xdr.o nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.o nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \ nfs4super.o nfs4file.o delegation.o idmap.o \ diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 65afa382c5e3..462de24482b4 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -51,25 +51,23 @@ #include "internal.h" #include "fscache.h" #include "pnfs.h" +#include "nfs.h" #include "netns.h" #define NFSDBG_FACILITY NFSDBG_CLIENT static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq); +static DEFINE_SPINLOCK(nfs_version_lock); +static DEFINE_MUTEX(nfs_version_mutex); +static LIST_HEAD(nfs_versions); /* * RPC cruft for NFS */ static const struct rpc_version *nfs_version[5] = { -#ifdef CONFIG_NFS_V2 - [2] = &nfs_version2, -#endif -#ifdef CONFIG_NFS_V3 - [3] = &nfs_version3, -#endif -#ifdef CONFIG_NFS_V4 - [4] = &nfs_version4, -#endif + [2] = NULL, + [3] = NULL, + [4] = NULL, }; const struct rpc_program nfs_program = { @@ -101,6 +99,93 @@ const struct rpc_program nfsacl_program = { }; #endif /* CONFIG_NFS_V3_ACL */ +static struct nfs_subversion *find_nfs_version(unsigned int version) +{ + struct nfs_subversion *nfs; + spin_lock(&nfs_version_lock); + + list_for_each_entry(nfs, &nfs_versions, list) { + if (nfs->rpc_ops->version == version) { + spin_unlock(&nfs_version_lock); + return nfs; + } + }; + + spin_unlock(&nfs_version_lock); + return ERR_PTR(-EPROTONOSUPPORT);; +} + +struct nfs_subversion *get_nfs_version(unsigned int version) +{ + struct nfs_subversion *nfs = find_nfs_version(version); + + if (IS_ERR(nfs)) { + mutex_lock(&nfs_version_mutex); + request_module("nfs%d", version); + nfs = find_nfs_version(version); + mutex_unlock(&nfs_version_mutex); + } + + if (!IS_ERR(nfs)) + try_module_get(nfs->owner); + return nfs; +} + +void put_nfs_version(struct nfs_subversion *nfs) +{ + module_put(nfs->owner); +} + +void register_nfs_version(struct nfs_subversion *nfs) +{ + spin_lock(&nfs_version_lock); + + list_add(&nfs->list, &nfs_versions); + nfs_version[nfs->rpc_ops->version] = nfs->rpc_vers; + + spin_unlock(&nfs_version_lock); +} +EXPORT_SYMBOL_GPL(register_nfs_version); + +void unregister_nfs_version(struct nfs_subversion *nfs) +{ + spin_lock(&nfs_version_lock); + + nfs_version[nfs->rpc_ops->version] = NULL; + list_del(&nfs->list); + + spin_unlock(&nfs_version_lock); +} +EXPORT_SYMBOL_GPL(unregister_nfs_version); + +/* + * Preload all configured NFS versions during module init. + * This function should be edited after each protocol is converted, + * and eventually removed. + */ +int __init nfs_register_versions(void) +{ + int err = init_nfs_v2(); + if (err) + return err; + + err = init_nfs_v3(); + if (err) + return err; + + return init_nfs_v4(); +} + +/* + * Remove each pre-loaded NFS version + */ +void nfs_unregister_versions(void) +{ + exit_nfs_v2(); + exit_nfs_v3(); + exit_nfs_v4(); +} + /* * Allocate a shared client record * @@ -116,7 +201,10 @@ struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) goto error_0; - clp->rpc_ops = cl_init->rpc_ops; + clp->cl_nfs_mod = cl_init->nfs_mod; + try_module_get(clp->cl_nfs_mod->owner); + + clp->rpc_ops = clp->cl_nfs_mod->rpc_ops; atomic_set(&clp->cl_count, 1); clp->cl_cons_state = NFS_CS_INITING; @@ -145,6 +233,7 @@ struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) return clp; error_cleanup: + put_nfs_version(clp->cl_nfs_mod); kfree(clp); error_0: return ERR_PTR(err); @@ -205,6 +294,7 @@ void nfs_free_client(struct nfs_client *clp) put_rpccred(clp->cl_machine_cred); put_net(clp->cl_net); + put_nfs_version(clp->cl_nfs_mod); kfree(clp->cl_hostname); kfree(clp); @@ -362,7 +452,7 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat continue; /* Different NFS versions cannot share the same nfs_client */ - if (clp->rpc_ops != data->rpc_ops) + if (clp->rpc_ops != data->nfs_mod->rpc_ops) continue; if (clp->cl_proto != data->proto) @@ -431,9 +521,10 @@ nfs_get_client(const struct nfs_client_initdata *cl_init, { struct nfs_client *clp, *new = NULL; struct nfs_net *nn = net_generic(cl_init->net, nfs_net_id); + const struct nfs_rpc_ops *rpc_ops = cl_init->nfs_mod->rpc_ops; dprintk("--> nfs_get_client(%s,v%u)\n", - cl_init->hostname ?: "", cl_init->rpc_ops->version); + cl_init->hostname ?: "", rpc_ops->version); /* see if the client already exists */ do { @@ -450,14 +541,13 @@ nfs_get_client(const struct nfs_client_initdata *cl_init, list_add(&new->cl_share_link, &nn->nfs_client_list); spin_unlock(&nn->nfs_client_lock); new->cl_flags = cl_init->init_flags; - return cl_init->rpc_ops->init_client(new, - timeparms, ip_addr, - authflavour); + return rpc_ops->init_client(new, timeparms, ip_addr, + authflavour); } spin_unlock(&nn->nfs_client_lock); - new = cl_init->rpc_ops->alloc_client(cl_init); + new = rpc_ops->alloc_client(cl_init); } while (!IS_ERR(new)); dprintk("<-- nfs_get_client() Failed to find %s (%ld)\n", @@ -714,13 +804,14 @@ error: * Create a version 2 or 3 client */ static int nfs_init_server(struct nfs_server *server, - const struct nfs_parsed_mount_data *data) + const struct nfs_parsed_mount_data *data, + struct nfs_subversion *nfs_mod) { struct nfs_client_initdata cl_init = { .hostname = data->nfs_server.hostname, .addr = (const struct sockaddr *)&data->nfs_server.address, .addrlen = data->nfs_server.addrlen, - .rpc_ops = NULL, + .nfs_mod = nfs_mod, .proto = data->nfs_server.protocol, .net = data->net, }; @@ -730,21 +821,6 @@ static int nfs_init_server(struct nfs_server *server, dprintk("--> nfs_init_server()\n"); - switch (data->version) { -#ifdef CONFIG_NFS_V2 - case 2: - cl_init.rpc_ops = &nfs_v2_clientops; - break; -#endif -#ifdef CONFIG_NFS_V3 - case 3: - cl_init.rpc_ops = &nfs_v3_clientops; - break; -#endif - default: - return -EPROTONOSUPPORT; - } - nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, data->timeo, data->retrans); if (data->flags & NFS_MOUNT_NORESVPORT) @@ -1033,7 +1109,8 @@ void nfs_free_server(struct nfs_server *server) * - keyed on server and FSID */ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, - struct nfs_fh *mntfh) + struct nfs_fh *mntfh, + struct nfs_subversion *nfs_mod) { struct nfs_server *server; struct nfs_fattr *fattr; @@ -1049,7 +1126,7 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, goto error; /* Get a client representation */ - error = nfs_init_server(server, data); + error = nfs_init_server(server, data, nfs_mod); if (error < 0) goto error; diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 35f7e4bc680e..e8877c82582d 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -50,6 +50,7 @@ #include "fscache.h" #include "dns_resolve.h" #include "pnfs.h" +#include "nfs.h" #include "netns.h" #define NFSDBG_FACILITY NFSDBG_VFS @@ -1671,21 +1672,17 @@ static int __init init_nfs_fs(void) rpc_proc_register(&init_net, &nfs_rpcstat); #endif -#ifdef CONFIG_NFS_V4 - err = init_nfs_v4(); + err = nfs_register_versions(); if (err) goto out1; -#endif if ((err = register_nfs_fs()) != 0) goto out0; return 0; out0: -#ifdef CONFIG_NFS_V4 - exit_nfs_v4(); + nfs_unregister_versions(); out1: -#endif #ifdef CONFIG_PROC_FS rpc_proc_unregister(&init_net, "nfs"); #endif diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index cfafd13b6fe9..ac936476b3bc 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -90,7 +90,7 @@ struct nfs_client_initdata { const char *hostname; const struct sockaddr *addr; size_t addrlen; - const struct nfs_rpc_ops *rpc_ops; + struct nfs_subversion *nfs_mod; int proto; u32 minorversion; struct net *net; @@ -189,7 +189,8 @@ nfs4_find_client_sessionid(struct net *, const struct sockaddr *, struct nfs4_sessionid *); extern struct nfs_server *nfs_create_server( const struct nfs_parsed_mount_data *, - struct nfs_fh *); + struct nfs_fh *, + struct nfs_subversion *); extern struct nfs_server *nfs4_create_server( const struct nfs_parsed_mount_data *, struct nfs_fh *); @@ -321,6 +322,7 @@ void nfs_zap_acl_cache(struct inode *inode); extern int nfs_wait_bit_killable(void *word); /* super.c */ +extern struct file_system_type nfs_fs_type; extern struct file_system_type nfs_xdev_fs_type; #ifdef CONFIG_NFS_V4 extern struct file_system_type nfs4_xdev_fs_type; @@ -329,8 +331,8 @@ extern struct file_system_type nfs4_referral_fs_type; void nfs_initialise_sb(struct super_block *); int nfs_set_sb_security(struct super_block *, struct dentry *, struct nfs_mount_info *); int nfs_clone_sb_security(struct super_block *, struct dentry *, struct nfs_mount_info *); -struct dentry *nfs_fs_mount_common(struct file_system_type *, struct nfs_server *, - int, const char *, struct nfs_mount_info *); +struct dentry *nfs_fs_mount_common(struct nfs_server *, int, const char *, + struct nfs_mount_info *, struct nfs_subversion *); struct dentry *nfs_fs_mount(struct file_system_type *, int, const char *, void *); struct dentry * nfs_xdev_mount_common(struct file_system_type *, int, const char *, struct nfs_mount_info *); diff --git a/fs/nfs/nfs.h b/fs/nfs/nfs.h new file mode 100644 index 000000000000..ac10b9e6c920 --- /dev/null +++ b/fs/nfs/nfs.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2012 Netapp, Inc. All rights reserved. + * + * Function and structures exported by the NFS module + * for use by NFS version-specific modules. + */ +#ifndef __LINUX_INTERNAL_NFS_H +#define __LINUX_INTERNAL_NFS_H + +#include +#include +#include + +struct nfs_subversion { + struct module *owner; /* THIS_MODULE pointer */ + struct file_system_type *nfs_fs; /* NFS filesystem type */ + const struct rpc_version *rpc_vers; /* NFS version information */ + const struct nfs_rpc_ops *rpc_ops; /* NFS operations */ + struct list_head list; /* List of NFS versions */ +}; + +int nfs_register_versions(void); +void nfs_unregister_versions(void); + +#ifdef CONFIG_NFS_V2 +int init_nfs_v2(void); +void exit_nfs_v2(void); +#else /* CONFIG_NFS_V2 */ +static inline int __init init_nfs_v2(void) +{ + return 0; +} + +static inline void exit_nfs_v2(void) +{ +} +#endif /* CONFIG_NFS_V2 */ + +#ifdef CONFIG_NFS_V3 +int init_nfs_v3(void); +void exit_nfs_v3(void); +#else /* CONFIG_NFS_V3 */ +static inline int __init init_nfs_v3(void) +{ + return 0; +} + +static inline void exit_nfs_v3(void) +{ +} +#endif /* CONFIG_NFS_V3 */ + +#ifdef CONFIG_NFS_V4 +int init_nfs_v4(void); +void exit_nfs_v4(void); +#else /* CONFIG_NFS_V4 */ +static inline int __init init_nfs_v4(void) +{ + return 0; +} + +static inline void exit_nfs_v4(void) +{ +} +#endif /* CONFIG_NFS_V4 */ + +struct nfs_subversion *get_nfs_version(unsigned int); +void put_nfs_version(struct nfs_subversion *); +void register_nfs_version(struct nfs_subversion *); +void unregister_nfs_version(struct nfs_subversion *); + +#endif /* __LINUX_INTERNAL_NFS_H */ diff --git a/fs/nfs/nfs2super.c b/fs/nfs/nfs2super.c new file mode 100644 index 000000000000..cef06d42334a --- /dev/null +++ b/fs/nfs/nfs2super.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2012 Netapp, Inc. All rights reserved. + */ +#include +#include +#include "internal.h" +#include "nfs.h" + +static struct nfs_subversion nfs_v2 = { + .owner = THIS_MODULE, + .nfs_fs = &nfs_fs_type, + .rpc_vers = &nfs_version2, + .rpc_ops = &nfs_v2_clientops, +}; + +int __init init_nfs_v2(void) +{ + register_nfs_version(&nfs_v2); + return 0; +} + +void exit_nfs_v2(void) +{ + unregister_nfs_version(&nfs_v2); +} diff --git a/fs/nfs/nfs3super.c b/fs/nfs/nfs3super.c new file mode 100644 index 000000000000..f815cf359d97 --- /dev/null +++ b/fs/nfs/nfs3super.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2012 Netapp, Inc. All rights reserved. + */ +#include +#include +#include "internal.h" +#include "nfs.h" + +static struct nfs_subversion nfs_v3 = { + .owner = THIS_MODULE, + .nfs_fs = &nfs_fs_type, + .rpc_vers = &nfs_version3, + .rpc_ops = &nfs_v3_clientops, +}; + +int __init init_nfs_v3(void) +{ + register_nfs_version(&nfs_v3); + return 0; +} + +void exit_nfs_v3(void) +{ + unregister_nfs_version(&nfs_v3); +} diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 5511690de8a5..99c2e7e4d3ea 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -366,6 +366,7 @@ extern const nfs4_stateid zero_stateid; /* nfs4super.c */ struct nfs_mount_info; +extern struct nfs_subversion nfs_v4; struct dentry *nfs4_try_mount(int, const char *, struct nfs_mount_info *); int init_nfs_v4(void); void exit_nfs_v4(void); diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 1c3f13c8e472..769e798b3959 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -357,7 +357,7 @@ static int nfs4_set_client(struct nfs_server *server, .hostname = hostname, .addr = addr, .addrlen = addrlen, - .rpc_ops = &nfs_v4_clientops, + .nfs_mod = &nfs_v4, .proto = proto, .minorversion = minorversion, .net = net, @@ -411,7 +411,7 @@ struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp, struct nfs_client_initdata cl_init = { .addr = ds_addr, .addrlen = ds_addrlen, - .rpc_ops = &nfs_v4_clientops, + .nfs_mod = &nfs_v4, .proto = ds_proto, .minorversion = mds_clp->cl_minorversion, .net = mds_clp->cl_net, diff --git a/fs/nfs/nfs4super.c b/fs/nfs/nfs4super.c index 59264fb335c8..1f3401902c2f 100644 --- a/fs/nfs/nfs4super.c +++ b/fs/nfs/nfs4super.c @@ -8,6 +8,7 @@ #include #include "internal.h" #include "nfs4_fs.h" +#include "nfs.h" #define NFSDBG_FACILITY NFSDBG_VFS @@ -75,6 +76,13 @@ static const struct super_operations nfs4_sops = { .remount_fs = nfs_remount, }; +struct nfs_subversion nfs_v4 = { + .owner = THIS_MODULE, + .nfs_fs = &nfs4_fs_type, + .rpc_vers = &nfs_version4, + .rpc_ops = &nfs_v4_clientops, +}; + /* * Set up an NFS4 superblock */ @@ -113,7 +121,7 @@ nfs4_remote_mount(struct file_system_type *fs_type, int flags, goto out; } - mntroot = nfs_fs_mount_common(fs_type, server, flags, dev_name, mount_info); + mntroot = nfs_fs_mount_common(server, flags, dev_name, mount_info, &nfs_v4); out: return mntroot; @@ -293,7 +301,7 @@ nfs4_remote_referral_mount(struct file_system_type *fs_type, int flags, goto out; } - mntroot = nfs_fs_mount_common(&nfs4_fs_type, server, flags, dev_name, &mount_info); + mntroot = nfs_fs_mount_common(server, flags, dev_name, &mount_info, &nfs_v4); out: nfs_free_fhandle(mount_info.mntfh); return mntroot; @@ -343,6 +351,7 @@ int __init init_nfs_v4(void) if (err < 0) goto out2; + register_nfs_version(&nfs_v4); return 0; out2: nfs4_unregister_sysctl(); @@ -354,6 +363,7 @@ out: void exit_nfs_v4(void) { + unregister_nfs_version(&nfs_v4); unregister_filesystem(&nfs4_fs_type); nfs4_unregister_sysctl(); nfs_idmap_quit(); diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 95866a8c21bb..61405a7a6b3c 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -64,6 +64,7 @@ #include "internal.h" #include "fscache.h" #include "pnfs.h" +#include "nfs.h" #define NFSDBG_FACILITY NFSDBG_VFS #define NFS_TEXT_DATA 1 @@ -281,7 +282,7 @@ static match_table_t nfs_vers_tokens = { static struct dentry *nfs_xdev_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *raw_data); -static struct file_system_type nfs_fs_type = { +struct file_system_type nfs_fs_type = { .owner = THIS_MODULE, .name = "nfs", .mount = nfs_fs_mount, @@ -1650,7 +1651,8 @@ static int nfs_request_mount(struct nfs_parsed_mount_data *args, } static struct dentry *nfs_try_mount(int flags, const char *dev_name, - struct nfs_mount_info *mount_info) + struct nfs_mount_info *mount_info, + struct nfs_subversion *nfs_mod) { int status; struct nfs_server *server; @@ -1662,11 +1664,11 @@ static struct dentry *nfs_try_mount(int flags, const char *dev_name, } /* Get a volume representation */ - server = nfs_create_server(mount_info->parsed, mount_info->mntfh); + server = nfs_create_server(mount_info->parsed, mount_info->mntfh, nfs_mod); if (IS_ERR(server)) return ERR_CAST(server); - return nfs_fs_mount_common(&nfs_fs_type, server, flags, dev_name, mount_info); + return nfs_fs_mount_common(server, flags, dev_name, mount_info, nfs_mod); } /* @@ -2297,10 +2299,10 @@ int nfs_clone_sb_security(struct super_block *s, struct dentry *mntroot, return 0; } -struct dentry *nfs_fs_mount_common(struct file_system_type *fs_type, - struct nfs_server *server, +struct dentry *nfs_fs_mount_common(struct nfs_server *server, int flags, const char *dev_name, - struct nfs_mount_info *mount_info) + struct nfs_mount_info *mount_info, + struct nfs_subversion *nfs_mod) { struct super_block *s; struct dentry *mntroot = ERR_PTR(-ENOMEM); @@ -2319,7 +2321,7 @@ struct dentry *nfs_fs_mount_common(struct file_system_type *fs_type, sb_mntdata.mntflags |= MS_SYNCHRONOUS; /* Get a superblock - note that we may end up sharing one that already exists */ - s = sget(fs_type, compare_super, nfs_set_super, flags, &sb_mntdata); + s = sget(nfs_mod->nfs_fs, compare_super, nfs_set_super, flags, &sb_mntdata); if (IS_ERR(s)) { mntroot = ERR_CAST(s); goto out_err_nosb; @@ -2378,6 +2380,7 @@ struct dentry *nfs_fs_mount(struct file_system_type *fs_type, .set_security = nfs_set_sb_security, }; struct dentry *mntroot = ERR_PTR(-ENOMEM); + struct nfs_subversion *nfs_mod; int error; mount_info.parsed = nfs_alloc_parsed_mount_data(); @@ -2394,12 +2397,20 @@ struct dentry *nfs_fs_mount(struct file_system_type *fs_type, goto out; } + nfs_mod = get_nfs_version(mount_info.parsed->version); + if (IS_ERR(nfs_mod)) { + mntroot = ERR_CAST(nfs_mod); + goto out; + } + #ifdef CONFIG_NFS_V4 if (mount_info.parsed->version == 4) mntroot = nfs4_try_mount(flags, dev_name, &mount_info); else #endif /* CONFIG_NFS_V4 */ - mntroot = nfs_try_mount(flags, dev_name, &mount_info); + mntroot = nfs_try_mount(flags, dev_name, &mount_info, nfs_mod); + + put_nfs_version(nfs_mod); out: nfs_free_parsed_mount_data(mount_info.parsed); @@ -2440,6 +2451,7 @@ nfs_xdev_mount_common(struct file_system_type *fs_type, int flags, struct nfs_clone_mount *data = mount_info->cloned; struct nfs_server *server; struct dentry *mntroot = ERR_PTR(-ENOMEM); + struct nfs_subversion *nfs_mod = NFS_SB(data->sb)->nfs_client->cl_nfs_mod; int error; dprintk("--> nfs_xdev_mount_common()\n"); @@ -2453,7 +2465,7 @@ nfs_xdev_mount_common(struct file_system_type *fs_type, int flags, goto out_err; } - mntroot = nfs_fs_mount_common(fs_type, server, flags, dev_name, mount_info); + mntroot = nfs_fs_mount_common(server, flags, dev_name, mount_info, nfs_mod); dprintk("<-- nfs_xdev_mount_common() = 0\n"); out: return mntroot; diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 65327652c61a..6039297801f4 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -48,6 +48,7 @@ struct nfs_client { struct rpc_clnt * cl_rpcclient; const struct nfs_rpc_ops *rpc_ops; /* NFS protocol vector */ int cl_proto; /* Network transport protocol */ + struct nfs_subversion * cl_nfs_mod; /* pointer to nfs version module */ u32 cl_minorversion;/* NFSv4 minorversion */ struct rpc_cred *cl_machine_cred; -- cgit v1.2.3 From ff9099f26645818563c8d396a154c2ce6ee422eb Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Mon, 30 Jul 2012 16:05:18 -0400 Subject: NFS: Create a try_mount rpc op I'm already looking up the nfs subversion in nfs_fs_mount(), so I have easy access to rpc_ops that used to be difficult to reach. This allows me to set up a different mount path for NFS v2/3 and NFS v4. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/internal.h | 2 ++ fs/nfs/nfs3proc.c | 1 + fs/nfs/nfs4_fs.h | 2 +- fs/nfs/nfs4proc.c | 1 + fs/nfs/nfs4super.c | 3 ++- fs/nfs/proc.c | 1 + fs/nfs/super.c | 14 ++++---------- include/linux/nfs_xdr.h | 4 ++++ 8 files changed, 16 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index ac936476b3bc..3364eccd17ef 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -328,6 +328,8 @@ extern struct file_system_type nfs_xdev_fs_type; extern struct file_system_type nfs4_xdev_fs_type; extern struct file_system_type nfs4_referral_fs_type; #endif +struct dentry *nfs_try_mount(int, const char *, struct nfs_mount_info *, + struct nfs_subversion *); void nfs_initialise_sb(struct super_block *); int nfs_set_sb_security(struct super_block *, struct dentry *, struct nfs_mount_info *); int nfs_clone_sb_security(struct super_block *, struct dentry *, struct nfs_mount_info *); diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 65d23eb92fe0..4f4cb8e49716 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -925,6 +925,7 @@ const struct nfs_rpc_ops nfs_v3_clientops = { .file_ops = &nfs_file_operations, .getroot = nfs3_proc_get_root, .submount = nfs_submount, + .try_mount = nfs_try_mount, .getattr = nfs3_proc_getattr, .setattr = nfs3_proc_setattr, .lookup = nfs3_proc_lookup, diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 99c2e7e4d3ea..c321fb59d801 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -367,7 +367,7 @@ extern const nfs4_stateid zero_stateid; /* nfs4super.c */ struct nfs_mount_info; extern struct nfs_subversion nfs_v4; -struct dentry *nfs4_try_mount(int, const char *, struct nfs_mount_info *); +struct dentry *nfs4_try_mount(int, const char *, struct nfs_mount_info *, struct nfs_subversion *); int init_nfs_v4(void); void exit_nfs_v4(void); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 6843e0a37de8..eb4ba1d99df9 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -6870,6 +6870,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .file_ops = &nfs4_file_operations, .getroot = nfs4_proc_get_root, .submount = nfs4_submount, + .try_mount = nfs4_try_mount, .getattr = nfs4_proc_getattr, .setattr = nfs4_proc_setattr, .lookup = nfs4_proc_lookup, diff --git a/fs/nfs/nfs4super.c b/fs/nfs/nfs4super.c index 8a505573c289..9384f666b6ab 100644 --- a/fs/nfs/nfs4super.c +++ b/fs/nfs/nfs4super.c @@ -226,7 +226,8 @@ static struct dentry *nfs_follow_remote_path(struct vfsmount *root_mnt, } struct dentry *nfs4_try_mount(int flags, const char *dev_name, - struct nfs_mount_info *mount_info) + struct nfs_mount_info *mount_info, + struct nfs_subversion *nfs_mod) { char *export_path; struct vfsmount *root_mnt; diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 4d3356af3309..ebb3d9c5227b 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -774,6 +774,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = { .file_ops = &nfs_file_operations, .getroot = nfs_proc_get_root, .submount = nfs_submount, + .try_mount = nfs_try_mount, .getattr = nfs_proc_getattr, .setattr = nfs_proc_setattr, .lookup = nfs_proc_lookup, diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 4faefa19a8c3..5fca59d73e40 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -1650,9 +1650,9 @@ static int nfs_request_mount(struct nfs_parsed_mount_data *args, return nfs_walk_authlist(args, &request); } -static struct dentry *nfs_try_mount(int flags, const char *dev_name, - struct nfs_mount_info *mount_info, - struct nfs_subversion *nfs_mod) +struct dentry *nfs_try_mount(int flags, const char *dev_name, + struct nfs_mount_info *mount_info, + struct nfs_subversion *nfs_mod) { int status; struct nfs_server *server; @@ -2403,15 +2403,9 @@ struct dentry *nfs_fs_mount(struct file_system_type *fs_type, goto out; } -#ifdef CONFIG_NFS_V4 - if (mount_info.parsed->version == 4) - mntroot = nfs4_try_mount(flags, dev_name, &mount_info); - else -#endif /* CONFIG_NFS_V4 */ - mntroot = nfs_try_mount(flags, dev_name, &mount_info, nfs_mod); + mntroot = nfs_mod->rpc_ops->try_mount(flags, dev_name, &mount_info, nfs_mod); put_nfs_version(nfs_mod); - out: nfs_free_parsed_mount_data(mount_info.parsed); nfs_free_fhandle(mount_info.mntfh); diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 0e181c2320b7..bc7415baf44d 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1353,6 +1353,8 @@ struct nfs_renamedata { struct nfs_access_entry; struct nfs_client; struct rpc_timeout; +struct nfs_subversion; +struct nfs_mount_info; struct nfs_client_initdata; struct nfs_pageio_descriptor; @@ -1370,6 +1372,8 @@ struct nfs_rpc_ops { struct nfs_fsinfo *); struct vfsmount *(*submount) (struct nfs_server *, struct dentry *, struct nfs_fh *, struct nfs_fattr *); + struct dentry *(*try_mount) (int, const char *, struct nfs_mount_info *, + struct nfs_subversion *); int (*getattr) (struct nfs_server *, struct nfs_fh *, struct nfs_fattr *); int (*setattr) (struct dentry *, struct nfs_fattr *, -- cgit v1.2.3 From 1179acc6a3e260bc4edc74fa94f6c7908290eaec Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Mon, 30 Jul 2012 16:05:19 -0400 Subject: NFS: Only initialize the ACL client in the v3 case v2 and v4 don't use it, so I create two new nfs_rpc_ops functions to initialize the ACL client only when we are using v3. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/Makefile | 2 +- fs/nfs/client.c | 61 ++++------------------------------------------ fs/nfs/internal.h | 15 ++++++++---- fs/nfs/nfs3client.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/nfs/nfs3proc.c | 2 ++ fs/nfs/nfs4client.c | 10 +++++--- fs/nfs/nfs4proc.c | 2 ++ fs/nfs/nfs4super.c | 2 +- fs/nfs/proc.c | 2 ++ fs/nfs/super.c | 4 +-- include/linux/nfs_xdr.h | 3 +++ 11 files changed, 99 insertions(+), 69 deletions(-) create mode 100644 fs/nfs/nfs3client.c (limited to 'include') diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index 66dd3075e5db..7ca0125da65e 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -10,7 +10,7 @@ nfs-y := client.o dir.o file.o getroot.o inode.o super.o \ dns_resolve.o cache_lib.o nfs-$(CONFIG_ROOT_NFS) += nfsroot.o nfs-$(CONFIG_NFS_V2) += nfs2super.o proc.o nfs2xdr.o -nfs-$(CONFIG_NFS_V3) += nfs3super.o nfs3proc.o nfs3xdr.o +nfs-$(CONFIG_NFS_V3) += nfs3super.o nfs3client.o nfs3proc.o nfs3xdr.o nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.o nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \ nfs4super.o nfs4file.o delegation.o idmap.o \ diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 462de24482b4..1f2908287cba 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -83,22 +83,6 @@ struct rpc_stat nfs_rpcstat = { .program = &nfs_program }; - -#ifdef CONFIG_NFS_V3_ACL -static struct rpc_stat nfsacl_rpcstat = { &nfsacl_program }; -static const struct rpc_version *nfsacl_version[] = { - [3] = &nfsacl_version3, -}; - -const struct rpc_program nfsacl_program = { - .name = "nfsacl", - .number = NFS_ACL_PROGRAM, - .nrvers = ARRAY_SIZE(nfsacl_version), - .version = nfsacl_version, - .stats = &nfsacl_rpcstat, -}; -#endif /* CONFIG_NFS_V3_ACL */ - static struct nfs_subversion *find_nfs_version(unsigned int version) { struct nfs_subversion *nfs; @@ -695,36 +679,6 @@ static int nfs_start_lockd(struct nfs_server *server) return 0; } -/* - * Initialise an NFSv3 ACL client connection - */ -#ifdef CONFIG_NFS_V3_ACL -static void nfs_init_server_aclclient(struct nfs_server *server) -{ - if (server->nfs_client->rpc_ops->version != 3) - goto out_noacl; - if (server->flags & NFS_MOUNT_NOACL) - goto out_noacl; - - server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3); - if (IS_ERR(server->client_acl)) - goto out_noacl; - - /* No errors! Assume that Sun nfsacls are supported */ - server->caps |= NFS_CAP_ACLS; - return; - -out_noacl: - server->caps &= ~NFS_CAP_ACLS; -} -#else -static inline void nfs_init_server_aclclient(struct nfs_server *server) -{ - server->flags &= ~NFS_MOUNT_NOACL; - server->caps &= ~NFS_CAP_ACLS; -} -#endif - /* * Create a general RPC client */ @@ -874,8 +828,6 @@ static int nfs_init_server(struct nfs_server *server, server->mountd_protocol = data->mount_server.protocol; server->namelen = data->namlen; - /* Create a client RPC handle for the NFSv3 ACL management interface */ - nfs_init_server_aclclient(server); dprintk("<-- nfs_init_server() = 0 [new %p]\n", clp); return 0; @@ -1108,8 +1060,7 @@ void nfs_free_server(struct nfs_server *server) * Create a version 2 or 3 volume record * - keyed on server and FSID */ -struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, - struct nfs_fh *mntfh, +struct nfs_server *nfs_create_server(struct nfs_mount_info *mount_info, struct nfs_subversion *nfs_mod) { struct nfs_server *server; @@ -1126,7 +1077,7 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, goto error; /* Get a client representation */ - error = nfs_init_server(server, data, nfs_mod); + error = nfs_init_server(server, mount_info->parsed, nfs_mod); if (error < 0) goto error; @@ -1135,13 +1086,13 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); /* Probe the root fh to retrieve its FSID */ - error = nfs_probe_fsinfo(server, mntfh, fattr); + error = nfs_probe_fsinfo(server, mount_info->mntfh, fattr); if (error < 0) goto error; if (server->nfs_client->rpc_ops->version == 3) { if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) server->namelen = NFS3_MAXNAMLEN; - if (!(data->flags & NFS_MOUNT_NORDIRPLUS)) + if (!(mount_info->parsed->flags & NFS_MOUNT_NORDIRPLUS)) server->caps |= NFS_CAP_READDIRPLUS; } else { if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN) @@ -1149,7 +1100,7 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, } if (!(fattr->valid & NFS_ATTR_FATTR)) { - error = server->nfs_client->rpc_ops->getattr(server, mntfh, fattr); + error = nfs_mod->rpc_ops->getattr(server, mount_info->mntfh, fattr); if (error < 0) { dprintk("nfs_create_server: getattr error = %d\n", -error); goto error; @@ -1210,8 +1161,6 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source, flavor); if (error < 0) goto out_free_server; - if (!IS_ERR(source->client_acl)) - nfs_init_server_aclclient(server); /* probe the filesystem info for this server filesystem */ error = nfs_probe_fsinfo(server, fh, fattr_fsinfo); diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 3364eccd17ef..2151bafd55b4 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -187,13 +187,11 @@ extern struct nfs_client *nfs4_find_client_ident(struct net *, int); extern struct nfs_client * nfs4_find_client_sessionid(struct net *, const struct sockaddr *, struct nfs4_sessionid *); -extern struct nfs_server *nfs_create_server( - const struct nfs_parsed_mount_data *, - struct nfs_fh *, +extern struct nfs_server *nfs_create_server(struct nfs_mount_info *, struct nfs_subversion *); extern struct nfs_server *nfs4_create_server( - const struct nfs_parsed_mount_data *, - struct nfs_fh *); + struct nfs_mount_info *, + struct nfs_subversion *); extern struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *, struct nfs_fh *); extern void nfs_free_server(struct nfs_server *server); @@ -225,6 +223,13 @@ static inline void nfs_fs_proc_exit(void) int nfs_sockaddr_match_ipaddr(const struct sockaddr *, const struct sockaddr *); #endif +/* nfs3client.c */ +#ifdef CONFIG_NFS_V3 +struct nfs_server *nfs3_create_server(struct nfs_mount_info *, struct nfs_subversion *); +struct nfs_server *nfs3_clone_server(struct nfs_server *, struct nfs_fh *, + struct nfs_fattr *, rpc_authflavor_t); +#endif + /* callback_xdr.c */ extern struct svc_version nfs4_callback_version1; extern struct svc_version nfs4_callback_version4; diff --git a/fs/nfs/nfs3client.c b/fs/nfs/nfs3client.c new file mode 100644 index 000000000000..b3fc65ef39ca --- /dev/null +++ b/fs/nfs/nfs3client.c @@ -0,0 +1,65 @@ +#include +#include +#include "internal.h" + +#ifdef CONFIG_NFS_V3_ACL +static struct rpc_stat nfsacl_rpcstat = { &nfsacl_program }; +static const struct rpc_version *nfsacl_version[] = { + [3] = &nfsacl_version3, +}; + +const struct rpc_program nfsacl_program = { + .name = "nfsacl", + .number = NFS_ACL_PROGRAM, + .nrvers = ARRAY_SIZE(nfsacl_version), + .version = nfsacl_version, + .stats = &nfsacl_rpcstat, +}; + +/* + * Initialise an NFSv3 ACL client connection + */ +static void nfs_init_server_aclclient(struct nfs_server *server) +{ + if (server->flags & NFS_MOUNT_NOACL) + goto out_noacl; + + server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3); + if (IS_ERR(server->client_acl)) + goto out_noacl; + + /* No errors! Assume that Sun nfsacls are supported */ + server->caps |= NFS_CAP_ACLS; + return; + +out_noacl: + server->caps &= ~NFS_CAP_ACLS; +} +#else +static inline void nfs_init_server_aclclient(struct nfs_server *server) +{ + server->flags &= ~NFS_MOUNT_NOACL; + server->caps &= ~NFS_CAP_ACLS; +} +#endif + +struct nfs_server *nfs3_create_server(struct nfs_mount_info *mount_info, + struct nfs_subversion *nfs_mod) +{ + struct nfs_server *server = nfs_create_server(mount_info, nfs_mod); + /* Create a client RPC handle for the NFS v3 ACL management interface */ + if (!IS_ERR(server)) + nfs_init_server_aclclient(server); + return server; +} + +struct nfs_server *nfs3_clone_server(struct nfs_server *source, + struct nfs_fh *fh, + struct nfs_fattr *fattr, + rpc_authflavor_t flavor) +{ + struct nfs_server *server = nfs_clone_server(source, fh, fattr, flavor); + if (!IS_ERR(server) && !IS_ERR(source->client_acl)) + nfs_init_server_aclclient(server); + return server; +} diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 4f4cb8e49716..0952c791df36 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -969,4 +969,6 @@ const struct nfs_rpc_ops nfs_v3_clientops = { .alloc_client = nfs_alloc_client, .init_client = nfs_init_client, .free_client = nfs_free_client, + .create_server = nfs3_create_server, + .clone_server = nfs3_clone_server, }; diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 769e798b3959..b2d409d2805a 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -574,8 +574,10 @@ error: * Create a version 4 volume record * - keyed on server and FSID */ -struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, - struct nfs_fh *mntfh) +/*struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, + struct nfs_fh *mntfh)*/ +struct nfs_server *nfs4_create_server(struct nfs_mount_info *mount_info, + struct nfs_subversion *nfs_mod) { struct nfs_server *server; int error; @@ -587,11 +589,11 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, return ERR_PTR(-ENOMEM); /* set up the general RPC client */ - error = nfs4_init_server(server, data); + error = nfs4_init_server(server, mount_info->parsed); if (error < 0) goto error; - error = nfs4_server_common_setup(server, mntfh); + error = nfs4_server_common_setup(server, mount_info->mntfh); if (error < 0) goto error; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index eb4ba1d99df9..36c6432aac7b 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -6916,6 +6916,8 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .alloc_client = nfs4_alloc_client, .init_client = nfs4_init_client, .free_client = nfs4_free_client, + .create_server = nfs4_create_server, + .clone_server = nfs_clone_server, }; static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = { diff --git a/fs/nfs/nfs4super.c b/fs/nfs/nfs4super.c index 9384f666b6ab..a62836256665 100644 --- a/fs/nfs/nfs4super.c +++ b/fs/nfs/nfs4super.c @@ -105,7 +105,7 @@ nfs4_remote_mount(struct file_system_type *fs_type, int flags, mount_info->set_security = nfs_set_sb_security; /* Get a volume representation */ - server = nfs4_create_server(mount_info->parsed, mount_info->mntfh); + server = nfs4_create_server(mount_info, &nfs_v4); if (IS_ERR(server)) { mntroot = ERR_CAST(server); goto out; diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index ebb3d9c5227b..50a88c3546ed 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -817,4 +817,6 @@ const struct nfs_rpc_ops nfs_v2_clientops = { .alloc_client = nfs_alloc_client, .init_client = nfs_init_client, .free_client = nfs_free_client, + .create_server = nfs_create_server, + .clone_server = nfs_clone_server, }; diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 5fca59d73e40..a5f9fb3bfdcc 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -1664,7 +1664,7 @@ struct dentry *nfs_try_mount(int flags, const char *dev_name, } /* Get a volume representation */ - server = nfs_create_server(mount_info->parsed, mount_info->mntfh, nfs_mod); + server = nfs_mod->rpc_ops->create_server(mount_info, nfs_mod); if (IS_ERR(server)) return ERR_CAST(server); @@ -2458,7 +2458,7 @@ nfs_xdev_mount(struct file_system_type *fs_type, int flags, mount_info.mntfh = mount_info.cloned->fh; /* create a new volume representation */ - server = nfs_clone_server(NFS_SB(data->sb), data->fh, data->fattr, data->authflavor); + server = nfs_mod->rpc_ops->clone_server(NFS_SB(data->sb), data->fh, data->fattr, data->authflavor); if (IS_ERR(server)) { error = PTR_ERR(server); goto out_err; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index bc7415baf44d..631182062994 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1439,6 +1439,9 @@ struct nfs_rpc_ops { (*init_client) (struct nfs_client *, const struct rpc_timeout *, const char *, rpc_authflavor_t); 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 *, + struct nfs_fattr *, rpc_authflavor_t); }; /* -- cgit v1.2.3 From 89d77c8fa8e6d1cb7e2cce95b428be30ddcc6f23 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Mon, 30 Jul 2012 16:05:25 -0400 Subject: NFS: Convert v4 into a module This patch exports symbols needed by the v4 module. In addition, I also switch over to using IS_ENABLED() to check if CONFIG_NFS_V4 or CONFIG_NFS_V4_MODULE are set. The module (nfs4.ko) will be created in the same directory as nfs.ko and will be automatically loaded the first time you try to mount over NFS v4. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/Kconfig | 2 +- fs/nfs/Makefile | 19 ++++++-------- fs/nfs/callback.h | 2 +- fs/nfs/client.c | 34 +++++++++++-------------- fs/nfs/delegation.h | 2 +- fs/nfs/dir.c | 6 ++++- fs/nfs/direct.c | 2 +- fs/nfs/dns_resolve.c | 4 +++ fs/nfs/file.c | 13 ++++++++++ fs/nfs/inode.c | 64 +++++++++++++++++++++++------------------------ fs/nfs/internal.h | 8 +++--- fs/nfs/namespace.c | 2 ++ fs/nfs/netns.h | 2 +- fs/nfs/nfs.h | 17 ------------- fs/nfs/nfs4_fs.h | 5 ++-- fs/nfs/nfs4super.c | 9 +++++-- fs/nfs/pagelist.c | 4 +++ fs/nfs/pnfs.c | 2 ++ fs/nfs/read.c | 4 +++ fs/nfs/super.c | 41 +++++++++++++++++++++++------- fs/nfs/write.c | 13 +++++++--- include/linux/nfs_fs.h | 6 ++--- include/linux/nfs_fs_sb.h | 6 ++--- include/linux/nfs_idmap.h | 2 +- include/linux/nfs_xdr.h | 2 +- 25 files changed, 155 insertions(+), 116 deletions(-) (limited to 'include') diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index f81a729c00e9..195c1ea6151a 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -72,7 +72,7 @@ config NFS_V3_ACL If unsure, say N. config NFS_V4 - bool "NFS client support for NFS version 4" + tristate "NFS client support for NFS version 4" depends on NFS_FS select SUNRPC_GSS select KEYS diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index 01846edc5c94..8bf3a3f6925a 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -9,17 +9,7 @@ nfs-y := client.o dir.o file.o getroot.o inode.o super.o \ write.o namespace.o mount_clnt.o \ dns_resolve.o cache_lib.o nfs-$(CONFIG_ROOT_NFS) += nfsroot.o -nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \ - nfs4super.o nfs4file.o delegation.o idmap.o \ - callback.o callback_xdr.o callback_proc.o \ - nfs4namespace.o nfs4getroot.o nfs4client.o -nfs-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o - -ifeq ($(CONFIG_SYSCTL), y) -nfs-y += sysctl.o -nfs-$(CONFIG_NFS_V4) += nfs4sysctl.o -endif - +nfs-$(CONFIG_SYSCTL) += sysctl.o nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o obj-$(CONFIG_NFS_V2) += nfs2.o @@ -29,6 +19,13 @@ obj-$(CONFIG_NFS_V3) += nfs3.o nfs3-y := nfs3super.o nfs3client.o nfs3proc.o nfs3xdr.o nfs3-$(CONFIG_NFS_V3_ACL) += nfs3acl.o +obj-$(CONFIG_NFS_V4) += nfs4.o +nfs4-y := nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o nfs4super.o nfs4file.o \ + delegation.o idmap.o callback.o callback_xdr.o callback_proc.o \ + nfs4namespace.o nfs4getroot.o nfs4client.o +nfs4-$(CONFIG_SYSCTL) += nfs4sysctl.o +nfs4-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o + obj-$(CONFIG_PNFS_FILE_LAYOUT) += nfs_layout_nfsv41_files.o nfs_layout_nfsv41_files-y := nfs4filelayout.o nfs4filelayoutdev.o diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h index a5527c90a5aa..b44d7b128b71 100644 --- a/fs/nfs/callback.h +++ b/fs/nfs/callback.h @@ -192,7 +192,7 @@ extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_process_state *cps); extern __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy, struct cb_process_state *cps); -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) extern int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt); extern void nfs_callback_down(int minorversion); extern int nfs4_validate_delegation_stateid(struct nfs_delegation *delegation, diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 8687b6b6edc1..9fc0d9dfc91b 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -142,24 +142,6 @@ void unregister_nfs_version(struct nfs_subversion *nfs) } EXPORT_SYMBOL_GPL(unregister_nfs_version); -/* - * Preload all configured NFS versions during module init. - * This function should be edited after each protocol is converted, - * and eventually removed. - */ -int __init nfs_register_versions(void) -{ - return init_nfs_v4(); -} - -/* - * Remove each pre-loaded NFS version - */ -void nfs_unregister_versions(void) -{ - exit_nfs_v4(); -} - /* * Allocate a shared client record * @@ -214,7 +196,7 @@ error_0: } EXPORT_SYMBOL_GPL(nfs_alloc_client); -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) /* idr_remove_all is not needed as all id's are removed by nfs_put_client */ void nfs_cleanup_cb_ident_idr(struct net *net) { @@ -390,6 +372,7 @@ int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1, } return 0; } +EXPORT_SYMBOL_GPL(nfs_sockaddr_match_ipaddr); #endif /* CONFIG_NFS_V4_1 */ /* @@ -456,6 +439,7 @@ int nfs_wait_client_init_complete(const struct nfs_client *clp) return wait_event_killable(nfs_client_active_wq, nfs_client_init_is_complete(clp)); } +EXPORT_SYMBOL_GPL(nfs_wait_client_init_complete); /* * Found an existing client. Make sure it's ready before returning. @@ -530,6 +514,7 @@ nfs_get_client(const struct nfs_client_initdata *cl_init, cl_init->hostname ?: "", PTR_ERR(new)); return new; } +EXPORT_SYMBOL_GPL(nfs_get_client); /* * Mark a server as ready or failed @@ -540,6 +525,7 @@ void nfs_mark_client_ready(struct nfs_client *clp, int state) clp->cl_cons_state = state; wake_up_all(&nfs_client_active_wq); } +EXPORT_SYMBOL_GPL(nfs_mark_client_ready); /* * Initialise the timeout values for a connection @@ -581,6 +567,7 @@ void nfs_init_timeout_values(struct rpc_timeout *to, int proto, BUG(); } } +EXPORT_SYMBOL_GPL(nfs_init_timeout_values); /* * Create an RPC client handle @@ -620,6 +607,7 @@ int nfs_create_rpc_client(struct nfs_client *clp, clp->cl_rpcclient = clnt; return 0; } +EXPORT_SYMBOL_GPL(nfs_create_rpc_client); /* * Version 2 or 3 client destruction @@ -706,6 +694,7 @@ int nfs_init_server_rpcclient(struct nfs_server *server, return 0; } +EXPORT_SYMBOL_GPL(nfs_init_server_rpcclient); /** * nfs_init_client - Initialise an NFS2 or NFS3 client @@ -932,6 +921,7 @@ out_error: dprintk("nfs_probe_fsinfo: error = %d\n", -error); return error; } +EXPORT_SYMBOL_GPL(nfs_probe_fsinfo); /* * Copy useful information when duplicating a server record @@ -948,6 +938,7 @@ void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *sour target->caps = source->caps; target->options = source->options; } +EXPORT_SYMBOL_GPL(nfs_server_copy_userdata); void nfs_server_insert_lists(struct nfs_server *server) { @@ -961,6 +952,7 @@ void nfs_server_insert_lists(struct nfs_server *server) spin_unlock(&nn->nfs_client_lock); } +EXPORT_SYMBOL_GPL(nfs_server_insert_lists); static void nfs_server_remove_lists(struct nfs_server *server) { @@ -1020,6 +1012,7 @@ struct nfs_server *nfs_alloc_server(void) return server; } +EXPORT_SYMBOL_GPL(nfs_alloc_server); /* * Free up a server record @@ -1048,6 +1041,7 @@ void nfs_free_server(struct nfs_server *server) nfs_release_automount_timer(); dprintk("<-- nfs_free_server()\n"); } +EXPORT_SYMBOL_GPL(nfs_free_server); /* * Create a version 2 or 3 volume record @@ -1193,7 +1187,7 @@ void nfs_clients_init(struct net *net) INIT_LIST_HEAD(&nn->nfs_client_list); INIT_LIST_HEAD(&nn->nfs_volume_list); -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) idr_init(&nn->cb_ident_idr); #endif spin_lock_init(&nn->nfs_client_lock); diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h index 1f3ccd934635..bbc6a4dba0d8 100644 --- a/fs/nfs/delegation.h +++ b/fs/nfs/delegation.h @@ -8,7 +8,7 @@ #ifndef FS_NFS_DELEGATION_H #define FS_NFS_DELEGATION_H -#if defined(CONFIG_NFS_V4) +#if IS_ENABLED(CONFIG_NFS_V4) /* * NFSv4 delegation */ diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 55438c970cbf..627f108ede23 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -936,6 +936,7 @@ void nfs_force_lookup_revalidate(struct inode *dir) { NFS_I(dir)->cache_change_attribute++; } +EXPORT_SYMBOL_GPL(nfs_force_lookup_revalidate); /* * A check for whether or not the parent directory has changed. @@ -1267,7 +1268,7 @@ out: } EXPORT_SYMBOL_GPL(nfs_lookup); -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) static int nfs4_lookup_revalidate(struct dentry *, unsigned int); const struct dentry_operations nfs4_dentry_operations = { @@ -1277,6 +1278,7 @@ const struct dentry_operations nfs4_dentry_operations = { .d_automount = nfs_d_automount, .d_release = nfs_d_release, }; +EXPORT_SYMBOL_GPL(nfs4_dentry_operations); static fmode_t flags_to_mode(int flags) { @@ -1419,6 +1421,7 @@ no_open: return finish_no_open(file, res); } +EXPORT_SYMBOL_GPL(nfs_atomic_open); static int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags) { @@ -2142,6 +2145,7 @@ int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags) { return nfs_do_access(inode, cred, nfs_open_permission_mask(openflags)); } +EXPORT_SYMBOL_GPL(nfs_may_open); int nfs_permission(struct inode *inode, int mask) { diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 899238156b11..b7b4f80968b5 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -460,7 +460,7 @@ static void nfs_inode_dio_write_done(struct inode *inode) inode_dio_done(inode); } -#if IS_ENABLED(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) +#if IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4) static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq) { struct nfs_pageio_descriptor desc; diff --git a/fs/nfs/dns_resolve.c b/fs/nfs/dns_resolve.c index b3924b8a6000..31c26c4dcc23 100644 --- a/fs/nfs/dns_resolve.c +++ b/fs/nfs/dns_resolve.c @@ -8,6 +8,7 @@ #ifdef CONFIG_NFS_USE_KERNEL_DNS +#include #include #include #include "dns_resolve.h" @@ -27,9 +28,11 @@ ssize_t nfs_dns_resolve_name(struct net *net, char *name, size_t namelen, kfree(ip_addr); return ret; } +EXPORT_SYMBOL_GPL(nfs_dns_resolve_name); #else +#include #include #include #include @@ -345,6 +348,7 @@ ssize_t nfs_dns_resolve_name(struct net *net, char *name, ret = -ESRCH; return ret; } +EXPORT_SYMBOL_GPL(nfs_dns_resolve_name); int nfs_dns_resolver_cache_init(struct net *net) { diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 5b3e70389553..1557978ca7b3 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -53,6 +53,7 @@ int nfs_check_flags(int flags) return 0; } +EXPORT_SYMBOL_GPL(nfs_check_flags); /* * Open file @@ -85,6 +86,7 @@ nfs_file_release(struct inode *inode, struct file *filp) nfs_inc_stats(inode, NFSIOS_VFSRELEASE); return nfs_release(inode, filp); } +EXPORT_SYMBOL_GPL(nfs_file_release); /** * nfs_revalidate_size - Revalidate the file size @@ -138,6 +140,7 @@ loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin) return generic_file_llseek(filp, offset, origin); } +EXPORT_SYMBOL_GPL(nfs_file_llseek); /* * Flush all dirty pages, and check for write errors. @@ -166,6 +169,7 @@ nfs_file_flush(struct file *file, fl_owner_t id) /* Flush writes to the server and return any errors */ return vfs_fsync(file, 0); } +EXPORT_SYMBOL_GPL(nfs_file_flush); ssize_t nfs_file_read(struct kiocb *iocb, const struct iovec *iov, @@ -190,6 +194,7 @@ nfs_file_read(struct kiocb *iocb, const struct iovec *iov, } return result; } +EXPORT_SYMBOL_GPL(nfs_file_read); ssize_t nfs_file_splice_read(struct file *filp, loff_t *ppos, @@ -212,6 +217,7 @@ nfs_file_splice_read(struct file *filp, loff_t *ppos, } return res; } +EXPORT_SYMBOL_GPL(nfs_file_splice_read); int nfs_file_mmap(struct file * file, struct vm_area_struct * vma) @@ -233,6 +239,7 @@ nfs_file_mmap(struct file * file, struct vm_area_struct * vma) } return status; } +EXPORT_SYMBOL_GPL(nfs_file_mmap); /* * Flush any dirty pages for this process, and check for write errors. @@ -271,6 +278,7 @@ nfs_file_fsync_commit(struct file *file, loff_t start, loff_t end, int datasync) ret = status; return ret; } +EXPORT_SYMBOL_GPL(nfs_file_fsync_commit); static int nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync) @@ -615,6 +623,7 @@ out_swapfile: printk(KERN_INFO "NFS: attempt to write to active swap file!\n"); goto out; } +EXPORT_SYMBOL_GPL(nfs_file_write); ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe, struct file *filp, loff_t *ppos, @@ -646,6 +655,7 @@ ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe, nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, written); return ret; } +EXPORT_SYMBOL_GPL(nfs_file_splice_write); static int do_getlk(struct file *filp, int cmd, struct file_lock *fl, int is_local) @@ -806,6 +816,7 @@ int nfs_lock(struct file *filp, int cmd, struct file_lock *fl) out_err: return ret; } +EXPORT_SYMBOL_GPL(nfs_lock); /* * Lock a (portion of) a file @@ -835,6 +846,7 @@ int nfs_flock(struct file *filp, int cmd, struct file_lock *fl) return do_unlk(filp, cmd, fl, is_local); return do_setlk(filp, cmd, fl, is_local); } +EXPORT_SYMBOL_GPL(nfs_flock); /* * There is no protocol support for leases, so we have no way to implement @@ -847,6 +859,7 @@ int nfs_setlease(struct file *file, long arg, struct file_lock **fl) file->f_path.dentry->d_name.name, arg); return -EINVAL; } +EXPORT_SYMBOL_GPL(nfs_setlease); const struct file_operations nfs_file_operations = { .llseek = nfs_file_llseek, diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 78dfc3e895ec..2ed6138f32ad 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -82,6 +82,7 @@ int nfs_wait_bit_killable(void *word) freezable_schedule(); return 0; } +EXPORT_SYMBOL_GPL(nfs_wait_bit_killable); /** * nfs_compat_user_ino64 - returns the user-visible inode number @@ -117,6 +118,7 @@ void nfs_clear_inode(struct inode *inode) nfs_access_zap_cache(inode); nfs_fscache_release_inode_cookie(inode); } +EXPORT_SYMBOL_GPL(nfs_clear_inode); void nfs_evict_inode(struct inode *inode) { @@ -393,6 +395,7 @@ out_no_inode: dprintk("nfs_fhget: iget failed with error %ld\n", PTR_ERR(inode)); goto out; } +EXPORT_SYMBOL_GPL(nfs_fhget); #define NFS_VALID_ATTRS (ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_SIZE|ATTR_ATIME|ATTR_ATIME_SET|ATTR_MTIME|ATTR_MTIME_SET|ATTR_FILE|ATTR_OPEN) @@ -655,6 +658,7 @@ struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, fmode_t f ctx->mdsthreshold = NULL; return ctx; } +EXPORT_SYMBOL_GPL(alloc_nfs_open_context); struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx) { @@ -662,6 +666,7 @@ struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx) atomic_inc(&ctx->lock_context.count); return ctx; } +EXPORT_SYMBOL_GPL(get_nfs_open_context); static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync) { @@ -689,6 +694,7 @@ void put_nfs_open_context(struct nfs_open_context *ctx) { __put_nfs_open_context(ctx, 0); } +EXPORT_SYMBOL_GPL(put_nfs_open_context); /* * Ensure that mmap has a recent RPC credential for use when writing out @@ -704,6 +710,7 @@ void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx) list_add(&ctx->list, &nfsi->open_files); spin_unlock(&inode->i_lock); } +EXPORT_SYMBOL_GPL(nfs_file_set_open_context); /* * Given an inode, search for an open context with the desired characteristics @@ -1497,11 +1504,12 @@ struct inode *nfs_alloc_inode(struct super_block *sb) nfsi->acl_access = ERR_PTR(-EAGAIN); nfsi->acl_default = ERR_PTR(-EAGAIN); #endif -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) nfsi->nfs4_acl = NULL; #endif /* CONFIG_NFS_V4 */ return &nfsi->vfs_inode; } +EXPORT_SYMBOL_GPL(nfs_alloc_inode); static void nfs_i_callback(struct rcu_head *head) { @@ -1513,10 +1521,11 @@ void nfs_destroy_inode(struct inode *inode) { call_rcu(&inode->i_rcu, nfs_i_callback); } +EXPORT_SYMBOL_GPL(nfs_destroy_inode); static inline void nfs4_init_once(struct nfs_inode *nfsi) { -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) INIT_LIST_HEAD(&nfsi->open_states); nfsi->delegation = NULL; nfsi->delegation_state = 0; @@ -1562,6 +1571,7 @@ static void nfs_destroy_inodecache(void) } struct workqueue_struct *nfsiod_workqueue; +EXPORT_SYMBOL_GPL(nfsiod_workqueue); /* * start up the nfsiod workqueue @@ -1622,90 +1632,80 @@ static int __init init_nfs_fs(void) err = nfs_dns_resolver_init(); if (err < 0) - goto out11; + goto out10;; err = register_pernet_subsys(&nfs_net_ops); if (err < 0) - goto out10; + goto out9; err = nfs_fscache_register(); if (err < 0) - goto out9; + goto out8; err = nfsiod_start(); if (err) - goto out8; + goto out7; err = nfs_fs_proc_init(); if (err) - goto out7; + goto out6; err = nfs_init_nfspagecache(); if (err) - goto out6; + goto out5; err = nfs_init_inodecache(); if (err) - goto out5; + goto out4; err = nfs_init_readpagecache(); if (err) - goto out4; + goto out3; err = nfs_init_writepagecache(); if (err) - goto out3; + goto out2; err = nfs_init_directcache(); if (err) - goto out2; + goto out1; #ifdef CONFIG_PROC_FS rpc_proc_register(&init_net, &nfs_rpcstat); #endif - - err = nfs_register_versions(); - if (err) - goto out1; - if ((err = register_nfs_fs()) != 0) goto out0; return 0; out0: - nfs_unregister_versions(); -out1: #ifdef CONFIG_PROC_FS rpc_proc_unregister(&init_net, "nfs"); #endif nfs_destroy_directcache(); -out2: +out1: nfs_destroy_writepagecache(); -out3: +out2: nfs_destroy_readpagecache(); -out4: +out3: nfs_destroy_inodecache(); -out5: +out4: nfs_destroy_nfspagecache(); -out6: +out5: nfs_fs_proc_exit(); -out7: +out6: nfsiod_stop(); -out8: +out7: nfs_fscache_unregister(); -out9: +out8: unregister_pernet_subsys(&nfs_net_ops); -out10: +out9: nfs_dns_resolver_destroy(); -out11: +out10: return err; } static void __exit exit_nfs_fs(void) { -#ifdef CONFIG_NFS_V4 - exit_nfs_v4(); -#endif nfs_destroy_directcache(); nfs_destroy_writepagecache(); nfs_destroy_readpagecache(); diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 64f0dc41a9b7..8865538b26b6 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -262,7 +262,7 @@ extern int nfs3_decode_dirent(struct xdr_stream *, struct nfs_entry *, int); /* nfs4xdr.c */ -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) extern int nfs4_decode_dirent(struct xdr_stream *, struct nfs_entry *, int); #endif @@ -272,7 +272,7 @@ extern const u32 nfs41_maxwrite_overhead; #endif /* nfs4proc.c */ -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) extern struct rpc_procinfo nfs4_procedures[]; #endif @@ -328,7 +328,7 @@ extern int nfs_wait_bit_killable(void *word); extern const struct super_operations nfs_sops; extern struct file_system_type nfs_fs_type; extern struct file_system_type nfs_xdev_fs_type; -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) extern struct file_system_type nfs4_xdev_fs_type; extern struct file_system_type nfs4_referral_fs_type; #endif @@ -364,7 +364,7 @@ struct vfsmount *nfs_do_submount(struct dentry *, struct nfs_fh *, /* getroot.c */ extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *, const char *); -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) extern struct dentry *nfs4_get_root(struct super_block *, struct nfs_fh *, const char *); diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c index 2a3b170e88e0..655925373b91 100644 --- a/fs/nfs/namespace.c +++ b/fs/nfs/namespace.c @@ -113,6 +113,7 @@ Elong_unlock: Elong: return ERR_PTR(-ENAMETOOLONG); } +EXPORT_SYMBOL_GPL(nfs_path); /* * nfs_d_automount - Handle crossing a mountpoint on the server @@ -241,6 +242,7 @@ out: dprintk("<-- nfs_do_submount() = %p\n", mnt); return mnt; } +EXPORT_SYMBOL_GPL(nfs_do_submount); struct vfsmount *nfs_submount(struct nfs_server *server, struct dentry *dentry, struct nfs_fh *fh, struct nfs_fattr *fattr) diff --git a/fs/nfs/netns.h b/fs/nfs/netns.h index 8a6394edb8b0..0539de1b8d1f 100644 --- a/fs/nfs/netns.h +++ b/fs/nfs/netns.h @@ -20,7 +20,7 @@ struct nfs_net { wait_queue_head_t bl_wq; struct list_head nfs_client_list; struct list_head nfs_volume_list; -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) struct idr cb_ident_idr; /* Protected by nfs_client_lock */ #endif spinlock_t nfs_client_lock; diff --git a/fs/nfs/nfs.h b/fs/nfs/nfs.h index 3e1b84baa57f..43679df56cd0 100644 --- a/fs/nfs/nfs.h +++ b/fs/nfs/nfs.h @@ -21,23 +21,6 @@ struct nfs_subversion { struct list_head list; /* List of NFS versions */ }; -int nfs_register_versions(void); -void nfs_unregister_versions(void); - -#ifdef CONFIG_NFS_V4 -int init_nfs_v4(void); -void exit_nfs_v4(void); -#else /* CONFIG_NFS_V4 */ -static inline int __init init_nfs_v4(void) -{ - return 0; -} - -static inline void exit_nfs_v4(void) -{ -} -#endif /* CONFIG_NFS_V4 */ - struct nfs_subversion *get_nfs_version(unsigned int); void put_nfs_version(struct nfs_subversion *); void register_nfs_version(struct nfs_subversion *); diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index bafe5186c9cd..3b950dd81e81 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -9,7 +9,7 @@ #ifndef __LINUX_FS_NFS_NFS4_FS_H #define __LINUX_FS_NFS_NFS4_FS_H -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) struct idmap; @@ -365,11 +365,10 @@ extern const nfs4_stateid zero_stateid; struct nfs_mount_info; extern struct nfs_subversion nfs_v4; struct dentry *nfs4_try_mount(int, const char *, struct nfs_mount_info *, struct nfs_subversion *); -int init_nfs_v4(void); -void exit_nfs_v4(void); extern bool nfs4_disable_idmapping; extern unsigned short max_session_slots; extern unsigned short send_implementation_id; + /* nfs4sysctl.c */ #ifdef CONFIG_SYSCTL int nfs4_register_sysctl(void); diff --git a/fs/nfs/nfs4super.c b/fs/nfs/nfs4super.c index 1c825f3bef51..12a31a9dbcdd 100644 --- a/fs/nfs/nfs4super.c +++ b/fs/nfs/nfs4super.c @@ -332,7 +332,7 @@ static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type, } -int __init init_nfs_v4(void) +static int __init init_nfs_v4(void) { int err; @@ -358,10 +358,15 @@ out: return err; } -void exit_nfs_v4(void) +static void __exit exit_nfs_v4(void) { unregister_nfs_version(&nfs_v4); unregister_filesystem(&nfs4_fs_type); nfs4_unregister_sysctl(); nfs_idmap_quit(); } + +MODULE_LICENSE("GPL"); + +module_init(init_nfs_v4); +module_exit(exit_nfs_v4); diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index aed913c833f4..1e7d8879dae6 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -54,6 +54,7 @@ void nfs_pgheader_init(struct nfs_pageio_descriptor *desc, if (hdr->completion_ops->init_hdr) hdr->completion_ops->init_hdr(hdr); } +EXPORT_SYMBOL_GPL(nfs_pgheader_init); void nfs_set_pgio_error(struct nfs_pgio_header *hdr, int error, loff_t pos) { @@ -268,6 +269,7 @@ void nfs_pageio_init(struct nfs_pageio_descriptor *desc, desc->pg_lseg = NULL; desc->pg_dreq = NULL; } +EXPORT_SYMBOL_GPL(nfs_pageio_init); /** * nfs_can_coalesce_requests - test two requests for compatibility @@ -409,6 +411,7 @@ int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc, } while (ret); return ret; } +EXPORT_SYMBOL_GPL(nfs_pageio_add_request); /** * nfs_pageio_complete - Complete I/O on an nfs_pageio_descriptor @@ -424,6 +427,7 @@ void nfs_pageio_complete(struct nfs_pageio_descriptor *desc) break; } } +EXPORT_SYMBOL_GPL(nfs_pageio_complete); /** * nfs_pageio_cond_complete - Conditional I/O completion diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 7fbd25afe418..76875bfcf19c 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1407,6 +1407,7 @@ static void pnfs_writehdr_free(struct nfs_pgio_header *hdr) put_lseg(hdr->lseg); nfs_writehdr_free(hdr); } +EXPORT_SYMBOL_GPL(pnfs_writehdr_free); int pnfs_generic_pg_writepages(struct nfs_pageio_descriptor *desc) @@ -1561,6 +1562,7 @@ static void pnfs_readhdr_free(struct nfs_pgio_header *hdr) put_lseg(hdr->lseg); nfs_readhdr_free(hdr); } +EXPORT_SYMBOL_GPL(pnfs_readhdr_free); int pnfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc) diff --git a/fs/nfs/read.c b/fs/nfs/read.c index b000e4c0cf83..6935e401ad76 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -48,6 +48,7 @@ struct nfs_read_header *nfs_readhdr_alloc(void) } return rhdr; } +EXPORT_SYMBOL_GPL(nfs_readhdr_alloc); static struct nfs_read_data *nfs_readdata_alloc(struct nfs_pgio_header *hdr, unsigned int pagecount) @@ -80,6 +81,7 @@ void nfs_readhdr_free(struct nfs_pgio_header *hdr) kmem_cache_free(nfs_rdata_cachep, rhdr); } +EXPORT_SYMBOL_GPL(nfs_readhdr_free); void nfs_readdata_release(struct nfs_read_data *rdata) { @@ -96,6 +98,7 @@ void nfs_readdata_release(struct nfs_read_data *rdata) if (atomic_dec_and_test(&hdr->refcnt)) hdr->completion_ops->completion(hdr); } +EXPORT_SYMBOL_GPL(nfs_readdata_release); static int nfs_return_empty_page(struct page *page) @@ -398,6 +401,7 @@ int nfs_generic_pagein(struct nfs_pageio_descriptor *desc, return nfs_pagein_multi(desc, hdr); return nfs_pagein_one(desc, hdr); } +EXPORT_SYMBOL_GPL(nfs_generic_pagein); static int nfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc) { diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 558a85c9594a..ac6a3c55dce4 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -315,7 +315,7 @@ const struct super_operations nfs_sops = { }; EXPORT_SYMBOL_GPL(nfs_sops); -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *); static int nfs4_validate_mount_data(void *options, struct nfs_parsed_mount_data *args, const char *dev_name); @@ -366,6 +366,7 @@ void nfs_sb_active(struct super_block *sb) if (atomic_inc_return(&server->active) == 1) atomic_inc(&sb->s_active); } +EXPORT_SYMBOL_GPL(nfs_sb_active); void nfs_sb_deactive(struct super_block *sb) { @@ -374,6 +375,7 @@ void nfs_sb_deactive(struct super_block *sb) if (atomic_dec_and_test(&server->active)) deactivate_super(sb); } +EXPORT_SYMBOL_GPL(nfs_sb_deactive); /* * Deliver file system statistics to userspace @@ -439,6 +441,7 @@ int nfs_statfs(struct dentry *dentry, struct kstatfs *buf) dprintk("%s: statfs error = %d\n", __func__, -error); return error; } +EXPORT_SYMBOL_GPL(nfs_statfs); /* * Map the security flavour number to a name @@ -544,7 +547,7 @@ static void nfs_show_mountd_options(struct seq_file *m, struct nfs_server *nfss, nfs_show_mountd_netid(m, nfss, showdefaults); } -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) static void nfs_show_nfsv4_options(struct seq_file *m, struct nfs_server *nfss, int showdefaults) { @@ -675,8 +678,9 @@ int nfs_show_options(struct seq_file *m, struct dentry *root) return 0; } +EXPORT_SYMBOL_GPL(nfs_show_options); -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) #ifdef CONFIG_NFS_V4_1 static void show_sessions(struct seq_file *m, struct nfs_server *server) { @@ -709,7 +713,7 @@ static void show_implementation_id(struct seq_file *m, struct nfs_server *nfss) } } #else -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) static void show_pnfs(struct seq_file *m, struct nfs_server *server) { } @@ -734,12 +738,14 @@ int nfs_show_devname(struct seq_file *m, struct dentry *root) free_page((unsigned long)page); return err; } +EXPORT_SYMBOL_GPL(nfs_show_devname); int nfs_show_path(struct seq_file *m, struct dentry *dentry) { seq_puts(m, "/"); return 0; } +EXPORT_SYMBOL_GPL(nfs_show_path); /* * Present statistical information for this VFS mountpoint @@ -774,7 +780,7 @@ int nfs_show_stats(struct seq_file *m, struct dentry *root) seq_printf(m, ",bsize=%u", nfss->bsize); seq_printf(m, ",namlen=%u", nfss->namelen); -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) if (nfss->nfs_client->rpc_ops->version == 4) { seq_printf(m, "\n\tnfsv4:\t"); seq_printf(m, "bm0=0x%x", nfss->attr_bitmask[0]); @@ -832,6 +838,7 @@ int nfs_show_stats(struct seq_file *m, struct dentry *root) return 0; } +EXPORT_SYMBOL_GPL(nfs_show_stats); /* * Begin unmount by attempting to remove all automounted mountpoints we added @@ -851,6 +858,7 @@ void nfs_umount_begin(struct super_block *sb) if (!IS_ERR(rpc)) rpc_killall_tasks(rpc); } +EXPORT_SYMBOL_GPL(nfs_umount_begin); static struct nfs_parsed_mount_data *nfs_alloc_parsed_mount_data(void) { @@ -1915,7 +1923,7 @@ out_invalid_fh: return -EINVAL; } -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) static int nfs_validate_mount_data(struct file_system_type *fs_type, void *options, struct nfs_parsed_mount_data *args, @@ -1953,7 +1961,7 @@ static int nfs_validate_text_mount_data(void *options, goto out_no_address; if (args->version == 4) { -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) port = NFS_PORT; max_namelen = NFS4_MAXNAMLEN; max_pathlen = NFS4_MAXPATHLEN; @@ -1976,7 +1984,7 @@ static int nfs_validate_text_mount_data(void *options, &args->nfs_server.export_path, max_pathlen); -#ifndef CONFIG_NFS_V4 +#if !IS_ENABLED(CONFIG_NFS_V4) out_v4_not_compiled: dfprintk(MOUNT, "NFS: NFSv4 is not compiled into kernel\n"); return -EPROTONOSUPPORT; @@ -2075,6 +2083,7 @@ out: kfree(data); return error; } +EXPORT_SYMBOL_GPL(nfs_remount); /* * Initialise the common bits of the superblock @@ -2123,6 +2132,7 @@ void nfs_fill_super(struct super_block *sb, struct nfs_mount_info *mount_info) nfs_initialise_sb(sb); } +EXPORT_SYMBOL_GPL(nfs_fill_super); /* * Finish setting up a cloned NFS2/3/4 superblock @@ -2292,6 +2302,7 @@ int nfs_set_sb_security(struct super_block *s, struct dentry *mntroot, { return security_sb_set_mnt_opts(s, &mount_info->parsed->lsm_opts); } +EXPORT_SYMBOL_GPL(nfs_set_sb_security); int nfs_clone_sb_security(struct super_block *s, struct dentry *mntroot, struct nfs_mount_info *mount_info) @@ -2302,6 +2313,7 @@ int nfs_clone_sb_security(struct super_block *s, struct dentry *mntroot, return -ESTALE; return 0; } +EXPORT_SYMBOL_GPL(nfs_clone_sb_security); struct dentry *nfs_fs_mount_common(struct nfs_server *server, int flags, const char *dev_name, @@ -2375,6 +2387,7 @@ error_splat_bdi: deactivate_locked_super(s); goto out; } +EXPORT_SYMBOL_GPL(nfs_fs_mount_common); struct dentry *nfs_fs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *raw_data) @@ -2415,6 +2428,7 @@ out: nfs_free_fhandle(mount_info.mntfh); return mntroot; } +EXPORT_SYMBOL_GPL(nfs_fs_mount); /* * Ensure that we unregister the bdi before kill_anon_super @@ -2426,6 +2440,7 @@ void nfs_put_super(struct super_block *s) bdi_unregister(&server->backing_dev_info); } +EXPORT_SYMBOL_GPL(nfs_put_super); /* * Destroy an NFS2/3 superblock @@ -2438,6 +2453,7 @@ void nfs_kill_super(struct super_block *s) nfs_fscache_release_super_cookie(s); nfs_free_server(server); } +EXPORT_SYMBOL_GPL(nfs_kill_super); /* * Clone an NFS2/3/4 server record on xdev traversal (FSID-change) @@ -2478,7 +2494,7 @@ out_err: goto out; } -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *args) { @@ -2590,6 +2606,13 @@ bool nfs4_disable_idmapping = true; unsigned short max_session_slots = NFS4_DEF_SLOT_TABLE_SIZE; unsigned short send_implementation_id = 1; +EXPORT_SYMBOL_GPL(nfs_callback_set_tcpport); +EXPORT_SYMBOL_GPL(nfs_callback_tcpport); +EXPORT_SYMBOL_GPL(nfs_idmap_cache_timeout); +EXPORT_SYMBOL_GPL(nfs4_disable_idmapping); +EXPORT_SYMBOL_GPL(max_session_slots); +EXPORT_SYMBOL_GPL(send_implementation_id); + #define NFS_CALLBACK_MAXPORTNR (65535U) static int param_set_portnr(const char *val, const struct kernel_param *kp) diff --git a/fs/nfs/write.c b/fs/nfs/write.c index f268fe4f2785..e4a2ad2059bd 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -84,6 +84,7 @@ struct nfs_write_header *nfs_writehdr_alloc(void) } return p; } +EXPORT_SYMBOL_GPL(nfs_writehdr_alloc); static struct nfs_write_data *nfs_writedata_alloc(struct nfs_pgio_header *hdr, unsigned int pagecount) @@ -115,6 +116,7 @@ void nfs_writehdr_free(struct nfs_pgio_header *hdr) struct nfs_write_header *whdr = container_of(hdr, struct nfs_write_header, header); mempool_free(whdr, nfs_wdata_mempool); } +EXPORT_SYMBOL_GPL(nfs_writehdr_free); void nfs_writedata_release(struct nfs_write_data *wdata) { @@ -131,6 +133,7 @@ void nfs_writedata_release(struct nfs_write_data *wdata) if (atomic_dec_and_test(&hdr->refcnt)) hdr->completion_ops->completion(hdr); } +EXPORT_SYMBOL_GPL(nfs_writedata_release); static void nfs_context_set_write_error(struct nfs_open_context *ctx, int error) { @@ -446,7 +449,7 @@ nfs_mark_request_dirty(struct nfs_page *req) __set_page_dirty_nobuffers(req->wb_page); } -#if IS_ENABLED(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) +#if IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4) /** * nfs_request_add_commit_list - add request to a commit list * @req: pointer to a struct nfs_page @@ -636,7 +639,7 @@ out: hdr->release(hdr); } -#if IS_ENABLED(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) +#if IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4) static unsigned long nfs_reqs_to_commit(struct nfs_commit_info *cinfo) { @@ -1173,6 +1176,7 @@ int nfs_generic_flush(struct nfs_pageio_descriptor *desc, return nfs_flush_multi(desc, hdr); return nfs_flush_one(desc, hdr); } +EXPORT_SYMBOL_GPL(nfs_generic_flush); static int nfs_generic_pg_writepages(struct nfs_pageio_descriptor *desc) { @@ -1298,7 +1302,7 @@ void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) return; nfs_add_stats(inode, NFSIOS_SERVERWRITTENBYTES, resp->count); -#if IS_ENABLED(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) +#if IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4) if (resp->verf->committed < argp->stable && task->tk_status >= 0) { /* We tried a write call, but the server did not * commit data to stable storage even though we @@ -1358,7 +1362,7 @@ void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) } -#if IS_ENABLED(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) +#if IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4) static int nfs_commit_set_lock(struct nfs_inode *nfsi, int may_wait) { int ret; @@ -1674,6 +1678,7 @@ int nfs_write_inode(struct inode *inode, struct writeback_control *wbc) { return nfs_commit_unstable_pages(inode, wbc); } +EXPORT_SYMBOL_GPL(nfs_write_inode); /* * flush the inode to disk. diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 4b6043c20f77..2889877318bc 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -191,7 +191,7 @@ struct nfs_inode { struct hlist_head silly_list; wait_queue_head_t waitqueue; -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) struct nfs4_cached_acl *nfs4_acl; /* NFSv4 state */ struct list_head open_states; @@ -428,7 +428,7 @@ extern __be32 root_nfs_parse_addr(char *name); /*__init*/ * linux/fs/nfs/file.c */ extern const struct file_operations nfs_file_operations; -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) extern const struct file_operations nfs4_file_operations; #endif /* CONFIG_NFS_V4 */ extern const struct address_space_operations nfs_file_aops; @@ -538,7 +538,7 @@ extern void nfs_writeback_done(struct rpc_task *, struct nfs_write_data *); extern int nfs_wb_all(struct inode *inode); extern int nfs_wb_page(struct inode *inode, struct page* page); extern int nfs_wb_page_cancel(struct inode *inode, struct page* page); -#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) +#if IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4) extern int nfs_commit_inode(struct inode *, int); extern struct nfs_commit_data *nfs_commitdata_alloc(void); extern void nfs_commit_free(struct nfs_commit_data *data); diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 6039297801f4..310c63c8ab2c 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -53,7 +53,7 @@ struct nfs_client { u32 cl_minorversion;/* NFSv4 minorversion */ struct rpc_cred *cl_machine_cred; -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) u64 cl_clientid; /* constant */ nfs4_verifier cl_confirm; /* Clientid verifier */ unsigned long cl_state; @@ -138,7 +138,7 @@ struct nfs_server { #endif u32 pnfs_blksize; /* layout_blksize attr */ -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) u32 attr_bitmask[3];/* V4 bitmask representing the set of attributes supported on this filesystem */ @@ -201,7 +201,7 @@ struct nfs_server { #define NFS4_MAX_SLOT_TABLE (256U) #define NFS4_NO_SLOT ((u32)-1) -#if defined(CONFIG_NFS_V4) +#if IS_ENABLED(CONFIG_NFS_V4) /* Sessions */ #define SLOT_TABLE_SZ DIV_ROUND_UP(NFS4_MAX_SLOT_TABLE, 8*sizeof(long)) diff --git a/include/linux/nfs_idmap.h b/include/linux/nfs_idmap.h index 7eed2012d288..ece91c57ad79 100644 --- a/include/linux/nfs_idmap.h +++ b/include/linux/nfs_idmap.h @@ -69,7 +69,7 @@ struct nfs_server; struct nfs_fattr; struct nfs4_string; -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) int nfs_idmap_init(void); void nfs_idmap_quit(void); #else diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 631182062994..00485e084394 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -824,7 +824,7 @@ struct nfs3_getaclres { struct posix_acl * acl_default; }; -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) typedef u64 clientid4; -- cgit v1.2.3 From 3d687b49ff085b325488e7aeb2ee4df99dd7ca6e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 5 Jul 2012 06:04:04 -0300 Subject: [media] videodev2.h: add VIDIOC_ENUM_FREQ_BANDS Add a new ioctl to enumerate the supported frequency bands of a tuner. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/linux/videodev2.h | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 4cf766e6f120..63c950f6fcc2 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -2032,6 +2032,7 @@ struct v4l2_modulator { #define V4L2_TUNER_CAP_RDS 0x0080 #define V4L2_TUNER_CAP_RDS_BLOCK_IO 0x0100 #define V4L2_TUNER_CAP_RDS_CONTROLS 0x0200 +#define V4L2_TUNER_CAP_FREQ_BANDS 0x0400 /* Flags for the 'rxsubchans' field */ #define V4L2_TUNER_SUB_MONO 0x0001 @@ -2050,19 +2051,34 @@ struct v4l2_modulator { #define V4L2_TUNER_MODE_LANG1_LANG2 0x0004 struct v4l2_frequency { - __u32 tuner; - __u32 type; /* enum v4l2_tuner_type */ - __u32 frequency; - __u32 reserved[8]; + __u32 tuner; + __u32 type; /* enum v4l2_tuner_type */ + __u32 frequency; + __u32 reserved[8]; +}; + +#define V4L2_BAND_MODULATION_VSB (1 << 1) +#define V4L2_BAND_MODULATION_FM (1 << 2) +#define V4L2_BAND_MODULATION_AM (1 << 3) + +struct v4l2_frequency_band { + __u32 tuner; + __u32 type; /* enum v4l2_tuner_type */ + __u32 index; + __u32 capability; + __u32 rangelow; + __u32 rangehigh; + __u32 modulation; + __u32 reserved[9]; }; struct v4l2_hw_freq_seek { - __u32 tuner; - __u32 type; /* enum v4l2_tuner_type */ - __u32 seek_upward; - __u32 wrap_around; - __u32 spacing; - __u32 reserved[7]; + __u32 tuner; + __u32 type; /* enum v4l2_tuner_type */ + __u32 seek_upward; + __u32 wrap_around; + __u32 spacing; + __u32 reserved[7]; }; /* @@ -2630,6 +2646,10 @@ struct v4l2_create_buffers { #define VIDIOC_QUERY_DV_TIMINGS _IOR('V', 99, struct v4l2_dv_timings) #define VIDIOC_DV_TIMINGS_CAP _IOWR('V', 100, struct v4l2_dv_timings_cap) +/* Experimental, this ioctl may change over the next couple of kernel + versions. */ +#define VIDIOC_ENUM_FREQ_BANDS _IOWR('V', 101, struct v4l2_frequency_band) + /* Reminder: when adding new ioctls please add support for them to drivers/media/video/v4l2-compat-ioctl32.c as well! */ -- cgit v1.2.3 From 82b655bfc32b793344ef0ddd46df8af8b98b55c7 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 5 Jul 2012 06:37:08 -0300 Subject: [media] v4l2: add core support for the new VIDIOC_ENUM_FREQ_BANDS ioctl This adds the usual core support code for this new ioctl. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-compat-ioctl32.c | 1 + drivers/media/video/v4l2-dev.c | 2 + drivers/media/video/v4l2-ioctl.c | 83 ++++++++++++++++++++++++++++++- include/media/v4l2-ioctl.h | 2 + 4 files changed, 86 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index ac365cfb3706..9ebd5c540d10 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -1025,6 +1025,7 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_ENUM_DV_TIMINGS: case VIDIOC_QUERY_DV_TIMINGS: case VIDIOC_DV_TIMINGS_CAP: + case VIDIOC_ENUM_FREQ_BANDS: ret = do_video_ioctl(file, cmd, arg); break; diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 1b2f1c52e6c3..625248585c85 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -730,6 +730,8 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_UNSUBSCRIBE_EVENT, vidioc_unsubscribe_event); SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs); SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf); + if (ops->vidioc_enum_freq_bands || ops->vidioc_g_tuner || ops->vidioc_g_modulator) + set_bit(_IOC_NR(VIDIOC_ENUM_FREQ_BANDS), valid_ioctls); bitmap_andnot(vdev->valid_ioctls, valid_ioctls, vdev->valid_ioctls, BASE_VIDIOC_PRIVATE); } diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 3e4db2c1c3d6..0f54f8efe66b 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -825,6 +825,17 @@ static void v4l_print_sliced_vbi_cap(const void *arg, bool write_only) p->service_lines[1][i]); } +static void v4l_print_freq_band(const void *arg, bool write_only) +{ + const struct v4l2_frequency_band *p = arg; + + pr_cont("tuner=%u, type=%u, index=%u, capability=0x%x, " + "rangelow=%u, rangehigh=%u, modulation=0x%x\n", + p->tuner, p->type, p->index, + p->capability, p->rangelow, + p->rangehigh, p->modulation); +} + static void v4l_print_u32(const void *arg, bool write_only) { pr_cont("value=%u\n", *(const u32 *)arg); @@ -1245,10 +1256,14 @@ static int v4l_g_tuner(const struct v4l2_ioctl_ops *ops, { struct video_device *vfd = video_devdata(file); struct v4l2_tuner *p = arg; + int err; p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - return ops->vidioc_g_tuner(file, fh, p); + err = ops->vidioc_g_tuner(file, fh, p); + if (!err) + p->capability |= V4L2_TUNER_CAP_FREQ_BANDS; + return err; } static int v4l_s_tuner(const struct v4l2_ioctl_ops *ops, @@ -1262,6 +1277,18 @@ static int v4l_s_tuner(const struct v4l2_ioctl_ops *ops, return ops->vidioc_s_tuner(file, fh, p); } +static int v4l_g_modulator(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct v4l2_modulator *p = arg; + int err; + + err = ops->vidioc_g_modulator(file, fh, p); + if (!err) + p->capability |= V4L2_TUNER_CAP_FREQ_BANDS; + return err; +} + static int v4l_g_frequency(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { @@ -1805,6 +1832,57 @@ static int v4l_g_sliced_vbi_cap(const struct v4l2_ioctl_ops *ops, return ops->vidioc_g_sliced_vbi_cap(file, fh, p); } +static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + struct v4l2_frequency_band *p = arg; + enum v4l2_tuner_type type; + int err; + + type = (vfd->vfl_type == VFL_TYPE_RADIO) ? + V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + + if (type != p->type) + return -EINVAL; + if (ops->vidioc_enum_freq_bands) + return ops->vidioc_enum_freq_bands(file, fh, p); + if (ops->vidioc_g_tuner) { + struct v4l2_tuner t = { + .index = p->tuner, + .type = type, + }; + + err = ops->vidioc_g_tuner(file, fh, &t); + if (err) + return err; + p->capability = t.capability | V4L2_TUNER_CAP_FREQ_BANDS; + p->rangelow = t.rangelow; + p->rangehigh = t.rangehigh; + p->modulation = (type == V4L2_TUNER_RADIO) ? + V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB; + return 0; + } + if (ops->vidioc_g_modulator) { + struct v4l2_modulator m = { + .index = p->tuner, + }; + + if (type != V4L2_TUNER_RADIO) + return -EINVAL; + err = ops->vidioc_g_modulator(file, fh, &m); + if (err) + return err; + p->capability = m.capability | V4L2_TUNER_CAP_FREQ_BANDS; + p->rangelow = m.rangelow; + p->rangehigh = m.rangehigh; + p->modulation = (type == V4L2_TUNER_RADIO) ? + V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB; + return 0; + } + return -ENOTTY; +} + struct v4l2_ioctl_info { unsigned int ioctl; u32 flags; @@ -1886,7 +1964,7 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_FNC(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)), IOCTL_INFO_STD(VIDIOC_G_AUDOUT, vidioc_g_audout, v4l_print_audioout, 0), IOCTL_INFO_STD(VIDIOC_S_AUDOUT, vidioc_s_audout, v4l_print_audioout, INFO_FL_PRIO), - IOCTL_INFO_STD(VIDIOC_G_MODULATOR, vidioc_g_modulator, v4l_print_modulator, INFO_FL_CLEAR(v4l2_modulator, index)), + IOCTL_INFO_FNC(VIDIOC_G_MODULATOR, v4l_g_modulator, v4l_print_modulator, INFO_FL_CLEAR(v4l2_modulator, index)), IOCTL_INFO_STD(VIDIOC_S_MODULATOR, vidioc_s_modulator, v4l_print_modulator, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_G_FREQUENCY, v4l_g_frequency, v4l_print_frequency, INFO_FL_CLEAR(v4l2_frequency, tuner)), IOCTL_INFO_FNC(VIDIOC_S_FREQUENCY, v4l_s_frequency, v4l_print_frequency, INFO_FL_PRIO), @@ -1933,6 +2011,7 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_STD(VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings, v4l_print_enum_dv_timings, 0), IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, 0), IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, type)), + IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0), }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index 19e93523c2d8..e614c9c15e56 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -230,6 +230,8 @@ struct v4l2_ioctl_ops { struct v4l2_frequency *a); int (*vidioc_s_frequency) (struct file *file, void *fh, struct v4l2_frequency *a); + int (*vidioc_enum_freq_bands) (struct file *file, void *fh, + struct v4l2_frequency_band *band); /* Sliced VBI cap */ int (*vidioc_g_sliced_vbi_cap) (struct file *file, void *fh, -- cgit v1.2.3 From e0a9b1770bac048171961625875aaf15118a7ae9 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 12 Jul 2012 16:55:45 -0300 Subject: [media] v4l2: Add rangelow and rangehigh fields to the v4l2_hw_freq_seek struct To allow apps to limit a hw-freq-seek to a specific band, for further info see the documentation this patch adds for these new fields. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- .../DocBook/media/v4l/vidioc-s-hw-freq-seek.xml | 50 ++++++++++++++++++---- include/linux/videodev2.h | 5 ++- 2 files changed, 46 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml b/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml index f4db44d0d95a..3dd1bec6d3c7 100644 --- a/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml +++ b/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml @@ -52,11 +52,23 @@ Start a hardware frequency seek from the current frequency. To do this applications initialize the tuner, type, seek_upward, -spacing and -wrap_around fields, and zero out the -reserved array of a &v4l2-hw-freq-seek; and -call the VIDIOC_S_HW_FREQ_SEEK ioctl with a pointer -to this structure. +wrap_around, spacing, +rangelow and rangehigh +fields, and zero out the reserved array of a +&v4l2-hw-freq-seek; and call the VIDIOC_S_HW_FREQ_SEEK +ioctl with a pointer to this structure. + + The rangelow and +rangehigh fields can be set to a non-zero value to +tell the driver to search a specific band. If the &v4l2-tuner; +capability field has the +V4L2_TUNER_CAP_HWSEEK_PROG_LIM flag set, these values +must fall within one of the bands returned by &VIDIOC-ENUM-FREQ-BANDS;. If +the V4L2_TUNER_CAP_HWSEEK_PROG_LIM flag is not set, +then these values must exactly match those of one of the bands returned by +&VIDIOC-ENUM-FREQ-BANDS;. If the current frequency of the tuner does not fall +within the selected band it will be clamped to fit in the band before the +seek is started. If an error is returned, then the original frequency will be restored. @@ -102,7 +114,27 @@ field and the &v4l2-tuner; index field. __u32 - reserved[7] + rangelow + If non-zero, the lowest tunable frequency of the band to +search in units of 62.5 kHz, or if the &v4l2-tuner; +capability field has the +V4L2_TUNER_CAP_LOW flag set, in units of 62.5 Hz. +If rangelow is zero a reasonable default value +is used. + + + __u32 + rangehigh + If non-zero, the highest tunable frequency of the band to +search in units of 62.5 kHz, or if the &v4l2-tuner; +capability field has the +V4L2_TUNER_CAP_LOW flag set, in units of 62.5 Hz. +If rangehigh is zero a reasonable default value +is used. + + + __u32 + reserved[5] Reserved for future extensions. Applications must set the array to zero. @@ -119,8 +151,10 @@ field and the &v4l2-tuner; index field. EINVAL The tuner index is out of -bounds, the wrap_around value is not supported or the value in the type field is -wrong. +bounds, the wrap_around value is not supported or +one of the values in the type, +rangelow or rangehigh +fields is wrong. diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 63c950f6fcc2..7a147c8299ab 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -2033,6 +2033,7 @@ struct v4l2_modulator { #define V4L2_TUNER_CAP_RDS_BLOCK_IO 0x0100 #define V4L2_TUNER_CAP_RDS_CONTROLS 0x0200 #define V4L2_TUNER_CAP_FREQ_BANDS 0x0400 +#define V4L2_TUNER_CAP_HWSEEK_PROG_LIM 0x0800 /* Flags for the 'rxsubchans' field */ #define V4L2_TUNER_SUB_MONO 0x0001 @@ -2078,7 +2079,9 @@ struct v4l2_hw_freq_seek { __u32 seek_upward; __u32 wrap_around; __u32 spacing; - __u32 reserved[7]; + __u32 rangelow; + __u32 rangehigh; + __u32 reserved[5]; }; /* -- cgit v1.2.3 From 1fe60e51a3744528f3939b1b1167ca909133d9ae Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 30 Jul 2012 16:23:22 -0700 Subject: libceph: move feature bits to separate header This is simply cleanup that will keep things more closely synced with the userland code. Signed-off-by: Sage Weil Reviewed-by: Alex Elder Reviewed-by: Yehuda Sadeh --- fs/ceph/mds_client.c | 1 + fs/ceph/super.c | 1 + include/linux/ceph/ceph_features.h | 24 ++++++++++++++++++++++++ include/linux/ceph/ceph_fs.h | 14 -------------- include/linux/ceph/libceph.h | 6 ------ net/ceph/ceph_common.c | 5 +++-- 6 files changed, 29 insertions(+), 22 deletions(-) create mode 100644 include/linux/ceph/ceph_features.h (limited to 'include') diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 418f6a82c90d..39b76d66bc5d 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -10,6 +10,7 @@ #include "super.h" #include "mds_client.h" +#include #include #include #include diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 1e67dd7305a4..2c47ecfe4373 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -18,6 +18,7 @@ #include "super.h" #include "mds_client.h" +#include #include #include #include diff --git a/include/linux/ceph/ceph_features.h b/include/linux/ceph/ceph_features.h new file mode 100644 index 000000000000..342f93dbe162 --- /dev/null +++ b/include/linux/ceph/ceph_features.h @@ -0,0 +1,24 @@ +#ifndef __CEPH_FEATURES +#define __CEPH_FEATURES + +/* + * feature bits + */ +#define CEPH_FEATURE_UID (1<<0) +#define CEPH_FEATURE_NOSRCADDR (1<<1) +#define CEPH_FEATURE_MONCLOCKCHECK (1<<2) +#define CEPH_FEATURE_FLOCK (1<<3) +#define CEPH_FEATURE_SUBSCRIBE2 (1<<4) +#define CEPH_FEATURE_MONNAMES (1<<5) +#define CEPH_FEATURE_RECONNECT_SEQ (1<<6) +#define CEPH_FEATURE_DIRLAYOUTHASH (1<<7) + +/* + * Features supported. + */ +#define CEPH_FEATURES_SUPPORTED_DEFAULT \ + (CEPH_FEATURE_NOSRCADDR) + +#define CEPH_FEATURES_REQUIRED_DEFAULT \ + (CEPH_FEATURE_NOSRCADDR) +#endif diff --git a/include/linux/ceph/ceph_fs.h b/include/linux/ceph/ceph_fs.h index e81ab30d4896..d021610efd65 100644 --- a/include/linux/ceph/ceph_fs.h +++ b/include/linux/ceph/ceph_fs.h @@ -35,20 +35,6 @@ /* arbitrary limit on max # of monitors (cluster of 3 is typical) */ #define CEPH_MAX_MON 31 - -/* - * feature bits - */ -#define CEPH_FEATURE_UID (1<<0) -#define CEPH_FEATURE_NOSRCADDR (1<<1) -#define CEPH_FEATURE_MONCLOCKCHECK (1<<2) -#define CEPH_FEATURE_FLOCK (1<<3) -#define CEPH_FEATURE_SUBSCRIBE2 (1<<4) -#define CEPH_FEATURE_MONNAMES (1<<5) -#define CEPH_FEATURE_RECONNECT_SEQ (1<<6) -#define CEPH_FEATURE_DIRLAYOUTHASH (1<<7) - - /* * ceph_file_layout - describe data layout for a file/inode */ diff --git a/include/linux/ceph/libceph.h b/include/linux/ceph/libceph.h index 98ec36ae8a3b..ea072e1f9db9 100644 --- a/include/linux/ceph/libceph.h +++ b/include/linux/ceph/libceph.h @@ -22,12 +22,6 @@ #include "osd_client.h" #include "ceph_fs.h" -/* - * Supported features - */ -#define CEPH_FEATURE_SUPPORTED_DEFAULT CEPH_FEATURE_NOSRCADDR -#define CEPH_FEATURE_REQUIRED_DEFAULT CEPH_FEATURE_NOSRCADDR - /* * mount options */ diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c index 3b45e01fa8d1..69e38db28e5f 100644 --- a/net/ceph/ceph_common.c +++ b/net/ceph/ceph_common.c @@ -17,6 +17,7 @@ #include +#include #include #include #include @@ -460,9 +461,9 @@ struct ceph_client *ceph_create_client(struct ceph_options *opt, void *private, client->auth_err = 0; client->extra_mon_dispatch = NULL; - client->supported_features = CEPH_FEATURE_SUPPORTED_DEFAULT | + client->supported_features = CEPH_FEATURES_SUPPORTED_DEFAULT | supported_features; - client->required_features = CEPH_FEATURE_REQUIRED_DEFAULT | + client->required_features = CEPH_FEATURES_REQUIRED_DEFAULT | required_features; /* msgr */ -- cgit v1.2.3 From 54b501992dd2a839e94e76aa392c392b55080ce8 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 30 Jul 2012 14:39:18 -0700 Subject: coredump: warn about unsafe suid_dumpable / core_pattern combo When suid_dumpable=2, detect unsafe core_pattern settings and warn when they are seen. Signed-off-by: Kees Cook Suggested-by: Andrew Morton Cc: Alexander Viro Cc: Alan Cox Cc: "Eric W. Biederman" Cc: Doug Ledford Cc: Serge Hallyn Cc: James Morris Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 10 +++++----- include/linux/sched.h | 5 +++++ kernel/sysctl.c | 37 +++++++++++++++++++++++++++++++++++-- 3 files changed, 45 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/fs/exec.c b/fs/exec.c index 95aae3f9c036..5af8390e0fae 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -2002,17 +2002,17 @@ static void coredump_finish(struct mm_struct *mm) void set_dumpable(struct mm_struct *mm, int value) { switch (value) { - case 0: + case SUID_DUMPABLE_DISABLED: clear_bit(MMF_DUMPABLE, &mm->flags); smp_wmb(); clear_bit(MMF_DUMP_SECURELY, &mm->flags); break; - case 1: + case SUID_DUMPABLE_ENABLED: set_bit(MMF_DUMPABLE, &mm->flags); smp_wmb(); clear_bit(MMF_DUMP_SECURELY, &mm->flags); break; - case 2: + case SUID_DUMPABLE_SAFE: set_bit(MMF_DUMP_SECURELY, &mm->flags); smp_wmb(); set_bit(MMF_DUMPABLE, &mm->flags); @@ -2025,7 +2025,7 @@ static int __get_dumpable(unsigned long mm_flags) int ret; ret = mm_flags & MMF_DUMPABLE_MASK; - return (ret >= 2) ? 2 : ret; + return (ret > SUID_DUMPABLE_ENABLED) ? SUID_DUMPABLE_SAFE : ret; } int get_dumpable(struct mm_struct *mm) @@ -2142,7 +2142,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs) * so we dump it as root in mode 2, and only into a controlled * environment (pipe handler or fully qualified path). */ - if (__get_dumpable(cprm.mm_flags) == 2) { + if (__get_dumpable(cprm.mm_flags) == SUID_DUMPABLE_SAFE) { /* Setuid core dump mode */ flag = O_EXCL; /* Stop rewrite attacks */ cred->fsuid = GLOBAL_ROOT_UID; /* Dump root private */ diff --git a/include/linux/sched.h b/include/linux/sched.h index a721cef7e2d4..1e26a5e45aa6 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -406,6 +406,11 @@ static inline void arch_pick_mmap_layout(struct mm_struct *mm) {} extern void set_dumpable(struct mm_struct *mm, int value); extern int get_dumpable(struct mm_struct *mm); +/* get/set_dumpable() values */ +#define SUID_DUMPABLE_DISABLED 0 +#define SUID_DUMPABLE_ENABLED 1 +#define SUID_DUMPABLE_SAFE 2 + /* mm flags */ /* dumpable bits */ #define MMF_DUMPABLE 0 /* core dump is permitted */ diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 4ab11879aeb4..b46f496405e4 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -174,6 +174,11 @@ static int proc_dointvec_minmax_sysadmin(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); #endif +static int proc_dointvec_minmax_coredump(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos); +static int proc_dostring_coredump(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos); + #ifdef CONFIG_MAGIC_SYSRQ /* Note: sysrq code uses it's own private copy */ static int __sysrq_enabled = SYSRQ_DEFAULT_ENABLE; @@ -410,7 +415,7 @@ static struct ctl_table kern_table[] = { .data = core_pattern, .maxlen = CORENAME_MAX_SIZE, .mode = 0644, - .proc_handler = proc_dostring, + .proc_handler = proc_dostring_coredump, }, { .procname = "core_pipe_limit", @@ -1498,7 +1503,7 @@ static struct ctl_table fs_table[] = { .data = &suid_dumpable, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec_minmax, + .proc_handler = proc_dointvec_minmax_coredump, .extra1 = &zero, .extra2 = &two, }, @@ -2009,6 +2014,34 @@ int proc_dointvec_minmax(struct ctl_table *table, int write, do_proc_dointvec_minmax_conv, ¶m); } +static void validate_coredump_safety(void) +{ + if (suid_dumpable == SUID_DUMPABLE_SAFE && + core_pattern[0] != '/' && core_pattern[0] != '|') { + printk(KERN_WARNING "Unsafe core_pattern used with "\ + "suid_dumpable=2. Pipe handler or fully qualified "\ + "core dump path required.\n"); + } +} + +static int proc_dointvec_minmax_coredump(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + int error = proc_dointvec_minmax(table, write, buffer, lenp, ppos); + if (!error) + validate_coredump_safety(); + return error; +} + +static int proc_dostring_coredump(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + int error = proc_dostring(table, write, buffer, lenp, ppos); + if (!error) + validate_coredump_safety(); + return error; +} + static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos, -- cgit v1.2.3 From 93abe8e4b13ae9a0428ce940a8a03ac72a7626f1 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 30 Jul 2012 14:39:27 -0700 Subject: clk: add non CONFIG_HAVE_CLK routines Many drivers are shared between architectures that may or may not have HAVE_CLK selected for them. To remove compilation errors for them we enclose clk_*() calls in these drivers within #ifdef CONFIG_HAVE_CLK, #endif. This patch removes the need of these CONFIG_HAVE_CLK statements, by introducing dummy routines when HAVE_CLK is not selected by platforms. So, definition of these routines will always be available. These calls will return error for platforms that don't select HAVE_CLK. Signed-off-by: Viresh Kumar Cc: Wolfram Sang Cc: Greg Kroah-Hartman Cc: Jeff Garzik Cc: Andrew Lunn Cc: Bhupesh Sharma Cc: Giuseppe Cavallaro Cc: Russell King Cc: Mike Turquette Cc: Sergei Shtylyov Cc: viresh kumar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/clk.h | 168 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 109 insertions(+), 59 deletions(-) (limited to 'include') diff --git a/include/linux/clk.h b/include/linux/clk.h index 2fd6a4234531..b3ac22d0fc1f 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -84,6 +84,43 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb); #endif +/** + * clk_prepare - prepare a clock source + * @clk: clock source + * + * This prepares the clock source for use. + * + * Must not be called from within atomic context. + */ +#ifdef CONFIG_HAVE_CLK_PREPARE +int clk_prepare(struct clk *clk); +#else +static inline int clk_prepare(struct clk *clk) +{ + might_sleep(); + return 0; +} +#endif + +/** + * clk_unprepare - undo preparation of a clock source + * @clk: clock source + * + * This undoes a previously prepared clock. The caller must balance + * the number of prepare and unprepare calls. + * + * Must not be called from within atomic context. + */ +#ifdef CONFIG_HAVE_CLK_PREPARE +void clk_unprepare(struct clk *clk); +#else +static inline void clk_unprepare(struct clk *clk) +{ + might_sleep(); +} +#endif + +#ifdef CONFIG_HAVE_CLK /** * clk_get - lookup and obtain a reference to a clock producer. * @dev: device for clock "consumer" @@ -121,24 +158,6 @@ struct clk *clk_get(struct device *dev, const char *id); */ struct clk *devm_clk_get(struct device *dev, const char *id); -/** - * clk_prepare - prepare a clock source - * @clk: clock source - * - * This prepares the clock source for use. - * - * Must not be called from within atomic context. - */ -#ifdef CONFIG_HAVE_CLK_PREPARE -int clk_prepare(struct clk *clk); -#else -static inline int clk_prepare(struct clk *clk) -{ - might_sleep(); - return 0; -} -#endif - /** * clk_enable - inform the system when the clock source should be running. * @clk: clock source @@ -167,47 +186,6 @@ int clk_enable(struct clk *clk); */ void clk_disable(struct clk *clk); - -/** - * clk_unprepare - undo preparation of a clock source - * @clk: clock source - * - * This undoes a previously prepared clock. The caller must balance - * the number of prepare and unprepare calls. - * - * Must not be called from within atomic context. - */ -#ifdef CONFIG_HAVE_CLK_PREPARE -void clk_unprepare(struct clk *clk); -#else -static inline void clk_unprepare(struct clk *clk) -{ - might_sleep(); -} -#endif - -/* clk_prepare_enable helps cases using clk_enable in non-atomic context. */ -static inline int clk_prepare_enable(struct clk *clk) -{ - int ret; - - ret = clk_prepare(clk); - if (ret) - return ret; - ret = clk_enable(clk); - if (ret) - clk_unprepare(clk); - - return ret; -} - -/* clk_disable_unprepare helps cases using clk_disable in non-atomic context. */ -static inline void clk_disable_unprepare(struct clk *clk) -{ - clk_disable(clk); - clk_unprepare(clk); -} - /** * clk_get_rate - obtain the current clock rate (in Hz) for a clock source. * This is only valid once the clock source has been enabled. @@ -298,6 +276,78 @@ struct clk *clk_get_parent(struct clk *clk); */ struct clk *clk_get_sys(const char *dev_id, const char *con_id); +#else /* !CONFIG_HAVE_CLK */ + +static inline struct clk *clk_get(struct device *dev, const char *id) +{ + return NULL; +} + +static inline struct clk *devm_clk_get(struct device *dev, const char *id) +{ + return NULL; +} + +static inline void clk_put(struct clk *clk) {} + +static inline void devm_clk_put(struct device *dev, struct clk *clk) {} + +static inline int clk_enable(struct clk *clk) +{ + return 0; +} + +static inline void clk_disable(struct clk *clk) {} + +static inline unsigned long clk_get_rate(struct clk *clk) +{ + return 0; +} + +static inline int clk_set_rate(struct clk *clk, unsigned long rate) +{ + return 0; +} + +static inline long clk_round_rate(struct clk *clk, unsigned long rate) +{ + return 0; +} + +static inline int clk_set_parent(struct clk *clk, struct clk *parent) +{ + return 0; +} + +static inline struct clk *clk_get_parent(struct clk *clk) +{ + return NULL; +} + +#endif + +/* clk_prepare_enable helps cases using clk_enable in non-atomic context. */ +static inline int clk_prepare_enable(struct clk *clk) +{ + int ret; + + ret = clk_prepare(clk); + if (ret) + return ret; + ret = clk_enable(clk); + if (ret) + clk_unprepare(clk); + + return ret; +} + +/* clk_disable_unprepare helps cases using clk_disable in non-atomic context. */ +static inline void clk_disable_unprepare(struct clk *clk) +{ + clk_disable(clk); + clk_unprepare(clk); +} + /** * clk_add_alias - add a new clock alias * @alias: name for clock alias -- cgit v1.2.3 From 714904e1c3f48b17ceff06b827485f8b1b5d8a91 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 30 Jul 2012 14:39:32 -0700 Subject: usb/marvell: remove conditional compilation of clk code With addition of dummy clk_*() calls for non CONFIG_HAVE_CLK cases in clk.h, there is no need to have clk code enclosed in #ifdef CONFIG_HAVE_CLK, #endif macros. Marvell usb also has these dummy macros defined locally. Remove them as they aren't required anymore. Signed-off-by: Viresh Kumar Cc: Greg Kroah-Hartman Cc: Russell King Cc: Mike Turquette Cc: Sergei Shtylyov Cc: viresh kumar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/platform_data/mv_usb.h | 9 --------- 1 file changed, 9 deletions(-) (limited to 'include') diff --git a/include/linux/platform_data/mv_usb.h b/include/linux/platform_data/mv_usb.h index d94804aca764..944b01dd103e 100644 --- a/include/linux/platform_data/mv_usb.h +++ b/include/linux/platform_data/mv_usb.h @@ -52,13 +52,4 @@ struct mv_usb_platform_data { int (*set_vbus)(unsigned int vbus); int (*private_init)(void __iomem *opregs, void __iomem *phyregs); }; - -#ifndef CONFIG_HAVE_CLK -/* Dummy stub for clk framework */ -#define clk_get(dev, id) NULL -#define clk_put(clock) do {} while (0) -#define clk_enable(clock) do {} while (0) -#define clk_disable(clock) do {} while (0) -#endif - #endif -- cgit v1.2.3 From 45226e944ce071d0231949f2fea90969437cd2dc Mon Sep 17 00:00:00 2001 From: Sameer Nanda Date: Mon, 30 Jul 2012 14:40:00 -0700 Subject: NMI watchdog: fix for lockup detector breakage on resume On the suspend/resume path the boot CPU does not go though an offline->online transition. This breaks the NMI detector post-resume since it depends on PMU state that is lost when the system gets suspended. Fix this by forcing a CPU offline->online transition for the lockup detector on the boot CPU during resume. To provide more context, we enable NMI watchdog on Chrome OS. We have seen several reports of systems freezing up completely which indicated that the NMI watchdog was not firing for some reason. Debugging further, we found a simple way of repro'ing system freezes -- issuing the command 'tasket 1 sh -c "echo nmilockup > /proc/breakme"' after the system has been suspended/resumed one or more times. With this patch in place, the system freeze result in panics, as expected. These panics provide a nice stack trace for us to debug the actual issue causing the freeze. [akpm@linux-foundation.org: fiddle with code comment] [akpm@linux-foundation.org: make lockup_detector_bootcpu_resume() conditional on CONFIG_SUSPEND] [akpm@linux-foundation.org: fix section errors] Signed-off-by: Sameer Nanda Cc: Ingo Molnar Cc: Peter Zijlstra Cc: "Rafael J. Wysocki" Cc: Don Zickus Cc: Mandeep Singh Baines Cc: Srivatsa S. Bhat Cc: Anshuman Khandual Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sched.h | 8 ++++++++ kernel/power/suspend.c | 3 +++ kernel/watchdog.c | 21 +++++++++++++++++++-- 3 files changed, 30 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 1e26a5e45aa6..68dcffaa62a0 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -334,6 +334,14 @@ static inline void lockup_detector_init(void) } #endif +#if defined(CONFIG_LOCKUP_DETECTOR) && defined(CONFIG_SUSPEND) +void lockup_detector_bootcpu_resume(void); +#else +static inline void lockup_detector_bootcpu_resume(void) +{ +} +#endif + #ifdef CONFIG_DETECT_HUNG_TASK extern unsigned int sysctl_hung_task_panic; extern unsigned long sysctl_hung_task_check_count; diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index c8b7446b27df..1da39ea248fd 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -178,6 +178,9 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) arch_suspend_enable_irqs(); BUG_ON(irqs_disabled()); + /* Kick the lockup detector */ + lockup_detector_bootcpu_resume(); + Enable_cpus: enable_nonboot_cpus(); diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 4b1dfba70f7c..69add8a9da68 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -575,7 +575,7 @@ out: /* * Create/destroy watchdog threads as CPUs come and go: */ -static int __cpuinit +static int cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { int hotcpu = (unsigned long)hcpu; @@ -610,10 +610,27 @@ cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) return NOTIFY_OK; } -static struct notifier_block __cpuinitdata cpu_nfb = { +static struct notifier_block cpu_nfb = { .notifier_call = cpu_callback }; +#ifdef CONFIG_SUSPEND +/* + * On exit from suspend we force an offline->online transition on the boot CPU + * so that the PMU state that was lost while in suspended state gets set up + * properly for the boot CPU. This information is required for restarting the + * NMI watchdog. + */ +void lockup_detector_bootcpu_resume(void) +{ + void *cpu = (void *)(long)smp_processor_id(); + + cpu_callback(&cpu_nfb, CPU_DEAD_FROZEN, cpu); + cpu_callback(&cpu_nfb, CPU_UP_PREPARE_FROZEN, cpu); + cpu_callback(&cpu_nfb, CPU_ONLINE_FROZEN, cpu); +} +#endif + void __init lockup_detector_init(void) { void *cpu = (void *)(long)smp_processor_id(); -- cgit v1.2.3 From acc8fa41ad31c576cdbc569cc3e0e443b1b98b44 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 30 Jul 2012 14:40:09 -0700 Subject: printk: add generic functions to find KERN_ headers The current form of a KERN_ is "<.>". Add printk_get_level and printk_skip_level functions to handle these formats. These functions centralize tests of KERN_ so a future modification can change the KERN_ style and shorten the number of bytes consumed by these headers. [akpm@linux-foundation.org: fix build error and warning] Signed-off-by: Joe Perches Cc: Kay Sievers Cc: Wu Fengguang Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/printk.h | 26 ++++++++++++++++++++++++++ kernel/printk.c | 14 +++++++++----- 2 files changed, 35 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/printk.h b/include/linux/printk.h index 1bec2f7a2d42..6e12e1f09047 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -24,6 +24,32 @@ extern const char linux_proc_banner[]; */ #define KERN_CONT "" +static inline int printk_get_level(const char *buffer) +{ + if (buffer[0] == '<' && buffer[1] && buffer[2] == '>') { + switch (buffer[1]) { + case '0' ... '7': + case 'd': /* KERN_DEFAULT */ + case 'c': /* KERN_CONT */ + return buffer[1]; + } + } + return 0; +} + +static inline const char *printk_skip_level(const char *buffer) +{ + if (printk_get_level(buffer)) { + switch (buffer[1]) { + case '0' ... '7': + case 'd': /* KERN_DEFAULT */ + case 'c': /* KERN_CONT */ + return buffer + 3; + } + } + return buffer; +} + extern int console_printk[]; #define console_loglevel (console_printk[0]) diff --git a/kernel/printk.c b/kernel/printk.c index 852269adad25..0d882a2f231e 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -1487,6 +1487,7 @@ asmlinkage int vprintk_emit(int facility, int level, size_t text_len; enum log_flags lflags = 0; unsigned long flags; + int kern_level; int this_cpu; int printed_len = 0; @@ -1543,17 +1544,20 @@ asmlinkage int vprintk_emit(int facility, int level, } /* strip syslog prefix and extract log level or control flags */ - if (text[0] == '<' && text[1] && text[2] == '>') { - switch (text[1]) { + kern_level = printk_get_level(text); + if (kern_level) { + const char *end_of_header = printk_skip_level(text); + switch (kern_level) { case '0' ... '7': if (level == -1) - level = text[1] - '0'; + level = kern_level - '0'; case 'd': /* KERN_DEFAULT */ lflags |= LOG_PREFIX; case 'c': /* KERN_CONT */ - text += 3; - text_len -= 3; + break; } + text_len -= end_of_header - text; + text = (char *)end_of_header; } if (level == -1) -- cgit v1.2.3 From 314ba3520e513a78be80e8c2ddbd65c91e78a114 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 30 Jul 2012 14:40:11 -0700 Subject: printk: add kern_levels.h to make KERN_ available for asm use Separate the printk.h file into 2 pieces so the definitions can be used in asm files. Signed-off-by: Joe Perches Cc: Kay Sievers Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kern_levels.h | 22 ++++++++++++++++++++++ include/linux/printk.h | 19 +------------------ 2 files changed, 23 insertions(+), 18 deletions(-) create mode 100644 include/linux/kern_levels.h (limited to 'include') diff --git a/include/linux/kern_levels.h b/include/linux/kern_levels.h new file mode 100644 index 000000000000..337e56ff5332 --- /dev/null +++ b/include/linux/kern_levels.h @@ -0,0 +1,22 @@ +#ifndef __KERN_LEVELS_H__ +#define __KERN_LEVELS_H__ + +#define KERN_EMERG "<0>" /* system is unusable */ +#define KERN_ALERT "<1>" /* action must be taken immediately */ +#define KERN_CRIT "<2>" /* critical conditions */ +#define KERN_ERR "<3>" /* error conditions */ +#define KERN_WARNING "<4>" /* warning conditions */ +#define KERN_NOTICE "<5>" /* normal but significant condition */ +#define KERN_INFO "<6>" /* informational */ +#define KERN_DEBUG "<7>" /* debug-level messages */ + +/* Use the default kernel loglevel */ +#define KERN_DEFAULT "" +/* + * Annotation for a "continued" line of log printout (only done after a + * line that had no enclosing \n). Only to be used by core/arch code + * during early bootup (a continued line is not SMP-safe otherwise). + */ +#define KERN_CONT "" + +#endif diff --git a/include/linux/printk.h b/include/linux/printk.h index 6e12e1f09047..fea2de37b645 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -2,28 +2,11 @@ #define __KERNEL_PRINTK__ #include +#include extern const char linux_banner[]; extern const char linux_proc_banner[]; -#define KERN_EMERG "<0>" /* system is unusable */ -#define KERN_ALERT "<1>" /* action must be taken immediately */ -#define KERN_CRIT "<2>" /* critical conditions */ -#define KERN_ERR "<3>" /* error conditions */ -#define KERN_WARNING "<4>" /* warning conditions */ -#define KERN_NOTICE "<5>" /* normal but significant condition */ -#define KERN_INFO "<6>" /* informational */ -#define KERN_DEBUG "<7>" /* debug-level messages */ - -/* Use the default kernel loglevel */ -#define KERN_DEFAULT "" -/* - * Annotation for a "continued" line of log printout (only done after a - * line that had no enclosing \n). Only to be used by core/arch code - * during early bootup (a continued line is not SMP-safe otherwise). - */ -#define KERN_CONT "" - static inline int printk_get_level(const char *buffer) { if (buffer[0] == '<' && buffer[1] && buffer[2] == '>') { -- cgit v1.2.3 From 04d2c8c83d0e3ac5f78aeede51babb3236200112 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 30 Jul 2012 14:40:17 -0700 Subject: printk: convert the format for KERN_ to a 2 byte pattern Instead of "<.>", use an ASCII SOH for the KERN_ prefix initiator. This saves 1 byte per printk, thousands of bytes in a normal kernel. No output changes are produced as vprintk_emit converts these uses to "<.>". Signed-off-by: Joe Perches Cc: Kay Sievers Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kern_levels.h | 25 ++++++++++++++----------- include/linux/printk.h | 4 ++-- 2 files changed, 16 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/linux/kern_levels.h b/include/linux/kern_levels.h index 337e56ff5332..8c719a955b5b 100644 --- a/include/linux/kern_levels.h +++ b/include/linux/kern_levels.h @@ -1,22 +1,25 @@ #ifndef __KERN_LEVELS_H__ #define __KERN_LEVELS_H__ -#define KERN_EMERG "<0>" /* system is unusable */ -#define KERN_ALERT "<1>" /* action must be taken immediately */ -#define KERN_CRIT "<2>" /* critical conditions */ -#define KERN_ERR "<3>" /* error conditions */ -#define KERN_WARNING "<4>" /* warning conditions */ -#define KERN_NOTICE "<5>" /* normal but significant condition */ -#define KERN_INFO "<6>" /* informational */ -#define KERN_DEBUG "<7>" /* debug-level messages */ +#define KERN_SOH "\001" /* ASCII Start Of Header */ +#define KERN_SOH_ASCII '\001' + +#define KERN_EMERG KERN_SOH "0" /* system is unusable */ +#define KERN_ALERT KERN_SOH "1" /* action must be taken immediately */ +#define KERN_CRIT KERN_SOH "2" /* critical conditions */ +#define KERN_ERR KERN_SOH "3" /* error conditions */ +#define KERN_WARNING KERN_SOH "4" /* warning conditions */ +#define KERN_NOTICE KERN_SOH "5" /* normal but significant condition */ +#define KERN_INFO KERN_SOH "6" /* informational */ +#define KERN_DEBUG KERN_SOH "7" /* debug-level messages */ + +#define KERN_DEFAULT KERN_SOH "d" /* the default kernel loglevel */ -/* Use the default kernel loglevel */ -#define KERN_DEFAULT "" /* * Annotation for a "continued" line of log printout (only done after a * line that had no enclosing \n). Only to be used by core/arch code * during early bootup (a continued line is not SMP-safe otherwise). */ -#define KERN_CONT "" +#define KERN_CONT KERN_SOH "c" #endif diff --git a/include/linux/printk.h b/include/linux/printk.h index fea2de37b645..93a231f9835c 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -9,7 +9,7 @@ extern const char linux_proc_banner[]; static inline int printk_get_level(const char *buffer) { - if (buffer[0] == '<' && buffer[1] && buffer[2] == '>') { + if (buffer[0] == KERN_SOH_ASCII && buffer[1]) { switch (buffer[1]) { case '0' ... '7': case 'd': /* KERN_DEFAULT */ @@ -27,7 +27,7 @@ static inline const char *printk_skip_level(const char *buffer) case '0' ... '7': case 'd': /* KERN_DEFAULT */ case 'c': /* KERN_CONT */ - return buffer + 3; + return buffer + 2; } } return buffer; -- cgit v1.2.3 From 61e99ab8e35a88b8c4d0f80d3df9ee16df471be5 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 30 Jul 2012 14:40:21 -0700 Subject: printk: remove the now unnecessary "C" annotation for KERN_CONT Now that all KERN_ uses are prefixed with ASCII SOH, there is no need for a KERN_CONT. Keep it backward compatible by adding #define KERN_CONT "" Reduces kernel image size a thousand bytes. Signed-off-by: Joe Perches Cc: Kay Sievers Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kern_levels.h | 2 +- include/linux/printk.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/kern_levels.h b/include/linux/kern_levels.h index 8c719a955b5b..866caaa9e2bb 100644 --- a/include/linux/kern_levels.h +++ b/include/linux/kern_levels.h @@ -20,6 +20,6 @@ * line that had no enclosing \n). Only to be used by core/arch code * during early bootup (a continued line is not SMP-safe otherwise). */ -#define KERN_CONT KERN_SOH "c" +#define KERN_CONT "" #endif diff --git a/include/linux/printk.h b/include/linux/printk.h index 93a231f9835c..9afc01e5a0a6 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -13,7 +13,6 @@ static inline int printk_get_level(const char *buffer) switch (buffer[1]) { case '0' ... '7': case 'd': /* KERN_DEFAULT */ - case 'c': /* KERN_CONT */ return buffer[1]; } } @@ -26,7 +25,6 @@ static inline const char *printk_skip_level(const char *buffer) switch (buffer[1]) { case '0' ... '7': case 'd': /* KERN_DEFAULT */ - case 'c': /* KERN_CONT */ return buffer + 2; } } -- cgit v1.2.3 From a1fcb2e31822c0617c6e274de4af2a2bb5dc7d3f Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Mon, 30 Jul 2012 14:40:50 -0700 Subject: backlight: move register definitions from header to source ROM boundary definitions do not need to be exported because these are used only internally in the lp855x driver. And few code cosmetic changes Signed-off-by: Milo(Woogyom) Kim Cc: Richard Purdie Cc: Bryan Wu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/backlight/lp855x_bl.c | 8 ++++++-- include/linux/lp855x.h | 6 ------ 2 files changed, 6 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/video/backlight/lp855x_bl.c b/drivers/video/backlight/lp855x_bl.c index 72a0e0c917cf..3d24314bc4e1 100644 --- a/drivers/video/backlight/lp855x_bl.c +++ b/drivers/video/backlight/lp855x_bl.c @@ -17,8 +17,12 @@ #include /* Registers */ -#define BRIGHTNESS_CTRL (0x00) -#define DEVICE_CTRL (0x01) +#define BRIGHTNESS_CTRL 0x00 +#define DEVICE_CTRL 0x01 +#define EEPROM_START 0xA0 +#define EEPROM_END 0xA7 +#define EPROM_START 0xA0 +#define EPROM_END 0xAF #define BUF_SIZE 20 #define DEFAULT_BL_NAME "lcd-backlight" diff --git a/include/linux/lp855x.h b/include/linux/lp855x.h index 781a490a451b..cc76f1f18f18 100644 --- a/include/linux/lp855x.h +++ b/include/linux/lp855x.h @@ -47,12 +47,6 @@ (LP8556_I2C_ONLY << BRT_MODE_SHFT)) #define LP8556_COMB2_CONFIG (LP8556_COMBINED2 << BRT_MODE_SHFT) -/* ROM area boundary */ -#define EEPROM_START (0xA0) -#define EEPROM_END (0xA7) -#define EPROM_START (0xA0) -#define EPROM_END (0xAF) - enum lp855x_chip_id { LP8550, LP8551, -- cgit v1.2.3 From f7f95056779eb69c5fc3ac30e5cb6fd28bdbba43 Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Mon, 30 Jul 2012 14:40:53 -0700 Subject: backlight: move lp855x header into platform_data directory The lp855x header is used only in the platform side, so it can be moved into platform_data directory Signed-off-by: Milo(Woogyom) Kim Cc: Richard Purdie Cc: Bryan Wu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/backlight/lp855x_bl.c | 2 +- include/linux/lp855x.h | 125 ----------------------------------- include/linux/platform_data/lp855x.h | 125 +++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 126 deletions(-) delete mode 100644 include/linux/lp855x.h create mode 100644 include/linux/platform_data/lp855x.h (limited to 'include') diff --git a/drivers/video/backlight/lp855x_bl.c b/drivers/video/backlight/lp855x_bl.c index 3d24314bc4e1..aa6d4f71131f 100644 --- a/drivers/video/backlight/lp855x_bl.c +++ b/drivers/video/backlight/lp855x_bl.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include /* Registers */ #define BRIGHTNESS_CTRL 0x00 diff --git a/include/linux/lp855x.h b/include/linux/lp855x.h deleted file mode 100644 index cc76f1f18f18..000000000000 --- a/include/linux/lp855x.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * LP855x Backlight Driver - * - * Copyright (C) 2011 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. - * - */ - -#ifndef _LP855X_H -#define _LP855X_H - -#define BL_CTL_SHFT (0) -#define BRT_MODE_SHFT (1) -#define BRT_MODE_MASK (0x06) - -/* Enable backlight. Only valid when BRT_MODE=10(I2C only) */ -#define ENABLE_BL (1) -#define DISABLE_BL (0) - -#define I2C_CONFIG(id) id ## _I2C_CONFIG -#define PWM_CONFIG(id) id ## _PWM_CONFIG - -/* DEVICE CONTROL register - LP8550 */ -#define LP8550_PWM_CONFIG (LP8550_PWM_ONLY << BRT_MODE_SHFT) -#define LP8550_I2C_CONFIG ((ENABLE_BL << BL_CTL_SHFT) | \ - (LP8550_I2C_ONLY << BRT_MODE_SHFT)) - -/* DEVICE CONTROL register - LP8551 */ -#define LP8551_PWM_CONFIG LP8550_PWM_CONFIG -#define LP8551_I2C_CONFIG LP8550_I2C_CONFIG - -/* DEVICE CONTROL register - LP8552 */ -#define LP8552_PWM_CONFIG LP8550_PWM_CONFIG -#define LP8552_I2C_CONFIG LP8550_I2C_CONFIG - -/* DEVICE CONTROL register - LP8553 */ -#define LP8553_PWM_CONFIG LP8550_PWM_CONFIG -#define LP8553_I2C_CONFIG LP8550_I2C_CONFIG - -/* DEVICE CONTROL register - LP8556 */ -#define LP8556_PWM_CONFIG (LP8556_PWM_ONLY << BRT_MODE_SHFT) -#define LP8556_COMB1_CONFIG (LP8556_COMBINED1 << BRT_MODE_SHFT) -#define LP8556_I2C_CONFIG ((ENABLE_BL << BL_CTL_SHFT) | \ - (LP8556_I2C_ONLY << BRT_MODE_SHFT)) -#define LP8556_COMB2_CONFIG (LP8556_COMBINED2 << BRT_MODE_SHFT) - -enum lp855x_chip_id { - LP8550, - LP8551, - LP8552, - LP8553, - LP8556, -}; - -enum lp855x_brightness_ctrl_mode { - PWM_BASED = 1, - REGISTER_BASED, -}; - -enum lp8550_brighntess_source { - LP8550_PWM_ONLY, - LP8550_I2C_ONLY = 2, -}; - -enum lp8551_brighntess_source { - LP8551_PWM_ONLY = LP8550_PWM_ONLY, - LP8551_I2C_ONLY = LP8550_I2C_ONLY, -}; - -enum lp8552_brighntess_source { - LP8552_PWM_ONLY = LP8550_PWM_ONLY, - LP8552_I2C_ONLY = LP8550_I2C_ONLY, -}; - -enum lp8553_brighntess_source { - LP8553_PWM_ONLY = LP8550_PWM_ONLY, - LP8553_I2C_ONLY = LP8550_I2C_ONLY, -}; - -enum lp8556_brightness_source { - LP8556_PWM_ONLY, - LP8556_COMBINED1, /* pwm + i2c before the shaper block */ - LP8556_I2C_ONLY, - LP8556_COMBINED2, /* pwm + i2c after the shaper block */ -}; - -struct lp855x_pwm_data { - void (*pwm_set_intensity) (int brightness, int max_brightness); - int (*pwm_get_intensity) (int max_brightness); -}; - -struct lp855x_rom_data { - u8 addr; - u8 val; -}; - -/** - * struct lp855x_platform_data - * @name : Backlight driver name. If it is not defined, default name is set. - * @mode : brightness control by pwm or lp855x register - * @device_control : value of DEVICE CONTROL register - * @initial_brightness : initial value of backlight brightness - * @pwm_data : platform specific pwm generation functions. - Only valid when mode is PWM_BASED. - * @load_new_rom_data : - 0 : use default configuration data - 1 : update values of eeprom or eprom registers on loading driver - * @size_program : total size of lp855x_rom_data - * @rom_data : list of new eeprom/eprom registers - */ -struct lp855x_platform_data { - char *name; - enum lp855x_brightness_ctrl_mode mode; - u8 device_control; - int initial_brightness; - struct lp855x_pwm_data pwm_data; - u8 load_new_rom_data; - int size_program; - struct lp855x_rom_data *rom_data; -}; - -#endif diff --git a/include/linux/platform_data/lp855x.h b/include/linux/platform_data/lp855x.h new file mode 100644 index 000000000000..cc76f1f18f18 --- /dev/null +++ b/include/linux/platform_data/lp855x.h @@ -0,0 +1,125 @@ +/* + * LP855x Backlight Driver + * + * Copyright (C) 2011 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. + * + */ + +#ifndef _LP855X_H +#define _LP855X_H + +#define BL_CTL_SHFT (0) +#define BRT_MODE_SHFT (1) +#define BRT_MODE_MASK (0x06) + +/* Enable backlight. Only valid when BRT_MODE=10(I2C only) */ +#define ENABLE_BL (1) +#define DISABLE_BL (0) + +#define I2C_CONFIG(id) id ## _I2C_CONFIG +#define PWM_CONFIG(id) id ## _PWM_CONFIG + +/* DEVICE CONTROL register - LP8550 */ +#define LP8550_PWM_CONFIG (LP8550_PWM_ONLY << BRT_MODE_SHFT) +#define LP8550_I2C_CONFIG ((ENABLE_BL << BL_CTL_SHFT) | \ + (LP8550_I2C_ONLY << BRT_MODE_SHFT)) + +/* DEVICE CONTROL register - LP8551 */ +#define LP8551_PWM_CONFIG LP8550_PWM_CONFIG +#define LP8551_I2C_CONFIG LP8550_I2C_CONFIG + +/* DEVICE CONTROL register - LP8552 */ +#define LP8552_PWM_CONFIG LP8550_PWM_CONFIG +#define LP8552_I2C_CONFIG LP8550_I2C_CONFIG + +/* DEVICE CONTROL register - LP8553 */ +#define LP8553_PWM_CONFIG LP8550_PWM_CONFIG +#define LP8553_I2C_CONFIG LP8550_I2C_CONFIG + +/* DEVICE CONTROL register - LP8556 */ +#define LP8556_PWM_CONFIG (LP8556_PWM_ONLY << BRT_MODE_SHFT) +#define LP8556_COMB1_CONFIG (LP8556_COMBINED1 << BRT_MODE_SHFT) +#define LP8556_I2C_CONFIG ((ENABLE_BL << BL_CTL_SHFT) | \ + (LP8556_I2C_ONLY << BRT_MODE_SHFT)) +#define LP8556_COMB2_CONFIG (LP8556_COMBINED2 << BRT_MODE_SHFT) + +enum lp855x_chip_id { + LP8550, + LP8551, + LP8552, + LP8553, + LP8556, +}; + +enum lp855x_brightness_ctrl_mode { + PWM_BASED = 1, + REGISTER_BASED, +}; + +enum lp8550_brighntess_source { + LP8550_PWM_ONLY, + LP8550_I2C_ONLY = 2, +}; + +enum lp8551_brighntess_source { + LP8551_PWM_ONLY = LP8550_PWM_ONLY, + LP8551_I2C_ONLY = LP8550_I2C_ONLY, +}; + +enum lp8552_brighntess_source { + LP8552_PWM_ONLY = LP8550_PWM_ONLY, + LP8552_I2C_ONLY = LP8550_I2C_ONLY, +}; + +enum lp8553_brighntess_source { + LP8553_PWM_ONLY = LP8550_PWM_ONLY, + LP8553_I2C_ONLY = LP8550_I2C_ONLY, +}; + +enum lp8556_brightness_source { + LP8556_PWM_ONLY, + LP8556_COMBINED1, /* pwm + i2c before the shaper block */ + LP8556_I2C_ONLY, + LP8556_COMBINED2, /* pwm + i2c after the shaper block */ +}; + +struct lp855x_pwm_data { + void (*pwm_set_intensity) (int brightness, int max_brightness); + int (*pwm_get_intensity) (int max_brightness); +}; + +struct lp855x_rom_data { + u8 addr; + u8 val; +}; + +/** + * struct lp855x_platform_data + * @name : Backlight driver name. If it is not defined, default name is set. + * @mode : brightness control by pwm or lp855x register + * @device_control : value of DEVICE CONTROL register + * @initial_brightness : initial value of backlight brightness + * @pwm_data : platform specific pwm generation functions. + Only valid when mode is PWM_BASED. + * @load_new_rom_data : + 0 : use default configuration data + 1 : update values of eeprom or eprom registers on loading driver + * @size_program : total size of lp855x_rom_data + * @rom_data : list of new eeprom/eprom registers + */ +struct lp855x_platform_data { + char *name; + enum lp855x_brightness_ctrl_mode mode; + u8 device_control; + int initial_brightness; + struct lp855x_pwm_data pwm_data; + u8 load_new_rom_data; + int size_program; + struct lp855x_rom_data *rom_data; +}; + +#endif -- cgit v1.2.3 From 639b9e34f15e4b2c30068a4e4485586af0cdf709 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Mon, 30 Jul 2012 14:40:55 -0700 Subject: string: introduce memweight() memweight() is the function that counts the total number of bits set in memory area. Unlike bitmap_weight(), memweight() takes pointer and size in bytes to specify a memory area which does not need to be aligned to long-word boundary. [akpm@linux-foundation.org: rename `w' to `ret'] Signed-off-by: Akinobu Mita Cc: Anders Larsen Cc: Alasdair Kergon Cc: Laurent Pinchart Cc: Mark Fasheh Cc: Joel Becker Cc: Jan Kara Cc: Andreas Dilger Cc: "Theodore Ts'o" Cc: Matthew Wilcox Cc: Mauro Carvalho Chehab Cc: Tony Luck Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/string.h | 3 +++ lib/Makefile | 2 +- lib/memweight.c | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 lib/memweight.c (limited to 'include') diff --git a/include/linux/string.h b/include/linux/string.h index e033564f10ba..ffe0442e18d2 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -145,4 +145,7 @@ static inline bool strstarts(const char *str, const char *prefix) return strncmp(str, prefix, strlen(prefix)) == 0; } #endif + +extern size_t memweight(const void *ptr, size_t bytes); + #endif /* _LINUX_STRING_H_ */ diff --git a/lib/Makefile b/lib/Makefile index 8c31a0cb75e9..df663cc05c73 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -22,7 +22,7 @@ lib-y += kobject.o klist.o obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \ bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \ string_helpers.o gcd.o lcm.o list_sort.o uuid.o flex_array.o \ - bsearch.o find_last_bit.o find_next_bit.o llist.o + bsearch.o find_last_bit.o find_next_bit.o llist.o memweight.o obj-y += kstrtox.o obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o diff --git a/lib/memweight.c b/lib/memweight.c new file mode 100644 index 000000000000..e35fc8771893 --- /dev/null +++ b/lib/memweight.c @@ -0,0 +1,38 @@ +#include +#include +#include + +/** + * memweight - count the total number of bits set in memory area + * @ptr: pointer to the start of the area + * @bytes: the size of the area + */ +size_t memweight(const void *ptr, size_t bytes) +{ + size_t ret = 0; + size_t longs; + const unsigned char *bitmap = ptr; + + for (; bytes > 0 && ((unsigned long)bitmap) % sizeof(long); + bytes--, bitmap++) + ret += hweight8(*bitmap); + + longs = bytes / sizeof(long); + if (longs) { + BUG_ON(longs >= INT_MAX / BITS_PER_LONG); + ret += bitmap_weight((unsigned long *)bitmap, + longs * BITS_PER_LONG); + bytes -= longs * sizeof(long); + bitmap += longs * sizeof(long); + } + /* + * The reason that this last loop is distinct from the preceding + * bitmap_weight() call is to compute 1-bits in the last region smaller + * than sizeof(long) properly on big-endian systems. + */ + for (; bytes > 0; bytes--, bitmap++) + ret += hweight8(*bitmap); + + return ret; +} +EXPORT_SYMBOL(memweight); -- cgit v1.2.3 From 8c74ac0557b4cabc9017c2740c5f62b330192416 Mon Sep 17 00:00:00 2001 From: Vyacheslav Dubeyko Date: Mon, 30 Jul 2012 14:42:09 -0700 Subject: nilfs2: add omitted comments for structures in nilfs2_fs.h Add omitted comments for structures in nilfs2_fs.h. Signed-off-by: Vyacheslav Dubeyko Signed-off-by: Ryusuke Konishi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/nilfs2_fs.h | 63 +++++++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/linux/nilfs2_fs.h b/include/linux/nilfs2_fs.h index 89bd4a4dcfb4..98755767c7b0 100644 --- a/include/linux/nilfs2_fs.h +++ b/include/linux/nilfs2_fs.h @@ -293,7 +293,7 @@ struct nilfs_dir_entry { __le64 inode; /* Inode number */ __le16 rec_len; /* Directory entry length */ __u8 name_len; /* Name length */ - __u8 file_type; + __u8 file_type; /* Dir entry type (file, dir, etc) */ char name[NILFS_NAME_LEN]; /* File name */ char pad; }; @@ -395,7 +395,7 @@ union nilfs_binfo { }; /** - * struct nilfs_segment_summary - segment summary + * struct nilfs_segment_summary - segment summary header * @ss_datasum: checksum of data * @ss_sumsum: checksum of segment summary * @ss_magic: magic number @@ -683,9 +683,9 @@ struct nilfs_sufile_header { /** * nilfs_suinfo - segment usage information - * @sui_lastmod: - * @sui_nblocks: - * @sui_flags: + * @sui_lastmod: timestamp of last modification + * @sui_nblocks: number of written blocks in segment + * @sui_flags: segment usage flags */ struct nilfs_suinfo { __u64 sui_lastmod; @@ -716,9 +716,10 @@ enum { }; /** - * struct nilfs_cpmode - - * @cc_cno: - * @cc_mode: + * struct nilfs_cpmode - change checkpoint mode structure + * @cm_cno: checkpoint number + * @cm_mode: mode of checkpoint + * @cm_pad: padding */ struct nilfs_cpmode { __u64 cm_cno; @@ -728,11 +729,11 @@ struct nilfs_cpmode { /** * struct nilfs_argv - argument vector - * @v_base: - * @v_nmembs: - * @v_size: - * @v_flags: - * @v_index: + * @v_base: pointer on data array from userspace + * @v_nmembs: number of members in data array + * @v_size: size of data array in bytes + * @v_flags: flags + * @v_index: start number of target data items */ struct nilfs_argv { __u64 v_base; @@ -743,9 +744,9 @@ struct nilfs_argv { }; /** - * struct nilfs_period - - * @p_start: - * @p_end: + * struct nilfs_period - period of checkpoint numbers + * @p_start: start checkpoint number (inclusive) + * @p_end: end checkpoint number (exclusive) */ struct nilfs_period { __u64 p_start; @@ -753,7 +754,7 @@ struct nilfs_period { }; /** - * struct nilfs_cpstat - + * struct nilfs_cpstat - checkpoint statistics * @cs_cno: checkpoint number * @cs_ncps: number of checkpoints * @cs_nsss: number of snapshots @@ -765,7 +766,7 @@ struct nilfs_cpstat { }; /** - * struct nilfs_sustat - + * struct nilfs_sustat - segment usage statistics * @ss_nsegs: number of segments * @ss_ncleansegs: number of clean segments * @ss_ndirtysegs: number of dirty segments @@ -784,10 +785,10 @@ struct nilfs_sustat { /** * struct nilfs_vinfo - virtual block number information - * @vi_vblocknr: - * @vi_start: - * @vi_end: - * @vi_blocknr: + * @vi_vblocknr: virtual block number + * @vi_start: start checkpoint number (inclusive) + * @vi_end: end checkpoint number (exclusive) + * @vi_blocknr: disk block number */ struct nilfs_vinfo { __u64 vi_vblocknr; @@ -797,7 +798,15 @@ struct nilfs_vinfo { }; /** - * struct nilfs_vdesc - + * struct nilfs_vdesc - descriptor of virtual block number + * @vd_ino: inode number + * @vd_cno: checkpoint number + * @vd_vblocknr: virtual block number + * @vd_period: period of checkpoint numbers + * @vd_blocknr: disk block number + * @vd_offset: logical block offset inside a file + * @vd_flags: flags (data or node block) + * @vd_pad: padding */ struct nilfs_vdesc { __u64 vd_ino; @@ -811,7 +820,13 @@ struct nilfs_vdesc { }; /** - * struct nilfs_bdesc - + * struct nilfs_bdesc - descriptor of disk block number + * @bd_ino: inode number + * @bd_oblocknr: disk block address (for skipping dead blocks) + * @bd_blocknr: disk block address + * @bd_offset: logical block offset inside a file + * @bd_level: level in the b-tree organization + * @bd_pad: padding */ struct nilfs_bdesc { __u64 bd_ino; -- cgit v1.2.3 From 079a96ae3871f0ed9083aac2218136ccec5b9877 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 30 Jul 2012 14:42:38 -0700 Subject: ipc: add COMPAT_SHMLBA support If the SHMLBA definition for a native task differs from the definition for a compat task, the do_shmat() function would need to handle both. This patch introduces COMPAT_SHMLBA, which is used by the compat shmat syscall when calling the ipc code and allows architectures such as AArch64 (where the native SHMLBA is 64k but the compat (AArch32) definition is 16k) to provide the correct semantics for compat IPC system calls. Cc: David S. Miller Cc: Chris Zankel Cc: Arnd Bergmann Acked-by: Catalin Marinas Signed-off-by: Will Deacon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/sparc/kernel/sys_sparc_64.c | 2 +- arch/xtensa/kernel/syscall.c | 2 +- include/linux/shm.h | 6 ++++-- ipc/compat.c | 8 ++++++-- ipc/shm.c | 9 +++++---- ipc/syscall.c | 2 +- 6 files changed, 18 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/arch/sparc/kernel/sys_sparc_64.c b/arch/sparc/kernel/sys_sparc_64.c index c38e5aaae56f..0dc1f5786081 100644 --- a/arch/sparc/kernel/sys_sparc_64.c +++ b/arch/sparc/kernel/sys_sparc_64.c @@ -470,7 +470,7 @@ SYSCALL_DEFINE6(sparc_ipc, unsigned int, call, int, first, unsigned long, second switch (call) { case SHMAT: { ulong raddr; - err = do_shmat(first, ptr, (int)second, &raddr); + err = do_shmat(first, ptr, (int)second, &raddr, SHMLBA); if (!err) { if (put_user(raddr, (ulong __user *) third)) diff --git a/arch/xtensa/kernel/syscall.c b/arch/xtensa/kernel/syscall.c index 816e6d0d686c..05b3f093d5d7 100644 --- a/arch/xtensa/kernel/syscall.c +++ b/arch/xtensa/kernel/syscall.c @@ -44,7 +44,7 @@ asmlinkage long xtensa_shmat(int shmid, char __user *shmaddr, int shmflg) unsigned long ret; long err; - err = do_shmat(shmid, shmaddr, shmflg, &ret); + err = do_shmat(shmid, shmaddr, shmflg, &ret, SHMLBA); if (err) return err; return (long)ret; diff --git a/include/linux/shm.h b/include/linux/shm.h index 92808b86703b..edd086883ccb 100644 --- a/include/linux/shm.h +++ b/include/linux/shm.h @@ -107,12 +107,14 @@ struct shmid_kernel /* private to the kernel */ #define SHM_NORESERVE 010000 /* don't check for reservations */ #ifdef CONFIG_SYSVIPC -long do_shmat(int shmid, char __user *shmaddr, int shmflg, unsigned long *addr); +long do_shmat(int shmid, char __user *shmaddr, int shmflg, unsigned long *addr, + unsigned long shmlba); extern int is_file_shm_hugepages(struct file *file); extern void exit_shm(struct task_struct *task); #else static inline long do_shmat(int shmid, char __user *shmaddr, - int shmflg, unsigned long *addr) + int shmflg, unsigned long *addr, + unsigned long shmlba) { return -ENOSYS; } diff --git a/ipc/compat.c b/ipc/compat.c index a6df704f521e..53cebdf80e3c 100644 --- a/ipc/compat.c +++ b/ipc/compat.c @@ -514,6 +514,10 @@ long compat_sys_msgctl(int first, int second, void __user *uptr) return err; } +#ifndef COMPAT_SHMLBA +#define COMPAT_SHMLBA SHMLBA +#endif + #ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC long compat_sys_shmat(int first, int second, compat_uptr_t third, int version, void __user *uptr) @@ -524,7 +528,7 @@ long compat_sys_shmat(int first, int second, compat_uptr_t third, int version, if (version == 1) return -EINVAL; - err = do_shmat(first, uptr, second, &raddr); + err = do_shmat(first, uptr, second, &raddr, COMPAT_SHMLBA); if (err < 0) return err; uaddr = compat_ptr(third); @@ -536,7 +540,7 @@ long compat_sys_shmat(int shmid, compat_uptr_t shmaddr, int shmflg) unsigned long ret; long err; - err = do_shmat(shmid, compat_ptr(shmaddr), shmflg, &ret); + err = do_shmat(shmid, compat_ptr(shmaddr), shmflg, &ret, COMPAT_SHMLBA); if (err) return err; force_successful_syscall_return(); diff --git a/ipc/shm.c b/ipc/shm.c index 41c1285d697a..00faa05cf72a 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -953,7 +953,8 @@ out: * "raddr" thing points to kernel space, and there has to be a wrapper around * this. */ -long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr) +long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr, + unsigned long shmlba) { struct shmid_kernel *shp; unsigned long addr; @@ -973,9 +974,9 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr) if (shmid < 0) goto out; else if ((addr = (ulong)shmaddr)) { - if (addr & (SHMLBA-1)) { + if (addr & (shmlba - 1)) { if (shmflg & SHM_RND) - addr &= ~(SHMLBA-1); /* round down */ + addr &= ~(shmlba - 1); /* round down */ else #ifndef __ARCH_FORCE_SHMLBA if (addr & ~PAGE_MASK) @@ -1107,7 +1108,7 @@ SYSCALL_DEFINE3(shmat, int, shmid, char __user *, shmaddr, int, shmflg) unsigned long ret; long err; - err = do_shmat(shmid, shmaddr, shmflg, &ret); + err = do_shmat(shmid, shmaddr, shmflg, &ret, SHMLBA); if (err) return err; force_successful_syscall_return(); diff --git a/ipc/syscall.c b/ipc/syscall.c index 1d6f53f6b562..0d1e32ce048e 100644 --- a/ipc/syscall.c +++ b/ipc/syscall.c @@ -73,7 +73,7 @@ SYSCALL_DEFINE6(ipc, unsigned int, call, int, first, unsigned long, second, default: { unsigned long raddr; ret = do_shmat(first, (char __user *)ptr, - second, &raddr); + second, &raddr, SHMLBA); if (ret) return ret; return put_user(raddr, (unsigned long __user *) third); -- cgit v1.2.3 From b610c04c667f3c056243fd64041c7f152a512ee4 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 30 Jul 2012 14:42:40 -0700 Subject: ipc: allow compat IPC version field parsing if !ARCH_WANT_OLD_COMPAT_IPC Commit 48b25c43e6ee ("ipc: provide generic compat versions of IPC syscalls") added a new ARCH_WANT_OLD_COMPAT_IPC config option for architectures to select if their compat target requires the old IPC syscall interface. For architectures (such as AArch64) that do not require the internal calling conventions provided by this option, but have a compat target where the C library passes the IPC_64 flag explicitly, compat_ipc_parse_version no longer strips out the flag before calling the native system call implementation, resulting in unknown SHM/IPC commands and -EINVAL being returned to userspace. This patch separates the selection of the internal calling conventions for the IPC syscalls from the version parsing, allowing architectures to select __ARCH_WANT_COMPAT_IPC_PARSE_VERSION if they want to use version parsing whilst retaining the newer syscall calling conventions. Acked-by: Chris Metcalf Cc: Arnd Bergmann Acked-by: Catalin Marinas Signed-off-by: Will Deacon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/compat.h | 1 + ipc/compat.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/compat.h b/include/linux/compat.h index 4e890394ef99..9f68e90a14ec 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -256,6 +256,7 @@ compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr, compat_size_t __user *len_ptr); #ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC +#define __ARCH_WANT_COMPAT_IPC_PARSE_VERSION long compat_sys_semctl(int first, int second, int third, void __user *uptr); long compat_sys_msgsnd(int first, int second, int third, void __user *uptr); long compat_sys_msgrcv(int first, int second, int msgtyp, int third, diff --git a/ipc/compat.c b/ipc/compat.c index 53cebdf80e3c..a41600f6ba52 100644 --- a/ipc/compat.c +++ b/ipc/compat.c @@ -118,7 +118,7 @@ extern int sem_ctls[]; static inline int compat_ipc_parse_version(int *cmd) { -#ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC +#ifdef __ARCH_WANT_COMPAT_IPC_PARSE_VERSION int version = *cmd & IPC_64; /* this is tricky: architectures that have support for the old -- cgit v1.2.3 From 05ba3f1aa1b04e921068249dd52a80bc84c2aeb4 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 30 Jul 2012 14:42:43 -0700 Subject: ipc: compat: use signed size_t types for msgsnd and msgrcv The msgsnd and msgrcv system calls use size_t to represent the size of the message being transferred. POSIX states that values of msgsz greater than SSIZE_MAX cause the result to be implementation-defined. On Linux, this equates to returning -EINVAL if (long) msgsz < 0. For compat tasks where !CONFIG_ARCH_WANT_OLD_COMPAT_IPC and compat_size_t is smaller than size_t, negative size values passed from userspace will be interpreted as positive values by do_msg{rcv,snd} and will fail to exit early with -EINVAL. This patch changes the compat prototypes for msg{rcv,snd} so that the message size is represented as a compat_ssize_t, which we cast to the native ssize_t type for the core IPC code. Cc: Arnd Bergmann Acked-by: Chris Metcalf Acked-by: Catalin Marinas Signed-off-by: Will Deacon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/compat.h | 4 ++-- ipc/compat.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/compat.h b/include/linux/compat.h index 9f68e90a14ec..f2b8fe20cc8e 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -266,9 +266,9 @@ long compat_sys_shmat(int first, int second, compat_uptr_t third, int version, #else long compat_sys_semctl(int semid, int semnum, int cmd, int arg); long compat_sys_msgsnd(int msqid, struct compat_msgbuf __user *msgp, - size_t msgsz, int msgflg); + compat_ssize_t msgsz, int msgflg); long compat_sys_msgrcv(int msqid, struct compat_msgbuf __user *msgp, - size_t msgsz, long msgtyp, int msgflg); + compat_ssize_t msgsz, long msgtyp, int msgflg); long compat_sys_shmat(int shmid, compat_uptr_t shmaddr, int shmflg); #endif long compat_sys_msgctl(int first, int second, void __user *uptr); diff --git a/ipc/compat.c b/ipc/compat.c index a41600f6ba52..20f92b2f2932 100644 --- a/ipc/compat.c +++ b/ipc/compat.c @@ -373,21 +373,21 @@ long compat_sys_semctl(int semid, int semnum, int cmd, int arg) } long compat_sys_msgsnd(int msqid, struct compat_msgbuf __user *msgp, - size_t msgsz, int msgflg) + compat_ssize_t msgsz, int msgflg) { compat_long_t mtype; if (get_user(mtype, &msgp->mtype)) return -EFAULT; - return do_msgsnd(msqid, mtype, msgp->mtext, msgsz, msgflg); + return do_msgsnd(msqid, mtype, msgp->mtext, (ssize_t)msgsz, msgflg); } long compat_sys_msgrcv(int msqid, struct compat_msgbuf __user *msgp, - size_t msgsz, long msgtyp, int msgflg) + compat_ssize_t msgsz, long msgtyp, int msgflg) { long err, mtype; - err = do_msgrcv(msqid, &mtype, msgp->mtext, msgsz, msgtyp, msgflg); + err = do_msgrcv(msqid, &mtype, msgp->mtext, (ssize_t)msgsz, msgtyp, msgflg); if (err < 0) goto out; -- cgit v1.2.3 From c1d7e01d7877a397655277a920aeaa3830ed9461 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 30 Jul 2012 14:42:46 -0700 Subject: ipc: use Kconfig options for __ARCH_WANT_[COMPAT_]IPC_PARSE_VERSION Rather than #define the options manually in the architecture code, add Kconfig options for them and select them there instead. This also allows us to select the compat IPC version parsing automatically for platforms using the old compat IPC interface. Reported-by: Andrew Morton Signed-off-by: Will Deacon Cc: Arnd Bergmann Cc: Chris Metcalf Cc: Catalin Marinas Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/Kconfig | 7 +++++++ arch/alpha/Kconfig | 1 + arch/alpha/include/asm/unistd.h | 1 - arch/arm/Kconfig | 1 + arch/arm/include/asm/unistd.h | 1 - arch/avr32/Kconfig | 1 + arch/avr32/include/asm/unistd.h | 1 - arch/blackfin/Kconfig | 1 + arch/blackfin/include/asm/unistd.h | 1 - arch/cris/Kconfig | 1 + arch/cris/include/asm/unistd.h | 1 - arch/frv/Kconfig | 1 + arch/frv/include/asm/unistd.h | 1 - arch/h8300/Kconfig | 1 + arch/h8300/include/asm/unistd.h | 1 - arch/m32r/Kconfig | 1 + arch/m32r/include/asm/unistd.h | 1 - arch/m68k/Kconfig | 1 + arch/m68k/include/asm/unistd.h | 1 - arch/microblaze/Kconfig | 1 + arch/microblaze/include/asm/unistd.h | 1 - arch/mips/Kconfig | 1 + arch/mips/include/asm/unistd.h | 1 - arch/mn10300/Kconfig | 1 + arch/mn10300/include/asm/unistd.h | 1 - arch/powerpc/Kconfig | 1 + arch/powerpc/include/asm/unistd.h | 1 - arch/s390/Kconfig | 1 + arch/s390/include/asm/unistd.h | 1 - arch/sh/Kconfig | 2 ++ arch/sh/include/asm/unistd.h | 1 - arch/sparc/Kconfig | 1 + arch/sparc/include/asm/unistd.h | 1 - arch/x86/Kconfig | 1 + arch/x86/include/asm/unistd.h | 1 - include/linux/compat.h | 1 - ipc/compat.c | 2 +- ipc/util.c | 4 ++-- ipc/util.h | 2 +- 39 files changed, 29 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/arch/Kconfig b/arch/Kconfig index 8c3d957fa8e2..72f2fa189cc5 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -248,7 +248,14 @@ config HAVE_CMPXCHG_LOCAL config HAVE_CMPXCHG_DOUBLE bool +config ARCH_WANT_IPC_PARSE_VERSION + bool + +config ARCH_WANT_COMPAT_IPC_PARSE_VERSION + bool + config ARCH_WANT_OLD_COMPAT_IPC + select ARCH_WANT_COMPAT_IPC_PARSE_VERSION bool config HAVE_ARCH_SECCOMP_FILTER diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index 3de74c9f9610..d5b9b5e645cc 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -14,6 +14,7 @@ config ALPHA select AUTO_IRQ_AFFINITY if SMP select GENERIC_IRQ_SHOW select ARCH_WANT_OPTIONAL_GPIOLIB + select ARCH_WANT_IPC_PARSE_VERSION select ARCH_HAVE_NMI_SAFE_CMPXCHG select GENERIC_SMP_IDLE_THREAD select GENERIC_CMOS_UPDATE diff --git a/arch/alpha/include/asm/unistd.h b/arch/alpha/include/asm/unistd.h index d1f23b722df4..633b23b0664a 100644 --- a/arch/alpha/include/asm/unistd.h +++ b/arch/alpha/include/asm/unistd.h @@ -470,7 +470,6 @@ #define NR_SYSCALLS 504 -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_STAT64 #define __ARCH_WANT_SYS_GETHOSTNAME diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 4172c3cea228..5df11147be84 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -39,6 +39,7 @@ config ARM select GENERIC_IRQ_PROBE select GENERIC_IRQ_SHOW select GENERIC_IRQ_PROBE + select ARCH_WANT_IPC_PARSE_VERSION select HARDIRQS_SW_RESEND select CPU_PM if (SUSPEND || CPU_IDLE) select GENERIC_PCI_IOMAP diff --git a/arch/arm/include/asm/unistd.h b/arch/arm/include/asm/unistd.h index 512cd1473454..0cab47d4a83f 100644 --- a/arch/arm/include/asm/unistd.h +++ b/arch/arm/include/asm/unistd.h @@ -446,7 +446,6 @@ #ifdef __KERNEL__ -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_STAT64 #define __ARCH_WANT_SYS_GETHOSTNAME #define __ARCH_WANT_SYS_PAUSE diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig index 71d38c76726c..5ade51c8a87f 100644 --- a/arch/avr32/Kconfig +++ b/arch/avr32/Kconfig @@ -12,6 +12,7 @@ config AVR32 select HARDIRQS_SW_RESEND select GENERIC_IRQ_SHOW select ARCH_HAVE_CUSTOM_GPIO_H + select ARCH_WANT_IPC_PARSE_VERSION select ARCH_HAVE_NMI_SAFE_CMPXCHG select GENERIC_CLOCKEVENTS help diff --git a/arch/avr32/include/asm/unistd.h b/arch/avr32/include/asm/unistd.h index f714544e5560..1358e366f4be 100644 --- a/arch/avr32/include/asm/unistd.h +++ b/arch/avr32/include/asm/unistd.h @@ -318,7 +318,6 @@ /* SMP stuff */ #define __IGNORE_getcpu -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_STAT64 #define __ARCH_WANT_SYS_ALARM #define __ARCH_WANT_SYS_GETHOSTNAME diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig index 9b765107e15c..fb9fe00e51a6 100644 --- a/arch/blackfin/Kconfig +++ b/arch/blackfin/Kconfig @@ -33,6 +33,7 @@ config BLACKFIN select HAVE_PERF_EVENTS select ARCH_HAVE_CUSTOM_GPIO_H select ARCH_WANT_OPTIONAL_GPIOLIB + select ARCH_WANT_IPC_PARSE_VERSION select HAVE_GENERIC_HARDIRQS select GENERIC_ATOMIC64 select GENERIC_IRQ_PROBE diff --git a/arch/blackfin/include/asm/unistd.h b/arch/blackfin/include/asm/unistd.h index 3287222cba34..5b2a0748d7d3 100644 --- a/arch/blackfin/include/asm/unistd.h +++ b/arch/blackfin/include/asm/unistd.h @@ -434,7 +434,6 @@ #define __IGNORE_getcpu #ifdef __KERNEL__ -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_STAT64 #define __ARCH_WANT_SYS_ALARM #define __ARCH_WANT_SYS_GETHOSTNAME diff --git a/arch/cris/Kconfig b/arch/cris/Kconfig index bb344650a14f..e92215428a37 100644 --- a/arch/cris/Kconfig +++ b/arch/cris/Kconfig @@ -42,6 +42,7 @@ config CRIS select HAVE_IDE select GENERIC_ATOMIC64 select HAVE_GENERIC_HARDIRQS + select ARCH_WANT_IPC_PARSE_VERSION select GENERIC_IRQ_SHOW select GENERIC_IOMAP select GENERIC_SMP_IDLE_THREAD if ETRAX_ARCH_V32 diff --git a/arch/cris/include/asm/unistd.h b/arch/cris/include/asm/unistd.h index f921b8b0f97e..51873a446f87 100644 --- a/arch/cris/include/asm/unistd.h +++ b/arch/cris/include/asm/unistd.h @@ -347,7 +347,6 @@ #include -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_STAT #define __ARCH_WANT_STAT64 diff --git a/arch/frv/Kconfig b/arch/frv/Kconfig index a685910d2d5c..971c0a19facb 100644 --- a/arch/frv/Kconfig +++ b/arch/frv/Kconfig @@ -9,6 +9,7 @@ config FRV select GENERIC_IRQ_SHOW select ARCH_HAVE_NMI_SAFE_CMPXCHG select GENERIC_CPU_DEVICES + select ARCH_WANT_IPC_PARSE_VERSION config ZONE_DMA bool diff --git a/arch/frv/include/asm/unistd.h b/arch/frv/include/asm/unistd.h index a569dff7cd59..67f23a311db6 100644 --- a/arch/frv/include/asm/unistd.h +++ b/arch/frv/include/asm/unistd.h @@ -349,7 +349,6 @@ #define NR_syscalls 338 -#define __ARCH_WANT_IPC_PARSE_VERSION /* #define __ARCH_WANT_OLD_READDIR */ #define __ARCH_WANT_OLD_STAT #define __ARCH_WANT_STAT64 diff --git a/arch/h8300/Kconfig b/arch/h8300/Kconfig index 56e890df5053..5e8a0d9a09ce 100644 --- a/arch/h8300/Kconfig +++ b/arch/h8300/Kconfig @@ -3,6 +3,7 @@ config H8300 default y select HAVE_IDE select HAVE_GENERIC_HARDIRQS + select ARCH_WANT_IPC_PARSE_VERSION select GENERIC_IRQ_SHOW select GENERIC_CPU_DEVICES diff --git a/arch/h8300/include/asm/unistd.h b/arch/h8300/include/asm/unistd.h index 718511303b4e..5cd882801d79 100644 --- a/arch/h8300/include/asm/unistd.h +++ b/arch/h8300/include/asm/unistd.h @@ -331,7 +331,6 @@ #define NR_syscalls 321 -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_STAT #define __ARCH_WANT_STAT64 diff --git a/arch/m32r/Kconfig b/arch/m32r/Kconfig index b638d5bfa14d..49498bbb9616 100644 --- a/arch/m32r/Kconfig +++ b/arch/m32r/Kconfig @@ -7,6 +7,7 @@ config M32R select HAVE_KERNEL_GZIP select HAVE_KERNEL_BZIP2 select HAVE_KERNEL_LZMA + select ARCH_WANT_IPC_PARSE_VERSION select HAVE_GENERIC_HARDIRQS select GENERIC_IRQ_PROBE select GENERIC_IRQ_SHOW diff --git a/arch/m32r/include/asm/unistd.h b/arch/m32r/include/asm/unistd.h index 3e1db561aacc..d5e66a480782 100644 --- a/arch/m32r/include/asm/unistd.h +++ b/arch/m32r/include/asm/unistd.h @@ -336,7 +336,6 @@ #define NR_syscalls 326 -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_STAT64 #define __ARCH_WANT_SYS_ALARM #define __ARCH_WANT_SYS_GETHOSTNAME diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig index 147120128260..0b0f8b8c4a26 100644 --- a/arch/m68k/Kconfig +++ b/arch/m68k/Kconfig @@ -10,6 +10,7 @@ config M68K select GENERIC_STRNCPY_FROM_USER if MMU select GENERIC_STRNLEN_USER if MMU select FPU if MMU + select ARCH_WANT_IPC_PARSE_VERSION select ARCH_USES_GETTIMEOFFSET if MMU && !COLDFIRE config RWSEM_GENERIC_SPINLOCK diff --git a/arch/m68k/include/asm/unistd.h b/arch/m68k/include/asm/unistd.h index ea0b502f845e..045cfd6a9e31 100644 --- a/arch/m68k/include/asm/unistd.h +++ b/arch/m68k/include/asm/unistd.h @@ -357,7 +357,6 @@ #define NR_syscalls 347 -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_STAT #define __ARCH_WANT_STAT64 diff --git a/arch/microblaze/Kconfig b/arch/microblaze/Kconfig index 0bf44231aaf9..ab9afcaa7f6a 100644 --- a/arch/microblaze/Kconfig +++ b/arch/microblaze/Kconfig @@ -15,6 +15,7 @@ config MICROBLAZE select TRACING_SUPPORT select OF select OF_EARLY_FLATTREE + select ARCH_WANT_IPC_PARSE_VERSION select IRQ_DOMAIN select HAVE_GENERIC_HARDIRQS select GENERIC_IRQ_PROBE diff --git a/arch/microblaze/include/asm/unistd.h b/arch/microblaze/include/asm/unistd.h index d20ffbc86beb..6985e6e9d826 100644 --- a/arch/microblaze/include/asm/unistd.h +++ b/arch/microblaze/include/asm/unistd.h @@ -400,7 +400,6 @@ #ifdef __KERNEL__ #ifndef __ASSEMBLY__ -#define __ARCH_WANT_IPC_PARSE_VERSION /* #define __ARCH_WANT_OLD_READDIR */ /* #define __ARCH_WANT_OLD_STAT */ #define __ARCH_WANT_STAT64 diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 5e238d03960d..2d56cd5af336 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -27,6 +27,7 @@ config MIPS select GENERIC_IRQ_PROBE select GENERIC_IRQ_SHOW select HAVE_ARCH_JUMP_LABEL + select ARCH_WANT_IPC_PARSE_VERSION select IRQ_FORCED_THREADING select HAVE_MEMBLOCK select HAVE_MEMBLOCK_NODE_MAP diff --git a/arch/mips/include/asm/unistd.h b/arch/mips/include/asm/unistd.h index d8dad5340ea3..bebbde01be92 100644 --- a/arch/mips/include/asm/unistd.h +++ b/arch/mips/include/asm/unistd.h @@ -1034,7 +1034,6 @@ #ifndef __ASSEMBLY__ #define __ARCH_OMIT_COMPAT_SYS_GETDENTS64 -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_SYS_ALARM #define __ARCH_WANT_SYS_GETHOSTNAME diff --git a/arch/mn10300/Kconfig b/arch/mn10300/Kconfig index 687f9b4a2ed6..5cfb086b3903 100644 --- a/arch/mn10300/Kconfig +++ b/arch/mn10300/Kconfig @@ -3,6 +3,7 @@ config MN10300 select HAVE_OPROFILE select HAVE_GENERIC_HARDIRQS select GENERIC_IRQ_SHOW + select ARCH_WANT_IPC_PARSE_VERSION select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_KGDB select HAVE_NMI_WATCHDOG if MN10300_WD_TIMER diff --git a/arch/mn10300/include/asm/unistd.h b/arch/mn10300/include/asm/unistd.h index 9051f921cbc7..866eb14749d7 100644 --- a/arch/mn10300/include/asm/unistd.h +++ b/arch/mn10300/include/asm/unistd.h @@ -358,7 +358,6 @@ /* * specify the deprecated syscalls we want to support on this arch */ -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_STAT #define __ARCH_WANT_STAT64 diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 4f681b78dd8b..352f416269ce 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -121,6 +121,7 @@ config PPC select HAVE_REGS_AND_STACK_ACCESS_API select HAVE_HW_BREAKPOINT if PERF_EVENTS && PPC_BOOK3S_64 select HAVE_GENERIC_HARDIRQS + select ARCH_WANT_IPC_PARSE_VERSION select SPARSE_IRQ select IRQ_PER_CPU select IRQ_DOMAIN diff --git a/arch/powerpc/include/asm/unistd.h b/arch/powerpc/include/asm/unistd.h index d3d1b5efd7eb..bd377a368611 100644 --- a/arch/powerpc/include/asm/unistd.h +++ b/arch/powerpc/include/asm/unistd.h @@ -389,7 +389,6 @@ #include #include -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_STAT64 #define __ARCH_WANT_SYS_ALARM diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index d0a5e92b6b9e..296cd32466df 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -118,6 +118,7 @@ config S390 select ARCH_INLINE_WRITE_UNLOCK_BH select ARCH_INLINE_WRITE_UNLOCK_IRQ select ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE + select ARCH_WANT_IPC_PARSE_VERSION select GENERIC_SMP_IDLE_THREAD select GENERIC_TIME_VSYSCALL select GENERIC_CLOCKEVENTS diff --git a/arch/s390/include/asm/unistd.h b/arch/s390/include/asm/unistd.h index 2e37157ba6a9..6756e78f4808 100644 --- a/arch/s390/include/asm/unistd.h +++ b/arch/s390/include/asm/unistd.h @@ -388,7 +388,6 @@ #define __IGNORE_recvmmsg #define __IGNORE_sendmmsg -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_SYS_ALARM #define __ARCH_WANT_SYS_GETHOSTNAME diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index a24595d83ad6..36f5141e8041 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -21,6 +21,7 @@ config SUPERH select HAVE_KERNEL_LZMA select HAVE_KERNEL_XZ select HAVE_KERNEL_LZO + select ARCH_WANT_IPC_PARSE_VERSION select HAVE_SYSCALL_TRACEPOINTS select HAVE_REGS_AND_STACK_ACCESS_API select HAVE_GENERIC_HARDIRQS @@ -50,6 +51,7 @@ config SUPERH32 select HAVE_DYNAMIC_FTRACE select HAVE_FUNCTION_TRACE_MCOUNT_TEST select HAVE_FTRACE_NMI_ENTER if DYNAMIC_FTRACE + select ARCH_WANT_IPC_PARSE_VERSION select HAVE_FUNCTION_GRAPH_TRACER select HAVE_ARCH_KGDB select HAVE_HW_BREAKPOINT diff --git a/arch/sh/include/asm/unistd.h b/arch/sh/include/asm/unistd.h index e800a38c9f8d..7bc67076baac 100644 --- a/arch/sh/include/asm/unistd.h +++ b/arch/sh/include/asm/unistd.h @@ -6,7 +6,6 @@ # endif # define __ARCH_WANT_SYS_RT_SIGSUSPEND -# define __ARCH_WANT_IPC_PARSE_VERSION # define __ARCH_WANT_OLD_READDIR # define __ARCH_WANT_OLD_STAT # define __ARCH_WANT_STAT64 diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index e74ff1377626..67f1f6f5f4e1 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -27,6 +27,7 @@ config SPARC select HAVE_ARCH_JUMP_LABEL select HAVE_GENERIC_HARDIRQS select GENERIC_IRQ_SHOW + select ARCH_WANT_IPC_PARSE_VERSION select USE_GENERIC_SMP_HELPERS if SMP select GENERIC_PCI_IOMAP select HAVE_NMI_WATCHDOG if SPARC64 diff --git a/arch/sparc/include/asm/unistd.h b/arch/sparc/include/asm/unistd.h index c7cb0af0eb59..fb2693464807 100644 --- a/arch/sparc/include/asm/unistd.h +++ b/arch/sparc/include/asm/unistd.h @@ -423,7 +423,6 @@ #endif #ifdef __KERNEL__ -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_STAT64 #define __ARCH_WANT_SYS_ALARM diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index ca4fdefe79e6..ba2657c49217 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -85,6 +85,7 @@ config X86 select GENERIC_IOMAP select DCACHE_WORD_ACCESS select GENERIC_SMP_IDLE_THREAD + select ARCH_WANT_IPC_PARSE_VERSION if X86_32 select HAVE_ARCH_SECCOMP_FILTER select BUILDTIME_EXTABLE_SORT select GENERIC_CMOS_UPDATE diff --git a/arch/x86/include/asm/unistd.h b/arch/x86/include/asm/unistd.h index 4437001d8e3d..0d9776e9e2dc 100644 --- a/arch/x86/include/asm/unistd.h +++ b/arch/x86/include/asm/unistd.h @@ -15,7 +15,6 @@ # ifdef CONFIG_X86_32 # include -# define __ARCH_WANT_IPC_PARSE_VERSION # define __ARCH_WANT_STAT64 # define __ARCH_WANT_SYS_IPC # define __ARCH_WANT_SYS_OLD_MMAP diff --git a/include/linux/compat.h b/include/linux/compat.h index f2b8fe20cc8e..09b28b7369d7 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -256,7 +256,6 @@ compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr, compat_size_t __user *len_ptr); #ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC -#define __ARCH_WANT_COMPAT_IPC_PARSE_VERSION long compat_sys_semctl(int first, int second, int third, void __user *uptr); long compat_sys_msgsnd(int first, int second, int third, void __user *uptr); long compat_sys_msgrcv(int first, int second, int msgtyp, int third, diff --git a/ipc/compat.c b/ipc/compat.c index 20f92b2f2932..ad9518eb26e0 100644 --- a/ipc/compat.c +++ b/ipc/compat.c @@ -118,7 +118,7 @@ extern int sem_ctls[]; static inline int compat_ipc_parse_version(int *cmd) { -#ifdef __ARCH_WANT_COMPAT_IPC_PARSE_VERSION +#ifdef CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION int version = *cmd & IPC_64; /* this is tricky: architectures that have support for the old diff --git a/ipc/util.c b/ipc/util.c index 75261a31d48d..eb07fd356f27 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -804,7 +804,7 @@ out_up: return ERR_PTR(err); } -#ifdef __ARCH_WANT_IPC_PARSE_VERSION +#ifdef CONFIG_ARCH_WANT_IPC_PARSE_VERSION /** @@ -826,7 +826,7 @@ int ipc_parse_version (int *cmd) } } -#endif /* __ARCH_WANT_IPC_PARSE_VERSION */ +#endif /* CONFIG_ARCH_WANT_IPC_PARSE_VERSION */ #ifdef CONFIG_PROC_FS struct ipc_proc_iter { diff --git a/ipc/util.h b/ipc/util.h index 6f5c20bedaab..850ef3e962cb 100644 --- a/ipc/util.h +++ b/ipc/util.h @@ -130,7 +130,7 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, struct ipc_ids *ids, int id, int cmd, struct ipc64_perm *perm, int extra_perm); -#ifndef __ARCH_WANT_IPC_PARSE_VERSION +#ifndef CONFIG_ARCH_WANT_IPC_PARSE_VERSION /* On IA-64, we always use the "64-bit version" of the IPC structures. */ # define ipc_parse_version(cmd) IPC_64 #else -- cgit v1.2.3 From f7e1becb078c2b996420a61f2a411ef19335e2da Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 30 Jul 2012 14:42:56 -0700 Subject: include/linux/aio.h: cpp->C conversions Convert init_sync_kiocb() from a nasty macro into a nice C function. The struct assignment trick takes care of zeroing all unmentioned fields. Shrinks fs/read_write.o's .text from 9857 bytes to 9714. Also demacroize is_sync_kiocb() and aio_ring_avail(). The latter fixes an arg-referenced-multiple-times hand grenade. Cc: Junxiao Bi Cc: Mark Fasheh Acked-by: Jeff Moyer Cc: Joel Becker Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/aio.h | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/aio.h b/include/linux/aio.h index b1a520ec8b59..31ff6dba4872 100644 --- a/include/linux/aio.h +++ b/include/linux/aio.h @@ -126,22 +126,20 @@ struct kiocb { struct eventfd_ctx *ki_eventfd; }; -#define is_sync_kiocb(iocb) ((iocb)->ki_key == KIOCB_SYNC_KEY) -#define init_sync_kiocb(x, filp) \ - do { \ - struct task_struct *tsk = current; \ - (x)->ki_flags = 0; \ - (x)->ki_users = 1; \ - (x)->ki_key = KIOCB_SYNC_KEY; \ - (x)->ki_filp = (filp); \ - (x)->ki_ctx = NULL; \ - (x)->ki_cancel = NULL; \ - (x)->ki_retry = NULL; \ - (x)->ki_dtor = NULL; \ - (x)->ki_obj.tsk = tsk; \ - (x)->ki_user_data = 0; \ - (x)->private = NULL; \ - } while (0) +static inline bool is_sync_kiocb(struct kiocb *kiocb) +{ + return kiocb->ki_key == KIOCB_SYNC_KEY; +} + +static inline void init_sync_kiocb(struct kiocb *kiocb, struct file *filp) +{ + *kiocb = (struct kiocb) { + .ki_users = 1, + .ki_key = KIOCB_SYNC_KEY, + .ki_filp = filp, + .ki_obj.tsk = current, + }; +} #define AIO_RING_MAGIC 0xa10a10a1 #define AIO_RING_COMPAT_FEATURES 1 @@ -161,8 +159,6 @@ struct aio_ring { struct io_event io_events[0]; }; /* 128 bytes + ring size */ -#define aio_ring_avail(info, ring) (((ring)->head + (info)->nr - 1 - (ring)->tail) % (info)->nr) - #define AIO_RING_PAGES 8 struct aio_ring_info { unsigned long mmap_base; @@ -177,6 +173,12 @@ struct aio_ring_info { struct page *internal_pages[AIO_RING_PAGES]; }; +static inline unsigned aio_ring_avail(struct aio_ring_info *info, + struct aio_ring *ring) +{ + return (ring->head + info->nr - 1 - ring->tail) % info->nr; +} + struct kioctx { atomic_t users; int dead; -- cgit v1.2.3 From 1d151c337d79fa3de88654d2514f58fbd916a8e0 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Mon, 30 Jul 2012 14:43:00 -0700 Subject: c/r: fcntl: add F_GETOWNER_UIDS option When we restore file descriptors we would like them to look exactly as they were at dumping time. With help of fcntl it's almost possible, the missing snippet is file owners UIDs. To be able to read their values the F_GETOWNER_UIDS is introduced. This option is valid iif CONFIG_CHECKPOINT_RESTORE is turned on, otherwise returning -EINVAL. Signed-off-by: Cyrill Gorcunov Acked-by: "Eric W. Biederman" Cc: "Serge E. Hallyn" Cc: Oleg Nesterov Cc: Pavel Emelyanov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fcntl.c | 29 +++++++++++++++++++++++++++++ include/asm-generic/fcntl.h | 4 ++++ security/selinux/hooks.c | 1 + 3 files changed, 34 insertions(+) (limited to 'include') diff --git a/fs/fcntl.c b/fs/fcntl.c index 81b70e665bf0..887b5ba8c9b5 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -340,6 +341,31 @@ static int f_getown_ex(struct file *filp, unsigned long arg) return ret; } +#ifdef CONFIG_CHECKPOINT_RESTORE +static int f_getowner_uids(struct file *filp, unsigned long arg) +{ + struct user_namespace *user_ns = current_user_ns(); + uid_t * __user dst = (void * __user)arg; + uid_t src[2]; + int err; + + read_lock(&filp->f_owner.lock); + src[0] = from_kuid(user_ns, filp->f_owner.uid); + src[1] = from_kuid(user_ns, filp->f_owner.euid); + read_unlock(&filp->f_owner.lock); + + err = put_user(src[0], &dst[0]); + err |= put_user(src[1], &dst[1]); + + return err; +} +#else +static int f_getowner_uids(struct file *filp, unsigned long arg) +{ + return -EINVAL; +} +#endif + static long do_fcntl(int fd, unsigned int cmd, unsigned long arg, struct file *filp) { @@ -396,6 +422,9 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg, case F_SETOWN_EX: err = f_setown_ex(filp, arg); break; + case F_GETOWNER_UIDS: + err = f_getowner_uids(filp, arg); + break; case F_GETSIG: err = filp->f_owner.signum; break; diff --git a/include/asm-generic/fcntl.h b/include/asm-generic/fcntl.h index 9e5b0356e2bb..a48937d4a5ea 100644 --- a/include/asm-generic/fcntl.h +++ b/include/asm-generic/fcntl.h @@ -120,6 +120,10 @@ #define F_GETOWN_EX 16 #endif +#ifndef F_GETOWNER_UIDS +#define F_GETOWNER_UIDS 17 +#endif + #define F_OWNER_TID 0 #define F_OWNER_PID 1 #define F_OWNER_PGRP 2 diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 94c45a1531a4..ec43760a8a03 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3180,6 +3180,7 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd, case F_GETFL: case F_GETOWN: case F_GETSIG: + case F_GETOWNER_UIDS: /* Just check FD__USE permission */ err = file_has_perm(cred, file, 0); break; -- cgit v1.2.3 From 3f6f49c7854c9294119437a82c5b35be78f9cea6 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Thu, 26 Jul 2012 20:08:54 -0400 Subject: ACPI: delete _GTS/_BFS support _GTS and _BFS were added to the suspend/resume flow in the ACPI 2.0 specification. Linux dutifully implemented _GTS and _BFS. We discovered that it was rarely seen in systems in the field. Further, some of those systems had AML so bogus that it could never work -- proof that no other operating system supports _GTS and _BFS. So we made _GTS and _BFS optional via modparam, and disabled them by default. But we've had to complicate some code to keep this support in the kernel, as these methods are defined to be evaluated very close to sleep entry and exit. Indeed, no other AML is ever evaluated with interrupts off. We have submitted a proposal for _GTS and _BFS to be officially removed from the ACPI specification on the next revision. Here we remove it from Linux. Signed-off-by: Len Brown Acked-by: Ingo Molnar Acked-by: Konrad Rzeszutek Wilk --- drivers/acpi/acpica/achware.h | 12 +++---- drivers/acpi/acpica/hwesleep.c | 19 ++--------- drivers/acpi/acpica/hwsleep.c | 20 ++--------- drivers/acpi/acpica/hwxfsleep.c | 22 ++++++------- drivers/acpi/sleep.c | 73 +++++------------------------------------ include/acpi/acpixf.h | 4 +-- include/acpi/actypes.h | 2 +- 7 files changed, 34 insertions(+), 118 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpica/achware.h b/drivers/acpi/acpica/achware.h index 5ccb99ae3a6f..5de4ec72766d 100644 --- a/drivers/acpi/acpica/achware.h +++ b/drivers/acpi/acpica/achware.h @@ -83,22 +83,22 @@ acpi_status acpi_hw_clear_acpi_status(void); /* * hwsleep - sleep/wake support (Legacy sleep registers) */ -acpi_status acpi_hw_legacy_sleep(u8 sleep_state, u8 flags); +acpi_status acpi_hw_legacy_sleep(u8 sleep_state); -acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state, u8 flags); +acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state); -acpi_status acpi_hw_legacy_wake(u8 sleep_state, u8 flags); +acpi_status acpi_hw_legacy_wake(u8 sleep_state); /* * hwesleep - sleep/wake support (Extended FADT-V5 sleep registers) */ void acpi_hw_execute_sleep_method(char *method_name, u32 integer_argument); -acpi_status acpi_hw_extended_sleep(u8 sleep_state, u8 flags); +acpi_status acpi_hw_extended_sleep(u8 sleep_state); -acpi_status acpi_hw_extended_wake_prep(u8 sleep_state, u8 flags); +acpi_status acpi_hw_extended_wake_prep(u8 sleep_state); -acpi_status acpi_hw_extended_wake(u8 sleep_state, u8 flags); +acpi_status acpi_hw_extended_wake(u8 sleep_state); /* * hwvalid - Port I/O with validation diff --git a/drivers/acpi/acpica/hwesleep.c b/drivers/acpi/acpica/hwesleep.c index 48518dac5342..94996f9ae3ad 100644 --- a/drivers/acpi/acpica/hwesleep.c +++ b/drivers/acpi/acpica/hwesleep.c @@ -90,7 +90,6 @@ void acpi_hw_execute_sleep_method(char *method_pathname, u32 integer_argument) * FUNCTION: acpi_hw_extended_sleep * * PARAMETERS: sleep_state - Which sleep state to enter - * flags - ACPI_EXECUTE_GTS to run optional method * * RETURN: Status * @@ -100,7 +99,7 @@ void acpi_hw_execute_sleep_method(char *method_pathname, u32 integer_argument) * ******************************************************************************/ -acpi_status acpi_hw_extended_sleep(u8 sleep_state, u8 flags) +acpi_status acpi_hw_extended_sleep(u8 sleep_state) { acpi_status status; u8 sleep_type_value; @@ -125,12 +124,6 @@ acpi_status acpi_hw_extended_sleep(u8 sleep_state, u8 flags) acpi_gbl_system_awake_and_running = FALSE; - /* Optionally execute _GTS (Going To Sleep) */ - - if (flags & ACPI_EXECUTE_GTS) { - acpi_hw_execute_sleep_method(METHOD_PATHNAME__GTS, sleep_state); - } - /* Flush caches, as per ACPI specification */ ACPI_FLUSH_CPU_CACHE(); @@ -172,7 +165,6 @@ acpi_status acpi_hw_extended_sleep(u8 sleep_state, u8 flags) * FUNCTION: acpi_hw_extended_wake_prep * * PARAMETERS: sleep_state - Which sleep state we just exited - * flags - ACPI_EXECUTE_BFS to run optional method * * RETURN: Status * @@ -181,7 +173,7 @@ acpi_status acpi_hw_extended_sleep(u8 sleep_state, u8 flags) * ******************************************************************************/ -acpi_status acpi_hw_extended_wake_prep(u8 sleep_state, u8 flags) +acpi_status acpi_hw_extended_wake_prep(u8 sleep_state) { acpi_status status; u8 sleep_type_value; @@ -200,11 +192,6 @@ acpi_status acpi_hw_extended_wake_prep(u8 sleep_state, u8 flags) &acpi_gbl_FADT.sleep_control); } - /* Optionally execute _BFS (Back From Sleep) */ - - if (flags & ACPI_EXECUTE_BFS) { - acpi_hw_execute_sleep_method(METHOD_PATHNAME__BFS, sleep_state); - } return_ACPI_STATUS(AE_OK); } @@ -222,7 +209,7 @@ acpi_status acpi_hw_extended_wake_prep(u8 sleep_state, u8 flags) * ******************************************************************************/ -acpi_status acpi_hw_extended_wake(u8 sleep_state, u8 flags) +acpi_status acpi_hw_extended_wake(u8 sleep_state) { ACPI_FUNCTION_TRACE(hw_extended_wake); diff --git a/drivers/acpi/acpica/hwsleep.c b/drivers/acpi/acpica/hwsleep.c index 9960fe9ef533..3fddde056a5e 100644 --- a/drivers/acpi/acpica/hwsleep.c +++ b/drivers/acpi/acpica/hwsleep.c @@ -56,7 +56,6 @@ ACPI_MODULE_NAME("hwsleep") * FUNCTION: acpi_hw_legacy_sleep * * PARAMETERS: sleep_state - Which sleep state to enter - * flags - ACPI_EXECUTE_GTS to run optional method * * RETURN: Status * @@ -64,7 +63,7 @@ ACPI_MODULE_NAME("hwsleep") * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED * ******************************************************************************/ -acpi_status acpi_hw_legacy_sleep(u8 sleep_state, u8 flags) +acpi_status acpi_hw_legacy_sleep(u8 sleep_state) { struct acpi_bit_register_info *sleep_type_reg_info; struct acpi_bit_register_info *sleep_enable_reg_info; @@ -110,12 +109,6 @@ acpi_status acpi_hw_legacy_sleep(u8 sleep_state, u8 flags) return_ACPI_STATUS(status); } - /* Optionally execute _GTS (Going To Sleep) */ - - if (flags & ACPI_EXECUTE_GTS) { - acpi_hw_execute_sleep_method(METHOD_PATHNAME__GTS, sleep_state); - } - /* Get current value of PM1A control */ status = acpi_hw_register_read(ACPI_REGISTER_PM1_CONTROL, @@ -214,7 +207,6 @@ acpi_status acpi_hw_legacy_sleep(u8 sleep_state, u8 flags) * FUNCTION: acpi_hw_legacy_wake_prep * * PARAMETERS: sleep_state - Which sleep state we just exited - * flags - ACPI_EXECUTE_BFS to run optional method * * RETURN: Status * @@ -224,7 +216,7 @@ acpi_status acpi_hw_legacy_sleep(u8 sleep_state, u8 flags) * ******************************************************************************/ -acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state, u8 flags) +acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state) { acpi_status status; struct acpi_bit_register_info *sleep_type_reg_info; @@ -275,11 +267,6 @@ acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state, u8 flags) } } - /* Optionally execute _BFS (Back From Sleep) */ - - if (flags & ACPI_EXECUTE_BFS) { - acpi_hw_execute_sleep_method(METHOD_PATHNAME__BFS, sleep_state); - } return_ACPI_STATUS(status); } @@ -288,7 +275,6 @@ acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state, u8 flags) * FUNCTION: acpi_hw_legacy_wake * * PARAMETERS: sleep_state - Which sleep state we just exited - * flags - Reserved, set to zero * * RETURN: Status * @@ -297,7 +283,7 @@ acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state, u8 flags) * ******************************************************************************/ -acpi_status acpi_hw_legacy_wake(u8 sleep_state, u8 flags) +acpi_status acpi_hw_legacy_wake(u8 sleep_state) { acpi_status status; diff --git a/drivers/acpi/acpica/hwxfsleep.c b/drivers/acpi/acpica/hwxfsleep.c index f8684bfe7907..1f165a750ae2 100644 --- a/drivers/acpi/acpica/hwxfsleep.c +++ b/drivers/acpi/acpica/hwxfsleep.c @@ -50,7 +50,7 @@ ACPI_MODULE_NAME("hwxfsleep") /* Local prototypes */ static acpi_status -acpi_hw_sleep_dispatch(u8 sleep_state, u8 flags, u32 function_id); +acpi_hw_sleep_dispatch(u8 sleep_state, u32 function_id); /* * Dispatch table used to efficiently branch to the various sleep @@ -235,7 +235,7 @@ ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_s4bios) * ******************************************************************************/ static acpi_status -acpi_hw_sleep_dispatch(u8 sleep_state, u8 flags, u32 function_id) +acpi_hw_sleep_dispatch(u8 sleep_state, u32 function_id) { acpi_status status; struct acpi_sleep_functions *sleep_functions = @@ -248,11 +248,11 @@ acpi_hw_sleep_dispatch(u8 sleep_state, u8 flags, u32 function_id) * use the extended sleep registers */ if (acpi_gbl_reduced_hardware || acpi_gbl_FADT.sleep_control.address) { - status = sleep_functions->extended_function(sleep_state, flags); + status = sleep_functions->extended_function(sleep_state); } else { /* Legacy sleep */ - status = sleep_functions->legacy_function(sleep_state, flags); + status = sleep_functions->legacy_function(sleep_state); } return (status); @@ -262,7 +262,7 @@ acpi_hw_sleep_dispatch(u8 sleep_state, u8 flags, u32 function_id) * For the case where reduced-hardware-only code is being generated, * we know that only the extended sleep registers are available */ - status = sleep_functions->extended_function(sleep_state, flags); + status = sleep_functions->extended_function(sleep_state); return (status); #endif /* !ACPI_REDUCED_HARDWARE */ @@ -349,7 +349,6 @@ ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_prep) * FUNCTION: acpi_enter_sleep_state * * PARAMETERS: sleep_state - Which sleep state to enter - * flags - ACPI_EXECUTE_GTS to run optional method * * RETURN: Status * @@ -357,7 +356,7 @@ ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_prep) * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED * ******************************************************************************/ -acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state, u8 flags) +acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state) { acpi_status status; @@ -371,7 +370,7 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state, u8 flags) } status = - acpi_hw_sleep_dispatch(sleep_state, flags, ACPI_SLEEP_FUNCTION_ID); + acpi_hw_sleep_dispatch(sleep_state, ACPI_SLEEP_FUNCTION_ID); return_ACPI_STATUS(status); } @@ -391,14 +390,14 @@ ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state) * Called with interrupts DISABLED. * ******************************************************************************/ -acpi_status acpi_leave_sleep_state_prep(u8 sleep_state, u8 flags) +acpi_status acpi_leave_sleep_state_prep(u8 sleep_state) { acpi_status status; ACPI_FUNCTION_TRACE(acpi_leave_sleep_state_prep); status = - acpi_hw_sleep_dispatch(sleep_state, flags, + acpi_hw_sleep_dispatch(sleep_state, ACPI_WAKE_PREP_FUNCTION_ID); return_ACPI_STATUS(status); } @@ -423,8 +422,7 @@ acpi_status acpi_leave_sleep_state(u8 sleep_state) ACPI_FUNCTION_TRACE(acpi_leave_sleep_state); - - status = acpi_hw_sleep_dispatch(sleep_state, 0, ACPI_WAKE_FUNCTION_ID); + status = acpi_hw_sleep_dispatch(sleep_state, ACPI_WAKE_FUNCTION_ID); return_ACPI_STATUS(status); } diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 23a53c013f1e..bd35e3c5e530 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -28,34 +28,6 @@ #include "internal.h" #include "sleep.h" -u8 wake_sleep_flags = ACPI_NO_OPTIONAL_METHODS; -static unsigned int gts, bfs; -static int set_param_wake_flag(const char *val, struct kernel_param *kp) -{ - int ret = param_set_int(val, kp); - - if (ret) - return ret; - - if (kp->arg == (const char *)>s) { - if (gts) - wake_sleep_flags |= ACPI_EXECUTE_GTS; - else - wake_sleep_flags &= ~ACPI_EXECUTE_GTS; - } - if (kp->arg == (const char *)&bfs) { - if (bfs) - wake_sleep_flags |= ACPI_EXECUTE_BFS; - else - wake_sleep_flags &= ~ACPI_EXECUTE_BFS; - } - return ret; -} -module_param_call(gts, set_param_wake_flag, param_get_int, >s, 0644); -module_param_call(bfs, set_param_wake_flag, param_get_int, &bfs, 0644); -MODULE_PARM_DESC(gts, "Enable evaluation of _GTS on suspend."); -MODULE_PARM_DESC(bfs, "Enable evaluation of _BFS on resume".); - static u8 sleep_states[ACPI_S_STATE_COUNT]; static bool pwr_btn_event_pending; @@ -305,7 +277,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state) switch (acpi_state) { case ACPI_STATE_S1: barrier(); - status = acpi_enter_sleep_state(acpi_state, wake_sleep_flags); + status = acpi_enter_sleep_state(acpi_state); break; case ACPI_STATE_S3: @@ -319,8 +291,8 @@ static int acpi_suspend_enter(suspend_state_t pm_state) /* This violates the spec but is required for bug compatibility. */ acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1); - /* Reprogram control registers and execute _BFS */ - acpi_leave_sleep_state_prep(acpi_state, wake_sleep_flags); + /* Reprogram control registers */ + acpi_leave_sleep_state_prep(acpi_state); /* ACPI 3.0 specs (P62) says that it's the responsibility * of the OSPM to clear the status bit [ implying that the @@ -603,9 +575,9 @@ static int acpi_hibernation_enter(void) ACPI_FLUSH_CPU_CACHE(); /* This shouldn't return. If it returns, we have a problem */ - status = acpi_enter_sleep_state(ACPI_STATE_S4, wake_sleep_flags); - /* Reprogram control registers and execute _BFS */ - acpi_leave_sleep_state_prep(ACPI_STATE_S4, wake_sleep_flags); + status = acpi_enter_sleep_state(ACPI_STATE_S4); + /* Reprogram control registers */ + acpi_leave_sleep_state_prep(ACPI_STATE_S4); return ACPI_SUCCESS(status) ? 0 : -EFAULT; } @@ -617,8 +589,8 @@ static void acpi_hibernation_leave(void) * enable it here. */ acpi_enable(); - /* Reprogram control registers and execute _BFS */ - acpi_leave_sleep_state_prep(ACPI_STATE_S4, wake_sleep_flags); + /* Reprogram control registers */ + acpi_leave_sleep_state_prep(ACPI_STATE_S4); /* Check the hardware signature */ if (facs && s4_hardware_signature != facs->hardware_signature) { printk(KERN_EMERG "ACPI: Hardware changed while hibernated, " @@ -876,33 +848,7 @@ static void acpi_power_off(void) /* acpi_sleep_prepare(ACPI_STATE_S5) should have already been called */ printk(KERN_DEBUG "%s called\n", __func__); local_irq_disable(); - acpi_enter_sleep_state(ACPI_STATE_S5, wake_sleep_flags); -} - -/* - * ACPI 2.0 created the optional _GTS and _BFS, - * but industry adoption has been neither rapid nor broad. - * - * Linux gets into trouble when it executes poorly validated - * paths through the BIOS, so disable _GTS and _BFS by default, - * but do speak up and offer the option to enable them. - */ -static void __init acpi_gts_bfs_check(void) -{ - acpi_handle dummy; - - if (ACPI_SUCCESS(acpi_get_handle(ACPI_ROOT_OBJECT, METHOD_PATHNAME__GTS, &dummy))) - { - printk(KERN_NOTICE PREFIX "BIOS offers _GTS\n"); - printk(KERN_NOTICE PREFIX "If \"acpi.gts=1\" improves suspend, " - "please notify linux-acpi@vger.kernel.org\n"); - } - if (ACPI_SUCCESS(acpi_get_handle(ACPI_ROOT_OBJECT, METHOD_PATHNAME__BFS, &dummy))) - { - printk(KERN_NOTICE PREFIX "BIOS offers _BFS\n"); - printk(KERN_NOTICE PREFIX "If \"acpi.bfs=1\" improves resume, " - "please notify linux-acpi@vger.kernel.org\n"); - } + acpi_enter_sleep_state(ACPI_STATE_S5); } int __init acpi_sleep_init(void) @@ -963,6 +909,5 @@ int __init acpi_sleep_init(void) * object can also be evaluated when the system enters S5. */ register_reboot_notifier(&tts_notifier); - acpi_gts_bfs_check(); return 0; } diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 2c744c7a5b3d..26a92fc28a59 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -491,11 +491,11 @@ acpi_get_sleep_type_data(u8 sleep_state, u8 * slp_typ_a, u8 * slp_typ_b); acpi_status acpi_enter_sleep_state_prep(u8 sleep_state); -acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state, u8 flags); +acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state); ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status asmlinkage acpi_enter_sleep_state_s4bios(void)) -acpi_status acpi_leave_sleep_state_prep(u8 sleep_state, u8 flags); +acpi_status acpi_leave_sleep_state_prep(u8 sleep_state); acpi_status acpi_leave_sleep_state(u8 sleep_state); diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 3af87de6a68c..3d00bd5bd7e3 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -803,7 +803,7 @@ typedef u8 acpi_adr_space_type; /* Sleep function dispatch */ -typedef acpi_status(*ACPI_SLEEP_FUNCTION) (u8 sleep_state, u8 flags); +typedef acpi_status(*ACPI_SLEEP_FUNCTION) (u8 sleep_state); struct acpi_sleep_functions { ACPI_SLEEP_FUNCTION legacy_function; -- cgit v1.2.3 From 546f04ef716dd49521774653d8b032a7d64c05d9 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 30 Jul 2012 18:15:23 -0700 Subject: libceph: support crush tunables The server side recently added support for tuning some magic crush variables. Decode these variables if they are present, or use the default values if they are not present. Corresponds to ceph.git commit 89af369c25f274fe62ef730e5e8aad0c54f1e5a5. Signed-off-by: caleb miles Reviewed-by: Sage Weil Reviewed-by: Alex Elder Reviewed-by: Yehuda Sadeh --- include/linux/ceph/ceph_features.h | 5 ++++- include/linux/crush/crush.h | 8 ++++++++ net/ceph/crush/mapper.c | 13 +++++++------ net/ceph/osdmap.c | 39 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/ceph_features.h b/include/linux/ceph/ceph_features.h index 342f93dbe162..dad579b0c0e6 100644 --- a/include/linux/ceph/ceph_features.h +++ b/include/linux/ceph/ceph_features.h @@ -12,12 +12,15 @@ #define CEPH_FEATURE_MONNAMES (1<<5) #define CEPH_FEATURE_RECONNECT_SEQ (1<<6) #define CEPH_FEATURE_DIRLAYOUTHASH (1<<7) +/* bits 8-17 defined by user-space; not supported yet here */ +#define CEPH_FEATURE_CRUSH_TUNABLES (1<<18) /* * Features supported. */ #define CEPH_FEATURES_SUPPORTED_DEFAULT \ - (CEPH_FEATURE_NOSRCADDR) + (CEPH_FEATURE_NOSRCADDR | \ + CEPH_FEATURE_CRUSH_TUNABLES) #define CEPH_FEATURES_REQUIRED_DEFAULT \ (CEPH_FEATURE_NOSRCADDR) diff --git a/include/linux/crush/crush.h b/include/linux/crush/crush.h index 7c4750811b96..25baa287cff7 100644 --- a/include/linux/crush/crush.h +++ b/include/linux/crush/crush.h @@ -154,6 +154,14 @@ struct crush_map { __s32 max_buckets; __u32 max_rules; __s32 max_devices; + + /* choose local retries before re-descent */ + __u32 choose_local_tries; + /* choose local attempts using a fallback permutation before + * re-descent */ + __u32 choose_local_fallback_tries; + /* choose attempts before giving up */ + __u32 choose_total_tries; }; diff --git a/net/ceph/crush/mapper.c b/net/ceph/crush/mapper.c index d7edc24333b8..35fce755ce10 100644 --- a/net/ceph/crush/mapper.c +++ b/net/ceph/crush/mapper.c @@ -306,7 +306,6 @@ static int crush_choose(const struct crush_map *map, int item = 0; int itemtype; int collide, reject; - const unsigned int orig_tries = 5; /* attempts before we fall back to search */ dprintk("CHOOSE%s bucket %d x %d outpos %d numrep %d\n", recurse_to_leaf ? "_LEAF" : "", bucket->id, x, outpos, numrep); @@ -351,8 +350,9 @@ static int crush_choose(const struct crush_map *map, reject = 1; goto reject; } - if (flocal >= (in->size>>1) && - flocal > orig_tries) + if (map->choose_local_fallback_tries > 0 && + flocal >= (in->size>>1) && + flocal > map->choose_local_fallback_tries) item = bucket_perm_choose(in, x, r); else item = crush_bucket_choose(in, x, r); @@ -422,13 +422,14 @@ reject: ftotal++; flocal++; - if (collide && flocal < 3) + if (collide && flocal <= map->choose_local_tries) /* retry locally a few times */ retry_bucket = 1; - else if (flocal <= in->size + orig_tries) + else if (map->choose_local_fallback_tries > 0 && + flocal <= in->size + map->choose_local_fallback_tries) /* exhaustive bucket search */ retry_bucket = 1; - else if (ftotal < 20) + else if (ftotal <= map->choose_total_tries) /* then retry descent */ retry_descent = 1; else diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index 9600674c2c39..3124b71a8883 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -135,6 +135,21 @@ bad: return -EINVAL; } +static int skip_name_map(void **p, void *end) +{ + int len; + ceph_decode_32_safe(p, end, len ,bad); + while (len--) { + int strlen; + *p += sizeof(u32); + ceph_decode_32_safe(p, end, strlen, bad); + *p += strlen; +} + return 0; +bad: + return -EINVAL; +} + static struct crush_map *crush_decode(void *pbyval, void *end) { struct crush_map *c; @@ -143,6 +158,7 @@ static struct crush_map *crush_decode(void *pbyval, void *end) void **p = &pbyval; void *start = pbyval; u32 magic; + u32 num_name_maps; dout("crush_decode %p to %p len %d\n", *p, end, (int)(end - *p)); @@ -150,6 +166,11 @@ static struct crush_map *crush_decode(void *pbyval, void *end) if (c == NULL) return ERR_PTR(-ENOMEM); + /* set tunables to default values */ + c->choose_local_tries = 2; + c->choose_local_fallback_tries = 5; + c->choose_total_tries = 19; + ceph_decode_need(p, end, 4*sizeof(u32), bad); magic = ceph_decode_32(p); if (magic != CRUSH_MAGIC) { @@ -297,7 +318,25 @@ static struct crush_map *crush_decode(void *pbyval, void *end) } /* ignore trailing name maps. */ + for (num_name_maps = 0; num_name_maps < 3; num_name_maps++) { + err = skip_name_map(p, end); + if (err < 0) + goto done; + } + + /* tunables */ + ceph_decode_need(p, end, 3*sizeof(u32), done); + c->choose_local_tries = ceph_decode_32(p); + c->choose_local_fallback_tries = ceph_decode_32(p); + c->choose_total_tries = ceph_decode_32(p); + dout("crush decode tunable choose_local_tries = %d", + c->choose_local_tries); + dout("crush decode tunable choose_local_fallback_tries = %d", + c->choose_local_fallback_tries); + dout("crush decode tunable choose_total_tries = %d", + c->choose_total_tries); +done: dout("crush_decode success\n"); return c; -- cgit v1.2.3 From aa711ee3402ad10ffd5b70ce0417fadc9a95cccf Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 13 Jul 2012 20:35:11 -0500 Subject: ceph: define snap counts as u32 everywhere There are two structures in which a count of snapshots are maintained: struct ceph_snap_context { ... u32 num_snaps; ... } and struct ceph_snap_realm { ... u32 num_prior_parent_snaps; /* had prior to parent_since */ ... u32 num_snaps; ... } These fields never take on negative values (e.g., to hold special meaning), and so are really inherently unsigned. Furthermore they take their value from over-the-wire or on-disk formatted 32-bit values. So change their definition to have type u32, and change some spots elsewhere in the code to account for this change. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- fs/ceph/snap.c | 18 ++++++++++-------- fs/ceph/super.h | 4 ++-- include/linux/ceph/libceph.h | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c index e5206fc76562..cbb2f54a3019 100644 --- a/fs/ceph/snap.c +++ b/fs/ceph/snap.c @@ -296,8 +296,7 @@ static int build_snap_context(struct ceph_snap_realm *realm) struct ceph_snap_realm *parent = realm->parent; struct ceph_snap_context *snapc; int err = 0; - int i; - int num = realm->num_prior_parent_snaps + realm->num_snaps; + u32 num = realm->num_prior_parent_snaps + realm->num_snaps; /* * build parent context, if it hasn't been built. @@ -321,11 +320,11 @@ static int build_snap_context(struct ceph_snap_realm *realm) realm->cached_context->seq == realm->seq && (!parent || realm->cached_context->seq >= parent->cached_context->seq)) { - dout("build_snap_context %llx %p: %p seq %lld (%d snaps)" + dout("build_snap_context %llx %p: %p seq %lld (%u snaps)" " (unchanged)\n", realm->ino, realm, realm->cached_context, realm->cached_context->seq, - realm->cached_context->num_snaps); + (unsigned int) realm->cached_context->num_snaps); return 0; } @@ -342,6 +341,8 @@ static int build_snap_context(struct ceph_snap_realm *realm) num = 0; snapc->seq = realm->seq; if (parent) { + u32 i; + /* include any of parent's snaps occurring _after_ my parent became my parent */ for (i = 0; i < parent->cached_context->num_snaps; i++) @@ -361,8 +362,9 @@ static int build_snap_context(struct ceph_snap_realm *realm) sort(snapc->snaps, num, sizeof(u64), cmpu64_rev, NULL); snapc->num_snaps = num; - dout("build_snap_context %llx %p: %p seq %lld (%d snaps)\n", - realm->ino, realm, snapc, snapc->seq, snapc->num_snaps); + dout("build_snap_context %llx %p: %p seq %lld (%u snaps)\n", + realm->ino, realm, snapc, snapc->seq, + (unsigned int) snapc->num_snaps); if (realm->cached_context) ceph_put_snap_context(realm->cached_context); @@ -402,9 +404,9 @@ static void rebuild_snap_realms(struct ceph_snap_realm *realm) * helper to allocate and decode an array of snapids. free prior * instance, if any. */ -static int dup_array(u64 **dst, __le64 *src, int num) +static int dup_array(u64 **dst, __le64 *src, u32 num) { - int i; + u32 i; kfree(*dst); if (num) { diff --git a/fs/ceph/super.h b/fs/ceph/super.h index fc35036d258d..3ea48b7b98b3 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -612,9 +612,9 @@ struct ceph_snap_realm { u64 parent_since; /* snapid when our current parent became so */ u64 *prior_parent_snaps; /* snaps inherited from any parents we */ - int num_prior_parent_snaps; /* had prior to parent_since */ + u32 num_prior_parent_snaps; /* had prior to parent_since */ u64 *snaps; /* snaps specific to this realm */ - int num_snaps; + u32 num_snaps; struct ceph_snap_realm *parent; struct list_head children; /* list of child realms */ diff --git a/include/linux/ceph/libceph.h b/include/linux/ceph/libceph.h index ea072e1f9db9..42624789b06f 100644 --- a/include/linux/ceph/libceph.h +++ b/include/linux/ceph/libceph.h @@ -154,7 +154,7 @@ struct ceph_client { struct ceph_snap_context { atomic_t nref; u64 seq; - int num_snaps; + u32 num_snaps; u64 snaps[]; }; -- cgit v1.2.3 From 8dacc7da69a491c515851e68de6036f21b5663ce Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 20 Jul 2012 17:24:40 -0700 Subject: libceph: replace connection state bits with states Use a simple set of 6 enumerated values for the socket states (CON_STATE_*) and use those instead of the state bits. All of the con->state checks are now under the protection of the con mutex, so this is safe. It also simplifies many of the state checks because we can check for anything other than the expected state instead of various bits for races we can think of. This appears to hold up well to stress testing both with and without socket failure injection on the server side. Signed-off-by: Sage Weil --- include/linux/ceph/messenger.h | 12 ---- net/ceph/messenger.c | 130 +++++++++++++++++++++-------------------- 2 files changed, 68 insertions(+), 74 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index a310d7fe6e29..d9c2b8f5abde 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -116,18 +116,6 @@ struct ceph_msg_pos { #define SOCK_CLOSED 11 /* socket state changed to closed */ #define BACKOFF 15 -/* - * ceph_connection states - */ -#define CONNECTING 1 -#define NEGOTIATING 2 -#define CONNECTED 5 -#define STANDBY 8 /* no outgoing messages, socket closed. we keep - * the ceph_connection around to maintain shared - * state with the peer. */ -#define CLOSED 10 /* we've closed the connection */ -#define OPENING 13 /* open connection w/ (possibly new) peer */ - /* * A single connection with another host. * diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index e7320cd5fdbc..563e46aa4d6d 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -77,6 +77,17 @@ #define CON_SOCK_STATE_CONNECTED 3 /* -> CLOSING or -> CLOSED */ #define CON_SOCK_STATE_CLOSING 4 /* -> CLOSED */ +/* + * connection states + */ +#define CON_STATE_CLOSED 1 /* -> PREOPEN */ +#define CON_STATE_PREOPEN 2 /* -> CONNECTING, CLOSED */ +#define CON_STATE_CONNECTING 3 /* -> NEGOTIATING, CLOSED */ +#define CON_STATE_NEGOTIATING 4 /* -> OPEN, CLOSED */ +#define CON_STATE_OPEN 5 /* -> STANDBY, CLOSED */ +#define CON_STATE_STANDBY 6 /* -> PREOPEN, CLOSED */ + + /* static tag bytes (protocol control messages) */ static char tag_msg = CEPH_MSGR_TAG_MSG; static char tag_ack = CEPH_MSGR_TAG_ACK; @@ -503,11 +514,7 @@ void ceph_con_close(struct ceph_connection *con) mutex_lock(&con->mutex); dout("con_close %p peer %s\n", con, ceph_pr_addr(&con->peer_addr.in_addr)); - clear_bit(NEGOTIATING, &con->state); - clear_bit(CONNECTING, &con->state); - clear_bit(CONNECTED, &con->state); - clear_bit(STANDBY, &con->state); /* avoid connect_seq bump */ - set_bit(CLOSED, &con->state); + con->state = CON_STATE_CLOSED; clear_bit(LOSSYTX, &con->flags); /* so we retry next connect */ clear_bit(KEEPALIVE_PENDING, &con->flags); @@ -530,8 +537,9 @@ void ceph_con_open(struct ceph_connection *con, { mutex_lock(&con->mutex); dout("con_open %p %s\n", con, ceph_pr_addr(&addr->in_addr)); - set_bit(OPENING, &con->state); - WARN_ON(!test_and_clear_bit(CLOSED, &con->state)); + + BUG_ON(con->state != CON_STATE_CLOSED); + con->state = CON_STATE_PREOPEN; con->peer_name.type = (__u8) entity_type; con->peer_name.num = cpu_to_le64(entity_num); @@ -571,7 +579,7 @@ void ceph_con_init(struct ceph_connection *con, void *private, INIT_LIST_HEAD(&con->out_sent); INIT_DELAYED_WORK(&con->work, con_work); - set_bit(CLOSED, &con->state); + con->state = CON_STATE_CLOSED; } EXPORT_SYMBOL(ceph_con_init); @@ -809,27 +817,21 @@ static struct ceph_auth_handshake *get_connect_authorizer(struct ceph_connection if (!con->ops->get_authorizer) { con->out_connect.authorizer_protocol = CEPH_AUTH_UNKNOWN; con->out_connect.authorizer_len = 0; - return NULL; } /* Can't hold the mutex while getting authorizer */ - mutex_unlock(&con->mutex); - auth = con->ops->get_authorizer(con, auth_proto, con->auth_retry); - mutex_lock(&con->mutex); if (IS_ERR(auth)) return auth; - if (test_bit(CLOSED, &con->state) || test_bit(OPENING, &con->flags)) + if (con->state != CON_STATE_NEGOTIATING) return ERR_PTR(-EAGAIN); con->auth_reply_buf = auth->authorizer_reply_buf; con->auth_reply_buf_len = auth->authorizer_reply_buf_len; - - return auth; } @@ -1484,7 +1486,8 @@ static int process_banner(struct ceph_connection *con) static void fail_protocol(struct ceph_connection *con) { reset_connection(con); - set_bit(CLOSED, &con->state); /* in case there's queued work */ + BUG_ON(con->state != CON_STATE_NEGOTIATING); + con->state = CON_STATE_CLOSED; } static int process_connect(struct ceph_connection *con) @@ -1558,8 +1561,7 @@ static int process_connect(struct ceph_connection *con) if (con->ops->peer_reset) con->ops->peer_reset(con); mutex_lock(&con->mutex); - if (test_bit(CLOSED, &con->state) || - test_bit(OPENING, &con->state)) + if (con->state != CON_STATE_NEGOTIATING) return -EAGAIN; break; @@ -1605,8 +1607,10 @@ static int process_connect(struct ceph_connection *con) fail_protocol(con); return -1; } - clear_bit(NEGOTIATING, &con->state); - set_bit(CONNECTED, &con->state); + + BUG_ON(con->state != CON_STATE_NEGOTIATING); + con->state = CON_STATE_OPEN; + con->peer_global_seq = le32_to_cpu(con->in_reply.global_seq); con->connect_seq++; con->peer_features = server_feat; @@ -1994,8 +1998,9 @@ more: dout("try_write out_kvec_bytes %d\n", con->out_kvec_bytes); /* open the socket first? */ - if (con->sock == NULL) { - set_bit(CONNECTING, &con->state); + if (con->state == CON_STATE_PREOPEN) { + BUG_ON(con->sock); + con->state = CON_STATE_CONNECTING; con_out_kvec_reset(con); prepare_write_banner(con); @@ -2046,8 +2051,7 @@ more_kvec: } do_next: - if (!test_bit(CONNECTING, &con->state) && - !test_bit(NEGOTIATING, &con->state)) { + if (con->state == CON_STATE_OPEN) { /* is anything else pending? */ if (!list_empty(&con->out_queue)) { prepare_write_message(con); @@ -2081,29 +2085,19 @@ static int try_read(struct ceph_connection *con) { int ret = -1; - if (!con->sock) - return 0; - - if (test_bit(STANDBY, &con->state)) +more: + dout("try_read start on %p state %lu\n", con, con->state); + if (con->state != CON_STATE_CONNECTING && + con->state != CON_STATE_NEGOTIATING && + con->state != CON_STATE_OPEN) return 0; - dout("try_read start on %p\n", con); + BUG_ON(!con->sock); -more: dout("try_read tag %d in_base_pos %d\n", (int)con->in_tag, con->in_base_pos); - /* - * process_connect and process_message drop and re-take - * con->mutex. make sure we handle a racing close or reopen. - */ - if (test_bit(CLOSED, &con->state) || - test_bit(OPENING, &con->state)) { - ret = -EAGAIN; - goto out; - } - - if (test_bit(CONNECTING, &con->state)) { + if (con->state == CON_STATE_CONNECTING) { dout("try_read connecting\n"); ret = read_partial_banner(con); if (ret <= 0) @@ -2112,8 +2106,8 @@ more: if (ret < 0) goto out; - clear_bit(CONNECTING, &con->state); - set_bit(NEGOTIATING, &con->state); + BUG_ON(con->state != CON_STATE_CONNECTING); + con->state = CON_STATE_NEGOTIATING; /* Banner is good, exchange connection info */ ret = prepare_write_connect(con); @@ -2125,7 +2119,7 @@ more: goto out; } - if (test_bit(NEGOTIATING, &con->state)) { + if (con->state == CON_STATE_NEGOTIATING) { dout("try_read negotiating\n"); ret = read_partial_connect(con); if (ret <= 0) @@ -2136,6 +2130,8 @@ more: goto more; } + BUG_ON(con->state != CON_STATE_OPEN); + if (con->in_base_pos < 0) { /* * skipping + discarding content. @@ -2169,8 +2165,8 @@ more: prepare_read_ack(con); break; case CEPH_MSGR_TAG_CLOSE: - clear_bit(CONNECTED, &con->state); - set_bit(CLOSED, &con->state); /* fixme */ + con_close_socket(con); + con->state = CON_STATE_CLOSED; goto out; default: goto bad_tag; @@ -2246,14 +2242,21 @@ static void con_work(struct work_struct *work) mutex_lock(&con->mutex); restart: if (test_and_clear_bit(SOCK_CLOSED, &con->flags)) { - if (test_and_clear_bit(CONNECTED, &con->state)) - con->error_msg = "socket closed"; - else if (test_and_clear_bit(NEGOTIATING, &con->state)) - con->error_msg = "negotiation failed"; - else if (test_and_clear_bit(CONNECTING, &con->state)) + switch (con->state) { + case CON_STATE_CONNECTING: con->error_msg = "connection failed"; - else + break; + case CON_STATE_NEGOTIATING: + con->error_msg = "negotiation failed"; + break; + case CON_STATE_OPEN: + con->error_msg = "socket closed"; + break; + default: + dout("unrecognized con state %d\n", (int)con->state); con->error_msg = "unrecognized con state"; + BUG(); + } goto fault; } @@ -2271,17 +2274,16 @@ restart: } } - if (test_bit(STANDBY, &con->state)) { + if (con->state == CON_STATE_STANDBY) { dout("con_work %p STANDBY\n", con); goto done; } - if (test_bit(CLOSED, &con->state)) { + if (con->state == CON_STATE_CLOSED) { dout("con_work %p CLOSED\n", con); BUG_ON(con->sock); goto done; } - if (test_and_clear_bit(OPENING, &con->state)) { - /* reopen w/ new peer */ + if (con->state == CON_STATE_PREOPEN) { dout("con_work OPENING\n"); BUG_ON(con->sock); } @@ -2328,13 +2330,15 @@ static void ceph_fault(struct ceph_connection *con) dout("fault %p state %lu to peer %s\n", con, con->state, ceph_pr_addr(&con->peer_addr.in_addr)); - if (test_bit(CLOSED, &con->state)) - goto out_unlock; + BUG_ON(con->state != CON_STATE_CONNECTING && + con->state != CON_STATE_NEGOTIATING && + con->state != CON_STATE_OPEN); con_close_socket(con); if (test_bit(LOSSYTX, &con->flags)) { - dout("fault on LOSSYTX channel\n"); + dout("fault on LOSSYTX channel, marking CLOSED\n"); + con->state = CON_STATE_CLOSED; goto out_unlock; } @@ -2355,9 +2359,10 @@ static void ceph_fault(struct ceph_connection *con) !test_bit(KEEPALIVE_PENDING, &con->flags)) { dout("fault %p setting STANDBY clearing WRITE_PENDING\n", con); clear_bit(WRITE_PENDING, &con->flags); - set_bit(STANDBY, &con->state); + con->state = CON_STATE_STANDBY; } else { /* retry after a delay. */ + con->state = CON_STATE_PREOPEN; if (con->delay == 0) con->delay = BASE_DELAY_INTERVAL; else if (con->delay < MAX_DELAY_INTERVAL) @@ -2431,8 +2436,9 @@ EXPORT_SYMBOL(ceph_messenger_init); static void clear_standby(struct ceph_connection *con) { /* come back from STANDBY? */ - if (test_and_clear_bit(STANDBY, &con->state)) { + if (con->state == CON_STATE_STANDBY) { dout("clear_standby %p and ++connect_seq\n", con); + con->state = CON_STATE_PREOPEN; con->connect_seq++; WARN_ON(test_bit(WRITE_PENDING, &con->flags)); WARN_ON(test_bit(KEEPALIVE_PENDING, &con->flags)); @@ -2451,7 +2457,7 @@ void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg) mutex_lock(&con->mutex); - if (test_bit(CLOSED, &con->state)) { + if (con->state == CON_STATE_CLOSED) { dout("con_send %p closed, dropping %p\n", con, msg); ceph_msg_put(msg); mutex_unlock(&con->mutex); -- cgit v1.2.3 From 4a8616920860920abaa51193146fe36b38ef09aa Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 20 Jul 2012 17:29:55 -0700 Subject: libceph: clean up con flags Rename flags with CON_FLAG prefix, move the definitions into the c file, and (better) document their meaning. Signed-off-by: Sage Weil --- include/linux/ceph/messenger.h | 10 ------- net/ceph/messenger.c | 62 ++++++++++++++++++++++++------------------ 2 files changed, 36 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index d9c2b8f5abde..189ae0637634 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -106,16 +106,6 @@ struct ceph_msg_pos { #define BASE_DELAY_INTERVAL (HZ/2) #define MAX_DELAY_INTERVAL (5 * 60 * HZ) -/* - * ceph_connection flag bits - */ - -#define LOSSYTX 0 /* we can close channel or drop messages on errors */ -#define KEEPALIVE_PENDING 3 -#define WRITE_PENDING 4 /* we have data ready to send */ -#define SOCK_CLOSED 11 /* socket state changed to closed */ -#define BACKOFF 15 - /* * A single connection with another host. * diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 563e46aa4d6d..b872db5c4989 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -87,6 +87,15 @@ #define CON_STATE_OPEN 5 /* -> STANDBY, CLOSED */ #define CON_STATE_STANDBY 6 /* -> PREOPEN, CLOSED */ +/* + * ceph_connection flag bits + */ +#define CON_FLAG_LOSSYTX 0 /* we can close channel or drop + * messages on errors */ +#define CON_FLAG_KEEPALIVE_PENDING 1 /* we need to send a keepalive */ +#define CON_FLAG_WRITE_PENDING 2 /* we have data ready to send */ +#define CON_FLAG_SOCK_CLOSED 3 /* socket state changed to closed */ +#define CON_FLAG_BACKOFF 4 /* need to retry queuing delayed work */ /* static tag bytes (protocol control messages) */ static char tag_msg = CEPH_MSGR_TAG_MSG; @@ -288,7 +297,7 @@ static void ceph_sock_write_space(struct sock *sk) * buffer. See net/ipv4/tcp_input.c:tcp_check_space() * and net/core/stream.c:sk_stream_write_space(). */ - if (test_bit(WRITE_PENDING, &con->flags)) { + if (test_bit(CON_FLAG_WRITE_PENDING, &con->flags)) { if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) { dout("%s %p queueing write work\n", __func__, con); clear_bit(SOCK_NOSPACE, &sk->sk_socket->flags); @@ -313,7 +322,7 @@ static void ceph_sock_state_change(struct sock *sk) case TCP_CLOSE_WAIT: dout("%s TCP_CLOSE_WAIT\n", __func__); con_sock_state_closing(con); - set_bit(SOCK_CLOSED, &con->flags); + set_bit(CON_FLAG_SOCK_CLOSED, &con->flags); queue_con(con); break; case TCP_ESTABLISHED: @@ -449,12 +458,12 @@ static int con_close_socket(struct ceph_connection *con) con->sock = NULL; /* - * Forcibly clear the SOCK_CLOSE flag. It gets set + * Forcibly clear the SOCK_CLOSED flag. It gets set * independent of the connection mutex, and we could have * received a socket close event before we had the chance to * shut the socket down. */ - clear_bit(SOCK_CLOSED, &con->flags); + clear_bit(CON_FLAG_SOCK_CLOSED, &con->flags); con_sock_state_closed(con); return rc; } @@ -516,9 +525,9 @@ void ceph_con_close(struct ceph_connection *con) ceph_pr_addr(&con->peer_addr.in_addr)); con->state = CON_STATE_CLOSED; - clear_bit(LOSSYTX, &con->flags); /* so we retry next connect */ - clear_bit(KEEPALIVE_PENDING, &con->flags); - clear_bit(WRITE_PENDING, &con->flags); + clear_bit(CON_FLAG_LOSSYTX, &con->flags); /* so we retry next connect */ + clear_bit(CON_FLAG_KEEPALIVE_PENDING, &con->flags); + clear_bit(CON_FLAG_WRITE_PENDING, &con->flags); reset_connection(con); con->peer_global_seq = 0; @@ -770,7 +779,7 @@ static void prepare_write_message(struct ceph_connection *con) /* no, queue up footer too and be done */ prepare_write_message_footer(con); - set_bit(WRITE_PENDING, &con->flags); + set_bit(CON_FLAG_WRITE_PENDING, &con->flags); } /* @@ -791,7 +800,7 @@ static void prepare_write_ack(struct ceph_connection *con) &con->out_temp_ack); con->out_more = 1; /* more will follow.. eventually.. */ - set_bit(WRITE_PENDING, &con->flags); + set_bit(CON_FLAG_WRITE_PENDING, &con->flags); } /* @@ -802,7 +811,7 @@ static void prepare_write_keepalive(struct ceph_connection *con) dout("prepare_write_keepalive %p\n", con); con_out_kvec_reset(con); con_out_kvec_add(con, sizeof (tag_keepalive), &tag_keepalive); - set_bit(WRITE_PENDING, &con->flags); + set_bit(CON_FLAG_WRITE_PENDING, &con->flags); } /* @@ -845,7 +854,7 @@ static void prepare_write_banner(struct ceph_connection *con) &con->msgr->my_enc_addr); con->out_more = 0; - set_bit(WRITE_PENDING, &con->flags); + set_bit(CON_FLAG_WRITE_PENDING, &con->flags); } static int prepare_write_connect(struct ceph_connection *con) @@ -896,7 +905,7 @@ static int prepare_write_connect(struct ceph_connection *con) auth->authorizer_buf); con->out_more = 0; - set_bit(WRITE_PENDING, &con->flags); + set_bit(CON_FLAG_WRITE_PENDING, &con->flags); return 0; } @@ -1622,7 +1631,7 @@ static int process_connect(struct ceph_connection *con) le32_to_cpu(con->in_reply.connect_seq)); if (con->in_reply.flags & CEPH_MSG_CONNECT_LOSSY) - set_bit(LOSSYTX, &con->flags); + set_bit(CON_FLAG_LOSSYTX, &con->flags); con->delay = 0; /* reset backoff memory */ @@ -2061,14 +2070,15 @@ do_next: prepare_write_ack(con); goto more; } - if (test_and_clear_bit(KEEPALIVE_PENDING, &con->flags)) { + if (test_and_clear_bit(CON_FLAG_KEEPALIVE_PENDING, + &con->flags)) { prepare_write_keepalive(con); goto more; } } /* Nothing to do! */ - clear_bit(WRITE_PENDING, &con->flags); + clear_bit(CON_FLAG_WRITE_PENDING, &con->flags); dout("try_write nothing else to write.\n"); ret = 0; out: @@ -2241,7 +2251,7 @@ static void con_work(struct work_struct *work) mutex_lock(&con->mutex); restart: - if (test_and_clear_bit(SOCK_CLOSED, &con->flags)) { + if (test_and_clear_bit(CON_FLAG_SOCK_CLOSED, &con->flags)) { switch (con->state) { case CON_STATE_CONNECTING: con->error_msg = "connection failed"; @@ -2260,7 +2270,7 @@ restart: goto fault; } - if (test_and_clear_bit(BACKOFF, &con->flags)) { + if (test_and_clear_bit(CON_FLAG_BACKOFF, &con->flags)) { dout("con_work %p backing off\n", con); if (queue_delayed_work(ceph_msgr_wq, &con->work, round_jiffies_relative(con->delay))) { @@ -2336,7 +2346,7 @@ static void ceph_fault(struct ceph_connection *con) con_close_socket(con); - if (test_bit(LOSSYTX, &con->flags)) { + if (test_bit(CON_FLAG_LOSSYTX, &con->flags)) { dout("fault on LOSSYTX channel, marking CLOSED\n"); con->state = CON_STATE_CLOSED; goto out_unlock; @@ -2356,9 +2366,9 @@ static void ceph_fault(struct ceph_connection *con) /* If there are no messages queued or keepalive pending, place * the connection in a STANDBY state */ if (list_empty(&con->out_queue) && - !test_bit(KEEPALIVE_PENDING, &con->flags)) { + !test_bit(CON_FLAG_KEEPALIVE_PENDING, &con->flags)) { dout("fault %p setting STANDBY clearing WRITE_PENDING\n", con); - clear_bit(WRITE_PENDING, &con->flags); + clear_bit(CON_FLAG_WRITE_PENDING, &con->flags); con->state = CON_STATE_STANDBY; } else { /* retry after a delay. */ @@ -2383,7 +2393,7 @@ static void ceph_fault(struct ceph_connection *con) * that when con_work restarts we schedule the * delay then. */ - set_bit(BACKOFF, &con->flags); + set_bit(CON_FLAG_BACKOFF, &con->flags); } } @@ -2440,8 +2450,8 @@ static void clear_standby(struct ceph_connection *con) dout("clear_standby %p and ++connect_seq\n", con); con->state = CON_STATE_PREOPEN; con->connect_seq++; - WARN_ON(test_bit(WRITE_PENDING, &con->flags)); - WARN_ON(test_bit(KEEPALIVE_PENDING, &con->flags)); + WARN_ON(test_bit(CON_FLAG_WRITE_PENDING, &con->flags)); + WARN_ON(test_bit(CON_FLAG_KEEPALIVE_PENDING, &con->flags)); } } @@ -2482,7 +2492,7 @@ void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg) /* if there wasn't anything waiting to send before, queue * new work */ - if (test_and_set_bit(WRITE_PENDING, &con->flags) == 0) + if (test_and_set_bit(CON_FLAG_WRITE_PENDING, &con->flags) == 0) queue_con(con); } EXPORT_SYMBOL(ceph_con_send); @@ -2571,8 +2581,8 @@ void ceph_con_keepalive(struct ceph_connection *con) mutex_lock(&con->mutex); clear_standby(con); mutex_unlock(&con->mutex); - if (test_and_set_bit(KEEPALIVE_PENDING, &con->flags) == 0 && - test_and_set_bit(WRITE_PENDING, &con->flags) == 0) + if (test_and_set_bit(CON_FLAG_KEEPALIVE_PENDING, &con->flags) == 0 && + test_and_set_bit(CON_FLAG_WRITE_PENDING, &con->flags) == 0) queue_con(con); } EXPORT_SYMBOL(ceph_con_keepalive); -- cgit v1.2.3 From 5accdf82ba25cacefd6c1867f1704beb4d244cdd Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 12 Jun 2012 16:20:34 +0200 Subject: fs: Improve filesystem freezing handling vfs_check_frozen() tests are racy since the filesystem can be frozen just after the test is performed. Thus in write paths we can end up marking some pages or inodes dirty even though the file system is already frozen. This creates problems with flusher thread hanging on frozen filesystem. Another problem is that exclusion between ->page_mkwrite() and filesystem freezing has been handled by setting page dirty and then verifying s_frozen. This guaranteed that either the freezing code sees the faulted page, writes it, and writeprotects it again or we see s_frozen set and bail out of page fault. This works to protect from page being marked writeable while filesystem freezing is running but has an unpleasant artefact of leaving dirty (although unmodified and writeprotected) pages on frozen filesystem resulting in similar problems with flusher thread as the first problem. This patch aims at providing exclusion between write paths and filesystem freezing. We implement a writer-freeze read-write semaphore in the superblock. Actually, there are three such semaphores because of lock ranking reasons - one for page fault handlers (->page_mkwrite), one for all other writers, and one of internal filesystem purposes (used e.g. to track running transactions). Write paths which should block freezing (e.g. directory operations, ->aio_write(), ->page_mkwrite) hold reader side of the semaphore. Code freezing the filesystem takes the writer side. Only that we don't really want to bounce cachelines of the semaphores between CPUs for each write happening. So we implement the reader side of the semaphore as a per-cpu counter and the writer side is implemented using s_writers.frozen superblock field. [AV: microoptimize sb_start_write(); we want it fast in normal case] BugLink: https://bugs.launchpad.net/bugs/897421 Tested-by: Kamal Mostafa Tested-by: Peter M. Petrakis Tested-by: Dann Frazier Tested-by: Massimo Morana Signed-off-by: Jan Kara Signed-off-by: Al Viro --- fs/super.c | 251 ++++++++++++++++++++++++++++++++++++++++++++++++----- include/linux/fs.h | 150 ++++++++++++++++++++++++++++++-- 2 files changed, 373 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/fs/super.c b/fs/super.c index c743fb3be4b8..0f64ecb7b1bf 100644 --- a/fs/super.c +++ b/fs/super.c @@ -33,12 +33,19 @@ #include #include #include +#include #include "internal.h" LIST_HEAD(super_blocks); DEFINE_SPINLOCK(sb_lock); +static char *sb_writers_name[SB_FREEZE_LEVELS] = { + "sb_writers", + "sb_pagefaults", + "sb_internal", +}; + /* * One thing we have to be careful of with a per-sb shrinker is that we don't * drop the last active reference to the superblock from within the shrinker. @@ -102,6 +109,35 @@ static int prune_super(struct shrinker *shrink, struct shrink_control *sc) return total_objects; } +static int init_sb_writers(struct super_block *s, struct file_system_type *type) +{ + int err; + int i; + + for (i = 0; i < SB_FREEZE_LEVELS; i++) { + err = percpu_counter_init(&s->s_writers.counter[i], 0); + if (err < 0) + goto err_out; + lockdep_init_map(&s->s_writers.lock_map[i], sb_writers_name[i], + &type->s_writers_key[i], 0); + } + init_waitqueue_head(&s->s_writers.wait); + init_waitqueue_head(&s->s_writers.wait_unfrozen); + return 0; +err_out: + while (--i >= 0) + percpu_counter_destroy(&s->s_writers.counter[i]); + return err; +} + +static void destroy_sb_writers(struct super_block *s) +{ + int i; + + for (i = 0; i < SB_FREEZE_LEVELS; i++) + percpu_counter_destroy(&s->s_writers.counter[i]); +} + /** * alloc_super - create new superblock * @type: filesystem type superblock should belong to @@ -117,18 +153,19 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags) if (s) { if (security_sb_alloc(s)) { + /* + * We cannot call security_sb_free() without + * security_sb_alloc() succeeding. So bail out manually + */ kfree(s); s = NULL; goto out; } #ifdef CONFIG_SMP s->s_files = alloc_percpu(struct list_head); - if (!s->s_files) { - security_sb_free(s); - kfree(s); - s = NULL; - goto out; - } else { + if (!s->s_files) + goto err_out; + else { int i; for_each_possible_cpu(i) @@ -137,6 +174,8 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags) #else INIT_LIST_HEAD(&s->s_files); #endif + if (init_sb_writers(s, type)) + goto err_out; s->s_flags = flags; s->s_bdi = &default_backing_dev_info; INIT_HLIST_NODE(&s->s_instances); @@ -190,6 +229,16 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags) } out: return s; +err_out: + security_sb_free(s); +#ifdef CONFIG_SMP + if (s->s_files) + free_percpu(s->s_files); +#endif + destroy_sb_writers(s); + kfree(s); + s = NULL; + goto out; } /** @@ -203,6 +252,7 @@ static inline void destroy_super(struct super_block *s) #ifdef CONFIG_SMP free_percpu(s->s_files); #endif + destroy_sb_writers(s); security_sb_free(s); WARN_ON(!list_empty(&s->s_mounts)); kfree(s->s_subtype); @@ -651,10 +701,11 @@ struct super_block *get_super_thawed(struct block_device *bdev) { while (1) { struct super_block *s = get_super(bdev); - if (!s || s->s_frozen == SB_UNFROZEN) + if (!s || s->s_writers.frozen == SB_UNFROZEN) return s; up_read(&s->s_umount); - vfs_check_frozen(s, SB_FREEZE_WRITE); + wait_event(s->s_writers.wait_unfrozen, + s->s_writers.frozen == SB_UNFROZEN); put_super(s); } } @@ -732,7 +783,7 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force) int retval; int remount_ro; - if (sb->s_frozen != SB_UNFROZEN) + if (sb->s_writers.frozen != SB_UNFROZEN) return -EBUSY; #ifdef CONFIG_BLOCK @@ -1163,6 +1214,120 @@ out: return ERR_PTR(error); } +/* + * This is an internal function, please use sb_end_{write,pagefault,intwrite} + * instead. + */ +void __sb_end_write(struct super_block *sb, int level) +{ + percpu_counter_dec(&sb->s_writers.counter[level-1]); + /* + * Make sure s_writers are updated before we wake up waiters in + * freeze_super(). + */ + smp_mb(); + if (waitqueue_active(&sb->s_writers.wait)) + wake_up(&sb->s_writers.wait); + rwsem_release(&sb->s_writers.lock_map[level-1], 1, _RET_IP_); +} +EXPORT_SYMBOL(__sb_end_write); + +#ifdef CONFIG_LOCKDEP +/* + * We want lockdep to tell us about possible deadlocks with freezing but + * it's it bit tricky to properly instrument it. Getting a freeze protection + * works as getting a read lock but there are subtle problems. XFS for example + * gets freeze protection on internal level twice in some cases, which is OK + * only because we already hold a freeze protection also on higher level. Due + * to these cases we have to tell lockdep we are doing trylock when we + * already hold a freeze protection for a higher freeze level. + */ +static void acquire_freeze_lock(struct super_block *sb, int level, bool trylock, + unsigned long ip) +{ + int i; + + if (!trylock) { + for (i = 0; i < level - 1; i++) + if (lock_is_held(&sb->s_writers.lock_map[i])) { + trylock = true; + break; + } + } + rwsem_acquire_read(&sb->s_writers.lock_map[level-1], 0, trylock, ip); +} +#endif + +/* + * This is an internal function, please use sb_start_{write,pagefault,intwrite} + * instead. + */ +int __sb_start_write(struct super_block *sb, int level, bool wait) +{ +retry: + if (unlikely(sb->s_writers.frozen >= level)) { + if (!wait) + return 0; + wait_event(sb->s_writers.wait_unfrozen, + sb->s_writers.frozen < level); + } + +#ifdef CONFIG_LOCKDEP + acquire_freeze_lock(sb, level, !wait, _RET_IP_); +#endif + percpu_counter_inc(&sb->s_writers.counter[level-1]); + /* + * Make sure counter is updated before we check for frozen. + * freeze_super() first sets frozen and then checks the counter. + */ + smp_mb(); + if (unlikely(sb->s_writers.frozen >= level)) { + __sb_end_write(sb, level); + goto retry; + } + return 1; +} +EXPORT_SYMBOL(__sb_start_write); + +/** + * sb_wait_write - wait until all writers to given file system finish + * @sb: the super for which we wait + * @level: type of writers we wait for (normal vs page fault) + * + * This function waits until there are no writers of given type to given file + * system. Caller of this function should make sure there can be no new writers + * of type @level before calling this function. Otherwise this function can + * livelock. + */ +static void sb_wait_write(struct super_block *sb, int level) +{ + s64 writers; + + /* + * We just cycle-through lockdep here so that it does not complain + * about returning with lock to userspace + */ + rwsem_acquire(&sb->s_writers.lock_map[level-1], 0, 0, _THIS_IP_); + rwsem_release(&sb->s_writers.lock_map[level-1], 1, _THIS_IP_); + + do { + DEFINE_WAIT(wait); + + /* + * We use a barrier in prepare_to_wait() to separate setting + * of frozen and checking of the counter + */ + prepare_to_wait(&sb->s_writers.wait, &wait, + TASK_UNINTERRUPTIBLE); + + writers = percpu_counter_sum(&sb->s_writers.counter[level-1]); + if (writers) + schedule(); + + finish_wait(&sb->s_writers.wait, &wait); + } while (writers); +} + /** * freeze_super - lock the filesystem and force it into a consistent state * @sb: the super to lock @@ -1170,6 +1335,31 @@ out: * Syncs the super to make sure the filesystem is consistent and calls the fs's * freeze_fs. Subsequent calls to this without first thawing the fs will return * -EBUSY. + * + * During this function, sb->s_writers.frozen goes through these values: + * + * SB_UNFROZEN: File system is normal, all writes progress as usual. + * + * SB_FREEZE_WRITE: The file system is in the process of being frozen. New + * writes should be blocked, though page faults are still allowed. We wait for + * all writes to complete and then proceed to the next stage. + * + * SB_FREEZE_PAGEFAULT: Freezing continues. Now also page faults are blocked + * but internal fs threads can still modify the filesystem (although they + * should not dirty new pages or inodes), writeback can run etc. After waiting + * for all running page faults we sync the filesystem which will clean all + * dirty pages and inodes (no new dirty pages or inodes can be created when + * sync is running). + * + * SB_FREEZE_FS: The file system is frozen. Now all internal sources of fs + * modification are blocked (e.g. XFS preallocation truncation on inode + * reclaim). This is usually implemented by blocking new transactions for + * filesystems that have them and need this additional guard. After all + * internal writers are finished we call ->freeze_fs() to finish filesystem + * freezing. Then we transition to SB_FREEZE_COMPLETE state. This state is + * mostly auxiliary for filesystems to verify they do not modify frozen fs. + * + * sb->s_writers.frozen is protected by sb->s_umount. */ int freeze_super(struct super_block *sb) { @@ -1177,7 +1367,7 @@ int freeze_super(struct super_block *sb) atomic_inc(&sb->s_active); down_write(&sb->s_umount); - if (sb->s_frozen) { + if (sb->s_writers.frozen != SB_UNFROZEN) { deactivate_locked_super(sb); return -EBUSY; } @@ -1188,33 +1378,53 @@ int freeze_super(struct super_block *sb) } if (sb->s_flags & MS_RDONLY) { - sb->s_frozen = SB_FREEZE_TRANS; - smp_wmb(); + /* Nothing to do really... */ + sb->s_writers.frozen = SB_FREEZE_COMPLETE; up_write(&sb->s_umount); return 0; } - sb->s_frozen = SB_FREEZE_WRITE; + /* From now on, no new normal writers can start */ + sb->s_writers.frozen = SB_FREEZE_WRITE; + smp_wmb(); + + /* Release s_umount to preserve sb_start_write -> s_umount ordering */ + up_write(&sb->s_umount); + + sb_wait_write(sb, SB_FREEZE_WRITE); + + /* Now we go and block page faults... */ + down_write(&sb->s_umount); + sb->s_writers.frozen = SB_FREEZE_PAGEFAULT; smp_wmb(); + sb_wait_write(sb, SB_FREEZE_PAGEFAULT); + + /* All writers are done so after syncing there won't be dirty data */ sync_filesystem(sb); - sb->s_frozen = SB_FREEZE_TRANS; + /* Now wait for internal filesystem counter */ + sb->s_writers.frozen = SB_FREEZE_FS; smp_wmb(); + sb_wait_write(sb, SB_FREEZE_FS); - sync_blockdev(sb->s_bdev); if (sb->s_op->freeze_fs) { ret = sb->s_op->freeze_fs(sb); if (ret) { printk(KERN_ERR "VFS:Filesystem freeze failed\n"); - sb->s_frozen = SB_UNFROZEN; + sb->s_writers.frozen = SB_UNFROZEN; smp_wmb(); - wake_up(&sb->s_wait_unfrozen); + wake_up(&sb->s_writers.wait_unfrozen); deactivate_locked_super(sb); return ret; } } + /* + * This is just for debugging purposes so that fs can warn if it + * sees write activity when frozen is set to SB_FREEZE_COMPLETE. + */ + sb->s_writers.frozen = SB_FREEZE_COMPLETE; up_write(&sb->s_umount); return 0; } @@ -1231,7 +1441,7 @@ int thaw_super(struct super_block *sb) int error; down_write(&sb->s_umount); - if (sb->s_frozen == SB_UNFROZEN) { + if (sb->s_writers.frozen == SB_UNFROZEN) { up_write(&sb->s_umount); return -EINVAL; } @@ -1244,16 +1454,15 @@ int thaw_super(struct super_block *sb) if (error) { printk(KERN_ERR "VFS:Filesystem thaw failed\n"); - sb->s_frozen = SB_FREEZE_TRANS; up_write(&sb->s_umount); return error; } } out: - sb->s_frozen = SB_UNFROZEN; + sb->s_writers.frozen = SB_UNFROZEN; smp_wmb(); - wake_up(&sb->s_wait_unfrozen); + wake_up(&sb->s_writers.wait_unfrozen); deactivate_locked_super(sb); return 0; diff --git a/include/linux/fs.h b/include/linux/fs.h index 80c819cbe272..aefed9426b03 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -412,6 +412,7 @@ struct inodes_stat_t { #include #include #include +#include #include @@ -1439,6 +1440,8 @@ extern void f_delown(struct file *filp); extern pid_t f_getown(struct file *filp); extern int send_sigurg(struct fown_struct *fown); +struct mm_struct; + /* * Umount options */ @@ -1452,6 +1455,32 @@ extern int send_sigurg(struct fown_struct *fown); extern struct list_head super_blocks; extern spinlock_t sb_lock; +/* Possible states of 'frozen' field */ +enum { + SB_UNFROZEN = 0, /* FS is unfrozen */ + SB_FREEZE_WRITE = 1, /* Writes, dir ops, ioctls frozen */ + SB_FREEZE_TRANS = 2, + SB_FREEZE_PAGEFAULT = 2, /* Page faults stopped as well */ + SB_FREEZE_FS = 3, /* For internal FS use (e.g. to stop + * internal threads if needed) */ + SB_FREEZE_COMPLETE = 4, /* ->freeze_fs finished successfully */ +}; + +#define SB_FREEZE_LEVELS (SB_FREEZE_COMPLETE - 1) + +struct sb_writers { + /* Counters for counting writers at each level */ + struct percpu_counter counter[SB_FREEZE_LEVELS]; + wait_queue_head_t wait; /* queue for waiting for + writers / faults to finish */ + int frozen; /* Is sb frozen? */ + wait_queue_head_t wait_unfrozen; /* queue for waiting for + sb to be thawed */ +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lockdep_map lock_map[SB_FREEZE_LEVELS]; +#endif +}; + struct super_block { struct list_head s_list; /* Keep this first */ dev_t s_dev; /* search index; _not_ kdev_t */ @@ -1501,6 +1530,7 @@ struct super_block { int s_frozen; wait_queue_head_t s_wait_unfrozen; + struct sb_writers s_writers; char s_id[32]; /* Informational name */ u8 s_uuid[16]; /* UUID */ @@ -1555,14 +1585,119 @@ extern struct timespec current_fs_time(struct super_block *sb); /* * Snapshotting support. */ -enum { - SB_UNFROZEN = 0, - SB_FREEZE_WRITE = 1, - SB_FREEZE_TRANS = 2, -}; +/* Will go away when all users are converted */ +#define vfs_check_frozen(sb, level) do { } while (0) + +void __sb_end_write(struct super_block *sb, int level); +int __sb_start_write(struct super_block *sb, int level, bool wait); + +/** + * sb_end_write - drop write access to a superblock + * @sb: the super we wrote to + * + * Decrement number of writers to the filesystem. Wake up possible waiters + * wanting to freeze the filesystem. + */ +static inline void sb_end_write(struct super_block *sb) +{ + __sb_end_write(sb, SB_FREEZE_WRITE); +} + +/** + * sb_end_pagefault - drop write access to a superblock from a page fault + * @sb: the super we wrote to + * + * Decrement number of processes handling write page fault to the filesystem. + * Wake up possible waiters wanting to freeze the filesystem. + */ +static inline void sb_end_pagefault(struct super_block *sb) +{ + __sb_end_write(sb, SB_FREEZE_PAGEFAULT); +} + +/** + * sb_end_intwrite - drop write access to a superblock for internal fs purposes + * @sb: the super we wrote to + * + * Decrement fs-internal number of writers to the filesystem. Wake up possible + * waiters wanting to freeze the filesystem. + */ +static inline void sb_end_intwrite(struct super_block *sb) +{ + __sb_end_write(sb, SB_FREEZE_FS); +} + +/** + * sb_start_write - get write access to a superblock + * @sb: the super we write to + * + * When a process wants to write data or metadata to a file system (i.e. dirty + * a page or an inode), it should embed the operation in a sb_start_write() - + * sb_end_write() pair to get exclusion against file system freezing. This + * function increments number of writers preventing freezing. If the file + * system is already frozen, the function waits until the file system is + * thawed. + * + * Since freeze protection behaves as a lock, users have to preserve + * ordering of freeze protection and other filesystem locks. Generally, + * freeze protection should be the outermost lock. In particular, we have: + * + * sb_start_write + * -> i_mutex (write path, truncate, directory ops, ...) + * -> s_umount (freeze_super, thaw_super) + */ +static inline void sb_start_write(struct super_block *sb) +{ + __sb_start_write(sb, SB_FREEZE_WRITE, true); +} + +static inline int sb_start_write_trylock(struct super_block *sb) +{ + return __sb_start_write(sb, SB_FREEZE_WRITE, false); +} + +/** + * sb_start_pagefault - get write access to a superblock from a page fault + * @sb: the super we write to + * + * When a process starts handling write page fault, it should embed the + * operation into sb_start_pagefault() - sb_end_pagefault() pair to get + * exclusion against file system freezing. This is needed since the page fault + * is going to dirty a page. This function increments number of running page + * faults preventing freezing. If the file system is already frozen, the + * function waits until the file system is thawed. + * + * Since page fault freeze protection behaves as a lock, users have to preserve + * ordering of freeze protection and other filesystem locks. It is advised to + * put sb_start_pagefault() close to mmap_sem in lock ordering. Page fault + * handling code implies lock dependency: + * + * mmap_sem + * -> sb_start_pagefault + */ +static inline void sb_start_pagefault(struct super_block *sb) +{ + __sb_start_write(sb, SB_FREEZE_PAGEFAULT, true); +} + +/* + * sb_start_intwrite - get write access to a superblock for internal fs purposes + * @sb: the super we write to + * + * This is the third level of protection against filesystem freezing. It is + * free for use by a filesystem. The only requirement is that it must rank + * below sb_start_pagefault. + * + * For example filesystem can call sb_start_intwrite() when starting a + * transaction which somewhat eases handling of freezing for internal sources + * of filesystem changes (internal fs threads, discarding preallocation on file + * close, etc.). + */ +static inline void sb_start_intwrite(struct super_block *sb) +{ + __sb_start_write(sb, SB_FREEZE_FS, true); +} -#define vfs_check_frozen(sb, level) \ - wait_event((sb)->s_wait_unfrozen, ((sb)->s_frozen < (level))) extern bool inode_owner_or_capable(const struct inode *inode); @@ -1886,6 +2021,7 @@ struct file_system_type { struct lock_class_key s_lock_key; struct lock_class_key s_umount_key; struct lock_class_key s_vfs_rename_key; + struct lock_class_key s_writers_key[SB_FREEZE_LEVELS]; struct lock_class_key i_lock_key; struct lock_class_key i_mutex_key; -- cgit v1.2.3 From d9c95bdd53a8d9116d269c91ce3d151472e6bcd6 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 12 Jun 2012 16:20:47 +0200 Subject: fs: Remove old freezing mechanism Now that all users are converted, we can remove functions, variables, and constants defined by the old freezing mechanism. BugLink: https://bugs.launchpad.net/bugs/897421 Tested-by: Kamal Mostafa Tested-by: Peter M. Petrakis Tested-by: Dann Frazier Tested-by: Massimo Morana Signed-off-by: Jan Kara Signed-off-by: Al Viro --- fs/super.c | 1 - include/linux/fs.h | 5 ----- 2 files changed, 6 deletions(-) (limited to 'include') diff --git a/fs/super.c b/fs/super.c index 0f64ecb7b1bf..a87dc1b1ac92 100644 --- a/fs/super.c +++ b/fs/super.c @@ -217,7 +217,6 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags) mutex_init(&s->s_dquot.dqio_mutex); mutex_init(&s->s_dquot.dqonoff_mutex); init_rwsem(&s->s_dquot.dqptr_sem); - init_waitqueue_head(&s->s_wait_unfrozen); s->s_maxbytes = MAX_NON_LFS; s->s_op = &default_op; s->s_time_gran = 1000000000; diff --git a/include/linux/fs.h b/include/linux/fs.h index aefed9426b03..0f4b79be8717 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1459,7 +1459,6 @@ extern spinlock_t sb_lock; enum { SB_UNFROZEN = 0, /* FS is unfrozen */ SB_FREEZE_WRITE = 1, /* Writes, dir ops, ioctls frozen */ - SB_FREEZE_TRANS = 2, SB_FREEZE_PAGEFAULT = 2, /* Page faults stopped as well */ SB_FREEZE_FS = 3, /* For internal FS use (e.g. to stop * internal threads if needed) */ @@ -1528,8 +1527,6 @@ struct super_block { struct hlist_node s_instances; struct quota_info s_dquot; /* Diskquota specific options */ - int s_frozen; - wait_queue_head_t s_wait_unfrozen; struct sb_writers s_writers; char s_id[32]; /* Informational name */ @@ -1585,8 +1582,6 @@ extern struct timespec current_fs_time(struct super_block *sb); /* * Snapshotting support. */ -/* Will go away when all users are converted */ -#define vfs_check_frozen(sb, level) do { } while (0) void __sb_end_write(struct super_block *sb, int level); int __sb_start_write(struct super_block *sb, int level, bool wait); -- cgit v1.2.3 From 9cbb17508808f8a6bdd83354b61e126ac4fa6fed Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 31 Jul 2012 09:08:14 +0200 Subject: blk: centralize non-request unplug handling. Both md and umem has similar code for getting notified on an blk_finish_plug event. Centralize this code in block/ and allow each driver to provide its distinctive difference. Signed-off-by: NeilBrown Signed-off-by: Jens Axboe --- block/blk-core.c | 25 ++++++++++++++++++++++ drivers/block/umem.c | 35 ++++++------------------------- drivers/md/md.c | 56 +++++--------------------------------------------- drivers/md/md.h | 8 +++++++- include/linux/blkdev.h | 8 ++++++-- 5 files changed, 49 insertions(+), 83 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index dd134d834d58..177ddcf356e6 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -2927,6 +2927,31 @@ static void flush_plug_callbacks(struct blk_plug *plug) } } +struct blk_plug_cb *blk_check_plugged(blk_plug_cb_fn unplug, void *data, + int size) +{ + struct blk_plug *plug = current->plug; + struct blk_plug_cb *cb; + + if (!plug) + return NULL; + + list_for_each_entry(cb, &plug->cb_list, list) + if (cb->callback == unplug && cb->data == data) + return cb; + + /* Not currently on the callback list */ + BUG_ON(size < sizeof(*cb)); + cb = kzalloc(size, GFP_ATOMIC); + if (cb) { + cb->data = data; + cb->callback = unplug; + list_add(&cb->list, &plug->cb_list); + } + return cb; +} +EXPORT_SYMBOL(blk_check_plugged); + void blk_flush_plug_list(struct blk_plug *plug, bool from_schedule) { struct request_queue *q; diff --git a/drivers/block/umem.c b/drivers/block/umem.c index 9a72277a31df..6ef3489568e3 100644 --- a/drivers/block/umem.c +++ b/drivers/block/umem.c @@ -513,42 +513,19 @@ static void process_page(unsigned long data) } } -struct mm_plug_cb { - struct blk_plug_cb cb; - struct cardinfo *card; -}; - static void mm_unplug(struct blk_plug_cb *cb) { - struct mm_plug_cb *mmcb = container_of(cb, struct mm_plug_cb, cb); + struct cardinfo *card = cb->data; - spin_lock_irq(&mmcb->card->lock); - activate(mmcb->card); - spin_unlock_irq(&mmcb->card->lock); - kfree(mmcb); + spin_lock_irq(&card->lock); + activate(card); + spin_unlock_irq(&card->lock); + kfree(cb); } static int mm_check_plugged(struct cardinfo *card) { - struct blk_plug *plug = current->plug; - struct mm_plug_cb *mmcb; - - if (!plug) - return 0; - - list_for_each_entry(mmcb, &plug->cb_list, cb.list) { - if (mmcb->cb.callback == mm_unplug && mmcb->card == card) - return 1; - } - /* Not currently on the callback list */ - mmcb = kmalloc(sizeof(*mmcb), GFP_ATOMIC); - if (!mmcb) - return 0; - - mmcb->card = card; - mmcb->cb.callback = mm_unplug; - list_add(&mmcb->cb.list, &plug->cb_list); - return 1; + return !!blk_check_plugged(mm_unplug, card, sizeof(struct blk_plug_cb)); } static void mm_make_request(struct request_queue *q, struct bio *bio) diff --git a/drivers/md/md.c b/drivers/md/md.c index 34381172a947..b493fa417387 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -498,59 +498,13 @@ void md_flush_request(struct mddev *mddev, struct bio *bio) } EXPORT_SYMBOL(md_flush_request); -/* Support for plugging. - * This mirrors the plugging support in request_queue, but does not - * require having a whole queue or request structures. - * We allocate an md_plug_cb for each md device and each thread it gets - * plugged on. This links tot the private plug_handle structure in the - * personality data where we keep a count of the number of outstanding - * plugs so other code can see if a plug is active. - */ -struct md_plug_cb { - struct blk_plug_cb cb; - struct mddev *mddev; -}; - -static void plugger_unplug(struct blk_plug_cb *cb) +void md_unplug(struct blk_plug_cb *cb) { - struct md_plug_cb *mdcb = container_of(cb, struct md_plug_cb, cb); - md_wakeup_thread(mdcb->mddev->thread); - kfree(mdcb); -} - -/* Check that an unplug wakeup will come shortly. - * If not, wakeup the md thread immediately - */ -int mddev_check_plugged(struct mddev *mddev) -{ - struct blk_plug *plug = current->plug; - struct md_plug_cb *mdcb; - - if (!plug) - return 0; - - list_for_each_entry(mdcb, &plug->cb_list, cb.list) { - if (mdcb->cb.callback == plugger_unplug && - mdcb->mddev == mddev) { - /* Already on the list, move to top */ - if (mdcb != list_first_entry(&plug->cb_list, - struct md_plug_cb, - cb.list)) - list_move(&mdcb->cb.list, &plug->cb_list); - return 1; - } - } - /* Not currently on the callback list */ - mdcb = kmalloc(sizeof(*mdcb), GFP_ATOMIC); - if (!mdcb) - return 0; - - mdcb->mddev = mddev; - mdcb->cb.callback = plugger_unplug; - list_add(&mdcb->cb.list, &plug->cb_list); - return 1; + struct mddev *mddev = cb->data; + md_wakeup_thread(mddev->thread); + kfree(cb); } -EXPORT_SYMBOL_GPL(mddev_check_plugged); +EXPORT_SYMBOL(md_unplug); static inline struct mddev *mddev_get(struct mddev *mddev) { diff --git a/drivers/md/md.h b/drivers/md/md.h index 91786c46b85c..8f998e08fb87 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -627,6 +627,12 @@ extern struct bio *bio_clone_mddev(struct bio *bio, gfp_t gfp_mask, struct mddev *mddev); extern struct bio *bio_alloc_mddev(gfp_t gfp_mask, int nr_iovecs, struct mddev *mddev); -extern int mddev_check_plugged(struct mddev *mddev); extern void md_trim_bio(struct bio *bio, int offset, int size); + +extern void md_unplug(struct blk_plug_cb *cb); +static inline int mddev_check_plugged(struct mddev *mddev) +{ + return !!blk_check_plugged(md_unplug, mddev, + sizeof(struct blk_plug_cb)); +} #endif /* _MD_MD_H */ diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 3816ce8a08fc..607ca228f47e 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -922,11 +922,15 @@ struct blk_plug { }; #define BLK_MAX_REQUEST_COUNT 16 +struct blk_plug_cb; +typedef void (*blk_plug_cb_fn)(struct blk_plug_cb *); struct blk_plug_cb { struct list_head list; - void (*callback)(struct blk_plug_cb *); + blk_plug_cb_fn callback; + void *data; }; - +extern struct blk_plug_cb *blk_check_plugged(blk_plug_cb_fn unplug, + void *data, int size); extern void blk_start_plug(struct blk_plug *); extern void blk_finish_plug(struct blk_plug *); extern void blk_flush_plug_list(struct blk_plug *, bool); -- cgit v1.2.3 From 74018dc3063a2c729fc73041c0a9f03aac995920 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 31 Jul 2012 09:08:15 +0200 Subject: blk: pass from_schedule to non-request unplug functions. This will allow md/raid to know why the unplug was called, and will be able to act according - if !from_schedule it is safe to perform tasks which could themselves schedule. Signed-off-by: NeilBrown Signed-off-by: Jens Axboe --- block/blk-core.c | 6 +++--- drivers/block/umem.c | 2 +- drivers/md/md.c | 2 +- drivers/md/md.h | 2 +- include/linux/blkdev.h | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index 35bf4fe8234c..4b4dbdfbca89 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -2909,7 +2909,7 @@ static void queue_unplugged(struct request_queue *q, unsigned int depth, } -static void flush_plug_callbacks(struct blk_plug *plug) +static void flush_plug_callbacks(struct blk_plug *plug, bool from_schedule) { LIST_HEAD(callbacks); @@ -2921,7 +2921,7 @@ static void flush_plug_callbacks(struct blk_plug *plug) struct blk_plug_cb, list); list_del(&cb->list); - cb->callback(cb); + cb->callback(cb, from_schedule); } } } @@ -2961,7 +2961,7 @@ void blk_flush_plug_list(struct blk_plug *plug, bool from_schedule) BUG_ON(plug->magic != PLUG_MAGIC); - flush_plug_callbacks(plug); + flush_plug_callbacks(plug, from_schedule); if (list_empty(&plug->list)) return; diff --git a/drivers/block/umem.c b/drivers/block/umem.c index 6ef3489568e3..eb0d8216f557 100644 --- a/drivers/block/umem.c +++ b/drivers/block/umem.c @@ -513,7 +513,7 @@ static void process_page(unsigned long data) } } -static void mm_unplug(struct blk_plug_cb *cb) +static void mm_unplug(struct blk_plug_cb *cb, bool from_schedule) { struct cardinfo *card = cb->data; diff --git a/drivers/md/md.c b/drivers/md/md.c index b493fa417387..db02d2efb76f 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -498,7 +498,7 @@ void md_flush_request(struct mddev *mddev, struct bio *bio) } EXPORT_SYMBOL(md_flush_request); -void md_unplug(struct blk_plug_cb *cb) +void md_unplug(struct blk_plug_cb *cb, bool from_schedule) { struct mddev *mddev = cb->data; md_wakeup_thread(mddev->thread); diff --git a/drivers/md/md.h b/drivers/md/md.h index 8f998e08fb87..f385b038589d 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -629,7 +629,7 @@ extern struct bio *bio_alloc_mddev(gfp_t gfp_mask, int nr_iovecs, struct mddev *mddev); extern void md_trim_bio(struct bio *bio, int offset, int size); -extern void md_unplug(struct blk_plug_cb *cb); +extern void md_unplug(struct blk_plug_cb *cb, bool from_schedule); static inline int mddev_check_plugged(struct mddev *mddev) { return !!blk_check_plugged(md_unplug, mddev, diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 607ca228f47e..4e72a9d48232 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -923,7 +923,7 @@ struct blk_plug { #define BLK_MAX_REQUEST_COUNT 16 struct blk_plug_cb; -typedef void (*blk_plug_cb_fn)(struct blk_plug_cb *); +typedef void (*blk_plug_cb_fn)(struct blk_plug_cb *, bool); struct blk_plug_cb { struct list_head list; blk_plug_cb_fn callback; -- cgit v1.2.3 From 7bedaa5537604f34d1d63c5ec7891e559d2a61ed Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 13 Apr 2012 12:10:24 +0100 Subject: dmaengine: add OMAP DMA engine driver Tested-by: Tony Lindgren Signed-off-by: Russell King --- drivers/dma/Kconfig | 6 + drivers/dma/Makefile | 1 + drivers/dma/omap-dma.c | 524 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/omap-dma.h | 22 ++ 4 files changed, 553 insertions(+) create mode 100644 drivers/dma/omap-dma.c create mode 100644 include/linux/omap-dma.h (limited to 'include') diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index eb2b60e8e1cb..8be3bf68c0bd 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -261,6 +261,12 @@ config DMA_SA11X0 SA-1110 SoCs. This DMA engine can only be used with on-chip devices. +config DMA_OMAP + tristate "OMAP DMA support" + depends on ARCH_OMAP + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + config DMA_ENGINE bool diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index fc05f7ddac7e..ddc291a2116a 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -29,3 +29,4 @@ obj-$(CONFIG_PCH_DMA) += pch_dma.o obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o +obj-$(CONFIG_DMA_OMAP) += omap-dma.o diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c new file mode 100644 index 000000000000..fe33ddd8eb0f --- /dev/null +++ b/drivers/dma/omap-dma.c @@ -0,0 +1,524 @@ +/* + * OMAP DMAengine support + * + * 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 "virt-dma.h" +#include + +struct omap_dmadev { + struct dma_device ddev; + spinlock_t lock; + struct tasklet_struct task; + struct list_head pending; +}; + +struct omap_chan { + struct virt_dma_chan vc; + struct list_head node; + + struct dma_slave_config cfg; + unsigned dma_sig; + + int dma_ch; + struct omap_desc *desc; + unsigned sgidx; +}; + +struct omap_sg { + dma_addr_t addr; + uint32_t en; /* number of elements (24-bit) */ + uint32_t fn; /* number of frames (16-bit) */ +}; + +struct omap_desc { + struct virt_dma_desc vd; + enum dma_transfer_direction dir; + dma_addr_t dev_addr; + + uint8_t es; /* OMAP_DMA_DATA_TYPE_xxx */ + uint8_t sync_mode; /* OMAP_DMA_SYNC_xxx */ + uint8_t sync_type; /* OMAP_DMA_xxx_SYNC* */ + uint8_t periph_port; /* Peripheral port */ + + unsigned sglen; + struct omap_sg sg[0]; +}; + +static const unsigned es_bytes[] = { + [OMAP_DMA_DATA_TYPE_S8] = 1, + [OMAP_DMA_DATA_TYPE_S16] = 2, + [OMAP_DMA_DATA_TYPE_S32] = 4, +}; + +static inline struct omap_dmadev *to_omap_dma_dev(struct dma_device *d) +{ + return container_of(d, struct omap_dmadev, ddev); +} + +static inline struct omap_chan *to_omap_dma_chan(struct dma_chan *c) +{ + return container_of(c, struct omap_chan, vc.chan); +} + +static inline struct omap_desc *to_omap_dma_desc(struct dma_async_tx_descriptor *t) +{ + return container_of(t, struct omap_desc, vd.tx); +} + +static void omap_dma_desc_free(struct virt_dma_desc *vd) +{ + kfree(container_of(vd, struct omap_desc, vd)); +} + +static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d, + unsigned idx) +{ + struct omap_sg *sg = d->sg + idx; + + if (d->dir == DMA_DEV_TO_MEM) + omap_set_dma_dest_params(c->dma_ch, OMAP_DMA_PORT_EMIFF, + OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0); + else + omap_set_dma_src_params(c->dma_ch, OMAP_DMA_PORT_EMIFF, + OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0); + + omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn, + d->sync_mode, c->dma_sig, d->sync_type); + + omap_start_dma(c->dma_ch); +} + +static void omap_dma_start_desc(struct omap_chan *c) +{ + struct virt_dma_desc *vd = vchan_next_desc(&c->vc); + struct omap_desc *d; + + if (!vd) { + c->desc = NULL; + return; + } + + list_del(&vd->node); + + c->desc = d = to_omap_dma_desc(&vd->tx); + c->sgidx = 0; + + if (d->dir == DMA_DEV_TO_MEM) + omap_set_dma_src_params(c->dma_ch, d->periph_port, + OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, 0); + else + omap_set_dma_dest_params(c->dma_ch, d->periph_port, + OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, 0); + + omap_dma_start_sg(c, d, 0); +} + +static void omap_dma_callback(int ch, u16 status, void *data) +{ + struct omap_chan *c = data; + struct omap_desc *d; + unsigned long flags; + + spin_lock_irqsave(&c->vc.lock, flags); + d = c->desc; + if (d) { + if (++c->sgidx < d->sglen) { + omap_dma_start_sg(c, d, c->sgidx); + } else { + omap_dma_start_desc(c); + vchan_cookie_complete(&d->vd); + } + } + spin_unlock_irqrestore(&c->vc.lock, flags); +} + +/* + * This callback schedules all pending channels. We could be more + * clever here by postponing allocation of the real DMA channels to + * this point, and freeing them when our virtual channel becomes idle. + * + * We would then need to deal with 'all channels in-use' + */ +static void omap_dma_sched(unsigned long data) +{ + struct omap_dmadev *d = (struct omap_dmadev *)data; + LIST_HEAD(head); + + spin_lock_irq(&d->lock); + list_splice_tail_init(&d->pending, &head); + spin_unlock_irq(&d->lock); + + while (!list_empty(&head)) { + struct omap_chan *c = list_first_entry(&head, + struct omap_chan, node); + + spin_lock_irq(&c->vc.lock); + list_del_init(&c->node); + omap_dma_start_desc(c); + spin_unlock_irq(&c->vc.lock); + } +} + +static int omap_dma_alloc_chan_resources(struct dma_chan *chan) +{ + struct omap_chan *c = to_omap_dma_chan(chan); + + dev_info(c->vc.chan.device->dev, "allocating channel for %u\n", c->dma_sig); + + return omap_request_dma(c->dma_sig, "DMA engine", + omap_dma_callback, c, &c->dma_ch); +} + +static void omap_dma_free_chan_resources(struct dma_chan *chan) +{ + struct omap_chan *c = to_omap_dma_chan(chan); + + vchan_free_chan_resources(&c->vc); + omap_free_dma(c->dma_ch); + + dev_info(c->vc.chan.device->dev, "freeing channel for %u\n", c->dma_sig); +} + +static enum dma_status omap_dma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *txstate) +{ + /* + * FIXME: do we need to return pending bytes? + * We have no users of that info at the moment... + */ + return dma_cookie_status(chan, cookie, txstate); +} + +static void omap_dma_issue_pending(struct dma_chan *chan) +{ + struct omap_chan *c = to_omap_dma_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&c->vc.lock, flags); + if (vchan_issue_pending(&c->vc) && !c->desc) { + struct omap_dmadev *d = to_omap_dma_dev(chan->device); + spin_lock(&d->lock); + if (list_empty(&c->node)) + list_add_tail(&c->node, &d->pending); + spin_unlock(&d->lock); + tasklet_schedule(&d->task); + } + spin_unlock_irqrestore(&c->vc.lock, flags); +} + +static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg( + struct dma_chan *chan, struct scatterlist *sgl, unsigned sglen, + enum dma_transfer_direction dir, unsigned long tx_flags, void *context) +{ + struct omap_chan *c = to_omap_dma_chan(chan); + enum dma_slave_buswidth dev_width; + struct scatterlist *sgent; + struct omap_desc *d; + dma_addr_t dev_addr; + unsigned i, j = 0, es, en, frame_bytes, sync_type; + u32 burst; + + if (dir == DMA_DEV_TO_MEM) { + dev_addr = c->cfg.src_addr; + dev_width = c->cfg.src_addr_width; + burst = c->cfg.src_maxburst; + sync_type = OMAP_DMA_SRC_SYNC; + } else if (dir == DMA_MEM_TO_DEV) { + dev_addr = c->cfg.dst_addr; + dev_width = c->cfg.dst_addr_width; + burst = c->cfg.dst_maxburst; + sync_type = OMAP_DMA_DST_SYNC; + } else { + dev_err(chan->device->dev, "%s: bad direction?\n", __func__); + return NULL; + } + + /* Bus width translates to the element size (ES) */ + switch (dev_width) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + es = OMAP_DMA_DATA_TYPE_S8; + break; + case DMA_SLAVE_BUSWIDTH_2_BYTES: + es = OMAP_DMA_DATA_TYPE_S16; + break; + case DMA_SLAVE_BUSWIDTH_4_BYTES: + es = OMAP_DMA_DATA_TYPE_S32; + break; + default: /* not reached */ + return NULL; + } + + /* Now allocate and setup the descriptor. */ + d = kzalloc(sizeof(*d) + sglen * sizeof(d->sg[0]), GFP_ATOMIC); + if (!d) + return NULL; + + d->dir = dir; + d->dev_addr = dev_addr; + d->es = es; + d->sync_mode = OMAP_DMA_SYNC_FRAME; + d->sync_type = sync_type; + d->periph_port = OMAP_DMA_PORT_TIPB; + + /* + * Build our scatterlist entries: each contains the address, + * the number of elements (EN) in each frame, and the number of + * frames (FN). Number of bytes for this entry = ES * EN * FN. + * + * Burst size translates to number of elements with frame sync. + * Note: DMA engine defines burst to be the number of dev-width + * transfers. + */ + en = burst; + frame_bytes = es_bytes[es] * en; + for_each_sg(sgl, sgent, sglen, i) { + d->sg[j].addr = sg_dma_address(sgent); + d->sg[j].en = en; + d->sg[j].fn = sg_dma_len(sgent) / frame_bytes; + j++; + } + + d->sglen = j; + + return vchan_tx_prep(&c->vc, &d->vd, tx_flags); +} + +static int omap_dma_slave_config(struct omap_chan *c, struct dma_slave_config *cfg) +{ + if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES || + cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) + return -EINVAL; + + memcpy(&c->cfg, cfg, sizeof(c->cfg)); + + return 0; +} + +static int omap_dma_terminate_all(struct omap_chan *c) +{ + struct omap_dmadev *d = to_omap_dma_dev(c->vc.chan.device); + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&c->vc.lock, flags); + + /* Prevent this channel being scheduled */ + spin_lock(&d->lock); + list_del_init(&c->node); + spin_unlock(&d->lock); + + /* + * Stop DMA activity: we assume the callback will not be called + * after omap_stop_dma() returns (even if it does, it will see + * c->desc is NULL and exit.) + */ + if (c->desc) { + c->desc = NULL; + omap_stop_dma(c->dma_ch); + } + + vchan_get_all_descriptors(&c->vc, &head); + spin_unlock_irqrestore(&c->vc.lock, flags); + vchan_dma_desc_free_list(&c->vc, &head); + + return 0; +} + +static int omap_dma_pause(struct omap_chan *c) +{ + /* FIXME: not supported by platform private API */ + return -EINVAL; +} + +static int omap_dma_resume(struct omap_chan *c) +{ + /* FIXME: not supported by platform private API */ + return -EINVAL; +} + +static int omap_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, + unsigned long arg) +{ + struct omap_chan *c = to_omap_dma_chan(chan); + int ret; + + switch (cmd) { + case DMA_SLAVE_CONFIG: + ret = omap_dma_slave_config(c, (struct dma_slave_config *)arg); + break; + + case DMA_TERMINATE_ALL: + ret = omap_dma_terminate_all(c); + break; + + case DMA_PAUSE: + ret = omap_dma_pause(c); + break; + + case DMA_RESUME: + ret = omap_dma_resume(c); + break; + + default: + ret = -ENXIO; + break; + } + + return ret; +} + +static int omap_dma_chan_init(struct omap_dmadev *od, int dma_sig) +{ + struct omap_chan *c; + + c = kzalloc(sizeof(*c), GFP_KERNEL); + if (!c) + return -ENOMEM; + + c->dma_sig = dma_sig; + c->vc.desc_free = omap_dma_desc_free; + vchan_init(&c->vc, &od->ddev); + INIT_LIST_HEAD(&c->node); + + od->ddev.chancnt++; + + return 0; +} + +static void omap_dma_free(struct omap_dmadev *od) +{ + tasklet_kill(&od->task); + while (!list_empty(&od->ddev.channels)) { + struct omap_chan *c = list_first_entry(&od->ddev.channels, + struct omap_chan, vc.chan.device_node); + + list_del(&c->vc.chan.device_node); + tasklet_kill(&c->vc.task); + kfree(c); + } + kfree(od); +} + +static int omap_dma_probe(struct platform_device *pdev) +{ + struct omap_dmadev *od; + int rc, i; + + od = kzalloc(sizeof(*od), GFP_KERNEL); + if (!od) + return -ENOMEM; + + dma_cap_set(DMA_SLAVE, od->ddev.cap_mask); + od->ddev.device_alloc_chan_resources = omap_dma_alloc_chan_resources; + od->ddev.device_free_chan_resources = omap_dma_free_chan_resources; + od->ddev.device_tx_status = omap_dma_tx_status; + od->ddev.device_issue_pending = omap_dma_issue_pending; + od->ddev.device_prep_slave_sg = omap_dma_prep_slave_sg; + od->ddev.device_control = omap_dma_control; + od->ddev.dev = &pdev->dev; + INIT_LIST_HEAD(&od->ddev.channels); + INIT_LIST_HEAD(&od->pending); + spin_lock_init(&od->lock); + + tasklet_init(&od->task, omap_dma_sched, (unsigned long)od); + + for (i = 0; i < 127; i++) { + rc = omap_dma_chan_init(od, i); + if (rc) { + omap_dma_free(od); + return rc; + } + } + + rc = dma_async_device_register(&od->ddev); + if (rc) { + pr_warn("OMAP-DMA: failed to register slave DMA engine device: %d\n", + rc); + omap_dma_free(od); + } else { + platform_set_drvdata(pdev, od); + } + + dev_info(&pdev->dev, "OMAP DMA engine driver\n"); + + return rc; +} + +static int omap_dma_remove(struct platform_device *pdev) +{ + struct omap_dmadev *od = platform_get_drvdata(pdev); + + dma_async_device_unregister(&od->ddev); + omap_dma_free(od); + + return 0; +} + +static struct platform_driver omap_dma_driver = { + .probe = omap_dma_probe, + .remove = omap_dma_remove, + .driver = { + .name = "omap-dma-engine", + .owner = THIS_MODULE, + }, +}; + +bool omap_dma_filter_fn(struct dma_chan *chan, void *param) +{ + if (chan->device->dev->driver == &omap_dma_driver.driver) { + struct omap_chan *c = to_omap_dma_chan(chan); + unsigned req = *(unsigned *)param; + + return req == c->dma_sig; + } + return false; +} +EXPORT_SYMBOL_GPL(omap_dma_filter_fn); + +static struct platform_device *pdev; + +static const struct platform_device_info omap_dma_dev_info = { + .name = "omap-dma-engine", + .id = -1, + .dma_mask = DMA_BIT_MASK(32), +}; + +static int omap_dma_init(void) +{ + int rc = platform_driver_register(&omap_dma_driver); + + if (rc == 0) { + pdev = platform_device_register_full(&omap_dma_dev_info); + if (IS_ERR(pdev)) { + platform_driver_unregister(&omap_dma_driver); + rc = PTR_ERR(pdev); + } + } + return rc; +} +subsys_initcall(omap_dma_init); + +static void __exit omap_dma_exit(void) +{ + platform_device_unregister(pdev); + platform_driver_unregister(&omap_dma_driver); +} +module_exit(omap_dma_exit); + +MODULE_AUTHOR("Russell King"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/omap-dma.h b/include/linux/omap-dma.h new file mode 100644 index 000000000000..eb475a8ea25b --- /dev/null +++ b/include/linux/omap-dma.h @@ -0,0 +1,22 @@ +/* + * OMAP DMA Engine support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __LINUX_OMAP_DMA_H +#define __LINUX_OMAP_DMA_H + +struct dma_chan; + +#if defined(CONFIG_DMA_OMAP) || defined(CONFIG_DMA_OMAP_MODULE) +bool omap_dma_filter_fn(struct dma_chan *, void *); +#else +static inline bool omap_dma_filter_fn(struct dma_chan *c, void *d) +{ + return false; +} +#endif + +#endif -- cgit v1.2.3 From 4b1bf5871f7d59de6484cc887e205d6d2f1e6fbd Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 31 Jul 2012 04:39:30 -0700 Subject: thermal: Constify 'type' argument for the registration routine thermal_zone_device_register() does not modify 'type' argument, so it is safe to declare it as const. Otherwise, if we pass a const string, we are getting the ugly warning: CC drivers/power/power_supply_core.o drivers/power/power_supply_core.c: In function 'psy_register_thermal': drivers/power/power_supply_core.c:204:6: warning: passing argument 1 of 'thermal_zone_device_register' discards 'const' qualifier from pointer target type [enabled by default] include/linux/thermal.h:140:29: note: expected 'char *' but argument is of type 'const char *' Signed-off-by: Anton Vorontsov Acked-by: Jean Delvare --- drivers/thermal/thermal_sys.c | 2 +- include/linux/thermal.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 5feb3353213f..32aa66d7d6b3 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -1173,7 +1173,7 @@ static void remove_trip_attrs(struct thermal_zone_device *tz) * longer needed. The passive cooling formula uses tc1 and tc2 as described in * section 11.1.5.1 of the ACPI specification 3.0. */ -struct thermal_zone_device *thermal_zone_device_register(char *type, +struct thermal_zone_device *thermal_zone_device_register(const char *type, int trips, int mask, void *devdata, const struct thermal_zone_device_ops *ops, int tc1, int tc2, int passive_delay, int polling_delay) diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 6eaf9146c847..4821735f3e77 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -146,7 +146,7 @@ enum { }; #define THERMAL_GENL_CMD_MAX (__THERMAL_GENL_CMD_MAX - 1) -struct thermal_zone_device *thermal_zone_device_register(char *, int, int, +struct thermal_zone_device *thermal_zone_device_register(const char *, int, int, void *, const struct thermal_zone_device_ops *, int tc1, int tc2, int passive_freq, int polling_freq); void thermal_zone_device_unregister(struct thermal_zone_device *); -- cgit v1.2.3 From 0f26d0e0a715556270d85b7946b99546a2f92888 Mon Sep 17 00:00:00 2001 From: Jason Wessel Date: Mon, 30 Jul 2012 22:44:41 -0500 Subject: kdb: Remove unused KDB_FLAG_ONLY_DO_DUMP This code cleanup was missed in the original kdb merge, and this code is simply not used at all. The code that was previously used to set the KDB_FLAG_ONLY_DO_DUMP was removed prior to the initial kdb merge. Signed-off-by: Jason Wessel --- include/linux/kdb.h | 2 -- kernel/debug/kdb/kdb_main.c | 15 +-------------- 2 files changed, 1 insertion(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/kdb.h b/include/linux/kdb.h index 064725854db8..42d9e863a313 100644 --- a/include/linux/kdb.h +++ b/include/linux/kdb.h @@ -75,8 +75,6 @@ extern const char *kdb_diemsg; #define KDB_FLAG_CATASTROPHIC (1 << 1) /* A catastrophic event has occurred */ #define KDB_FLAG_CMD_INTERRUPT (1 << 2) /* Previous command was interrupted */ #define KDB_FLAG_NOIPI (1 << 3) /* Do not send IPIs */ -#define KDB_FLAG_ONLY_DO_DUMP (1 << 4) /* Only do a dump, used when - * kdb is off */ #define KDB_FLAG_NO_CONSOLE (1 << 5) /* No console is available, * kdb is disabled */ #define KDB_FLAG_NO_VT_CONSOLE (1 << 6) /* No VT console is available, do diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c index 1f91413edb87..31df1706b9a9 100644 --- a/kernel/debug/kdb/kdb_main.c +++ b/kernel/debug/kdb/kdb_main.c @@ -139,11 +139,10 @@ static const int __nkdb_err = sizeof(kdbmsgs) / sizeof(kdbmsg_t); static char *__env[] = { #if defined(CONFIG_SMP) "PROMPT=[%d]kdb> ", - "MOREPROMPT=[%d]more> ", #else "PROMPT=kdb> ", - "MOREPROMPT=more> ", #endif + "MOREPROMPT=more> ", "RADIX=16", "MDCOUNT=8", /* lines of md output */ KDB_PLATFORM_ENV, @@ -1236,18 +1235,6 @@ static int kdb_local(kdb_reason_t reason, int error, struct pt_regs *regs, *cmdbuf = '\0'; *(cmd_hist[cmd_head]) = '\0'; - if (KDB_FLAG(ONLY_DO_DUMP)) { - /* kdb is off but a catastrophic error requires a dump. - * Take the dump and reboot. - * Turn on logging so the kdb output appears in the log - * buffer in the dump. - */ - const char *setargs[] = { "set", "LOGGING", "1" }; - kdb_set(2, setargs); - kdb_reboot(0, NULL); - /*NOTREACHED*/ - } - do_full_getstr: #if defined(CONFIG_SMP) snprintf(kdb_prompt_str, CMD_BUFLEN, kdbgetenv("PROMPT"), -- cgit v1.2.3 From 8f88731d052d2b14f332249a9332690ac1b365ac Mon Sep 17 00:00:00 2001 From: Bryan Wu Date: Sat, 25 Jun 2011 18:33:50 +0800 Subject: led-triggers: create a trigger for CPU activity Attempting to consolidate the ARM LED code, this removes the custom RealView LED trigger code to turn LEDs on and off in response to CPU activity and replace it with a standard trigger. (bryan.wu@canonical.com: It introduces several syscore stubs into this trigger. It also provides ledtrig_cpu trigger event stub in . Although it was inspired by ARM work, it can be used in other arch.) Cc: Richard Purdie Signed-off-by: Linus Walleij Signed-off-by: Bryan Wu Reviewed-by: Jamie Iles Tested-by: Jochen Friedrich --- drivers/leds/Kconfig | 10 +++ drivers/leds/Makefile | 1 + drivers/leds/ledtrig-cpu.c | 163 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/leds.h | 16 +++++ 4 files changed, 190 insertions(+) create mode 100644 drivers/leds/ledtrig-cpu.c (limited to 'include') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 12b2b55c519e..c96fd29199da 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -469,6 +469,16 @@ config LEDS_TRIGGER_BACKLIGHT If unsure, say N. +config LEDS_TRIGGER_CPU + bool "LED CPU Trigger" + depends on LEDS_TRIGGERS + help + This allows LEDs to be controlled by active CPUs. This shows + the active CPUs across an array of LEDs so you can see which + CPUs are active on the system at any given moment. + + If unsure, say N. + config LEDS_TRIGGER_GPIO tristate "LED GPIO Trigger" depends on LEDS_TRIGGERS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index f8958cd6cf6e..fa0f53626618 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -57,5 +57,6 @@ obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.o +obj-$(CONFIG_LEDS_TRIGGER_CPU) += ledtrig-cpu.o obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT) += ledtrig-transient.o diff --git a/drivers/leds/ledtrig-cpu.c b/drivers/leds/ledtrig-cpu.c new file mode 100644 index 000000000000..b312056da14d --- /dev/null +++ b/drivers/leds/ledtrig-cpu.c @@ -0,0 +1,163 @@ +/* + * ledtrig-cpu.c - LED trigger based on CPU activity + * + * This LED trigger will be registered for each possible CPU and named as + * cpu0, cpu1, cpu2, cpu3, etc. + * + * It can be bound to any LED just like other triggers using either a + * board file or via sysfs interface. + * + * An API named ledtrig_cpu is exported for any user, who want to add CPU + * activity indication in their code + * + * Copyright 2011 Linus Walleij + * Copyright 2011 - 2012 Bryan Wu + * + * 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 "leds.h" + +#define MAX_NAME_LEN 8 + +struct led_trigger_cpu { + char name[MAX_NAME_LEN]; + struct led_trigger *_trig; + struct mutex lock; + int lock_is_inited; +}; + +static DEFINE_PER_CPU(struct led_trigger_cpu, cpu_trig); + +/** + * ledtrig_cpu - emit a CPU event as a trigger + * @evt: CPU event to be emitted + * + * Emit a CPU event on a CPU core, which will trigger a + * binded LED to turn on or turn off. + */ +void ledtrig_cpu(enum cpu_led_event ledevt) +{ + struct led_trigger_cpu *trig = &__get_cpu_var(cpu_trig); + + /* mutex lock should be initialized before calling mutex_call() */ + if (!trig->lock_is_inited) + return; + + mutex_lock(&trig->lock); + + /* Locate the correct CPU LED */ + switch (ledevt) { + case CPU_LED_IDLE_END: + case CPU_LED_START: + /* Will turn the LED on, max brightness */ + led_trigger_event(trig->_trig, LED_FULL); + break; + + case CPU_LED_IDLE_START: + case CPU_LED_STOP: + case CPU_LED_HALTED: + /* Will turn the LED off */ + led_trigger_event(trig->_trig, LED_OFF); + break; + + default: + /* Will leave the LED as it is */ + break; + } + + mutex_unlock(&trig->lock); +} +EXPORT_SYMBOL(ledtrig_cpu); + +static int ledtrig_cpu_syscore_suspend(void) +{ + ledtrig_cpu(CPU_LED_STOP); + return 0; +} + +static void ledtrig_cpu_syscore_resume(void) +{ + ledtrig_cpu(CPU_LED_START); +} + +static void ledtrig_cpu_syscore_shutdown(void) +{ + ledtrig_cpu(CPU_LED_HALTED); +} + +static struct syscore_ops ledtrig_cpu_syscore_ops = { + .shutdown = ledtrig_cpu_syscore_shutdown, + .suspend = ledtrig_cpu_syscore_suspend, + .resume = ledtrig_cpu_syscore_resume, +}; + +static int __init ledtrig_cpu_init(void) +{ + int cpu; + + /* Supports up to 9999 cpu cores */ + BUILD_BUG_ON(CONFIG_NR_CPUS > 9999); + + /* + * Registering CPU led trigger for each CPU core here + * ignores CPU hotplug, but after this CPU hotplug works + * fine with this trigger. + */ + for_each_possible_cpu(cpu) { + struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu); + + mutex_init(&trig->lock); + + snprintf(trig->name, MAX_NAME_LEN, "cpu%d", cpu); + + mutex_lock(&trig->lock); + led_trigger_register_simple(trig->name, &trig->_trig); + trig->lock_is_inited = 1; + mutex_unlock(&trig->lock); + } + + register_syscore_ops(&ledtrig_cpu_syscore_ops); + + pr_info("ledtrig-cpu: registered to indicate activity on CPUs\n"); + + return 0; +} +module_init(ledtrig_cpu_init); + +static void __exit ledtrig_cpu_exit(void) +{ + int cpu; + + for_each_possible_cpu(cpu) { + struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu); + + mutex_lock(&trig->lock); + + led_trigger_unregister_simple(trig->_trig); + trig->_trig = NULL; + memset(trig->name, 0, MAX_NAME_LEN); + trig->lock_is_inited = 0; + + mutex_unlock(&trig->lock); + mutex_destroy(&trig->lock); + } + + unregister_syscore_ops(&ledtrig_cpu_syscore_ops); +} +module_exit(ledtrig_cpu_exit); + +MODULE_AUTHOR("Linus Walleij "); +MODULE_AUTHOR("Bryan Wu "); +MODULE_DESCRIPTION("CPU LED trigger"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/leds.h b/include/linux/leds.h index 39eee41d8c6f..cd545580e069 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -212,4 +212,20 @@ struct gpio_led_platform_data { struct platform_device *gpio_led_register_device( int id, const struct gpio_led_platform_data *pdata); +enum cpu_led_event { + CPU_LED_IDLE_START, /* CPU enters idle */ + CPU_LED_IDLE_END, /* CPU idle ends */ + CPU_LED_START, /* Machine starts, especially resume */ + CPU_LED_STOP, /* Machine stops, especially suspend */ + CPU_LED_HALTED, /* Machine shutdown */ +}; +#ifdef CONFIG_LEDS_TRIGGER_CPU +extern void ledtrig_cpu(enum cpu_led_event evt); +#else +static inline void ledtrig_cpu(enum cpu_led_event evt) +{ + return; +} +#endif + #endif /* __LINUX_LEDS_H_INCLUDED */ -- cgit v1.2.3 From ab09587740fddf6b4116be7b6716ab47f34d2634 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Fri, 27 Jul 2012 12:33:22 +0300 Subject: mac80211: add PS flag to bss_conf Currently, ps mode is indicated per device (rather than per interface), which doesn't make a lot of sense. Moreover, there are subtle bugs caused by the inability to indicate ps change along with other changes (e.g. when the AP deauth us, we'd like to indicate CHANGED_PS | CHANGED_ASSOC, as changing PS before notifying about disassociation will result in null-packets being sent (if IEEE80211_HW_SUPPORTS_DYNAMIC_PS) while the sta is already disconnected.) Keep the current per-device notifications, and add parallel per-vif notifications. In order to keep it simple, the per-device ps and the per-vif ps are orthogonal - the per-vif ps configuration is determined only by the user configuration (enable/disable) and the connection state, and is not affected by other vifs state and (temporary) dynamic_ps/offchannel operations (unlike per-device ps). Signed-off-by: Eliad Peller Signed-off-by: Johannes Berg --- include/net/mac80211.h | 5 +++++ net/mac80211/cfg.c | 6 ++++-- net/mac80211/ieee80211_i.h | 1 + net/mac80211/mlme.c | 15 +++++++++++++++ net/mac80211/util.c | 3 ++- 5 files changed, 27 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index bb86aa6f98dd..d67d3bbe21c1 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -171,6 +171,7 @@ struct ieee80211_low_level_stats { * @BSS_CHANGED_IDLE: Idle changed for this BSS/interface. * @BSS_CHANGED_SSID: SSID changed for this BSS (AP mode) * @BSS_CHANGED_AP_PROBE_RESP: Probe Response changed for this BSS (AP mode) + * @BSS_CHANGED_PS: PS changed for this BSS (STA mode) */ enum ieee80211_bss_change { BSS_CHANGED_ASSOC = 1<<0, @@ -190,6 +191,7 @@ enum ieee80211_bss_change { BSS_CHANGED_IDLE = 1<<14, BSS_CHANGED_SSID = 1<<15, BSS_CHANGED_AP_PROBE_RESP = 1<<16, + BSS_CHANGED_PS = 1<<17, /* when adding here, make sure to change ieee80211_reconfig */ }; @@ -266,6 +268,8 @@ enum ieee80211_rssi_event { * @idle: This interface is idle. There's also a global idle flag in the * hardware config which may be more appropriate depending on what * your driver/device needs to do. + * @ps: power-save mode (STA only). This flag is NOT affected by + * offchannel/dynamic_ps operations. * @ssid: The SSID of the current vif. Only valid in AP-mode. * @ssid_len: Length of SSID given in @ssid. * @hidden_ssid: The SSID of the current vif is hidden. Only valid in AP-mode. @@ -296,6 +300,7 @@ struct ieee80211_bss_conf { bool arp_filter_enabled; bool qos; bool idle; + bool ps; u8 ssid[IEEE80211_MAX_SSID_LEN]; size_t ssid_len; bool hidden_ssid; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index d41974aacf51..06b8d39780e9 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1285,9 +1285,10 @@ static int ieee80211_change_station(struct wiphy *wiphy, mutex_unlock(&local->sta_mtx); if (sdata->vif.type == NL80211_IFTYPE_STATION && - params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) + params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { ieee80211_recalc_ps(local, -1); - + ieee80211_recalc_ps_vif(sdata); + } return 0; } @@ -2079,6 +2080,7 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); ieee80211_recalc_ps(local, -1); + ieee80211_recalc_ps_vif(sdata); return 0; } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 3e2f03b1b50e..8e65ad9c870a 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1203,6 +1203,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, void ieee80211_send_pspoll(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata); void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency); +void ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata); int ieee80211_max_network_latency(struct notifier_block *nb, unsigned long data, void *dummy); int ieee80211_set_arp_filter(struct ieee80211_sub_if_data *sdata); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index f0d6fa283071..ea46c6446450 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1032,6 +1032,16 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency) ieee80211_change_ps(local); } +void ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata) +{ + bool ps_allowed = ieee80211_powersave_allowed(sdata); + + if (sdata->vif.bss_conf.ps != ps_allowed) { + sdata->vif.bss_conf.ps = ps_allowed; + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_PS); + } +} + void ieee80211_dynamic_ps_disable_work(struct work_struct *work) { struct ieee80211_local *local = @@ -1335,6 +1345,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, ieee80211_recalc_smps(local); mutex_unlock(&local->iflist_mtx); + ieee80211_recalc_ps_vif(sdata); + netif_tx_start_all_queues(sdata->dev); netif_carrier_on(sdata->dev); } @@ -1396,6 +1408,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, } local->ps_sdata = NULL; + /* disable per-vif ps */ + ieee80211_recalc_ps_vif(sdata); + /* flush out any pending frame (e.g. DELBA) before deauth/disassoc */ if (tx) drv_flush(local, false); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 39b82fee4904..037d148e9f19 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1359,7 +1359,8 @@ int ieee80211_reconfig(struct ieee80211_local *local) switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: changed |= BSS_CHANGED_ASSOC | - BSS_CHANGED_ARP_FILTER; + BSS_CHANGED_ARP_FILTER | + BSS_CHANGED_PS; mutex_lock(&sdata->u.mgd.mtx); ieee80211_bss_info_change_notify(sdata, changed); mutex_unlock(&sdata->u.mgd.mtx); -- cgit v1.2.3 From cba3345cc494ad286ca8823f44b2c16cae496679 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 31 Jul 2012 08:16:22 -0600 Subject: vfio: VFIO core VFIO is a secure user level driver for use with both virtual machines and user level drivers. VFIO makes use of IOMMU groups to ensure the isolation of devices in use, allowing unprivileged user access. It's intended that VFIO will replace KVM device assignment and UIO drivers (in cases where the target platform includes a sufficiently capable IOMMU). New in this version of VFIO is support for IOMMU groups managed through the IOMMU core as well as a rework of the API, removing the group merge interface. We now go back to a model more similar to original VFIO with UIOMMU support where the file descriptor obtained from /dev/vfio/vfio allows access to the IOMMU, but only after a group is added, avoiding the previous privilege issues with this type of model. IOMMU support is also now fully modular as IOMMUs have vastly different interface requirements on different platforms. VFIO users are able to query and initialize the IOMMU model of their choice. Please see the follow-on Documentation commit for further description and usage example. Signed-off-by: Alex Williamson --- Documentation/ioctl/ioctl-number.txt | 1 + MAINTAINERS | 8 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/vfio/Kconfig | 8 + drivers/vfio/Makefile | 1 + drivers/vfio/vfio.c | 1413 ++++++++++++++++++++++++++++++++++ include/linux/vfio.h | 367 +++++++++ 8 files changed, 1801 insertions(+) create mode 100644 drivers/vfio/Kconfig create mode 100644 drivers/vfio/Makefile create mode 100644 drivers/vfio/vfio.c create mode 100644 include/linux/vfio.h (limited to 'include') diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 915f28c470e9..849b771c5e03 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -88,6 +88,7 @@ Code Seq#(hex) Include File Comments and kernel/power/user.c '8' all SNP8023 advanced NIC card +';' 64-7F linux/vfio.h '@' 00-0F linux/radeonfb.h conflict! '@' 00-0F drivers/video/aty/aty128fb.c conflict! 'A' 00-1F linux/apm_bios.h conflict! diff --git a/MAINTAINERS b/MAINTAINERS index fb036a062a5d..eceaab93811e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7411,6 +7411,14 @@ S: Maintained F: Documentation/filesystems/vfat.txt F: fs/fat/ +VFIO DRIVER +M: Alex Williamson +L: kvm@vger.kernel.org +S: Maintained +F: Documentation/vfio.txt +F: drivers/vfio/ +F: include/linux/vfio.h + VIDEOBUF2 FRAMEWORK M: Pawel Osciak M: Marek Szyprowski diff --git a/drivers/Kconfig b/drivers/Kconfig index 805c432c9439..ece958d3762e 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -112,6 +112,8 @@ source "drivers/auxdisplay/Kconfig" source "drivers/uio/Kconfig" +source "drivers/vfio/Kconfig" + source "drivers/vlynq/Kconfig" source "drivers/virtio/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index bd36f09f2246..5b421840c48d 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -60,6 +60,7 @@ obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_FUSION) += message/ obj-y += firewire/ obj-$(CONFIG_UIO) += uio/ +obj-$(CONFIG_VFIO) += vfio/ obj-y += cdrom/ obj-y += auxdisplay/ obj-$(CONFIG_PCCARD) += pcmcia/ diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig new file mode 100644 index 000000000000..9acb1e729bd6 --- /dev/null +++ b/drivers/vfio/Kconfig @@ -0,0 +1,8 @@ +menuconfig VFIO + tristate "VFIO Non-Privileged userspace driver framework" + depends on IOMMU_API + help + VFIO provides a framework for secure userspace device drivers. + See Documentation/vfio.txt for more details. + + If you don't know what to do here, say N. diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile new file mode 100644 index 000000000000..7500a67a42a0 --- /dev/null +++ b/drivers/vfio/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_VFIO) += vfio.o diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c new file mode 100644 index 000000000000..052e310aed72 --- /dev/null +++ b/drivers/vfio/vfio.c @@ -0,0 +1,1413 @@ +/* + * VFIO core + * + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * 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. + * + * Derived from original vfio: + * Copyright 2010 Cisco Systems, Inc. All rights reserved. + * Author: Tom Lyon, pugs@cisco.com + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION "0.3" +#define DRIVER_AUTHOR "Alex Williamson " +#define DRIVER_DESC "VFIO - User Level meta-driver" + +static struct vfio { + struct class *class; + struct list_head iommu_drivers_list; + struct mutex iommu_drivers_lock; + struct list_head group_list; + struct idr group_idr; + struct mutex group_lock; + struct cdev group_cdev; + struct device *dev; + dev_t devt; + struct cdev cdev; + wait_queue_head_t release_q; +} vfio; + +struct vfio_iommu_driver { + const struct vfio_iommu_driver_ops *ops; + struct list_head vfio_next; +}; + +struct vfio_container { + struct kref kref; + struct list_head group_list; + struct mutex group_lock; + struct vfio_iommu_driver *iommu_driver; + void *iommu_data; +}; + +struct vfio_group { + struct kref kref; + int minor; + atomic_t container_users; + struct iommu_group *iommu_group; + struct vfio_container *container; + struct list_head device_list; + struct mutex device_lock; + struct device *dev; + struct notifier_block nb; + struct list_head vfio_next; + struct list_head container_next; +}; + +struct vfio_device { + struct kref kref; + struct device *dev; + const struct vfio_device_ops *ops; + struct vfio_group *group; + struct list_head group_next; + void *device_data; +}; + +/** + * IOMMU driver registration + */ +int vfio_register_iommu_driver(const struct vfio_iommu_driver_ops *ops) +{ + struct vfio_iommu_driver *driver, *tmp; + + driver = kzalloc(sizeof(*driver), GFP_KERNEL); + if (!driver) + return -ENOMEM; + + driver->ops = ops; + + mutex_lock(&vfio.iommu_drivers_lock); + + /* Check for duplicates */ + list_for_each_entry(tmp, &vfio.iommu_drivers_list, vfio_next) { + if (tmp->ops == ops) { + mutex_unlock(&vfio.iommu_drivers_lock); + kfree(driver); + return -EINVAL; + } + } + + list_add(&driver->vfio_next, &vfio.iommu_drivers_list); + + mutex_unlock(&vfio.iommu_drivers_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(vfio_register_iommu_driver); + +void vfio_unregister_iommu_driver(const struct vfio_iommu_driver_ops *ops) +{ + struct vfio_iommu_driver *driver; + + mutex_lock(&vfio.iommu_drivers_lock); + list_for_each_entry(driver, &vfio.iommu_drivers_list, vfio_next) { + if (driver->ops == ops) { + list_del(&driver->vfio_next); + mutex_unlock(&vfio.iommu_drivers_lock); + kfree(driver); + return; + } + } + mutex_unlock(&vfio.iommu_drivers_lock); +} +EXPORT_SYMBOL_GPL(vfio_unregister_iommu_driver); + +/** + * Group minor allocation/free - both called with vfio.group_lock held + */ +static int vfio_alloc_group_minor(struct vfio_group *group) +{ + int ret, minor; + +again: + if (unlikely(idr_pre_get(&vfio.group_idr, GFP_KERNEL) == 0)) + return -ENOMEM; + + /* index 0 is used by /dev/vfio/vfio */ + ret = idr_get_new_above(&vfio.group_idr, group, 1, &minor); + if (ret == -EAGAIN) + goto again; + if (ret || minor > MINORMASK) { + if (minor > MINORMASK) + idr_remove(&vfio.group_idr, minor); + return -ENOSPC; + } + + return minor; +} + +static void vfio_free_group_minor(int minor) +{ + idr_remove(&vfio.group_idr, minor); +} + +static int vfio_iommu_group_notifier(struct notifier_block *nb, + unsigned long action, void *data); +static void vfio_group_get(struct vfio_group *group); + +/** + * Container objects - containers are created when /dev/vfio/vfio is + * opened, but their lifecycle extends until the last user is done, so + * it's freed via kref. Must support container/group/device being + * closed in any order. + */ +static void vfio_container_get(struct vfio_container *container) +{ + kref_get(&container->kref); +} + +static void vfio_container_release(struct kref *kref) +{ + struct vfio_container *container; + container = container_of(kref, struct vfio_container, kref); + + kfree(container); +} + +static void vfio_container_put(struct vfio_container *container) +{ + kref_put(&container->kref, vfio_container_release); +} + +/** + * Group objects - create, release, get, put, search + */ +static struct vfio_group *vfio_create_group(struct iommu_group *iommu_group) +{ + struct vfio_group *group, *tmp; + struct device *dev; + int ret, minor; + + group = kzalloc(sizeof(*group), GFP_KERNEL); + if (!group) + return ERR_PTR(-ENOMEM); + + kref_init(&group->kref); + INIT_LIST_HEAD(&group->device_list); + mutex_init(&group->device_lock); + atomic_set(&group->container_users, 0); + group->iommu_group = iommu_group; + + group->nb.notifier_call = vfio_iommu_group_notifier; + + /* + * blocking notifiers acquire a rwsem around registering and hold + * it around callback. Therefore, need to register outside of + * vfio.group_lock to avoid A-B/B-A contention. Our callback won't + * do anything unless it can find the group in vfio.group_list, so + * no harm in registering early. + */ + ret = iommu_group_register_notifier(iommu_group, &group->nb); + if (ret) { + kfree(group); + return ERR_PTR(ret); + } + + mutex_lock(&vfio.group_lock); + + minor = vfio_alloc_group_minor(group); + if (minor < 0) { + mutex_unlock(&vfio.group_lock); + kfree(group); + return ERR_PTR(minor); + } + + /* Did we race creating this group? */ + list_for_each_entry(tmp, &vfio.group_list, vfio_next) { + if (tmp->iommu_group == iommu_group) { + vfio_group_get(tmp); + vfio_free_group_minor(minor); + mutex_unlock(&vfio.group_lock); + kfree(group); + return tmp; + } + } + + dev = device_create(vfio.class, NULL, MKDEV(MAJOR(vfio.devt), minor), + group, "%d", iommu_group_id(iommu_group)); + if (IS_ERR(dev)) { + vfio_free_group_minor(minor); + mutex_unlock(&vfio.group_lock); + kfree(group); + return (struct vfio_group *)dev; /* ERR_PTR */ + } + + group->minor = minor; + group->dev = dev; + + list_add(&group->vfio_next, &vfio.group_list); + + mutex_unlock(&vfio.group_lock); + + return group; +} + +static void vfio_group_release(struct kref *kref) +{ + struct vfio_group *group = container_of(kref, struct vfio_group, kref); + + WARN_ON(!list_empty(&group->device_list)); + + device_destroy(vfio.class, MKDEV(MAJOR(vfio.devt), group->minor)); + list_del(&group->vfio_next); + vfio_free_group_minor(group->minor); + + mutex_unlock(&vfio.group_lock); + + /* + * Unregister outside of lock. A spurious callback is harmless now + * that the group is no longer in vfio.group_list. + */ + iommu_group_unregister_notifier(group->iommu_group, &group->nb); + + kfree(group); +} + +static void vfio_group_put(struct vfio_group *group) +{ + mutex_lock(&vfio.group_lock); + /* + * Release needs to unlock to unregister the notifier, so only + * unlock if not released. + */ + if (!kref_put(&group->kref, vfio_group_release)) + mutex_unlock(&vfio.group_lock); +} + +/* Assume group_lock or group reference is held */ +static void vfio_group_get(struct vfio_group *group) +{ + kref_get(&group->kref); +} + +/* + * Not really a try as we will sleep for mutex, but we need to make + * sure the group pointer is valid under lock and get a reference. + */ +static struct vfio_group *vfio_group_try_get(struct vfio_group *group) +{ + struct vfio_group *target = group; + + mutex_lock(&vfio.group_lock); + list_for_each_entry(group, &vfio.group_list, vfio_next) { + if (group == target) { + vfio_group_get(group); + mutex_unlock(&vfio.group_lock); + return group; + } + } + mutex_unlock(&vfio.group_lock); + + return NULL; +} + +static +struct vfio_group *vfio_group_get_from_iommu(struct iommu_group *iommu_group) +{ + struct vfio_group *group; + + mutex_lock(&vfio.group_lock); + list_for_each_entry(group, &vfio.group_list, vfio_next) { + if (group->iommu_group == iommu_group) { + vfio_group_get(group); + mutex_unlock(&vfio.group_lock); + return group; + } + } + mutex_unlock(&vfio.group_lock); + + return NULL; +} + +static struct vfio_group *vfio_group_get_from_minor(int minor) +{ + struct vfio_group *group; + + mutex_lock(&vfio.group_lock); + group = idr_find(&vfio.group_idr, minor); + if (!group) { + mutex_unlock(&vfio.group_lock); + return NULL; + } + vfio_group_get(group); + mutex_unlock(&vfio.group_lock); + + return group; +} + +/** + * Device objects - create, release, get, put, search + */ +static +struct vfio_device *vfio_group_create_device(struct vfio_group *group, + struct device *dev, + const struct vfio_device_ops *ops, + void *device_data) +{ + struct vfio_device *device; + int ret; + + device = kzalloc(sizeof(*device), GFP_KERNEL); + if (!device) + return ERR_PTR(-ENOMEM); + + kref_init(&device->kref); + device->dev = dev; + device->group = group; + device->ops = ops; + device->device_data = device_data; + + ret = dev_set_drvdata(dev, device); + if (ret) { + kfree(device); + return ERR_PTR(ret); + } + + /* No need to get group_lock, caller has group reference */ + vfio_group_get(group); + + mutex_lock(&group->device_lock); + list_add(&device->group_next, &group->device_list); + mutex_unlock(&group->device_lock); + + return device; +} + +static void vfio_device_release(struct kref *kref) +{ + struct vfio_device *device = container_of(kref, + struct vfio_device, kref); + struct vfio_group *group = device->group; + + mutex_lock(&group->device_lock); + list_del(&device->group_next); + mutex_unlock(&group->device_lock); + + dev_set_drvdata(device->dev, NULL); + + kfree(device); + + /* vfio_del_group_dev may be waiting for this device */ + wake_up(&vfio.release_q); +} + +/* Device reference always implies a group reference */ +static void vfio_device_put(struct vfio_device *device) +{ + kref_put(&device->kref, vfio_device_release); + vfio_group_put(device->group); +} + +static void vfio_device_get(struct vfio_device *device) +{ + vfio_group_get(device->group); + kref_get(&device->kref); +} + +static struct vfio_device *vfio_group_get_device(struct vfio_group *group, + struct device *dev) +{ + struct vfio_device *device; + + mutex_lock(&group->device_lock); + list_for_each_entry(device, &group->device_list, group_next) { + if (device->dev == dev) { + vfio_device_get(device); + mutex_unlock(&group->device_lock); + return device; + } + } + mutex_unlock(&group->device_lock); + return NULL; +} + +/* + * Whitelist some drivers that we know are safe (no dma) or just sit on + * a device. It's not always practical to leave a device within a group + * driverless as it could get re-bound to something unsafe. + */ +static const char * const vfio_driver_whitelist[] = { "pci-stub" }; + +static bool vfio_whitelisted_driver(struct device_driver *drv) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vfio_driver_whitelist); i++) { + if (!strcmp(drv->name, vfio_driver_whitelist[i])) + return true; + } + + return false; +} + +/* + * A vfio group is viable for use by userspace if all devices are either + * driver-less or bound to a vfio or whitelisted driver. We test the + * latter by the existence of a struct vfio_device matching the dev. + */ +static int vfio_dev_viable(struct device *dev, void *data) +{ + struct vfio_group *group = data; + struct vfio_device *device; + + if (!dev->driver || vfio_whitelisted_driver(dev->driver)) + return 0; + + device = vfio_group_get_device(group, dev); + if (device) { + vfio_device_put(device); + return 0; + } + + return -EINVAL; +} + +/** + * Async device support + */ +static int vfio_group_nb_add_dev(struct vfio_group *group, struct device *dev) +{ + struct vfio_device *device; + + /* Do we already know about it? We shouldn't */ + device = vfio_group_get_device(group, dev); + if (WARN_ON_ONCE(device)) { + vfio_device_put(device); + return 0; + } + + /* Nothing to do for idle groups */ + if (!atomic_read(&group->container_users)) + return 0; + + /* TODO Prevent device auto probing */ + WARN("Device %s added to live group %d!\n", dev_name(dev), + iommu_group_id(group->iommu_group)); + + return 0; +} + +static int vfio_group_nb_del_dev(struct vfio_group *group, struct device *dev) +{ + struct vfio_device *device; + + /* + * Expect to fall out here. If a device was in use, it would + * have been bound to a vfio sub-driver, which would have blocked + * in .remove at vfio_del_group_dev. Sanity check that we no + * longer track the device, so it's safe to remove. + */ + device = vfio_group_get_device(group, dev); + if (likely(!device)) + return 0; + + WARN("Device %s removed from live group %d!\n", dev_name(dev), + iommu_group_id(group->iommu_group)); + + vfio_device_put(device); + return 0; +} + +static int vfio_group_nb_verify(struct vfio_group *group, struct device *dev) +{ + /* We don't care what happens when the group isn't in use */ + if (!atomic_read(&group->container_users)) + return 0; + + return vfio_dev_viable(dev, group); +} + +static int vfio_iommu_group_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct vfio_group *group = container_of(nb, struct vfio_group, nb); + struct device *dev = data; + + /* + * Need to go through a group_lock lookup to get a reference or + * we risk racing a group being removed. Leave a WARN_ON for + * debuging, but if the group no longer exists, a spurious notify + * is harmless. + */ + group = vfio_group_try_get(group); + if (WARN_ON(!group)) + return NOTIFY_OK; + + switch (action) { + case IOMMU_GROUP_NOTIFY_ADD_DEVICE: + vfio_group_nb_add_dev(group, dev); + break; + case IOMMU_GROUP_NOTIFY_DEL_DEVICE: + vfio_group_nb_del_dev(group, dev); + break; + case IOMMU_GROUP_NOTIFY_BIND_DRIVER: + pr_debug("%s: Device %s, group %d binding to driver\n", + __func__, dev_name(dev), + iommu_group_id(group->iommu_group)); + break; + case IOMMU_GROUP_NOTIFY_BOUND_DRIVER: + pr_debug("%s: Device %s, group %d bound to driver %s\n", + __func__, dev_name(dev), + iommu_group_id(group->iommu_group), dev->driver->name); + BUG_ON(vfio_group_nb_verify(group, dev)); + break; + case IOMMU_GROUP_NOTIFY_UNBIND_DRIVER: + pr_debug("%s: Device %s, group %d unbinding from driver %s\n", + __func__, dev_name(dev), + iommu_group_id(group->iommu_group), dev->driver->name); + break; + case IOMMU_GROUP_NOTIFY_UNBOUND_DRIVER: + pr_debug("%s: Device %s, group %d unbound from driver\n", + __func__, dev_name(dev), + iommu_group_id(group->iommu_group)); + /* + * XXX An unbound device in a live group is ok, but we'd + * really like to avoid the above BUG_ON by preventing other + * drivers from binding to it. Once that occurs, we have to + * stop the system to maintain isolation. At a minimum, we'd + * want a toggle to disable driver auto probe for this device. + */ + break; + } + + vfio_group_put(group); + return NOTIFY_OK; +} + +/** + * VFIO driver API + */ +int vfio_add_group_dev(struct device *dev, + const struct vfio_device_ops *ops, void *device_data) +{ + struct iommu_group *iommu_group; + struct vfio_group *group; + struct vfio_device *device; + + iommu_group = iommu_group_get(dev); + if (!iommu_group) + return -EINVAL; + + group = vfio_group_get_from_iommu(iommu_group); + if (!group) { + group = vfio_create_group(iommu_group); + if (IS_ERR(group)) { + iommu_group_put(iommu_group); + return PTR_ERR(group); + } + } + + device = vfio_group_get_device(group, dev); + if (device) { + WARN(1, "Device %s already exists on group %d\n", + dev_name(dev), iommu_group_id(iommu_group)); + vfio_device_put(device); + vfio_group_put(group); + iommu_group_put(iommu_group); + return -EBUSY; + } + + device = vfio_group_create_device(group, dev, ops, device_data); + if (IS_ERR(device)) { + vfio_group_put(group); + iommu_group_put(iommu_group); + return PTR_ERR(device); + } + + /* + * Added device holds reference to iommu_group and vfio_device + * (which in turn holds reference to vfio_group). Drop extra + * group reference used while acquiring device. + */ + vfio_group_put(group); + + return 0; +} +EXPORT_SYMBOL_GPL(vfio_add_group_dev); + +/* Test whether a struct device is present in our tracking */ +static bool vfio_dev_present(struct device *dev) +{ + struct iommu_group *iommu_group; + struct vfio_group *group; + struct vfio_device *device; + + iommu_group = iommu_group_get(dev); + if (!iommu_group) + return false; + + group = vfio_group_get_from_iommu(iommu_group); + if (!group) { + iommu_group_put(iommu_group); + return false; + } + + device = vfio_group_get_device(group, dev); + if (!device) { + vfio_group_put(group); + iommu_group_put(iommu_group); + return false; + } + + vfio_device_put(device); + vfio_group_put(group); + iommu_group_put(iommu_group); + return true; +} + +/* + * Decrement the device reference count and wait for the device to be + * removed. Open file descriptors for the device... */ +void *vfio_del_group_dev(struct device *dev) +{ + struct vfio_device *device = dev_get_drvdata(dev); + struct vfio_group *group = device->group; + struct iommu_group *iommu_group = group->iommu_group; + void *device_data = device->device_data; + + vfio_device_put(device); + + /* TODO send a signal to encourage this to be released */ + wait_event(vfio.release_q, !vfio_dev_present(dev)); + + iommu_group_put(iommu_group); + + return device_data; +} +EXPORT_SYMBOL_GPL(vfio_del_group_dev); + +/** + * VFIO base fd, /dev/vfio/vfio + */ +static long vfio_ioctl_check_extension(struct vfio_container *container, + unsigned long arg) +{ + struct vfio_iommu_driver *driver = container->iommu_driver; + long ret = 0; + + switch (arg) { + /* No base extensions yet */ + default: + /* + * If no driver is set, poll all registered drivers for + * extensions and return the first positive result. If + * a driver is already set, further queries will be passed + * only to that driver. + */ + if (!driver) { + mutex_lock(&vfio.iommu_drivers_lock); + list_for_each_entry(driver, &vfio.iommu_drivers_list, + vfio_next) { + if (!try_module_get(driver->ops->owner)) + continue; + + ret = driver->ops->ioctl(NULL, + VFIO_CHECK_EXTENSION, + arg); + module_put(driver->ops->owner); + if (ret > 0) + break; + } + mutex_unlock(&vfio.iommu_drivers_lock); + } else + ret = driver->ops->ioctl(container->iommu_data, + VFIO_CHECK_EXTENSION, arg); + } + + return ret; +} + +/* hold container->group_lock */ +static int __vfio_container_attach_groups(struct vfio_container *container, + struct vfio_iommu_driver *driver, + void *data) +{ + struct vfio_group *group; + int ret = -ENODEV; + + list_for_each_entry(group, &container->group_list, container_next) { + ret = driver->ops->attach_group(data, group->iommu_group); + if (ret) + goto unwind; + } + + return ret; + +unwind: + list_for_each_entry_continue_reverse(group, &container->group_list, + container_next) { + driver->ops->detach_group(data, group->iommu_group); + } + + return ret; +} + +static long vfio_ioctl_set_iommu(struct vfio_container *container, + unsigned long arg) +{ + struct vfio_iommu_driver *driver; + long ret = -ENODEV; + + mutex_lock(&container->group_lock); + + /* + * The container is designed to be an unprivileged interface while + * the group can be assigned to specific users. Therefore, only by + * adding a group to a container does the user get the privilege of + * enabling the iommu, which may allocate finite resources. There + * is no unset_iommu, but by removing all the groups from a container, + * the container is deprivileged and returns to an unset state. + */ + if (list_empty(&container->group_list) || container->iommu_driver) { + mutex_unlock(&container->group_lock); + return -EINVAL; + } + + mutex_lock(&vfio.iommu_drivers_lock); + list_for_each_entry(driver, &vfio.iommu_drivers_list, vfio_next) { + void *data; + + if (!try_module_get(driver->ops->owner)) + continue; + + /* + * The arg magic for SET_IOMMU is the same as CHECK_EXTENSION, + * so test which iommu driver reported support for this + * extension and call open on them. We also pass them the + * magic, allowing a single driver to support multiple + * interfaces if they'd like. + */ + if (driver->ops->ioctl(NULL, VFIO_CHECK_EXTENSION, arg) <= 0) { + module_put(driver->ops->owner); + continue; + } + + /* module reference holds the driver we're working on */ + mutex_unlock(&vfio.iommu_drivers_lock); + + data = driver->ops->open(arg); + if (IS_ERR(data)) { + ret = PTR_ERR(data); + module_put(driver->ops->owner); + goto skip_drivers_unlock; + } + + ret = __vfio_container_attach_groups(container, driver, data); + if (!ret) { + container->iommu_driver = driver; + container->iommu_data = data; + } else { + driver->ops->release(data); + module_put(driver->ops->owner); + } + + goto skip_drivers_unlock; + } + + mutex_unlock(&vfio.iommu_drivers_lock); +skip_drivers_unlock: + mutex_unlock(&container->group_lock); + + return ret; +} + +static long vfio_fops_unl_ioctl(struct file *filep, + unsigned int cmd, unsigned long arg) +{ + struct vfio_container *container = filep->private_data; + struct vfio_iommu_driver *driver; + void *data; + long ret = -EINVAL; + + if (!container) + return ret; + + driver = container->iommu_driver; + data = container->iommu_data; + + switch (cmd) { + case VFIO_GET_API_VERSION: + ret = VFIO_API_VERSION; + break; + case VFIO_CHECK_EXTENSION: + ret = vfio_ioctl_check_extension(container, arg); + break; + case VFIO_SET_IOMMU: + ret = vfio_ioctl_set_iommu(container, arg); + break; + default: + if (driver) /* passthrough all unrecognized ioctls */ + ret = driver->ops->ioctl(data, cmd, arg); + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long vfio_fops_compat_ioctl(struct file *filep, + unsigned int cmd, unsigned long arg) +{ + arg = (unsigned long)compat_ptr(arg); + return vfio_fops_unl_ioctl(filep, cmd, arg); +} +#endif /* CONFIG_COMPAT */ + +static int vfio_fops_open(struct inode *inode, struct file *filep) +{ + struct vfio_container *container; + + container = kzalloc(sizeof(*container), GFP_KERNEL); + if (!container) + return -ENOMEM; + + INIT_LIST_HEAD(&container->group_list); + mutex_init(&container->group_lock); + kref_init(&container->kref); + + filep->private_data = container; + + return 0; +} + +static int vfio_fops_release(struct inode *inode, struct file *filep) +{ + struct vfio_container *container = filep->private_data; + + filep->private_data = NULL; + + vfio_container_put(container); + + return 0; +} + +/* + * Once an iommu driver is set, we optionally pass read/write/mmap + * on to the driver, allowing management interfaces beyond ioctl. + */ +static ssize_t vfio_fops_read(struct file *filep, char __user *buf, + size_t count, loff_t *ppos) +{ + struct vfio_container *container = filep->private_data; + struct vfio_iommu_driver *driver = container->iommu_driver; + + if (unlikely(!driver || !driver->ops->read)) + return -EINVAL; + + return driver->ops->read(container->iommu_data, buf, count, ppos); +} + +static ssize_t vfio_fops_write(struct file *filep, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct vfio_container *container = filep->private_data; + struct vfio_iommu_driver *driver = container->iommu_driver; + + if (unlikely(!driver || !driver->ops->write)) + return -EINVAL; + + return driver->ops->write(container->iommu_data, buf, count, ppos); +} + +static int vfio_fops_mmap(struct file *filep, struct vm_area_struct *vma) +{ + struct vfio_container *container = filep->private_data; + struct vfio_iommu_driver *driver = container->iommu_driver; + + if (unlikely(!driver || !driver->ops->mmap)) + return -EINVAL; + + return driver->ops->mmap(container->iommu_data, vma); +} + +static const struct file_operations vfio_fops = { + .owner = THIS_MODULE, + .open = vfio_fops_open, + .release = vfio_fops_release, + .read = vfio_fops_read, + .write = vfio_fops_write, + .unlocked_ioctl = vfio_fops_unl_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = vfio_fops_compat_ioctl, +#endif + .mmap = vfio_fops_mmap, +}; + +/** + * VFIO Group fd, /dev/vfio/$GROUP + */ +static void __vfio_group_unset_container(struct vfio_group *group) +{ + struct vfio_container *container = group->container; + struct vfio_iommu_driver *driver; + + mutex_lock(&container->group_lock); + + driver = container->iommu_driver; + if (driver) + driver->ops->detach_group(container->iommu_data, + group->iommu_group); + + group->container = NULL; + list_del(&group->container_next); + + /* Detaching the last group deprivileges a container, remove iommu */ + if (driver && list_empty(&container->group_list)) { + driver->ops->release(container->iommu_data); + module_put(driver->ops->owner); + container->iommu_driver = NULL; + container->iommu_data = NULL; + } + + mutex_unlock(&container->group_lock); + + vfio_container_put(container); +} + +/* + * VFIO_GROUP_UNSET_CONTAINER should fail if there are other users or + * if there was no container to unset. Since the ioctl is called on + * the group, we know that still exists, therefore the only valid + * transition here is 1->0. + */ +static int vfio_group_unset_container(struct vfio_group *group) +{ + int users = atomic_cmpxchg(&group->container_users, 1, 0); + + if (!users) + return -EINVAL; + if (users != 1) + return -EBUSY; + + __vfio_group_unset_container(group); + + return 0; +} + +/* + * When removing container users, anything that removes the last user + * implicitly removes the group from the container. That is, if the + * group file descriptor is closed, as well as any device file descriptors, + * the group is free. + */ +static void vfio_group_try_dissolve_container(struct vfio_group *group) +{ + if (0 == atomic_dec_if_positive(&group->container_users)) + __vfio_group_unset_container(group); +} + +static int vfio_group_set_container(struct vfio_group *group, int container_fd) +{ + struct file *filep; + struct vfio_container *container; + struct vfio_iommu_driver *driver; + int ret = 0; + + if (atomic_read(&group->container_users)) + return -EINVAL; + + filep = fget(container_fd); + if (!filep) + return -EBADF; + + /* Sanity check, is this really our fd? */ + if (filep->f_op != &vfio_fops) { + fput(filep); + return -EINVAL; + } + + container = filep->private_data; + WARN_ON(!container); /* fget ensures we don't race vfio_release */ + + mutex_lock(&container->group_lock); + + driver = container->iommu_driver; + if (driver) { + ret = driver->ops->attach_group(container->iommu_data, + group->iommu_group); + if (ret) + goto unlock_out; + } + + group->container = container; + list_add(&group->container_next, &container->group_list); + + /* Get a reference on the container and mark a user within the group */ + vfio_container_get(container); + atomic_inc(&group->container_users); + +unlock_out: + mutex_unlock(&container->group_lock); + fput(filep); + + return ret; +} + +static bool vfio_group_viable(struct vfio_group *group) +{ + return (iommu_group_for_each_dev(group->iommu_group, + group, vfio_dev_viable) == 0); +} + +static const struct file_operations vfio_device_fops; + +static int vfio_group_get_device_fd(struct vfio_group *group, char *buf) +{ + struct vfio_device *device; + struct file *filep; + int ret = -ENODEV; + + if (0 == atomic_read(&group->container_users) || + !group->container->iommu_driver || !vfio_group_viable(group)) + return -EINVAL; + + mutex_lock(&group->device_lock); + list_for_each_entry(device, &group->device_list, group_next) { + if (strcmp(dev_name(device->dev), buf)) + continue; + + ret = device->ops->open(device->device_data); + if (ret) + break; + /* + * We can't use anon_inode_getfd() because we need to modify + * the f_mode flags directly to allow more than just ioctls + */ + ret = get_unused_fd(); + if (ret < 0) { + device->ops->release(device->device_data); + break; + } + + filep = anon_inode_getfile("[vfio-device]", &vfio_device_fops, + device, O_RDWR); + if (IS_ERR(filep)) { + put_unused_fd(ret); + ret = PTR_ERR(filep); + device->ops->release(device->device_data); + break; + } + + /* + * TODO: add an anon_inode interface to do this. + * Appears to be missing by lack of need rather than + * explicitly prevented. Now there's need. + */ + filep->f_mode |= (FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); + + fd_install(ret, filep); + + vfio_device_get(device); + atomic_inc(&group->container_users); + break; + } + mutex_unlock(&group->device_lock); + + return ret; +} + +static long vfio_group_fops_unl_ioctl(struct file *filep, + unsigned int cmd, unsigned long arg) +{ + struct vfio_group *group = filep->private_data; + long ret = -ENOTTY; + + switch (cmd) { + case VFIO_GROUP_GET_STATUS: + { + struct vfio_group_status status; + unsigned long minsz; + + minsz = offsetofend(struct vfio_group_status, flags); + + if (copy_from_user(&status, (void __user *)arg, minsz)) + return -EFAULT; + + if (status.argsz < minsz) + return -EINVAL; + + status.flags = 0; + + if (vfio_group_viable(group)) + status.flags |= VFIO_GROUP_FLAGS_VIABLE; + + if (group->container) + status.flags |= VFIO_GROUP_FLAGS_CONTAINER_SET; + + if (copy_to_user((void __user *)arg, &status, minsz)) + return -EFAULT; + + ret = 0; + break; + } + case VFIO_GROUP_SET_CONTAINER: + { + int fd; + + if (get_user(fd, (int __user *)arg)) + return -EFAULT; + + if (fd < 0) + return -EINVAL; + + ret = vfio_group_set_container(group, fd); + break; + } + case VFIO_GROUP_UNSET_CONTAINER: + ret = vfio_group_unset_container(group); + break; + case VFIO_GROUP_GET_DEVICE_FD: + { + char *buf; + + buf = strndup_user((const char __user *)arg, PAGE_SIZE); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + ret = vfio_group_get_device_fd(group, buf); + kfree(buf); + break; + } + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long vfio_group_fops_compat_ioctl(struct file *filep, + unsigned int cmd, unsigned long arg) +{ + arg = (unsigned long)compat_ptr(arg); + return vfio_group_fops_unl_ioctl(filep, cmd, arg); +} +#endif /* CONFIG_COMPAT */ + +static int vfio_group_fops_open(struct inode *inode, struct file *filep) +{ + struct vfio_group *group; + + group = vfio_group_get_from_minor(iminor(inode)); + if (!group) + return -ENODEV; + + if (group->container) { + vfio_group_put(group); + return -EBUSY; + } + + filep->private_data = group; + + return 0; +} + +static int vfio_group_fops_release(struct inode *inode, struct file *filep) +{ + struct vfio_group *group = filep->private_data; + + filep->private_data = NULL; + + vfio_group_try_dissolve_container(group); + + vfio_group_put(group); + + return 0; +} + +static const struct file_operations vfio_group_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = vfio_group_fops_unl_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = vfio_group_fops_compat_ioctl, +#endif + .open = vfio_group_fops_open, + .release = vfio_group_fops_release, +}; + +/** + * VFIO Device fd + */ +static int vfio_device_fops_release(struct inode *inode, struct file *filep) +{ + struct vfio_device *device = filep->private_data; + + device->ops->release(device->device_data); + + vfio_group_try_dissolve_container(device->group); + + vfio_device_put(device); + + return 0; +} + +static long vfio_device_fops_unl_ioctl(struct file *filep, + unsigned int cmd, unsigned long arg) +{ + struct vfio_device *device = filep->private_data; + + if (unlikely(!device->ops->ioctl)) + return -EINVAL; + + return device->ops->ioctl(device->device_data, cmd, arg); +} + +static ssize_t vfio_device_fops_read(struct file *filep, char __user *buf, + size_t count, loff_t *ppos) +{ + struct vfio_device *device = filep->private_data; + + if (unlikely(!device->ops->read)) + return -EINVAL; + + return device->ops->read(device->device_data, buf, count, ppos); +} + +static ssize_t vfio_device_fops_write(struct file *filep, + const char __user *buf, + size_t count, loff_t *ppos) +{ + struct vfio_device *device = filep->private_data; + + if (unlikely(!device->ops->write)) + return -EINVAL; + + return device->ops->write(device->device_data, buf, count, ppos); +} + +static int vfio_device_fops_mmap(struct file *filep, struct vm_area_struct *vma) +{ + struct vfio_device *device = filep->private_data; + + if (unlikely(!device->ops->mmap)) + return -EINVAL; + + return device->ops->mmap(device->device_data, vma); +} + +#ifdef CONFIG_COMPAT +static long vfio_device_fops_compat_ioctl(struct file *filep, + unsigned int cmd, unsigned long arg) +{ + arg = (unsigned long)compat_ptr(arg); + return vfio_device_fops_unl_ioctl(filep, cmd, arg); +} +#endif /* CONFIG_COMPAT */ + +static const struct file_operations vfio_device_fops = { + .owner = THIS_MODULE, + .release = vfio_device_fops_release, + .read = vfio_device_fops_read, + .write = vfio_device_fops_write, + .unlocked_ioctl = vfio_device_fops_unl_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = vfio_device_fops_compat_ioctl, +#endif + .mmap = vfio_device_fops_mmap, +}; + +/** + * Module/class support + */ +static char *vfio_devnode(struct device *dev, umode_t *mode) +{ + return kasprintf(GFP_KERNEL, "vfio/%s", dev_name(dev)); +} + +static int __init vfio_init(void) +{ + int ret; + + idr_init(&vfio.group_idr); + mutex_init(&vfio.group_lock); + mutex_init(&vfio.iommu_drivers_lock); + INIT_LIST_HEAD(&vfio.group_list); + INIT_LIST_HEAD(&vfio.iommu_drivers_list); + init_waitqueue_head(&vfio.release_q); + + vfio.class = class_create(THIS_MODULE, "vfio"); + if (IS_ERR(vfio.class)) { + ret = PTR_ERR(vfio.class); + goto err_class; + } + + vfio.class->devnode = vfio_devnode; + + ret = alloc_chrdev_region(&vfio.devt, 0, MINORMASK, "vfio"); + if (ret) + goto err_base_chrdev; + + cdev_init(&vfio.cdev, &vfio_fops); + ret = cdev_add(&vfio.cdev, vfio.devt, 1); + if (ret) + goto err_base_cdev; + + vfio.dev = device_create(vfio.class, NULL, vfio.devt, NULL, "vfio"); + if (IS_ERR(vfio.dev)) { + ret = PTR_ERR(vfio.dev); + goto err_base_dev; + } + + /* /dev/vfio/$GROUP */ + cdev_init(&vfio.group_cdev, &vfio_group_fops); + ret = cdev_add(&vfio.group_cdev, + MKDEV(MAJOR(vfio.devt), 1), MINORMASK - 1); + if (ret) + goto err_groups_cdev; + + pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); + + return 0; + +err_groups_cdev: + device_destroy(vfio.class, vfio.devt); +err_base_dev: + cdev_del(&vfio.cdev); +err_base_cdev: + unregister_chrdev_region(vfio.devt, MINORMASK); +err_base_chrdev: + class_destroy(vfio.class); + vfio.class = NULL; +err_class: + return ret; +} + +static void __exit vfio_cleanup(void) +{ + WARN_ON(!list_empty(&vfio.group_list)); + + idr_destroy(&vfio.group_idr); + cdev_del(&vfio.group_cdev); + device_destroy(vfio.class, vfio.devt); + cdev_del(&vfio.cdev); + unregister_chrdev_region(vfio.devt, MINORMASK); + class_destroy(vfio.class); + vfio.class = NULL; +} + +module_init(vfio_init); +module_exit(vfio_cleanup); + +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/include/linux/vfio.h b/include/linux/vfio.h new file mode 100644 index 000000000000..03e56a5154b6 --- /dev/null +++ b/include/linux/vfio.h @@ -0,0 +1,367 @@ +/* + * VFIO API definition + * + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * 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 VFIO_H +#define VFIO_H + +#include +#include + +#define VFIO_API_VERSION 0 + +#ifdef __KERNEL__ /* Internal VFIO-core/bus driver API */ + +#include +#include + +/** + * struct vfio_device_ops - VFIO bus driver device callbacks + * + * @open: Called when userspace creates new file descriptor for device + * @release: Called when userspace releases file descriptor for device + * @read: Perform read(2) on device file descriptor + * @write: Perform write(2) on device file descriptor + * @ioctl: Perform ioctl(2) on device file descriptor, supporting VFIO_DEVICE_* + * operations documented below + * @mmap: Perform mmap(2) on a region of the device file descriptor + */ +struct vfio_device_ops { + char *name; + int (*open)(void *device_data); + void (*release)(void *device_data); + ssize_t (*read)(void *device_data, char __user *buf, + size_t count, loff_t *ppos); + ssize_t (*write)(void *device_data, const char __user *buf, + size_t count, loff_t *size); + long (*ioctl)(void *device_data, unsigned int cmd, + unsigned long arg); + int (*mmap)(void *device_data, struct vm_area_struct *vma); +}; + +extern int vfio_add_group_dev(struct device *dev, + const struct vfio_device_ops *ops, + void *device_data); + +extern void *vfio_del_group_dev(struct device *dev); + +/** + * struct vfio_iommu_driver_ops - VFIO IOMMU driver callbacks + */ +struct vfio_iommu_driver_ops { + char *name; + struct module *owner; + void *(*open)(unsigned long arg); + void (*release)(void *iommu_data); + ssize_t (*read)(void *iommu_data, char __user *buf, + size_t count, loff_t *ppos); + ssize_t (*write)(void *iommu_data, const char __user *buf, + size_t count, loff_t *size); + long (*ioctl)(void *iommu_data, unsigned int cmd, + unsigned long arg); + int (*mmap)(void *iommu_data, struct vm_area_struct *vma); + int (*attach_group)(void *iommu_data, + struct iommu_group *group); + void (*detach_group)(void *iommu_data, + struct iommu_group *group); + +}; + +extern int vfio_register_iommu_driver(const struct vfio_iommu_driver_ops *ops); + +extern void vfio_unregister_iommu_driver( + const struct vfio_iommu_driver_ops *ops); + +/** + * offsetofend(TYPE, MEMBER) + * + * @TYPE: The type of the structure + * @MEMBER: The member within the structure to get the end offset of + * + * Simple helper macro for dealing with variable sized structures passed + * from user space. This allows us to easily determine if the provided + * structure is sized to include various fields. + */ +#define offsetofend(TYPE, MEMBER) ({ \ + TYPE tmp; \ + offsetof(TYPE, MEMBER) + sizeof(tmp.MEMBER); }) \ + +#endif /* __KERNEL__ */ + +/* Kernel & User level defines for VFIO IOCTLs. */ + +/* Extensions */ + +/* None yet */ + +/* + * The IOCTL interface is designed for extensibility by embedding the + * structure length (argsz) and flags into structures passed between + * kernel and userspace. We therefore use the _IO() macro for these + * defines to avoid implicitly embedding a size into the ioctl request. + * As structure fields are added, argsz will increase to match and flag + * bits will be defined to indicate additional fields with valid data. + * It's *always* the caller's responsibility to indicate the size of + * the structure passed by setting argsz appropriately. + */ + +#define VFIO_TYPE (';') +#define VFIO_BASE 100 + +/* -------- IOCTLs for VFIO file descriptor (/dev/vfio/vfio) -------- */ + +/** + * VFIO_GET_API_VERSION - _IO(VFIO_TYPE, VFIO_BASE + 0) + * + * Report the version of the VFIO API. This allows us to bump the entire + * API version should we later need to add or change features in incompatible + * ways. + * Return: VFIO_API_VERSION + * Availability: Always + */ +#define VFIO_GET_API_VERSION _IO(VFIO_TYPE, VFIO_BASE + 0) + +/** + * VFIO_CHECK_EXTENSION - _IOW(VFIO_TYPE, VFIO_BASE + 1, __u32) + * + * Check whether an extension is supported. + * Return: 0 if not supported, 1 (or some other positive integer) if supported. + * Availability: Always + */ +#define VFIO_CHECK_EXTENSION _IO(VFIO_TYPE, VFIO_BASE + 1) + +/** + * VFIO_SET_IOMMU - _IOW(VFIO_TYPE, VFIO_BASE + 2, __s32) + * + * Set the iommu to the given type. The type must be supported by an + * iommu driver as verified by calling CHECK_EXTENSION using the same + * type. A group must be set to this file descriptor before this + * ioctl is available. The IOMMU interfaces enabled by this call are + * specific to the value set. + * Return: 0 on success, -errno on failure + * Availability: When VFIO group attached + */ +#define VFIO_SET_IOMMU _IO(VFIO_TYPE, VFIO_BASE + 2) + +/* -------- IOCTLs for GROUP file descriptors (/dev/vfio/$GROUP) -------- */ + +/** + * VFIO_GROUP_GET_STATUS - _IOR(VFIO_TYPE, VFIO_BASE + 3, + * struct vfio_group_status) + * + * Retrieve information about the group. Fills in provided + * struct vfio_group_info. Caller sets argsz. + * Return: 0 on succes, -errno on failure. + * Availability: Always + */ +struct vfio_group_status { + __u32 argsz; + __u32 flags; +#define VFIO_GROUP_FLAGS_VIABLE (1 << 0) +#define VFIO_GROUP_FLAGS_CONTAINER_SET (1 << 1) +}; +#define VFIO_GROUP_GET_STATUS _IO(VFIO_TYPE, VFIO_BASE + 3) + +/** + * VFIO_GROUP_SET_CONTAINER - _IOW(VFIO_TYPE, VFIO_BASE + 4, __s32) + * + * Set the container for the VFIO group to the open VFIO file + * descriptor provided. Groups may only belong to a single + * container. Containers may, at their discretion, support multiple + * groups. Only when a container is set are all of the interfaces + * of the VFIO file descriptor and the VFIO group file descriptor + * available to the user. + * Return: 0 on success, -errno on failure. + * Availability: Always + */ +#define VFIO_GROUP_SET_CONTAINER _IO(VFIO_TYPE, VFIO_BASE + 4) + +/** + * VFIO_GROUP_UNSET_CONTAINER - _IO(VFIO_TYPE, VFIO_BASE + 5) + * + * Remove the group from the attached container. This is the + * opposite of the SET_CONTAINER call and returns the group to + * an initial state. All device file descriptors must be released + * prior to calling this interface. When removing the last group + * from a container, the IOMMU will be disabled and all state lost, + * effectively also returning the VFIO file descriptor to an initial + * state. + * Return: 0 on success, -errno on failure. + * Availability: When attached to container + */ +#define VFIO_GROUP_UNSET_CONTAINER _IO(VFIO_TYPE, VFIO_BASE + 5) + +/** + * VFIO_GROUP_GET_DEVICE_FD - _IOW(VFIO_TYPE, VFIO_BASE + 6, char) + * + * Return a new file descriptor for the device object described by + * the provided string. The string should match a device listed in + * the devices subdirectory of the IOMMU group sysfs entry. The + * group containing the device must already be added to this context. + * Return: new file descriptor on success, -errno on failure. + * Availability: When attached to container + */ +#define VFIO_GROUP_GET_DEVICE_FD _IO(VFIO_TYPE, VFIO_BASE + 6) + +/* --------------- IOCTLs for DEVICE file descriptors --------------- */ + +/** + * VFIO_DEVICE_GET_INFO - _IOR(VFIO_TYPE, VFIO_BASE + 7, + * struct vfio_device_info) + * + * Retrieve information about the device. Fills in provided + * struct vfio_device_info. Caller sets argsz. + * Return: 0 on success, -errno on failure. + */ +struct vfio_device_info { + __u32 argsz; + __u32 flags; +#define VFIO_DEVICE_FLAGS_RESET (1 << 0) /* Device supports reset */ + __u32 num_regions; /* Max region index + 1 */ + __u32 num_irqs; /* Max IRQ index + 1 */ +}; +#define VFIO_DEVICE_GET_INFO _IO(VFIO_TYPE, VFIO_BASE + 7) + +/** + * VFIO_DEVICE_GET_REGION_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 8, + * struct vfio_region_info) + * + * Retrieve information about a device region. Caller provides + * struct vfio_region_info with index value set. Caller sets argsz. + * Implementation of region mapping is bus driver specific. This is + * intended to describe MMIO, I/O port, as well as bus specific + * regions (ex. PCI config space). Zero sized regions may be used + * to describe unimplemented regions (ex. unimplemented PCI BARs). + * Return: 0 on success, -errno on failure. + */ +struct vfio_region_info { + __u32 argsz; + __u32 flags; +#define VFIO_REGION_INFO_FLAG_READ (1 << 0) /* Region supports read */ +#define VFIO_REGION_INFO_FLAG_WRITE (1 << 1) /* Region supports write */ +#define VFIO_REGION_INFO_FLAG_MMAP (1 << 2) /* Region supports mmap */ + __u32 index; /* Region index */ + __u32 resv; /* Reserved for alignment */ + __u64 size; /* Region size (bytes) */ + __u64 offset; /* Region offset from start of device fd */ +}; +#define VFIO_DEVICE_GET_REGION_INFO _IO(VFIO_TYPE, VFIO_BASE + 8) + +/** + * VFIO_DEVICE_GET_IRQ_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 9, + * struct vfio_irq_info) + * + * Retrieve information about a device IRQ. Caller provides + * struct vfio_irq_info with index value set. Caller sets argsz. + * Implementation of IRQ mapping is bus driver specific. Indexes + * using multiple IRQs are primarily intended to support MSI-like + * interrupt blocks. Zero count irq blocks may be used to describe + * unimplemented interrupt types. + * + * The EVENTFD flag indicates the interrupt index supports eventfd based + * signaling. + * + * The MASKABLE flags indicates the index supports MASK and UNMASK + * actions described below. + * + * AUTOMASKED indicates that after signaling, the interrupt line is + * automatically masked by VFIO and the user needs to unmask the line + * to receive new interrupts. This is primarily intended to distinguish + * level triggered interrupts. + * + * The NORESIZE flag indicates that the interrupt lines within the index + * are setup as a set and new subindexes cannot be enabled without first + * disabling the entire index. This is used for interrupts like PCI MSI + * and MSI-X where the driver may only use a subset of the available + * indexes, but VFIO needs to enable a specific number of vectors + * upfront. In the case of MSI-X, where the user can enable MSI-X and + * then add and unmask vectors, it's up to userspace to make the decision + * whether to allocate the maximum supported number of vectors or tear + * down setup and incrementally increase the vectors as each is enabled. + */ +struct vfio_irq_info { + __u32 argsz; + __u32 flags; +#define VFIO_IRQ_INFO_EVENTFD (1 << 0) +#define VFIO_IRQ_INFO_MASKABLE (1 << 1) +#define VFIO_IRQ_INFO_AUTOMASKED (1 << 2) +#define VFIO_IRQ_INFO_NORESIZE (1 << 3) + __u32 index; /* IRQ index */ + __u32 count; /* Number of IRQs within this index */ +}; +#define VFIO_DEVICE_GET_IRQ_INFO _IO(VFIO_TYPE, VFIO_BASE + 9) + +/** + * VFIO_DEVICE_SET_IRQS - _IOW(VFIO_TYPE, VFIO_BASE + 10, struct vfio_irq_set) + * + * Set signaling, masking, and unmasking of interrupts. Caller provides + * struct vfio_irq_set with all fields set. 'start' and 'count' indicate + * the range of subindexes being specified. + * + * The DATA flags specify the type of data provided. If DATA_NONE, the + * operation performs the specified action immediately on the specified + * interrupt(s). For example, to unmask AUTOMASKED interrupt [0,0]: + * flags = (DATA_NONE|ACTION_UNMASK), index = 0, start = 0, count = 1. + * + * DATA_BOOL allows sparse support for the same on arrays of interrupts. + * For example, to mask interrupts [0,1] and [0,3] (but not [0,2]): + * flags = (DATA_BOOL|ACTION_MASK), index = 0, start = 1, count = 3, + * data = {1,0,1} + * + * DATA_EVENTFD binds the specified ACTION to the provided __s32 eventfd. + * A value of -1 can be used to either de-assign interrupts if already + * assigned or skip un-assigned interrupts. For example, to set an eventfd + * to be trigger for interrupts [0,0] and [0,2]: + * flags = (DATA_EVENTFD|ACTION_TRIGGER), index = 0, start = 0, count = 3, + * data = {fd1, -1, fd2} + * If index [0,1] is previously set, two count = 1 ioctls calls would be + * required to set [0,0] and [0,2] without changing [0,1]. + * + * Once a signaling mechanism is set, DATA_BOOL or DATA_NONE can be used + * with ACTION_TRIGGER to perform kernel level interrupt loopback testing + * from userspace (ie. simulate hardware triggering). + * + * Setting of an event triggering mechanism to userspace for ACTION_TRIGGER + * enables the interrupt index for the device. Individual subindex interrupts + * can be disabled using the -1 value for DATA_EVENTFD or the index can be + * disabled as a whole with: flags = (DATA_NONE|ACTION_TRIGGER), count = 0. + * + * Note that ACTION_[UN]MASK specify user->kernel signaling (irqfds) while + * ACTION_TRIGGER specifies kernel->user signaling. + */ +struct vfio_irq_set { + __u32 argsz; + __u32 flags; +#define VFIO_IRQ_SET_DATA_NONE (1 << 0) /* Data not present */ +#define VFIO_IRQ_SET_DATA_BOOL (1 << 1) /* Data is bool (u8) */ +#define VFIO_IRQ_SET_DATA_EVENTFD (1 << 2) /* Data is eventfd (s32) */ +#define VFIO_IRQ_SET_ACTION_MASK (1 << 3) /* Mask interrupt */ +#define VFIO_IRQ_SET_ACTION_UNMASK (1 << 4) /* Unmask interrupt */ +#define VFIO_IRQ_SET_ACTION_TRIGGER (1 << 5) /* Trigger interrupt */ + __u32 index; + __u32 start; + __u32 count; + __u8 data[]; +}; +#define VFIO_DEVICE_SET_IRQS _IO(VFIO_TYPE, VFIO_BASE + 10) + +#define VFIO_IRQ_SET_DATA_TYPE_MASK (VFIO_IRQ_SET_DATA_NONE | \ + VFIO_IRQ_SET_DATA_BOOL | \ + VFIO_IRQ_SET_DATA_EVENTFD) +#define VFIO_IRQ_SET_ACTION_TYPE_MASK (VFIO_IRQ_SET_ACTION_MASK | \ + VFIO_IRQ_SET_ACTION_UNMASK | \ + VFIO_IRQ_SET_ACTION_TRIGGER) +/** + * VFIO_DEVICE_RESET - _IO(VFIO_TYPE, VFIO_BASE + 11) + * + * Reset a device. + */ +#define VFIO_DEVICE_RESET _IO(VFIO_TYPE, VFIO_BASE + 11) + +#endif /* VFIO_H */ -- cgit v1.2.3 From 73fa0d10d077d9521ee2dace2307ae2c9a965336 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 31 Jul 2012 08:16:23 -0600 Subject: vfio: Type1 IOMMU implementation This VFIO IOMMU backend is designed primarily for AMD-Vi and Intel VT-d hardware, but is potentially usable by anything supporting similar mapping functionality. We arbitrarily call this a Type1 backend for lack of a better name. This backend has no IOVA or host memory mapping restrictions for the user and is optimized for relatively static mappings. Mapped areas are pinned into system memory. Signed-off-by: Alex Williamson --- drivers/vfio/Kconfig | 6 + drivers/vfio/Makefile | 2 + drivers/vfio/vfio.c | 7 + drivers/vfio/vfio_iommu_type1.c | 753 ++++++++++++++++++++++++++++++++++++++++ include/linux/vfio.h | 54 ++- 5 files changed, 821 insertions(+), 1 deletion(-) create mode 100644 drivers/vfio/vfio_iommu_type1.c (limited to 'include') diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig index 9acb1e729bd6..128b97910b8e 100644 --- a/drivers/vfio/Kconfig +++ b/drivers/vfio/Kconfig @@ -1,6 +1,12 @@ +config VFIO_IOMMU_TYPE1 + tristate + depends on VFIO + default n + menuconfig VFIO tristate "VFIO Non-Privileged userspace driver framework" depends on IOMMU_API + select VFIO_IOMMU_TYPE1 if X86 help VFIO provides a framework for secure userspace device drivers. See Documentation/vfio.txt for more details. diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile index 7500a67a42a0..2398d4a0e38b 100644 --- a/drivers/vfio/Makefile +++ b/drivers/vfio/Makefile @@ -1 +1,3 @@ obj-$(CONFIG_VFIO) += vfio.o +obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o +obj-$(CONFIG_VFIO_PCI) += pci/ diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index 052e310aed72..9591e2b509d7 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c @@ -1376,6 +1376,13 @@ static int __init vfio_init(void) pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); + /* + * Attempt to load known iommu-drivers. This gives us a working + * environment without the user needing to explicitly load iommu + * drivers. + */ + request_module_nowait("vfio_iommu_type1"); + return 0; err_groups_cdev: diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c new file mode 100644 index 000000000000..6f3fbc48a6c7 --- /dev/null +++ b/drivers/vfio/vfio_iommu_type1.c @@ -0,0 +1,753 @@ +/* + * VFIO: IOMMU DMA mapping support for Type1 IOMMU + * + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * 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. + * + * Derived from original vfio: + * Copyright 2010 Cisco Systems, Inc. All rights reserved. + * Author: Tom Lyon, pugs@cisco.com + * + * We arbitrarily define a Type1 IOMMU as one matching the below code. + * It could be called the x86 IOMMU as it's designed for AMD-Vi & Intel + * VT-d, but that makes it harder to re-use as theoretically anyone + * implementing a similar IOMMU could make use of this. We expect the + * IOMMU to support the IOMMU API and have few to no restrictions around + * the IOVA range that can be mapped. The Type1 IOMMU is currently + * optimized for relatively static mappings of a userspace process with + * userpsace pages pinned into memory. We also assume devices and IOMMU + * domains are PCI based as the IOMMU API is still centered around a + * device/bus interface rather than a group interface. + */ + +#include +#include +#include +#include +#include +#include +#include /* pci_bus_type */ +#include +#include +#include +#include +#include + +#define DRIVER_VERSION "0.2" +#define DRIVER_AUTHOR "Alex Williamson " +#define DRIVER_DESC "Type1 IOMMU driver for VFIO" + +static bool allow_unsafe_interrupts; +module_param_named(allow_unsafe_interrupts, + allow_unsafe_interrupts, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(allow_unsafe_interrupts, + "Enable VFIO IOMMU support for on platforms without interrupt remapping support."); + +struct vfio_iommu { + struct iommu_domain *domain; + struct mutex lock; + struct list_head dma_list; + struct list_head group_list; + bool cache; +}; + +struct vfio_dma { + struct list_head next; + dma_addr_t iova; /* Device address */ + unsigned long vaddr; /* Process virtual addr */ + long npage; /* Number of pages */ + int prot; /* IOMMU_READ/WRITE */ +}; + +struct vfio_group { + struct iommu_group *iommu_group; + struct list_head next; +}; + +/* + * This code handles mapping and unmapping of user data buffers + * into DMA'ble space using the IOMMU + */ + +#define NPAGE_TO_SIZE(npage) ((size_t)(npage) << PAGE_SHIFT) + +struct vwork { + struct mm_struct *mm; + long npage; + struct work_struct work; +}; + +/* delayed decrement/increment for locked_vm */ +static void vfio_lock_acct_bg(struct work_struct *work) +{ + struct vwork *vwork = container_of(work, struct vwork, work); + struct mm_struct *mm; + + mm = vwork->mm; + down_write(&mm->mmap_sem); + mm->locked_vm += vwork->npage; + up_write(&mm->mmap_sem); + mmput(mm); + kfree(vwork); +} + +static void vfio_lock_acct(long npage) +{ + struct vwork *vwork; + struct mm_struct *mm; + + if (!current->mm) + return; /* process exited */ + + if (down_write_trylock(¤t->mm->mmap_sem)) { + current->mm->locked_vm += npage; + up_write(¤t->mm->mmap_sem); + return; + } + + /* + * Couldn't get mmap_sem lock, so must setup to update + * mm->locked_vm later. If locked_vm were atomic, we + * wouldn't need this silliness + */ + vwork = kmalloc(sizeof(struct vwork), GFP_KERNEL); + if (!vwork) + return; + mm = get_task_mm(current); + if (!mm) { + kfree(vwork); + return; + } + INIT_WORK(&vwork->work, vfio_lock_acct_bg); + vwork->mm = mm; + vwork->npage = npage; + schedule_work(&vwork->work); +} + +/* + * Some mappings aren't backed by a struct page, for example an mmap'd + * MMIO range for our own or another device. These use a different + * pfn conversion and shouldn't be tracked as locked pages. + */ +static bool is_invalid_reserved_pfn(unsigned long pfn) +{ + if (pfn_valid(pfn)) { + bool reserved; + struct page *tail = pfn_to_page(pfn); + struct page *head = compound_trans_head(tail); + reserved = !!(PageReserved(head)); + if (head != tail) { + /* + * "head" is not a dangling pointer + * (compound_trans_head takes care of that) + * but the hugepage may have been split + * from under us (and we may not hold a + * reference count on the head page so it can + * be reused before we run PageReferenced), so + * we've to check PageTail before returning + * what we just read. + */ + smp_rmb(); + if (PageTail(tail)) + return reserved; + } + return PageReserved(tail); + } + + return true; +} + +static int put_pfn(unsigned long pfn, int prot) +{ + if (!is_invalid_reserved_pfn(pfn)) { + struct page *page = pfn_to_page(pfn); + if (prot & IOMMU_WRITE) + SetPageDirty(page); + put_page(page); + return 1; + } + return 0; +} + +/* Unmap DMA region */ +static long __vfio_dma_do_unmap(struct vfio_iommu *iommu, dma_addr_t iova, + long npage, int prot) +{ + long i, unlocked = 0; + + for (i = 0; i < npage; i++, iova += PAGE_SIZE) { + unsigned long pfn; + + pfn = iommu_iova_to_phys(iommu->domain, iova) >> PAGE_SHIFT; + if (pfn) { + iommu_unmap(iommu->domain, iova, PAGE_SIZE); + unlocked += put_pfn(pfn, prot); + } + } + return unlocked; +} + +static void vfio_dma_unmap(struct vfio_iommu *iommu, dma_addr_t iova, + long npage, int prot) +{ + long unlocked; + + unlocked = __vfio_dma_do_unmap(iommu, iova, npage, prot); + vfio_lock_acct(-unlocked); +} + +static int vaddr_get_pfn(unsigned long vaddr, int prot, unsigned long *pfn) +{ + struct page *page[1]; + struct vm_area_struct *vma; + int ret = -EFAULT; + + if (get_user_pages_fast(vaddr, 1, !!(prot & IOMMU_WRITE), page) == 1) { + *pfn = page_to_pfn(page[0]); + return 0; + } + + down_read(¤t->mm->mmap_sem); + + vma = find_vma_intersection(current->mm, vaddr, vaddr + 1); + + if (vma && vma->vm_flags & VM_PFNMAP) { + *pfn = ((vaddr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff; + if (is_invalid_reserved_pfn(*pfn)) + ret = 0; + } + + up_read(¤t->mm->mmap_sem); + + return ret; +} + +/* Map DMA region */ +static int __vfio_dma_map(struct vfio_iommu *iommu, dma_addr_t iova, + unsigned long vaddr, long npage, int prot) +{ + dma_addr_t start = iova; + long i, locked = 0; + int ret; + + /* Verify that pages are not already mapped */ + for (i = 0; i < npage; i++, iova += PAGE_SIZE) + if (iommu_iova_to_phys(iommu->domain, iova)) + return -EBUSY; + + iova = start; + + if (iommu->cache) + prot |= IOMMU_CACHE; + + /* + * XXX We break mappings into pages and use get_user_pages_fast to + * pin the pages in memory. It's been suggested that mlock might + * provide a more efficient mechanism, but nothing prevents the + * user from munlocking the pages, which could then allow the user + * access to random host memory. We also have no guarantee from the + * IOMMU API that the iommu driver can unmap sub-pages of previous + * mappings. This means we might lose an entire range if a single + * page within it is unmapped. Single page mappings are inefficient, + * but provide the most flexibility for now. + */ + for (i = 0; i < npage; i++, iova += PAGE_SIZE, vaddr += PAGE_SIZE) { + unsigned long pfn = 0; + + ret = vaddr_get_pfn(vaddr, prot, &pfn); + if (ret) { + __vfio_dma_do_unmap(iommu, start, i, prot); + return ret; + } + + /* + * Only add actual locked pages to accounting + * XXX We're effectively marking a page locked for every + * IOVA page even though it's possible the user could be + * backing multiple IOVAs with the same vaddr. This over- + * penalizes the user process, but we currently have no + * easy way to do this properly. + */ + if (!is_invalid_reserved_pfn(pfn)) + locked++; + + ret = iommu_map(iommu->domain, iova, + (phys_addr_t)pfn << PAGE_SHIFT, + PAGE_SIZE, prot); + if (ret) { + /* Back out mappings on error */ + put_pfn(pfn, prot); + __vfio_dma_do_unmap(iommu, start, i, prot); + return ret; + } + } + vfio_lock_acct(locked); + return 0; +} + +static inline bool ranges_overlap(dma_addr_t start1, size_t size1, + dma_addr_t start2, size_t size2) +{ + if (start1 < start2) + return (start2 - start1 < size1); + else if (start2 < start1) + return (start1 - start2 < size2); + return (size1 > 0 && size2 > 0); +} + +static struct vfio_dma *vfio_find_dma(struct vfio_iommu *iommu, + dma_addr_t start, size_t size) +{ + struct vfio_dma *dma; + + list_for_each_entry(dma, &iommu->dma_list, next) { + if (ranges_overlap(dma->iova, NPAGE_TO_SIZE(dma->npage), + start, size)) + return dma; + } + return NULL; +} + +static long vfio_remove_dma_overlap(struct vfio_iommu *iommu, dma_addr_t start, + size_t size, struct vfio_dma *dma) +{ + struct vfio_dma *split; + long npage_lo, npage_hi; + + /* Existing dma region is completely covered, unmap all */ + if (start <= dma->iova && + start + size >= dma->iova + NPAGE_TO_SIZE(dma->npage)) { + vfio_dma_unmap(iommu, dma->iova, dma->npage, dma->prot); + list_del(&dma->next); + npage_lo = dma->npage; + kfree(dma); + return npage_lo; + } + + /* Overlap low address of existing range */ + if (start <= dma->iova) { + size_t overlap; + + overlap = start + size - dma->iova; + npage_lo = overlap >> PAGE_SHIFT; + + vfio_dma_unmap(iommu, dma->iova, npage_lo, dma->prot); + dma->iova += overlap; + dma->vaddr += overlap; + dma->npage -= npage_lo; + return npage_lo; + } + + /* Overlap high address of existing range */ + if (start + size >= dma->iova + NPAGE_TO_SIZE(dma->npage)) { + size_t overlap; + + overlap = dma->iova + NPAGE_TO_SIZE(dma->npage) - start; + npage_hi = overlap >> PAGE_SHIFT; + + vfio_dma_unmap(iommu, start, npage_hi, dma->prot); + dma->npage -= npage_hi; + return npage_hi; + } + + /* Split existing */ + npage_lo = (start - dma->iova) >> PAGE_SHIFT; + npage_hi = dma->npage - (size >> PAGE_SHIFT) - npage_lo; + + split = kzalloc(sizeof *split, GFP_KERNEL); + if (!split) + return -ENOMEM; + + vfio_dma_unmap(iommu, start, size >> PAGE_SHIFT, dma->prot); + + dma->npage = npage_lo; + + split->npage = npage_hi; + split->iova = start + size; + split->vaddr = dma->vaddr + NPAGE_TO_SIZE(npage_lo) + size; + split->prot = dma->prot; + list_add(&split->next, &iommu->dma_list); + return size >> PAGE_SHIFT; +} + +static int vfio_dma_do_unmap(struct vfio_iommu *iommu, + struct vfio_iommu_type1_dma_unmap *unmap) +{ + long ret = 0, npage = unmap->size >> PAGE_SHIFT; + struct vfio_dma *dma, *tmp; + uint64_t mask; + + mask = ((uint64_t)1 << __ffs(iommu->domain->ops->pgsize_bitmap)) - 1; + + if (unmap->iova & mask) + return -EINVAL; + if (unmap->size & mask) + return -EINVAL; + + /* XXX We still break these down into PAGE_SIZE */ + WARN_ON(mask & PAGE_MASK); + + mutex_lock(&iommu->lock); + + list_for_each_entry_safe(dma, tmp, &iommu->dma_list, next) { + if (ranges_overlap(dma->iova, NPAGE_TO_SIZE(dma->npage), + unmap->iova, unmap->size)) { + ret = vfio_remove_dma_overlap(iommu, unmap->iova, + unmap->size, dma); + if (ret > 0) + npage -= ret; + if (ret < 0 || npage == 0) + break; + } + } + mutex_unlock(&iommu->lock); + return ret > 0 ? 0 : (int)ret; +} + +static int vfio_dma_do_map(struct vfio_iommu *iommu, + struct vfio_iommu_type1_dma_map *map) +{ + struct vfio_dma *dma, *pdma = NULL; + dma_addr_t iova = map->iova; + unsigned long locked, lock_limit, vaddr = map->vaddr; + size_t size = map->size; + int ret = 0, prot = 0; + uint64_t mask; + long npage; + + mask = ((uint64_t)1 << __ffs(iommu->domain->ops->pgsize_bitmap)) - 1; + + /* READ/WRITE from device perspective */ + if (map->flags & VFIO_DMA_MAP_FLAG_WRITE) + prot |= IOMMU_WRITE; + if (map->flags & VFIO_DMA_MAP_FLAG_READ) + prot |= IOMMU_READ; + + if (!prot) + return -EINVAL; /* No READ/WRITE? */ + + if (vaddr & mask) + return -EINVAL; + if (iova & mask) + return -EINVAL; + if (size & mask) + return -EINVAL; + + /* XXX We still break these down into PAGE_SIZE */ + WARN_ON(mask & PAGE_MASK); + + /* Don't allow IOVA wrap */ + if (iova + size && iova + size < iova) + return -EINVAL; + + /* Don't allow virtual address wrap */ + if (vaddr + size && vaddr + size < vaddr) + return -EINVAL; + + npage = size >> PAGE_SHIFT; + if (!npage) + return -EINVAL; + + mutex_lock(&iommu->lock); + + if (vfio_find_dma(iommu, iova, size)) { + ret = -EBUSY; + goto out_lock; + } + + /* account for locked pages */ + locked = current->mm->locked_vm + npage; + lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; + if (locked > lock_limit && !capable(CAP_IPC_LOCK)) { + pr_warn("%s: RLIMIT_MEMLOCK (%ld) exceeded\n", + __func__, rlimit(RLIMIT_MEMLOCK)); + ret = -ENOMEM; + goto out_lock; + } + + ret = __vfio_dma_map(iommu, iova, vaddr, npage, prot); + if (ret) + goto out_lock; + + /* Check if we abut a region below - nothing below 0 */ + if (iova) { + dma = vfio_find_dma(iommu, iova - 1, 1); + if (dma && dma->prot == prot && + dma->vaddr + NPAGE_TO_SIZE(dma->npage) == vaddr) { + + dma->npage += npage; + iova = dma->iova; + vaddr = dma->vaddr; + npage = dma->npage; + size = NPAGE_TO_SIZE(npage); + + pdma = dma; + } + } + + /* Check if we abut a region above - nothing above ~0 + 1 */ + if (iova + size) { + dma = vfio_find_dma(iommu, iova + size, 1); + if (dma && dma->prot == prot && + dma->vaddr == vaddr + size) { + + dma->npage += npage; + dma->iova = iova; + dma->vaddr = vaddr; + + /* + * If merged above and below, remove previously + * merged entry. New entry covers it. + */ + if (pdma) { + list_del(&pdma->next); + kfree(pdma); + } + pdma = dma; + } + } + + /* Isolated, new region */ + if (!pdma) { + dma = kzalloc(sizeof *dma, GFP_KERNEL); + if (!dma) { + ret = -ENOMEM; + vfio_dma_unmap(iommu, iova, npage, prot); + goto out_lock; + } + + dma->npage = npage; + dma->iova = iova; + dma->vaddr = vaddr; + dma->prot = prot; + list_add(&dma->next, &iommu->dma_list); + } + +out_lock: + mutex_unlock(&iommu->lock); + return ret; +} + +static int vfio_iommu_type1_attach_group(void *iommu_data, + struct iommu_group *iommu_group) +{ + struct vfio_iommu *iommu = iommu_data; + struct vfio_group *group, *tmp; + int ret; + + group = kzalloc(sizeof(*group), GFP_KERNEL); + if (!group) + return -ENOMEM; + + mutex_lock(&iommu->lock); + + list_for_each_entry(tmp, &iommu->group_list, next) { + if (tmp->iommu_group == iommu_group) { + mutex_unlock(&iommu->lock); + kfree(group); + return -EINVAL; + } + } + + /* + * TODO: Domain have capabilities that might change as we add + * groups (see iommu->cache, currently never set). Check for + * them and potentially disallow groups to be attached when it + * would change capabilities (ugh). + */ + ret = iommu_attach_group(iommu->domain, iommu_group); + if (ret) { + mutex_unlock(&iommu->lock); + kfree(group); + return ret; + } + + group->iommu_group = iommu_group; + list_add(&group->next, &iommu->group_list); + + mutex_unlock(&iommu->lock); + + return 0; +} + +static void vfio_iommu_type1_detach_group(void *iommu_data, + struct iommu_group *iommu_group) +{ + struct vfio_iommu *iommu = iommu_data; + struct vfio_group *group; + + mutex_lock(&iommu->lock); + + list_for_each_entry(group, &iommu->group_list, next) { + if (group->iommu_group == iommu_group) { + iommu_detach_group(iommu->domain, iommu_group); + list_del(&group->next); + kfree(group); + break; + } + } + + mutex_unlock(&iommu->lock); +} + +static void *vfio_iommu_type1_open(unsigned long arg) +{ + struct vfio_iommu *iommu; + + if (arg != VFIO_TYPE1_IOMMU) + return ERR_PTR(-EINVAL); + + iommu = kzalloc(sizeof(*iommu), GFP_KERNEL); + if (!iommu) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&iommu->group_list); + INIT_LIST_HEAD(&iommu->dma_list); + mutex_init(&iommu->lock); + + /* + * Wish we didn't have to know about bus_type here. + */ + iommu->domain = iommu_domain_alloc(&pci_bus_type); + if (!iommu->domain) { + kfree(iommu); + return ERR_PTR(-EIO); + } + + /* + * Wish we could specify required capabilities rather than create + * a domain, see what comes out and hope it doesn't change along + * the way. Fortunately we know interrupt remapping is global for + * our iommus. + */ + if (!allow_unsafe_interrupts && + !iommu_domain_has_cap(iommu->domain, IOMMU_CAP_INTR_REMAP)) { + pr_warn("%s: No interrupt remapping support. Use the module param \"allow_unsafe_interrupts\" to enable VFIO IOMMU support on this platform\n", + __func__); + iommu_domain_free(iommu->domain); + kfree(iommu); + return ERR_PTR(-EPERM); + } + + return iommu; +} + +static void vfio_iommu_type1_release(void *iommu_data) +{ + struct vfio_iommu *iommu = iommu_data; + struct vfio_group *group, *group_tmp; + struct vfio_dma *dma, *dma_tmp; + + list_for_each_entry_safe(group, group_tmp, &iommu->group_list, next) { + iommu_detach_group(iommu->domain, group->iommu_group); + list_del(&group->next); + kfree(group); + } + + list_for_each_entry_safe(dma, dma_tmp, &iommu->dma_list, next) { + vfio_dma_unmap(iommu, dma->iova, dma->npage, dma->prot); + list_del(&dma->next); + kfree(dma); + } + + iommu_domain_free(iommu->domain); + iommu->domain = NULL; + kfree(iommu); +} + +static long vfio_iommu_type1_ioctl(void *iommu_data, + unsigned int cmd, unsigned long arg) +{ + struct vfio_iommu *iommu = iommu_data; + unsigned long minsz; + + if (cmd == VFIO_CHECK_EXTENSION) { + switch (arg) { + case VFIO_TYPE1_IOMMU: + return 1; + default: + return 0; + } + } else if (cmd == VFIO_IOMMU_GET_INFO) { + struct vfio_iommu_type1_info info; + + minsz = offsetofend(struct vfio_iommu_type1_info, iova_pgsizes); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + info.flags = 0; + + info.iova_pgsizes = iommu->domain->ops->pgsize_bitmap; + + return copy_to_user((void __user *)arg, &info, minsz); + + } else if (cmd == VFIO_IOMMU_MAP_DMA) { + struct vfio_iommu_type1_dma_map map; + uint32_t mask = VFIO_DMA_MAP_FLAG_READ | + VFIO_DMA_MAP_FLAG_WRITE; + + minsz = offsetofend(struct vfio_iommu_type1_dma_map, size); + + if (copy_from_user(&map, (void __user *)arg, minsz)) + return -EFAULT; + + if (map.argsz < minsz || map.flags & ~mask) + return -EINVAL; + + return vfio_dma_do_map(iommu, &map); + + } else if (cmd == VFIO_IOMMU_UNMAP_DMA) { + struct vfio_iommu_type1_dma_unmap unmap; + + minsz = offsetofend(struct vfio_iommu_type1_dma_unmap, size); + + if (copy_from_user(&unmap, (void __user *)arg, minsz)) + return -EFAULT; + + if (unmap.argsz < minsz || unmap.flags) + return -EINVAL; + + return vfio_dma_do_unmap(iommu, &unmap); + } + + return -ENOTTY; +} + +static const struct vfio_iommu_driver_ops vfio_iommu_driver_ops_type1 = { + .name = "vfio-iommu-type1", + .owner = THIS_MODULE, + .open = vfio_iommu_type1_open, + .release = vfio_iommu_type1_release, + .ioctl = vfio_iommu_type1_ioctl, + .attach_group = vfio_iommu_type1_attach_group, + .detach_group = vfio_iommu_type1_detach_group, +}; + +static int __init vfio_iommu_type1_init(void) +{ + if (!iommu_present(&pci_bus_type)) + return -ENODEV; + + return vfio_register_iommu_driver(&vfio_iommu_driver_ops_type1); +} + +static void __exit vfio_iommu_type1_cleanup(void) +{ + vfio_unregister_iommu_driver(&vfio_iommu_driver_ops_type1); +} + +module_init(vfio_iommu_type1_init); +module_exit(vfio_iommu_type1_cleanup); + +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/include/linux/vfio.h b/include/linux/vfio.h index 03e56a5154b6..acb046fd5b70 100644 --- a/include/linux/vfio.h +++ b/include/linux/vfio.h @@ -98,7 +98,7 @@ extern void vfio_unregister_iommu_driver( /* Extensions */ -/* None yet */ +#define VFIO_TYPE1_IOMMU 1 /* * The IOCTL interface is designed for extensibility by embedding the @@ -364,4 +364,56 @@ struct vfio_irq_set { */ #define VFIO_DEVICE_RESET _IO(VFIO_TYPE, VFIO_BASE + 11) +/* -------- API for Type1 VFIO IOMMU -------- */ + +/** + * VFIO_IOMMU_GET_INFO - _IOR(VFIO_TYPE, VFIO_BASE + 12, struct vfio_iommu_info) + * + * Retrieve information about the IOMMU object. Fills in provided + * struct vfio_iommu_info. Caller sets argsz. + * + * XXX Should we do these by CHECK_EXTENSION too? + */ +struct vfio_iommu_type1_info { + __u32 argsz; + __u32 flags; +#define VFIO_IOMMU_INFO_PGSIZES (1 << 0) /* supported page sizes info */ + __u64 iova_pgsizes; /* Bitmap of supported page sizes */ +}; + +#define VFIO_IOMMU_GET_INFO _IO(VFIO_TYPE, VFIO_BASE + 12) + +/** + * VFIO_IOMMU_MAP_DMA - _IOW(VFIO_TYPE, VFIO_BASE + 13, struct vfio_dma_map) + * + * Map process virtual addresses to IO virtual addresses using the + * provided struct vfio_dma_map. Caller sets argsz. READ &/ WRITE required. + */ +struct vfio_iommu_type1_dma_map { + __u32 argsz; + __u32 flags; +#define VFIO_DMA_MAP_FLAG_READ (1 << 0) /* readable from device */ +#define VFIO_DMA_MAP_FLAG_WRITE (1 << 1) /* writable from device */ + __u64 vaddr; /* Process virtual address */ + __u64 iova; /* IO virtual address */ + __u64 size; /* Size of mapping (bytes) */ +}; + +#define VFIO_IOMMU_MAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 13) + +/** + * VFIO_IOMMU_UNMAP_DMA - _IOW(VFIO_TYPE, VFIO_BASE + 14, struct vfio_dma_unmap) + * + * Unmap IO virtual addresses using the provided struct vfio_dma_unmap. + * Caller sets argsz. + */ +struct vfio_iommu_type1_dma_unmap { + __u32 argsz; + __u32 flags; + __u64 iova; /* IO virtual address */ + __u64 size; /* Size of mapping (bytes) */ +}; + +#define VFIO_IOMMU_UNMAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 14) + #endif /* VFIO_H */ -- cgit v1.2.3 From 89e1f7d4c66d85f42c3d52ea3866eb10cadf6153 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 31 Jul 2012 08:16:24 -0600 Subject: vfio: Add PCI device driver Add PCI device support for VFIO. PCI devices expose regions for accessing config space, I/O port space, and MMIO areas of the device. PCI config access is virtualized in the kernel, allowing us to ensure the integrity of the system, by preventing various accesses while reducing duplicate support across various userspace drivers. I/O port supports read/write access while MMIO also supports mmap of sufficiently sized regions. Support for INTx, MSI, and MSI-X interrupts are provided using eventfds to userspace. Signed-off-by: Alex Williamson --- drivers/vfio/Kconfig | 2 + drivers/vfio/pci/Kconfig | 8 + drivers/vfio/pci/Makefile | 4 + drivers/vfio/pci/vfio_pci.c | 579 +++++++++++++ drivers/vfio/pci/vfio_pci_config.c | 1540 +++++++++++++++++++++++++++++++++++ drivers/vfio/pci/vfio_pci_intrs.c | 740 +++++++++++++++++ drivers/vfio/pci/vfio_pci_private.h | 91 +++ drivers/vfio/pci/vfio_pci_rdwr.c | 269 ++++++ include/linux/vfio.h | 26 + 9 files changed, 3259 insertions(+) create mode 100644 drivers/vfio/pci/Kconfig create mode 100644 drivers/vfio/pci/Makefile create mode 100644 drivers/vfio/pci/vfio_pci.c create mode 100644 drivers/vfio/pci/vfio_pci_config.c create mode 100644 drivers/vfio/pci/vfio_pci_intrs.c create mode 100644 drivers/vfio/pci/vfio_pci_private.h create mode 100644 drivers/vfio/pci/vfio_pci_rdwr.c (limited to 'include') diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig index 128b97910b8e..7cd5dec0abd1 100644 --- a/drivers/vfio/Kconfig +++ b/drivers/vfio/Kconfig @@ -12,3 +12,5 @@ menuconfig VFIO See Documentation/vfio.txt for more details. If you don't know what to do here, say N. + +source "drivers/vfio/pci/Kconfig" diff --git a/drivers/vfio/pci/Kconfig b/drivers/vfio/pci/Kconfig new file mode 100644 index 000000000000..5980758563eb --- /dev/null +++ b/drivers/vfio/pci/Kconfig @@ -0,0 +1,8 @@ +config VFIO_PCI + tristate "VFIO support for PCI devices" + depends on VFIO && PCI && EVENTFD + help + Support for the PCI VFIO bus driver. This is required to make + use of PCI drivers using the VFIO framework. + + If you don't know what to do here, say N. diff --git a/drivers/vfio/pci/Makefile b/drivers/vfio/pci/Makefile new file mode 100644 index 000000000000..131079255fd9 --- /dev/null +++ b/drivers/vfio/pci/Makefile @@ -0,0 +1,4 @@ + +vfio-pci-y := vfio_pci.o vfio_pci_intrs.o vfio_pci_rdwr.o vfio_pci_config.o + +obj-$(CONFIG_VFIO_PCI) += vfio-pci.o diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c new file mode 100644 index 000000000000..6968b7232232 --- /dev/null +++ b/drivers/vfio/pci/vfio_pci.c @@ -0,0 +1,579 @@ +/* + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * 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. + * + * Derived from original vfio: + * Copyright 2010 Cisco Systems, Inc. All rights reserved. + * Author: Tom Lyon, pugs@cisco.com + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vfio_pci_private.h" + +#define DRIVER_VERSION "0.2" +#define DRIVER_AUTHOR "Alex Williamson " +#define DRIVER_DESC "VFIO PCI - User Level meta-driver" + +static bool nointxmask; +module_param_named(nointxmask, nointxmask, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(nointxmask, + "Disable support for PCI 2.3 style INTx masking. If this resolves problems for specific devices, report lspci -vvvxxx to linux-pci@vger.kernel.org so the device can be fixed automatically via the broken_intx_masking flag."); + +static int vfio_pci_enable(struct vfio_pci_device *vdev) +{ + struct pci_dev *pdev = vdev->pdev; + int ret; + u16 cmd; + u8 msix_pos; + + vdev->reset_works = (pci_reset_function(pdev) == 0); + pci_save_state(pdev); + vdev->pci_saved_state = pci_store_saved_state(pdev); + if (!vdev->pci_saved_state) + pr_debug("%s: Couldn't store %s saved state\n", + __func__, dev_name(&pdev->dev)); + + ret = vfio_config_init(vdev); + if (ret) + goto out; + + if (likely(!nointxmask)) + vdev->pci_2_3 = pci_intx_mask_supported(pdev); + + pci_read_config_word(pdev, PCI_COMMAND, &cmd); + if (vdev->pci_2_3 && (cmd & PCI_COMMAND_INTX_DISABLE)) { + cmd &= ~PCI_COMMAND_INTX_DISABLE; + pci_write_config_word(pdev, PCI_COMMAND, cmd); + } + + msix_pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX); + if (msix_pos) { + u16 flags; + u32 table; + + pci_read_config_word(pdev, msix_pos + PCI_MSIX_FLAGS, &flags); + pci_read_config_dword(pdev, msix_pos + PCI_MSIX_TABLE, &table); + + vdev->msix_bar = table & PCI_MSIX_FLAGS_BIRMASK; + vdev->msix_offset = table & ~PCI_MSIX_FLAGS_BIRMASK; + vdev->msix_size = ((flags & PCI_MSIX_FLAGS_QSIZE) + 1) * 16; + } else + vdev->msix_bar = 0xFF; + + ret = pci_enable_device(pdev); + if (ret) + goto out; + + return ret; + +out: + kfree(vdev->pci_saved_state); + vdev->pci_saved_state = NULL; + vfio_config_free(vdev); + return ret; +} + +static void vfio_pci_disable(struct vfio_pci_device *vdev) +{ + int bar; + + pci_disable_device(vdev->pdev); + + vfio_pci_set_irqs_ioctl(vdev, VFIO_IRQ_SET_DATA_NONE | + VFIO_IRQ_SET_ACTION_TRIGGER, + vdev->irq_type, 0, 0, NULL); + + vdev->virq_disabled = false; + + vfio_config_free(vdev); + + pci_reset_function(vdev->pdev); + + if (pci_load_and_free_saved_state(vdev->pdev, + &vdev->pci_saved_state) == 0) + pci_restore_state(vdev->pdev); + else + pr_info("%s: Couldn't reload %s saved state\n", + __func__, dev_name(&vdev->pdev->dev)); + + for (bar = PCI_STD_RESOURCES; bar <= PCI_STD_RESOURCE_END; bar++) { + if (!vdev->barmap[bar]) + continue; + pci_iounmap(vdev->pdev, vdev->barmap[bar]); + pci_release_selected_regions(vdev->pdev, 1 << bar); + vdev->barmap[bar] = NULL; + } +} + +static void vfio_pci_release(void *device_data) +{ + struct vfio_pci_device *vdev = device_data; + + if (atomic_dec_and_test(&vdev->refcnt)) + vfio_pci_disable(vdev); + + module_put(THIS_MODULE); +} + +static int vfio_pci_open(void *device_data) +{ + struct vfio_pci_device *vdev = device_data; + + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + + if (atomic_inc_return(&vdev->refcnt) == 1) { + int ret = vfio_pci_enable(vdev); + if (ret) { + module_put(THIS_MODULE); + return ret; + } + } + + return 0; +} + +static int vfio_pci_get_irq_count(struct vfio_pci_device *vdev, int irq_type) +{ + if (irq_type == VFIO_PCI_INTX_IRQ_INDEX) { + u8 pin; + pci_read_config_byte(vdev->pdev, PCI_INTERRUPT_PIN, &pin); + if (pin) + return 1; + + } else if (irq_type == VFIO_PCI_MSI_IRQ_INDEX) { + u8 pos; + u16 flags; + + pos = pci_find_capability(vdev->pdev, PCI_CAP_ID_MSI); + if (pos) { + pci_read_config_word(vdev->pdev, + pos + PCI_MSI_FLAGS, &flags); + + return 1 << (flags & PCI_MSI_FLAGS_QMASK); + } + } else if (irq_type == VFIO_PCI_MSIX_IRQ_INDEX) { + u8 pos; + u16 flags; + + pos = pci_find_capability(vdev->pdev, PCI_CAP_ID_MSIX); + if (pos) { + pci_read_config_word(vdev->pdev, + pos + PCI_MSIX_FLAGS, &flags); + + return (flags & PCI_MSIX_FLAGS_QSIZE) + 1; + } + } + + return 0; +} + +static long vfio_pci_ioctl(void *device_data, + unsigned int cmd, unsigned long arg) +{ + struct vfio_pci_device *vdev = device_data; + unsigned long minsz; + + if (cmd == VFIO_DEVICE_GET_INFO) { + struct vfio_device_info info; + + minsz = offsetofend(struct vfio_device_info, num_irqs); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + info.flags = VFIO_DEVICE_FLAGS_PCI; + + if (vdev->reset_works) + info.flags |= VFIO_DEVICE_FLAGS_RESET; + + info.num_regions = VFIO_PCI_NUM_REGIONS; + info.num_irqs = VFIO_PCI_NUM_IRQS; + + return copy_to_user((void __user *)arg, &info, minsz); + + } else if (cmd == VFIO_DEVICE_GET_REGION_INFO) { + struct pci_dev *pdev = vdev->pdev; + struct vfio_region_info info; + + minsz = offsetofend(struct vfio_region_info, offset); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + switch (info.index) { + case VFIO_PCI_CONFIG_REGION_INDEX: + info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index); + info.size = pdev->cfg_size; + info.flags = VFIO_REGION_INFO_FLAG_READ | + VFIO_REGION_INFO_FLAG_WRITE; + break; + case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX: + info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index); + info.size = pci_resource_len(pdev, info.index); + if (!info.size) { + info.flags = 0; + break; + } + + info.flags = VFIO_REGION_INFO_FLAG_READ | + VFIO_REGION_INFO_FLAG_WRITE; + if (pci_resource_flags(pdev, info.index) & + IORESOURCE_MEM && info.size >= PAGE_SIZE) + info.flags |= VFIO_REGION_INFO_FLAG_MMAP; + break; + case VFIO_PCI_ROM_REGION_INDEX: + { + void __iomem *io; + size_t size; + + info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index); + info.flags = 0; + + /* Report the BAR size, not the ROM size */ + info.size = pci_resource_len(pdev, info.index); + if (!info.size) + break; + + /* Is it really there? */ + io = pci_map_rom(pdev, &size); + if (!io || !size) { + info.size = 0; + break; + } + pci_unmap_rom(pdev, io); + + info.flags = VFIO_REGION_INFO_FLAG_READ; + break; + } + default: + return -EINVAL; + } + + return copy_to_user((void __user *)arg, &info, minsz); + + } else if (cmd == VFIO_DEVICE_GET_IRQ_INFO) { + struct vfio_irq_info info; + + minsz = offsetofend(struct vfio_irq_info, count); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz || info.index >= VFIO_PCI_NUM_IRQS) + return -EINVAL; + + info.flags = VFIO_IRQ_INFO_EVENTFD; + + info.count = vfio_pci_get_irq_count(vdev, info.index); + + if (info.index == VFIO_PCI_INTX_IRQ_INDEX) + info.flags |= (VFIO_IRQ_INFO_MASKABLE | + VFIO_IRQ_INFO_AUTOMASKED); + else + info.flags |= VFIO_IRQ_INFO_NORESIZE; + + return copy_to_user((void __user *)arg, &info, minsz); + + } else if (cmd == VFIO_DEVICE_SET_IRQS) { + struct vfio_irq_set hdr; + u8 *data = NULL; + int ret = 0; + + minsz = offsetofend(struct vfio_irq_set, count); + + if (copy_from_user(&hdr, (void __user *)arg, minsz)) + return -EFAULT; + + if (hdr.argsz < minsz || hdr.index >= VFIO_PCI_NUM_IRQS || + hdr.flags & ~(VFIO_IRQ_SET_DATA_TYPE_MASK | + VFIO_IRQ_SET_ACTION_TYPE_MASK)) + return -EINVAL; + + if (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE)) { + size_t size; + + if (hdr.flags & VFIO_IRQ_SET_DATA_BOOL) + size = sizeof(uint8_t); + else if (hdr.flags & VFIO_IRQ_SET_DATA_EVENTFD) + size = sizeof(int32_t); + else + return -EINVAL; + + if (hdr.argsz - minsz < hdr.count * size || + hdr.count > vfio_pci_get_irq_count(vdev, hdr.index)) + return -EINVAL; + + data = kmalloc(hdr.count * size, GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (copy_from_user(data, (void __user *)(arg + minsz), + hdr.count * size)) { + kfree(data); + return -EFAULT; + } + } + + mutex_lock(&vdev->igate); + + ret = vfio_pci_set_irqs_ioctl(vdev, hdr.flags, hdr.index, + hdr.start, hdr.count, data); + + mutex_unlock(&vdev->igate); + kfree(data); + + return ret; + + } else if (cmd == VFIO_DEVICE_RESET) + return vdev->reset_works ? + pci_reset_function(vdev->pdev) : -EINVAL; + + return -ENOTTY; +} + +static ssize_t vfio_pci_read(void *device_data, char __user *buf, + size_t count, loff_t *ppos) +{ + unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); + struct vfio_pci_device *vdev = device_data; + struct pci_dev *pdev = vdev->pdev; + + if (index >= VFIO_PCI_NUM_REGIONS) + return -EINVAL; + + if (index == VFIO_PCI_CONFIG_REGION_INDEX) + return vfio_pci_config_readwrite(vdev, buf, count, ppos, false); + else if (index == VFIO_PCI_ROM_REGION_INDEX) + return vfio_pci_mem_readwrite(vdev, buf, count, ppos, false); + else if (pci_resource_flags(pdev, index) & IORESOURCE_IO) + return vfio_pci_io_readwrite(vdev, buf, count, ppos, false); + else if (pci_resource_flags(pdev, index) & IORESOURCE_MEM) + return vfio_pci_mem_readwrite(vdev, buf, count, ppos, false); + + return -EINVAL; +} + +static ssize_t vfio_pci_write(void *device_data, const char __user *buf, + size_t count, loff_t *ppos) +{ + unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); + struct vfio_pci_device *vdev = device_data; + struct pci_dev *pdev = vdev->pdev; + + if (index >= VFIO_PCI_NUM_REGIONS) + return -EINVAL; + + if (index == VFIO_PCI_CONFIG_REGION_INDEX) + return vfio_pci_config_readwrite(vdev, (char __user *)buf, + count, ppos, true); + else if (index == VFIO_PCI_ROM_REGION_INDEX) + return -EINVAL; + else if (pci_resource_flags(pdev, index) & IORESOURCE_IO) + return vfio_pci_io_readwrite(vdev, (char __user *)buf, + count, ppos, true); + else if (pci_resource_flags(pdev, index) & IORESOURCE_MEM) { + return vfio_pci_mem_readwrite(vdev, (char __user *)buf, + count, ppos, true); + } + + return -EINVAL; +} + +static int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma) +{ + struct vfio_pci_device *vdev = device_data; + struct pci_dev *pdev = vdev->pdev; + unsigned int index; + u64 phys_len, req_len, pgoff, req_start, phys; + int ret; + + index = vma->vm_pgoff >> (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT); + + if (vma->vm_end < vma->vm_start) + return -EINVAL; + if ((vma->vm_flags & VM_SHARED) == 0) + return -EINVAL; + if (index >= VFIO_PCI_ROM_REGION_INDEX) + return -EINVAL; + if (!(pci_resource_flags(pdev, index) & IORESOURCE_MEM)) + return -EINVAL; + + phys_len = pci_resource_len(pdev, index); + req_len = vma->vm_end - vma->vm_start; + pgoff = vma->vm_pgoff & + ((1U << (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT)) - 1); + req_start = pgoff << PAGE_SHIFT; + + if (phys_len < PAGE_SIZE || req_start + req_len > phys_len) + return -EINVAL; + + if (index == vdev->msix_bar) { + /* + * Disallow mmaps overlapping the MSI-X table; users don't + * get to touch this directly. We could find somewhere + * else to map the overlap, but page granularity is only + * a recommendation, not a requirement, so the user needs + * to know which bits are real. Requiring them to mmap + * around the table makes that clear. + */ + + /* If neither entirely above nor below, then it overlaps */ + if (!(req_start >= vdev->msix_offset + vdev->msix_size || + req_start + req_len <= vdev->msix_offset)) + return -EINVAL; + } + + /* + * Even though we don't make use of the barmap for the mmap, + * we need to request the region and the barmap tracks that. + */ + if (!vdev->barmap[index]) { + ret = pci_request_selected_regions(pdev, + 1 << index, "vfio-pci"); + if (ret) + return ret; + + vdev->barmap[index] = pci_iomap(pdev, index, 0); + } + + vma->vm_private_data = vdev; + vma->vm_flags |= (VM_IO | VM_RESERVED); + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + phys = (pci_resource_start(pdev, index) >> PAGE_SHIFT) + pgoff; + + return remap_pfn_range(vma, vma->vm_start, phys, + req_len, vma->vm_page_prot); +} + +static const struct vfio_device_ops vfio_pci_ops = { + .name = "vfio-pci", + .open = vfio_pci_open, + .release = vfio_pci_release, + .ioctl = vfio_pci_ioctl, + .read = vfio_pci_read, + .write = vfio_pci_write, + .mmap = vfio_pci_mmap, +}; + +static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + u8 type; + struct vfio_pci_device *vdev; + struct iommu_group *group; + int ret; + + pci_read_config_byte(pdev, PCI_HEADER_TYPE, &type); + if ((type & PCI_HEADER_TYPE) != PCI_HEADER_TYPE_NORMAL) + return -EINVAL; + + group = iommu_group_get(&pdev->dev); + if (!group) + return -EINVAL; + + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); + if (!vdev) { + iommu_group_put(group); + return -ENOMEM; + } + + vdev->pdev = pdev; + vdev->irq_type = VFIO_PCI_NUM_IRQS; + mutex_init(&vdev->igate); + spin_lock_init(&vdev->irqlock); + atomic_set(&vdev->refcnt, 0); + + ret = vfio_add_group_dev(&pdev->dev, &vfio_pci_ops, vdev); + if (ret) { + iommu_group_put(group); + kfree(vdev); + } + + return ret; +} + +static void vfio_pci_remove(struct pci_dev *pdev) +{ + struct vfio_pci_device *vdev; + + vdev = vfio_del_group_dev(&pdev->dev); + if (!vdev) + return; + + iommu_group_put(pdev->dev.iommu_group); + kfree(vdev); +} + +static struct pci_driver vfio_pci_driver = { + .name = "vfio-pci", + .id_table = NULL, /* only dynamic ids */ + .probe = vfio_pci_probe, + .remove = vfio_pci_remove, +}; + +static void __exit vfio_pci_cleanup(void) +{ + pci_unregister_driver(&vfio_pci_driver); + vfio_pci_virqfd_exit(); + vfio_pci_uninit_perm_bits(); +} + +static int __init vfio_pci_init(void) +{ + int ret; + + /* Allocate shared config space permision data used by all devices */ + ret = vfio_pci_init_perm_bits(); + if (ret) + return ret; + + /* Start the virqfd cleanup handler */ + ret = vfio_pci_virqfd_init(); + if (ret) + goto out_virqfd; + + /* Register and scan for devices */ + ret = pci_register_driver(&vfio_pci_driver); + if (ret) + goto out_driver; + + return 0; + +out_virqfd: + vfio_pci_virqfd_exit(); +out_driver: + vfio_pci_uninit_perm_bits(); + return ret; +} + +module_init(vfio_pci_init); +module_exit(vfio_pci_cleanup); + +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c new file mode 100644 index 000000000000..8b8f7d11e102 --- /dev/null +++ b/drivers/vfio/pci/vfio_pci_config.c @@ -0,0 +1,1540 @@ +/* + * VFIO PCI config space virtualization + * + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * 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. + * + * Derived from original vfio: + * Copyright 2010 Cisco Systems, Inc. All rights reserved. + * Author: Tom Lyon, pugs@cisco.com + */ + +/* + * This code handles reading and writing of PCI configuration registers. + * This is hairy because we want to allow a lot of flexibility to the + * user driver, but cannot trust it with all of the config fields. + * Tables determine which fields can be read and written, as well as + * which fields are 'virtualized' - special actions and translations to + * make it appear to the user that he has control, when in fact things + * must be negotiated with the underlying OS. + */ + +#include +#include +#include +#include + +#include "vfio_pci_private.h" + +#define PCI_CFG_SPACE_SIZE 256 + +/* Useful "pseudo" capabilities */ +#define PCI_CAP_ID_BASIC 0 +#define PCI_CAP_ID_INVALID 0xFF + +#define is_bar(offset) \ + ((offset >= PCI_BASE_ADDRESS_0 && offset < PCI_BASE_ADDRESS_5 + 4) || \ + (offset >= PCI_ROM_ADDRESS && offset < PCI_ROM_ADDRESS + 4)) + +/* + * Lengths of PCI Config Capabilities + * 0: Removed from the user visible capability list + * FF: Variable length + */ +static u8 pci_cap_length[] = { + [PCI_CAP_ID_BASIC] = PCI_STD_HEADER_SIZEOF, /* pci config header */ + [PCI_CAP_ID_PM] = PCI_PM_SIZEOF, + [PCI_CAP_ID_AGP] = PCI_AGP_SIZEOF, + [PCI_CAP_ID_VPD] = PCI_CAP_VPD_SIZEOF, + [PCI_CAP_ID_SLOTID] = 0, /* bridge - don't care */ + [PCI_CAP_ID_MSI] = 0xFF, /* 10, 14, 20, or 24 */ + [PCI_CAP_ID_CHSWP] = 0, /* cpci - not yet */ + [PCI_CAP_ID_PCIX] = 0xFF, /* 8 or 24 */ + [PCI_CAP_ID_HT] = 0xFF, /* hypertransport */ + [PCI_CAP_ID_VNDR] = 0xFF, /* variable */ + [PCI_CAP_ID_DBG] = 0, /* debug - don't care */ + [PCI_CAP_ID_CCRC] = 0, /* cpci - not yet */ + [PCI_CAP_ID_SHPC] = 0, /* hotswap - not yet */ + [PCI_CAP_ID_SSVID] = 0, /* bridge - don't care */ + [PCI_CAP_ID_AGP3] = 0, /* AGP8x - not yet */ + [PCI_CAP_ID_SECDEV] = 0, /* secure device not yet */ + [PCI_CAP_ID_EXP] = 0xFF, /* 20 or 44 */ + [PCI_CAP_ID_MSIX] = PCI_CAP_MSIX_SIZEOF, + [PCI_CAP_ID_SATA] = 0xFF, + [PCI_CAP_ID_AF] = PCI_CAP_AF_SIZEOF, +}; + +/* + * Lengths of PCIe/PCI-X Extended Config Capabilities + * 0: Removed or masked from the user visible capabilty list + * FF: Variable length + */ +static u16 pci_ext_cap_length[] = { + [PCI_EXT_CAP_ID_ERR] = PCI_ERR_ROOT_COMMAND, + [PCI_EXT_CAP_ID_VC] = 0xFF, + [PCI_EXT_CAP_ID_DSN] = PCI_EXT_CAP_DSN_SIZEOF, + [PCI_EXT_CAP_ID_PWR] = PCI_EXT_CAP_PWR_SIZEOF, + [PCI_EXT_CAP_ID_RCLD] = 0, /* root only - don't care */ + [PCI_EXT_CAP_ID_RCILC] = 0, /* root only - don't care */ + [PCI_EXT_CAP_ID_RCEC] = 0, /* root only - don't care */ + [PCI_EXT_CAP_ID_MFVC] = 0xFF, + [PCI_EXT_CAP_ID_VC9] = 0xFF, /* same as CAP_ID_VC */ + [PCI_EXT_CAP_ID_RCRB] = 0, /* root only - don't care */ + [PCI_EXT_CAP_ID_VNDR] = 0xFF, + [PCI_EXT_CAP_ID_CAC] = 0, /* obsolete */ + [PCI_EXT_CAP_ID_ACS] = 0xFF, + [PCI_EXT_CAP_ID_ARI] = PCI_EXT_CAP_ARI_SIZEOF, + [PCI_EXT_CAP_ID_ATS] = PCI_EXT_CAP_ATS_SIZEOF, + [PCI_EXT_CAP_ID_SRIOV] = PCI_EXT_CAP_SRIOV_SIZEOF, + [PCI_EXT_CAP_ID_MRIOV] = 0, /* not yet */ + [PCI_EXT_CAP_ID_MCAST] = PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF, + [PCI_EXT_CAP_ID_PRI] = PCI_EXT_CAP_PRI_SIZEOF, + [PCI_EXT_CAP_ID_AMD_XXX] = 0, /* not yet */ + [PCI_EXT_CAP_ID_REBAR] = 0xFF, + [PCI_EXT_CAP_ID_DPA] = 0xFF, + [PCI_EXT_CAP_ID_TPH] = 0xFF, + [PCI_EXT_CAP_ID_LTR] = PCI_EXT_CAP_LTR_SIZEOF, + [PCI_EXT_CAP_ID_SECPCI] = 0, /* not yet */ + [PCI_EXT_CAP_ID_PMUX] = 0, /* not yet */ + [PCI_EXT_CAP_ID_PASID] = 0, /* not yet */ +}; + +/* + * Read/Write Permission Bits - one bit for each bit in capability + * Any field can be read if it exists, but what is read depends on + * whether the field is 'virtualized', or just pass thru to the + * hardware. Any virtualized field is also virtualized for writes. + * Writes are only permitted if they have a 1 bit here. + */ +struct perm_bits { + u8 *virt; /* read/write virtual data, not hw */ + u8 *write; /* writeable bits */ + int (*readfn)(struct vfio_pci_device *vdev, int pos, int count, + struct perm_bits *perm, int offset, __le32 *val); + int (*writefn)(struct vfio_pci_device *vdev, int pos, int count, + struct perm_bits *perm, int offset, __le32 val); +}; + +#define NO_VIRT 0 +#define ALL_VIRT 0xFFFFFFFFU +#define NO_WRITE 0 +#define ALL_WRITE 0xFFFFFFFFU + +static int vfio_user_config_read(struct pci_dev *pdev, int offset, + __le32 *val, int count) +{ + int ret = -EINVAL; + u32 tmp_val = 0; + + switch (count) { + case 1: + { + u8 tmp; + ret = pci_user_read_config_byte(pdev, offset, &tmp); + tmp_val = tmp; + break; + } + case 2: + { + u16 tmp; + ret = pci_user_read_config_word(pdev, offset, &tmp); + tmp_val = tmp; + break; + } + case 4: + ret = pci_user_read_config_dword(pdev, offset, &tmp_val); + break; + } + + *val = cpu_to_le32(tmp_val); + + return pcibios_err_to_errno(ret); +} + +static int vfio_user_config_write(struct pci_dev *pdev, int offset, + __le32 val, int count) +{ + int ret = -EINVAL; + u32 tmp_val = le32_to_cpu(val); + + switch (count) { + case 1: + ret = pci_user_write_config_byte(pdev, offset, tmp_val); + break; + case 2: + ret = pci_user_write_config_word(pdev, offset, tmp_val); + break; + case 4: + ret = pci_user_write_config_dword(pdev, offset, tmp_val); + break; + } + + return pcibios_err_to_errno(ret); +} + +static int vfio_default_config_read(struct vfio_pci_device *vdev, int pos, + int count, struct perm_bits *perm, + int offset, __le32 *val) +{ + __le32 virt = 0; + + memcpy(val, vdev->vconfig + pos, count); + + memcpy(&virt, perm->virt + offset, count); + + /* Any non-virtualized bits? */ + if (cpu_to_le32(~0U >> (32 - (count * 8))) != virt) { + struct pci_dev *pdev = vdev->pdev; + __le32 phys_val = 0; + int ret; + + ret = vfio_user_config_read(pdev, pos, &phys_val, count); + if (ret) + return ret; + + *val = (phys_val & ~virt) | (*val & virt); + } + + return count; +} + +static int vfio_default_config_write(struct vfio_pci_device *vdev, int pos, + int count, struct perm_bits *perm, + int offset, __le32 val) +{ + __le32 virt = 0, write = 0; + + memcpy(&write, perm->write + offset, count); + + if (!write) + return count; /* drop, no writable bits */ + + memcpy(&virt, perm->virt + offset, count); + + /* Virtualized and writable bits go to vconfig */ + if (write & virt) { + __le32 virt_val = 0; + + memcpy(&virt_val, vdev->vconfig + pos, count); + + virt_val &= ~(write & virt); + virt_val |= (val & (write & virt)); + + memcpy(vdev->vconfig + pos, &virt_val, count); + } + + /* Non-virtualzed and writable bits go to hardware */ + if (write & ~virt) { + struct pci_dev *pdev = vdev->pdev; + __le32 phys_val = 0; + int ret; + + ret = vfio_user_config_read(pdev, pos, &phys_val, count); + if (ret) + return ret; + + phys_val &= ~(write & ~virt); + phys_val |= (val & (write & ~virt)); + + ret = vfio_user_config_write(pdev, pos, phys_val, count); + if (ret) + return ret; + } + + return count; +} + +/* Allow direct read from hardware, except for capability next pointer */ +static int vfio_direct_config_read(struct vfio_pci_device *vdev, int pos, + int count, struct perm_bits *perm, + int offset, __le32 *val) +{ + int ret; + + ret = vfio_user_config_read(vdev->pdev, pos, val, count); + if (ret) + return pcibios_err_to_errno(ret); + + if (pos >= PCI_CFG_SPACE_SIZE) { /* Extended cap header mangling */ + if (offset < 4) + memcpy(val, vdev->vconfig + pos, count); + } else if (pos >= PCI_STD_HEADER_SIZEOF) { /* Std cap mangling */ + if (offset == PCI_CAP_LIST_ID && count > 1) + memcpy(val, vdev->vconfig + pos, + min(PCI_CAP_FLAGS, count)); + else if (offset == PCI_CAP_LIST_NEXT) + memcpy(val, vdev->vconfig + pos, 1); + } + + return count; +} + +static int vfio_direct_config_write(struct vfio_pci_device *vdev, int pos, + int count, struct perm_bits *perm, + int offset, __le32 val) +{ + int ret; + + ret = vfio_user_config_write(vdev->pdev, pos, val, count); + if (ret) + return ret; + + return count; +} + +/* Default all regions to read-only, no-virtualization */ +static struct perm_bits cap_perms[PCI_CAP_ID_MAX + 1] = { + [0 ... PCI_CAP_ID_MAX] = { .readfn = vfio_direct_config_read } +}; +static struct perm_bits ecap_perms[PCI_EXT_CAP_ID_MAX + 1] = { + [0 ... PCI_EXT_CAP_ID_MAX] = { .readfn = vfio_direct_config_read } +}; + +static void free_perm_bits(struct perm_bits *perm) +{ + kfree(perm->virt); + kfree(perm->write); + perm->virt = NULL; + perm->write = NULL; +} + +static int alloc_perm_bits(struct perm_bits *perm, int size) +{ + /* + * Round up all permission bits to the next dword, this lets us + * ignore whether a read/write exceeds the defined capability + * structure. We can do this because: + * - Standard config space is already dword aligned + * - Capabilities are all dword alinged (bits 0:1 of next reserved) + * - Express capabilities defined as dword aligned + */ + size = round_up(size, 4); + + /* + * Zero state is + * - All Readable, None Writeable, None Virtualized + */ + perm->virt = kzalloc(size, GFP_KERNEL); + perm->write = kzalloc(size, GFP_KERNEL); + if (!perm->virt || !perm->write) { + free_perm_bits(perm); + return -ENOMEM; + } + + perm->readfn = vfio_default_config_read; + perm->writefn = vfio_default_config_write; + + return 0; +} + +/* + * Helper functions for filling in permission tables + */ +static inline void p_setb(struct perm_bits *p, int off, u8 virt, u8 write) +{ + p->virt[off] = virt; + p->write[off] = write; +} + +/* Handle endian-ness - pci and tables are little-endian */ +static inline void p_setw(struct perm_bits *p, int off, u16 virt, u16 write) +{ + *(__le16 *)(&p->virt[off]) = cpu_to_le16(virt); + *(__le16 *)(&p->write[off]) = cpu_to_le16(write); +} + +/* Handle endian-ness - pci and tables are little-endian */ +static inline void p_setd(struct perm_bits *p, int off, u32 virt, u32 write) +{ + *(__le32 *)(&p->virt[off]) = cpu_to_le32(virt); + *(__le32 *)(&p->write[off]) = cpu_to_le32(write); +} + +/* + * Restore the *real* BARs after we detect a FLR or backdoor reset. + * (backdoor = some device specific technique that we didn't catch) + */ +static void vfio_bar_restore(struct vfio_pci_device *vdev) +{ + struct pci_dev *pdev = vdev->pdev; + u32 *rbar = vdev->rbar; + int i; + + if (pdev->is_virtfn) + return; + + pr_info("%s: %s reset recovery - restoring bars\n", + __func__, dev_name(&pdev->dev)); + + for (i = PCI_BASE_ADDRESS_0; i <= PCI_BASE_ADDRESS_5; i += 4, rbar++) + pci_user_write_config_dword(pdev, i, *rbar); + + pci_user_write_config_dword(pdev, PCI_ROM_ADDRESS, *rbar); +} + +static __le32 vfio_generate_bar_flags(struct pci_dev *pdev, int bar) +{ + unsigned long flags = pci_resource_flags(pdev, bar); + u32 val; + + if (flags & IORESOURCE_IO) + return cpu_to_le32(PCI_BASE_ADDRESS_SPACE_IO); + + val = PCI_BASE_ADDRESS_SPACE_MEMORY; + + if (flags & IORESOURCE_PREFETCH) + val |= PCI_BASE_ADDRESS_MEM_PREFETCH; + + if (flags & IORESOURCE_MEM_64) + val |= PCI_BASE_ADDRESS_MEM_TYPE_64; + + return cpu_to_le32(val); +} + +/* + * Pretend we're hardware and tweak the values of the *virtual* PCI BARs + * to reflect the hardware capabilities. This implements BAR sizing. + */ +static void vfio_bar_fixup(struct vfio_pci_device *vdev) +{ + struct pci_dev *pdev = vdev->pdev; + int i; + __le32 *bar; + u64 mask; + + bar = (__le32 *)&vdev->vconfig[PCI_BASE_ADDRESS_0]; + + for (i = PCI_STD_RESOURCES; i <= PCI_STD_RESOURCE_END; i++, bar++) { + if (!pci_resource_start(pdev, i)) { + *bar = 0; /* Unmapped by host = unimplemented to user */ + continue; + } + + mask = ~(pci_resource_len(pdev, i) - 1); + + *bar &= cpu_to_le32((u32)mask); + *bar |= vfio_generate_bar_flags(pdev, i); + + if (*bar & cpu_to_le32(PCI_BASE_ADDRESS_MEM_TYPE_64)) { + bar++; + *bar &= cpu_to_le32((u32)(mask >> 32)); + i++; + } + } + + bar = (__le32 *)&vdev->vconfig[PCI_ROM_ADDRESS]; + + /* + * NB. we expose the actual BAR size here, regardless of whether + * we can read it. When we report the REGION_INFO for the ROM + * we report what PCI tells us is the actual ROM size. + */ + if (pci_resource_start(pdev, PCI_ROM_RESOURCE)) { + mask = ~(pci_resource_len(pdev, PCI_ROM_RESOURCE) - 1); + mask |= PCI_ROM_ADDRESS_ENABLE; + *bar &= cpu_to_le32((u32)mask); + } else + *bar = 0; + + vdev->bardirty = false; +} + +static int vfio_basic_config_read(struct vfio_pci_device *vdev, int pos, + int count, struct perm_bits *perm, + int offset, __le32 *val) +{ + if (is_bar(offset)) /* pos == offset for basic config */ + vfio_bar_fixup(vdev); + + count = vfio_default_config_read(vdev, pos, count, perm, offset, val); + + /* Mask in virtual memory enable for SR-IOV devices */ + if (offset == PCI_COMMAND && vdev->pdev->is_virtfn) { + u16 cmd = le16_to_cpu(*(__le16 *)&vdev->vconfig[PCI_COMMAND]); + u32 tmp_val = le32_to_cpu(*val); + + tmp_val |= cmd & PCI_COMMAND_MEMORY; + *val = cpu_to_le32(tmp_val); + } + + return count; +} + +static int vfio_basic_config_write(struct vfio_pci_device *vdev, int pos, + int count, struct perm_bits *perm, + int offset, __le32 val) +{ + struct pci_dev *pdev = vdev->pdev; + __le16 *virt_cmd; + u16 new_cmd = 0; + int ret; + + virt_cmd = (__le16 *)&vdev->vconfig[PCI_COMMAND]; + + if (offset == PCI_COMMAND) { + bool phys_mem, virt_mem, new_mem, phys_io, virt_io, new_io; + u16 phys_cmd; + + ret = pci_user_read_config_word(pdev, PCI_COMMAND, &phys_cmd); + if (ret) + return ret; + + new_cmd = le32_to_cpu(val); + + phys_mem = !!(phys_cmd & PCI_COMMAND_MEMORY); + virt_mem = !!(le16_to_cpu(*virt_cmd) & PCI_COMMAND_MEMORY); + new_mem = !!(new_cmd & PCI_COMMAND_MEMORY); + + phys_io = !!(phys_cmd & PCI_COMMAND_IO); + virt_io = !!(le16_to_cpu(*virt_cmd) & PCI_COMMAND_IO); + new_io = !!(new_cmd & PCI_COMMAND_IO); + + /* + * If the user is writing mem/io enable (new_mem/io) and we + * think it's already enabled (virt_mem/io), but the hardware + * shows it disabled (phys_mem/io, then the device has + * undergone some kind of backdoor reset and needs to be + * restored before we allow it to enable the bars. + * SR-IOV devices will trigger this, but we catch them later + */ + if ((new_mem && virt_mem && !phys_mem) || + (new_io && virt_io && !phys_io)) + vfio_bar_restore(vdev); + } + + count = vfio_default_config_write(vdev, pos, count, perm, offset, val); + if (count < 0) + return count; + + /* + * Save current memory/io enable bits in vconfig to allow for + * the test above next time. + */ + if (offset == PCI_COMMAND) { + u16 mask = PCI_COMMAND_MEMORY | PCI_COMMAND_IO; + + *virt_cmd &= cpu_to_le16(~mask); + *virt_cmd |= cpu_to_le16(new_cmd & mask); + } + + /* Emulate INTx disable */ + if (offset >= PCI_COMMAND && offset <= PCI_COMMAND + 1) { + bool virt_intx_disable; + + virt_intx_disable = !!(le16_to_cpu(*virt_cmd) & + PCI_COMMAND_INTX_DISABLE); + + if (virt_intx_disable && !vdev->virq_disabled) { + vdev->virq_disabled = true; + vfio_pci_intx_mask(vdev); + } else if (!virt_intx_disable && vdev->virq_disabled) { + vdev->virq_disabled = false; + vfio_pci_intx_unmask(vdev); + } + } + + if (is_bar(offset)) + vdev->bardirty = true; + + return count; +} + +/* Permissions for the Basic PCI Header */ +static int __init init_pci_cap_basic_perm(struct perm_bits *perm) +{ + if (alloc_perm_bits(perm, PCI_STD_HEADER_SIZEOF)) + return -ENOMEM; + + perm->readfn = vfio_basic_config_read; + perm->writefn = vfio_basic_config_write; + + /* Virtualized for SR-IOV functions, which just have FFFF */ + p_setw(perm, PCI_VENDOR_ID, (u16)ALL_VIRT, NO_WRITE); + p_setw(perm, PCI_DEVICE_ID, (u16)ALL_VIRT, NO_WRITE); + + /* + * Virtualize INTx disable, we use it internally for interrupt + * control and can emulate it for non-PCI 2.3 devices. + */ + p_setw(perm, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE, (u16)ALL_WRITE); + + /* Virtualize capability list, we might want to skip/disable */ + p_setw(perm, PCI_STATUS, PCI_STATUS_CAP_LIST, NO_WRITE); + + /* No harm to write */ + p_setb(perm, PCI_CACHE_LINE_SIZE, NO_VIRT, (u8)ALL_WRITE); + p_setb(perm, PCI_LATENCY_TIMER, NO_VIRT, (u8)ALL_WRITE); + p_setb(perm, PCI_BIST, NO_VIRT, (u8)ALL_WRITE); + + /* Virtualize all bars, can't touch the real ones */ + p_setd(perm, PCI_BASE_ADDRESS_0, ALL_VIRT, ALL_WRITE); + p_setd(perm, PCI_BASE_ADDRESS_1, ALL_VIRT, ALL_WRITE); + p_setd(perm, PCI_BASE_ADDRESS_2, ALL_VIRT, ALL_WRITE); + p_setd(perm, PCI_BASE_ADDRESS_3, ALL_VIRT, ALL_WRITE); + p_setd(perm, PCI_BASE_ADDRESS_4, ALL_VIRT, ALL_WRITE); + p_setd(perm, PCI_BASE_ADDRESS_5, ALL_VIRT, ALL_WRITE); + p_setd(perm, PCI_ROM_ADDRESS, ALL_VIRT, ALL_WRITE); + + /* Allow us to adjust capability chain */ + p_setb(perm, PCI_CAPABILITY_LIST, (u8)ALL_VIRT, NO_WRITE); + + /* Sometimes used by sw, just virtualize */ + p_setb(perm, PCI_INTERRUPT_LINE, (u8)ALL_VIRT, (u8)ALL_WRITE); + return 0; +} + +/* Permissions for the Power Management capability */ +static int __init init_pci_cap_pm_perm(struct perm_bits *perm) +{ + if (alloc_perm_bits(perm, pci_cap_length[PCI_CAP_ID_PM])) + return -ENOMEM; + + /* + * We always virtualize the next field so we can remove + * capabilities from the chain if we want to. + */ + p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE); + + /* + * Power management is defined *per function*, + * so we let the user write this + */ + p_setd(perm, PCI_PM_CTRL, NO_VIRT, ALL_WRITE); + return 0; +} + +/* Permissions for PCI-X capability */ +static int __init init_pci_cap_pcix_perm(struct perm_bits *perm) +{ + /* Alloc 24, but only 8 are used in v0 */ + if (alloc_perm_bits(perm, PCI_CAP_PCIX_SIZEOF_V2)) + return -ENOMEM; + + p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE); + + p_setw(perm, PCI_X_CMD, NO_VIRT, (u16)ALL_WRITE); + p_setd(perm, PCI_X_ECC_CSR, NO_VIRT, ALL_WRITE); + return 0; +} + +/* Permissions for PCI Express capability */ +static int __init init_pci_cap_exp_perm(struct perm_bits *perm) +{ + /* Alloc larger of two possible sizes */ + if (alloc_perm_bits(perm, PCI_CAP_EXP_ENDPOINT_SIZEOF_V2)) + return -ENOMEM; + + p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE); + + /* + * Allow writes to device control fields (includes FLR!) + * but not to devctl_phantom which could confuse IOMMU + * or to the ARI bit in devctl2 which is set at probe time + */ + p_setw(perm, PCI_EXP_DEVCTL, NO_VIRT, ~PCI_EXP_DEVCTL_PHANTOM); + p_setw(perm, PCI_EXP_DEVCTL2, NO_VIRT, ~PCI_EXP_DEVCTL2_ARI); + return 0; +} + +/* Permissions for Advanced Function capability */ +static int __init init_pci_cap_af_perm(struct perm_bits *perm) +{ + if (alloc_perm_bits(perm, pci_cap_length[PCI_CAP_ID_AF])) + return -ENOMEM; + + p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE); + p_setb(perm, PCI_AF_CTRL, NO_VIRT, PCI_AF_CTRL_FLR); + return 0; +} + +/* Permissions for Advanced Error Reporting extended capability */ +static int __init init_pci_ext_cap_err_perm(struct perm_bits *perm) +{ + u32 mask; + + if (alloc_perm_bits(perm, pci_ext_cap_length[PCI_EXT_CAP_ID_ERR])) + return -ENOMEM; + + /* + * Virtualize the first dword of all express capabilities + * because it includes the next pointer. This lets us later + * remove capabilities from the chain if we need to. + */ + p_setd(perm, 0, ALL_VIRT, NO_WRITE); + + /* Writable bits mask */ + mask = PCI_ERR_UNC_TRAIN | /* Training */ + PCI_ERR_UNC_DLP | /* Data Link Protocol */ + PCI_ERR_UNC_SURPDN | /* Surprise Down */ + PCI_ERR_UNC_POISON_TLP | /* Poisoned TLP */ + PCI_ERR_UNC_FCP | /* Flow Control Protocol */ + PCI_ERR_UNC_COMP_TIME | /* Completion Timeout */ + PCI_ERR_UNC_COMP_ABORT | /* Completer Abort */ + PCI_ERR_UNC_UNX_COMP | /* Unexpected Completion */ + PCI_ERR_UNC_RX_OVER | /* Receiver Overflow */ + PCI_ERR_UNC_MALF_TLP | /* Malformed TLP */ + PCI_ERR_UNC_ECRC | /* ECRC Error Status */ + PCI_ERR_UNC_UNSUP | /* Unsupported Request */ + PCI_ERR_UNC_ACSV | /* ACS Violation */ + PCI_ERR_UNC_INTN | /* internal error */ + PCI_ERR_UNC_MCBTLP | /* MC blocked TLP */ + PCI_ERR_UNC_ATOMEG | /* Atomic egress blocked */ + PCI_ERR_UNC_TLPPRE; /* TLP prefix blocked */ + p_setd(perm, PCI_ERR_UNCOR_STATUS, NO_VIRT, mask); + p_setd(perm, PCI_ERR_UNCOR_MASK, NO_VIRT, mask); + p_setd(perm, PCI_ERR_UNCOR_SEVER, NO_VIRT, mask); + + mask = PCI_ERR_COR_RCVR | /* Receiver Error Status */ + PCI_ERR_COR_BAD_TLP | /* Bad TLP Status */ + PCI_ERR_COR_BAD_DLLP | /* Bad DLLP Status */ + PCI_ERR_COR_REP_ROLL | /* REPLAY_NUM Rollover */ + PCI_ERR_COR_REP_TIMER | /* Replay Timer Timeout */ + PCI_ERR_COR_ADV_NFAT | /* Advisory Non-Fatal */ + PCI_ERR_COR_INTERNAL | /* Corrected Internal */ + PCI_ERR_COR_LOG_OVER; /* Header Log Overflow */ + p_setd(perm, PCI_ERR_COR_STATUS, NO_VIRT, mask); + p_setd(perm, PCI_ERR_COR_MASK, NO_VIRT, mask); + + mask = PCI_ERR_CAP_ECRC_GENE | /* ECRC Generation Enable */ + PCI_ERR_CAP_ECRC_CHKE; /* ECRC Check Enable */ + p_setd(perm, PCI_ERR_CAP, NO_VIRT, mask); + return 0; +} + +/* Permissions for Power Budgeting extended capability */ +static int __init init_pci_ext_cap_pwr_perm(struct perm_bits *perm) +{ + if (alloc_perm_bits(perm, pci_ext_cap_length[PCI_EXT_CAP_ID_PWR])) + return -ENOMEM; + + p_setd(perm, 0, ALL_VIRT, NO_WRITE); + + /* Writing the data selector is OK, the info is still read-only */ + p_setb(perm, PCI_PWR_DATA, NO_VIRT, (u8)ALL_WRITE); + return 0; +} + +/* + * Initialize the shared permission tables + */ +void vfio_pci_uninit_perm_bits(void) +{ + free_perm_bits(&cap_perms[PCI_CAP_ID_BASIC]); + + free_perm_bits(&cap_perms[PCI_CAP_ID_PM]); + free_perm_bits(&cap_perms[PCI_CAP_ID_PCIX]); + free_perm_bits(&cap_perms[PCI_CAP_ID_EXP]); + free_perm_bits(&cap_perms[PCI_CAP_ID_AF]); + + free_perm_bits(&ecap_perms[PCI_EXT_CAP_ID_ERR]); + free_perm_bits(&ecap_perms[PCI_EXT_CAP_ID_PWR]); +} + +int __init vfio_pci_init_perm_bits(void) +{ + int ret; + + /* Basic config space */ + ret = init_pci_cap_basic_perm(&cap_perms[PCI_CAP_ID_BASIC]); + + /* Capabilities */ + ret |= init_pci_cap_pm_perm(&cap_perms[PCI_CAP_ID_PM]); + cap_perms[PCI_CAP_ID_VPD].writefn = vfio_direct_config_write; + ret |= init_pci_cap_pcix_perm(&cap_perms[PCI_CAP_ID_PCIX]); + cap_perms[PCI_CAP_ID_VNDR].writefn = vfio_direct_config_write; + ret |= init_pci_cap_exp_perm(&cap_perms[PCI_CAP_ID_EXP]); + ret |= init_pci_cap_af_perm(&cap_perms[PCI_CAP_ID_AF]); + + /* Extended capabilities */ + ret |= init_pci_ext_cap_err_perm(&ecap_perms[PCI_EXT_CAP_ID_ERR]); + ret |= init_pci_ext_cap_pwr_perm(&ecap_perms[PCI_EXT_CAP_ID_PWR]); + ecap_perms[PCI_EXT_CAP_ID_VNDR].writefn = vfio_direct_config_write; + + if (ret) + vfio_pci_uninit_perm_bits(); + + return ret; +} + +static int vfio_find_cap_start(struct vfio_pci_device *vdev, int pos) +{ + u8 cap; + int base = (pos >= PCI_CFG_SPACE_SIZE) ? PCI_CFG_SPACE_SIZE : + PCI_STD_HEADER_SIZEOF; + base /= 4; + pos /= 4; + + cap = vdev->pci_config_map[pos]; + + if (cap == PCI_CAP_ID_BASIC) + return 0; + + /* XXX Can we have to abutting capabilities of the same type? */ + while (pos - 1 >= base && vdev->pci_config_map[pos - 1] == cap) + pos--; + + return pos * 4; +} + +static int vfio_msi_config_read(struct vfio_pci_device *vdev, int pos, + int count, struct perm_bits *perm, + int offset, __le32 *val) +{ + /* Update max available queue size from msi_qmax */ + if (offset <= PCI_MSI_FLAGS && offset + count >= PCI_MSI_FLAGS) { + __le16 *flags; + int start; + + start = vfio_find_cap_start(vdev, pos); + + flags = (__le16 *)&vdev->vconfig[start]; + + *flags &= cpu_to_le16(~PCI_MSI_FLAGS_QMASK); + *flags |= cpu_to_le16(vdev->msi_qmax << 1); + } + + return vfio_default_config_read(vdev, pos, count, perm, offset, val); +} + +static int vfio_msi_config_write(struct vfio_pci_device *vdev, int pos, + int count, struct perm_bits *perm, + int offset, __le32 val) +{ + count = vfio_default_config_write(vdev, pos, count, perm, offset, val); + if (count < 0) + return count; + + /* Fixup and write configured queue size and enable to hardware */ + if (offset <= PCI_MSI_FLAGS && offset + count >= PCI_MSI_FLAGS) { + __le16 *pflags; + u16 flags; + int start, ret; + + start = vfio_find_cap_start(vdev, pos); + + pflags = (__le16 *)&vdev->vconfig[start + PCI_MSI_FLAGS]; + + flags = le16_to_cpu(*pflags); + + /* MSI is enabled via ioctl */ + if (!is_msi(vdev)) + flags &= ~PCI_MSI_FLAGS_ENABLE; + + /* Check queue size */ + if ((flags & PCI_MSI_FLAGS_QSIZE) >> 4 > vdev->msi_qmax) { + flags &= ~PCI_MSI_FLAGS_QSIZE; + flags |= vdev->msi_qmax << 4; + } + + /* Write back to virt and to hardware */ + *pflags = cpu_to_le16(flags); + ret = pci_user_write_config_word(vdev->pdev, + start + PCI_MSI_FLAGS, + flags); + if (ret) + return pcibios_err_to_errno(ret); + } + + return count; +} + +/* + * MSI determination is per-device, so this routine gets used beyond + * initialization time. Don't add __init + */ +static int init_pci_cap_msi_perm(struct perm_bits *perm, int len, u16 flags) +{ + if (alloc_perm_bits(perm, len)) + return -ENOMEM; + + perm->readfn = vfio_msi_config_read; + perm->writefn = vfio_msi_config_write; + + p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE); + + /* + * The upper byte of the control register is reserved, + * just setup the lower byte. + */ + p_setb(perm, PCI_MSI_FLAGS, (u8)ALL_VIRT, (u8)ALL_WRITE); + p_setd(perm, PCI_MSI_ADDRESS_LO, ALL_VIRT, ALL_WRITE); + if (flags & PCI_MSI_FLAGS_64BIT) { + p_setd(perm, PCI_MSI_ADDRESS_HI, ALL_VIRT, ALL_WRITE); + p_setw(perm, PCI_MSI_DATA_64, (u16)ALL_VIRT, (u16)ALL_WRITE); + if (flags & PCI_MSI_FLAGS_MASKBIT) { + p_setd(perm, PCI_MSI_MASK_64, NO_VIRT, ALL_WRITE); + p_setd(perm, PCI_MSI_PENDING_64, NO_VIRT, ALL_WRITE); + } + } else { + p_setw(perm, PCI_MSI_DATA_32, (u16)ALL_VIRT, (u16)ALL_WRITE); + if (flags & PCI_MSI_FLAGS_MASKBIT) { + p_setd(perm, PCI_MSI_MASK_32, NO_VIRT, ALL_WRITE); + p_setd(perm, PCI_MSI_PENDING_32, NO_VIRT, ALL_WRITE); + } + } + return 0; +} + +/* Determine MSI CAP field length; initialize msi_perms on 1st call per vdev */ +static int vfio_msi_cap_len(struct vfio_pci_device *vdev, u8 pos) +{ + struct pci_dev *pdev = vdev->pdev; + int len, ret; + u16 flags; + + ret = pci_read_config_word(pdev, pos + PCI_MSI_FLAGS, &flags); + if (ret) + return pcibios_err_to_errno(ret); + + len = 10; /* Minimum size */ + if (flags & PCI_MSI_FLAGS_64BIT) + len += 4; + if (flags & PCI_MSI_FLAGS_MASKBIT) + len += 10; + + if (vdev->msi_perm) + return len; + + vdev->msi_perm = kmalloc(sizeof(struct perm_bits), GFP_KERNEL); + if (!vdev->msi_perm) + return -ENOMEM; + + ret = init_pci_cap_msi_perm(vdev->msi_perm, len, flags); + if (ret) + return ret; + + return len; +} + +/* Determine extended capability length for VC (2 & 9) and MFVC */ +static int vfio_vc_cap_len(struct vfio_pci_device *vdev, u16 pos) +{ + struct pci_dev *pdev = vdev->pdev; + u32 tmp; + int ret, evcc, phases, vc_arb; + int len = PCI_CAP_VC_BASE_SIZEOF; + + ret = pci_read_config_dword(pdev, pos + PCI_VC_PORT_REG1, &tmp); + if (ret) + return pcibios_err_to_errno(ret); + + evcc = tmp & PCI_VC_REG1_EVCC; /* extended vc count */ + ret = pci_read_config_dword(pdev, pos + PCI_VC_PORT_REG2, &tmp); + if (ret) + return pcibios_err_to_errno(ret); + + if (tmp & PCI_VC_REG2_128_PHASE) + phases = 128; + else if (tmp & PCI_VC_REG2_64_PHASE) + phases = 64; + else if (tmp & PCI_VC_REG2_32_PHASE) + phases = 32; + else + phases = 0; + + vc_arb = phases * 4; + + /* + * Port arbitration tables are root & switch only; + * function arbitration tables are function 0 only. + * In either case, we'll never let user write them so + * we don't care how big they are + */ + len += (1 + evcc) * PCI_CAP_VC_PER_VC_SIZEOF; + if (vc_arb) { + len = round_up(len, 16); + len += vc_arb / 8; + } + return len; +} + +static int vfio_cap_len(struct vfio_pci_device *vdev, u8 cap, u8 pos) +{ + struct pci_dev *pdev = vdev->pdev; + u16 word; + u8 byte; + int ret; + + switch (cap) { + case PCI_CAP_ID_MSI: + return vfio_msi_cap_len(vdev, pos); + case PCI_CAP_ID_PCIX: + ret = pci_read_config_word(pdev, pos + PCI_X_CMD, &word); + if (ret) + return pcibios_err_to_errno(ret); + + if (PCI_X_CMD_VERSION(word)) { + vdev->extended_caps = true; + return PCI_CAP_PCIX_SIZEOF_V2; + } else + return PCI_CAP_PCIX_SIZEOF_V0; + case PCI_CAP_ID_VNDR: + /* length follows next field */ + ret = pci_read_config_byte(pdev, pos + PCI_CAP_FLAGS, &byte); + if (ret) + return pcibios_err_to_errno(ret); + + return byte; + case PCI_CAP_ID_EXP: + /* length based on version */ + ret = pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, &word); + if (ret) + return pcibios_err_to_errno(ret); + + if ((word & PCI_EXP_FLAGS_VERS) == 1) + return PCI_CAP_EXP_ENDPOINT_SIZEOF_V1; + else { + vdev->extended_caps = true; + return PCI_CAP_EXP_ENDPOINT_SIZEOF_V2; + } + case PCI_CAP_ID_HT: + ret = pci_read_config_byte(pdev, pos + 3, &byte); + if (ret) + return pcibios_err_to_errno(ret); + + return (byte & HT_3BIT_CAP_MASK) ? + HT_CAP_SIZEOF_SHORT : HT_CAP_SIZEOF_LONG; + case PCI_CAP_ID_SATA: + ret = pci_read_config_byte(pdev, pos + PCI_SATA_REGS, &byte); + if (ret) + return pcibios_err_to_errno(ret); + + byte &= PCI_SATA_REGS_MASK; + if (byte == PCI_SATA_REGS_INLINE) + return PCI_SATA_SIZEOF_LONG; + else + return PCI_SATA_SIZEOF_SHORT; + default: + pr_warn("%s: %s unknown length for pci cap 0x%x@0x%x\n", + dev_name(&pdev->dev), __func__, cap, pos); + } + + return 0; +} + +static int vfio_ext_cap_len(struct vfio_pci_device *vdev, u16 ecap, u16 epos) +{ + struct pci_dev *pdev = vdev->pdev; + u8 byte; + u32 dword; + int ret; + + switch (ecap) { + case PCI_EXT_CAP_ID_VNDR: + ret = pci_read_config_dword(pdev, epos + PCI_VSEC_HDR, &dword); + if (ret) + return pcibios_err_to_errno(ret); + + return dword >> PCI_VSEC_HDR_LEN_SHIFT; + case PCI_EXT_CAP_ID_VC: + case PCI_EXT_CAP_ID_VC9: + case PCI_EXT_CAP_ID_MFVC: + return vfio_vc_cap_len(vdev, epos); + case PCI_EXT_CAP_ID_ACS: + ret = pci_read_config_byte(pdev, epos + PCI_ACS_CAP, &byte); + if (ret) + return pcibios_err_to_errno(ret); + + if (byte & PCI_ACS_EC) { + int bits; + + ret = pci_read_config_byte(pdev, + epos + PCI_ACS_EGRESS_BITS, + &byte); + if (ret) + return pcibios_err_to_errno(ret); + + bits = byte ? round_up(byte, 32) : 256; + return 8 + (bits / 8); + } + return 8; + + case PCI_EXT_CAP_ID_REBAR: + ret = pci_read_config_byte(pdev, epos + PCI_REBAR_CTRL, &byte); + if (ret) + return pcibios_err_to_errno(ret); + + byte &= PCI_REBAR_CTRL_NBAR_MASK; + byte >>= PCI_REBAR_CTRL_NBAR_SHIFT; + + return 4 + (byte * 8); + case PCI_EXT_CAP_ID_DPA: + ret = pci_read_config_byte(pdev, epos + PCI_DPA_CAP, &byte); + if (ret) + return pcibios_err_to_errno(ret); + + byte &= PCI_DPA_CAP_SUBSTATE_MASK; + byte = round_up(byte + 1, 4); + return PCI_DPA_BASE_SIZEOF + byte; + case PCI_EXT_CAP_ID_TPH: + ret = pci_read_config_dword(pdev, epos + PCI_TPH_CAP, &dword); + if (ret) + return pcibios_err_to_errno(ret); + + if ((dword & PCI_TPH_CAP_LOC_MASK) == PCI_TPH_LOC_CAP) { + int sts; + + sts = byte & PCI_TPH_CAP_ST_MASK; + sts >>= PCI_TPH_CAP_ST_SHIFT; + return PCI_TPH_BASE_SIZEOF + round_up(sts * 2, 4); + } + return PCI_TPH_BASE_SIZEOF; + default: + pr_warn("%s: %s unknown length for pci ecap 0x%x@0x%x\n", + dev_name(&pdev->dev), __func__, ecap, epos); + } + + return 0; +} + +static int vfio_fill_vconfig_bytes(struct vfio_pci_device *vdev, + int offset, int size) +{ + struct pci_dev *pdev = vdev->pdev; + int ret = 0; + + /* + * We try to read physical config space in the largest chunks + * we can, assuming that all of the fields support dword access. + * pci_save_state() makes this same assumption and seems to do ok. + */ + while (size) { + int filled; + + if (size >= 4 && !(offset % 4)) { + __le32 *dwordp = (__le32 *)&vdev->vconfig[offset]; + u32 dword; + + ret = pci_read_config_dword(pdev, offset, &dword); + if (ret) + return ret; + *dwordp = cpu_to_le32(dword); + filled = 4; + } else if (size >= 2 && !(offset % 2)) { + __le16 *wordp = (__le16 *)&vdev->vconfig[offset]; + u16 word; + + ret = pci_read_config_word(pdev, offset, &word); + if (ret) + return ret; + *wordp = cpu_to_le16(word); + filled = 2; + } else { + u8 *byte = &vdev->vconfig[offset]; + ret = pci_read_config_byte(pdev, offset, byte); + if (ret) + return ret; + filled = 1; + } + + offset += filled; + size -= filled; + } + + return ret; +} + +static int vfio_cap_init(struct vfio_pci_device *vdev) +{ + struct pci_dev *pdev = vdev->pdev; + u8 *map = vdev->pci_config_map; + u16 status; + u8 pos, *prev, cap; + int loops, ret, caps = 0; + + /* Any capabilities? */ + ret = pci_read_config_word(pdev, PCI_STATUS, &status); + if (ret) + return ret; + + if (!(status & PCI_STATUS_CAP_LIST)) + return 0; /* Done */ + + ret = pci_read_config_byte(pdev, PCI_CAPABILITY_LIST, &pos); + if (ret) + return ret; + + /* Mark the previous position in case we want to skip a capability */ + prev = &vdev->vconfig[PCI_CAPABILITY_LIST]; + + /* We can bound our loop, capabilities are dword aligned */ + loops = (PCI_CFG_SPACE_SIZE - PCI_STD_HEADER_SIZEOF) / PCI_CAP_SIZEOF; + while (pos && loops--) { + u8 next; + int i, len = 0; + + ret = pci_read_config_byte(pdev, pos, &cap); + if (ret) + return ret; + + ret = pci_read_config_byte(pdev, + pos + PCI_CAP_LIST_NEXT, &next); + if (ret) + return ret; + + if (cap <= PCI_CAP_ID_MAX) { + len = pci_cap_length[cap]; + if (len == 0xFF) { /* Variable length */ + len = vfio_cap_len(vdev, cap, pos); + if (len < 0) + return len; + } + } + + if (!len) { + pr_info("%s: %s hiding cap 0x%x\n", + __func__, dev_name(&pdev->dev), cap); + *prev = next; + pos = next; + continue; + } + + /* Sanity check, do we overlap other capabilities? */ + for (i = 0; i < len; i += 4) { + if (likely(map[(pos + i) / 4] == PCI_CAP_ID_INVALID)) + continue; + + pr_warn("%s: %s pci config conflict @0x%x, was cap 0x%x now cap 0x%x\n", + __func__, dev_name(&pdev->dev), + pos + i, map[pos + i], cap); + } + + memset(map + (pos / 4), cap, len / 4); + ret = vfio_fill_vconfig_bytes(vdev, pos, len); + if (ret) + return ret; + + prev = &vdev->vconfig[pos + PCI_CAP_LIST_NEXT]; + pos = next; + caps++; + } + + /* If we didn't fill any capabilities, clear the status flag */ + if (!caps) { + __le16 *vstatus = (__le16 *)&vdev->vconfig[PCI_STATUS]; + *vstatus &= ~cpu_to_le16(PCI_STATUS_CAP_LIST); + } + + return 0; +} + +static int vfio_ecap_init(struct vfio_pci_device *vdev) +{ + struct pci_dev *pdev = vdev->pdev; + u8 *map = vdev->pci_config_map; + u16 epos; + __le32 *prev = NULL; + int loops, ret, ecaps = 0; + + if (!vdev->extended_caps) + return 0; + + epos = PCI_CFG_SPACE_SIZE; + + loops = (pdev->cfg_size - PCI_CFG_SPACE_SIZE) / PCI_CAP_SIZEOF; + + while (loops-- && epos >= PCI_CFG_SPACE_SIZE) { + u32 header; + u16 ecap; + int i, len = 0; + bool hidden = false; + + ret = pci_read_config_dword(pdev, epos, &header); + if (ret) + return ret; + + ecap = PCI_EXT_CAP_ID(header); + + if (ecap <= PCI_EXT_CAP_ID_MAX) { + len = pci_ext_cap_length[ecap]; + if (len == 0xFF) { + len = vfio_ext_cap_len(vdev, ecap, epos); + if (len < 0) + return ret; + } + } + + if (!len) { + pr_info("%s: %s hiding ecap 0x%x@0x%x\n", + __func__, dev_name(&pdev->dev), ecap, epos); + + /* If not the first in the chain, we can skip over it */ + if (prev) { + u32 val = epos = PCI_EXT_CAP_NEXT(header); + *prev &= cpu_to_le32(~(0xffcU << 20)); + *prev |= cpu_to_le32(val << 20); + continue; + } + + /* + * Otherwise, fill in a placeholder, the direct + * readfn will virtualize this automatically + */ + len = PCI_CAP_SIZEOF; + hidden = true; + } + + for (i = 0; i < len; i += 4) { + if (likely(map[(epos + i) / 4] == PCI_CAP_ID_INVALID)) + continue; + + pr_warn("%s: %s pci config conflict @0x%x, was ecap 0x%x now ecap 0x%x\n", + __func__, dev_name(&pdev->dev), + epos + i, map[epos + i], ecap); + } + + /* + * Even though ecap is 2 bytes, we're currently a long way + * from exceeding 1 byte capabilities. If we ever make it + * up to 0xFF we'll need to up this to a two-byte, byte map. + */ + BUILD_BUG_ON(PCI_EXT_CAP_ID_MAX >= PCI_CAP_ID_INVALID); + + memset(map + (epos / 4), ecap, len / 4); + ret = vfio_fill_vconfig_bytes(vdev, epos, len); + if (ret) + return ret; + + /* + * If we're just using this capability to anchor the list, + * hide the real ID. Only count real ecaps. XXX PCI spec + * indicates to use cap id = 0, version = 0, next = 0 if + * ecaps are absent, hope users check all the way to next. + */ + if (hidden) + *(__le32 *)&vdev->vconfig[epos] &= + cpu_to_le32((0xffcU << 20)); + else + ecaps++; + + prev = (__le32 *)&vdev->vconfig[epos]; + epos = PCI_EXT_CAP_NEXT(header); + } + + if (!ecaps) + *(u32 *)&vdev->vconfig[PCI_CFG_SPACE_SIZE] = 0; + + return 0; +} + +/* + * For each device we allocate a pci_config_map that indicates the + * capability occupying each dword and thus the struct perm_bits we + * use for read and write. We also allocate a virtualized config + * space which tracks reads and writes to bits that we emulate for + * the user. Initial values filled from device. + * + * Using shared stuct perm_bits between all vfio-pci devices saves + * us from allocating cfg_size buffers for virt and write for every + * device. We could remove vconfig and allocate individual buffers + * for each area requring emulated bits, but the array of pointers + * would be comparable in size (at least for standard config space). + */ +int vfio_config_init(struct vfio_pci_device *vdev) +{ + struct pci_dev *pdev = vdev->pdev; + u8 *map, *vconfig; + int ret; + + /* + * Config space, caps and ecaps are all dword aligned, so we can + * use one byte per dword to record the type. + */ + map = kmalloc(pdev->cfg_size / 4, GFP_KERNEL); + if (!map) + return -ENOMEM; + + vconfig = kmalloc(pdev->cfg_size, GFP_KERNEL); + if (!vconfig) { + kfree(map); + return -ENOMEM; + } + + vdev->pci_config_map = map; + vdev->vconfig = vconfig; + + memset(map, PCI_CAP_ID_BASIC, PCI_STD_HEADER_SIZEOF / 4); + memset(map + (PCI_STD_HEADER_SIZEOF / 4), PCI_CAP_ID_INVALID, + (pdev->cfg_size - PCI_STD_HEADER_SIZEOF) / 4); + + ret = vfio_fill_vconfig_bytes(vdev, 0, PCI_STD_HEADER_SIZEOF); + if (ret) + goto out; + + vdev->bardirty = true; + + /* + * XXX can we just pci_load_saved_state/pci_restore_state? + * may need to rebuild vconfig after that + */ + + /* For restore after reset */ + vdev->rbar[0] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_0]); + vdev->rbar[1] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_1]); + vdev->rbar[2] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_2]); + vdev->rbar[3] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_3]); + vdev->rbar[4] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_4]); + vdev->rbar[5] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_5]); + vdev->rbar[6] = le32_to_cpu(*(__le32 *)&vconfig[PCI_ROM_ADDRESS]); + + if (pdev->is_virtfn) { + *(__le16 *)&vconfig[PCI_VENDOR_ID] = cpu_to_le16(pdev->vendor); + *(__le16 *)&vconfig[PCI_DEVICE_ID] = cpu_to_le16(pdev->device); + } + + ret = vfio_cap_init(vdev); + if (ret) + goto out; + + ret = vfio_ecap_init(vdev); + if (ret) + goto out; + + return 0; + +out: + kfree(map); + vdev->pci_config_map = NULL; + kfree(vconfig); + vdev->vconfig = NULL; + return pcibios_err_to_errno(ret); +} + +void vfio_config_free(struct vfio_pci_device *vdev) +{ + kfree(vdev->vconfig); + vdev->vconfig = NULL; + kfree(vdev->pci_config_map); + vdev->pci_config_map = NULL; + kfree(vdev->msi_perm); + vdev->msi_perm = NULL; +} + +static ssize_t vfio_config_do_rw(struct vfio_pci_device *vdev, char __user *buf, + size_t count, loff_t *ppos, bool iswrite) +{ + struct pci_dev *pdev = vdev->pdev; + struct perm_bits *perm; + __le32 val = 0; + int cap_start = 0, offset; + u8 cap_id; + ssize_t ret = count; + + if (*ppos < 0 || *ppos + count > pdev->cfg_size) + return -EFAULT; + + /* + * gcc can't seem to figure out we're a static function, only called + * with count of 1/2/4 and hits copy_from_user_overflow without this. + */ + if (count > sizeof(val)) + return -EINVAL; + + cap_id = vdev->pci_config_map[*ppos / 4]; + + if (cap_id == PCI_CAP_ID_INVALID) { + if (iswrite) + return ret; /* drop */ + + /* + * Per PCI spec 3.0, section 6.1, reads from reserved and + * unimplemented registers return 0 + */ + if (copy_to_user(buf, &val, count)) + return -EFAULT; + + return ret; + } + + /* + * All capabilities are minimum 4 bytes and aligned on dword + * boundaries. Since we don't support unaligned accesses, we're + * only ever accessing a single capability. + */ + if (*ppos >= PCI_CFG_SPACE_SIZE) { + WARN_ON(cap_id > PCI_EXT_CAP_ID_MAX); + + perm = &ecap_perms[cap_id]; + cap_start = vfio_find_cap_start(vdev, *ppos); + + } else { + WARN_ON(cap_id > PCI_CAP_ID_MAX); + + perm = &cap_perms[cap_id]; + + if (cap_id == PCI_CAP_ID_MSI) + perm = vdev->msi_perm; + + if (cap_id > PCI_CAP_ID_BASIC) + cap_start = vfio_find_cap_start(vdev, *ppos); + } + + WARN_ON(!cap_start && cap_id != PCI_CAP_ID_BASIC); + WARN_ON(cap_start > *ppos); + + offset = *ppos - cap_start; + + if (iswrite) { + if (!perm->writefn) + return ret; + + if (copy_from_user(&val, buf, count)) + return -EFAULT; + + ret = perm->writefn(vdev, *ppos, count, perm, offset, val); + } else { + if (perm->readfn) { + ret = perm->readfn(vdev, *ppos, count, + perm, offset, &val); + if (ret < 0) + return ret; + } + + if (copy_to_user(buf, &val, count)) + return -EFAULT; + } + + return ret; +} + +ssize_t vfio_pci_config_readwrite(struct vfio_pci_device *vdev, + char __user *buf, size_t count, + loff_t *ppos, bool iswrite) +{ + size_t done = 0; + int ret = 0; + loff_t pos = *ppos; + + pos &= VFIO_PCI_OFFSET_MASK; + + /* + * We want to both keep the access size the caller users as well as + * support reading large chunks of config space in a single call. + * PCI doesn't support unaligned accesses, so we can safely break + * those apart. + */ + while (count) { + if (count >= 4 && !(pos % 4)) + ret = vfio_config_do_rw(vdev, buf, 4, &pos, iswrite); + else if (count >= 2 && !(pos % 2)) + ret = vfio_config_do_rw(vdev, buf, 2, &pos, iswrite); + else + ret = vfio_config_do_rw(vdev, buf, 1, &pos, iswrite); + + if (ret < 0) + return ret; + + count -= ret; + done += ret; + buf += ret; + pos += ret; + } + + *ppos += done; + + return done; +} diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c new file mode 100644 index 000000000000..211a4920b88a --- /dev/null +++ b/drivers/vfio/pci/vfio_pci_intrs.c @@ -0,0 +1,740 @@ +/* + * VFIO PCI interrupt handling + * + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * 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. + * + * Derived from original vfio: + * Copyright 2010 Cisco Systems, Inc. All rights reserved. + * Author: Tom Lyon, pugs@cisco.com + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vfio_pci_private.h" + +/* + * IRQfd - generic + */ +struct virqfd { + struct vfio_pci_device *vdev; + struct eventfd_ctx *eventfd; + int (*handler)(struct vfio_pci_device *, void *); + void (*thread)(struct vfio_pci_device *, void *); + void *data; + struct work_struct inject; + wait_queue_t wait; + poll_table pt; + struct work_struct shutdown; + struct virqfd **pvirqfd; +}; + +static struct workqueue_struct *vfio_irqfd_cleanup_wq; + +int __init vfio_pci_virqfd_init(void) +{ + vfio_irqfd_cleanup_wq = + create_singlethread_workqueue("vfio-irqfd-cleanup"); + if (!vfio_irqfd_cleanup_wq) + return -ENOMEM; + + return 0; +} + +void vfio_pci_virqfd_exit(void) +{ + destroy_workqueue(vfio_irqfd_cleanup_wq); +} + +static void virqfd_deactivate(struct virqfd *virqfd) +{ + queue_work(vfio_irqfd_cleanup_wq, &virqfd->shutdown); +} + +static int virqfd_wakeup(wait_queue_t *wait, unsigned mode, int sync, void *key) +{ + struct virqfd *virqfd = container_of(wait, struct virqfd, wait); + unsigned long flags = (unsigned long)key; + + if (flags & POLLIN) { + /* An event has been signaled, call function */ + if ((!virqfd->handler || + virqfd->handler(virqfd->vdev, virqfd->data)) && + virqfd->thread) + schedule_work(&virqfd->inject); + } + + if (flags & POLLHUP) + /* The eventfd is closing, detach from VFIO */ + virqfd_deactivate(virqfd); + + return 0; +} + +static void virqfd_ptable_queue_proc(struct file *file, + wait_queue_head_t *wqh, poll_table *pt) +{ + struct virqfd *virqfd = container_of(pt, struct virqfd, pt); + add_wait_queue(wqh, &virqfd->wait); +} + +static void virqfd_shutdown(struct work_struct *work) +{ + struct virqfd *virqfd = container_of(work, struct virqfd, shutdown); + struct virqfd **pvirqfd = virqfd->pvirqfd; + u64 cnt; + + eventfd_ctx_remove_wait_queue(virqfd->eventfd, &virqfd->wait, &cnt); + flush_work(&virqfd->inject); + eventfd_ctx_put(virqfd->eventfd); + + kfree(virqfd); + *pvirqfd = NULL; +} + +static void virqfd_inject(struct work_struct *work) +{ + struct virqfd *virqfd = container_of(work, struct virqfd, inject); + if (virqfd->thread) + virqfd->thread(virqfd->vdev, virqfd->data); +} + +static int virqfd_enable(struct vfio_pci_device *vdev, + int (*handler)(struct vfio_pci_device *, void *), + void (*thread)(struct vfio_pci_device *, void *), + void *data, struct virqfd **pvirqfd, int fd) +{ + struct file *file = NULL; + struct eventfd_ctx *ctx = NULL; + struct virqfd *virqfd; + int ret = 0; + unsigned int events; + + if (*pvirqfd) + return -EBUSY; + + virqfd = kzalloc(sizeof(*virqfd), GFP_KERNEL); + if (!virqfd) + return -ENOMEM; + + virqfd->pvirqfd = pvirqfd; + *pvirqfd = virqfd; + virqfd->vdev = vdev; + virqfd->handler = handler; + virqfd->thread = thread; + virqfd->data = data; + + INIT_WORK(&virqfd->shutdown, virqfd_shutdown); + INIT_WORK(&virqfd->inject, virqfd_inject); + + file = eventfd_fget(fd); + if (IS_ERR(file)) { + ret = PTR_ERR(file); + goto fail; + } + + ctx = eventfd_ctx_fileget(file); + if (IS_ERR(ctx)) { + ret = PTR_ERR(ctx); + goto fail; + } + + virqfd->eventfd = ctx; + + /* + * Install our own custom wake-up handling so we are notified via + * a callback whenever someone signals the underlying eventfd. + */ + init_waitqueue_func_entry(&virqfd->wait, virqfd_wakeup); + init_poll_funcptr(&virqfd->pt, virqfd_ptable_queue_proc); + + events = file->f_op->poll(file, &virqfd->pt); + + /* + * Check if there was an event already pending on the eventfd + * before we registered and trigger it as if we didn't miss it. + */ + if (events & POLLIN) { + if ((!handler || handler(vdev, data)) && thread) + schedule_work(&virqfd->inject); + } + + /* + * Do not drop the file until the irqfd is fully initialized, + * otherwise we might race against the POLLHUP. + */ + fput(file); + + return 0; + +fail: + if (ctx && !IS_ERR(ctx)) + eventfd_ctx_put(ctx); + + if (file && !IS_ERR(file)) + fput(file); + + kfree(virqfd); + *pvirqfd = NULL; + + return ret; +} + +static void virqfd_disable(struct virqfd *virqfd) +{ + if (!virqfd) + return; + + virqfd_deactivate(virqfd); + + /* Block until we know all outstanding shutdown jobs have completed. */ + flush_workqueue(vfio_irqfd_cleanup_wq); +} + +/* + * INTx + */ +static void vfio_send_intx_eventfd(struct vfio_pci_device *vdev, void *unused) +{ + if (likely(is_intx(vdev) && !vdev->virq_disabled)) + eventfd_signal(vdev->ctx[0].trigger, 1); +} + +void vfio_pci_intx_mask(struct vfio_pci_device *vdev) +{ + struct pci_dev *pdev = vdev->pdev; + unsigned long flags; + + spin_lock_irqsave(&vdev->irqlock, flags); + + /* + * Masking can come from interrupt, ioctl, or config space + * via INTx disable. The latter means this can get called + * even when not using intx delivery. In this case, just + * try to have the physical bit follow the virtual bit. + */ + if (unlikely(!is_intx(vdev))) { + if (vdev->pci_2_3) + pci_intx(pdev, 0); + } else if (!vdev->ctx[0].masked) { + /* + * Can't use check_and_mask here because we always want to + * mask, not just when something is pending. + */ + if (vdev->pci_2_3) + pci_intx(pdev, 0); + else + disable_irq_nosync(pdev->irq); + + vdev->ctx[0].masked = true; + } + + spin_unlock_irqrestore(&vdev->irqlock, flags); +} + +/* + * If this is triggered by an eventfd, we can't call eventfd_signal + * or else we'll deadlock on the eventfd wait queue. Return >0 when + * a signal is necessary, which can then be handled via a work queue + * or directly depending on the caller. + */ +int vfio_pci_intx_unmask_handler(struct vfio_pci_device *vdev, void *unused) +{ + struct pci_dev *pdev = vdev->pdev; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&vdev->irqlock, flags); + + /* + * Unmasking comes from ioctl or config, so again, have the + * physical bit follow the virtual even when not using INTx. + */ + if (unlikely(!is_intx(vdev))) { + if (vdev->pci_2_3) + pci_intx(pdev, 1); + } else if (vdev->ctx[0].masked && !vdev->virq_disabled) { + /* + * A pending interrupt here would immediately trigger, + * but we can avoid that overhead by just re-sending + * the interrupt to the user. + */ + if (vdev->pci_2_3) { + if (!pci_check_and_unmask_intx(pdev)) + ret = 1; + } else + enable_irq(pdev->irq); + + vdev->ctx[0].masked = (ret > 0); + } + + spin_unlock_irqrestore(&vdev->irqlock, flags); + + return ret; +} + +void vfio_pci_intx_unmask(struct vfio_pci_device *vdev) +{ + if (vfio_pci_intx_unmask_handler(vdev, NULL) > 0) + vfio_send_intx_eventfd(vdev, NULL); +} + +static irqreturn_t vfio_intx_handler(int irq, void *dev_id) +{ + struct vfio_pci_device *vdev = dev_id; + unsigned long flags; + int ret = IRQ_NONE; + + spin_lock_irqsave(&vdev->irqlock, flags); + + if (!vdev->pci_2_3) { + disable_irq_nosync(vdev->pdev->irq); + vdev->ctx[0].masked = true; + ret = IRQ_HANDLED; + } else if (!vdev->ctx[0].masked && /* may be shared */ + pci_check_and_mask_intx(vdev->pdev)) { + vdev->ctx[0].masked = true; + ret = IRQ_HANDLED; + } + + spin_unlock_irqrestore(&vdev->irqlock, flags); + + if (ret == IRQ_HANDLED) + vfio_send_intx_eventfd(vdev, NULL); + + return ret; +} + +static int vfio_intx_enable(struct vfio_pci_device *vdev) +{ + if (!is_irq_none(vdev)) + return -EINVAL; + + if (!vdev->pdev->irq) + return -ENODEV; + + vdev->ctx = kzalloc(sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL); + if (!vdev->ctx) + return -ENOMEM; + + vdev->num_ctx = 1; + vdev->irq_type = VFIO_PCI_INTX_IRQ_INDEX; + + return 0; +} + +static int vfio_intx_set_signal(struct vfio_pci_device *vdev, int fd) +{ + struct pci_dev *pdev = vdev->pdev; + unsigned long irqflags = IRQF_SHARED; + struct eventfd_ctx *trigger; + unsigned long flags; + int ret; + + if (vdev->ctx[0].trigger) { + free_irq(pdev->irq, vdev); + kfree(vdev->ctx[0].name); + eventfd_ctx_put(vdev->ctx[0].trigger); + vdev->ctx[0].trigger = NULL; + } + + if (fd < 0) /* Disable only */ + return 0; + + vdev->ctx[0].name = kasprintf(GFP_KERNEL, "vfio-intx(%s)", + pci_name(pdev)); + if (!vdev->ctx[0].name) + return -ENOMEM; + + trigger = eventfd_ctx_fdget(fd); + if (IS_ERR(trigger)) { + kfree(vdev->ctx[0].name); + return PTR_ERR(trigger); + } + + if (!vdev->pci_2_3) + irqflags = 0; + + ret = request_irq(pdev->irq, vfio_intx_handler, + irqflags, vdev->ctx[0].name, vdev); + if (ret) { + kfree(vdev->ctx[0].name); + eventfd_ctx_put(trigger); + return ret; + } + + vdev->ctx[0].trigger = trigger; + + /* + * INTx disable will stick across the new irq setup, + * disable_irq won't. + */ + spin_lock_irqsave(&vdev->irqlock, flags); + if (!vdev->pci_2_3 && (vdev->ctx[0].masked || vdev->virq_disabled)) + disable_irq_nosync(pdev->irq); + spin_unlock_irqrestore(&vdev->irqlock, flags); + + return 0; +} + +static void vfio_intx_disable(struct vfio_pci_device *vdev) +{ + vfio_intx_set_signal(vdev, -1); + virqfd_disable(vdev->ctx[0].unmask); + virqfd_disable(vdev->ctx[0].mask); + vdev->irq_type = VFIO_PCI_NUM_IRQS; + vdev->num_ctx = 0; + kfree(vdev->ctx); +} + +/* + * MSI/MSI-X + */ +static irqreturn_t vfio_msihandler(int irq, void *arg) +{ + struct eventfd_ctx *trigger = arg; + + eventfd_signal(trigger, 1); + return IRQ_HANDLED; +} + +static int vfio_msi_enable(struct vfio_pci_device *vdev, int nvec, bool msix) +{ + struct pci_dev *pdev = vdev->pdev; + int ret; + + if (!is_irq_none(vdev)) + return -EINVAL; + + vdev->ctx = kzalloc(nvec * sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL); + if (!vdev->ctx) + return -ENOMEM; + + if (msix) { + int i; + + vdev->msix = kzalloc(nvec * sizeof(struct msix_entry), + GFP_KERNEL); + if (!vdev->msix) { + kfree(vdev->ctx); + return -ENOMEM; + } + + for (i = 0; i < nvec; i++) + vdev->msix[i].entry = i; + + ret = pci_enable_msix(pdev, vdev->msix, nvec); + if (ret) { + kfree(vdev->msix); + kfree(vdev->ctx); + return ret; + } + } else { + ret = pci_enable_msi_block(pdev, nvec); + if (ret) { + kfree(vdev->ctx); + return ret; + } + } + + vdev->num_ctx = nvec; + vdev->irq_type = msix ? VFIO_PCI_MSIX_IRQ_INDEX : + VFIO_PCI_MSI_IRQ_INDEX; + + if (!msix) { + /* + * Compute the virtual hardware field for max msi vectors - + * it is the log base 2 of the number of vectors. + */ + vdev->msi_qmax = fls(nvec * 2 - 1) - 1; + } + + return 0; +} + +static int vfio_msi_set_vector_signal(struct vfio_pci_device *vdev, + int vector, int fd, bool msix) +{ + struct pci_dev *pdev = vdev->pdev; + int irq = msix ? vdev->msix[vector].vector : pdev->irq + vector; + char *name = msix ? "vfio-msix" : "vfio-msi"; + struct eventfd_ctx *trigger; + int ret; + + if (vector >= vdev->num_ctx) + return -EINVAL; + + if (vdev->ctx[vector].trigger) { + free_irq(irq, vdev->ctx[vector].trigger); + kfree(vdev->ctx[vector].name); + eventfd_ctx_put(vdev->ctx[vector].trigger); + vdev->ctx[vector].trigger = NULL; + } + + if (fd < 0) + return 0; + + vdev->ctx[vector].name = kasprintf(GFP_KERNEL, "%s[%d](%s)", + name, vector, pci_name(pdev)); + if (!vdev->ctx[vector].name) + return -ENOMEM; + + trigger = eventfd_ctx_fdget(fd); + if (IS_ERR(trigger)) { + kfree(vdev->ctx[vector].name); + return PTR_ERR(trigger); + } + + ret = request_irq(irq, vfio_msihandler, 0, + vdev->ctx[vector].name, trigger); + if (ret) { + kfree(vdev->ctx[vector].name); + eventfd_ctx_put(trigger); + return ret; + } + + vdev->ctx[vector].trigger = trigger; + + return 0; +} + +static int vfio_msi_set_block(struct vfio_pci_device *vdev, unsigned start, + unsigned count, int32_t *fds, bool msix) +{ + int i, j, ret = 0; + + if (start + count > vdev->num_ctx) + return -EINVAL; + + for (i = 0, j = start; i < count && !ret; i++, j++) { + int fd = fds ? fds[i] : -1; + ret = vfio_msi_set_vector_signal(vdev, j, fd, msix); + } + + if (ret) { + for (--j; j >= start; j--) + vfio_msi_set_vector_signal(vdev, j, -1, msix); + } + + return ret; +} + +static void vfio_msi_disable(struct vfio_pci_device *vdev, bool msix) +{ + struct pci_dev *pdev = vdev->pdev; + int i; + + vfio_msi_set_block(vdev, 0, vdev->num_ctx, NULL, msix); + + for (i = 0; i < vdev->num_ctx; i++) { + virqfd_disable(vdev->ctx[i].unmask); + virqfd_disable(vdev->ctx[i].mask); + } + + if (msix) { + pci_disable_msix(vdev->pdev); + kfree(vdev->msix); + } else + pci_disable_msi(pdev); + + vdev->irq_type = VFIO_PCI_NUM_IRQS; + vdev->num_ctx = 0; + kfree(vdev->ctx); +} + +/* + * IOCTL support + */ +static int vfio_pci_set_intx_unmask(struct vfio_pci_device *vdev, + unsigned index, unsigned start, + unsigned count, uint32_t flags, void *data) +{ + if (!is_intx(vdev) || start != 0 || count != 1) + return -EINVAL; + + if (flags & VFIO_IRQ_SET_DATA_NONE) { + vfio_pci_intx_unmask(vdev); + } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { + uint8_t unmask = *(uint8_t *)data; + if (unmask) + vfio_pci_intx_unmask(vdev); + } else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { + int32_t fd = *(int32_t *)data; + if (fd >= 0) + return virqfd_enable(vdev, vfio_pci_intx_unmask_handler, + vfio_send_intx_eventfd, NULL, + &vdev->ctx[0].unmask, fd); + + virqfd_disable(vdev->ctx[0].unmask); + } + + return 0; +} + +static int vfio_pci_set_intx_mask(struct vfio_pci_device *vdev, + unsigned index, unsigned start, + unsigned count, uint32_t flags, void *data) +{ + if (!is_intx(vdev) || start != 0 || count != 1) + return -EINVAL; + + if (flags & VFIO_IRQ_SET_DATA_NONE) { + vfio_pci_intx_mask(vdev); + } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { + uint8_t mask = *(uint8_t *)data; + if (mask) + vfio_pci_intx_mask(vdev); + } else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { + return -ENOTTY; /* XXX implement me */ + } + + return 0; +} + +static int vfio_pci_set_intx_trigger(struct vfio_pci_device *vdev, + unsigned index, unsigned start, + unsigned count, uint32_t flags, void *data) +{ + if (is_intx(vdev) && !count && (flags & VFIO_IRQ_SET_DATA_NONE)) { + vfio_intx_disable(vdev); + return 0; + } + + if (!(is_intx(vdev) || is_irq_none(vdev)) || start != 0 || count != 1) + return -EINVAL; + + if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { + int32_t fd = *(int32_t *)data; + int ret; + + if (is_intx(vdev)) + return vfio_intx_set_signal(vdev, fd); + + ret = vfio_intx_enable(vdev); + if (ret) + return ret; + + ret = vfio_intx_set_signal(vdev, fd); + if (ret) + vfio_intx_disable(vdev); + + return ret; + } + + if (!is_intx(vdev)) + return -EINVAL; + + if (flags & VFIO_IRQ_SET_DATA_NONE) { + vfio_send_intx_eventfd(vdev, NULL); + } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { + uint8_t trigger = *(uint8_t *)data; + if (trigger) + vfio_send_intx_eventfd(vdev, NULL); + } + return 0; +} + +static int vfio_pci_set_msi_trigger(struct vfio_pci_device *vdev, + unsigned index, unsigned start, + unsigned count, uint32_t flags, void *data) +{ + int i; + bool msix = (index == VFIO_PCI_MSIX_IRQ_INDEX) ? true : false; + + if (irq_is(vdev, index) && !count && (flags & VFIO_IRQ_SET_DATA_NONE)) { + vfio_msi_disable(vdev, msix); + return 0; + } + + if (!(irq_is(vdev, index) || is_irq_none(vdev))) + return -EINVAL; + + if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { + int32_t *fds = data; + int ret; + + if (vdev->irq_type == index) + return vfio_msi_set_block(vdev, start, count, + fds, msix); + + ret = vfio_msi_enable(vdev, start + count, msix); + if (ret) + return ret; + + ret = vfio_msi_set_block(vdev, start, count, fds, msix); + if (ret) + vfio_msi_disable(vdev, msix); + + return ret; + } + + if (!irq_is(vdev, index) || start + count > vdev->num_ctx) + return -EINVAL; + + for (i = start; i < start + count; i++) { + if (!vdev->ctx[i].trigger) + continue; + if (flags & VFIO_IRQ_SET_DATA_NONE) { + eventfd_signal(vdev->ctx[i].trigger, 1); + } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { + uint8_t *bools = data; + if (bools[i - start]) + eventfd_signal(vdev->ctx[i].trigger, 1); + } + } + return 0; +} + +int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev, uint32_t flags, + unsigned index, unsigned start, unsigned count, + void *data) +{ + int (*func)(struct vfio_pci_device *vdev, unsigned index, + unsigned start, unsigned count, uint32_t flags, + void *data) = NULL; + + switch (index) { + case VFIO_PCI_INTX_IRQ_INDEX: + switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { + case VFIO_IRQ_SET_ACTION_MASK: + func = vfio_pci_set_intx_mask; + break; + case VFIO_IRQ_SET_ACTION_UNMASK: + func = vfio_pci_set_intx_unmask; + break; + case VFIO_IRQ_SET_ACTION_TRIGGER: + func = vfio_pci_set_intx_trigger; + break; + } + break; + case VFIO_PCI_MSI_IRQ_INDEX: + case VFIO_PCI_MSIX_IRQ_INDEX: + switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { + case VFIO_IRQ_SET_ACTION_MASK: + case VFIO_IRQ_SET_ACTION_UNMASK: + /* XXX Need masking support exported */ + break; + case VFIO_IRQ_SET_ACTION_TRIGGER: + func = vfio_pci_set_msi_trigger; + break; + } + break; + } + + if (!func) + return -ENOTTY; + + return func(vdev, index, start, count, flags, data); +} diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h new file mode 100644 index 000000000000..611827cba8cd --- /dev/null +++ b/drivers/vfio/pci/vfio_pci_private.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * 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. + * + * Derived from original vfio: + * Copyright 2010 Cisco Systems, Inc. All rights reserved. + * Author: Tom Lyon, pugs@cisco.com + */ + +#include +#include + +#ifndef VFIO_PCI_PRIVATE_H +#define VFIO_PCI_PRIVATE_H + +#define VFIO_PCI_OFFSET_SHIFT 40 + +#define VFIO_PCI_OFFSET_TO_INDEX(off) (off >> VFIO_PCI_OFFSET_SHIFT) +#define VFIO_PCI_INDEX_TO_OFFSET(index) ((u64)(index) << VFIO_PCI_OFFSET_SHIFT) +#define VFIO_PCI_OFFSET_MASK (((u64)(1) << VFIO_PCI_OFFSET_SHIFT) - 1) + +struct vfio_pci_irq_ctx { + struct eventfd_ctx *trigger; + struct virqfd *unmask; + struct virqfd *mask; + char *name; + bool masked; +}; + +struct vfio_pci_device { + struct pci_dev *pdev; + void __iomem *barmap[PCI_STD_RESOURCE_END + 1]; + u8 *pci_config_map; + u8 *vconfig; + struct perm_bits *msi_perm; + spinlock_t irqlock; + struct mutex igate; + struct msix_entry *msix; + struct vfio_pci_irq_ctx *ctx; + int num_ctx; + int irq_type; + u8 msi_qmax; + u8 msix_bar; + u16 msix_size; + u32 msix_offset; + u32 rbar[7]; + bool pci_2_3; + bool virq_disabled; + bool reset_works; + bool extended_caps; + bool bardirty; + struct pci_saved_state *pci_saved_state; + atomic_t refcnt; +}; + +#define is_intx(vdev) (vdev->irq_type == VFIO_PCI_INTX_IRQ_INDEX) +#define is_msi(vdev) (vdev->irq_type == VFIO_PCI_MSI_IRQ_INDEX) +#define is_msix(vdev) (vdev->irq_type == VFIO_PCI_MSIX_IRQ_INDEX) +#define is_irq_none(vdev) (!(is_intx(vdev) || is_msi(vdev) || is_msix(vdev))) +#define irq_is(vdev, type) (vdev->irq_type == type) + +extern void vfio_pci_intx_mask(struct vfio_pci_device *vdev); +extern void vfio_pci_intx_unmask(struct vfio_pci_device *vdev); + +extern int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev, + uint32_t flags, unsigned index, + unsigned start, unsigned count, void *data); + +extern ssize_t vfio_pci_config_readwrite(struct vfio_pci_device *vdev, + char __user *buf, size_t count, + loff_t *ppos, bool iswrite); +extern ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev, + char __user *buf, size_t count, + loff_t *ppos, bool iswrite); +extern ssize_t vfio_pci_io_readwrite(struct vfio_pci_device *vdev, + char __user *buf, size_t count, + loff_t *ppos, bool iswrite); + +extern int vfio_pci_init_perm_bits(void); +extern void vfio_pci_uninit_perm_bits(void); + +extern int vfio_pci_virqfd_init(void); +extern void vfio_pci_virqfd_exit(void); + +extern int vfio_config_init(struct vfio_pci_device *vdev); +extern void vfio_config_free(struct vfio_pci_device *vdev); +#endif /* VFIO_PCI_PRIVATE_H */ diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c new file mode 100644 index 000000000000..4362d9e7baa3 --- /dev/null +++ b/drivers/vfio/pci/vfio_pci_rdwr.c @@ -0,0 +1,269 @@ +/* + * VFIO PCI I/O Port & MMIO access + * + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * 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. + * + * Derived from original vfio: + * Copyright 2010 Cisco Systems, Inc. All rights reserved. + * Author: Tom Lyon, pugs@cisco.com + */ + +#include +#include +#include +#include + +#include "vfio_pci_private.h" + +/* I/O Port BAR access */ +ssize_t vfio_pci_io_readwrite(struct vfio_pci_device *vdev, char __user *buf, + size_t count, loff_t *ppos, bool iswrite) +{ + struct pci_dev *pdev = vdev->pdev; + loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; + int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos); + void __iomem *io; + size_t done = 0; + + if (!pci_resource_start(pdev, bar)) + return -EINVAL; + + if (pos + count > pci_resource_len(pdev, bar)) + return -EINVAL; + + if (!vdev->barmap[bar]) { + int ret; + + ret = pci_request_selected_regions(pdev, 1 << bar, "vfio"); + if (ret) + return ret; + + vdev->barmap[bar] = pci_iomap(pdev, bar, 0); + + if (!vdev->barmap[bar]) { + pci_release_selected_regions(pdev, 1 << bar); + return -EINVAL; + } + } + + io = vdev->barmap[bar]; + + while (count) { + int filled; + + if (count >= 3 && !(pos % 4)) { + __le32 val; + + if (iswrite) { + if (copy_from_user(&val, buf, 4)) + return -EFAULT; + + iowrite32(le32_to_cpu(val), io + pos); + } else { + val = cpu_to_le32(ioread32(io + pos)); + + if (copy_to_user(buf, &val, 4)) + return -EFAULT; + } + + filled = 4; + + } else if ((pos % 2) == 0 && count >= 2) { + __le16 val; + + if (iswrite) { + if (copy_from_user(&val, buf, 2)) + return -EFAULT; + + iowrite16(le16_to_cpu(val), io + pos); + } else { + val = cpu_to_le16(ioread16(io + pos)); + + if (copy_to_user(buf, &val, 2)) + return -EFAULT; + } + + filled = 2; + } else { + u8 val; + + if (iswrite) { + if (copy_from_user(&val, buf, 1)) + return -EFAULT; + + iowrite8(val, io + pos); + } else { + val = ioread8(io + pos); + + if (copy_to_user(buf, &val, 1)) + return -EFAULT; + } + + filled = 1; + } + + count -= filled; + done += filled; + buf += filled; + pos += filled; + } + + *ppos += done; + + return done; +} + +/* + * MMIO BAR access + * We handle two excluded ranges here as well, if the user tries to read + * the ROM beyond what PCI tells us is available or the MSI-X table region, + * we return 0xFF and writes are dropped. + */ +ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev, char __user *buf, + size_t count, loff_t *ppos, bool iswrite) +{ + struct pci_dev *pdev = vdev->pdev; + loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; + int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos); + void __iomem *io; + resource_size_t end; + size_t done = 0; + size_t x_start = 0, x_end = 0; /* excluded range */ + + if (!pci_resource_start(pdev, bar)) + return -EINVAL; + + end = pci_resource_len(pdev, bar); + + if (pos > end) + return -EINVAL; + + if (pos == end) + return 0; + + if (pos + count > end) + count = end - pos; + + if (bar == PCI_ROM_RESOURCE) { + io = pci_map_rom(pdev, &x_start); + x_end = end; + } else { + if (!vdev->barmap[bar]) { + int ret; + + ret = pci_request_selected_regions(pdev, 1 << bar, + "vfio"); + if (ret) + return ret; + + vdev->barmap[bar] = pci_iomap(pdev, bar, 0); + + if (!vdev->barmap[bar]) { + pci_release_selected_regions(pdev, 1 << bar); + return -EINVAL; + } + } + + io = vdev->barmap[bar]; + + if (bar == vdev->msix_bar) { + x_start = vdev->msix_offset; + x_end = vdev->msix_offset + vdev->msix_size; + } + } + + if (!io) + return -EINVAL; + + while (count) { + size_t fillable, filled; + + if (pos < x_start) + fillable = x_start - pos; + else if (pos >= x_end) + fillable = end - pos; + else + fillable = 0; + + if (fillable >= 4 && !(pos % 4) && (count >= 4)) { + __le32 val; + + if (iswrite) { + if (copy_from_user(&val, buf, 4)) + goto out; + + iowrite32(le32_to_cpu(val), io + pos); + } else { + val = cpu_to_le32(ioread32(io + pos)); + + if (copy_to_user(buf, &val, 4)) + goto out; + } + + filled = 4; + } else if (fillable >= 2 && !(pos % 2) && (count >= 2)) { + __le16 val; + + if (iswrite) { + if (copy_from_user(&val, buf, 2)) + goto out; + + iowrite16(le16_to_cpu(val), io + pos); + } else { + val = cpu_to_le16(ioread16(io + pos)); + + if (copy_to_user(buf, &val, 2)) + goto out; + } + + filled = 2; + } else if (fillable) { + u8 val; + + if (iswrite) { + if (copy_from_user(&val, buf, 1)) + goto out; + + iowrite8(val, io + pos); + } else { + val = ioread8(io + pos); + + if (copy_to_user(buf, &val, 1)) + goto out; + } + + filled = 1; + } else { + /* Drop writes, fill reads with FF */ + if (!iswrite) { + char val = 0xFF; + size_t i; + + for (i = 0; i < x_end - pos; i++) { + if (put_user(val, buf + i)) + goto out; + } + } + + filled = x_end - pos; + } + + count -= filled; + done += filled; + buf += filled; + pos += filled; + } + + *ppos += done; + +out: + if (bar == PCI_ROM_RESOURCE) + pci_unmap_rom(pdev, io); + + return count ? -EFAULT : done; +} diff --git a/include/linux/vfio.h b/include/linux/vfio.h index acb046fd5b70..0a4f180a11d8 100644 --- a/include/linux/vfio.h +++ b/include/linux/vfio.h @@ -223,6 +223,7 @@ struct vfio_device_info { __u32 argsz; __u32 flags; #define VFIO_DEVICE_FLAGS_RESET (1 << 0) /* Device supports reset */ +#define VFIO_DEVICE_FLAGS_PCI (1 << 1) /* vfio-pci device */ __u32 num_regions; /* Max region index + 1 */ __u32 num_irqs; /* Max IRQ index + 1 */ }; @@ -364,6 +365,31 @@ struct vfio_irq_set { */ #define VFIO_DEVICE_RESET _IO(VFIO_TYPE, VFIO_BASE + 11) +/* + * The VFIO-PCI bus driver makes use of the following fixed region and + * IRQ index mapping. Unimplemented regions return a size of zero. + * Unimplemented IRQ types return a count of zero. + */ + +enum { + VFIO_PCI_BAR0_REGION_INDEX, + VFIO_PCI_BAR1_REGION_INDEX, + VFIO_PCI_BAR2_REGION_INDEX, + VFIO_PCI_BAR3_REGION_INDEX, + VFIO_PCI_BAR4_REGION_INDEX, + VFIO_PCI_BAR5_REGION_INDEX, + VFIO_PCI_ROM_REGION_INDEX, + VFIO_PCI_CONFIG_REGION_INDEX, + VFIO_PCI_NUM_REGIONS +}; + +enum { + VFIO_PCI_INTX_IRQ_INDEX, + VFIO_PCI_MSI_IRQ_INDEX, + VFIO_PCI_MSIX_IRQ_INDEX, + VFIO_PCI_NUM_IRQS +}; + /* -------- API for Type1 VFIO IOMMU -------- */ /** -- cgit v1.2.3 From 36323f817af0376c78612cfdab714b0feb05fea5 Mon Sep 17 00:00:00 2001 From: Thomas Huehn Date: Mon, 23 Jul 2012 21:33:42 +0200 Subject: mac80211: move TX station pointer and restructure TX Remove the control.sta pointer from ieee80211_tx_info to free up sufficient space in the TX skb control buffer for the upcoming Transmit Power Control (TPC). Instead, the pointer is now on the stack in a new control struct that is passed as a function parameter to the drivers' tx method. Signed-off-by: Thomas Huehn Signed-off-by: Alina Friedrichsen Signed-off-by: Felix Fietkau [reworded commit message] Signed-off-by: Johannes Berg --- drivers/net/wireless/adm8211.c | 4 +++- drivers/net/wireless/at76c50x-usb.c | 4 +++- drivers/net/wireless/ath/ath5k/mac80211-ops.c | 3 ++- drivers/net/wireless/ath/ath9k/ath9k.h | 1 + drivers/net/wireless/ath/ath9k/htc.h | 1 + drivers/net/wireless/ath/ath9k/htc_drv_beacon.c | 2 +- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 6 +++-- drivers/net/wireless/ath/ath9k/htc_drv_txrx.c | 2 +- drivers/net/wireless/ath/ath9k/main.c | 5 ++++- drivers/net/wireless/ath/ath9k/xmit.c | 9 ++++---- drivers/net/wireless/ath/carl9170/carl9170.h | 4 +++- drivers/net/wireless/ath/carl9170/tx.c | 16 ++++++------- drivers/net/wireless/b43/main.c | 3 ++- drivers/net/wireless/b43legacy/main.c | 1 + .../net/wireless/brcm80211/brcmsmac/mac80211_if.c | 6 +++-- drivers/net/wireless/iwlegacy/3945-mac.c | 12 ++++++---- drivers/net/wireless/iwlegacy/4965-mac.c | 26 +++++++++++++--------- drivers/net/wireless/iwlegacy/4965.h | 8 +++++-- drivers/net/wireless/iwlwifi/dvm/agn.h | 4 +++- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 6 +++-- drivers/net/wireless/iwlwifi/dvm/tx.c | 16 +++++++------ drivers/net/wireless/libertas_tf/main.c | 4 +++- drivers/net/wireless/mac80211_hwsim.c | 8 ++++--- drivers/net/wireless/mwl8k.c | 17 ++++++++------ drivers/net/wireless/p54/lmac.h | 4 +++- drivers/net/wireless/p54/main.c | 2 +- drivers/net/wireless/p54/txrx.c | 15 ++++++++----- drivers/net/wireless/rt2x00/rt2x00.h | 4 +++- drivers/net/wireless/rt2x00/rt2x00dev.c | 2 +- drivers/net/wireless/rt2x00/rt2x00mac.c | 4 +++- drivers/net/wireless/rt2x00/rt2x00queue.c | 20 +++++++++-------- drivers/net/wireless/rtl818x/rtl8180/dev.c | 6 +++-- drivers/net/wireless/rtl818x/rtl8187/dev.c | 6 +++-- drivers/net/wireless/rtlwifi/base.c | 3 +-- drivers/net/wireless/rtlwifi/core.c | 8 ++++--- drivers/net/wireless/rtlwifi/pci.c | 16 ++++++------- drivers/net/wireless/rtlwifi/rtl8192ce/trx.c | 5 +++-- drivers/net/wireless/rtlwifi/rtl8192ce/trx.h | 1 + drivers/net/wireless/rtlwifi/rtl8192cu/trx.c | 5 +++-- drivers/net/wireless/rtlwifi/rtl8192cu/trx.h | 4 +++- drivers/net/wireless/rtlwifi/rtl8192de/trx.c | 5 +++-- drivers/net/wireless/rtlwifi/rtl8192de/trx.h | 1 + drivers/net/wireless/rtlwifi/rtl8192se/trx.c | 5 +++-- drivers/net/wireless/rtlwifi/rtl8192se/trx.h | 1 + drivers/net/wireless/rtlwifi/usb.c | 15 ++++++++----- drivers/net/wireless/rtlwifi/wifi.h | 13 +++++++---- drivers/net/wireless/ti/wl1251/main.c | 4 +++- drivers/net/wireless/ti/wlcore/main.c | 6 +++-- drivers/net/wireless/zd1211rw/zd_mac.c | 6 +++-- drivers/staging/winbond/wbusb.c | 4 +++- include/net/mac80211.h | 20 ++++++++++++----- net/mac80211/driver-ops.h | 6 +++-- net/mac80211/tx.c | 5 +++-- 53 files changed, 232 insertions(+), 132 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c index 689a71c1af71..154a4965be4f 100644 --- a/drivers/net/wireless/adm8211.c +++ b/drivers/net/wireless/adm8211.c @@ -1661,7 +1661,9 @@ static void adm8211_tx_raw(struct ieee80211_hw *dev, struct sk_buff *skb, } /* Put adm8211_tx_hdr on skb and transmit */ -static void adm8211_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +static void adm8211_tx(struct ieee80211_hw *dev, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct adm8211_tx_hdr *txhdr; size_t payload_len, hdrlen; diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index efc162e0b511..abb520dec032 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -1726,7 +1726,9 @@ static void at76_mac80211_tx_callback(struct urb *urb) ieee80211_wake_queues(priv->hw); } -static void at76_mac80211_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void at76_mac80211_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct at76_priv *priv = hw->priv; struct at76_tx_buffer *tx_buffer = priv->bulk_out_buffer; diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index 260e7dc7f751..934f04c1cb9d 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -55,7 +55,8 @@ \********************/ static void -ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +ath5k_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct ath5k_hw *ah = hw->priv; u16 qnum = skb_get_queue_mapping(skb); diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index b09285c36c4a..7373e4b92c92 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -280,6 +280,7 @@ struct ath_tx_control { struct ath_txq *txq; struct ath_node *an; u8 paprd; + struct ieee80211_sta *sta; }; #define ATH_TX_ERROR 0x01 diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h index 936e920fb88e..b30596fcf73a 100644 --- a/drivers/net/wireless/ath/ath9k/htc.h +++ b/drivers/net/wireless/ath/ath9k/htc.h @@ -542,6 +542,7 @@ void ath9k_htc_stop_ani(struct ath9k_htc_priv *priv); int ath9k_tx_init(struct ath9k_htc_priv *priv); int ath9k_htc_tx_start(struct ath9k_htc_priv *priv, + struct ieee80211_sta *sta, struct sk_buff *skb, u8 slot, bool is_cab); void ath9k_tx_cleanup(struct ath9k_htc_priv *priv); bool ath9k_htc_txq_setup(struct ath9k_htc_priv *priv, int subtype); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c index 77d541feb910..f42d2eb6af99 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c @@ -326,7 +326,7 @@ static void ath9k_htc_send_buffered(struct ath9k_htc_priv *priv, goto next; } - ret = ath9k_htc_tx_start(priv, skb, tx_slot, true); + ret = ath9k_htc_tx_start(priv, NULL, skb, tx_slot, true); if (ret != 0) { ath9k_htc_tx_clear_slot(priv, tx_slot); dev_kfree_skb_any(skb); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index c785129692ff..8a0ccf70aa14 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -856,7 +856,9 @@ set_timer: /* mac80211 Callbacks */ /**********************/ -static void ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void ath9k_htc_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct ieee80211_hdr *hdr; struct ath9k_htc_priv *priv = hw->priv; @@ -883,7 +885,7 @@ static void ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb) goto fail_tx; } - ret = ath9k_htc_tx_start(priv, skb, slot, false); + ret = ath9k_htc_tx_start(priv, control->sta, skb, slot, false); if (ret != 0) { ath_dbg(common, XMIT, "Tx failed\n"); goto clear_slot; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index 47e61d0da33b..06cdcb772d78 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -333,12 +333,12 @@ static void ath9k_htc_tx_data(struct ath9k_htc_priv *priv, } int ath9k_htc_tx_start(struct ath9k_htc_priv *priv, + struct ieee80211_sta *sta, struct sk_buff *skb, u8 slot, bool is_cab) { struct ieee80211_hdr *hdr; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - struct ieee80211_sta *sta = tx_info->control.sta; struct ieee80211_vif *vif = tx_info->control.vif; struct ath9k_htc_sta *ista; struct ath9k_htc_vif *avp = NULL; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 6049d8b82855..4d8dc9ff5a75 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -694,7 +694,9 @@ mutex_unlock: return r; } -static void ath9k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void ath9k_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); @@ -754,6 +756,7 @@ static void ath9k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) memset(&txctl, 0, sizeof(struct ath_tx_control)); txctl.txq = sc->tx.txq_map[skb_get_queue_mapping(skb)]; + txctl.sta = control->sta; ath_dbg(common, XMIT, "transmitting packet, skb: %p\n", skb); diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 2c9da6b2ecb1..ef91f6cc2d79 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1773,11 +1773,12 @@ static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq, TX_STAT_INC(txq->axq_qnum, queued); } -static void setup_frame_info(struct ieee80211_hw *hw, struct sk_buff *skb, +static void setup_frame_info(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + struct sk_buff *skb, int framelen) { struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - struct ieee80211_sta *sta = tx_info->control.sta; struct ieee80211_key_conf *hw_key = tx_info->control.hw_key; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; const struct ieee80211_rate *rate; @@ -1935,7 +1936,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_sta *sta = info->control.sta; + struct ieee80211_sta *sta = txctl->sta; struct ieee80211_vif *vif = info->control.vif; struct ath_softc *sc = hw->priv; struct ath_txq *txq = txctl->txq; @@ -1979,7 +1980,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, !ieee80211_is_data(hdr->frame_control)) info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT; - setup_frame_info(hw, skb, frmlen); + setup_frame_info(hw, sta, skb, frmlen); /* * At this point, the vif, hw_key and sta pointers in the tx control diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h index 376be11161c0..8f0cbc35816f 100644 --- a/drivers/net/wireless/ath/carl9170/carl9170.h +++ b/drivers/net/wireless/ath/carl9170/carl9170.h @@ -577,7 +577,9 @@ void carl9170_rx(struct ar9170 *ar, void *buf, unsigned int len); void carl9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len); /* TX */ -void carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb); +void carl9170_op_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb); void carl9170_tx_janitor(struct work_struct *work); void carl9170_tx_process_status(struct ar9170 *ar, const struct carl9170_rsp *cmd); diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c index 6a8681407a1d..84377cf580e0 100644 --- a/drivers/net/wireless/ath/carl9170/tx.c +++ b/drivers/net/wireless/ath/carl9170/tx.c @@ -867,14 +867,15 @@ static bool carl9170_tx_cts_check(struct ar9170 *ar, return false; } -static int carl9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb) +static int carl9170_tx_prepare(struct ar9170 *ar, + struct ieee80211_sta *sta, + struct sk_buff *skb) { struct ieee80211_hdr *hdr; struct _carl9170_tx_superframe *txc; struct carl9170_vif_info *cvif; struct ieee80211_tx_info *info; struct ieee80211_tx_rate *txrate; - struct ieee80211_sta *sta; struct carl9170_tx_info *arinfo; unsigned int hw_queue; int i; @@ -910,8 +911,6 @@ static int carl9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb) else cvif = NULL; - sta = info->control.sta; - txc = (void *)skb_push(skb, sizeof(*txc)); memset(txc, 0, sizeof(*txc)); @@ -1457,20 +1456,21 @@ err_unlock_rcu: return false; } -void carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +void carl9170_op_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct ar9170 *ar = hw->priv; struct ieee80211_tx_info *info; - struct ieee80211_sta *sta; + struct ieee80211_sta *sta = control->sta; bool run; if (unlikely(!IS_STARTED(ar))) goto err_free; info = IEEE80211_SKB_CB(skb); - sta = info->control.sta; - if (unlikely(carl9170_tx_prepare(ar, skb))) + if (unlikely(carl9170_tx_prepare(ar, sta, skb))) goto err_free; carl9170_tx_accounting(ar, skb); diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index b80352b308d5..2ca4e885f5ff 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3407,7 +3407,8 @@ static void b43_tx_work(struct work_struct *work) } static void b43_op_tx(struct ieee80211_hw *hw, - struct sk_buff *skb) + struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct b43_wl *wl = hw_to_b43_wl(hw); diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index 8156135a0590..74d4c20cc31b 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -2492,6 +2492,7 @@ static void b43legacy_tx_work(struct work_struct *work) } static void b43legacy_op_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, struct sk_buff *skb) { struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index 9e79d47e077f..a7be68d2eaf7 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -264,7 +264,9 @@ static void brcms_set_basic_rate(struct brcm_rateset *rs, u16 rate, bool is_br) } } -static void brcms_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void brcms_ops_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct brcms_info *wl = hw->priv; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); @@ -276,7 +278,7 @@ static void brcms_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb) goto done; } brcms_c_sendpkt_mac80211(wl->wlc, skb, hw); - tx_info->rate_driver_data[0] = tx_info->control.sta; + tx_info->rate_driver_data[0] = control->sta; done: spin_unlock_bh(&wl->lock); } diff --git a/drivers/net/wireless/iwlegacy/3945-mac.c b/drivers/net/wireless/iwlegacy/3945-mac.c index faec40467208..e252acb9c862 100644 --- a/drivers/net/wireless/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/iwlegacy/3945-mac.c @@ -460,7 +460,9 @@ il3945_build_tx_cmd_basic(struct il_priv *il, struct il_device_cmd *cmd, * start C_TX command process */ static int -il3945_tx_skb(struct il_priv *il, struct sk_buff *skb) +il3945_tx_skb(struct il_priv *il, + struct ieee80211_sta *sta, + struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -512,7 +514,7 @@ il3945_tx_skb(struct il_priv *il, struct sk_buff *skb) hdr_len = ieee80211_hdrlen(fc); /* Find idx into station table for destination station */ - sta_id = il_sta_id_or_broadcast(il, info->control.sta); + sta_id = il_sta_id_or_broadcast(il, sta); if (sta_id == IL_INVALID_STATION) { D_DROP("Dropping - INVALID STATION: %pM\n", hdr->addr1); goto drop; @@ -2859,7 +2861,9 @@ il3945_mac_stop(struct ieee80211_hw *hw) } static void -il3945_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +il3945_mac_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct il_priv *il = hw->priv; @@ -2868,7 +2872,7 @@ il3945_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) D_TX("dev->xmit(%d bytes) at rate 0x%02x\n", skb->len, ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate); - if (il3945_tx_skb(il, skb)) + if (il3945_tx_skb(il, control->sta, skb)) dev_kfree_skb_any(skb); D_MAC80211("leave\n"); diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index 34f61a0581a2..eac4dc8bc879 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -1526,8 +1526,11 @@ il4965_tx_cmd_build_basic(struct il_priv *il, struct sk_buff *skb, } static void -il4965_tx_cmd_build_rate(struct il_priv *il, struct il_tx_cmd *tx_cmd, - struct ieee80211_tx_info *info, __le16 fc) +il4965_tx_cmd_build_rate(struct il_priv *il, + struct il_tx_cmd *tx_cmd, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, + __le16 fc) { const u8 rts_retry_limit = 60; u32 rate_flags; @@ -1561,9 +1564,7 @@ il4965_tx_cmd_build_rate(struct il_priv *il, struct il_tx_cmd *tx_cmd, rate_idx = info->control.rates[0].idx; if ((info->control.rates[0].flags & IEEE80211_TX_RC_MCS) || rate_idx < 0 || rate_idx > RATE_COUNT_LEGACY) - rate_idx = - rate_lowest_index(&il->bands[info->band], - info->control.sta); + rate_idx = rate_lowest_index(&il->bands[info->band], sta); /* For 5 GHZ band, remap mac80211 rate indices into driver indices */ if (info->band == IEEE80211_BAND_5GHZ) rate_idx += IL_FIRST_OFDM_RATE; @@ -1630,11 +1631,12 @@ il4965_tx_cmd_build_hwcrypto(struct il_priv *il, struct ieee80211_tx_info *info, * start C_TX command process */ int -il4965_tx_skb(struct il_priv *il, struct sk_buff *skb) +il4965_tx_skb(struct il_priv *il, + struct ieee80211_sta *sta, + struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_sta *sta = info->control.sta; struct il_station_priv *sta_priv = NULL; struct il_tx_queue *txq; struct il_queue *q; @@ -1680,7 +1682,7 @@ il4965_tx_skb(struct il_priv *il, struct sk_buff *skb) sta_id = il->hw_params.bcast_id; else { /* Find idx into station table for destination station */ - sta_id = il_sta_id_or_broadcast(il, info->control.sta); + sta_id = il_sta_id_or_broadcast(il, sta); if (sta_id == IL_INVALID_STATION) { D_DROP("Dropping - INVALID STATION: %pM\n", hdr->addr1); @@ -1786,7 +1788,7 @@ il4965_tx_skb(struct il_priv *il, struct sk_buff *skb) /* TODO need this for burst mode later on */ il4965_tx_cmd_build_basic(il, skb, tx_cmd, info, hdr, sta_id); - il4965_tx_cmd_build_rate(il, tx_cmd, info, fc); + il4965_tx_cmd_build_rate(il, tx_cmd, info, sta, fc); il_update_stats(il, true, fc, len); /* @@ -5828,7 +5830,9 @@ il4965_mac_stop(struct ieee80211_hw *hw) } void -il4965_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +il4965_mac_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct il_priv *il = hw->priv; @@ -5837,7 +5841,7 @@ il4965_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) D_TX("dev->xmit(%d bytes) at rate 0x%02x\n", skb->len, ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate); - if (il4965_tx_skb(il, skb)) + if (il4965_tx_skb(il, control->sta, skb)) dev_kfree_skb_any(skb); D_MACDUMP("leave\n"); diff --git a/drivers/net/wireless/iwlegacy/4965.h b/drivers/net/wireless/iwlegacy/4965.h index 1db677689cfe..2d092f328547 100644 --- a/drivers/net/wireless/iwlegacy/4965.h +++ b/drivers/net/wireless/iwlegacy/4965.h @@ -78,7 +78,9 @@ int il4965_hw_txq_attach_buf_to_tfd(struct il_priv *il, struct il_tx_queue *txq, int il4965_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq); void il4965_hwrate_to_tx_control(struct il_priv *il, u32 rate_n_flags, struct ieee80211_tx_info *info); -int il4965_tx_skb(struct il_priv *il, struct sk_buff *skb); +int il4965_tx_skb(struct il_priv *il, + struct ieee80211_sta *sta, + struct sk_buff *skb); int il4965_tx_agg_start(struct il_priv *il, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid, u16 * ssn); int il4965_tx_agg_stop(struct il_priv *il, struct ieee80211_vif *vif, @@ -163,7 +165,9 @@ void il4965_eeprom_release_semaphore(struct il_priv *il); int il4965_eeprom_check_version(struct il_priv *il); /* mac80211 handlers (for 4965) */ -void il4965_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb); +void il4965_mac_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb); int il4965_mac_start(struct ieee80211_hw *hw); void il4965_mac_stop(struct ieee80211_hw *hw); void il4965_configure_filter(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h index 9bb16bdf6d26..f0b8c1f7591c 100644 --- a/drivers/net/wireless/iwlwifi/dvm/agn.h +++ b/drivers/net/wireless/iwlwifi/dvm/agn.h @@ -201,7 +201,9 @@ void iwl_chswitch_done(struct iwl_priv *priv, bool is_success); /* tx */ -int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb); +int iwlagn_tx_skb(struct iwl_priv *priv, + struct ieee80211_sta *sta, + struct sk_buff *skb); int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid, u16 *ssn); int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index a5f7bce96325..e64af60a37c1 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -511,14 +511,16 @@ static void iwlagn_mac_set_wakeup(struct ieee80211_hw *hw, bool enabled) } #endif -static void iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void iwlagn_mac_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); IWL_DEBUG_TX(priv, "dev->xmit(%d bytes) at rate 0x%02x\n", skb->len, ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate); - if (iwlagn_tx_skb(priv, skb)) + if (iwlagn_tx_skb(priv, control->sta, skb)) dev_kfree_skb_any(skb); } diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index 5971a23aa47d..d17799b316d7 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -127,6 +127,7 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv, static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv, struct iwl_tx_cmd *tx_cmd, struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, __le16 fc) { u32 rate_flags; @@ -187,8 +188,7 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv, if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS || (rate_idx < 0) || (rate_idx > IWL_RATE_COUNT_LEGACY)) rate_idx = rate_lowest_index( - &priv->eeprom_data->bands[info->band], - info->control.sta); + &priv->eeprom_data->bands[info->band], sta); /* For 5 GHZ band, remap mac80211 rate indices into driver indices */ if (info->band == IEEE80211_BAND_5GHZ) rate_idx += IWL_FIRST_OFDM_RATE; @@ -291,7 +291,9 @@ static int iwl_sta_id_or_broadcast(struct iwl_rxon_context *context, /* * start REPLY_TX command process */ -int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) +int iwlagn_tx_skb(struct iwl_priv *priv, + struct ieee80211_sta *sta, + struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -345,7 +347,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) sta_id = ctx->bcast_sta_id; else { /* Find index into station table for destination station */ - sta_id = iwl_sta_id_or_broadcast(ctx, info->control.sta); + sta_id = iwl_sta_id_or_broadcast(ctx, sta); if (sta_id == IWL_INVALID_STATION) { IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n", hdr->addr1); @@ -355,8 +357,8 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) IWL_DEBUG_TX(priv, "station Id %d\n", sta_id); - if (info->control.sta) - sta_priv = (void *)info->control.sta->drv_priv; + if (sta) + sta_priv = (void *)sta->drv_priv; if (sta_priv && sta_priv->asleep && (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)) { @@ -397,7 +399,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) /* TODO need this for burst mode later on */ iwlagn_tx_cmd_build_basic(priv, skb, tx_cmd, info, hdr, sta_id); - iwlagn_tx_cmd_build_rate(priv, tx_cmd, info, fc); + iwlagn_tx_cmd_build_rate(priv, tx_cmd, info, sta, fc); memset(&info->status, 0, sizeof(info->status)); diff --git a/drivers/net/wireless/libertas_tf/main.c b/drivers/net/wireless/libertas_tf/main.c index a03457292c88..7001856241e6 100644 --- a/drivers/net/wireless/libertas_tf/main.c +++ b/drivers/net/wireless/libertas_tf/main.c @@ -227,7 +227,9 @@ static void lbtf_free_adapter(struct lbtf_private *priv) lbtf_deb_leave(LBTF_DEB_MAIN); } -static void lbtf_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void lbtf_op_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct lbtf_private *priv = hw->priv; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 643f968b05ee..ed1386aa3b17 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -709,7 +709,9 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, return ack; } -static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void mac80211_hwsim_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { bool ack; struct ieee80211_tx_info *txi; @@ -741,8 +743,8 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (txi->control.vif) hwsim_check_magic(txi->control.vif); - if (txi->control.sta) - hwsim_check_sta_magic(txi->control.sta); + if (control->sta) + hwsim_check_sta_magic(control->sta); ieee80211_tx_info_clear_status(txi); diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 224e03ade145..5099e5375cb3 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -1830,12 +1830,14 @@ static inline void mwl8k_tx_count_packet(struct ieee80211_sta *sta, u8 tid) } static void -mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) +mwl8k_txq_xmit(struct ieee80211_hw *hw, + int index, + struct ieee80211_sta *sta, + struct sk_buff *skb) { struct mwl8k_priv *priv = hw->priv; struct ieee80211_tx_info *tx_info; struct mwl8k_vif *mwl8k_vif; - struct ieee80211_sta *sta; struct ieee80211_hdr *wh; struct mwl8k_tx_queue *txq; struct mwl8k_tx_desc *tx; @@ -1867,7 +1869,6 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) wh = &((struct mwl8k_dma_data *)skb->data)->wh; tx_info = IEEE80211_SKB_CB(skb); - sta = tx_info->control.sta; mwl8k_vif = MWL8K_VIF(tx_info->control.vif); if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { @@ -2019,8 +2020,8 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) tx->pkt_phys_addr = cpu_to_le32(dma); tx->pkt_len = cpu_to_le16(skb->len); tx->rate_info = 0; - if (!priv->ap_fw && tx_info->control.sta != NULL) - tx->peer_id = MWL8K_STA(tx_info->control.sta)->peer_id; + if (!priv->ap_fw && sta != NULL) + tx->peer_id = MWL8K_STA(sta)->peer_id; else tx->peer_id = 0; @@ -4364,7 +4365,9 @@ static void mwl8k_rx_poll(unsigned long data) /* * Core driver operations. */ -static void mwl8k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void mwl8k_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct mwl8k_priv *priv = hw->priv; int index = skb_get_queue_mapping(skb); @@ -4376,7 +4379,7 @@ static void mwl8k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) return; } - mwl8k_txq_xmit(hw, index, skb); + mwl8k_txq_xmit(hw, index, control->sta, skb); } static int mwl8k_start(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/p54/lmac.h b/drivers/net/wireless/p54/lmac.h index 3d8d622bec55..de1d46bf97df 100644 --- a/drivers/net/wireless/p54/lmac.h +++ b/drivers/net/wireless/p54/lmac.h @@ -526,7 +526,9 @@ int p54_init_leds(struct p54_common *priv); void p54_unregister_leds(struct p54_common *priv); /* xmit functions */ -void p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb); +void p54_tx_80211(struct ieee80211_hw *dev, + struct ieee80211_tx_control *control, + struct sk_buff *skb); int p54_tx_cancel(struct p54_common *priv, __le32 req_id); void p54_tx(struct p54_common *priv, struct sk_buff *skb); diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index 7cffea795ad2..5e91ad06dd5d 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -158,7 +158,7 @@ static int p54_beacon_update(struct p54_common *priv, * to cancel the old beacon template by hand, instead the firmware * will release the previous one through the feedback mechanism. */ - p54_tx_80211(priv->hw, beacon); + p54_tx_80211(priv->hw, NULL, beacon); priv->tsf_high32 = 0; priv->tsf_low32 = 0; diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index f38786e02623..5861e13a6fd8 100644 --- a/drivers/net/wireless/p54/txrx.c +++ b/drivers/net/wireless/p54/txrx.c @@ -676,8 +676,9 @@ int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb) EXPORT_SYMBOL_GPL(p54_rx); static void p54_tx_80211_header(struct p54_common *priv, struct sk_buff *skb, - struct ieee80211_tx_info *info, u8 *queue, - u32 *extra_len, u16 *flags, u16 *aid, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, + u8 *queue, u32 *extra_len, u16 *flags, u16 *aid, bool *burst_possible) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; @@ -746,8 +747,8 @@ static void p54_tx_80211_header(struct p54_common *priv, struct sk_buff *skb, } } - if (info->control.sta) - *aid = info->control.sta->aid; + if (sta) + *aid = sta->aid; break; } } @@ -767,7 +768,9 @@ static u8 p54_convert_algo(u32 cipher) } } -void p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb) +void p54_tx_80211(struct ieee80211_hw *dev, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct p54_common *priv = dev->priv; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -784,7 +787,7 @@ void p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb) u8 nrates = 0, nremaining = 8; bool burst_allowed = false; - p54_tx_80211_header(priv, skb, info, &queue, &extra_len, + p54_tx_80211_header(priv, skb, info, control->sta, &queue, &extra_len, &hdr_flags, &aid, &burst_allowed); if (p54_tx_qos_accounting_alloc(priv, skb, queue)) { diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 8afb546c2b2d..f991e8bedc70 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -1287,7 +1287,9 @@ void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp); /* * mac80211 handlers. */ -void rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb); +void rt2x00mac_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb); int rt2x00mac_start(struct ieee80211_hw *hw); void rt2x00mac_stop(struct ieee80211_hw *hw); int rt2x00mac_add_interface(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index a6b88bd4a1a5..a59048ffa092 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -194,7 +194,7 @@ static void rt2x00lib_bc_buffer_iter(void *data, u8 *mac, */ skb = ieee80211_get_buffered_bc(rt2x00dev->hw, vif); while (skb) { - rt2x00mac_tx(rt2x00dev->hw, skb); + rt2x00mac_tx(rt2x00dev->hw, NULL, skb); skb = ieee80211_get_buffered_bc(rt2x00dev->hw, vif); } } diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index 4ff26c2159bf..c3d0f2f87b69 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -99,7 +99,9 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, return retval; } -void rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +void rt2x00mac_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct rt2x00_dev *rt2x00dev = hw->priv; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index f7e74a0a7759..e488b944a034 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -315,6 +315,7 @@ static void rt2x00queue_create_tx_descriptor_plcp(struct rt2x00_dev *rt2x00dev, static void rt2x00queue_create_tx_descriptor_ht(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb, struct txentry_desc *txdesc, + struct ieee80211_sta *sta, const struct rt2x00_rate *hwrate) { struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); @@ -322,11 +323,11 @@ static void rt2x00queue_create_tx_descriptor_ht(struct rt2x00_dev *rt2x00dev, struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct rt2x00_sta *sta_priv = NULL; - if (tx_info->control.sta) { + if (sta) { txdesc->u.ht.mpdu_density = - tx_info->control.sta->ht_cap.ampdu_density; + sta->ht_cap.ampdu_density; - sta_priv = sta_to_rt2x00_sta(tx_info->control.sta); + sta_priv = sta_to_rt2x00_sta(sta); txdesc->u.ht.wcid = sta_priv->wcid; } @@ -341,8 +342,8 @@ static void rt2x00queue_create_tx_descriptor_ht(struct rt2x00_dev *rt2x00dev, * MIMO PS should be set to 1 for STA's using dynamic SM PS * when using more then one tx stream (>MCS7). */ - if (tx_info->control.sta && txdesc->u.ht.mcs > 7 && - ((tx_info->control.sta->ht_cap.cap & + if (sta && txdesc->u.ht.mcs > 7 && + ((sta->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> IEEE80211_HT_CAP_SM_PS_SHIFT) == WLAN_HT_CAP_SM_PS_DYNAMIC) @@ -409,7 +410,8 @@ static void rt2x00queue_create_tx_descriptor_ht(struct rt2x00_dev *rt2x00dev, static void rt2x00queue_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb, - struct txentry_desc *txdesc) + struct txentry_desc *txdesc, + struct ieee80211_sta *sta) { struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; @@ -503,7 +505,7 @@ static void rt2x00queue_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, if (test_bit(REQUIRE_HT_TX_DESC, &rt2x00dev->cap_flags)) rt2x00queue_create_tx_descriptor_ht(rt2x00dev, skb, txdesc, - hwrate); + sta, hwrate); else rt2x00queue_create_tx_descriptor_plcp(rt2x00dev, skb, txdesc, hwrate); @@ -595,7 +597,7 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, * after that we are free to use the skb->cb array * for our information. */ - rt2x00queue_create_tx_descriptor(queue->rt2x00dev, skb, &txdesc); + rt2x00queue_create_tx_descriptor(queue->rt2x00dev, skb, &txdesc, NULL); /* * All information is retrieved from the skb->cb array, @@ -740,7 +742,7 @@ int rt2x00queue_update_beacon_locked(struct rt2x00_dev *rt2x00dev, * after that we are free to use the skb->cb array * for our information. */ - rt2x00queue_create_tx_descriptor(rt2x00dev, intf->beacon->skb, &txdesc); + rt2x00queue_create_tx_descriptor(rt2x00dev, intf->beacon->skb, &txdesc, NULL); /* * Fill in skb descriptor diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index aceaf689f737..021d83e1b1d3 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -244,7 +244,9 @@ static irqreturn_t rtl8180_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static void rtl8180_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +static void rtl8180_tx(struct ieee80211_hw *dev, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; @@ -710,7 +712,7 @@ static void rtl8180_beacon_work(struct work_struct *work) /* TODO: use actual beacon queue */ skb_set_queue_mapping(skb, 0); - rtl8180_tx(dev, skb); + rtl8180_tx(dev, NULL, skb); resched: /* diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c b/drivers/net/wireless/rtl818x/rtl8187/dev.c index 71a30b026089..05d8ca045afd 100644 --- a/drivers/net/wireless/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c @@ -228,7 +228,9 @@ static void rtl8187_tx_cb(struct urb *urb) } } -static void rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +static void rtl8187_tx(struct ieee80211_hw *dev, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct rtl8187_priv *priv = dev->priv; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -1076,7 +1078,7 @@ static void rtl8187_beacon_work(struct work_struct *work) /* TODO: use actual beacon queue */ skb_set_queue_mapping(skb, 0); - rtl8187_tx(dev, skb); + rtl8187_tx(dev, NULL, skb); resched: /* diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c index 942e56b77b60..59381fe8ed06 100644 --- a/drivers/net/wireless/rtlwifi/base.c +++ b/drivers/net/wireless/rtlwifi/base.c @@ -1341,9 +1341,8 @@ int rtl_send_smps_action(struct ieee80211_hw *hw, rtlpriv->cfg->ops->update_rate_tbl(hw, sta, 0); info->control.rates[0].idx = 0; - info->control.sta = sta; info->band = hw->conf.channel->band; - rtlpriv->intf_ops->adapter_tx(hw, skb, &tcb_desc); + rtlpriv->intf_ops->adapter_tx(hw, sta, skb, &tcb_desc); } err_free: return 0; diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index a18ad2a98938..a7c0e52869ba 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -124,7 +124,9 @@ static void rtl_op_stop(struct ieee80211_hw *hw) mutex_unlock(&rtlpriv->locks.conf_mutex); } -static void rtl_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void rtl_op_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); @@ -138,8 +140,8 @@ static void rtl_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (!test_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status)) goto err_free; - if (!rtlpriv->intf_ops->waitq_insert(hw, skb)) - rtlpriv->intf_ops->adapter_tx(hw, skb, &tcb_desc); + if (!rtlpriv->intf_ops->waitq_insert(hw, control->sta, skb)) + rtlpriv->intf_ops->adapter_tx(hw, control->sta, skb, &tcb_desc); return; diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c index 80f75d3ba84a..aad9d44c0a51 100644 --- a/drivers/net/wireless/rtlwifi/pci.c +++ b/drivers/net/wireless/rtlwifi/pci.c @@ -504,7 +504,7 @@ static void _rtl_pci_tx_chk_waitq(struct ieee80211_hw *hw) _rtl_update_earlymode_info(hw, skb, &tcb_desc, tid); - rtlpriv->intf_ops->adapter_tx(hw, skb, &tcb_desc); + rtlpriv->intf_ops->adapter_tx(hw, NULL, skb, &tcb_desc); } } } @@ -929,7 +929,7 @@ static void _rtl_pci_prepare_bcn_tasklet(struct ieee80211_hw *hw) info = IEEE80211_SKB_CB(pskb); pdesc = &ring->desc[0]; rtlpriv->cfg->ops->fill_tx_desc(hw, hdr, (u8 *) pdesc, - info, pskb, BEACON_QUEUE, &tcb_desc); + info, NULL, pskb, BEACON_QUEUE, &tcb_desc); __skb_queue_tail(&ring->queue, pskb); @@ -1305,11 +1305,10 @@ int rtl_pci_reset_trx_ring(struct ieee80211_hw *hw) } static bool rtl_pci_tx_chk_waitq_insert(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, struct sk_buff *skb) { struct rtl_priv *rtlpriv = rtl_priv(hw); - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_sta *sta = info->control.sta; struct rtl_sta_info *sta_entry = NULL; u8 tid = rtl_get_tid(skb); @@ -1337,13 +1336,14 @@ static bool rtl_pci_tx_chk_waitq_insert(struct ieee80211_hw *hw, return true; } -static int rtl_pci_tx(struct ieee80211_hw *hw, struct sk_buff *skb, - struct rtl_tcb_desc *ptcb_desc) +static int rtl_pci_tx(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + struct sk_buff *skb, + struct rtl_tcb_desc *ptcb_desc) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_sta_info *sta_entry = NULL; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_sta *sta = info->control.sta; struct rtl8192_tx_ring *ring; struct rtl_tx_desc *pdesc; u8 idx; @@ -1418,7 +1418,7 @@ static int rtl_pci_tx(struct ieee80211_hw *hw, struct sk_buff *skb, rtlpriv->cfg->ops->led_control(hw, LED_CTL_TX); rtlpriv->cfg->ops->fill_tx_desc(hw, hdr, (u8 *)pdesc, - info, skb, hw_queue, ptcb_desc); + info, sta, skb, hw_queue, ptcb_desc); __skb_queue_tail(&ring->queue, skb); diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c index 52166640f167..390d6d4fcaa0 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c @@ -596,7 +596,9 @@ bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw, void rtl92ce_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc_tx, - struct ieee80211_tx_info *info, struct sk_buff *skb, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, + struct sk_buff *skb, u8 hw_queue, struct rtl_tcb_desc *tcb_desc) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -604,7 +606,6 @@ void rtl92ce_tx_fill_desc(struct ieee80211_hw *hw, struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); bool defaultadapter = true; - struct ieee80211_sta *sta; u8 *pdesc = pdesc_tx; u16 seq_number; __le16 fc = hdr->frame_control; diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h index c4adb9777365..a7cdd514cb2e 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h @@ -713,6 +713,7 @@ struct rx_desc_92c { void rtl92ce_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc, struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, struct sk_buff *skb, u8 hw_queue, struct rtl_tcb_desc *ptcb_desc); bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c index 2e6eb356a93e..27863d773790 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c @@ -496,7 +496,9 @@ static void _rtl_tx_desc_checksum(u8 *txdesc) void rtl92cu_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc_tx, - struct ieee80211_tx_info *info, struct sk_buff *skb, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, + struct sk_buff *skb, u8 queue_index, struct rtl_tcb_desc *tcb_desc) { @@ -504,7 +506,6 @@ void rtl92cu_tx_fill_desc(struct ieee80211_hw *hw, struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); bool defaultadapter = true; - struct ieee80211_sta *sta = info->control.sta = info->control.sta; u8 *qc = ieee80211_get_qos_ctl(hdr); u8 tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; u16 seq_number; diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.h b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.h index 332b06e78b00..725c53accc58 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.h +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.h @@ -420,7 +420,9 @@ struct sk_buff *rtl8192c_tx_aggregate_hdl(struct ieee80211_hw *, struct sk_buff_head *); void rtl92cu_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc_tx, - struct ieee80211_tx_info *info, struct sk_buff *skb, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, + struct sk_buff *skb, u8 queue_index, struct rtl_tcb_desc *tcb_desc); void rtl92cu_fill_fake_txdesc(struct ieee80211_hw *hw, u8 * pDesc, diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c index f80690d82c11..4686f340b9d6 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c @@ -551,7 +551,9 @@ static void _rtl92de_insert_emcontent(struct rtl_tcb_desc *ptcb_desc, void rtl92de_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc_tx, - struct ieee80211_tx_info *info, struct sk_buff *skb, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, + struct sk_buff *skb, u8 hw_queue, struct rtl_tcb_desc *ptcb_desc) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -559,7 +561,6 @@ void rtl92de_tx_fill_desc(struct ieee80211_hw *hw, struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtlpriv); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); - struct ieee80211_sta *sta = info->control.sta; u8 *pdesc = pdesc_tx; u16 seq_number; __le16 fc = hdr->frame_control; diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/trx.h b/drivers/net/wireless/rtlwifi/rtl8192de/trx.h index 057a52431b00..c1b5dfb79d53 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/trx.h +++ b/drivers/net/wireless/rtlwifi/rtl8192de/trx.h @@ -730,6 +730,7 @@ struct rx_desc_92d { void rtl92de_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc, struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, struct sk_buff *skb, u8 hw_queue, struct rtl_tcb_desc *ptcb_desc); bool rtl92de_rx_query_desc(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c index 36d1cb3aef8a..28c53fb12aeb 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c @@ -591,14 +591,15 @@ bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, void rtl92se_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc_tx, - struct ieee80211_tx_info *info, struct sk_buff *skb, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, + struct sk_buff *skb, u8 hw_queue, struct rtl_tcb_desc *ptcb_desc) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); - struct ieee80211_sta *sta = info->control.sta; u8 *pdesc = pdesc_tx; u16 seq_number; __le16 fc = hdr->frame_control; diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/trx.h b/drivers/net/wireless/rtlwifi/rtl8192se/trx.h index 011e7b0695f2..64dd66f287c1 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/trx.h +++ b/drivers/net/wireless/rtlwifi/rtl8192se/trx.h @@ -31,6 +31,7 @@ void rtl92se_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc, struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, struct sk_buff *skb, u8 hw_queue, struct rtl_tcb_desc *ptcb_desc); void rtl92se_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, bool firstseg, diff --git a/drivers/net/wireless/rtlwifi/usb.c b/drivers/net/wireless/rtlwifi/usb.c index aa970fc18a21..914046903cfd 100644 --- a/drivers/net/wireless/rtlwifi/usb.c +++ b/drivers/net/wireless/rtlwifi/usb.c @@ -848,8 +848,10 @@ static void _rtl_usb_transmit(struct ieee80211_hw *hw, struct sk_buff *skb, _rtl_submit_tx_urb(hw, _urb); } -static void _rtl_usb_tx_preprocess(struct ieee80211_hw *hw, struct sk_buff *skb, - u16 hw_queue) +static void _rtl_usb_tx_preprocess(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + struct sk_buff *skb, + u16 hw_queue) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); @@ -891,7 +893,7 @@ static void _rtl_usb_tx_preprocess(struct ieee80211_hw *hw, struct sk_buff *skb, seq_number += 1; seq_number <<= 4; } - rtlpriv->cfg->ops->fill_tx_desc(hw, hdr, (u8 *)pdesc, info, skb, + rtlpriv->cfg->ops->fill_tx_desc(hw, hdr, (u8 *)pdesc, info, sta, skb, hw_queue, &tcb_desc); if (!ieee80211_has_morefrags(hdr->frame_control)) { if (qc) @@ -901,7 +903,9 @@ static void _rtl_usb_tx_preprocess(struct ieee80211_hw *hw, struct sk_buff *skb, rtlpriv->cfg->ops->led_control(hw, LED_CTL_TX); } -static int rtl_usb_tx(struct ieee80211_hw *hw, struct sk_buff *skb, +static int rtl_usb_tx(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + struct sk_buff *skb, struct rtl_tcb_desc *dummy) { struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw)); @@ -913,7 +917,7 @@ static int rtl_usb_tx(struct ieee80211_hw *hw, struct sk_buff *skb, if (unlikely(is_hal_stop(rtlhal))) goto err_free; hw_queue = rtlusb->usb_mq_to_hwq(fc, skb_get_queue_mapping(skb)); - _rtl_usb_tx_preprocess(hw, skb, hw_queue); + _rtl_usb_tx_preprocess(hw, sta, skb, hw_queue); _rtl_usb_transmit(hw, skb, hw_queue); return NETDEV_TX_OK; @@ -923,6 +927,7 @@ err_free: } static bool rtl_usb_tx_chk_waitq_insert(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, struct sk_buff *skb) { return false; diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h index cdaa21f29710..40153e7bf702 100644 --- a/drivers/net/wireless/rtlwifi/wifi.h +++ b/drivers/net/wireless/rtlwifi/wifi.h @@ -122,7 +122,7 @@ enum rt_eeprom_type { EEPROM_BOOT_EFUSE, }; -enum rtl_status { +enum ttl_status { RTL_STATUS_INTERFACE_START = 0, }; @@ -1418,6 +1418,7 @@ struct rtl_hal_ops { void (*fill_tx_desc) (struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc_tx, struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, struct sk_buff *skb, u8 hw_queue, struct rtl_tcb_desc *ptcb_desc); void (*fill_fake_txdesc) (struct ieee80211_hw *hw, u8 *pDesc, @@ -1475,11 +1476,15 @@ struct rtl_intf_ops { int (*adapter_start) (struct ieee80211_hw *hw); void (*adapter_stop) (struct ieee80211_hw *hw); - int (*adapter_tx) (struct ieee80211_hw *hw, struct sk_buff *skb, - struct rtl_tcb_desc *ptcb_desc); + int (*adapter_tx) (struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + struct sk_buff *skb, + struct rtl_tcb_desc *ptcb_desc); void (*flush)(struct ieee80211_hw *hw, bool drop); int (*reset_trx_ring) (struct ieee80211_hw *hw); - bool (*waitq_insert) (struct ieee80211_hw *hw, struct sk_buff *skb); + bool (*waitq_insert) (struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + struct sk_buff *skb); /*pci */ void (*disable_aspm) (struct ieee80211_hw *hw); diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c index 3118c425bcf1..441cbccbd381 100644 --- a/drivers/net/wireless/ti/wl1251/main.c +++ b/drivers/net/wireless/ti/wl1251/main.c @@ -354,7 +354,9 @@ out: return ret; } -static void wl1251_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void wl1251_op_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct wl1251 *wl = hw->priv; unsigned long flags; diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 5efd5919db3b..ff830cf50c70 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1181,7 +1181,9 @@ out: return ret; } -static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void wl1271_op_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct wl1271 *wl = hw->priv; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -1197,7 +1199,7 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) mapping = skb_get_queue_mapping(skb); q = wl1271_tx_get_queue(mapping); - hlid = wl12xx_tx_get_hlid(wl, wlvif, skb, info->control.sta); + hlid = wl12xx_tx_get_hlid(wl, wlvif, skb, control->sta); spin_lock_irqsave(&wl->wl_lock, flags); diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index c9e2660e1263..459880104758 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -937,7 +937,9 @@ static int fill_ctrlset(struct zd_mac *mac, * control block of the skbuff will be initialized. If necessary the incoming * mac80211 queues will be stopped. */ -static void zd_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void zd_op_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct zd_mac *mac = zd_hw_mac(hw); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -1176,7 +1178,7 @@ static void zd_beacon_done(struct zd_mac *mac) skb = ieee80211_get_buffered_bc(mac->hw, mac->vif); if (!skb) break; - zd_op_tx(mac->hw, skb); + zd_op_tx(mac->hw, NULL, skb); } /* diff --git a/drivers/staging/winbond/wbusb.c b/drivers/staging/winbond/wbusb.c index ef360547ecec..b76d95e180fa 100644 --- a/drivers/staging/winbond/wbusb.c +++ b/drivers/staging/winbond/wbusb.c @@ -119,7 +119,9 @@ static void wbsoft_configure_filter(struct ieee80211_hw *dev, *total_flags = new_flags; } -static void wbsoft_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +static void wbsoft_tx(struct ieee80211_hw *dev, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct wbsoft_priv *priv = dev->priv; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index d67d3bbe21c1..8114f590f715 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -527,9 +527,6 @@ struct ieee80211_tx_rate { * (2) driver internal use (if applicable) * (3) TX status information - driver tells mac80211 what happened * - * The TX control's sta pointer is only valid during the ->tx call, - * it may be NULL. - * * @flags: transmit info flags, defined above * @band: the band to transmit on (use for checking for races) * @hw_queue: HW queue to put the frame on, skb_get_queue_mapping() gives the AC @@ -560,6 +557,7 @@ struct ieee80211_tx_info { struct ieee80211_tx_rate rates[ IEEE80211_TX_MAX_RATES]; s8 rts_cts_rate_idx; + /* 3 bytes free */ }; /* only needed before rate control */ unsigned long jiffies; @@ -567,7 +565,7 @@ struct ieee80211_tx_info { /* NB: vif can be NULL for injected frames */ struct ieee80211_vif *vif; struct ieee80211_key_conf *hw_key; - struct ieee80211_sta *sta; + /* 8 bytes free */ } control; struct { struct ieee80211_tx_rate rates[IEEE80211_TX_MAX_RATES]; @@ -1078,6 +1076,16 @@ enum sta_notify_cmd { STA_NOTIFY_SLEEP, STA_NOTIFY_AWAKE, }; +/** + * struct ieee80211_tx_control - TX control data + * + * @sta: station table entry, this sta pointer may be NULL and + * it is not allowed to copy the pointer, due to RCU. + */ +struct ieee80211_tx_control { + struct ieee80211_sta *sta; +}; + /** * enum ieee80211_hw_flags - hardware flags * @@ -2269,7 +2277,9 @@ enum ieee80211_rate_control_changed { * The callback is optional and can (should!) sleep. */ struct ieee80211_ops { - void (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); + void (*tx)(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb); int (*start)(struct ieee80211_hw *hw); void (*stop)(struct ieee80211_hw *hw); #ifdef CONFIG_PM diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index df9203199102..a81117a83996 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -22,9 +22,11 @@ get_bss_sdata(struct ieee80211_sub_if_data *sdata) return sdata; } -static inline void drv_tx(struct ieee80211_local *local, struct sk_buff *skb) +static inline void drv_tx(struct ieee80211_local *local, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { - local->ops->tx(&local->hw, skb); + local->ops->tx(&local->hw, control, skb); } static inline void drv_get_et_strings(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index acf712ffb5e6..7558ba58ea23 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1204,6 +1204,7 @@ static bool ieee80211_tx_frags(struct ieee80211_local *local, struct sk_buff_head *skbs, bool txpending) { + struct ieee80211_tx_control control; struct sk_buff *skb, *tmp; unsigned long flags; @@ -1240,10 +1241,10 @@ static bool ieee80211_tx_frags(struct ieee80211_local *local, spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); info->control.vif = vif; - info->control.sta = sta; + control.sta = sta; __skb_unlink(skb, skbs); - drv_tx(local, skb); + drv_tx(local, &control, skb); } return true; -- cgit v1.2.3 From 4740974a6844156c14d741b0080b59d275679a23 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 20 Jul 2012 11:04:44 -0400 Subject: ftrace: Add default recursion protection for function tracing As more users of the function tracer utility are being added, they do not always add the necessary recursion protection. To protect from function recursion due to tracing, if the callback ftrace_ops does not specifically specify that it protects against recursion (by setting the FTRACE_OPS_FL_RECURSION_SAFE flag), the list operation will be called by the mcount trampoline which adds recursion protection. If the flag is set, then the function will be called directly with no extra protection. Note, the list operation is called if more than one function callback is registered, or if the arch does not support all of the function tracer features. Signed-off-by: Steven Rostedt --- include/linux/ftrace.h | 5 +++++ kernel/trace/ftrace.c | 10 ++++++++-- kernel/trace/trace_events.c | 1 + kernel/trace/trace_functions.c | 4 ++-- kernel/trace/trace_irqsoff.c | 2 +- kernel/trace/trace_sched_wakeup.c | 2 +- kernel/trace/trace_selftest.c | 7 +++++-- kernel/trace/trace_stack.c | 1 + 8 files changed, 24 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index ab39990cc43f..65a14e47a0db 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -85,6 +85,10 @@ typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip, * passing regs to the handler. * Note, if this flag is set, the SAVE_REGS flag will automatically * get set upon registering the ftrace_ops, if the arch supports it. + * RECURSION_SAFE - The ftrace_ops can set this to tell the ftrace infrastructure + * that the call back has its own recursion protection. If it does + * not set this, then the ftrace infrastructure will add recursion + * protection for the caller. */ enum { FTRACE_OPS_FL_ENABLED = 1 << 0, @@ -93,6 +97,7 @@ enum { FTRACE_OPS_FL_CONTROL = 1 << 3, FTRACE_OPS_FL_SAVE_REGS = 1 << 4, FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED = 1 << 5, + FTRACE_OPS_FL_RECURSION_SAFE = 1 << 6, }; struct ftrace_ops { diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index c55f7e274613..ad765b4ba426 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -66,6 +66,7 @@ static struct ftrace_ops ftrace_list_end __read_mostly = { .func = ftrace_stub, + .flags = FTRACE_OPS_FL_RECURSION_SAFE, }; /* ftrace_enabled is a method to turn ftrace on or off */ @@ -221,12 +222,13 @@ static void update_ftrace_function(void) /* * If we are at the end of the list and this ops is - * not dynamic and the arch supports passing ops, then have the - * mcount trampoline call the function directly. + * recursion safe and not dynamic and the arch supports passing ops, + * then have the mcount trampoline call the function directly. */ if (ftrace_ops_list == &ftrace_list_end || (ftrace_ops_list->next == &ftrace_list_end && !(ftrace_ops_list->flags & FTRACE_OPS_FL_DYNAMIC) && + (ftrace_ops_list->flags & FTRACE_OPS_FL_RECURSION_SAFE) && !FTRACE_FORCE_LIST_FUNC)) { /* Set the ftrace_ops that the arch callback uses */ if (ftrace_ops_list == &global_ops) @@ -867,6 +869,7 @@ static void unregister_ftrace_profiler(void) #else static struct ftrace_ops ftrace_profile_ops __read_mostly = { .func = function_profile_call, + .flags = FTRACE_OPS_FL_RECURSION_SAFE, }; static int register_ftrace_profiler(void) @@ -1049,6 +1052,7 @@ static struct ftrace_ops global_ops = { .func = ftrace_stub, .notrace_hash = EMPTY_HASH, .filter_hash = EMPTY_HASH, + .flags = FTRACE_OPS_FL_RECURSION_SAFE, }; static DEFINE_MUTEX(ftrace_regex_lock); @@ -3967,6 +3971,7 @@ void __init ftrace_init(void) static struct ftrace_ops global_ops = { .func = ftrace_stub, + .flags = FTRACE_OPS_FL_RECURSION_SAFE, }; static int __init ftrace_nodyn_init(void) @@ -4023,6 +4028,7 @@ ftrace_ops_control_func(unsigned long ip, unsigned long parent_ip, static struct ftrace_ops control_ops = { .func = ftrace_ops_control_func, + .flags = FTRACE_OPS_FL_RECURSION_SAFE, }; static inline void diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 8c6696833686..6825d833a257 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -1721,6 +1721,7 @@ function_test_events_call(unsigned long ip, unsigned long parent_ip, static struct ftrace_ops trace_ops __initdata = { .func = function_test_events_call, + .flags = FTRACE_OPS_FL_RECURSION_SAFE, }; static __init void event_trace_self_test_with_function(void) diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c index 5675ebd541f0..fdff65dff1bb 100644 --- a/kernel/trace/trace_functions.c +++ b/kernel/trace/trace_functions.c @@ -153,13 +153,13 @@ function_stack_trace_call(unsigned long ip, unsigned long parent_ip, static struct ftrace_ops trace_ops __read_mostly = { .func = function_trace_call, - .flags = FTRACE_OPS_FL_GLOBAL, + .flags = FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_RECURSION_SAFE, }; static struct ftrace_ops trace_stack_ops __read_mostly = { .func = function_stack_trace_call, - .flags = FTRACE_OPS_FL_GLOBAL, + .flags = FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_RECURSION_SAFE, }; /* Our two options */ diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c index c7a9ba936de6..d98ee8283b29 100644 --- a/kernel/trace/trace_irqsoff.c +++ b/kernel/trace/trace_irqsoff.c @@ -154,7 +154,7 @@ irqsoff_tracer_call(unsigned long ip, unsigned long parent_ip, static struct ftrace_ops trace_ops __read_mostly = { .func = irqsoff_tracer_call, - .flags = FTRACE_OPS_FL_GLOBAL, + .flags = FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_RECURSION_SAFE, }; #endif /* CONFIG_FUNCTION_TRACER */ diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c index 7547e36d483e..02170c00c413 100644 --- a/kernel/trace/trace_sched_wakeup.c +++ b/kernel/trace/trace_sched_wakeup.c @@ -130,7 +130,7 @@ wakeup_tracer_call(unsigned long ip, unsigned long parent_ip, static struct ftrace_ops trace_ops __read_mostly = { .func = wakeup_tracer_call, - .flags = FTRACE_OPS_FL_GLOBAL, + .flags = FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_RECURSION_SAFE, }; #endif /* CONFIG_FUNCTION_TRACER */ diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index add37e019fd0..1fb6da85ff8b 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c @@ -148,19 +148,22 @@ static void trace_selftest_test_dyn_func(unsigned long ip, static struct ftrace_ops test_probe1 = { .func = trace_selftest_test_probe1_func, + .flags = FTRACE_OPS_FL_RECURSION_SAFE, }; static struct ftrace_ops test_probe2 = { .func = trace_selftest_test_probe2_func, + .flags = FTRACE_OPS_FL_RECURSION_SAFE, }; static struct ftrace_ops test_probe3 = { .func = trace_selftest_test_probe3_func, + .flags = FTRACE_OPS_FL_RECURSION_SAFE, }; static struct ftrace_ops test_global = { - .func = trace_selftest_test_global_func, - .flags = FTRACE_OPS_FL_GLOBAL, + .func = trace_selftest_test_global_func, + .flags = FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_RECURSION_SAFE, }; static void print_counts(void) diff --git a/kernel/trace/trace_stack.c b/kernel/trace/trace_stack.c index 2fa5328e8893..0c1b165778e5 100644 --- a/kernel/trace/trace_stack.c +++ b/kernel/trace/trace_stack.c @@ -137,6 +137,7 @@ stack_trace_call(unsigned long ip, unsigned long parent_ip, static struct ftrace_ops trace_ops __read_mostly = { .func = stack_trace_call, + .flags = FTRACE_OPS_FL_RECURSION_SAFE, }; static ssize_t -- cgit v1.2.3 From ea701f11da44b44907af226fe5a5f57d2f26eeb2 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 20 Jul 2012 13:08:05 -0400 Subject: ftrace: Add selftest to test function trace recursion protection Add selftests to test the function tracing recursion protection actually does work. It also tests if a ftrace_ops states it will perform its own protection. Although, even if the ftrace_ops states it will protect itself, the ftrace infrastructure may still provide protection if the arch does not support all features or another ftrace_ops is registered. Signed-off-by: Steven Rostedt --- include/linux/ftrace.h | 6 ++ kernel/trace/ftrace.c | 21 +++++++ kernel/trace/trace_selftest.c | 136 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+) (limited to 'include') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 65a14e47a0db..9962e954a633 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -220,6 +220,10 @@ extern void ftrace_stub(unsigned long a0, unsigned long a1, */ #define register_ftrace_function(ops) ({ 0; }) #define unregister_ftrace_function(ops) ({ 0; }) +static inline int ftrace_nr_registered_ops(void) +{ + return 0; +} static inline void clear_ftrace_function(void) { } static inline void ftrace_kill(void) { } static inline void ftrace_stop(void) { } @@ -275,6 +279,8 @@ extern void unregister_ftrace_function_probe_all(char *glob); extern int ftrace_text_reserved(void *start, void *end); +extern int ftrace_nr_registered_ops(void); + /* * The dyn_ftrace record's flags field is split into two parts. * the first part which is '0-FTRACE_REF_MAX' is a counter of diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index ad765b4ba426..528d997c7f99 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -111,6 +111,27 @@ static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip); #define ftrace_ops_list_func ((ftrace_func_t)ftrace_ops_no_ops) #endif +/** + * ftrace_nr_registered_ops - return number of ops registered + * + * Returns the number of ftrace_ops registered and tracing functions + */ +int ftrace_nr_registered_ops(void) +{ + struct ftrace_ops *ops; + int cnt = 0; + + mutex_lock(&ftrace_lock); + + for (ops = ftrace_ops_list; + ops != &ftrace_list_end; ops = ops->next) + cnt++; + + mutex_unlock(&ftrace_lock); + + return cnt; +} + /* * Traverse the ftrace_global_list, invoking all entries. The reason that we * can use rcu_dereference_raw() is that elements removed from this list diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index 1fb6da85ff8b..86422f91dbe1 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c @@ -406,8 +406,141 @@ int trace_selftest_startup_dynamic_tracing(struct tracer *trace, return ret; } + +static int trace_selftest_recursion_cnt; +static void trace_selftest_test_recursion_func(unsigned long ip, + unsigned long pip, + struct ftrace_ops *op, + struct pt_regs *pt_regs) +{ + /* + * This function is registered without the recursion safe flag. + * The ftrace infrastructure should provide the recursion + * protection. If not, this will crash the kernel! + */ + trace_selftest_recursion_cnt++; + DYN_FTRACE_TEST_NAME(); +} + +static void trace_selftest_test_recursion_safe_func(unsigned long ip, + unsigned long pip, + struct ftrace_ops *op, + struct pt_regs *pt_regs) +{ + /* + * We said we would provide our own recursion. By calling + * this function again, we should recurse back into this function + * and count again. But this only happens if the arch supports + * all of ftrace features and nothing else is using the function + * tracing utility. + */ + if (trace_selftest_recursion_cnt++) + return; + DYN_FTRACE_TEST_NAME(); +} + +static struct ftrace_ops test_rec_probe = { + .func = trace_selftest_test_recursion_func, +}; + +static struct ftrace_ops test_recsafe_probe = { + .func = trace_selftest_test_recursion_safe_func, + .flags = FTRACE_OPS_FL_RECURSION_SAFE, +}; + +static int +trace_selftest_function_recursion(void) +{ + int save_ftrace_enabled = ftrace_enabled; + int save_tracer_enabled = tracer_enabled; + char *func_name; + int len; + int ret; + int cnt; + + /* The previous test PASSED */ + pr_cont("PASSED\n"); + pr_info("Testing ftrace recursion: "); + + + /* enable tracing, and record the filter function */ + ftrace_enabled = 1; + tracer_enabled = 1; + + /* Handle PPC64 '.' name */ + func_name = "*" __stringify(DYN_FTRACE_TEST_NAME); + len = strlen(func_name); + + ret = ftrace_set_filter(&test_rec_probe, func_name, len, 1); + if (ret) { + pr_cont("*Could not set filter* "); + goto out; + } + + ret = register_ftrace_function(&test_rec_probe); + if (ret) { + pr_cont("*could not register callback* "); + goto out; + } + + DYN_FTRACE_TEST_NAME(); + + unregister_ftrace_function(&test_rec_probe); + + ret = -1; + if (trace_selftest_recursion_cnt != 1) { + pr_cont("*callback not called once (%d)* ", + trace_selftest_recursion_cnt); + goto out; + } + + trace_selftest_recursion_cnt = 1; + + pr_cont("PASSED\n"); + pr_info("Testing ftrace recursion safe: "); + + ret = ftrace_set_filter(&test_recsafe_probe, func_name, len, 1); + if (ret) { + pr_cont("*Could not set filter* "); + goto out; + } + + ret = register_ftrace_function(&test_recsafe_probe); + if (ret) { + pr_cont("*could not register callback* "); + goto out; + } + + DYN_FTRACE_TEST_NAME(); + + unregister_ftrace_function(&test_recsafe_probe); + + /* + * If arch supports all ftrace features, and no other task + * was on the list, we should be fine. + */ + if (!ftrace_nr_registered_ops() && !FTRACE_FORCE_LIST_FUNC) + cnt = 2; /* Should have recursed */ + else + cnt = 1; + + ret = -1; + if (trace_selftest_recursion_cnt != cnt) { + pr_cont("*callback not called expected %d times (%d)* ", + cnt, trace_selftest_recursion_cnt); + goto out; + } + + ret = 0; +out: + ftrace_enabled = save_ftrace_enabled; + tracer_enabled = save_tracer_enabled; + + return ret; +} #else # define trace_selftest_startup_dynamic_tracing(trace, tr, func) ({ 0; }) +# define trace_selftest_function_recursion() ({ 0; }) #endif /* CONFIG_DYNAMIC_FTRACE */ /* @@ -455,7 +588,10 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) ret = trace_selftest_startup_dynamic_tracing(trace, tr, DYN_FTRACE_TEST_NAME); + if (ret) + goto out; + ret = trace_selftest_function_recursion(); out: ftrace_enabled = save_ftrace_enabled; tracer_enabled = save_tracer_enabled; -- cgit v1.2.3 From 647664eaf4033501739ac1f42dd52ce8c9266ccc Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 5 Jun 2012 19:28:08 +0900 Subject: ftrace: add ftrace_set_filter_ip() for address based filter Add a new filter update interface ftrace_set_filter_ip() to set ftrace filter by ip address, not only glob pattern. Link: http://lkml.kernel.org/r/20120605102808.27845.67952.stgit@localhost.localdomain Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" Cc: Ananth N Mavinakayanahalli Cc: "Frank Ch. Eigler" Cc: Andrew Morton Cc: Frederic Weisbecker Signed-off-by: Masami Hiramatsu Signed-off-by: Steven Rostedt --- include/linux/ftrace.h | 3 +++ kernel/trace/ftrace.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 9962e954a633..3e711122f66c 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -317,6 +317,8 @@ struct dyn_ftrace { }; int ftrace_force_update(void); +int ftrace_set_filter_ip(struct ftrace_ops *ops, unsigned long ip, + int remove, int reset); int ftrace_set_filter(struct ftrace_ops *ops, unsigned char *buf, int len, int reset); int ftrace_set_notrace(struct ftrace_ops *ops, unsigned char *buf, @@ -544,6 +546,7 @@ static inline int ftrace_text_reserved(void *start, void *end) */ #define ftrace_regex_open(ops, flag, inod, file) ({ -ENODEV; }) #define ftrace_set_early_filter(ops, buf, enable) do { } while (0) +#define ftrace_set_filter_ip(ops, ip, remove, reset) ({ -ENODEV; }) #define ftrace_set_filter(ops, buf, len, reset) ({ -ENODEV; }) #define ftrace_set_notrace(ops, buf, len, reset) ({ -ENODEV; }) #define ftrace_free_filter(ops) do { } while (0) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 528d997c7f99..9dcf15d38380 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -3242,8 +3242,27 @@ ftrace_notrace_write(struct file *file, const char __user *ubuf, } static int -ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len, - int reset, int enable) +ftrace_match_addr(struct ftrace_hash *hash, unsigned long ip, int remove) +{ + struct ftrace_func_entry *entry; + + if (!ftrace_location(ip)) + return -EINVAL; + + if (remove) { + entry = ftrace_lookup_ip(hash, ip); + if (!entry) + return -ENOENT; + free_hash_entry(hash, entry); + return 0; + } + + return add_hash_entry(hash, ip); +} + +static int +ftrace_set_hash(struct ftrace_ops *ops, unsigned char *buf, int len, + unsigned long ip, int remove, int reset, int enable) { struct ftrace_hash **orig_hash; struct ftrace_hash *hash; @@ -3272,6 +3291,11 @@ ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len, ret = -EINVAL; goto out_regex_unlock; } + if (ip) { + ret = ftrace_match_addr(hash, ip, remove); + if (ret < 0) + goto out_regex_unlock; + } mutex_lock(&ftrace_lock); ret = ftrace_hash_move(ops, enable, orig_hash, hash); @@ -3288,6 +3312,37 @@ ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len, return ret; } +static int +ftrace_set_addr(struct ftrace_ops *ops, unsigned long ip, int remove, + int reset, int enable) +{ + return ftrace_set_hash(ops, 0, 0, ip, remove, reset, enable); +} + +/** + * ftrace_set_filter_ip - set a function to filter on in ftrace by address + * @ops - the ops to set the filter with + * @ip - the address to add to or remove from the filter. + * @remove - non zero to remove the ip from the filter + * @reset - non zero to reset all filters before applying this filter. + * + * Filters denote which functions should be enabled when tracing is enabled + * If @ip is NULL, it failes to update filter. + */ +int ftrace_set_filter_ip(struct ftrace_ops *ops, unsigned long ip, + int remove, int reset) +{ + return ftrace_set_addr(ops, ip, remove, reset, 1); +} +EXPORT_SYMBOL_GPL(ftrace_set_filter_ip); + +static int +ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len, + int reset, int enable) +{ + return ftrace_set_hash(ops, buf, len, 0, 0, reset, enable); +} + /** * ftrace_set_filter - set a function to filter on in ftrace * @ops - the ops to set the filter with -- cgit v1.2.3 From 4dc936769e8a6382a4cc12375e8a4daa2b829fda Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Wed, 6 Jun 2012 13:45:31 -0400 Subject: ftrace: Make ftrace_location() a nop on !DYNAMIC_FTRACE When CONFIG_DYNAMIC_FTRACE is not set, ftrace_location() is not defined. If a user (like kprobes) references this function, it will break the compile when CONFIG_DYNAMIC_FTRACE is not set. Add ftrace_location() as a nop (return 0) when DYNAMIC_FTRACE is not defined. Link: http://lkml.kernel.org/r/20120612225426.961092717@goodmis.org Signed-off-by: Steven Rostedt --- include/linux/ftrace.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 3e711122f66c..a52f2f4fe030 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -520,7 +520,7 @@ extern int skip_trace(unsigned long ip); extern void ftrace_disable_daemon(void); extern void ftrace_enable_daemon(void); -#else +#else /* CONFIG_DYNAMIC_FTRACE */ static inline int skip_trace(unsigned long ip) { return 0; } static inline int ftrace_force_update(void) { return 0; } static inline void ftrace_disable_daemon(void) { } @@ -538,6 +538,10 @@ static inline int ftrace_text_reserved(void *start, void *end) { return 0; } +static inline unsigned long ftrace_location(unsigned long ip) +{ + return 0; +} /* * Again users of functions that have ftrace_ops may not -- cgit v1.2.3 From ae6aa16fdc163afe6b04b6c073ad4ddd4663c03b Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 5 Jun 2012 19:28:32 +0900 Subject: kprobes: introduce ftrace based optimization Introduce function trace based kprobes optimization. With using ftrace optimization, kprobes on the mcount calling address, use ftrace's mcount call instead of breakpoint. Furthermore, this optimization works with preemptive kernel not like as current jump-based optimization. Of cource, this feature works only if the probe is on mcount call. Only if kprobe.break_handler is set, that probe is not optimized with ftrace (nor put on ftrace). The reason why this limitation comes is that this break_handler may be used only from jprobes which changes ip address (for fetching the function arguments), but function tracer ignores modified ip address. Changes in v2: - Fix ftrace_ops registering right after setting its filter. - Unregister ftrace_ops if there is no kprobe using. - Remove notrace dependency from __kprobes macro. Link: http://lkml.kernel.org/r/20120605102832.27845.63461.stgit@localhost.localdomain Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" Cc: Ananth N Mavinakayanahalli Cc: "Frank Ch. Eigler" Cc: Andrew Morton Cc: Frederic Weisbecker Signed-off-by: Masami Hiramatsu Signed-off-by: Steven Rostedt --- include/linux/kprobes.h | 27 +++++++++++++ kernel/kprobes.c | 105 ++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 119 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index b6e1f8c00577..aa0d05e852e3 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h @@ -38,6 +38,7 @@ #include #include #include +#include #ifdef CONFIG_KPROBES #include @@ -48,14 +49,26 @@ #define KPROBE_REENTER 0x00000004 #define KPROBE_HIT_SSDONE 0x00000008 +/* + * If function tracer is enabled and the arch supports full + * passing of pt_regs to function tracing, then kprobes can + * optimize on top of function tracing. + */ +#if defined(CONFIG_FUNCTION_TRACER) && defined(ARCH_SUPPORTS_FTRACE_SAVE_REGS) \ + && defined(ARCH_SUPPORTS_KPROBES_ON_FTRACE) +# define KPROBES_CAN_USE_FTRACE +#endif + /* Attach to insert probes on any functions which should be ignored*/ #define __kprobes __attribute__((__section__(".kprobes.text"))) + #else /* CONFIG_KPROBES */ typedef int kprobe_opcode_t; struct arch_specific_insn { int dummy; }; #define __kprobes + #endif /* CONFIG_KPROBES */ struct kprobe; @@ -128,6 +141,7 @@ struct kprobe { * NOTE: * this flag is only for optimized_kprobe. */ +#define KPROBE_FLAG_FTRACE 8 /* probe is using ftrace */ /* Has this kprobe gone ? */ static inline int kprobe_gone(struct kprobe *p) @@ -146,6 +160,13 @@ static inline int kprobe_optimized(struct kprobe *p) { return p->flags & KPROBE_FLAG_OPTIMIZED; } + +/* Is this kprobe uses ftrace ? */ +static inline int kprobe_ftrace(struct kprobe *p) +{ + return p->flags & KPROBE_FLAG_FTRACE; +} + /* * Special probe type that uses setjmp-longjmp type tricks to resume * execution at a specified entry with a matching prototype corresponding @@ -295,6 +316,12 @@ extern int proc_kprobes_optimization_handler(struct ctl_table *table, #endif #endif /* CONFIG_OPTPROBES */ +#ifdef KPROBES_CAN_USE_FTRACE +extern void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip, + struct pt_regs *regs); +extern int arch_prepare_kprobe_ftrace(struct kprobe *p); +#endif + /* Get the kprobe at this addr (if any) - called with preemption disabled */ struct kprobe *get_kprobe(void *addr); diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 9e47f44f3531..69c16efc315b 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -759,6 +759,10 @@ static __kprobes void try_to_optimize_kprobe(struct kprobe *p) struct kprobe *ap; struct optimized_kprobe *op; + /* Impossible to optimize ftrace-based kprobe */ + if (kprobe_ftrace(p)) + return; + /* For preparing optimization, jump_label_text_reserved() is called */ jump_label_lock(); mutex_lock(&text_mutex); @@ -915,9 +919,64 @@ static __kprobes struct kprobe *alloc_aggr_kprobe(struct kprobe *p) } #endif /* CONFIG_OPTPROBES */ +#ifdef KPROBES_CAN_USE_FTRACE +static struct ftrace_ops kprobe_ftrace_ops __read_mostly = { + .regs_func = kprobe_ftrace_handler, + .flags = FTRACE_OPS_FL_SAVE_REGS, +}; +static int kprobe_ftrace_enabled; + +/* Must ensure p->addr is really on ftrace */ +static int __kprobes prepare_kprobe(struct kprobe *p) +{ + if (!kprobe_ftrace(p)) + return arch_prepare_kprobe(p); + + return arch_prepare_kprobe_ftrace(p); +} + +/* Caller must lock kprobe_mutex */ +static void __kprobes arm_kprobe_ftrace(struct kprobe *p) +{ + int ret; + + ret = ftrace_set_filter_ip(&kprobe_ftrace_ops, + (unsigned long)p->addr, 0, 0); + WARN(ret < 0, "Failed to arm kprobe-ftrace at %p (%d)\n", p->addr, ret); + kprobe_ftrace_enabled++; + if (kprobe_ftrace_enabled == 1) { + ret = register_ftrace_function(&kprobe_ftrace_ops); + WARN(ret < 0, "Failed to init kprobe-ftrace (%d)\n", ret); + } +} + +/* Caller must lock kprobe_mutex */ +static void __kprobes disarm_kprobe_ftrace(struct kprobe *p) +{ + int ret; + + kprobe_ftrace_enabled--; + if (kprobe_ftrace_enabled == 0) { + ret = unregister_ftrace_function(&kprobe_ftrace_ops); + WARN(ret < 0, "Failed to init kprobe-ftrace (%d)\n", ret); + } + ret = ftrace_set_filter_ip(&kprobe_ftrace_ops, + (unsigned long)p->addr, 1, 0); + WARN(ret < 0, "Failed to disarm kprobe-ftrace at %p (%d)\n", p->addr, ret); +} +#else /* !KPROBES_CAN_USE_FTRACE */ +#define prepare_kprobe(p) arch_prepare_kprobe(p) +#define arm_kprobe_ftrace(p) do {} while (0) +#define disarm_kprobe_ftrace(p) do {} while (0) +#endif + /* Arm a kprobe with text_mutex */ static void __kprobes arm_kprobe(struct kprobe *kp) { + if (unlikely(kprobe_ftrace(kp))) { + arm_kprobe_ftrace(kp); + return; + } /* * Here, since __arm_kprobe() doesn't use stop_machine(), * this doesn't cause deadlock on text_mutex. So, we don't @@ -929,11 +988,15 @@ static void __kprobes arm_kprobe(struct kprobe *kp) } /* Disarm a kprobe with text_mutex */ -static void __kprobes disarm_kprobe(struct kprobe *kp) +static void __kprobes disarm_kprobe(struct kprobe *kp, bool reopt) { + if (unlikely(kprobe_ftrace(kp))) { + disarm_kprobe_ftrace(kp); + return; + } /* Ditto */ mutex_lock(&text_mutex); - __disarm_kprobe(kp, true); + __disarm_kprobe(kp, reopt); mutex_unlock(&text_mutex); } @@ -1343,6 +1406,26 @@ static __kprobes int check_kprobe_address_safe(struct kprobe *p, struct module **probed_mod) { int ret = 0; + unsigned long ftrace_addr; + + /* + * If the address is located on a ftrace nop, set the + * breakpoint to the following instruction. + */ + ftrace_addr = ftrace_location((unsigned long)p->addr); + if (ftrace_addr) { +#ifdef KPROBES_CAN_USE_FTRACE + /* Given address is not on the instruction boundary */ + if ((unsigned long)p->addr != ftrace_addr) + return -EILSEQ; + /* break_handler (jprobe) can not work with ftrace */ + if (p->break_handler) + return -EINVAL; + p->flags |= KPROBE_FLAG_FTRACE; +#else /* !KPROBES_CAN_USE_FTRACE */ + return -EINVAL; +#endif + } jump_label_lock(); preempt_disable(); @@ -1350,7 +1433,6 @@ static __kprobes int check_kprobe_address_safe(struct kprobe *p, /* Ensure it is not in reserved area nor out of text */ if (!kernel_text_address((unsigned long) p->addr) || in_kprobes_functions((unsigned long) p->addr) || - ftrace_text_reserved(p->addr, p->addr) || jump_label_text_reserved(p->addr, p->addr)) { ret = -EINVAL; goto out; @@ -1422,7 +1504,7 @@ int __kprobes register_kprobe(struct kprobe *p) } mutex_lock(&text_mutex); /* Avoiding text modification */ - ret = arch_prepare_kprobe(p); + ret = prepare_kprobe(p); mutex_unlock(&text_mutex); if (ret) goto out; @@ -1480,7 +1562,7 @@ static struct kprobe *__kprobes __disable_kprobe(struct kprobe *p) /* Try to disarm and disable this/parent probe */ if (p == orig_p || aggr_kprobe_disabled(orig_p)) { - disarm_kprobe(orig_p); + disarm_kprobe(orig_p, true); orig_p->flags |= KPROBE_FLAG_DISABLED; } } @@ -2078,10 +2160,11 @@ static void __kprobes report_probe(struct seq_file *pi, struct kprobe *p, if (!pp) pp = p; - seq_printf(pi, "%s%s%s\n", + seq_printf(pi, "%s%s%s%s\n", (kprobe_gone(p) ? "[GONE]" : ""), ((kprobe_disabled(p) && !kprobe_gone(p)) ? "[DISABLED]" : ""), - (kprobe_optimized(pp) ? "[OPTIMIZED]" : "")); + (kprobe_optimized(pp) ? "[OPTIMIZED]" : ""), + (kprobe_ftrace(pp) ? "[FTRACE]" : "")); } static void __kprobes *kprobe_seq_start(struct seq_file *f, loff_t *pos) @@ -2160,14 +2243,12 @@ static void __kprobes arm_all_kprobes(void) goto already_enabled; /* Arming kprobes doesn't optimize kprobe itself */ - mutex_lock(&text_mutex); for (i = 0; i < KPROBE_TABLE_SIZE; i++) { head = &kprobe_table[i]; hlist_for_each_entry_rcu(p, node, head, hlist) if (!kprobe_disabled(p)) - __arm_kprobe(p); + arm_kprobe(p); } - mutex_unlock(&text_mutex); kprobes_all_disarmed = false; printk(KERN_INFO "Kprobes globally enabled\n"); @@ -2195,15 +2276,13 @@ static void __kprobes disarm_all_kprobes(void) kprobes_all_disarmed = true; printk(KERN_INFO "Kprobes globally disabled\n"); - mutex_lock(&text_mutex); for (i = 0; i < KPROBE_TABLE_SIZE; i++) { head = &kprobe_table[i]; hlist_for_each_entry_rcu(p, node, head, hlist) { if (!arch_trampoline_kprobe(p) && !kprobe_disabled(p)) - __disarm_kprobe(p, false); + disarm_kprobe(p, false); } } - mutex_unlock(&text_mutex); mutex_unlock(&kprobe_mutex); /* Wait for disarming all kprobes by optimizer */ -- cgit v1.2.3 From e52538965119319447c0800c534da73142c27be2 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 5 Jun 2012 19:28:38 +0900 Subject: kprobes/x86: ftrace based optimization for x86 Add function tracer based kprobe optimization support handlers on x86. This allows kprobes to use function tracer for probing on mcount call. Link: http://lkml.kernel.org/r/20120605102838.27845.26317.stgit@localhost.localdomain Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" Cc: Ananth N Mavinakayanahalli Cc: "Frank Ch. Eigler" Cc: Andrew Morton Cc: Frederic Weisbecker Signed-off-by: Masami Hiramatsu [ Updated to new port of ftrace save regs functions ] Signed-off-by: Steven Rostedt --- arch/x86/include/asm/kprobes.h | 1 + arch/x86/kernel/kprobes.c | 48 ++++++++++++++++++++++++++++++++++++++++++ include/linux/kprobes.h | 2 +- kernel/kprobes.c | 2 +- 4 files changed, 51 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/kprobes.h b/arch/x86/include/asm/kprobes.h index 547882539157..d3ddd17405d0 100644 --- a/arch/x86/include/asm/kprobes.h +++ b/arch/x86/include/asm/kprobes.h @@ -27,6 +27,7 @@ #include #define __ARCH_WANT_KPROBES_INSN_SLOT +#define ARCH_SUPPORTS_KPROBES_ON_FTRACE struct pt_regs; struct kprobe; diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c index e2f751efb7b1..47ae1023a93c 100644 --- a/arch/x86/kernel/kprobes.c +++ b/arch/x86/kernel/kprobes.c @@ -1052,6 +1052,54 @@ int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) return 0; } +#ifdef KPROBES_CAN_USE_FTRACE +/* Ftrace callback handler for kprobes */ +void __kprobes kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip, + struct ftrace_ops *ops, struct pt_regs *regs) +{ + struct kprobe *p; + struct kprobe_ctlblk *kcb; + unsigned long flags; + + /* Disable irq for emulating a breakpoint and avoiding preempt */ + local_irq_save(flags); + + p = get_kprobe((kprobe_opcode_t *)ip); + if (unlikely(!p) || kprobe_disabled(p)) + goto end; + + kcb = get_kprobe_ctlblk(); + if (kprobe_running()) { + kprobes_inc_nmissed_count(p); + } else { + regs->ip += sizeof(kprobe_opcode_t); + + __this_cpu_write(current_kprobe, p); + kcb->kprobe_status = KPROBE_HIT_ACTIVE; + if (p->pre_handler) + p->pre_handler(p, regs); + + if (unlikely(p->post_handler)) { + /* Emulate singlestep as if there is a 5byte nop */ + regs->ip = ip + MCOUNT_INSN_SIZE; + kcb->kprobe_status = KPROBE_HIT_SSDONE; + p->post_handler(p, regs, 0); + } + __this_cpu_write(current_kprobe, NULL); + regs->ip = ip; /* Recover for next callback */ + } +end: + local_irq_restore(flags); +} + +int __kprobes arch_prepare_kprobe_ftrace(struct kprobe *p) +{ + p->ainsn.insn = NULL; + p->ainsn.boostable = -1; + return 0; +} +#endif + int __init arch_init_kprobes(void) { return arch_init_optprobes(); diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index aa0d05e852e3..23755ba42abc 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h @@ -318,7 +318,7 @@ extern int proc_kprobes_optimization_handler(struct ctl_table *table, #endif /* CONFIG_OPTPROBES */ #ifdef KPROBES_CAN_USE_FTRACE extern void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip, - struct pt_regs *regs); + struct ftrace_ops *ops, struct pt_regs *regs); extern int arch_prepare_kprobe_ftrace(struct kprobe *p); #endif diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 69c16efc315b..35b4315d84f5 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -921,7 +921,7 @@ static __kprobes struct kprobe *alloc_aggr_kprobe(struct kprobe *p) #ifdef KPROBES_CAN_USE_FTRACE static struct ftrace_ops kprobe_ftrace_ops __read_mostly = { - .regs_func = kprobe_ftrace_handler, + .func = kprobe_ftrace_handler, .flags = FTRACE_OPS_FL_SAVE_REGS, }; static int kprobe_ftrace_enabled; -- cgit v1.2.3 From e6dab5ffab59e910ec0e3355f4a6f29f7a7be474 Mon Sep 17 00:00:00 2001 From: Andrew Vagin Date: Wed, 11 Jul 2012 18:14:58 +0400 Subject: perf/trace: Add ability to set a target task for events A few events are interesting not only for a current task. For example, sched_stat_* events are interesting for a task which wakes up. For this reason, it will be good if such events will be delivered to a target task too. Now a target task can be set by using __perf_task(). The original idea and a draft patch belongs to Peter Zijlstra. I need these events for profiling sleep times. sched_switch is used for getting callchains and sched_stat_* is used for getting time periods. These events are combined in user space, then it can be analyzed by perf tools. Inspired-by: Peter Zijlstra Cc: Steven Rostedt Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo Cc: Steven Rostedt Cc: Arun Sharma Signed-off-by: Andrew Vagin Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1342016098-213063-1-git-send-email-avagin@openvz.org Signed-off-by: Ingo Molnar --- include/linux/ftrace_event.h | 5 +++-- include/linux/perf_event.h | 3 ++- include/trace/events/sched.h | 4 ++++ include/trace/ftrace.h | 6 +++++- kernel/events/callchain.c | 9 ++++++++- kernel/events/core.c | 30 ++++++++++++++++++++++++++++-- kernel/events/internal.h | 3 ++- kernel/trace/trace_event_perf.c | 2 +- kernel/trace/trace_kprobe.c | 6 ++++-- kernel/trace/trace_syscalls.c | 4 ++-- kernel/trace/trace_uprobe.c | 2 +- 11 files changed, 60 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index af961d6f7ab1..642928cf57b4 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -306,9 +306,10 @@ extern void *perf_trace_buf_prepare(int size, unsigned short type, static inline void perf_trace_buf_submit(void *raw_data, int size, int rctx, u64 addr, - u64 count, struct pt_regs *regs, void *head) + u64 count, struct pt_regs *regs, void *head, + struct task_struct *task) { - perf_tp_event(addr, count, raw_data, size, regs, head, rctx); + perf_tp_event(addr, count, raw_data, size, regs, head, rctx, task); } #endif diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 76c5c8b724a7..7602ccb3f40e 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -1272,7 +1272,8 @@ static inline bool perf_paranoid_kernel(void) extern void perf_event_init(void); extern void perf_tp_event(u64 addr, u64 count, void *record, int entry_size, struct pt_regs *regs, - struct hlist_head *head, int rctx); + struct hlist_head *head, int rctx, + struct task_struct *task); extern void perf_bp_event(struct perf_event *event, void *data); #ifndef perf_misc_flags diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index ea7a2035456d..5a8671e8a67f 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -73,6 +73,9 @@ 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", @@ -325,6 +328,7 @@ DECLARE_EVENT_CLASS(sched_stat_template, ) TP_perf_assign( __perf_count(delay); + __perf_task(tsk); ), TP_printk("comm=%s pid=%d delay=%Lu [ns]", diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index c6bc2faaf261..a763888a36f9 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h @@ -712,6 +712,9 @@ __attribute__((section("_ftrace_events"))) *__event_##call = &event_##call #undef __perf_count #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 @@ -725,6 +728,7 @@ perf_trace_##call(void *__data, proto) \ struct ftrace_raw_##call *entry; \ struct pt_regs __regs; \ u64 __addr = 0, __count = 1; \ + struct task_struct *__task = NULL; \ struct hlist_head *head; \ int __entry_size; \ int __data_size; \ @@ -752,7 +756,7 @@ perf_trace_##call(void *__data, proto) \ \ head = this_cpu_ptr(event_call->perf_events); \ perf_trace_buf_submit(entry, __entry_size, rctx, __addr, \ - __count, &__regs, head); \ + __count, &__regs, head, __task); \ } /* diff --git a/kernel/events/callchain.c b/kernel/events/callchain.c index 6581a040f399..98d4597f43d6 100644 --- a/kernel/events/callchain.c +++ b/kernel/events/callchain.c @@ -153,7 +153,8 @@ put_callchain_entry(int rctx) put_recursion_context(__get_cpu_var(callchain_recursion), rctx); } -struct perf_callchain_entry *perf_callchain(struct pt_regs *regs) +struct perf_callchain_entry * +perf_callchain(struct perf_event *event, struct pt_regs *regs) { int rctx; struct perf_callchain_entry *entry; @@ -178,6 +179,12 @@ struct perf_callchain_entry *perf_callchain(struct pt_regs *regs) } if (regs) { + /* + * Disallow cross-task user callchains. + */ + if (event->ctx->task && event->ctx->task != current) + goto exit_put; + perf_callchain_store(entry, PERF_CONTEXT_USER); perf_callchain_user(entry, regs); } diff --git a/kernel/events/core.c b/kernel/events/core.c index f1cf0edeb39a..b7935fcec7d9 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -4039,7 +4039,7 @@ void perf_prepare_sample(struct perf_event_header *header, if (sample_type & PERF_SAMPLE_CALLCHAIN) { int size = 1; - data->callchain = perf_callchain(regs); + data->callchain = perf_callchain(event, regs); if (data->callchain) size += data->callchain->nr; @@ -5209,7 +5209,8 @@ static int perf_tp_event_match(struct perf_event *event, } void perf_tp_event(u64 addr, u64 count, void *record, int entry_size, - struct pt_regs *regs, struct hlist_head *head, int rctx) + struct pt_regs *regs, struct hlist_head *head, int rctx, + struct task_struct *task) { struct perf_sample_data data; struct perf_event *event; @@ -5228,6 +5229,31 @@ void perf_tp_event(u64 addr, u64 count, void *record, int entry_size, perf_swevent_event(event, count, &data, regs); } + /* + * If we got specified a target task, also iterate its context and + * deliver this event there too. + */ + if (task && task != current) { + struct perf_event_context *ctx; + struct trace_entry *entry = record; + + rcu_read_lock(); + ctx = rcu_dereference(task->perf_event_ctxp[perf_sw_context]); + if (!ctx) + goto unlock; + + list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { + if (event->attr.type != PERF_TYPE_TRACEPOINT) + continue; + if (event->attr.config != entry->type) + continue; + if (perf_tp_event_match(event, &data, regs)) + perf_swevent_event(event, count, &data, regs); + } +unlock: + rcu_read_unlock(); + } + perf_swevent_put_recursion_context(rctx); } EXPORT_SYMBOL_GPL(perf_tp_event); diff --git a/kernel/events/internal.h b/kernel/events/internal.h index b0b107f90afc..a096c19f2c2a 100644 --- a/kernel/events/internal.h +++ b/kernel/events/internal.h @@ -101,7 +101,8 @@ __output_copy(struct perf_output_handle *handle, } /* Callchain handling */ -extern struct perf_callchain_entry *perf_callchain(struct pt_regs *regs); +extern struct perf_callchain_entry * +perf_callchain(struct perf_event *event, struct pt_regs *regs); extern int get_callchain_buffers(void); extern void put_callchain_buffers(void); diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c index fee3752ae8f6..8a6d2ee2086c 100644 --- a/kernel/trace/trace_event_perf.c +++ b/kernel/trace/trace_event_perf.c @@ -281,7 +281,7 @@ perf_ftrace_function_call(unsigned long ip, unsigned long parent_ip) head = this_cpu_ptr(event_function.perf_events); perf_trace_buf_submit(entry, ENTRY_SIZE, rctx, 0, - 1, ®s, head); + 1, ®s, head, NULL); #undef ENTRY_SIZE } diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index b31d3d5699fe..1a2117043bb1 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -1002,7 +1002,8 @@ static __kprobes void kprobe_perf_func(struct kprobe *kp, store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize); head = this_cpu_ptr(call->perf_events); - perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, regs, head); + perf_trace_buf_submit(entry, size, rctx, + entry->ip, 1, regs, head, NULL); } /* Kretprobe profile handler */ @@ -1033,7 +1034,8 @@ static __kprobes void kretprobe_perf_func(struct kretprobe_instance *ri, store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize); head = this_cpu_ptr(call->perf_events); - perf_trace_buf_submit(entry, size, rctx, entry->ret_ip, 1, regs, head); + perf_trace_buf_submit(entry, size, rctx, + entry->ret_ip, 1, regs, head, NULL); } #endif /* CONFIG_PERF_EVENTS */ diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index 96fc73369099..60e4d7875672 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c @@ -532,7 +532,7 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id) (unsigned long *)&rec->args); head = this_cpu_ptr(sys_data->enter_event->perf_events); - perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head); + perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head, NULL); } int perf_sysenter_enable(struct ftrace_event_call *call) @@ -608,7 +608,7 @@ static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret) rec->ret = syscall_get_return_value(current, regs); head = this_cpu_ptr(sys_data->exit_event->perf_events); - perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head); + perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head, NULL); } int perf_sysexit_enable(struct ftrace_event_call *call) diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c index 2b36ac68549e..03003cd7dd96 100644 --- a/kernel/trace/trace_uprobe.c +++ b/kernel/trace/trace_uprobe.c @@ -670,7 +670,7 @@ static void uprobe_perf_func(struct trace_uprobe *tu, struct pt_regs *regs) call_fetch(&tu->args[i].fetch, regs, data + tu->args[i].offset); head = this_cpu_ptr(call->perf_events); - perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, regs, head); + perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, regs, head, NULL); out: preempt_enable(); -- cgit v1.2.3 From a7ea3bbf5d58f4df2265d312f91d5769eabc8144 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Fri, 27 Jul 2012 14:48:09 -0400 Subject: time/jiffies: Allow CLOCK_TICK_RATE to be undefined CLOCK_TICK_RATE is a legacy constant that defines the timer device's granularity. On hardware with particularly coarse granularity, this constant is used to reduce accumulated time error when using jiffies as a clocksource, by calculating the hardware's actual tick length rather then just assuming it is 1sec/HZ. However, for the most part this is unnecessary, as most modern systems don't use jiffies for their clocksource, and their tick device is sufficiently fine grained to avoid major error. Thus, this patch allows an architecture to not define CLOCK_TICK_RATE, in which case ACTHZ defaults to (HZ << 8). Signed-off-by: Catalin Marinas Acked-by: Arnd Bergmann Cc: Richard Cochran Cc: Prarit Bhargava Cc: Andrew Morton [ Commit log & intention tweaks ] Signed-off-by: John Stultz Link: http://lkml.kernel.org/r/1343414893-45779-2-git-send-email-john.stultz@linaro.org Signed-off-by: Ingo Molnar --- include/linux/jiffies.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/jiffies.h b/include/linux/jiffies.h index 265e2c3cbd1c..7d24466fb80b 100644 --- a/include/linux/jiffies.h +++ b/include/linux/jiffies.h @@ -39,9 +39,6 @@ # error Invalid value of HZ. #endif -/* LATCH is used in the interval timer and ftape setup. */ -#define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */ - /* Suppose we want to divide two numbers NOM and DEN: NOM/DEN, then we can * improve accuracy by shifting LSH bits, hence calculating: * (NOM << LSH) / DEN @@ -54,8 +51,15 @@ #define SH_DIV(NOM,DEN,LSH) ( (((NOM) / (DEN)) << (LSH)) \ + ((((NOM) % (DEN)) << (LSH)) + (DEN) / 2) / (DEN)) +#ifdef CLOCK_TICK_RATE +/* LATCH is used in the interval timer and ftape setup. */ +# define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */ + /* HZ is the requested value. ACTHZ is actual HZ ("<< 8" is for accuracy) */ -#define ACTHZ (SH_DIV (CLOCK_TICK_RATE, LATCH, 8)) +# define ACTHZ (SH_DIV(CLOCK_TICK_RATE, LATCH, 8)) +#else +# define ACTHZ (HZ << 8) +#endif /* TICK_NSEC is the time between ticks in nsec assuming real ACTHZ */ #define TICK_NSEC (SH_DIV (1000000UL * 1000, ACTHZ, 8)) -- cgit v1.2.3 From 02ab20ae38337b99b5c29c81090f594b8fd61283 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 27 Jul 2012 14:48:10 -0400 Subject: time/jiffies: Rename ACTHZ to SHIFTED_HZ Ingo noted that ACTHZ is a confusing name, and requested it be renamed, so this patch renames ACTHZ to SHIFTED_HZ to better describe it. Signed-off-by: John Stultz Cc: Prarit Bhargava Link: http://lkml.kernel.org/r/1343414893-45779-3-git-send-email-john.stultz@linaro.org Signed-off-by: Ingo Molnar --- include/linux/jiffies.h | 21 +++++++++++++-------- include/linux/timex.h | 2 +- kernel/time/jiffies.c | 2 +- kernel/time/ntp.c | 2 +- 4 files changed, 16 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/linux/jiffies.h b/include/linux/jiffies.h index 7d24466fb80b..82680541576d 100644 --- a/include/linux/jiffies.h +++ b/include/linux/jiffies.h @@ -55,21 +55,26 @@ /* LATCH is used in the interval timer and ftape setup. */ # define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */ -/* HZ is the requested value. ACTHZ is actual HZ ("<< 8" is for accuracy) */ -# define ACTHZ (SH_DIV(CLOCK_TICK_RATE, LATCH, 8)) +/* + * HZ is the requested value. However the CLOCK_TICK_RATE may not allow + * for exactly HZ. So SHIFTED_HZ is high res HZ ("<< 8" is for accuracy) + */ +# define SHIFTED_HZ (SH_DIV(CLOCK_TICK_RATE, LATCH, 8)) #else -# define ACTHZ (HZ << 8) +# define SHIFTED_HZ (HZ << 8) #endif -/* TICK_NSEC is the time between ticks in nsec assuming real ACTHZ */ -#define TICK_NSEC (SH_DIV (1000000UL * 1000, ACTHZ, 8)) +/* TICK_NSEC is the time between ticks in nsec assuming SHIFTED_HZ */ +#define TICK_NSEC (SH_DIV(1000000UL * 1000, SHIFTED_HZ, 8)) /* TICK_USEC is the time between ticks in usec assuming fake USER_HZ */ #define TICK_USEC ((1000000UL + USER_HZ/2) / USER_HZ) -/* TICK_USEC_TO_NSEC is the time between ticks in nsec assuming real ACTHZ and */ -/* a value TUSEC for TICK_USEC (can be set bij adjtimex) */ -#define TICK_USEC_TO_NSEC(TUSEC) (SH_DIV (TUSEC * USER_HZ * 1000, ACTHZ, 8)) +/* + * TICK_USEC_TO_NSEC is the time between ticks in nsec assuming SHIFTED_HZ and + * a value TUSEC for TICK_USEC (can be set bij adjtimex) + */ +#define TICK_USEC_TO_NSEC(TUSEC) (SH_DIV(TUSEC * USER_HZ * 1000, SHIFTED_HZ, 8)) /* some arch's have a small-data section that can be accessed register-relative * but that can only take up to, say, 4-byte variables. jiffies being part of diff --git a/include/linux/timex.h b/include/linux/timex.h index 99bc88b1fc02..7c5ceb20e03a 100644 --- a/include/linux/timex.h +++ b/include/linux/timex.h @@ -232,7 +232,7 @@ struct timex { * estimated error = NTP dispersion. */ extern unsigned long tick_usec; /* USER_HZ period (usec) */ -extern unsigned long tick_nsec; /* ACTHZ period (nsec) */ +extern unsigned long tick_nsec; /* SHIFTED_HZ period (nsec) */ extern void ntp_init(void); extern void ntp_clear(void); diff --git a/kernel/time/jiffies.c b/kernel/time/jiffies.c index a470154e0408..46da0537c10b 100644 --- a/kernel/time/jiffies.c +++ b/kernel/time/jiffies.c @@ -37,7 +37,7 @@ * requested HZ value. It is also not recommended * for "tick-less" systems. */ -#define NSEC_PER_JIFFY ((u32)((((u64)NSEC_PER_SEC)<<8)/ACTHZ)) +#define NSEC_PER_JIFFY ((u32)((((u64)NSEC_PER_SEC)<<8)/SHIFTED_HZ)) /* Since jiffies uses a simple NSEC_PER_JIFFY multiplier * conversion, the .shift value could be zero. However diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index b7fbadc5c973..24174b4d669b 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c @@ -28,7 +28,7 @@ DEFINE_SPINLOCK(ntp_lock); /* USER_HZ period (usecs): */ unsigned long tick_usec = TICK_USEC; -/* ACTHZ period (nsecs): */ +/* SHIFTED_HZ period (nsecs): */ unsigned long tick_nsec; static u64 tick_length; -- cgit v1.2.3 From 54764bb647b2e847c512acf8d443df965da35000 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 31 Jul 2012 01:08:23 +0000 Subject: ipv4: Restore old dst_free() behavior. commit 404e0a8b6a55 (net: ipv4: fix RCU races on dst refcounts) tried to solve a race but added a problem at device/fib dismantle time : We really want to call dst_free() as soon as possible, even if sockets still have dst in their cache. dst_release() calls in free_fib_info_rcu() are not welcomed. Root of the problem was that now we also cache output routes (in nh_rth_output), we must use call_rcu() instead of call_rcu_bh() in rt_free(), because output route lookups are done in process context. Based on feedback and initial patch from David Miller (adding another call_rcu_bh() call in fib, but it appears it was not the right fix) I left the inet_sk_rx_dst_set() helper and added __rcu attributes to nh_rth_output and nh_rth_input to better document what is going on in this code. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/dst.h | 7 ++++++- include/net/inet_sock.h | 10 +++------- include/net/ip_fib.h | 4 ++-- net/core/dst.c | 26 +++++--------------------- net/decnet/dn_route.c | 6 ------ net/ipv4/fib_semantics.c | 21 +++++++++++++++++---- net/ipv4/route.c | 26 +++++++++++++++++--------- 7 files changed, 50 insertions(+), 50 deletions(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index 31a9fd39edb6..baf597890064 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -61,7 +61,6 @@ struct dst_entry { #define DST_NOPEER 0x0040 #define DST_FAKE_RTABLE 0x0080 #define DST_XFRM_TUNNEL 0x0100 -#define DST_RCU_FREE 0x0200 unsigned short pending_confirm; @@ -383,6 +382,12 @@ static inline void dst_free(struct dst_entry *dst) __dst_free(dst); } +static inline void dst_rcu_free(struct rcu_head *head) +{ + struct dst_entry *dst = container_of(head, struct dst_entry, rcu_head); + dst_free(dst); +} + static inline void dst_confirm(struct dst_entry *dst) { dst->pending_confirm = 1; diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index e3fd34c83ac9..83b567fe1941 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -253,13 +253,9 @@ static inline void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb { struct dst_entry *dst = skb_dst(skb); - if (atomic_inc_not_zero(&dst->__refcnt)) { - if (!(dst->flags & DST_RCU_FREE)) - dst->flags |= DST_RCU_FREE; - - sk->sk_rx_dst = dst; - inet_sk(sk)->rx_dst_ifindex = skb->skb_iif; - } + dst_hold(dst); + sk->sk_rx_dst = dst; + inet_sk(sk)->rx_dst_ifindex = skb->skb_iif; } #endif /* _INET_SOCK_H */ diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index e69c3a47153d..e521a03515b1 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -81,8 +81,8 @@ struct fib_nh { __be32 nh_gw; __be32 nh_saddr; int nh_saddr_genid; - struct rtable *nh_rth_output; - struct rtable *nh_rth_input; + struct rtable __rcu *nh_rth_output; + struct rtable __rcu *nh_rth_input; struct fnhe_hash_bucket *nh_exceptions; }; diff --git a/net/core/dst.c b/net/core/dst.c index d9e33ebe170f..069d51d29414 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -258,15 +258,6 @@ again: } EXPORT_SYMBOL(dst_destroy); -static void dst_rcu_destroy(struct rcu_head *head) -{ - struct dst_entry *dst = container_of(head, struct dst_entry, rcu_head); - - dst = dst_destroy(dst); - if (dst) - __dst_free(dst); -} - void dst_release(struct dst_entry *dst) { if (dst) { @@ -274,14 +265,10 @@ void dst_release(struct dst_entry *dst) newrefcnt = atomic_dec_return(&dst->__refcnt); WARN_ON(newrefcnt < 0); - if (unlikely(dst->flags & (DST_NOCACHE | DST_RCU_FREE)) && !newrefcnt) { - if (dst->flags & DST_RCU_FREE) { - call_rcu_bh(&dst->rcu_head, dst_rcu_destroy); - } else { - dst = dst_destroy(dst); - if (dst) - __dst_free(dst); - } + if (unlikely(dst->flags & DST_NOCACHE) && !newrefcnt) { + dst = dst_destroy(dst); + if (dst) + __dst_free(dst); } } } @@ -333,14 +320,11 @@ EXPORT_SYMBOL(__dst_destroy_metrics_generic); */ void skb_dst_set_noref(struct sk_buff *skb, struct dst_entry *dst) { - bool hold; - WARN_ON(!rcu_read_lock_held() && !rcu_read_lock_bh_held()); /* If dst not in cache, we must take a reference, because * dst_release() will destroy dst as soon as its refcount becomes zero */ - hold = (dst->flags & (DST_NOCACHE | DST_RCU_FREE)) == DST_NOCACHE; - if (unlikely(hold)) { + if (unlikely(dst->flags & DST_NOCACHE)) { dst_hold(dst); skb_dst_set(skb, dst); } else { diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 26719779ad8e..85a3604c87c8 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -184,12 +184,6 @@ static __inline__ unsigned int dn_hash(__le16 src, __le16 dst) return dn_rt_hash_mask & (unsigned int)tmp; } -static inline void dst_rcu_free(struct rcu_head *head) -{ - struct dst_entry *dst = container_of(head, struct dst_entry, rcu_head); - dst_free(dst); -} - static inline void dnrt_free(struct dn_route *rt) { call_rcu_bh(&rt->dst.rcu_head, dst_rcu_free); diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index e55171f184f9..625cf185c489 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -161,6 +161,21 @@ static void free_nh_exceptions(struct fib_nh *nh) kfree(hash); } +static void rt_nexthop_free(struct rtable __rcu **rtp) +{ + struct rtable *rt = rcu_dereference_protected(*rtp, 1); + + if (!rt) + return; + + /* Not even needed : RCU_INIT_POINTER(*rtp, NULL); + * because we waited an RCU grace period before calling + * free_fib_info_rcu() + */ + + dst_free(&rt->dst); +} + /* Release a nexthop info record */ static void free_fib_info_rcu(struct rcu_head *head) { @@ -171,10 +186,8 @@ static void free_fib_info_rcu(struct rcu_head *head) dev_put(nexthop_nh->nh_dev); if (nexthop_nh->nh_exceptions) free_nh_exceptions(nexthop_nh); - if (nexthop_nh->nh_rth_output) - dst_release(&nexthop_nh->nh_rth_output->dst); - if (nexthop_nh->nh_rth_input) - dst_release(&nexthop_nh->nh_rth_input->dst); + rt_nexthop_free(&nexthop_nh->nh_rth_output); + rt_nexthop_free(&nexthop_nh->nh_rth_input); } endfor_nexthops(fi); release_net(fi->fib_net); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index d6eabcfe8a90..2bd107477469 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1199,23 +1199,31 @@ restart: fnhe->fnhe_stamp = jiffies; } +static inline void rt_free(struct rtable *rt) +{ + call_rcu(&rt->dst.rcu_head, dst_rcu_free); +} + static void rt_cache_route(struct fib_nh *nh, struct rtable *rt) { - struct rtable *orig, *prev, **p = &nh->nh_rth_output; + struct rtable *orig, *prev, **p = (struct rtable **)&nh->nh_rth_output; if (rt_is_input_route(rt)) - p = &nh->nh_rth_input; + p = (struct rtable **)&nh->nh_rth_input; orig = *p; - rt->dst.flags |= DST_RCU_FREE; - dst_hold(&rt->dst); prev = cmpxchg(p, orig, rt); if (prev == orig) { if (orig) - dst_release(&orig->dst); + rt_free(orig); } else { - dst_release(&rt->dst); + /* Routes we intend to cache in the FIB nexthop have + * the DST_NOCACHE bit clear. However, if we are + * unsuccessful at storing this route into the cache + * we really need to set it. + */ + rt->dst.flags |= DST_NOCACHE; } } @@ -1412,7 +1420,7 @@ static int __mkroute_input(struct sk_buff *skb, do_cache = false; if (res->fi) { if (!itag) { - rth = FIB_RES_NH(*res).nh_rth_input; + rth = rcu_dereference(FIB_RES_NH(*res).nh_rth_input); if (rt_cache_valid(rth)) { skb_dst_set_noref(skb, &rth->dst); goto out; @@ -1574,7 +1582,7 @@ local_input: do_cache = false; if (res.fi) { if (!itag) { - rth = FIB_RES_NH(res).nh_rth_input; + rth = rcu_dereference(FIB_RES_NH(res).nh_rth_input); if (rt_cache_valid(rth)) { skb_dst_set_noref(skb, &rth->dst); err = 0; @@ -1742,7 +1750,7 @@ static struct rtable *__mkroute_output(const struct fib_result *res, if (fi) { fnhe = find_exception(&FIB_RES_NH(*res), fl4->daddr); if (!fnhe) { - rth = FIB_RES_NH(*res).nh_rth_output; + rth = rcu_dereference(FIB_RES_NH(*res).nh_rth_output); if (rt_cache_valid(rth)) { dst_hold(&rth->dst); return rth; -- cgit v1.2.3 From d26b3a7c4b3b26319f18bb645de93eba8f4bdcd5 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 31 Jul 2012 05:45:30 +0000 Subject: ipv4: percpu nh_rth_output cache Input path is mostly run under RCU and doesnt touch dst refcnt But output path on forwarding or UDP workloads hits badly dst refcount, and we have lot of false sharing, for example in ipv4_mtu() when reading rt->rt_pmtu Using a percpu cache for nh_rth_output gives a nice performance increase at a small cost. 24 udpflood test on my 24 cpu machine (dummy0 output device) (each process sends 1.000.000 udp frames, 24 processes are started) before : 5.24 s after : 2.06 s For reference, time on linux-3.5 : 6.60 s Signed-off-by: Eric Dumazet Tested-by: Alexander Duyck Signed-off-by: David S. Miller --- include/net/ip_fib.h | 3 ++- net/ipv4/fib_semantics.c | 20 +++++++++++++++++++- net/ipv4/route.c | 18 +++++++++++++----- 3 files changed, 34 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index e521a03515b1..e331746029b4 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -21,6 +21,7 @@ #include #include #include +#include struct fib_config { u8 fc_dst_len; @@ -81,7 +82,7 @@ struct fib_nh { __be32 nh_gw; __be32 nh_saddr; int nh_saddr_genid; - struct rtable __rcu *nh_rth_output; + struct rtable __rcu * __percpu *nh_pcpu_rth_output; struct rtable __rcu *nh_rth_input; struct fnhe_hash_bucket *nh_exceptions; }; diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 625cf185c489..fe2ca02a1979 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -176,6 +176,23 @@ static void rt_nexthop_free(struct rtable __rcu **rtp) dst_free(&rt->dst); } +static void rt_nexthop_free_cpus(struct rtable __rcu * __percpu *rtp) +{ + int cpu; + + if (!rtp) + return; + + for_each_possible_cpu(cpu) { + struct rtable *rt; + + rt = rcu_dereference_protected(*per_cpu_ptr(rtp, cpu), 1); + if (rt) + dst_free(&rt->dst); + } + free_percpu(rtp); +} + /* Release a nexthop info record */ static void free_fib_info_rcu(struct rcu_head *head) { @@ -186,7 +203,7 @@ static void free_fib_info_rcu(struct rcu_head *head) dev_put(nexthop_nh->nh_dev); if (nexthop_nh->nh_exceptions) free_nh_exceptions(nexthop_nh); - rt_nexthop_free(&nexthop_nh->nh_rth_output); + rt_nexthop_free_cpus(nexthop_nh->nh_pcpu_rth_output); rt_nexthop_free(&nexthop_nh->nh_rth_input); } endfor_nexthops(fi); @@ -817,6 +834,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg) fi->fib_nhs = nhs; change_nexthops(fi) { nexthop_nh->nh_parent = fi; + nexthop_nh->nh_pcpu_rth_output = alloc_percpu(struct rtable __rcu *); } endfor_nexthops(fi) if (cfg->fc_mx) { diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 2bd107477469..4f6276ce0af3 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1206,11 +1206,15 @@ static inline void rt_free(struct rtable *rt) static void rt_cache_route(struct fib_nh *nh, struct rtable *rt) { - struct rtable *orig, *prev, **p = (struct rtable **)&nh->nh_rth_output; + struct rtable *orig, *prev, **p; - if (rt_is_input_route(rt)) + if (rt_is_input_route(rt)) { p = (struct rtable **)&nh->nh_rth_input; - + } else { + if (!nh->nh_pcpu_rth_output) + goto nocache; + p = (struct rtable **)__this_cpu_ptr(nh->nh_pcpu_rth_output); + } orig = *p; prev = cmpxchg(p, orig, rt); @@ -1223,6 +1227,7 @@ static void rt_cache_route(struct fib_nh *nh, struct rtable *rt) * unsuccessful at storing this route into the cache * we really need to set it. */ +nocache: rt->dst.flags |= DST_NOCACHE; } } @@ -1749,8 +1754,11 @@ static struct rtable *__mkroute_output(const struct fib_result *res, fnhe = NULL; if (fi) { fnhe = find_exception(&FIB_RES_NH(*res), fl4->daddr); - if (!fnhe) { - rth = rcu_dereference(FIB_RES_NH(*res).nh_rth_output); + if (!fnhe && FIB_RES_NH(*res).nh_pcpu_rth_output) { + struct rtable __rcu **prth; + + prth = __this_cpu_ptr(FIB_RES_NH(*res).nh_pcpu_rth_output); + rth = rcu_dereference(*prth); if (rt_cache_valid(rth)) { dst_hold(&rth->dst); return rth; -- cgit v1.2.3 From c5038a8327b980a5b279fa193163c468011de009 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 31 Jul 2012 15:02:02 -0700 Subject: ipv4: Cache routes in nexthop exception entries. Signed-off-by: David S. Miller --- include/net/ip_fib.h | 1 + net/ipv4/fib_semantics.c | 39 +++++++++--------- net/ipv4/route.c | 103 ++++++++++++++++++++++++++--------------------- 3 files changed, 79 insertions(+), 64 deletions(-) (limited to 'include') diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index e331746029b4..926142ed8d7a 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -55,6 +55,7 @@ struct fib_nh_exception { u32 fnhe_pmtu; __be32 fnhe_gw; unsigned long fnhe_expires; + struct rtable __rcu *fnhe_rth; unsigned long fnhe_stamp; }; diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index fe2ca02a1979..da80dc14cc76 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -140,6 +140,21 @@ const struct fib_prop fib_props[RTN_MAX + 1] = { }, }; +static void rt_fibinfo_free(struct rtable __rcu **rtp) +{ + struct rtable *rt = rcu_dereference_protected(*rtp, 1); + + if (!rt) + return; + + /* Not even needed : RCU_INIT_POINTER(*rtp, NULL); + * because we waited an RCU grace period before calling + * free_fib_info_rcu() + */ + + dst_free(&rt->dst); +} + static void free_nh_exceptions(struct fib_nh *nh) { struct fnhe_hash_bucket *hash = nh->nh_exceptions; @@ -153,6 +168,9 @@ static void free_nh_exceptions(struct fib_nh *nh) struct fib_nh_exception *next; next = rcu_dereference_protected(fnhe->fnhe_next, 1); + + rt_fibinfo_free(&fnhe->fnhe_rth); + kfree(fnhe); fnhe = next; @@ -161,22 +179,7 @@ static void free_nh_exceptions(struct fib_nh *nh) kfree(hash); } -static void rt_nexthop_free(struct rtable __rcu **rtp) -{ - struct rtable *rt = rcu_dereference_protected(*rtp, 1); - - if (!rt) - return; - - /* Not even needed : RCU_INIT_POINTER(*rtp, NULL); - * because we waited an RCU grace period before calling - * free_fib_info_rcu() - */ - - dst_free(&rt->dst); -} - -static void rt_nexthop_free_cpus(struct rtable __rcu * __percpu *rtp) +static void rt_fibinfo_free_cpus(struct rtable __rcu * __percpu *rtp) { int cpu; @@ -203,8 +206,8 @@ static void free_fib_info_rcu(struct rcu_head *head) dev_put(nexthop_nh->nh_dev); if (nexthop_nh->nh_exceptions) free_nh_exceptions(nexthop_nh); - rt_nexthop_free_cpus(nexthop_nh->nh_pcpu_rth_output); - rt_nexthop_free(&nexthop_nh->nh_rth_input); + rt_fibinfo_free_cpus(nexthop_nh->nh_pcpu_rth_output); + rt_fibinfo_free(&nexthop_nh->nh_rth_input); } endfor_nexthops(fi); release_net(fi->fib_net); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 4f6276ce0af3..b102eeb16e34 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -587,11 +587,17 @@ static void ip_rt_build_flow_key(struct flowi4 *fl4, const struct sock *sk, build_sk_flow_key(fl4, sk); } -static DEFINE_SEQLOCK(fnhe_seqlock); +static inline void rt_free(struct rtable *rt) +{ + call_rcu(&rt->dst.rcu_head, dst_rcu_free); +} + +static DEFINE_SPINLOCK(fnhe_lock); static struct fib_nh_exception *fnhe_oldest(struct fnhe_hash_bucket *hash) { struct fib_nh_exception *fnhe, *oldest; + struct rtable *orig; oldest = rcu_dereference(hash->chain); for (fnhe = rcu_dereference(oldest->fnhe_next); fnhe; @@ -599,6 +605,11 @@ static struct fib_nh_exception *fnhe_oldest(struct fnhe_hash_bucket *hash) if (time_before(fnhe->fnhe_stamp, oldest->fnhe_stamp)) oldest = fnhe; } + orig = rcu_dereference(oldest->fnhe_rth); + if (orig) { + RCU_INIT_POINTER(oldest->fnhe_rth, NULL); + rt_free(orig); + } return oldest; } @@ -620,7 +631,7 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw, int depth; u32 hval = fnhe_hashfun(daddr); - write_seqlock_bh(&fnhe_seqlock); + spin_lock_bh(&fnhe_lock); hash = nh->nh_exceptions; if (!hash) { @@ -667,7 +678,7 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw, fnhe->fnhe_stamp = jiffies; out_unlock: - write_sequnlock_bh(&fnhe_seqlock); + spin_unlock_bh(&fnhe_lock); return; } @@ -1167,41 +1178,40 @@ static struct fib_nh_exception *find_exception(struct fib_nh *nh, __be32 daddr) static void rt_bind_exception(struct rtable *rt, struct fib_nh_exception *fnhe, __be32 daddr) { - __be32 fnhe_daddr, gw; - unsigned long expires; - unsigned int seq; - u32 pmtu; - -restart: - seq = read_seqbegin(&fnhe_seqlock); - fnhe_daddr = fnhe->fnhe_daddr; - gw = fnhe->fnhe_gw; - pmtu = fnhe->fnhe_pmtu; - expires = fnhe->fnhe_expires; - if (read_seqretry(&fnhe_seqlock, seq)) - goto restart; - - if (daddr != fnhe_daddr) - return; + spin_lock_bh(&fnhe_lock); - if (pmtu) { - unsigned long diff = expires - jiffies; + if (daddr == fnhe->fnhe_daddr) { + struct rtable *orig; - if (time_before(jiffies, expires)) { - rt->rt_pmtu = pmtu; - dst_set_expires(&rt->dst, diff); + if (fnhe->fnhe_pmtu) { + unsigned long expires = fnhe->fnhe_expires; + unsigned long diff = expires - jiffies; + + if (time_before(jiffies, expires)) { + rt->rt_pmtu = fnhe->fnhe_pmtu; + dst_set_expires(&rt->dst, diff); + } + } + if (fnhe->fnhe_gw) { + rt->rt_flags |= RTCF_REDIRECTED; + rt->rt_gateway = fnhe->fnhe_gw; } - } - if (gw) { - rt->rt_flags |= RTCF_REDIRECTED; - rt->rt_gateway = gw; - } - fnhe->fnhe_stamp = jiffies; -} -static inline void rt_free(struct rtable *rt) -{ - call_rcu(&rt->dst.rcu_head, dst_rcu_free); + orig = rcu_dereference(fnhe->fnhe_rth); + rcu_assign_pointer(fnhe->fnhe_rth, rt); + if (orig) + rt_free(orig); + + fnhe->fnhe_stamp = jiffies; + } else { + /* Routes we intend to cache in nexthop exception have + * the DST_NOCACHE bit clear. However, if we are + * unsuccessful at storing this route into the cache + * we really need to set it. + */ + rt->dst.flags |= DST_NOCACHE; + } + spin_unlock_bh(&fnhe_lock); } static void rt_cache_route(struct fib_nh *nh, struct rtable *rt) @@ -1249,13 +1259,13 @@ static void rt_set_nexthop(struct rtable *rt, __be32 daddr, if (nh->nh_gw && nh->nh_scope == RT_SCOPE_LINK) rt->rt_gateway = nh->nh_gw; - if (unlikely(fnhe)) - rt_bind_exception(rt, fnhe, daddr); dst_init_metrics(&rt->dst, fi->fib_metrics, true); #ifdef CONFIG_IP_ROUTE_CLASSID rt->dst.tclassid = nh->nh_tclassid; #endif - if (!(rt->dst.flags & DST_NOCACHE)) + if (unlikely(fnhe)) + rt_bind_exception(rt, fnhe, daddr); + else if (!(rt->dst.flags & DST_NOCACHE)) rt_cache_route(nh, rt); } @@ -1753,22 +1763,23 @@ static struct rtable *__mkroute_output(const struct fib_result *res, fnhe = NULL; if (fi) { - fnhe = find_exception(&FIB_RES_NH(*res), fl4->daddr); - if (!fnhe && FIB_RES_NH(*res).nh_pcpu_rth_output) { - struct rtable __rcu **prth; + struct rtable __rcu **prth; + fnhe = find_exception(&FIB_RES_NH(*res), fl4->daddr); + if (fnhe) + prth = &fnhe->fnhe_rth; + else prth = __this_cpu_ptr(FIB_RES_NH(*res).nh_pcpu_rth_output); - rth = rcu_dereference(*prth); - if (rt_cache_valid(rth)) { - dst_hold(&rth->dst); - return rth; - } + rth = rcu_dereference(*prth); + if (rt_cache_valid(rth)) { + dst_hold(&rth->dst); + return rth; } } rth = rt_dst_alloc(dev_out, IN_DEV_CONF_GET(in_dev, NOPOLICY), IN_DEV_CONF_GET(in_dev, NOXFRM), - fi && !fnhe); + fi); if (!rth) return ERR_PTR(-ENOBUFS); -- cgit v1.2.3 From caacf05e5ad1abf0a2864863da4e33024bc68ec6 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 31 Jul 2012 15:06:50 -0700 Subject: ipv4: Properly purge netdev references on uncached routes. When a device is unregistered, we have to purge all of the references to it that may exist in the entire system. If a route is uncached, we currently have no way of accomplishing this. So create a global list that is scanned when a network device goes down. This mirrors the logic in net/core/dst.c's dst_ifdown(). Signed-off-by: David S. Miller --- include/net/route.h | 3 +++ net/ipv4/fib_frontend.c | 1 + net/ipv4/route.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++--- net/ipv4/xfrm4_policy.c | 1 + 4 files changed, 69 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/route.h b/include/net/route.h index 8c52bc6f1c90..776a27f1ab78 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -57,6 +57,8 @@ struct rtable { /* Miscellaneous cached information */ u32 rt_pmtu; + + struct list_head rt_uncached; }; static inline bool rt_is_input_route(const struct rtable *rt) @@ -107,6 +109,7 @@ extern struct ip_rt_acct __percpu *ip_rt_acct; struct in_device; extern int ip_rt_init(void); extern void rt_cache_flush(struct net *net, int how); +extern void rt_flush_dev(struct net_device *dev); extern struct rtable *__ip_route_output_key(struct net *, struct flowi4 *flp); extern struct rtable *ip_route_output_flow(struct net *, struct flowi4 *flp, struct sock *sk); diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 8732cc7920ed..c43ae3fba792 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -1046,6 +1046,7 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo if (event == NETDEV_UNREGISTER) { fib_disable_ip(dev, 2, -1); + rt_flush_dev(dev); return NOTIFY_DONE; } diff --git a/net/ipv4/route.c b/net/ipv4/route.c index b102eeb16e34..c035251beb07 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -147,6 +147,7 @@ static void ip_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb, u32 mtu); static void ip_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb); +static void ipv4_dst_destroy(struct dst_entry *dst); static void ipv4_dst_ifdown(struct dst_entry *dst, struct net_device *dev, int how) @@ -170,6 +171,7 @@ static struct dst_ops ipv4_dst_ops = { .default_advmss = ipv4_default_advmss, .mtu = ipv4_mtu, .cow_metrics = ipv4_cow_metrics, + .destroy = ipv4_dst_destroy, .ifdown = ipv4_dst_ifdown, .negative_advice = ipv4_negative_advice, .link_failure = ipv4_link_failure, @@ -1175,9 +1177,11 @@ static struct fib_nh_exception *find_exception(struct fib_nh *nh, __be32 daddr) return NULL; } -static void rt_bind_exception(struct rtable *rt, struct fib_nh_exception *fnhe, +static bool rt_bind_exception(struct rtable *rt, struct fib_nh_exception *fnhe, __be32 daddr) { + bool ret = false; + spin_lock_bh(&fnhe_lock); if (daddr == fnhe->fnhe_daddr) { @@ -1203,6 +1207,7 @@ static void rt_bind_exception(struct rtable *rt, struct fib_nh_exception *fnhe, rt_free(orig); fnhe->fnhe_stamp = jiffies; + ret = true; } else { /* Routes we intend to cache in nexthop exception have * the DST_NOCACHE bit clear. However, if we are @@ -1212,11 +1217,14 @@ static void rt_bind_exception(struct rtable *rt, struct fib_nh_exception *fnhe, rt->dst.flags |= DST_NOCACHE; } spin_unlock_bh(&fnhe_lock); + + return ret; } -static void rt_cache_route(struct fib_nh *nh, struct rtable *rt) +static bool rt_cache_route(struct fib_nh *nh, struct rtable *rt) { struct rtable *orig, *prev, **p; + bool ret = true; if (rt_is_input_route(rt)) { p = (struct rtable **)&nh->nh_rth_input; @@ -1239,6 +1247,48 @@ static void rt_cache_route(struct fib_nh *nh, struct rtable *rt) */ nocache: rt->dst.flags |= DST_NOCACHE; + ret = false; + } + + return ret; +} + +static DEFINE_SPINLOCK(rt_uncached_lock); +static LIST_HEAD(rt_uncached_list); + +static void rt_add_uncached_list(struct rtable *rt) +{ + spin_lock_bh(&rt_uncached_lock); + list_add_tail(&rt->rt_uncached, &rt_uncached_list); + spin_unlock_bh(&rt_uncached_lock); +} + +static void ipv4_dst_destroy(struct dst_entry *dst) +{ + struct rtable *rt = (struct rtable *) dst; + + if (dst->flags & DST_NOCACHE) { + spin_lock_bh(&rt_uncached_lock); + list_del(&rt->rt_uncached); + spin_unlock_bh(&rt_uncached_lock); + } +} + +void rt_flush_dev(struct net_device *dev) +{ + if (!list_empty(&rt_uncached_list)) { + struct net *net = dev_net(dev); + struct rtable *rt; + + spin_lock_bh(&rt_uncached_lock); + list_for_each_entry(rt, &rt_uncached_list, rt_uncached) { + if (rt->dst.dev != dev) + continue; + rt->dst.dev = net->loopback_dev; + dev_hold(rt->dst.dev); + dev_put(dev); + } + spin_unlock_bh(&rt_uncached_lock); } } @@ -1254,6 +1304,8 @@ static void rt_set_nexthop(struct rtable *rt, __be32 daddr, struct fib_nh_exception *fnhe, struct fib_info *fi, u16 type, u32 itag) { + bool cached = false; + if (fi) { struct fib_nh *nh = &FIB_RES_NH(*res); @@ -1264,10 +1316,12 @@ static void rt_set_nexthop(struct rtable *rt, __be32 daddr, rt->dst.tclassid = nh->nh_tclassid; #endif if (unlikely(fnhe)) - rt_bind_exception(rt, fnhe, daddr); + cached = rt_bind_exception(rt, fnhe, daddr); else if (!(rt->dst.flags & DST_NOCACHE)) - rt_cache_route(nh, rt); + cached = rt_cache_route(nh, rt); } + if (unlikely(!cached)) + rt_add_uncached_list(rt); #ifdef CONFIG_IP_ROUTE_CLASSID #ifdef CONFIG_IP_MULTIPLE_TABLES @@ -1334,6 +1388,7 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, rth->rt_iif = 0; rth->rt_pmtu = 0; rth->rt_gateway = 0; + INIT_LIST_HEAD(&rth->rt_uncached); if (our) { rth->dst.input= ip_local_deliver; rth->rt_flags |= RTCF_LOCAL; @@ -1459,6 +1514,7 @@ static int __mkroute_input(struct sk_buff *skb, rth->rt_iif = 0; rth->rt_pmtu = 0; rth->rt_gateway = 0; + INIT_LIST_HEAD(&rth->rt_uncached); rth->dst.input = ip_forward; rth->dst.output = ip_output; @@ -1625,6 +1681,7 @@ local_input: rth->rt_iif = 0; rth->rt_pmtu = 0; rth->rt_gateway = 0; + INIT_LIST_HEAD(&rth->rt_uncached); if (res.type == RTN_UNREACHABLE) { rth->dst.input= ip_error; rth->dst.error= -err; @@ -1792,6 +1849,7 @@ static struct rtable *__mkroute_output(const struct fib_result *res, rth->rt_iif = orig_oif ? : 0; rth->rt_pmtu = 0; rth->rt_gateway = 0; + INIT_LIST_HEAD(&rth->rt_uncached); RT_CACHE_STAT_INC(out_slow_tot); @@ -2071,6 +2129,8 @@ struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_or rt->rt_type = ort->rt_type; rt->rt_gateway = ort->rt_gateway; + INIT_LIST_HEAD(&rt->rt_uncached); + dst_free(new); } diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index c6281847f16a..681ea2f413e2 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -92,6 +92,7 @@ static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, xdst->u.rt.rt_type = rt->rt_type; xdst->u.rt.rt_gateway = rt->rt_gateway; xdst->u.rt.rt_pmtu = rt->rt_pmtu; + INIT_LIST_HEAD(&xdst->u.rt.rt_uncached); return 0; } -- cgit v1.2.3 From 44de9d0cad41f2c51ef26916842be046b582dcc9 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Tue, 31 Jul 2012 16:41:49 -0700 Subject: mm: account the total_vm in the vm_stat_account() vm_stat_account() accounts the shared_vm, stack_vm and reserved_vm now. But we can also account for total_vm in the vm_stat_account() which makes the code tidy. Even for mprotect_fixup(), we can get the right result in the end. Signed-off-by: Huang Shijie Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/ia64/kernel/perfmon.c | 1 - include/linux/mm.h | 1 + kernel/fork.c | 4 +--- mm/mmap.c | 5 ++--- mm/mremap.c | 2 -- 5 files changed, 4 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c index d7f558c1e711..3fa4bc536953 100644 --- a/arch/ia64/kernel/perfmon.c +++ b/arch/ia64/kernel/perfmon.c @@ -2353,7 +2353,6 @@ pfm_smpl_buffer_alloc(struct task_struct *task, struct file *filp, pfm_context_t */ insert_vm_struct(mm, vma); - mm->total_vm += size >> PAGE_SHIFT; vm_stat_account(vma->vm_mm, vma->vm_flags, vma->vm_file, vma_pages(vma)); up_write(&task->mm->mmap_sem); diff --git a/include/linux/mm.h b/include/linux/mm.h index f9f279cf5b1b..3955bedeeed1 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1528,6 +1528,7 @@ void vm_stat_account(struct mm_struct *, unsigned long, struct file *, long); static inline void vm_stat_account(struct mm_struct *mm, unsigned long flags, struct file *file, long pages) { + mm->total_vm += pages; } #endif /* CONFIG_PROC_FS */ diff --git a/kernel/fork.c b/kernel/fork.c index 8efac1fe56bc..aaa8813c45d1 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -381,10 +381,8 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) struct file *file; if (mpnt->vm_flags & VM_DONTCOPY) { - long pages = vma_pages(mpnt); - mm->total_vm -= pages; vm_stat_account(mm, mpnt->vm_flags, mpnt->vm_file, - -pages); + -vma_pages(mpnt)); continue; } charge = 0; diff --git a/mm/mmap.c b/mm/mmap.c index 3edfcdfa42d9..1ee2fd8bc486 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -943,6 +943,8 @@ void vm_stat_account(struct mm_struct *mm, unsigned long flags, const unsigned long stack_flags = VM_STACK_FLAGS & (VM_GROWSUP|VM_GROWSDOWN); + mm->total_vm += pages; + if (file) { mm->shared_vm += pages; if ((flags & (VM_EXEC|VM_WRITE)) == VM_EXEC) @@ -1347,7 +1349,6 @@ munmap_back: out: perf_event_mmap(vma); - mm->total_vm += len >> PAGE_SHIFT; vm_stat_account(mm, vm_flags, file, len >> PAGE_SHIFT); if (vm_flags & VM_LOCKED) { if (!mlock_vma_pages_range(vma, addr, addr + len)) @@ -1707,7 +1708,6 @@ static int acct_stack_growth(struct vm_area_struct *vma, unsigned long size, uns return -ENOMEM; /* Ok, everything looks good - let it rip */ - mm->total_vm += grow; if (vma->vm_flags & VM_LOCKED) mm->locked_vm += grow; vm_stat_account(mm, vma->vm_flags, vma->vm_file, grow); @@ -1889,7 +1889,6 @@ static void remove_vma_list(struct mm_struct *mm, struct vm_area_struct *vma) if (vma->vm_flags & VM_ACCOUNT) nr_accounted += nrpages; - mm->total_vm -= nrpages; vm_stat_account(mm, vma->vm_flags, vma->vm_file, -nrpages); vma = remove_vma(vma); } while (vma); diff --git a/mm/mremap.c b/mm/mremap.c index 21fed202ddad..cc06d0e48d05 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -260,7 +260,6 @@ static unsigned long move_vma(struct vm_area_struct *vma, * If this were a serious issue, we'd add a flag to do_munmap(). */ hiwater_vm = mm->hiwater_vm; - mm->total_vm += new_len >> PAGE_SHIFT; vm_stat_account(mm, vma->vm_flags, vma->vm_file, new_len>>PAGE_SHIFT); if (do_munmap(mm, old_addr, old_len) < 0) { @@ -497,7 +496,6 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len, goto out; } - mm->total_vm += pages; vm_stat_account(mm, vma->vm_flags, vma->vm_file, pages); if (vma->vm_flags & VM_LOCKED) { mm->locked_vm += pages; -- cgit v1.2.3 From 3965c9ae47d64aadf6f13b6fcd37767b83c0689a Mon Sep 17 00:00:00 2001 From: Wanpeng Li Date: Tue, 31 Jul 2012 16:41:52 -0700 Subject: mm: prepare for removal of obsolete /proc/sys/vm/nr_pdflush_threads Since per-BDI flusher threads were introduced in 2.6, the pdflush mechanism is not used any more. But the old interface exported through /proc/sys/vm/nr_pdflush_threads still exists and is obviously useless. For back-compatibility, printk warning information and return 2 to notify the users that the interface is removed. Signed-off-by: Wanpeng Li Cc: Wu Fengguang Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- .../ABI/obsolete/proc-sys-vm-nr_pdflush_threads | 5 +++++ Documentation/feature-removal-schedule.txt | 8 ++++++++ Documentation/sysctl/vm.txt | 11 ----------- fs/fs-writeback.c | 5 ----- include/linux/backing-dev.h | 3 +++ include/linux/writeback.h | 5 ----- kernel/sysctl.c | 8 +++----- kernel/sysctl_binary.c | 2 +- mm/backing-dev.c | 20 ++++++++++++++++++++ 9 files changed, 40 insertions(+), 27 deletions(-) create mode 100644 Documentation/ABI/obsolete/proc-sys-vm-nr_pdflush_threads (limited to 'include') diff --git a/Documentation/ABI/obsolete/proc-sys-vm-nr_pdflush_threads b/Documentation/ABI/obsolete/proc-sys-vm-nr_pdflush_threads new file mode 100644 index 000000000000..b0b0eeb20fe3 --- /dev/null +++ b/Documentation/ABI/obsolete/proc-sys-vm-nr_pdflush_threads @@ -0,0 +1,5 @@ +What: /proc/sys/vm/nr_pdflush_threads +Date: June 2012 +Contact: Wanpeng Li +Description: Since pdflush is replaced by per-BDI flusher, the interface of old pdflush + exported in /proc/sys/vm/ should be removed. diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index e9237fb71950..88f2fa48bb63 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -13,6 +13,14 @@ Who: Jim Cromie , Jason Baron --------------------------- +What: /proc/sys/vm/nr_pdflush_threads +When: 2012 +Why: Since pdflush is deprecated, the interface exported in /proc/sys/vm/ + should be removed. +Who: Wanpeng Li + +--------------------------- + What: CONFIG_APM_CPU_IDLE, and its ability to call APM BIOS in idle When: 2012 Why: This optional sub-feature of APM is of dubious reliability, diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt index 84eb25cd69aa..06d662b1c5d5 100644 --- a/Documentation/sysctl/vm.txt +++ b/Documentation/sysctl/vm.txt @@ -42,7 +42,6 @@ Currently, these files are in /proc/sys/vm: - mmap_min_addr - nr_hugepages - nr_overcommit_hugepages -- nr_pdflush_threads - nr_trim_pages (only if CONFIG_MMU=n) - numa_zonelist_order - oom_dump_tasks @@ -426,16 +425,6 @@ See Documentation/vm/hugetlbpage.txt ============================================================== -nr_pdflush_threads - -The current number of pdflush threads. This value is read-only. -The value changes according to the number of dirty pages in the system. - -When necessary, additional pdflush threads are created, one per second, up to -nr_pdflush_threads_max. - -============================================================== - nr_trim_pages This is available only on NOMMU kernels. diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 50d0b78130a1..be3efc4f64f4 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -52,11 +52,6 @@ struct wb_writeback_work { struct completion *done; /* set if the caller waits */ }; -/* - * We don't actually have pdflush, but this one is exported though /proc... - */ -int nr_pdflush_threads; - /** * writeback_in_progress - determine whether there is writeback in progress * @bdi: the device's backing_dev_info structure. diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index 489de625cd25..c97c6b9cd38e 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -17,6 +17,7 @@ #include #include #include +#include struct page; struct device; @@ -304,6 +305,8 @@ void clear_bdi_congested(struct backing_dev_info *bdi, int sync); void set_bdi_congested(struct backing_dev_info *bdi, int sync); long congestion_wait(int sync, long timeout); long wait_iff_congested(struct zone *zone, int sync, long timeout); +int pdflush_proc_obsolete(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos); static inline bool bdi_cap_writeback_dirty(struct backing_dev_info *bdi) { diff --git a/include/linux/writeback.h b/include/linux/writeback.h index 6d0a0fcd80e7..c66fe3332d83 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -189,9 +189,4 @@ void tag_pages_for_writeback(struct address_space *mapping, void account_page_redirty(struct page *page); -/* pdflush.c */ -extern int nr_pdflush_threads; /* Global so it can be exported to sysctl - read-only. */ - - #endif /* WRITEBACK_H */ diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 97186b99b0e4..6502d35a25ba 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1101,11 +1101,9 @@ static struct ctl_table vm_table[] = { .extra1 = &zero, }, { - .procname = "nr_pdflush_threads", - .data = &nr_pdflush_threads, - .maxlen = sizeof nr_pdflush_threads, - .mode = 0444 /* read-only*/, - .proc_handler = proc_dointvec, + .procname = "nr_pdflush_threads", + .mode = 0444 /* read-only */, + .proc_handler = pdflush_proc_obsolete, }, { .procname = "swappiness", diff --git a/kernel/sysctl_binary.c b/kernel/sysctl_binary.c index a650694883a1..65bdcf198d4e 100644 --- a/kernel/sysctl_binary.c +++ b/kernel/sysctl_binary.c @@ -147,7 +147,7 @@ static const struct bin_table bin_vm_table[] = { { CTL_INT, VM_DIRTY_RATIO, "dirty_ratio" }, /* VM_DIRTY_WB_CS "dirty_writeback_centisecs" no longer used */ /* VM_DIRTY_EXPIRE_CS "dirty_expire_centisecs" no longer used */ - { CTL_INT, VM_NR_PDFLUSH_THREADS, "nr_pdflush_threads" }, + /* VM_NR_PDFLUSH_THREADS "nr_pdflush_threads" no longer used */ { CTL_INT, VM_OVERCOMMIT_RATIO, "overcommit_ratio" }, /* VM_PAGEBUF unused */ /* VM_HUGETLB_PAGES "nr_hugepages" no longer used */ diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 3387aea11209..6b4718e2ee34 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -886,3 +886,23 @@ out: return ret; } EXPORT_SYMBOL(wait_iff_congested); + +int pdflush_proc_obsolete(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + char kbuf[] = "0\n"; + + if (*ppos) { + *lenp = 0; + return 0; + } + + if (copy_to_user(buffer, kbuf, sizeof(kbuf))) + return -EFAULT; + printk_once(KERN_WARNING "%s exported in /proc is scheduled for removal\n", + table->procname); + + *lenp = 2; + *ppos += *lenp; + return 2; +} -- cgit v1.2.3 From 972dc4de13f667a7df27ee32573b2e6fc6cc8434 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 31 Jul 2012 16:42:00 -0700 Subject: hugetlb: add an inline helper for finding hstate index Add an inline helper and use it in the code. Signed-off-by: Aneesh Kumar K.V Acked-by: David Rientjes Acked-by: Michal Hocko Reviewed-by: KAMEZAWA Hiroyuki Cc: Hillf Danton Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hugetlb.h | 6 ++++++ mm/hugetlb.c | 20 +++++++++++--------- 2 files changed, 17 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index d5d6bbe2259e..217f52859fa7 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -302,6 +302,11 @@ static inline unsigned hstate_index_to_shift(unsigned index) return hstates[index].order + PAGE_SHIFT; } +static inline int hstate_index(struct hstate *h) +{ + return h - hstates; +} + #else struct hstate {}; #define alloc_huge_page_node(h, nid) NULL @@ -320,6 +325,7 @@ static inline unsigned int pages_per_huge_page(struct hstate *h) return 1; } #define hstate_index_to_shift(index) 0 +#define hstate_index(h) 0 #endif #endif /* _LINUX_HUGETLB_H */ diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 34a7e2375478..b1e0ed1ea912 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -1646,7 +1646,7 @@ static int hugetlb_sysfs_add_hstate(struct hstate *h, struct kobject *parent, struct attribute_group *hstate_attr_group) { int retval; - int hi = h - hstates; + int hi = hstate_index(h); hstate_kobjs[hi] = kobject_create_and_add(h->name, parent); if (!hstate_kobjs[hi]) @@ -1741,11 +1741,13 @@ void hugetlb_unregister_node(struct node *node) if (!nhs->hugepages_kobj) return; /* no hstate attributes */ - for_each_hstate(h) - if (nhs->hstate_kobjs[h - hstates]) { - kobject_put(nhs->hstate_kobjs[h - hstates]); - nhs->hstate_kobjs[h - hstates] = NULL; + for_each_hstate(h) { + int idx = hstate_index(h); + if (nhs->hstate_kobjs[idx]) { + kobject_put(nhs->hstate_kobjs[idx]); + nhs->hstate_kobjs[idx] = NULL; } + } kobject_put(nhs->hugepages_kobj); nhs->hugepages_kobj = NULL; @@ -1848,7 +1850,7 @@ static void __exit hugetlb_exit(void) hugetlb_unregister_all_nodes(); for_each_hstate(h) { - kobject_put(hstate_kobjs[h - hstates]); + kobject_put(hstate_kobjs[hstate_index(h)]); } kobject_put(hugepages_kobj); @@ -1869,7 +1871,7 @@ static int __init hugetlb_init(void) if (!size_to_hstate(default_hstate_size)) hugetlb_add_hstate(HUGETLB_PAGE_ORDER); } - default_hstate_idx = size_to_hstate(default_hstate_size) - hstates; + default_hstate_idx = hstate_index(size_to_hstate(default_hstate_size)); if (default_hstate_max_huge_pages) default_hstate.max_huge_pages = default_hstate_max_huge_pages; @@ -2687,7 +2689,7 @@ retry: */ if (unlikely(PageHWPoison(page))) { ret = VM_FAULT_HWPOISON | - VM_FAULT_SET_HINDEX(h - hstates); + VM_FAULT_SET_HINDEX(hstate_index(h)); goto backout_unlocked; } } @@ -2760,7 +2762,7 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, return 0; } else if (unlikely(is_hugetlb_entry_hwpoisoned(entry))) return VM_FAULT_HWPOISON_LARGE | - VM_FAULT_SET_HINDEX(h - hstates); + VM_FAULT_SET_HINDEX(hstate_index(h)); } ptep = huge_pte_alloc(mm, address, huge_page_size(h)); -- cgit v1.2.3 From 24669e58477e2752c1fbca9c1c988e9dd0d79d15 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 31 Jul 2012 16:42:03 -0700 Subject: hugetlb: use mmu_gather instead of a temporary linked list for accumulating pages Use a mmu_gather instead of a temporary linked list for accumulating pages when we unmap a hugepage range Signed-off-by: Aneesh Kumar K.V Reviewed-by: KAMEZAWA Hiroyuki Cc: David Rientjes Cc: Hillf Danton Cc: Michal Hocko Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/hugetlbfs/inode.c | 4 ++-- include/linux/hugetlb.h | 22 +++++++++++++----- mm/hugetlb.c | 59 +++++++++++++++++++++++++++++-------------------- mm/memory.c | 7 ++++-- 4 files changed, 59 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index e13e9bdb0bf5..8349a899912e 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -416,8 +416,8 @@ hugetlb_vmtruncate_list(struct prio_tree_root *root, pgoff_t pgoff) else v_offset = 0; - __unmap_hugepage_range(vma, - vma->vm_start + v_offset, vma->vm_end, NULL); + unmap_hugepage_range(vma, vma->vm_start + v_offset, + vma->vm_end, NULL); } } diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 217f52859fa7..0f23c1840c9b 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -7,6 +7,7 @@ struct ctl_table; struct user_struct; +struct mmu_gather; #ifdef CONFIG_HUGETLB_PAGE @@ -40,9 +41,10 @@ int follow_hugetlb_page(struct mm_struct *, struct vm_area_struct *, struct page **, struct vm_area_struct **, unsigned long *, int *, int, unsigned int flags); void unmap_hugepage_range(struct vm_area_struct *, - unsigned long, unsigned long, struct page *); -void __unmap_hugepage_range(struct vm_area_struct *, - unsigned long, unsigned long, struct page *); + unsigned long, unsigned long, struct page *); +void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma, + unsigned long start, unsigned long end, + struct page *ref_page); int hugetlb_prefault(struct address_space *, struct vm_area_struct *); void hugetlb_report_meminfo(struct seq_file *); int hugetlb_report_node_meminfo(int, char *); @@ -98,7 +100,6 @@ static inline unsigned long hugetlb_total_pages(void) #define follow_huge_addr(mm, addr, write) ERR_PTR(-EINVAL) #define copy_hugetlb_page_range(src, dst, vma) ({ BUG(); 0; }) #define hugetlb_prefault(mapping, vma) ({ BUG(); 0; }) -#define unmap_hugepage_range(vma, start, end, page) BUG() static inline void hugetlb_report_meminfo(struct seq_file *m) { } @@ -112,13 +113,24 @@ static inline void hugetlb_report_meminfo(struct seq_file *m) #define hugetlb_free_pgd_range(tlb, addr, end, floor, ceiling) ({BUG(); 0; }) #define hugetlb_fault(mm, vma, addr, flags) ({ BUG(); 0; }) #define huge_pte_offset(mm, address) 0 -#define dequeue_hwpoisoned_huge_page(page) 0 +static inline int dequeue_hwpoisoned_huge_page(struct page *page) +{ + return 0; +} + static inline void copy_huge_page(struct page *dst, struct page *src) { } #define hugetlb_change_protection(vma, address, end, newprot) +static inline void __unmap_hugepage_range(struct mmu_gather *tlb, + struct vm_area_struct *vma, unsigned long start, + unsigned long end, struct page *ref_page) +{ + BUG(); +} + #endif /* !CONFIG_HUGETLB_PAGE */ #define HUGETLB_ANON_FILE "anon_hugepage" diff --git a/mm/hugetlb.c b/mm/hugetlb.c index b1e0ed1ea912..e54b695336f9 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -24,8 +24,9 @@ #include #include -#include +#include +#include #include #include #include "internal.h" @@ -2310,30 +2311,26 @@ static int is_hugetlb_entry_hwpoisoned(pte_t pte) return 0; } -void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, - unsigned long end, struct page *ref_page) +void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma, + unsigned long start, unsigned long end, + struct page *ref_page) { + int force_flush = 0; struct mm_struct *mm = vma->vm_mm; unsigned long address; pte_t *ptep; pte_t pte; struct page *page; - struct page *tmp; struct hstate *h = hstate_vma(vma); unsigned long sz = huge_page_size(h); - /* - * A page gathering list, protected by per file i_mmap_mutex. The - * lock is used to avoid list corruption from multiple unmapping - * of the same page since we are using page->lru. - */ - LIST_HEAD(page_list); - WARN_ON(!is_vm_hugetlb_page(vma)); BUG_ON(start & ~huge_page_mask(h)); BUG_ON(end & ~huge_page_mask(h)); + tlb_start_vma(tlb, vma); mmu_notifier_invalidate_range_start(mm, start, end); +again: spin_lock(&mm->page_table_lock); for (address = start; address < end; address += sz) { ptep = huge_pte_offset(mm, address); @@ -2372,30 +2369,45 @@ void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, } pte = huge_ptep_get_and_clear(mm, address, ptep); + tlb_remove_tlb_entry(tlb, ptep, address); if (pte_dirty(pte)) set_page_dirty(page); - list_add(&page->lru, &page_list); + page_remove_rmap(page); + force_flush = !__tlb_remove_page(tlb, page); + if (force_flush) + break; /* Bail out after unmapping reference page if supplied */ if (ref_page) break; } - flush_tlb_range(vma, start, end); spin_unlock(&mm->page_table_lock); - mmu_notifier_invalidate_range_end(mm, start, end); - list_for_each_entry_safe(page, tmp, &page_list, lru) { - page_remove_rmap(page); - list_del(&page->lru); - put_page(page); + /* + * mmu_gather ran out of room to batch pages, we break out of + * the PTE lock to avoid doing the potential expensive TLB invalidate + * and page-free while holding it. + */ + if (force_flush) { + force_flush = 0; + tlb_flush_mmu(tlb); + if (address < end && !ref_page) + goto again; } + mmu_notifier_invalidate_range_end(mm, start, end); + tlb_end_vma(tlb, vma); } void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, unsigned long end, struct page *ref_page) { - mutex_lock(&vma->vm_file->f_mapping->i_mmap_mutex); - __unmap_hugepage_range(vma, start, end, ref_page); - mutex_unlock(&vma->vm_file->f_mapping->i_mmap_mutex); + struct mm_struct *mm; + struct mmu_gather tlb; + + mm = vma->vm_mm; + + tlb_gather_mmu(&tlb, mm, 0); + __unmap_hugepage_range(&tlb, vma, start, end, ref_page); + tlb_finish_mmu(&tlb, start, end); } /* @@ -2440,9 +2452,8 @@ static int unmap_ref_private(struct mm_struct *mm, struct vm_area_struct *vma, * from the time of fork. This would look like data corruption */ if (!is_vma_resv_set(iter_vma, HPAGE_RESV_OWNER)) - __unmap_hugepage_range(iter_vma, - address, address + huge_page_size(h), - page); + unmap_hugepage_range(iter_vma, address, + address + huge_page_size(h), page); } mutex_unlock(&mapping->i_mmap_mutex); diff --git a/mm/memory.c b/mm/memory.c index 91f69459d3e8..59e5bebc2e35 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1343,8 +1343,11 @@ static void unmap_single_vma(struct mmu_gather *tlb, * Since no pte has actually been setup, it is * safe to do nothing in this case. */ - if (vma->vm_file) - unmap_hugepage_range(vma, start, end, NULL); + if (vma->vm_file) { + mutex_lock(&vma->vm_file->f_mapping->i_mmap_mutex); + __unmap_hugepage_range(tlb, vma, start, end, NULL); + mutex_unlock(&vma->vm_file->f_mapping->i_mmap_mutex); + } } else unmap_page_range(tlb, vma, start, end, details); } -- cgit v1.2.3 From 189ebff2894a9d0f4e250dd1e154d282ef0a6779 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 31 Jul 2012 16:42:06 -0700 Subject: hugetlb: simplify migrate_huge_page() Since we migrate only one hugepage, don't use linked list for passing the page around. Directly pass the page that need to be migrated as argument. This also removes the usage of page->lru in the migrate path. Signed-off-by: Aneesh Kumar K.V Reviewed-by: KAMEZAWA Hiroyuki Cc: David Rientjes Cc: Hillf Danton Reviewed-by: Michal Hocko Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/migrate.h | 4 +-- mm/memory-failure.c | 15 +++--------- mm/migrate.c | 65 ++++++++++++++++--------------------------------- 3 files changed, 27 insertions(+), 57 deletions(-) (limited to 'include') diff --git a/include/linux/migrate.h b/include/linux/migrate.h index 855c337b20c3..ce7e6671968b 100644 --- a/include/linux/migrate.h +++ b/include/linux/migrate.h @@ -15,7 +15,7 @@ extern int migrate_page(struct address_space *, extern int migrate_pages(struct list_head *l, new_page_t x, unsigned long private, bool offlining, enum migrate_mode mode); -extern int migrate_huge_pages(struct list_head *l, new_page_t x, +extern int migrate_huge_page(struct page *, new_page_t x, unsigned long private, bool offlining, enum migrate_mode mode); @@ -36,7 +36,7 @@ static inline void putback_lru_pages(struct list_head *l) {} static inline int migrate_pages(struct list_head *l, new_page_t x, unsigned long private, bool offlining, enum migrate_mode mode) { return -ENOSYS; } -static inline int migrate_huge_pages(struct list_head *l, new_page_t x, +static inline int migrate_huge_page(struct page *page, new_page_t x, unsigned long private, bool offlining, enum migrate_mode mode) { return -ENOSYS; } diff --git a/mm/memory-failure.c b/mm/memory-failure.c index 6de0d613bbe6..b04ff2d6f73d 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -1416,7 +1416,6 @@ static int soft_offline_huge_page(struct page *page, int flags) int ret; unsigned long pfn = page_to_pfn(page); struct page *hpage = compound_head(page); - LIST_HEAD(pagelist); ret = get_any_page(page, pfn, flags); if (ret < 0) @@ -1431,24 +1430,18 @@ static int soft_offline_huge_page(struct page *page, int flags) } /* Keep page count to indicate a given hugepage is isolated. */ - - list_add(&hpage->lru, &pagelist); - ret = migrate_huge_pages(&pagelist, new_page, MPOL_MF_MOVE_ALL, false, + ret = migrate_huge_page(hpage, new_page, MPOL_MF_MOVE_ALL, false, MIGRATE_SYNC); + put_page(hpage); if (ret) { - struct page *page1, *page2; - list_for_each_entry_safe(page1, page2, &pagelist, lru) - put_page(page1); - pr_info("soft offline: %#lx: migration failed %d, type %lx\n", pfn, ret, page->flags); - if (ret > 0) - ret = -EIO; return ret; } done: if (!PageHWPoison(hpage)) - atomic_long_add(1 << compound_trans_order(hpage), &mce_bad_pages); + atomic_long_add(1 << compound_trans_order(hpage), + &mce_bad_pages); set_page_hwpoison_huge_page(hpage); dequeue_hwpoisoned_huge_page(hpage); /* keep elevated page count for bad page */ diff --git a/mm/migrate.c b/mm/migrate.c index be26d5cbe56b..fdce3a29fc4c 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -932,15 +932,8 @@ static int unmap_and_move_huge_page(new_page_t get_new_page, if (anon_vma) put_anon_vma(anon_vma); unlock_page(hpage); - out: - if (rc != -EAGAIN) { - list_del(&hpage->lru); - put_page(hpage); - } - put_page(new_hpage); - if (result) { if (rc) *result = rc; @@ -1016,48 +1009,32 @@ out: return nr_failed + retry; } -int migrate_huge_pages(struct list_head *from, - new_page_t get_new_page, unsigned long private, bool offlining, - enum migrate_mode mode) +int migrate_huge_page(struct page *hpage, new_page_t get_new_page, + unsigned long private, bool offlining, + enum migrate_mode mode) { - int retry = 1; - int nr_failed = 0; - int pass = 0; - struct page *page; - struct page *page2; - int rc; - - for (pass = 0; pass < 10 && retry; pass++) { - retry = 0; - - list_for_each_entry_safe(page, page2, from, lru) { + int pass, rc; + + for (pass = 0; pass < 10; pass++) { + rc = unmap_and_move_huge_page(get_new_page, + private, hpage, pass > 2, offlining, + mode); + switch (rc) { + case -ENOMEM: + goto out; + case -EAGAIN: + /* try again */ cond_resched(); - - rc = unmap_and_move_huge_page(get_new_page, - private, page, pass > 2, offlining, - mode); - - switch(rc) { - case -ENOMEM: - goto out; - case -EAGAIN: - retry++; - break; - case 0: - break; - default: - /* Permanent failure */ - nr_failed++; - break; - } + break; + case 0: + goto out; + default: + rc = -EIO; + goto out; } } - rc = 0; out: - if (rc) - return rc; - - return nr_failed + retry; + return rc; } #ifdef CONFIG_NUMA -- cgit v1.2.3 From 0edaecfab218d747d30de4575e911907371e2cd2 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 31 Jul 2012 16:42:07 -0700 Subject: hugetlb: add a list for tracking in-use HugeTLB pages hugepage_activelist will be used to track currently used HugeTLB pages. We need to find the in-use HugeTLB pages to support HugeTLB cgroup removal. On cgroup removal we update the page's HugeTLB cgroup to point to parent cgroup. Signed-off-by: Aneesh Kumar K.V Reviewed-by: KAMEZAWA Hiroyuki Cc: David Rientjes Cc: Hillf Danton Reviewed-by: Michal Hocko Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hugetlb.h | 1 + mm/hugetlb.c | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 0f23c1840c9b..ed550d819044 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -211,6 +211,7 @@ struct hstate { unsigned long resv_huge_pages; unsigned long surplus_huge_pages; unsigned long nr_overcommit_huge_pages; + struct list_head hugepage_activelist; struct list_head hugepage_freelists[MAX_NUMNODES]; unsigned int nr_huge_pages_node[MAX_NUMNODES]; unsigned int free_huge_pages_node[MAX_NUMNODES]; diff --git a/mm/hugetlb.c b/mm/hugetlb.c index e54b695336f9..b5b6e156ca76 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -510,7 +510,7 @@ void copy_huge_page(struct page *dst, struct page *src) static void enqueue_huge_page(struct hstate *h, struct page *page) { int nid = page_to_nid(page); - list_add(&page->lru, &h->hugepage_freelists[nid]); + list_move(&page->lru, &h->hugepage_freelists[nid]); h->free_huge_pages++; h->free_huge_pages_node[nid]++; } @@ -522,7 +522,7 @@ static struct page *dequeue_huge_page_node(struct hstate *h, int nid) if (list_empty(&h->hugepage_freelists[nid])) return NULL; page = list_entry(h->hugepage_freelists[nid].next, struct page, lru); - list_del(&page->lru); + list_move(&page->lru, &h->hugepage_activelist); set_page_refcounted(page); h->free_huge_pages--; h->free_huge_pages_node[nid]--; @@ -626,10 +626,11 @@ static void free_huge_page(struct page *page) page->mapping = NULL; BUG_ON(page_count(page)); BUG_ON(page_mapcount(page)); - INIT_LIST_HEAD(&page->lru); spin_lock(&hugetlb_lock); if (h->surplus_huge_pages_node[nid] && huge_page_order(h) < MAX_ORDER) { + /* remove the page from active list */ + list_del(&page->lru); update_and_free_page(h, page); h->surplus_huge_pages--; h->surplus_huge_pages_node[nid]--; @@ -642,6 +643,7 @@ static void free_huge_page(struct page *page) static void prep_new_huge_page(struct hstate *h, struct page *page, int nid) { + INIT_LIST_HEAD(&page->lru); set_compound_page_dtor(page, free_huge_page); spin_lock(&hugetlb_lock); h->nr_huge_pages++; @@ -890,6 +892,7 @@ static struct page *alloc_buddy_huge_page(struct hstate *h, int nid) spin_lock(&hugetlb_lock); if (page) { + INIT_LIST_HEAD(&page->lru); r_nid = page_to_nid(page); set_compound_page_dtor(page, free_huge_page); /* @@ -994,7 +997,6 @@ retry: list_for_each_entry_safe(page, tmp, &surplus_list, lru) { if ((--needed) < 0) break; - list_del(&page->lru); /* * This page is now managed by the hugetlb allocator and has * no users -- drop the buddy allocator's reference. @@ -1009,7 +1011,6 @@ free: /* Free unnecessary surplus pages to the buddy allocator */ if (!list_empty(&surplus_list)) { list_for_each_entry_safe(page, tmp, &surplus_list, lru) { - list_del(&page->lru); put_page(page); } } @@ -1909,6 +1910,7 @@ void __init hugetlb_add_hstate(unsigned order) h->free_huge_pages = 0; for (i = 0; i < MAX_NUMNODES; ++i) INIT_LIST_HEAD(&h->hugepage_freelists[i]); + INIT_LIST_HEAD(&h->hugepage_activelist); h->next_nid_to_alloc = first_node(node_states[N_HIGH_MEMORY]); h->next_nid_to_free = first_node(node_states[N_HIGH_MEMORY]); snprintf(h->name, HSTATE_NAME_LEN, "hugepages-%lukB", -- cgit v1.2.3 From c3f38a38715e9b4e7b1dda6840a1375f6894744d Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 31 Jul 2012 16:42:10 -0700 Subject: hugetlb: make some static variables global We will use them later in hugetlb_cgroup.c Signed-off-by: Aneesh Kumar K.V Cc: David Rientjes Acked-by: KAMEZAWA Hiroyuki Cc: Hillf Danton Reviewed-by: Michal Hocko Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hugetlb.h | 5 +++++ mm/hugetlb.c | 7 ++----- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index ed550d819044..3d677dd41898 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -21,6 +21,11 @@ struct hugepage_subpool { long max_hpages, used_hpages; }; +extern spinlock_t hugetlb_lock; +extern int hugetlb_max_hstate __read_mostly; +#define for_each_hstate(h) \ + for ((h) = hstates; (h) < &hstates[hugetlb_max_hstate]; (h)++) + struct hugepage_subpool *hugepage_new_subpool(long nr_blocks); void hugepage_put_subpool(struct hugepage_subpool *spool); diff --git a/mm/hugetlb.c b/mm/hugetlb.c index b5b6e156ca76..d5971597736b 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -35,7 +35,7 @@ const unsigned long hugetlb_zero = 0, hugetlb_infinity = ~0UL; static gfp_t htlb_alloc_mask = GFP_HIGHUSER; unsigned long hugepages_treat_as_movable; -static int hugetlb_max_hstate; +int hugetlb_max_hstate __read_mostly; unsigned int default_hstate_idx; struct hstate hstates[HUGE_MAX_HSTATE]; @@ -46,13 +46,10 @@ static struct hstate * __initdata parsed_hstate; static unsigned long __initdata default_hstate_max_huge_pages; static unsigned long __initdata default_hstate_size; -#define for_each_hstate(h) \ - for ((h) = hstates; (h) < &hstates[hugetlb_max_hstate]; (h)++) - /* * Protects updates to hugepage_freelists, nr_huge_pages, and free_huge_pages */ -static DEFINE_SPINLOCK(hugetlb_lock); +DEFINE_SPINLOCK(hugetlb_lock); static inline void unlock_or_release_subpool(struct hugepage_subpool *spool) { -- cgit v1.2.3 From 2bc64a2046975410505bb119bba32705892b9255 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 31 Jul 2012 16:42:12 -0700 Subject: mm/hugetlb: add new HugeTLB cgroup Implement a new controller that allows us to control HugeTLB allocations. The extension allows to limit the HugeTLB usage per control group and enforces the controller limit during page fault. Since HugeTLB doesn't support page reclaim, enforcing the limit at page fault time implies that, the application will get SIGBUS signal if it tries to access HugeTLB pages beyond its limit. This requires the application to know beforehand how much HugeTLB pages it would require for its use. The charge/uncharge calls will be added to HugeTLB code in later patch. Support for cgroup removal will be added in later patches. [akpm@linux-foundation.org: s/CONFIG_CGROUP_HUGETLB_RES_CTLR/CONFIG_MEMCG_HUGETLB/g] [akpm@linux-foundation.org: s/CONFIG_MEMCG_HUGETLB/CONFIG_CGROUP_HUGETLB/g] Reviewed-by: KAMEZAWA Hiroyuki Signed-off-by: Aneesh Kumar K.V Cc: David Rientjes Cc: Hillf Danton Reviewed-by: Michal Hocko Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/cgroup_subsys.h | 6 +++ include/linux/hugetlb_cgroup.h | 37 +++++++++++++ init/Kconfig | 15 ++++++ mm/Makefile | 1 + mm/hugetlb_cgroup.c | 120 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 179 insertions(+) create mode 100644 include/linux/hugetlb_cgroup.h create mode 100644 mm/hugetlb_cgroup.c (limited to 'include') diff --git a/include/linux/cgroup_subsys.h b/include/linux/cgroup_subsys.h index 0bd390ce98b2..5b41ce079024 100644 --- a/include/linux/cgroup_subsys.h +++ b/include/linux/cgroup_subsys.h @@ -72,3 +72,9 @@ SUBSYS(net_prio) #endif /* */ + +#ifdef CONFIG_CGROUP_HUGETLB +SUBSYS(hugetlb) +#endif + +/* */ diff --git a/include/linux/hugetlb_cgroup.h b/include/linux/hugetlb_cgroup.h new file mode 100644 index 000000000000..f19889e56b47 --- /dev/null +++ b/include/linux/hugetlb_cgroup.h @@ -0,0 +1,37 @@ +/* + * Copyright IBM Corporation, 2012 + * Author Aneesh Kumar K.V + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef _LINUX_HUGETLB_CGROUP_H +#define _LINUX_HUGETLB_CGROUP_H + +#include + +struct hugetlb_cgroup; + +#ifdef CONFIG_CGROUP_HUGETLB +static inline bool hugetlb_cgroup_disabled(void) +{ + if (hugetlb_subsys.disabled) + return true; + return false; +} + +#else +static inline bool hugetlb_cgroup_disabled(void) +{ + return true; +} + +#endif /* CONFIG_MEM_RES_CTLR_HUGETLB */ +#endif diff --git a/init/Kconfig b/init/Kconfig index b3f55f15e107..72437760e90e 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -751,6 +751,21 @@ config CGROUP_MEM_RES_CTLR_KMEM the kmem extension can use it to guarantee that no group of processes will ever exhaust kernel resources alone. +config CGROUP_HUGETLB + bool "HugeTLB Resource Controller for Control Groups" + depends on RESOURCE_COUNTERS && HUGETLB_PAGE && EXPERIMENTAL + default n + help + Provides a cgroup Resource Controller for HugeTLB pages. + When you enable this, you can put a per cgroup limit on HugeTLB usage. + The limit is enforced during page fault. Since HugeTLB doesn't + support page reclaim, enforcing the limit at page fault time implies + that, the application will get SIGBUS signal if it tries to access + HugeTLB pages beyond its limit. This requires the application to know + beforehand how much HugeTLB pages it would require for its use. The + control group is tracked in the third page lru pointer. This means + that we cannot use the controller with huge page less than 3 pages. + config CGROUP_PERF bool "Enable perf_event per-cpu per-container group (cgroup) monitoring" depends on PERF_EVENTS && CGROUPS diff --git a/mm/Makefile b/mm/Makefile index 8e81fe263c94..fd6fc1c1966c 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_MIGRATION) += migrate.o obj-$(CONFIG_QUICKLIST) += quicklist.o obj-$(CONFIG_TRANSPARENT_HUGEPAGE) += huge_memory.o obj-$(CONFIG_CGROUP_MEM_RES_CTLR) += memcontrol.o page_cgroup.o +obj-$(CONFIG_CGROUP_HUGETLB) += hugetlb_cgroup.o obj-$(CONFIG_MEMORY_FAILURE) += memory-failure.o obj-$(CONFIG_HWPOISON_INJECT) += hwpoison-inject.o obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c new file mode 100644 index 000000000000..0d1a66e9039b --- /dev/null +++ b/mm/hugetlb_cgroup.c @@ -0,0 +1,120 @@ +/* + * + * Copyright IBM Corporation, 2012 + * Author Aneesh Kumar K.V + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#include +#include +#include +#include + +struct hugetlb_cgroup { + struct cgroup_subsys_state css; + /* + * the counter to account for hugepages from hugetlb. + */ + struct res_counter hugepage[HUGE_MAX_HSTATE]; +}; + +struct cgroup_subsys hugetlb_subsys __read_mostly; +static struct hugetlb_cgroup *root_h_cgroup __read_mostly; + +static inline +struct hugetlb_cgroup *hugetlb_cgroup_from_css(struct cgroup_subsys_state *s) +{ + return container_of(s, struct hugetlb_cgroup, css); +} + +static inline +struct hugetlb_cgroup *hugetlb_cgroup_from_cgroup(struct cgroup *cgroup) +{ + return hugetlb_cgroup_from_css(cgroup_subsys_state(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)); +} + +static inline bool hugetlb_cgroup_is_root(struct hugetlb_cgroup *h_cg) +{ + return (h_cg == root_h_cgroup); +} + +static inline struct hugetlb_cgroup *parent_hugetlb_cgroup(struct cgroup *cg) +{ + if (!cg->parent) + return NULL; + return hugetlb_cgroup_from_cgroup(cg->parent); +} + +static inline bool hugetlb_cgroup_have_usage(struct cgroup *cg) +{ + int idx; + struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cg); + + for (idx = 0; idx < hugetlb_max_hstate; idx++) { + if ((res_counter_read_u64(&h_cg->hugepage[idx], RES_USAGE)) > 0) + return true; + } + return false; +} + +static struct cgroup_subsys_state *hugetlb_cgroup_create(struct cgroup *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); + for (idx = 0; idx < HUGE_MAX_HSTATE; idx++) + res_counter_init(&h_cgroup->hugepage[idx], + &parent_h_cgroup->hugepage[idx]); + } else { + root_h_cgroup = h_cgroup; + for (idx = 0; idx < HUGE_MAX_HSTATE; idx++) + res_counter_init(&h_cgroup->hugepage[idx], NULL); + } + return &h_cgroup->css; +} + +static void hugetlb_cgroup_destroy(struct cgroup *cgroup) +{ + struct hugetlb_cgroup *h_cgroup; + + h_cgroup = hugetlb_cgroup_from_cgroup(cgroup); + kfree(h_cgroup); +} + +static int hugetlb_cgroup_pre_destroy(struct cgroup *cgroup) +{ + /* We will add the cgroup removal support in later patches */ + return -EBUSY; +} + +struct cgroup_subsys hugetlb_subsys = { + .name = "hugetlb", + .create = hugetlb_cgroup_create, + .pre_destroy = hugetlb_cgroup_pre_destroy, + .destroy = hugetlb_cgroup_destroy, + .subsys_id = hugetlb_subsys_id, +}; -- cgit v1.2.3 From 9dd540e23111d8884773ab942a736f3aba4040d4 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 31 Jul 2012 16:42:15 -0700 Subject: hugetlb/cgroup: add the cgroup pointer to page lru Add the hugetlb cgroup pointer to 3rd page lru.next. This limit the usage to hugetlb cgroup to only hugepages with 3 or more normal pages. I guess that is an acceptable limitation. Signed-off-by: Aneesh Kumar K.V Cc: David Rientjes Acked-by: KAMEZAWA Hiroyuki Cc: Hillf Danton Reviewed-by: Michal Hocko Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hugetlb_cgroup.h | 37 +++++++++++++++++++++++++++++++++++++ mm/hugetlb.c | 4 ++++ 2 files changed, 41 insertions(+) (limited to 'include') diff --git a/include/linux/hugetlb_cgroup.h b/include/linux/hugetlb_cgroup.h index f19889e56b47..e5451a3b4ebc 100644 --- a/include/linux/hugetlb_cgroup.h +++ b/include/linux/hugetlb_cgroup.h @@ -18,8 +18,34 @@ #include struct hugetlb_cgroup; +/* + * Minimum page order trackable by hugetlb cgroup. + * At least 3 pages are necessary for all the tracking information. + */ +#define HUGETLB_CGROUP_MIN_ORDER 2 #ifdef CONFIG_CGROUP_HUGETLB + +static inline struct hugetlb_cgroup *hugetlb_cgroup_from_page(struct page *page) +{ + VM_BUG_ON(!PageHuge(page)); + + if (compound_order(page) < HUGETLB_CGROUP_MIN_ORDER) + return NULL; + return (struct hugetlb_cgroup *)page[2].lru.next; +} + +static inline +int set_hugetlb_cgroup(struct page *page, struct hugetlb_cgroup *h_cg) +{ + VM_BUG_ON(!PageHuge(page)); + + if (compound_order(page) < HUGETLB_CGROUP_MIN_ORDER) + return -1; + page[2].lru.next = (void *)h_cg; + return 0; +} + static inline bool hugetlb_cgroup_disabled(void) { if (hugetlb_subsys.disabled) @@ -28,6 +54,17 @@ static inline bool hugetlb_cgroup_disabled(void) } #else +static inline struct hugetlb_cgroup *hugetlb_cgroup_from_page(struct page *page) +{ + return NULL; +} + +static inline +int set_hugetlb_cgroup(struct page *page, struct hugetlb_cgroup *h_cg) +{ + return 0; +} + static inline bool hugetlb_cgroup_disabled(void) { return true; diff --git a/mm/hugetlb.c b/mm/hugetlb.c index d5971597736b..efe29b53daff 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -28,6 +28,7 @@ #include #include +#include #include #include "internal.h" @@ -591,6 +592,7 @@ static void update_and_free_page(struct hstate *h, struct page *page) 1 << PG_active | 1 << PG_reserved | 1 << PG_private | 1 << PG_writeback); } + VM_BUG_ON(hugetlb_cgroup_from_page(page)); set_compound_page_dtor(page, NULL); set_page_refcounted(page); arch_release_hugepage(page); @@ -643,6 +645,7 @@ static void prep_new_huge_page(struct hstate *h, struct page *page, int nid) INIT_LIST_HEAD(&page->lru); set_compound_page_dtor(page, free_huge_page); spin_lock(&hugetlb_lock); + set_hugetlb_cgroup(page, NULL); h->nr_huge_pages++; h->nr_huge_pages_node[nid]++; spin_unlock(&hugetlb_lock); @@ -892,6 +895,7 @@ static struct page *alloc_buddy_huge_page(struct hstate *h, int nid) INIT_LIST_HEAD(&page->lru); r_nid = page_to_nid(page); set_compound_page_dtor(page, free_huge_page); + set_hugetlb_cgroup(page, NULL); /* * We incremented the global counters already */ -- cgit v1.2.3 From 6d76dcf40405144a448040a350fd214ddc243d5e Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 31 Jul 2012 16:42:18 -0700 Subject: hugetlb/cgroup: add charge/uncharge routines for hugetlb cgroup Add the charge and uncharge routines for hugetlb cgroup. We do cgroup charging in page alloc and uncharge in compound page destructor. Assigning page's hugetlb cgroup is protected by hugetlb_lock. [liwp@linux.vnet.ibm.com: add huge_page_order check to avoid incorrect uncharge] Signed-off-by: Aneesh Kumar K.V Cc: David Rientjes Acked-by: KAMEZAWA Hiroyuki Cc: Hillf Danton Cc: Michal Hocko Cc: KOSAKI Motohiro Signed-off-by: Aneesh Kumar K.V Signed-off-by: Wanpeng Li Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hugetlb_cgroup.h | 38 ++++++++++++++++++++ mm/hugetlb.c | 16 ++++++++- mm/hugetlb_cgroup.c | 80 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/hugetlb_cgroup.h b/include/linux/hugetlb_cgroup.h index e5451a3b4ebc..7d3fde996be3 100644 --- a/include/linux/hugetlb_cgroup.h +++ b/include/linux/hugetlb_cgroup.h @@ -53,6 +53,16 @@ static inline bool hugetlb_cgroup_disabled(void) return false; } +extern int hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages, + struct hugetlb_cgroup **ptr); +extern void hugetlb_cgroup_commit_charge(int idx, unsigned long nr_pages, + struct hugetlb_cgroup *h_cg, + struct page *page); +extern void hugetlb_cgroup_uncharge_page(int idx, unsigned long nr_pages, + struct page *page); +extern void hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages, + struct hugetlb_cgroup *h_cg); + #else static inline struct hugetlb_cgroup *hugetlb_cgroup_from_page(struct page *page) { @@ -70,5 +80,33 @@ static inline bool hugetlb_cgroup_disabled(void) return true; } +static inline int +hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages, + struct hugetlb_cgroup **ptr) +{ + return 0; +} + +static inline void +hugetlb_cgroup_commit_charge(int idx, unsigned long nr_pages, + struct hugetlb_cgroup *h_cg, + struct page *page) +{ + return; +} + +static inline void +hugetlb_cgroup_uncharge_page(int idx, unsigned long nr_pages, struct page *page) +{ + return; +} + +static inline void +hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages, + struct hugetlb_cgroup *h_cg) +{ + return; +} + #endif /* CONFIG_MEM_RES_CTLR_HUGETLB */ #endif diff --git a/mm/hugetlb.c b/mm/hugetlb.c index efe29b53daff..16a0f32c4820 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -627,6 +627,8 @@ static void free_huge_page(struct page *page) BUG_ON(page_mapcount(page)); spin_lock(&hugetlb_lock); + hugetlb_cgroup_uncharge_page(hstate_index(h), + pages_per_huge_page(h), page); if (h->surplus_huge_pages_node[nid] && huge_page_order(h) < MAX_ORDER) { /* remove the page from active list */ list_del(&page->lru); @@ -1115,7 +1117,10 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, struct hstate *h = hstate_vma(vma); struct page *page; long chg; + int ret, idx; + struct hugetlb_cgroup *h_cg; + idx = hstate_index(h); /* * Processes that did not create the mapping will have no * reserves and will not have accounted against subpool @@ -1131,6 +1136,11 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, if (hugepage_subpool_get_pages(spool, chg)) return ERR_PTR(-ENOSPC); + ret = hugetlb_cgroup_charge_cgroup(idx, pages_per_huge_page(h), &h_cg); + if (ret) { + hugepage_subpool_put_pages(spool, chg); + return ERR_PTR(-ENOSPC); + } spin_lock(&hugetlb_lock); page = dequeue_huge_page_vma(h, vma, addr, avoid_reserve); spin_unlock(&hugetlb_lock); @@ -1138,6 +1148,9 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, if (!page) { page = alloc_buddy_huge_page(h, NUMA_NO_NODE); if (!page) { + hugetlb_cgroup_uncharge_cgroup(idx, + pages_per_huge_page(h), + h_cg); hugepage_subpool_put_pages(spool, chg); return ERR_PTR(-ENOSPC); } @@ -1146,7 +1159,8 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, set_page_private(page, (unsigned long)spool); vma_commit_reservation(h, vma, addr); - + /* update page cgroup details */ + hugetlb_cgroup_commit_charge(idx, pages_per_huge_page(h), h_cg, page); return page; } diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c index 0d1a66e9039b..63e04cfa437d 100644 --- a/mm/hugetlb_cgroup.c +++ b/mm/hugetlb_cgroup.c @@ -111,6 +111,86 @@ static int hugetlb_cgroup_pre_destroy(struct cgroup *cgroup) return -EBUSY; } +int hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages, + struct hugetlb_cgroup **ptr) +{ + int ret = 0; + struct res_counter *fail_res; + struct hugetlb_cgroup *h_cg = NULL; + unsigned long csize = nr_pages * PAGE_SIZE; + + if (hugetlb_cgroup_disabled()) + goto done; + /* + * We don't charge any cgroup if the compound page have less + * than 3 pages. + */ + if (huge_page_order(&hstates[idx]) < HUGETLB_CGROUP_MIN_ORDER) + goto done; +again: + rcu_read_lock(); + h_cg = hugetlb_cgroup_from_task(current); + if (!css_tryget(&h_cg->css)) { + rcu_read_unlock(); + goto again; + } + rcu_read_unlock(); + + ret = res_counter_charge(&h_cg->hugepage[idx], csize, &fail_res); + css_put(&h_cg->css); +done: + *ptr = h_cg; + return ret; +} + +void hugetlb_cgroup_commit_charge(int idx, unsigned long nr_pages, + struct hugetlb_cgroup *h_cg, + struct page *page) +{ + if (hugetlb_cgroup_disabled() || !h_cg) + return; + + spin_lock(&hugetlb_lock); + set_hugetlb_cgroup(page, h_cg); + spin_unlock(&hugetlb_lock); + return; +} + +/* + * Should be called with hugetlb_lock held + */ +void hugetlb_cgroup_uncharge_page(int idx, unsigned long nr_pages, + struct page *page) +{ + struct hugetlb_cgroup *h_cg; + unsigned long csize = nr_pages * PAGE_SIZE; + + if (hugetlb_cgroup_disabled()) + return; + VM_BUG_ON(!spin_is_locked(&hugetlb_lock)); + h_cg = hugetlb_cgroup_from_page(page); + if (unlikely(!h_cg)) + return; + set_hugetlb_cgroup(page, NULL); + res_counter_uncharge(&h_cg->hugepage[idx], csize); + return; +} + +void hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages, + struct hugetlb_cgroup *h_cg) +{ + unsigned long csize = nr_pages * PAGE_SIZE; + + if (hugetlb_cgroup_disabled() || !h_cg) + return; + + if (huge_page_order(&hstates[idx]) < HUGETLB_CGROUP_MIN_ORDER) + return; + + res_counter_uncharge(&h_cg->hugepage[idx], csize); + return; +} + struct cgroup_subsys hugetlb_subsys = { .name = "hugetlb", .create = hugetlb_cgroup_create, -- cgit v1.2.3 From abb8206cb07734d0b7bf033c715995d6371a94c3 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 31 Jul 2012 16:42:24 -0700 Subject: hugetlb/cgroup: add hugetlb cgroup control files Add the control files for hugetlb controller [akpm@linux-foundation.org: s/CONFIG_CGROUP_HUGETLB_RES_CTLR/CONFIG_MEMCG_HUGETLB/g] [akpm@linux-foundation.org: s/CONFIG_MEMCG_HUGETLB/CONFIG_CGROUP_HUGETLB/] Signed-off-by: Aneesh Kumar K.V Cc: David Rientjes Acked-by: KAMEZAWA Hiroyuki Cc: Hillf Danton Reviewed-by: Michal Hocko Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hugetlb.h | 5 ++ include/linux/hugetlb_cgroup.h | 6 ++ mm/hugetlb.c | 8 +++ mm/hugetlb_cgroup.c | 129 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 148 insertions(+) (limited to 'include') diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 3d677dd41898..f9db20bfa9fc 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -4,6 +4,7 @@ #include #include #include +#include struct ctl_table; struct user_struct; @@ -221,6 +222,10 @@ struct hstate { unsigned int nr_huge_pages_node[MAX_NUMNODES]; unsigned int free_huge_pages_node[MAX_NUMNODES]; unsigned int surplus_huge_pages_node[MAX_NUMNODES]; +#ifdef CONFIG_CGROUP_HUGETLB + /* cgroup control files */ + struct cftype cgroup_files[5]; +#endif char name[HSTATE_NAME_LEN]; }; diff --git a/include/linux/hugetlb_cgroup.h b/include/linux/hugetlb_cgroup.h index 7d3fde996be3..73f1e600fc12 100644 --- a/include/linux/hugetlb_cgroup.h +++ b/include/linux/hugetlb_cgroup.h @@ -62,6 +62,7 @@ extern void hugetlb_cgroup_uncharge_page(int idx, unsigned long nr_pages, struct page *page); extern void hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages, struct hugetlb_cgroup *h_cg); +extern int hugetlb_cgroup_file_init(int idx) __init; #else static inline struct hugetlb_cgroup *hugetlb_cgroup_from_page(struct page *page) @@ -108,5 +109,10 @@ hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages, return; } +static inline int __init hugetlb_cgroup_file_init(int idx) +{ + return 0; +} + #endif /* CONFIG_MEM_RES_CTLR_HUGETLB */ #endif diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 16a0f32c4820..c57740bb203a 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "internal.h" const unsigned long hugetlb_zero = 0, hugetlb_infinity = ~0UL; @@ -1930,6 +1931,13 @@ void __init hugetlb_add_hstate(unsigned order) h->next_nid_to_free = first_node(node_states[N_HIGH_MEMORY]); snprintf(h->name, HSTATE_NAME_LEN, "hugepages-%lukB", huge_page_size(h)/1024); + /* + * Add cgroup control files only if the huge page consists + * of more than two normal pages. This is because we use + * page[2].lru.next for storing cgoup details. + */ + if (order >= HUGETLB_CGROUP_MIN_ORDER) + hugetlb_cgroup_file_init(hugetlb_max_hstate - 1); parsed_hstate = h; } diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c index bc518bedea98..d1ca1196e62f 100644 --- a/mm/hugetlb_cgroup.c +++ b/mm/hugetlb_cgroup.c @@ -26,6 +26,10 @@ struct hugetlb_cgroup { struct res_counter hugepage[HUGE_MAX_HSTATE]; }; +#define MEMFILE_PRIVATE(x, val) (((x) << 16) | (val)) +#define MEMFILE_IDX(val) (((val) >> 16) & 0xffff) +#define MEMFILE_ATTR(val) ((val) & 0xffff) + struct cgroup_subsys hugetlb_subsys __read_mostly; static struct hugetlb_cgroup *root_h_cgroup __read_mostly; @@ -257,6 +261,131 @@ 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) +{ + u64 val; + char str[64]; + int idx, name, len; + struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cgroup); + + idx = MEMFILE_IDX(cft->private); + name = MEMFILE_ATTR(cft->private); + + val = res_counter_read_u64(&h_cg->hugepage[idx], name); + len = scnprintf(str, sizeof(str), "%llu\n", (unsigned long long)val); + return simple_read_from_buffer(buf, nbytes, ppos, str, len); +} + +static int hugetlb_cgroup_write(struct cgroup *cgroup, struct cftype *cft, + const char *buffer) +{ + int idx, name, ret; + unsigned long long val; + struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cgroup); + + idx = MEMFILE_IDX(cft->private); + name = MEMFILE_ATTR(cft->private); + + switch (name) { + case RES_LIMIT: + if (hugetlb_cgroup_is_root(h_cg)) { + /* Can't set limit on root */ + ret = -EINVAL; + break; + } + /* This function does all necessary parse...reuse it */ + ret = res_counter_memparse_write_strategy(buffer, &val); + if (ret) + break; + ret = res_counter_set_limit(&h_cg->hugepage[idx], val); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int hugetlb_cgroup_reset(struct cgroup *cgroup, unsigned int event) +{ + int idx, name, ret = 0; + struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cgroup); + + idx = MEMFILE_IDX(event); + name = MEMFILE_ATTR(event); + + switch (name) { + case RES_MAX_USAGE: + res_counter_reset_max(&h_cg->hugepage[idx]); + break; + case RES_FAILCNT: + res_counter_reset_failcnt(&h_cg->hugepage[idx]); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static char *mem_fmt(char *buf, int size, unsigned long hsize) +{ + if (hsize >= (1UL << 30)) + snprintf(buf, size, "%luGB", hsize >> 30); + else if (hsize >= (1UL << 20)) + snprintf(buf, size, "%luMB", hsize >> 20); + else + snprintf(buf, size, "%luKB", hsize >> 10); + return buf; +} + +int __init hugetlb_cgroup_file_init(int idx) +{ + char buf[32]; + struct cftype *cft; + struct hstate *h = &hstates[idx]; + + /* format the size */ + mem_fmt(buf, 32, huge_page_size(h)); + + /* Add the limit file */ + cft = &h->cgroup_files[0]; + snprintf(cft->name, MAX_CFTYPE_NAME, "%s.limit_in_bytes", buf); + cft->private = MEMFILE_PRIVATE(idx, RES_LIMIT); + cft->read = hugetlb_cgroup_read; + cft->write_string = hugetlb_cgroup_write; + + /* Add the usage file */ + cft = &h->cgroup_files[1]; + snprintf(cft->name, MAX_CFTYPE_NAME, "%s.usage_in_bytes", buf); + cft->private = MEMFILE_PRIVATE(idx, RES_USAGE); + cft->read = hugetlb_cgroup_read; + + /* Add the MAX usage file */ + cft = &h->cgroup_files[2]; + snprintf(cft->name, MAX_CFTYPE_NAME, "%s.max_usage_in_bytes", buf); + cft->private = MEMFILE_PRIVATE(idx, RES_MAX_USAGE); + cft->trigger = hugetlb_cgroup_reset; + cft->read = hugetlb_cgroup_read; + + /* Add the failcntfile */ + cft = &h->cgroup_files[3]; + snprintf(cft->name, MAX_CFTYPE_NAME, "%s.failcnt", buf); + cft->private = MEMFILE_PRIVATE(idx, RES_FAILCNT); + cft->trigger = hugetlb_cgroup_reset; + cft->read = hugetlb_cgroup_read; + + /* NULL terminate the last cft */ + cft = &h->cgroup_files[4]; + memset(cft, 0, sizeof(*cft)); + + WARN_ON(cgroup_add_cftypes(&hugetlb_subsys, h->cgroup_files)); + + return 0; +} + struct cgroup_subsys hugetlb_subsys = { .name = "hugetlb", .create = hugetlb_cgroup_create, -- cgit v1.2.3 From 8e6ac7fab374816de9a8b0a8fbb02ef761a30ff4 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 31 Jul 2012 16:42:27 -0700 Subject: hugetlb/cgroup: migrate hugetlb cgroup info from oldpage to new page during migration With HugeTLB pages, hugetlb cgroup is uncharged in compound page destructor. Since we are holding a hugepage reference, we can be sure that old page won't get uncharged till the last put_page(). Signed-off-by: Aneesh Kumar K.V Cc: David Rientjes Acked-by: KAMEZAWA Hiroyuki Cc: Hillf Danton Cc: Michal Hocko Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hugetlb_cgroup.h | 8 ++++++++ mm/hugetlb_cgroup.c | 20 ++++++++++++++++++++ mm/migrate.c | 5 +++++ 3 files changed, 33 insertions(+) (limited to 'include') diff --git a/include/linux/hugetlb_cgroup.h b/include/linux/hugetlb_cgroup.h index 73f1e600fc12..d73878c694b3 100644 --- a/include/linux/hugetlb_cgroup.h +++ b/include/linux/hugetlb_cgroup.h @@ -63,6 +63,8 @@ extern void hugetlb_cgroup_uncharge_page(int idx, unsigned long nr_pages, extern void hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages, struct hugetlb_cgroup *h_cg); extern int hugetlb_cgroup_file_init(int idx) __init; +extern void hugetlb_cgroup_migrate(struct page *oldhpage, + struct page *newhpage); #else static inline struct hugetlb_cgroup *hugetlb_cgroup_from_page(struct page *page) @@ -114,5 +116,11 @@ static inline int __init hugetlb_cgroup_file_init(int idx) return 0; } +static inline void hugetlb_cgroup_migrate(struct page *oldhpage, + struct page *newhpage) +{ + return; +} + #endif /* CONFIG_MEM_RES_CTLR_HUGETLB */ #endif diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c index d1ca1196e62f..680e4819e077 100644 --- a/mm/hugetlb_cgroup.c +++ b/mm/hugetlb_cgroup.c @@ -386,6 +386,26 @@ int __init hugetlb_cgroup_file_init(int idx) return 0; } +void hugetlb_cgroup_migrate(struct page *oldhpage, struct page *newhpage) +{ + struct hugetlb_cgroup *h_cg; + + if (hugetlb_cgroup_disabled()) + return; + + VM_BUG_ON(!PageHuge(oldhpage)); + spin_lock(&hugetlb_lock); + h_cg = hugetlb_cgroup_from_page(oldhpage); + set_hugetlb_cgroup(oldhpage, NULL); + cgroup_exclude_rmdir(&h_cg->css); + + /* move the h_cg details to new cgroup */ + set_hugetlb_cgroup(newhpage, h_cg); + spin_unlock(&hugetlb_lock); + cgroup_release_and_wakeup_rmdir(&h_cg->css); + return; +} + struct cgroup_subsys hugetlb_subsys = { .name = "hugetlb", .create = hugetlb_cgroup_create, diff --git a/mm/migrate.c b/mm/migrate.c index fdce3a29fc4c..6c37c51565e5 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -931,6 +932,10 @@ static int unmap_and_move_huge_page(new_page_t get_new_page, if (anon_vma) put_anon_vma(anon_vma); + + if (!rc) + hugetlb_cgroup_migrate(hpage, new_hpage); + unlock_page(hpage); out: put_page(new_hpage); -- cgit v1.2.3 From c59e26104e3e0e952cd7d63e79cd71ee5a9ec25a Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Tue, 31 Jul 2012 16:42:49 -0700 Subject: mm/compaction: cleanup on compaction_deferred When CONFIG_COMPACTION is enabled, compaction_deferred() tries to recalculate the deferred limit again, which isn't necessary. When CONFIG_COMPACTION is disabled, compaction_deferred() should return "true" or "false" since it has "bool" for its return value. Signed-off-by: Gavin Shan Acked-by: Minchan Kim Acked-by: Johannes Weiner Acked-by: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/compaction.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/compaction.h b/include/linux/compaction.h index 51a90b7f2d60..133ddcf83397 100644 --- a/include/linux/compaction.h +++ b/include/linux/compaction.h @@ -58,7 +58,7 @@ static inline bool compaction_deferred(struct zone *zone, int order) if (++zone->compact_considered > defer_limit) zone->compact_considered = defer_limit; - return zone->compact_considered < (1UL << zone->compact_defer_shift); + return zone->compact_considered < defer_limit; } #else @@ -85,7 +85,7 @@ static inline void defer_compaction(struct zone *zone, int order) static inline bool compaction_deferred(struct zone *zone, int order) { - return 1; + return true; } #endif /* CONFIG_COMPACTION */ -- cgit v1.2.3 From c255a458055e459f65eb7b7f51dc5dbdd0caf1d8 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Tue, 31 Jul 2012 16:43:02 -0700 Subject: memcg: rename config variables Sanity: CONFIG_CGROUP_MEM_RES_CTLR -> CONFIG_MEMCG CONFIG_CGROUP_MEM_RES_CTLR_SWAP -> CONFIG_MEMCG_SWAP CONFIG_CGROUP_MEM_RES_CTLR_SWAP_ENABLED -> CONFIG_MEMCG_SWAP_ENABLED CONFIG_CGROUP_MEM_RES_CTLR_KMEM -> CONFIG_MEMCG_KMEM [mhocko@suse.cz: fix missed bits] Cc: Glauber Costa Acked-by: Michal Hocko Cc: Johannes Weiner Cc: KAMEZAWA Hiroyuki Cc: Hugh Dickins Cc: Tejun Heo Cc: Aneesh Kumar K.V Cc: David Rientjes Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/cgroups/memory.txt | 10 +++++----- arch/powerpc/configs/chroma_defconfig | 4 ++-- arch/s390/defconfig | 2 +- arch/sh/configs/apsh4ad0a_defconfig | 2 +- arch/sh/configs/sdk7786_defconfig | 4 ++-- arch/sh/configs/se7206_defconfig | 2 +- arch/sh/configs/shx3_defconfig | 2 +- arch/sh/configs/urquell_defconfig | 4 ++-- arch/tile/configs/tilegx_defconfig | 4 ++-- arch/tile/configs/tilepro_defconfig | 4 ++-- arch/um/defconfig | 8 ++++---- include/linux/cgroup_subsys.h | 2 +- include/linux/memcontrol.h | 14 +++++++------- include/linux/mmzone.h | 8 ++++---- include/linux/page_cgroup.h | 10 +++++----- include/linux/sched.h | 2 +- include/linux/swap.h | 6 +++--- include/net/sock.h | 4 ++-- init/Kconfig | 14 +++++++------- kernel/fork.c | 2 +- mm/Makefile | 2 +- mm/hwpoison-inject.c | 2 +- mm/memcontrol.c | 20 ++++++++++---------- mm/memory-failure.c | 2 +- mm/mmzone.c | 2 +- mm/oom_kill.c | 2 +- mm/page_cgroup.c | 2 +- mm/vmscan.c | 4 ++-- net/core/sock.c | 2 +- net/ipv4/Makefile | 2 +- net/ipv4/sysctl_net_ipv4.c | 4 ++-- net/ipv4/tcp_ipv4.c | 2 +- net/ipv6/tcp_ipv6.c | 2 +- 33 files changed, 78 insertions(+), 78 deletions(-) (limited to 'include') diff --git a/Documentation/cgroups/memory.txt b/Documentation/cgroups/memory.txt index dd88540bb995..672676ac9615 100644 --- a/Documentation/cgroups/memory.txt +++ b/Documentation/cgroups/memory.txt @@ -187,12 +187,12 @@ the cgroup that brought it in -- this will happen on memory pressure). But see section 8.2: when moving a task to another cgroup, its pages may be recharged to the new cgroup, if move_charge_at_immigrate has been chosen. -Exception: If CONFIG_CGROUP_CGROUP_MEM_RES_CTLR_SWAP is not used. +Exception: If CONFIG_CGROUP_CGROUP_MEMCG_SWAP is not used. When you do swapoff and make swapped-out pages of shmem(tmpfs) to be backed into memory in force, charges for pages are accounted against the caller of swapoff rather than the users of shmem. -2.4 Swap Extension (CONFIG_CGROUP_MEM_RES_CTLR_SWAP) +2.4 Swap Extension (CONFIG_MEMCG_SWAP) Swap Extension allows you to record charge for swap. A swapped-in page is charged back to original page allocator if possible. @@ -259,7 +259,7 @@ When oom event notifier is registered, event will be delivered. per-zone-per-cgroup LRU (cgroup's private LRU) is just guarded by zone->lru_lock, it has no lock of its own. -2.7 Kernel Memory Extension (CONFIG_CGROUP_MEM_RES_CTLR_KMEM) +2.7 Kernel Memory Extension (CONFIG_MEMCG_KMEM) With the Kernel memory extension, the Memory Controller is able to limit the amount of kernel memory used by the system. Kernel memory is fundamentally @@ -286,8 +286,8 @@ per cgroup, instead of globally. a. Enable CONFIG_CGROUPS b. Enable CONFIG_RESOURCE_COUNTERS -c. Enable CONFIG_CGROUP_MEM_RES_CTLR -d. Enable CONFIG_CGROUP_MEM_RES_CTLR_SWAP (to use swap extension) +c. Enable CONFIG_MEMCG +d. Enable CONFIG_MEMCG_SWAP (to use swap extension) 1. Prepare the cgroups (see cgroups.txt, Why are cgroups needed?) # mount -t tmpfs none /sys/fs/cgroup diff --git a/arch/powerpc/configs/chroma_defconfig b/arch/powerpc/configs/chroma_defconfig index b1f9597fe312..29bb11ec6c64 100644 --- a/arch/powerpc/configs/chroma_defconfig +++ b/arch/powerpc/configs/chroma_defconfig @@ -21,8 +21,8 @@ CONFIG_CGROUP_DEVICE=y CONFIG_CPUSETS=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEM_RES_CTLR=y -CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y +CONFIG_CGROUP_MEMCG=y +CONFIG_CGROUP_MEMCG_SWAP=y CONFIG_NAMESPACES=y CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y diff --git a/arch/s390/defconfig b/arch/s390/defconfig index 37d2bf267964..de57702a3f44 100644 --- a/arch/s390/defconfig +++ b/arch/s390/defconfig @@ -13,7 +13,7 @@ CONFIG_CGROUPS=y CONFIG_CPUSETS=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEM_RES_CTLR=y +CONFIG_CGROUP_MEMCG=y CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y diff --git a/arch/sh/configs/apsh4ad0a_defconfig b/arch/sh/configs/apsh4ad0a_defconfig index e7583484cc07..95ae23fcfdd6 100644 --- a/arch/sh/configs/apsh4ad0a_defconfig +++ b/arch/sh/configs/apsh4ad0a_defconfig @@ -11,7 +11,7 @@ CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEM_RES_CTLR=y +CONFIG_CGROUP_MEMCG=y CONFIG_BLK_CGROUP=y CONFIG_NAMESPACES=y CONFIG_BLK_DEV_INITRD=y diff --git a/arch/sh/configs/sdk7786_defconfig b/arch/sh/configs/sdk7786_defconfig index 8a7dd7b59c5c..76a76a295d74 100644 --- a/arch/sh/configs/sdk7786_defconfig +++ b/arch/sh/configs/sdk7786_defconfig @@ -18,8 +18,8 @@ CONFIG_CPUSETS=y # CONFIG_PROC_PID_CPUSET is not set CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEM_RES_CTLR=y -CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y +CONFIG_CGROUP_MEMCG=y +CONFIG_CGROUP_MEMCG_SWAP=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_BLK_CGROUP=y diff --git a/arch/sh/configs/se7206_defconfig b/arch/sh/configs/se7206_defconfig index 72c3fad7383f..6bc30ab9fd18 100644 --- a/arch/sh/configs/se7206_defconfig +++ b/arch/sh/configs/se7206_defconfig @@ -11,7 +11,7 @@ CONFIG_CGROUP_DEBUG=y CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEM_RES_CTLR=y +CONFIG_CGROUP_MEMCG=y CONFIG_RELAY=y CONFIG_NAMESPACES=y CONFIG_UTS_NS=y diff --git a/arch/sh/configs/shx3_defconfig b/arch/sh/configs/shx3_defconfig index 6bb413036892..cd6c519f8fad 100644 --- a/arch/sh/configs/shx3_defconfig +++ b/arch/sh/configs/shx3_defconfig @@ -13,7 +13,7 @@ CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEM_RES_CTLR=y +CONFIG_CGROUP_MEMCG=y CONFIG_RELAY=y CONFIG_NAMESPACES=y CONFIG_UTS_NS=y diff --git a/arch/sh/configs/urquell_defconfig b/arch/sh/configs/urquell_defconfig index 8bfa4d056d7a..d7f89be9f474 100644 --- a/arch/sh/configs/urquell_defconfig +++ b/arch/sh/configs/urquell_defconfig @@ -15,8 +15,8 @@ CONFIG_CPUSETS=y # CONFIG_PROC_PID_CPUSET is not set CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEM_RES_CTLR=y -CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y +CONFIG_CGROUP_MEMCG=y +CONFIG_CGROUP_MEMCG_SWAP=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_BLK_DEV_INITRD=y diff --git a/arch/tile/configs/tilegx_defconfig b/arch/tile/configs/tilegx_defconfig index b8d99aca5431..0270620a1692 100644 --- a/arch/tile/configs/tilegx_defconfig +++ b/arch/tile/configs/tilegx_defconfig @@ -18,8 +18,8 @@ CONFIG_CGROUP_DEVICE=y CONFIG_CPUSETS=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEM_RES_CTLR=y -CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y +CONFIG_CGROUP_MEMCG=y +CONFIG_CGROUP_MEMCG_SWAP=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_BLK_CGROUP=y diff --git a/arch/tile/configs/tilepro_defconfig b/arch/tile/configs/tilepro_defconfig index 2b1fd31894f1..c11de27a9bcb 100644 --- a/arch/tile/configs/tilepro_defconfig +++ b/arch/tile/configs/tilepro_defconfig @@ -17,8 +17,8 @@ CONFIG_CGROUP_DEVICE=y CONFIG_CPUSETS=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEM_RES_CTLR=y -CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y +CONFIG_CGROUP_MEMCG=y +CONFIG_CGROUP_MEMCG_SWAP=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_BLK_CGROUP=y diff --git a/arch/um/defconfig b/arch/um/defconfig index 7823ab12e6a4..fec0d5d27460 100644 --- a/arch/um/defconfig +++ b/arch/um/defconfig @@ -155,10 +155,10 @@ CONFIG_CPUSETS=y CONFIG_PROC_PID_CPUSET=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEM_RES_CTLR=y -CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y -# CONFIG_CGROUP_MEM_RES_CTLR_SWAP_ENABLED is not set -# CONFIG_CGROUP_MEM_RES_CTLR_KMEM is not set +CONFIG_CGROUP_MEMCG=y +CONFIG_CGROUP_MEMCG_SWAP=y +# CONFIG_CGROUP_MEMCG_SWAP_ENABLED is not set +# CONFIG_CGROUP_MEMCG_KMEM is not set CONFIG_CGROUP_SCHED=y CONFIG_FAIR_GROUP_SCHED=y # CONFIG_CFS_BANDWIDTH is not set diff --git a/include/linux/cgroup_subsys.h b/include/linux/cgroup_subsys.h index 5b41ce079024..dfae957398c3 100644 --- a/include/linux/cgroup_subsys.h +++ b/include/linux/cgroup_subsys.h @@ -31,7 +31,7 @@ SUBSYS(cpuacct) /* */ -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG SUBSYS(mem_cgroup) #endif diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 83e7ba90d6e5..170076222431 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -38,7 +38,7 @@ struct mem_cgroup_reclaim_cookie { unsigned int generation; }; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG /* * All "charge" functions with gfp_mask should use GFP_KERNEL or * (gfp_mask & GFP_RECLAIM_MASK). In current implementatin, memcg doesn't @@ -124,7 +124,7 @@ extern void mem_cgroup_print_oom_info(struct mem_cgroup *memcg, extern void mem_cgroup_replace_page_cache(struct page *oldpage, struct page *newpage); -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP +#ifdef CONFIG_MEMCG_SWAP extern int do_swap_account; #endif @@ -193,7 +193,7 @@ void mem_cgroup_split_huge_fixup(struct page *head); bool mem_cgroup_bad_page_check(struct page *page); void mem_cgroup_print_bad_page(struct page *page); #endif -#else /* CONFIG_CGROUP_MEM_RES_CTLR */ +#else /* CONFIG_MEMCG */ struct mem_cgroup; static inline int mem_cgroup_newpage_charge(struct page *page, @@ -384,9 +384,9 @@ static inline void mem_cgroup_replace_page_cache(struct page *oldpage, struct page *newpage) { } -#endif /* CONFIG_CGROUP_MEM_RES_CTLR */ +#endif /* CONFIG_MEMCG */ -#if !defined(CONFIG_CGROUP_MEM_RES_CTLR) || !defined(CONFIG_DEBUG_VM) +#if !defined(CONFIG_MEMCG) || !defined(CONFIG_DEBUG_VM) static inline bool mem_cgroup_bad_page_check(struct page *page) { @@ -406,7 +406,7 @@ enum { }; struct sock; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM +#ifdef CONFIG_MEMCG_KMEM void sock_update_memcg(struct sock *sk); void sock_release_memcg(struct sock *sk); #else @@ -416,6 +416,6 @@ static inline void sock_update_memcg(struct sock *sk) static inline void sock_release_memcg(struct sock *sk) { } -#endif /* CONFIG_CGROUP_MEM_RES_CTLR_KMEM */ +#endif /* CONFIG_MEMCG_KMEM */ #endif /* _LINUX_MEMCONTROL_H */ diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 458988bd55a1..3bdfa15b2012 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -201,7 +201,7 @@ struct zone_reclaim_stat { struct lruvec { struct list_head lists[NR_LRU_LISTS]; struct zone_reclaim_stat reclaim_stat; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG struct zone *zone; #endif }; @@ -671,7 +671,7 @@ typedef struct pglist_data { int nr_zones; #ifdef CONFIG_FLAT_NODE_MEM_MAP /* means !SPARSEMEM */ struct page *node_mem_map; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG struct page_cgroup *node_page_cgroup; #endif #endif @@ -736,7 +736,7 @@ extern void lruvec_init(struct lruvec *lruvec, struct zone *zone); static inline struct zone *lruvec_zone(struct lruvec *lruvec) { -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG return lruvec->zone; #else return container_of(lruvec, struct zone, lruvec); @@ -1052,7 +1052,7 @@ struct mem_section { /* See declaration of similar field in struct zone */ unsigned long *pageblock_flags; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG /* * If !SPARSEMEM, pgdat doesn't have page_cgroup pointer. We use * section. (see memcontrol.h/page_cgroup.h about this.) diff --git a/include/linux/page_cgroup.h b/include/linux/page_cgroup.h index a88cdba27809..777a524716db 100644 --- a/include/linux/page_cgroup.h +++ b/include/linux/page_cgroup.h @@ -12,7 +12,7 @@ enum { #ifndef __GENERATING_BOUNDS_H #include -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG #include /* @@ -82,7 +82,7 @@ static inline void unlock_page_cgroup(struct page_cgroup *pc) bit_spin_unlock(PCG_LOCK, &pc->flags); } -#else /* CONFIG_CGROUP_MEM_RES_CTLR */ +#else /* CONFIG_MEMCG */ struct page_cgroup; static inline void __meminit pgdat_page_cgroup_init(struct pglist_data *pgdat) @@ -102,11 +102,11 @@ static inline void __init page_cgroup_init_flatmem(void) { } -#endif /* CONFIG_CGROUP_MEM_RES_CTLR */ +#endif /* CONFIG_MEMCG */ #include -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP +#ifdef CONFIG_MEMCG_SWAP extern unsigned short swap_cgroup_cmpxchg(swp_entry_t ent, unsigned short old, unsigned short new); extern unsigned short swap_cgroup_record(swp_entry_t ent, unsigned short id); @@ -138,7 +138,7 @@ static inline void swap_cgroup_swapoff(int type) return; } -#endif /* CONFIG_CGROUP_MEM_RES_CTLR_SWAP */ +#endif /* CONFIG_MEMCG_SWAP */ #endif /* !__GENERATING_BOUNDS_H */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 68dcffaa62a0..865725adb9d3 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1584,7 +1584,7 @@ struct task_struct { /* bitmask and counter of trace recursion */ unsigned long trace_recursion; #endif /* CONFIG_TRACING */ -#ifdef CONFIG_CGROUP_MEM_RES_CTLR /* memcg uses this to do batch job */ +#ifdef CONFIG_MEMCG /* memcg uses this to do batch job */ struct memcg_batch_info { int do_batch; /* incremented when batch uncharge started */ struct mem_cgroup *memcg; /* target memcg of uncharge */ diff --git a/include/linux/swap.h b/include/linux/swap.h index c84ec68eaec9..9a16bb1cefd1 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -301,7 +301,7 @@ static inline void scan_unevictable_unregister_node(struct node *node) extern int kswapd_run(int nid); extern void kswapd_stop(int nid); -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG extern int mem_cgroup_swappiness(struct mem_cgroup *mem); #else static inline int mem_cgroup_swappiness(struct mem_cgroup *mem) @@ -309,7 +309,7 @@ static inline int mem_cgroup_swappiness(struct mem_cgroup *mem) return vm_swappiness; } #endif -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP +#ifdef CONFIG_MEMCG_SWAP extern void mem_cgroup_uncharge_swap(swp_entry_t ent); #else static inline void mem_cgroup_uncharge_swap(swp_entry_t ent) @@ -360,7 +360,7 @@ extern int reuse_swap_page(struct page *); extern int try_to_free_swap(struct page *); struct backing_dev_info; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG extern void mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout); #else diff --git a/include/net/sock.h b/include/net/sock.h index e067f8c18f88..cee528c119ca 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -913,7 +913,7 @@ struct proto { #ifdef SOCK_REFCNT_DEBUG atomic_t socks; #endif -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM +#ifdef CONFIG_MEMCG_KMEM /* * cgroup specific init/deinit functions. Called once for all * protocols that implement it, from cgroups populate function. @@ -994,7 +994,7 @@ inline void sk_refcnt_debug_release(const struct sock *sk) #define sk_refcnt_debug_release(sk) do { } while (0) #endif /* SOCK_REFCNT_DEBUG */ -#if defined(CONFIG_CGROUP_MEM_RES_CTLR_KMEM) && defined(CONFIG_NET) +#if defined(CONFIG_MEMCG_KMEM) && defined(CONFIG_NET) extern struct static_key memcg_socket_limit_enabled; static inline struct cg_proto *parent_cg_proto(struct proto *proto, struct cg_proto *cg_proto) diff --git a/init/Kconfig b/init/Kconfig index 72437760e90e..af6c7f8ba019 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -686,7 +686,7 @@ config RESOURCE_COUNTERS This option enables controller independent resource accounting infrastructure that works with cgroups. -config CGROUP_MEM_RES_CTLR +config MEMCG bool "Memory Resource Controller for Control Groups" depends on RESOURCE_COUNTERS select MM_OWNER @@ -709,9 +709,9 @@ config CGROUP_MEM_RES_CTLR This config option also selects MM_OWNER config option, which could in turn add some fork/exit overhead. -config CGROUP_MEM_RES_CTLR_SWAP +config MEMCG_SWAP bool "Memory Resource Controller Swap Extension" - depends on CGROUP_MEM_RES_CTLR && SWAP + depends on MEMCG && SWAP help Add swap management feature to memory resource controller. When you enable this, you can limit mem+swap usage per cgroup. In other words, @@ -726,9 +726,9 @@ config CGROUP_MEM_RES_CTLR_SWAP if boot option "swapaccount=0" is set, swap will not be accounted. Now, memory usage of swap_cgroup is 2 bytes per entry. If swap page size is 4096bytes, 512k per 1Gbytes of swap. -config CGROUP_MEM_RES_CTLR_SWAP_ENABLED +config MEMCG_SWAP_ENABLED bool "Memory Resource Controller Swap Extension enabled by default" - depends on CGROUP_MEM_RES_CTLR_SWAP + depends on MEMCG_SWAP default y help Memory Resource Controller Swap Extension comes with its price in @@ -739,9 +739,9 @@ config CGROUP_MEM_RES_CTLR_SWAP_ENABLED For those who want to have the feature enabled by default should select this option (if, for some reason, they need to disable it then swapaccount=0 does the trick). -config CGROUP_MEM_RES_CTLR_KMEM +config MEMCG_KMEM bool "Memory Resource Controller Kernel Memory accounting (EXPERIMENTAL)" - depends on CGROUP_MEM_RES_CTLR && EXPERIMENTAL + depends on MEMCG && EXPERIMENTAL default n help The Kernel Memory extension for Memory Resource Controller can limit diff --git a/kernel/fork.c b/kernel/fork.c index aaa8813c45d1..3bd2280d79f6 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1306,7 +1306,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, #ifdef CONFIG_DEBUG_MUTEXES p->blocked_on = NULL; /* not blocked yet */ #endif -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG p->memcg_batch.do_batch = 0; p->memcg_batch.memcg = NULL; #endif diff --git a/mm/Makefile b/mm/Makefile index fd6fc1c1966c..290bbfe33698 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -49,7 +49,7 @@ obj-$(CONFIG_FS_XIP) += filemap_xip.o obj-$(CONFIG_MIGRATION) += migrate.o obj-$(CONFIG_QUICKLIST) += quicklist.o obj-$(CONFIG_TRANSPARENT_HUGEPAGE) += huge_memory.o -obj-$(CONFIG_CGROUP_MEM_RES_CTLR) += memcontrol.o page_cgroup.o +obj-$(CONFIG_MEMCG) += memcontrol.o page_cgroup.o obj-$(CONFIG_CGROUP_HUGETLB) += hugetlb_cgroup.o obj-$(CONFIG_MEMORY_FAILURE) += memory-failure.o obj-$(CONFIG_HWPOISON_INJECT) += hwpoison-inject.o diff --git a/mm/hwpoison-inject.c b/mm/hwpoison-inject.c index cc448bb983ba..3a61efc518d5 100644 --- a/mm/hwpoison-inject.c +++ b/mm/hwpoison-inject.c @@ -123,7 +123,7 @@ static int pfn_inject_init(void) if (!dentry) goto fail; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP +#ifdef CONFIG_MEMCG_SWAP dentry = debugfs_create_u64("corrupt-filter-memcg", 0600, hwpoison_dir, &hwpoison_filter_memcg); if (!dentry) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index a2677e0a6387..55a85e1a342f 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -61,12 +61,12 @@ struct cgroup_subsys mem_cgroup_subsys __read_mostly; #define MEM_CGROUP_RECLAIM_RETRIES 5 static struct mem_cgroup *root_mem_cgroup __read_mostly; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP +#ifdef CONFIG_MEMCG_SWAP /* Turned on only when memory cgroup is enabled && really_do_swap_account = 1 */ int do_swap_account __read_mostly; /* for remember boot option*/ -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP_ENABLED +#ifdef CONFIG_MEMCG_SWAP_ENABLED static int really_do_swap_account __initdata = 1; #else static int really_do_swap_account __initdata = 0; @@ -407,7 +407,7 @@ static void mem_cgroup_get(struct mem_cgroup *memcg); static void mem_cgroup_put(struct mem_cgroup *memcg); /* Writing them here to avoid exposing memcg's inner layout */ -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM +#ifdef CONFIG_MEMCG_KMEM #include #include @@ -466,9 +466,9 @@ struct cg_proto *tcp_proto_cgroup(struct mem_cgroup *memcg) } EXPORT_SYMBOL(tcp_proto_cgroup); #endif /* CONFIG_INET */ -#endif /* CONFIG_CGROUP_MEM_RES_CTLR_KMEM */ +#endif /* CONFIG_MEMCG_KMEM */ -#if defined(CONFIG_INET) && defined(CONFIG_CGROUP_MEM_RES_CTLR_KMEM) +#if defined(CONFIG_INET) && defined(CONFIG_MEMCG_KMEM) static void disarm_sock_keys(struct mem_cgroup *memcg) { if (!memcg_proto_activated(&memcg->tcp_mem.cg_proto)) @@ -3085,7 +3085,7 @@ mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout) } #endif -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP +#ifdef CONFIG_MEMCG_SWAP /* * called from swap_entry_free(). remove record in swap_cgroup and * uncharge "memsw" account. @@ -4518,7 +4518,7 @@ static int mem_cgroup_oom_control_write(struct cgroup *cgrp, return 0; } -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM +#ifdef CONFIG_MEMCG_KMEM static int memcg_init_kmem(struct mem_cgroup *memcg, struct cgroup_subsys *ss) { return mem_cgroup_sockets_init(memcg, ss); @@ -4608,7 +4608,7 @@ static struct cftype mem_cgroup_files[] = { .read_seq_string = mem_control_numa_stat_show, }, #endif -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP +#ifdef CONFIG_MEMCG_SWAP { .name = "memsw.usage_in_bytes", .private = MEMFILE_PRIVATE(_MEMSWAP, RES_USAGE), @@ -4795,7 +4795,7 @@ struct mem_cgroup *parent_mem_cgroup(struct mem_cgroup *memcg) } EXPORT_SYMBOL(parent_mem_cgroup); -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP +#ifdef CONFIG_MEMCG_SWAP static void __init enable_swap_cgroup(void) { if (!mem_cgroup_disabled() && really_do_swap_account) @@ -5526,7 +5526,7 @@ struct cgroup_subsys mem_cgroup_subsys = { .__DEPRECATED_clear_css_refs = true, }; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP +#ifdef CONFIG_MEMCG_SWAP static int __init enable_swap_account(char *s) { /* consider enabled if no parameter or 1 is given */ diff --git a/mm/memory-failure.c b/mm/memory-failure.c index b04ff2d6f73d..a6e2141a6610 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -128,7 +128,7 @@ static int hwpoison_filter_flags(struct page *p) * can only guarantee that the page either belongs to the memcg tasks, or is * a freed page. */ -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP +#ifdef CONFIG_MEMCG_SWAP u64 hwpoison_filter_memcg; EXPORT_SYMBOL_GPL(hwpoison_filter_memcg); static int hwpoison_filter_task(struct page *p) diff --git a/mm/mmzone.c b/mm/mmzone.c index 6830eab5bf09..3cef80f6ac79 100644 --- a/mm/mmzone.c +++ b/mm/mmzone.c @@ -96,7 +96,7 @@ void lruvec_init(struct lruvec *lruvec, struct zone *zone) for_each_lru(lru) INIT_LIST_HEAD(&lruvec->lists[lru]); -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG lruvec->zone = zone; #endif } diff --git a/mm/oom_kill.c b/mm/oom_kill.c index c82ede69bf3f..e6c10640e56b 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -541,7 +541,7 @@ static void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask, sysctl_panic_on_oom == 2 ? "compulsory" : "system-wide"); } -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, int order) { diff --git a/mm/page_cgroup.c b/mm/page_cgroup.c index eb750f851395..5ddad0c6daa6 100644 --- a/mm/page_cgroup.c +++ b/mm/page_cgroup.c @@ -317,7 +317,7 @@ void __meminit pgdat_page_cgroup_init(struct pglist_data *pgdat) #endif -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP +#ifdef CONFIG_MEMCG_SWAP static DEFINE_MUTEX(swap_cgroup_mutex); struct swap_cgroup_ctrl { diff --git a/mm/vmscan.c b/mm/vmscan.c index 347b3ff2a478..6b1f89a91212 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -133,7 +133,7 @@ long vm_total_pages; /* The total number of pages which the VM controls */ static LIST_HEAD(shrinker_list); static DECLARE_RWSEM(shrinker_rwsem); -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG static bool global_reclaim(struct scan_control *sc) { return !sc->target_mem_cgroup; @@ -2142,7 +2142,7 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order, return nr_reclaimed; } -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG unsigned long mem_cgroup_shrink_node_zone(struct mem_cgroup *memcg, gfp_t gfp_mask, bool noswap, diff --git a/net/core/sock.c b/net/core/sock.c index 2676a88f533e..a67b06280e4c 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -142,7 +142,7 @@ static DEFINE_MUTEX(proto_list_mutex); static LIST_HEAD(proto_list); -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM +#ifdef CONFIG_MEMCG_KMEM int mem_cgroup_sockets_init(struct mem_cgroup *memcg, struct cgroup_subsys *ss) { struct proto *proto; diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index ae2ccf2890e4..15ca63ec604e 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -49,7 +49,7 @@ obj-$(CONFIG_TCP_CONG_SCALABLE) += tcp_scalable.o obj-$(CONFIG_TCP_CONG_LP) += tcp_lp.o obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah.o obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o -obj-$(CONFIG_CGROUP_MEM_RES_CTLR_KMEM) += tcp_memcontrol.o +obj-$(CONFIG_MEMCG_KMEM) += tcp_memcontrol.o obj-$(CONFIG_NETLABEL) += cipso_ipv4.o obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 5840c3255721..ed7db3f1b6f2 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -184,7 +184,7 @@ static int ipv4_tcp_mem(ctl_table *ctl, int write, int ret; unsigned long vec[3]; struct net *net = current->nsproxy->net_ns; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM +#ifdef CONFIG_MEMCG_KMEM struct mem_cgroup *memcg; #endif @@ -203,7 +203,7 @@ static int ipv4_tcp_mem(ctl_table *ctl, int write, if (ret) return ret; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM +#ifdef CONFIG_MEMCG_KMEM rcu_read_lock(); memcg = mem_cgroup_from_task(current); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 2fbd9921253f..4bc8f6769b57 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2633,7 +2633,7 @@ struct proto tcp_prot = { .compat_setsockopt = compat_tcp_setsockopt, .compat_getsockopt = compat_tcp_getsockopt, #endif -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM +#ifdef CONFIG_MEMCG_KMEM .init_cgroup = tcp_init_cgroup, .destroy_cgroup = tcp_destroy_cgroup, .proto_cgroup = tcp_proto_cgroup, diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 221224e72507..61c7b6d83176 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -2015,7 +2015,7 @@ struct proto tcpv6_prot = { .compat_setsockopt = compat_tcp_setsockopt, .compat_getsockopt = compat_tcp_getsockopt, #endif -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM +#ifdef CONFIG_MEMCG_KMEM .proto_cgroup = tcp_proto_cgroup, #endif }; -- cgit v1.2.3 From ca28ddc908fcfef0e5c1b6e5df632db7fc26de10 Mon Sep 17 00:00:00 2001 From: Wanpeng Li Date: Tue, 31 Jul 2012 16:43:04 -0700 Subject: mm: remove unused LRU_ALL_EVICTABLE Signed-off-by: Wanpeng Li Acked-by: KAMEZAWA Hiroyuki Cc: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 3bdfa15b2012..1495d952e641 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -209,7 +209,6 @@ struct lruvec { /* Mask used at gathering information at once (see memcontrol.c) */ #define LRU_ALL_FILE (BIT(LRU_INACTIVE_FILE) | BIT(LRU_ACTIVE_FILE)) #define LRU_ALL_ANON (BIT(LRU_INACTIVE_ANON) | BIT(LRU_ACTIVE_ANON)) -#define LRU_ALL_EVICTABLE (LRU_ALL_FILE | LRU_ALL_ANON) #define LRU_ALL ((1 << NR_LRU_LISTS) - 1) /* Isolate clean file */ -- cgit v1.2.3 From 7db8889ab05b57200158432755af318fb68854a2 Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Tue, 31 Jul 2012 16:43:12 -0700 Subject: mm: have order > 0 compaction start off where it left Order > 0 compaction stops when enough free pages of the correct page order have been coalesced. When doing subsequent higher order allocations, it is possible for compaction to be invoked many times. However, the compaction code always starts out looking for things to compact at the start of the zone, and for free pages to compact things to at the end of the zone. This can cause quadratic behaviour, with isolate_freepages starting at the end of the zone each time, even though previous invocations of the compaction code already filled up all free memory on that end of the zone. This can cause isolate_freepages to take enormous amounts of CPU with certain workloads on larger memory systems. The obvious solution is to have isolate_freepages remember where it left off last time, and continue at that point the next time it gets invoked for an order > 0 compaction. This could cause compaction to fail if cc->free_pfn and cc->migrate_pfn are close together initially, in that case we restart from the end of the zone and try once more. Forced full (order == -1) compactions are left alone. [akpm@linux-foundation.org: checkpatch fixes] [akpm@linux-foundation.org: s/laste/last/, use 80 cols] Signed-off-by: Rik van Riel Reported-by: Jim Schutt Tested-by: Jim Schutt Cc: Minchan Kim Reviewed-by: KAMEZAWA Hiroyuki Acked-by: Mel Gorman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 4 ++++ mm/compaction.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++---- mm/internal.h | 6 +++++ mm/page_alloc.c | 5 ++++ 4 files changed, 73 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 1495d952e641..1aeadce4d56e 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -368,6 +368,10 @@ struct zone { */ spinlock_t lock; int all_unreclaimable; /* All pages pinned */ +#if defined CONFIG_COMPACTION || defined CONFIG_CMA + /* pfn where the last incremental compaction isolated free pages */ + unsigned long compact_cached_free_pfn; +#endif #ifdef CONFIG_MEMORY_HOTPLUG /* see spanned/present_pages for more description */ seqlock_t span_seqlock; diff --git a/mm/compaction.c b/mm/compaction.c index 2f42d9528539..e78cb9688421 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -422,6 +422,17 @@ static void isolate_freepages(struct zone *zone, pfn -= pageblock_nr_pages) { unsigned long isolated; + /* + * Skip ahead if another thread is compacting in the area + * simultaneously. If we wrapped around, we can only skip + * ahead if zone->compact_cached_free_pfn also wrapped to + * above our starting point. + */ + if (cc->order > 0 && (!cc->wrapped || + zone->compact_cached_free_pfn > + cc->start_free_pfn)) + pfn = min(pfn, zone->compact_cached_free_pfn); + if (!pfn_valid(pfn)) continue; @@ -461,8 +472,11 @@ static void isolate_freepages(struct zone *zone, * looking for free pages, the search will restart here as * page migration may have returned some pages to the allocator */ - if (isolated) + if (isolated) { high_pfn = max(high_pfn, pfn); + if (cc->order > 0) + zone->compact_cached_free_pfn = high_pfn; + } } /* split_free_page does not map the pages */ @@ -556,6 +570,20 @@ static isolate_migrate_t isolate_migratepages(struct zone *zone, return ISOLATE_SUCCESS; } +/* + * Returns the start pfn of the last page block in a zone. This is the starting + * point for full compaction of a zone. Compaction searches for free pages from + * the end of each zone, while isolate_freepages_block scans forward inside each + * page block. + */ +static unsigned long start_free_pfn(struct zone *zone) +{ + unsigned long free_pfn; + free_pfn = zone->zone_start_pfn + zone->spanned_pages; + free_pfn &= ~(pageblock_nr_pages-1); + return free_pfn; +} + static int compact_finished(struct zone *zone, struct compact_control *cc) { @@ -565,8 +593,26 @@ static int compact_finished(struct zone *zone, if (fatal_signal_pending(current)) return COMPACT_PARTIAL; - /* Compaction run completes if the migrate and free scanner meet */ - if (cc->free_pfn <= cc->migrate_pfn) + /* + * A full (order == -1) compaction run starts at the beginning and + * end of a zone; it completes when the migrate and free scanner meet. + * A partial (order > 0) compaction can start with the free scanner + * at a random point in the zone, and may have to restart. + */ + if (cc->free_pfn <= cc->migrate_pfn) { + if (cc->order > 0 && !cc->wrapped) { + /* We started partway through; restart at the end. */ + unsigned long free_pfn = start_free_pfn(zone); + zone->compact_cached_free_pfn = free_pfn; + cc->free_pfn = free_pfn; + cc->wrapped = 1; + return COMPACT_CONTINUE; + } + return COMPACT_COMPLETE; + } + + /* We wrapped around and ended up where we started. */ + if (cc->wrapped && cc->free_pfn <= cc->start_free_pfn) return COMPACT_COMPLETE; /* @@ -664,8 +710,15 @@ static int compact_zone(struct zone *zone, struct compact_control *cc) /* Setup to move all movable pages to the end of the zone */ cc->migrate_pfn = zone->zone_start_pfn; - cc->free_pfn = cc->migrate_pfn + zone->spanned_pages; - cc->free_pfn &= ~(pageblock_nr_pages-1); + + if (cc->order > 0) { + /* Incremental compaction. Start where the last one stopped. */ + cc->free_pfn = zone->compact_cached_free_pfn; + cc->start_free_pfn = cc->free_pfn; + } else { + /* Order == -1 starts at the end of the zone. */ + cc->free_pfn = start_free_pfn(zone); + } migrate_prep_local(); diff --git a/mm/internal.h b/mm/internal.h index 2ba87fbfb75b..da6b9b2ed3fc 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -118,8 +118,14 @@ struct compact_control { unsigned long nr_freepages; /* Number of isolated free pages */ unsigned long nr_migratepages; /* Number of pages to migrate */ unsigned long free_pfn; /* isolate_freepages search base */ + unsigned long start_free_pfn; /* where we started the search */ unsigned long migrate_pfn; /* isolate_migratepages search base */ bool sync; /* Synchronous migration */ + bool wrapped; /* Order > 0 compactions are + incremental, once free_pfn + and migrate_pfn meet, we restart + from the top of the zone; + remember we wrapped around. */ int order; /* order a direct compactor needs */ int migratetype; /* MOVABLE, RECLAIMABLE etc */ diff --git a/mm/page_alloc.c b/mm/page_alloc.c index fba2a1223f14..94fc475c3f94 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -4397,6 +4397,11 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat, zone->spanned_pages = size; zone->present_pages = realsize; +#if defined CONFIG_COMPACTION || defined CONFIG_CMA + zone->compact_cached_free_pfn = zone->zone_start_pfn + + zone->spanned_pages; + zone->compact_cached_free_pfn &= ~(pageblock_nr_pages-1); +#endif #ifdef CONFIG_NUMA zone->node = nid; zone->min_unmapped_pages = (realsize*sysctl_min_unmapped_ratio) -- cgit v1.2.3 From fe03025db3f4ade1f231b174938e0fe224722759 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Tue, 31 Jul 2012 16:43:14 -0700 Subject: mm: CONFIG_HAVE_MEMBLOCK_NODE -> CONFIG_HAVE_MEMBLOCK_NODE_MAP 0ee332c14518699 ("memblock: Kill early_node_map[]") wanted to replace CONFIG_ARCH_POPULATES_NODE_MAP with CONFIG_HAVE_MEMBLOCK_NODE_MAP but ended up replacing one occurence with a reference to the non-existent symbol CONFIG_HAVE_MEMBLOCK_NODE. The resulting omission of code would probably have been causing problems to 32-bit machines with memory hotplug. Signed-off-by: Rabin Vincent Cc: Tejun Heo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 1aeadce4d56e..f64afa5929fe 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -776,7 +776,7 @@ extern int movable_zone; static inline int zone_movable_is_highmem(void) { -#if defined(CONFIG_HIGHMEM) && defined(CONFIG_HAVE_MEMBLOCK_NODE) +#if defined(CONFIG_HIGHMEM) && defined(CONFIG_HAVE_MEMBLOCK_NODE_MAP) return movable_zone == ZONE_HIGHMEM; #else return 0; -- cgit v1.2.3 From 8e125cd85517c9716695b0abfabc0a4a3fcb94f3 Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Tue, 31 Jul 2012 16:43:16 -0700 Subject: vmscan: remove obsolete shrink_control comment 09f363c7 ("vmscan: fix shrinker callback bug in fs/super.c") fixed a shrinker callback which was returning -1 when nr_to_scan is zero, which caused excessive slab scanning. But 635697c6 ("vmscan: fix initial shrinker size handling") fixed the problem, again so we can freely return -1 although nr_to_scan is zero. So let's revert 09f363c7 because the comment added in 09f363c7 made an unnecessary rule. Signed-off-by: Minchan Kim Cc: Al Viro Cc: Mikulas Patocka Cc: Konstantin Khlebnikov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/super.c | 2 +- include/linux/shrinker.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/fs/super.c b/fs/super.c index 4c5d82f56ec4..4bf714459a4b 100644 --- a/fs/super.c +++ b/fs/super.c @@ -62,7 +62,7 @@ static int prune_super(struct shrinker *shrink, struct shrink_control *sc) return -1; if (!grab_super_passive(sb)) - return !sc->nr_to_scan ? 0 : -1; + return -1; if (sb->s_op && sb->s_op->nr_cached_objects) fs_objects = sb->s_op->nr_cached_objects(sb); diff --git a/include/linux/shrinker.h b/include/linux/shrinker.h index 07ceb97d53fa..ac6b8ee07825 100644 --- a/include/linux/shrinker.h +++ b/include/linux/shrinker.h @@ -20,7 +20,6 @@ struct shrink_control { * 'nr_to_scan' entries and attempt to free them up. It should return * the number of objects which remain in the cache. If it returns -1, it means * it cannot do any scanning at this time (eg. there is a risk of deadlock). - * The callback must not return -1 if nr_to_scan is zero. * * The 'gfpmask' refers to the allocation we are currently trying to * fulfil. -- cgit v1.2.3 From 9adb62a5df9c0fbef7b4665919329f73a34651ed Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Tue, 31 Jul 2012 16:43:28 -0700 Subject: mm/hotplug: correctly setup fallback zonelists when creating new pgdat When hotadd_new_pgdat() is called to create new pgdat for a new node, a fallback zonelist should be created for the new node. There's code to try to achieve that in hotadd_new_pgdat() as below: /* * The node we allocated has no zone fallback lists. For avoiding * to access not-initialized zonelist, build here. */ mutex_lock(&zonelists_mutex); build_all_zonelists(pgdat, NULL); mutex_unlock(&zonelists_mutex); But it doesn't work as expected. When hotadd_new_pgdat() is called, the new node is still in offline state because node_set_online(nid) hasn't been called yet. And build_all_zonelists() only builds zonelists for online nodes as: for_each_online_node(nid) { pg_data_t *pgdat = NODE_DATA(nid); build_zonelists(pgdat); build_zonelist_cache(pgdat); } Though we hope to create zonelist for the new pgdat, but it doesn't. So add a new parameter "pgdat" the build_all_zonelists() to build pgdat for the new pgdat too. Signed-off-by: Jiang Liu Signed-off-by: Xishi Qiu Cc: Mel Gorman Cc: Michal Hocko Cc: Minchan Kim Cc: Rusty Russell Cc: Yinghai Lu Cc: Tony Luck Cc: KAMEZAWA Hiroyuki Cc: KOSAKI Motohiro Cc: David Rientjes Cc: Keping Chen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 2 +- init/main.c | 2 +- kernel/cpu.c | 2 +- mm/memory_hotplug.c | 4 ++-- mm/page_alloc.c | 17 ++++++++++++----- 5 files changed, 17 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index f64afa5929fe..98f079bcf399 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -721,7 +721,7 @@ typedef struct pglist_data { #include extern struct mutex zonelists_mutex; -void build_all_zonelists(void *data); +void build_all_zonelists(pg_data_t *pgdat, struct zone *zone); void wakeup_kswapd(struct zone *zone, int order, enum zone_type classzone_idx); bool zone_watermark_ok(struct zone *z, int order, unsigned long mark, int classzone_idx, int alloc_flags); diff --git a/init/main.c b/init/main.c index 95316a1b4a76..e60679de61c3 100644 --- a/init/main.c +++ b/init/main.c @@ -506,7 +506,7 @@ asmlinkage void __init start_kernel(void) setup_per_cpu_areas(); smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */ - build_all_zonelists(NULL); + build_all_zonelists(NULL, NULL); page_alloc_init(); printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line); diff --git a/kernel/cpu.c b/kernel/cpu.c index a4eb5227a19e..14d32588cccd 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -416,7 +416,7 @@ int __cpuinit cpu_up(unsigned int cpu) if (pgdat->node_zonelists->_zonerefs->zone == NULL) { mutex_lock(&zonelists_mutex); - build_all_zonelists(NULL); + build_all_zonelists(NULL, NULL); mutex_unlock(&zonelists_mutex); } #endif diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 427bb291dd0f..b8731040b9f9 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -513,7 +513,7 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages) zone->present_pages += onlined_pages; zone->zone_pgdat->node_present_pages += onlined_pages; if (need_zonelists_rebuild) - build_all_zonelists(zone); + build_all_zonelists(NULL, zone); else zone_pcp_update(zone); @@ -562,7 +562,7 @@ static pg_data_t __ref *hotadd_new_pgdat(int nid, u64 start) * to access not-initialized zonelist, build here. */ mutex_lock(&zonelists_mutex); - build_all_zonelists(NULL); + build_all_zonelists(pgdat, NULL); mutex_unlock(&zonelists_mutex); return pgdat; diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 6c7e3bd93a85..9ad6866ac49c 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -3032,7 +3032,7 @@ int numa_zonelist_order_handler(ctl_table *table, int write, user_zonelist_order = oldval; } else if (oldval != user_zonelist_order) { mutex_lock(&zonelists_mutex); - build_all_zonelists(NULL); + build_all_zonelists(NULL, NULL); mutex_unlock(&zonelists_mutex); } } @@ -3415,10 +3415,17 @@ static __init_refok int __build_all_zonelists(void *data) { int nid; int cpu; + pg_data_t *self = data; #ifdef CONFIG_NUMA memset(node_load, 0, sizeof(node_load)); #endif + + if (self && !node_online(self->node_id)) { + build_zonelists(self); + build_zonelist_cache(self); + } + for_each_online_node(nid) { pg_data_t *pgdat = NODE_DATA(nid); @@ -3463,7 +3470,7 @@ static __init_refok int __build_all_zonelists(void *data) * Called with zonelists_mutex held always * unless system_state == SYSTEM_BOOTING. */ -void __ref build_all_zonelists(void *data) +void __ref build_all_zonelists(pg_data_t *pgdat, struct zone *zone) { set_zonelist_order(); @@ -3475,10 +3482,10 @@ void __ref build_all_zonelists(void *data) /* we have to stop all cpus to guarantee there is no user of zonelist */ #ifdef CONFIG_MEMORY_HOTPLUG - if (data) - setup_zone_pageset((struct zone *)data); + if (zone) + setup_zone_pageset(zone); #endif - stop_machine(__build_all_zonelists, NULL, NULL); + stop_machine(__build_all_zonelists, pgdat, NULL); /* cpuset refresh routine should be here */ } vm_total_pages = nr_free_pagecache_pages(); -- cgit v1.2.3 From 340175b7d14d5617559d0c1a54fa0ea204d9edcd Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Tue, 31 Jul 2012 16:43:32 -0700 Subject: mm/hotplug: free zone->pageset when a zone becomes empty When a zone becomes empty after memory offlining, free zone->pageset. Otherwise it will cause memory leak when adding memory to the empty zone again because build_all_zonelists() will allocate zone->pageset for an empty zone. Signed-off-by: Jiang Liu Signed-off-by: Wei Wang Cc: Mel Gorman Cc: Michal Hocko Cc: Minchan Kim Cc: Rusty Russell Cc: Yinghai Lu Cc: Tony Luck Cc: KAMEZAWA Hiroyuki Cc: KOSAKI Motohiro Cc: David Rientjes Cc: Keping Chen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm.h | 1 + mm/memory_hotplug.c | 3 +++ mm/page_alloc.c | 13 +++++++++++++ 3 files changed, 17 insertions(+) (limited to 'include') diff --git a/include/linux/mm.h b/include/linux/mm.h index 3955bedeeed1..7c6dfd2faa69 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1331,6 +1331,7 @@ void warn_alloc_failed(gfp_t gfp_mask, int order, const char *fmt, ...); extern void setup_per_cpu_pageset(void); extern void zone_pcp_update(struct zone *zone); +extern void zone_pcp_reset(struct zone *zone); /* nommu.c */ extern atomic_long_t mmap_pages_allocated; diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 597d371329d3..3ad25f9d1fc1 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -966,6 +966,9 @@ repeat: init_per_zone_wmark_min(); + if (!populated_zone(zone)) + zone_pcp_reset(zone); + if (!node_present_pages(node)) { node_clear_state(node, N_HIGH_MEMORY); kswapd_stop(node); diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 9ad6866ac49c..9c9a31665a78 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -5894,6 +5894,19 @@ void free_contig_range(unsigned long pfn, unsigned nr_pages) #endif #ifdef CONFIG_MEMORY_HOTREMOVE +void zone_pcp_reset(struct zone *zone) +{ + unsigned long flags; + + /* avoid races with drain_pages() */ + local_irq_save(flags); + if (zone->pageset != &boot_pageset) { + free_percpu(zone->pageset); + zone->pageset = &boot_pageset; + } + local_irq_restore(flags); +} + /* * All pages in the range must be isolated before calling this. */ -- cgit v1.2.3 From 62ce1c706f817cb9defef3ac2dfdd815149f2968 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Tue, 31 Jul 2012 16:43:39 -0700 Subject: mm, oom: move declaration for mem_cgroup_out_of_memory to oom.h mem_cgroup_out_of_memory() is defined in mm/oom_kill.c, so declare it in linux/oom.h rather than linux/memcontrol.h. Acked-by: KAMEZAWA Hiroyuki Acked-by: KOSAKI Motohiro Acked-by: Michal Hocko Signed-off-by: David Rientjes Cc: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 2 -- include/linux/oom.h | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 170076222431..c0bff8976a69 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -72,8 +72,6 @@ extern void mem_cgroup_uncharge_end(void); extern void mem_cgroup_uncharge_page(struct page *page); extern void mem_cgroup_uncharge_cache_page(struct page *page); -extern void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, - int order); bool __mem_cgroup_same_or_subtree(const struct mem_cgroup *root_memcg, struct mem_cgroup *memcg); int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *memcg); diff --git a/include/linux/oom.h b/include/linux/oom.h index e4c29bc72e70..eb9dc14362c5 100644 --- a/include/linux/oom.h +++ b/include/linux/oom.h @@ -49,6 +49,8 @@ extern unsigned long oom_badness(struct task_struct *p, extern int try_set_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags); extern void clear_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags); +extern void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, + int order); extern void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, int order, nodemask_t *mask, bool force_kill); extern int register_oom_notifier(struct notifier_block *nb); -- cgit v1.2.3 From 9cbb78bb314360a860a8b23723971cb6fcb54176 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Tue, 31 Jul 2012 16:43:44 -0700 Subject: mm, memcg: introduce own oom handler to iterate only over its own threads The global oom killer is serialized by the per-zonelist try_set_zonelist_oom() which is used in the page allocator. Concurrent oom kills are thus a rare event and only occur in systems using mempolicies and with a large number of nodes. Memory controller oom kills, however, can frequently be concurrent since there is no serialization once the oom killer is called for oom conditions in several different memcgs in parallel. This creates a massive contention on tasklist_lock since the oom killer requires the readside for the tasklist iteration. If several memcgs are calling the oom killer, this lock can be held for a substantial amount of time, especially if threads continue to enter it as other threads are exiting. Since the exit path grabs the writeside of the lock with irqs disabled in a few different places, this can cause a soft lockup on cpus as a result of tasklist_lock starvation. The kernel lacks unfair writelocks, and successful calls to the oom killer usually result in at least one thread entering the exit path, so an alternative solution is needed. This patch introduces a seperate oom handler for memcgs so that they do not require tasklist_lock for as much time. Instead, it iterates only over the threads attached to the oom memcg and grabs a reference to the selected thread before calling oom_kill_process() to ensure it doesn't prematurely exit. This still requires tasklist_lock for the tasklist dump, iterating children of the selected process, and killing all other threads on the system sharing the same memory as the selected victim. So while this isn't a complete solution to tasklist_lock starvation, it significantly reduces the amount of time that it is held. Acked-by: KAMEZAWA Hiroyuki Acked-by: Michal Hocko Signed-off-by: David Rientjes Cc: Oleg Nesterov Cc: KOSAKI Motohiro Reviewed-by: Sha Zhengju Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 9 ++----- include/linux/oom.h | 16 ++++++++++++ mm/memcontrol.c | 61 +++++++++++++++++++++++++++++++++++++++++++++- mm/oom_kill.c | 48 ++++++++++++------------------------ 4 files changed, 93 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index c0bff8976a69..2a80544aec99 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -180,7 +180,8 @@ static inline void mem_cgroup_dec_page_stat(struct page *page, unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order, gfp_t gfp_mask, unsigned long *total_scanned); -u64 mem_cgroup_get_limit(struct mem_cgroup *memcg); +extern void __mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, + int order); void mem_cgroup_count_vm_event(struct mm_struct *mm, enum vm_event_item idx); #ifdef CONFIG_TRANSPARENT_HUGEPAGE @@ -364,12 +365,6 @@ unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order, return 0; } -static inline -u64 mem_cgroup_get_limit(struct mem_cgroup *memcg) -{ - return 0; -} - static inline void mem_cgroup_split_huge_fixup(struct page *head) { } diff --git a/include/linux/oom.h b/include/linux/oom.h index eb9dc14362c5..5dc0e384ae9e 100644 --- a/include/linux/oom.h +++ b/include/linux/oom.h @@ -40,17 +40,33 @@ enum oom_constraint { CONSTRAINT_MEMCG, }; +enum oom_scan_t { + OOM_SCAN_OK, /* scan thread and find its badness */ + OOM_SCAN_CONTINUE, /* do not consider thread for oom kill */ + OOM_SCAN_ABORT, /* abort the iteration and return */ + OOM_SCAN_SELECT, /* always select this thread first */ +}; + extern void compare_swap_oom_score_adj(int old_val, int new_val); extern int test_set_oom_score_adj(int new_val); extern unsigned long oom_badness(struct task_struct *p, struct mem_cgroup *memcg, const nodemask_t *nodemask, unsigned long totalpages); +extern void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, + unsigned int points, unsigned long totalpages, + struct mem_cgroup *memcg, nodemask_t *nodemask, + const char *message); + extern int try_set_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags); extern void clear_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags); +extern enum oom_scan_t oom_scan_process_thread(struct task_struct *task, + unsigned long totalpages, const nodemask_t *nodemask, + bool force_kill); extern void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, int order); + extern void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, int order, nodemask_t *mask, bool force_kill); extern int register_oom_notifier(struct notifier_block *nb); diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 4f73c823c59f..b78972e2f43f 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1453,7 +1453,7 @@ static int mem_cgroup_count_children(struct mem_cgroup *memcg) /* * Return the memory (and swap, if configured) limit for a memcg. */ -u64 mem_cgroup_get_limit(struct mem_cgroup *memcg) +static u64 mem_cgroup_get_limit(struct mem_cgroup *memcg) { u64 limit; u64 memsw; @@ -1469,6 +1469,65 @@ u64 mem_cgroup_get_limit(struct mem_cgroup *memcg) return min(limit, memsw); } +void __mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, + int order) +{ + struct mem_cgroup *iter; + unsigned long chosen_points = 0; + unsigned long totalpages; + unsigned int points = 0; + struct task_struct *chosen = 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_iter it; + struct task_struct *task; + + cgroup_iter_start(cgroup, &it); + while ((task = cgroup_iter_next(cgroup, &it))) { + switch (oom_scan_process_thread(task, totalpages, NULL, + false)) { + case OOM_SCAN_SELECT: + if (chosen) + put_task_struct(chosen); + chosen = task; + chosen_points = ULONG_MAX; + get_task_struct(chosen); + /* fall through */ + case OOM_SCAN_CONTINUE: + continue; + case OOM_SCAN_ABORT: + cgroup_iter_end(cgroup, &it); + mem_cgroup_iter_break(memcg, iter); + if (chosen) + put_task_struct(chosen); + return; + case OOM_SCAN_OK: + break; + }; + points = oom_badness(task, memcg, NULL, totalpages); + if (points > chosen_points) { + if (chosen) + put_task_struct(chosen); + chosen = task; + chosen_points = points; + get_task_struct(chosen); + } + } + cgroup_iter_end(cgroup, &it); + } + + if (!chosen) + return; + points = chosen_points * 1000 / totalpages; + read_lock(&tasklist_lock); + oom_kill_process(chosen, gfp_mask, order, points, totalpages, memcg, + NULL, "Memory cgroup out of memory"); + read_unlock(&tasklist_lock); + put_task_struct(chosen); +} + static unsigned long mem_cgroup_reclaim(struct mem_cgroup *memcg, gfp_t gfp_mask, unsigned long flags) diff --git a/mm/oom_kill.c b/mm/oom_kill.c index f8eba9651c0c..c0c97aea837f 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -288,20 +288,13 @@ static enum oom_constraint constrained_alloc(struct zonelist *zonelist, } #endif -enum oom_scan_t { - OOM_SCAN_OK, /* scan thread and find its badness */ - OOM_SCAN_CONTINUE, /* do not consider thread for oom kill */ - OOM_SCAN_ABORT, /* abort the iteration and return */ - OOM_SCAN_SELECT, /* always select this thread first */ -}; - -static enum oom_scan_t oom_scan_process_thread(struct task_struct *task, - struct mem_cgroup *memcg, unsigned long totalpages, - const nodemask_t *nodemask, bool force_kill) +enum oom_scan_t oom_scan_process_thread(struct task_struct *task, + unsigned long totalpages, const nodemask_t *nodemask, + bool force_kill) { if (task->exit_state) return OOM_SCAN_CONTINUE; - if (oom_unkillable_task(task, memcg, nodemask)) + if (oom_unkillable_task(task, NULL, nodemask)) return OOM_SCAN_CONTINUE; /* @@ -348,8 +341,8 @@ static enum oom_scan_t oom_scan_process_thread(struct task_struct *task, * (not docbooked, we don't want this one cluttering up the manual) */ static struct task_struct *select_bad_process(unsigned int *ppoints, - unsigned long totalpages, struct mem_cgroup *memcg, - const nodemask_t *nodemask, bool force_kill) + unsigned long totalpages, const nodemask_t *nodemask, + bool force_kill) { struct task_struct *g, *p; struct task_struct *chosen = NULL; @@ -358,7 +351,7 @@ static struct task_struct *select_bad_process(unsigned int *ppoints, do_each_thread(g, p) { unsigned int points; - switch (oom_scan_process_thread(p, memcg, totalpages, nodemask, + switch (oom_scan_process_thread(p, totalpages, nodemask, force_kill)) { case OOM_SCAN_SELECT: chosen = p; @@ -371,7 +364,7 @@ static struct task_struct *select_bad_process(unsigned int *ppoints, case OOM_SCAN_OK: break; }; - points = oom_badness(p, memcg, nodemask, totalpages); + points = oom_badness(p, NULL, nodemask, totalpages); if (points > chosen_points) { chosen = p; chosen_points = points; @@ -443,10 +436,10 @@ static void dump_header(struct task_struct *p, gfp_t gfp_mask, int order, } #define K(x) ((x) << (PAGE_SHIFT-10)) -static void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, - unsigned int points, unsigned long totalpages, - struct mem_cgroup *memcg, nodemask_t *nodemask, - const char *message) +void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, + unsigned int points, unsigned long totalpages, + struct mem_cgroup *memcg, nodemask_t *nodemask, + const char *message) { struct task_struct *victim = p; struct task_struct *child; @@ -564,10 +557,6 @@ static void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask, void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, int order) { - unsigned long limit; - unsigned int points = 0; - struct task_struct *p; - /* * If current has a pending SIGKILL, then automatically select it. The * goal is to allow it to allocate so that it may quickly exit and free @@ -579,13 +568,7 @@ void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, } check_panic_on_oom(CONSTRAINT_MEMCG, gfp_mask, order, NULL); - limit = mem_cgroup_get_limit(memcg) >> PAGE_SHIFT ? : 1; - read_lock(&tasklist_lock); - p = select_bad_process(&points, limit, memcg, NULL, false); - if (p && PTR_ERR(p) != -1UL) - oom_kill_process(p, gfp_mask, order, points, limit, memcg, NULL, - "Memory cgroup out of memory"); - read_unlock(&tasklist_lock); + __mem_cgroup_out_of_memory(memcg, gfp_mask, order); } #endif @@ -710,7 +693,7 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, struct task_struct *p; unsigned long totalpages; unsigned long freed = 0; - unsigned int points; + unsigned int uninitialized_var(points); enum oom_constraint constraint = CONSTRAINT_NONE; int killed = 0; @@ -748,8 +731,7 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, goto out; } - p = select_bad_process(&points, totalpages, NULL, mpol_mask, - force_kill); + p = select_bad_process(&points, totalpages, mpol_mask, force_kill); /* Found nothing?!?! Either we hang forever, or we panic. */ if (!p) { dump_header(NULL, gfp_mask, order, NULL, mpol_mask); -- cgit v1.2.3 From 876aafbfd9ba5bb352f1b14622c27f3fe9a99013 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Tue, 31 Jul 2012 16:43:48 -0700 Subject: mm, memcg: move all oom handling to memcontrol.c By globally defining check_panic_on_oom(), the memcg oom handler can be moved entirely to mm/memcontrol.c. This removes the ugly #ifdef in the oom killer and cleans up the code. Signed-off-by: David Rientjes Cc: KAMEZAWA Hiroyuki Acked-by: Michal Hocko Cc: Oleg Nesterov Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 2 -- include/linux/oom.h | 3 +++ mm/memcontrol.c | 15 +++++++++++++-- mm/oom_kill.c | 23 ++--------------------- 4 files changed, 18 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 2a80544aec99..5a3ee6423634 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -180,8 +180,6 @@ static inline void mem_cgroup_dec_page_stat(struct page *page, unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order, gfp_t gfp_mask, unsigned long *total_scanned); -extern void __mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, - int order); void mem_cgroup_count_vm_event(struct mm_struct *mm, enum vm_event_item idx); #ifdef CONFIG_TRANSPARENT_HUGEPAGE diff --git a/include/linux/oom.h b/include/linux/oom.h index 5dc0e384ae9e..49a3031fda50 100644 --- a/include/linux/oom.h +++ b/include/linux/oom.h @@ -61,6 +61,9 @@ extern void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, extern int try_set_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags); extern void clear_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags); +extern void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask, + int order, const nodemask_t *nodemask); + extern enum oom_scan_t oom_scan_process_thread(struct task_struct *task, unsigned long totalpages, const nodemask_t *nodemask, bool force_kill); diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 77a29cea5d76..0f692a2dbfcb 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1469,8 +1469,8 @@ static u64 mem_cgroup_get_limit(struct mem_cgroup *memcg) return min(limit, memsw); } -void __mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, - int order) +void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, + int order) { struct mem_cgroup *iter; unsigned long chosen_points = 0; @@ -1478,6 +1478,17 @@ void __mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, unsigned int points = 0; struct task_struct *chosen = NULL; + /* + * If current has a pending SIGKILL, then automatically select it. The + * goal is to allow it to allocate so that it may quickly exit and free + * its memory. + */ + if (fatal_signal_pending(current)) { + set_thread_flag(TIF_MEMDIE); + return; + } + + 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; diff --git a/mm/oom_kill.c b/mm/oom_kill.c index a3a32ae02e9d..198600861638 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -556,8 +556,8 @@ void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, /* * Determines whether the kernel must panic because of the panic_on_oom sysctl. */ -static void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask, - int order, const nodemask_t *nodemask) +void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask, + int order, const nodemask_t *nodemask) { if (likely(!sysctl_panic_on_oom)) return; @@ -575,25 +575,6 @@ static void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask, sysctl_panic_on_oom == 2 ? "compulsory" : "system-wide"); } -#ifdef CONFIG_MEMCG -void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, - int order) -{ - /* - * If current has a pending SIGKILL, then automatically select it. The - * goal is to allow it to allocate so that it may quickly exit and free - * its memory. - */ - if (fatal_signal_pending(current)) { - set_thread_flag(TIF_MEMDIE); - return; - } - - check_panic_on_oom(CONSTRAINT_MEMCG, gfp_mask, order, NULL); - __mem_cgroup_out_of_memory(memcg, gfp_mask, order); -} -#endif - static BLOCKING_NOTIFIER_HEAD(oom_notify_list); int register_oom_notifier(struct notifier_block *nb) -- cgit v1.2.3 From ee6f509c3274014d1f52e7a7a10aee9f85393c5e Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Tue, 31 Jul 2012 16:43:50 -0700 Subject: mm: factor out memory isolate functions mm/page_alloc.c has some memory isolation functions but they are used only when we enable CONFIG_{CMA|MEMORY_HOTPLUG|MEMORY_FAILURE}. So let's make it configurable by new CONFIG_MEMORY_ISOLATION so that it can reduce binary size and we can check it simple by CONFIG_MEMORY_ISOLATION, not if defined CONFIG_{CMA|MEMORY_HOTPLUG|MEMORY_FAILURE}. Signed-off-by: Minchan Kim Cc: Andi Kleen Cc: Marek Szyprowski Acked-by: KAMEZAWA Hiroyuki Cc: KOSAKI Motohiro Cc: Mel Gorman Cc: Michal Hocko Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/base/Kconfig | 1 + include/linux/page-isolation.h | 13 ++++--- mm/Kconfig | 5 +++ mm/Makefile | 5 +-- mm/page_alloc.c | 80 +++--------------------------------------- mm/page_isolation.c | 71 +++++++++++++++++++++++++++++++++++++ 6 files changed, 93 insertions(+), 82 deletions(-) (limited to 'include') diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 9b21469482ae..08b4c5209384 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -196,6 +196,7 @@ config CMA bool "Contiguous Memory Allocator (EXPERIMENTAL)" depends on HAVE_DMA_CONTIGUOUS && HAVE_MEMBLOCK && EXPERIMENTAL select MIGRATION + select MEMORY_ISOLATION help This enables the Contiguous Memory Allocator which allows drivers to allocate big physically-contiguous blocks of memory for use with diff --git a/include/linux/page-isolation.h b/include/linux/page-isolation.h index 3bdcab30ca41..105077aa7685 100644 --- a/include/linux/page-isolation.h +++ b/include/linux/page-isolation.h @@ -1,6 +1,11 @@ #ifndef __LINUX_PAGEISOLATION_H #define __LINUX_PAGEISOLATION_H + +bool has_unmovable_pages(struct zone *zone, struct page *page, int count); +void set_pageblock_migratetype(struct page *page, int migratetype); +int move_freepages_block(struct zone *zone, struct page *page, + int migratetype); /* * Changes migrate type in [start_pfn, end_pfn) to be MIGRATE_ISOLATE. * If specified range includes migrate types other than MOVABLE or CMA, @@ -10,7 +15,7 @@ * free all pages in the range. test_page_isolated() can be used for * test it. */ -extern int +int start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn, unsigned migratetype); @@ -18,7 +23,7 @@ start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn, * Changes MIGRATE_ISOLATE to MIGRATE_MOVABLE. * target range is [start_pfn, end_pfn) */ -extern int +int undo_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn, unsigned migratetype); @@ -30,8 +35,8 @@ int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn); /* * Internal functions. Changes pageblock's migrate type. */ -extern int set_migratetype_isolate(struct page *page); -extern void unset_migratetype_isolate(struct page *page, unsigned migratetype); +int set_migratetype_isolate(struct page *page); +void unset_migratetype_isolate(struct page *page, unsigned migratetype); #endif diff --git a/mm/Kconfig b/mm/Kconfig index 82fed4eb2b6f..d5c8019c6627 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -140,9 +140,13 @@ config ARCH_DISCARD_MEMBLOCK config NO_BOOTMEM boolean +config MEMORY_ISOLATION + boolean + # eventually, we can have this option just 'select SPARSEMEM' config MEMORY_HOTPLUG bool "Allow for memory hot-add" + select MEMORY_ISOLATION depends on SPARSEMEM || X86_64_ACPI_NUMA depends on HOTPLUG && ARCH_ENABLE_MEMORY_HOTPLUG depends on (IA64 || X86 || PPC_BOOK3S_64 || SUPERH || S390) @@ -272,6 +276,7 @@ config MEMORY_FAILURE depends on MMU depends on ARCH_SUPPORTS_MEMORY_FAILURE bool "Enable recovery from hardware memory errors" + select MEMORY_ISOLATION help Enables code to recover from some memory failures on systems with MCA recovery. This allows a system to continue running diff --git a/mm/Makefile b/mm/Makefile index 290bbfe33698..92753e2d82da 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -15,8 +15,8 @@ obj-y := filemap.o mempool.o oom_kill.o fadvise.o \ maccess.o page_alloc.o page-writeback.o \ readahead.o swap.o truncate.o vmscan.o shmem.o \ prio_tree.o util.o mmzone.o vmstat.o backing-dev.o \ - page_isolation.o mm_init.o mmu_context.o percpu.o \ - compaction.o slab_common.o $(mmu-y) + mm_init.o mmu_context.o percpu.o slab_common.o \ + compaction.o $(mmu-y) obj-y += init-mm.o @@ -56,3 +56,4 @@ obj-$(CONFIG_HWPOISON_INJECT) += hwpoison-inject.o obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o obj-$(CONFIG_CLEANCACHE) += cleancache.o +obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 667338e80e94..228194728ccd 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -51,7 +51,6 @@ #include #include #include -#include #include #include #include @@ -219,7 +218,7 @@ EXPORT_SYMBOL(nr_online_nodes); int page_group_by_mobility_disabled __read_mostly; -static void set_pageblock_migratetype(struct page *page, int migratetype) +void set_pageblock_migratetype(struct page *page, int migratetype) { if (unlikely(page_group_by_mobility_disabled)) @@ -954,7 +953,7 @@ static int move_freepages(struct zone *zone, return pages_moved; } -static int move_freepages_block(struct zone *zone, struct page *page, +int move_freepages_block(struct zone *zone, struct page *page, int migratetype) { unsigned long start_pfn, end_pfn; @@ -5463,8 +5462,7 @@ void set_pageblock_flags_group(struct page *page, unsigned long flags, * MIGRATE_MOVABLE block might include unmovable pages. It means you can't * expect this function should be exact. */ -static bool -__has_unmovable_pages(struct zone *zone, struct page *page, int count) +bool has_unmovable_pages(struct zone *zone, struct page *page, int count) { unsigned long pfn, iter, found; int mt; @@ -5541,77 +5539,7 @@ bool is_pageblock_removable_nolock(struct page *page) zone->zone_start_pfn + zone->spanned_pages <= pfn) return false; - return !__has_unmovable_pages(zone, page, 0); -} - -int set_migratetype_isolate(struct page *page) -{ - struct zone *zone; - unsigned long flags, pfn; - struct memory_isolate_notify arg; - int notifier_ret; - int ret = -EBUSY; - - zone = page_zone(page); - - spin_lock_irqsave(&zone->lock, flags); - - pfn = page_to_pfn(page); - arg.start_pfn = pfn; - arg.nr_pages = pageblock_nr_pages; - arg.pages_found = 0; - - /* - * It may be possible to isolate a pageblock even if the - * migratetype is not MIGRATE_MOVABLE. The memory isolation - * notifier chain is used by balloon drivers to return the - * number of pages in a range that are held by the balloon - * driver to shrink memory. If all the pages are accounted for - * by balloons, are free, or on the LRU, isolation can continue. - * Later, for example, when memory hotplug notifier runs, these - * pages reported as "can be isolated" should be isolated(freed) - * by the balloon driver through the memory notifier chain. - */ - notifier_ret = memory_isolate_notify(MEM_ISOLATE_COUNT, &arg); - notifier_ret = notifier_to_errno(notifier_ret); - if (notifier_ret) - goto out; - /* - * FIXME: Now, memory hotplug doesn't call shrink_slab() by itself. - * We just check MOVABLE pages. - */ - if (!__has_unmovable_pages(zone, page, arg.pages_found)) - ret = 0; - /* - * Unmovable means "not-on-lru" pages. If Unmovable pages are - * larger than removable-by-driver pages reported by notifier, - * we'll fail. - */ - -out: - if (!ret) { - set_pageblock_migratetype(page, MIGRATE_ISOLATE); - move_freepages_block(zone, page, MIGRATE_ISOLATE); - } - - spin_unlock_irqrestore(&zone->lock, flags); - if (!ret) - drain_all_pages(); - return ret; -} - -void unset_migratetype_isolate(struct page *page, unsigned migratetype) -{ - struct zone *zone; - unsigned long flags; - zone = page_zone(page); - spin_lock_irqsave(&zone->lock, flags); - if (get_pageblock_migratetype(page) != MIGRATE_ISOLATE) - goto out; - set_pageblock_migratetype(page, migratetype); - move_freepages_block(zone, page, migratetype); -out: - spin_unlock_irqrestore(&zone->lock, flags); + return !has_unmovable_pages(zone, page, 0); } #ifdef CONFIG_CMA diff --git a/mm/page_isolation.c b/mm/page_isolation.c index c9f04774f2b8..fb482cf438da 100644 --- a/mm/page_isolation.c +++ b/mm/page_isolation.c @@ -5,8 +5,79 @@ #include #include #include +#include #include "internal.h" +int set_migratetype_isolate(struct page *page) +{ + struct zone *zone; + unsigned long flags, pfn; + struct memory_isolate_notify arg; + int notifier_ret; + int ret = -EBUSY; + + zone = page_zone(page); + + spin_lock_irqsave(&zone->lock, flags); + + pfn = page_to_pfn(page); + arg.start_pfn = pfn; + arg.nr_pages = pageblock_nr_pages; + arg.pages_found = 0; + + /* + * It may be possible to isolate a pageblock even if the + * migratetype is not MIGRATE_MOVABLE. The memory isolation + * notifier chain is used by balloon drivers to return the + * number of pages in a range that are held by the balloon + * driver to shrink memory. If all the pages are accounted for + * by balloons, are free, or on the LRU, isolation can continue. + * Later, for example, when memory hotplug notifier runs, these + * pages reported as "can be isolated" should be isolated(freed) + * by the balloon driver through the memory notifier chain. + */ + notifier_ret = memory_isolate_notify(MEM_ISOLATE_COUNT, &arg); + notifier_ret = notifier_to_errno(notifier_ret); + if (notifier_ret) + goto out; + /* + * FIXME: Now, memory hotplug doesn't call shrink_slab() by itself. + * We just check MOVABLE pages. + */ + if (!has_unmovable_pages(zone, page, arg.pages_found)) + ret = 0; + + /* + * immobile means "not-on-lru" paes. If immobile is larger than + * removable-by-driver pages reported by notifier, we'll fail. + */ + +out: + if (!ret) { + set_pageblock_migratetype(page, MIGRATE_ISOLATE); + move_freepages_block(zone, page, MIGRATE_ISOLATE); + } + + spin_unlock_irqrestore(&zone->lock, flags); + if (!ret) + drain_all_pages(); + return ret; +} + +void unset_migratetype_isolate(struct page *page, unsigned migratetype) +{ + struct zone *zone; + unsigned long flags; + zone = page_zone(page); + spin_lock_irqsave(&zone->lock, flags); + if (get_pageblock_migratetype(page) != MIGRATE_ISOLATE) + goto out; + set_pageblock_migratetype(page, migratetype); + move_freepages_block(zone, page, migratetype); +out: + spin_unlock_irqrestore(&zone->lock, flags); +} + static inline struct page * __first_valid_page(unsigned long pfn, unsigned long nr_pages) { -- cgit v1.2.3 From 702d1a6e0766d45642c934444fd41f658d251305 Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Tue, 31 Jul 2012 16:43:56 -0700 Subject: memory-hotplug: fix kswapd looping forever problem When hotplug offlining happens on zone A, it starts to mark freed page as MIGRATE_ISOLATE type in buddy for preventing further allocation. (MIGRATE_ISOLATE is very irony type because it's apparently on buddy but we can't allocate them). When the memory shortage happens during hotplug offlining, current task starts to reclaim, then wake up kswapd. Kswapd checks watermark, then go sleep because current zone_watermark_ok_safe doesn't consider MIGRATE_ISOLATE freed page count. Current task continue to reclaim in direct reclaim path without kswapd's helping. The problem is that zone->all_unreclaimable is set by only kswapd so that current task would be looping forever like below. __alloc_pages_slowpath restart: wake_all_kswapd rebalance: __alloc_pages_direct_reclaim do_try_to_free_pages if global_reclaim && !all_unreclaimable return 1; /* It means we did did_some_progress */ skip __alloc_pages_may_oom should_alloc_retry goto rebalance; If we apply KOSAKI's patch[1] which doesn't depends on kswapd about setting zone->all_unreclaimable, we can solve this problem by killing some task in direct reclaim path. But it doesn't wake up kswapd, still. It could be a problem still if other subsystem needs GFP_ATOMIC request. So kswapd should consider MIGRATE_ISOLATE when it calculate free pages BEFORE going sleep. This patch counts the number of MIGRATE_ISOLATE page block and zone_watermark_ok_safe will consider it if the system has such blocks (fortunately, it's very rare so no problem in POV overhead and kswapd is never hotpath). Copy/modify from Mel's quote " Ideal solution would be "allocating" the pageblock. It would keep the free space accounting as it is but historically, memory hotplug didn't allocate pages because it would be difficult to detect if a pageblock was isolated or if part of some balloon. Allocating just full pageblocks would work around this, However, it would play very badly with CMA. " [1] http://lkml.org/lkml/2012/6/14/74 [akpm@linux-foundation.org: simplify nr_zone_isolate_freepages(), rework zone_watermark_ok_safe() comment, simplify set_pageblock_isolate() and restore_pageblock_isolate()] [akpm@linux-foundation.org: fix CONFIG_MEMORY_ISOLATION=n build] Signed-off-by: Minchan Kim Suggested-by: KOSAKI Motohiro Tested-by: Aaditya Kumar Cc: KAMEZAWA Hiroyuki Cc: Mel Gorman Cc: Michal Hocko Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 8 ++++++++ mm/page_alloc.c | 30 ++++++++++++++++++++++++++++++ mm/page_isolation.c | 26 ++++++++++++++++++++++++-- 3 files changed, 62 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 98f079bcf399..64b2c3a48286 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -478,6 +478,14 @@ struct zone { * rarely used fields: */ const char *name; +#ifdef CONFIG_MEMORY_ISOLATION + /* + * the number of MIGRATE_ISOLATE *pageblock*. + * We need this for free page counting. Look at zone_watermark_ok_safe. + * It's protected by zone->lock + */ + int nr_pageblock_isolate; +#endif } ____cacheline_internodealigned_in_smp; typedef enum { diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 2e6635993558..6a29ed8e6e60 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -218,6 +218,11 @@ EXPORT_SYMBOL(nr_online_nodes); int page_group_by_mobility_disabled __read_mostly; +/* + * NOTE: + * Don't use set_pageblock_migratetype(page, MIGRATE_ISOLATE) directly. + * Instead, use {un}set_pageblock_isolate. + */ void set_pageblock_migratetype(struct page *page, int migratetype) { @@ -1619,6 +1624,20 @@ static bool __zone_watermark_ok(struct zone *z, int order, unsigned long mark, return true; } +#ifdef CONFIG_MEMORY_ISOLATION +static inline unsigned long nr_zone_isolate_freepages(struct zone *zone) +{ + if (unlikely(zone->nr_pageblock_isolate)) + return zone->nr_pageblock_isolate * pageblock_nr_pages; + return 0; +} +#else +static inline unsigned long nr_zone_isolate_freepages(struct zone *zone) +{ + return 0; +} +#endif + bool zone_watermark_ok(struct zone *z, int order, unsigned long mark, int classzone_idx, int alloc_flags) { @@ -1634,6 +1653,14 @@ bool zone_watermark_ok_safe(struct zone *z, int order, unsigned long mark, if (z->percpu_drift_mark && free_pages < z->percpu_drift_mark) free_pages = zone_page_state_snapshot(z, NR_FREE_PAGES); + /* + * If the zone has MIGRATE_ISOLATE type free pages, we should consider + * it. nr_zone_isolate_freepages is never accurate so kswapd might not + * sleep although it could do so. But this is more desirable for memory + * hotplug than sleeping which can cause a livelock in the direct + * reclaim path. + */ + free_pages -= nr_zone_isolate_freepages(z); return __zone_watermark_ok(z, order, mark, classzone_idx, alloc_flags, free_pages); } @@ -4398,6 +4425,9 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat, lruvec_init(&zone->lruvec, zone); zap_zone_vm_stats(zone); zone->flags = 0; +#ifdef CONFIG_MEMORY_ISOLATION + zone->nr_pageblock_isolate = 0; +#endif if (!size) continue; diff --git a/mm/page_isolation.c b/mm/page_isolation.c index fb482cf438da..247d1f175739 100644 --- a/mm/page_isolation.c +++ b/mm/page_isolation.c @@ -8,6 +8,28 @@ #include #include "internal.h" +/* called while holding zone->lock */ +static void set_pageblock_isolate(struct page *page) +{ + if (get_pageblock_migratetype(page) == MIGRATE_ISOLATE) + return; + + set_pageblock_migratetype(page, MIGRATE_ISOLATE); + page_zone(page)->nr_pageblock_isolate++; +} + +/* called while holding zone->lock */ +static void restore_pageblock_isolate(struct page *page, int migratetype) +{ + struct zone *zone = page_zone(page); + if (WARN_ON(get_pageblock_migratetype(page) != MIGRATE_ISOLATE)) + return; + + BUG_ON(zone->nr_pageblock_isolate <= 0); + set_pageblock_migratetype(page, migratetype); + zone->nr_pageblock_isolate--; +} + int set_migratetype_isolate(struct page *page) { struct zone *zone; @@ -54,7 +76,7 @@ int set_migratetype_isolate(struct page *page) out: if (!ret) { - set_pageblock_migratetype(page, MIGRATE_ISOLATE); + set_pageblock_isolate(page); move_freepages_block(zone, page, MIGRATE_ISOLATE); } @@ -72,8 +94,8 @@ void unset_migratetype_isolate(struct page *page, unsigned migratetype) spin_lock_irqsave(&zone->lock, flags); if (get_pageblock_migratetype(page) != MIGRATE_ISOLATE) goto out; - set_pageblock_migratetype(page, migratetype); move_freepages_block(zone, page, migratetype); + restore_pageblock_isolate(page, migratetype); out: spin_unlock_irqrestore(&zone->lock, flags); } -- cgit v1.2.3 From 072bb0aa5e062902968c5c1007bba332c7820cf4 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:43:58 -0700 Subject: mm: sl[au]b: add knowledge of PFMEMALLOC reserve pages When a user or administrator requires swap for their application, they create a swap partition and file, format it with mkswap and activate it with swapon. Swap over the network is considered as an option in diskless systems. The two likely scenarios are when blade servers are used as part of a cluster where the form factor or maintenance costs do not allow the use of disks and thin clients. The Linux Terminal Server Project recommends the use of the Network Block Device (NBD) for swap according to the manual at https://sourceforge.net/projects/ltsp/files/Docs-Admin-Guide/LTSPManual.pdf/download There is also documentation and tutorials on how to setup swap over NBD at places like https://help.ubuntu.com/community/UbuntuLTSP/EnableNBDSWAP The nbd-client also documents the use of NBD as swap. Despite this, the fact is that a machine using NBD for swap can deadlock within minutes if swap is used intensively. This patch series addresses the problem. The core issue is that network block devices do not use mempools like normal block devices do. As the host cannot control where they receive packets from, they cannot reliably work out in advance how much memory they might need. Some years ago, Peter Zijlstra developed a series of patches that supported swap over an NFS that at least one distribution is carrying within their kernels. This patch series borrows very heavily from Peter's work to support swapping over NBD as a pre-requisite to supporting swap-over-NFS. The bulk of the complexity is concerned with preserving memory that is allocated from the PFMEMALLOC reserves for use by the network layer which is needed for both NBD and NFS. Patch 1 adds knowledge of the PFMEMALLOC reserves to SLAB and SLUB to preserve access to pages allocated under low memory situations to callers that are freeing memory. Patch 2 optimises the SLUB fast path to avoid pfmemalloc checks Patch 3 introduces __GFP_MEMALLOC to allow access to the PFMEMALLOC reserves without setting PFMEMALLOC. Patch 4 opens the possibility for softirqs to use PFMEMALLOC reserves for later use by network packet processing. Patch 5 only sets page->pfmemalloc when ALLOC_NO_WATERMARKS was required Patch 6 ignores memory policies when ALLOC_NO_WATERMARKS is set. Patches 7-12 allows network processing to use PFMEMALLOC reserves when the socket has been marked as being used by the VM to clean pages. If packets are received and stored in pages that were allocated under low-memory situations and are unrelated to the VM, the packets are dropped. Patch 11 reintroduces __skb_alloc_page which the networking folk may object to but is needed in some cases to propogate pfmemalloc from a newly allocated page to an skb. If there is a strong objection, this patch can be dropped with the impact being that swap-over-network will be slower in some cases but it should not fail. Patch 13 is a micro-optimisation to avoid a function call in the common case. Patch 14 tags NBD sockets as being SOCK_MEMALLOC so they can use PFMEMALLOC if necessary. Patch 15 notes that it is still possible for the PFMEMALLOC reserve to be depleted. To prevent this, direct reclaimers get throttled on a waitqueue if 50% of the PFMEMALLOC reserves are depleted. It is expected that kswapd and the direct reclaimers already running will clean enough pages for the low watermark to be reached and the throttled processes are woken up. Patch 16 adds a statistic to track how often processes get throttled Some basic performance testing was run using kernel builds, netperf on loopback for UDP and TCP, hackbench (pipes and sockets), iozone and sysbench. Each of them were expected to use the sl*b allocators reasonably heavily but there did not appear to be significant performance variances. For testing swap-over-NBD, a machine was booted with 2G of RAM with a swapfile backed by NBD. 8*NUM_CPU processes were started that create anonymous memory mappings and read them linearly in a loop. The total size of the mappings were 4*PHYSICAL_MEMORY to use swap heavily under memory pressure. Without the patches and using SLUB, the machine locks up within minutes and runs to completion with them applied. With SLAB, the story is different as an unpatched kernel run to completion. However, the patched kernel completed the test 45% faster. MICRO 3.5.0-rc2 3.5.0-rc2 vanilla swapnbd Unrecognised test vmscan-anon-mmap-write MMTests Statistics: duration Sys Time Running Test (seconds) 197.80 173.07 User+Sys Time Running Test (seconds) 206.96 182.03 Total Elapsed Time (seconds) 3240.70 1762.09 This patch: mm: sl[au]b: add knowledge of PFMEMALLOC reserve pages Allocations of pages below the min watermark run a risk of the machine hanging due to a lack of memory. To prevent this, only callers who have PF_MEMALLOC or TIF_MEMDIE set and are not processing an interrupt are allowed to allocate with ALLOC_NO_WATERMARKS. Once they are allocated to a slab though, nothing prevents other callers consuming free objects within those slabs. This patch limits access to slab pages that were alloced from the PFMEMALLOC reserves. When this patch is applied, pages allocated from below the low watermark are returned with page->pfmemalloc set and it is up to the caller to determine how the page should be protected. SLAB restricts access to any page with page->pfmemalloc set to callers which are known to able to access the PFMEMALLOC reserve. If one is not available, an attempt is made to allocate a new page rather than use a reserve. SLUB is a bit more relaxed in that it only records if the current per-CPU page was allocated from PFMEMALLOC reserve and uses another partial slab if the caller does not have the necessary GFP or process flags. This was found to be sufficient in tests to avoid hangs due to SLUB generally maintaining smaller lists than SLAB. In low-memory conditions it does mean that !PFMEMALLOC allocators can fail a slab allocation even though free objects are available because they are being preserved for callers that are freeing pages. [a.p.zijlstra@chello.nl: Original implementation] [sebastian@breakpoint.cc: Correct order of page flag clearing] Signed-off-by: Mel Gorman Cc: David Miller Cc: Neil Brown Cc: Peter Zijlstra Cc: Mike Christie Cc: Eric B Munson Cc: Eric Dumazet Cc: Sebastian Andrzej Siewior Cc: Mel Gorman Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm_types.h | 9 +++ include/linux/page-flags.h | 29 +++++++ mm/internal.h | 3 + mm/page_alloc.c | 27 +++++-- mm/slab.c | 192 ++++++++++++++++++++++++++++++++++++++++----- mm/slub.c | 29 ++++++- 6 files changed, 264 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 074eb98fe15d..375e79eb009b 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -54,6 +54,15 @@ struct page { union { pgoff_t index; /* Our offset within mapping. */ void *freelist; /* slub/slob first free object */ + bool pfmemalloc; /* If set by the page allocator, + * ALLOC_PFMEMALLOC was set + * and the low watermark was not + * met implying that the system + * is under some pressure. The + * caller should try ensure + * this page is only used to + * free other pages. + */ }; union { diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index c88d2a9451af..b5d13841604e 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -7,6 +7,7 @@ #include #include +#include #ifndef __GENERATING_BOUNDS_H #include #include @@ -453,6 +454,34 @@ static inline int PageTransTail(struct page *page) } #endif +/* + * If network-based swap is enabled, sl*b must keep track of whether pages + * were allocated from pfmemalloc reserves. + */ +static inline int PageSlabPfmemalloc(struct page *page) +{ + VM_BUG_ON(!PageSlab(page)); + return PageActive(page); +} + +static inline void SetPageSlabPfmemalloc(struct page *page) +{ + VM_BUG_ON(!PageSlab(page)); + SetPageActive(page); +} + +static inline void __ClearPageSlabPfmemalloc(struct page *page) +{ + VM_BUG_ON(!PageSlab(page)); + __ClearPageActive(page); +} + +static inline void ClearPageSlabPfmemalloc(struct page *page) +{ + VM_BUG_ON(!PageSlab(page)); + ClearPageActive(page); +} + #ifdef CONFIG_MMU #define __PG_MLOCKED (1 << PG_mlocked) #else diff --git a/mm/internal.h b/mm/internal.h index 3314f79d775a..eb76b67890d0 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -279,6 +279,9 @@ static inline struct page *mem_map_next(struct page *iter, #define __paginginit __init #endif +/* Returns true if the gfp_mask allows use of ALLOC_NO_WATERMARK */ +bool gfp_pfmemalloc_allowed(gfp_t gfp_mask); + /* Memory initialisation debug and verification */ enum mminit_level { MMINIT_WARNING, diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 6a29ed8e6e60..38e5be65f24e 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1513,6 +1513,7 @@ failed: #define ALLOC_HARDER 0x10 /* try to alloc harder */ #define ALLOC_HIGH 0x20 /* __GFP_HIGH set */ #define ALLOC_CPUSET 0x40 /* check for correct cpuset */ +#define ALLOC_PFMEMALLOC 0x80 /* Caller has PF_MEMALLOC set */ #ifdef CONFIG_FAIL_PAGE_ALLOC @@ -2293,16 +2294,22 @@ gfp_to_alloc_flags(gfp_t gfp_mask) } else if (unlikely(rt_task(current)) && !in_interrupt()) alloc_flags |= ALLOC_HARDER; - if (likely(!(gfp_mask & __GFP_NOMEMALLOC))) { - if (!in_interrupt() && - ((current->flags & PF_MEMALLOC) || - unlikely(test_thread_flag(TIF_MEMDIE)))) + if ((current->flags & PF_MEMALLOC) || + unlikely(test_thread_flag(TIF_MEMDIE))) { + alloc_flags |= ALLOC_PFMEMALLOC; + + if (likely(!(gfp_mask & __GFP_NOMEMALLOC)) && !in_interrupt()) alloc_flags |= ALLOC_NO_WATERMARKS; } return alloc_flags; } +bool gfp_pfmemalloc_allowed(gfp_t gfp_mask) +{ + return !!(gfp_to_alloc_flags(gfp_mask) & ALLOC_PFMEMALLOC); +} + static inline struct page * __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, struct zonelist *zonelist, enum zone_type high_zoneidx, @@ -2490,10 +2497,18 @@ nopage: warn_alloc_failed(gfp_mask, order, NULL); return page; got_pg: + /* + * page->pfmemalloc is set when the caller had PFMEMALLOC set or is + * been OOM killed. The expectation is that the caller is taking + * steps that will free more memory. The caller should avoid the + * page being used for !PFMEMALLOC purposes. + */ + page->pfmemalloc = !!(alloc_flags & ALLOC_PFMEMALLOC); + if (kmemcheck_enabled) kmemcheck_pagealloc_alloc(page, order, gfp_mask); - return page; + return page; } /* @@ -2544,6 +2559,8 @@ retry_cpuset: page = __alloc_pages_slowpath(gfp_mask, order, zonelist, high_zoneidx, nodemask, preferred_zone, migratetype); + else + page->pfmemalloc = false; trace_mm_page_alloc(page, order, gfp_mask, migratetype); diff --git a/mm/slab.c b/mm/slab.c index 1fcf3ac94b6c..55d84a22ad96 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -124,6 +124,8 @@ #include +#include "internal.h" + /* * DEBUG - 1 for kmem_cache_create() to honour; SLAB_RED_ZONE & SLAB_POISON. * 0 for faster, smaller code (especially in the critical paths). @@ -152,6 +154,12 @@ #define ARCH_KMALLOC_FLAGS SLAB_HWCACHE_ALIGN #endif +/* + * true if a page was allocated from pfmemalloc reserves for network-based + * swap + */ +static bool pfmemalloc_active __read_mostly; + /* Legal flag mask for kmem_cache_create(). */ #if DEBUG # define CREATE_MASK (SLAB_RED_ZONE | \ @@ -257,9 +265,30 @@ struct array_cache { * Must have this definition in here for the proper * alignment of array_cache. Also simplifies accessing * the entries. + * + * Entries should not be directly dereferenced as + * entries belonging to slabs marked pfmemalloc will + * have the lower bits set SLAB_OBJ_PFMEMALLOC */ }; +#define SLAB_OBJ_PFMEMALLOC 1 +static inline bool is_obj_pfmemalloc(void *objp) +{ + return (unsigned long)objp & SLAB_OBJ_PFMEMALLOC; +} + +static inline void set_obj_pfmemalloc(void **objp) +{ + *objp = (void *)((unsigned long)*objp | SLAB_OBJ_PFMEMALLOC); + return; +} + +static inline void clear_obj_pfmemalloc(void **objp) +{ + *objp = (void *)((unsigned long)*objp & ~SLAB_OBJ_PFMEMALLOC); +} + /* * bootstrap: The caches do not work without cpuarrays anymore, but the * cpuarrays are allocated from the generic caches... @@ -900,6 +929,102 @@ static struct array_cache *alloc_arraycache(int node, int entries, return nc; } +static inline bool is_slab_pfmemalloc(struct slab *slabp) +{ + struct page *page = virt_to_page(slabp->s_mem); + + return PageSlabPfmemalloc(page); +} + +/* Clears pfmemalloc_active if no slabs have pfmalloc set */ +static void recheck_pfmemalloc_active(struct kmem_cache *cachep, + struct array_cache *ac) +{ + struct kmem_list3 *l3 = cachep->nodelists[numa_mem_id()]; + struct slab *slabp; + unsigned long flags; + + if (!pfmemalloc_active) + return; + + spin_lock_irqsave(&l3->list_lock, flags); + list_for_each_entry(slabp, &l3->slabs_full, list) + if (is_slab_pfmemalloc(slabp)) + goto out; + + list_for_each_entry(slabp, &l3->slabs_partial, list) + if (is_slab_pfmemalloc(slabp)) + goto out; + + list_for_each_entry(slabp, &l3->slabs_free, list) + if (is_slab_pfmemalloc(slabp)) + goto out; + + pfmemalloc_active = false; +out: + spin_unlock_irqrestore(&l3->list_lock, flags); +} + +static void *ac_get_obj(struct kmem_cache *cachep, struct array_cache *ac, + gfp_t flags, bool force_refill) +{ + int i; + void *objp = ac->entry[--ac->avail]; + + /* Ensure the caller is allowed to use objects from PFMEMALLOC slab */ + if (unlikely(is_obj_pfmemalloc(objp))) { + struct kmem_list3 *l3; + + if (gfp_pfmemalloc_allowed(flags)) { + clear_obj_pfmemalloc(&objp); + return objp; + } + + /* The caller cannot use PFMEMALLOC objects, find another one */ + for (i = 1; i < ac->avail; i++) { + /* If a !PFMEMALLOC object is found, swap them */ + if (!is_obj_pfmemalloc(ac->entry[i])) { + objp = ac->entry[i]; + ac->entry[i] = ac->entry[ac->avail]; + ac->entry[ac->avail] = objp; + return objp; + } + } + + /* + * If there are empty slabs on the slabs_free list and we are + * being forced to refill the cache, mark this one !pfmemalloc. + */ + l3 = cachep->nodelists[numa_mem_id()]; + if (!list_empty(&l3->slabs_free) && force_refill) { + struct slab *slabp = virt_to_slab(objp); + ClearPageSlabPfmemalloc(virt_to_page(slabp->s_mem)); + clear_obj_pfmemalloc(&objp); + recheck_pfmemalloc_active(cachep, ac); + return objp; + } + + /* No !PFMEMALLOC objects available */ + ac->avail++; + objp = NULL; + } + + return objp; +} + +static void ac_put_obj(struct kmem_cache *cachep, struct array_cache *ac, + void *objp) +{ + if (unlikely(pfmemalloc_active)) { + /* Some pfmemalloc slabs exist, check if this is one */ + struct page *page = virt_to_page(objp); + if (PageSlabPfmemalloc(page)) + set_obj_pfmemalloc(&objp); + } + + ac->entry[ac->avail++] = objp; +} + /* * Transfer objects in one arraycache to another. * Locking must be handled by the caller. @@ -1076,7 +1201,7 @@ static inline int cache_free_alien(struct kmem_cache *cachep, void *objp) STATS_INC_ACOVERFLOW(cachep); __drain_alien_cache(cachep, alien, nodeid); } - alien->entry[alien->avail++] = objp; + ac_put_obj(cachep, alien, objp); spin_unlock(&alien->lock); } else { spin_lock(&(cachep->nodelists[nodeid])->list_lock); @@ -1759,6 +1884,10 @@ static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid) return NULL; } + /* Record if ALLOC_PFMEMALLOC was set when allocating the slab */ + if (unlikely(page->pfmemalloc)) + pfmemalloc_active = true; + nr_pages = (1 << cachep->gfporder); if (cachep->flags & SLAB_RECLAIM_ACCOUNT) add_zone_page_state(page_zone(page), @@ -1766,9 +1895,13 @@ static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid) else add_zone_page_state(page_zone(page), NR_SLAB_UNRECLAIMABLE, nr_pages); - for (i = 0; i < nr_pages; i++) + for (i = 0; i < nr_pages; i++) { __SetPageSlab(page + i); + if (page->pfmemalloc) + SetPageSlabPfmemalloc(page + i); + } + if (kmemcheck_enabled && !(cachep->flags & SLAB_NOTRACK)) { kmemcheck_alloc_shadow(page, cachep->gfporder, flags, nodeid); @@ -1800,6 +1933,7 @@ static void kmem_freepages(struct kmem_cache *cachep, void *addr) NR_SLAB_UNRECLAIMABLE, nr_freed); while (i--) { BUG_ON(!PageSlab(page)); + __ClearPageSlabPfmemalloc(page); __ClearPageSlab(page); page++; } @@ -3015,16 +3149,19 @@ bad: #define check_slabp(x,y) do { } while(0) #endif -static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags) +static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags, + bool force_refill) { int batchcount; struct kmem_list3 *l3; struct array_cache *ac; int node; -retry: check_irq_off(); node = numa_mem_id(); + if (unlikely(force_refill)) + goto force_grow; +retry: ac = cpu_cache_get(cachep); batchcount = ac->batchcount; if (!ac->touched && batchcount > BATCHREFILL_LIMIT) { @@ -3074,8 +3211,8 @@ retry: STATS_INC_ACTIVE(cachep); STATS_SET_HIGH(cachep); - ac->entry[ac->avail++] = slab_get_obj(cachep, slabp, - node); + ac_put_obj(cachep, ac, slab_get_obj(cachep, slabp, + node)); } check_slabp(cachep, slabp); @@ -3094,18 +3231,22 @@ alloc_done: if (unlikely(!ac->avail)) { int x; +force_grow: x = cache_grow(cachep, flags | GFP_THISNODE, node, NULL); /* cache_grow can reenable interrupts, then ac could change. */ ac = cpu_cache_get(cachep); - if (!x && ac->avail == 0) /* no objects in sight? abort */ + + /* no objects in sight? abort */ + if (!x && (ac->avail == 0 || force_refill)) return NULL; if (!ac->avail) /* objects refilled by interrupt? */ goto retry; } ac->touched = 1; - return ac->entry[--ac->avail]; + + return ac_get_obj(cachep, ac, flags, force_refill); } static inline void cache_alloc_debugcheck_before(struct kmem_cache *cachep, @@ -3187,23 +3328,35 @@ static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags) { void *objp; struct array_cache *ac; + bool force_refill = false; check_irq_off(); ac = cpu_cache_get(cachep); if (likely(ac->avail)) { - STATS_INC_ALLOCHIT(cachep); ac->touched = 1; - objp = ac->entry[--ac->avail]; - } else { - STATS_INC_ALLOCMISS(cachep); - objp = cache_alloc_refill(cachep, flags); + objp = ac_get_obj(cachep, ac, flags, false); + /* - * the 'ac' may be updated by cache_alloc_refill(), - * and kmemleak_erase() requires its correct value. + * Allow for the possibility all avail objects are not allowed + * by the current flags */ - ac = cpu_cache_get(cachep); + if (objp) { + STATS_INC_ALLOCHIT(cachep); + goto out; + } + force_refill = true; } + + STATS_INC_ALLOCMISS(cachep); + objp = cache_alloc_refill(cachep, flags, force_refill); + /* + * the 'ac' may be updated by cache_alloc_refill(), + * and kmemleak_erase() requires its correct value. + */ + ac = cpu_cache_get(cachep); + +out: /* * To avoid a false negative, if an object that is in one of the * per-CPU caches is leaked, we need to make sure kmemleak doesn't @@ -3525,9 +3678,12 @@ static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects, struct kmem_list3 *l3; for (i = 0; i < nr_objects; i++) { - void *objp = objpp[i]; + void *objp; struct slab *slabp; + clear_obj_pfmemalloc(&objpp[i]); + objp = objpp[i]; + slabp = virt_to_slab(objp); l3 = cachep->nodelists[node]; list_del(&slabp->list); @@ -3645,7 +3801,7 @@ static inline void __cache_free(struct kmem_cache *cachep, void *objp, cache_flusharray(cachep, ac); } - ac->entry[ac->avail++] = objp; + ac_put_obj(cachep, ac, objp); } /** diff --git a/mm/slub.c b/mm/slub.c index e517d435e5dc..c3f05e1599c0 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -34,6 +34,8 @@ #include +#include "internal.h" + /* * Lock order: * 1. slab_mutex (Global Mutex) @@ -1354,6 +1356,8 @@ static struct page *new_slab(struct kmem_cache *s, gfp_t flags, int node) inc_slabs_node(s, page_to_nid(page), page->objects); page->slab = s; __SetPageSlab(page); + if (page->pfmemalloc) + SetPageSlabPfmemalloc(page); start = page_address(page); @@ -1397,6 +1401,7 @@ static void __free_slab(struct kmem_cache *s, struct page *page) NR_SLAB_RECLAIMABLE : NR_SLAB_UNRECLAIMABLE, -pages); + __ClearPageSlabPfmemalloc(page); __ClearPageSlab(page); reset_page_mapcount(page); if (current->reclaim_state) @@ -2126,6 +2131,14 @@ static inline void *new_slab_objects(struct kmem_cache *s, gfp_t flags, return freelist; } +static inline bool pfmemalloc_match(struct page *page, gfp_t gfpflags) +{ + if (unlikely(PageSlabPfmemalloc(page))) + return gfp_pfmemalloc_allowed(gfpflags); + + return true; +} + /* * Check the page->freelist of a page and either transfer the freelist to the per cpu freelist * or deactivate the page. @@ -2206,6 +2219,18 @@ redo: goto new_slab; } + /* + * By rights, we should be searching for a slab page that was + * PFMEMALLOC but right now, we are losing the pfmemalloc + * information when the page leaves the per-cpu allocator + */ + if (unlikely(!pfmemalloc_match(page, gfpflags))) { + deactivate_slab(s, page, c->freelist); + c->page = NULL; + c->freelist = NULL; + goto new_slab; + } + /* must check again c->freelist in case of cpu migration or IRQ */ freelist = c->freelist; if (freelist) @@ -2312,8 +2337,8 @@ redo: object = c->freelist; page = c->page; - if (unlikely(!object || !node_match(page, node))) - + if (unlikely(!object || !node_match(page, node) || + !pfmemalloc_match(page, gfpflags))) object = __slab_alloc(s, gfpflags, node, addr, c); else { -- cgit v1.2.3 From b37f1dd0f543d9714f96c2f9b9f74f7bdfdfdf31 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:03 -0700 Subject: mm: introduce __GFP_MEMALLOC to allow access to emergency reserves __GFP_MEMALLOC will allow the allocation to disregard the watermarks, much like PF_MEMALLOC. It allows one to pass along the memalloc state in object related allocation flags as opposed to task related flags, such as sk->sk_allocation. This removes the need for ALLOC_PFMEMALLOC as callers using __GFP_MEMALLOC can get the ALLOC_NO_WATERMARK flag which is now enough to identify allocations related to page reclaim. Signed-off-by: Peter Zijlstra Signed-off-by: Mel Gorman Cc: David Miller Cc: Neil Brown Cc: Mike Christie Cc: Eric B Munson Cc: Eric Dumazet Cc: Sebastian Andrzej Siewior Cc: Mel Gorman Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/gfp.h | 10 ++++++++-- include/linux/mm_types.h | 2 +- include/trace/events/gfpflags.h | 1 + mm/page_alloc.c | 22 ++++++++++------------ mm/slab.c | 2 +- 5 files changed, 21 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/gfp.h b/include/linux/gfp.h index 1e49be49d324..cbd7400e5862 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -23,6 +23,7 @@ struct vm_area_struct; #define ___GFP_REPEAT 0x400u #define ___GFP_NOFAIL 0x800u #define ___GFP_NORETRY 0x1000u +#define ___GFP_MEMALLOC 0x2000u #define ___GFP_COMP 0x4000u #define ___GFP_ZERO 0x8000u #define ___GFP_NOMEMALLOC 0x10000u @@ -76,9 +77,14 @@ struct vm_area_struct; #define __GFP_REPEAT ((__force gfp_t)___GFP_REPEAT) /* See above */ #define __GFP_NOFAIL ((__force gfp_t)___GFP_NOFAIL) /* See above */ #define __GFP_NORETRY ((__force gfp_t)___GFP_NORETRY) /* See above */ +#define __GFP_MEMALLOC ((__force gfp_t)___GFP_MEMALLOC)/* Allow access to emergency reserves */ #define __GFP_COMP ((__force gfp_t)___GFP_COMP) /* Add compound page metadata */ #define __GFP_ZERO ((__force gfp_t)___GFP_ZERO) /* Return zeroed page on success */ -#define __GFP_NOMEMALLOC ((__force gfp_t)___GFP_NOMEMALLOC) /* Don't use emergency reserves */ +#define __GFP_NOMEMALLOC ((__force gfp_t)___GFP_NOMEMALLOC) /* Don't use emergency reserves. + * This takes precedence over the + * __GFP_MEMALLOC flag if both are + * set + */ #define __GFP_HARDWALL ((__force gfp_t)___GFP_HARDWALL) /* Enforce hardwall cpuset memory allocs */ #define __GFP_THISNODE ((__force gfp_t)___GFP_THISNODE)/* No fallback, no policies */ #define __GFP_RECLAIMABLE ((__force gfp_t)___GFP_RECLAIMABLE) /* Page is reclaimable */ @@ -129,7 +135,7 @@ struct vm_area_struct; /* Control page allocator reclaim behavior */ #define GFP_RECLAIM_MASK (__GFP_WAIT|__GFP_HIGH|__GFP_IO|__GFP_FS|\ __GFP_NOWARN|__GFP_REPEAT|__GFP_NOFAIL|\ - __GFP_NORETRY|__GFP_NOMEMALLOC) + __GFP_NORETRY|__GFP_MEMALLOC|__GFP_NOMEMALLOC) /* Control slab gfp mask during early boot */ #define GFP_BOOT_MASK (__GFP_BITS_MASK & ~(__GFP_WAIT|__GFP_IO|__GFP_FS)) diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 375e79eb009b..bf7867200b95 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -55,7 +55,7 @@ struct page { pgoff_t index; /* Our offset within mapping. */ void *freelist; /* slub/slob first free object */ bool pfmemalloc; /* If set by the page allocator, - * ALLOC_PFMEMALLOC was set + * ALLOC_NO_WATERMARKS was set * and the low watermark was not * met implying that the system * is under some pressure. The diff --git a/include/trace/events/gfpflags.h b/include/trace/events/gfpflags.h index 9fe3a36646e9..d6fd8e5b14b7 100644 --- a/include/trace/events/gfpflags.h +++ b/include/trace/events/gfpflags.h @@ -30,6 +30,7 @@ {(unsigned long)__GFP_COMP, "GFP_COMP"}, \ {(unsigned long)__GFP_ZERO, "GFP_ZERO"}, \ {(unsigned long)__GFP_NOMEMALLOC, "GFP_NOMEMALLOC"}, \ + {(unsigned long)__GFP_MEMALLOC, "GFP_MEMALLOC"}, \ {(unsigned long)__GFP_HARDWALL, "GFP_HARDWALL"}, \ {(unsigned long)__GFP_THISNODE, "GFP_THISNODE"}, \ {(unsigned long)__GFP_RECLAIMABLE, "GFP_RECLAIMABLE"}, \ diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 38e5be65f24e..8f65abeb9ad6 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1513,7 +1513,6 @@ failed: #define ALLOC_HARDER 0x10 /* try to alloc harder */ #define ALLOC_HIGH 0x20 /* __GFP_HIGH set */ #define ALLOC_CPUSET 0x40 /* check for correct cpuset */ -#define ALLOC_PFMEMALLOC 0x80 /* Caller has PF_MEMALLOC set */ #ifdef CONFIG_FAIL_PAGE_ALLOC @@ -2294,11 +2293,10 @@ gfp_to_alloc_flags(gfp_t gfp_mask) } else if (unlikely(rt_task(current)) && !in_interrupt()) alloc_flags |= ALLOC_HARDER; - if ((current->flags & PF_MEMALLOC) || - unlikely(test_thread_flag(TIF_MEMDIE))) { - alloc_flags |= ALLOC_PFMEMALLOC; - - if (likely(!(gfp_mask & __GFP_NOMEMALLOC)) && !in_interrupt()) + if (likely(!(gfp_mask & __GFP_NOMEMALLOC))) { + if (gfp_mask & __GFP_MEMALLOC) + alloc_flags |= ALLOC_NO_WATERMARKS; + else if (likely(!(gfp_mask & __GFP_NOMEMALLOC)) && !in_interrupt()) alloc_flags |= ALLOC_NO_WATERMARKS; } @@ -2307,7 +2305,7 @@ gfp_to_alloc_flags(gfp_t gfp_mask) bool gfp_pfmemalloc_allowed(gfp_t gfp_mask) { - return !!(gfp_to_alloc_flags(gfp_mask) & ALLOC_PFMEMALLOC); + return !!(gfp_to_alloc_flags(gfp_mask) & ALLOC_NO_WATERMARKS); } static inline struct page * @@ -2498,12 +2496,12 @@ nopage: return page; got_pg: /* - * page->pfmemalloc is set when the caller had PFMEMALLOC set or is - * been OOM killed. The expectation is that the caller is taking - * steps that will free more memory. The caller should avoid the - * page being used for !PFMEMALLOC purposes. + * page->pfmemalloc is set when the caller had PFMEMALLOC set, is + * been OOM killed or specified __GFP_MEMALLOC. The expectation is + * that the caller is taking steps that will free more memory. The + * caller should avoid the page being used for !PFMEMALLOC purposes. */ - page->pfmemalloc = !!(alloc_flags & ALLOC_PFMEMALLOC); + page->pfmemalloc = !!(alloc_flags & ALLOC_NO_WATERMARKS); if (kmemcheck_enabled) kmemcheck_pagealloc_alloc(page, order, gfp_mask); diff --git a/mm/slab.c b/mm/slab.c index 55d84a22ad96..77be18dab73c 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -1884,7 +1884,7 @@ static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid) return NULL; } - /* Record if ALLOC_PFMEMALLOC was set when allocating the slab */ + /* Record if ALLOC_NO_WATERMARKS was set when allocating the slab */ if (unlikely(page->pfmemalloc)) pfmemalloc_active = true; -- cgit v1.2.3 From 907aed48f65efeecf91575397e3d79335d93a466 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:07 -0700 Subject: mm: allow PF_MEMALLOC from softirq context This is needed to allow network softirq packet processing to make use of PF_MEMALLOC. Currently softirq context cannot use PF_MEMALLOC due to it not being associated with a task, and therefore not having task flags to fiddle with - thus the gfp to alloc flag mapping ignores the task flags when in interrupts (hard or soft) context. Allowing softirqs to make use of PF_MEMALLOC therefore requires some trickery. This patch borrows the task flags from whatever process happens to be preempted by the softirq. It then modifies the gfp to alloc flags mapping to not exclude task flags in softirq context, and modify the softirq code to save, clear and restore the PF_MEMALLOC flag. The save and clear, ensures the preempted task's PF_MEMALLOC flag doesn't leak into the softirq. The restore ensures a softirq's PF_MEMALLOC flag cannot leak back into the preempted process. This should be safe due to the following reasons Softirqs can run on multiple CPUs sure but the same task should not be executing the same softirq code. Neither should the softirq handler be preempted by any other softirq handler so the flags should not leak to an unrelated softirq. Softirqs re-enable hardware interrupts in __do_softirq() so can be preempted by hardware interrupts so PF_MEMALLOC is inherited by the hard IRQ. However, this is similar to a process in reclaim being preempted by a hardirq. While PF_MEMALLOC is set, gfp_to_alloc_flags() distinguishes between hard and soft irqs and avoids giving a hardirq the ALLOC_NO_WATERMARKS flag. If the softirq is deferred to ksoftirq then its flags may be used instead of a normal tasks but as the softirq cannot be preempted, the PF_MEMALLOC flag does not leak to other code by accident. [davem@davemloft.net: Document why PF_MEMALLOC is safe] Signed-off-by: Peter Zijlstra Signed-off-by: Mel Gorman Cc: David Miller Cc: Neil Brown Cc: Mike Christie Cc: Eric B Munson Cc: Eric Dumazet Cc: Sebastian Andrzej Siewior Cc: Mel Gorman Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sched.h | 7 +++++++ kernel/softirq.c | 9 +++++++++ mm/page_alloc.c | 6 +++++- 3 files changed, 21 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 865725adb9d3..c147e7024f11 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1894,6 +1894,13 @@ static inline void rcu_copy_process(struct task_struct *p) #endif +static inline void tsk_restore_flags(struct task_struct *task, + unsigned long orig_flags, unsigned long flags) +{ + task->flags &= ~flags; + task->flags |= orig_flags & flags; +} + #ifdef CONFIG_SMP extern void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new_mask); diff --git a/kernel/softirq.c b/kernel/softirq.c index 671f9594e368..b73e681df09e 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -210,6 +210,14 @@ asmlinkage void __do_softirq(void) __u32 pending; int max_restart = MAX_SOFTIRQ_RESTART; int cpu; + unsigned long old_flags = current->flags; + + /* + * Mask out PF_MEMALLOC s current task context is borrowed for the + * softirq. A softirq handled such as network RX might set PF_MEMALLOC + * again if the socket is related to swap + */ + current->flags &= ~PF_MEMALLOC; pending = local_softirq_pending(); account_system_vtime(current); @@ -265,6 +273,7 @@ restart: account_system_vtime(current); __local_bh_enable(SOFTIRQ_OFFSET); + tsk_restore_flags(current, old_flags, PF_MEMALLOC); } #ifndef __ARCH_HAS_DO_SOFTIRQ diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 8f65abeb9ad6..cd5390f2f18d 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2296,7 +2296,11 @@ gfp_to_alloc_flags(gfp_t gfp_mask) if (likely(!(gfp_mask & __GFP_NOMEMALLOC))) { if (gfp_mask & __GFP_MEMALLOC) alloc_flags |= ALLOC_NO_WATERMARKS; - else if (likely(!(gfp_mask & __GFP_NOMEMALLOC)) && !in_interrupt()) + else if (in_serving_softirq() && (current->flags & PF_MEMALLOC)) + alloc_flags |= ALLOC_NO_WATERMARKS; + else if (!in_interrupt() && + ((current->flags & PF_MEMALLOC) || + unlikely(test_thread_flag(TIF_MEMDIE)))) alloc_flags |= ALLOC_NO_WATERMARKS; } -- cgit v1.2.3 From 99a1dec70d5acbd8c6b3928cdebb4a2d1da676c8 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:14 -0700 Subject: net: introduce sk_gfp_atomic() to allow addition of GFP flags depending on the individual socket Introduce sk_gfp_atomic(), this function allows to inject sock specific flags to each sock related allocation. It is only used on allocation paths that may be required for writing pages back to network storage. [davem@davemloft.net: Use sk_gfp_atomic only when necessary] Signed-off-by: Peter Zijlstra Signed-off-by: Mel Gorman Acked-by: David S. Miller Cc: Neil Brown Cc: Mike Christie Cc: Eric B Munson Cc: Eric Dumazet Cc: Sebastian Andrzej Siewior Cc: Mel Gorman Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/net/sock.h | 5 +++++ net/ipv4/tcp_output.c | 12 +++++++----- net/ipv6/tcp_ipv6.c | 8 +++++--- 3 files changed, 17 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index cee528c119ca..11ccde65c4cf 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -658,6 +658,11 @@ static inline bool sock_flag(const struct sock *sk, enum sock_flags flag) return test_bit(flag, &sk->sk_flags); } +static inline gfp_t sk_gfp_atomic(struct sock *sk, gfp_t gfp_mask) +{ + return GFP_ATOMIC; +} + static inline void sk_acceptq_removed(struct sock *sk) { sk->sk_ack_backlog--; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 33cd065cfbd8..3f1bcff0b10b 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2045,7 +2045,8 @@ void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss, if (unlikely(sk->sk_state == TCP_CLOSE)) return; - if (tcp_write_xmit(sk, cur_mss, nonagle, 0, GFP_ATOMIC)) + if (tcp_write_xmit(sk, cur_mss, nonagle, 0, + sk_gfp_atomic(sk, GFP_ATOMIC))) tcp_check_probe_timer(sk); } @@ -2666,7 +2667,8 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, if (cvp != NULL && cvp->s_data_constant && cvp->s_data_desired) s_data_desired = cvp->s_data_desired; - skb = alloc_skb(MAX_TCP_HEADER + 15 + s_data_desired, GFP_ATOMIC); + skb = alloc_skb(MAX_TCP_HEADER + 15 + s_data_desired, + sk_gfp_atomic(sk, GFP_ATOMIC)); if (unlikely(!skb)) { dst_release(dst); return NULL; @@ -3064,7 +3066,7 @@ void tcp_send_ack(struct sock *sk) * tcp_transmit_skb() will set the ownership to this * sock. */ - buff = alloc_skb(MAX_TCP_HEADER, GFP_ATOMIC); + buff = alloc_skb(MAX_TCP_HEADER, sk_gfp_atomic(sk, GFP_ATOMIC)); if (buff == NULL) { inet_csk_schedule_ack(sk); inet_csk(sk)->icsk_ack.ato = TCP_ATO_MIN; @@ -3079,7 +3081,7 @@ void tcp_send_ack(struct sock *sk) /* Send it off, this clears delayed acks for us. */ TCP_SKB_CB(buff)->when = tcp_time_stamp; - tcp_transmit_skb(sk, buff, 0, GFP_ATOMIC); + tcp_transmit_skb(sk, buff, 0, sk_gfp_atomic(sk, GFP_ATOMIC)); } /* This routine sends a packet with an out of date sequence @@ -3099,7 +3101,7 @@ static int tcp_xmit_probe_skb(struct sock *sk, int urgent) struct sk_buff *skb; /* We don't queue it, tcp_transmit_skb() sets ownership. */ - skb = alloc_skb(MAX_TCP_HEADER, GFP_ATOMIC); + skb = alloc_skb(MAX_TCP_HEADER, sk_gfp_atomic(sk, GFP_ATOMIC)); if (skb == NULL) return -1; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 61c7b6d83176..c66b90f71c9b 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1299,7 +1299,8 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, /* Clone pktoptions received with SYN */ newnp->pktoptions = NULL; if (treq->pktopts != NULL) { - newnp->pktoptions = skb_clone(treq->pktopts, GFP_ATOMIC); + newnp->pktoptions = skb_clone(treq->pktopts, + sk_gfp_atomic(sk, GFP_ATOMIC)); consume_skb(treq->pktopts); treq->pktopts = NULL; if (newnp->pktoptions) @@ -1349,7 +1350,8 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, * across. Shucks. */ tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newnp->daddr, - AF_INET6, key->key, key->keylen, GFP_ATOMIC); + AF_INET6, key->key, key->keylen, + sk_gfp_atomic(sk, GFP_ATOMIC)); } #endif @@ -1442,7 +1444,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) --ANK (980728) */ if (np->rxopt.all) - opt_skb = skb_clone(skb, GFP_ATOMIC); + opt_skb = skb_clone(skb, sk_gfp_atomic(sk, GFP_ATOMIC)); if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */ sock_rps_save_rxhash(sk, skb); -- cgit v1.2.3 From 7cb0240492caea2f6467f827313478f41877e6ef Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:16 -0700 Subject: netvm: allow the use of __GFP_MEMALLOC by specific sockets Allow specific sockets to be tagged SOCK_MEMALLOC and use __GFP_MEMALLOC for their allocations. These sockets will be able to go below watermarks and allocate from the emergency reserve. Such sockets are to be used to service the VM (iow. to swap over). They must be handled kernel side, exposing such a socket to user-space is a bug. There is a risk that the reserves be depleted so for now, the administrator is responsible for increasing min_free_kbytes as necessary to prevent deadlock for their workloads. [a.p.zijlstra@chello.nl: Original patches] Signed-off-by: Mel Gorman Acked-by: David S. Miller Cc: Neil Brown Cc: Peter Zijlstra Cc: Mike Christie Cc: Eric B Munson Cc: Eric Dumazet Cc: Sebastian Andrzej Siewior Cc: Mel Gorman Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/net/sock.h | 5 ++++- net/core/sock.c | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index 11ccde65c4cf..bfa7d20e6646 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -621,6 +621,7 @@ enum sock_flags { SOCK_RCVTSTAMPNS, /* %SO_TIMESTAMPNS setting */ SOCK_LOCALROUTE, /* route locally only, %SO_DONTROUTE setting */ SOCK_QUEUE_SHRUNK, /* write queue has been shrunk recently */ + SOCK_MEMALLOC, /* VM depends on this socket for swapping */ SOCK_TIMESTAMPING_TX_HARDWARE, /* %SOF_TIMESTAMPING_TX_HARDWARE */ SOCK_TIMESTAMPING_TX_SOFTWARE, /* %SOF_TIMESTAMPING_TX_SOFTWARE */ SOCK_TIMESTAMPING_RX_HARDWARE, /* %SOF_TIMESTAMPING_RX_HARDWARE */ @@ -660,7 +661,7 @@ static inline bool sock_flag(const struct sock *sk, enum sock_flags flag) static inline gfp_t sk_gfp_atomic(struct sock *sk, gfp_t gfp_mask) { - return GFP_ATOMIC; + return GFP_ATOMIC | (sk->sk_allocation & __GFP_MEMALLOC); } static inline void sk_acceptq_removed(struct sock *sk) @@ -803,6 +804,8 @@ extern int sk_stream_wait_memory(struct sock *sk, long *timeo_p); extern void sk_stream_wait_close(struct sock *sk, long timeo_p); extern int sk_stream_error(struct sock *sk, int flags, int err); extern void sk_stream_kill_queues(struct sock *sk); +extern void sk_set_memalloc(struct sock *sk); +extern void sk_clear_memalloc(struct sock *sk); extern int sk_wait_data(struct sock *sk, long *timeo); diff --git a/net/core/sock.c b/net/core/sock.c index a67b06280e4c..3617f652f6b0 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -271,6 +271,28 @@ __u32 sysctl_rmem_default __read_mostly = SK_RMEM_MAX; int sysctl_optmem_max __read_mostly = sizeof(unsigned long)*(2*UIO_MAXIOV+512); EXPORT_SYMBOL(sysctl_optmem_max); +/** + * sk_set_memalloc - sets %SOCK_MEMALLOC + * @sk: socket to set it on + * + * Set %SOCK_MEMALLOC on a socket for access to emergency reserves. + * It's the responsibility of the admin to adjust min_free_kbytes + * to meet the requirements + */ +void sk_set_memalloc(struct sock *sk) +{ + sock_set_flag(sk, SOCK_MEMALLOC); + sk->sk_allocation |= __GFP_MEMALLOC; +} +EXPORT_SYMBOL_GPL(sk_set_memalloc); + +void sk_clear_memalloc(struct sock *sk) +{ + sock_reset_flag(sk, SOCK_MEMALLOC); + sk->sk_allocation &= ~__GFP_MEMALLOC; +} +EXPORT_SYMBOL_GPL(sk_clear_memalloc); + #if defined(CONFIG_CGROUPS) #if !defined(CONFIG_NET_CLS_CGROUP) int net_cls_subsys_id = -1; -- cgit v1.2.3 From c93bdd0e03e848555d144eb44a1f275b871a8dd5 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:19 -0700 Subject: netvm: allow skb allocation to use PFMEMALLOC reserves Change the skb allocation API to indicate RX usage and use this to fall back to the PFMEMALLOC reserve when needed. SKBs allocated from the reserve are tagged in skb->pfmemalloc. If an SKB is allocated from the reserve and the socket is later found to be unrelated to page reclaim, the packet is dropped so that the memory remains available for page reclaim. Network protocols are expected to recover from this packet loss. [a.p.zijlstra@chello.nl: Ideas taken from various patches] [davem@davemloft.net: Use static branches, coding style corrections] [sebastian@breakpoint.cc: Avoid unnecessary cast, fix !CONFIG_NET build] Signed-off-by: Mel Gorman Acked-by: David S. Miller Cc: Neil Brown Cc: Peter Zijlstra Cc: Mike Christie Cc: Eric B Munson Cc: Eric Dumazet Cc: Sebastian Andrzej Siewior Cc: Mel Gorman Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/gfp.h | 3 ++ include/linux/skbuff.h | 14 +++++- include/net/sock.h | 15 ++++++ mm/internal.h | 3 -- net/core/filter.c | 8 ++++ net/core/skbuff.c | 124 +++++++++++++++++++++++++++++++++++++++---------- net/core/sock.c | 5 ++ 7 files changed, 142 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/linux/gfp.h b/include/linux/gfp.h index cbd7400e5862..4883f393f50a 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -385,6 +385,9 @@ void drain_local_pages(void *dummy); */ extern gfp_t gfp_allowed_mask; +/* Returns true if the gfp_mask allows use of ALLOC_NO_WATERMARK */ +bool gfp_pfmemalloc_allowed(gfp_t gfp_mask); + extern void pm_restrict_gfp_mask(void); extern void pm_restore_gfp_mask(void); diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index d205c4be7f5b..0336f02e3667 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -462,6 +462,7 @@ struct sk_buff { #ifdef CONFIG_IPV6_NDISC_NODETYPE __u8 ndisc_nodetype:2; #endif + __u8 pfmemalloc:1; __u8 ooo_okay:1; __u8 l4_rxhash:1; __u8 wifi_acked_valid:1; @@ -502,6 +503,15 @@ struct sk_buff { #include +#define SKB_ALLOC_FCLONE 0x01 +#define SKB_ALLOC_RX 0x02 + +/* Returns true if the skb was allocated from PFMEMALLOC reserves */ +static inline bool skb_pfmemalloc(const struct sk_buff *skb) +{ + return unlikely(skb->pfmemalloc); +} + /* * skb might have a dst pointer attached, refcounted or not. * _skb_refdst low order bit is set if refcount was _not_ taken @@ -565,7 +575,7 @@ extern bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from, bool *fragstolen, int *delta_truesize); extern struct sk_buff *__alloc_skb(unsigned int size, - gfp_t priority, int fclone, int node); + gfp_t priority, int flags, int node); extern struct sk_buff *build_skb(void *data, unsigned int frag_size); static inline struct sk_buff *alloc_skb(unsigned int size, gfp_t priority) @@ -576,7 +586,7 @@ static inline struct sk_buff *alloc_skb(unsigned int size, static inline struct sk_buff *alloc_skb_fclone(unsigned int size, gfp_t priority) { - return __alloc_skb(size, priority, 1, NUMA_NO_NODE); + return __alloc_skb(size, priority, SKB_ALLOC_FCLONE, NUMA_NO_NODE); } extern void skb_recycle(struct sk_buff *skb); diff --git a/include/net/sock.h b/include/net/sock.h index bfa7d20e6646..81198632ac2a 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -659,6 +659,21 @@ static inline bool sock_flag(const struct sock *sk, enum sock_flags flag) return test_bit(flag, &sk->sk_flags); } +#ifdef CONFIG_NET +extern struct static_key memalloc_socks; +static inline int sk_memalloc_socks(void) +{ + return static_key_false(&memalloc_socks); +} +#else + +static inline int sk_memalloc_socks(void) +{ + return 0; +} + +#endif + static inline gfp_t sk_gfp_atomic(struct sock *sk, gfp_t gfp_mask) { return GFP_ATOMIC | (sk->sk_allocation & __GFP_MEMALLOC); diff --git a/mm/internal.h b/mm/internal.h index eb76b67890d0..3314f79d775a 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -279,9 +279,6 @@ static inline struct page *mem_map_next(struct page *iter, #define __paginginit __init #endif -/* Returns true if the gfp_mask allows use of ALLOC_NO_WATERMARK */ -bool gfp_pfmemalloc_allowed(gfp_t gfp_mask); - /* Memory initialisation debug and verification */ enum mminit_level { MMINIT_WARNING, diff --git a/net/core/filter.c b/net/core/filter.c index d4ce2dc712e3..907efd27ec77 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -83,6 +83,14 @@ int sk_filter(struct sock *sk, struct sk_buff *skb) int err; struct sk_filter *filter; + /* + * If the skb was allocated from pfmemalloc reserves, only + * allow SOCK_MEMALLOC sockets to use it as this socket is + * helping free memory + */ + if (skb_pfmemalloc(skb) && !sock_flag(sk, SOCK_MEMALLOC)) + return -ENOMEM; + err = security_sock_rcv_skb(sk, skb); if (err) return err; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 368f65c15e4f..fe00d1208167 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -145,6 +145,43 @@ static void skb_under_panic(struct sk_buff *skb, int sz, void *here) BUG(); } + +/* + * kmalloc_reserve is a wrapper around kmalloc_node_track_caller that tells + * the caller if emergency pfmemalloc reserves are being used. If it is and + * the socket is later found to be SOCK_MEMALLOC then PFMEMALLOC reserves + * may be used. Otherwise, the packet data may be discarded until enough + * memory is free + */ +#define kmalloc_reserve(size, gfp, node, pfmemalloc) \ + __kmalloc_reserve(size, gfp, node, _RET_IP_, pfmemalloc) +void *__kmalloc_reserve(size_t size, gfp_t flags, int node, unsigned long ip, + bool *pfmemalloc) +{ + void *obj; + bool ret_pfmemalloc = false; + + /* + * Try a regular allocation, when that fails and we're not entitled + * to the reserves, fail. + */ + obj = kmalloc_node_track_caller(size, + flags | __GFP_NOMEMALLOC | __GFP_NOWARN, + node); + if (obj || !(gfp_pfmemalloc_allowed(flags))) + goto out; + + /* Try again but now we are using pfmemalloc reserves */ + ret_pfmemalloc = true; + obj = kmalloc_node_track_caller(size, flags, node); + +out: + if (pfmemalloc) + *pfmemalloc = ret_pfmemalloc; + + return obj; +} + /* Allocate a new skbuff. We do this ourselves so we can fill in a few * 'private' fields and also do memory statistics to find all the * [BEEP] leaks. @@ -155,8 +192,10 @@ static void skb_under_panic(struct sk_buff *skb, int sz, void *here) * __alloc_skb - allocate a network buffer * @size: size to allocate * @gfp_mask: allocation mask - * @fclone: allocate from fclone cache instead of head cache - * and allocate a cloned (child) skb + * @flags: If SKB_ALLOC_FCLONE is set, allocate from fclone cache + * instead of head cache and allocate a cloned (child) skb. + * If SKB_ALLOC_RX is set, __GFP_MEMALLOC will be used for + * allocations in case the data is required for writeback * @node: numa node to allocate memory on * * Allocate a new &sk_buff. The returned buffer has no headroom and a @@ -167,14 +206,19 @@ static void skb_under_panic(struct sk_buff *skb, int sz, void *here) * %GFP_ATOMIC. */ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, - int fclone, int node) + int flags, int node) { struct kmem_cache *cache; struct skb_shared_info *shinfo; struct sk_buff *skb; u8 *data; + bool pfmemalloc; - cache = fclone ? skbuff_fclone_cache : skbuff_head_cache; + cache = (flags & SKB_ALLOC_FCLONE) + ? skbuff_fclone_cache : skbuff_head_cache; + + if (sk_memalloc_socks() && (flags & SKB_ALLOC_RX)) + gfp_mask |= __GFP_MEMALLOC; /* Get the HEAD */ skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node); @@ -189,7 +233,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, */ size = SKB_DATA_ALIGN(size); size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); - data = kmalloc_node_track_caller(size, gfp_mask, node); + data = kmalloc_reserve(size, gfp_mask, node, &pfmemalloc); if (!data) goto nodata; /* kmalloc(size) might give us more room than requested. @@ -207,6 +251,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, memset(skb, 0, offsetof(struct sk_buff, tail)); /* Account for allocated memory : skb + skb->head */ skb->truesize = SKB_TRUESIZE(size); + skb->pfmemalloc = pfmemalloc; atomic_set(&skb->users, 1); skb->head = data; skb->data = data; @@ -222,7 +267,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, atomic_set(&shinfo->dataref, 1); kmemcheck_annotate_variable(shinfo->destructor_arg); - if (fclone) { + if (flags & SKB_ALLOC_FCLONE) { struct sk_buff *child = skb + 1; atomic_t *fclone_ref = (atomic_t *) (child + 1); @@ -232,6 +277,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, atomic_set(fclone_ref, 1); child->fclone = SKB_FCLONE_UNAVAILABLE; + child->pfmemalloc = pfmemalloc; } out: return skb; @@ -302,14 +348,7 @@ static DEFINE_PER_CPU(struct netdev_alloc_cache, netdev_alloc_cache); #define NETDEV_PAGECNT_BIAS (PAGE_SIZE / SMP_CACHE_BYTES) -/** - * netdev_alloc_frag - allocate a page fragment - * @fragsz: fragment size - * - * Allocates a frag from a page for receive buffer. - * Uses GFP_ATOMIC allocations. - */ -void *netdev_alloc_frag(unsigned int fragsz) +static void *__netdev_alloc_frag(unsigned int fragsz, gfp_t gfp_mask) { struct netdev_alloc_cache *nc; void *data = NULL; @@ -319,7 +358,7 @@ void *netdev_alloc_frag(unsigned int fragsz) nc = &__get_cpu_var(netdev_alloc_cache); if (unlikely(!nc->page)) { refill: - nc->page = alloc_page(GFP_ATOMIC | __GFP_COLD); + nc->page = alloc_page(gfp_mask); if (unlikely(!nc->page)) goto end; recycle: @@ -343,6 +382,18 @@ end: local_irq_restore(flags); return data; } + +/** + * netdev_alloc_frag - allocate a page fragment + * @fragsz: fragment size + * + * Allocates a frag from a page for receive buffer. + * Uses GFP_ATOMIC allocations. + */ +void *netdev_alloc_frag(unsigned int fragsz) +{ + return __netdev_alloc_frag(fragsz, GFP_ATOMIC | __GFP_COLD); +} EXPORT_SYMBOL(netdev_alloc_frag); /** @@ -366,7 +417,12 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev, SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); if (fragsz <= PAGE_SIZE && !(gfp_mask & (__GFP_WAIT | GFP_DMA))) { - void *data = netdev_alloc_frag(fragsz); + void *data; + + if (sk_memalloc_socks()) + gfp_mask |= __GFP_MEMALLOC; + + data = __netdev_alloc_frag(fragsz, gfp_mask); if (likely(data)) { skb = build_skb(data, fragsz); @@ -374,7 +430,8 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev, put_page(virt_to_head_page(data)); } } else { - skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask, 0, NUMA_NO_NODE); + skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask, + SKB_ALLOC_RX, NUMA_NO_NODE); } if (likely(skb)) { skb_reserve(skb, NET_SKB_PAD); @@ -656,6 +713,7 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old) #if IS_ENABLED(CONFIG_IP_VS) new->ipvs_property = old->ipvs_property; #endif + new->pfmemalloc = old->pfmemalloc; new->protocol = old->protocol; new->mark = old->mark; new->skb_iif = old->skb_iif; @@ -814,6 +872,9 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask) n->fclone = SKB_FCLONE_CLONE; atomic_inc(fclone_ref); } else { + if (skb_pfmemalloc(skb)) + gfp_mask |= __GFP_MEMALLOC; + n = kmem_cache_alloc(skbuff_head_cache, gfp_mask); if (!n) return NULL; @@ -850,6 +911,13 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old) skb_shinfo(new)->gso_type = skb_shinfo(old)->gso_type; } +static inline int skb_alloc_rx_flag(const struct sk_buff *skb) +{ + if (skb_pfmemalloc(skb)) + return SKB_ALLOC_RX; + return 0; +} + /** * skb_copy - create private copy of an sk_buff * @skb: buffer to copy @@ -871,7 +939,8 @@ struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask) { int headerlen = skb_headroom(skb); unsigned int size = skb_end_offset(skb) + skb->data_len; - struct sk_buff *n = alloc_skb(size, gfp_mask); + struct sk_buff *n = __alloc_skb(size, gfp_mask, + skb_alloc_rx_flag(skb), NUMA_NO_NODE); if (!n) return NULL; @@ -906,7 +975,8 @@ EXPORT_SYMBOL(skb_copy); struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom, gfp_t gfp_mask) { unsigned int size = skb_headlen(skb) + headroom; - struct sk_buff *n = alloc_skb(size, gfp_mask); + struct sk_buff *n = __alloc_skb(size, gfp_mask, + skb_alloc_rx_flag(skb), NUMA_NO_NODE); if (!n) goto out; @@ -979,8 +1049,10 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, size = SKB_DATA_ALIGN(size); - data = kmalloc(size + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)), - gfp_mask); + if (skb_pfmemalloc(skb)) + gfp_mask |= __GFP_MEMALLOC; + data = kmalloc_reserve(size + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)), + gfp_mask, NUMA_NO_NODE, NULL); if (!data) goto nodata; size = SKB_WITH_OVERHEAD(ksize(data)); @@ -1092,8 +1164,9 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb, /* * Allocate the copy buffer */ - struct sk_buff *n = alloc_skb(newheadroom + skb->len + newtailroom, - gfp_mask); + struct sk_buff *n = __alloc_skb(newheadroom + skb->len + newtailroom, + gfp_mask, skb_alloc_rx_flag(skb), + NUMA_NO_NODE); int oldheadroom = skb_headroom(skb); int head_copy_len, head_copy_off; int off; @@ -2775,8 +2848,9 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) skb_release_head_state(nskb); __skb_push(nskb, doffset); } else { - nskb = alloc_skb(hsize + doffset + headroom, - GFP_ATOMIC); + nskb = __alloc_skb(hsize + doffset + headroom, + GFP_ATOMIC, skb_alloc_rx_flag(skb), + NUMA_NO_NODE); if (unlikely(!nskb)) goto err; diff --git a/net/core/sock.c b/net/core/sock.c index 3617f652f6b0..c8c5816289fe 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -271,6 +271,9 @@ __u32 sysctl_rmem_default __read_mostly = SK_RMEM_MAX; int sysctl_optmem_max __read_mostly = sizeof(unsigned long)*(2*UIO_MAXIOV+512); EXPORT_SYMBOL(sysctl_optmem_max); +struct static_key memalloc_socks = STATIC_KEY_INIT_FALSE; +EXPORT_SYMBOL_GPL(memalloc_socks); + /** * sk_set_memalloc - sets %SOCK_MEMALLOC * @sk: socket to set it on @@ -283,6 +286,7 @@ void sk_set_memalloc(struct sock *sk) { sock_set_flag(sk, SOCK_MEMALLOC); sk->sk_allocation |= __GFP_MEMALLOC; + static_key_slow_inc(&memalloc_socks); } EXPORT_SYMBOL_GPL(sk_set_memalloc); @@ -290,6 +294,7 @@ void sk_clear_memalloc(struct sock *sk) { sock_reset_flag(sk, SOCK_MEMALLOC); sk->sk_allocation &= ~__GFP_MEMALLOC; + static_key_slow_dec(&memalloc_socks); } EXPORT_SYMBOL_GPL(sk_clear_memalloc); -- cgit v1.2.3 From c48a11c7ad2623b99bbd6859b0b4234e7f11176f Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:23 -0700 Subject: netvm: propagate page->pfmemalloc to skb The skb->pfmemalloc flag gets set to true iff during the slab allocation of data in __alloc_skb that the the PFMEMALLOC reserves were used. If the packet is fragmented, it is possible that pages will be allocated from the PFMEMALLOC reserve without propagating this information to the skb. This patch propagates page->pfmemalloc from pages allocated for fragments to the skb. Signed-off-by: Mel Gorman Acked-by: David S. Miller Cc: Neil Brown Cc: Peter Zijlstra Cc: Mike Christie Cc: Eric B Munson Cc: Eric Dumazet Cc: Sebastian Andrzej Siewior Cc: Mel Gorman Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/skbuff.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 0336f02e3667..b814bb8fd5ab 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1247,6 +1247,17 @@ static inline void __skb_fill_page_desc(struct sk_buff *skb, int i, { skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + /* + * Propagate page->pfmemalloc to the skb if we can. The problem is + * that not all callers have unique ownership of the page. If + * pfmemalloc is set, we check the mapping as a mapping implies + * page->index is set (index and pfmemalloc share space). + * If it's a valid mapping, we cannot use page->pfmemalloc but we + * do not lose pfmemalloc information as the pages would not be + * allocated using __GFP_MEMALLOC. + */ + if (page->pfmemalloc && !page->mapping) + skb->pfmemalloc = true; frag->page.p = page; frag->page_offset = off; skb_frag_size_set(frag, size); -- cgit v1.2.3 From 0614002bb5f7411e61ffa0dfe5be1f2c84df3da3 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:24 -0700 Subject: netvm: propagate page->pfmemalloc from skb_alloc_page to skb The skb->pfmemalloc flag gets set to true iff during the slab allocation of data in __alloc_skb that the the PFMEMALLOC reserves were used. If page splitting is used, it is possible that pages will be allocated from the PFMEMALLOC reserve without propagating this information to the skb. This patch propagates page->pfmemalloc from pages allocated for fragments to the skb. It works by reintroducing and expanding the skb_alloc_page() API to take an skb. If the page was allocated from pfmemalloc reserves, it is automatically copied. If the driver allocates the page before the skb, it should call skb_propagate_pfmemalloc() after the skb is allocated to ensure the flag is copied properly. Failure to do so is not critical. The resulting driver may perform slower if it is used for swap-over-NBD or swap-over-NFS but it should not result in failure. [davem@davemloft.net: API rename and consistency] Signed-off-by: Mel Gorman Acked-by: David S. Miller Cc: Neil Brown Cc: Peter Zijlstra Cc: Mike Christie Cc: Eric B Munson Cc: Eric Dumazet Cc: Sebastian Andrzej Siewior Cc: Mel Gorman Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/net/ethernet/chelsio/cxgb4/sge.c | 2 +- drivers/net/ethernet/chelsio/cxgb4vf/sge.c | 2 +- drivers/net/ethernet/intel/igb/igb_main.c | 2 +- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 4 +- drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 1 - drivers/net/usb/cdc-phonet.c | 2 +- drivers/usb/gadget/f_phonet.c | 2 +- include/linux/skbuff.h | 55 +++++++++++++++++++++++ 8 files changed, 62 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index 8596acaa402b..d49933ed551f 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -528,7 +528,7 @@ static unsigned int refill_fl(struct adapter *adap, struct sge_fl *q, int n, #endif while (n--) { - pg = alloc_page(gfp); + pg = __skb_alloc_page(gfp, NULL); if (unlikely(!pg)) { q->alloc_failed++; break; diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c index f2d1ecdcaf98..8877fbfefb63 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c @@ -653,7 +653,7 @@ static unsigned int refill_fl(struct adapter *adapter, struct sge_fl *fl, alloc_small_pages: while (n--) { - page = alloc_page(gfp | __GFP_NOWARN | __GFP_COLD); + page = __skb_alloc_page(gfp | __GFP_NOWARN, NULL); if (unlikely(!page)) { fl->alloc_failed++; break; diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 1050411e7ca3..b7c2d5050572 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -6235,7 +6235,7 @@ static bool igb_alloc_mapped_page(struct igb_ring *rx_ring, return true; if (!page) { - page = alloc_page(GFP_ATOMIC | __GFP_COLD); + page = __skb_alloc_page(GFP_ATOMIC, bi->skb); bi->page = page; if (unlikely(!page)) { rx_ring->rx_stats.alloc_failed++; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index c709eae58c63..4326f74f7137 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -1141,8 +1141,8 @@ static bool ixgbe_alloc_mapped_page(struct ixgbe_ring *rx_ring, /* alloc new page for storage */ if (likely(!page)) { - page = alloc_pages(GFP_ATOMIC | __GFP_COLD | __GFP_COMP, - ixgbe_rx_pg_order(rx_ring)); + page = __skb_alloc_pages(GFP_ATOMIC | __GFP_COLD | __GFP_COMP, + bi->skb, ixgbe_rx_pg_order(rx_ring)); if (unlikely(!page)) { rx_ring->rx_stats.alloc_rx_page_failed++; return false; diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 3f9841d619ad..60ef64587412 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -352,7 +352,6 @@ static void ixgbevf_alloc_rx_buffers(struct ixgbevf_adapter *adapter, adapter->alloc_rx_buff_failed++; goto no_buffers; } - bi->skb = skb; } if (!bi->dma) { diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c index 187c144c5e5b..64610048ce87 100644 --- a/drivers/net/usb/cdc-phonet.c +++ b/drivers/net/usb/cdc-phonet.c @@ -130,7 +130,7 @@ static int rx_submit(struct usbpn_dev *pnd, struct urb *req, gfp_t gfp_flags) struct page *page; int err; - page = alloc_page(gfp_flags); + page = __skb_alloc_page(gfp_flags | __GFP_NOMEMALLOC, NULL); if (!page) return -ENOMEM; diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c index 965a6293206a..8ee9268fe253 100644 --- a/drivers/usb/gadget/f_phonet.c +++ b/drivers/usb/gadget/f_phonet.c @@ -301,7 +301,7 @@ pn_rx_submit(struct f_phonet *fp, struct usb_request *req, gfp_t gfp_flags) struct page *page; int err; - page = alloc_page(gfp_flags); + page = __skb_alloc_page(gfp_flags | __GFP_NOMEMALLOC, NULL); if (!page) return -ENOMEM; diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index b814bb8fd5ab..7632c87da2c9 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1774,6 +1774,61 @@ static inline struct sk_buff *netdev_alloc_skb_ip_align(struct net_device *dev, return __netdev_alloc_skb_ip_align(dev, length, GFP_ATOMIC); } +/* + * __skb_alloc_page - allocate pages for ps-rx on a skb and preserve pfmemalloc data + * @gfp_mask: alloc_pages_node mask. Set __GFP_NOMEMALLOC if not for network packet RX + * @skb: skb to set pfmemalloc on if __GFP_MEMALLOC is used + * @order: size of the allocation + * + * Allocate a new page. + * + * %NULL is returned if there is no free memory. +*/ +static inline struct page *__skb_alloc_pages(gfp_t gfp_mask, + struct sk_buff *skb, + unsigned int order) +{ + struct page *page; + + gfp_mask |= __GFP_COLD; + + if (!(gfp_mask & __GFP_NOMEMALLOC)) + gfp_mask |= __GFP_MEMALLOC; + + page = alloc_pages_node(NUMA_NO_NODE, gfp_mask, order); + if (skb && page && page->pfmemalloc) + skb->pfmemalloc = true; + + return page; +} + +/** + * __skb_alloc_page - allocate a page for ps-rx for a given skb and preserve pfmemalloc data + * @gfp_mask: alloc_pages_node mask. Set __GFP_NOMEMALLOC if not for network packet RX + * @skb: skb to set pfmemalloc on if __GFP_MEMALLOC is used + * + * Allocate a new page. + * + * %NULL is returned if there is no free memory. + */ +static inline struct page *__skb_alloc_page(gfp_t gfp_mask, + struct sk_buff *skb) +{ + return __skb_alloc_pages(gfp_mask, skb, 0); +} + +/** + * skb_propagate_pfmemalloc - Propagate pfmemalloc if skb is allocated after RX page + * @page: The page that was allocated from skb_alloc_page + * @skb: The skb that may need pfmemalloc set + */ +static inline void skb_propagate_pfmemalloc(struct page *page, + struct sk_buff *skb) +{ + if (page && page->pfmemalloc) + skb->pfmemalloc = true; +} + /** * skb_frag_page - retrieve the page refered to by a paged fragment * @frag: the paged fragment -- cgit v1.2.3 From b4b9e3558508980fc0cd161a545ffb55a1f13ee9 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:26 -0700 Subject: netvm: set PF_MEMALLOC as appropriate during SKB processing In order to make sure pfmemalloc packets receive all memory needed to proceed, ensure processing of pfmemalloc SKBs happens under PF_MEMALLOC. This is limited to a subset of protocols that are expected to be used for writing to swap. Taps are not allowed to use PF_MEMALLOC as these are expected to communicate with userspace processes which could be paged out. [a.p.zijlstra@chello.nl: Ideas taken from various patches] [jslaby@suse.cz: Lock imbalance fix] Signed-off-by: Mel Gorman Acked-by: David S. Miller Cc: Neil Brown Cc: Peter Zijlstra Cc: Mike Christie Cc: Eric B Munson Cc: Eric Dumazet Cc: Sebastian Andrzej Siewior Cc: Mel Gorman Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/net/sock.h | 5 +++++ net/core/dev.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++------ net/core/sock.c | 16 ++++++++++++++++ 3 files changed, 68 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index 81198632ac2a..43a470d40d76 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -754,8 +754,13 @@ static inline __must_check int sk_add_backlog(struct sock *sk, struct sk_buff *s return 0; } +extern int __sk_backlog_rcv(struct sock *sk, struct sk_buff *skb); + static inline int sk_backlog_rcv(struct sock *sk, struct sk_buff *skb) { + if (sk_memalloc_socks() && skb_pfmemalloc(skb)) + return __sk_backlog_rcv(sk, skb); + return sk->sk_backlog_rcv(sk, skb); } diff --git a/net/core/dev.c b/net/core/dev.c index 0ebaea16632f..ce132443d5d1 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3155,6 +3155,23 @@ void netdev_rx_handler_unregister(struct net_device *dev) } EXPORT_SYMBOL_GPL(netdev_rx_handler_unregister); +/* + * Limit the use of PFMEMALLOC reserves to those protocols that implement + * the special handling of PFMEMALLOC skbs. + */ +static bool skb_pfmemalloc_protocol(struct sk_buff *skb) +{ + switch (skb->protocol) { + case __constant_htons(ETH_P_ARP): + case __constant_htons(ETH_P_IP): + case __constant_htons(ETH_P_IPV6): + case __constant_htons(ETH_P_8021Q): + return true; + default: + return false; + } +} + static int __netif_receive_skb(struct sk_buff *skb) { struct packet_type *ptype, *pt_prev; @@ -3164,14 +3181,27 @@ static int __netif_receive_skb(struct sk_buff *skb) bool deliver_exact = false; int ret = NET_RX_DROP; __be16 type; + unsigned long pflags = current->flags; net_timestamp_check(!netdev_tstamp_prequeue, skb); trace_netif_receive_skb(skb); + /* + * PFMEMALLOC skbs are special, they should + * - be delivered to SOCK_MEMALLOC sockets only + * - stay away from userspace + * - have bounded memory usage + * + * Use PF_MEMALLOC as this saves us from propagating the allocation + * context down to all allocation sites. + */ + if (sk_memalloc_socks() && skb_pfmemalloc(skb)) + current->flags |= PF_MEMALLOC; + /* if we've gotten here through NAPI, check netpoll */ if (netpoll_receive_skb(skb)) - return NET_RX_DROP; + goto out; orig_dev = skb->dev; @@ -3191,7 +3221,7 @@ another_round: if (skb->protocol == cpu_to_be16(ETH_P_8021Q)) { skb = vlan_untag(skb); if (unlikely(!skb)) - goto out; + goto unlock; } #ifdef CONFIG_NET_CLS_ACT @@ -3201,6 +3231,9 @@ another_round: } #endif + if (sk_memalloc_socks() && skb_pfmemalloc(skb)) + goto skip_taps; + list_for_each_entry_rcu(ptype, &ptype_all, list) { if (!ptype->dev || ptype->dev == skb->dev) { if (pt_prev) @@ -3209,13 +3242,18 @@ another_round: } } +skip_taps: #ifdef CONFIG_NET_CLS_ACT skb = handle_ing(skb, &pt_prev, &ret, orig_dev); if (!skb) - goto out; + goto unlock; ncls: #endif + if (sk_memalloc_socks() && skb_pfmemalloc(skb) + && !skb_pfmemalloc_protocol(skb)) + goto drop; + rx_handler = rcu_dereference(skb->dev->rx_handler); if (vlan_tx_tag_present(skb)) { if (pt_prev) { @@ -3225,7 +3263,7 @@ ncls: if (vlan_do_receive(&skb, !rx_handler)) goto another_round; else if (unlikely(!skb)) - goto out; + goto unlock; } if (rx_handler) { @@ -3235,7 +3273,7 @@ ncls: } switch (rx_handler(&skb)) { case RX_HANDLER_CONSUMED: - goto out; + goto unlock; case RX_HANDLER_ANOTHER: goto another_round; case RX_HANDLER_EXACT: @@ -3268,6 +3306,7 @@ ncls: else ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev); } else { +drop: atomic_long_inc(&skb->dev->rx_dropped); kfree_skb(skb); /* Jamal, now you will not able to escape explaining @@ -3276,8 +3315,10 @@ ncls: ret = NET_RX_DROP; } -out: +unlock: rcu_read_unlock(); +out: + tsk_restore_flags(current, pflags, PF_MEMALLOC); return ret; } diff --git a/net/core/sock.c b/net/core/sock.c index c8c5816289fe..32fdcd2d6e8f 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -298,6 +298,22 @@ void sk_clear_memalloc(struct sock *sk) } EXPORT_SYMBOL_GPL(sk_clear_memalloc); +int __sk_backlog_rcv(struct sock *sk, struct sk_buff *skb) +{ + int ret; + unsigned long pflags = current->flags; + + /* these should have been dropped before queueing */ + BUG_ON(!sock_flag(sk, SOCK_MEMALLOC)); + + current->flags |= PF_MEMALLOC; + ret = sk->sk_backlog_rcv(sk, skb); + tsk_restore_flags(current, pflags, PF_MEMALLOC); + + return ret; +} +EXPORT_SYMBOL(__sk_backlog_rcv); + #if defined(CONFIG_CGROUPS) #if !defined(CONFIG_NET_CLS_CGROUP) int net_cls_subsys_id = -1; -- cgit v1.2.3 From 5515061d22f0f9976ae7815864bfd22042d36848 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:35 -0700 Subject: mm: throttle direct reclaimers if PF_MEMALLOC reserves are low and swap is backed by network storage If swap is backed by network storage such as NBD, there is a risk that a large number of reclaimers can hang the system by consuming all PF_MEMALLOC reserves. To avoid these hangs, the administrator must tune min_free_kbytes in advance which is a bit fragile. This patch throttles direct reclaimers if half the PF_MEMALLOC reserves are in use. If the system is routinely getting throttled the system administrator can increase min_free_kbytes so degradation is smoother but the system will keep running. Signed-off-by: Mel Gorman Cc: David Miller Cc: Neil Brown Cc: Peter Zijlstra Cc: Mike Christie Cc: Eric B Munson Cc: Eric Dumazet Cc: Sebastian Andrzej Siewior Cc: Mel Gorman Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 1 + mm/page_alloc.c | 1 + mm/vmscan.c | 128 +++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 122 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 64b2c3a48286..2daa54f55db7 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -705,6 +705,7 @@ typedef struct pglist_data { range, including holes */ int node_id; wait_queue_head_t kswapd_wait; + wait_queue_head_t pfmemalloc_wait; struct task_struct *kswapd; /* Protected by lock_memory_hotplug() */ int kswapd_max_order; enum zone_type classzone_idx; diff --git a/mm/page_alloc.c b/mm/page_alloc.c index ef2d1e72fc07..48aee0f5902f 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -4389,6 +4389,7 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat, pgdat_resize_init(pgdat); pgdat->nr_zones = 0; init_waitqueue_head(&pgdat->kswapd_wait); + init_waitqueue_head(&pgdat->pfmemalloc_wait); pgdat->kswapd_max_order = 0; pgdat_page_cgroup_init(pgdat); diff --git a/mm/vmscan.c b/mm/vmscan.c index 6b1f89a91212..021a44a7bd20 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2112,6 +2112,80 @@ out: return 0; } +static bool pfmemalloc_watermark_ok(pg_data_t *pgdat) +{ + struct zone *zone; + unsigned long pfmemalloc_reserve = 0; + unsigned long free_pages = 0; + int i; + bool wmark_ok; + + for (i = 0; i <= ZONE_NORMAL; i++) { + zone = &pgdat->node_zones[i]; + pfmemalloc_reserve += min_wmark_pages(zone); + free_pages += zone_page_state(zone, NR_FREE_PAGES); + } + + wmark_ok = free_pages > pfmemalloc_reserve / 2; + + /* kswapd must be awake if processes are being throttled */ + if (!wmark_ok && waitqueue_active(&pgdat->kswapd_wait)) { + pgdat->classzone_idx = min(pgdat->classzone_idx, + (enum zone_type)ZONE_NORMAL); + wake_up_interruptible(&pgdat->kswapd_wait); + } + + return wmark_ok; +} + +/* + * Throttle direct reclaimers if backing storage is backed by the network + * and the PFMEMALLOC reserve for the preferred node is getting dangerously + * depleted. kswapd will continue to make progress and wake the processes + * when the low watermark is reached + */ +static void throttle_direct_reclaim(gfp_t gfp_mask, struct zonelist *zonelist, + nodemask_t *nodemask) +{ + struct zone *zone; + int high_zoneidx = gfp_zone(gfp_mask); + pg_data_t *pgdat; + + /* + * Kernel threads should not be throttled as they may be indirectly + * responsible for cleaning pages necessary for reclaim to make forward + * progress. kjournald for example may enter direct reclaim while + * committing a transaction where throttling it could forcing other + * processes to block on log_wait_commit(). + */ + if (current->flags & PF_KTHREAD) + return; + + /* Check if the pfmemalloc reserves are ok */ + first_zones_zonelist(zonelist, high_zoneidx, NULL, &zone); + pgdat = zone->zone_pgdat; + if (pfmemalloc_watermark_ok(pgdat)) + return; + + /* + * If the caller cannot enter the filesystem, it's possible that it + * is due to the caller holding an FS lock or performing a journal + * transaction in the case of a filesystem like ext[3|4]. In this case, + * it is not safe to block on pfmemalloc_wait as kswapd could be + * blocked waiting on the same lock. Instead, throttle for up to a + * second before continuing. + */ + if (!(gfp_mask & __GFP_FS)) { + wait_event_interruptible_timeout(pgdat->pfmemalloc_wait, + pfmemalloc_watermark_ok(pgdat), HZ); + return; + } + + /* Throttle until kswapd wakes the process */ + wait_event_killable(zone->zone_pgdat->pfmemalloc_wait, + pfmemalloc_watermark_ok(pgdat)); +} + unsigned long try_to_free_pages(struct zonelist *zonelist, int order, gfp_t gfp_mask, nodemask_t *nodemask) { @@ -2131,6 +2205,15 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order, .gfp_mask = sc.gfp_mask, }; + throttle_direct_reclaim(gfp_mask, zonelist, nodemask); + + /* + * Do not enter reclaim if fatal signal is pending. 1 is returned so + * that the page allocator does not consider triggering OOM + */ + if (fatal_signal_pending(current)) + return 1; + trace_mm_vmscan_direct_reclaim_begin(order, sc.may_writepage, gfp_mask); @@ -2275,8 +2358,13 @@ static bool pgdat_balanced(pg_data_t *pgdat, unsigned long balanced_pages, return balanced_pages >= (present_pages >> 2); } -/* is kswapd sleeping prematurely? */ -static bool sleeping_prematurely(pg_data_t *pgdat, int order, long remaining, +/* + * Prepare kswapd for sleeping. This verifies that there are no processes + * waiting in throttle_direct_reclaim() and that watermarks have been met. + * + * Returns true if kswapd is ready to sleep + */ +static bool prepare_kswapd_sleep(pg_data_t *pgdat, int order, long remaining, int classzone_idx) { int i; @@ -2285,7 +2373,21 @@ static bool sleeping_prematurely(pg_data_t *pgdat, int order, long remaining, /* If a direct reclaimer woke kswapd within HZ/10, it's premature */ if (remaining) - return true; + return false; + + /* + * There is a potential race between when kswapd checks its watermarks + * and a process gets throttled. There is also a potential race if + * processes get throttled, kswapd wakes, a large process exits therby + * balancing the zones that causes kswapd to miss a wakeup. If kswapd + * is going to sleep, no process should be sleeping on pfmemalloc_wait + * so wake them now if necessary. If necessary, processes will wake + * kswapd and get throttled again + */ + if (waitqueue_active(&pgdat->pfmemalloc_wait)) { + wake_up(&pgdat->pfmemalloc_wait); + return false; + } /* Check the watermark levels */ for (i = 0; i <= classzone_idx; i++) { @@ -2318,9 +2420,9 @@ static bool sleeping_prematurely(pg_data_t *pgdat, int order, long remaining, * must be balanced */ if (order) - return !pgdat_balanced(pgdat, balanced, classzone_idx); + return pgdat_balanced(pgdat, balanced, classzone_idx); else - return !all_zones_ok; + return all_zones_ok; } /* @@ -2546,6 +2648,16 @@ loop_again: } } + + /* + * If the low watermark is met there is no need for processes + * to be throttled on pfmemalloc_wait as they should not be + * able to safely make forward progress. Wake them + */ + if (waitqueue_active(&pgdat->pfmemalloc_wait) && + pfmemalloc_watermark_ok(pgdat)) + wake_up(&pgdat->pfmemalloc_wait); + if (all_zones_ok || (order && pgdat_balanced(pgdat, balanced, *classzone_idx))) break; /* kswapd: all done */ /* @@ -2647,7 +2759,7 @@ out: } /* - * Return the order we were reclaiming at so sleeping_prematurely() + * Return the order we were reclaiming at so prepare_kswapd_sleep() * makes a decision on the order we were last reclaiming at. However, * if another caller entered the allocator slow path while kswapd * was awake, order will remain at the higher level @@ -2667,7 +2779,7 @@ static void kswapd_try_to_sleep(pg_data_t *pgdat, int order, int classzone_idx) prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE); /* Try to sleep for a short interval */ - if (!sleeping_prematurely(pgdat, order, remaining, classzone_idx)) { + if (prepare_kswapd_sleep(pgdat, order, remaining, classzone_idx)) { remaining = schedule_timeout(HZ/10); finish_wait(&pgdat->kswapd_wait, &wait); prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE); @@ -2677,7 +2789,7 @@ static void kswapd_try_to_sleep(pg_data_t *pgdat, int order, int classzone_idx) * After a short sleep, check if it was a premature sleep. If not, then * go fully to sleep until explicitly woken up. */ - if (!sleeping_prematurely(pgdat, order, remaining, classzone_idx)) { + if (prepare_kswapd_sleep(pgdat, order, remaining, classzone_idx)) { trace_mm_vmscan_kswapd_sleep(pgdat->node_id); /* -- cgit v1.2.3 From 68243e76ee343d63c6cf76978588a885951e2818 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:39 -0700 Subject: mm: account for the number of times direct reclaimers get throttled Under significant pressure when writing back to network-backed storage, direct reclaimers may get throttled. This is expected to be a short-lived event and the processes get woken up again but processes do get stalled. This patch counts how many times such stalling occurs. It's up to the administrator whether to reduce these stalls by increasing min_free_kbytes. Signed-off-by: Mel Gorman Cc: David Miller Cc: Neil Brown Cc: Peter Zijlstra Cc: Mike Christie Cc: Eric B Munson Cc: Eric Dumazet Cc: Sebastian Andrzej Siewior Cc: Mel Gorman Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/vm_event_item.h | 1 + mm/vmscan.c | 3 +++ mm/vmstat.c | 1 + 3 files changed, 5 insertions(+) (limited to 'include') diff --git a/include/linux/vm_event_item.h b/include/linux/vm_event_item.h index 06f8e3858251..57f7b1091511 100644 --- a/include/linux/vm_event_item.h +++ b/include/linux/vm_event_item.h @@ -30,6 +30,7 @@ enum vm_event_item { PGPGIN, PGPGOUT, PSWPIN, PSWPOUT, FOR_ALL_ZONES(PGSTEAL_DIRECT), FOR_ALL_ZONES(PGSCAN_KSWAPD), FOR_ALL_ZONES(PGSCAN_DIRECT), + PGSCAN_DIRECT_THROTTLE, #ifdef CONFIG_NUMA PGSCAN_ZONE_RECLAIM_FAILED, #endif diff --git a/mm/vmscan.c b/mm/vmscan.c index 021a44a7bd20..88804017e7d6 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2167,6 +2167,9 @@ static void throttle_direct_reclaim(gfp_t gfp_mask, struct zonelist *zonelist, if (pfmemalloc_watermark_ok(pgdat)) return; + /* Account for the throttling */ + count_vm_event(PGSCAN_DIRECT_THROTTLE); + /* * If the caller cannot enter the filesystem, it's possible that it * is due to the caller holding an FS lock or performing a journal diff --git a/mm/vmstat.c b/mm/vmstat.c index 1bbbbd9776ad..df7a6748231d 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -745,6 +745,7 @@ const char * const vmstat_text[] = { TEXTS_FOR_ZONES("pgsteal_direct") TEXTS_FOR_ZONES("pgscan_kswapd") TEXTS_FOR_ZONES("pgscan_direct") + "pgscan_direct_throttle", #ifdef CONFIG_NUMA "zone_reclaim_failed", -- cgit v1.2.3 From c76562b6709fee5eff8a6a779be41c0bce661fd7 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:41 -0700 Subject: netvm: prevent a stream-specific deadlock This patch series is based on top of "Swap-over-NBD without deadlocking v15" as it depends on the same reservation of PF_MEMALLOC reserves logic. When a user or administrator requires swap for their application, they create a swap partition and file, format it with mkswap and activate it with swapon. In diskless systems this is not an option so if swap if required then swapping over the network is considered. The two likely scenarios are when blade servers are used as part of a cluster where the form factor or maintenance costs do not allow the use of disks and thin clients. The Linux Terminal Server Project recommends the use of the Network Block Device (NBD) for swap but this is not always an option. There is no guarantee that the network attached storage (NAS) device is running Linux or supports NBD. However, it is likely that it supports NFS so there are users that want support for swapping over NFS despite any performance concern. Some distributions currently carry patches that support swapping over NFS but it would be preferable to support it in the mainline kernel. Patch 1 avoids a stream-specific deadlock that potentially affects TCP. Patch 2 is a small modification to SELinux to avoid using PFMEMALLOC reserves. Patch 3 adds three helpers for filesystems to handle swap cache pages. For example, page_file_mapping() returns page->mapping for file-backed pages and the address_space of the underlying swap file for swap cache pages. Patch 4 adds two address_space_operations to allow a filesystem to pin all metadata relevant to a swapfile in memory. Upon successful activation, the swapfile is marked SWP_FILE and the address space operation ->direct_IO is used for writing and ->readpage for reading in swap pages. Patch 5 notes that patch 3 is bolting filesystem-specific-swapfile-support onto the side and that the default handlers have different information to what is available to the filesystem. This patch refactors the code so that there are generic handlers for each of the new address_space operations. Patch 6 adds an API to allow a vector of kernel addresses to be translated to struct pages and pinned for IO. Patch 7 adds support for using highmem pages for swap by kmapping the pages before calling the direct_IO handler. Patch 8 updates NFS to use the helpers from patch 3 where necessary. Patch 9 avoids setting PF_private on PG_swapcache pages within NFS. Patch 10 implements the new swapfile-related address_space operations for NFS and teaches the direct IO handler how to manage kernel addresses. Patch 11 prevents page allocator recursions in NFS by using GFP_NOIO where appropriate. Patch 12 fixes a NULL pointer dereference that occurs when using swap-over-NFS. With the patches applied, it is possible to mount a swapfile that is on an NFS filesystem. Swap performance is not great with a swap stress test taking roughly twice as long to complete than if the swap device was backed by NBD. This patch: netvm: prevent a stream-specific deadlock It could happen that all !SOCK_MEMALLOC sockets have buffered so much data that we're over the global rmem limit. This will prevent SOCK_MEMALLOC buffers from receiving data, which will prevent userspace from running, which is needed to reduce the buffered data. Fix this by exempting the SOCK_MEMALLOC sockets from the rmem limit. Once this change it applied, it is important that sockets that set SOCK_MEMALLOC do not clear the flag until the socket is being torn down. If this happens, a warning is generated and the tokens reclaimed to avoid accounting errors until the bug is fixed. [davem@davemloft.net: Warning about clearing SOCK_MEMALLOC] Signed-off-by: Peter Zijlstra Signed-off-by: Mel Gorman Acked-by: David S. Miller Acked-by: Rik van Riel Cc: Trond Myklebust Cc: Neil Brown Cc: Christoph Hellwig Cc: Mike Christie Cc: Eric B Munson Cc: Sebastian Andrzej Siewior Cc: Mel Gorman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/net/sock.h | 8 +++++--- net/caif/caif_socket.c | 2 +- net/core/sock.c | 14 +++++++++++++- net/ipv4/tcp_input.c | 21 +++++++++++---------- net/sctp/ulpevent.c | 3 ++- 5 files changed, 32 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index 43a470d40d76..b3730239bf18 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1329,12 +1329,14 @@ static inline bool sk_wmem_schedule(struct sock *sk, int size) __sk_mem_schedule(sk, size, SK_MEM_SEND); } -static inline bool sk_rmem_schedule(struct sock *sk, int size) +static inline bool +sk_rmem_schedule(struct sock *sk, struct sk_buff *skb, unsigned int size) { if (!sk_has_account(sk)) return true; - return size <= sk->sk_forward_alloc || - __sk_mem_schedule(sk, size, SK_MEM_RECV); + return size<= sk->sk_forward_alloc || + __sk_mem_schedule(sk, size, SK_MEM_RECV) || + skb_pfmemalloc(skb); } static inline void sk_mem_reclaim(struct sock *sk) diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c index 78f1cdad5b33..095259f83902 100644 --- a/net/caif/caif_socket.c +++ b/net/caif/caif_socket.c @@ -141,7 +141,7 @@ static int caif_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) err = sk_filter(sk, skb); if (err) return err; - if (!sk_rmem_schedule(sk, skb->truesize) && rx_flow_is_on(cf_sk)) { + if (!sk_rmem_schedule(sk, skb, skb->truesize) && rx_flow_is_on(cf_sk)) { set_rx_flow_off(cf_sk); net_dbg_ratelimited("sending flow OFF due to rmem_schedule\n"); caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_OFF_REQ); diff --git a/net/core/sock.c b/net/core/sock.c index 32fdcd2d6e8f..6b654b3ddfda 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -295,6 +295,18 @@ void sk_clear_memalloc(struct sock *sk) sock_reset_flag(sk, SOCK_MEMALLOC); sk->sk_allocation &= ~__GFP_MEMALLOC; static_key_slow_dec(&memalloc_socks); + + /* + * SOCK_MEMALLOC is allowed to ignore rmem limits to ensure forward + * progress of swapping. However, if SOCK_MEMALLOC is cleared while + * it has rmem allocations there is a risk that the user of the + * socket cannot make forward progress due to exceeding the rmem + * limits. By rights, sk_clear_memalloc() should only be called + * on sockets being torn down but warn and reset the accounting if + * that assumption breaks. + */ + if (WARN_ON(sk->sk_forward_alloc)) + sk_mem_reclaim(sk); } EXPORT_SYMBOL_GPL(sk_clear_memalloc); @@ -396,7 +408,7 @@ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) if (err) return err; - if (!sk_rmem_schedule(sk, skb->truesize)) { + if (!sk_rmem_schedule(sk, skb, skb->truesize)) { atomic_inc(&sk->sk_drops); return -ENOBUFS; } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index a356e1fecf9a..00b91b4b8665 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4351,19 +4351,20 @@ static void tcp_ofo_queue(struct sock *sk) static bool tcp_prune_ofo_queue(struct sock *sk); static int tcp_prune_queue(struct sock *sk); -static int tcp_try_rmem_schedule(struct sock *sk, unsigned int size) +static int tcp_try_rmem_schedule(struct sock *sk, struct sk_buff *skb, + unsigned int size) { if (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf || - !sk_rmem_schedule(sk, size)) { + !sk_rmem_schedule(sk, skb, size)) { if (tcp_prune_queue(sk) < 0) return -1; - if (!sk_rmem_schedule(sk, size)) { + if (!sk_rmem_schedule(sk, skb, size)) { if (!tcp_prune_ofo_queue(sk)) return -1; - if (!sk_rmem_schedule(sk, size)) + if (!sk_rmem_schedule(sk, skb, size)) return -1; } } @@ -4418,7 +4419,7 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb) TCP_ECN_check_ce(tp, skb); - if (unlikely(tcp_try_rmem_schedule(sk, skb->truesize))) { + if (unlikely(tcp_try_rmem_schedule(sk, skb, skb->truesize))) { NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPOFODROP); __kfree_skb(skb); return; @@ -4552,17 +4553,17 @@ static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size) { - struct sk_buff *skb; + struct sk_buff *skb = NULL; struct tcphdr *th; bool fragstolen; - if (tcp_try_rmem_schedule(sk, size + sizeof(*th))) - goto err; - skb = alloc_skb(size + sizeof(*th), sk->sk_allocation); if (!skb) goto err; + if (tcp_try_rmem_schedule(sk, skb, size + sizeof(*th))) + goto err_free; + th = (struct tcphdr *)skb_put(skb, sizeof(*th)); skb_reset_transport_header(skb); memset(th, 0, sizeof(*th)); @@ -4633,7 +4634,7 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) if (eaten <= 0) { queue_and_out: if (eaten < 0 && - tcp_try_rmem_schedule(sk, skb->truesize)) + tcp_try_rmem_schedule(sk, skb, skb->truesize)) goto drop; eaten = tcp_queue_rcv(sk, skb, 0, &fragstolen); diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c index 33d894776192..10c018a5b9fe 100644 --- a/net/sctp/ulpevent.c +++ b/net/sctp/ulpevent.c @@ -702,7 +702,8 @@ struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc, if (rx_count >= asoc->base.sk->sk_rcvbuf) { if ((asoc->base.sk->sk_userlocks & SOCK_RCVBUF_LOCK) || - (!sk_rmem_schedule(asoc->base.sk, chunk->skb->truesize))) + (!sk_rmem_schedule(asoc->base.sk, chunk->skb, + chunk->skb->truesize))) goto fail; } -- cgit v1.2.3 From f981c5950fa85916ba49bea5d9a7a5078f47e569 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:47 -0700 Subject: mm: methods for teaching filesystems about PG_swapcache pages In order to teach filesystems to handle swap cache pages, three new page functions are introduced: pgoff_t page_file_index(struct page *); loff_t page_file_offset(struct page *); struct address_space *page_file_mapping(struct page *); page_file_index() - gives the offset of this page in the file in PAGE_CACHE_SIZE blocks. Like page->index is for mapped pages, this function also gives the correct index for PG_swapcache pages. page_file_offset() - uses page_file_index(), so that it will give the expected result, even for PG_swapcache pages. page_file_mapping() - gives the mapping backing the actual page; that is for swap cache pages it will give swap_file->f_mapping. Signed-off-by: Peter Zijlstra Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Christoph Hellwig Cc: David S. Miller Cc: Eric B Munson Cc: Eric Paris Cc: James Morris Cc: Mel Gorman Cc: Mike Christie Cc: Neil Brown Cc: Sebastian Andrzej Siewior Cc: Trond Myklebust Cc: Xiaotian Feng Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm.h | 25 +++++++++++++++++++++++++ include/linux/pagemap.h | 5 +++++ include/linux/swap.h | 1 + mm/swapfile.c | 26 ++++++++++++++++++++++++++ 4 files changed, 57 insertions(+) (limited to 'include') diff --git a/include/linux/mm.h b/include/linux/mm.h index 7c6dfd2faa69..7cdac1676b59 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -805,6 +805,17 @@ static inline void *page_rmapping(struct page *page) return (void *)((unsigned long)page->mapping & ~PAGE_MAPPING_FLAGS); } +extern struct address_space *__page_file_mapping(struct page *); + +static inline +struct address_space *page_file_mapping(struct page *page) +{ + if (unlikely(PageSwapCache(page))) + return __page_file_mapping(page); + + return page->mapping; +} + static inline int PageAnon(struct page *page) { return ((unsigned long)page->mapping & PAGE_MAPPING_ANON) != 0; @@ -821,6 +832,20 @@ static inline pgoff_t page_index(struct page *page) return page->index; } +extern pgoff_t __page_file_index(struct page *page); + +/* + * Return the file index of the page. Regular pagecache pages use ->index + * whereas swapcache pages use swp_offset(->private) + */ +static inline pgoff_t page_file_index(struct page *page) +{ + if (unlikely(PageSwapCache(page))) + return __page_file_index(page); + + return page->index; +} + /* * Return true if this page is mapped into pagetables. */ diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 7cfad3bbb0cc..e42c762f0dc7 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -286,6 +286,11 @@ static inline loff_t page_offset(struct page *page) return ((loff_t)page->index) << PAGE_CACHE_SHIFT; } +static inline loff_t page_file_offset(struct page *page) +{ + return ((loff_t)page_file_index(page)) << PAGE_CACHE_SHIFT; +} + extern pgoff_t linear_hugepage_index(struct vm_area_struct *vma, unsigned long address); diff --git a/include/linux/swap.h b/include/linux/swap.h index 9a16bb1cefd1..e62425ded2ed 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -356,6 +356,7 @@ extern unsigned int count_swap_pages(int, int); extern sector_t map_swap_page(struct page *, struct block_device **); extern sector_t swapdev_block(int, pgoff_t); extern int page_swapcount(struct page *); +extern struct swap_info_struct *page_swap_info(struct page *); extern int reuse_swap_page(struct page *); extern int try_to_free_swap(struct page *); struct backing_dev_info; diff --git a/mm/swapfile.c b/mm/swapfile.c index 71373d03fcee..f89af5ba2eb2 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -2285,6 +2286,31 @@ int swapcache_prepare(swp_entry_t entry) return __swap_duplicate(entry, SWAP_HAS_CACHE); } +struct swap_info_struct *page_swap_info(struct page *page) +{ + swp_entry_t swap = { .val = page_private(page) }; + BUG_ON(!PageSwapCache(page)); + return swap_info[swp_type(swap)]; +} + +/* + * out-of-line __page_file_ methods to avoid include hell. + */ +struct address_space *__page_file_mapping(struct page *page) +{ + VM_BUG_ON(!PageSwapCache(page)); + return page_swap_info(page)->swap_file->f_mapping; +} +EXPORT_SYMBOL_GPL(__page_file_mapping); + +pgoff_t __page_file_index(struct page *page) +{ + swp_entry_t swap = { .val = page_private(page) }; + VM_BUG_ON(!PageSwapCache(page)); + return swp_offset(swap); +} +EXPORT_SYMBOL_GPL(__page_file_index); + /* * add_swap_count_continuation - called when a swap count is duplicated * beyond SWAP_MAP_MAX, it allocates a new page and links that to the entry's -- cgit v1.2.3 From 18022c5d8627a7a9ba8097a0f238b513fae6f5b8 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:51 -0700 Subject: mm: add get_kernel_page[s] for pinning of kernel addresses for I/O This patch adds two new APIs get_kernel_pages() and get_kernel_page() that may be used to pin a vector of kernel addresses for IO. The initial user is expected to be NFS for allowing pages to be written to swap using aops->direct_IO(). Strictly speaking, swap-over-NFS only needs to pin one page for IO but it makes sense to express the API in terms of a vector and add a helper for pinning single pages. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Christoph Hellwig Cc: David S. Miller Cc: Eric B Munson Cc: Eric Paris Cc: James Morris Cc: Mel Gorman Cc: Mike Christie Cc: Neil Brown Cc: Peter Zijlstra Cc: Sebastian Andrzej Siewior Cc: Trond Myklebust Cc: Xiaotian Feng Cc: Mark Salter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/blk_types.h | 2 ++ include/linux/fs.h | 2 ++ include/linux/mm.h | 4 ++++ mm/swap.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+) (limited to 'include') diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h index 0edb65dd8edd..7b7ac9ccec7a 100644 --- a/include/linux/blk_types.h +++ b/include/linux/blk_types.h @@ -160,6 +160,7 @@ enum rq_flag_bits { __REQ_FLUSH_SEQ, /* request for flush sequence */ __REQ_IO_STAT, /* account I/O stat */ __REQ_MIXED_MERGE, /* merge of different types, fail separately */ + __REQ_KERNEL, /* direct IO to kernel pages */ __REQ_NR_BITS, /* stops here */ }; @@ -201,5 +202,6 @@ enum rq_flag_bits { #define REQ_IO_STAT (1 << __REQ_IO_STAT) #define REQ_MIXED_MERGE (1 << __REQ_MIXED_MERGE) #define REQ_SECURE (1 << __REQ_SECURE) +#define REQ_KERNEL (1 << __REQ_KERNEL) #endif /* __LINUX_BLK_TYPES_H */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 8fabb037a48d..9d77309da153 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -165,6 +165,8 @@ struct inodes_stat_t { #define READ 0 #define WRITE RW_MASK #define READA RWA_MASK +#define KERNEL_READ (READ|REQ_KERNEL) +#define KERNEL_WRITE (WRITE|REQ_KERNEL) #define READ_SYNC (READ | REQ_SYNC) #define WRITE_SYNC (WRITE | REQ_SYNC | REQ_NOIDLE) diff --git a/include/linux/mm.h b/include/linux/mm.h index 7cdac1676b59..bd079a1b0fdc 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1019,6 +1019,10 @@ int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, struct page **pages, struct vm_area_struct **vmas); int get_user_pages_fast(unsigned long start, int nr_pages, int write, struct page **pages); +struct kvec; +int get_kernel_pages(const struct kvec *iov, int nr_pages, int write, + struct page **pages); +int get_kernel_page(unsigned long start, int write, struct page **pages); struct page *get_dump_page(unsigned long addr); extern int try_to_release_page(struct page * page, gfp_t gfp_mask); diff --git a/mm/swap.c b/mm/swap.c index 4e7e2ec67078..7d7f80c8044a 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -236,6 +236,59 @@ void put_pages_list(struct list_head *pages) } EXPORT_SYMBOL(put_pages_list); +/* + * get_kernel_pages() - pin kernel pages in memory + * @kiov: An array of struct kvec structures + * @nr_segs: number of segments to pin + * @write: pinning for read/write, currently ignored + * @pages: array that receives pointers to the pages pinned. + * Should be at least nr_segs long. + * + * Returns number of pages pinned. This may be fewer than the number + * requested. If nr_pages is 0 or negative, returns 0. If no pages + * were pinned, returns -errno. Each page returned must be released + * with a put_page() call when it is finished with. + */ +int get_kernel_pages(const struct kvec *kiov, int nr_segs, int write, + struct page **pages) +{ + int seg; + + for (seg = 0; seg < nr_segs; seg++) { + if (WARN_ON(kiov[seg].iov_len != PAGE_SIZE)) + return seg; + + /* virt_to_page sanity checks the PFN */ + pages[seg] = virt_to_page(kiov[seg].iov_base); + page_cache_get(pages[seg]); + } + + return seg; +} +EXPORT_SYMBOL_GPL(get_kernel_pages); + +/* + * get_kernel_page() - pin a kernel page in memory + * @start: starting kernel address + * @write: pinning for read/write, currently ignored + * @pages: array that receives pointer to the page pinned. + * Must be at least nr_segs long. + * + * Returns 1 if page is pinned. If the page was not pinned, returns + * -errno. The page returned must be released with a put_page() call + * when it is finished with. + */ +int get_kernel_page(unsigned long start, int write, struct page **pages) +{ + const struct kvec kiov = { + .iov_base = (void *)start, + .iov_len = PAGE_SIZE + }; + + return get_kernel_pages(&kiov, 1, write, pages); +} +EXPORT_SYMBOL_GPL(get_kernel_page); + static void pagevec_lru_move_fn(struct pagevec *pvec, void (*move_fn)(struct page *page, struct lruvec *lruvec, void *arg), void *arg) -- cgit v1.2.3 From 62c230bc1790923a1b35da03596a68a6c9b5b100 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:55 -0700 Subject: mm: add support for a filesystem to activate swap files and use direct_IO for writing swap pages Currently swapfiles are managed entirely by the core VM by using ->bmap to allocate space and write to the blocks directly. This effectively ensures that the underlying blocks are allocated and avoids the need for the swap subsystem to locate what physical blocks store offsets within a file. If the swap subsystem is to use the filesystem information to locate the blocks, it is critical that information such as block groups, block bitmaps and the block descriptor table that map the swap file were resident in memory. This patch adds address_space_operations that the VM can call when activating or deactivating swap backed by a file. int swap_activate(struct file *); int swap_deactivate(struct file *); The ->swap_activate() method is used to communicate to the file that the VM relies on it, and the address_space should take adequate measures such as reserving space in the underlying device, reserving memory for mempools and pinning information such as the block descriptor table in memory. The ->swap_deactivate() method is called on sys_swapoff() if ->swap_activate() returned success. After a successful swapfile ->swap_activate, the swapfile is marked SWP_FILE and swapper_space.a_ops will proxy to sis->swap_file->f_mappings->a_ops using ->direct_io to write swapcache pages and ->readpage to read. It is perfectly possible that direct_IO be used to read the swap pages but it is an unnecessary complication. Similarly, it is possible that ->writepage be used instead of direct_io to write the pages but filesystem developers have stated that calling writepage from the VM is undesirable for a variety of reasons and using direct_IO opens up the possibility of writing back batches of swap pages in the future. [a.p.zijlstra@chello.nl: Original patch] Signed-off-by: Mel Gorman Acked-by: Rik van Riel Cc: Christoph Hellwig Cc: David S. Miller Cc: Eric B Munson Cc: Eric Paris Cc: James Morris Cc: Mel Gorman Cc: Mike Christie Cc: Neil Brown Cc: Peter Zijlstra Cc: Sebastian Andrzej Siewior Cc: Trond Myklebust Cc: Xiaotian Feng Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/Locking | 13 ++++++++++ Documentation/filesystems/vfs.txt | 12 +++++++++ include/linux/fs.h | 4 +++ include/linux/swap.h | 2 ++ mm/page_io.c | 52 +++++++++++++++++++++++++++++++++++++++ mm/swap_state.c | 2 +- mm/swapfile.c | 23 +++++++++++++++-- 7 files changed, 105 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index e0cce2a5f820..2db1900d7538 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -206,6 +206,8 @@ prototypes: int (*launder_page)(struct page *); int (*is_partially_uptodate)(struct page *, read_descriptor_t *, unsigned long); int (*error_remove_page)(struct address_space *, struct page *); + int (*swap_activate)(struct file *); + int (*swap_deactivate)(struct file *); locking rules: All except set_page_dirty and freepage may block @@ -229,6 +231,8 @@ migratepage: yes (both) launder_page: yes is_partially_uptodate: yes error_remove_page: yes +swap_activate: no +swap_deactivate: no ->write_begin(), ->write_end(), ->sync_page() and ->readpage() may be called from the request handler (/dev/loop). @@ -330,6 +334,15 @@ cleaned, or an error value if not. Note that in order to prevent the page getting mapped back in and redirtied, it needs to be kept locked across the entire operation. + ->swap_activate will be called with a non-zero argument on +files backing (non block device backed) swapfiles. A return value +of zero indicates success, in which case this file can be used for +backing swapspace. The swapspace operations will be proxied to the +address space operations. + + ->swap_deactivate() will be called in the sys_swapoff() +path after ->swap_activate() returned success. + ----------------------- file_lock_operations ------------------------------ prototypes: void (*fl_copy_lock)(struct file_lock *, struct file_lock *); diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index aa754e01464e..065aa2dc0835 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -592,6 +592,8 @@ struct address_space_operations { int (*migratepage) (struct page *, struct page *); int (*launder_page) (struct page *); int (*error_remove_page) (struct mapping *mapping, struct page *page); + int (*swap_activate)(struct file *); + int (*swap_deactivate)(struct file *); }; writepage: called by the VM to write a dirty page to backing store. @@ -760,6 +762,16 @@ struct address_space_operations { Setting this implies you deal with pages going away under you, unless you have them locked or reference counts increased. + swap_activate: Called when swapon is used on a file to allocate + space if necessary and pin the block lookup information in + memory. A return value of zero indicates success, + in which case this file can be used to back swapspace. The + swapspace operations will be proxied to this address space's + ->swap_{out,in} methods. + + swap_deactivate: Called during swapoff on files where swap_activate + was successful. + The File Object =============== diff --git a/include/linux/fs.h b/include/linux/fs.h index 9d77309da153..38356ab827c9 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -638,6 +638,10 @@ struct address_space_operations { int (*is_partially_uptodate) (struct page *, read_descriptor_t *, unsigned long); int (*error_remove_page)(struct address_space *, struct page *); + + /* swapfile support */ + int (*swap_activate)(struct file *file); + int (*swap_deactivate)(struct file *file); }; extern const struct address_space_operations empty_aops; diff --git a/include/linux/swap.h b/include/linux/swap.h index e62425ded2ed..ab230b1ebf61 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -151,6 +151,7 @@ enum { SWP_SOLIDSTATE = (1 << 4), /* blkdev seeks are cheap */ SWP_CONTINUED = (1 << 5), /* swap_map has count continuation */ SWP_BLKDEV = (1 << 6), /* its a block device */ + SWP_FILE = (1 << 7), /* set after swap_activate success */ /* add others here before... */ SWP_SCANNING = (1 << 8), /* refcount in scan_swap_map */ }; @@ -320,6 +321,7 @@ static inline void mem_cgroup_uncharge_swap(swp_entry_t ent) /* linux/mm/page_io.c */ extern int swap_readpage(struct page *); extern int swap_writepage(struct page *page, struct writeback_control *wbc); +extern int swap_set_page_dirty(struct page *page); extern void end_swap_bio_read(struct bio *bio, int err); /* linux/mm/swap_state.c */ diff --git a/mm/page_io.c b/mm/page_io.c index 34f02923744c..307a3e795290 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -94,6 +95,7 @@ int swap_writepage(struct page *page, struct writeback_control *wbc) { struct bio *bio; int ret = 0, rw = WRITE; + struct swap_info_struct *sis = page_swap_info(page); if (try_to_free_swap(page)) { unlock_page(page); @@ -105,6 +107,32 @@ int swap_writepage(struct page *page, struct writeback_control *wbc) end_page_writeback(page); goto out; } + + if (sis->flags & SWP_FILE) { + struct kiocb kiocb; + struct file *swap_file = sis->swap_file; + struct address_space *mapping = swap_file->f_mapping; + struct iovec iov = { + .iov_base = page_address(page), + .iov_len = PAGE_SIZE, + }; + + init_sync_kiocb(&kiocb, swap_file); + kiocb.ki_pos = page_file_offset(page); + kiocb.ki_left = PAGE_SIZE; + kiocb.ki_nbytes = PAGE_SIZE; + + unlock_page(page); + ret = mapping->a_ops->direct_IO(KERNEL_WRITE, + &kiocb, &iov, + kiocb.ki_pos, 1); + if (ret == PAGE_SIZE) { + count_vm_event(PSWPOUT); + ret = 0; + } + return ret; + } + bio = get_swap_bio(GFP_NOIO, page, end_swap_bio_write); if (bio == NULL) { set_page_dirty(page); @@ -126,6 +154,7 @@ int swap_readpage(struct page *page) { struct bio *bio; int ret = 0; + struct swap_info_struct *sis = page_swap_info(page); VM_BUG_ON(!PageLocked(page)); VM_BUG_ON(PageUptodate(page)); @@ -134,6 +163,17 @@ int swap_readpage(struct page *page) unlock_page(page); goto out; } + + if (sis->flags & SWP_FILE) { + struct file *swap_file = sis->swap_file; + struct address_space *mapping = swap_file->f_mapping; + + ret = mapping->a_ops->readpage(swap_file, page); + if (!ret) + count_vm_event(PSWPIN); + return ret; + } + bio = get_swap_bio(GFP_KERNEL, page, end_swap_bio_read); if (bio == NULL) { unlock_page(page); @@ -145,3 +185,15 @@ int swap_readpage(struct page *page) out: return ret; } + +int swap_set_page_dirty(struct page *page) +{ + struct swap_info_struct *sis = page_swap_info(page); + + if (sis->flags & SWP_FILE) { + struct address_space *mapping = sis->swap_file->f_mapping; + return mapping->a_ops->set_page_dirty(page); + } else { + return __set_page_dirty_no_writeback(page); + } +} diff --git a/mm/swap_state.c b/mm/swap_state.c index c85b5590cccd..0cb36fb1f61c 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -27,7 +27,7 @@ */ static const struct address_space_operations swap_aops = { .writepage = swap_writepage, - .set_page_dirty = __set_page_dirty_no_writeback, + .set_page_dirty = swap_set_page_dirty, .migratepage = migrate_page, }; diff --git a/mm/swapfile.c b/mm/swapfile.c index f89af5ba2eb2..6ffc87602f4a 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -1329,6 +1329,14 @@ static void destroy_swap_extents(struct swap_info_struct *sis) list_del(&se->list); kfree(se); } + + if (sis->flags & SWP_FILE) { + struct file *swap_file = sis->swap_file; + struct address_space *mapping = swap_file->f_mapping; + + sis->flags &= ~SWP_FILE; + mapping->a_ops->swap_deactivate(swap_file); + } } /* @@ -1410,7 +1418,9 @@ add_swap_extent(struct swap_info_struct *sis, unsigned long start_page, */ static int setup_swap_extents(struct swap_info_struct *sis, sector_t *span) { - struct inode *inode; + struct file *swap_file = sis->swap_file; + struct address_space *mapping = swap_file->f_mapping; + struct inode *inode = mapping->host; unsigned blocks_per_page; unsigned long page_no; unsigned blkbits; @@ -1421,13 +1431,22 @@ static int setup_swap_extents(struct swap_info_struct *sis, sector_t *span) int nr_extents = 0; int ret; - inode = sis->swap_file->f_mapping->host; if (S_ISBLK(inode->i_mode)) { ret = add_swap_extent(sis, 0, sis->max, 0); *span = sis->pages; goto out; } + if (mapping->a_ops->swap_activate) { + ret = mapping->a_ops->swap_activate(swap_file); + if (!ret) { + sis->flags |= SWP_FILE; + ret = add_swap_extent(sis, 0, sis->max, 0); + *span = sis->pages; + } + goto out; + } + blkbits = inode->i_blkbits; blocks_per_page = PAGE_SIZE >> blkbits; -- cgit v1.2.3 From a509bc1a9e487d952d9404318f7f990166ab57a7 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:57 -0700 Subject: mm: swap: implement generic handler for swap_activate The version of swap_activate introduced is sufficient for swap-over-NFS but would not provide enough information to implement a generic handler. This patch shuffles things slightly to ensure the same information is available for aops->swap_activate() as is available to the core. No functionality change. Signed-off-by: Mel Gorman Acked-by: Rik van Riel Cc: Christoph Hellwig Cc: David S. Miller Cc: Eric B Munson Cc: Eric Paris Cc: James Morris Cc: Mel Gorman Cc: Mike Christie Cc: Neil Brown Cc: Peter Zijlstra Cc: Sebastian Andrzej Siewior Cc: Trond Myklebust Cc: Xiaotian Feng Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/fs.h | 6 ++-- include/linux/swap.h | 5 +++ mm/page_io.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++ mm/swapfile.c | 91 +++------------------------------------------------ 4 files changed, 106 insertions(+), 88 deletions(-) (limited to 'include') diff --git a/include/linux/fs.h b/include/linux/fs.h index 38356ab827c9..c8667f8b5358 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -429,6 +429,7 @@ struct kstatfs; struct vm_area_struct; struct vfsmount; struct cred; +struct swap_info_struct; extern void __init inode_init(void); extern void __init inode_init_early(void); @@ -640,8 +641,9 @@ struct address_space_operations { int (*error_remove_page)(struct address_space *, struct page *); /* swapfile support */ - int (*swap_activate)(struct file *file); - int (*swap_deactivate)(struct file *file); + int (*swap_activate)(struct swap_info_struct *sis, struct file *file, + sector_t *span); + void (*swap_deactivate)(struct file *file); }; extern const struct address_space_operations empty_aops; diff --git a/include/linux/swap.h b/include/linux/swap.h index ab230b1ebf61..388e70601413 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -324,6 +324,11 @@ extern int swap_writepage(struct page *page, struct writeback_control *wbc); extern int swap_set_page_dirty(struct page *page); extern void end_swap_bio_read(struct bio *bio, int err); +int add_swap_extent(struct swap_info_struct *sis, unsigned long start_page, + unsigned long nr_pages, sector_t start_block); +int generic_swapfile_activate(struct swap_info_struct *, struct file *, + sector_t *); + /* linux/mm/swap_state.c */ extern struct address_space swapper_space; #define total_swapcache_pages swapper_space.nrpages diff --git a/mm/page_io.c b/mm/page_io.c index 307a3e795290..4a379629e31f 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -87,6 +87,98 @@ void end_swap_bio_read(struct bio *bio, int err) bio_put(bio); } +int generic_swapfile_activate(struct swap_info_struct *sis, + struct file *swap_file, + sector_t *span) +{ + struct address_space *mapping = swap_file->f_mapping; + struct inode *inode = mapping->host; + unsigned blocks_per_page; + unsigned long page_no; + unsigned blkbits; + sector_t probe_block; + sector_t last_block; + sector_t lowest_block = -1; + sector_t highest_block = 0; + int nr_extents = 0; + int ret; + + blkbits = inode->i_blkbits; + blocks_per_page = PAGE_SIZE >> blkbits; + + /* + * Map all the blocks into the extent list. This code doesn't try + * to be very smart. + */ + probe_block = 0; + page_no = 0; + last_block = i_size_read(inode) >> blkbits; + while ((probe_block + blocks_per_page) <= last_block && + page_no < sis->max) { + unsigned block_in_page; + sector_t first_block; + + first_block = bmap(inode, probe_block); + if (first_block == 0) + goto bad_bmap; + + /* + * It must be PAGE_SIZE aligned on-disk + */ + if (first_block & (blocks_per_page - 1)) { + probe_block++; + goto reprobe; + } + + for (block_in_page = 1; block_in_page < blocks_per_page; + block_in_page++) { + sector_t block; + + block = bmap(inode, probe_block + block_in_page); + if (block == 0) + goto bad_bmap; + if (block != first_block + block_in_page) { + /* Discontiguity */ + probe_block++; + goto reprobe; + } + } + + first_block >>= (PAGE_SHIFT - blkbits); + if (page_no) { /* exclude the header page */ + if (first_block < lowest_block) + lowest_block = first_block; + if (first_block > highest_block) + highest_block = first_block; + } + + /* + * We found a PAGE_SIZE-length, PAGE_SIZE-aligned run of blocks + */ + ret = add_swap_extent(sis, page_no, 1, first_block); + if (ret < 0) + goto out; + nr_extents += ret; + page_no++; + probe_block += blocks_per_page; +reprobe: + continue; + } + ret = nr_extents; + *span = 1 + highest_block - lowest_block; + if (page_no == 0) + page_no = 1; /* force Empty message */ + sis->max = page_no; + sis->pages = page_no - 1; + sis->highest_bit = page_no - 1; +out: + return ret; +bad_bmap: + printk(KERN_ERR "swapon: swapfile has holes\n"); + ret = -EINVAL; + goto out; +} + /* * We may have stale swap cache pages in memory: notice * them here and get rid of the unnecessary final write. diff --git a/mm/swapfile.c b/mm/swapfile.c index 6ffc87602f4a..7307fc928d7b 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -1345,7 +1345,7 @@ static void destroy_swap_extents(struct swap_info_struct *sis) * * This function rather assumes that it is called in ascending page order. */ -static int +int add_swap_extent(struct swap_info_struct *sis, unsigned long start_page, unsigned long nr_pages, sector_t start_block) { @@ -1421,106 +1421,25 @@ static int setup_swap_extents(struct swap_info_struct *sis, sector_t *span) struct file *swap_file = sis->swap_file; struct address_space *mapping = swap_file->f_mapping; struct inode *inode = mapping->host; - unsigned blocks_per_page; - unsigned long page_no; - unsigned blkbits; - sector_t probe_block; - sector_t last_block; - sector_t lowest_block = -1; - sector_t highest_block = 0; - int nr_extents = 0; int ret; if (S_ISBLK(inode->i_mode)) { ret = add_swap_extent(sis, 0, sis->max, 0); *span = sis->pages; - goto out; + return ret; } if (mapping->a_ops->swap_activate) { - ret = mapping->a_ops->swap_activate(swap_file); + ret = mapping->a_ops->swap_activate(sis, swap_file, span); if (!ret) { sis->flags |= SWP_FILE; ret = add_swap_extent(sis, 0, sis->max, 0); *span = sis->pages; } - goto out; + return ret; } - blkbits = inode->i_blkbits; - blocks_per_page = PAGE_SIZE >> blkbits; - - /* - * Map all the blocks into the extent list. This code doesn't try - * to be very smart. - */ - probe_block = 0; - page_no = 0; - last_block = i_size_read(inode) >> blkbits; - while ((probe_block + blocks_per_page) <= last_block && - page_no < sis->max) { - unsigned block_in_page; - sector_t first_block; - - first_block = bmap(inode, probe_block); - if (first_block == 0) - goto bad_bmap; - - /* - * It must be PAGE_SIZE aligned on-disk - */ - if (first_block & (blocks_per_page - 1)) { - probe_block++; - goto reprobe; - } - - for (block_in_page = 1; block_in_page < blocks_per_page; - block_in_page++) { - sector_t block; - - block = bmap(inode, probe_block + block_in_page); - if (block == 0) - goto bad_bmap; - if (block != first_block + block_in_page) { - /* Discontiguity */ - probe_block++; - goto reprobe; - } - } - - first_block >>= (PAGE_SHIFT - blkbits); - if (page_no) { /* exclude the header page */ - if (first_block < lowest_block) - lowest_block = first_block; - if (first_block > highest_block) - highest_block = first_block; - } - - /* - * We found a PAGE_SIZE-length, PAGE_SIZE-aligned run of blocks - */ - ret = add_swap_extent(sis, page_no, 1, first_block); - if (ret < 0) - goto out; - nr_extents += ret; - page_no++; - probe_block += blocks_per_page; -reprobe: - continue; - } - ret = nr_extents; - *span = 1 + highest_block - lowest_block; - if (page_no == 0) - page_no = 1; /* force Empty message */ - sis->max = page_no; - sis->pages = page_no - 1; - sis->highest_bit = page_no - 1; -out: - return ret; -bad_bmap: - printk(KERN_ERR "swapon: swapfile has holes\n"); - ret = -EINVAL; - goto out; + return generic_swapfile_activate(sis, swap_file, span); } static void enable_swap_info(struct swap_info_struct *p, int prio, -- cgit v1.2.3 From 5a178119b0fbe37f7dfb602b37df9cc4b1dc9d71 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:45:02 -0700 Subject: mm: add support for direct_IO to highmem pages The patch "mm: add support for a filesystem to activate swap files and use direct_IO for writing swap pages" added support for using direct_IO to write swap pages but it is insufficient for highmem pages. To support highmem pages, this patch kmaps() the page before calling the direct_IO() handler. As direct_IO deals with virtual addresses an additional helper is necessary for get_kernel_pages() to lookup the struct page for a kmap virtual address. Signed-off-by: Mel Gorman Acked-by: Rik van Riel Cc: Christoph Hellwig Cc: David S. Miller Cc: Eric B Munson Cc: Eric Paris Cc: James Morris Cc: Mel Gorman Cc: Mike Christie Cc: Neil Brown Cc: Peter Zijlstra Cc: Sebastian Andrzej Siewior Cc: Trond Myklebust Cc: Xiaotian Feng Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/highmem.h | 7 +++++++ mm/highmem.c | 12 ++++++++++++ mm/page_io.c | 3 ++- mm/swap.c | 3 +-- 4 files changed, 22 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/highmem.h b/include/linux/highmem.h index 774fa47b3b5b..ef788b5b4a35 100644 --- a/include/linux/highmem.h +++ b/include/linux/highmem.h @@ -39,10 +39,17 @@ extern unsigned long totalhigh_pages; void kmap_flush_unused(void); +struct page *kmap_to_page(void *addr); + #else /* CONFIG_HIGHMEM */ static inline unsigned int nr_free_highpages(void) { return 0; } +static inline struct page *kmap_to_page(void *addr) +{ + return virt_to_page(addr); +} + #define totalhigh_pages 0UL #ifndef ARCH_HAS_KMAP diff --git a/mm/highmem.c b/mm/highmem.c index 57d82c6250c3..d517cd16a6eb 100644 --- a/mm/highmem.c +++ b/mm/highmem.c @@ -94,6 +94,18 @@ static DECLARE_WAIT_QUEUE_HEAD(pkmap_map_wait); do { spin_unlock(&kmap_lock); (void)(flags); } while (0) #endif +struct page *kmap_to_page(void *vaddr) +{ + unsigned long addr = (unsigned long)vaddr; + + if (addr >= PKMAP_ADDR(0) && addr <= PKMAP_ADDR(LAST_PKMAP)) { + int i = (addr - PKMAP_ADDR(0)) >> PAGE_SHIFT; + return pte_page(pkmap_page_table[i]); + } + + return virt_to_page(addr); +} + static void flush_all_zero_pkmaps(void) { int i; diff --git a/mm/page_io.c b/mm/page_io.c index 4a379629e31f..78eee32ee486 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -205,7 +205,7 @@ int swap_writepage(struct page *page, struct writeback_control *wbc) struct file *swap_file = sis->swap_file; struct address_space *mapping = swap_file->f_mapping; struct iovec iov = { - .iov_base = page_address(page), + .iov_base = kmap(page), .iov_len = PAGE_SIZE, }; @@ -218,6 +218,7 @@ int swap_writepage(struct page *page, struct writeback_control *wbc) ret = mapping->a_ops->direct_IO(KERNEL_WRITE, &kiocb, &iov, kiocb.ki_pos, 1); + kunmap(page); if (ret == PAGE_SIZE) { count_vm_event(PSWPOUT); ret = 0; diff --git a/mm/swap.c b/mm/swap.c index 7d7f80c8044a..77825883298f 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -258,8 +258,7 @@ int get_kernel_pages(const struct kvec *kiov, int nr_segs, int write, if (WARN_ON(kiov[seg].iov_len != PAGE_SIZE)) return seg; - /* virt_to_page sanity checks the PFN */ - pages[seg] = virt_to_page(kiov[seg].iov_base); + pages[seg] = kmap_to_page(kiov[seg].iov_base); page_cache_get(pages[seg]); } -- cgit v1.2.3 From a564b8f0398636ba30b07c0eaebdef7ff7837249 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:45:12 -0700 Subject: nfs: enable swap on NFS Implement the new swapfile a_ops for NFS and hook up ->direct_IO. This will set the NFS socket to SOCK_MEMALLOC and run socket reconnect under PF_MEMALLOC as well as reset SOCK_MEMALLOC before engaging the protocol ->connect() method. PF_MEMALLOC should allow the allocation of struct socket and related objects and the early (re)setting of SOCK_MEMALLOC should allow us to receive the packets required for the TCP connection buildup. [jlayton@redhat.com: Restore PF_MEMALLOC task flags in all cases] [dfeng@redhat.com: Fix handling of multiple swap files] [a.p.zijlstra@chello.nl: Original patch] Signed-off-by: Mel Gorman Acked-by: Rik van Riel Cc: Christoph Hellwig Cc: David S. Miller Cc: Eric B Munson Cc: Eric Paris Cc: James Morris Cc: Mel Gorman Cc: Mike Christie Cc: Neil Brown Cc: Sebastian Andrzej Siewior Cc: Trond Myklebust Cc: Xiaotian Feng Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/nfs/Kconfig | 8 +++++ fs/nfs/direct.c | 82 +++++++++++++++++++++++++++++---------------- fs/nfs/file.c | 22 ++++++++++-- include/linux/nfs_fs.h | 4 +-- include/linux/sunrpc/xprt.h | 3 ++ net/sunrpc/Kconfig | 5 +++ net/sunrpc/clnt.c | 9 +++++ net/sunrpc/sched.c | 7 ++-- net/sunrpc/xprtsock.c | 43 ++++++++++++++++++++++++ 9 files changed, 149 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index 404c6a8ac394..6fd5f2cdcd1e 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -86,6 +86,14 @@ config NFS_V4 If unsure, say Y. +config NFS_SWAP + bool "Provide swap over NFS support" + default n + depends on NFS_FS + select SUNRPC_SWAP + help + This option enables swapon to work on files located on NFS mounts. + config NFS_V4_1 bool "NFS client support for NFSv4.1 (EXPERIMENTAL)" depends on NFS_V4 && EXPERIMENTAL diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 42dce909ec70..bf9c8d0ec16a 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -115,17 +115,28 @@ static inline int put_dreq(struct nfs_direct_req *dreq) * @nr_segs: size of iovec array * * The presence of this routine in the address space ops vector means - * the NFS client supports direct I/O. However, we shunt off direct - * read and write requests before the VFS gets them, so this method - * should never be called. + * the NFS client supports direct I/O. However, for most direct IO, we + * shunt off direct read and write requests before the VFS gets them, + * so this method is only ever called for swap. */ ssize_t nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_t pos, unsigned long nr_segs) { +#ifndef CONFIG_NFS_SWAP dprintk("NFS: nfs_direct_IO (%s) off/no(%Ld/%lu) EINVAL\n", iocb->ki_filp->f_path.dentry->d_name.name, (long long) pos, nr_segs); 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) + return nfs_file_direct_read(iocb, iov, nr_segs, pos, + rw == READ ? true : false); + return nfs_file_direct_write(iocb, iov, nr_segs, pos, + rw == WRITE ? true : false); +#endif /* CONFIG_NFS_SWAP */ } static void nfs_direct_release_pages(struct page **pages, unsigned int npages) @@ -303,7 +314,7 @@ static const struct nfs_pgio_completion_ops nfs_direct_read_completion_ops = { */ static ssize_t nfs_direct_read_schedule_segment(struct nfs_pageio_descriptor *desc, const struct iovec *iov, - loff_t pos) + loff_t pos, bool uio) { struct nfs_direct_req *dreq = desc->pg_dreq; struct nfs_open_context *ctx = dreq->ctx; @@ -331,12 +342,20 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_pageio_descriptor *de GFP_KERNEL); if (!pagevec) break; - down_read(¤t->mm->mmap_sem); - result = get_user_pages(current, current->mm, user_addr, + if (uio) { + down_read(¤t->mm->mmap_sem); + result = get_user_pages(current, current->mm, user_addr, npages, 1, 0, pagevec, NULL); - up_read(¤t->mm->mmap_sem); - if (result < 0) - break; + up_read(¤t->mm->mmap_sem); + if (result < 0) + break; + } else { + WARN_ON(npages != 1); + result = get_kernel_page(user_addr, 1, pagevec); + if (WARN_ON(result != 1)) + break; + } + if ((unsigned)result < npages) { bytes = result * PAGE_SIZE; if (bytes <= pgbase) { @@ -386,7 +405,7 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_pageio_descriptor *de static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq, const struct iovec *iov, unsigned long nr_segs, - loff_t pos) + loff_t pos, bool uio) { struct nfs_pageio_descriptor desc; ssize_t result = -EINVAL; @@ -400,7 +419,7 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq, for (seg = 0; seg < nr_segs; seg++) { const struct iovec *vec = &iov[seg]; - result = nfs_direct_read_schedule_segment(&desc, vec, pos); + result = nfs_direct_read_schedule_segment(&desc, vec, pos, uio); if (result < 0) break; requested_bytes += result; @@ -426,7 +445,7 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq, } static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) + unsigned long nr_segs, loff_t pos, bool uio) { ssize_t result = -ENOMEM; struct inode *inode = iocb->ki_filp->f_mapping->host; @@ -444,7 +463,7 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov, if (!is_sync_kiocb(iocb)) dreq->iocb = iocb; - result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos); + result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos, uio); if (!result) result = nfs_direct_wait(dreq); NFS_I(inode)->read_io += result; @@ -610,7 +629,7 @@ static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode */ static ssize_t nfs_direct_write_schedule_segment(struct nfs_pageio_descriptor *desc, const struct iovec *iov, - loff_t pos) + loff_t pos, bool uio) { struct nfs_direct_req *dreq = desc->pg_dreq; struct nfs_open_context *ctx = dreq->ctx; @@ -638,12 +657,19 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_pageio_descriptor *d if (!pagevec) break; - down_read(¤t->mm->mmap_sem); - result = get_user_pages(current, current->mm, user_addr, - npages, 0, 0, pagevec, NULL); - up_read(¤t->mm->mmap_sem); - if (result < 0) - break; + if (uio) { + down_read(¤t->mm->mmap_sem); + result = get_user_pages(current, current->mm, user_addr, + npages, 0, 0, pagevec, NULL); + up_read(¤t->mm->mmap_sem); + if (result < 0) + break; + } else { + WARN_ON(npages != 1); + result = get_kernel_page(user_addr, 0, pagevec); + if (WARN_ON(result != 1)) + break; + } if ((unsigned)result < npages) { bytes = result * PAGE_SIZE; @@ -774,7 +800,7 @@ static const struct nfs_pgio_completion_ops nfs_direct_write_completion_ops = { static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, const struct iovec *iov, unsigned long nr_segs, - loff_t pos) + loff_t pos, bool uio) { struct nfs_pageio_descriptor desc; struct inode *inode = dreq->inode; @@ -790,7 +816,7 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, for (seg = 0; seg < nr_segs; seg++) { const struct iovec *vec = &iov[seg]; - result = nfs_direct_write_schedule_segment(&desc, vec, pos); + result = nfs_direct_write_schedule_segment(&desc, vec, pos, uio); if (result < 0) break; requested_bytes += result; @@ -818,7 +844,7 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos, - size_t count) + size_t count, bool uio) { ssize_t result = -ENOMEM; struct inode *inode = iocb->ki_filp->f_mapping->host; @@ -836,7 +862,7 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov, if (!is_sync_kiocb(iocb)) dreq->iocb = iocb; - result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos); + result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos, uio); if (!result) result = nfs_direct_wait(dreq); out_release: @@ -867,7 +893,7 @@ out: * cache. */ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) + unsigned long nr_segs, loff_t pos, bool uio) { ssize_t retval = -EINVAL; struct file *file = iocb->ki_filp; @@ -892,7 +918,7 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov, task_io_account_read(count); - retval = nfs_direct_read(iocb, iov, nr_segs, pos); + retval = nfs_direct_read(iocb, iov, nr_segs, pos, uio); if (retval > 0) iocb->ki_pos = pos + retval; @@ -923,7 +949,7 @@ out: * is no atomic O_APPEND write facility in the NFS protocol. */ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) + unsigned long nr_segs, loff_t pos, bool uio) { ssize_t retval = -EINVAL; struct file *file = iocb->ki_filp; @@ -955,7 +981,7 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov, task_io_account_write(count); - retval = nfs_direct_write(iocb, iov, nr_segs, pos, count); + retval = nfs_direct_write(iocb, iov, nr_segs, pos, count, uio); if (retval > 0) { struct inode *inode = mapping->host; diff --git a/fs/nfs/file.c b/fs/nfs/file.c index acd4e4cd2906..50fb83a88b1b 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -175,7 +175,7 @@ nfs_file_read(struct kiocb *iocb, const struct iovec *iov, ssize_t result; if (iocb->ki_filp->f_flags & O_DIRECT) - return nfs_file_direct_read(iocb, iov, nr_segs, pos); + return nfs_file_direct_read(iocb, iov, nr_segs, pos, true); dprintk("NFS: read(%s/%s, %lu@%lu)\n", dentry->d_parent->d_name.name, dentry->d_name.name, @@ -482,6 +482,20 @@ static int nfs_launder_page(struct page *page) return nfs_wb_page(inode, page); } +#ifdef CONFIG_NFS_SWAP +static int nfs_swap_activate(struct swap_info_struct *sis, struct file *file, + sector_t *span) +{ + *span = sis->pages; + return xs_swapper(NFS_CLIENT(file->f_mapping->host)->cl_xprt, 1); +} + +static void nfs_swap_deactivate(struct file *file) +{ + xs_swapper(NFS_CLIENT(file->f_mapping->host)->cl_xprt, 0); +} +#endif + const struct address_space_operations nfs_file_aops = { .readpage = nfs_readpage, .readpages = nfs_readpages, @@ -496,6 +510,10 @@ const struct address_space_operations nfs_file_aops = { .migratepage = nfs_migrate_page, .launder_page = nfs_launder_page, .error_remove_page = generic_error_remove_page, +#ifdef CONFIG_NFS_SWAP + .swap_activate = nfs_swap_activate, + .swap_deactivate = nfs_swap_deactivate, +#endif }; /* @@ -570,7 +588,7 @@ ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov, size_t count = iov_length(iov, nr_segs); if (iocb->ki_filp->f_flags & O_DIRECT) - return nfs_file_direct_write(iocb, iov, nr_segs, pos); + return nfs_file_direct_write(iocb, iov, nr_segs, pos, true); dprintk("NFS: write(%s/%s, %lu@%Ld)\n", dentry->d_parent->d_name.name, dentry->d_name.name, diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 4b6043c20f77..35994f975a7f 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -473,10 +473,10 @@ extern ssize_t nfs_direct_IO(int, struct kiocb *, const struct iovec *, loff_t, unsigned long); extern ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, - loff_t pos); + loff_t pos, bool uio); extern ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, - loff_t pos); + loff_t pos, bool uio); /* * linux/fs/nfs/dir.c diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 77d278defa70..cff40aa7db62 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -174,6 +174,8 @@ struct rpc_xprt { unsigned long state; /* transport state */ unsigned char shutdown : 1, /* being shut down */ resvport : 1; /* use a reserved port */ + unsigned int swapper; /* we're swapping over this + transport */ unsigned int bind_index; /* bind function index */ /* @@ -316,6 +318,7 @@ void xprt_release_rqst_cong(struct rpc_task *task); void xprt_disconnect_done(struct rpc_xprt *xprt); void xprt_force_disconnect(struct rpc_xprt *xprt); void xprt_conditional_disconnect(struct rpc_xprt *xprt, unsigned int cookie); +int xs_swapper(struct rpc_xprt *xprt, int enable); /* * Reserved bit positions in xprt->state diff --git a/net/sunrpc/Kconfig b/net/sunrpc/Kconfig index 9fe8857d8d59..03d03e37a7d5 100644 --- a/net/sunrpc/Kconfig +++ b/net/sunrpc/Kconfig @@ -21,6 +21,11 @@ config SUNRPC_XPRT_RDMA If unsure, say N. +config SUNRPC_SWAP + bool + depends on SUNRPC + select NETVM + config RPCSEC_GSS_KRB5 tristate "Secure RPC: Kerberos V mechanism" depends on SUNRPC && CRYPTO diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index b05df36692ff..fa48c60aef23 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -717,6 +717,15 @@ void rpc_task_set_client(struct rpc_task *task, struct rpc_clnt *clnt) atomic_inc(&clnt->cl_count); if (clnt->cl_softrtry) task->tk_flags |= RPC_TASK_SOFT; + if (sk_memalloc_socks()) { + struct rpc_xprt *xprt; + + rcu_read_lock(); + xprt = rcu_dereference(clnt->cl_xprt); + if (xprt->swapper) + task->tk_flags |= RPC_TASK_SWAPPER; + rcu_read_unlock(); + } /* Add to the client's list of all tasks */ spin_lock(&clnt->cl_lock); list_add_tail(&task->tk_task, &clnt->cl_tasks); diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 994cfea2bad6..83a4c43cee7f 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -812,7 +812,10 @@ static void rpc_async_schedule(struct work_struct *work) void *rpc_malloc(struct rpc_task *task, size_t size) { struct rpc_buffer *buf; - gfp_t gfp = RPC_IS_SWAPPER(task) ? GFP_ATOMIC : GFP_NOWAIT; + gfp_t gfp = GFP_NOWAIT; + + if (RPC_IS_SWAPPER(task)) + gfp |= __GFP_MEMALLOC; size += sizeof(struct rpc_buffer); if (size <= RPC_BUFFER_MAXSIZE) @@ -886,7 +889,7 @@ static void rpc_init_task(struct rpc_task *task, const struct rpc_task_setup *ta static struct rpc_task * rpc_alloc_task(void) { - return (struct rpc_task *)mempool_alloc(rpc_task_mempool, GFP_NOFS); + return (struct rpc_task *)mempool_alloc(rpc_task_mempool, GFP_NOIO); } /* diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 62d0dac8f780..bd59d01f035b 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -1927,6 +1927,45 @@ out: xprt_wake_pending_tasks(xprt, status); } +#ifdef CONFIG_SUNRPC_SWAP +static void xs_set_memalloc(struct rpc_xprt *xprt) +{ + struct sock_xprt *transport = container_of(xprt, struct sock_xprt, + xprt); + + if (xprt->swapper) + sk_set_memalloc(transport->inet); +} + +/** + * xs_swapper - Tag this transport as being used for swap. + * @xprt: transport to tag + * @enable: enable/disable + * + */ +int xs_swapper(struct rpc_xprt *xprt, int enable) +{ + struct sock_xprt *transport = container_of(xprt, struct sock_xprt, + xprt); + int err = 0; + + if (enable) { + xprt->swapper++; + xs_set_memalloc(xprt); + } else if (xprt->swapper) { + xprt->swapper--; + sk_clear_memalloc(transport->inet); + } + + return err; +} +EXPORT_SYMBOL_GPL(xs_swapper); +#else +static void xs_set_memalloc(struct rpc_xprt *xprt) +{ +} +#endif + static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) { struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt); @@ -1951,6 +1990,8 @@ static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) transport->sock = sock; transport->inet = sk; + xs_set_memalloc(xprt); + write_unlock_bh(&sk->sk_callback_lock); } xs_udp_do_set_buffer_size(xprt); @@ -2075,6 +2116,8 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) if (!xprt_bound(xprt)) goto out; + xs_set_memalloc(xprt); + /* Tell the socket layer to start connecting... */ xprt->stat.connect_count++; xprt->stat.connect_start = jiffies; -- cgit v1.2.3 From 0030f535a5cf9b1841d2088c10a0b2f8f2987460 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Tue, 31 Jul 2012 16:45:25 -0700 Subject: mm: memcg: fix compaction/migration failing due to memcg limits Compaction (and page migration in general) can currently be hindered through pages being owned by memory cgroups that are at their limits and unreclaimable. The reason is that the replacement page is being charged against the limit while the page being replaced is also still charged. But this seems unnecessary, given that only one of the two pages will still be in use after migration finishes. This patch changes the memcg migration sequence so that the replacement page is not charged. Whatever page is still in use after successful or failed migration gets to keep the charge of the page that was going to be replaced. The replacement page will still show up temporarily in the rss/cache statistics, this can be fixed in a later patch as it's less urgent. Reported-by: David Rientjes Signed-off-by: Johannes Weiner Acked-by: KAMEZAWA Hiroyuki Acked-by: Michal Hocko Cc: Hugh Dickins Cc: David Rientjes Cc: Wanpeng Li Cc: Mel Gorman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 11 ++++---- mm/memcontrol.c | 67 +++++++++++++++++++++++++--------------------- mm/migrate.c | 11 ++------ 3 files changed, 43 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 5a3ee6423634..8d9489fdab2e 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -98,9 +98,9 @@ int mm_match_cgroup(const struct mm_struct *mm, const struct mem_cgroup *cgroup) extern struct cgroup_subsys_state *mem_cgroup_css(struct mem_cgroup *memcg); -extern int -mem_cgroup_prepare_migration(struct page *page, - struct page *newpage, struct mem_cgroup **memcgp, gfp_t gfp_mask); +extern void +mem_cgroup_prepare_migration(struct page *page, struct page *newpage, + struct mem_cgroup **memcgp); extern void mem_cgroup_end_migration(struct mem_cgroup *memcg, struct page *oldpage, struct page *newpage, bool migration_ok); @@ -276,11 +276,10 @@ static inline struct cgroup_subsys_state return NULL; } -static inline int +static inline void mem_cgroup_prepare_migration(struct page *page, struct page *newpage, - struct mem_cgroup **memcgp, gfp_t gfp_mask) + struct mem_cgroup **memcgp) { - return 0; } static inline void mem_cgroup_end_migration(struct mem_cgroup *memcg, diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 0f692a2dbfcb..7eadcdad06f3 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -2976,7 +2976,8 @@ direct_uncharge: * uncharge if !page_mapped(page) */ static struct mem_cgroup * -__mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype) +__mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype, + bool end_migration) { struct mem_cgroup *memcg = NULL; unsigned int nr_pages = 1; @@ -3020,7 +3021,16 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype) /* fallthrough */ case MEM_CGROUP_CHARGE_TYPE_DROP: /* See mem_cgroup_prepare_migration() */ - if (page_mapped(page) || PageCgroupMigration(pc)) + if (page_mapped(page)) + goto unlock_out; + /* + * Pages under migration may not be uncharged. But + * end_migration() /must/ be the one uncharging the + * unused post-migration page and so it has to call + * here with the migration bit still set. See the + * res_counter handling below. + */ + if (!end_migration && PageCgroupMigration(pc)) goto unlock_out; break; case MEM_CGROUP_CHARGE_TYPE_SWAPOUT: @@ -3054,7 +3064,12 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype) mem_cgroup_swap_statistics(memcg, true); mem_cgroup_get(memcg); } - if (!mem_cgroup_is_root(memcg)) + /* + * Migration does not charge the res_counter for the + * replacement page, so leave it alone when phasing out the + * page that is unused after the migration. + */ + if (!end_migration && !mem_cgroup_is_root(memcg)) mem_cgroup_do_uncharge(memcg, nr_pages, ctype); return memcg; @@ -3070,14 +3085,14 @@ void mem_cgroup_uncharge_page(struct page *page) if (page_mapped(page)) return; VM_BUG_ON(page->mapping && !PageAnon(page)); - __mem_cgroup_uncharge_common(page, MEM_CGROUP_CHARGE_TYPE_ANON); + __mem_cgroup_uncharge_common(page, MEM_CGROUP_CHARGE_TYPE_ANON, false); } void mem_cgroup_uncharge_cache_page(struct page *page) { VM_BUG_ON(page_mapped(page)); VM_BUG_ON(page->mapping); - __mem_cgroup_uncharge_common(page, MEM_CGROUP_CHARGE_TYPE_CACHE); + __mem_cgroup_uncharge_common(page, MEM_CGROUP_CHARGE_TYPE_CACHE, false); } /* @@ -3141,7 +3156,7 @@ mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout) if (!swapout) /* this was a swap cache but the swap is unused ! */ ctype = MEM_CGROUP_CHARGE_TYPE_DROP; - memcg = __mem_cgroup_uncharge_common(page, ctype); + memcg = __mem_cgroup_uncharge_common(page, ctype, false); /* * record memcg information, if swapout && memcg != NULL, @@ -3231,19 +3246,18 @@ static inline int mem_cgroup_move_swap_account(swp_entry_t entry, * Before starting migration, account PAGE_SIZE to mem_cgroup that the old * page belongs to. */ -int mem_cgroup_prepare_migration(struct page *page, - struct page *newpage, struct mem_cgroup **memcgp, gfp_t gfp_mask) +void mem_cgroup_prepare_migration(struct page *page, struct page *newpage, + struct mem_cgroup **memcgp) { struct mem_cgroup *memcg = NULL; struct page_cgroup *pc; enum charge_type ctype; - int ret = 0; *memcgp = NULL; VM_BUG_ON(PageTransHuge(page)); if (mem_cgroup_disabled()) - return 0; + return; pc = lookup_page_cgroup(page); lock_page_cgroup(pc); @@ -3288,24 +3302,9 @@ int mem_cgroup_prepare_migration(struct page *page, * we return here. */ if (!memcg) - return 0; + return; *memcgp = memcg; - ret = __mem_cgroup_try_charge(NULL, gfp_mask, 1, memcgp, false); - css_put(&memcg->css);/* drop extra refcnt */ - if (ret) { - if (PageAnon(page)) { - lock_page_cgroup(pc); - ClearPageCgroupMigration(pc); - unlock_page_cgroup(pc); - /* - * The old page may be fully unmapped while we kept it. - */ - mem_cgroup_uncharge_page(page); - } - /* we'll need to revisit this error code (we have -EINTR) */ - return -ENOMEM; - } /* * We charge new page before it's used/mapped. So, even if unlock_page() * is called before end_migration, we can catch all events on this new @@ -3318,8 +3317,12 @@ int mem_cgroup_prepare_migration(struct page *page, ctype = MEM_CGROUP_CHARGE_TYPE_CACHE; else ctype = MEM_CGROUP_CHARGE_TYPE_SHMEM; + /* + * The page is committed to the memcg, but it's not actually + * charged to the res_counter since we plan on replacing the + * old one and only one page is going to be left afterwards. + */ __mem_cgroup_commit_charge(memcg, newpage, 1, ctype, false); - return ret; } /* remove redundant charge if migration failed*/ @@ -3341,6 +3344,12 @@ void mem_cgroup_end_migration(struct mem_cgroup *memcg, used = newpage; unused = oldpage; } + anon = PageAnon(used); + __mem_cgroup_uncharge_common(unused, + anon ? MEM_CGROUP_CHARGE_TYPE_ANON + : MEM_CGROUP_CHARGE_TYPE_CACHE, + true); + css_put(&memcg->css); /* * We disallowed uncharge of pages under migration because mapcount * of the page goes down to zero, temporarly. @@ -3350,10 +3359,6 @@ void mem_cgroup_end_migration(struct mem_cgroup *memcg, lock_page_cgroup(pc); ClearPageCgroupMigration(pc); unlock_page_cgroup(pc); - anon = PageAnon(used); - __mem_cgroup_uncharge_common(unused, - anon ? MEM_CGROUP_CHARGE_TYPE_ANON - : MEM_CGROUP_CHARGE_TYPE_CACHE); /* * If a page is a file cache, radix-tree replacement is very atomic diff --git a/mm/migrate.c b/mm/migrate.c index 6c37c51565e5..77ed2d773705 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -683,7 +683,6 @@ static int __unmap_and_move(struct page *page, struct page *newpage, { int rc = -EAGAIN; int remap_swapcache = 1; - int charge = 0; struct mem_cgroup *mem; struct anon_vma *anon_vma = NULL; @@ -725,12 +724,7 @@ static int __unmap_and_move(struct page *page, struct page *newpage, } /* charge against new page */ - charge = mem_cgroup_prepare_migration(page, newpage, &mem, GFP_KERNEL); - if (charge == -ENOMEM) { - rc = -ENOMEM; - goto unlock; - } - BUG_ON(charge); + mem_cgroup_prepare_migration(page, newpage, &mem); if (PageWriteback(page)) { /* @@ -820,8 +814,7 @@ skip_unmap: put_anon_vma(anon_vma); uncharge: - if (!charge) - mem_cgroup_end_migration(mem, page, newpage, rc == 0); + mem_cgroup_end_migration(mem, page, newpage, rc == 0); unlock: unlock_page(page); out: -- cgit v1.2.3 From 6527af5d1bea219d64095a5e30c1b1e0868aae16 Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Tue, 31 Jul 2012 16:46:16 -0700 Subject: mm: remove redundant initialization pg_data_t is zeroed before reaching free_area_init_core(), so remove the now unnecessary initializations. Signed-off-by: Minchan Kim Cc: Tejun Heo Cc: Ralf Baechle Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/vmstat.h | 5 ----- mm/page_alloc.c | 9 ++------- 2 files changed, 2 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/vmstat.h b/include/linux/vmstat.h index 65efb92da996..ad2cfd53dadc 100644 --- a/include/linux/vmstat.h +++ b/include/linux/vmstat.h @@ -179,11 +179,6 @@ extern void zone_statistics(struct zone *, struct zone *, gfp_t gfp); #define add_zone_page_state(__z, __i, __d) mod_zone_page_state(__z, __i, __d) #define sub_zone_page_state(__z, __i, __d) mod_zone_page_state(__z, __i, -(__d)) -static inline void zap_zone_vm_stats(struct zone *zone) -{ - memset(zone->vm_stat, 0, sizeof(zone->vm_stat)); -} - extern void inc_zone_state(struct zone *, enum zone_stat_item); #ifdef CONFIG_SMP diff --git a/mm/page_alloc.c b/mm/page_alloc.c index a6f7576aecae..889532b8e6c1 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -4377,6 +4377,8 @@ void __init set_pageblock_order(void) * - mark all pages reserved * - mark all memory queues empty * - clear the memory bitmaps + * + * NOTE: pgdat should get zeroed by caller. */ static void __paginginit free_area_init_core(struct pglist_data *pgdat, unsigned long *zones_size, unsigned long *zholes_size) @@ -4387,10 +4389,8 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat, int ret; pgdat_resize_init(pgdat); - pgdat->nr_zones = 0; init_waitqueue_head(&pgdat->kswapd_wait); init_waitqueue_head(&pgdat->pfmemalloc_wait); - pgdat->kswapd_max_order = 0; pgdat_page_cgroup_init(pgdat); for (j = 0; j < MAX_NR_ZONES; j++) { @@ -4451,11 +4451,6 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat, zone_pcp_init(zone); lruvec_init(&zone->lruvec, zone); - zap_zone_vm_stats(zone); - zone->flags = 0; -#ifdef CONFIG_MEMORY_ISOLATION - zone->nr_pageblock_isolate = 0; -#endif if (!size) continue; -- cgit v1.2.3 From d833352a4338dc31295ed832a30c9ccff5c7a183 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:46:20 -0700 Subject: mm: hugetlbfs: close race during teardown of hugetlbfs shared page tables If a process creates a large hugetlbfs mapping that is eligible for page table sharing and forks heavily with children some of whom fault and others which destroy the mapping then it is possible for page tables to get corrupted. Some teardowns of the mapping encounter a "bad pmd" and output a message to the kernel log. The final teardown will trigger a BUG_ON in mm/filemap.c. This was reproduced in 3.4 but is known to have existed for a long time and goes back at least as far as 2.6.37. It was probably was introduced in 2.6.20 by [39dde65c: shared page table for hugetlb page]. The messages look like this; [ ..........] Lots of bad pmd messages followed by this [ 127.164256] mm/memory.c:391: bad pmd ffff880412e04fe8(80000003de4000e7). [ 127.164257] mm/memory.c:391: bad pmd ffff880412e04ff0(80000003de6000e7). [ 127.164258] mm/memory.c:391: bad pmd ffff880412e04ff8(80000003de0000e7). [ 127.186778] ------------[ cut here ]------------ [ 127.186781] kernel BUG at mm/filemap.c:134! [ 127.186782] invalid opcode: 0000 [#1] SMP [ 127.186783] CPU 7 [ 127.186784] Modules linked in: af_packet cpufreq_conservative cpufreq_userspace cpufreq_powersave acpi_cpufreq mperf ext3 jbd dm_mod coretemp crc32c_intel usb_storage ghash_clmulni_intel aesni_intel i2c_i801 r8169 mii uas sr_mod cdrom sg iTCO_wdt iTCO_vendor_support shpchp serio_raw cryptd aes_x86_64 e1000e pci_hotplug dcdbas aes_generic container microcode ext4 mbcache jbd2 crc16 sd_mod crc_t10dif i915 drm_kms_helper drm i2c_algo_bit ehci_hcd ahci libahci usbcore rtc_cmos usb_common button i2c_core intel_agp video intel_gtt fan processor thermal thermal_sys hwmon ata_generic pata_atiixp libata scsi_mod [ 127.186801] [ 127.186802] Pid: 9017, comm: hugetlbfs-test Not tainted 3.4.0-autobuild #53 Dell Inc. OptiPlex 990/06D7TR [ 127.186804] RIP: 0010:[] [] __delete_from_page_cache+0x15e/0x160 [ 127.186809] RSP: 0000:ffff8804144b5c08 EFLAGS: 00010002 [ 127.186810] RAX: 0000000000000001 RBX: ffffea000a5c9000 RCX: 00000000ffffffc0 [ 127.186811] RDX: 0000000000000000 RSI: 0000000000000009 RDI: ffff88042dfdad00 [ 127.186812] RBP: ffff8804144b5c18 R08: 0000000000000009 R09: 0000000000000003 [ 127.186813] R10: 0000000000000000 R11: 000000000000002d R12: ffff880412ff83d8 [ 127.186814] R13: ffff880412ff83d8 R14: 0000000000000000 R15: ffff880412ff83d8 [ 127.186815] FS: 00007fe18ed2c700(0000) GS:ffff88042dce0000(0000) knlGS:0000000000000000 [ 127.186816] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 127.186817] CR2: 00007fe340000503 CR3: 0000000417a14000 CR4: 00000000000407e0 [ 127.186818] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 127.186819] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 127.186820] Process hugetlbfs-test (pid: 9017, threadinfo ffff8804144b4000, task ffff880417f803c0) [ 127.186821] Stack: [ 127.186822] ffffea000a5c9000 0000000000000000 ffff8804144b5c48 ffffffff810ed83b [ 127.186824] ffff8804144b5c48 000000000000138a 0000000000001387 ffff8804144b5c98 [ 127.186825] ffff8804144b5d48 ffffffff811bc925 ffff8804144b5cb8 0000000000000000 [ 127.186827] Call Trace: [ 127.186829] [] delete_from_page_cache+0x3b/0x80 [ 127.186832] [] truncate_hugepages+0x115/0x220 [ 127.186834] [] hugetlbfs_evict_inode+0x13/0x30 [ 127.186837] [] evict+0xa7/0x1b0 [ 127.186839] [] iput_final+0xd3/0x1f0 [ 127.186840] [] iput+0x39/0x50 [ 127.186842] [] d_kill+0xf8/0x130 [ 127.186843] [] dput+0xd2/0x1a0 [ 127.186845] [] __fput+0x170/0x230 [ 127.186848] [] ? rb_erase+0xce/0x150 [ 127.186849] [] fput+0x1d/0x30 [ 127.186851] [] remove_vma+0x37/0x80 [ 127.186853] [] do_munmap+0x2d2/0x360 [ 127.186855] [] sys_shmdt+0xc9/0x170 [ 127.186857] [] system_call_fastpath+0x16/0x1b [ 127.186858] Code: 0f 1f 44 00 00 48 8b 43 08 48 8b 00 48 8b 40 28 8b b0 40 03 00 00 85 f6 0f 88 df fe ff ff 48 89 df e8 e7 cb 05 00 e9 d2 fe ff ff <0f> 0b 55 83 e2 fd 48 89 e5 48 83 ec 30 48 89 5d d8 4c 89 65 e0 [ 127.186868] RIP [] __delete_from_page_cache+0x15e/0x160 [ 127.186870] RSP [ 127.186871] ---[ end trace 7cbac5d1db69f426 ]--- The bug is a race and not always easy to reproduce. To reproduce it I was doing the following on a single socket I7-based machine with 16G of RAM. $ hugeadm --pool-pages-max DEFAULT:13G $ echo $((18*1048576*1024)) > /proc/sys/kernel/shmmax $ echo $((18*1048576*1024)) > /proc/sys/kernel/shmall $ for i in `seq 1 9000`; do ./hugetlbfs-test; done On my particular machine, it usually triggers within 10 minutes but enabling debug options can change the timing such that it never hits. Once the bug is triggered, the machine is in trouble and needs to be rebooted. The machine will respond but processes accessing proc like "ps aux" will hang due to the BUG_ON. shutdown will also hang and needs a hard reset or a sysrq-b. The basic problem is a race between page table sharing and teardown. For the most part page table sharing depends on i_mmap_mutex. In some cases, it is also taking the mm->page_table_lock for the PTE updates but with shared page tables, it is the i_mmap_mutex that is more important. Unfortunately it appears to be also insufficient. Consider the following situation Process A Process B --------- --------- hugetlb_fault shmdt LockWrite(mmap_sem) do_munmap unmap_region unmap_vmas unmap_single_vma unmap_hugepage_range Lock(i_mmap_mutex) Lock(mm->page_table_lock) huge_pmd_unshare/unmap tables <--- (1) Unlock(mm->page_table_lock) Unlock(i_mmap_mutex) huge_pte_alloc ... Lock(i_mmap_mutex) ... vma_prio_walk, find svma, spte ... Lock(mm->page_table_lock) ... share spte ... Unlock(mm->page_table_lock) ... Unlock(i_mmap_mutex) ... hugetlb_no_page <--- (2) free_pgtables unlink_file_vma hugetlb_free_pgd_range remove_vma_list In this scenario, it is possible for Process A to share page tables with Process B that is trying to tear them down. The i_mmap_mutex on its own does not prevent Process A walking Process B's page tables. At (1) above, the page tables are not shared yet so it unmaps the PMDs. Process A sets up page table sharing and at (2) faults a new entry. Process B then trips up on it in free_pgtables. This patch fixes the problem by adding a new function __unmap_hugepage_range_final that is only called when the VMA is about to be destroyed. This function clears VM_MAYSHARE during unmap_hugepage_range() under the i_mmap_mutex. This makes the VMA ineligible for sharing and avoids the race. Superficially this looks like it would then be vunerable to truncate and madvise issues but hugetlbfs has its own truncate handlers so does not use unmap_mapping_range() and does not support madvise(DONTNEED). This should be treated as a -stable candidate if it is merged. Test program is as follows. The test case was mostly written by Michal Hocko with a few minor changes to reproduce this bug. ==== CUT HERE ==== static size_t huge_page_size = (2UL << 20); static size_t nr_huge_page_A = 512; static size_t nr_huge_page_B = 5632; unsigned int get_random(unsigned int max) { struct timeval tv; gettimeofday(&tv, NULL); srandom(tv.tv_usec); return random() % max; } static void play(void *addr, size_t size) { unsigned char *start = addr, *end = start + size, *a; start += get_random(size/2); /* we could itterate on huge pages but let's give it more time. */ for (a = start; a < end; a += 4096) *a = 0; } int main(int argc, char **argv) { key_t key = IPC_PRIVATE; size_t sizeA = nr_huge_page_A * huge_page_size; size_t sizeB = nr_huge_page_B * huge_page_size; int shmidA, shmidB; void *addrA = NULL, *addrB = NULL; int nr_children = 300, n = 0; if ((shmidA = shmget(key, sizeA, IPC_CREAT|SHM_HUGETLB|0660)) == -1) { perror("shmget:"); return 1; } if ((addrA = shmat(shmidA, addrA, SHM_R|SHM_W)) == (void *)-1UL) { perror("shmat"); return 1; } if ((shmidB = shmget(key, sizeB, IPC_CREAT|SHM_HUGETLB|0660)) == -1) { perror("shmget:"); return 1; } if ((addrB = shmat(shmidB, addrB, SHM_R|SHM_W)) == (void *)-1UL) { perror("shmat"); return 1; } fork_child: switch(fork()) { case 0: switch (n%3) { case 0: play(addrA, sizeA); break; case 1: play(addrB, sizeB); break; case 2: break; } break; case -1: perror("fork:"); break; default: if (++n < nr_children) goto fork_child; play(addrA, sizeA); break; } shmdt(addrA); shmdt(addrB); do { wait(NULL); } while (--n > 0); shmctl(shmidA, IPC_RMID, NULL); shmctl(shmidB, IPC_RMID, NULL); return 0; } [akpm@linux-foundation.org: name the declaration's args, fix CONFIG_HUGETLBFS=n build] Signed-off-by: Hugh Dickins Reviewed-by: Michal Hocko Signed-off-by: Mel Gorman Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hugetlb.h | 11 +++++++++++ mm/hugetlb.c | 28 ++++++++++++++++++++++++++-- mm/memory.c | 2 +- 3 files changed, 38 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index f9db20bfa9fc..225164842ab6 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -48,6 +48,10 @@ int follow_hugetlb_page(struct mm_struct *, struct vm_area_struct *, unsigned long *, int *, int, unsigned int flags); void unmap_hugepage_range(struct vm_area_struct *, unsigned long, unsigned long, struct page *); +void __unmap_hugepage_range_final(struct mmu_gather *tlb, + struct vm_area_struct *vma, + unsigned long start, unsigned long end, + struct page *ref_page); void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma, unsigned long start, unsigned long end, struct page *ref_page); @@ -130,6 +134,13 @@ static inline void copy_huge_page(struct page *dst, struct page *src) #define hugetlb_change_protection(vma, address, end, newprot) +static inline void __unmap_hugepage_range_final(struct mmu_gather *tlb, + struct vm_area_struct *vma, unsigned long start, + unsigned long end, struct page *ref_page) +{ + BUG(); +} + static inline void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma, unsigned long start, unsigned long end, struct page *ref_page) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index c39e4beeb63a..bc727122dd44 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -2429,6 +2429,25 @@ again: tlb_end_vma(tlb, vma); } +void __unmap_hugepage_range_final(struct mmu_gather *tlb, + struct vm_area_struct *vma, unsigned long start, + unsigned long end, struct page *ref_page) +{ + __unmap_hugepage_range(tlb, vma, start, end, ref_page); + + /* + * Clear this flag so that x86's huge_pmd_share page_table_shareable + * test will fail on a vma being torn down, and not grab a page table + * on its way out. We're lucky that the flag has such an appropriate + * name, and can in fact be safely cleared here. We could clear it + * before the __unmap_hugepage_range above, but all that's necessary + * is to clear it before releasing the i_mmap_mutex. This works + * because in the context this is called, the VMA is about to be + * destroyed and the i_mmap_mutex is held. + */ + vma->vm_flags &= ~VM_MAYSHARE; +} + void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, unsigned long end, struct page *ref_page) { @@ -3012,9 +3031,14 @@ void hugetlb_change_protection(struct vm_area_struct *vma, } } spin_unlock(&mm->page_table_lock); - mutex_unlock(&vma->vm_file->f_mapping->i_mmap_mutex); - + /* + * Must flush TLB before releasing i_mmap_mutex: x86's huge_pmd_unshare + * may have cleared our pud entry and done put_page on the page table: + * once we release i_mmap_mutex, another task can do the final put_page + * and that page table be reused and filled with junk. + */ flush_tlb_range(vma, start, end); + mutex_unlock(&vma->vm_file->f_mapping->i_mmap_mutex); } int hugetlb_reserve_pages(struct inode *inode, diff --git a/mm/memory.c b/mm/memory.c index ec72a616ccd4..482f089765ff 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1345,7 +1345,7 @@ static void unmap_single_vma(struct mmu_gather *tlb, */ if (vma->vm_file) { mutex_lock(&vma->vm_file->f_mapping->i_mmap_mutex); - __unmap_hugepage_range(tlb, vma, start, end, NULL); + __unmap_hugepage_range_final(tlb, vma, start, end, NULL); mutex_unlock(&vma->vm_file->f_mapping->i_mmap_mutex); } } else -- cgit v1.2.3 From 392a325c4351339cfbf182bb5a1444df1cf65dbb Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Tue, 10 Jul 2012 19:31:51 -0700 Subject: Platform: OLPC: add a stub to drivers/platform/ for the OLPC EC driver The OLPC EC driver has outgrown arch/x86/platform/. It's time to both share common code amongst different architectures, as well as move it out of arch/x86/. The XO-1.75 is ARM-based, and the EC driver shares a lot of code with the x86 code. Signed-off-by: Andres Salomon Acked-by: Paul Fox Reviewed-by: Thomas Gleixner --- arch/x86/include/asm/olpc.h | 19 +++---------------- arch/x86/platform/olpc/olpc.c | 4 ++-- drivers/platform/Makefile | 1 + drivers/platform/olpc/Makefile | 4 ++++ drivers/platform/olpc/olpc-ec.c | 16 ++++++++++++++++ include/linux/olpc-ec.h | 29 +++++++++++++++++++++++++++++ 6 files changed, 55 insertions(+), 18 deletions(-) create mode 100644 drivers/platform/olpc/Makefile create mode 100644 drivers/platform/olpc/olpc-ec.c create mode 100644 include/linux/olpc-ec.h (limited to 'include') diff --git a/arch/x86/include/asm/olpc.h b/arch/x86/include/asm/olpc.h index 87bdbca72f94..513e9992771d 100644 --- a/arch/x86/include/asm/olpc.h +++ b/arch/x86/include/asm/olpc.h @@ -4,6 +4,7 @@ #define _ASM_X86_OLPC_H #include +#include struct olpc_platform_t { int flags; @@ -102,22 +103,8 @@ extern int pci_olpc_init(void); /* EC related functions */ -extern int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen, - unsigned char *outbuf, size_t outlen); - -/* EC commands */ - -#define EC_FIRMWARE_REV 0x08 -#define EC_WRITE_SCI_MASK 0x1b -#define EC_WAKE_UP_WLAN 0x24 -#define EC_WLAN_LEAVE_RESET 0x25 -#define EC_READ_EB_MODE 0x2a -#define EC_SET_SCI_INHIBIT 0x32 -#define EC_SET_SCI_INHIBIT_RELEASE 0x34 -#define EC_WLAN_ENTER_RESET 0x35 -#define EC_WRITE_EXT_SCI_MASK 0x38 -#define EC_SCI_QUERY 0x84 -#define EC_EXT_SCI_QUERY 0x85 +extern int olpc_ec_cmd_x86(unsigned char cmd, unsigned char *inbuf, + size_t inlen, unsigned char *outbuf, size_t outlen); /* SCI source values */ diff --git a/arch/x86/platform/olpc/olpc.c b/arch/x86/platform/olpc/olpc.c index a4bee53c2e54..796e199ac77a 100644 --- a/arch/x86/platform/olpc/olpc.c +++ b/arch/x86/platform/olpc/olpc.c @@ -125,7 +125,7 @@ static int __wait_on_obf(unsigned int line, unsigned int port, int desired) * . Unfortunately, while * OpenFirmware's source is available, the EC's is not. */ -int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen, +int olpc_ec_cmd_x86(unsigned char cmd, unsigned char *inbuf, size_t inlen, unsigned char *outbuf, size_t outlen) { unsigned long flags; @@ -201,7 +201,7 @@ err: spin_unlock_irqrestore(&ec_lock, flags); return ret; } -EXPORT_SYMBOL_GPL(olpc_ec_cmd); +EXPORT_SYMBOL_GPL(olpc_ec_cmd_x86); void olpc_ec_wakeup_set(u16 value) { diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile index 782953ae4c03..b17c16ce54ad 100644 --- a/drivers/platform/Makefile +++ b/drivers/platform/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_X86) += x86/ +obj-$(CONFIG_OLPC) += olpc/ diff --git a/drivers/platform/olpc/Makefile b/drivers/platform/olpc/Makefile new file mode 100644 index 000000000000..dc8b26bc7209 --- /dev/null +++ b/drivers/platform/olpc/Makefile @@ -0,0 +1,4 @@ +# +# OLPC XO platform-specific drivers +# +obj-$(CONFIG_OLPC) += olpc-ec.o diff --git a/drivers/platform/olpc/olpc-ec.c b/drivers/platform/olpc/olpc-ec.c new file mode 100644 index 000000000000..42026036cd3e --- /dev/null +++ b/drivers/platform/olpc/olpc-ec.c @@ -0,0 +1,16 @@ +/* + * Generic driver for the OLPC Embedded Controller. + * + * Copyright (C) 2011-2012 One Laptop per Child Foundation. + * + * Licensed under the GPL v2 or later. + */ +#include +#include + +int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen) +{ + /* Currently a stub; this will be expanded upon later. */ + return olpc_ec_cmd_x86(cmd, inbuf, inlen, outbuf, outlen); +} +EXPORT_SYMBOL_GPL(olpc_ec_cmd); diff --git a/include/linux/olpc-ec.h b/include/linux/olpc-ec.h new file mode 100644 index 000000000000..6d4e426d9fdc --- /dev/null +++ b/include/linux/olpc-ec.h @@ -0,0 +1,29 @@ +#ifndef _LINUX_OLPC_EC_H +#define _LINUX_OLPC_EC_H + +/* XO-1 EC commands */ +#define EC_FIRMWARE_REV 0x08 +#define EC_WRITE_SCI_MASK 0x1b +#define EC_WAKE_UP_WLAN 0x24 +#define EC_WLAN_LEAVE_RESET 0x25 +#define EC_READ_EB_MODE 0x2a +#define EC_SET_SCI_INHIBIT 0x32 +#define EC_SET_SCI_INHIBIT_RELEASE 0x34 +#define EC_WLAN_ENTER_RESET 0x35 +#define EC_WRITE_EXT_SCI_MASK 0x38 +#define EC_SCI_QUERY 0x84 +#define EC_EXT_SCI_QUERY 0x85 + +#ifdef CONFIG_OLPC + +extern int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, + size_t outlen); + +#else + +static inline int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, + size_t outlen) { return -ENODEV; } + +#endif /* CONFIG_OLPC */ + +#endif /* _LINUX_OLPC_EC_H */ -- cgit v1.2.3 From 3d26c20bae9e97c98f7240184427d3a38515d406 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Wed, 11 Jul 2012 17:40:25 -0700 Subject: Platform: OLPC: allow EC cmd to be overridden, and create a workqueue to call it This provides a new API allows different OLPC architectures to override the EC driver. x86 and ARM OLPC machines use completely different EC backends. The olpc_ec_cmd is synchronous, and waits for the workqueue to send the command to the EC. Multiple callers can run olpc_ec_cmd() at once, and they will by serialized and sleep while only one executes on the EC at a time. We don't provide an unregister function, as that doesn't make sense within the context of OLPC machines - there's only ever 1 EC, it's critical to functionality, and it certainly not hotpluggable. Signed-off-by: Andres Salomon Acked-by: Paul Fox Reviewed-by: Thomas Gleixner --- drivers/platform/olpc/olpc-ec.c | 112 +++++++++++++++++++++++++++++++++++++++- include/linux/olpc-ec.h | 6 +++ 2 files changed, 116 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/platform/olpc/olpc-ec.c b/drivers/platform/olpc/olpc-ec.c index 42026036cd3e..44e6a4fae79b 100644 --- a/drivers/platform/olpc/olpc-ec.c +++ b/drivers/platform/olpc/olpc-ec.c @@ -5,12 +5,120 @@ * * Licensed under the GPL v2 or later. */ +#include +#include +#include +#include #include +#include +#include #include +struct ec_cmd_desc { + u8 cmd; + u8 *inbuf, *outbuf; + size_t inlen, outlen; + + int err; + struct completion finished; + struct list_head node; + + void *priv; +}; + +static void olpc_ec_worker(struct work_struct *w); + +static DECLARE_WORK(ec_worker, olpc_ec_worker); +static LIST_HEAD(ec_cmd_q); +static DEFINE_SPINLOCK(ec_cmd_q_lock); + +static struct olpc_ec_driver *ec_driver; +static void *ec_cb_arg; +static DEFINE_MUTEX(ec_cb_lock); + +void olpc_ec_driver_register(struct olpc_ec_driver *drv, void *arg) +{ + ec_driver = drv; + ec_cb_arg = arg; +} +EXPORT_SYMBOL_GPL(olpc_ec_driver_register); + +static void olpc_ec_worker(struct work_struct *w) +{ + struct ec_cmd_desc *desc = NULL; + unsigned long flags; + + /* Grab the first pending command from the queue */ + spin_lock_irqsave(&ec_cmd_q_lock, flags); + if (!list_empty(&ec_cmd_q)) { + desc = list_first_entry(&ec_cmd_q, struct ec_cmd_desc, node); + list_del(&desc->node); + } + spin_unlock_irqrestore(&ec_cmd_q_lock, flags); + + /* Do we actually have anything to do? */ + if (!desc) + return; + + /* Protect the EC hw with a mutex; only run one cmd at a time */ + mutex_lock(&ec_cb_lock); + desc->err = ec_driver->ec_cmd(desc->cmd, desc->inbuf, desc->inlen, + desc->outbuf, desc->outlen, ec_cb_arg); + mutex_unlock(&ec_cb_lock); + + /* Finished, wake up olpc_ec_cmd() */ + complete(&desc->finished); + + /* Run the worker thread again in case there are more cmds pending */ + schedule_work(&ec_worker); +} + +/* + * Throw a cmd descripter onto the list. We now have SMP OLPC machines, so + * locking is pretty critical. + */ +static void queue_ec_descriptor(struct ec_cmd_desc *desc) +{ + unsigned long flags; + + INIT_LIST_HEAD(&desc->node); + + spin_lock_irqsave(&ec_cmd_q_lock, flags); + list_add_tail(&desc->node, &ec_cmd_q); + spin_unlock_irqrestore(&ec_cmd_q_lock, flags); + + schedule_work(&ec_worker); +} + int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen) { - /* Currently a stub; this will be expanded upon later. */ - return olpc_ec_cmd_x86(cmd, inbuf, inlen, outbuf, outlen); + struct ec_cmd_desc desc; + + /* XXX: this will be removed in later patches */ + /* Are we using old-style callers? */ + if (!ec_driver || !ec_driver->ec_cmd) + return olpc_ec_cmd_x86(cmd, inbuf, inlen, outbuf, outlen); + + /* Ensure a driver and ec hook have been registered */ + if (WARN_ON(!ec_driver || !ec_driver->ec_cmd)) + return -ENODEV; + + might_sleep(); + + desc.cmd = cmd; + desc.inbuf = inbuf; + desc.outbuf = outbuf; + desc.inlen = inlen; + desc.outlen = outlen; + desc.err = 0; + init_completion(&desc.finished); + + queue_ec_descriptor(&desc); + + /* Timeouts must be handled in the platform-specific EC hook */ + wait_for_completion(&desc.finished); + + /* The worker thread dequeues the cmd; no need to do anything here */ + return desc.err; } EXPORT_SYMBOL_GPL(olpc_ec_cmd); diff --git a/include/linux/olpc-ec.h b/include/linux/olpc-ec.h index 6d4e426d9fdc..231e96f5dfe2 100644 --- a/include/linux/olpc-ec.h +++ b/include/linux/olpc-ec.h @@ -14,8 +14,14 @@ #define EC_SCI_QUERY 0x84 #define EC_EXT_SCI_QUERY 0x85 +struct olpc_ec_driver { + int (*ec_cmd)(u8, u8 *, size_t, u8 *, size_t, void *); +}; + #ifdef CONFIG_OLPC +extern void olpc_ec_driver_register(struct olpc_ec_driver *drv, void *arg); + extern int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen); -- cgit v1.2.3 From ac2504151f5af27bbf0c0362b7da5951e05dfc43 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Fri, 13 Jul 2012 05:57:17 -0700 Subject: Platform: OLPC: turn EC driver into a platform_driver The 1.75-based OLPC EC driver already does this; let's do it for all EC drivers. This gives us nice suspend/resume hooks, amongst other things. We want to run the EC's suspend hooks later than other drivers (which may be setting wakeup masks or be running EC commands). We also want to run the EC's resume hooks earlier than other drivers (which may want to run EC commands). Signed-off-by: Andres Salomon Acked-by: Paul Fox Reviewed-by: Thomas Gleixner --- drivers/platform/olpc/olpc-ec.c | 48 +++++++++++++++++++++++++++++++++++++++++ include/linux/olpc-ec.h | 6 ++++++ 2 files changed, 54 insertions(+) (limited to 'include') diff --git a/drivers/platform/olpc/olpc-ec.c b/drivers/platform/olpc/olpc-ec.c index 44e6a4fae79b..d00523c65191 100644 --- a/drivers/platform/olpc/olpc-ec.c +++ b/drivers/platform/olpc/olpc-ec.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -122,3 +123,50 @@ int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen) return desc.err; } EXPORT_SYMBOL_GPL(olpc_ec_cmd); + +static int olpc_ec_probe(struct platform_device *pdev) +{ + int err; + + if (!ec_driver) + return -ENODEV; + + err = ec_driver->probe ? ec_driver->probe(pdev) : 0; + + return err; +} + +static int olpc_ec_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + return ec_driver->suspend ? ec_driver->suspend(pdev) : 0; +} + +static int olpc_ec_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + return ec_driver->resume ? ec_driver->resume(pdev) : 0; +} + +static const struct dev_pm_ops olpc_ec_pm_ops = { + .suspend_late = olpc_ec_suspend, + .resume_early = olpc_ec_resume, +}; + +static struct platform_driver olpc_ec_plat_driver = { + .probe = olpc_ec_probe, + .driver = { + .name = "olpc-ec", + .pm = &olpc_ec_pm_ops, + }, +}; + +static int __init olpc_ec_init_module(void) +{ + return platform_driver_register(&olpc_ec_plat_driver); +} + +module_init(olpc_ec_init_module); + +MODULE_AUTHOR("Andres Salomon "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/olpc-ec.h b/include/linux/olpc-ec.h index 231e96f5dfe2..5bb6e760aa61 100644 --- a/include/linux/olpc-ec.h +++ b/include/linux/olpc-ec.h @@ -14,7 +14,13 @@ #define EC_SCI_QUERY 0x84 #define EC_EXT_SCI_QUERY 0x85 +struct platform_device; + struct olpc_ec_driver { + int (*probe)(struct platform_device *); + int (*suspend)(struct platform_device *); + int (*resume)(struct platform_device *); + int (*ec_cmd)(u8, u8 *, size_t, u8 *, size_t, void *); }; -- cgit v1.2.3 From 4f46f8ac80416b0e8fd3aba6a0d842205fb29140 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 30 Jul 2012 21:28:27 +0200 Subject: dmaengine: shdma: restore partial transfer calculation The recent shdma driver split has mistakenly removed support for partial DMA transfer size calculation on forced termination. This patch restores it. Signed-off-by: Guennadi Liakhovetski Acked-by: Vinod Koul Signed-off-by: Paul Mundt --- drivers/dma/sh/shdma-base.c | 9 +++++++++ drivers/dma/sh/shdma.c | 12 ++++++++++++ include/linux/shdma-base.h | 2 ++ 3 files changed, 23 insertions(+) (limited to 'include') diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c index 27f5c781fd73..f4cd946d259d 100644 --- a/drivers/dma/sh/shdma-base.c +++ b/drivers/dma/sh/shdma-base.c @@ -483,6 +483,7 @@ static struct shdma_desc *shdma_add_desc(struct shdma_chan *schan, new->mark = DESC_PREPARED; new->async_tx.flags = flags; new->direction = direction; + new->partial = 0; *len -= copy_size; if (direction == DMA_MEM_TO_MEM || direction == DMA_MEM_TO_DEV) @@ -644,6 +645,14 @@ static int shdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, case DMA_TERMINATE_ALL: spin_lock_irqsave(&schan->chan_lock, flags); ops->halt_channel(schan); + + if (ops->get_partial && !list_empty(&schan->ld_queue)) { + /* Record partial transfer */ + struct shdma_desc *desc = list_first_entry(&schan->ld_queue, + struct shdma_desc, node); + desc->partial = ops->get_partial(schan, desc); + } + spin_unlock_irqrestore(&schan->chan_lock, flags); shdma_chan_ld_cleanup(schan, true); diff --git a/drivers/dma/sh/shdma.c b/drivers/dma/sh/shdma.c index 027c9be97654..f41bcc5267fd 100644 --- a/drivers/dma/sh/shdma.c +++ b/drivers/dma/sh/shdma.c @@ -381,6 +381,17 @@ static bool sh_dmae_chan_irq(struct shdma_chan *schan, int irq) 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) { @@ -632,6 +643,7 @@ static const struct shdma_ops sh_dmae_shdma_ops = { .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 __devinit sh_dmae_probe(struct platform_device *pdev) diff --git a/include/linux/shdma-base.h b/include/linux/shdma-base.h index 93f9821554b6..a3728bf66f0e 100644 --- a/include/linux/shdma-base.h +++ b/include/linux/shdma-base.h @@ -50,6 +50,7 @@ struct shdma_desc { struct list_head node; struct dma_async_tx_descriptor async_tx; enum dma_transfer_direction direction; + size_t partial; dma_cookie_t cookie; int chunks; int mark; @@ -98,6 +99,7 @@ struct shdma_ops { void (*start_xfer)(struct shdma_chan *, struct shdma_desc *); struct shdma_desc *(*embedded_desc)(void *, int); bool (*chan_irq)(struct shdma_chan *, int); + size_t (*get_partial)(struct shdma_chan *, struct shdma_desc *); }; struct shdma_dev { -- cgit v1.2.3 From c83f6bf98dc1f1a194118b3830706cebbebda8c4 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Wed, 1 Aug 2012 12:24:18 +0200 Subject: block: add partition resize function to blkpg ioctl Add a new operation code (BLKPG_RESIZE_PARTITION) to the BLKPG ioctl that allows altering the size of an existing partition, even if it is currently in use. This patch converts hd_struct->nr_sects into sequence counter because One might extend a partition while IO is happening to it and update of nr_sects can be non-atomic on 32bit machines with 64bit sector_t. This can lead to issues like reading inconsistent size of a partition. Sequence counter have been used so that readers don't have to take bdev mutex lock as we call sector_in_part() very frequently. Now all the access to hd_struct->nr_sects should happen using sequence counter read/update helper functions part_nr_sects_read/part_nr_sects_write. There is one exception though, set_capacity()/get_capacity(). I think theoritically race should exist there too but this patch does not modify set_capacity()/get_capacity() due to sheer number of call sites and I am afraid that change might break something. I have left that as a TODO item. We can handle it later if need be. This patch does not introduce any new races as such w.r.t set_capacity()/get_capacity(). v2: Add CONFIG_LBDAF test to UP preempt case as suggested by Phillip. Signed-off-by: Vivek Goyal Signed-off-by: Phillip Susi Signed-off-by: Jens Axboe --- block/genhd.c | 20 ++++++++++++---- block/ioctl.c | 59 ++++++++++++++++++++++++++++++++++++++++++++--- block/partition-generic.c | 4 +++- include/linux/blkpg.h | 1 + include/linux/genhd.h | 57 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 132 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/block/genhd.c b/block/genhd.c index 9cf5583c90ff..cac7366957c3 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -154,7 +154,7 @@ struct hd_struct *disk_part_iter_next(struct disk_part_iter *piter) part = rcu_dereference(ptbl->part[piter->idx]); if (!part) continue; - if (!part->nr_sects && + if (!part_nr_sects_read(part) && !(piter->flags & DISK_PITER_INCL_EMPTY) && !(piter->flags & DISK_PITER_INCL_EMPTY_PART0 && piter->idx == 0)) @@ -191,7 +191,7 @@ EXPORT_SYMBOL_GPL(disk_part_iter_exit); static inline int sector_in_part(struct hd_struct *part, sector_t sector) { return part->start_sect <= sector && - sector < part->start_sect + part->nr_sects; + sector < part->start_sect + part_nr_sects_read(part); } /** @@ -769,8 +769,8 @@ void __init printk_all_partitions(void) printk("%s%s %10llu %s %s", is_part0 ? "" : " ", bdevt_str(part_devt(part), devt_buf), - (unsigned long long)part->nr_sects >> 1, - disk_name(disk, part->partno, name_buf), + (unsigned long long)part_nr_sects_read(part) >> 1 + , disk_name(disk, part->partno, name_buf), uuid_buf); if (is_part0) { if (disk->driverfs_dev != NULL && @@ -862,7 +862,7 @@ static int show_partition(struct seq_file *seqf, void *v) while ((part = disk_part_iter_next(&piter))) seq_printf(seqf, "%4d %7d %10llu %s\n", MAJOR(part_devt(part)), MINOR(part_devt(part)), - (unsigned long long)part->nr_sects >> 1, + (unsigned long long)part_nr_sects_read(part) >> 1, disk_name(sgp, part->partno, buf)); disk_part_iter_exit(&piter); @@ -1268,6 +1268,16 @@ struct gendisk *alloc_disk_node(int minors, int node_id) } disk->part_tbl->part[0] = &disk->part0; + /* + * set_capacity() and get_capacity() currently don't use + * seqcounter to read/update the part0->nr_sects. Still init + * the counter as we can read the sectors in IO submission + * patch using seqence counters. + * + * TODO: Ideally set_capacity() and get_capacity() should be + * converted to make use of bd_mutex and sequence counters. + */ + seqcount_init(&disk->part0.nr_sects_seq); hd_ref_init(&disk->part0); disk->minors = minors; diff --git a/block/ioctl.c b/block/ioctl.c index ba15b2dbfb98..4476e0e85d16 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -13,7 +13,7 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user { struct block_device *bdevp; struct gendisk *disk; - struct hd_struct *part; + struct hd_struct *part, *lpart; struct blkpg_ioctl_arg a; struct blkpg_partition p; struct disk_part_iter piter; @@ -36,8 +36,8 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user case BLKPG_ADD_PARTITION: start = p.start >> 9; length = p.length >> 9; - /* check for fit in a hd_struct */ - if (sizeof(sector_t) == sizeof(long) && + /* check for fit in a hd_struct */ + if (sizeof(sector_t) == sizeof(long) && sizeof(long long) > sizeof(long)) { long pstart = start, plength = length; if (pstart != start || plength != length @@ -91,6 +91,59 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user mutex_unlock(&bdevp->bd_mutex); bdput(bdevp); + return 0; + case BLKPG_RESIZE_PARTITION: + start = p.start >> 9; + /* new length of partition in bytes */ + length = p.length >> 9; + /* check for fit in a hd_struct */ + if (sizeof(sector_t) == sizeof(long) && + sizeof(long long) > sizeof(long)) { + long pstart = start, plength = length; + if (pstart != start || plength != length + || pstart < 0 || plength < 0) + return -EINVAL; + } + part = disk_get_part(disk, partno); + if (!part) + return -ENXIO; + bdevp = bdget(part_devt(part)); + if (!bdevp) { + disk_put_part(part); + return -ENOMEM; + } + mutex_lock(&bdevp->bd_mutex); + mutex_lock_nested(&bdev->bd_mutex, 1); + if (start != part->start_sect) { + mutex_unlock(&bdevp->bd_mutex); + mutex_unlock(&bdev->bd_mutex); + bdput(bdevp); + disk_put_part(part); + return -EINVAL; + } + /* overlap? */ + disk_part_iter_init(&piter, disk, + DISK_PITER_INCL_EMPTY); + while ((lpart = disk_part_iter_next(&piter))) { + if (lpart->partno != partno && + !(start + length <= lpart->start_sect || + start >= lpart->start_sect + lpart->nr_sects) + ) { + disk_part_iter_exit(&piter); + mutex_unlock(&bdevp->bd_mutex); + mutex_unlock(&bdev->bd_mutex); + bdput(bdevp); + disk_put_part(part); + return -EBUSY; + } + } + disk_part_iter_exit(&piter); + part_nr_sects_write(part, (sector_t)length); + i_size_write(bdevp->bd_inode, p.length); + mutex_unlock(&bdevp->bd_mutex); + mutex_unlock(&bdev->bd_mutex); + bdput(bdevp); + disk_put_part(part); return 0; default: return -EINVAL; diff --git a/block/partition-generic.c b/block/partition-generic.c index 6df5d6928a44..f1d14519cc04 100644 --- a/block/partition-generic.c +++ b/block/partition-generic.c @@ -84,7 +84,7 @@ ssize_t part_size_show(struct device *dev, struct device_attribute *attr, char *buf) { struct hd_struct *p = dev_to_part(dev); - return sprintf(buf, "%llu\n",(unsigned long long)p->nr_sects); + return sprintf(buf, "%llu\n",(unsigned long long)part_nr_sects_read(p)); } static ssize_t part_ro_show(struct device *dev, @@ -294,6 +294,8 @@ struct hd_struct *add_partition(struct gendisk *disk, int partno, err = -ENOMEM; goto out_free; } + + seqcount_init(&p->nr_sects_seq); pdev = part_to_dev(p); p->start_sect = start; diff --git a/include/linux/blkpg.h b/include/linux/blkpg.h index faf8a45af210..a8519446c111 100644 --- a/include/linux/blkpg.h +++ b/include/linux/blkpg.h @@ -40,6 +40,7 @@ struct blkpg_ioctl_arg { /* The subfunctions (for the op field) */ #define BLKPG_ADD_PARTITION 1 #define BLKPG_DEL_PARTITION 2 +#define BLKPG_RESIZE_PARTITION 3 /* Sizes of name fields. Unused at present. */ #define BLKPG_DEVNAMELTH 64 diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 017a7fb5a1fc..b88723b81b3d 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -98,7 +98,13 @@ struct partition_meta_info { struct hd_struct { sector_t start_sect; + /* + * nr_sects is protected by sequence counter. One might extend a + * partition while IO is happening to it and update of nr_sects + * can be non-atomic on 32bit machines with 64bit sector_t. + */ sector_t nr_sects; + seqcount_t nr_sects_seq; sector_t alignment_offset; unsigned int discard_alignment; struct device __dev; @@ -648,6 +654,57 @@ static inline void hd_struct_put(struct hd_struct *part) __delete_partition(part); } +/* + * Any access of part->nr_sects which is not protected by partition + * bd_mutex or gendisk bdev bd_mutex, should be done using this + * accessor function. + * + * Code written along the lines of i_size_read() and i_size_write(). + * CONFIG_PREEMPT case optimizes the case of UP kernel with preemption + * on. + */ +static inline sector_t part_nr_sects_read(struct hd_struct *part) +{ +#if BITS_PER_LONG==32 && defined(CONFIG_LBDAF) && defined(CONFIG_SMP) + sector_t nr_sects; + unsigned seq; + do { + seq = read_seqcount_begin(&part->nr_sects_seq); + nr_sects = part->nr_sects; + } while (read_seqcount_retry(&part->nr_sects_seq, seq)); + return nr_sects; +#elif BITS_PER_LONG==32 && defined(CONFIG_LBDAF) && defined(CONFIG_PREEMPT) + sector_t nr_sects; + + preempt_disable(); + nr_sects = part->nr_sects; + preempt_enable(); + return nr_sects; +#else + return part->nr_sects; +#endif +} + +/* + * Should be called with mutex lock held (typically bd_mutex) of partition + * to provide mutual exlusion among writers otherwise seqcount might be + * left in wrong state leaving the readers spinning infinitely. + */ +static inline void part_nr_sects_write(struct hd_struct *part, sector_t size) +{ +#if BITS_PER_LONG==32 && defined(CONFIG_LBDAF) && defined(CONFIG_SMP) + write_seqcount_begin(&part->nr_sects_seq); + part->nr_sects = size; + write_seqcount_end(&part->nr_sects_seq); +#elif BITS_PER_LONG==32 && defined(CONFIG_LBDAF) && defined(CONFIG_PREEMPT) + preempt_disable(); + part->nr_sects = size; + preempt_enable(); +#else + part->nr_sects = size; +#endif +} + #else /* CONFIG_BLOCK */ static inline void printk_all_partitions(void) { } -- cgit v1.2.3 From 068535f1fef4c90aee23eb7b9b9a71c5b72d7cd0 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 1 Aug 2012 07:56:16 -0400 Subject: locks: remove unused lm_release_private In commit 3b6e2723f32d ("locks: prevent side-effects of locks_release_private before file_lock is initialized") we removed the last user of lm_release_private without removing the field itself. Signed-off-by: J. Bruce Fields Signed-off-by: Linus Torvalds --- Documentation/filesystems/Locking | 2 -- fs/locks.c | 6 +----- include/linux/fs.h | 1 - 3 files changed, 1 insertion(+), 8 deletions(-) (limited to 'include') diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 2db1900d7538..7f647e17830c 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -359,7 +359,6 @@ prototypes: int (*lm_compare_owner)(struct file_lock *, struct file_lock *); void (*lm_notify)(struct file_lock *); /* unblock callback */ int (*lm_grant)(struct file_lock *, struct file_lock *, int); - void (*lm_release_private)(struct file_lock *); void (*lm_break)(struct file_lock *); /* break_lease callback */ int (*lm_change)(struct file_lock **, int); @@ -368,7 +367,6 @@ locking rules: lm_compare_owner: yes no lm_notify: yes no lm_grant: no no -lm_release_private: maybe no lm_break: yes no lm_change yes no diff --git a/fs/locks.c b/fs/locks.c index cdcf219a7391..7e81bfc75164 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -200,11 +200,7 @@ void locks_release_private(struct file_lock *fl) fl->fl_ops->fl_release_private(fl); fl->fl_ops = NULL; } - if (fl->fl_lmops) { - if (fl->fl_lmops->lm_release_private) - fl->fl_lmops->lm_release_private(fl); - fl->fl_lmops = NULL; - } + fl->fl_lmops = NULL; } EXPORT_SYMBOL_GPL(locks_release_private); diff --git a/include/linux/fs.h b/include/linux/fs.h index d7eed5b98ae2..4ba5c8715523 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1162,7 +1162,6 @@ struct lock_manager_operations { int (*lm_compare_owner)(struct file_lock *, struct file_lock *); void (*lm_notify)(struct file_lock *); /* unblock callback */ int (*lm_grant)(struct file_lock *, struct file_lock *, int); - void (*lm_release_private)(struct file_lock *); void (*lm_break)(struct file_lock *); int (*lm_change)(struct file_lock **, int); }; -- cgit v1.2.3 From 30b678d844af3305cda5953467005cebb5d7b687 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 30 Jul 2012 15:57:00 +0000 Subject: net: Allow driver to limit number of GSO segments per skb A peer (or local user) may cause TCP to use a nominal MSS of as little as 88 (actual MSS of 76 with timestamps). Given that we have a sufficiently prodigious local sender and the peer ACKs quickly enough, it is nevertheless possible to grow the window for such a connection to the point that we will try to send just under 64K at once. This results in a single skb that expands to 861 segments. In some drivers with TSO support, such an skb will require hundreds of DMA descriptors; a substantial fraction of a TX ring or even more than a full ring. The TX queue selected for the skb may stall and trigger the TX watchdog repeatedly (since the problem skb will be retried after the TX reset). This particularly affects sfc, for which the issue is designated as CVE-2012-3412. Therefore: 1. Add the field net_device::gso_max_segs holding the device-specific limit. 2. In netif_skb_features(), if the number of segments is too high then mask out GSO features to force fall back to software GSO. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 ++ net/core/dev.c | 4 ++++ 2 files changed, 6 insertions(+) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index eb06e58bed0b..a9db4f33407f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1300,6 +1300,8 @@ struct net_device { /* for setting kernel sock attribute on TCP connection setup */ #define GSO_MAX_SIZE 65536 unsigned int gso_max_size; +#define GSO_MAX_SEGS 65535 + u16 gso_max_segs; #ifdef CONFIG_DCB /* Data Center Bridging netlink ops */ diff --git a/net/core/dev.c b/net/core/dev.c index 0cb3fe8d8e72..f91abf800161 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2134,6 +2134,9 @@ netdev_features_t netif_skb_features(struct sk_buff *skb) __be16 protocol = skb->protocol; netdev_features_t features = skb->dev->features; + if (skb_shinfo(skb)->gso_segs > skb->dev->gso_max_segs) + features &= ~NETIF_F_GSO_MASK; + if (protocol == htons(ETH_P_8021Q)) { struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data; protocol = veh->h_vlan_encapsulated_proto; @@ -5986,6 +5989,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, dev_net_set(dev, &init_net); dev->gso_max_size = GSO_MAX_SIZE; + dev->gso_max_segs = GSO_MAX_SEGS; INIT_LIST_HEAD(&dev->napi_list); INIT_LIST_HEAD(&dev->unreg_list); -- cgit v1.2.3 From 1485348d2424e1131ea42efc033cbd9366462b01 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 30 Jul 2012 16:11:42 +0000 Subject: tcp: Apply device TSO segment limit earlier Cache the device gso_max_segs in sock::sk_gso_max_segs and use it to limit the size of TSO skbs. This avoids the need to fall back to software GSO for local TCP senders. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- include/net/sock.h | 2 ++ net/core/sock.c | 1 + net/ipv4/tcp.c | 4 +++- net/ipv4/tcp_cong.c | 3 ++- net/ipv4/tcp_output.c | 21 ++++++++++++--------- 5 files changed, 20 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index b3730239bf18..72132aef53fc 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -218,6 +218,7 @@ struct cg_proto; * @sk_route_nocaps: forbidden route capabilities (e.g NETIF_F_GSO_MASK) * @sk_gso_type: GSO type (e.g. %SKB_GSO_TCPV4) * @sk_gso_max_size: Maximum GSO segment size to build + * @sk_gso_max_segs: Maximum number of GSO segments * @sk_lingertime: %SO_LINGER l_linger setting * @sk_backlog: always used with the per-socket spinlock held * @sk_callback_lock: used with the callbacks in the end of this struct @@ -338,6 +339,7 @@ struct sock { netdev_features_t sk_route_nocaps; int sk_gso_type; unsigned int sk_gso_max_size; + u16 sk_gso_max_segs; int sk_rcvlowat; unsigned long sk_lingertime; struct sk_buff_head sk_error_queue; diff --git a/net/core/sock.c b/net/core/sock.c index 6b654b3ddfda..8f67ced8d6a8 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1458,6 +1458,7 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst) } else { sk->sk_route_caps |= NETIF_F_SG | NETIF_F_HW_CSUM; sk->sk_gso_max_size = dst->dev->gso_max_size; + sk->sk_gso_max_segs = dst->dev->gso_max_segs; } } } diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index e7e6eeae49c0..2109ff4a1daf 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -811,7 +811,9 @@ static unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now, old_size_goal + mss_now > xmit_size_goal)) { xmit_size_goal = old_size_goal; } else { - tp->xmit_size_goal_segs = xmit_size_goal / mss_now; + tp->xmit_size_goal_segs = + min_t(u16, xmit_size_goal / mss_now, + sk->sk_gso_max_segs); xmit_size_goal = tp->xmit_size_goal_segs * mss_now; } } diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index 4d4db16e336e..1432cdb0644c 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -291,7 +291,8 @@ bool tcp_is_cwnd_limited(const struct sock *sk, u32 in_flight) left = tp->snd_cwnd - in_flight; if (sk_can_gso(sk) && left * sysctl_tcp_tso_win_divisor < tp->snd_cwnd && - left * tp->mss_cache < sk->sk_gso_max_size) + left * tp->mss_cache < sk->sk_gso_max_size && + left < sk->sk_gso_max_segs) return true; return left <= tcp_max_tso_deferred_mss(tp); } diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 3f1bcff0b10b..a7b3ec9b6c3e 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1522,21 +1522,21 @@ static void tcp_cwnd_validate(struct sock *sk) * when we would be allowed to send the split-due-to-Nagle skb fully. */ static unsigned int tcp_mss_split_point(const struct sock *sk, const struct sk_buff *skb, - unsigned int mss_now, unsigned int cwnd) + unsigned int mss_now, unsigned int max_segs) { const struct tcp_sock *tp = tcp_sk(sk); - u32 needed, window, cwnd_len; + u32 needed, window, max_len; window = tcp_wnd_end(tp) - TCP_SKB_CB(skb)->seq; - cwnd_len = mss_now * cwnd; + max_len = mss_now * max_segs; - if (likely(cwnd_len <= window && skb != tcp_write_queue_tail(sk))) - return cwnd_len; + if (likely(max_len <= window && skb != tcp_write_queue_tail(sk))) + return max_len; needed = min(skb->len, window); - if (cwnd_len <= needed) - return cwnd_len; + if (max_len <= needed) + return max_len; return needed - needed % mss_now; } @@ -1765,7 +1765,8 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb) limit = min(send_win, cong_win); /* If a full-sized TSO skb can be sent, do it. */ - if (limit >= sk->sk_gso_max_size) + if (limit >= min_t(unsigned int, sk->sk_gso_max_size, + sk->sk_gso_max_segs * tp->mss_cache)) goto send_now; /* Middle in queue won't get any more data, full sendable already? */ @@ -1999,7 +2000,9 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, limit = mss_now; if (tso_segs > 1 && !tcp_urg_mode(tp)) limit = tcp_mss_split_point(sk, skb, mss_now, - cwnd_quota); + min_t(unsigned int, + cwnd_quota, + sk->sk_gso_max_segs)); if (skb->len > limit && unlikely(tso_fragment(sk, skb, limit, mss_now, gfp))) -- cgit v1.2.3 From e3c0d04750751389d5116267f8cf4687444d9a50 Mon Sep 17 00:00:00 2001 From: Fan Du Date: Mon, 30 Jul 2012 21:43:54 +0000 Subject: Fix unexpected SA hard expiration after changing date After SA is setup, one timer is armed to detect soft/hard expiration, however the timer handler uses xtime to do the math. This makes hard expiration occurs first before soft expiration after setting new date with big interval. As a result new child SA is deleted before rekeying the new one. Signed-off-by: Fan Du Signed-off-by: David S. Miller --- include/net/xfrm.h | 4 ++++ net/xfrm/xfrm_state.c | 21 +++++++++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index d9509eb29b80..62b619e82a90 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -213,6 +213,9 @@ struct xfrm_state { struct xfrm_lifetime_cur curlft; struct tasklet_hrtimer mtimer; + /* used to fix curlft->add_time when changing date */ + long saved_tmo; + /* Last used time */ unsigned long lastused; @@ -238,6 +241,7 @@ static inline struct net *xs_net(struct xfrm_state *x) /* xflags - make enum if more show up */ #define XFRM_TIME_DEFER 1 +#define XFRM_SOFT_EXPIRE 2 enum { XFRM_STATE_VOID, diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 5b228f97d4b3..87cd0e4d4282 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -415,8 +415,17 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer * me) if (x->lft.hard_add_expires_seconds) { long tmo = x->lft.hard_add_expires_seconds + x->curlft.add_time - now; - if (tmo <= 0) - goto expired; + if (tmo <= 0) { + if (x->xflags & XFRM_SOFT_EXPIRE) { + /* enter hard expire without soft expire first?! + * setting a new date could trigger this. + * workarbound: fix x->curflt.add_time by below: + */ + x->curlft.add_time = now - x->saved_tmo - 1; + tmo = x->lft.hard_add_expires_seconds - x->saved_tmo; + } else + goto expired; + } if (tmo < next) next = tmo; } @@ -433,10 +442,14 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer * me) if (x->lft.soft_add_expires_seconds) { long tmo = x->lft.soft_add_expires_seconds + x->curlft.add_time - now; - if (tmo <= 0) + if (tmo <= 0) { warn = 1; - else if (tmo < next) + x->xflags &= ~XFRM_SOFT_EXPIRE; + } else if (tmo < next) { next = tmo; + x->xflags |= XFRM_SOFT_EXPIRE; + x->saved_tmo = tmo; + } } if (x->lft.soft_use_expires_seconds) { long tmo = x->lft.soft_use_expires_seconds + -- cgit v1.2.3 From c6e666345e1b79c62ba82339cc7d55a89cb73f88 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 2 Aug 2012 09:48:50 +0200 Subject: block: split discard into aligned requests When a disk has large discard_granularity and small max_discard_sectors, discards are not split with optimal alignment. In the limit case of discard_granularity == max_discard_sectors, no request could be aligned correctly, so in fact you might end up with no discarded logical blocks at all. Another example that helps showing the condition in the patch is with discard_granularity == 64, max_discard_sectors == 128. A request that is submitted for 256 sectors 2..257 will be split in two: 2..129, 130..257. However, only 2 aligned blocks out of 3 are included in the request; 128..191 may be left intact and not discarded. With this patch, the first request will be truncated to ensure good alignment of what's left, and the split will be 2..127, 128..255, 256..257. The patch will also take into account the discard_alignment. At most one extra request will be introduced, because the first request will be reduced by at most granularity-1 sectors, and granularity must be less than max_discard_sectors. Subsequent requests will run on round_down(max_discard_sectors, granularity) sectors, as in the current code. Signed-off-by: Paolo Bonzini Acked-by: Vivek Goyal Tested-by: Mike Snitzer Signed-off-by: Jens Axboe --- block/blk-lib.c | 34 ++++++++++++++++++++++++---------- include/linux/blkdev.h | 10 ++++++++++ 2 files changed, 34 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/block/blk-lib.c b/block/blk-lib.c index 16b06f62e68c..19cc761cacb2 100644 --- a/block/blk-lib.c +++ b/block/blk-lib.c @@ -44,7 +44,7 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector, struct request_queue *q = bdev_get_queue(bdev); int type = REQ_WRITE | REQ_DISCARD; unsigned int max_discard_sectors; - unsigned int granularity; + unsigned int granularity, alignment, mask; struct bio_batch bb; struct bio *bio; int ret = 0; @@ -57,10 +57,12 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector, /* Zero-sector (unknown) and one-sector granularities are the same. */ granularity = max(q->limits.discard_granularity >> 9, 1U); + mask = granularity - 1; + alignment = (bdev_discard_alignment(bdev) >> 9) & mask; /* * Ensure that max_discard_sectors is of the proper - * granularity + * granularity, so that requests stay aligned after a split. */ max_discard_sectors = min(q->limits.max_discard_sectors, UINT_MAX >> 9); max_discard_sectors = round_down(max_discard_sectors, granularity); @@ -80,25 +82,37 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector, bb.wait = &wait; while (nr_sects) { + unsigned int req_sects; + sector_t end_sect; + bio = bio_alloc(gfp_mask, 1); if (!bio) { ret = -ENOMEM; break; } + req_sects = min_t(sector_t, nr_sects, max_discard_sectors); + + /* + * If splitting a request, and the next starting sector would be + * misaligned, stop the discard at the previous aligned sector. + */ + end_sect = sector + req_sects; + if (req_sects < nr_sects && (end_sect & mask) != alignment) { + end_sect = + round_down(end_sect - alignment, granularity) + + alignment; + req_sects = end_sect - sector; + } + bio->bi_sector = sector; bio->bi_end_io = bio_batch_end_io; bio->bi_bdev = bdev; bio->bi_private = &bb; - if (nr_sects > max_discard_sectors) { - bio->bi_size = max_discard_sectors << 9; - nr_sects -= max_discard_sectors; - sector += max_discard_sectors; - } else { - bio->bi_size = nr_sects << 9; - nr_sects = 0; - } + bio->bi_size = req_sects << 9; + nr_sects -= req_sects; + sector = end_sect; atomic_inc(&bb.done); submit_bio(type, bio); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 4e72a9d48232..281516ae8b4e 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1139,6 +1139,16 @@ static inline int queue_limit_discard_alignment(struct queue_limits *lim, sector & (lim->discard_granularity - 1); } +static inline int bdev_discard_alignment(struct block_device *bdev) +{ + struct request_queue *q = bdev_get_queue(bdev); + + if (bdev != bdev->bd_contains) + return bdev->bd_part->discard_alignment; + + return q->limits.discard_alignment; +} + static inline unsigned int queue_discard_zeroes_data(struct request_queue *q) { if (q->limits.max_discard_sectors && q->limits.discard_zeroes_data == 1) -- cgit v1.2.3 From c263c2c1ad615e935d563cd7be11d417f94895d9 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Mon, 23 Jul 2012 18:20:12 +0200 Subject: bcma: BCM43228 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/bcma/host_pci.c | 1 + drivers/bcma/sprom.c | 4 +++- include/linux/bcma/bcma_driver_chipcommon.h | 6 ++++++ 3 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c index 11b32d2642df..a6e5672c67e7 100644 --- a/drivers/bcma/host_pci.c +++ b/drivers/bcma/host_pci.c @@ -272,6 +272,7 @@ static DEFINE_PCI_DEVICE_TABLE(bcma_pci_bridge_tbl) = { { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4331) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4353) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4357) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4359) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) }, { 0, }, }; diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c index 26823d97fd9f..9ea4627dc0c2 100644 --- a/drivers/bcma/sprom.c +++ b/drivers/bcma/sprom.c @@ -507,7 +507,9 @@ static bool bcma_sprom_onchip_available(struct bcma_bus *bus) /* for these chips OTP is always available */ present = true; break; - + case BCMA_CHIP_ID_BCM43228: + present = chip_status & BCMA_CC_CHIPST_43228_OTP_PRESENT; + break; default: present = false; break; diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h index 3c80885fa829..d323a4b4143c 100644 --- a/include/linux/bcma/bcma_driver_chipcommon.h +++ b/include/linux/bcma/bcma_driver_chipcommon.h @@ -89,6 +89,12 @@ #define BCMA_CC_CHIPST_4313_OTP_PRESENT 2 #define BCMA_CC_CHIPST_4331_SPROM_PRESENT 2 #define BCMA_CC_CHIPST_4331_OTP_PRESENT 4 +#define BCMA_CC_CHIPST_43228_ILP_DIV_EN 0x00000001 +#define BCMA_CC_CHIPST_43228_OTP_PRESENT 0x00000002 +#define BCMA_CC_CHIPST_43228_SERDES_REFCLK_PADSEL 0x00000004 +#define BCMA_CC_CHIPST_43228_SDIO_MODE 0x00000008 +#define BCMA_CC_CHIPST_43228_SDIO_OTP_PRESENT 0x00000010 +#define BCMA_CC_CHIPST_43228_SDIO_RESET 0x00000020 #define BCMA_CC_CHIPST_4706_PKG_OPTION BIT(0) /* 0: full-featured package 1: low-cost package */ #define BCMA_CC_CHIPST_4706_SFLASH_PRESENT BIT(1) /* 0: parallel, 1: serial flash is present */ #define BCMA_CC_CHIPST_4706_SFLASH_TYPE BIT(2) /* 0: 8b-p/ST-s flash, 1: 16b-p/Atmal-s flash */ -- cgit v1.2.3 From f6166384095b7ecf77752b5e9096e6d03d75f7ae Mon Sep 17 00:00:00 2001 From: Peng Tao Date: Thu, 2 Aug 2012 15:36:09 +0300 Subject: NFS41: add pg_layout_private to nfs_pageio_descriptor To allow layout driver to pass private information around pg_init/pg_doio. Signed-off-by: Peng Tao Signed-off-by: Boaz Harrosh Signed-off-by: Trond Myklebust --- fs/nfs/pagelist.c | 2 ++ include/linux/nfs_page.h | 1 + include/linux/nfs_xdr.h | 1 + 3 files changed, 4 insertions(+) (limited to 'include') diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index 1a6732ed04a4..311a79681e2b 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -49,6 +49,7 @@ void nfs_pgheader_init(struct nfs_pageio_descriptor *desc, hdr->io_start = req_offset(hdr->req); hdr->good_bytes = desc->pg_count; hdr->dreq = desc->pg_dreq; + hdr->layout_private = desc->pg_layout_private; hdr->release = release; hdr->completion_ops = desc->pg_completion_ops; if (hdr->completion_ops->init_hdr) @@ -268,6 +269,7 @@ void nfs_pageio_init(struct nfs_pageio_descriptor *desc, desc->pg_error = 0; desc->pg_lseg = NULL; desc->pg_dreq = NULL; + desc->pg_layout_private = NULL; } EXPORT_SYMBOL_GPL(nfs_pageio_init); diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index 880805774f9f..92ce5783b707 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -69,6 +69,7 @@ struct nfs_pageio_descriptor { const struct nfs_pgio_completion_ops *pg_completion_ops; struct pnfs_layout_segment *pg_lseg; struct nfs_direct_req *pg_dreq; + void *pg_layout_private; }; #define NFS_WBACK_BUSY(req) (test_bit(PG_BUSY,&(req)->wb_flags)) diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 00485e084394..ac7c8ae254f2 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1248,6 +1248,7 @@ struct nfs_pgio_header { void (*release) (struct nfs_pgio_header *hdr); const struct nfs_pgio_completion_ops *completion_ops; struct nfs_direct_req *dreq; + void *layout_private; spinlock_t lock; /* fields protected by lock */ int pnfs_error; -- cgit v1.2.3 From 85b9f66a41eb8ee3f1dfc95707412705463cdd97 Mon Sep 17 00:00:00 2001 From: Asias He Date: Thu, 2 Aug 2012 23:42:04 +0200 Subject: block: Add blk_bio_map_sg() helper Add a helper to map a bio to a scatterlist, modelled after blk_rq_map_sg. This helper is useful for any driver that wants to create a scatterlist from its ->make_request_fn method. Changes in v2: - Use __blk_segment_map_sg to avoid duplicated code - Add cocbook style function comment Cc: Rusty Russell Cc: Christoph Hellwig Cc: Tejun Heo Cc: Shaohua Li Cc: "Michael S. Tsirkin" Cc: kvm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Cc: virtualization@lists.linux-foundation.org Signed-off-by: Christoph Hellwig Signed-off-by: Minchan Kim Signed-off-by: Asias He Signed-off-by: Jens Axboe --- block/blk-merge.c | 37 +++++++++++++++++++++++++++++++++++++ include/linux/blkdev.h | 2 ++ 2 files changed, 39 insertions(+) (limited to 'include') diff --git a/block/blk-merge.c b/block/blk-merge.c index 576b68e79248..e76279e41162 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -209,6 +209,43 @@ int blk_rq_map_sg(struct request_queue *q, struct request *rq, } EXPORT_SYMBOL(blk_rq_map_sg); +/** + * blk_bio_map_sg - map a bio to a scatterlist + * @q: request_queue in question + * @bio: bio being mapped + * @sglist: scatterlist being mapped + * + * Note: + * Caller must make sure sg can hold bio->bi_phys_segments entries + * + * Will return the number of sg entries setup + */ +int blk_bio_map_sg(struct request_queue *q, struct bio *bio, + struct scatterlist *sglist) +{ + struct bio_vec *bvec, *bvprv; + struct scatterlist *sg; + int nsegs, cluster; + unsigned long i; + + nsegs = 0; + cluster = blk_queue_cluster(q); + + bvprv = NULL; + sg = NULL; + bio_for_each_segment(bvec, bio, i) { + __blk_segment_map_sg(q, bvec, sglist, &bvprv, &sg, + &nsegs, &cluster); + } /* segments in bio */ + + if (sg) + sg_mark_end(sg); + + BUG_ON(bio->bi_phys_segments && nsegs > bio->bi_phys_segments); + return nsegs; +} +EXPORT_SYMBOL(blk_bio_map_sg); + static inline int ll_new_hw_segment(struct request_queue *q, struct request *req, struct bio *bio) diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 281516ae8b4e..dc632975d54f 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -894,6 +894,8 @@ extern void blk_queue_flush_queueable(struct request_queue *q, bool queueable); extern struct backing_dev_info *blk_get_backing_dev_info(struct block_device *bdev); extern int blk_rq_map_sg(struct request_queue *, struct request *, struct scatterlist *); +extern int blk_bio_map_sg(struct request_queue *q, struct bio *bio, + struct scatterlist *sglist); extern void blk_dump_rq_flags(struct request *, char *); extern long nr_blockdev_pages(void); -- cgit v1.2.3 From 095adbb6441172985f5ddc3b9e88cb3191bdeac4 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Tue, 31 Jul 2012 17:41:09 +0200 Subject: ACPI: Only count valid srat memory structures Otherwise you could run into: WARN_ON in numa_register_memblks(), because node_possible_map is zero References: https://bugzilla.novell.com/show_bug.cgi?id=757888 On this machine (ProLiant ML570 G3) the SRAT table contains: - No processor affinities - One memory affinity structure (which is set disabled) CC: Per Jessen CC: Andi Kleen Signed-off-by: Thomas Renninger Signed-off-by: Len Brown --- arch/ia64/kernel/acpi.c | 5 +++-- arch/x86/mm/srat.c | 15 ++++++++------- drivers/acpi/numa.c | 8 +++++--- include/linux/acpi.h | 2 +- 4 files changed, 17 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c index 6f38b6120d96..440578850ae5 100644 --- a/arch/ia64/kernel/acpi.c +++ b/arch/ia64/kernel/acpi.c @@ -497,7 +497,7 @@ acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa) srat_num_cpus++; } -void __init +int __init acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) { unsigned long paddr, size; @@ -512,7 +512,7 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) /* Ignore disabled entries */ if (!(ma->flags & ACPI_SRAT_MEM_ENABLED)) - return; + return -1; /* record this node in proximity bitmap */ pxm_bit_set(pxm); @@ -531,6 +531,7 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) p->size = size; p->nid = pxm; num_node_memblks++; + return 0; } void __init acpi_numa_arch_fixup(void) diff --git a/arch/x86/mm/srat.c b/arch/x86/mm/srat.c index 4599c3e8bcb6..4ddf497ca65b 100644 --- a/arch/x86/mm/srat.c +++ b/arch/x86/mm/srat.c @@ -142,23 +142,23 @@ static inline int save_add_info(void) {return 0;} #endif /* Callback for parsing of the Proximity Domain <-> Memory Area mappings */ -void __init +int __init acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) { u64 start, end; int node, pxm; if (srat_disabled()) - return; + return -1; if (ma->header.length != sizeof(struct acpi_srat_mem_affinity)) { bad_srat(); - return; + return -1; } if ((ma->flags & ACPI_SRAT_MEM_ENABLED) == 0) - return; + return -1; if ((ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) && !save_add_info()) - return; + return -1; start = ma->base_address; end = start + ma->length; pxm = ma->proximity_domain; @@ -168,12 +168,12 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) if (node < 0) { printk(KERN_ERR "SRAT: Too many proximity domains.\n"); bad_srat(); - return; + return -1; } if (numa_add_memblk(node, start, end) < 0) { bad_srat(); - return; + return -1; } node_set(node, numa_nodes_parsed); @@ -181,6 +181,7 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) printk(KERN_INFO "SRAT: Node %u PXM %u [mem %#010Lx-%#010Lx]\n", node, pxm, (unsigned long long) start, (unsigned long long) end - 1); + return 0; } void __init acpi_numa_arch_fixup(void) {} diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index 2a6399345c85..cb31298ca684 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -237,6 +237,8 @@ acpi_parse_processor_affinity(struct acpi_subtable_header *header, return 0; } +static int __initdata parsed_numa_memblks; + static int __init acpi_parse_memory_affinity(struct acpi_subtable_header * header, const unsigned long end) @@ -250,8 +252,8 @@ acpi_parse_memory_affinity(struct acpi_subtable_header * header, acpi_table_print_srat_entry(header); /* let architecture-dependent part to do it */ - acpi_numa_memory_affinity_init(memory_affinity); - + if (!acpi_numa_memory_affinity_init(memory_affinity)) + parsed_numa_memblks++; return 0; } @@ -306,7 +308,7 @@ int __init acpi_numa_init(void) if (cnt < 0) return cnt; - else if (cnt == 0) + else if (!parsed_numa_memblks) return -ENOENT; return 0; } diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 3ad510b25283..4f2a76224509 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -96,7 +96,7 @@ void acpi_table_print_madt_entry (struct acpi_subtable_header *madt); void acpi_numa_slit_init (struct acpi_table_slit *slit); void acpi_numa_processor_affinity_init (struct acpi_srat_cpu_affinity *pa); void acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa); -void acpi_numa_memory_affinity_init (struct acpi_srat_mem_affinity *ma); +int acpi_numa_memory_affinity_init (struct acpi_srat_mem_affinity *ma); void acpi_numa_arch_fixup(void); #ifdef CONFIG_ACPI_HOTPLUG_CPU -- cgit v1.2.3 From 78c289f8769aaefcc52d26ca53c8b2ee545fb332 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 19 Jul 2012 13:32:15 +0300 Subject: usb: xceiv: create nop-usb-xceiv.h and avoid pollution on otg.h nop-usb-xceiv was polluting otg.h with its own function prototypes. Move those prototypes to a nop-usb-xceiv.h header. Signed-off-by: Felipe Balbi --- arch/arm/mach-omap2/board-omap3evm.c | 1 + drivers/usb/musb/am35x.c | 1 + drivers/usb/musb/blackfin.c | 1 + drivers/usb/musb/da8xx.c | 1 + drivers/usb/musb/davinci.c | 1 + drivers/usb/musb/musb_dsps.c | 1 + drivers/usb/musb/tusb6010.c | 1 + drivers/usb/otg/nop-usb-xceiv.c | 1 + include/linux/usb/nop-usb-xceiv.h | 18 ++++++++++++++++++ include/linux/usb/otg.h | 14 -------------- 10 files changed, 26 insertions(+), 14 deletions(-) create mode 100644 include/linux/usb/nop-usb-xceiv.h (limited to 'include') diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c index ef230a0eb5eb..9894e3df49c2 100644 --- a/arch/arm/mach-omap2/board-omap3evm.c +++ b/arch/arm/mach-omap2/board-omap3evm.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 7a95ab87ac00..5d64c5b5ef52 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -33,6 +33,7 @@ #include #include #include +#include #include diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 428e6aa3e78a..b562623a8971 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -19,6 +19,7 @@ #include #include #include +#include #include diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 0f9fcec4e1d3..16798c9e6f38 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 472c8b42d38b..863a9b6286c3 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 217808d9fbe1..4e899bb12945 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 1a1bd9cf40c5..00f21dfee5d7 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "musb_core.h" diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index 803f958f4133..3c3ad6bb307c 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -30,6 +30,7 @@ #include #include #include +#include #include struct nop_usb_xceiv { diff --git a/include/linux/usb/nop-usb-xceiv.h b/include/linux/usb/nop-usb-xceiv.h new file mode 100644 index 000000000000..ecc7562a38cc --- /dev/null +++ b/include/linux/usb/nop-usb-xceiv.h @@ -0,0 +1,18 @@ +#ifndef __LINUX_USB_NOP_XCEIV_H +#define __LINUX_USB_NOP_XCEIV_H + +#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/otg.h b/include/linux/usb/otg.h index 45824be0a2f9..16dc520dfbad 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -138,20 +138,6 @@ struct usb_phy { extern int usb_add_phy(struct usb_phy *, enum usb_phy_type type); extern void usb_remove_phy(struct usb_phy *); -#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 - /* helpers for direct access thru low-level io interface */ static inline int usb_phy_io_read(struct usb_phy *x, u32 reg) { -- cgit v1.2.3 From c84d364f8178d2bc12827836bf58eb45598262a4 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 19 Jul 2012 13:38:06 +0300 Subject: usb: xceiv: nop: let it work as USB2 and USB3 phy We add a platform_data to pass the desired mode for the nop-xceiv so it can work on USB2 and USB3 modes. Signed-off-by: Felipe Balbi --- drivers/usb/otg/nop-usb-xceiv.c | 7 ++++++- include/linux/usb/nop-usb-xceiv.h | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index 3c3ad6bb307c..e52e35e7adaf 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -95,7 +95,9 @@ static int nop_set_host(struct usb_otg *otg, struct usb_bus *host) static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev) { + 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; nop = kzalloc(sizeof *nop, GFP_KERNEL); @@ -108,6 +110,9 @@ static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev) return -ENOMEM; } + if (pdata) + type = pdata->type; + nop->dev = &pdev->dev; nop->phy.dev = nop->dev; nop->phy.label = "nop-xceiv"; @@ -118,7 +123,7 @@ static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev) nop->phy.otg->set_host = nop_set_host; nop->phy.otg->set_peripheral = nop_set_peripheral; - err = usb_add_phy(&nop->phy, USB_PHY_TYPE_USB2); + err = usb_add_phy(&nop->phy, type); if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); diff --git a/include/linux/usb/nop-usb-xceiv.h b/include/linux/usb/nop-usb-xceiv.h index ecc7562a38cc..28884c717411 100644 --- a/include/linux/usb/nop-usb-xceiv.h +++ b/include/linux/usb/nop-usb-xceiv.h @@ -1,6 +1,12 @@ #ifndef __LINUX_USB_NOP_XCEIV_H #define __LINUX_USB_NOP_XCEIV_H +#include + +struct nop_usb_xceiv_platform_data { + enum usb_phy_type type; +}; + #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); -- cgit v1.2.3 From 76582d0a68ab6c46987dd678165c26ad81c44cdb Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 25 Jul 2012 16:24:49 +0200 Subject: iommu: Include linux/types.h The linux/iommu.h header uses types defined in linux/types.h but doesn't include it. Signed-off-by: Thierry Reding Signed-off-by: Joerg Roedel --- include/linux/iommu.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 54d6d690073c..0a85199191b9 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -20,6 +20,7 @@ #define __LINUX_IOMMU_H #include +#include #define IOMMU_READ (1) #define IOMMU_WRITE (2) -- cgit v1.2.3 From ba1eabfade3272596000bf3c62b68ca375e48b08 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Fri, 3 Aug 2012 15:55:41 +0200 Subject: iommu: Add missing forward declaration in include file The 'struct notifier_block' is not used in linux/iommu.h but not declared anywhere. Add a forward declaration for it. Reported-by: Thierry Reding Signed-off-by: Joerg Roedel --- include/linux/iommu.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 0a85199191b9..7e83370e6fd2 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -31,6 +31,7 @@ struct iommu_group; struct bus_type; struct device; struct iommu_domain; +struct notifier_block; /* iommu fault flags */ #define IOMMU_FAULT_READ 0x0 -- cgit v1.2.3 From 0a13c00e9d4502b8e3fd9260ce781758ff2c3970 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 3 Aug 2012 10:30:44 -0700 Subject: workqueue: reorder queueing functions so that _on() variants are on top Currently, queue/schedule[_delayed]_work_on() are located below the counterpart without the _on postifx even though the latter is usually implemented using the former. Swap them. This is cleanup and doesn't cause any functional difference. Signed-off-by: Tejun Heo --- include/linux/workqueue.h | 13 ++--- kernel/workqueue.c | 124 +++++++++++++++++++++++----------------------- 2 files changed, 69 insertions(+), 68 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index af155450cabb..597034276611 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -365,23 +365,24 @@ __alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active, extern void destroy_workqueue(struct workqueue_struct *wq); -extern int queue_work(struct workqueue_struct *wq, struct work_struct *work); extern int queue_work_on(int cpu, struct workqueue_struct *wq, struct work_struct *work); -extern int queue_delayed_work(struct workqueue_struct *wq, - struct delayed_work *work, unsigned long delay); +extern int queue_work(struct workqueue_struct *wq, struct work_struct *work); extern int queue_delayed_work_on(int cpu, struct workqueue_struct *wq, struct delayed_work *work, unsigned long delay); +extern int queue_delayed_work(struct workqueue_struct *wq, + struct delayed_work *work, unsigned long delay); extern void flush_workqueue(struct workqueue_struct *wq); extern void drain_workqueue(struct workqueue_struct *wq); extern void flush_scheduled_work(void); -extern int schedule_work(struct work_struct *work); extern int schedule_work_on(int cpu, struct work_struct *work); -extern int schedule_delayed_work(struct delayed_work *work, unsigned long delay); +extern int schedule_work(struct work_struct *work); extern int schedule_delayed_work_on(int cpu, struct delayed_work *work, - unsigned long delay); + unsigned long delay); +extern int schedule_delayed_work(struct delayed_work *work, + unsigned long delay); extern int schedule_on_each_cpu(work_func_t func); extern int keventd_up(void); diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 692d97628a10..07d309e7e359 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -1052,27 +1052,6 @@ static void __queue_work(unsigned int cpu, struct workqueue_struct *wq, spin_unlock_irqrestore(&gcwq->lock, flags); } -/** - * queue_work - queue work on a workqueue - * @wq: workqueue to use - * @work: work to queue - * - * Returns 0 if @work was already on a queue, non-zero otherwise. - * - * We queue the work to the CPU on which it was submitted, but if the CPU dies - * it can be processed by another CPU. - */ -int queue_work(struct workqueue_struct *wq, struct work_struct *work) -{ - int ret; - - ret = queue_work_on(get_cpu(), wq, work); - put_cpu(); - - return ret; -} -EXPORT_SYMBOL_GPL(queue_work); - /** * queue_work_on - queue work on specific cpu * @cpu: CPU number to execute work on @@ -1097,31 +1076,34 @@ queue_work_on(int cpu, struct workqueue_struct *wq, struct work_struct *work) } EXPORT_SYMBOL_GPL(queue_work_on); -static void delayed_work_timer_fn(unsigned long __data) -{ - struct delayed_work *dwork = (struct delayed_work *)__data; - struct cpu_workqueue_struct *cwq = get_work_cwq(&dwork->work); - - __queue_work(smp_processor_id(), cwq->wq, &dwork->work); -} - /** - * queue_delayed_work - queue work on a workqueue after delay + * queue_work - queue work on a workqueue * @wq: workqueue to use - * @dwork: delayable work to queue - * @delay: number of jiffies to wait before queueing + * @work: work to queue * * Returns 0 if @work was already on a queue, non-zero otherwise. + * + * We queue the work to the CPU on which it was submitted, but if the CPU dies + * it can be processed by another CPU. */ -int queue_delayed_work(struct workqueue_struct *wq, - struct delayed_work *dwork, unsigned long delay) +int queue_work(struct workqueue_struct *wq, struct work_struct *work) { - if (delay == 0) - return queue_work(wq, &dwork->work); + int ret; - return queue_delayed_work_on(-1, wq, dwork, delay); + ret = queue_work_on(get_cpu(), wq, work); + put_cpu(); + + return ret; +} +EXPORT_SYMBOL_GPL(queue_work); + +static void delayed_work_timer_fn(unsigned long __data) +{ + struct delayed_work *dwork = (struct delayed_work *)__data; + struct cpu_workqueue_struct *cwq = get_work_cwq(&dwork->work); + + __queue_work(smp_processor_id(), cwq->wq, &dwork->work); } -EXPORT_SYMBOL_GPL(queue_delayed_work); /** * queue_delayed_work_on - queue work on specific CPU after delay @@ -1178,6 +1160,24 @@ int queue_delayed_work_on(int cpu, struct workqueue_struct *wq, } EXPORT_SYMBOL_GPL(queue_delayed_work_on); +/** + * queue_delayed_work - queue work on a workqueue after delay + * @wq: workqueue to use + * @dwork: delayable work to queue + * @delay: number of jiffies to wait before queueing + * + * Returns 0 if @work was already on a queue, non-zero otherwise. + */ +int queue_delayed_work(struct workqueue_struct *wq, + struct delayed_work *dwork, unsigned long delay) +{ + if (delay == 0) + return queue_work(wq, &dwork->work); + + return queue_delayed_work_on(-1, wq, dwork, delay); +} +EXPORT_SYMBOL_GPL(queue_delayed_work); + /** * worker_enter_idle - enter idle state * @worker: worker which is entering idle state @@ -2877,6 +2877,19 @@ bool cancel_delayed_work_sync(struct delayed_work *dwork) } EXPORT_SYMBOL(cancel_delayed_work_sync); +/* + * schedule_work_on - put work task on a specific cpu + * @cpu: cpu to put the work task on + * @work: job to be done + * + * This puts a job on a specific cpu + */ +int schedule_work_on(int cpu, struct work_struct *work) +{ + return queue_work_on(cpu, system_wq, work); +} +EXPORT_SYMBOL(schedule_work_on); + /** * schedule_work - put work task in global workqueue * @work: job to be done @@ -2894,18 +2907,21 @@ int schedule_work(struct work_struct *work) } EXPORT_SYMBOL(schedule_work); -/* - * schedule_work_on - put work task on a specific cpu - * @cpu: cpu to put the work task on - * @work: job to be done +/** + * schedule_delayed_work_on - queue work in global workqueue on CPU after delay + * @cpu: cpu to use + * @dwork: job to be done + * @delay: number of jiffies to wait * - * This puts a job on a specific cpu + * After waiting for a given time this puts a job in the kernel-global + * workqueue on the specified CPU. */ -int schedule_work_on(int cpu, struct work_struct *work) +int schedule_delayed_work_on(int cpu, + struct delayed_work *dwork, unsigned long delay) { - return queue_work_on(cpu, system_wq, work); + return queue_delayed_work_on(cpu, system_wq, dwork, delay); } -EXPORT_SYMBOL(schedule_work_on); +EXPORT_SYMBOL(schedule_delayed_work_on); /** * schedule_delayed_work - put work task in global workqueue after delay @@ -2922,22 +2938,6 @@ int schedule_delayed_work(struct delayed_work *dwork, } EXPORT_SYMBOL(schedule_delayed_work); -/** - * schedule_delayed_work_on - queue work in global workqueue on CPU after delay - * @cpu: cpu to use - * @dwork: job to be done - * @delay: number of jiffies to wait - * - * After waiting for a given time this puts a job in the kernel-global - * workqueue on the specified CPU. - */ -int schedule_delayed_work_on(int cpu, - struct delayed_work *dwork, unsigned long delay) -{ - return queue_delayed_work_on(cpu, system_wq, dwork, delay); -} -EXPORT_SYMBOL(schedule_delayed_work_on); - /** * schedule_on_each_cpu - execute a function synchronously on each online CPU * @func: the function to call -- cgit v1.2.3 From d4283e9378619c14dc3826a6b0527eb5d967ffde Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 3 Aug 2012 10:30:44 -0700 Subject: workqueue: make queueing functions return bool All queueing functions return 1 on success, 0 if the work item was already pending. Update them to return bool instead. This signifies better that they don't return 0 / -errno. This is cleanup and doesn't cause any functional difference. While at it, fix comment opening for schedule_work_on(). Signed-off-by: Tejun Heo --- include/linux/workqueue.h | 20 ++++++++++---------- kernel/workqueue.c | 47 +++++++++++++++++++++++------------------------ 2 files changed, 33 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 597034276611..278dc5ddb73f 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -365,24 +365,24 @@ __alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active, extern void destroy_workqueue(struct workqueue_struct *wq); -extern int queue_work_on(int cpu, struct workqueue_struct *wq, +extern bool queue_work_on(int cpu, struct workqueue_struct *wq, struct work_struct *work); -extern int queue_work(struct workqueue_struct *wq, struct work_struct *work); -extern int queue_delayed_work_on(int cpu, struct workqueue_struct *wq, +extern bool queue_work(struct workqueue_struct *wq, struct work_struct *work); +extern bool queue_delayed_work_on(int cpu, struct workqueue_struct *wq, struct delayed_work *work, unsigned long delay); -extern int queue_delayed_work(struct workqueue_struct *wq, +extern bool queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *work, unsigned long delay); extern void flush_workqueue(struct workqueue_struct *wq); extern void drain_workqueue(struct workqueue_struct *wq); extern void flush_scheduled_work(void); -extern int schedule_work_on(int cpu, struct work_struct *work); -extern int schedule_work(struct work_struct *work); -extern int schedule_delayed_work_on(int cpu, struct delayed_work *work, - unsigned long delay); -extern int schedule_delayed_work(struct delayed_work *work, - unsigned long delay); +extern bool schedule_work_on(int cpu, struct work_struct *work); +extern bool schedule_work(struct work_struct *work); +extern bool schedule_delayed_work_on(int cpu, struct delayed_work *work, + unsigned long delay); +extern bool schedule_delayed_work(struct delayed_work *work, + unsigned long delay); extern int schedule_on_each_cpu(work_func_t func); extern int keventd_up(void); diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 07d309e7e359..70f95ab28f3d 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -1058,19 +1058,19 @@ static void __queue_work(unsigned int cpu, struct workqueue_struct *wq, * @wq: workqueue to use * @work: work to queue * - * Returns 0 if @work was already on a queue, non-zero otherwise. + * Returns %false if @work was already on a queue, %true otherwise. * * We queue the work to a specific CPU, the caller must ensure it * can't go away. */ -int -queue_work_on(int cpu, struct workqueue_struct *wq, struct work_struct *work) +bool queue_work_on(int cpu, struct workqueue_struct *wq, + struct work_struct *work) { - int ret = 0; + bool ret = false; if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) { __queue_work(cpu, wq, work); - ret = 1; + ret = true; } return ret; } @@ -1081,14 +1081,14 @@ EXPORT_SYMBOL_GPL(queue_work_on); * @wq: workqueue to use * @work: work to queue * - * Returns 0 if @work was already on a queue, non-zero otherwise. + * Returns %false if @work was already on a queue, %true otherwise. * * We queue the work to the CPU on which it was submitted, but if the CPU dies * it can be processed by another CPU. */ -int queue_work(struct workqueue_struct *wq, struct work_struct *work) +bool queue_work(struct workqueue_struct *wq, struct work_struct *work) { - int ret; + bool ret; ret = queue_work_on(get_cpu(), wq, work); put_cpu(); @@ -1112,14 +1112,14 @@ static void delayed_work_timer_fn(unsigned long __data) * @dwork: work to queue * @delay: number of jiffies to wait before queueing * - * Returns 0 if @work was already on a queue, non-zero otherwise. + * Returns %false if @work was already on a queue, %true otherwise. */ -int queue_delayed_work_on(int cpu, struct workqueue_struct *wq, - struct delayed_work *dwork, unsigned long delay) +bool queue_delayed_work_on(int cpu, struct workqueue_struct *wq, + struct delayed_work *dwork, unsigned long delay) { - int ret = 0; struct timer_list *timer = &dwork->timer; struct work_struct *work = &dwork->work; + bool ret = false; if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) { unsigned int lcpu; @@ -1154,7 +1154,7 @@ int queue_delayed_work_on(int cpu, struct workqueue_struct *wq, add_timer_on(timer, cpu); else add_timer(timer); - ret = 1; + ret = true; } return ret; } @@ -1166,9 +1166,9 @@ EXPORT_SYMBOL_GPL(queue_delayed_work_on); * @dwork: delayable work to queue * @delay: number of jiffies to wait before queueing * - * Returns 0 if @work was already on a queue, non-zero otherwise. + * Returns %false if @work was already on a queue, %true otherwise. */ -int queue_delayed_work(struct workqueue_struct *wq, +bool queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay) { if (delay == 0) @@ -2877,14 +2877,14 @@ bool cancel_delayed_work_sync(struct delayed_work *dwork) } EXPORT_SYMBOL(cancel_delayed_work_sync); -/* +/** * schedule_work_on - put work task on a specific cpu * @cpu: cpu to put the work task on * @work: job to be done * * This puts a job on a specific cpu */ -int schedule_work_on(int cpu, struct work_struct *work) +bool schedule_work_on(int cpu, struct work_struct *work) { return queue_work_on(cpu, system_wq, work); } @@ -2894,14 +2894,14 @@ EXPORT_SYMBOL(schedule_work_on); * schedule_work - put work task in global workqueue * @work: job to be done * - * Returns zero if @work was already on the kernel-global workqueue and - * non-zero otherwise. + * Returns %false if @work was already on the kernel-global workqueue and + * %true otherwise. * * This puts a job in the kernel-global workqueue if it was not already * queued and leaves it in the same position on the kernel-global * workqueue otherwise. */ -int schedule_work(struct work_struct *work) +bool schedule_work(struct work_struct *work) { return queue_work(system_wq, work); } @@ -2916,8 +2916,8 @@ EXPORT_SYMBOL(schedule_work); * After waiting for a given time this puts a job in the kernel-global * workqueue on the specified CPU. */ -int schedule_delayed_work_on(int cpu, - struct delayed_work *dwork, unsigned long delay) +bool schedule_delayed_work_on(int cpu, struct delayed_work *dwork, + unsigned long delay) { return queue_delayed_work_on(cpu, system_wq, dwork, delay); } @@ -2931,8 +2931,7 @@ EXPORT_SYMBOL(schedule_delayed_work_on); * After waiting for a given time this puts a job in the kernel-global * workqueue. */ -int schedule_delayed_work(struct delayed_work *dwork, - unsigned long delay) +bool schedule_delayed_work(struct delayed_work *dwork, unsigned long delay) { return queue_delayed_work(system_wq, dwork, delay); } -- cgit v1.2.3 From d8e794dfd51c368ed3f686b7f4172830b60ae47b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 3 Aug 2012 10:30:45 -0700 Subject: workqueue: set delayed_work->timer function on initialization delayed_work->timer.function is currently initialized during queue_delayed_work_on(). Export delayed_work_timer_fn() and set delayed_work timer function during delayed_work initialization together with other fields. This ensures the timer function is always valid on an initialized delayed_work. This is to help mod_delayed_work() implementation. To detect delayed_work users which diddle with the internal timer, trigger WARN if timer function doesn't match on queue. Signed-off-by: Tejun Heo --- include/linux/workqueue.h | 13 +++++++++++-- kernel/workqueue.c | 7 ++++--- 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 278dc5ddb73f..ab95fef38d56 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -16,6 +16,7 @@ struct workqueue_struct; struct work_struct; typedef void (*work_func_t)(struct work_struct *work); +void delayed_work_timer_fn(unsigned long __data); /* * The first word is the work queue pointer and the flags rolled into @@ -124,12 +125,14 @@ struct execute_work { #define __DELAYED_WORK_INITIALIZER(n, f) { \ .work = __WORK_INITIALIZER((n).work, (f)), \ - .timer = TIMER_INITIALIZER(NULL, 0, 0), \ + .timer = TIMER_INITIALIZER(delayed_work_timer_fn, \ + 0, (unsigned long)&(n)), \ } #define __DEFERRED_WORK_INITIALIZER(n, f) { \ .work = __WORK_INITIALIZER((n).work, (f)), \ - .timer = TIMER_DEFERRED_INITIALIZER(NULL, 0, 0), \ + .timer = TIMER_DEFERRED_INITIALIZER(delayed_work_timer_fn, \ + 0, (unsigned long)&(n)), \ } #define DECLARE_WORK(n, f) \ @@ -207,18 +210,24 @@ static inline unsigned int work_static(struct work_struct *work) { return 0; } do { \ INIT_WORK(&(_work)->work, (_func)); \ init_timer(&(_work)->timer); \ + (_work)->timer.function = delayed_work_timer_fn;\ + (_work)->timer.data = (unsigned long)(_work); \ } while (0) #define INIT_DELAYED_WORK_ONSTACK(_work, _func) \ do { \ INIT_WORK_ONSTACK(&(_work)->work, (_func)); \ init_timer_on_stack(&(_work)->timer); \ + (_work)->timer.function = delayed_work_timer_fn;\ + (_work)->timer.data = (unsigned long)(_work); \ } while (0) #define INIT_DELAYED_WORK_DEFERRABLE(_work, _func) \ do { \ INIT_WORK(&(_work)->work, (_func)); \ init_timer_deferrable(&(_work)->timer); \ + (_work)->timer.function = delayed_work_timer_fn;\ + (_work)->timer.data = (unsigned long)(_work); \ } while (0) /** diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 30474c4e107c..55392385fe30 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -1112,7 +1112,7 @@ bool queue_work(struct workqueue_struct *wq, struct work_struct *work) } EXPORT_SYMBOL_GPL(queue_work); -static void delayed_work_timer_fn(unsigned long __data) +void delayed_work_timer_fn(unsigned long __data) { struct delayed_work *dwork = (struct delayed_work *)__data; struct cpu_workqueue_struct *cwq = get_work_cwq(&dwork->work); @@ -1121,6 +1121,7 @@ static void delayed_work_timer_fn(unsigned long __data) __queue_work(smp_processor_id(), cwq->wq, &dwork->work); local_irq_enable(); } +EXPORT_SYMBOL_GPL(delayed_work_timer_fn); /** * queue_delayed_work_on - queue work on specific CPU after delay @@ -1145,6 +1146,8 @@ bool queue_delayed_work_on(int cpu, struct workqueue_struct *wq, if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) { unsigned int lcpu; + WARN_ON_ONCE(timer->function != delayed_work_timer_fn || + timer->data != (unsigned long)dwork); BUG_ON(timer_pending(timer)); BUG_ON(!list_empty(&work->entry)); @@ -1168,8 +1171,6 @@ bool queue_delayed_work_on(int cpu, struct workqueue_struct *wq, set_work_cwq(work, get_cwq(lcpu, wq), 0); timer->expires = jiffies + delay; - timer->data = (unsigned long)dwork; - timer->function = delayed_work_timer_fn; if (unlikely(cpu >= 0)) add_timer_on(timer, cpu); -- cgit v1.2.3 From b5490077274482efde57a50b060b99bc839acd45 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 3 Aug 2012 10:30:46 -0700 Subject: workqueue: introduce WORK_OFFQ_FLAG_* Low WORK_STRUCT_FLAG_BITS bits of work_struct->data contain WORK_STRUCT_FLAG_* and flush color. If the work item is queued, the rest point to the cpu_workqueue with WORK_STRUCT_CWQ set; otherwise, WORK_STRUCT_CWQ is clear and the bits contain the last CPU number - either a real CPU number or one of WORK_CPU_*. Scheduled addition of mod_delayed_work[_on]() requires an additional flag, which is used only while a work item is off queue. There are more than enough bits to represent off-queue CPU number on both 32 and 64bits. This patch introduces WORK_OFFQ_FLAG_* which occupy the lower part of the @work->data high bits while off queue. This patch doesn't define any actual OFFQ flag yet. Off-queue CPU number is now shifted by WORK_OFFQ_CPU_SHIFT, which adds the number of bits used by OFFQ flags to WORK_STRUCT_FLAG_SHIFT, to make room for OFFQ flags. To avoid shift width warning with large WORK_OFFQ_FLAG_BITS, ulong cast is added to WORK_STRUCT_NO_CPU and, just in case, BUILD_BUG_ON() to check that there are enough bits to accomodate off-queue CPU number is added. This patch doesn't make any functional difference. Signed-off-by: Tejun Heo --- include/linux/workqueue.h | 8 +++++++- kernel/workqueue.c | 14 +++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index ab95fef38d56..f562674db404 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -68,9 +68,15 @@ enum { WORK_STRUCT_FLAG_BITS = WORK_STRUCT_COLOR_SHIFT + WORK_STRUCT_COLOR_BITS, + /* data contains off-queue information when !WORK_STRUCT_CWQ */ + WORK_OFFQ_FLAG_BASE = WORK_STRUCT_FLAG_BITS, + WORK_OFFQ_FLAG_BITS = 0, + WORK_OFFQ_CPU_SHIFT = WORK_OFFQ_FLAG_BASE + WORK_OFFQ_FLAG_BITS, + + /* convenience constants */ WORK_STRUCT_FLAG_MASK = (1UL << WORK_STRUCT_FLAG_BITS) - 1, WORK_STRUCT_WQ_DATA_MASK = ~WORK_STRUCT_FLAG_MASK, - WORK_STRUCT_NO_CPU = WORK_CPU_NONE << WORK_STRUCT_FLAG_BITS, + WORK_STRUCT_NO_CPU = (unsigned long)WORK_CPU_NONE << WORK_OFFQ_CPU_SHIFT, /* bit mask for work_busy() return values */ WORK_BUSY_PENDING = 1 << 0, diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 0f50f4078e36..eeae77079483 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -533,9 +533,9 @@ static int work_next_color(int color) } /* - * A work's data points to the cwq with WORK_STRUCT_CWQ set while the - * work is on queue. Once execution starts, WORK_STRUCT_CWQ is - * cleared and the work data contains the cpu number it was last on. + * While queued, %WORK_STRUCT_CWQ is set and non flag bits of a work's data + * contain the pointer to the queued cwq. Once execution starts, the flag + * is cleared and the high bits contain OFFQ flags and CPU number. * * set_work_cwq(), set_work_cpu_and_clear_pending() and clear_work_data() * can be used to set the cwq, cpu or clear work->data. These functions @@ -565,7 +565,7 @@ static void set_work_cwq(struct work_struct *work, static void set_work_cpu_and_clear_pending(struct work_struct *work, unsigned int cpu) { - set_work_data(work, cpu << WORK_STRUCT_FLAG_BITS, 0); + set_work_data(work, (unsigned long)cpu << WORK_OFFQ_CPU_SHIFT, 0); } static void clear_work_data(struct work_struct *work) @@ -592,7 +592,7 @@ static struct global_cwq *get_work_gcwq(struct work_struct *work) return ((struct cpu_workqueue_struct *) (data & WORK_STRUCT_WQ_DATA_MASK))->pool->gcwq; - cpu = data >> WORK_STRUCT_FLAG_BITS; + cpu = data >> WORK_OFFQ_CPU_SHIFT; if (cpu == WORK_CPU_NONE) return NULL; @@ -3724,6 +3724,10 @@ static int __init init_workqueues(void) unsigned int cpu; int i; + /* make sure we have enough bits for OFFQ CPU number */ + BUILD_BUG_ON((1LU << (BITS_PER_LONG - WORK_OFFQ_CPU_SHIFT)) < + WORK_CPU_LAST); + cpu_notifier(workqueue_cpu_up_callback, CPU_PRI_WORKQUEUE_UP); cpu_notifier(workqueue_cpu_down_callback, CPU_PRI_WORKQUEUE_DOWN); -- cgit v1.2.3 From bbb68dfaba73e8338fe0f1dc711cc1d261daec87 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 3 Aug 2012 10:30:46 -0700 Subject: workqueue: mark a work item being canceled as such There can be two reasons try_to_grab_pending() can fail with -EAGAIN. One is when someone else is queueing or deqeueing the work item. With the previous patches, it is guaranteed that PENDING and queued state will soon agree making it safe to busy-retry in this case. The other is if multiple __cancel_work_timer() invocations are racing one another. __cancel_work_timer() grabs PENDING and then waits for running instances of the target work item on all CPUs while holding PENDING and !queued. try_to_grab_pending() invoked from another task will keep returning -EAGAIN while the current owner is waiting. Not distinguishing the two cases is okay because __cancel_work_timer() is the only user of try_to_grab_pending() and it invokes wait_on_work() whenever grabbing fails. For the first case, busy looping should be fine but wait_on_work() doesn't cause any critical problem. For the latter case, the new contender usually waits for the same condition as the current owner, so no unnecessarily extended busy-looping happens. Combined, these make __cancel_work_timer() technically correct even without irq protection while grabbing PENDING or distinguishing the two different cases. While the current code is technically correct, not distinguishing the two cases makes it difficult to use try_to_grab_pending() for other purposes than canceling because it's impossible to tell whether it's safe to busy-retry grabbing. This patch adds a mechanism to mark a work item being canceled. try_to_grab_pending() now disables irq on success and returns -EAGAIN to indicate that grabbing failed but PENDING and queued states are gonna agree soon and it's safe to busy-loop. It returns -ENOENT if the work item is being canceled and it may stay PENDING && !queued for arbitrary amount of time. __cancel_work_timer() is modified to mark the work canceling with WORK_OFFQ_CANCELING after grabbing PENDING, thus making try_to_grab_pending() fail with -ENOENT instead of -EAGAIN. Also, it invokes wait_on_work() iff grabbing failed with -ENOENT. This isn't necessary for correctness but makes it consistent with other future users of try_to_grab_pending(). v2: try_to_grab_pending() was testing preempt_count() to ensure that the caller has disabled preemption. This triggers spuriously if !CONFIG_PREEMPT_COUNT. Use preemptible() instead. Reported by Fengguang Wu. v3: Updated so that try_to_grab_pending() disables irq on success rather than requiring preemption disabled by the caller. This makes busy-looping easier and will allow try_to_grap_pending() to be used from bh/irq contexts. Signed-off-by: Tejun Heo Cc: Fengguang Wu --- include/linux/workqueue.h | 5 ++- kernel/workqueue.c | 90 +++++++++++++++++++++++++++++++++++++---------- 2 files changed, 76 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index f562674db404..5f4aeaa9f3e6 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -70,7 +70,10 @@ enum { /* data contains off-queue information when !WORK_STRUCT_CWQ */ WORK_OFFQ_FLAG_BASE = WORK_STRUCT_FLAG_BITS, - WORK_OFFQ_FLAG_BITS = 0, + + WORK_OFFQ_CANCELING = (1 << WORK_OFFQ_FLAG_BASE), + + WORK_OFFQ_FLAG_BITS = 1, WORK_OFFQ_CPU_SHIFT = WORK_OFFQ_FLAG_BASE + WORK_OFFQ_FLAG_BITS, /* convenience constants */ diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 4b3663b1c677..b4a4e05c89e1 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -537,15 +537,20 @@ static int work_next_color(int color) * contain the pointer to the queued cwq. Once execution starts, the flag * is cleared and the high bits contain OFFQ flags and CPU number. * - * set_work_cwq(), set_work_cpu_and_clear_pending() and clear_work_data() - * can be used to set the cwq, cpu or clear work->data. These functions - * should only be called while the work is owned - ie. while the PENDING - * bit is set. + * set_work_cwq(), set_work_cpu_and_clear_pending(), mark_work_canceling() + * and clear_work_data() can be used to set the cwq, cpu or clear + * work->data. These functions should only be called while the work is + * owned - ie. while the PENDING bit is set. * - * get_work_[g]cwq() can be used to obtain the gcwq or cwq - * corresponding to a work. gcwq is available once the work has been - * queued anywhere after initialization. cwq is available only from - * queueing until execution starts. + * get_work_[g]cwq() can be used to obtain the gcwq or cwq corresponding to + * a work. gcwq is available once the work has been queued anywhere after + * initialization until it is sync canceled. cwq is available only while + * the work item is queued. + * + * %WORK_OFFQ_CANCELING is used to mark a work item which is being + * canceled. While being canceled, a work item may have its PENDING set + * but stay off timer and worklist for arbitrarily long and nobody should + * try to steal the PENDING bit. */ static inline void set_work_data(struct work_struct *work, unsigned long data, unsigned long flags) @@ -600,6 +605,22 @@ static struct global_cwq *get_work_gcwq(struct work_struct *work) return get_gcwq(cpu); } +static void mark_work_canceling(struct work_struct *work) +{ + struct global_cwq *gcwq = get_work_gcwq(work); + unsigned long cpu = gcwq ? gcwq->cpu : WORK_CPU_NONE; + + set_work_data(work, (cpu << WORK_OFFQ_CPU_SHIFT) | WORK_OFFQ_CANCELING, + WORK_STRUCT_PENDING); +} + +static bool work_is_canceling(struct work_struct *work) +{ + unsigned long data = atomic_long_read(&work->data); + + return !(data & WORK_STRUCT_CWQ) && (data & WORK_OFFQ_CANCELING); +} + /* * Policy functions. These define the policies on how the global worker * pools are managed. Unless noted otherwise, these functions assume that @@ -1005,9 +1026,10 @@ static void cwq_dec_nr_in_flight(struct cpu_workqueue_struct *cwq, int color, } /** - * try_to_grab_pending - steal work item from worklist + * try_to_grab_pending - steal work item from worklist and disable irq * @work: work item to steal * @is_dwork: @work is a delayed_work + * @flags: place to store irq state * * Try to grab PENDING bit of @work. This function can handle @work in any * stable state - idle, on timer or on worklist. Return values are @@ -1015,13 +1037,30 @@ static void cwq_dec_nr_in_flight(struct cpu_workqueue_struct *cwq, int color, * 1 if @work was pending and we successfully stole PENDING * 0 if @work was idle and we claimed PENDING * -EAGAIN if PENDING couldn't be grabbed at the moment, safe to busy-retry + * -ENOENT if someone else is canceling @work, this state may persist + * for arbitrarily long * - * On >= 0 return, the caller owns @work's PENDING bit. + * On >= 0 return, the caller owns @work's PENDING bit. To avoid getting + * preempted while holding PENDING and @work off queue, preemption must be + * disabled on entry. This ensures that we don't return -EAGAIN while + * another task is preempted in this function. + * + * On successful return, >= 0, irq is disabled and the caller is + * responsible for releasing it using local_irq_restore(*@flags). + * + * This function is safe to call from any context other than IRQ handler. + * An IRQ handler may run on top of delayed_work_timer_fn() which can make + * this function return -EAGAIN perpetually. */ -static int try_to_grab_pending(struct work_struct *work, bool is_dwork) +static int try_to_grab_pending(struct work_struct *work, bool is_dwork, + unsigned long *flags) { struct global_cwq *gcwq; + WARN_ON_ONCE(in_irq()); + + local_irq_save(*flags); + /* try to steal the timer if it exists */ if (is_dwork) { struct delayed_work *dwork = to_delayed_work(work); @@ -1040,9 +1079,9 @@ static int try_to_grab_pending(struct work_struct *work, bool is_dwork) */ gcwq = get_work_gcwq(work); if (!gcwq) - return -EAGAIN; + goto fail; - spin_lock_irq(&gcwq->lock); + spin_lock(&gcwq->lock); if (!list_empty(&work->entry)) { /* * This work is queued, but perhaps we locked the wrong gcwq. @@ -1057,12 +1096,16 @@ static int try_to_grab_pending(struct work_struct *work, bool is_dwork) get_work_color(work), *work_data_bits(work) & WORK_STRUCT_DELAYED); - spin_unlock_irq(&gcwq->lock); + spin_unlock(&gcwq->lock); return 1; } } - spin_unlock_irq(&gcwq->lock); - + spin_unlock(&gcwq->lock); +fail: + local_irq_restore(*flags); + if (work_is_canceling(work)) + return -ENOENT; + cpu_relax(); return -EAGAIN; } @@ -2839,13 +2882,24 @@ EXPORT_SYMBOL_GPL(flush_work_sync); static bool __cancel_work_timer(struct work_struct *work, bool is_dwork) { + unsigned long flags; int ret; do { - ret = try_to_grab_pending(work, is_dwork); - wait_on_work(work); + ret = try_to_grab_pending(work, is_dwork, &flags); + /* + * If someone else is canceling, wait for the same event it + * would be waiting for before retrying. + */ + if (unlikely(ret == -ENOENT)) + wait_on_work(work); } while (unlikely(ret < 0)); + /* tell other tasks trying to grab @work to back off */ + mark_work_canceling(work); + local_irq_restore(flags); + + wait_on_work(work); clear_work_data(work); return ret; } -- cgit v1.2.3 From 8376fe22c7e79c7e90857d39f82aeae6cad6c4b8 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 3 Aug 2012 10:30:47 -0700 Subject: workqueue: implement mod_delayed_work[_on]() Workqueue was lacking a mechanism to modify the timeout of an already pending delayed_work. delayed_work users have been working around this using several methods - using an explicit timer + work item, messing directly with delayed_work->timer, and canceling before re-queueing, all of which are error-prone and/or ugly. This patch implements mod_delayed_work[_on]() which behaves similarly to mod_timer() - if the delayed_work is idle, it's queued with the given delay; otherwise, its timeout is modified to the new value. Zero @delay guarantees immediate execution. v2: Updated to reflect try_to_grab_pending() changes. Now safe to be called from bh context. Signed-off-by: Tejun Heo Cc: Linus Torvalds Cc: Andrew Morton Cc: Ingo Molnar --- include/linux/workqueue.h | 4 ++++ kernel/workqueue.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 5f4aeaa9f3e6..20000305a8a6 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -390,6 +390,10 @@ extern bool queue_delayed_work_on(int cpu, struct workqueue_struct *wq, struct delayed_work *work, unsigned long delay); extern bool queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *work, unsigned long delay); +extern bool mod_delayed_work_on(int cpu, struct workqueue_struct *wq, + struct delayed_work *dwork, unsigned long delay); +extern bool mod_delayed_work(struct workqueue_struct *wq, + struct delayed_work *dwork, unsigned long delay); extern void flush_workqueue(struct workqueue_struct *wq); extern void drain_workqueue(struct workqueue_struct *wq); diff --git a/kernel/workqueue.c b/kernel/workqueue.c index b4a4e05c89e1..41ae2c0979fe 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -1413,6 +1413,59 @@ bool queue_delayed_work(struct workqueue_struct *wq, } EXPORT_SYMBOL_GPL(queue_delayed_work); +/** + * mod_delayed_work_on - modify delay of or queue a delayed work on specific CPU + * @cpu: CPU number to execute work on + * @wq: workqueue to use + * @dwork: work to queue + * @delay: number of jiffies to wait before queueing + * + * If @dwork is idle, equivalent to queue_delayed_work_on(); otherwise, + * modify @dwork's timer so that it expires after @delay. If @delay is + * zero, @work is guaranteed to be scheduled immediately regardless of its + * current state. + * + * Returns %false if @dwork was idle and queued, %true if @dwork was + * pending and its timer was modified. + * + * This function is safe to call from any context other than IRQ handler. + * See try_to_grab_pending() for details. + */ +bool mod_delayed_work_on(int cpu, struct workqueue_struct *wq, + struct delayed_work *dwork, unsigned long delay) +{ + unsigned long flags; + int ret; + + do { + ret = try_to_grab_pending(&dwork->work, true, &flags); + } while (unlikely(ret == -EAGAIN)); + + if (likely(ret >= 0)) { + __queue_delayed_work(cpu, wq, dwork, delay); + local_irq_restore(flags); + } + + /* -ENOENT from try_to_grab_pending() becomes %true */ + return ret; +} +EXPORT_SYMBOL_GPL(mod_delayed_work_on); + +/** + * mod_delayed_work - modify delay of or queue a delayed work + * @wq: workqueue to use + * @dwork: work to queue + * @delay: number of jiffies to wait before queueing + * + * mod_delayed_work_on() on local CPU. + */ +bool mod_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork, + unsigned long delay) +{ + return mod_delayed_work_on(WORK_CPU_UNBOUND, wq, dwork, delay); +} +EXPORT_SYMBOL_GPL(mod_delayed_work); + /** * worker_enter_idle - enter idle state * @worker: worker which is entering idle state -- cgit v1.2.3 From f0cd2dbb6cf387c11f87265462e370bb5469299e Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 25 Jul 2012 18:11:59 +0300 Subject: vfs: kill write_super and sync_supers Finally we can kill the 'sync_supers' kernel thread along with the '->write_super()' superblock operation because all the users are gone. Now every file-system is supposed to self-manage own superblock and its dirty state. The nice thing about killing this thread is that it improves power management. Indeed, 'sync_supers' is a source of monotonic system wake-ups - it woke up every 5 seconds no matter what - even if there were no dirty superblocks and even if there were no file-systems using this service (e.g., btrfs and journalled ext4 do not need it). So it was wasting power most of the time. And because the thread was in the core of the kernel, all systems had to have it. So I am quite happy to make it go away. Interestingly, this thread is a left-over from the pdflush kernel thread which was a self-forking kernel thread responsible for all the write-back in old Linux kernels. It was turned into per-block device BDI threads, and 'sync_supers' was a left-over. Thus, R.I.P, pdflush as well. Signed-off-by: Artem Bityutskiy Signed-off-by: Al Viro --- fs/super.c | 40 ---------------------------------- include/linux/backing-dev.h | 1 - include/linux/fs.h | 3 --- mm/backing-dev.c | 52 --------------------------------------------- mm/page-writeback.c | 1 - 5 files changed, 97 deletions(-) (limited to 'include') diff --git a/fs/super.c b/fs/super.c index b05cf47463d0..0902cfa6a12e 100644 --- a/fs/super.c +++ b/fs/super.c @@ -536,46 +536,6 @@ void drop_super(struct super_block *sb) EXPORT_SYMBOL(drop_super); -/** - * sync_supers - helper for periodic superblock writeback - * - * Call the write_super method if present on all dirty superblocks in - * the system. This is for the periodic writeback used by most older - * filesystems. For data integrity superblock writeback use - * sync_filesystems() instead. - * - * Note: check the dirty flag before waiting, so we don't - * hold up the sync while mounting a device. (The newly - * mounted device won't need syncing.) - */ -void sync_supers(void) -{ - struct super_block *sb, *p = NULL; - - spin_lock(&sb_lock); - list_for_each_entry(sb, &super_blocks, s_list) { - if (hlist_unhashed(&sb->s_instances)) - continue; - if (sb->s_op->write_super && sb->s_dirt) { - sb->s_count++; - spin_unlock(&sb_lock); - - down_read(&sb->s_umount); - if (sb->s_root && sb->s_dirt && (sb->s_flags & MS_BORN)) - sb->s_op->write_super(sb); - up_read(&sb->s_umount); - - spin_lock(&sb_lock); - if (p) - __put_super(p); - p = sb; - } - } - if (p) - __put_super(p); - spin_unlock(&sb_lock); -} - /** * iterate_supers - call function for all active superblocks * @f: function to call diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index c97c6b9cd38e..2a9a9abc9126 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -124,7 +124,6 @@ void bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages, void bdi_start_background_writeback(struct backing_dev_info *bdi); int bdi_writeback_thread(void *data); int bdi_has_dirty_io(struct backing_dev_info *bdi); -void bdi_arm_supers_timer(void); void bdi_wakeup_thread_delayed(struct backing_dev_info *bdi); void bdi_lock_two(struct bdi_writeback *wb1, struct bdi_writeback *wb2); diff --git a/include/linux/fs.h b/include/linux/fs.h index 38dba16c4176..aa110476a95b 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1491,7 +1491,6 @@ struct sb_writers { struct super_block { struct list_head s_list; /* Keep this first */ dev_t s_dev; /* search index; _not_ kdev_t */ - unsigned char s_dirt; unsigned char s_blocksize_bits; unsigned long s_blocksize; loff_t s_maxbytes; /* Max file size */ @@ -1861,7 +1860,6 @@ struct super_operations { int (*drop_inode) (struct inode *); void (*evict_inode) (struct inode *); void (*put_super) (struct super_block *); - void (*write_super) (struct super_block *); int (*sync_fs)(struct super_block *sb, int wait); int (*freeze_fs) (struct super_block *); int (*unfreeze_fs) (struct super_block *); @@ -2397,7 +2395,6 @@ extern int vfs_fsync_range(struct file *file, loff_t start, loff_t end, int datasync); extern int vfs_fsync(struct file *file, int datasync); extern int generic_write_sync(struct file *file, loff_t pos, loff_t count); -extern void sync_supers(void); extern void emergency_sync(void); extern void emergency_remount(void); #ifdef CONFIG_BLOCK diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 6b4718e2ee34..b41823cc05e6 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -39,12 +39,6 @@ DEFINE_SPINLOCK(bdi_lock); LIST_HEAD(bdi_list); LIST_HEAD(bdi_pending_list); -static struct task_struct *sync_supers_tsk; -static struct timer_list sync_supers_timer; - -static int bdi_sync_supers(void *); -static void sync_supers_timer_fn(unsigned long); - void bdi_lock_two(struct bdi_writeback *wb1, struct bdi_writeback *wb2) { if (wb1 < wb2) { @@ -250,12 +244,6 @@ static int __init default_bdi_init(void) { int err; - sync_supers_tsk = kthread_run(bdi_sync_supers, NULL, "sync_supers"); - BUG_ON(IS_ERR(sync_supers_tsk)); - - setup_timer(&sync_supers_timer, sync_supers_timer_fn, 0); - bdi_arm_supers_timer(); - err = bdi_init(&default_backing_dev_info); if (!err) bdi_register(&default_backing_dev_info, NULL, "default"); @@ -270,46 +258,6 @@ int bdi_has_dirty_io(struct backing_dev_info *bdi) return wb_has_dirty_io(&bdi->wb); } -/* - * kupdated() used to do this. We cannot do it from the bdi_forker_thread() - * or we risk deadlocking on ->s_umount. The longer term solution would be - * to implement sync_supers_bdi() or similar and simply do it from the - * bdi writeback thread individually. - */ -static int bdi_sync_supers(void *unused) -{ - set_user_nice(current, 0); - - while (!kthread_should_stop()) { - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - - /* - * Do this periodically, like kupdated() did before. - */ - sync_supers(); - } - - return 0; -} - -void bdi_arm_supers_timer(void) -{ - unsigned long next; - - if (!dirty_writeback_interval) - return; - - next = msecs_to_jiffies(dirty_writeback_interval * 10) + jiffies; - mod_timer(&sync_supers_timer, round_jiffies_up(next)); -} - -static void sync_supers_timer_fn(unsigned long unused) -{ - wake_up_process(sync_supers_tsk); - bdi_arm_supers_timer(); -} - static void wakeup_timer_fn(unsigned long data) { struct backing_dev_info *bdi = (struct backing_dev_info *)data; diff --git a/mm/page-writeback.c b/mm/page-writeback.c index e5363f34e025..5ad5ce23c1e0 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -1532,7 +1532,6 @@ int dirty_writeback_centisecs_handler(ctl_table *table, int write, void __user *buffer, size_t *length, loff_t *ppos) { proc_dointvec(table, write, buffer, length, ppos); - bdi_arm_supers_timer(); return 0; } -- cgit v1.2.3 From 4778e0be16c291ba6d9d55eeff3a6764fc84a071 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 27 Jul 2012 06:28:52 +0000 Subject: netlink: add signed types Signed types might be needed in NL communication from time to time (I need s32 in team driver), so add them. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/netlink.h | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) (limited to 'include') diff --git a/include/net/netlink.h b/include/net/netlink.h index 785f37a3b44e..09175d5d1fbf 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -98,6 +98,10 @@ * nla_put_u16(skb, type, value) add u16 attribute to skb * nla_put_u32(skb, type, value) add u32 attribute to skb * nla_put_u64(skb, type, value) add u64 attribute to skb + * nla_put_s8(skb, type, value) add s8 attribute to skb + * nla_put_s16(skb, type, value) add s16 attribute to skb + * nla_put_s32(skb, type, value) add s32 attribute to skb + * nla_put_s64(skb, type, value) add s64 attribute to skb * nla_put_string(skb, type, str) add string attribute to skb * nla_put_flag(skb, type) add flag attribute to skb * nla_put_msecs(skb, type, jiffies) add msecs attribute to skb @@ -121,6 +125,10 @@ * nla_get_u16(nla) get payload for a u16 attribute * nla_get_u32(nla) get payload for a u32 attribute * nla_get_u64(nla) get payload for a u64 attribute + * nla_get_s8(nla) get payload for a s8 attribute + * nla_get_s16(nla) get payload for a s16 attribute + * nla_get_s32(nla) get payload for a s32 attribute + * nla_get_s64(nla) get payload for a s64 attribute * nla_get_flag(nla) return 1 if flag is true * nla_get_msecs(nla) get payload for a msecs attribute * @@ -160,6 +168,10 @@ enum { NLA_NESTED_COMPAT, NLA_NUL_STRING, NLA_BINARY, + NLA_S8, + NLA_S16, + NLA_S32, + NLA_S64, __NLA_TYPE_MAX, }; @@ -183,6 +195,8 @@ enum { * NLA_NESTED_COMPAT Minimum length of structure payload * NLA_U8, NLA_U16, * NLA_U32, NLA_U64, + * NLA_S8, NLA_S16, + * NLA_S32, NLA_S64, * NLA_MSECS Leaving the length field zero will verify the * given type fits, using it verifies minimum length * just like "All other" @@ -878,6 +892,50 @@ static inline int nla_put_le64(struct sk_buff *skb, int attrtype, __le64 value) return nla_put(skb, attrtype, sizeof(__le64), &value); } +/** + * nla_put_s8 - Add a s8 netlink attribute to a socket buffer + * @skb: socket buffer to add attribute to + * @attrtype: attribute type + * @value: numeric value + */ +static inline int nla_put_s8(struct sk_buff *skb, int attrtype, s8 value) +{ + return nla_put(skb, attrtype, sizeof(s8), &value); +} + +/** + * nla_put_s16 - Add a s16 netlink attribute to a socket buffer + * @skb: socket buffer to add attribute to + * @attrtype: attribute type + * @value: numeric value + */ +static inline int nla_put_s16(struct sk_buff *skb, int attrtype, s16 value) +{ + return nla_put(skb, attrtype, sizeof(s16), &value); +} + +/** + * nla_put_s32 - Add a s32 netlink attribute to a socket buffer + * @skb: socket buffer to add attribute to + * @attrtype: attribute type + * @value: numeric value + */ +static inline int nla_put_s32(struct sk_buff *skb, int attrtype, s32 value) +{ + return nla_put(skb, attrtype, sizeof(s32), &value); +} + +/** + * nla_put_s64 - Add a s64 netlink attribute to a socket buffer + * @skb: socket buffer to add attribute to + * @attrtype: attribute type + * @value: numeric value + */ +static inline int nla_put_s64(struct sk_buff *skb, int attrtype, s64 value) +{ + return nla_put(skb, attrtype, sizeof(s64), &value); +} + /** * nla_put_string - Add a string netlink attribute to a socket buffer * @skb: socket buffer to add attribute to @@ -993,6 +1051,46 @@ static inline __be64 nla_get_be64(const struct nlattr *nla) return tmp; } +/** + * nla_get_s32 - return payload of s32 attribute + * @nla: s32 netlink attribute + */ +static inline s32 nla_get_s32(const struct nlattr *nla) +{ + return *(s32 *) nla_data(nla); +} + +/** + * nla_get_s16 - return payload of s16 attribute + * @nla: s16 netlink attribute + */ +static inline s16 nla_get_s16(const struct nlattr *nla) +{ + return *(s16 *) nla_data(nla); +} + +/** + * nla_get_s8 - return payload of s8 attribute + * @nla: s8 netlink attribute + */ +static inline s8 nla_get_s8(const struct nlattr *nla) +{ + return *(s8 *) nla_data(nla); +} + +/** + * nla_get_s64 - return payload of s64 attribute + * @nla: s64 netlink attribute + */ +static inline s64 nla_get_s64(const struct nlattr *nla) +{ + s64 tmp; + + nla_memcpy(&tmp, nla, sizeof(tmp)); + + return tmp; +} + /** * nla_get_flag - return payload of flag attribute * @nla: flag netlink attribute -- cgit v1.2.3 From 69821638b27407d8648cb04de01b06b30a291bde Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 27 Jul 2012 06:28:53 +0000 Subject: team: add signed 32-bit team option type Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 12 ++++++++++++ include/linux/if_team.h | 2 ++ 2 files changed, 14 insertions(+) (limited to 'include') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 87707ab39430..70752e631a12 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1787,6 +1787,12 @@ static int team_nl_fill_one_option_get(struct sk_buff *skb, struct team *team, nla_put_flag(skb, TEAM_ATTR_OPTION_DATA)) goto nest_cancel; break; + case TEAM_OPTION_TYPE_S32: + if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_S32)) + goto nest_cancel; + if (nla_put_s32(skb, TEAM_ATTR_OPTION_DATA, ctx.data.s32_val)) + goto nest_cancel; + break; default: BUG(); } @@ -1975,6 +1981,9 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) case NLA_FLAG: opt_type = TEAM_OPTION_TYPE_BOOL; break; + case NLA_S32: + opt_type = TEAM_OPTION_TYPE_S32; + break; default: goto team_put; } @@ -2031,6 +2040,9 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) case TEAM_OPTION_TYPE_BOOL: ctx.data.bool_val = attr_data ? true : false; break; + case TEAM_OPTION_TYPE_S32: + ctx.data.s32_val = nla_get_s32(attr_data); + break; default: BUG(); } diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 6960fc1841a7..e5571c420800 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -130,6 +130,7 @@ enum team_option_type { TEAM_OPTION_TYPE_STRING, TEAM_OPTION_TYPE_BINARY, TEAM_OPTION_TYPE_BOOL, + TEAM_OPTION_TYPE_S32, }; struct team_option_inst_info { @@ -146,6 +147,7 @@ struct team_gsetter_ctx { u32 len; } bin_val; bool bool_val; + s32 s32_val; } data; struct team_option_inst_info *info; }; -- cgit v1.2.3 From a86fc6b7d603992070c04bd7a8c217d55688b077 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 27 Jul 2012 06:28:54 +0000 Subject: team: add per port priority option Allow userspace to set port priority. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 25 +++++++++++++++++++++++++ include/linux/if_team.h | 1 + 2 files changed, 26 insertions(+) (limited to 'include') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 70752e631a12..a30b7c1bd9f6 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1092,6 +1092,24 @@ static int team_user_linkup_en_option_set(struct team *team, return 0; } +static int team_priority_option_get(struct team *team, + struct team_gsetter_ctx *ctx) +{ + struct team_port *port = ctx->info->port; + + ctx->data.s32_val = port->priority; + return 0; +} + +static int team_priority_option_set(struct team *team, + struct team_gsetter_ctx *ctx) +{ + struct team_port *port = ctx->info->port; + + port->priority = ctx->data.s32_val; + return 0; +} + static const struct team_option team_options[] = { { .name = "mode", @@ -1120,6 +1138,13 @@ static const struct team_option team_options[] = { .getter = team_user_linkup_en_option_get, .setter = team_user_linkup_en_option_set, }, + { + .name = "priority", + .type = TEAM_OPTION_TYPE_S32, + .per_port = true, + .getter = team_priority_option_get, + .setter = team_priority_option_set, + }, }; static struct lock_class_key team_netdev_xmit_lock_key; diff --git a/include/linux/if_team.h b/include/linux/if_team.h index e5571c420800..06495b3e7a99 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -67,6 +67,7 @@ struct team_port { struct netpoll *np; #endif + s32 priority; /* lower number ~ higher priority */ long mode_priv[0]; }; -- cgit v1.2.3 From 8ff5105a2b9dd0ba596719b165c1827d101e5f1a Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 27 Jul 2012 06:28:55 +0000 Subject: team: add support for queue override by setting queue_id for port Similar to what bonding has. This allows to set queue_id for port so this port will be used when skb with matching skb->queue_mapping is going to be transmitted. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++- include/linux/if_team.h | 4 ++ 2 files changed, 165 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index a30b7c1bd9f6..ba10c469b02b 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -658,6 +658,122 @@ static rx_handler_result_t team_handle_frame(struct sk_buff **pskb) } +/************************************* + * Multiqueue Tx port select override + *************************************/ + +static int team_queue_override_init(struct team *team) +{ + struct list_head *listarr; + unsigned int queue_cnt = team->dev->num_tx_queues - 1; + unsigned int i; + + if (!queue_cnt) + return 0; + listarr = kmalloc(sizeof(struct list_head) * queue_cnt, GFP_KERNEL); + if (!listarr) + return -ENOMEM; + team->qom_lists = listarr; + for (i = 0; i < queue_cnt; i++) + INIT_LIST_HEAD(listarr++); + return 0; +} + +static void team_queue_override_fini(struct team *team) +{ + kfree(team->qom_lists); +} + +static struct list_head *__team_get_qom_list(struct team *team, u16 queue_id) +{ + return &team->qom_lists[queue_id - 1]; +} + +/* + * note: already called with rcu_read_lock + */ +static bool team_queue_override_transmit(struct team *team, struct sk_buff *skb) +{ + struct list_head *qom_list; + struct team_port *port; + + if (!team->queue_override_enabled || !skb->queue_mapping) + return false; + qom_list = __team_get_qom_list(team, skb->queue_mapping); + list_for_each_entry_rcu(port, qom_list, qom_list) { + if (!team_dev_queue_xmit(team, port, skb)) + return true; + } + return false; +} + +static void __team_queue_override_port_del(struct team *team, + struct team_port *port) +{ + list_del_rcu(&port->qom_list); + synchronize_rcu(); + INIT_LIST_HEAD(&port->qom_list); +} + +static bool team_queue_override_port_has_gt_prio_than(struct team_port *port, + struct team_port *cur) +{ + if (port->priority < cur->priority) + return true; + if (port->priority > cur->priority) + return false; + if (port->index < cur->index) + return true; + return false; +} + +static void __team_queue_override_port_add(struct team *team, + struct team_port *port) +{ + struct team_port *cur; + struct list_head *qom_list; + struct list_head *node; + + if (!port->queue_id || !team_port_enabled(port)) + return; + + qom_list = __team_get_qom_list(team, port->queue_id); + node = qom_list; + list_for_each_entry(cur, qom_list, qom_list) { + if (team_queue_override_port_has_gt_prio_than(port, cur)) + break; + node = &cur->qom_list; + } + list_add_tail_rcu(&port->qom_list, node); +} + +static void __team_queue_override_enabled_check(struct team *team) +{ + struct team_port *port; + bool enabled = false; + + list_for_each_entry(port, &team->port_list, list) { + if (!list_empty(&port->qom_list)) { + enabled = true; + break; + } + } + if (enabled == team->queue_override_enabled) + return; + netdev_dbg(team->dev, "%s queue override\n", + enabled ? "Enabling" : "Disabling"); + team->queue_override_enabled = enabled; +} + +static void team_queue_override_port_refresh(struct team *team, + struct team_port *port) +{ + __team_queue_override_port_del(team, port); + __team_queue_override_port_add(team, port); + __team_queue_override_enabled_check(team); +} + + /**************** * Port handling ****************/ @@ -688,6 +804,7 @@ static void team_port_enable(struct team *team, hlist_add_head_rcu(&port->hlist, team_port_index_hash(team, port->index)); team_adjust_ops(team); + team_queue_override_port_refresh(team, port); if (team->ops.port_enabled) team->ops.port_enabled(team, port); } @@ -716,6 +833,7 @@ static void team_port_disable(struct team *team, hlist_del_rcu(&port->hlist); __reconstruct_port_hlist(team, port->index); port->index = -1; + team_queue_override_port_refresh(team, port); __team_adjust_ops(team, team->en_port_count - 1); /* * Wait until readers see adjusted ops. This ensures that @@ -881,6 +999,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev) port->dev = port_dev; port->team = team; + INIT_LIST_HEAD(&port->qom_list); port->orig.mtu = port_dev->mtu; err = dev_set_mtu(port_dev, dev->mtu); @@ -1107,9 +1226,34 @@ static int team_priority_option_set(struct team *team, struct team_port *port = ctx->info->port; port->priority = ctx->data.s32_val; + team_queue_override_port_refresh(team, port); return 0; } +static int team_queue_id_option_get(struct team *team, + struct team_gsetter_ctx *ctx) +{ + struct team_port *port = ctx->info->port; + + ctx->data.u32_val = port->queue_id; + return 0; +} + +static int team_queue_id_option_set(struct team *team, + struct team_gsetter_ctx *ctx) +{ + struct team_port *port = ctx->info->port; + + if (port->queue_id == ctx->data.u32_val) + return 0; + if (ctx->data.u32_val >= team->dev->real_num_tx_queues) + return -EINVAL; + port->queue_id = ctx->data.u32_val; + team_queue_override_port_refresh(team, port); + return 0; +} + + static const struct team_option team_options[] = { { .name = "mode", @@ -1145,6 +1289,13 @@ static const struct team_option team_options[] = { .getter = team_priority_option_get, .setter = team_priority_option_set, }, + { + .name = "queue_id", + .type = TEAM_OPTION_TYPE_U32, + .per_port = true, + .getter = team_queue_id_option_get, + .setter = team_queue_id_option_set, + }, }; static struct lock_class_key team_netdev_xmit_lock_key; @@ -1180,6 +1331,9 @@ static int team_init(struct net_device *dev) for (i = 0; i < TEAM_PORT_HASHENTRIES; i++) INIT_HLIST_HEAD(&team->en_port_hlist[i]); INIT_LIST_HEAD(&team->port_list); + err = team_queue_override_init(team); + if (err) + goto err_team_queue_override_init; team_adjust_ops(team); @@ -1195,6 +1349,8 @@ static int team_init(struct net_device *dev) return 0; err_options_register: + team_queue_override_fini(team); +err_team_queue_override_init: free_percpu(team->pcpu_stats); return err; @@ -1212,6 +1368,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_queue_override_fini(team); mutex_unlock(&team->lock); } @@ -1241,10 +1398,12 @@ static int team_close(struct net_device *dev) static netdev_tx_t team_xmit(struct sk_buff *skb, struct net_device *dev) { struct team *team = netdev_priv(dev); - bool tx_success = false; + bool tx_success; unsigned int len = skb->len; - tx_success = team->ops.transmit(team, skb); + tx_success = team_queue_override_transmit(team, skb); + if (!tx_success) + tx_success = team->ops.transmit(team, skb); if (tx_success) { struct team_pcpu_stats *pcpu_stats; diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 06495b3e7a99..33fcc20b5881 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -68,6 +68,8 @@ struct team_port { #endif s32 priority; /* lower number ~ higher priority */ + u16 queue_id; + struct list_head qom_list; /* node in queue override mapping list */ long mode_priv[0]; }; @@ -200,6 +202,8 @@ struct team { const struct team_mode *mode; struct team_mode_ops ops; + bool queue_override_enabled; + struct list_head *qom_lists; /* array of queue override mapping lists */ long mode_priv[TEAM_MODE_PRIV_LONGS]; }; -- cgit v1.2.3 From 0d5c3eba2e1e5aa74e097f49bc90b58f607e101c Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 25 Jul 2012 18:12:08 +0300 Subject: vfs: nuke pdflush from comments The pdflush thread is long gone, so this patch removes references to pdflush from vfs comments. Signed-off-by: Artem Bityutskiy Signed-off-by: Al Viro --- fs/bio.c | 2 +- include/linux/writeback.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/fs/bio.c b/fs/bio.c index 73922abba832..5eaa70c9d96e 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -1312,7 +1312,7 @@ EXPORT_SYMBOL(bio_copy_kern); * Note that this code is very hard to test under normal circumstances because * direct-io pins the pages with get_user_pages(). This makes * is_page_cache_freeable return false, and the VM will not clean the pages. - * But other code (eg, pdflush) could clean the pages if they are mapped + * But other code (eg, flusher threads) could clean the pages if they are mapped * pagecache. * * Simply disabling the call to bio_set_pages_dirty() is a good way to test the diff --git a/include/linux/writeback.h b/include/linux/writeback.h index c66fe3332d83..50c3e8fa06a8 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -104,7 +104,6 @@ static inline void wait_on_inode(struct inode *inode) wait_on_bit(&inode->i_state, __I_NEW, inode_wait, TASK_UNINTERRUPTIBLE); } - /* * mm/page-writeback.c */ -- cgit v1.2.3 From 47061bc440b90a16d856fb0dba1cffc36427efa4 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 3 Aug 2012 20:54:15 +0000 Subject: net: skb_share_check() should use consume_skb() In order to avoid false drop_monitor indications, we should call consume_skb() if skb_clone() was successful. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/skbuff.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 7632c87da2c9..b33a3a1f205e 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -846,13 +846,16 @@ static inline int skb_shared(const struct sk_buff *skb) * * NULL is returned on a memory allocation failure. */ -static inline struct sk_buff *skb_share_check(struct sk_buff *skb, - gfp_t pri) +static inline struct sk_buff *skb_share_check(struct sk_buff *skb, gfp_t pri) { might_sleep_if(pri & __GFP_WAIT); if (skb_shared(skb)) { struct sk_buff *nskb = skb_clone(skb, pri); - kfree_skb(skb); + + if (likely(nskb)) + consume_skb(skb); + else + kfree_skb(skb); skb = nskb; } return skb; -- cgit v1.2.3 From 9eb43e765368f835d92c93844ebce30da7efeb84 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 3 Aug 2012 21:27:25 +0000 Subject: ipv4: Introduce IN_DEV_NET_ROUTE_LOCALNET performance profiles show a high cost in the IN_DEV_ROUTE_LOCALNET() call done in ip_route_input_slow(), because of multiple dereferences, even if cache lines are clean and available in cpu caches. Since we already have the 'net' pointer, introduce IN_DEV_NET_ROUTE_LOCALNET() macro avoiding two dereferences (dev_net(in_dev->dev)) Also change the tests to use IN_DEV_NET_ROUTE_LOCALNET() only if saddr or/and daddr are loopback addresse. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/inetdevice.h | 11 +++++++++-- net/ipv4/route.c | 11 +++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index 67f9ddacb70c..d032780d0ce5 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -104,9 +104,14 @@ static inline void ipv4_devconf_setall(struct in_device *in_dev) #define IN_DEV_ANDCONF(in_dev, attr) \ (IPV4_DEVCONF_ALL(dev_net(in_dev->dev), attr) && \ IN_DEV_CONF_GET((in_dev), attr)) -#define IN_DEV_ORCONF(in_dev, attr) \ - (IPV4_DEVCONF_ALL(dev_net(in_dev->dev), attr) || \ + +#define IN_DEV_NET_ORCONF(in_dev, net, attr) \ + (IPV4_DEVCONF_ALL(net, attr) || \ IN_DEV_CONF_GET((in_dev), attr)) + +#define IN_DEV_ORCONF(in_dev, attr) \ + IN_DEV_NET_ORCONF(in_dev, dev_net(in_dev->dev), attr) + #define IN_DEV_MAXCONF(in_dev, attr) \ (max(IPV4_DEVCONF_ALL(dev_net(in_dev->dev), attr), \ IN_DEV_CONF_GET((in_dev), attr))) @@ -133,6 +138,8 @@ static inline void ipv4_devconf_setall(struct in_device *in_dev) IN_DEV_ORCONF((in_dev), \ PROMOTE_SECONDARIES) #define IN_DEV_ROUTE_LOCALNET(in_dev) IN_DEV_ORCONF(in_dev, ROUTE_LOCALNET) +#define IN_DEV_NET_ROUTE_LOCALNET(in_dev, net) \ + IN_DEV_NET_ORCONF(in_dev, net, ROUTE_LOCALNET) #define IN_DEV_RX_REDIRECTS(in_dev) \ ((IN_DEV_FORWARD(in_dev) && \ diff --git a/net/ipv4/route.c b/net/ipv4/route.c index e4ba974f143c..21ad369014c0 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1587,11 +1587,14 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr, if (ipv4_is_zeronet(daddr)) goto martian_destination; - if (likely(!IN_DEV_ROUTE_LOCALNET(in_dev))) { - if (ipv4_is_loopback(daddr)) + /* Following code try to avoid calling IN_DEV_NET_ROUTE_LOCALNET(), + * and call it once if daddr or/and saddr are loopback addresses + */ + if (ipv4_is_loopback(daddr)) { + if (!IN_DEV_NET_ROUTE_LOCALNET(in_dev, net)) goto martian_destination; - - if (ipv4_is_loopback(saddr)) + } else if (ipv4_is_loopback(saddr)) { + if (!IN_DEV_NET_ROUTE_LOCALNET(in_dev, net)) goto martian_source; } -- cgit v1.2.3 From d796c52ef0b71a988364f6109aeb63d79c5b116b Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sun, 5 Aug 2012 19:04:57 -0400 Subject: ext4: make sure the journal sb is written in ext4_clear_journal_err() After we transfer set the EXT4_ERROR_FS bit in the file system superblock, it's not enough to call jbd2_journal_clear_err() to clear the error indication from journal superblock --- we need to call jbd2_journal_update_sb_errno() as well. Otherwise, when the root file system is mounted read-only, the journal is replayed, and the error indicator is transferred to the superblock --- but the s_errno field in the jbd2 superblock is left set (since although we cleared it in memory, we never flushed it out to disk). This can end up confusing e2fsck. We should make e2fsck more robust in this case, but the kernel shouldn't be leaving things in this confused state, either. Signed-off-by: "Theodore Ts'o" Cc: stable@kernel.org --- fs/ext4/super.c | 1 + fs/jbd2/journal.c | 3 ++- include/linux/jbd2.h | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/fs/ext4/super.c b/fs/ext4/super.c index d76ec8277d3f..ccc4bcad5616 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -4430,6 +4430,7 @@ static void ext4_clear_journal_err(struct super_block *sb, ext4_commit_super(sb, 1); jbd2_journal_clear_err(journal); + jbd2_journal_update_sb_errno(journal); } } diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index e9a3c4c85594..bd23f2ebaa67 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -1377,7 +1377,7 @@ static void jbd2_mark_journal_empty(journal_t *journal) * Update a journal's errno. Write updated superblock to disk waiting for IO * to complete. */ -static void jbd2_journal_update_sb_errno(journal_t *journal) +void jbd2_journal_update_sb_errno(journal_t *journal) { journal_superblock_t *sb = journal->j_superblock; @@ -1390,6 +1390,7 @@ static void jbd2_journal_update_sb_errno(journal_t *journal) jbd2_write_superblock(journal, WRITE_SYNC); } +EXPORT_SYMBOL(jbd2_journal_update_sb_errno); /* * Read the superblock for a given journal, performing initial diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index f334c7fab967..3efc43f3f162 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -1125,6 +1125,7 @@ extern int jbd2_journal_destroy (journal_t *); extern int jbd2_journal_recover (journal_t *journal); extern int jbd2_journal_wipe (journal_t *, int); extern int jbd2_journal_skip_recovery (journal_t *); +extern void jbd2_journal_update_sb_errno(journal_t *); extern void jbd2_journal_update_sb_log_tail (journal_t *, tid_t, unsigned long, int); extern void __jbd2_journal_abort_hard (journal_t *); -- cgit v1.2.3 From 3f1732462c0e45ac9b0c09035751d7b2c1b89cc0 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Thu, 19 Jul 2012 11:46:13 -0300 Subject: Bluetooth: Remove missing code This patch removes the struct adv_entry since it is not used anymore. This struct should have been removed in commit 479453d (Bluetooth: Remove advertising cache). Signed-off-by: Andre Guedes Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 6 ------ 1 file changed, 6 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 475b8c04ba52..41b26648e79e 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -115,12 +115,6 @@ struct oob_data { u8 randomizer[16]; }; -struct adv_entry { - struct list_head list; - bdaddr_t bdaddr; - u8 bdaddr_type; -}; - struct le_scan_params { u8 type; u16 interval; -- cgit v1.2.3 From bb4b2a9ae38ef3bac69627f35e4f916752631fd1 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 19 Jul 2012 17:03:40 +0300 Subject: Bluetooth: mgmt: Managing only BR/EDR HCI controllers Add check that HCI controller is BR/EDR. AMP controller shall not be managed by mgmt interface and consequently user space. Signed-off-by: Andrei Emeltchenko Acked-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_core.c | 6 ++++-- net/bluetooth/mgmt.c | 20 ++++++++++++++++++-- 3 files changed, 23 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 41b26648e79e..5c7a5f819fcf 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1050,7 +1050,7 @@ int mgmt_discovering(struct hci_dev *hdev, u8 discovering); int mgmt_interleaved_discovery(struct hci_dev *hdev); int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); - +bool mgmt_valid_hdev(struct hci_dev *hdev); int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent); /* HCI info for socket */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index d4de5db18d5a..fa974a19d365 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -696,7 +696,8 @@ int hci_dev_open(__u16 dev) hci_dev_hold(hdev); set_bit(HCI_UP, &hdev->flags); hci_notify(hdev, HCI_DEV_UP); - if (!test_bit(HCI_SETUP, &hdev->dev_flags)) { + if (!test_bit(HCI_SETUP, &hdev->dev_flags) && + mgmt_valid_hdev(hdev)) { hci_dev_lock(hdev); mgmt_powered(hdev, 1); hci_dev_unlock(hdev); @@ -797,7 +798,8 @@ static int hci_dev_do_close(struct hci_dev *hdev) * and no tasks are scheduled. */ hdev->close(hdev); - if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) { + if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags) && + mgmt_valid_hdev(hdev)) { hci_dev_lock(hdev); mgmt_powered(hdev, 0); hci_dev_unlock(hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ad6613d17ca6..2a0f695e33d4 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -193,6 +193,11 @@ static u8 mgmt_status_table[] = { MGMT_STATUS_CONNECT_FAILED, /* MAC Connection Failed */ }; +bool mgmt_valid_hdev(struct hci_dev *hdev) +{ + return hdev->dev_type == HCI_BREDR; +} + static u8 mgmt_status(u8 hci_status) { if (hci_status < ARRAY_SIZE(mgmt_status_table)) @@ -317,7 +322,6 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len) { struct mgmt_rp_read_index_list *rp; - struct list_head *p; struct hci_dev *d; size_t rp_len; u16 count; @@ -328,7 +332,10 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, read_lock(&hci_dev_list_lock); count = 0; - list_for_each(p, &hci_dev_list) { + list_for_each_entry(d, &hci_dev_list, list) { + if (!mgmt_valid_hdev(d)) + continue; + count++; } @@ -346,6 +353,9 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, if (test_bit(HCI_SETUP, &d->dev_flags)) continue; + if (!mgmt_valid_hdev(d)) + continue; + rp->index[i++] = cpu_to_le16(d->id); BT_DBG("Added hci%u", d->id); } @@ -2820,6 +2830,9 @@ static void cmd_status_rsp(struct pending_cmd *cmd, void *data) int mgmt_index_added(struct hci_dev *hdev) { + if (!mgmt_valid_hdev(hdev)) + return -ENOTSUPP; + return mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL); } @@ -2827,6 +2840,9 @@ int mgmt_index_removed(struct hci_dev *hdev) { u8 status = MGMT_STATUS_INVALID_INDEX; + if (!mgmt_valid_hdev(hdev)) + return -ENOTSUPP; + mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); return mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL); -- cgit v1.2.3 From 71becf0cea91380200ab9460e5126aeff5d7420b Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 19 Jul 2012 17:03:42 +0300 Subject: Bluetooth: debug: Fix printing refcnt for hci_conn Use the same style for refcnt printing through all Bluetooth code taking the reference the l2cap_chan refcnt printing. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 5c7a5f819fcf..03b8b6526400 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -581,8 +581,7 @@ void hci_conn_put_device(struct hci_conn *conn); static inline void hci_conn_hold(struct hci_conn *conn) { - BT_DBG("hcon %p refcnt %d -> %d", conn, atomic_read(&conn->refcnt), - atomic_read(&conn->refcnt) + 1); + BT_DBG("hcon %p orig refcnt %d", conn, atomic_read(&conn->refcnt)); atomic_inc(&conn->refcnt); cancel_delayed_work(&conn->disc_work); @@ -590,8 +589,7 @@ static inline void hci_conn_hold(struct hci_conn *conn) static inline void hci_conn_put(struct hci_conn *conn) { - BT_DBG("hcon %p refcnt %d -> %d", conn, atomic_read(&conn->refcnt), - atomic_read(&conn->refcnt) - 1); + BT_DBG("hcon %p orig refcnt %d", conn, atomic_read(&conn->refcnt)); if (atomic_dec_and_test(&conn->refcnt)) { unsigned long timeo; -- cgit v1.2.3 From b93a68295f3a2b1b66d235ce8f9f5a97553f0d0e Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 19 Jul 2012 17:03:44 +0300 Subject: Bluetooth: trivial: Fix mixing spaces and tabs in smp Change spaces to tabs in smp code Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/smp.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h index ca356a734920..50993a531d45 100644 --- a/include/net/bluetooth/smp.h +++ b/include/net/bluetooth/smp.h @@ -108,8 +108,8 @@ struct smp_cmd_security_req { #define SMP_CONFIRM_FAILED 0x04 #define SMP_PAIRING_NOTSUPP 0x05 #define SMP_ENC_KEY_SIZE 0x06 -#define SMP_CMD_NOTSUPP 0x07 -#define SMP_UNSPECIFIED 0x08 +#define SMP_CMD_NOTSUPP 0x07 +#define SMP_UNSPECIFIED 0x08 #define SMP_REPEATED_ATTEMPTS 0x09 #define SMP_MIN_ENC_KEY_SIZE 7 @@ -123,8 +123,8 @@ struct smp_chan { struct l2cap_conn *conn; u8 preq[7]; /* SMP Pairing Request */ u8 prsp[7]; /* SMP Pairing Response */ - u8 prnd[16]; /* SMP Pairing Random (local) */ - u8 rrnd[16]; /* SMP Pairing Random (remote) */ + u8 prnd[16]; /* SMP Pairing Random (local) */ + u8 rrnd[16]; /* SMP Pairing Random (remote) */ u8 pcnf[16]; /* SMP Pairing Confirm */ u8 tk[16]; /* SMP Temporary Key */ u8 enc_key_size; -- cgit v1.2.3 From ab846ec4eaea1156148841b194df808ad745bbe2 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 19 Jul 2012 17:03:45 +0300 Subject: Bluetooth: Define AMP controller statuses AMP status codes copied from Bluez patch sent by Peter Krystad . Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index ccd723e0f783..7f1955662455 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -62,6 +62,15 @@ /* First BR/EDR Controller shall have ID = 0 */ #define HCI_BREDR_ID 0 +/* AMP controller status */ +#define AMP_CTRL_POWERED_DOWN 0x00 +#define AMP_CTRL_BLUETOOTH_ONLY 0x01 +#define AMP_CTRL_NO_CAPACITY 0x02 +#define AMP_CTRL_LOW_CAPACITY 0x03 +#define AMP_CTRL_MEDIUM_CAPACITY 0x04 +#define AMP_CTRL_HIGH_CAPACITY 0x05 +#define AMP_CTRL_FULL_CAPACITY 0x06 + /* HCI device quirks */ enum { HCI_QUIRK_RESET_ON_CLOSE, -- cgit v1.2.3 From 9e66463127ff7238020c3c4e7f84dfbc23e5c2b5 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 24 Jul 2012 16:06:15 +0300 Subject: Bluetooth: Make connect / disconnect cfm functions return void Return values are never used because callers hci_proto_connect_cfm and hci_proto_disconn_cfm return void. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 8 ++++---- net/bluetooth/l2cap_core.c | 6 ++---- net/bluetooth/sco.c | 7 ++----- 3 files changed, 8 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 03b8b6526400..41d943926d2c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -350,16 +350,16 @@ extern rwlock_t hci_cb_list_lock; /* ----- HCI interface to upper protocols ----- */ extern int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr); -extern int l2cap_connect_cfm(struct hci_conn *hcon, u8 status); +extern void l2cap_connect_cfm(struct hci_conn *hcon, u8 status); extern int l2cap_disconn_ind(struct hci_conn *hcon); -extern int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason); +extern void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason); extern int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt); extern int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags); extern int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr); -extern int sco_connect_cfm(struct hci_conn *hcon, __u8 status); -extern int sco_disconn_cfm(struct hci_conn *hcon, __u8 reason); +extern void sco_connect_cfm(struct hci_conn *hcon, __u8 status); +extern void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason); extern int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb); /* ----- Inquiry cache ----- */ diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 64d88a603f0a..8391e0575494 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -5329,7 +5329,7 @@ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr) return exact ? lm1 : lm2; } -int l2cap_connect_cfm(struct hci_conn *hcon, u8 status) +void l2cap_connect_cfm(struct hci_conn *hcon, u8 status) { struct l2cap_conn *conn; @@ -5342,7 +5342,6 @@ int l2cap_connect_cfm(struct hci_conn *hcon, u8 status) } else l2cap_conn_del(hcon, bt_to_errno(status)); - return 0; } int l2cap_disconn_ind(struct hci_conn *hcon) @@ -5356,12 +5355,11 @@ int l2cap_disconn_ind(struct hci_conn *hcon) return conn->disc_reason; } -int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) +void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) { BT_DBG("hcon %p reason %d", hcon, reason); l2cap_conn_del(hcon, bt_to_errno(reason)); - return 0; } static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 40bbe25dcff7..0ef5a78a889f 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -913,7 +913,7 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr) return lm; } -int sco_connect_cfm(struct hci_conn *hcon, __u8 status) +void sco_connect_cfm(struct hci_conn *hcon, __u8 status) { BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status); if (!status) { @@ -924,16 +924,13 @@ int sco_connect_cfm(struct hci_conn *hcon, __u8 status) sco_conn_ready(conn); } else sco_conn_del(hcon, bt_to_errno(status)); - - return 0; } -int sco_disconn_cfm(struct hci_conn *hcon, __u8 reason) +void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason) { BT_DBG("hcon %p reason %d", hcon, reason); sco_conn_del(hcon, bt_to_errno(reason)); - return 0; } int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb) -- cgit v1.2.3 From 3064837289259843310b266a9422aca5f5b4b9c7 Mon Sep 17 00:00:00 2001 From: Jaganath Kanakkassery Date: Fri, 13 Jul 2012 18:17:54 +0530 Subject: Bluetooth: Move l2cap_chan_hold/put to l2cap_core.c Refactor the code in order to use the l2cap_chan_destroy() from l2cap_chan_put() under the refcnt protection. Signed-off-by: Jaganath Kanakkassery Signed-off-by: Syam Sidhardhan Reviewed-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 16 ++-------------- net/bluetooth/l2cap_core.c | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index a7679f8913d2..a1eb6786ce54 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -671,20 +671,8 @@ enum { L2CAP_EV_RECV_FRAME, }; -static inline void l2cap_chan_hold(struct l2cap_chan *c) -{ - BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->refcnt)); - - atomic_inc(&c->refcnt); -} - -static inline void l2cap_chan_put(struct l2cap_chan *c) -{ - BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->refcnt)); - - if (atomic_dec_and_test(&c->refcnt)) - kfree(c); -} +void l2cap_chan_hold(struct l2cap_chan *c); +void l2cap_chan_put(struct l2cap_chan *c); static inline void l2cap_chan_lock(struct l2cap_chan *chan) { diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 8391e0575494..79923d8bbe97 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -425,6 +425,21 @@ void l2cap_chan_destroy(struct l2cap_chan *chan) l2cap_chan_put(chan); } +void l2cap_chan_hold(struct l2cap_chan *c) +{ + BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->refcnt)); + + atomic_inc(&c->refcnt); +} + +void l2cap_chan_put(struct l2cap_chan *c) +{ + BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->refcnt)); + + if (atomic_dec_and_test(&c->refcnt)) + kfree(c); +} + void l2cap_chan_set_defaults(struct l2cap_chan *chan) { chan->fcs = L2CAP_FCS_CRC16; -- cgit v1.2.3 From 4af66c691f4e5c2db9bb00793669a548e9db1974 Mon Sep 17 00:00:00 2001 From: Jaganath Kanakkassery Date: Fri, 13 Jul 2012 18:17:55 +0530 Subject: Bluetooth: Free the l2cap channel list only when refcount is zero Move the l2cap channel list chan->global_l under the refcnt protection and free it based on the refcnt. Signed-off-by: Jaganath Kanakkassery Signed-off-by: Syam Sidhardhan Reviewed-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 1 - net/bluetooth/a2mp.c | 2 +- net/bluetooth/l2cap_core.c | 8 +++++--- net/bluetooth/l2cap_sock.c | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index a1eb6786ce54..d206296137e2 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -759,7 +759,6 @@ int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid); struct l2cap_chan *l2cap_chan_create(void); void l2cap_chan_close(struct l2cap_chan *chan, int reason); -void l2cap_chan_destroy(struct l2cap_chan *chan); int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *dst, u8 dst_type); int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 79af661a58dd..0760d1fed6f0 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -416,7 +416,7 @@ static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) static void a2mp_chan_close_cb(struct l2cap_chan *chan) { - l2cap_chan_destroy(chan); + l2cap_chan_put(chan); } static void a2mp_chan_state_change_cb(struct l2cap_chan *chan, int state) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 79923d8bbe97..9f8b29ef5b68 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -416,13 +416,15 @@ struct l2cap_chan *l2cap_chan_create(void) return chan; } -void l2cap_chan_destroy(struct l2cap_chan *chan) +static void l2cap_chan_destroy(struct l2cap_chan *chan) { + BT_DBG("chan %p", chan); + write_lock(&chan_list_lock); list_del(&chan->global_l); write_unlock(&chan_list_lock); - l2cap_chan_put(chan); + kfree(chan); } void l2cap_chan_hold(struct l2cap_chan *c) @@ -437,7 +439,7 @@ void l2cap_chan_put(struct l2cap_chan *c) BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->refcnt)); if (atomic_dec_and_test(&c->refcnt)) - kfree(c); + l2cap_chan_destroy(c); } void l2cap_chan_set_defaults(struct l2cap_chan *chan) diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index a4bb27e8427e..79350d10087c 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -823,7 +823,7 @@ static void l2cap_sock_kill(struct sock *sk) /* Kill poor orphan */ - l2cap_chan_destroy(l2cap_pi(sk)->chan); + l2cap_chan_put(l2cap_pi(sk)->chan); sock_set_flag(sk, SOCK_DEAD); sock_put(sk); } -- cgit v1.2.3 From 256a06c8a85df676e80263af349daad1283e529e Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Thu, 26 Jul 2012 01:26:32 +0900 Subject: Bluetooth: /proc/net/ entries for bluetooth protocols lsof command can tell the type of socket processes are using. Internal lsof uses inode numbers on socket fs to resolve the type of sockets. Files under /proc/net/, such as tcp, udp, unix, etc provides such inode information. Unfortunately bluetooth related protocols don't provide such inode information. This patch series introduces /proc/net files for the protocols. This patch against af_bluetooth.c provides facility to the implementation of protocols. This patch extends bt_sock_list and introduces two exported function bt_procfs_init, bt_procfs_cleanup. The type bt_sock_list is already used in some of implementation of protocols. bt_procfs_init prepare seq_operations which converts protocol own bt_sock_list data to protocol own proc entry when the entry is accessed. What I, lsof user, need is just inode number of bluetooth socket. However, people may want more information. The bt_procfs_init takes a function pointer for customizing the show handler of seq_operations. In v4 patch, __acquires and __releases attributes are added to suppress sparse warning. Suggested by Andrei Emeltchenko. In v5 patch, linux/proc_fs.h is included to use PDE. Build error is reported by Fengguang Wu. Signed-off-by: Masatake YAMATO Signed-off-by: Gustavo Padovan --- include/net/bluetooth/bluetooth.h | 10 +++ net/bluetooth/af_bluetooth.c | 141 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 565d4bee1e49..ede036977ae8 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -27,6 +27,7 @@ #include #include +#include #ifndef AF_BLUETOOTH #define AF_BLUETOOTH 31 @@ -202,6 +203,10 @@ enum { struct bt_sock_list { struct hlist_head head; rwlock_t lock; +#ifdef CONFIG_PROC_FS + struct file_operations fops; + int (* custom_seq_show)(struct seq_file *, void *); +#endif }; int bt_sock_register(int proto, const struct net_proto_family *ops); @@ -292,6 +297,11 @@ extern void hci_sock_cleanup(void); extern int bt_sysfs_init(void); extern void bt_sysfs_cleanup(void); +extern int bt_procfs_init(struct module* module, struct net *net, const char *name, + struct bt_sock_list* sk_list, + int (* seq_show)(struct seq_file *, void *)); +extern void bt_procfs_cleanup(struct net *net, const char *name); + extern struct dentry *bt_debugfs; int l2cap_init(void); diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index f7db5792ec64..58f9762b339a 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -28,6 +28,7 @@ #include #include +#include #define VERSION "2.16" @@ -532,6 +533,146 @@ int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo) } EXPORT_SYMBOL(bt_sock_wait_state); +#ifdef CONFIG_PROC_FS +struct bt_seq_state { + struct bt_sock_list *l; +}; + +static void *bt_seq_start(struct seq_file *seq, loff_t *pos) + __acquires(seq->private->l->lock) +{ + struct bt_seq_state *s = seq->private; + struct bt_sock_list *l = s->l; + + read_lock(&l->lock); + return seq_hlist_start_head(&l->head, *pos); +} + +static void *bt_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct bt_seq_state *s = seq->private; + struct bt_sock_list *l = s->l; + + return seq_hlist_next(v, &l->head, pos); +} + +static void bt_seq_stop(struct seq_file *seq, void *v) + __releases(seq->private->l->lock) +{ + struct bt_seq_state *s = seq->private; + struct bt_sock_list *l = s->l; + + read_unlock(&l->lock); +} + +static int bt_seq_show(struct seq_file *seq, void *v) +{ + struct sock *sk; + struct bt_sock *bt; + struct bt_seq_state *s = seq->private; + struct bt_sock_list *l = s->l; + bdaddr_t src_baswapped, dst_baswapped; + + if (v == SEQ_START_TOKEN) { + seq_puts(seq ,"sk RefCnt Rmem Wmem User Inode Src Dst Parent"); + + if (l->custom_seq_show) { + seq_putc(seq, ' '); + l->custom_seq_show(seq, v); + } + + seq_putc(seq, '\n'); + } else { + sk = sk_entry(v); + bt = bt_sk(sk); + baswap(&src_baswapped, &bt->src); + baswap(&dst_baswapped, &bt->dst); + + seq_printf(seq, "%pK %-6d %-6u %-6u %-6u %-6lu %pM %pM %-6lu", + sk, + atomic_read(&sk->sk_refcnt), + sk_rmem_alloc_get(sk), + sk_wmem_alloc_get(sk), + sock_i_uid(sk), + sock_i_ino(sk), + &src_baswapped, + &dst_baswapped, + bt->parent? sock_i_ino(bt->parent): 0LU); + + if (l->custom_seq_show) { + seq_putc(seq, ' '); + l->custom_seq_show(seq, v); + } + + seq_putc(seq, '\n'); + } + return 0; +} + +static struct seq_operations bt_seq_ops = { + .start = bt_seq_start, + .next = bt_seq_next, + .stop = bt_seq_stop, + .show = bt_seq_show, +}; + +static int bt_seq_open(struct inode *inode, struct file *file) +{ + struct bt_sock_list *sk_list; + struct bt_seq_state *s; + + sk_list = PDE(inode)->data; + s = __seq_open_private(file, &bt_seq_ops, + sizeof(struct bt_seq_state)); + if (s == NULL) + return -ENOMEM; + + s->l = sk_list; + return 0; +} + +int bt_procfs_init(struct module* module, struct net *net, const char *name, + struct bt_sock_list* sk_list, + int (* seq_show)(struct seq_file *, void *)) +{ + struct proc_dir_entry * pde; + + sk_list->custom_seq_show = seq_show; + + sk_list->fops.owner = module; + sk_list->fops.open = bt_seq_open; + sk_list->fops.read = seq_read; + sk_list->fops.llseek = seq_lseek; + sk_list->fops.release = seq_release_private; + + pde = proc_net_fops_create(net, name, 0, &sk_list->fops); + if (pde == NULL) + return -ENOMEM; + + pde->data = sk_list; + + return 0; +} + +void bt_procfs_cleanup(struct net *net, const char *name) +{ + proc_net_remove(net, name); +} +#else +int bt_procfs_init(struct module* module, struct net *net, const char *name, + struct bt_sock_list* sk_list, + int (* seq_show)(struct seq_file *, void *)) +{ + return 0; +} + +void bt_procfs_cleanup(struct net *net, const char *name) +{ +} +#endif +EXPORT_SYMBOL(bt_procfs_init); +EXPORT_SYMBOL(bt_procfs_cleanup); + static struct net_proto_family bt_sock_family_ops = { .owner = THIS_MODULE, .family = PF_BLUETOOTH, -- cgit v1.2.3 From b9b343d25484bbceaee454ab422daafb1c5eda96 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Fri, 27 Jul 2012 15:10:11 -0300 Subject: Bluetooth: Fix hci_le_conn_complete_evt We need to check the 'Role' parameter from the LE Connection Complete Event in order to properly set 'out' and 'link_mode' fields from hci_conn structure. Signed-off-by: Andre Guedes Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 2 ++ net/bluetooth/hci_event.c | 5 +++++ 2 files changed, 7 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 7f1955662455..23cf413e2acf 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1304,6 +1304,8 @@ struct hci_ev_num_comp_blocks { } __packed; /* Low energy meta events */ +#define LE_CONN_ROLE_MASTER 0x00 + #define HCI_EV_LE_CONN_COMPLETE 0x01 struct hci_ev_le_conn_complete { __u8 status; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index e89d7f24136b..8b13cccd50cd 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3368,6 +3368,11 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) } conn->dst_type = ev->bdaddr_type; + + if (ev->role == LE_CONN_ROLE_MASTER) { + conn->out = true; + conn->link_mode |= HCI_LM_MASTER; + } } if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) -- cgit v1.2.3 From 5d299f3d3c8a2fbc732b1bf03af36333ccec3130 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 6 Aug 2012 05:09:33 +0000 Subject: net: ipv6: fix TCP early demux IPv6 needs a cookie in dst_check() call. We need to add rx_dst_cookie and provide a family independent sk_rx_dst_set(sk, skb) method to properly support IPv6 TCP early demux. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/ipv6.h | 1 + include/net/inet_connection_sock.h | 1 + include/net/inet_sock.h | 9 --------- net/ipv4/tcp_input.c | 4 +++- net/ipv4/tcp_ipv4.c | 13 ++++++++++--- net/ipv4/tcp_minisocks.c | 2 +- net/ipv6/tcp_ipv6.c | 27 +++++++++++++++++++++++++-- 7 files changed, 41 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 379e433e15e0..879db26ec401 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -369,6 +369,7 @@ struct ipv6_pinfo { __u8 rcv_tclass; __u32 dst_cookie; + __u32 rx_dst_cookie; struct ipv6_mc_socklist __rcu *ipv6_mc_list; struct ipv6_ac_socklist *ipv6_ac_list; diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index 5ee66f517b4f..ba1d3615acbb 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -39,6 +39,7 @@ struct inet_connection_sock_af_ops { int (*queue_xmit)(struct sk_buff *skb, struct flowi *fl); void (*send_check)(struct sock *sk, struct sk_buff *skb); int (*rebuild_header)(struct sock *sk); + void (*sk_rx_dst_set)(struct sock *sk, const struct sk_buff *skb); int (*conn_request)(struct sock *sk, struct sk_buff *skb); struct sock *(*syn_recv_sock)(struct sock *sk, struct sk_buff *skb, struct request_sock *req, diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 83b567fe1941..613cfa401672 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -249,13 +249,4 @@ static inline __u8 inet_sk_flowi_flags(const struct sock *sk) return flags; } -static inline void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) -{ - struct dst_entry *dst = skb_dst(skb); - - dst_hold(dst); - sk->sk_rx_dst = dst; - inet_sk(sk)->rx_dst_ifindex = skb->skb_iif; -} - #endif /* _INET_SOCK_H */ diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 2fd2bc9e3c64..85308b90df80 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5392,6 +5392,8 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, { struct tcp_sock *tp = tcp_sk(sk); + if (unlikely(sk->sk_rx_dst == NULL)) + inet_csk(sk)->icsk_af_ops->sk_rx_dst_set(sk, skb); /* * Header prediction. * The code loosely follows the one in the famous @@ -5605,7 +5607,7 @@ void tcp_finish_connect(struct sock *sk, struct sk_buff *skb) tcp_set_state(sk, TCP_ESTABLISHED); if (skb != NULL) { - inet_sk_rx_dst_set(sk, skb); + icsk->icsk_af_ops->sk_rx_dst_set(sk, skb); security_inet_conn_established(sk, skb); } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 42b2a6a73092..272241f16fcb 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1627,9 +1627,6 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) sk->sk_rx_dst = NULL; } } - if (unlikely(sk->sk_rx_dst == NULL)) - inet_sk_rx_dst_set(sk, skb); - if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) { rsk = sk; goto reset; @@ -1872,10 +1869,20 @@ static struct timewait_sock_ops tcp_timewait_sock_ops = { .twsk_destructor= tcp_twsk_destructor, }; +static void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) +{ + struct dst_entry *dst = skb_dst(skb); + + dst_hold(dst); + sk->sk_rx_dst = dst; + inet_sk(sk)->rx_dst_ifindex = skb->skb_iif; +} + const struct inet_connection_sock_af_ops ipv4_specific = { .queue_xmit = ip_queue_xmit, .send_check = tcp_v4_send_check, .rebuild_header = inet_sk_rebuild_header, + .sk_rx_dst_set = inet_sk_rx_dst_set, .conn_request = tcp_v4_conn_request, .syn_recv_sock = tcp_v4_syn_recv_sock, .net_header_len = sizeof(struct iphdr), diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 232a90c3ec86..d9c9dcef2de3 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -387,7 +387,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, struct tcp_sock *oldtp = tcp_sk(sk); struct tcp_cookie_values *oldcvp = oldtp->cookie_values; - inet_sk_rx_dst_set(newsk, skb); + newicsk->icsk_af_ops->sk_rx_dst_set(newsk, skb); /* TCP Cookie Transactions require space for the cookie pair, * as it differs for each connection. There is no need to diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index c66b90f71c9b..5a439e9a4c01 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1447,7 +1447,17 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) opt_skb = skb_clone(skb, sk_gfp_atomic(sk, GFP_ATOMIC)); if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */ + struct dst_entry *dst = sk->sk_rx_dst; + sock_rps_save_rxhash(sk, skb); + if (dst) { + if (inet_sk(sk)->rx_dst_ifindex != skb->skb_iif || + dst->ops->check(dst, np->rx_dst_cookie) == NULL) { + dst_release(dst); + sk->sk_rx_dst = NULL; + } + } + if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) goto reset; if (opt_skb) @@ -1705,9 +1715,9 @@ static void tcp_v6_early_demux(struct sk_buff *skb) struct dst_entry *dst = sk->sk_rx_dst; struct inet_sock *icsk = inet_sk(sk); if (dst) - dst = dst_check(dst, 0); + dst = dst_check(dst, inet6_sk(sk)->rx_dst_cookie); if (dst && - icsk->rx_dst_ifindex == inet6_iif(skb)) + icsk->rx_dst_ifindex == skb->skb_iif) skb_dst_set_noref(skb, dst); } } @@ -1719,10 +1729,23 @@ static struct timewait_sock_ops tcp6_timewait_sock_ops = { .twsk_destructor= tcp_twsk_destructor, }; +static void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) +{ + struct dst_entry *dst = skb_dst(skb); + const struct rt6_info *rt = (const struct rt6_info *)dst; + + dst_hold(dst); + sk->sk_rx_dst = dst; + inet_sk(sk)->rx_dst_ifindex = skb->skb_iif; + if (rt->rt6i_node) + inet6_sk(sk)->rx_dst_cookie = rt->rt6i_node->fn_sernum; +} + static const struct inet_connection_sock_af_ops ipv6_specific = { .queue_xmit = inet6_csk_xmit, .send_check = tcp_v6_send_check, .rebuild_header = inet6_sk_rebuild_header, + .sk_rx_dst_set = inet6_sk_rx_dst_set, .conn_request = tcp_v6_conn_request, .syn_recv_sock = tcp_v6_syn_recv_sock, .net_header_len = sizeof(struct ipv6hdr), -- cgit v1.2.3 From d25398df59b561a26cb4000ceb4dea8a3ff94d22 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 4 Aug 2012 20:26:13 +0000 Subject: net: avoid reloads in SNMP_UPD_PO_STATS Avoid two instructions to reload dev->nd_net->mib.ip_statistics pointer, unsing a temp variable, in ip_rcv(), ip_output() paths for example. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/snmp.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/snmp.h b/include/net/snmp.h index 0147b901e79c..71596261fa99 100644 --- a/include/net/snmp.h +++ b/include/net/snmp.h @@ -154,13 +154,15 @@ struct linux_xfrm_mib { */ #define SNMP_UPD_PO_STATS(mib, basefield, addend) \ do { \ - this_cpu_inc(mib[0]->mibs[basefield##PKTS]); \ - this_cpu_add(mib[0]->mibs[basefield##OCTETS], addend); \ + __typeof__(*mib[0]->mibs) *ptr = mib[0]->mibs; \ + this_cpu_inc(ptr[basefield##PKTS]); \ + this_cpu_add(ptr[basefield##OCTETS], addend); \ } while (0) #define SNMP_UPD_PO_STATS_BH(mib, basefield, addend) \ do { \ - __this_cpu_inc(mib[0]->mibs[basefield##PKTS]); \ - __this_cpu_add(mib[0]->mibs[basefield##OCTETS], addend); \ + __typeof__(*mib[0]->mibs) *ptr = mib[0]->mibs; \ + __this_cpu_inc(ptr[basefield##PKTS]); \ + __this_cpu_add(ptr[basefield##OCTETS], addend); \ } while (0) -- cgit v1.2.3 From 14a196807482e6fc74f15fc03176d5c08880588f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 4 Aug 2012 20:33:59 +0000 Subject: net: reorganize IP MIB values Reduce IP latencies by placing hot MIB IP fields in a single cache line. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/snmp.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/snmp.h b/include/linux/snmp.h index 00bc189cb395..ad6e3a6bf9fb 100644 --- a/include/linux/snmp.h +++ b/include/linux/snmp.h @@ -18,7 +18,14 @@ enum { IPSTATS_MIB_NUM = 0, +/* frequently written fields in fast path, kept in same cache line */ IPSTATS_MIB_INPKTS, /* InReceives */ + IPSTATS_MIB_INOCTETS, /* InOctets */ + IPSTATS_MIB_INDELIVERS, /* InDelivers */ + IPSTATS_MIB_OUTFORWDATAGRAMS, /* OutForwDatagrams */ + IPSTATS_MIB_OUTPKTS, /* OutRequests */ + IPSTATS_MIB_OUTOCTETS, /* OutOctets */ +/* other fields */ IPSTATS_MIB_INHDRERRORS, /* InHdrErrors */ IPSTATS_MIB_INTOOBIGERRORS, /* InTooBigErrors */ IPSTATS_MIB_INNOROUTES, /* InNoRoutes */ @@ -26,9 +33,6 @@ enum IPSTATS_MIB_INUNKNOWNPROTOS, /* InUnknownProtos */ IPSTATS_MIB_INTRUNCATEDPKTS, /* InTruncatedPkts */ IPSTATS_MIB_INDISCARDS, /* InDiscards */ - IPSTATS_MIB_INDELIVERS, /* InDelivers */ - IPSTATS_MIB_OUTFORWDATAGRAMS, /* OutForwDatagrams */ - IPSTATS_MIB_OUTPKTS, /* OutRequests */ IPSTATS_MIB_OUTDISCARDS, /* OutDiscards */ IPSTATS_MIB_OUTNOROUTES, /* OutNoRoutes */ IPSTATS_MIB_REASMTIMEOUT, /* ReasmTimeout */ @@ -42,8 +46,6 @@ enum IPSTATS_MIB_OUTMCASTPKTS, /* OutMcastPkts */ IPSTATS_MIB_INBCASTPKTS, /* InBcastPkts */ IPSTATS_MIB_OUTBCASTPKTS, /* OutBcastPkts */ - IPSTATS_MIB_INOCTETS, /* InOctets */ - IPSTATS_MIB_OUTOCTETS, /* OutOctets */ IPSTATS_MIB_INMCASTOCTETS, /* InMcastOctets */ IPSTATS_MIB_OUTMCASTOCTETS, /* OutMcastOctets */ IPSTATS_MIB_INBCASTOCTETS, /* InBcastOctets */ -- cgit v1.2.3 From 035534ed3377d9def2c17717899fd64a111a785b Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Mon, 6 Aug 2012 18:02:29 +0200 Subject: canfd: remove redundant CAN FD flag The first idea of the CAN FD implementation started with a new struct canfd_frame to be used for both CAN FD frames and legacy CAN frames. The now mainlined implementation supports both CAN frame types simultaneously and distinguishes them only by their required sizes: CAN_MTU and CANFD_MTU. Only the struct canfd_frame contains a flags element which is needed for the additional CAN FD information. As CAN FD implicitly means that the 'Extened Data Length' mode is enabled the formerly defined CANFD_EDL bit became redundant and also confusing as an unset bit would be an error and would always need to be tested. This patch removes the obsolete CANFD_EDL bit and clarifies the documentation for the use of struct canfd_frame and the CAN FD relevant flags. Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- include/linux/can.h | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/can.h b/include/linux/can.h index 018055efc034..e52958d7c2d1 100644 --- a/include/linux/can.h +++ b/include/linux/can.h @@ -74,20 +74,21 @@ struct can_frame { /* * defined bits for canfd_frame.flags * - * As the default for CAN FD should be to support the high data rate in the - * payload section of the frame (HDR) and to support up to 64 byte in the - * data section (EDL) the bits are only set in the non-default case. - * Btw. as long as there's no real implementation for CAN FD network driver - * these bits are only preliminary. + * The use of struct canfd_frame implies the Extended Data Length (EDL) bit to + * be set in the CAN frame bitstream on the wire. The EDL bit switch turns + * the CAN controllers bitstream processor into the CAN FD mode which creates + * two new options within the CAN FD frame specification: * - * RX: NOHDR/NOEDL - info about received CAN FD frame - * ESI - bit from originating CAN controller - * TX: NOHDR/NOEDL - control per-frame settings if supported by CAN controller - * ESI - bit is set by local CAN controller + * Bit Rate Switch - to indicate a second bitrate is/was used for the payload + * Error State Indicator - represents the error state of the transmitting node + * + * As the CANFD_ESI bit is internally generated by the transmitting CAN + * controller only the CANFD_BRS bit is relevant for real CAN controllers when + * building a CAN FD frame for transmission. Setting the CANFD_ESI bit can make + * sense for virtual CAN interfaces to test applications with echoed frames. */ -#define CANFD_NOHDR 0x01 /* frame without high data rate */ -#define CANFD_NOEDL 0x02 /* frame without extended data length */ -#define CANFD_ESI 0x04 /* error state indicator */ +#define CANFD_BRS 0x01 /* bit rate switch (second bitrate for payload data) */ +#define CANFD_ESI 0x02 /* error state indicator of the transmitting node */ /** * struct canfd_frame - CAN flexible data rate frame structure -- cgit v1.2.3 From 817fea2df3c24b22f6123dc0106eb063b7132883 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 7 Aug 2012 11:48:33 -0600 Subject: vfio: Include vfio.h in installed headers Signed-off-by: Alex Williamson --- include/linux/Kbuild | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/Kbuild b/include/linux/Kbuild index d9a754474878..fa217607c582 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -391,6 +391,7 @@ header-y += v4l2-dv-timings.h header-y += v4l2-mediabus.h header-y += v4l2-subdev.h header-y += veth.h +header-y += vfio.h header-y += vhost.h header-y += videodev2.h header-y += virtio_9p.h -- cgit v1.2.3 From 0c00c50b41a9a6ee83eb16fb5c6c22e5115391b8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 24 Jul 2012 15:41:19 +0100 Subject: regmap: irq: Enable devices for runtime PM while handling interrupts Some devices need to have a runtime PM reference while handling interrupts to ensure that the register I/O is available. Support this with a flag in the chip. 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 6f8f0c1e550f..bb5e10305197 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include "internal.h" @@ -62,6 +63,13 @@ static void regmap_irq_sync_unlock(struct irq_data *data) int i, ret; u32 reg; + if (d->chip->runtime_pm) { + ret = pm_runtime_get_sync(map->dev); + if (ret < 0) + dev_err(map->dev, "IRQ sync failed to resume: %d\n", + ret); + } + /* * If there's been a change in the mask write it back to the * hardware. We rely on the use of the regmap core cache to @@ -77,6 +85,9 @@ static void regmap_irq_sync_unlock(struct irq_data *data) reg); } + if (d->chip->runtime_pm) + pm_runtime_put(map->dev); + /* If we've changed our wakeup count propagate it to the parent */ if (d->wake_count < 0) for (i = d->wake_count; i < 0; i++) @@ -147,6 +158,15 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) bool handled = false; u32 reg; + if (chip->runtime_pm) { + ret = pm_runtime_get_sync(map->dev); + if (ret < 0) { + dev_err(map->dev, "IRQ thread failed to resume: %d\n", + ret); + return IRQ_NONE; + } + } + /* * Ignore masked IRQs and ack if we need to; we ack early so * there is no race between handling and acknowleding the @@ -162,6 +182,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) if (ret != 0) { dev_err(map->dev, "Failed to read IRQ status: %d\n", ret); + if (chip->runtime_pm) + pm_runtime_put(map->dev); return IRQ_NONE; } @@ -185,6 +207,9 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) } } + if (chip->runtime_pm) + pm_runtime_put(map->dev); + if (handled) return IRQ_HANDLED; else diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 7f7e00df3adf..9c4c9c673f38 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -285,6 +285,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. + * @runtime_pm: Hold a runtime PM lock on the device when accessing it. * * @num_regs: Number of registers in each control bank. * @irqs: Descriptors for individual IRQs. Interrupt numbers are @@ -299,6 +300,7 @@ struct regmap_irq_chip { unsigned int ack_base; unsigned int wake_base; unsigned int irq_reg_stride; + bool runtime_pm; int num_regs; -- cgit v1.2.3 From 425f09ab7d1c9da6ca4137dd639cb6fe3f8a88f3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 7 Aug 2012 02:19:56 +0000 Subject: net: output path optimizations 1) Avoid dirtying neighbour's confirmed field. TCP workloads hits this cache line for each incoming ACK. Lets write n->confirmed only if there is a jiffie change. 2) Optimize neigh_hh_output() for the common Ethernet case, were hh_len is less than 16 bytes. Replace the memcpy() call by two inlined 64bit load/stores on x86_64. Bench results using udpflood test, with -C option (MSG_CONFIRM flag added to sendto(), to reproduce the n->confirmed dirtying on UDP) 24 threads doing 1.000.000 UDP sendto() on dummy device, 4 runs. before : 2.247s, 2.235s, 2.247s, 2.318s after : 1.884s, 1.905s, 1.891s, 1.895s Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/dst.h | 10 +++++++--- include/net/neighbour.h | 14 +++++++++----- 2 files changed, 16 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index baf597890064..77f52f7dc823 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -396,11 +396,15 @@ static inline void dst_confirm(struct dst_entry *dst) static inline int dst_neigh_output(struct dst_entry *dst, struct neighbour *n, struct sk_buff *skb) { - struct hh_cache *hh; + const struct hh_cache *hh; + + if (dst->pending_confirm) { + unsigned long now = jiffies; - if (unlikely(dst->pending_confirm)) { - n->confirmed = jiffies; dst->pending_confirm = 0; + /* avoid dirtying neighbour */ + if (n->confirmed != now) + n->confirmed = now; } hh = &n->hh; diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 344d8988842a..0dab173e27da 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -334,18 +334,22 @@ static inline int neigh_hh_bridge(struct hh_cache *hh, struct sk_buff *skb) } #endif -static inline int neigh_hh_output(struct hh_cache *hh, struct sk_buff *skb) +static inline int neigh_hh_output(const struct hh_cache *hh, struct sk_buff *skb) { unsigned int seq; int hh_len; do { - int hh_alen; - seq = read_seqbegin(&hh->hh_lock); hh_len = hh->hh_len; - hh_alen = HH_DATA_ALIGN(hh_len); - memcpy(skb->data - hh_alen, hh->hh_data, hh_alen); + if (likely(hh_len <= HH_DATA_MOD)) { + /* this is inlined by gcc */ + memcpy(skb->data - HH_DATA_MOD, hh->hh_data, HH_DATA_MOD); + } else { + int hh_alen = HH_DATA_ALIGN(hh_len); + + memcpy(skb->data - hh_alen, hh->hh_data, hh_alen); + } } while (read_seqretry(&hh->hh_lock, seq)); skb_push(skb, hh_len); -- cgit v1.2.3 From 3f1965776f6ec769192400810b6260fe48301bbb Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 5 Aug 2012 23:05:20 +0800 Subject: regulator: add a new API regulator_set_voltage_tol() There are some use cases where a voltage range could be reasonably specified by a target voltage and tolerance. Add a new API regulator_set_voltage_tol() wrapping regulator_set_voltage() call to ease the users. Signed-off-by: Shawn Guo 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 da339fd8c755..21603524bdb2 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -352,4 +352,11 @@ static inline void regulator_set_drvdata(struct regulator *regulator, #endif +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); +} + #endif -- cgit v1.2.3 From 300d3739e873d50d4c6e3656f89007a217fb1d29 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 7 Aug 2012 13:50:22 +0200 Subject: Revert "NMI watchdog: fix for lockup detector breakage on resume" Revert commit 45226e9 (NMI watchdog: fix for lockup detector breakage on resume) which breaks resume from system suspend on my SH7372 Mackerel board (by causing a NULL pointer dereference to happen) and is generally wrong, because it abuses the CPU hotplug functionality in a shamelessly blatant way. The original issue should be addressed through appropriate syscore resume callback instead. Signed-off-by: Rafael J. Wysocki --- include/linux/sched.h | 8 -------- kernel/power/suspend.c | 3 --- kernel/watchdog.c | 21 ++------------------- 3 files changed, 2 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index c147e7024f11..b8c86648a2f9 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -334,14 +334,6 @@ static inline void lockup_detector_init(void) } #endif -#if defined(CONFIG_LOCKUP_DETECTOR) && defined(CONFIG_SUSPEND) -void lockup_detector_bootcpu_resume(void); -#else -static inline void lockup_detector_bootcpu_resume(void) -{ -} -#endif - #ifdef CONFIG_DETECT_HUNG_TASK extern unsigned int sysctl_hung_task_panic; extern unsigned long sysctl_hung_task_check_count; diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 1da39ea248fd..c8b7446b27df 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -178,9 +178,6 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) arch_suspend_enable_irqs(); BUG_ON(irqs_disabled()); - /* Kick the lockup detector */ - lockup_detector_bootcpu_resume(); - Enable_cpus: enable_nonboot_cpus(); diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 69add8a9da68..4b1dfba70f7c 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -575,7 +575,7 @@ out: /* * Create/destroy watchdog threads as CPUs come and go: */ -static int +static int __cpuinit cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { int hotcpu = (unsigned long)hcpu; @@ -610,27 +610,10 @@ cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) return NOTIFY_OK; } -static struct notifier_block cpu_nfb = { +static struct notifier_block __cpuinitdata cpu_nfb = { .notifier_call = cpu_callback }; -#ifdef CONFIG_SUSPEND -/* - * On exit from suspend we force an offline->online transition on the boot CPU - * so that the PMU state that was lost while in suspended state gets set up - * properly for the boot CPU. This information is required for restarting the - * NMI watchdog. - */ -void lockup_detector_bootcpu_resume(void) -{ - void *cpu = (void *)(long)smp_processor_id(); - - cpu_callback(&cpu_nfb, CPU_DEAD_FROZEN, cpu); - cpu_callback(&cpu_nfb, CPU_UP_PREPARE_FROZEN, cpu); - cpu_callback(&cpu_nfb, CPU_ONLINE_FROZEN, cpu); -} -#endif - void __init lockup_detector_init(void) { void *cpu = (void *)(long)smp_processor_id(); -- cgit v1.2.3 From a37e6e344910a43b9ebc2bbf29a029f5ea942598 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 7 Aug 2012 10:55:45 +0000 Subject: net: force dst_default_metrics to const section While investigating on network performance problems, I found this little gem : $ nm -v vmlinux | grep -1 dst_default_metrics ffffffff82736540 b busy.46605 ffffffff82736560 B dst_default_metrics ffffffff82736598 b dst_busy_list Apparently, declaring a const array without initializer put it in (writeable) bss section, in middle of possibly often dirtied cache lines. Since we really want dst_default_metrics be const to avoid any possible false sharing and catch any buggy writes, I force a null initializer. ffffffff818a4c20 R dst_default_metrics Signed-off-by: Eric Dumazet Cc: Ben Hutchings Signed-off-by: David S. Miller --- include/net/dst.h | 2 +- net/core/dst.c | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index baf597890064..621e3513ef5e 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -110,7 +110,7 @@ struct dst_entry { }; extern u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old); -extern const u32 dst_default_metrics[RTAX_MAX]; +extern const u32 dst_default_metrics[]; #define DST_METRICS_READ_ONLY 0x1UL #define __DST_METRICS_PTR(Y) \ diff --git a/net/core/dst.c b/net/core/dst.c index 069d51d29414..56d63612e1e4 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -149,7 +149,15 @@ int dst_discard(struct sk_buff *skb) } EXPORT_SYMBOL(dst_discard); -const u32 dst_default_metrics[RTAX_MAX]; +const u32 dst_default_metrics[RTAX_MAX + 1] = { + /* This initializer is needed to force linker to place this variable + * into const section. Otherwise it might end into bss section. + * We really want to avoid false sharing on this variable, and catch + * any writes on it. + */ + [RTAX_MAX] = 0xdeadbeef, +}; + void *dst_alloc(struct dst_ops *ops, struct net_device *dev, int initial_ref, int initial_obsolete, unsigned short flags) -- cgit v1.2.3 From 59ee93a528b94ef4e81a08db252b0326feff171f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sun, 5 Aug 2012 14:58:37 +0000 Subject: ARM: pxa: remove irq_to_gpio from ezx-pcap driver The irq_to_gpio function was removed from the pxa platform in linux-3.2, and this driver has been broken since. There is actually no in-tree user of this driver that adds this platform device, but the driver can and does get enabled on some platforms. Without this patch, building ezx_defconfig results in: drivers/mfd/ezx-pcap.c: In function 'pcap_isr_work': drivers/mfd/ezx-pcap.c:205:2: error: implicit declaration of function 'irq_to_gpio' [-Werror=implicit-function-declaration] Signed-off-by: Arnd Bergmann Acked-by: Haojian Zhuang Cc: stable@vger.kernel.org (v3.2+) Cc: Samuel Ortiz Cc: Daniel Ribeiro --- drivers/mfd/ezx-pcap.c | 2 +- include/linux/mfd/ezx-pcap.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/mfd/ezx-pcap.c b/drivers/mfd/ezx-pcap.c index 43a76c41cfcc..db662e2dcfa5 100644 --- a/drivers/mfd/ezx-pcap.c +++ b/drivers/mfd/ezx-pcap.c @@ -202,7 +202,7 @@ static void pcap_isr_work(struct work_struct *work) } local_irq_enable(); ezx_pcap_write(pcap, PCAP_REG_MSR, pcap->msr); - } while (gpio_get_value(irq_to_gpio(pcap->spi->irq))); + } while (gpio_get_value(pdata->gpio)); } static void pcap_irq_handler(unsigned int irq, struct irq_desc *desc) diff --git a/include/linux/mfd/ezx-pcap.h b/include/linux/mfd/ezx-pcap.h index 40c372165f3e..32a1b5cfeba1 100644 --- a/include/linux/mfd/ezx-pcap.h +++ b/include/linux/mfd/ezx-pcap.h @@ -16,6 +16,7 @@ struct pcap_subdev { struct pcap_platform_data { unsigned int irq_base; unsigned int config; + int gpio; void (*init) (void *); /* board specific init */ int num_subdevs; struct pcap_subdev *subdevs; -- cgit v1.2.3 From 4eef6cbfcc03b294d9d334368a851b35b496ce53 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 30 Apr 2012 16:21:37 +0000 Subject: Input: eeti_ts: pass gpio value instead of IRQ The EETI touchscreen asserts its IRQ line as soon as it has data in its internal buffers. The line is automatically deasserted once all data has been read via I2C. Hence, the driver has to monitor the GPIO line and cannot simply rely on the interrupt handler reception. In the current implementation of the driver, irq_to_gpio() is used to determine the GPIO number from the i2c_client's IRQ value. As irq_to_gpio() is not available on all platforms, this patch changes this and makes the driver ignore the passed in IRQ. Instead, a GPIO is added to the platform_data struct and gpio_to_irq is used to derive the IRQ from that GPIO. If this fails, bail out. The driver is only able to work in environments where the touchscreen GPIO can be mapped to an IRQ. Without this patch, building raumfeld_defconfig results in: drivers/input/touchscreen/eeti_ts.c: In function 'eeti_ts_irq_active': drivers/input/touchscreen/eeti_ts.c:65:2: error: implicit declaration of function 'irq_to_gpio' [-Werror=implicit-function-declaration] Signed-off-by: Daniel Mack Signed-off-by: Arnd Bergmann Cc: stable@vger.kernel.org (v3.2+) Cc: Dmitry Torokhov Cc: Sven Neumann Cc: linux-input@vger.kernel.org Cc: Haojian Zhuang --- arch/arm/mach-pxa/raumfeld.c | 2 +- drivers/input/touchscreen/eeti_ts.c | 21 +++++++++++++-------- include/linux/input/eeti_ts.h | 1 + 3 files changed, 15 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c index 5905ed130e94..d89d87ae144c 100644 --- a/arch/arm/mach-pxa/raumfeld.c +++ b/arch/arm/mach-pxa/raumfeld.c @@ -953,12 +953,12 @@ static struct i2c_board_info raumfeld_connector_i2c_board_info __initdata = { static struct eeti_ts_platform_data eeti_ts_pdata = { .irq_active_high = 1, + .irq_gpio = GPIO_TOUCH_IRQ, }; static struct i2c_board_info raumfeld_controller_i2c_board_info __initdata = { .type = "eeti_ts", .addr = 0x0a, - .irq = PXA_GPIO_TO_IRQ(GPIO_TOUCH_IRQ), .platform_data = &eeti_ts_pdata, }; diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c index 503c7096ed36..908407efc672 100644 --- a/drivers/input/touchscreen/eeti_ts.c +++ b/drivers/input/touchscreen/eeti_ts.c @@ -48,7 +48,7 @@ struct eeti_ts_priv { struct input_dev *input; struct work_struct work; struct mutex mutex; - int irq, irq_active_high; + int irq_gpio, irq, irq_active_high; }; #define EETI_TS_BITDEPTH (11) @@ -62,7 +62,7 @@ struct eeti_ts_priv { static inline int eeti_ts_irq_active(struct eeti_ts_priv *priv) { - return gpio_get_value(irq_to_gpio(priv->irq)) == priv->irq_active_high; + return gpio_get_value(priv->irq_gpio) == priv->irq_active_high; } static void eeti_ts_read(struct work_struct *work) @@ -157,7 +157,7 @@ static void eeti_ts_close(struct input_dev *dev) static int __devinit eeti_ts_probe(struct i2c_client *client, const struct i2c_device_id *idp) { - struct eeti_ts_platform_data *pdata; + struct eeti_ts_platform_data *pdata = client->dev.platform_data; struct eeti_ts_priv *priv; struct input_dev *input; unsigned int irq_flags; @@ -199,9 +199,12 @@ static int __devinit eeti_ts_probe(struct i2c_client *client, priv->client = client; priv->input = input; - priv->irq = client->irq; + priv->irq_gpio = pdata->irq_gpio; + priv->irq = gpio_to_irq(pdata->irq_gpio); - pdata = client->dev.platform_data; + err = gpio_request_one(pdata->irq_gpio, GPIOF_IN, client->name); + if (err < 0) + goto err1; if (pdata) priv->irq_active_high = pdata->irq_active_high; @@ -215,13 +218,13 @@ static int __devinit eeti_ts_probe(struct i2c_client *client, err = input_register_device(input); if (err) - goto err1; + goto err2; err = request_irq(priv->irq, eeti_ts_isr, irq_flags, client->name, priv); if (err) { dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); - goto err2; + goto err3; } /* @@ -233,9 +236,11 @@ static int __devinit eeti_ts_probe(struct i2c_client *client, device_init_wakeup(&client->dev, 0); return 0; -err2: +err3: input_unregister_device(input); input = NULL; /* so we dont try to free it below */ +err2: + gpio_free(pdata->irq_gpio); err1: input_free_device(input); kfree(priv); diff --git a/include/linux/input/eeti_ts.h b/include/linux/input/eeti_ts.h index f875b316249d..16625d799b6f 100644 --- a/include/linux/input/eeti_ts.h +++ b/include/linux/input/eeti_ts.h @@ -2,6 +2,7 @@ #define LINUX_INPUT_EETI_TS_H struct eeti_ts_platform_data { + int irq_gpio; unsigned int irq_active_high; }; -- cgit v1.2.3 From 276f0f5d157bb4a816053f4f3a941dbcd4f76556 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Thu, 9 Aug 2012 15:20:23 +0200 Subject: block: disable discard request merge temporarily The SCSI discard request merge never worked, and looks no solution for in future, let's disable it temporarily. Signed-off-by: Shaohua Li Reviewed-by: Christoph Hellwig Signed-off-by: Jens Axboe --- include/linux/blkdev.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index dc632975d54f..4a2ab7c85393 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -601,7 +601,7 @@ static inline void blk_clear_rl_full(struct request_list *rl, bool sync) * it already be started by driver. */ #define RQ_NOMERGE_FLAGS \ - (REQ_NOMERGE | REQ_STARTED | REQ_SOFTBARRIER | REQ_FLUSH | REQ_FUA) + (REQ_NOMERGE | REQ_STARTED | REQ_SOFTBARRIER | REQ_FLUSH | REQ_FUA | REQ_DISCARD) #define rq_mergeable(rq) \ (!((rq)->cmd_flags & RQ_NOMERGE_FLAGS) && \ (((rq)->cmd_flags & REQ_DISCARD) || \ -- cgit v1.2.3 From a399a8053164ec8bcb06fed52be9941a26ecde11 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 8 Aug 2012 21:13:53 +0000 Subject: time: jiffies_delta_to_clock_t() helper to the rescue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Various /proc/net files sometimes report crazy timer values, expressed in clock_t units. This happens when an expired timer delta (expires - jiffies) is passed to jiffies_to_clock_t(). This function has an overflow in : return div_u64((u64)x * TICK_NSEC, NSEC_PER_SEC / USER_HZ); commit cbbc719fccdb8cb (time: Change jiffies_to_clock_t() argument type to unsigned long) only got around the problem. As we cant output negative values in /proc/net/tcp without breaking various tools, I suggest adding a jiffies_delta_to_clock_t() wrapper that caps the negative delta to a 0 value. Signed-off-by: Eric Dumazet Reported-by: Maciej Żenczykowski Cc: Thomas Gleixner Cc: Paul Gortmaker Cc: Andrew Morton Cc: hank Signed-off-by: David S. Miller --- include/linux/jiffies.h | 6 ++++++ net/bridge/br_fdb.c | 2 +- net/bridge/br_stp_timer.c | 2 +- net/core/rtnetlink.c | 2 +- net/ipv4/igmp.c | 7 +++++-- net/ipv4/tcp_ipv4.c | 13 +++++-------- net/ipv6/tcp_ipv6.c | 9 +++------ 7 files changed, 22 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/linux/jiffies.h b/include/linux/jiffies.h index 265e2c3cbd1c..aded9b1ac5ca 100644 --- a/include/linux/jiffies.h +++ b/include/linux/jiffies.h @@ -303,7 +303,13 @@ extern void jiffies_to_timespec(const unsigned long jiffies, extern unsigned long timeval_to_jiffies(const struct timeval *value); extern void jiffies_to_timeval(const unsigned long jiffies, struct timeval *value); + extern clock_t jiffies_to_clock_t(unsigned long x); +static inline clock_t jiffies_delta_to_clock_t(long delta) +{ + return jiffies_to_clock_t(max(0L, delta)); +} + extern unsigned long clock_t_to_jiffies(unsigned long x); extern u64 jiffies_64_to_clock_t(u64 x); extern u64 nsec_to_clock_t(u64 x); diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index d21f32383517..9ce430b4657c 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -312,7 +312,7 @@ int br_fdb_fillbuf(struct net_bridge *br, void *buf, fe->is_local = f->is_local; if (!f->is_static) - fe->ageing_timer_value = jiffies_to_clock_t(jiffies - f->updated); + fe->ageing_timer_value = jiffies_delta_to_clock_t(jiffies - f->updated); ++fe; ++num; } diff --git a/net/bridge/br_stp_timer.c b/net/bridge/br_stp_timer.c index a6747e673426..c3530a81a33b 100644 --- a/net/bridge/br_stp_timer.c +++ b/net/bridge/br_stp_timer.c @@ -170,5 +170,5 @@ void br_stp_port_timer_init(struct net_bridge_port *p) unsigned long br_timer_value(const struct timer_list *timer) { return timer_pending(timer) - ? jiffies_to_clock_t(timer->expires - jiffies) : 0; + ? jiffies_delta_to_clock_t(timer->expires - jiffies) : 0; } diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 2c5a0a06c4ce..db037c9a4c48 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -618,7 +618,7 @@ int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, u32 id, long expires, u32 error) { struct rta_cacheinfo ci = { - .rta_lastuse = jiffies_to_clock_t(jiffies - dst->lastuse), + .rta_lastuse = jiffies_delta_to_clock_t(jiffies - dst->lastuse), .rta_used = dst->__use, .rta_clntref = atomic_read(&(dst->__refcnt)), .rta_error = error, diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 6699f23e6f55..0b5580c69f2d 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -2435,6 +2435,8 @@ static int igmp_mc_seq_show(struct seq_file *seq, void *v) struct ip_mc_list *im = (struct ip_mc_list *)v; struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); char *querier; + long delta; + #ifdef CONFIG_IP_MULTICAST querier = IGMP_V1_SEEN(state->in_dev) ? "V1" : IGMP_V2_SEEN(state->in_dev) ? "V2" : @@ -2448,11 +2450,12 @@ static int igmp_mc_seq_show(struct seq_file *seq, void *v) state->dev->ifindex, state->dev->name, state->in_dev->mc_count, querier); } + delta = im->timer.expires - jiffies; seq_printf(seq, "\t\t\t\t%08X %5d %d:%08lX\t\t%d\n", im->multiaddr, im->users, - im->tm_running, im->tm_running ? - jiffies_to_clock_t(im->timer.expires-jiffies) : 0, + im->tm_running, + im->tm_running ? jiffies_delta_to_clock_t(delta) : 0, im->reporter); } return 0; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 42b2a6a73092..c660d2c19a2b 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2385,7 +2385,7 @@ static void get_openreq4(const struct sock *sk, const struct request_sock *req, struct seq_file *f, int i, int uid, int *len) { const struct inet_request_sock *ireq = inet_rsk(req); - int ttd = req->expires - jiffies; + long delta = req->expires - jiffies; seq_printf(f, "%4d: %08X:%04X %08X:%04X" " %02X %08X:%08X %02X:%08lX %08X %5d %8d %u %d %pK%n", @@ -2397,7 +2397,7 @@ static void get_openreq4(const struct sock *sk, const struct request_sock *req, TCP_SYN_RECV, 0, 0, /* could print option size, but that is af dependent. */ 1, /* timers active (only the expire timer) */ - jiffies_to_clock_t(ttd), + jiffies_delta_to_clock_t(delta), req->retrans, uid, 0, /* non standard timer */ @@ -2448,7 +2448,7 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i, int *len) tp->write_seq - tp->snd_una, rx_queue, timer_active, - jiffies_to_clock_t(timer_expires - jiffies), + jiffies_delta_to_clock_t(timer_expires - jiffies), icsk->icsk_retransmits, sock_i_uid(sk), icsk->icsk_probes_out, @@ -2467,10 +2467,7 @@ static void get_timewait4_sock(const struct inet_timewait_sock *tw, { __be32 dest, src; __u16 destp, srcp; - int ttd = tw->tw_ttd - jiffies; - - if (ttd < 0) - ttd = 0; + long delta = tw->tw_ttd - jiffies; dest = tw->tw_daddr; src = tw->tw_rcv_saddr; @@ -2480,7 +2477,7 @@ static void get_timewait4_sock(const struct inet_timewait_sock *tw, seq_printf(f, "%4d: %08X:%04X %08X:%04X" " %02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %pK%n", i, src, srcp, dest, destp, tw->tw_substate, 0, 0, - 3, jiffies_to_clock_t(ttd), 0, 0, 0, 0, + 3, jiffies_delta_to_clock_t(delta), 0, 0, 0, 0, atomic_read(&tw->tw_refcnt), tw, len); } diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index c66b90f71c9b..aa41b0e6b163 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1875,7 +1875,7 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i) tp->write_seq-tp->snd_una, (sp->sk_state == TCP_LISTEN) ? sp->sk_ack_backlog : (tp->rcv_nxt - tp->copied_seq), timer_active, - jiffies_to_clock_t(timer_expires - jiffies), + jiffies_delta_to_clock_t(timer_expires - jiffies), icsk->icsk_retransmits, sock_i_uid(sp), icsk->icsk_probes_out, @@ -1895,10 +1895,7 @@ static void get_timewait6_sock(struct seq_file *seq, const struct in6_addr *dest, *src; __u16 destp, srcp; const struct inet6_timewait_sock *tw6 = inet6_twsk((struct sock *)tw); - int ttd = tw->tw_ttd - jiffies; - - if (ttd < 0) - ttd = 0; + long delta = tw->tw_ttd - jiffies; dest = &tw6->tw_v6_daddr; src = &tw6->tw_v6_rcv_saddr; @@ -1914,7 +1911,7 @@ static void get_timewait6_sock(struct seq_file *seq, dest->s6_addr32[0], dest->s6_addr32[1], dest->s6_addr32[2], dest->s6_addr32[3], destp, tw->tw_substate, 0, 0, - 3, jiffies_to_clock_t(ttd), 0, 0, 0, 0, + 3, jiffies_delta_to_clock_t(delta), 0, 0, 0, 0, atomic_read(&tw->tw_refcnt), tw); } -- cgit v1.2.3 From b14f243a42c7aa43de71f878641acd003f223022 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Wed, 8 Aug 2012 21:52:28 +0000 Subject: net: Dont use ifindices in hash fns Eric noticed, that when there will be devices with equal indices, some hash functions that use them will become less effective as they could. Fix this in advance by mixing the net_device address into the hash value instead of the device index. This is true for arp and ndisc hash fns. The netlabel, can and llc ones are also ifindex-based, but that three are init_net-only, thus will not be affected. Many thanks to David and Eric for the hash32_ptr implementation! Signed-off-by: Pavel Emelyanov Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/hash.h | 10 ++++++++++ include/net/arp.h | 3 ++- include/net/ndisc.h | 3 ++- 3 files changed, 14 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/hash.h b/include/linux/hash.h index b80506bdd733..24df9e70406f 100644 --- a/include/linux/hash.h +++ b/include/linux/hash.h @@ -67,4 +67,14 @@ static inline unsigned long hash_ptr(const void *ptr, unsigned int bits) { return hash_long((unsigned long)ptr, bits); } + +static inline u32 hash32_ptr(const void *ptr) +{ + unsigned long val = (unsigned long)ptr; + +#if BITS_PER_LONG == 64 + val ^= (val >> 32); +#endif + return (u32)val; +} #endif /* _LINUX_HASH_H */ diff --git a/include/net/arp.h b/include/net/arp.h index 7f7df93f37cd..b630dae03411 100644 --- a/include/net/arp.h +++ b/include/net/arp.h @@ -3,6 +3,7 @@ #define _ARP_H #include +#include #include @@ -10,7 +11,7 @@ extern struct neigh_table arp_tbl; static inline u32 arp_hashfn(u32 key, const struct net_device *dev, u32 hash_rnd) { - u32 val = key ^ dev->ifindex; + u32 val = key ^ hash32_ptr(dev); return val * hash_rnd; } diff --git a/include/net/ndisc.h b/include/net/ndisc.h index 96a3b5c03e37..980d263765cf 100644 --- a/include/net/ndisc.h +++ b/include/net/ndisc.h @@ -49,6 +49,7 @@ enum { #include #include #include +#include #include @@ -134,7 +135,7 @@ static inline u32 ndisc_hashfn(const void *pkey, const struct net_device *dev, _ { const u32 *p32 = pkey; - return (((p32[0] ^ dev->ifindex) * hash_rnd[0]) + + return (((p32[0] ^ hash32_ptr(dev)) * hash_rnd[0]) + (p32[1] * hash_rnd[1]) + (p32[2] * hash_rnd[2]) + (p32[3] * hash_rnd[3])); -- cgit v1.2.3 From aa79e66eee5d525e2fcbd2a5fcb87ae3dd4aa9e9 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Wed, 8 Aug 2012 21:53:19 +0000 Subject: net: Make ifindex generation per-net namespace Strictly speaking this is only _really_ required for checkpoint-restore to make loopback device always have the same index. This change appears to be safe wrt "ifindex should be unique per-system" concept, as all the ifindex usage is either already made per net namespace of is explicitly limited with init_net only. There are two cool side effects of this. The first one -- ifindices of devices in container are always small, regardless of how many containers we've started (and re-started) so far. The second one is -- we can speed up the loopback ifidex access as shown in the next patch. v2: Place ifindex right after dev_base_seq : avoid two holes and use the same cache line, dirtied in list_netdevice()/unlist_netdevice() Signed-off-by: Pavel Emelyanov Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/net_namespace.h | 1 + net/core/dev.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index ae1cd6c9ba52..6dc3db3466bf 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -66,6 +66,7 @@ struct net { struct hlist_head *dev_name_head; struct hlist_head *dev_index_head; unsigned int dev_base_seq; /* protected by rtnl_mutex */ + int ifindex; /* core fib_rules */ struct list_head rules_ops; diff --git a/net/core/dev.c b/net/core/dev.c index 3ca300d85271..1f06df8d10a3 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5221,12 +5221,12 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg) */ static int dev_new_index(struct net *net) { - static int ifindex; + int ifindex = net->ifindex; for (;;) { if (++ifindex <= 0) ifindex = 1; if (!__dev_get_by_index(net, ifindex)) - return ifindex; + return net->ifindex = ifindex; } } -- cgit v1.2.3 From 1fb9489bf190ce2b3fc03891f3de4b2d30600e28 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Wed, 8 Aug 2012 21:53:36 +0000 Subject: net: Loopback ifindex is constant now As pointed out, there are places, that access net->loopback_dev->ifindex and after ifindex generation is made per-net this value becomes constant equals 1. So go ahead and introduce the LOOPBACK_IFINDEX constant and use it where appropriate. Signed-off-by: Pavel Emelyanov Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/loopback.c | 1 + include/net/net_namespace.h | 7 +++++++ net/decnet/dn_route.c | 6 +++--- net/ipv4/fib_frontend.c | 2 +- net/ipv4/ipmr.c | 2 +- net/ipv4/netfilter/ipt_rpfilter.c | 2 +- net/ipv4/route.c | 6 +++--- net/ipv6/route.c | 2 +- 8 files changed, 18 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index e2a06fd996d5..4a075babe193 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -197,6 +197,7 @@ static __net_init int loopback_net_init(struct net *net) if (err) goto out_free_netdev; + BUG_ON(dev->ifindex != LOOPBACK_IFINDEX); net->loopback_dev = dev; return 0; diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 6dc3db3466bf..97e441945c13 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -105,6 +105,13 @@ struct net { struct sock *diag_nlsk; }; +/* + * ifindex generation is per-net namespace, and loopback is + * always the 1st device in ns (see net_dev_init), thus any + * loopback device should get ifindex 1 + */ + +#define LOOPBACK_IFINDEX 1 #include diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 85a3604c87c8..c855e8d0738f 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -961,7 +961,7 @@ static int dn_route_output_slow(struct dst_entry **pprt, const struct flowidn *o .saddr = oldflp->saddr, .flowidn_scope = RT_SCOPE_UNIVERSE, .flowidn_mark = oldflp->flowidn_mark, - .flowidn_iif = init_net.loopback_dev->ifindex, + .flowidn_iif = LOOPBACK_IFINDEX, .flowidn_oif = oldflp->flowidn_oif, }; struct dn_route *rt = NULL; @@ -979,7 +979,7 @@ static int dn_route_output_slow(struct dst_entry **pprt, const struct flowidn *o "dn_route_output_slow: dst=%04x src=%04x mark=%d" " iif=%d oif=%d\n", le16_to_cpu(oldflp->daddr), le16_to_cpu(oldflp->saddr), - oldflp->flowidn_mark, init_net.loopback_dev->ifindex, + oldflp->flowidn_mark, LOOPBACK_IFINDEX, oldflp->flowidn_oif); /* If we have an output interface, verify its a DECnet device */ @@ -1042,7 +1042,7 @@ source_ok: if (!fld.daddr) goto out; } - fld.flowidn_oif = init_net.loopback_dev->ifindex; + fld.flowidn_oif = LOOPBACK_IFINDEX; res.type = RTN_LOCAL; goto make_route; } diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index c43ae3fba792..7f073a38c87d 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -218,7 +218,7 @@ __be32 fib_compute_spec_dst(struct sk_buff *skb) scope = RT_SCOPE_UNIVERSE; if (!ipv4_is_zeronet(ip_hdr(skb)->saddr)) { fl4.flowi4_oif = 0; - fl4.flowi4_iif = net->loopback_dev->ifindex; + fl4.flowi4_iif = LOOPBACK_IFINDEX; fl4.daddr = ip_hdr(skb)->saddr; fl4.saddr = 0; fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos); diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 8eec8f4a0536..3a57570c8ee5 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -1798,7 +1798,7 @@ static struct mr_table *ipmr_rt_fib_lookup(struct net *net, struct sk_buff *skb) .flowi4_oif = (rt_is_output_route(rt) ? skb->dev->ifindex : 0), .flowi4_iif = (rt_is_output_route(rt) ? - net->loopback_dev->ifindex : + LOOPBACK_IFINDEX : skb->dev->ifindex), .flowi4_mark = skb->mark, }; diff --git a/net/ipv4/netfilter/ipt_rpfilter.c b/net/ipv4/netfilter/ipt_rpfilter.c index 31371be8174b..c30130062cd6 100644 --- a/net/ipv4/netfilter/ipt_rpfilter.c +++ b/net/ipv4/netfilter/ipt_rpfilter.c @@ -85,7 +85,7 @@ static bool rpfilter_mt(const struct sk_buff *skb, struct xt_action_param *par) return ipv4_is_local_multicast(iph->daddr) ^ invert; flow.flowi4_iif = 0; } else { - flow.flowi4_iif = dev_net(par->in)->loopback_dev->ifindex; + flow.flowi4_iif = LOOPBACK_IFINDEX; } flow.daddr = iph->saddr; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 21ad369014c0..c58137391a3d 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1619,7 +1619,7 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr, if (res.type == RTN_LOCAL) { err = fib_validate_source(skb, saddr, daddr, tos, - net->loopback_dev->ifindex, + LOOPBACK_IFINDEX, dev, in_dev, &itag); if (err < 0) goto martian_source_keep_err; @@ -1895,7 +1895,7 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4) orig_oif = fl4->flowi4_oif; - fl4->flowi4_iif = net->loopback_dev->ifindex; + fl4->flowi4_iif = LOOPBACK_IFINDEX; fl4->flowi4_tos = tos & IPTOS_RT_MASK; fl4->flowi4_scope = ((tos & RTO_ONLINK) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE); @@ -1984,7 +1984,7 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4) if (!fl4->daddr) fl4->daddr = fl4->saddr = htonl(INADDR_LOOPBACK); dev_out = net->loopback_dev; - fl4->flowi4_oif = net->loopback_dev->ifindex; + fl4->flowi4_oif = LOOPBACK_IFINDEX; res.type = RTN_LOCAL; flags |= RTCF_LOCAL; goto make_route; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 8e80fd279100..0ddf2d132e7f 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -965,7 +965,7 @@ struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk, { int flags = 0; - fl6->flowi6_iif = net->loopback_dev->ifindex; + fl6->flowi6_iif = LOOPBACK_IFINDEX; if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr)) flags |= RT6_LOOKUP_F_IFACE; -- cgit v1.2.3 From be97fdb5fbcc828240c51769cd28cba609158703 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Thu, 12 Jul 2012 23:06:20 +0300 Subject: ipvs: generalize app registration in netns Get rid of the ftp_app pointer and allow applications to be registered without adding fields in the netns_ipvs structure. v2: fix coding style as suggested by Pablo Neira Ayuso Signed-off-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/ip_vs.h | 5 ++-- net/netfilter/ipvs/ip_vs_app.c | 58 ++++++++++++++++++++++++++++++------------ net/netfilter/ipvs/ip_vs_ftp.c | 21 ++++----------- 3 files changed, 49 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 95374d1696a1..4b8f18ff4789 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -808,8 +808,6 @@ struct netns_ipvs { struct list_head rs_table[IP_VS_RTAB_SIZE]; /* ip_vs_app */ struct list_head app_list; - /* ip_vs_ftp */ - struct ip_vs_app *ftp_app; /* ip_vs_proto */ #define IP_VS_PROTO_TAB_SIZE 32 /* must be power of 2 */ struct ip_vs_proto_data *proto_data_table[IP_VS_PROTO_TAB_SIZE]; @@ -1179,7 +1177,8 @@ extern void ip_vs_service_net_cleanup(struct net *net); * (from ip_vs_app.c) */ #define IP_VS_APP_MAX_PORTS 8 -extern int register_ip_vs_app(struct net *net, struct ip_vs_app *app); +extern struct ip_vs_app *register_ip_vs_app(struct net *net, + struct ip_vs_app *app); extern void unregister_ip_vs_app(struct net *net, struct ip_vs_app *app); extern int ip_vs_bind_app(struct ip_vs_conn *cp, struct ip_vs_protocol *pp); extern void ip_vs_unbind_app(struct ip_vs_conn *cp); diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c index 64f9e8f13207..9713e6e86d47 100644 --- a/net/netfilter/ipvs/ip_vs_app.c +++ b/net/netfilter/ipvs/ip_vs_app.c @@ -180,22 +180,38 @@ register_ip_vs_app_inc(struct net *net, struct ip_vs_app *app, __u16 proto, } -/* - * ip_vs_app registration routine - */ -int register_ip_vs_app(struct net *net, struct ip_vs_app *app) +/* Register application for netns */ +struct ip_vs_app *register_ip_vs_app(struct net *net, struct ip_vs_app *app) { struct netns_ipvs *ipvs = net_ipvs(net); - /* increase the module use count */ - ip_vs_use_count_inc(); + struct ip_vs_app *a; + int err = 0; + + if (!ipvs) + return ERR_PTR(-ENOENT); mutex_lock(&__ip_vs_app_mutex); - list_add(&app->a_list, &ipvs->app_list); + list_for_each_entry(a, &ipvs->app_list, a_list) { + if (!strcmp(app->name, a->name)) { + err = -EEXIST; + goto out_unlock; + } + } + a = kmemdup(app, sizeof(*app), GFP_KERNEL); + if (!a) { + err = -ENOMEM; + goto out_unlock; + } + INIT_LIST_HEAD(&a->incs_list); + list_add(&a->a_list, &ipvs->app_list); + /* increase the module use count */ + ip_vs_use_count_inc(); +out_unlock: mutex_unlock(&__ip_vs_app_mutex); - return 0; + return err ? ERR_PTR(err) : a; } @@ -205,20 +221,29 @@ int register_ip_vs_app(struct net *net, struct ip_vs_app *app) */ void unregister_ip_vs_app(struct net *net, struct ip_vs_app *app) { - struct ip_vs_app *inc, *nxt; + struct netns_ipvs *ipvs = net_ipvs(net); + struct ip_vs_app *a, *anxt, *inc, *nxt; + + if (!ipvs) + return; mutex_lock(&__ip_vs_app_mutex); - list_for_each_entry_safe(inc, nxt, &app->incs_list, a_list) { - ip_vs_app_inc_release(net, inc); - } + list_for_each_entry_safe(a, anxt, &ipvs->app_list, a_list) { + if (app && strcmp(app->name, a->name)) + continue; + list_for_each_entry_safe(inc, nxt, &a->incs_list, a_list) { + ip_vs_app_inc_release(net, inc); + } - list_del(&app->a_list); + list_del(&a->a_list); + kfree(a); - mutex_unlock(&__ip_vs_app_mutex); + /* decrease the module use count */ + ip_vs_use_count_dec(); + } - /* decrease the module use count */ - ip_vs_use_count_dec(); + mutex_unlock(&__ip_vs_app_mutex); } @@ -586,5 +611,6 @@ int __net_init ip_vs_app_net_init(struct net *net) void __net_exit ip_vs_app_net_cleanup(struct net *net) { + unregister_ip_vs_app(net, NULL /* all */); proc_net_remove(net, "ip_vs_app"); } diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c index b20b29c903ef..ad70b7e4ac4a 100644 --- a/net/netfilter/ipvs/ip_vs_ftp.c +++ b/net/netfilter/ipvs/ip_vs_ftp.c @@ -441,16 +441,10 @@ static int __net_init __ip_vs_ftp_init(struct net *net) if (!ipvs) return -ENOENT; - app = kmemdup(&ip_vs_ftp, sizeof(struct ip_vs_app), GFP_KERNEL); - if (!app) - return -ENOMEM; - INIT_LIST_HEAD(&app->a_list); - INIT_LIST_HEAD(&app->incs_list); - ipvs->ftp_app = app; - ret = register_ip_vs_app(net, app); - if (ret) - goto err_exit; + app = register_ip_vs_app(net, &ip_vs_ftp); + if (IS_ERR(app)) + return PTR_ERR(app); for (i = 0; i < ports_count; i++) { if (!ports[i]) @@ -464,9 +458,7 @@ static int __net_init __ip_vs_ftp_init(struct net *net) return 0; err_unreg: - unregister_ip_vs_app(net, app); -err_exit: - kfree(ipvs->ftp_app); + unregister_ip_vs_app(net, &ip_vs_ftp); return ret; } /* @@ -474,10 +466,7 @@ err_exit: */ static void __ip_vs_ftp_exit(struct net *net) { - struct netns_ipvs *ipvs = net_ipvs(net); - - unregister_ip_vs_app(net, ipvs->ftp_app); - kfree(ipvs->ftp_app); + unregister_ip_vs_app(net, &ip_vs_ftp); } static struct pernet_operations ip_vs_ftp_ops = { -- cgit v1.2.3 From 3654e61137db891f5312e6dd813b961484b5fdf3 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Fri, 20 Jul 2012 11:59:53 +0300 Subject: ipvs: add pmtu_disc option to disable IP DF for TUN packets Disabling PMTU discovery can increase the output packet rate but some users have enough resources and prefer to fragment than to drop traffic. By default, we copy the DF bit but if pmtu_disc is disabled we do not send FRAG_NEEDED messages anymore. Signed-off-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/ip_vs.h | 11 +++++++++++ net/netfilter/ipvs/ip_vs_ctl.c | 8 ++++++++ net/netfilter/ipvs/ip_vs_xmit.c | 6 +++--- 3 files changed, 22 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 4b8f18ff4789..ee75ccdf5188 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -888,6 +888,7 @@ struct netns_ipvs { unsigned int sysctl_sync_refresh_period; int sysctl_sync_retries; int sysctl_nat_icmp_send; + int sysctl_pmtu_disc; /* ip_vs_lblc */ int sysctl_lblc_expiration; @@ -974,6 +975,11 @@ static inline int sysctl_sync_sock_size(struct netns_ipvs *ipvs) return ipvs->sysctl_sync_sock_size; } +static inline int sysctl_pmtu_disc(struct netns_ipvs *ipvs) +{ + return ipvs->sysctl_pmtu_disc; +} + #else static inline int sysctl_sync_threshold(struct netns_ipvs *ipvs) @@ -1016,6 +1022,11 @@ static inline int sysctl_sync_sock_size(struct netns_ipvs *ipvs) return 0; } +static inline int sysctl_pmtu_disc(struct netns_ipvs *ipvs) +{ + return 1; +} + #endif /* diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index d6d5ccabe067..03d3fc6d9d64 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -1801,6 +1801,12 @@ static struct ctl_table vs_vars[] = { .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "pmtu_disc", + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, #ifdef CONFIG_IP_VS_DEBUG { .procname = "debug_level", @@ -3726,6 +3732,8 @@ static int __net_init ip_vs_control_net_init_sysctl(struct net *net) ipvs->sysctl_sync_retries = clamp_t(int, DEFAULT_SYNC_RETRIES, 0, 3); tbl[idx++].data = &ipvs->sysctl_sync_retries; tbl[idx++].data = &ipvs->sysctl_nat_icmp_send; + ipvs->sysctl_pmtu_disc = 1; + tbl[idx++].data = &ipvs->sysctl_pmtu_disc; ipvs->sysctl_hdr = register_net_sysctl(net, "net/ipv4/vs", tbl); diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index c2275ba048e9..543a554008af 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -795,6 +795,7 @@ int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp) { + struct netns_ipvs *ipvs = net_ipvs(skb_net(skb)); struct rtable *rt; /* Route to the other host */ __be32 saddr; /* Source for tunnel */ struct net_device *tdev; /* Device to other host */ @@ -830,10 +831,9 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu); /* Copy DF, reset fragment offset and MF */ - df = old_iph->frag_off & htons(IP_DF); + df = sysctl_pmtu_disc(ipvs) ? old_iph->frag_off & htons(IP_DF) : 0; - if ((old_iph->frag_off & htons(IP_DF) && - mtu < ntohs(old_iph->tot_len) && !skb_is_gso(skb))) { + if (df && mtu < ntohs(old_iph->tot_len) && !skb_is_gso(skb)) { icmp_send(skb, ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED, htonl(mtu)); IP_VS_DBG_RL("%s(): frag needed\n", __func__); goto tx_error_put; -- cgit v1.2.3 From 63d02d157ec4124990258d66517b6c11fd6df0cf Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 9 Aug 2012 14:11:00 +0000 Subject: net: tcp: ipv6_mapped needs sk_rx_dst_set method commit 5d299f3d3c8a2fb (net: ipv6: fix TCP early demux) added a regression for ipv6_mapped case. [ 67.422369] SELinux: initialized (dev autofs, type autofs), uses genfs_contexts [ 67.449678] SELinux: initialized (dev autofs, type autofs), uses genfs_contexts [ 92.631060] BUG: unable to handle kernel NULL pointer dereference at (null) [ 92.631435] IP: [< (null)>] (null) [ 92.631645] PGD 0 [ 92.631846] Oops: 0010 [#1] SMP [ 92.632095] Modules linked in: autofs4 sunrpc ipv6 dm_mirror dm_region_hash dm_log dm_multipath dm_mod video sbs sbshc battery ac lp parport sg snd_hda_intel snd_hda_codec snd_seq_oss snd_seq_midi_event snd_seq snd_seq_device pcspkr snd_pcm_oss snd_mixer_oss snd_pcm snd_timer serio_raw button floppy snd i2c_i801 i2c_core soundcore snd_page_alloc shpchp ide_cd_mod cdrom microcode ehci_hcd ohci_hcd uhci_hcd [ 92.634294] CPU 0 [ 92.634294] Pid: 4469, comm: sendmail Not tainted 3.6.0-rc1 #3 [ 92.634294] RIP: 0010:[<0000000000000000>] [< (null)>] (null) [ 92.634294] RSP: 0018:ffff880245fc7cb0 EFLAGS: 00010282 [ 92.634294] RAX: ffffffffa01985f0 RBX: ffff88024827ad00 RCX: 0000000000000000 [ 92.634294] RDX: 0000000000000218 RSI: ffff880254735380 RDI: ffff88024827ad00 [ 92.634294] RBP: ffff880245fc7cc8 R08: 0000000000000001 R09: 0000000000000000 [ 92.634294] R10: 0000000000000000 R11: ffff880245fc7bf8 R12: ffff880254735380 [ 92.634294] R13: ffff880254735380 R14: 0000000000000000 R15: 7fffffffffff0218 [ 92.634294] FS: 00007f4516ccd6f0(0000) GS:ffff880256600000(0000) knlGS:0000000000000000 [ 92.634294] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 92.634294] CR2: 0000000000000000 CR3: 0000000245ed1000 CR4: 00000000000007f0 [ 92.634294] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 92.634294] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 92.634294] Process sendmail (pid: 4469, threadinfo ffff880245fc6000, task ffff880254b8cac0) [ 92.634294] Stack: [ 92.634294] ffffffff813837a7 ffff88024827ad00 ffff880254b6b0e8 ffff880245fc7d68 [ 92.634294] ffffffff81385083 00000000001d2680 ffff8802547353a8 ffff880245fc7d18 [ 92.634294] ffffffff8105903a ffff88024827ad60 0000000000000002 00000000000000ff [ 92.634294] Call Trace: [ 92.634294] [] ? tcp_finish_connect+0x2c/0xfa [ 92.634294] [] tcp_rcv_state_process+0x2b6/0x9c6 [ 92.634294] [] ? sched_clock_cpu+0xc3/0xd1 [ 92.634294] [] ? local_clock+0x2b/0x3c [ 92.634294] [] tcp_v4_do_rcv+0x63a/0x670 [ 92.634294] [] release_sock+0x128/0x1bd [ 92.634294] [] __inet_stream_connect+0x1b1/0x352 [ 92.634294] [] ? lock_sock_nested+0x74/0x7f [ 92.634294] [] ? wake_up_bit+0x25/0x25 [ 92.634294] [] ? lock_sock_nested+0x74/0x7f [ 92.634294] [] ? inet_stream_connect+0x22/0x4b [ 92.634294] [] inet_stream_connect+0x33/0x4b [ 92.634294] [] sys_connect+0x78/0x9e [ 92.634294] [] ? sysret_check+0x1b/0x56 [ 92.634294] [] ? __audit_syscall_entry+0x195/0x1c8 [ 92.634294] [] ? trace_hardirqs_on_thunk+0x3a/0x3f [ 92.634294] [] system_call_fastpath+0x16/0x1b [ 92.634294] Code: Bad RIP value. [ 92.634294] RIP [< (null)>] (null) [ 92.634294] RSP [ 92.634294] CR2: 0000000000000000 [ 92.648982] ---[ end trace 24e2bed94314c8d9 ]--- [ 92.649146] Kernel panic - not syncing: Fatal exception in interrupt Fix this using inet_sk_rx_dst_set(), and export this function in case IPv6 is modular. Reported-by: Andrew Morton Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/tcp.h | 1 + net/ipv4/tcp_ipv4.c | 3 ++- net/ipv6/tcp_ipv6.c | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index e19124b84cd2..1f000ffe7075 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -464,6 +464,7 @@ extern int tcp_disconnect(struct sock *sk, int flags); void tcp_connect_init(struct sock *sk); void tcp_finish_connect(struct sock *sk, struct sk_buff *skb); int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size); +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]; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 272241f16fcb..767823764016 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1869,7 +1869,7 @@ static struct timewait_sock_ops tcp_timewait_sock_ops = { .twsk_destructor= tcp_twsk_destructor, }; -static void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) +void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); @@ -1877,6 +1877,7 @@ static void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) sk->sk_rx_dst = dst; inet_sk(sk)->rx_dst_ifindex = skb->skb_iif; } +EXPORT_SYMBOL(inet_sk_rx_dst_set); const struct inet_connection_sock_af_ops ipv4_specific = { .queue_xmit = ip_queue_xmit, diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 5a439e9a4c01..bb9ce2b2f377 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1777,6 +1777,7 @@ static const struct inet_connection_sock_af_ops ipv6_mapped = { .queue_xmit = ip_queue_xmit, .send_check = tcp_v4_send_check, .rebuild_header = inet_sk_rebuild_header, + .sk_rx_dst_set = inet_sk_rx_dst_set, .conn_request = tcp_v6_conn_request, .syn_recv_sock = tcp_v6_syn_recv_sock, .net_header_len = sizeof(struct iphdr), -- cgit v1.2.3 From 02b69cbdc2fb2e1bfbfd9ac0c246d7be1b08d3cd Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Thu, 9 Aug 2012 10:08:46 +0000 Subject: netfilter: nf_ct_sip: fix IPv6 address parsing Within SIP messages IPv6 addresses are enclosed in square brackets in most cases, with the exception of the "received=" header parameter. Currently the helper fails to parse enclosed addresses. This patch: - changes the SIP address parsing function to enforce square brackets when required, and accept them when not required but present, as recommended by RFC 5118. - adds a new SDP address parsing function that never accepts square brackets since SDP doesn't use them. With these changes, the SIP helper correctly parses all test messages from RFC 5118 (Session Initiation Protocol (SIP) Torture Test Messages for Internet Protocol Version 6 (IPv6)). Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nf_conntrack_sip.h | 2 +- net/ipv4/netfilter/nf_nat_sip.c | 4 +- net/netfilter/nf_conntrack_sip.c | 87 ++++++++++++++++++++++++------ 3 files changed, 73 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h index 0dfc8b7210a3..89f2a627f3f0 100644 --- a/include/linux/netfilter/nf_conntrack_sip.h +++ b/include/linux/netfilter/nf_conntrack_sip.h @@ -164,7 +164,7 @@ extern int ct_sip_parse_address_param(const struct nf_conn *ct, const char *dptr unsigned int dataoff, unsigned int datalen, const char *name, unsigned int *matchoff, unsigned int *matchlen, - union nf_inet_addr *addr); + union nf_inet_addr *addr, bool delim); extern int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr, unsigned int off, unsigned int datalen, const char *name, diff --git a/net/ipv4/netfilter/nf_nat_sip.c b/net/ipv4/netfilter/nf_nat_sip.c index ea4a23813d26..eef8f29e8bf8 100644 --- a/net/ipv4/netfilter/nf_nat_sip.c +++ b/net/ipv4/netfilter/nf_nat_sip.c @@ -173,7 +173,7 @@ static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int dataoff, * the reply. */ if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen, "maddr=", &poff, &plen, - &addr) > 0 && + &addr, true) > 0 && addr.ip == ct->tuplehash[dir].tuple.src.u3.ip && addr.ip != ct->tuplehash[!dir].tuple.dst.u3.ip) { buflen = sprintf(buffer, "%pI4", @@ -187,7 +187,7 @@ static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int dataoff, * from which the server received the request. */ if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen, "received=", &poff, &plen, - &addr) > 0 && + &addr, false) > 0 && addr.ip == ct->tuplehash[dir].tuple.dst.u3.ip && addr.ip != ct->tuplehash[!dir].tuple.src.u3.ip) { buflen = sprintf(buffer, "%pI4", diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 2fb666920cc9..5c0a112aeee6 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -183,12 +183,12 @@ static int media_len(const struct nf_conn *ct, const char *dptr, return len + digits_len(ct, dptr, limit, shift); } -static int parse_addr(const struct nf_conn *ct, const char *cp, - const char **endp, union nf_inet_addr *addr, - const char *limit) +static int sip_parse_addr(const struct nf_conn *ct, const char *cp, + const char **endp, union nf_inet_addr *addr, + const char *limit, bool delim) { const char *end; - int ret = 0; + int ret; if (!ct) return 0; @@ -197,16 +197,28 @@ static int parse_addr(const struct nf_conn *ct, const char *cp, switch (nf_ct_l3num(ct)) { case AF_INET: ret = in4_pton(cp, limit - cp, (u8 *)&addr->ip, -1, &end); + if (ret == 0) + return 0; break; case AF_INET6: + if (cp < limit && *cp == '[') + cp++; + else if (delim) + return 0; + ret = in6_pton(cp, limit - cp, (u8 *)&addr->ip6, -1, &end); + if (ret == 0) + return 0; + + if (end < limit && *end == ']') + end++; + else if (delim) + return 0; break; default: BUG(); } - if (ret == 0 || end == cp) - return 0; if (endp) *endp = end; return 1; @@ -219,7 +231,7 @@ static int epaddr_len(const struct nf_conn *ct, const char *dptr, union nf_inet_addr addr; const char *aux = dptr; - if (!parse_addr(ct, dptr, &dptr, &addr, limit)) { + if (!sip_parse_addr(ct, dptr, &dptr, &addr, limit, true)) { pr_debug("ip: %s parse failed.!\n", dptr); return 0; } @@ -296,7 +308,7 @@ int ct_sip_parse_request(const struct nf_conn *ct, return 0; dptr += shift; - if (!parse_addr(ct, dptr, &end, addr, limit)) + if (!sip_parse_addr(ct, dptr, &end, addr, limit, true)) return -1; if (end < limit && *end == ':') { end++; @@ -550,7 +562,7 @@ int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr, if (ret == 0) return ret; - if (!parse_addr(ct, dptr + *matchoff, &c, addr, limit)) + if (!sip_parse_addr(ct, dptr + *matchoff, &c, addr, limit, true)) return -1; if (*c == ':') { c++; @@ -599,7 +611,7 @@ int ct_sip_parse_address_param(const struct nf_conn *ct, const char *dptr, unsigned int dataoff, unsigned int datalen, const char *name, unsigned int *matchoff, unsigned int *matchlen, - union nf_inet_addr *addr) + union nf_inet_addr *addr, bool delim) { const char *limit = dptr + datalen; const char *start, *end; @@ -613,7 +625,7 @@ int ct_sip_parse_address_param(const struct nf_conn *ct, const char *dptr, return 0; start += strlen(name); - if (!parse_addr(ct, start, &end, addr, limit)) + if (!sip_parse_addr(ct, start, &end, addr, limit, delim)) return 0; *matchoff = start - dptr; *matchlen = end - start; @@ -675,6 +687,47 @@ static int ct_sip_parse_transport(struct nf_conn *ct, const char *dptr, return 1; } +static int sdp_parse_addr(const struct nf_conn *ct, const char *cp, + const char **endp, union nf_inet_addr *addr, + const char *limit) +{ + const char *end; + int ret; + + memset(addr, 0, sizeof(*addr)); + switch (nf_ct_l3num(ct)) { + case AF_INET: + ret = in4_pton(cp, limit - cp, (u8 *)&addr->ip, -1, &end); + break; + case AF_INET6: + ret = in6_pton(cp, limit - cp, (u8 *)&addr->ip6, -1, &end); + break; + default: + BUG(); + } + + if (ret == 0) + return 0; + if (endp) + *endp = end; + return 1; +} + +/* skip ip address. returns its length. */ +static int sdp_addr_len(const struct nf_conn *ct, const char *dptr, + const char *limit, int *shift) +{ + union nf_inet_addr addr; + const char *aux = dptr; + + if (!sdp_parse_addr(ct, dptr, &dptr, &addr, limit)) { + pr_debug("ip: %s parse failed.!\n", dptr); + return 0; + } + + return dptr - aux; +} + /* SDP header parsing: a SDP session description contains an ordered set of * headers, starting with a section containing general session parameters, * optionally followed by multiple media descriptions. @@ -686,10 +739,10 @@ static int ct_sip_parse_transport(struct nf_conn *ct, const char *dptr, */ static const struct sip_header ct_sdp_hdrs[] = { [SDP_HDR_VERSION] = SDP_HDR("v=", NULL, digits_len), - [SDP_HDR_OWNER_IP4] = SDP_HDR("o=", "IN IP4 ", epaddr_len), - [SDP_HDR_CONNECTION_IP4] = SDP_HDR("c=", "IN IP4 ", epaddr_len), - [SDP_HDR_OWNER_IP6] = SDP_HDR("o=", "IN IP6 ", epaddr_len), - [SDP_HDR_CONNECTION_IP6] = SDP_HDR("c=", "IN IP6 ", epaddr_len), + [SDP_HDR_OWNER_IP4] = SDP_HDR("o=", "IN IP4 ", sdp_addr_len), + [SDP_HDR_CONNECTION_IP4] = SDP_HDR("c=", "IN IP4 ", sdp_addr_len), + [SDP_HDR_OWNER_IP6] = SDP_HDR("o=", "IN IP6 ", sdp_addr_len), + [SDP_HDR_CONNECTION_IP6] = SDP_HDR("c=", "IN IP6 ", sdp_addr_len), [SDP_HDR_MEDIA] = SDP_HDR("m=", NULL, media_len), }; @@ -775,8 +828,8 @@ static int ct_sip_parse_sdp_addr(const struct nf_conn *ct, const char *dptr, if (ret <= 0) return ret; - if (!parse_addr(ct, dptr + *matchoff, NULL, addr, - dptr + *matchoff + *matchlen)) + if (!sdp_parse_addr(ct, dptr + *matchoff, NULL, addr, + dptr + *matchoff + *matchlen)) return -1; return 1; } -- cgit v1.2.3 From 9d8dad742ad1c74d7e7210ee05d0b44961d5ea16 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 9 Aug 2012 19:01:26 -0700 Subject: Yama: higher restrictions should block PTRACE_TRACEME The higher ptrace restriction levels should be blocking even PTRACE_TRACEME requests. The comments in the LSM documentation are misleading about when the checks happen (the parent does not go through security_ptrace_access_check() on a PTRACE_TRACEME call). Signed-off-by: Kees Cook Cc: stable@vger.kernel.org # 3.5.x and later Signed-off-by: James Morris --- Documentation/security/Yama.txt | 14 +++++++------- include/linux/security.h | 2 -- security/yama/yama_lsm.c | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/Documentation/security/Yama.txt b/Documentation/security/Yama.txt index e369de2d48cd..dd908cf64ecf 100644 --- a/Documentation/security/Yama.txt +++ b/Documentation/security/Yama.txt @@ -46,14 +46,13 @@ restrictions, it can call prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, ...) so that any otherwise allowed process (even those in external pid namespaces) may attach. -These restrictions do not change how ptrace via PTRACE_TRACEME operates. - -The sysctl settings are: +The sysctl settings (writable only with CAP_SYS_PTRACE) are: 0 - classic ptrace permissions: a process can PTRACE_ATTACH to any other process running under the same uid, as long as it is dumpable (i.e. did not transition uids, start privileged, or have called - prctl(PR_SET_DUMPABLE...) already). + prctl(PR_SET_DUMPABLE...) already). Similarly, PTRACE_TRACEME is + unchanged. 1 - restricted ptrace: a process must have a predefined relationship with the inferior it wants to call PTRACE_ATTACH on. By default, @@ -61,12 +60,13 @@ The sysctl settings are: classic criteria is also met. To change the relationship, an inferior can call prctl(PR_SET_PTRACER, debugger, ...) to declare an allowed debugger PID to call PTRACE_ATTACH on the inferior. + Using PTRACE_TRACEME is unchanged. 2 - admin-only attach: only processes with CAP_SYS_PTRACE may use ptrace - with PTRACE_ATTACH. + with PTRACE_ATTACH, or through children calling PTRACE_TRACEME. -3 - no attach: no processes may use ptrace with PTRACE_ATTACH. Once set, - this sysctl cannot be changed to a lower value. +3 - no attach: no processes may use ptrace with PTRACE_ATTACH nor via + PTRACE_TRACEME. Once set, this sysctl value cannot be changed. The original children-only logic was based on the restrictions in grsecurity. diff --git a/include/linux/security.h b/include/linux/security.h index 4e5a73cdbbef..3dea6a9d568f 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1242,8 +1242,6 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * Check that the @parent process has sufficient permission to trace the * current process before allowing the current process to present itself * to the @parent process for tracing. - * The parent process will still have to undergo the ptrace_access_check - * checks before it is allowed to trace this one. * @parent contains the task_struct structure for debugger process. * Return 0 if permission is granted. * @capget: diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index 83554ee8a587..d51b7c76c37d 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -290,10 +290,51 @@ static int yama_ptrace_access_check(struct task_struct *child, return rc; } +/** + * yama_ptrace_traceme - validate PTRACE_TRACEME calls + * @parent: task that will become the ptracer of the current task + * + * Returns 0 if following the ptrace is allowed, -ve on error. + */ +static int yama_ptrace_traceme(struct task_struct *parent) +{ + int rc; + + /* If standard caps disallows it, so does Yama. We should + * only tighten restrictions further. + */ + rc = cap_ptrace_traceme(parent); + if (rc) + return rc; + + /* Only disallow PTRACE_TRACEME on more aggressive settings. */ + switch (ptrace_scope) { + case YAMA_SCOPE_CAPABILITY: + if (!ns_capable(task_user_ns(parent), CAP_SYS_PTRACE)) + rc = -EPERM; + break; + case YAMA_SCOPE_NO_ATTACH: + rc = -EPERM; + break; + } + + if (rc) { + char name[sizeof(current->comm)]; + printk_ratelimited(KERN_NOTICE + "ptraceme of pid %d was attempted by: %s (pid %d)\n", + current->pid, + get_task_comm(name, parent), + parent->pid); + } + + return rc; +} + static struct security_operations yama_ops = { .name = "yama", .ptrace_access_check = yama_ptrace_access_check, + .ptrace_traceme = yama_ptrace_traceme, .task_prctl = yama_task_prctl, .task_free = yama_task_free, }; -- cgit v1.2.3 From c5e63197db519bae1c33e41ea0342a50f39e7a93 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 7 Aug 2012 15:20:36 +0200 Subject: perf: Unified API to record selective sets of arch registers This brings a new API to help the selective dump of registers on event sampling, and its implementation for x86 arch. Added HAVE_PERF_REGS config option to determine if the architecture provides perf registers ABI. The information about desired registers will be passed in u64 mask. It's up to the architecture to map the registers into the mask bits. For the x86 arch implementation, both 32 and 64 bit registers bits are defined within single enum to ensure 64 bit system can provide register dump for compat task if needed in the future. Original-patch-by: Frederic Weisbecker [ Added missing linux/errno.h include ] Signed-off-by: Jiri Olsa Cc: "Frank Ch. Eigler" Cc: Arun Sharma Cc: Benjamin Redelings Cc: Corey Ashford Cc: Cyrill Gorcunov Cc: Frank Ch. Eigler Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Masami Hiramatsu Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Robert Richter Cc: Stephane Eranian Cc: Tom Zanussi Cc: Ulrich Drepper Link: http://lkml.kernel.org/r/1344345647-11536-2-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- arch/Kconfig | 6 +++ arch/x86/Kconfig | 1 + arch/x86/include/asm/perf_regs.h | 33 +++++++++++++++ arch/x86/kernel/Makefile | 2 + arch/x86/kernel/perf_regs.c | 90 ++++++++++++++++++++++++++++++++++++++++ include/linux/perf_regs.h | 19 +++++++++ 6 files changed, 151 insertions(+) create mode 100644 arch/x86/include/asm/perf_regs.h create mode 100644 arch/x86/kernel/perf_regs.c create mode 100644 include/linux/perf_regs.h (limited to 'include') diff --git a/arch/Kconfig b/arch/Kconfig index 72f2fa189cc5..68d827b7ae82 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -222,6 +222,12 @@ config HAVE_PERF_EVENTS_NMI subsystem. Also has support for calculating CPU cycle events to determine how many clock cycles in a given period. +config HAVE_PERF_REGS + bool + help + Support selective register dumps for perf events. This includes + bit-mapping of each registers and a unique architecture id. + config HAVE_ARCH_JUMP_LABEL bool diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 8ec3a1aa4abd..3fab6ec9edc4 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -60,6 +60,7 @@ config X86 select HAVE_MIXED_BREAKPOINTS_REGS select PERF_EVENTS select HAVE_PERF_EVENTS_NMI + select HAVE_PERF_REGS select ANON_INODES select HAVE_ALIGNED_STRUCT_PAGE if SLUB && !M386 select HAVE_CMPXCHG_LOCAL if !M386 diff --git a/arch/x86/include/asm/perf_regs.h b/arch/x86/include/asm/perf_regs.h new file mode 100644 index 000000000000..3f2207bfd17b --- /dev/null +++ b/arch/x86/include/asm/perf_regs.h @@ -0,0 +1,33 @@ +#ifndef _ASM_X86_PERF_REGS_H +#define _ASM_X86_PERF_REGS_H + +enum perf_event_x86_regs { + PERF_REG_X86_AX, + PERF_REG_X86_BX, + PERF_REG_X86_CX, + PERF_REG_X86_DX, + PERF_REG_X86_SI, + PERF_REG_X86_DI, + PERF_REG_X86_BP, + PERF_REG_X86_SP, + PERF_REG_X86_IP, + PERF_REG_X86_FLAGS, + PERF_REG_X86_CS, + PERF_REG_X86_SS, + PERF_REG_X86_DS, + PERF_REG_X86_ES, + PERF_REG_X86_FS, + PERF_REG_X86_GS, + PERF_REG_X86_R8, + PERF_REG_X86_R9, + PERF_REG_X86_R10, + PERF_REG_X86_R11, + PERF_REG_X86_R12, + PERF_REG_X86_R13, + PERF_REG_X86_R14, + PERF_REG_X86_R15, + + PERF_REG_X86_32_MAX = PERF_REG_X86_GS + 1, + PERF_REG_X86_64_MAX = PERF_REG_X86_R15 + 1, +}; +#endif /* _ASM_X86_PERF_REGS_H */ diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 8215e5652d97..8d7a619718b5 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -100,6 +100,8 @@ obj-$(CONFIG_SWIOTLB) += pci-swiotlb.o obj-$(CONFIG_OF) += devicetree.o obj-$(CONFIG_UPROBES) += uprobes.o +obj-$(CONFIG_PERF_EVENTS) += perf_regs.o + ### # 64 bit specific files ifeq ($(CONFIG_X86_64),y) diff --git a/arch/x86/kernel/perf_regs.c b/arch/x86/kernel/perf_regs.c new file mode 100644 index 000000000000..3d6923528b1c --- /dev/null +++ b/arch/x86/kernel/perf_regs.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_X86_32 +#define PERF_REG_X86_MAX PERF_REG_X86_32_MAX +#else +#define PERF_REG_X86_MAX PERF_REG_X86_64_MAX +#endif + +#define PT_REGS_OFFSET(id, r) [id] = offsetof(struct pt_regs, r) + +static unsigned int pt_regs_offset[PERF_REG_X86_MAX] = { + PT_REGS_OFFSET(PERF_REG_X86_AX, ax), + PT_REGS_OFFSET(PERF_REG_X86_BX, bx), + PT_REGS_OFFSET(PERF_REG_X86_CX, cx), + PT_REGS_OFFSET(PERF_REG_X86_DX, dx), + PT_REGS_OFFSET(PERF_REG_X86_SI, si), + PT_REGS_OFFSET(PERF_REG_X86_DI, di), + PT_REGS_OFFSET(PERF_REG_X86_BP, bp), + PT_REGS_OFFSET(PERF_REG_X86_SP, sp), + PT_REGS_OFFSET(PERF_REG_X86_IP, ip), + PT_REGS_OFFSET(PERF_REG_X86_FLAGS, flags), + PT_REGS_OFFSET(PERF_REG_X86_CS, cs), + PT_REGS_OFFSET(PERF_REG_X86_SS, ss), +#ifdef CONFIG_X86_32 + PT_REGS_OFFSET(PERF_REG_X86_DS, ds), + PT_REGS_OFFSET(PERF_REG_X86_ES, es), + PT_REGS_OFFSET(PERF_REG_X86_FS, fs), + PT_REGS_OFFSET(PERF_REG_X86_GS, gs), +#else + /* + * The pt_regs struct does not store + * ds, es, fs, gs in 64 bit mode. + */ + (unsigned int) -1, + (unsigned int) -1, + (unsigned int) -1, + (unsigned int) -1, +#endif +#ifdef CONFIG_X86_64 + PT_REGS_OFFSET(PERF_REG_X86_R8, r8), + PT_REGS_OFFSET(PERF_REG_X86_R9, r9), + PT_REGS_OFFSET(PERF_REG_X86_R10, r10), + PT_REGS_OFFSET(PERF_REG_X86_R11, r11), + PT_REGS_OFFSET(PERF_REG_X86_R12, r12), + PT_REGS_OFFSET(PERF_REG_X86_R13, r13), + PT_REGS_OFFSET(PERF_REG_X86_R14, r14), + PT_REGS_OFFSET(PERF_REG_X86_R15, r15), +#endif +}; + +u64 perf_reg_value(struct pt_regs *regs, int idx) +{ + if (WARN_ON_ONCE(idx > ARRAY_SIZE(pt_regs_offset))) + return 0; + + return regs_get_register(regs, pt_regs_offset[idx]); +} + +#define REG_RESERVED (~((1ULL << PERF_REG_X86_MAX) - 1ULL)) + +#ifdef CONFIG_X86_32 +int perf_reg_validate(u64 mask) +{ + if (!mask || mask & REG_RESERVED) + return -EINVAL; + + return 0; +} +#else /* CONFIG_X86_64 */ +#define REG_NOSUPPORT ((1ULL << PERF_REG_X86_DS) | \ + (1ULL << PERF_REG_X86_ES) | \ + (1ULL << PERF_REG_X86_FS) | \ + (1ULL << PERF_REG_X86_GS)) + +int perf_reg_validate(u64 mask) +{ + if (!mask || mask & REG_RESERVED) + return -EINVAL; + + if (mask & REG_NOSUPPORT) + return -EINVAL; + + return 0; +} +#endif /* CONFIG_X86_32 */ diff --git a/include/linux/perf_regs.h b/include/linux/perf_regs.h new file mode 100644 index 000000000000..a2f1a98f7839 --- /dev/null +++ b/include/linux/perf_regs.h @@ -0,0 +1,19 @@ +#ifndef _LINUX_PERF_REGS_H +#define _LINUX_PERF_REGS_H + +#ifdef CONFIG_HAVE_PERF_REGS +#include +u64 perf_reg_value(struct pt_regs *regs, int idx); +int perf_reg_validate(u64 mask); +#else +static inline u64 perf_reg_value(struct pt_regs *regs, int idx) +{ + return 0; +} + +static inline int perf_reg_validate(u64 mask) +{ + return mask ? -ENOSYS : 0; +} +#endif /* CONFIG_HAVE_PERF_REGS */ +#endif /* _LINUX_PERF_REGS_H */ -- cgit v1.2.3 From 4018994f3d8785275ef0e7391b75c3462c029e56 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 7 Aug 2012 15:20:37 +0200 Subject: perf: Add ability to attach user level registers dump to sample Introducing PERF_SAMPLE_REGS_USER sample type bit to trigger the dump of user level registers on sample. Registers we want to dump are specified by sample_regs_user bitmask. Only user level registers are dumped at the moment. Meaning the register values of the user space context as it was before the user entered the kernel for whatever reason (syscall, irq, exception, or a PMI happening in userspace). The layout of the sample_regs_user bitmap is described in asm/perf_regs.h for archs that support register dump. This is going to be useful to bring Dwarf CFI based stack unwinding on top of samples. Original-patch-by: Frederic Weisbecker [ Dump registers ABI specification. ] Signed-off-by: Jiri Olsa Suggested-by: Stephane Eranian Cc: "Frank Ch. Eigler" Cc: Arun Sharma Cc: Benjamin Redelings Cc: Corey Ashford Cc: Cyrill Gorcunov Cc: Frank Ch. Eigler Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Masami Hiramatsu Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Robert Richter Cc: Stephane Eranian Cc: Tom Zanussi Cc: Ulrich Drepper Link: http://lkml.kernel.org/r/1344345647-11536-3-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- arch/x86/kernel/perf_regs.c | 15 +++++++++++ include/linux/perf_event.h | 35 +++++++++++++++++++++--- include/linux/perf_regs.h | 6 +++++ kernel/events/core.c | 66 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 119 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/perf_regs.c b/arch/x86/kernel/perf_regs.c index 3d6923528b1c..c5a3e5cfe07f 100644 --- a/arch/x86/kernel/perf_regs.c +++ b/arch/x86/kernel/perf_regs.c @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include #include @@ -71,6 +73,11 @@ int perf_reg_validate(u64 mask) return 0; } + +u64 perf_reg_abi(struct task_struct *task) +{ + return PERF_SAMPLE_REGS_ABI_32; +} #else /* CONFIG_X86_64 */ #define REG_NOSUPPORT ((1ULL << PERF_REG_X86_DS) | \ (1ULL << PERF_REG_X86_ES) | \ @@ -87,4 +94,12 @@ int perf_reg_validate(u64 mask) return 0; } + +u64 perf_reg_abi(struct task_struct *task) +{ + if (test_tsk_thread_flag(task, TIF_IA32)) + return PERF_SAMPLE_REGS_ABI_32; + else + return PERF_SAMPLE_REGS_ABI_64; +} #endif /* CONFIG_X86_32 */ diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 7602ccb3f40e..3d4d84745f07 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -130,8 +130,9 @@ enum perf_event_sample_format { PERF_SAMPLE_STREAM_ID = 1U << 9, PERF_SAMPLE_RAW = 1U << 10, PERF_SAMPLE_BRANCH_STACK = 1U << 11, + PERF_SAMPLE_REGS_USER = 1U << 12, - PERF_SAMPLE_MAX = 1U << 12, /* non-ABI */ + PERF_SAMPLE_MAX = 1U << 13, /* non-ABI */ }; /* @@ -162,6 +163,15 @@ enum perf_branch_sample_type { PERF_SAMPLE_BRANCH_KERNEL|\ PERF_SAMPLE_BRANCH_HV) +/* + * Values to determine ABI of the registers dump. + */ +enum perf_sample_regs_abi { + PERF_SAMPLE_REGS_ABI_NONE = 0, + PERF_SAMPLE_REGS_ABI_32 = 1, + PERF_SAMPLE_REGS_ABI_64 = 2, +}; + /* * The format of the data returned by read() on a perf event fd, * as specified by attr.read_format: @@ -194,6 +204,7 @@ enum perf_event_read_format { #define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */ #define PERF_ATTR_SIZE_VER1 72 /* add: config2 */ #define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */ +#define PERF_ATTR_SIZE_VER3 88 /* add: sample_regs_user */ /* * Hardware event_id to monitor via a performance monitoring event: @@ -271,7 +282,13 @@ struct perf_event_attr { __u64 bp_len; __u64 config2; /* extension of config1 */ }; - __u64 branch_sample_type; /* enum branch_sample_type */ + __u64 branch_sample_type; /* enum perf_branch_sample_type */ + + /* + * Defines set of user regs to dump on samples. + * See asm/perf_regs.h for details. + */ + __u64 sample_regs_user; }; /* @@ -548,6 +565,9 @@ enum perf_event_type { * char data[size];}&& PERF_SAMPLE_RAW * * { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK + * + * { u64 abi; # enum perf_sample_regs_abi + * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER * }; */ PERF_RECORD_SAMPLE = 9, @@ -609,6 +629,7 @@ struct perf_guest_info_callbacks { #include #include #include +#include #include struct perf_callchain_entry { @@ -654,6 +675,11 @@ struct perf_branch_stack { struct perf_branch_entry entries[0]; }; +struct perf_regs_user { + __u64 abi; + struct pt_regs *regs; +}; + struct task_struct; /* @@ -1133,6 +1159,7 @@ struct perf_sample_data { struct perf_callchain_entry *callchain; struct perf_raw_record *raw; struct perf_branch_stack *br_stack; + struct perf_regs_user regs_user; }; static inline void perf_sample_data_init(struct perf_sample_data *data, @@ -1142,7 +1169,9 @@ static inline void perf_sample_data_init(struct perf_sample_data *data, data->addr = addr; data->raw = NULL; data->br_stack = NULL; - data->period = period; + data->period = period; + data->regs_user.abi = PERF_SAMPLE_REGS_ABI_NONE; + data->regs_user.regs = NULL; } extern void perf_output_sample(struct perf_output_handle *handle, diff --git a/include/linux/perf_regs.h b/include/linux/perf_regs.h index a2f1a98f7839..3c73d5fe18be 100644 --- a/include/linux/perf_regs.h +++ b/include/linux/perf_regs.h @@ -5,6 +5,7 @@ #include u64 perf_reg_value(struct pt_regs *regs, int idx); int perf_reg_validate(u64 mask); +u64 perf_reg_abi(struct task_struct *task); #else static inline u64 perf_reg_value(struct pt_regs *regs, int idx) { @@ -15,5 +16,10 @@ static inline int perf_reg_validate(u64 mask) { return mask ? -ENOSYS : 0; } + +static inline u64 perf_reg_abi(struct task_struct *task) +{ + return PERF_SAMPLE_REGS_ABI_NONE; +} #endif /* CONFIG_HAVE_PERF_REGS */ #endif /* _LINUX_PERF_REGS_H */ diff --git a/kernel/events/core.c b/kernel/events/core.c index b7935fcec7d9..d3ce97525b9f 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -3756,6 +3756,37 @@ int perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *cbs) } EXPORT_SYMBOL_GPL(perf_unregister_guest_info_callbacks); +static void +perf_output_sample_regs(struct perf_output_handle *handle, + struct pt_regs *regs, u64 mask) +{ + int bit; + + for_each_set_bit(bit, (const unsigned long *) &mask, + sizeof(mask) * BITS_PER_BYTE) { + u64 val; + + val = perf_reg_value(regs, bit); + perf_output_put(handle, val); + } +} + +static void perf_sample_regs_user(struct perf_regs_user *regs_user, + struct pt_regs *regs) +{ + if (!user_mode(regs)) { + if (current->mm) + regs = task_pt_regs(current); + else + regs = NULL; + } + + if (regs) { + regs_user->regs = regs; + regs_user->abi = perf_reg_abi(current); + } +} + static void __perf_event_header__init_id(struct perf_event_header *header, struct perf_sample_data *data, struct perf_event *event) @@ -4016,6 +4047,23 @@ void perf_output_sample(struct perf_output_handle *handle, perf_output_put(handle, nr); } } + + if (sample_type & PERF_SAMPLE_REGS_USER) { + u64 abi = data->regs_user.abi; + + /* + * If there are no regs to dump, notice it through + * first u64 being zero (PERF_SAMPLE_REGS_ABI_NONE). + */ + perf_output_put(handle, abi); + + if (abi) { + u64 mask = event->attr.sample_regs_user; + perf_output_sample_regs(handle, + data->regs_user.regs, + mask); + } + } } void perf_prepare_sample(struct perf_event_header *header, @@ -4067,6 +4115,20 @@ void perf_prepare_sample(struct perf_event_header *header, } header->size += size; } + + if (sample_type & PERF_SAMPLE_REGS_USER) { + /* regs dump ABI info */ + int size = sizeof(u64); + + perf_sample_regs_user(&data->regs_user, regs); + + if (data->regs_user.regs) { + u64 mask = event->attr.sample_regs_user; + size += hweight64(mask) * sizeof(u64); + } + + header->size += size; + } } static void perf_event_output(struct perf_event *event, @@ -6142,6 +6204,10 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr, attr->branch_sample_type = mask; } } + + if (attr->sample_type & PERF_SAMPLE_REGS_USER) + ret = perf_reg_validate(attr->sample_regs_user); + out: return ret; -- cgit v1.2.3 From 91d7753a45f8525dc75b6be01e427dc1c378dc16 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 7 Aug 2012 15:20:38 +0200 Subject: perf: Factor __output_copy to be usable with specific copy function Adding a generic way to use __output_copy function with specific copy function via DEFINE_PERF_OUTPUT_COPY macro. Using this to add new __output_copy_user function, that provides output copy from user pointers. For x86 the copy_from_user_nmi function is used and __copy_from_user_inatomic for the rest of the architectures. This new function will be used in user stack dump on sample, coming in next patches. Signed-off-by: Jiri Olsa Cc: "Frank Ch. Eigler" Cc: Arun Sharma Cc: Benjamin Redelings Cc: Corey Ashford Cc: Cyrill Gorcunov Cc: Frank Ch. Eigler Cc: Ingo Molnar Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Robert Richter Cc: Stephane Eranian Cc: Tom Zanussi Cc: Ulrich Drepper Link: http://lkml.kernel.org/r/1344345647-11536-4-git-send-email-jolsa@redhat.com Signed-off-by: Frederic Weisbecker Signed-off-by: Arnaldo Carvalho de Melo --- arch/x86/include/asm/perf_event.h | 2 ++ include/linux/perf_event.h | 2 +- kernel/events/internal.h | 62 ++++++++++++++++++++++++++------------- kernel/events/ring_buffer.c | 4 +-- 4 files changed, 46 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h index cb4e43bce98a..4fabcdf1cfa7 100644 --- a/arch/x86/include/asm/perf_event.h +++ b/arch/x86/include/asm/perf_event.h @@ -262,4 +262,6 @@ static inline void perf_check_microcode(void) { } static inline void amd_pmu_disable_virt(void) { } #endif +#define arch_perf_out_copy_user copy_from_user_nmi + #endif /* _ASM_X86_PERF_EVENT_H */ diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 3d4d84745f07..d41394a1af36 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -1319,7 +1319,7 @@ static inline bool has_branch_stack(struct perf_event *event) extern int perf_output_begin(struct perf_output_handle *handle, struct perf_event *event, unsigned int size); extern void perf_output_end(struct perf_output_handle *handle); -extern void perf_output_copy(struct perf_output_handle *handle, +extern unsigned int perf_output_copy(struct perf_output_handle *handle, const void *buf, unsigned int len); extern int perf_swevent_get_recursion_context(void); extern void perf_swevent_put_recursion_context(int rctx); diff --git a/kernel/events/internal.h b/kernel/events/internal.h index a096c19f2c2a..7fd5408493d2 100644 --- a/kernel/events/internal.h +++ b/kernel/events/internal.h @@ -2,6 +2,7 @@ #define _KERNEL_EVENTS_INTERNAL_H #include +#include /* Buffer handling */ @@ -76,30 +77,49 @@ static inline unsigned long perf_data_size(struct ring_buffer *rb) return rb->nr_pages << (PAGE_SHIFT + page_order(rb)); } -static inline void -__output_copy(struct perf_output_handle *handle, - const void *buf, unsigned int len) +#define DEFINE_OUTPUT_COPY(func_name, memcpy_func) \ +static inline unsigned int \ +func_name(struct perf_output_handle *handle, \ + const void *buf, unsigned int len) \ +{ \ + unsigned long size, written; \ + \ + do { \ + size = min_t(unsigned long, handle->size, len); \ + \ + written = memcpy_func(handle->addr, buf, size); \ + \ + len -= written; \ + handle->addr += written; \ + buf += written; \ + handle->size -= written; \ + if (!handle->size) { \ + struct ring_buffer *rb = handle->rb; \ + \ + handle->page++; \ + handle->page &= rb->nr_pages - 1; \ + handle->addr = rb->data_pages[handle->page]; \ + handle->size = PAGE_SIZE << page_order(rb); \ + } \ + } while (len && written == size); \ + \ + return len; \ +} + +static inline int memcpy_common(void *dst, const void *src, size_t n) { - do { - unsigned long size = min_t(unsigned long, handle->size, len); - - memcpy(handle->addr, buf, size); - - len -= size; - handle->addr += size; - buf += size; - handle->size -= size; - if (!handle->size) { - struct ring_buffer *rb = handle->rb; - - handle->page++; - handle->page &= rb->nr_pages - 1; - handle->addr = rb->data_pages[handle->page]; - handle->size = PAGE_SIZE << page_order(rb); - } - } while (len); + memcpy(dst, src, n); + return n; } +DEFINE_OUTPUT_COPY(__output_copy, memcpy_common) + +#ifndef arch_perf_out_copy_user +#define arch_perf_out_copy_user __copy_from_user_inatomic +#endif + +DEFINE_OUTPUT_COPY(__output_copy_user, arch_perf_out_copy_user) + /* Callchain handling */ extern struct perf_callchain_entry * perf_callchain(struct perf_event *event, struct pt_regs *regs); diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index 6ddaba43fb7a..b4c2ad3dee7a 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -182,10 +182,10 @@ out: return -ENOSPC; } -void perf_output_copy(struct perf_output_handle *handle, +unsigned int perf_output_copy(struct perf_output_handle *handle, const void *buf, unsigned int len) { - __output_copy(handle, buf, len); + return __output_copy(handle, buf, len); } void perf_output_end(struct perf_output_handle *handle) -- cgit v1.2.3 From 5685e0ff45f5df67e79e9b052b6ffd501ff38c11 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 7 Aug 2012 15:20:39 +0200 Subject: perf: Add perf_output_skip function to skip bytes in sample Introducing perf_output_skip function to be able to skip data within the perf ring buffer. When writing data into perf ring buffer we first reserve needed place in ring buffer and then copy the actual data. There's a possibility we won't be able to fill all the reserved size with data, so we need a way to skip the remaining bytes. This is going to be useful when storing the user stack dump, where we might end up with less data than we originally requested. Signed-off-by: Jiri Olsa Acked-by: Frederic Weisbecker Cc: "Frank Ch. Eigler" Cc: Arun Sharma Cc: Benjamin Redelings Cc: Corey Ashford Cc: Cyrill Gorcunov Cc: Frank Ch. Eigler Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Masami Hiramatsu Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Robert Richter Cc: Stephane Eranian Cc: Tom Zanussi Cc: Ulrich Drepper Link: http://lkml.kernel.org/r/1344345647-11536-5-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- include/linux/perf_event.h | 2 ++ kernel/events/internal.h | 4 ++++ kernel/events/ring_buffer.c | 6 ++++++ 3 files changed, 12 insertions(+) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index d41394a1af36..8a73f75beb16 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -1321,6 +1321,8 @@ extern int perf_output_begin(struct perf_output_handle *handle, extern void perf_output_end(struct perf_output_handle *handle); extern unsigned int perf_output_copy(struct perf_output_handle *handle, const void *buf, unsigned int len); +extern unsigned int perf_output_skip(struct perf_output_handle *handle, + unsigned int len); extern int perf_swevent_get_recursion_context(void); extern void perf_swevent_put_recursion_context(int rctx); extern void perf_event_enable(struct perf_event *event); diff --git a/kernel/events/internal.h b/kernel/events/internal.h index 7fd5408493d2..ce7bdfc1d045 100644 --- a/kernel/events/internal.h +++ b/kernel/events/internal.h @@ -114,6 +114,10 @@ static inline int memcpy_common(void *dst, const void *src, size_t n) DEFINE_OUTPUT_COPY(__output_copy, memcpy_common) +#define MEMCPY_SKIP(dst, src, n) (n) + +DEFINE_OUTPUT_COPY(__output_skip, MEMCPY_SKIP) + #ifndef arch_perf_out_copy_user #define arch_perf_out_copy_user __copy_from_user_inatomic #endif diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index b4c2ad3dee7a..23cb34ff3973 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -188,6 +188,12 @@ unsigned int perf_output_copy(struct perf_output_handle *handle, return __output_copy(handle, buf, len); } +unsigned int perf_output_skip(struct perf_output_handle *handle, + unsigned int len) +{ + return __output_skip(handle, NULL, len); +} + void perf_output_end(struct perf_output_handle *handle) { perf_output_put_handle(handle); -- cgit v1.2.3 From c5ebcedb566ef17bda7b02686e0d658a7bb42ee7 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 7 Aug 2012 15:20:40 +0200 Subject: perf: Add ability to attach user stack dump to sample Introducing PERF_SAMPLE_STACK_USER sample type bit to trigger the dump of the user level stack on sample. The size of the dump is specified by sample_stack_user value. Being able to dump parts of the user stack, starting from the stack pointer, will be useful to make a post mortem dwarf CFI based stack unwinding. Added HAVE_PERF_USER_STACK_DUMP config option to determine if the architecture provides user stack dump on perf event samples. This needs access to the user stack pointer which is not unified across architectures. Enabling this for x86 architecture. Signed-off-by: Jiri Olsa Original-patch-by: Frederic Weisbecker Cc: "Frank Ch. Eigler" Cc: Arun Sharma Cc: Benjamin Redelings Cc: Corey Ashford Cc: Cyrill Gorcunov Cc: Frank Ch. Eigler Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Masami Hiramatsu Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Robert Richter Cc: Stephane Eranian Cc: Tom Zanussi Cc: Ulrich Drepper Link: http://lkml.kernel.org/r/1344345647-11536-6-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- arch/Kconfig | 7 +++ arch/x86/Kconfig | 1 + include/linux/perf_event.h | 18 +++++- kernel/events/core.c | 150 ++++++++++++++++++++++++++++++++++++++++++++- kernel/events/internal.h | 16 +++++ 5 files changed, 190 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/Kconfig b/arch/Kconfig index 68d827b7ae82..2a83a3f6a615 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -228,6 +228,13 @@ config HAVE_PERF_REGS Support selective register dumps for perf events. This includes bit-mapping of each registers and a unique architecture id. +config HAVE_PERF_USER_STACK_DUMP + bool + help + Support user stack dumps for perf event samples. This needs + access to the user stack pointer which is not unified across + architectures. + config HAVE_ARCH_JUMP_LABEL bool diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 3fab6ec9edc4..a2d19ee750ca 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -61,6 +61,7 @@ config X86 select PERF_EVENTS select HAVE_PERF_EVENTS_NMI select HAVE_PERF_REGS + select HAVE_PERF_USER_STACK_DUMP select ANON_INODES select HAVE_ALIGNED_STRUCT_PAGE if SLUB && !M386 select HAVE_CMPXCHG_LOCAL if !M386 diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 8a73f75beb16..d1d25f6a5e24 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -131,8 +131,9 @@ enum perf_event_sample_format { PERF_SAMPLE_RAW = 1U << 10, PERF_SAMPLE_BRANCH_STACK = 1U << 11, PERF_SAMPLE_REGS_USER = 1U << 12, + PERF_SAMPLE_STACK_USER = 1U << 13, - PERF_SAMPLE_MAX = 1U << 13, /* non-ABI */ + PERF_SAMPLE_MAX = 1U << 14, /* non-ABI */ }; /* @@ -205,6 +206,7 @@ enum perf_event_read_format { #define PERF_ATTR_SIZE_VER1 72 /* add: config2 */ #define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */ #define PERF_ATTR_SIZE_VER3 88 /* add: sample_regs_user */ +#define PERF_ATTR_SIZE_VER4 96 /* add: sample_stack_user */ /* * Hardware event_id to monitor via a performance monitoring event: @@ -289,6 +291,14 @@ struct perf_event_attr { * See asm/perf_regs.h for details. */ __u64 sample_regs_user; + + /* + * Defines size of the user stack to dump on samples. + */ + __u32 sample_stack_user; + + /* Align to u64. */ + __u32 __reserved_2; }; /* @@ -568,6 +578,10 @@ enum perf_event_type { * * { u64 abi; # enum perf_sample_regs_abi * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER + * + * { u64 size; + * char data[size]; + * u64 dyn_size; } && PERF_SAMPLE_STACK_USER * }; */ PERF_RECORD_SAMPLE = 9, @@ -1160,6 +1174,7 @@ struct perf_sample_data { struct perf_raw_record *raw; struct perf_branch_stack *br_stack; struct perf_regs_user regs_user; + u64 stack_user_size; }; static inline void perf_sample_data_init(struct perf_sample_data *data, @@ -1172,6 +1187,7 @@ static inline void perf_sample_data_init(struct perf_sample_data *data, data->period = period; data->regs_user.abi = PERF_SAMPLE_REGS_ABI_NONE; data->regs_user.regs = NULL; + data->stack_user_size = 0; } extern void perf_output_sample(struct perf_output_handle *handle, diff --git a/kernel/events/core.c b/kernel/events/core.c index d3ce97525b9f..2ba890450d15 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "internal.h" @@ -3787,6 +3788,101 @@ static void perf_sample_regs_user(struct perf_regs_user *regs_user, } } +/* + * Get remaining task size from user stack pointer. + * + * It'd be better to take stack vma map and limit this more + * precisly, but there's no way to get it safely under interrupt, + * so using TASK_SIZE as limit. + */ +static u64 perf_ustack_task_size(struct pt_regs *regs) +{ + unsigned long addr = perf_user_stack_pointer(regs); + + if (!addr || addr >= TASK_SIZE) + return 0; + + return TASK_SIZE - addr; +} + +static u16 +perf_sample_ustack_size(u16 stack_size, u16 header_size, + struct pt_regs *regs) +{ + u64 task_size; + + /* No regs, no stack pointer, no dump. */ + if (!regs) + return 0; + + /* + * Check if we fit in with the requested stack size into the: + * - TASK_SIZE + * If we don't, we limit the size to the TASK_SIZE. + * + * - remaining sample size + * If we don't, we customize the stack size to + * fit in to the remaining sample size. + */ + + task_size = min((u64) USHRT_MAX, perf_ustack_task_size(regs)); + stack_size = min(stack_size, (u16) task_size); + + /* Current header size plus static size and dynamic size. */ + header_size += 2 * sizeof(u64); + + /* Do we fit in with the current stack dump size? */ + if ((u16) (header_size + stack_size) < header_size) { + /* + * If we overflow the maximum size for the sample, + * we customize the stack dump size to fit in. + */ + stack_size = USHRT_MAX - header_size - sizeof(u64); + stack_size = round_up(stack_size, sizeof(u64)); + } + + return stack_size; +} + +static void +perf_output_sample_ustack(struct perf_output_handle *handle, u64 dump_size, + struct pt_regs *regs) +{ + /* Case of a kernel thread, nothing to dump */ + if (!regs) { + u64 size = 0; + perf_output_put(handle, size); + } else { + unsigned long sp; + unsigned int rem; + u64 dyn_size; + + /* + * We dump: + * static size + * - the size requested by user or the best one we can fit + * in to the sample max size + * data + * - user stack dump data + * dynamic size + * - the actual dumped size + */ + + /* Static size. */ + perf_output_put(handle, dump_size); + + /* Data. */ + sp = perf_user_stack_pointer(regs); + rem = __output_copy_user(handle, (void *) sp, dump_size); + dyn_size = dump_size - rem; + + perf_output_skip(handle, rem); + + /* Dynamic size. */ + perf_output_put(handle, dyn_size); + } +} + static void __perf_event_header__init_id(struct perf_event_header *header, struct perf_sample_data *data, struct perf_event *event) @@ -4064,6 +4160,11 @@ void perf_output_sample(struct perf_output_handle *handle, mask); } } + + if (sample_type & PERF_SAMPLE_STACK_USER) + perf_output_sample_ustack(handle, + data->stack_user_size, + data->regs_user.regs); } void perf_prepare_sample(struct perf_event_header *header, @@ -4129,6 +4230,35 @@ void perf_prepare_sample(struct perf_event_header *header, header->size += size; } + + if (sample_type & PERF_SAMPLE_STACK_USER) { + /* + * Either we need PERF_SAMPLE_STACK_USER bit to be allways + * processed as the last one or have additional check added + * in case new sample type is added, because we could eat + * up the rest of the sample size. + */ + struct perf_regs_user *uregs = &data->regs_user; + u16 stack_size = event->attr.sample_stack_user; + u16 size = sizeof(u64); + + if (!uregs->abi) + perf_sample_regs_user(uregs, regs); + + stack_size = perf_sample_ustack_size(stack_size, header->size, + uregs->regs); + + /* + * If there is something to dump, add space for the dump + * itself and for the field that tells the dynamic size, + * which is how many have been actually dumped. + */ + if (stack_size) + size += sizeof(u64) + stack_size; + + data->stack_user_size = stack_size; + header->size += size; + } } static void perf_event_output(struct perf_event *event, @@ -6205,8 +6335,26 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr, } } - if (attr->sample_type & PERF_SAMPLE_REGS_USER) + if (attr->sample_type & PERF_SAMPLE_REGS_USER) { ret = perf_reg_validate(attr->sample_regs_user); + if (ret) + return ret; + } + + if (attr->sample_type & PERF_SAMPLE_STACK_USER) { + if (!arch_perf_have_user_stack_dump()) + return -ENOSYS; + + /* + * We have __u32 type for the size, but so far + * we can only use __u16 as maximum due to the + * __u16 sample size limit. + */ + if (attr->sample_stack_user >= USHRT_MAX) + ret = -EINVAL; + else if (!IS_ALIGNED(attr->sample_stack_user, sizeof(u64))) + ret = -EINVAL; + } out: return ret; diff --git a/kernel/events/internal.h b/kernel/events/internal.h index ce7bdfc1d045..d56a64c99a8b 100644 --- a/kernel/events/internal.h +++ b/kernel/events/internal.h @@ -158,4 +158,20 @@ static inline void put_recursion_context(int *recursion, int rctx) recursion[rctx]--; } +#ifdef CONFIG_HAVE_PERF_USER_STACK_DUMP +static inline bool arch_perf_have_user_stack_dump(void) +{ + return true; +} + +#define perf_user_stack_pointer(regs) user_stack_pointer(regs) +#else +static inline bool arch_perf_have_user_stack_dump(void) +{ + return false; +} + +#define perf_user_stack_pointer(regs) 0 +#endif /* CONFIG_HAVE_PERF_USER_STACK_DUMP */ + #endif /* _KERNEL_EVENTS_INTERNAL_H */ -- cgit v1.2.3 From d077526485d5c9b12fe85d0b2b3b7041e6bc5f91 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 7 Aug 2012 15:20:41 +0200 Subject: perf: Add attribute to filter out callchains Introducing following bits to the the perf_event_attr struct: - exclude_callchain_kernel to filter out kernel callchain from the sample dump - exclude_callchain_user to filter out user callchain from the sample dump We need to be able to disable standard user callchain dump when we use the dwarf cfi callchain mode, because frame pointer based user callchains are useless in this mode. Implementing also exclude_callchain_kernel to have complete set of options. Signed-off-by: Jiri Olsa [ Added kernel callchains filtering ] Cc: "Frank Ch. Eigler" Cc: Arun Sharma Cc: Benjamin Redelings Cc: Corey Ashford Cc: Cyrill Gorcunov Cc: Frank Ch. Eigler Cc: Ingo Molnar Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Robert Richter Cc: Stephane Eranian Cc: Tom Zanussi Cc: Ulrich Drepper Link: http://lkml.kernel.org/r/1344345647-11536-7-git-send-email-jolsa@redhat.com Signed-off-by: Frederic Weisbecker Signed-off-by: Arnaldo Carvalho de Melo --- include/linux/perf_event.h | 5 ++++- kernel/events/callchain.c | 38 ++++++++++++++++++++++++-------------- 2 files changed, 28 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index d1d25f6a5e24..297ca3db6b4a 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -268,7 +268,10 @@ struct perf_event_attr { exclude_host : 1, /* don't count in host */ exclude_guest : 1, /* don't count in guest */ - __reserved_1 : 43; + exclude_callchain_kernel : 1, /* exclude kernel callchains */ + exclude_callchain_user : 1, /* exclude user callchains */ + + __reserved_1 : 41; union { __u32 wakeup_events; /* wakeup every n events */ diff --git a/kernel/events/callchain.c b/kernel/events/callchain.c index 98d4597f43d6..c77206184b8b 100644 --- a/kernel/events/callchain.c +++ b/kernel/events/callchain.c @@ -159,6 +159,11 @@ perf_callchain(struct perf_event *event, struct pt_regs *regs) int rctx; struct perf_callchain_entry *entry; + int kernel = !event->attr.exclude_callchain_kernel; + int user = !event->attr.exclude_callchain_user; + + if (!kernel && !user) + return NULL; entry = get_callchain_entry(&rctx); if (rctx == -1) @@ -169,24 +174,29 @@ perf_callchain(struct perf_event *event, struct pt_regs *regs) entry->nr = 0; - if (!user_mode(regs)) { + if (kernel && !user_mode(regs)) { perf_callchain_store(entry, PERF_CONTEXT_KERNEL); perf_callchain_kernel(entry, regs); - if (current->mm) - regs = task_pt_regs(current); - else - regs = NULL; } - if (regs) { - /* - * Disallow cross-task user callchains. - */ - if (event->ctx->task && event->ctx->task != current) - goto exit_put; - - perf_callchain_store(entry, PERF_CONTEXT_USER); - perf_callchain_user(entry, regs); + if (user) { + if (!user_mode(regs)) { + if (current->mm) + regs = task_pt_regs(current); + else + regs = NULL; + } + + if (regs) { + /* + * Disallow cross-task user callchains. + */ + if (event->ctx->task && event->ctx->task != current) + goto exit_put; + + perf_callchain_store(entry, PERF_CONTEXT_USER); + perf_callchain_user(entry, regs); + } } exit_put: -- cgit v1.2.3 From 04216bedafb1b3992a6c2b7f1518281d2ba5fc7b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 6 Aug 2012 18:08:39 -0700 Subject: usb: host: ehci-platform: add platform specific power callback This patch enables to call platform specific power callback function. Signed-off-by: Kuninori Morimoto Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-platform.c | 40 +++++++++++++++++++++++++++++++++++++--- include/linux/usb/ehci_pdriver.h | 8 ++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index a2aaef618aea..91acdde8d21f 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -105,10 +105,18 @@ static int __devinit ehci_platform_probe(struct platform_device *dev) return -ENXIO; } + if (pdata->power_on) { + err = pdata->power_on(dev); + if (err < 0) + return err; + } + hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev, dev_name(&dev->dev)); - if (!hcd) - return -ENOMEM; + if (!hcd) { + err = -ENOMEM; + goto err_power; + } hcd->rsrc_start = res_mem->start; hcd->rsrc_len = resource_size(res_mem); @@ -136,12 +144,17 @@ err_release_region: release_mem_region(hcd->rsrc_start, hcd->rsrc_len); err_put_hcd: usb_put_hcd(hcd); +err_power: + if (pdata->power_off) + pdata->power_off(dev); + return err; } static int __devexit ehci_platform_remove(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); + struct usb_ehci_pdata *pdata = dev->dev.platform_data; usb_remove_hcd(hcd); iounmap(hcd->regs); @@ -149,6 +162,9 @@ static int __devexit ehci_platform_remove(struct platform_device *dev) usb_put_hcd(hcd); platform_set_drvdata(dev, NULL); + if (pdata->power_off) + pdata->power_off(dev); + return 0; } @@ -157,14 +173,32 @@ static int __devexit ehci_platform_remove(struct platform_device *dev) static int ehci_platform_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); + struct usb_ehci_pdata *pdata = dev->platform_data; + struct platform_device *pdev = + container_of(dev, struct platform_device, dev); bool do_wakeup = device_may_wakeup(dev); + int ret; + + ret = ehci_suspend(hcd, do_wakeup); - return ehci_suspend(hcd, do_wakeup); + if (pdata->power_suspend) + pdata->power_suspend(pdev); + + return ret; } static int ehci_platform_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); + struct usb_ehci_pdata *pdata = dev->platform_data; + struct platform_device *pdev = + container_of(dev, struct platform_device, dev); + + if (pdata->power_on) { + int err = pdata->power_on(pdev); + if (err < 0) + return err; + } ehci_resume(hcd, false); return 0; diff --git a/include/linux/usb/ehci_pdriver.h b/include/linux/usb/ehci_pdriver.h index 1894f42fe3f7..c9d09f8b7ff2 100644 --- a/include/linux/usb/ehci_pdriver.h +++ b/include/linux/usb/ehci_pdriver.h @@ -41,6 +41,14 @@ struct usb_ehci_pdata { unsigned big_endian_mmio:1; unsigned port_power_on:1; unsigned port_power_off:1; + + /* Turn on all power and clocks */ + int (*power_on)(struct platform_device *pdev); + /* Turn off all power and clocks */ + void (*power_off)(struct platform_device *pdev); + /* Turn on only VBUS suspend power and hotplug detection, + * turn off everything else */ + void (*power_suspend)(struct platform_device *pdev); }; #endif /* __USB_CORE_EHCI_PDRIVER_H */ -- cgit v1.2.3 From e4d37aeb373a5edceecc1dadc76fabbe8bc18e44 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 6 Aug 2012 18:09:10 -0700 Subject: usb: host: ohci-platform: add platform specific power callback This patch enables to call platform specific power callback function. Signed-off-by: Kuninori Morimoto Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-platform.c | 36 ++++++++++++++++++++++++++++++++++-- include/linux/usb/ohci_pdriver.h | 8 ++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index 4c886f938f6c..10d85b9d9e93 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -107,10 +107,18 @@ static int __devinit ohci_platform_probe(struct platform_device *dev) return -ENXIO; } + if (pdata->power_on) { + err = pdata->power_on(dev); + if (err < 0) + return err; + } + hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev, dev_name(&dev->dev)); - if (!hcd) - return -ENOMEM; + if (!hcd) { + err = -ENOMEM; + goto err_power; + } hcd->rsrc_start = res_mem->start; hcd->rsrc_len = resource_size(res_mem); @@ -138,12 +146,17 @@ err_release_region: release_mem_region(hcd->rsrc_start, hcd->rsrc_len); err_put_hcd: usb_put_hcd(hcd); +err_power: + if (pdata->power_off) + pdata->power_off(dev); + return err; } static int __devexit ohci_platform_remove(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); + struct usb_ohci_pdata *pdata = dev->dev.platform_data; usb_remove_hcd(hcd); iounmap(hcd->regs); @@ -151,6 +164,9 @@ static int __devexit ohci_platform_remove(struct platform_device *dev) usb_put_hcd(hcd); platform_set_drvdata(dev, NULL); + if (pdata->power_off) + pdata->power_off(dev); + return 0; } @@ -158,12 +174,28 @@ static int __devexit ohci_platform_remove(struct platform_device *dev) static int ohci_platform_suspend(struct device *dev) { + struct usb_ohci_pdata *pdata = dev->platform_data; + struct platform_device *pdev = + container_of(dev, struct platform_device, dev); + + if (pdata->power_suspend) + pdata->power_suspend(pdev); + return 0; } static int ohci_platform_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); + struct usb_ohci_pdata *pdata = dev->platform_data; + struct platform_device *pdev = + container_of(dev, struct platform_device, dev); + + if (pdata->power_on) { + int err = pdata->power_on(pdev); + if (err < 0) + return err; + } ohci_finish_controller_resume(hcd); return 0; diff --git a/include/linux/usb/ohci_pdriver.h b/include/linux/usb/ohci_pdriver.h index 2808f2a9cce8..74e7755168b7 100644 --- a/include/linux/usb/ohci_pdriver.h +++ b/include/linux/usb/ohci_pdriver.h @@ -33,6 +33,14 @@ struct usb_ohci_pdata { unsigned big_endian_desc:1; unsigned big_endian_mmio:1; unsigned no_big_frame_no:1; + + /* Turn on all power and clocks */ + int (*power_on)(struct platform_device *pdev); + /* Turn off all power and clocks */ + void (*power_off)(struct platform_device *pdev); + /* Turn on only VBUS suspend power and hotplug detection, + * turn off everything else */ + void (*power_suspend)(struct platform_device *pdev); }; #endif /* __USB_CORE_OHCI_PDRIVER_H */ -- cgit v1.2.3 From 3213e1a570783ca3a41d025cede4a27b18bc24c9 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Wed, 8 Aug 2012 19:10:14 +0200 Subject: bcma: add (mostly) NAND defines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- include/linux/bcma/bcma_driver_chipcommon.h | 85 +++++++++++++++++++++++++++++ include/linux/bcma/bcma_regs.h | 2 + 2 files changed, 87 insertions(+) (limited to 'include') diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h index 3c80885fa829..fcb06fb284eb 100644 --- a/include/linux/bcma/bcma_driver_chipcommon.h +++ b/include/linux/bcma/bcma_driver_chipcommon.h @@ -94,6 +94,7 @@ #define BCMA_CC_CHIPST_4706_SFLASH_TYPE BIT(2) /* 0: 8b-p/ST-s flash, 1: 16b-p/Atmal-s flash */ #define BCMA_CC_CHIPST_4706_MIPS_BENDIAN BIT(3) /* 0: little, 1: big endian */ #define BCMA_CC_CHIPST_4706_PCIE1_DISABLE BIT(5) /* PCIE1 enable strap pin */ +#define BCMA_CC_CHIPST_5357_NAND_BOOT BIT(4) /* NAND boot, valid for CC rev 38 and/or BCM5357 */ #define BCMA_CC_JCMD 0x0030 /* Rev >= 10 only */ #define BCMA_CC_JCMD_START 0x80000000 #define BCMA_CC_JCMD_BUSY 0x80000000 @@ -260,6 +261,29 @@ #define BCMA_CC_SROM_CONTROL_SIZE_16K 0x00000004 #define BCMA_CC_SROM_CONTROL_SIZE_SHIFT 1 #define BCMA_CC_SROM_CONTROL_PRESENT 0x00000001 +/* Block 0x140 - 0x190 registers are chipset specific */ +#define BCMA_CC_4706_FLASHSCFG 0x18C /* Flash struct configuration */ +#define BCMA_CC_4706_FLASHSCFG_MASK 0x000000ff +#define BCMA_CC_4706_FLASHSCFG_SF1 0x00000001 /* 2nd serial flash present */ +#define BCMA_CC_4706_FLASHSCFG_PF1 0x00000002 /* 2nd parallel flash present */ +#define BCMA_CC_4706_FLASHSCFG_SF1_TYPE 0x00000004 /* 2nd serial flash type : 0 : ST, 1 : Atmel */ +#define BCMA_CC_4706_FLASHSCFG_NF1 0x00000008 /* 2nd NAND flash present */ +#define BCMA_CC_4706_FLASHSCFG_1ST_MADDR_SEG_MASK 0x000000f0 +#define BCMA_CC_4706_FLASHSCFG_1ST_MADDR_SEG_4MB 0x00000010 /* 4MB */ +#define BCMA_CC_4706_FLASHSCFG_1ST_MADDR_SEG_8MB 0x00000020 /* 8MB */ +#define BCMA_CC_4706_FLASHSCFG_1ST_MADDR_SEG_16MB 0x00000030 /* 16MB */ +#define BCMA_CC_4706_FLASHSCFG_1ST_MADDR_SEG_32MB 0x00000040 /* 32MB */ +#define BCMA_CC_4706_FLASHSCFG_1ST_MADDR_SEG_64MB 0x00000050 /* 64MB */ +#define BCMA_CC_4706_FLASHSCFG_1ST_MADDR_SEG_128MB 0x00000060 /* 128MB */ +#define BCMA_CC_4706_FLASHSCFG_1ST_MADDR_SEG_256MB 0x00000070 /* 256MB */ +/* NAND flash registers for BCM4706 (corerev = 31) */ +#define BCMA_CC_NFLASH_CTL 0x01A0 +#define BCMA_CC_NFLASH_CTL_ERR 0x08000000 +#define BCMA_CC_NFLASH_CONF 0x01A4 +#define BCMA_CC_NFLASH_COL_ADDR 0x01A8 +#define BCMA_CC_NFLASH_ROW_ADDR 0x01AC +#define BCMA_CC_NFLASH_DATA 0x01B0 +#define BCMA_CC_NFLASH_WAITCNT0 0x01B4 /* 0x1E0 is defined as shared BCMA_CLKCTLST */ #define BCMA_CC_HW_WORKAROUND 0x01E4 /* Hardware workaround (rev >= 20) */ #define BCMA_CC_UART0_DATA 0x0300 @@ -319,6 +343,60 @@ #define BCMA_CC_PLLCTL_ADDR 0x0660 #define BCMA_CC_PLLCTL_DATA 0x0664 #define BCMA_CC_SPROM 0x0800 /* SPROM beginning */ +/* NAND flash MLC controller registers (corerev >= 38) */ +#define BCMA_CC_NAND_REVISION 0x0C00 +#define BCMA_CC_NAND_CMD_START 0x0C04 +#define BCMA_CC_NAND_CMD_ADDR_X 0x0C08 +#define BCMA_CC_NAND_CMD_ADDR 0x0C0C +#define BCMA_CC_NAND_CMD_END_ADDR 0x0C10 +#define BCMA_CC_NAND_CS_NAND_SELECT 0x0C14 +#define BCMA_CC_NAND_CS_NAND_XOR 0x0C18 +#define BCMA_CC_NAND_SPARE_RD0 0x0C20 +#define BCMA_CC_NAND_SPARE_RD4 0x0C24 +#define BCMA_CC_NAND_SPARE_RD8 0x0C28 +#define BCMA_CC_NAND_SPARE_RD12 0x0C2C +#define BCMA_CC_NAND_SPARE_WR0 0x0C30 +#define BCMA_CC_NAND_SPARE_WR4 0x0C34 +#define BCMA_CC_NAND_SPARE_WR8 0x0C38 +#define BCMA_CC_NAND_SPARE_WR12 0x0C3C +#define BCMA_CC_NAND_ACC_CONTROL 0x0C40 +#define BCMA_CC_NAND_CONFIG 0x0C48 +#define BCMA_CC_NAND_TIMING_1 0x0C50 +#define BCMA_CC_NAND_TIMING_2 0x0C54 +#define BCMA_CC_NAND_SEMAPHORE 0x0C58 +#define BCMA_CC_NAND_DEVID 0x0C60 +#define BCMA_CC_NAND_DEVID_X 0x0C64 +#define BCMA_CC_NAND_BLOCK_LOCK_STATUS 0x0C68 +#define BCMA_CC_NAND_INTFC_STATUS 0x0C6C +#define BCMA_CC_NAND_ECC_CORR_ADDR_X 0x0C70 +#define BCMA_CC_NAND_ECC_CORR_ADDR 0x0C74 +#define BCMA_CC_NAND_ECC_UNC_ADDR_X 0x0C78 +#define BCMA_CC_NAND_ECC_UNC_ADDR 0x0C7C +#define BCMA_CC_NAND_READ_ERROR_COUNT 0x0C80 +#define BCMA_CC_NAND_CORR_STAT_THRESHOLD 0x0C84 +#define BCMA_CC_NAND_READ_ADDR_X 0x0C90 +#define BCMA_CC_NAND_READ_ADDR 0x0C94 +#define BCMA_CC_NAND_PAGE_PROGRAM_ADDR_X 0x0C98 +#define BCMA_CC_NAND_PAGE_PROGRAM_ADDR 0x0C9C +#define BCMA_CC_NAND_COPY_BACK_ADDR_X 0x0CA0 +#define BCMA_CC_NAND_COPY_BACK_ADDR 0x0CA4 +#define BCMA_CC_NAND_BLOCK_ERASE_ADDR_X 0x0CA8 +#define BCMA_CC_NAND_BLOCK_ERASE_ADDR 0x0CAC +#define BCMA_CC_NAND_INV_READ_ADDR_X 0x0CB0 +#define BCMA_CC_NAND_INV_READ_ADDR 0x0CB4 +#define BCMA_CC_NAND_BLK_WR_PROTECT 0x0CC0 +#define BCMA_CC_NAND_ACC_CONTROL_CS1 0x0CD0 +#define BCMA_CC_NAND_CONFIG_CS1 0x0CD4 +#define BCMA_CC_NAND_TIMING_1_CS1 0x0CD8 +#define BCMA_CC_NAND_TIMING_2_CS1 0x0CDC +#define BCMA_CC_NAND_SPARE_RD16 0x0D30 +#define BCMA_CC_NAND_SPARE_RD20 0x0D34 +#define BCMA_CC_NAND_SPARE_RD24 0x0D38 +#define BCMA_CC_NAND_SPARE_RD28 0x0D3C +#define BCMA_CC_NAND_CACHE_ADDR 0x0D40 +#define BCMA_CC_NAND_CACHE_DATA 0x0D44 +#define BCMA_CC_NAND_CTRL_CONFIG 0x0D48 +#define BCMA_CC_NAND_CTRL_STATUS 0x0D4C /* Divider allocation in 4716/47162/5356 */ #define BCMA_CC_PMU5_MAINPLL_CPU 1 @@ -409,6 +487,13 @@ /* 4313 Chip specific ChipControl register bits */ #define BCMA_CCTRL_4313_12MA_LED_DRIVE 0x00000007 /* 12 mA drive strengh for later 4313 */ +/* BCM5357 ChipControl register bits */ +#define BCMA_CHIPCTL_5357_EXTPA BIT(14) +#define BCMA_CHIPCTL_5357_ANT_MUX_2O3 BIT(15) +#define BCMA_CHIPCTL_5357_NFLASH BIT(16) +#define BCMA_CHIPCTL_5357_I2S_PINS_ENABLE BIT(18) +#define BCMA_CHIPCTL_5357_I2CSPI_PINS_ENABLE BIT(19) + /* Data for the PMU, if available. * Check availability with ((struct bcma_chipcommon)->capabilities & BCMA_CC_CAP_PMU) */ diff --git a/include/linux/bcma/bcma_regs.h b/include/linux/bcma/bcma_regs.h index 5a71d5719640..a393e82bf7bf 100644 --- a/include/linux/bcma/bcma_regs.h +++ b/include/linux/bcma/bcma_regs.h @@ -11,11 +11,13 @@ #define BCMA_CLKCTLST_HAVEHTREQ 0x00000010 /* HT available request */ #define BCMA_CLKCTLST_HWCROFF 0x00000020 /* Force HW clock request off */ #define BCMA_CLKCTLST_EXTRESREQ 0x00000700 /* Mask of external resource requests */ +#define BCMA_CLKCTLST_EXTRESREQ_SHIFT 8 #define BCMA_CLKCTLST_HAVEALP 0x00010000 /* ALP available */ #define BCMA_CLKCTLST_HAVEHT 0x00020000 /* HT available */ #define BCMA_CLKCTLST_BP_ON_ALP 0x00040000 /* RO: running on ALP clock */ #define BCMA_CLKCTLST_BP_ON_HT 0x00080000 /* RO: running on HT clock */ #define BCMA_CLKCTLST_EXTRESST 0x07000000 /* Mask of external resource status */ +#define BCMA_CLKCTLST_EXTRESST_SHIFT 24 /* Is there any BCM4328 on BCMA bus? */ #define BCMA_CLKCTLST_4328A0_HAVEHT 0x00010000 /* 4328a0 has reversed bits */ #define BCMA_CLKCTLST_4328A0_HAVEALP 0x00020000 /* 4328a0 has reversed bits */ -- cgit v1.2.3 From 902d9e0f48ddc18fb37c1b1edf5e3b27aaba1505 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Wed, 8 Aug 2012 19:37:04 +0200 Subject: ssb: check for flash presentence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can not assume parallel flash is always present, there are boards with *serial* flash and probably some without flash at all. Define some bits by the way. Signed-off-by: Rafał Miłecki Reviewed-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/ssb/driver_mipscore.c | 28 +++++++++++++++++++++------- include/linux/ssb/ssb_driver_chipcommon.h | 4 +++- 2 files changed, 24 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/ssb/driver_mipscore.c b/drivers/ssb/driver_mipscore.c index 7e2ddc042f5b..c6250867a95d 100644 --- a/drivers/ssb/driver_mipscore.c +++ b/drivers/ssb/driver_mipscore.c @@ -190,16 +190,30 @@ static void ssb_mips_flash_detect(struct ssb_mipscore *mcore) { struct ssb_bus *bus = mcore->dev->bus; - mcore->flash_buswidth = 2; - if (bus->chipco.dev) { - mcore->flash_window = 0x1c000000; - mcore->flash_window_size = 0x02000000; + /* When there is no chipcommon on the bus there is 4MB flash */ + if (!bus->chipco.dev) { + mcore->flash_buswidth = 2; + mcore->flash_window = SSB_FLASH1; + mcore->flash_window_size = SSB_FLASH1_SZ; + return; + } + + /* There is ChipCommon, so use it to read info about flash */ + switch (bus->chipco.capabilities & SSB_CHIPCO_CAP_FLASHT) { + case SSB_CHIPCO_FLASHT_STSER: + case SSB_CHIPCO_FLASHT_ATSER: + pr_err("Serial flash not supported\n"); + break; + case SSB_CHIPCO_FLASHT_PARA: + pr_debug("Found parallel flash\n"); + mcore->flash_window = SSB_FLASH2; + mcore->flash_window_size = SSB_FLASH2_SZ; if ((ssb_read32(bus->chipco.dev, SSB_CHIPCO_FLASH_CFG) & SSB_CHIPCO_CFG_DS16) == 0) mcore->flash_buswidth = 1; - } else { - mcore->flash_window = 0x1fc00000; - mcore->flash_window_size = 0x00400000; + else + mcore->flash_buswidth = 2; + break; } } diff --git a/include/linux/ssb/ssb_driver_chipcommon.h b/include/linux/ssb/ssb_driver_chipcommon.h index 1a6b0045b06b..c2b02a5c86ae 100644 --- a/include/linux/ssb/ssb_driver_chipcommon.h +++ b/include/linux/ssb/ssb_driver_chipcommon.h @@ -504,7 +504,9 @@ #define SSB_CHIPCO_FLASHCTL_ST_SE 0x02D8 /* Sector Erase */ #define SSB_CHIPCO_FLASHCTL_ST_BE 0x00C7 /* Bulk Erase */ #define SSB_CHIPCO_FLASHCTL_ST_DP 0x00B9 /* Deep Power-down */ -#define SSB_CHIPCO_FLASHCTL_ST_RSIG 0x03AB /* Read Electronic Signature */ +#define SSB_CHIPCO_FLASHCTL_ST_RES 0x03AB /* Read Electronic Signature */ +#define SSB_CHIPCO_FLASHCTL_ST_CSA 0x1000 /* Keep chip select asserted */ +#define SSB_CHIPCO_FLASHCTL_ST_SSE 0x0220 /* Sub-sector Erase */ /* Status register bits for ST flashes */ #define SSB_CHIPCO_FLASHSTA_ST_WIP 0x01 /* Write In Progress */ -- cgit v1.2.3 From 89c8d91e31f267703e365593f6bfebb9f6d2ad01 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 8 Aug 2012 16:30:13 +0100 Subject: tty: localise the lock The termios and other changes mean the other protections needed on the driver tty arrays should be adequate. Turn it all back on. This contains pieces folded in from the fixes made to the original patches | From: Geert Uytterhoeven (fix m68k) | From: Paul Gortmaker (fix cris) | From: Jiri Kosina (lockdep) | From: Eric Dumazet (lockdep) Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/amiserial.c | 14 ++++----- drivers/tty/cyclades.c | 2 +- drivers/tty/n_r3964.c | 10 +++---- drivers/tty/pty.c | 25 +++++++++------- drivers/tty/serial/crisv10.c | 8 ++--- drivers/tty/synclink.c | 4 +-- drivers/tty/synclink_gt.c | 4 +-- drivers/tty/synclinkmp.c | 4 +-- drivers/tty/tty_io.c | 66 +++++++++++++++++++++++----------------- drivers/tty/tty_ldisc.c | 69 +++++++++++++++++++++++------------------- drivers/tty/tty_mutex.c | 71 ++++++++++++++++++++++++++++++++++---------- drivers/tty/tty_port.c | 6 ++-- include/linux/tty.h | 23 ++++++++------ net/bluetooth/rfcomm/tty.c | 4 +-- 14 files changed, 190 insertions(+), 120 deletions(-) (limited to 'include') diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 0e8441e73ee0..998731f01d62 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1033,7 +1033,7 @@ static int get_serial_info(struct tty_struct *tty, struct serial_state *state, if (!retinfo) return -EFAULT; memset(&tmp, 0, sizeof(tmp)); - tty_lock(); + tty_lock(tty); tmp.line = tty->index; tmp.port = state->port; tmp.flags = state->tport.flags; @@ -1042,7 +1042,7 @@ static int get_serial_info(struct tty_struct *tty, struct serial_state *state, tmp.close_delay = state->tport.close_delay; tmp.closing_wait = state->tport.closing_wait; tmp.custom_divisor = state->custom_divisor; - tty_unlock(); + tty_unlock(tty); if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) return -EFAULT; return 0; @@ -1059,12 +1059,12 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) return -EFAULT; - tty_lock(); + tty_lock(tty); change_spd = ((new_serial.flags ^ port->flags) & ASYNC_SPD_MASK) || new_serial.custom_divisor != state->custom_divisor; if (new_serial.irq || new_serial.port != state->port || new_serial.xmit_fifo_size != state->xmit_fifo_size) { - tty_unlock(); + tty_unlock(tty); return -EINVAL; } @@ -1074,7 +1074,7 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, (new_serial.xmit_fifo_size != state->xmit_fifo_size) || ((new_serial.flags & ~ASYNC_USR_MASK) != (port->flags & ~ASYNC_USR_MASK))) { - tty_unlock(); + tty_unlock(tty); return -EPERM; } port->flags = ((port->flags & ~ASYNC_USR_MASK) | @@ -1084,7 +1084,7 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, } if (new_serial.baud_base < 9600) { - tty_unlock(); + tty_unlock(tty); return -EINVAL; } @@ -1116,7 +1116,7 @@ check_and_exit: } } else retval = startup(tty, state); - tty_unlock(); + tty_unlock(tty); return retval; } diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index e77db714ab26..c8850ea832af 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -1599,7 +1599,7 @@ static int cy_open(struct tty_struct *tty, struct file *filp) * If the port is the middle of closing, bail out now */ if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(info->port.close_wait, + wait_event_interruptible_tty(tty, info->port.close_wait, !(info->port.flags & ASYNC_CLOSING)); return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; } diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c index 5c6c31459a2f..1e6405070ce6 100644 --- a/drivers/tty/n_r3964.c +++ b/drivers/tty/n_r3964.c @@ -1065,7 +1065,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, TRACE_L("read()"); - tty_lock(); + tty_lock(tty); pClient = findClient(pInfo, task_pid(current)); if (pClient) { @@ -1077,7 +1077,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, goto unlock; } /* block until there is a message: */ - wait_event_interruptible_tty(pInfo->read_wait, + wait_event_interruptible_tty(tty, pInfo->read_wait, (pMsg = remove_msg(pInfo, pClient))); } @@ -1107,7 +1107,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, } ret = -EPERM; unlock: - tty_unlock(); + tty_unlock(tty); return ret; } @@ -1156,7 +1156,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file, pHeader->locks = 0; pHeader->owner = NULL; - tty_lock(); + tty_lock(tty); pClient = findClient(pInfo, task_pid(current)); if (pClient) { @@ -1175,7 +1175,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file, add_tx_queue(pInfo, pHeader); trigger_transmit(pInfo); - tty_unlock(); + tty_unlock(tty); return 0; } diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index d6579a9064c4..4399f1dbd131 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -47,6 +47,7 @@ static void pty_close(struct tty_struct *tty, struct file *filp) wake_up_interruptible(&tty->read_wait); wake_up_interruptible(&tty->write_wait); tty->packet = 0; + /* Review - krefs on tty_link ?? */ if (!tty->link) return; tty->link->packet = 0; @@ -62,9 +63,9 @@ static void pty_close(struct tty_struct *tty, struct file *filp) mutex_unlock(&devpts_mutex); } #endif - tty_unlock(); + tty_unlock(tty); tty_vhangup(tty->link); - tty_lock(); + tty_lock(tty); } } @@ -617,26 +618,27 @@ static int ptmx_open(struct inode *inode, struct file *filp) return retval; /* find a device that is not in use. */ - tty_lock(); + mutex_lock(&devpts_mutex); index = devpts_new_index(inode); - tty_unlock(); if (index < 0) { retval = index; goto err_file; } + mutex_unlock(&devpts_mutex); + mutex_lock(&tty_mutex); - mutex_lock(&devpts_mutex); tty = tty_init_dev(ptm_driver, index); - mutex_unlock(&devpts_mutex); - tty_lock(); - mutex_unlock(&tty_mutex); if (IS_ERR(tty)) { retval = PTR_ERR(tty); goto out; } + /* The tty returned here is locked so we can safely + drop the mutex */ + mutex_unlock(&tty_mutex); + set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ tty_add_file(tty, filp); @@ -649,16 +651,17 @@ static int ptmx_open(struct inode *inode, struct file *filp) if (retval) goto err_release; - tty_unlock(); + tty_unlock(tty); return 0; err_release: - tty_unlock(); + tty_unlock(tty); tty_release(inode, filp); return retval; out: + mutex_unlock(&tty_mutex); devpts_kill_index(inode, index); - tty_unlock(); err_file: + mutex_unlock(&devpts_mutex); tty_free_file(filp); return retval; } diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 6b705b243522..a770b1012962 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -3976,7 +3976,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(info->close_wait, + wait_event_interruptible_tty(tty, info->close_wait, !(info->flags & ASYNC_CLOSING)); #ifdef SERIAL_DO_RESTART if (info->flags & ASYNC_HUP_NOTIFY) @@ -4052,9 +4052,9 @@ block_til_ready(struct tty_struct *tty, struct file * filp, printk("block_til_ready blocking: ttyS%d, count = %d\n", info->line, info->count); #endif - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); remove_wait_queue(&info->open_wait, &wait); @@ -4115,7 +4115,7 @@ rs_open(struct tty_struct *tty, struct file * filp) */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(info->close_wait, + wait_event_interruptible_tty(tty, info->close_wait, !(info->flags & ASYNC_CLOSING)); #ifdef SERIAL_DO_RESTART return ((info->flags & ASYNC_HUP_NOTIFY) ? diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index bdeeb3133f62..991bae821232 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -3338,9 +3338,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, printk("%s(%d):block_til_ready blocking on %s count=%d\n", __FILE__,__LINE__, tty->driver->name, port->count ); - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index f02d18a391e5..913025369fe7 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -3336,9 +3336,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, } DBGINFO(("%s block_til_ready wait\n", tty->driver->name)); - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index ae75a3c21fd3..95fd4e20b963 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -3357,9 +3357,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, printk("%s(%d):%s block_til_ready() count=%d\n", __FILE__,__LINE__, tty->driver->name, port->count ); - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 6784aae210e3..690224483fab 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -187,6 +187,7 @@ void free_tty_struct(struct tty_struct *tty) put_device(tty->dev); kfree(tty->write_buf); tty_buffer_free_all(tty); + tty->magic = 0xDEADDEAD; kfree(tty); } @@ -575,7 +576,7 @@ void __tty_hangup(struct tty_struct *tty) } spin_unlock(&redirect_lock); - tty_lock(); + tty_lock(tty); /* some functions below drop BTM, so we need this bit */ set_bit(TTY_HUPPING, &tty->flags); @@ -668,7 +669,7 @@ void __tty_hangup(struct tty_struct *tty) clear_bit(TTY_HUPPING, &tty->flags); tty_ldisc_enable(tty); - tty_unlock(); + tty_unlock(tty); if (f) fput(f); @@ -1105,12 +1106,12 @@ void tty_write_message(struct tty_struct *tty, char *msg) { if (tty) { mutex_lock(&tty->atomic_write_lock); - tty_lock(); + tty_lock(tty); if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) { - tty_unlock(); + tty_unlock(tty); tty->ops->write(tty, msg, strlen(msg)); } else - tty_unlock(); + tty_unlock(tty); tty_write_unlock(tty); } return; @@ -1403,6 +1404,7 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) } initialize_tty_struct(tty, driver, idx); + tty_lock(tty); retval = tty_driver_install_tty(driver, tty); if (retval < 0) goto err_deinit_tty; @@ -1418,9 +1420,11 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) retval = tty_ldisc_setup(tty, tty->link); if (retval) goto err_release_tty; + /* Return the tty locked so that it cannot vanish under the caller */ return tty; err_deinit_tty: + tty_unlock(tty); deinitialize_tty_struct(tty); free_tty_struct(tty); err_module_put: @@ -1429,6 +1433,7 @@ err_module_put: /* call the tty release_tty routine to clean out this slot */ err_release_tty: + tty_unlock(tty); printk_ratelimited(KERN_INFO "tty_init_dev: ldisc open failed, " "clearing slot %d\n", idx); release_tty(tty, idx); @@ -1622,7 +1627,7 @@ int tty_release(struct inode *inode, struct file *filp) if (tty_paranoia_check(tty, inode, __func__)) return 0; - tty_lock(); + tty_lock(tty); check_tty_count(tty, __func__); __tty_fasync(-1, filp, 0); @@ -1631,10 +1636,11 @@ int tty_release(struct inode *inode, struct file *filp) pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER); devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0; + /* Review: parallel close */ o_tty = tty->link; if (tty_release_checks(tty, o_tty, idx)) { - tty_unlock(); + tty_unlock(tty); return 0; } @@ -1646,7 +1652,7 @@ int tty_release(struct inode *inode, struct file *filp) if (tty->ops->close) tty->ops->close(tty, filp); - tty_unlock(); + tty_unlock(tty); /* * Sanity check: if tty->count is going to zero, there shouldn't be * any waiters on tty->read_wait or tty->write_wait. We test the @@ -1669,7 +1675,7 @@ int tty_release(struct inode *inode, struct file *filp) opens on /dev/tty */ mutex_lock(&tty_mutex); - tty_lock(); + tty_lock_pair(tty, o_tty); tty_closing = tty->count <= 1; o_tty_closing = o_tty && (o_tty->count <= (pty_master ? 1 : 0)); @@ -1700,7 +1706,7 @@ int tty_release(struct inode *inode, struct file *filp) printk(KERN_WARNING "%s: %s: read/write wait queue active!\n", __func__, tty_name(tty, buf)); - tty_unlock(); + tty_unlock_pair(tty, o_tty); mutex_unlock(&tty_mutex); schedule(); } @@ -1763,7 +1769,7 @@ int tty_release(struct inode *inode, struct file *filp) } mutex_unlock(&tty_mutex); - tty_unlock(); + tty_unlock_pair(tty, o_tty); /* At this point the TTY_CLOSING flag should ensure a dead tty cannot be re-opened by a racing opener */ @@ -1780,7 +1786,9 @@ int tty_release(struct inode *inode, struct file *filp) tty_ldisc_release(tty, o_tty); /* * The release_tty function takes care of the details of clearing - * the slots and preserving the termios structure. + * the slots and preserving the termios structure. The tty_unlock_pair + * should be safe as we keep a kref while the tty is locked (so the + * unlock never unlocks a freed tty). */ mutex_lock(&tty_mutex); release_tty(tty, idx); @@ -1789,7 +1797,6 @@ int tty_release(struct inode *inode, struct file *filp) /* Make this pty number available for reallocation */ if (devpts) devpts_kill_index(inode, idx); - return 0; } @@ -1893,6 +1900,9 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp, * Locking: tty_mutex protects tty, tty_lookup_driver and tty_init_dev. * tty->count should protect the rest. * ->siglock protects ->signal/->sighand + * + * Note: the tty_unlock/lock cases without a ref are only safe due to + * tty_mutex */ static int tty_open(struct inode *inode, struct file *filp) @@ -1916,8 +1926,7 @@ retry_open: retval = 0; mutex_lock(&tty_mutex); - tty_lock(); - + /* This is protected by the tty_mutex */ tty = tty_open_current_tty(device, filp); if (IS_ERR(tty)) { retval = PTR_ERR(tty); @@ -1938,17 +1947,19 @@ retry_open: } if (tty) { + tty_lock(tty); retval = tty_reopen(tty); - if (retval) + if (retval < 0) { + tty_unlock(tty); tty = ERR_PTR(retval); - } else + } + } else /* Returns with the tty_lock held for now */ tty = tty_init_dev(driver, index); mutex_unlock(&tty_mutex); if (driver) tty_driver_kref_put(driver); if (IS_ERR(tty)) { - tty_unlock(); retval = PTR_ERR(tty); goto err_file; } @@ -1977,7 +1988,7 @@ retry_open: printk(KERN_DEBUG "%s: error %d in opening %s...\n", __func__, retval, tty->name); #endif - tty_unlock(); /* need to call tty_release without BTM */ + tty_unlock(tty); /* need to call tty_release without BTM */ tty_release(inode, filp); if (retval != -ERESTARTSYS) return retval; @@ -1989,17 +2000,15 @@ retry_open: /* * Need to reset f_op in case a hangup happened. */ - tty_lock(); if (filp->f_op == &hung_up_tty_fops) filp->f_op = &tty_fops; - tty_unlock(); goto retry_open; } - tty_unlock(); + tty_unlock(tty); mutex_lock(&tty_mutex); - tty_lock(); + tty_lock(tty); spin_lock_irq(¤t->sighand->siglock); if (!noctty && current->signal->leader && @@ -2007,11 +2016,10 @@ retry_open: tty->session == NULL) __proc_set_tty(current, tty); spin_unlock_irq(¤t->sighand->siglock); - tty_unlock(); + tty_unlock(tty); mutex_unlock(&tty_mutex); return 0; err_unlock: - tty_unlock(); mutex_unlock(&tty_mutex); /* after locks to avoid deadlock */ if (!IS_ERR_OR_NULL(driver)) @@ -2094,10 +2102,13 @@ out: static int tty_fasync(int fd, struct file *filp, int on) { + struct tty_struct *tty = file_tty(filp); int retval; - tty_lock(); + + tty_lock(tty); retval = __tty_fasync(fd, filp, on); - tty_unlock(); + tty_unlock(tty); + return retval; } @@ -2934,6 +2945,7 @@ void initialize_tty_struct(struct tty_struct *tty, tty->pgrp = NULL; tty->overrun_time = jiffies; tty_buffer_init(tty); + mutex_init(&tty->legacy_mutex); mutex_init(&tty->termios_mutex); mutex_init(&tty->ldisc_mutex); init_waitqueue_head(&tty->write_wait); diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 3d0687197d09..4d7b56268c79 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -568,7 +568,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) if (IS_ERR(new_ldisc)) return PTR_ERR(new_ldisc); - tty_lock(); + tty_lock(tty); /* * We need to look at the tty locking here for pty/tty pairs * when both sides try to change in parallel. @@ -582,12 +582,12 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) */ if (tty->ldisc->ops->num == ldisc) { - tty_unlock(); + tty_unlock(tty); tty_ldisc_put(new_ldisc); return 0; } - tty_unlock(); + tty_unlock(tty); /* * Problem: What do we do if this blocks ? * We could deadlock here @@ -595,7 +595,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) tty_wait_until_sent(tty, 0); - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); /* @@ -605,10 +605,10 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) { mutex_unlock(&tty->ldisc_mutex); - tty_unlock(); + tty_unlock(tty); wait_event(tty_ldisc_wait, test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0); - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); } @@ -623,7 +623,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) o_ldisc = tty->ldisc; - tty_unlock(); + tty_unlock(tty); /* * Make sure we don't change while someone holds a * reference to the line discipline. The TTY_LDISC bit @@ -650,7 +650,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) retval = tty_ldisc_wait_idle(tty, 5 * HZ); - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); /* handle wait idle failure locked */ @@ -665,7 +665,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) clear_bit(TTY_LDISC_CHANGING, &tty->flags); mutex_unlock(&tty->ldisc_mutex); tty_ldisc_put(new_ldisc); - tty_unlock(); + tty_unlock(tty); return -EIO; } @@ -708,7 +708,7 @@ enable: if (o_work) schedule_work(&o_tty->buf.work); mutex_unlock(&tty->ldisc_mutex); - tty_unlock(); + tty_unlock(tty); return retval; } @@ -816,11 +816,11 @@ void tty_ldisc_hangup(struct tty_struct *tty) * need to wait for another function taking the BTM */ clear_bit(TTY_LDISC, &tty->flags); - tty_unlock(); + tty_unlock(tty); cancel_work_sync(&tty->buf.work); mutex_unlock(&tty->ldisc_mutex); retry: - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); /* At this point we have a closed ldisc and we want to @@ -831,7 +831,7 @@ retry: if (atomic_read(&tty->ldisc->users) != 1) { char cur_n[TASK_COMM_LEN], tty_n[64]; long timeout = 3 * HZ; - tty_unlock(); + tty_unlock(tty); while (tty_ldisc_wait_idle(tty, timeout) == -EBUSY) { timeout = MAX_SCHEDULE_TIMEOUT; @@ -894,6 +894,23 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *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 + */ + tty_ldisc_close(tty, tty->ldisc); + tty_ldisc_put(tty->ldisc); + /* Force an oops if we mess this up */ + tty->ldisc = NULL; + + /* Ensure the next open requests the N_TTY ldisc */ + tty_set_termios_ldisc(tty, N_TTY); + mutex_unlock(&tty->ldisc_mutex); +} + /** * tty_ldisc_release - release line discipline * @tty: tty being shut down @@ -912,29 +929,21 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) * race with the set_ldisc code path. */ + tty_lock_pair(tty, o_tty); tty_ldisc_halt(tty); tty_ldisc_flush_works(tty); - tty_lock(); - - mutex_lock(&tty->ldisc_mutex); - /* - * Now kill off the ldisc - */ - tty_ldisc_close(tty, tty->ldisc); - tty_ldisc_put(tty->ldisc); - /* Force an oops if we mess this up */ - tty->ldisc = NULL; - - /* Ensure the next open requests the N_TTY ldisc */ - tty_set_termios_ldisc(tty, N_TTY); - mutex_unlock(&tty->ldisc_mutex); - - tty_unlock(); + if (o_tty) { + tty_ldisc_halt(o_tty); + tty_ldisc_flush_works(o_tty); + } /* This will need doing differently if we need to lock */ + tty_ldisc_kill(tty); + if (o_tty) - tty_ldisc_release(o_tty, NULL); + tty_ldisc_kill(o_tty); + tty_unlock_pair(tty, o_tty); /* And the memory resources remaining (buffers, termios) will be disposed of when the kref hits zero */ } diff --git a/drivers/tty/tty_mutex.c b/drivers/tty/tty_mutex.c index 9ff986c32a21..67feac9e6ebb 100644 --- a/drivers/tty/tty_mutex.c +++ b/drivers/tty/tty_mutex.c @@ -4,29 +4,70 @@ #include #include -/* - * The 'big tty mutex' - * - * This mutex is taken and released by tty_lock() and tty_unlock(), - * replacing the older big kernel lock. - * It can no longer be taken recursively, and does not get - * released implicitly while sleeping. - * - * Don't use in new code. - */ -static DEFINE_MUTEX(big_tty_mutex); +/* Legacy tty mutex glue */ + +enum { + TTY_MUTEX_NORMAL, + TTY_MUTEX_NESTED, +}; /* * Getting the big tty mutex. */ -void __lockfunc tty_lock(void) + +static void __lockfunc tty_lock_nested(struct tty_struct *tty, + unsigned int subclass) { - mutex_lock(&big_tty_mutex); + if (tty->magic != TTY_MAGIC) { + printk(KERN_ERR "L Bad %p\n", tty); + WARN_ON(1); + return; + } + tty_kref_get(tty); + mutex_lock_nested(&tty->legacy_mutex, subclass); +} + +void __lockfunc tty_lock(struct tty_struct *tty) +{ + return tty_lock_nested(tty, TTY_MUTEX_NORMAL); } EXPORT_SYMBOL(tty_lock); -void __lockfunc tty_unlock(void) +void __lockfunc tty_unlock(struct tty_struct *tty) { - mutex_unlock(&big_tty_mutex); + if (tty->magic != TTY_MAGIC) { + printk(KERN_ERR "U Bad %p\n", tty); + WARN_ON(1); + return; + } + mutex_unlock(&tty->legacy_mutex); + tty_kref_put(tty); } EXPORT_SYMBOL(tty_unlock); + +/* + * Getting the big tty mutex for a pair of ttys with lock ordering + * On a non pty/tty pair tty2 can be NULL which is just fine. + */ +void __lockfunc tty_lock_pair(struct tty_struct *tty, + struct tty_struct *tty2) +{ + if (tty < tty2) { + tty_lock(tty); + tty_lock_nested(tty2, TTY_MUTEX_NESTED); + } else { + if (tty2 && tty2 != tty) + tty_lock(tty2); + tty_lock_nested(tty, TTY_MUTEX_NESTED); + } +} +EXPORT_SYMBOL(tty_lock_pair); + +void __lockfunc tty_unlock_pair(struct tty_struct *tty, + struct tty_struct *tty2) +{ + tty_unlock(tty); + if (tty2 && tty2 != tty) + tty_unlock(tty2); +} +EXPORT_SYMBOL(tty_unlock_pair); diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index edcb827c1286..5246763cff0c 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -239,7 +239,7 @@ int tty_port_block_til_ready(struct tty_port *port, /* block if port is in the process of being closed */ if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { - wait_event_interruptible_tty(port->close_wait, + wait_event_interruptible_tty(tty, port->close_wait, !(port->flags & ASYNC_CLOSING)); if (port->flags & ASYNC_HUP_NOTIFY) return -EAGAIN; @@ -305,9 +305,9 @@ int tty_port_block_til_ready(struct tty_port *port, retval = -ERESTARTSYS; break; } - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } finish_wait(&port->open_wait, &wait); diff --git a/include/linux/tty.h b/include/linux/tty.h index a39e72325e78..acca24bf06a7 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -268,6 +268,7 @@ struct tty_struct { struct mutex ldisc_mutex; struct tty_ldisc *ldisc; + struct mutex legacy_mutex; struct mutex termios_mutex; spinlock_t ctrl_lock; /* Termios values are protected by the termios mutex */ @@ -609,8 +610,12 @@ extern long vt_compat_ioctl(struct tty_struct *tty, /* tty_mutex.c */ /* functions for preparation of BKL removal */ -extern void __lockfunc tty_lock(void) __acquires(tty_lock); -extern void __lockfunc tty_unlock(void) __releases(tty_lock); +extern void __lockfunc tty_lock(struct tty_struct *tty); +extern void __lockfunc tty_unlock(struct tty_struct *tty); +extern void __lockfunc tty_lock_pair(struct tty_struct *tty, + struct tty_struct *tty2); +extern void __lockfunc tty_unlock_pair(struct tty_struct *tty, + struct tty_struct *tty2); /* * this shall be called only from where BTM is held (like close) @@ -625,9 +630,9 @@ extern void __lockfunc tty_unlock(void) __releases(tty_lock); static inline void tty_wait_until_sent_from_close(struct tty_struct *tty, long timeout) { - tty_unlock(); /* tty->ops->close holds the BTM, drop it while waiting */ + tty_unlock(tty); /* tty->ops->close holds the BTM, drop it while waiting */ tty_wait_until_sent(tty, timeout); - tty_lock(); + tty_lock(tty); } /* @@ -642,16 +647,16 @@ static inline void tty_wait_until_sent_from_close(struct tty_struct *tty, * * Do not use in new code. */ -#define wait_event_interruptible_tty(wq, condition) \ +#define wait_event_interruptible_tty(tty, wq, condition) \ ({ \ int __ret = 0; \ if (!(condition)) { \ - __wait_event_interruptible_tty(wq, condition, __ret); \ + __wait_event_interruptible_tty(tty, wq, condition, __ret); \ } \ __ret; \ }) -#define __wait_event_interruptible_tty(wq, condition, ret) \ +#define __wait_event_interruptible_tty(tty, wq, condition, ret) \ do { \ DEFINE_WAIT(__wait); \ \ @@ -660,9 +665,9 @@ do { \ if (condition) \ break; \ if (!signal_pending(current)) { \ - tty_unlock(); \ + tty_unlock(tty); \ schedule(); \ - tty_lock(); \ + tty_lock(tty); \ continue; \ } \ ret = -ERESTARTSYS; \ diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 87ddd051881b..b54c86a2e66b 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -705,9 +705,9 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp) break; } - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); remove_wait_queue(&dev->wait, &wait); -- cgit v1.2.3 From 7f0bc6a68ed93f3b4ad77b94df5ef32446c583e3 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 7 Aug 2012 21:47:42 +0200 Subject: TTY: pass flags to alloc_tty_driver We need to allow drivers that use neither tty_port_install nor tty_port_register_device to link a tty_port to a tty somehow. To avoid a race with open, this has to be performed before tty_register_device. But currently tty_driver->ports is allocated even in tty_register_device because we do not know whether this is the PTY driver. The PTY driver is special here due to an excessive count of lines it declares to handle. We cannot handle tty_ports there this way. To circumvent this, we start passing tty_driver flags to alloc_tty_driver already and we create tty_alloc_driver for this purpose. There we can allocate tty_driver->ports and do all the magic between tty_alloc_driver and tty_register_device. Later we will introduce tty_port_link_device function for that purpose. All drivers should eventually switch to this new tty driver allocation interface. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 34 +++++++++++++++++++++++++--------- include/linux/tty_driver.h | 23 +++++++++++++++++++---- 2 files changed, 44 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 690224483fab..098a7c72b640 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -3061,21 +3061,37 @@ void tty_unregister_device(struct tty_driver *driver, unsigned index) } EXPORT_SYMBOL(tty_unregister_device); -struct tty_driver *__alloc_tty_driver(int lines, struct module *owner) +/** + * __tty_alloc_driver -- allocate tty driver + * @lines: count of lines this driver can handle at most + * @owner: module which is repsonsible for this driver + * @flags: some of TTY_DRIVER_* flags, will be set in driver->flags + * + * This should not be called directly, some of the provided macros should be + * used instead. Use IS_ERR and friends on @retval. + */ +struct tty_driver *__tty_alloc_driver(unsigned int lines, struct module *owner, + unsigned long flags) { struct tty_driver *driver; + if (!lines) + return ERR_PTR(-EINVAL); + driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL); - if (driver) { - kref_init(&driver->kref); - driver->magic = TTY_DRIVER_MAGIC; - driver->num = lines; - driver->owner = owner; - /* later we'll move allocation of tables here */ - } + if (!driver) + return ERR_PTR(-ENOMEM); + + kref_init(&driver->kref); + driver->magic = TTY_DRIVER_MAGIC; + driver->num = lines; + driver->owner = owner; + driver->flags = flags; + /* later we'll move allocation of tables here */ + return driver; } -EXPORT_SYMBOL(__alloc_tty_driver); +EXPORT_SYMBOL(__tty_alloc_driver); static void destruct_tty_driver(struct kref *kref) { diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 80e72dc564a5..3adc362f0bd2 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -296,11 +296,11 @@ struct tty_driver { int name_base; /* offset of printed name */ int major; /* major device number */ int minor_start; /* start of minor device number */ - int num; /* number of devices allocated */ + unsigned int num; /* number of devices allocated */ short type; /* type of tty driver */ short subtype; /* subtype of tty driver */ struct ktermios init_termios; /* Initial termios */ - int flags; /* tty driver flags */ + unsigned long flags; /* tty driver flags */ struct proc_dir_entry *proc_entry; /* /proc fs entry */ struct tty_driver *other; /* only used for the PTY driver */ @@ -322,7 +322,8 @@ struct tty_driver { extern struct list_head tty_drivers; -extern struct tty_driver *__alloc_tty_driver(int lines, struct module *owner); +extern struct tty_driver *__tty_alloc_driver(unsigned int lines, + struct module *owner, unsigned long flags); extern void put_tty_driver(struct tty_driver *driver); extern void tty_set_operations(struct tty_driver *driver, const struct tty_operations *op); @@ -330,7 +331,21 @@ extern struct tty_driver *tty_find_polling_driver(char *name, int *line); extern void tty_driver_kref_put(struct tty_driver *driver); -#define alloc_tty_driver(lines) __alloc_tty_driver(lines, THIS_MODULE) +/* Use TTY_DRIVER_* flags below */ +#define tty_alloc_driver(lines, flags) \ + __tty_alloc_driver(lines, THIS_MODULE, flags) + +/* + * DEPRECATED Do not use this in new code, use tty_alloc_driver instead. + * (And change the return value checks.) + */ +static inline struct tty_driver *alloc_tty_driver(unsigned int lines) +{ + struct tty_driver *ret = tty_alloc_driver(lines, 0); + if (IS_ERR(ret)) + return NULL; + return ret; +} static inline struct tty_driver *tty_driver_kref_get(struct tty_driver *d) { -- cgit v1.2.3 From b5ec8eeac46a99004c26791f70b15d001e970acf Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 10 Aug 2012 02:22:47 +0000 Subject: ipv4: fix ip_send_skb() ip_send_skb() can send orphaned skb, so we must pass the net pointer to avoid possible NULL dereference in error path. Bug added by commit 3a7c384ffd57 (ipv4: tcp: unicast_sock should not land outside of TCP stack) Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/ip.h | 2 +- net/ipv4/ip_output.c | 5 ++--- net/ipv4/udp.c | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/ip.h b/include/net/ip.h index bd5e444a19ce..5a5d84d3d2c6 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -120,7 +120,7 @@ extern struct sk_buff *__ip_make_skb(struct sock *sk, struct flowi4 *fl4, struct sk_buff_head *queue, struct inet_cork *cork); -extern int ip_send_skb(struct sk_buff *skb); +extern int ip_send_skb(struct net *net, struct sk_buff *skb); extern int ip_push_pending_frames(struct sock *sk, struct flowi4 *fl4); extern void ip_flush_pending_frames(struct sock *sk); extern struct sk_buff *ip_make_skb(struct sock *sk, diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index ec410e08b4b9..147ccc3e93db 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -1366,9 +1366,8 @@ out: return skb; } -int ip_send_skb(struct sk_buff *skb) +int ip_send_skb(struct net *net, struct sk_buff *skb) { - struct net *net = sock_net(skb->sk); int err; err = ip_local_out(skb); @@ -1391,7 +1390,7 @@ int ip_push_pending_frames(struct sock *sk, struct flowi4 *fl4) return 0; /* Netfilter gets whole the not fragmented skb. */ - return ip_send_skb(skb); + return ip_send_skb(sock_net(sk), skb); } /* diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index b4c3582a991f..6f6d1aca3c3d 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -758,7 +758,7 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4) uh->check = CSUM_MANGLED_0; send: - err = ip_send_skb(skb); + err = ip_send_skb(sock_net(sk), skb); if (err) { if (err == -ENOBUFS && !inet->recverr) { UDP_INC_STATS_USER(sock_net(sk), -- cgit v1.2.3 From 2359a47671fc4fb0fe5e9945f76c2cb10792c0f8 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 29 Jul 2012 20:52:21 +0000 Subject: codel: refine one condition to avoid a nul rec_inv_sqrt One condition before codel_Newton_step() was not good if we never left the dropping state for a flow. As a result rec_inv_sqrt was 0, instead of the ~0 initial value. codel control law was then set to a very aggressive mode, dropping many packets before reaching 'target' and recovering from this problem. To keep codel_vars_init() as efficient as possible, refine the condition to make sure rec_inv_sqrt initial value is correct Many thanks to Anton Mich for discovering the issue and suggesting a fix. Reported-by: Anton Mich Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/codel.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/codel.h b/include/net/codel.h index 550debfc2403..389cf621161d 100644 --- a/include/net/codel.h +++ b/include/net/codel.h @@ -305,6 +305,8 @@ static struct sk_buff *codel_dequeue(struct Qdisc *sch, } } } else if (drop) { + u32 delta; + if (params->ecn && INET_ECN_set_ce(skb)) { stats->ecn_mark++; } else { @@ -320,9 +322,11 @@ static struct sk_buff *codel_dequeue(struct Qdisc *sch, * assume that the drop rate that controlled the queue on the * last cycle is a good starting point to control it now. */ - if (codel_time_before(now - vars->drop_next, + delta = vars->count - vars->lastcount; + if (delta > 1 && + codel_time_before(now - vars->drop_next, 16 * params->interval)) { - vars->count = (vars->count - vars->lastcount) | 1; + vars->count = delta; /* we dont care if rec_inv_sqrt approximation * is not very precise : * Next Newton steps will correct it quadratically. -- cgit v1.2.3 From 651728507140246e9cf4e809f1877cb2eddbc84e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 13 Aug 2012 10:56:43 +0200 Subject: pinctrl/coh901: move header to platform data dir Move the platform-specific COH901 pin control header out of the ARM tree and down into the proper platform data include directory. Signed-off-by: Linus Walleij --- arch/arm/mach-u300/core.c | 2 +- arch/arm/mach-u300/include/mach/gpio-u300.h | 26 -------------------------- drivers/pinctrl/pinctrl-coh901.c | 2 +- include/linux/platform_data/pinctrl-coh901.h | 26 ++++++++++++++++++++++++++ 4 files changed, 28 insertions(+), 28 deletions(-) delete mode 100644 arch/arm/mach-u300/include/mach/gpio-u300.h create mode 100644 include/linux/platform_data/pinctrl-coh901.h (limited to 'include') diff --git a/arch/arm/mach-u300/core.c b/arch/arm/mach-u300/core.c index 883d5c26d50b..0158189a4dab 100644 --- a/arch/arm/mach-u300/core.c +++ b/arch/arm/mach-u300/core.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -43,7 +44,6 @@ #include #include #include -#include #include "spi.h" #include "i2c.h" diff --git a/arch/arm/mach-u300/include/mach/gpio-u300.h b/arch/arm/mach-u300/include/mach/gpio-u300.h deleted file mode 100644 index 30dea251b835..000000000000 --- a/arch/arm/mach-u300/include/mach/gpio-u300.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2007-2012 ST-Ericsson AB - * License terms: GNU General Public License (GPL) version 2 - * GPIO block resgister definitions and inline macros for - * U300 GPIO COH 901 335 or COH 901 571/3 - * Author: Linus Walleij - */ - -#ifndef __MACH_U300_GPIO_U300_H -#define __MACH_U300_GPIO_U300_H - -/** - * struct u300_gpio_platform - U300 GPIO platform data - * @ports: number of GPIO block ports - * @gpio_base: first GPIO number for this block (use a free range) - * @gpio_irq_base: first GPIO IRQ number for this block (use a free range) - * @pinctrl_device: pin control device to spawn as child - */ -struct u300_gpio_platform { - u8 ports; - int gpio_base; - int gpio_irq_base; - struct platform_device *pinctrl_device; -}; - -#endif /* __MACH_U300_GPIO_U300_H */ diff --git a/drivers/pinctrl/pinctrl-coh901.c b/drivers/pinctrl/pinctrl-coh901.c index 91a5dc7f6766..b446c9641212 100644 --- a/drivers/pinctrl/pinctrl-coh901.c +++ b/drivers/pinctrl/pinctrl-coh901.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include "pinctrl-coh901.h" #define U300_GPIO_PORT_STRIDE (0x30) diff --git a/include/linux/platform_data/pinctrl-coh901.h b/include/linux/platform_data/pinctrl-coh901.h new file mode 100644 index 000000000000..30dea251b835 --- /dev/null +++ b/include/linux/platform_data/pinctrl-coh901.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2007-2012 ST-Ericsson AB + * License terms: GNU General Public License (GPL) version 2 + * GPIO block resgister definitions and inline macros for + * U300 GPIO COH 901 335 or COH 901 571/3 + * Author: Linus Walleij + */ + +#ifndef __MACH_U300_GPIO_U300_H +#define __MACH_U300_GPIO_U300_H + +/** + * struct u300_gpio_platform - U300 GPIO platform data + * @ports: number of GPIO block ports + * @gpio_base: first GPIO number for this block (use a free range) + * @gpio_irq_base: first GPIO IRQ number for this block (use a free range) + * @pinctrl_device: pin control device to spawn as child + */ +struct u300_gpio_platform { + u8 ports; + int gpio_base; + int gpio_irq_base; + struct platform_device *pinctrl_device; +}; + +#endif /* __MACH_U300_GPIO_U300_H */ -- cgit v1.2.3 From 2f292004dd1fb005788dc0a9cdd5559812ed866e Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 6 Aug 2012 10:03:59 -0400 Subject: drm/radeon: add some new SI pci ids Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org --- include/drm/drm_pciids.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/drm/drm_pciids.h b/include/drm/drm_pciids.h index 7ff5c99b1638..c78bb997e2c6 100644 --- a/include/drm/drm_pciids.h +++ b/include/drm/drm_pciids.h @@ -213,9 +213,12 @@ {0x1002, 0x6800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6801, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6802, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6806, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6808, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6809, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6810, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6816, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6817, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6818, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6819, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6820, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ -- cgit v1.2.3 From 6759a0a7a0496dbbd4fb062c6a76d61c55d0fbd9 Mon Sep 17 00:00:00 2001 From: Marek Olšák Date: Thu, 9 Aug 2012 16:34:17 +0200 Subject: drm/radeon/kms: implement timestamp userspace query (v2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Returns a snapshot of the GPU clock counter. Needed for certain OpenGL extensions. v2: agd5f - address Jerome's comments - add function documentation Signed-off-by: Marek Olšák Reviewed-by: Jerome Glisse Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/r600.c | 20 +++++++++++++++++++ drivers/gpu/drm/radeon/r600d.h | 3 +++ drivers/gpu/drm/radeon/radeon.h | 1 + drivers/gpu/drm/radeon/radeon_asic.h | 2 ++ drivers/gpu/drm/radeon/radeon_device.c | 1 + drivers/gpu/drm/radeon/radeon_drv.c | 3 ++- drivers/gpu/drm/radeon/radeon_kms.c | 35 ++++++++++++++++++++++++++++------ drivers/gpu/drm/radeon/si.c | 19 ++++++++++++++++++ drivers/gpu/drm/radeon/sid.h | 3 +++ include/drm/radeon_drm.h | 2 ++ 10 files changed, 82 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 637280f541a3..d79c639ae739 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -3789,3 +3789,23 @@ static void r600_pcie_gen2_enable(struct radeon_device *rdev) WREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); } } + +/** + * r600_get_gpu_clock - return GPU clock counter snapshot + * + * @rdev: radeon_device pointer + * + * Fetches a GPU clock counter snapshot (R6xx-cayman). + * Returns the 64 bit clock counter snapshot. + */ +uint64_t r600_get_gpu_clock(struct radeon_device *rdev) +{ + uint64_t clock; + + mutex_lock(&rdev->gpu_clock_mutex); + WREG32(RLC_CAPTURE_GPU_CLOCK_COUNT, 1); + clock = (uint64_t)RREG32(RLC_GPU_CLOCK_COUNT_LSB) | + ((uint64_t)RREG32(RLC_GPU_CLOCK_COUNT_MSB) << 32ULL); + mutex_unlock(&rdev->gpu_clock_mutex); + return clock; +} diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h index 4b116ae75fc2..fd328f4c3ea8 100644 --- a/drivers/gpu/drm/radeon/r600d.h +++ b/drivers/gpu/drm/radeon/r600d.h @@ -602,6 +602,9 @@ #define RLC_HB_WPTR 0x3f1c #define RLC_HB_WPTR_LSB_ADDR 0x3f14 #define RLC_HB_WPTR_MSB_ADDR 0x3f18 +#define RLC_GPU_CLOCK_COUNT_LSB 0x3f38 +#define RLC_GPU_CLOCK_COUNT_MSB 0x3f3c +#define RLC_CAPTURE_GPU_CLOCK_COUNT 0x3f40 #define RLC_MC_CNTL 0x3f44 #define RLC_UCODE_CNTL 0x3f48 #define RLC_UCODE_ADDR 0x3f2c diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index b237a29142d1..99304194a65c 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1534,6 +1534,7 @@ struct radeon_device { unsigned debugfs_count; /* virtual memory */ struct radeon_vm_manager vm_manager; + struct mutex gpu_clock_mutex; }; int radeon_device_init(struct radeon_device *rdev, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 0d445e7d00d1..18c38d14c8cd 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -368,6 +368,7 @@ void r600_kms_blit_copy(struct radeon_device *rdev, unsigned num_gpu_pages, struct radeon_sa_bo *vb); int r600_mc_wait_for_idle(struct radeon_device *rdev); +uint64_t r600_get_gpu_clock(struct radeon_device *rdev); /* * rv770,rv730,rv710,rv740 @@ -468,5 +469,6 @@ int si_vm_bind(struct radeon_device *rdev, struct radeon_vm *vm, int id); void si_vm_unbind(struct radeon_device *rdev, struct radeon_vm *vm); void si_vm_tlb_flush(struct radeon_device *rdev, struct radeon_vm *vm); int si_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib); +uint64_t si_get_gpu_clock(struct radeon_device *rdev); #endif diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 742af8244e89..d2e243867ac6 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -1009,6 +1009,7 @@ int radeon_device_init(struct radeon_device *rdev, atomic_set(&rdev->ih.lock, 0); mutex_init(&rdev->gem.mutex); mutex_init(&rdev->pm.mutex); + mutex_init(&rdev->gpu_clock_mutex); init_rwsem(&rdev->pm.mclk_lock); init_rwsem(&rdev->exclusive_lock); init_waitqueue_head(&rdev->irq.vblank_queue); diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index a7f8ac0d8f03..d7269f48d37c 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -61,9 +61,10 @@ * 2.17.0 - add STRMOUT_BASE_UPDATE for r7xx * 2.18.0 - r600-eg: allow "invalid" DB formats * 2.19.0 - r600-eg: MSAA textures + * 2.20.0 - r600-si: RADEON_INFO_TIMESTAMP query */ #define KMS_DRIVER_MAJOR 2 -#define KMS_DRIVER_MINOR 19 +#define KMS_DRIVER_MINOR 20 #define KMS_DRIVER_PATCHLEVEL 0 int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags); int radeon_driver_unload_kms(struct drm_device *dev); diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 1d73f16b5d97..414b4acf6947 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -29,6 +29,7 @@ #include "drm_sarea.h" #include "radeon.h" #include "radeon_drm.h" +#include "radeon_asic.h" #include #include @@ -167,17 +168,39 @@ static void radeon_set_filp_rights(struct drm_device *dev, int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { struct radeon_device *rdev = dev->dev_private; - struct drm_radeon_info *info; + struct drm_radeon_info *info = data; struct radeon_mode_info *minfo = &rdev->mode_info; - uint32_t *value_ptr; - uint32_t value; + uint32_t value, *value_ptr; + uint64_t value64, *value_ptr64; struct drm_crtc *crtc; int i, found; - info = data; + /* TIMESTAMP is a 64-bit value, needs special handling. */ + if (info->request == RADEON_INFO_TIMESTAMP) { + if (rdev->family >= CHIP_R600) { + value_ptr64 = (uint64_t*)((unsigned long)info->value); + if (rdev->family >= CHIP_TAHITI) { + value64 = si_get_gpu_clock(rdev); + } else { + value64 = r600_get_gpu_clock(rdev); + } + + if (DRM_COPY_TO_USER(value_ptr64, &value64, sizeof(value64))) { + DRM_ERROR("copy_to_user %s:%u\n", __func__, __LINE__); + return -EFAULT; + } + return 0; + } else { + DRM_DEBUG_KMS("timestamp is r6xx+ only!\n"); + return -EINVAL; + } + } + value_ptr = (uint32_t *)((unsigned long)info->value); - if (DRM_COPY_FROM_USER(&value, value_ptr, sizeof(value))) + if (DRM_COPY_FROM_USER(&value, value_ptr, sizeof(value))) { + DRM_ERROR("copy_from_user %s:%u\n", __func__, __LINE__); return -EFAULT; + } switch (info->request) { case RADEON_INFO_DEVICE_ID: @@ -337,7 +360,7 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) return -EINVAL; } if (DRM_COPY_TO_USER(value_ptr, &value, sizeof(uint32_t))) { - DRM_ERROR("copy_to_user\n"); + DRM_ERROR("copy_to_user %s:%u\n", __func__, __LINE__); return -EFAULT; } return 0; diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index c153a7f359c8..0139e227e3c7 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -3968,3 +3968,22 @@ void si_fini(struct radeon_device *rdev) rdev->bios = NULL; } +/** + * si_get_gpu_clock - return GPU clock counter snapshot + * + * @rdev: radeon_device pointer + * + * Fetches a GPU clock counter snapshot (SI). + * Returns the 64 bit clock counter snapshot. + */ +uint64_t si_get_gpu_clock(struct radeon_device *rdev) +{ + uint64_t clock; + + mutex_lock(&rdev->gpu_clock_mutex); + WREG32(RLC_CAPTURE_GPU_CLOCK_COUNT, 1); + clock = (uint64_t)RREG32(RLC_GPU_CLOCK_COUNT_LSB) | + ((uint64_t)RREG32(RLC_GPU_CLOCK_COUNT_MSB) << 32ULL); + mutex_unlock(&rdev->gpu_clock_mutex); + return clock; +} diff --git a/drivers/gpu/drm/radeon/sid.h b/drivers/gpu/drm/radeon/sid.h index 7869089e8761..ef4815c27b1c 100644 --- a/drivers/gpu/drm/radeon/sid.h +++ b/drivers/gpu/drm/radeon/sid.h @@ -698,6 +698,9 @@ #define RLC_UCODE_ADDR 0xC32C #define RLC_UCODE_DATA 0xC330 +#define RLC_GPU_CLOCK_COUNT_LSB 0xC338 +#define RLC_GPU_CLOCK_COUNT_MSB 0xC33C +#define RLC_CAPTURE_GPU_CLOCK_COUNT 0xC340 #define RLC_MC_CNTL 0xC344 #define RLC_UCODE_CNTL 0xC348 diff --git a/include/drm/radeon_drm.h b/include/drm/radeon_drm.h index 58056865b8e9..dc3a8cd7db8a 100644 --- a/include/drm/radeon_drm.h +++ b/include/drm/radeon_drm.h @@ -964,6 +964,8 @@ struct drm_radeon_cs { #define RADEON_INFO_IB_VM_MAX_SIZE 0x0f /* max pipes - needed for compute shaders */ #define RADEON_INFO_MAX_PIPES 0x10 +/* timestamp for GL_ARB_timer_query (OpenGL), returns the current GPU clock */ +#define RADEON_INFO_TIMESTAMP 0x11 struct drm_radeon_info { uint32_t request; -- cgit v1.2.3 From 2a1d446019f9a5983ec5a335b95e8593fdb6fa2e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 16 Jul 2012 10:42:36 +0000 Subject: kthread: Implement park/unpark facility To avoid the full teardown/setup of per cpu kthreads in the case of cpu hot(un)plug, provide a facility which allows to put the kthread into a park position and unpark it when the cpu comes online again. Signed-off-by: Thomas Gleixner Reviewed-by: Namhyung Kim Cc: Peter Zijlstra Reviewed-by: Srivatsa S. Bhat Cc: Rusty Russell Reviewed-by: Paul E. McKenney Link: http://lkml.kernel.org/r/20120716103948.236618824@linutronix.de Signed-off-by: Thomas Gleixner --- include/linux/kthread.h | 11 ++- kernel/kthread.c | 185 +++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 176 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/linux/kthread.h b/include/linux/kthread.h index 22ccf9dee177..8d816646f766 100644 --- a/include/linux/kthread.h +++ b/include/linux/kthread.h @@ -14,6 +14,11 @@ struct task_struct *kthread_create_on_node(int (*threadfn)(void *data), kthread_create_on_node(threadfn, data, -1, namefmt, ##arg) +struct task_struct *kthread_create_on_cpu(int (*threadfn)(void *data), + void *data, + unsigned int cpu, + const char *namefmt); + /** * kthread_run - create and wake a thread. * @threadfn: the function to run until signal_pending(current). @@ -34,9 +39,13 @@ struct task_struct *kthread_create_on_node(int (*threadfn)(void *data), void kthread_bind(struct task_struct *k, unsigned int cpu); int kthread_stop(struct task_struct *k); -int kthread_should_stop(void); +bool kthread_should_stop(void); +bool kthread_should_park(void); bool kthread_freezable_should_stop(bool *was_frozen); void *kthread_data(struct task_struct *k); +int kthread_park(struct task_struct *k); +void kthread_unpark(struct task_struct *k); +void kthread_parkme(void); int kthreadd(void *unused); extern struct task_struct *kthreadd_task; diff --git a/kernel/kthread.c b/kernel/kthread.c index b579af57ea10..146a6fa96825 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -37,11 +37,20 @@ struct kthread_create_info }; struct kthread { - int should_stop; + unsigned long flags; + unsigned int cpu; void *data; + struct completion parked; struct completion exited; }; +enum KTHREAD_BITS { + KTHREAD_IS_PER_CPU = 0, + KTHREAD_SHOULD_STOP, + KTHREAD_SHOULD_PARK, + KTHREAD_IS_PARKED, +}; + #define to_kthread(tsk) \ container_of((tsk)->vfork_done, struct kthread, exited) @@ -52,12 +61,28 @@ struct kthread { * and this will return true. You should then return, and your return * value will be passed through to kthread_stop(). */ -int kthread_should_stop(void) +bool kthread_should_stop(void) { - return to_kthread(current)->should_stop; + return test_bit(KTHREAD_SHOULD_STOP, &to_kthread(current)->flags); } EXPORT_SYMBOL(kthread_should_stop); +/** + * kthread_should_park - should this kthread park now? + * + * When someone calls kthread_park() on your kthread, it will be woken + * and this will return true. You should then do the necessary + * cleanup and call kthread_parkme() + * + * Similar to kthread_should_stop(), but this keeps the thread alive + * and in a park position. kthread_unpark() "restarts" the thread and + * calls the thread function again. + */ +bool kthread_should_park(void) +{ + return test_bit(KTHREAD_SHOULD_PARK, &to_kthread(current)->flags); +} + /** * kthread_freezable_should_stop - should this freezable kthread return now? * @was_frozen: optional out parameter, indicates whether %current was frozen @@ -96,6 +121,24 @@ void *kthread_data(struct task_struct *task) return to_kthread(task)->data; } +static void __kthread_parkme(struct kthread *self) +{ + __set_current_state(TASK_INTERRUPTIBLE); + while (test_bit(KTHREAD_SHOULD_PARK, &self->flags)) { + if (!test_and_set_bit(KTHREAD_IS_PARKED, &self->flags)) + complete(&self->parked); + schedule(); + __set_current_state(TASK_INTERRUPTIBLE); + } + clear_bit(KTHREAD_IS_PARKED, &self->flags); + __set_current_state(TASK_RUNNING); +} + +void kthread_parkme(void) +{ + __kthread_parkme(to_kthread(current)); +} + static int kthread(void *_create) { /* Copy data: it's on kthread's stack */ @@ -105,9 +148,10 @@ static int kthread(void *_create) struct kthread self; int ret; - self.should_stop = 0; + self.flags = 0; self.data = data; init_completion(&self.exited); + init_completion(&self.parked); current->vfork_done = &self.exited; /* OK, tell user we're spawned, wait for stop or wakeup */ @@ -117,9 +161,11 @@ static int kthread(void *_create) schedule(); ret = -EINTR; - if (!self.should_stop) - ret = threadfn(data); + if (!test_bit(KTHREAD_SHOULD_STOP, &self.flags)) { + __kthread_parkme(&self); + ret = threadfn(data); + } /* we can't just return, we must preserve "self" on stack */ do_exit(ret); } @@ -172,8 +218,7 @@ static void create_kthread(struct kthread_create_info *create) * Returns a task_struct or ERR_PTR(-ENOMEM). */ struct task_struct *kthread_create_on_node(int (*threadfn)(void *data), - void *data, - int node, + void *data, int node, const char namefmt[], ...) { @@ -210,6 +255,13 @@ struct task_struct *kthread_create_on_node(int (*threadfn)(void *data), } EXPORT_SYMBOL(kthread_create_on_node); +static void __kthread_bind(struct task_struct *p, unsigned int cpu) +{ + /* It's safe because the task is inactive. */ + do_set_cpus_allowed(p, cpumask_of(cpu)); + p->flags |= PF_THREAD_BOUND; +} + /** * kthread_bind - bind a just-created kthread to a cpu. * @p: thread created by kthread_create(). @@ -226,13 +278,111 @@ void kthread_bind(struct task_struct *p, unsigned int cpu) WARN_ON(1); return; } - - /* It's safe because the task is inactive. */ - do_set_cpus_allowed(p, cpumask_of(cpu)); - p->flags |= PF_THREAD_BOUND; + __kthread_bind(p, cpu); } EXPORT_SYMBOL(kthread_bind); +/** + * kthread_create_on_cpu - Create a cpu bound kthread + * @threadfn: the function to run until signal_pending(current). + * @data: data ptr for @threadfn. + * @cpu: The cpu on which the thread should be bound, + * @namefmt: printf-style name for the thread. Format is restricted + * to "name.*%u". Code fills in cpu number. + * + * Description: This helper function creates and names a kernel thread + * The thread will be woken and put into park mode. + */ +struct task_struct *kthread_create_on_cpu(int (*threadfn)(void *data), + void *data, unsigned int cpu, + const char *namefmt) +{ + struct task_struct *p; + + p = kthread_create_on_node(threadfn, data, cpu_to_node(cpu), namefmt, + cpu); + if (IS_ERR(p)) + return p; + set_bit(KTHREAD_IS_PER_CPU, &to_kthread(p)->flags); + to_kthread(p)->cpu = cpu; + /* Park the thread to get it out of TASK_UNINTERRUPTIBLE state */ + kthread_park(p); + return p; +} + +static struct kthread *task_get_live_kthread(struct task_struct *k) +{ + struct kthread *kthread; + + get_task_struct(k); + kthread = to_kthread(k); + /* It might have exited */ + barrier(); + if (k->vfork_done != NULL) + return kthread; + return NULL; +} + +/** + * kthread_unpark - unpark a thread created by kthread_create(). + * @k: thread created by kthread_create(). + * + * Sets kthread_should_park() for @k to return false, wakes it, and + * waits for it to return. If the thread is marked percpu then its + * bound to the cpu again. + */ +void kthread_unpark(struct task_struct *k) +{ + struct kthread *kthread = task_get_live_kthread(k); + + if (kthread) { + clear_bit(KTHREAD_SHOULD_PARK, &kthread->flags); + /* + * We clear the IS_PARKED bit here as we don't wait + * until the task has left the park code. So if we'd + * park before that happens we'd see the IS_PARKED bit + * which might be about to be cleared. + */ + if (test_and_clear_bit(KTHREAD_IS_PARKED, &kthread->flags)) { + if (test_bit(KTHREAD_IS_PER_CPU, &kthread->flags)) + __kthread_bind(k, kthread->cpu); + wake_up_process(k); + } + } + put_task_struct(k); +} + +/** + * kthread_park - park a thread created by kthread_create(). + * @k: thread created by kthread_create(). + * + * Sets kthread_should_park() for @k to return true, wakes it, and + * waits for it to return. This can also be called after kthread_create() + * instead of calling wake_up_process(): the thread will park without + * calling threadfn(). + * + * Returns 0 if the thread is parked, -ENOSYS if the thread exited. + * If called by the kthread itself just the park bit is set. + */ +int kthread_park(struct task_struct *k) +{ + struct kthread *kthread = task_get_live_kthread(k); + int ret = -ENOSYS; + + if (kthread) { + if (!test_bit(KTHREAD_IS_PARKED, &kthread->flags)) { + set_bit(KTHREAD_SHOULD_PARK, &kthread->flags); + if (k != current) { + wake_up_process(k); + wait_for_completion(&kthread->parked); + } + } + ret = 0; + } + put_task_struct(k); + return ret; +} + /** * kthread_stop - stop a thread created by kthread_create(). * @k: thread created by kthread_create(). @@ -250,16 +400,13 @@ EXPORT_SYMBOL(kthread_bind); */ int kthread_stop(struct task_struct *k) { - struct kthread *kthread; + struct kthread *kthread = task_get_live_kthread(k); int ret; trace_sched_kthread_stop(k); - get_task_struct(k); - - kthread = to_kthread(k); - barrier(); /* it might have exited */ - if (k->vfork_done != NULL) { - kthread->should_stop = 1; + if (kthread) { + set_bit(KTHREAD_SHOULD_STOP, &kthread->flags); + clear_bit(KTHREAD_SHOULD_PARK, &kthread->flags); wake_up_process(k); wait_for_completion(&kthread->exited); } -- cgit v1.2.3 From f97f8f06a49febbc3cb3635172efbe64ddc79700 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 16 Jul 2012 10:42:36 +0000 Subject: smpboot: Provide infrastructure for percpu hotplug threads Provide a generic interface for setting up and tearing down percpu threads. On registration the threads for already online cpus are created and started. On deregistration (modules) the threads are stoppped. During hotplug operations the threads are created, started, parked and unparked. The datastructure for registration provides a pointer to percpu storage space and optional setup, cleanup, park, unpark functions. These functions are called when the thread state changes. Each implementation has to provide a function which is queried and returns whether the thread should run and the thread function itself. The core code handles all state transitions and avoids duplicated code in the call sites. [ paulmck: Preemption leak fix ] Signed-off-by: Thomas Gleixner Cc: Peter Zijlstra Reviewed-by: Srivatsa S. Bhat Cc: Rusty Russell Reviewed-by: Paul E. McKenney Cc: Namhyung Kim Link: http://lkml.kernel.org/r/20120716103948.352501068@linutronix.de Signed-off-by: Thomas Gleixner --- include/linux/smpboot.h | 43 +++++++++ kernel/cpu.c | 10 ++- kernel/smpboot.c | 229 ++++++++++++++++++++++++++++++++++++++++++++++++ kernel/smpboot.h | 4 + 4 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 include/linux/smpboot.h (limited to 'include') diff --git a/include/linux/smpboot.h b/include/linux/smpboot.h new file mode 100644 index 000000000000..e0106d8581d3 --- /dev/null +++ b/include/linux/smpboot.h @@ -0,0 +1,43 @@ +#ifndef _LINUX_SMPBOOT_H +#define _LINUX_SMPBOOT_H + +#include + +struct task_struct; +/* Cookie handed to the thread_fn*/ +struct smpboot_thread_data; + +/** + * struct smp_hotplug_thread - CPU hotplug related thread descriptor + * @store: Pointer to per cpu storage for the task pointers + * @list: List head for core management + * @thread_should_run: Check whether the thread should run or not. Called with + * preemption disabled. + * @thread_fn: The associated thread function + * @setup: Optional setup function, called when the thread gets + * operational the first time + * @cleanup: Optional cleanup function, called when the thread + * should stop (module exit) + * @park: Optional park function, called when the thread is + * parked (cpu offline) + * @unpark: Optional unpark function, called when the thread is + * unparked (cpu online) + * @thread_comm: The base name of the thread + */ +struct smp_hotplug_thread { + struct task_struct __percpu **store; + struct list_head list; + int (*thread_should_run)(unsigned int cpu); + void (*thread_fn)(unsigned int cpu); + void (*setup)(unsigned int cpu); + void (*cleanup)(unsigned int cpu, bool online); + void (*park)(unsigned int cpu); + void (*unpark)(unsigned int cpu); + const char *thread_comm; +}; + +int smpboot_register_percpu_thread(struct smp_hotplug_thread *plug_thread); +void smpboot_unregister_percpu_thread(struct smp_hotplug_thread *plug_thread); +int smpboot_thread_schedule(void); + +#endif diff --git a/kernel/cpu.c b/kernel/cpu.c index 14d32588cccd..e615dfbcf794 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -280,12 +280,13 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen) __func__, cpu); goto out_release; } + smpboot_park_threads(cpu); err = __stop_machine(take_cpu_down, &tcd_param, cpumask_of(cpu)); if (err) { /* CPU didn't die: tell everyone. Can't complain. */ + smpboot_unpark_threads(cpu); cpu_notify_nofail(CPU_DOWN_FAILED | mod, hcpu); - goto out_release; } BUG_ON(cpu_online(cpu)); @@ -354,6 +355,10 @@ static int __cpuinit _cpu_up(unsigned int cpu, int tasks_frozen) goto out; } + ret = smpboot_create_threads(cpu); + if (ret) + goto out; + ret = __cpu_notify(CPU_UP_PREPARE | mod, hcpu, -1, &nr_calls); if (ret) { nr_calls--; @@ -368,6 +373,9 @@ static int __cpuinit _cpu_up(unsigned int cpu, int tasks_frozen) goto out_notify; BUG_ON(!cpu_online(cpu)); + /* Wake the per cpu threads */ + smpboot_unpark_threads(cpu); + /* Now call notifier in preparation. */ cpu_notify(CPU_ONLINE | mod, hcpu); diff --git a/kernel/smpboot.c b/kernel/smpboot.c index 98f60c5caa1b..9d5f7b04025d 100644 --- a/kernel/smpboot.c +++ b/kernel/smpboot.c @@ -1,11 +1,17 @@ /* * Common SMP CPU bringup/teardown functions */ +#include #include #include #include +#include +#include #include +#include #include +#include +#include #include "smpboot.h" @@ -65,3 +71,226 @@ void __init idle_threads_init(void) } } #endif + +static LIST_HEAD(hotplug_threads); +static DEFINE_MUTEX(smpboot_threads_lock); + +struct smpboot_thread_data { + unsigned int cpu; + unsigned int status; + struct smp_hotplug_thread *ht; +}; + +enum { + HP_THREAD_NONE = 0, + HP_THREAD_ACTIVE, + HP_THREAD_PARKED, +}; + +/** + * smpboot_thread_fn - percpu hotplug thread loop function + * @data: thread data pointer + * + * Checks for thread stop and park conditions. Calls the necessary + * setup, cleanup, park and unpark functions for the registered + * thread. + * + * Returns 1 when the thread should exit, 0 otherwise. + */ +static int smpboot_thread_fn(void *data) +{ + struct smpboot_thread_data *td = data; + struct smp_hotplug_thread *ht = td->ht; + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + preempt_disable(); + if (kthread_should_stop()) { + set_current_state(TASK_RUNNING); + preempt_enable(); + if (ht->cleanup) + ht->cleanup(td->cpu, cpu_online(td->cpu)); + kfree(td); + return 0; + } + + if (kthread_should_park()) { + __set_current_state(TASK_RUNNING); + preempt_enable(); + if (ht->park && td->status == HP_THREAD_ACTIVE) { + BUG_ON(td->cpu != smp_processor_id()); + ht->park(td->cpu); + td->status = HP_THREAD_PARKED; + } + kthread_parkme(); + /* We might have been woken for stop */ + continue; + } + + BUG_ON(td->cpu != smp_processor_id()); + + /* Check for state change setup */ + switch (td->status) { + case HP_THREAD_NONE: + preempt_enable(); + if (ht->setup) + ht->setup(td->cpu); + td->status = HP_THREAD_ACTIVE; + preempt_disable(); + break; + case HP_THREAD_PARKED: + preempt_enable(); + if (ht->unpark) + ht->unpark(td->cpu); + td->status = HP_THREAD_ACTIVE; + preempt_disable(); + break; + } + + if (!ht->thread_should_run(td->cpu)) { + preempt_enable(); + schedule(); + } else { + set_current_state(TASK_RUNNING); + preempt_enable(); + ht->thread_fn(td->cpu); + } + } +} + +static int +__smpboot_create_thread(struct smp_hotplug_thread *ht, unsigned int cpu) +{ + struct task_struct *tsk = *per_cpu_ptr(ht->store, cpu); + struct smpboot_thread_data *td; + + if (tsk) + return 0; + + td = kzalloc_node(sizeof(*td), GFP_KERNEL, cpu_to_node(cpu)); + if (!td) + return -ENOMEM; + td->cpu = cpu; + td->ht = ht; + + tsk = kthread_create_on_cpu(smpboot_thread_fn, td, cpu, + ht->thread_comm); + if (IS_ERR(tsk)) { + kfree(td); + return PTR_ERR(tsk); + } + + get_task_struct(tsk); + *per_cpu_ptr(ht->store, cpu) = tsk; + return 0; +} + +int smpboot_create_threads(unsigned int cpu) +{ + struct smp_hotplug_thread *cur; + int ret = 0; + + mutex_lock(&smpboot_threads_lock); + list_for_each_entry(cur, &hotplug_threads, list) { + ret = __smpboot_create_thread(cur, cpu); + if (ret) + break; + } + mutex_unlock(&smpboot_threads_lock); + return ret; +} + +static void smpboot_unpark_thread(struct smp_hotplug_thread *ht, unsigned int cpu) +{ + struct task_struct *tsk = *per_cpu_ptr(ht->store, cpu); + + kthread_unpark(tsk); +} + +void smpboot_unpark_threads(unsigned int cpu) +{ + struct smp_hotplug_thread *cur; + + mutex_lock(&smpboot_threads_lock); + list_for_each_entry(cur, &hotplug_threads, list) + smpboot_unpark_thread(cur, cpu); + mutex_unlock(&smpboot_threads_lock); +} + +static void smpboot_park_thread(struct smp_hotplug_thread *ht, unsigned int cpu) +{ + struct task_struct *tsk = *per_cpu_ptr(ht->store, cpu); + + if (tsk) + kthread_park(tsk); +} + +void smpboot_park_threads(unsigned int cpu) +{ + struct smp_hotplug_thread *cur; + + mutex_lock(&smpboot_threads_lock); + list_for_each_entry_reverse(cur, &hotplug_threads, list) + smpboot_park_thread(cur, cpu); + mutex_unlock(&smpboot_threads_lock); +} + +static void smpboot_destroy_threads(struct smp_hotplug_thread *ht) +{ + unsigned int cpu; + + /* We need to destroy also the parked threads of offline cpus */ + for_each_possible_cpu(cpu) { + struct task_struct *tsk = *per_cpu_ptr(ht->store, cpu); + + if (tsk) { + kthread_stop(tsk); + put_task_struct(tsk); + *per_cpu_ptr(ht->store, cpu) = NULL; + } + } +} + +/** + * smpboot_register_percpu_thread - Register a per_cpu thread related to hotplug + * @plug_thread: Hotplug thread descriptor + * + * Creates and starts the threads on all online cpus. + */ +int smpboot_register_percpu_thread(struct smp_hotplug_thread *plug_thread) +{ + unsigned int cpu; + int ret = 0; + + mutex_lock(&smpboot_threads_lock); + for_each_online_cpu(cpu) { + ret = __smpboot_create_thread(plug_thread, cpu); + if (ret) { + smpboot_destroy_threads(plug_thread); + goto out; + } + smpboot_unpark_thread(plug_thread, cpu); + } + list_add(&plug_thread->list, &hotplug_threads); +out: + mutex_unlock(&smpboot_threads_lock); + return ret; +} +EXPORT_SYMBOL_GPL(smpboot_register_percpu_thread); + +/** + * smpboot_unregister_percpu_thread - Unregister a per_cpu thread related to hotplug + * @plug_thread: Hotplug thread descriptor + * + * Stops all threads on all possible cpus. + */ +void smpboot_unregister_percpu_thread(struct smp_hotplug_thread *plug_thread) +{ + get_online_cpus(); + mutex_lock(&smpboot_threads_lock); + list_del(&plug_thread->list); + smpboot_destroy_threads(plug_thread); + mutex_unlock(&smpboot_threads_lock); + put_online_cpus(); +} +EXPORT_SYMBOL_GPL(smpboot_unregister_percpu_thread); diff --git a/kernel/smpboot.h b/kernel/smpboot.h index 6ef9433e1c70..72415a0eb955 100644 --- a/kernel/smpboot.h +++ b/kernel/smpboot.h @@ -13,4 +13,8 @@ static inline void idle_thread_set_boot_cpu(void) { } static inline void idle_threads_init(void) { } #endif +int smpboot_create_threads(unsigned int cpu); +void smpboot_park_threads(unsigned int cpu); +void smpboot_unpark_threads(unsigned int cpu); + #endif -- cgit v1.2.3 From 0bce9c46bf3b15f485d82d7e81dabed6ebcc24b1 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 10 Aug 2012 15:22:09 +0100 Subject: mutex: Place lock in contended state after fastpath_lock failure ARM recently moved to asm-generic/mutex-xchg.h for its mutex implementation after the previous implementation was found to be missing some crucial memory barriers. However, this has revealed some problems running hackbench on SMP platforms due to the way in which the MUTEX_SPIN_ON_OWNER code operates. The symptoms are that a bunch of hackbench tasks are left waiting on an unlocked mutex and therefore never get woken up to claim it. This boils down to the following sequence of events: Task A Task B Task C Lock value 0 1 1 lock() 0 2 lock() 0 3 spin(A) 0 4 unlock() 1 5 lock() 0 6 cmpxchg(1,0) 0 7 contended() -1 8 lock() 0 9 spin(C) 0 10 unlock() 1 11 cmpxchg(1,0) 0 12 unlock() 1 At this point, the lock is unlocked, but Task B is in an uninterruptible sleep with nobody to wake it up. This patch fixes the problem by ensuring we put the lock into the contended state if we fail to acquire it on the fastpath, ensuring that any blocked waiters are woken up when the mutex is released. Signed-off-by: Will Deacon Cc: Arnd Bergmann Cc: Chris Mason Cc: Ingo Molnar Cc: Reviewed-by: Nicolas Pitre Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-6e9lrw2avczr0617fzl5vqb8@git.kernel.org Signed-off-by: Thomas Gleixner --- include/asm-generic/mutex-xchg.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/asm-generic/mutex-xchg.h b/include/asm-generic/mutex-xchg.h index 580a6d35c700..c04e0db8a2d6 100644 --- a/include/asm-generic/mutex-xchg.h +++ b/include/asm-generic/mutex-xchg.h @@ -26,7 +26,13 @@ static inline void __mutex_fastpath_lock(atomic_t *count, void (*fail_fn)(atomic_t *)) { if (unlikely(atomic_xchg(count, 0) != 1)) - fail_fn(count); + /* + * We failed to acquire the lock, so mark it contended + * to ensure that any waiting tasks are woken up by the + * unlock slow path. + */ + if (likely(atomic_xchg(count, -1) != 1)) + fail_fn(count); } /** @@ -43,7 +49,8 @@ static inline int __mutex_fastpath_lock_retval(atomic_t *count, int (*fail_fn)(atomic_t *)) { if (unlikely(atomic_xchg(count, 0) != 1)) - return fail_fn(count); + if (likely(atomic_xchg(count, -1) != 1)) + return fail_fn(count); return 0; } -- cgit v1.2.3 From f03542a7019c600163ac4441d8a826c92c1bd510 Mon Sep 17 00:00:00 2001 From: Alex Shi Date: Thu, 26 Jul 2012 08:55:34 +0800 Subject: sched: recover SD_WAKE_AFFINE in select_task_rq_fair and code clean up Since power saving code was removed from sched now, the implement code is out of service in this function, and even pollute other logical. like, 'want_sd' never has chance to be set '0', that remove the effect of SD_WAKE_AFFINE here. So, clean up the obsolete code, includes SD_PREFER_LOCAL. Signed-off-by: Alex Shi Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/5028F431.6000306@intel.com Signed-off-by: Thomas Gleixner --- include/linux/sched.h | 1 - include/linux/topology.h | 2 -- kernel/sched/core.c | 1 - kernel/sched/fair.c | 34 +++------------------------------- 4 files changed, 3 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index b8c86648a2f9..f3eebc121ebc 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -860,7 +860,6 @@ enum cpu_idle_type { #define SD_BALANCE_FORK 0x0008 /* Balance on fork, clone */ #define SD_BALANCE_WAKE 0x0010 /* Balance on wakeup */ #define SD_WAKE_AFFINE 0x0020 /* Wake task to waking CPU */ -#define SD_PREFER_LOCAL 0x0040 /* Prefer to keep tasks local to this domain */ #define SD_SHARE_CPUPOWER 0x0080 /* Domain members share cpu power */ #define SD_SHARE_PKG_RESOURCES 0x0200 /* Domain members share cpu pkg resources */ #define SD_SERIALIZE 0x0400 /* Only a single load balancing instance */ diff --git a/include/linux/topology.h b/include/linux/topology.h index fec12d667211..d3cf0d6e7712 100644 --- a/include/linux/topology.h +++ b/include/linux/topology.h @@ -129,7 +129,6 @@ int arch_update_cpu_topology(void); | 1*SD_BALANCE_FORK \ | 0*SD_BALANCE_WAKE \ | 1*SD_WAKE_AFFINE \ - | 0*SD_PREFER_LOCAL \ | 0*SD_SHARE_CPUPOWER \ | 1*SD_SHARE_PKG_RESOURCES \ | 0*SD_SERIALIZE \ @@ -160,7 +159,6 @@ int arch_update_cpu_topology(void); | 1*SD_BALANCE_FORK \ | 0*SD_BALANCE_WAKE \ | 1*SD_WAKE_AFFINE \ - | 0*SD_PREFER_LOCAL \ | 0*SD_SHARE_CPUPOWER \ | 0*SD_SHARE_PKG_RESOURCES \ | 0*SD_SERIALIZE \ diff --git a/kernel/sched/core.c b/kernel/sched/core.c index c9a3655e572d..4376c9f34790 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -6622,7 +6622,6 @@ sd_numa_init(struct sched_domain_topology_level *tl, int cpu) | 0*SD_BALANCE_FORK | 0*SD_BALANCE_WAKE | 0*SD_WAKE_AFFINE - | 0*SD_PREFER_LOCAL | 0*SD_SHARE_CPUPOWER | 0*SD_SHARE_PKG_RESOURCES | 1*SD_SERIALIZE diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 287bfaca6420..01d3eda6b7f9 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -2686,7 +2686,6 @@ select_task_rq_fair(struct task_struct *p, int sd_flag, int wake_flags) int prev_cpu = task_cpu(p); int new_cpu = cpu; int want_affine = 0; - int want_sd = 1; int sync = wake_flags & WF_SYNC; if (p->nr_cpus_allowed == 1) @@ -2703,27 +2702,6 @@ select_task_rq_fair(struct task_struct *p, int sd_flag, int wake_flags) if (!(tmp->flags & SD_LOAD_BALANCE)) continue; - /* - * If power savings logic is enabled for a domain, see if we - * are not overloaded, if so, don't balance wider. - */ - if (tmp->flags & (SD_PREFER_LOCAL)) { - unsigned long power = 0; - unsigned long nr_running = 0; - unsigned long capacity; - int i; - - for_each_cpu(i, sched_domain_span(tmp)) { - power += power_of(i); - nr_running += cpu_rq(i)->cfs.nr_running; - } - - capacity = DIV_ROUND_CLOSEST(power, SCHED_POWER_SCALE); - - if (nr_running < capacity) - want_sd = 0; - } - /* * If both cpu and prev_cpu are part of this domain, * cpu is a valid SD_WAKE_AFFINE target. @@ -2731,21 +2709,15 @@ select_task_rq_fair(struct task_struct *p, int sd_flag, int wake_flags) if (want_affine && (tmp->flags & SD_WAKE_AFFINE) && cpumask_test_cpu(prev_cpu, sched_domain_span(tmp))) { affine_sd = tmp; - want_affine = 0; - } - - if (!want_sd && !want_affine) break; + } - if (!(tmp->flags & sd_flag)) - continue; - - if (want_sd) + if (tmp->flags & sd_flag) sd = tmp; } if (affine_sd) { - if (cpu == prev_cpu || wake_affine(affine_sd, p, sync)) + if (cpu != prev_cpu && wake_affine(affine_sd, p, sync)) prev_cpu = cpu; new_cpu = select_idle_sibling(p, prev_cpu); -- cgit v1.2.3 From 1265057fa02c7bed3b6d9ddc8a2048065a370364 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 8 Aug 2012 09:38:42 -0700 Subject: workqueue: fix CPU binding of flush_delayed_work[_sync]() delayed_work encodes the workqueue to use and the last CPU in delayed_work->work.data while it's on timer. The target CPU is implicitly recorded as the CPU the timer is queued on and delayed_work_timer_fn() queues delayed_work->work to the CPU it is running on. Unfortunately, this leaves flush_delayed_work[_sync]() no way to find out which CPU the delayed_work was queued for when they try to re-queue after killing the timer. Currently, it chooses the local CPU flush is running on. This can unexpectedly move a delayed_work queued on a specific CPU to another CPU and lead to subtle errors. There isn't much point in trying to save several bytes in struct delayed_work, which is already close to a hundred bytes on 64bit with all debug options turned off. This patch adds delayed_work->cpu to remember the CPU it's queued for. Note that if the timer is migrated during CPU down, the work item could be queued to the downed global_cwq after this change. As a detached global_cwq behaves like an unbound one, this doesn't change much for the delayed_work. Signed-off-by: Tejun Heo Cc: Linus Torvalds Cc: Ingo Molnar Cc: Andrew Morton --- include/linux/workqueue.h | 1 + kernel/workqueue.c | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 20000305a8a6..b14d5d59af7c 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -102,6 +102,7 @@ struct work_struct { struct delayed_work { struct work_struct work; struct timer_list timer; + int cpu; }; static inline struct delayed_work *to_delayed_work(struct work_struct *work) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 41ae2c0979fe..11723c5b2b20 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -1319,7 +1319,7 @@ void delayed_work_timer_fn(unsigned long __data) struct cpu_workqueue_struct *cwq = get_work_cwq(&dwork->work); local_irq_disable(); - __queue_work(WORK_CPU_UNBOUND, cwq->wq, &dwork->work); + __queue_work(dwork->cpu, cwq->wq, &dwork->work); local_irq_enable(); } EXPORT_SYMBOL_GPL(delayed_work_timer_fn); @@ -1356,6 +1356,7 @@ static void __queue_delayed_work(int cpu, struct workqueue_struct *wq, set_work_cwq(work, get_cwq(lcpu, wq), 0); + dwork->cpu = cpu; timer->expires = jiffies + delay; if (unlikely(cpu != WORK_CPU_UNBOUND)) @@ -2997,7 +2998,7 @@ bool flush_delayed_work(struct delayed_work *dwork) { local_irq_disable(); if (del_timer_sync(&dwork->timer)) - __queue_work(WORK_CPU_UNBOUND, + __queue_work(dwork->cpu, get_work_cwq(&dwork->work)->wq, &dwork->work); local_irq_enable(); return flush_work(&dwork->work); @@ -3020,7 +3021,7 @@ bool flush_delayed_work_sync(struct delayed_work *dwork) { local_irq_disable(); if (del_timer_sync(&dwork->timer)) - __queue_work(WORK_CPU_UNBOUND, + __queue_work(dwork->cpu, get_work_cwq(&dwork->work)->wq, &dwork->work); local_irq_enable(); return flush_work_sync(&dwork->work); -- cgit v1.2.3 From 21aca2fa00259f781d228496631b61dbb4274e90 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 8 Aug 2012 22:26:41 +0200 Subject: TTY: pty, switch to tty_alloc_driver Switch to the new driver allocation interface, as this is one of the special call-sites. Here, we need TTY_DRIVER_DYNAMIC_ALLOC to not allocate tty_driver->ports, cdevs and potentially other structures because we reserve too many lines in pty. Instead, it provides the tty_port<->tty_struct link in tty->ops->install already. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 31 ++++++++++++++++++++----------- include/linux/tty_driver.h | 4 ++++ 2 files changed, 24 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index d9ea9e2c9ec5..f5a27c66ff60 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -444,11 +444,17 @@ static void __init legacy_pty_init(void) if (legacy_count <= 0) return; - pty_driver = alloc_tty_driver(legacy_count); + pty_driver = tty_alloc_driver(legacy_count, + TTY_DRIVER_RESET_TERMIOS | + TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_ALLOC); if (!pty_driver) panic("Couldn't allocate pty driver"); - pty_slave_driver = alloc_tty_driver(legacy_count); + pty_slave_driver = tty_alloc_driver(legacy_count, + TTY_DRIVER_RESET_TERMIOS | + TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_ALLOC); if (!pty_slave_driver) panic("Couldn't allocate pty slave driver"); @@ -465,7 +471,6 @@ static void __init legacy_pty_init(void) pty_driver->init_termios.c_lflag = 0; pty_driver->init_termios.c_ispeed = 38400; pty_driver->init_termios.c_ospeed = 38400; - pty_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW; pty_driver->other = pty_slave_driver; tty_set_operations(pty_driver, &master_pty_ops_bsd); @@ -479,8 +484,6 @@ static void __init legacy_pty_init(void) pty_slave_driver->init_termios.c_cflag = B38400 | CS8 | CREAD; pty_slave_driver->init_termios.c_ispeed = 38400; pty_slave_driver->init_termios.c_ospeed = 38400; - pty_slave_driver->flags = TTY_DRIVER_RESET_TERMIOS | - TTY_DRIVER_REAL_RAW; pty_slave_driver->other = pty_driver; tty_set_operations(pty_slave_driver, &slave_pty_ops_bsd); @@ -673,10 +676,20 @@ static struct file_operations ptmx_fops; static void __init unix98_pty_init(void) { - ptm_driver = alloc_tty_driver(NR_UNIX98_PTY_MAX); + ptm_driver = tty_alloc_driver(NR_UNIX98_PTY_MAX, + TTY_DRIVER_RESET_TERMIOS | + TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV | + TTY_DRIVER_DEVPTS_MEM | + TTY_DRIVER_DYNAMIC_ALLOC); if (!ptm_driver) panic("Couldn't allocate Unix98 ptm driver"); - pts_driver = alloc_tty_driver(NR_UNIX98_PTY_MAX); + pts_driver = tty_alloc_driver(NR_UNIX98_PTY_MAX, + TTY_DRIVER_RESET_TERMIOS | + TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV | + TTY_DRIVER_DEVPTS_MEM | + TTY_DRIVER_DYNAMIC_ALLOC); if (!pts_driver) panic("Couldn't allocate Unix98 pts driver"); @@ -693,8 +706,6 @@ static void __init unix98_pty_init(void) ptm_driver->init_termios.c_lflag = 0; ptm_driver->init_termios.c_ispeed = 38400; ptm_driver->init_termios.c_ospeed = 38400; - ptm_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM; ptm_driver->other = pts_driver; tty_set_operations(ptm_driver, &ptm_unix98_ops); @@ -708,8 +719,6 @@ static void __init unix98_pty_init(void) pts_driver->init_termios.c_cflag = B38400 | CS8 | CREAD; pts_driver->init_termios.c_ispeed = 38400; pts_driver->init_termios.c_ospeed = 38400; - pts_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM; pts_driver->other = ptm_driver; tty_set_operations(pts_driver, &pty_unix98_ops); diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 3adc362f0bd2..1a85f003d646 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -391,6 +391,9 @@ static inline struct tty_driver *tty_driver_kref_get(struct tty_driver *d) * the requested timeout to the caller instead of using a simple * on/off interface. * + * TTY_DRIVER_DYNAMIC_ALLOC -- do not allocate structures which are + * needed per line for this driver as it would waste memory. + * The driver will take care. */ #define TTY_DRIVER_INSTALLED 0x0001 #define TTY_DRIVER_RESET_TERMIOS 0x0002 @@ -398,6 +401,7 @@ static inline struct tty_driver *tty_driver_kref_get(struct tty_driver *d) #define TTY_DRIVER_DYNAMIC_DEV 0x0008 #define TTY_DRIVER_DEVPTS_MEM 0x0010 #define TTY_DRIVER_HARDWARE_BREAK 0x0020 +#define TTY_DRIVER_DYNAMIC_ALLOC 0x0040 /* tty driver types */ #define TTY_DRIVER_TYPE_SYSTEM 0x0001 -- cgit v1.2.3 From 0019b4089ccef8148d8be83cc8adfc81a75b47d4 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 8 Aug 2012 22:26:43 +0200 Subject: TTY: add support for unnumbered device nodes This allows drivers like ttyprintk to avoid hacks to create an unnumbered node in /dev. It used to set TTY_DRIVER_DYNAMIC_DEV in flags and call device_create on its own. That is incorrect, because TTY_DRIVER_DYNAMIC_DEV may be set only if tty_register_device is called explicitly. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/ttyprintk.c | 17 ++++------------- drivers/tty/tty_io.c | 7 +++++-- include/linux/tty_driver.h | 6 ++++++ 3 files changed, 15 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c index be1c3fb186c1..9e6272f0b98c 100644 --- a/drivers/char/ttyprintk.c +++ b/drivers/char/ttyprintk.c @@ -178,13 +178,15 @@ static struct tty_driver *ttyprintk_driver; static int __init ttyprintk_init(void) { int ret = -ENOMEM; - void *rp; tty_port_init(&tpk_port.port); tpk_port.port.ops = &null_ops; mutex_init(&tpk_port.port_write_mutex); - ttyprintk_driver = alloc_tty_driver(1); + ttyprintk_driver = tty_alloc_driver(1, + TTY_DRIVER_RESET_TERMIOS | + TTY_DRIVER_REAL_RAW | + TTY_DRIVER_UNNUMBERED_NODE); if (!ttyprintk_driver) return ret; @@ -195,8 +197,6 @@ static int __init ttyprintk_init(void) ttyprintk_driver->type = TTY_DRIVER_TYPE_CONSOLE; ttyprintk_driver->init_termios = tty_std_termios; ttyprintk_driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET; - ttyprintk_driver->flags = TTY_DRIVER_RESET_TERMIOS | - TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; tty_set_operations(ttyprintk_driver, &ttyprintk_ops); ret = tty_register_driver(ttyprintk_driver); @@ -205,15 +205,6 @@ static int __init ttyprintk_init(void) goto error; } - /* create our unnumbered device */ - rp = device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 3), NULL, - ttyprintk_driver->name); - if (IS_ERR(rp)) { - printk(KERN_ERR "Couldn't create ttyprintk device\n"); - ret = PTR_ERR(rp); - goto error; - } - return 0; error: diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index a67609b9f21b..4d9898c2b641 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1216,7 +1216,10 @@ static void pty_line_name(struct tty_driver *driver, int index, char *p) */ static void tty_line_name(struct tty_driver *driver, int index, char *p) { - sprintf(p, "%s%d", driver->name, index + driver->name_base); + if (driver->flags & TTY_DRIVER_UNNUMBERED_NODE) + strcpy(p, driver->name); + else + sprintf(p, "%s%d", driver->name, index + driver->name_base); } /** @@ -3076,7 +3079,7 @@ struct tty_driver *__tty_alloc_driver(unsigned int lines, struct module *owner, struct tty_driver *driver; int err; - if (!lines) + if (!lines || (flags & TTY_DRIVER_UNNUMBERED_NODE && lines > 1)) return ERR_PTR(-EINVAL); driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL); diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 1a85f003d646..44e05b75d573 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -394,6 +394,11 @@ static inline struct tty_driver *tty_driver_kref_get(struct tty_driver *d) * TTY_DRIVER_DYNAMIC_ALLOC -- do not allocate structures which are * needed per line for this driver as it would waste memory. * The driver will take care. + * + * TTY_DRIVER_UNNUMBERED_NODE -- do not create numbered /dev nodes. In + * other words create /dev/ttyprintk and not /dev/ttyprintk0. + * Applicable only when a driver for a single tty device is + * being allocated. */ #define TTY_DRIVER_INSTALLED 0x0001 #define TTY_DRIVER_RESET_TERMIOS 0x0002 @@ -402,6 +407,7 @@ static inline struct tty_driver *tty_driver_kref_get(struct tty_driver *d) #define TTY_DRIVER_DEVPTS_MEM 0x0010 #define TTY_DRIVER_HARDWARE_BREAK 0x0020 #define TTY_DRIVER_DYNAMIC_ALLOC 0x0040 +#define TTY_DRIVER_UNNUMBERED_NODE 0x0080 /* tty driver types */ #define TTY_DRIVER_TYPE_SYSTEM 0x0001 -- cgit v1.2.3 From 7e73eca6a7b2967423902a4543821bb97cbbe698 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 8 Aug 2012 22:26:44 +0200 Subject: TTY: move cdev_add to tty_register_device We need the /dev/ node not to be available before we call tty_register_device. Otherwise we might race with open and tty_struct->port might not be available at that time. This is not an issue now, but would be a problem after "TTY: use tty_port_register_device" is applied. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 48 +++++++++++++++++++++++++++++++++++++++------- include/linux/tty_driver.h | 2 +- 2 files changed, 42 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 4d9898c2b641..28c3e869ebba 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -3006,6 +3006,15 @@ EXPORT_SYMBOL_GPL(tty_put_char); struct class *tty_class; +static int tty_cdev_add(struct tty_driver *driver, dev_t dev, + unsigned int index, unsigned int count) +{ + /* init here, since reused cdevs cause crashes */ + cdev_init(&driver->cdevs[index], &tty_fops); + driver->cdevs[index].owner = driver->owner; + return cdev_add(&driver->cdevs[index], dev, count); +} + /** * tty_register_device - register a tty device * @driver: the tty driver that describes the tty device @@ -3028,8 +3037,10 @@ struct class *tty_class; struct device *tty_register_device(struct tty_driver *driver, unsigned index, struct device *device) { + struct device *ret; char name[64]; dev_t dev = MKDEV(driver->major, driver->minor_start) + index; + bool cdev = false; if (index >= driver->num) { printk(KERN_ERR "Attempt to register invalid tty line number " @@ -3042,7 +3053,18 @@ struct device *tty_register_device(struct tty_driver *driver, unsigned index, else tty_line_name(driver, index, name); - return device_create(tty_class, device, dev, NULL, name); + if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) { + int error = tty_cdev_add(driver, dev, index, 1); + if (error) + return ERR_PTR(error); + cdev = true; + } + + ret = device_create(tty_class, device, dev, NULL, name); + if (IS_ERR(ret) && cdev) + cdev_del(&driver->cdevs[index]); + + return ret; } EXPORT_SYMBOL(tty_register_device); @@ -3061,6 +3083,8 @@ void tty_unregister_device(struct tty_driver *driver, unsigned index) { device_destroy(tty_class, MKDEV(driver->major, driver->minor_start) + index); + if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) + cdev_del(&driver->cdevs[index]); } EXPORT_SYMBOL(tty_unregister_device); @@ -3077,6 +3101,7 @@ struct tty_driver *__tty_alloc_driver(unsigned int lines, struct module *owner, unsigned long flags) { struct tty_driver *driver; + unsigned int cdevs = 1; int err; if (!lines || (flags & TTY_DRIVER_UNNUMBERED_NODE && lines > 1)) @@ -3110,6 +3135,13 @@ struct tty_driver *__tty_alloc_driver(unsigned int lines, struct module *owner, err = -ENOMEM; goto err_free_all; } + cdevs = lines; + } + + driver->cdevs = kcalloc(cdevs, sizeof(*driver->cdevs), GFP_KERNEL); + if (!driver->cdevs) { + err = -ENOMEM; + goto err_free_all; } return driver; @@ -3144,8 +3176,10 @@ static void destruct_tty_driver(struct kref *kref) tty_unregister_device(driver, i); } proc_tty_unregister_driver(driver); - cdev_del(&driver->cdev); + if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) + cdev_del(&driver->cdevs[0]); } + kfree(driver->cdevs); kfree(driver->ports); kfree(driver->termios); kfree(driver->ttys); @@ -3195,11 +3229,11 @@ int tty_register_driver(struct tty_driver *driver) if (error < 0) goto err; - cdev_init(&driver->cdev, &tty_fops); - driver->cdev.owner = driver->owner; - error = cdev_add(&driver->cdev, dev, driver->num); - if (error) - goto err_unreg_char; + if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) { + error = tty_cdev_add(driver, dev, 0, driver->num); + if (error) + goto err_unreg_char; + } mutex_lock(&tty_mutex); list_add(&driver->tty_drivers, &tty_drivers); diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 44e05b75d573..dd976cfb6131 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -289,7 +289,7 @@ struct tty_operations { struct tty_driver { int magic; /* magic number for this structure */ struct kref kref; /* Reference management */ - struct cdev cdev; + struct cdev *cdevs; struct module *owner; const char *driver_name; const char *name; -- cgit v1.2.3 From 2cb4ca0208722836e921d5ba780b09f29d4026b8 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 7 Aug 2012 21:47:50 +0200 Subject: TTY: add tty_port_link_device This is for those drivers which do not have dynamic device creation (do not call tty_port_register_device) and do not want to implement tty->ops->install (will not call tty_port_install). They still have to provide the link somehow though. And this newly added function is exactly to serve that purpose. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_port.c | 22 +++++++++++++++++++++- include/linux/tty.h | 2 ++ 2 files changed, 23 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index c2f0592bcb2c..96302f4c7079 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -33,6 +33,26 @@ void tty_port_init(struct tty_port *port) } EXPORT_SYMBOL(tty_port_init); +/** + * tty_port_link_device - link tty and tty_port + * @port: tty_port of the device + * @driver: tty_driver for this device + * @index: index of the tty + * + * Provide the tty layer wit ha link from a tty (specified by @index) to a + * tty_port (@port). Use this only if neither tty_port_register_device nor + * tty_port_install is used in the driver. If used, this has to be called before + * tty_register_driver. + */ +void tty_port_link_device(struct tty_port *port, + struct tty_driver *driver, unsigned index) +{ + if (WARN_ON(index >= driver->num)) + return; + driver->ports[index] = port; +} +EXPORT_SYMBOL_GPL(tty_port_link_device); + /** * tty_port_register_device - register tty device * @port: tty_port of the device @@ -48,7 +68,7 @@ struct device *tty_port_register_device(struct tty_port *port, struct tty_driver *driver, unsigned index, struct device *device) { - driver->ports[index] = port; + tty_port_link_device(port, driver, index); return tty_register_device(driver, index, device); } EXPORT_SYMBOL_GPL(tty_port_register_device); diff --git a/include/linux/tty.h b/include/linux/tty.h index acca24bf06a7..69a787fdfa9c 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -497,6 +497,8 @@ extern int tty_write_lock(struct tty_struct *tty, int ndelay); #define tty_is_writelocked(tty) (mutex_is_locked(&tty->atomic_write_lock)) extern void tty_port_init(struct tty_port *port); +extern void tty_port_link_device(struct tty_port *port, + struct tty_driver *driver, unsigned index); extern struct device *tty_port_register_device(struct tty_port *port, struct tty_driver *driver, unsigned index, struct device *device); -- cgit v1.2.3 From f026cfa82f628db24b8cea41b9d6202af104cecb Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 14 Aug 2012 09:53:38 -0700 Subject: Revert "x86-64/efi: Use EFI to deal with platform wall clock" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit bacef661acdb634170a8faddbc1cf28e8f8b9eee. This commit has been found to cause serious regressions on a number of ASUS machines at the least. We probably need to provide a 1:1 map in addition to the EFI virtual memory map in order for this to work. Signed-off-by: H. Peter Anvin Reported-and-bisected-by: Jérôme Carretero Cc: Jan Beulich Cc: Matt Fleming Cc: Matthew Garrett Cc: Linus Torvalds Cc: Andrew Morton Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20120805172903.5f8bb24c@zougloub.eu --- arch/x86/mm/pageattr.c | 10 ++++------ arch/x86/platform/efi/efi.c | 30 ++++++++++++++++++++++++++---- include/linux/efi.h | 2 ++ init/main.c | 8 ++++---- 4 files changed, 36 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 931930a96160..a718e0d23503 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -919,13 +919,11 @@ static int change_page_attr_set_clr(unsigned long *addr, int numpages, /* * On success we use clflush, when the CPU supports it to - * avoid the wbindv. If the CPU does not support it, in the - * error case, and during early boot (for EFI) we fall back - * to cpa_flush_all (which uses wbinvd): + * avoid the wbindv. If the CPU does not support it and in the + * error case we fall back to cpa_flush_all (which uses + * wbindv): */ - if (early_boot_irqs_disabled) - __cpa_flush_all((void *)(long)cache); - else if (!ret && cpu_has_clflush) { + if (!ret && cpu_has_clflush) { if (cpa.flags & (CPA_PAGES_ARRAY | CPA_ARRAY)) { cpa_flush_array(addr, numpages, cache, cpa.flags, pages); diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 2dc29f51e75a..92660edaa1e7 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -234,7 +234,22 @@ static efi_status_t __init phys_efi_set_virtual_address_map( return status; } -static int efi_set_rtc_mmss(unsigned long nowtime) +static efi_status_t __init phys_efi_get_time(efi_time_t *tm, + efi_time_cap_t *tc) +{ + unsigned long flags; + efi_status_t status; + + spin_lock_irqsave(&rtc_lock, flags); + efi_call_phys_prelog(); + status = efi_call_phys2(efi_phys.get_time, virt_to_phys(tm), + virt_to_phys(tc)); + efi_call_phys_epilog(); + spin_unlock_irqrestore(&rtc_lock, flags); + return status; +} + +int efi_set_rtc_mmss(unsigned long nowtime) { int real_seconds, real_minutes; efi_status_t status; @@ -263,7 +278,7 @@ static int efi_set_rtc_mmss(unsigned long nowtime) return 0; } -static unsigned long efi_get_time(void) +unsigned long efi_get_time(void) { efi_status_t status; efi_time_t eft; @@ -606,13 +621,18 @@ static int __init efi_runtime_init(void) } /* * We will only need *early* access to the following - * EFI runtime service before set_virtual_address_map + * two EFI runtime services before set_virtual_address_map * is invoked. */ + efi_phys.get_time = (efi_get_time_t *)runtime->get_time; efi_phys.set_virtual_address_map = (efi_set_virtual_address_map_t *) runtime->set_virtual_address_map; - + /* + * Make efi_get_time can be called before entering + * virtual mode. + */ + efi.get_time = phys_efi_get_time; early_iounmap(runtime, sizeof(efi_runtime_services_t)); return 0; @@ -700,10 +720,12 @@ void __init efi_init(void) efi_enabled = 0; return; } +#ifdef CONFIG_X86_32 if (efi_native) { x86_platform.get_wallclock = efi_get_time; x86_platform.set_wallclock = efi_set_rtc_mmss; } +#endif #if EFI_DEBUG print_efi_memmap(); diff --git a/include/linux/efi.h b/include/linux/efi.h index 103adc6d7e3a..ec45ccd8708a 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -503,6 +503,8 @@ extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size); extern int __init efi_uart_console_only (void); extern void efi_initialize_iomem_resources(struct resource *code_resource, struct resource *data_resource, struct resource *bss_resource); +extern unsigned long efi_get_time(void); +extern int efi_set_rtc_mmss(unsigned long nowtime); extern void efi_reserve_boot_services(void); extern struct efi_memory_map memmap; diff --git a/init/main.c b/init/main.c index e60679de61c3..b28673087ac0 100644 --- a/init/main.c +++ b/init/main.c @@ -461,10 +461,6 @@ static void __init mm_init(void) percpu_init_late(); pgtable_cache_init(); vmalloc_init(); -#ifdef CONFIG_X86 - if (efi_enabled) - efi_enter_virtual_mode(); -#endif } asmlinkage void __init start_kernel(void) @@ -606,6 +602,10 @@ asmlinkage void __init start_kernel(void) calibrate_delay(); pidmap_init(); anon_vma_init(); +#ifdef CONFIG_X86 + if (efi_enabled) + efi_enter_virtual_mode(); +#endif thread_info_cache_init(); cred_init(); fork_init(totalram_pages); -- cgit v1.2.3 From ee89bab14e857678f83a71ee99e575b0fdbb58d4 Mon Sep 17 00:00:00 2001 From: Amerigo Wang Date: Thu, 9 Aug 2012 22:14:56 +0000 Subject: net: move and rename netif_notify_peers() I believe net/core/dev.c is a better place for netif_notify_peers(), because other net event notify functions also stay in this file. And rename it to netdev_notify_peers(). Cc: David S. Miller Cc: Ian Campbell Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 2 +- drivers/net/virtio_net.c | 2 +- drivers/net/xen-netfront.c | 2 +- include/linux/netdevice.h | 3 +-- net/core/dev.c | 18 ++++++++++++++++++ net/sched/sch_generic.c | 18 ------------------ 6 files changed, 22 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 8c5a1c43c81d..e91111a656f7 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -400,7 +400,7 @@ static void netvsc_send_garp(struct work_struct *w) ndev_ctx = container_of(w, struct net_device_context, dwork.work); net_device = hv_get_drvdata(ndev_ctx->device_ctx); net = net_device->ndev; - netif_notify_peers(net); + netdev_notify_peers(net); } diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 83d2b0c34c5e..81a64c58e8ad 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -993,7 +993,7 @@ static void virtnet_config_changed_work(struct work_struct *work) goto done; if (v & VIRTIO_NET_S_ANNOUNCE) { - netif_notify_peers(vi->dev); + netdev_notify_peers(vi->dev); virtnet_ack_link_announce(vi); } diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 30899901aef5..39afd37e62b3 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -1731,7 +1731,7 @@ static void netback_changed(struct xenbus_device *dev, break; case XenbusStateConnected: - netif_notify_peers(netdev); + netdev_notify_peers(netdev); break; case XenbusStateClosing: diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index a9db4f33407f..8d4b7316c734 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2246,8 +2246,6 @@ extern void netif_carrier_on(struct net_device *dev); extern void netif_carrier_off(struct net_device *dev); -extern void netif_notify_peers(struct net_device *dev); - /** * netif_dormant_on - mark device as dormant. * @dev: network device @@ -2596,6 +2594,7 @@ extern void __dev_set_rx_mode(struct net_device *dev); extern int dev_set_promiscuity(struct net_device *dev, int inc); extern int dev_set_allmulti(struct net_device *dev, int inc); extern void netdev_state_change(struct net_device *dev); +extern void netdev_notify_peers(struct net_device *dev); extern int netdev_bonding_change(struct net_device *dev, unsigned long event); extern void netdev_features_change(struct net_device *dev); diff --git a/net/core/dev.c b/net/core/dev.c index 1f06df8d10a3..5defcf940005 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1106,6 +1106,24 @@ void netdev_state_change(struct net_device *dev) } EXPORT_SYMBOL(netdev_state_change); +/** + * netdev_notify_peers - notify network peers about existence of @dev + * @dev: network device + * + * Generate traffic such that interested network peers are aware of + * @dev, such as by generating a gratuitous ARP. This may be used when + * a device wants to inform the rest of the network about some sort of + * reconfiguration such as a failover event or virtual machine + * migration. + */ +void netdev_notify_peers(struct net_device *dev) +{ + rtnl_lock(); + call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, dev); + rtnl_unlock(); +} +EXPORT_SYMBOL(netdev_notify_peers); + int netdev_bonding_change(struct net_device *dev, unsigned long event) { return call_netdevice_notifiers(event, dev); diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 511323e89cec..6c4d5fe53ce8 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -324,24 +324,6 @@ void netif_carrier_off(struct net_device *dev) } EXPORT_SYMBOL(netif_carrier_off); -/** - * netif_notify_peers - notify network peers about existence of @dev - * @dev: network device - * - * Generate traffic such that interested network peers are aware of - * @dev, such as by generating a gratuitous ARP. This may be used when - * a device wants to inform the rest of the network about some sort of - * reconfiguration such as a failover event or virtual machine - * migration. - */ -void netif_notify_peers(struct net_device *dev) -{ - rtnl_lock(); - call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, dev); - rtnl_unlock(); -} -EXPORT_SYMBOL(netif_notify_peers); - /* "NOOP" scheduler: the best scheduler, recommended for all interfaces under all circumstances. It is difficult to invent anything faster or cheaper. -- cgit v1.2.3 From b7bc2a5b5bd99b216c3e5fe68c7f45c684ab5745 Mon Sep 17 00:00:00 2001 From: Amerigo Wang Date: Thu, 9 Aug 2012 22:14:57 +0000 Subject: net: remove netdev_bonding_change() I don't see any benifits to use netdev_bonding_change() than using call_netdevice_notifiers() directly. Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 20 ++++++++++---------- include/linux/netdevice.h | 2 -- net/core/dev.c | 6 ------ 3 files changed, 10 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 6fae5f3ec7f6..d95fbc34b229 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1120,10 +1120,10 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active) write_unlock_bh(&bond->curr_slave_lock); read_unlock(&bond->lock); - netdev_bonding_change(bond->dev, NETDEV_BONDING_FAILOVER); + call_netdevice_notifiers(NETDEV_BONDING_FAILOVER, bond->dev); if (should_notify_peers) - netdev_bonding_change(bond->dev, - NETDEV_NOTIFY_PEERS); + call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, + bond->dev); read_lock(&bond->lock); write_lock_bh(&bond->curr_slave_lock); @@ -1560,8 +1560,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) bond_dev->name, bond_dev->type, slave_dev->type); - res = netdev_bonding_change(bond_dev, - NETDEV_PRE_TYPE_CHANGE); + res = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, + bond_dev); res = notifier_to_errno(res); if (res) { pr_err("%s: refused to change device type\n", @@ -1581,8 +1581,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) bond_dev->priv_flags &= ~IFF_TX_SKB_SHARING; } - netdev_bonding_change(bond_dev, - NETDEV_POST_TYPE_CHANGE); + call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, + bond_dev); } } else if (bond_dev->type != slave_dev->type) { pr_err("%s ether type (%d) is different from other slaves (%d), can not enslave it.\n", @@ -1943,7 +1943,7 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev) } block_netpoll_tx(); - netdev_bonding_change(bond_dev, NETDEV_RELEASE); + call_netdevice_notifiers(NETDEV_RELEASE, bond_dev); write_lock_bh(&bond->lock); slave = bond_get_slave_by_dev(bond, slave_dev); @@ -2586,7 +2586,7 @@ re_arm: read_unlock(&bond->lock); return; } - netdev_bonding_change(bond->dev, NETDEV_NOTIFY_PEERS); + call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, bond->dev); rtnl_unlock(); } } @@ -3205,7 +3205,7 @@ re_arm: read_unlock(&bond->lock); return; } - netdev_bonding_change(bond->dev, NETDEV_NOTIFY_PEERS); + call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, bond->dev); rtnl_unlock(); } } diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 8d4b7316c734..1d6ab69c1f3f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2595,8 +2595,6 @@ extern int dev_set_promiscuity(struct net_device *dev, int inc); extern int dev_set_allmulti(struct net_device *dev, int inc); extern void netdev_state_change(struct net_device *dev); extern void netdev_notify_peers(struct net_device *dev); -extern int netdev_bonding_change(struct net_device *dev, - unsigned long event); extern void netdev_features_change(struct net_device *dev); /* Load a device via the kmod */ extern void dev_load(struct net *net, const char *name); diff --git a/net/core/dev.c b/net/core/dev.c index 5defcf940005..ce1bccb08de5 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1124,12 +1124,6 @@ void netdev_notify_peers(struct net_device *dev) } EXPORT_SYMBOL(netdev_notify_peers); -int netdev_bonding_change(struct net_device *dev, unsigned long event) -{ - return call_netdevice_notifiers(event, dev); -} -EXPORT_SYMBOL(netdev_bonding_change); - /** * dev_load - load a network module * @net: the applicable net namespace -- cgit v1.2.3 From c12b395a46646bab69089ce7016ac78177f6001f Mon Sep 17 00:00:00 2001 From: "xeb@mail.ru" Date: Fri, 10 Aug 2012 00:51:50 +0000 Subject: gre: Support GRE over IPv6 GRE over IPv6 implementation. Signed-off-by: Dmitry Kozlov Signed-off-by: David S. Miller --- include/linux/if_arp.h | 1 + include/linux/if_tunnel.h | 3 + include/linux/ip6_tunnel.h | 17 + include/net/ip6_tunnel.h | 41 +- include/net/ipv6.h | 1 + net/ipv6/Kconfig | 16 + net/ipv6/Makefile | 1 + net/ipv6/ip6_gre.c | 1790 ++++++++++++++++++++++++++++++++++++++++++++ net/ipv6/ip6_tunnel.c | 89 ++- 9 files changed, 1933 insertions(+), 26 deletions(-) create mode 100644 net/ipv6/ip6_gre.c (limited to 'include') diff --git a/include/linux/if_arp.h b/include/linux/if_arp.h index f0e69c6e8208..9adcc29f084a 100644 --- a/include/linux/if_arp.h +++ b/include/linux/if_arp.h @@ -92,6 +92,7 @@ #define ARPHRD_PHONET 820 /* PhoNet media type */ #define ARPHRD_PHONET_PIPE 821 /* PhoNet pipe header */ #define ARPHRD_CAIF 822 /* CAIF media type */ +#define ARPHRD_IP6GRE 823 /* GRE over IPv6 */ #define ARPHRD_VOID 0xFFFF /* Void type, nothing is known */ #define ARPHRD_NONE 0xFFFE /* zero header length */ diff --git a/include/linux/if_tunnel.h b/include/linux/if_tunnel.h index 5efff60b6f56..8c5035ac3142 100644 --- a/include/linux/if_tunnel.h +++ b/include/linux/if_tunnel.h @@ -75,6 +75,9 @@ enum { IFLA_GRE_TTL, IFLA_GRE_TOS, IFLA_GRE_PMTUDISC, + IFLA_GRE_ENCAP_LIMIT, + IFLA_GRE_FLOWINFO, + IFLA_GRE_FLAGS, __IFLA_GRE_MAX, }; diff --git a/include/linux/ip6_tunnel.h b/include/linux/ip6_tunnel.h index bf22b0317902..48af63c9a48d 100644 --- a/include/linux/ip6_tunnel.h +++ b/include/linux/ip6_tunnel.h @@ -31,4 +31,21 @@ struct ip6_tnl_parm { struct in6_addr raddr; /* remote tunnel end-point address */ }; +struct ip6_tnl_parm2 { + char name[IFNAMSIZ]; /* name of tunnel device */ + int link; /* ifindex of underlying L2 interface */ + __u8 proto; /* tunnel protocol */ + __u8 encap_limit; /* encapsulation limit for tunnel */ + __u8 hop_limit; /* hop limit for tunnel */ + __be32 flowinfo; /* traffic class and flowlabel for tunnel */ + __u32 flags; /* tunnel flags */ + struct in6_addr laddr; /* local tunnel end-point address */ + struct in6_addr raddr; /* remote tunnel end-point address */ + + __be16 i_flags; + __be16 o_flags; + __be32 i_key; + __be32 o_key; +}; + #endif diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h index 358fb86f57eb..e03047f7090b 100644 --- a/include/net/ip6_tunnel.h +++ b/include/net/ip6_tunnel.h @@ -5,6 +5,8 @@ #include #include +#define IP6TUNNEL_ERR_TIMEO (30*HZ) + /* capable of sending packets */ #define IP6_TNL_F_CAP_XMIT 0x10000 /* capable of receiving packets */ @@ -12,15 +14,40 @@ /* determine capability on a per-packet basis */ #define IP6_TNL_F_CAP_PER_PACKET 0x40000 -/* IPv6 tunnel */ +struct __ip6_tnl_parm { + char name[IFNAMSIZ]; /* name of tunnel device */ + int link; /* ifindex of underlying L2 interface */ + __u8 proto; /* tunnel protocol */ + __u8 encap_limit; /* encapsulation limit for tunnel */ + __u8 hop_limit; /* hop limit for tunnel */ + __be32 flowinfo; /* traffic class and flowlabel for tunnel */ + __u32 flags; /* tunnel flags */ + struct in6_addr laddr; /* local tunnel end-point address */ + struct in6_addr raddr; /* remote tunnel end-point address */ + + __be16 i_flags; + __be16 o_flags; + __be32 i_key; + __be32 o_key; +}; +/* IPv6 tunnel */ struct ip6_tnl { struct ip6_tnl __rcu *next; /* next tunnel in list */ struct net_device *dev; /* virtual device associated with tunnel */ - struct ip6_tnl_parm parms; /* tunnel configuration parameters */ + struct __ip6_tnl_parm parms; /* tunnel configuration parameters */ struct flowi fl; /* flowi template for xmit */ struct dst_entry *dst_cache; /* cached dst */ u32 dst_cookie; + + int err_count; + unsigned long err_time; + + /* These fields used only by GRE */ + __u32 i_seqno; /* The last seen seqno */ + __u32 o_seqno; /* The last output seqno */ + int hlen; /* Precalculated GRE header length */ + int mlink; }; /* Tunnel encapsulation limit destination sub-option */ @@ -31,4 +58,14 @@ struct ipv6_tlv_tnl_enc_lim { __u8 encap_limit; /* tunnel encapsulation limit */ } __packed; +struct dst_entry *ip6_tnl_dst_check(struct ip6_tnl *t); +void ip6_tnl_dst_reset(struct ip6_tnl *t); +void ip6_tnl_dst_store(struct ip6_tnl *t, struct dst_entry *dst); +int ip6_tnl_rcv_ctl(struct ip6_tnl *t, const struct in6_addr *laddr, + const struct in6_addr *raddr); +int ip6_tnl_xmit_ctl(struct ip6_tnl *t); +__u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw); +__u32 ip6_tnl_get_cap(struct ip6_tnl *t, const struct in6_addr *laddr, + const struct in6_addr *raddr); + #endif diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 01c34b363a34..6d01fb00ff2b 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -34,6 +34,7 @@ #define NEXTHDR_IPV6 41 /* IPv6 in IPv6 */ #define NEXTHDR_ROUTING 43 /* Routing header. */ #define NEXTHDR_FRAGMENT 44 /* Fragmentation/reassembly header. */ +#define NEXTHDR_GRE 47 /* GRE header. */ #define NEXTHDR_ESP 50 /* Encapsulating security payload. */ #define NEXTHDR_AUTH 51 /* Authentication header. */ #define NEXTHDR_ICMP 58 /* ICMP for IPv6. */ diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig index 5728695b5449..4f7fe7270e37 100644 --- a/net/ipv6/Kconfig +++ b/net/ipv6/Kconfig @@ -201,6 +201,22 @@ config IPV6_TUNNEL If unsure, say N. +config IPV6_GRE + tristate "IPv6: GRE tunnel" + select IPV6_TUNNEL + ---help--- + Tunneling means encapsulating data of one protocol type within + another protocol and sending it over a channel that understands the + encapsulating protocol. This particular tunneling driver implements + GRE (Generic Routing Encapsulation) and at this time allows + encapsulating of IPv4 or IPv6 over existing IPv6 infrastructure. + This driver is useful if the other endpoint is a Cisco router: Cisco + likes GRE much better than the other Linux tunneling driver ("IP + tunneling" above). In addition, GRE allows multicast redistribution + through the tunnel. + + Saying M here will produce a module called ip6_gre. If unsure, say N. + config IPV6_MULTIPLE_TABLES bool "IPv6: Multiple Routing Tables" depends on EXPERIMENTAL diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index 686934acfac1..b6d3f79151e2 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_NETFILTER) += netfilter/ obj-$(CONFIG_IPV6_SIT) += sit.o obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o +obj-$(CONFIG_IPV6_GRE) += ip6_gre.o obj-y += addrconf_core.o exthdrs_core.o diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c new file mode 100644 index 000000000000..a84ad5dc4fcf --- /dev/null +++ b/net/ipv6/ip6_gre.c @@ -0,0 +1,1790 @@ +/* + * GRE over IPv6 protocol decoder. + * + * Authors: Dmitry Kozlov (xeb@mail.ru) + * + * 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. + * + */ + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +#define IPV6_TCLASS_MASK (IPV6_FLOWINFO_MASK & ~IPV6_FLOWLABEL_MASK) +#define IPV6_TCLASS_SHIFT 20 + +#define HASH_SIZE_SHIFT 5 +#define HASH_SIZE (1 << HASH_SIZE_SHIFT) + +static int ip6gre_net_id __read_mostly; +struct ip6gre_net { + struct ip6_tnl __rcu *tunnels[4][HASH_SIZE]; + + struct net_device *fb_tunnel_dev; +}; + +static struct rtnl_link_ops ip6gre_link_ops __read_mostly; +static int ip6gre_tunnel_init(struct net_device *dev); +static void ip6gre_tunnel_setup(struct net_device *dev); +static void ip6gre_tunnel_link(struct ip6gre_net *ign, struct ip6_tnl *t); +static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu); + +/* Tunnel hash table */ + +/* + 4 hash tables: + + 3: (remote,local) + 2: (remote,*) + 1: (*,local) + 0: (*,*) + + We require exact key match i.e. if a key is present in packet + it will match only tunnel with the same key; if it is not present, + it will match only keyless tunnel. + + All keysless packets, if not matched configured keyless tunnels + will match fallback tunnel. + */ + +#define HASH_KEY(key) (((__force u32)key^((__force u32)key>>4))&(HASH_SIZE - 1)) +static u32 HASH_ADDR(const struct in6_addr *addr) +{ + u32 hash = ipv6_addr_hash(addr); + + return hash_32(hash, HASH_SIZE_SHIFT); +} + +#define tunnels_r_l tunnels[3] +#define tunnels_r tunnels[2] +#define tunnels_l tunnels[1] +#define tunnels_wc tunnels[0] +/* + * Locking : hash tables are protected by RCU and RTNL + */ + +#define for_each_ip_tunnel_rcu(start) \ + for (t = rcu_dereference(start); t; t = rcu_dereference(t->next)) + +/* often modified stats are per cpu, other are shared (netdev->stats) */ +struct pcpu_tstats { + u64 rx_packets; + u64 rx_bytes; + u64 tx_packets; + u64 tx_bytes; + struct u64_stats_sync syncp; +}; + +static struct rtnl_link_stats64 *ip6gre_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *tot) +{ + int i; + + for_each_possible_cpu(i) { + const struct pcpu_tstats *tstats = per_cpu_ptr(dev->tstats, i); + u64 rx_packets, rx_bytes, tx_packets, tx_bytes; + unsigned int start; + + do { + start = u64_stats_fetch_begin_bh(&tstats->syncp); + rx_packets = tstats->rx_packets; + tx_packets = tstats->tx_packets; + rx_bytes = tstats->rx_bytes; + tx_bytes = tstats->tx_bytes; + } while (u64_stats_fetch_retry_bh(&tstats->syncp, start)); + + tot->rx_packets += rx_packets; + tot->tx_packets += tx_packets; + tot->rx_bytes += rx_bytes; + tot->tx_bytes += tx_bytes; + } + + tot->multicast = dev->stats.multicast; + tot->rx_crc_errors = dev->stats.rx_crc_errors; + tot->rx_fifo_errors = dev->stats.rx_fifo_errors; + tot->rx_length_errors = dev->stats.rx_length_errors; + tot->rx_errors = dev->stats.rx_errors; + tot->tx_fifo_errors = dev->stats.tx_fifo_errors; + tot->tx_carrier_errors = dev->stats.tx_carrier_errors; + tot->tx_dropped = dev->stats.tx_dropped; + tot->tx_aborted_errors = dev->stats.tx_aborted_errors; + tot->tx_errors = dev->stats.tx_errors; + + return tot; +} + +/* Given src, dst and key, find appropriate for input tunnel. */ + +static struct ip6_tnl *ip6gre_tunnel_lookup(struct net_device *dev, + const struct in6_addr *remote, const struct in6_addr *local, + __be32 key, __be16 gre_proto) +{ + struct net *net = dev_net(dev); + int link = dev->ifindex; + unsigned int h0 = HASH_ADDR(remote); + unsigned int h1 = HASH_KEY(key); + struct ip6_tnl *t, *cand = NULL; + struct ip6gre_net *ign = net_generic(net, ip6gre_net_id); + int dev_type = (gre_proto == htons(ETH_P_TEB)) ? + ARPHRD_ETHER : ARPHRD_IP6GRE; + int score, cand_score = 4; + + for_each_ip_tunnel_rcu(ign->tunnels_r_l[h0 ^ h1]) { + if (!ipv6_addr_equal(local, &t->parms.laddr) || + !ipv6_addr_equal(remote, &t->parms.raddr) || + key != t->parms.i_key || + !(t->dev->flags & IFF_UP)) + continue; + + if (t->dev->type != ARPHRD_IP6GRE && + t->dev->type != dev_type) + continue; + + score = 0; + if (t->parms.link != link) + score |= 1; + if (t->dev->type != dev_type) + score |= 2; + if (score == 0) + return t; + + if (score < cand_score) { + cand = t; + cand_score = score; + } + } + + for_each_ip_tunnel_rcu(ign->tunnels_r[h0 ^ h1]) { + if (!ipv6_addr_equal(remote, &t->parms.raddr) || + key != t->parms.i_key || + !(t->dev->flags & IFF_UP)) + continue; + + if (t->dev->type != ARPHRD_IP6GRE && + t->dev->type != dev_type) + continue; + + score = 0; + if (t->parms.link != link) + score |= 1; + if (t->dev->type != dev_type) + score |= 2; + if (score == 0) + return t; + + if (score < cand_score) { + cand = t; + cand_score = score; + } + } + + for_each_ip_tunnel_rcu(ign->tunnels_l[h1]) { + if ((!ipv6_addr_equal(local, &t->parms.laddr) && + (!ipv6_addr_equal(local, &t->parms.raddr) || + !ipv6_addr_is_multicast(local))) || + key != t->parms.i_key || + !(t->dev->flags & IFF_UP)) + continue; + + if (t->dev->type != ARPHRD_IP6GRE && + t->dev->type != dev_type) + continue; + + score = 0; + if (t->parms.link != link) + score |= 1; + if (t->dev->type != dev_type) + score |= 2; + if (score == 0) + return t; + + if (score < cand_score) { + cand = t; + cand_score = score; + } + } + + for_each_ip_tunnel_rcu(ign->tunnels_wc[h1]) { + if (t->parms.i_key != key || + !(t->dev->flags & IFF_UP)) + continue; + + if (t->dev->type != ARPHRD_IP6GRE && + t->dev->type != dev_type) + continue; + + score = 0; + if (t->parms.link != link) + score |= 1; + if (t->dev->type != dev_type) + score |= 2; + if (score == 0) + return t; + + if (score < cand_score) { + cand = t; + cand_score = score; + } + } + + if (cand != NULL) + return cand; + + dev = ign->fb_tunnel_dev; + if (dev->flags & IFF_UP) + return netdev_priv(dev); + + return NULL; +} + +static struct ip6_tnl __rcu **__ip6gre_bucket(struct ip6gre_net *ign, + const struct __ip6_tnl_parm *p) +{ + const struct in6_addr *remote = &p->raddr; + const struct in6_addr *local = &p->laddr; + unsigned int h = HASH_KEY(p->i_key); + int prio = 0; + + if (!ipv6_addr_any(local)) + prio |= 1; + if (!ipv6_addr_any(remote) && !ipv6_addr_is_multicast(remote)) { + prio |= 2; + h ^= HASH_ADDR(remote); + } + + return &ign->tunnels[prio][h]; +} + +static inline struct ip6_tnl __rcu **ip6gre_bucket(struct ip6gre_net *ign, + const struct ip6_tnl *t) +{ + return __ip6gre_bucket(ign, &t->parms); +} + +static void ip6gre_tunnel_link(struct ip6gre_net *ign, struct ip6_tnl *t) +{ + struct ip6_tnl __rcu **tp = ip6gre_bucket(ign, t); + + rcu_assign_pointer(t->next, rtnl_dereference(*tp)); + rcu_assign_pointer(*tp, t); +} + +static void ip6gre_tunnel_unlink(struct ip6gre_net *ign, struct ip6_tnl *t) +{ + struct ip6_tnl __rcu **tp; + struct ip6_tnl *iter; + + for (tp = ip6gre_bucket(ign, t); + (iter = rtnl_dereference(*tp)) != NULL; + tp = &iter->next) { + if (t == iter) { + rcu_assign_pointer(*tp, t->next); + break; + } + } +} + +static struct ip6_tnl *ip6gre_tunnel_find(struct net *net, + const struct __ip6_tnl_parm *parms, + int type) +{ + const struct in6_addr *remote = &parms->raddr; + const struct in6_addr *local = &parms->laddr; + __be32 key = parms->i_key; + int link = parms->link; + struct ip6_tnl *t; + struct ip6_tnl __rcu **tp; + struct ip6gre_net *ign = net_generic(net, ip6gre_net_id); + + for (tp = __ip6gre_bucket(ign, parms); + (t = rtnl_dereference(*tp)) != NULL; + tp = &t->next) + if (ipv6_addr_equal(local, &t->parms.laddr) && + ipv6_addr_equal(remote, &t->parms.raddr) && + key == t->parms.i_key && + link == t->parms.link && + type == t->dev->type) + break; + + return t; +} + +static struct ip6_tnl *ip6gre_tunnel_locate(struct net *net, + const struct __ip6_tnl_parm *parms, int create) +{ + struct ip6_tnl *t, *nt; + struct net_device *dev; + char name[IFNAMSIZ]; + struct ip6gre_net *ign = net_generic(net, ip6gre_net_id); + + t = ip6gre_tunnel_find(net, parms, ARPHRD_IP6GRE); + if (t || !create) + return t; + + if (parms->name[0]) + strlcpy(name, parms->name, IFNAMSIZ); + else + strcpy(name, "ip6gre%d"); + + dev = alloc_netdev(sizeof(*t), name, ip6gre_tunnel_setup); + if (!dev) + return NULL; + + dev_net_set(dev, net); + + nt = netdev_priv(dev); + nt->parms = *parms; + dev->rtnl_link_ops = &ip6gre_link_ops; + + nt->dev = dev; + ip6gre_tnl_link_config(nt, 1); + + if (register_netdevice(dev) < 0) + goto failed_free; + + /* Can use a lockless transmit, unless we generate output sequences */ + if (!(nt->parms.o_flags & GRE_SEQ)) + dev->features |= NETIF_F_LLTX; + + dev_hold(dev); + ip6gre_tunnel_link(ign, nt); + return nt; + +failed_free: + free_netdev(dev); + return NULL; +} + +static void ip6gre_tunnel_uninit(struct net_device *dev) +{ + struct net *net = dev_net(dev); + struct ip6gre_net *ign = net_generic(net, ip6gre_net_id); + + ip6gre_tunnel_unlink(ign, netdev_priv(dev)); + dev_put(dev); +} + + +static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + u8 type, u8 code, int offset, __be32 info) +{ + const struct ipv6hdr *ipv6h = (const struct ipv6hdr *)skb->data; + __be16 *p = (__be16 *)(ipv6h + 1); + int grehlen = sizeof(ipv6h) + 4; + struct ip6_tnl *t; + __be16 flags; + + flags = p[0]; + if (flags&(GRE_CSUM|GRE_KEY|GRE_SEQ|GRE_ROUTING|GRE_VERSION)) { + if (flags&(GRE_VERSION|GRE_ROUTING)) + return; + if (flags&GRE_KEY) { + grehlen += 4; + if (flags&GRE_CSUM) + grehlen += 4; + } + } + + /* If only 8 bytes returned, keyed message will be dropped here */ + if (skb_headlen(skb) < grehlen) + return; + + rcu_read_lock(); + + t = ip6gre_tunnel_lookup(skb->dev, &ipv6h->daddr, &ipv6h->saddr, + flags & GRE_KEY ? + *(((__be32 *)p) + (grehlen / 4) - 1) : 0, + p[1]); + if (t == NULL) + goto out; + + switch (type) { + __u32 teli; + struct ipv6_tlv_tnl_enc_lim *tel; + __u32 mtu; + case ICMPV6_DEST_UNREACH: + net_warn_ratelimited("%s: Path to destination invalid or inactive!\n", + t->parms.name); + break; + case ICMPV6_TIME_EXCEED: + if (code == ICMPV6_EXC_HOPLIMIT) { + net_warn_ratelimited("%s: Too small hop limit or routing loop in tunnel!\n", + t->parms.name); + } + break; + case ICMPV6_PARAMPROB: + teli = 0; + if (code == ICMPV6_HDR_FIELD) + teli = ip6_tnl_parse_tlv_enc_lim(skb, skb->data); + + if (teli && teli == info - 2) { + tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->data[teli]; + if (tel->encap_limit == 0) { + net_warn_ratelimited("%s: Too small encapsulation limit or routing loop in tunnel!\n", + t->parms.name); + } + } else { + net_warn_ratelimited("%s: Recipient unable to parse tunneled packet!\n", + t->parms.name); + } + break; + case ICMPV6_PKT_TOOBIG: + mtu = info - offset; + if (mtu < IPV6_MIN_MTU) + mtu = IPV6_MIN_MTU; + t->dev->mtu = mtu; + break; + } + + if (time_before(jiffies, t->err_time + IP6TUNNEL_ERR_TIMEO)) + t->err_count++; + else + t->err_count = 1; + t->err_time = jiffies; +out: + rcu_read_unlock(); +} + +static inline void ip6gre_ecn_decapsulate_ipv4(const struct ip6_tnl *t, + const struct ipv6hdr *ipv6h, struct sk_buff *skb) +{ + __u8 dsfield = ipv6_get_dsfield(ipv6h) & ~INET_ECN_MASK; + + if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY) + ipv4_change_dsfield(ip_hdr(skb), INET_ECN_MASK, dsfield); + + if (INET_ECN_is_ce(dsfield)) + IP_ECN_set_ce(ip_hdr(skb)); +} + +static inline void ip6gre_ecn_decapsulate_ipv6(const struct ip6_tnl *t, + const struct ipv6hdr *ipv6h, struct sk_buff *skb) +{ + if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY) + ipv6_copy_dscp(ipv6_get_dsfield(ipv6h), ipv6_hdr(skb)); + + if (INET_ECN_is_ce(ipv6_get_dsfield(ipv6h))) + IP6_ECN_set_ce(ipv6_hdr(skb)); +} + +static int ip6gre_rcv(struct sk_buff *skb) +{ + const struct ipv6hdr *ipv6h; + u8 *h; + __be16 flags; + __sum16 csum = 0; + __be32 key = 0; + u32 seqno = 0; + struct ip6_tnl *tunnel; + int offset = 4; + __be16 gre_proto; + + if (!pskb_may_pull(skb, sizeof(struct in6_addr))) + goto drop_nolock; + + ipv6h = ipv6_hdr(skb); + h = skb->data; + flags = *(__be16 *)h; + + if (flags&(GRE_CSUM|GRE_KEY|GRE_ROUTING|GRE_SEQ|GRE_VERSION)) { + /* - Version must be 0. + - We do not support routing headers. + */ + if (flags&(GRE_VERSION|GRE_ROUTING)) + goto drop_nolock; + + if (flags&GRE_CSUM) { + switch (skb->ip_summed) { + case CHECKSUM_COMPLETE: + csum = csum_fold(skb->csum); + if (!csum) + break; + /* fall through */ + case CHECKSUM_NONE: + skb->csum = 0; + csum = __skb_checksum_complete(skb); + skb->ip_summed = CHECKSUM_COMPLETE; + } + offset += 4; + } + if (flags&GRE_KEY) { + key = *(__be32 *)(h + offset); + offset += 4; + } + if (flags&GRE_SEQ) { + seqno = ntohl(*(__be32 *)(h + offset)); + offset += 4; + } + } + + gre_proto = *(__be16 *)(h + 2); + + rcu_read_lock(); + tunnel = ip6gre_tunnel_lookup(skb->dev, + &ipv6h->saddr, &ipv6h->daddr, key, + gre_proto); + if (tunnel) { + struct pcpu_tstats *tstats; + + if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) + goto drop; + + if (!ip6_tnl_rcv_ctl(tunnel, &ipv6h->daddr, &ipv6h->saddr)) { + tunnel->dev->stats.rx_dropped++; + goto drop; + } + + secpath_reset(skb); + + skb->protocol = gre_proto; + /* WCCP version 1 and 2 protocol decoding. + * - Change protocol to IP + * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header + */ + if (flags == 0 && gre_proto == htons(ETH_P_WCCP)) { + skb->protocol = htons(ETH_P_IP); + if ((*(h + offset) & 0xF0) != 0x40) + offset += 4; + } + + skb->mac_header = skb->network_header; + __pskb_pull(skb, offset); + skb_postpull_rcsum(skb, skb_transport_header(skb), offset); + skb->pkt_type = PACKET_HOST; + + if (((flags&GRE_CSUM) && csum) || + (!(flags&GRE_CSUM) && tunnel->parms.i_flags&GRE_CSUM)) { + tunnel->dev->stats.rx_crc_errors++; + tunnel->dev->stats.rx_errors++; + goto drop; + } + if (tunnel->parms.i_flags&GRE_SEQ) { + if (!(flags&GRE_SEQ) || + (tunnel->i_seqno && + (s32)(seqno - tunnel->i_seqno) < 0)) { + tunnel->dev->stats.rx_fifo_errors++; + tunnel->dev->stats.rx_errors++; + goto drop; + } + tunnel->i_seqno = seqno + 1; + } + + /* Warning: All skb pointers will be invalidated! */ + if (tunnel->dev->type == ARPHRD_ETHER) { + if (!pskb_may_pull(skb, ETH_HLEN)) { + tunnel->dev->stats.rx_length_errors++; + tunnel->dev->stats.rx_errors++; + goto drop; + } + + ipv6h = ipv6_hdr(skb); + skb->protocol = eth_type_trans(skb, tunnel->dev); + skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN); + } + + tstats = this_cpu_ptr(tunnel->dev->tstats); + u64_stats_update_begin(&tstats->syncp); + tstats->rx_packets++; + tstats->rx_bytes += skb->len; + u64_stats_update_end(&tstats->syncp); + + __skb_tunnel_rx(skb, tunnel->dev); + + skb_reset_network_header(skb); + if (skb->protocol == htons(ETH_P_IP)) + ip6gre_ecn_decapsulate_ipv4(tunnel, ipv6h, skb); + else if (skb->protocol == htons(ETH_P_IPV6)) + ip6gre_ecn_decapsulate_ipv6(tunnel, ipv6h, skb); + + netif_rx(skb); + + rcu_read_unlock(); + return 0; + } + icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); + +drop: + rcu_read_unlock(); +drop_nolock: + kfree_skb(skb); + return 0; +} + +struct ipv6_tel_txoption { + struct ipv6_txoptions ops; + __u8 dst_opt[8]; +}; + +static void init_tel_txopt(struct ipv6_tel_txoption *opt, __u8 encap_limit) +{ + memset(opt, 0, sizeof(struct ipv6_tel_txoption)); + + opt->dst_opt[2] = IPV6_TLV_TNL_ENCAP_LIMIT; + opt->dst_opt[3] = 1; + opt->dst_opt[4] = encap_limit; + opt->dst_opt[5] = IPV6_TLV_PADN; + opt->dst_opt[6] = 1; + + opt->ops.dst0opt = (struct ipv6_opt_hdr *) opt->dst_opt; + opt->ops.opt_nflen = 8; +} + +static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb, + struct net_device *dev, + __u8 dsfield, + struct flowi6 *fl6, + int encap_limit, + __u32 *pmtu) +{ + struct net *net = dev_net(dev); + struct ip6_tnl *tunnel = netdev_priv(dev); + struct net_device *tdev; /* Device to other host */ + struct ipv6hdr *ipv6h; /* Our new IP header */ + unsigned int max_headroom; /* The extra header space needed */ + int gre_hlen; + struct ipv6_tel_txoption opt; + int mtu; + struct dst_entry *dst = NULL, *ndst = NULL; + struct net_device_stats *stats = &tunnel->dev->stats; + int err = -1; + u8 proto; + int pkt_len; + struct sk_buff *new_skb; + + if (dev->type == ARPHRD_ETHER) + IPCB(skb)->flags = 0; + + if (dev->header_ops && dev->type == ARPHRD_IP6GRE) { + gre_hlen = 0; + ipv6h = (struct ipv6hdr *)skb->data; + fl6->daddr = ipv6h->daddr; + } else { + gre_hlen = tunnel->hlen; + fl6->daddr = tunnel->parms.raddr; + } + + if (!fl6->flowi6_mark) + dst = ip6_tnl_dst_check(tunnel); + + if (!dst) { + ndst = ip6_route_output(net, NULL, fl6); + + if (ndst->error) + goto tx_err_link_failure; + ndst = xfrm_lookup(net, ndst, flowi6_to_flowi(fl6), NULL, 0); + if (IS_ERR(ndst)) { + err = PTR_ERR(ndst); + ndst = NULL; + goto tx_err_link_failure; + } + dst = ndst; + } + + tdev = dst->dev; + + if (tdev == dev) { + stats->collisions++; + net_warn_ratelimited("%s: Local routing loop detected!\n", + tunnel->parms.name); + goto tx_err_dst_release; + } + + mtu = dst_mtu(dst) - sizeof(*ipv6h); + if (encap_limit >= 0) { + max_headroom += 8; + mtu -= 8; + } + if (mtu < IPV6_MIN_MTU) + mtu = IPV6_MIN_MTU; + if (skb_dst(skb)) + skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu); + if (skb->len > mtu) { + *pmtu = mtu; + err = -EMSGSIZE; + goto tx_err_dst_release; + } + + if (tunnel->err_count > 0) { + if (time_before(jiffies, + tunnel->err_time + IP6TUNNEL_ERR_TIMEO)) { + tunnel->err_count--; + + dst_link_failure(skb); + } else + tunnel->err_count = 0; + } + + max_headroom = LL_RESERVED_SPACE(tdev) + gre_hlen + dst->header_len; + + if (skb_headroom(skb) < max_headroom || skb_shared(skb) || + (skb_cloned(skb) && !skb_clone_writable(skb, 0))) { + new_skb = skb_realloc_headroom(skb, max_headroom); + if (max_headroom > dev->needed_headroom) + dev->needed_headroom = max_headroom; + if (!new_skb) + goto tx_err_dst_release; + + if (skb->sk) + skb_set_owner_w(new_skb, skb->sk); + consume_skb(skb); + skb = new_skb; + } + + skb_dst_drop(skb); + + if (fl6->flowi6_mark) { + skb_dst_set(skb, dst); + ndst = NULL; + } else { + skb_dst_set_noref(skb, dst); + } + + skb->transport_header = skb->network_header; + + proto = NEXTHDR_GRE; + if (encap_limit >= 0) { + init_tel_txopt(&opt, encap_limit); + ipv6_push_nfrag_opts(skb, &opt.ops, &proto, NULL); + } + + skb_push(skb, gre_hlen); + skb_reset_network_header(skb); + + /* + * Push down and install the IP header. + */ + ipv6h = ipv6_hdr(skb); + *(__be32 *)ipv6h = fl6->flowlabel | htonl(0x60000000); + dsfield = INET_ECN_encapsulate(0, dsfield); + ipv6_change_dsfield(ipv6h, ~INET_ECN_MASK, dsfield); + ipv6h->hop_limit = tunnel->parms.hop_limit; + ipv6h->nexthdr = proto; + ipv6h->saddr = fl6->saddr; + ipv6h->daddr = fl6->daddr; + + ((__be16 *)(ipv6h + 1))[0] = tunnel->parms.o_flags; + ((__be16 *)(ipv6h + 1))[1] = (dev->type == ARPHRD_ETHER) ? + htons(ETH_P_TEB) : skb->protocol; + + if (tunnel->parms.o_flags&(GRE_KEY|GRE_CSUM|GRE_SEQ)) { + __be32 *ptr = (__be32 *)(((u8 *)ipv6h) + tunnel->hlen - 4); + + if (tunnel->parms.o_flags&GRE_SEQ) { + ++tunnel->o_seqno; + *ptr = htonl(tunnel->o_seqno); + ptr--; + } + if (tunnel->parms.o_flags&GRE_KEY) { + *ptr = tunnel->parms.o_key; + ptr--; + } + if (tunnel->parms.o_flags&GRE_CSUM) { + *ptr = 0; + *(__sum16 *)ptr = ip_compute_csum((void *)(ipv6h+1), + skb->len - sizeof(struct ipv6hdr)); + } + } + + nf_reset(skb); + pkt_len = skb->len; + err = ip6_local_out(skb); + + if (net_xmit_eval(err) == 0) { + struct pcpu_tstats *tstats = this_cpu_ptr(tunnel->dev->tstats); + + tstats->tx_bytes += pkt_len; + tstats->tx_packets++; + } else { + stats->tx_errors++; + stats->tx_aborted_errors++; + } + + if (ndst) + ip6_tnl_dst_store(tunnel, ndst); + + return 0; +tx_err_link_failure: + stats->tx_carrier_errors++; + dst_link_failure(skb); +tx_err_dst_release: + dst_release(ndst); + return err; +} + +static inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev) +{ + struct ip6_tnl *t = netdev_priv(dev); + const struct iphdr *iph = ip_hdr(skb); + int encap_limit = -1; + struct flowi6 fl6; + __u8 dsfield; + __u32 mtu; + int err; + + if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) + encap_limit = t->parms.encap_limit; + + memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6)); + fl6.flowi6_proto = IPPROTO_IPIP; + + dsfield = ipv4_get_dsfield(iph); + + if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS) + fl6.flowlabel |= htonl((__u32)iph->tos << IPV6_TCLASS_SHIFT) + & IPV6_TCLASS_MASK; + if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK) + fl6.flowi6_mark = skb->mark; + + err = ip6gre_xmit2(skb, dev, dsfield, &fl6, encap_limit, &mtu); + if (err != 0) { + /* XXX: send ICMP error even if DF is not set. */ + if (err == -EMSGSIZE) + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, + htonl(mtu)); + return -1; + } + + return 0; +} + +static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev) +{ + struct ip6_tnl *t = netdev_priv(dev); + struct ipv6hdr *ipv6h = ipv6_hdr(skb); + int encap_limit = -1; + __u16 offset; + struct flowi6 fl6; + __u8 dsfield; + __u32 mtu; + int err; + + if (ipv6_addr_equal(&t->parms.raddr, &ipv6h->saddr)) + return -1; + + offset = ip6_tnl_parse_tlv_enc_lim(skb, skb_network_header(skb)); + if (offset > 0) { + struct ipv6_tlv_tnl_enc_lim *tel; + tel = (struct ipv6_tlv_tnl_enc_lim *)&skb_network_header(skb)[offset]; + if (tel->encap_limit == 0) { + icmpv6_send(skb, ICMPV6_PARAMPROB, + ICMPV6_HDR_FIELD, offset + 2); + return -1; + } + encap_limit = tel->encap_limit - 1; + } else if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) + encap_limit = t->parms.encap_limit; + + memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6)); + fl6.flowi6_proto = IPPROTO_IPV6; + + dsfield = ipv6_get_dsfield(ipv6h); + if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS) + fl6.flowlabel |= (*(__be32 *) ipv6h & IPV6_TCLASS_MASK); + if (t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL) + fl6.flowlabel |= (*(__be32 *) ipv6h & IPV6_FLOWLABEL_MASK); + if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK) + fl6.flowi6_mark = skb->mark; + + err = ip6gre_xmit2(skb, dev, dsfield, &fl6, encap_limit, &mtu); + if (err != 0) { + if (err == -EMSGSIZE) + icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); + return -1; + } + + return 0; +} + +/** + * ip6_tnl_addr_conflict - compare packet addresses to tunnel's own + * @t: the outgoing tunnel device + * @hdr: IPv6 header from the incoming packet + * + * Description: + * Avoid trivial tunneling loop by checking that tunnel exit-point + * doesn't match source of incoming packet. + * + * Return: + * 1 if conflict, + * 0 else + **/ + +static inline bool ip6gre_tnl_addr_conflict(const struct ip6_tnl *t, + const struct ipv6hdr *hdr) +{ + return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr); +} + +static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev) +{ + struct ip6_tnl *t = netdev_priv(dev); + int encap_limit = -1; + struct flowi6 fl6; + __u32 mtu; + int err; + + if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) + encap_limit = t->parms.encap_limit; + + memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6)); + fl6.flowi6_proto = skb->protocol; + + err = ip6gre_xmit2(skb, dev, 0, &fl6, encap_limit, &mtu); + + return err; +} + +static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct ip6_tnl *t = netdev_priv(dev); + struct net_device_stats *stats = &t->dev->stats; + int ret; + + if (!ip6_tnl_xmit_ctl(t)) + return -1; + + switch (skb->protocol) { + case htons(ETH_P_IP): + ret = ip6gre_xmit_ipv4(skb, dev); + break; + case htons(ETH_P_IPV6): + ret = ip6gre_xmit_ipv6(skb, dev); + break; + default: + ret = ip6gre_xmit_other(skb, dev); + break; + } + + if (ret < 0) + goto tx_err; + + return NETDEV_TX_OK; + +tx_err: + stats->tx_errors++; + stats->tx_dropped++; + kfree_skb(skb); + return NETDEV_TX_OK; +} + +static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu) +{ + struct net_device *dev = t->dev; + struct __ip6_tnl_parm *p = &t->parms; + struct flowi6 *fl6 = &t->fl.u.ip6; + int addend = sizeof(struct ipv6hdr) + 4; + + if (dev->type != ARPHRD_ETHER) { + memcpy(dev->dev_addr, &p->laddr, sizeof(struct in6_addr)); + memcpy(dev->broadcast, &p->raddr, sizeof(struct in6_addr)); + } + + /* Set up flowi template */ + fl6->saddr = p->laddr; + fl6->daddr = p->raddr; + fl6->flowi6_oif = p->link; + fl6->flowlabel = 0; + + if (!(p->flags&IP6_TNL_F_USE_ORIG_TCLASS)) + fl6->flowlabel |= IPV6_TCLASS_MASK & p->flowinfo; + if (!(p->flags&IP6_TNL_F_USE_ORIG_FLOWLABEL)) + fl6->flowlabel |= IPV6_FLOWLABEL_MASK & p->flowinfo; + + p->flags &= ~(IP6_TNL_F_CAP_XMIT|IP6_TNL_F_CAP_RCV|IP6_TNL_F_CAP_PER_PACKET); + p->flags |= ip6_tnl_get_cap(t, &p->laddr, &p->raddr); + + if (p->flags&IP6_TNL_F_CAP_XMIT && + p->flags&IP6_TNL_F_CAP_RCV && dev->type != ARPHRD_ETHER) + dev->flags |= IFF_POINTOPOINT; + else + dev->flags &= ~IFF_POINTOPOINT; + + dev->iflink = p->link; + + /* Precalculate GRE options length */ + if (t->parms.o_flags&(GRE_CSUM|GRE_KEY|GRE_SEQ)) { + if (t->parms.o_flags&GRE_CSUM) + addend += 4; + if (t->parms.o_flags&GRE_KEY) + addend += 4; + if (t->parms.o_flags&GRE_SEQ) + addend += 4; + } + + if (p->flags & IP6_TNL_F_CAP_XMIT) { + int strict = (ipv6_addr_type(&p->raddr) & + (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL)); + + struct rt6_info *rt = rt6_lookup(dev_net(dev), + &p->raddr, &p->laddr, + p->link, strict); + + if (rt == NULL) + return; + + if (rt->dst.dev) { + dev->hard_header_len = rt->dst.dev->hard_header_len + addend; + + if (set_mtu) { + dev->mtu = rt->dst.dev->mtu - addend; + if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) + dev->mtu -= 8; + + if (dev->mtu < IPV6_MIN_MTU) + dev->mtu = IPV6_MIN_MTU; + } + } + dst_release(&rt->dst); + } + + t->hlen = addend; +} + +static int ip6gre_tnl_change(struct ip6_tnl *t, + const struct __ip6_tnl_parm *p, int set_mtu) +{ + t->parms.laddr = p->laddr; + t->parms.raddr = p->raddr; + t->parms.flags = p->flags; + t->parms.hop_limit = p->hop_limit; + t->parms.encap_limit = p->encap_limit; + t->parms.flowinfo = p->flowinfo; + t->parms.link = p->link; + t->parms.proto = p->proto; + t->parms.i_key = p->i_key; + t->parms.o_key = p->o_key; + t->parms.i_flags = p->i_flags; + t->parms.o_flags = p->o_flags; + ip6_tnl_dst_reset(t); + ip6gre_tnl_link_config(t, set_mtu); + return 0; +} + +static void ip6gre_tnl_parm_from_user(struct __ip6_tnl_parm *p, + const struct ip6_tnl_parm2 *u) +{ + p->laddr = u->laddr; + p->raddr = u->raddr; + p->flags = u->flags; + p->hop_limit = u->hop_limit; + p->encap_limit = u->encap_limit; + p->flowinfo = u->flowinfo; + p->link = u->link; + p->i_key = u->i_key; + p->o_key = u->o_key; + p->i_flags = u->i_flags; + p->o_flags = u->o_flags; + memcpy(p->name, u->name, sizeof(u->name)); +} + +static void ip6gre_tnl_parm_to_user(struct ip6_tnl_parm2 *u, + const struct __ip6_tnl_parm *p) +{ + u->proto = IPPROTO_GRE; + u->laddr = p->laddr; + u->raddr = p->raddr; + u->flags = p->flags; + u->hop_limit = p->hop_limit; + u->encap_limit = p->encap_limit; + u->flowinfo = p->flowinfo; + u->link = p->link; + u->i_key = p->i_key; + u->o_key = p->o_key; + u->i_flags = p->i_flags; + u->o_flags = p->o_flags; + memcpy(u->name, p->name, sizeof(u->name)); +} + +static int ip6gre_tunnel_ioctl(struct net_device *dev, + struct ifreq *ifr, int cmd) +{ + int err = 0; + struct ip6_tnl_parm2 p; + struct __ip6_tnl_parm p1; + struct ip6_tnl *t; + struct net *net = dev_net(dev); + struct ip6gre_net *ign = net_generic(net, ip6gre_net_id); + + switch (cmd) { + case SIOCGETTUNNEL: + t = NULL; + if (dev == ign->fb_tunnel_dev) { + if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) { + err = -EFAULT; + break; + } + ip6gre_tnl_parm_from_user(&p1, &p); + t = ip6gre_tunnel_locate(net, &p1, 0); + } + if (t == NULL) + t = netdev_priv(dev); + ip6gre_tnl_parm_to_user(&p, &t->parms); + if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p))) + err = -EFAULT; + break; + + case SIOCADDTUNNEL: + case SIOCCHGTUNNEL: + err = -EPERM; + if (!capable(CAP_NET_ADMIN)) + goto done; + + err = -EFAULT; + if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) + goto done; + + err = -EINVAL; + if ((p.i_flags|p.o_flags)&(GRE_VERSION|GRE_ROUTING)) + goto done; + + if (!(p.i_flags&GRE_KEY)) + p.i_key = 0; + if (!(p.o_flags&GRE_KEY)) + p.o_key = 0; + + ip6gre_tnl_parm_from_user(&p1, &p); + t = ip6gre_tunnel_locate(net, &p1, cmd == SIOCADDTUNNEL); + + if (dev != ign->fb_tunnel_dev && cmd == SIOCCHGTUNNEL) { + if (t != NULL) { + if (t->dev != dev) { + err = -EEXIST; + break; + } + } else { + t = netdev_priv(dev); + + ip6gre_tunnel_unlink(ign, t); + synchronize_net(); + ip6gre_tnl_change(t, &p1, 1); + ip6gre_tunnel_link(ign, t); + netdev_state_change(dev); + } + } + + if (t) { + err = 0; + + ip6gre_tnl_parm_to_user(&p, &t->parms); + if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p))) + err = -EFAULT; + } else + err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT); + break; + + case SIOCDELTUNNEL: + err = -EPERM; + if (!capable(CAP_NET_ADMIN)) + goto done; + + if (dev == ign->fb_tunnel_dev) { + err = -EFAULT; + if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) + goto done; + err = -ENOENT; + ip6gre_tnl_parm_from_user(&p1, &p); + t = ip6gre_tunnel_locate(net, &p1, 0); + if (t == NULL) + goto done; + err = -EPERM; + if (t == netdev_priv(ign->fb_tunnel_dev)) + goto done; + dev = t->dev; + } + unregister_netdevice(dev); + err = 0; + break; + + default: + err = -EINVAL; + } + +done: + return err; +} + +static int ip6gre_tunnel_change_mtu(struct net_device *dev, int new_mtu) +{ + struct ip6_tnl *tunnel = netdev_priv(dev); + if (new_mtu < 68 || + new_mtu > 0xFFF8 - dev->hard_header_len - tunnel->hlen) + return -EINVAL; + dev->mtu = new_mtu; + return 0; +} + +static int ip6gre_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, + const void *daddr, const void *saddr, unsigned int len) +{ + struct ip6_tnl *t = netdev_priv(dev); + struct ipv6hdr *ipv6h = (struct ipv6hdr *)skb_push(skb, t->hlen); + __be16 *p = (__be16 *)(ipv6h+1); + + *(__be32 *)ipv6h = t->fl.u.ip6.flowlabel | htonl(0x60000000); + ipv6h->hop_limit = t->parms.hop_limit; + ipv6h->nexthdr = NEXTHDR_GRE; + ipv6h->saddr = t->parms.laddr; + ipv6h->daddr = t->parms.raddr; + + p[0] = t->parms.o_flags; + p[1] = htons(type); + + /* + * Set the source hardware address. + */ + + if (saddr) + memcpy(&ipv6h->saddr, saddr, sizeof(struct in6_addr)); + if (daddr) + memcpy(&ipv6h->daddr, daddr, sizeof(struct in6_addr)); + if (!ipv6_addr_any(&ipv6h->daddr)) + return t->hlen; + + return -t->hlen; +} + +static int ip6gre_header_parse(const struct sk_buff *skb, unsigned char *haddr) +{ + const struct ipv6hdr *ipv6h = (const struct ipv6hdr *)skb_mac_header(skb); + memcpy(haddr, &ipv6h->saddr, sizeof(struct in6_addr)); + return sizeof(struct in6_addr); +} + +static const struct header_ops ip6gre_header_ops = { + .create = ip6gre_header, + .parse = ip6gre_header_parse, +}; + +static const struct net_device_ops ip6gre_netdev_ops = { + .ndo_init = ip6gre_tunnel_init, + .ndo_uninit = ip6gre_tunnel_uninit, + .ndo_start_xmit = ip6gre_tunnel_xmit, + .ndo_do_ioctl = ip6gre_tunnel_ioctl, + .ndo_change_mtu = ip6gre_tunnel_change_mtu, + .ndo_get_stats64 = ip6gre_get_stats64, +}; + +static void ip6gre_dev_free(struct net_device *dev) +{ + free_percpu(dev->tstats); + free_netdev(dev); +} + +static void ip6gre_tunnel_setup(struct net_device *dev) +{ + struct ip6_tnl *t; + + dev->netdev_ops = &ip6gre_netdev_ops; + dev->destructor = ip6gre_dev_free; + + dev->type = ARPHRD_IP6GRE; + dev->hard_header_len = LL_MAX_HEADER + sizeof(struct ipv6hdr) + 4; + dev->mtu = ETH_DATA_LEN - sizeof(struct ipv6hdr) - 4; + t = netdev_priv(dev); + if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) + dev->mtu -= 8; + dev->flags |= IFF_NOARP; + dev->iflink = 0; + dev->addr_len = sizeof(struct in6_addr); + dev->features |= NETIF_F_NETNS_LOCAL; + dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; +} + +static int ip6gre_tunnel_init(struct net_device *dev) +{ + struct ip6_tnl *tunnel; + + tunnel = netdev_priv(dev); + + tunnel->dev = dev; + strcpy(tunnel->parms.name, dev->name); + + memcpy(dev->dev_addr, &tunnel->parms.laddr, sizeof(struct in6_addr)); + memcpy(dev->broadcast, &tunnel->parms.raddr, sizeof(struct in6_addr)); + + if (ipv6_addr_any(&tunnel->parms.raddr)) + dev->header_ops = &ip6gre_header_ops; + + dev->tstats = alloc_percpu(struct pcpu_tstats); + if (!dev->tstats) + return -ENOMEM; + + return 0; +} + +static void ip6gre_fb_tunnel_init(struct net_device *dev) +{ + struct ip6_tnl *tunnel = netdev_priv(dev); + + tunnel->dev = dev; + strcpy(tunnel->parms.name, dev->name); + + tunnel->hlen = sizeof(struct ipv6hdr) + 4; + + dev_hold(dev); +} + + +static struct inet6_protocol ip6gre_protocol __read_mostly = { + .handler = ip6gre_rcv, + .err_handler = ip6gre_err, + .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, +}; + +static void ip6gre_destroy_tunnels(struct ip6gre_net *ign, + struct list_head *head) +{ + int prio; + + for (prio = 0; prio < 4; prio++) { + int h; + for (h = 0; h < HASH_SIZE; h++) { + struct ip6_tnl *t; + + t = rtnl_dereference(ign->tunnels[prio][h]); + + while (t != NULL) { + unregister_netdevice_queue(t->dev, head); + t = rtnl_dereference(t->next); + } + } + } +} + +static int __net_init ip6gre_init_net(struct net *net) +{ + struct ip6gre_net *ign = net_generic(net, ip6gre_net_id); + int err; + + ign->fb_tunnel_dev = alloc_netdev(sizeof(struct ip6_tnl), "ip6gre0", + ip6gre_tunnel_setup); + if (!ign->fb_tunnel_dev) { + err = -ENOMEM; + goto err_alloc_dev; + } + dev_net_set(ign->fb_tunnel_dev, net); + + ip6gre_fb_tunnel_init(ign->fb_tunnel_dev); + ign->fb_tunnel_dev->rtnl_link_ops = &ip6gre_link_ops; + + err = register_netdev(ign->fb_tunnel_dev); + if (err) + goto err_reg_dev; + + rcu_assign_pointer(ign->tunnels_wc[0], + netdev_priv(ign->fb_tunnel_dev)); + return 0; + +err_reg_dev: + ip6gre_dev_free(ign->fb_tunnel_dev); +err_alloc_dev: + return err; +} + +static void __net_exit ip6gre_exit_net(struct net *net) +{ + struct ip6gre_net *ign; + LIST_HEAD(list); + + ign = net_generic(net, ip6gre_net_id); + rtnl_lock(); + ip6gre_destroy_tunnels(ign, &list); + unregister_netdevice_many(&list); + rtnl_unlock(); +} + +static struct pernet_operations ip6gre_net_ops = { + .init = ip6gre_init_net, + .exit = ip6gre_exit_net, + .id = &ip6gre_net_id, + .size = sizeof(struct ip6gre_net), +}; + +static int ip6gre_tunnel_validate(struct nlattr *tb[], struct nlattr *data[]) +{ + __be16 flags; + + if (!data) + return 0; + + flags = 0; + if (data[IFLA_GRE_IFLAGS]) + flags |= nla_get_be16(data[IFLA_GRE_IFLAGS]); + if (data[IFLA_GRE_OFLAGS]) + flags |= nla_get_be16(data[IFLA_GRE_OFLAGS]); + if (flags & (GRE_VERSION|GRE_ROUTING)) + return -EINVAL; + + return 0; +} + +static int ip6gre_tap_validate(struct nlattr *tb[], struct nlattr *data[]) +{ + struct in6_addr daddr; + + if (tb[IFLA_ADDRESS]) { + if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) + return -EINVAL; + if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) + return -EADDRNOTAVAIL; + } + + if (!data) + goto out; + + if (data[IFLA_GRE_REMOTE]) { + nla_memcpy(&daddr, data[IFLA_GRE_REMOTE], sizeof(struct in6_addr)); + if (ipv6_addr_any(&daddr)) + return -EINVAL; + } + +out: + return ip6gre_tunnel_validate(tb, data); +} + + +static void ip6gre_netlink_parms(struct nlattr *data[], + struct __ip6_tnl_parm *parms) +{ + memset(parms, 0, sizeof(*parms)); + + if (!data) + return; + + if (data[IFLA_GRE_LINK]) + parms->link = nla_get_u32(data[IFLA_GRE_LINK]); + + if (data[IFLA_GRE_IFLAGS]) + parms->i_flags = nla_get_be16(data[IFLA_GRE_IFLAGS]); + + if (data[IFLA_GRE_OFLAGS]) + parms->o_flags = nla_get_be16(data[IFLA_GRE_OFLAGS]); + + if (data[IFLA_GRE_IKEY]) + parms->i_key = nla_get_be32(data[IFLA_GRE_IKEY]); + + if (data[IFLA_GRE_OKEY]) + parms->o_key = nla_get_be32(data[IFLA_GRE_OKEY]); + + if (data[IFLA_GRE_LOCAL]) + nla_memcpy(&parms->laddr, data[IFLA_GRE_LOCAL], sizeof(struct in6_addr)); + + if (data[IFLA_GRE_REMOTE]) + nla_memcpy(&parms->raddr, data[IFLA_GRE_REMOTE], sizeof(struct in6_addr)); + + if (data[IFLA_GRE_TTL]) + parms->hop_limit = nla_get_u8(data[IFLA_GRE_TTL]); + + if (data[IFLA_GRE_ENCAP_LIMIT]) + parms->encap_limit = nla_get_u8(data[IFLA_GRE_ENCAP_LIMIT]); + + if (data[IFLA_GRE_FLOWINFO]) + parms->flowinfo = nla_get_u32(data[IFLA_GRE_FLOWINFO]); + + if (data[IFLA_GRE_FLAGS]) + parms->flags = nla_get_u32(data[IFLA_GRE_FLAGS]); +} + +static int ip6gre_tap_init(struct net_device *dev) +{ + struct ip6_tnl *tunnel; + + tunnel = netdev_priv(dev); + + tunnel->dev = dev; + strcpy(tunnel->parms.name, dev->name); + + ip6gre_tnl_link_config(tunnel, 1); + + dev->tstats = alloc_percpu(struct pcpu_tstats); + if (!dev->tstats) + return -ENOMEM; + + return 0; +} + +static const struct net_device_ops ip6gre_tap_netdev_ops = { + .ndo_init = ip6gre_tap_init, + .ndo_uninit = ip6gre_tunnel_uninit, + .ndo_start_xmit = ip6gre_tunnel_xmit, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = ip6gre_tunnel_change_mtu, + .ndo_get_stats64 = ip6gre_get_stats64, +}; + +static void ip6gre_tap_setup(struct net_device *dev) +{ + + ether_setup(dev); + + dev->netdev_ops = &ip6gre_tap_netdev_ops; + dev->destructor = ip6gre_dev_free; + + dev->iflink = 0; + dev->features |= NETIF_F_NETNS_LOCAL; +} + +static int ip6gre_newlink(struct net *src_net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[]) +{ + struct ip6_tnl *nt; + struct net *net = dev_net(dev); + struct ip6gre_net *ign = net_generic(net, ip6gre_net_id); + int err; + + nt = netdev_priv(dev); + ip6gre_netlink_parms(data, &nt->parms); + + if (ip6gre_tunnel_find(net, &nt->parms, dev->type)) + return -EEXIST; + + if (dev->type == ARPHRD_ETHER && !tb[IFLA_ADDRESS]) + eth_hw_addr_random(dev); + + nt->dev = dev; + ip6gre_tnl_link_config(nt, !tb[IFLA_MTU]); + + /* Can use a lockless transmit, unless we generate output sequences */ + if (!(nt->parms.o_flags & GRE_SEQ)) + dev->features |= NETIF_F_LLTX; + + err = register_netdevice(dev); + if (err) + goto out; + + dev_hold(dev); + ip6gre_tunnel_link(ign, nt); + +out: + return err; +} + +static int ip6gre_changelink(struct net_device *dev, struct nlattr *tb[], + struct nlattr *data[]) +{ + struct ip6_tnl *t, *nt; + struct net *net = dev_net(dev); + struct ip6gre_net *ign = net_generic(net, ip6gre_net_id); + struct __ip6_tnl_parm p; + + if (dev == ign->fb_tunnel_dev) + return -EINVAL; + + nt = netdev_priv(dev); + ip6gre_netlink_parms(data, &p); + + t = ip6gre_tunnel_locate(net, &p, 0); + + if (t) { + if (t->dev != dev) + return -EEXIST; + } else { + t = nt; + + ip6gre_tunnel_unlink(ign, t); + ip6gre_tnl_change(t, &p, !tb[IFLA_MTU]); + ip6gre_tunnel_link(ign, t); + netdev_state_change(dev); + } + + return 0; +} + +static size_t ip6gre_get_size(const struct net_device *dev) +{ + return + /* IFLA_GRE_LINK */ + nla_total_size(4) + + /* IFLA_GRE_IFLAGS */ + nla_total_size(2) + + /* IFLA_GRE_OFLAGS */ + nla_total_size(2) + + /* IFLA_GRE_IKEY */ + nla_total_size(4) + + /* IFLA_GRE_OKEY */ + nla_total_size(4) + + /* IFLA_GRE_LOCAL */ + nla_total_size(4) + + /* IFLA_GRE_REMOTE */ + nla_total_size(4) + + /* IFLA_GRE_TTL */ + nla_total_size(1) + + /* IFLA_GRE_TOS */ + nla_total_size(1) + + /* IFLA_GRE_ENCAP_LIMIT */ + nla_total_size(1) + + /* IFLA_GRE_FLOWINFO */ + nla_total_size(4) + + /* IFLA_GRE_FLAGS */ + nla_total_size(4) + + 0; +} + +static int ip6gre_fill_info(struct sk_buff *skb, const struct net_device *dev) +{ + struct ip6_tnl *t = netdev_priv(dev); + struct __ip6_tnl_parm *p = &t->parms; + + if (nla_put_u32(skb, IFLA_GRE_LINK, p->link) || + nla_put_be16(skb, IFLA_GRE_IFLAGS, p->i_flags) || + nla_put_be16(skb, IFLA_GRE_OFLAGS, p->o_flags) || + nla_put_be32(skb, IFLA_GRE_IKEY, p->i_key) || + nla_put_be32(skb, IFLA_GRE_OKEY, p->o_key) || + nla_put(skb, IFLA_GRE_LOCAL, sizeof(struct in6_addr), &p->raddr) || + nla_put(skb, IFLA_GRE_REMOTE, sizeof(struct in6_addr), &p->laddr) || + nla_put_u8(skb, IFLA_GRE_TTL, p->hop_limit) || + /*nla_put_u8(skb, IFLA_GRE_TOS, t->priority) ||*/ + nla_put_u8(skb, IFLA_GRE_ENCAP_LIMIT, p->encap_limit) || + nla_put_be32(skb, IFLA_GRE_FLOWINFO, p->flowinfo) || + nla_put_u32(skb, IFLA_GRE_FLAGS, p->flags)) + goto nla_put_failure; + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + +static const struct nla_policy ip6gre_policy[IFLA_GRE_MAX + 1] = { + [IFLA_GRE_LINK] = { .type = NLA_U32 }, + [IFLA_GRE_IFLAGS] = { .type = NLA_U16 }, + [IFLA_GRE_OFLAGS] = { .type = NLA_U16 }, + [IFLA_GRE_IKEY] = { .type = NLA_U32 }, + [IFLA_GRE_OKEY] = { .type = NLA_U32 }, + [IFLA_GRE_LOCAL] = { .len = FIELD_SIZEOF(struct ipv6hdr, saddr) }, + [IFLA_GRE_REMOTE] = { .len = FIELD_SIZEOF(struct ipv6hdr, daddr) }, + [IFLA_GRE_TTL] = { .type = NLA_U8 }, + [IFLA_GRE_ENCAP_LIMIT] = { .type = NLA_U8 }, + [IFLA_GRE_FLOWINFO] = { .type = NLA_U32 }, + [IFLA_GRE_FLAGS] = { .type = NLA_U32 }, +}; + +static struct rtnl_link_ops ip6gre_link_ops __read_mostly = { + .kind = "ip6gre", + .maxtype = IFLA_GRE_MAX, + .policy = ip6gre_policy, + .priv_size = sizeof(struct ip6_tnl), + .setup = ip6gre_tunnel_setup, + .validate = ip6gre_tunnel_validate, + .newlink = ip6gre_newlink, + .changelink = ip6gre_changelink, + .get_size = ip6gre_get_size, + .fill_info = ip6gre_fill_info, +}; + +static struct rtnl_link_ops ip6gre_tap_ops __read_mostly = { + .kind = "ip6gretap", + .maxtype = IFLA_GRE_MAX, + .policy = ip6gre_policy, + .priv_size = sizeof(struct ip6_tnl), + .setup = ip6gre_tap_setup, + .validate = ip6gre_tap_validate, + .newlink = ip6gre_newlink, + .changelink = ip6gre_changelink, + .get_size = ip6gre_get_size, + .fill_info = ip6gre_fill_info, +}; + +/* + * And now the modules code and kernel interface. + */ + +static int __init ip6gre_init(void) +{ + int err; + + pr_info("GRE over IPv6 tunneling driver\n"); + + err = register_pernet_device(&ip6gre_net_ops); + if (err < 0) + return err; + + err = inet6_add_protocol(&ip6gre_protocol, IPPROTO_GRE); + if (err < 0) { + pr_info("%s: can't add protocol\n", __func__); + goto add_proto_failed; + } + + err = rtnl_link_register(&ip6gre_link_ops); + if (err < 0) + goto rtnl_link_failed; + + err = rtnl_link_register(&ip6gre_tap_ops); + if (err < 0) + goto tap_ops_failed; + +out: + return err; + +tap_ops_failed: + rtnl_link_unregister(&ip6gre_link_ops); +rtnl_link_failed: + inet6_del_protocol(&ip6gre_protocol, IPPROTO_GRE); +add_proto_failed: + unregister_pernet_device(&ip6gre_net_ops); + goto out; +} + +static void __exit ip6gre_fini(void) +{ + rtnl_link_unregister(&ip6gre_tap_ops); + rtnl_link_unregister(&ip6gre_link_ops); + inet6_del_protocol(&ip6gre_protocol, IPPROTO_GRE); + unregister_pernet_device(&ip6gre_net_ops); +} + +module_init(ip6gre_init); +module_exit(ip6gre_fini); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)"); +MODULE_DESCRIPTION("GRE over IPv6 tunneling device"); +MODULE_ALIAS_RTNL_LINK("ip6gre"); +MODULE_ALIAS_NETDEV("ip6gre0"); diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 9a1d5fe6aef8..33d2a0e6712d 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -126,7 +126,7 @@ static struct net_device_stats *ip6_get_stats(struct net_device *dev) * Locking : hash tables are protected by RCU and RTNL */ -static inline struct dst_entry *ip6_tnl_dst_check(struct ip6_tnl *t) +struct dst_entry *ip6_tnl_dst_check(struct ip6_tnl *t) { struct dst_entry *dst = t->dst_cache; @@ -139,20 +139,23 @@ static inline struct dst_entry *ip6_tnl_dst_check(struct ip6_tnl *t) return dst; } +EXPORT_SYMBOL_GPL(ip6_tnl_dst_check); -static inline void ip6_tnl_dst_reset(struct ip6_tnl *t) +void ip6_tnl_dst_reset(struct ip6_tnl *t) { dst_release(t->dst_cache); t->dst_cache = NULL; } +EXPORT_SYMBOL_GPL(ip6_tnl_dst_reset); -static inline void ip6_tnl_dst_store(struct ip6_tnl *t, struct dst_entry *dst) +void ip6_tnl_dst_store(struct ip6_tnl *t, struct dst_entry *dst) { struct rt6_info *rt = (struct rt6_info *) dst; t->dst_cookie = rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0; dst_release(t->dst_cache); t->dst_cache = dst; } +EXPORT_SYMBOL_GPL(ip6_tnl_dst_store); /** * ip6_tnl_lookup - fetch tunnel matching the end-point addresses @@ -200,7 +203,7 @@ ip6_tnl_lookup(struct net *net, const struct in6_addr *remote, const struct in6_ **/ static struct ip6_tnl __rcu ** -ip6_tnl_bucket(struct ip6_tnl_net *ip6n, const struct ip6_tnl_parm *p) +ip6_tnl_bucket(struct ip6_tnl_net *ip6n, const struct __ip6_tnl_parm *p) { const struct in6_addr *remote = &p->raddr; const struct in6_addr *local = &p->laddr; @@ -267,7 +270,7 @@ static void ip6_dev_free(struct net_device *dev) * created tunnel or NULL **/ -static struct ip6_tnl *ip6_tnl_create(struct net *net, struct ip6_tnl_parm *p) +static struct ip6_tnl *ip6_tnl_create(struct net *net, struct __ip6_tnl_parm *p) { struct net_device *dev; struct ip6_tnl *t; @@ -322,7 +325,7 @@ failed: **/ static struct ip6_tnl *ip6_tnl_locate(struct net *net, - struct ip6_tnl_parm *p, int create) + struct __ip6_tnl_parm *p, int create) { const struct in6_addr *remote = &p->raddr; const struct in6_addr *local = &p->laddr; @@ -374,8 +377,7 @@ ip6_tnl_dev_uninit(struct net_device *dev) * else index to encapsulation limit **/ -static __u16 -parse_tlv_tnl_enc_lim(struct sk_buff *skb, __u8 * raw) +__u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw) { const struct ipv6hdr *ipv6h = (const struct ipv6hdr *) raw; __u8 nexthdr = ipv6h->nexthdr; @@ -425,6 +427,7 @@ parse_tlv_tnl_enc_lim(struct sk_buff *skb, __u8 * raw) } return 0; } +EXPORT_SYMBOL(ip6_tnl_parse_tlv_enc_lim); /** * ip6_tnl_err - tunnel error handler @@ -480,7 +483,7 @@ ip6_tnl_err(struct sk_buff *skb, __u8 ipproto, struct inet6_skb_parm *opt, case ICMPV6_PARAMPROB: teli = 0; if ((*code) == ICMPV6_HDR_FIELD) - teli = parse_tlv_tnl_enc_lim(skb, skb->data); + teli = ip6_tnl_parse_tlv_enc_lim(skb, skb->data); if (teli && teli == *info - 2) { tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->data[teli]; @@ -693,11 +696,11 @@ static void ip6ip6_dscp_ecn_decapsulate(const struct ip6_tnl *t, IP6_ECN_set_ce(ipv6_hdr(skb)); } -static __u32 ip6_tnl_get_cap(struct ip6_tnl *t, +__u32 ip6_tnl_get_cap(struct ip6_tnl *t, const struct in6_addr *laddr, const struct in6_addr *raddr) { - struct ip6_tnl_parm *p = &t->parms; + struct __ip6_tnl_parm *p = &t->parms; int ltype = ipv6_addr_type(laddr); int rtype = ipv6_addr_type(raddr); __u32 flags = 0; @@ -715,13 +718,14 @@ static __u32 ip6_tnl_get_cap(struct ip6_tnl *t, } return flags; } +EXPORT_SYMBOL(ip6_tnl_get_cap); /* called with rcu_read_lock() */ -static inline int ip6_tnl_rcv_ctl(struct ip6_tnl *t, +int ip6_tnl_rcv_ctl(struct ip6_tnl *t, const struct in6_addr *laddr, const struct in6_addr *raddr) { - struct ip6_tnl_parm *p = &t->parms; + struct __ip6_tnl_parm *p = &t->parms; int ret = 0; struct net *net = dev_net(t->dev); @@ -740,6 +744,7 @@ static inline int ip6_tnl_rcv_ctl(struct ip6_tnl *t, } return ret; } +EXPORT_SYMBOL_GPL(ip6_tnl_rcv_ctl); /** * ip6_tnl_rcv - decapsulate IPv6 packet and retransmit it locally @@ -859,9 +864,9 @@ ip6_tnl_addr_conflict(const struct ip6_tnl *t, const struct ipv6hdr *hdr) return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr); } -static inline int ip6_tnl_xmit_ctl(struct ip6_tnl *t) +int ip6_tnl_xmit_ctl(struct ip6_tnl *t) { - struct ip6_tnl_parm *p = &t->parms; + struct __ip6_tnl_parm *p = &t->parms; int ret = 0; struct net *net = dev_net(t->dev); @@ -885,6 +890,8 @@ static inline int ip6_tnl_xmit_ctl(struct ip6_tnl *t) } return ret; } +EXPORT_SYMBOL_GPL(ip6_tnl_xmit_ctl); + /** * ip6_tnl_xmit2 - encapsulate packet and send * @skb: the outgoing socket buffer @@ -1085,7 +1092,7 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) !ip6_tnl_xmit_ctl(t) || ip6_tnl_addr_conflict(t, ipv6h)) return -1; - offset = parse_tlv_tnl_enc_lim(skb, skb_network_header(skb)); + offset = ip6_tnl_parse_tlv_enc_lim(skb, skb_network_header(skb)); if (offset > 0) { struct ipv6_tlv_tnl_enc_lim *tel; tel = (struct ipv6_tlv_tnl_enc_lim *)&skb_network_header(skb)[offset]; @@ -1152,7 +1159,7 @@ tx_err: static void ip6_tnl_link_config(struct ip6_tnl *t) { struct net_device *dev = t->dev; - struct ip6_tnl_parm *p = &t->parms; + struct __ip6_tnl_parm *p = &t->parms; struct flowi6 *fl6 = &t->fl.u.ip6; memcpy(dev->dev_addr, &p->laddr, sizeof(struct in6_addr)); @@ -1215,7 +1222,7 @@ static void ip6_tnl_link_config(struct ip6_tnl *t) **/ static int -ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p) +ip6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p) { t->parms.laddr = p->laddr; t->parms.raddr = p->raddr; @@ -1230,6 +1237,34 @@ ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p) return 0; } +static void +ip6_tnl_parm_from_user(struct __ip6_tnl_parm *p, const struct ip6_tnl_parm *u) +{ + p->laddr = u->laddr; + p->raddr = u->raddr; + p->flags = u->flags; + p->hop_limit = u->hop_limit; + p->encap_limit = u->encap_limit; + p->flowinfo = u->flowinfo; + p->link = u->link; + p->proto = u->proto; + memcpy(p->name, u->name, sizeof(u->name)); +} + +static void +ip6_tnl_parm_to_user(struct ip6_tnl_parm *u, const struct __ip6_tnl_parm *p) +{ + u->laddr = p->laddr; + u->raddr = p->raddr; + u->flags = p->flags; + u->hop_limit = p->hop_limit; + u->encap_limit = p->encap_limit; + u->flowinfo = p->flowinfo; + u->link = p->link; + u->proto = p->proto; + memcpy(u->name, p->name, sizeof(u->name)); +} + /** * ip6_tnl_ioctl - configure ipv6 tunnels from userspace * @dev: virtual device associated with tunnel @@ -1263,6 +1298,7 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { int err = 0; struct ip6_tnl_parm p; + struct __ip6_tnl_parm p1; struct ip6_tnl *t = NULL; struct net *net = dev_net(dev); struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); @@ -1274,11 +1310,12 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) err = -EFAULT; break; } - t = ip6_tnl_locate(net, &p, 0); + ip6_tnl_parm_from_user(&p1, &p); + t = ip6_tnl_locate(net, &p1, 0); } if (t == NULL) t = netdev_priv(dev); - memcpy(&p, &t->parms, sizeof (p)); + ip6_tnl_parm_to_user(&p, &t->parms); if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof (p))) { err = -EFAULT; } @@ -1295,7 +1332,8 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) if (p.proto != IPPROTO_IPV6 && p.proto != IPPROTO_IPIP && p.proto != 0) break; - t = ip6_tnl_locate(net, &p, cmd == SIOCADDTUNNEL); + ip6_tnl_parm_from_user(&p1, &p); + t = ip6_tnl_locate(net, &p1, cmd == SIOCADDTUNNEL); if (dev != ip6n->fb_tnl_dev && cmd == SIOCCHGTUNNEL) { if (t != NULL) { if (t->dev != dev) { @@ -1307,13 +1345,14 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) ip6_tnl_unlink(ip6n, t); synchronize_net(); - err = ip6_tnl_change(t, &p); + err = ip6_tnl_change(t, &p1); ip6_tnl_link(ip6n, t); netdev_state_change(dev); } if (t) { err = 0; - if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof (p))) + ip6_tnl_parm_to_user(&p, &t->parms); + if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p))) err = -EFAULT; } else @@ -1329,7 +1368,9 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) break; err = -ENOENT; - if ((t = ip6_tnl_locate(net, &p, 0)) == NULL) + ip6_tnl_parm_from_user(&p1, &p); + t = ip6_tnl_locate(net, &p1, 0); + if (t == NULL) break; err = -EPERM; if (t->dev == ip6n->fb_tnl_dev) -- cgit v1.2.3 From 47be03a28cc6c80e3aa2b3e8ed6d960ff0c5c0af Mon Sep 17 00:00:00 2001 From: Amerigo Wang Date: Fri, 10 Aug 2012 01:24:37 +0000 Subject: netpoll: use GFP_ATOMIC in slave_enable_netpoll() and __netpoll_setup() slave_enable_netpoll() and __netpoll_setup() may be called with read_lock() held, so should use GFP_ATOMIC to allocate memory. Eric suggested to pass gfp flags to __netpoll_setup(). Cc: Eric Dumazet Cc: "David S. Miller" Reported-by: Dan Carpenter Signed-off-by: Eric Dumazet Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 6 +++--- drivers/net/team/team.c | 16 +++++++++------- include/linux/netdevice.h | 3 ++- include/linux/netpoll.h | 2 +- net/8021q/vlan_dev.c | 7 ++++--- net/bridge/br_device.c | 12 ++++++------ net/bridge/br_if.c | 2 +- net/bridge/br_private.h | 4 ++-- net/core/netpoll.c | 8 ++++---- 9 files changed, 32 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 6fae5f3ec7f6..8697136e27c0 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1235,12 +1235,12 @@ static inline int slave_enable_netpoll(struct slave *slave) struct netpoll *np; int err = 0; - np = kzalloc(sizeof(*np), GFP_KERNEL); + np = kzalloc(sizeof(*np), GFP_ATOMIC); err = -ENOMEM; if (!np) goto out; - err = __netpoll_setup(np, slave->dev); + err = __netpoll_setup(np, slave->dev, GFP_ATOMIC); if (err) { kfree(np); goto out; @@ -1292,7 +1292,7 @@ static void bond_netpoll_cleanup(struct net_device *bond_dev) read_unlock(&bond->lock); } -static int bond_netpoll_setup(struct net_device *dev, struct netpoll_info *ni) +static int bond_netpoll_setup(struct net_device *dev, struct netpoll_info *ni, gfp_t gfp) { struct bonding *bond = netdev_priv(dev); struct slave *slave; diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 87707ab39430..341b65dbbcd3 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -795,16 +795,17 @@ static void team_port_leave(struct team *team, struct team_port *port) } #ifdef CONFIG_NET_POLL_CONTROLLER -static int team_port_enable_netpoll(struct team *team, struct team_port *port) +static int team_port_enable_netpoll(struct team *team, struct team_port *port, + gfp_t gfp) { struct netpoll *np; int err; - np = kzalloc(sizeof(*np), GFP_KERNEL); + np = kzalloc(sizeof(*np), gfp); if (!np) return -ENOMEM; - err = __netpoll_setup(np, port->dev); + err = __netpoll_setup(np, port->dev, gfp); if (err) { kfree(np); return err; @@ -833,7 +834,8 @@ static struct netpoll_info *team_netpoll_info(struct team *team) } #else -static int team_port_enable_netpoll(struct team *team, struct team_port *port) +static int team_port_enable_netpoll(struct team *team, struct team_port *port, + gfp_t gfp) { return 0; } @@ -913,7 +915,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev) } if (team_netpoll_info(team)) { - err = team_port_enable_netpoll(team, port); + err = team_port_enable_netpoll(team, port, GFP_KERNEL); if (err) { netdev_err(dev, "Failed to enable netpoll on device %s\n", portname); @@ -1443,7 +1445,7 @@ static void team_netpoll_cleanup(struct net_device *dev) } static int team_netpoll_setup(struct net_device *dev, - struct netpoll_info *npifo) + struct netpoll_info *npifo, gfp_t gfp) { struct team *team = netdev_priv(dev); struct team_port *port; @@ -1451,7 +1453,7 @@ static int team_netpoll_setup(struct net_device *dev, mutex_lock(&team->lock); list_for_each_entry(port, &team->port_list, list) { - err = team_port_enable_netpoll(team, port); + err = team_port_enable_netpoll(team, port, gfp); if (err) { __team_netpoll_cleanup(team); break; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index a9db4f33407f..3560d688161e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -953,7 +953,8 @@ struct net_device_ops { #ifdef CONFIG_NET_POLL_CONTROLLER void (*ndo_poll_controller)(struct net_device *dev); int (*ndo_netpoll_setup)(struct net_device *dev, - struct netpoll_info *info); + struct netpoll_info *info, + gfp_t gfp); void (*ndo_netpoll_cleanup)(struct net_device *dev); #endif int (*ndo_set_vf_mac)(struct net_device *dev, diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index 28f5389c924b..bf2d51eec0f3 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -43,7 +43,7 @@ struct netpoll_info { void netpoll_send_udp(struct netpoll *np, const char *msg, int len); void netpoll_print_options(struct netpoll *np); int netpoll_parse_options(struct netpoll *np, char *opt); -int __netpoll_setup(struct netpoll *np, struct net_device *ndev); +int __netpoll_setup(struct netpoll *np, struct net_device *ndev, gfp_t gfp); int netpoll_setup(struct netpoll *np); int netpoll_trap(void); void netpoll_set_trap(int trap); diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 73a2a83ee2da..ee4ae0944cef 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -669,19 +669,20 @@ static void vlan_dev_poll_controller(struct net_device *dev) return; } -static int vlan_dev_netpoll_setup(struct net_device *dev, struct netpoll_info *npinfo) +static int vlan_dev_netpoll_setup(struct net_device *dev, struct netpoll_info *npinfo, + gfp_t gfp) { struct vlan_dev_priv *info = vlan_dev_priv(dev); struct net_device *real_dev = info->real_dev; struct netpoll *netpoll; int err = 0; - netpoll = kzalloc(sizeof(*netpoll), GFP_KERNEL); + netpoll = kzalloc(sizeof(*netpoll), gfp); err = -ENOMEM; if (!netpoll) goto out; - err = __netpoll_setup(netpoll, real_dev); + err = __netpoll_setup(netpoll, real_dev, gfp); if (err) { kfree(netpoll); goto out; diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 333484537600..ed0e0f9dc788 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -213,7 +213,8 @@ static void br_netpoll_cleanup(struct net_device *dev) } } -static int br_netpoll_setup(struct net_device *dev, struct netpoll_info *ni) +static int br_netpoll_setup(struct net_device *dev, struct netpoll_info *ni, + gfp_t gfp) { struct net_bridge *br = netdev_priv(dev); struct net_bridge_port *p, *n; @@ -222,8 +223,7 @@ static int br_netpoll_setup(struct net_device *dev, struct netpoll_info *ni) list_for_each_entry_safe(p, n, &br->port_list, list) { if (!p->dev) continue; - - err = br_netpoll_enable(p); + err = br_netpoll_enable(p, gfp); if (err) goto fail; } @@ -236,17 +236,17 @@ fail: goto out; } -int br_netpoll_enable(struct net_bridge_port *p) +int br_netpoll_enable(struct net_bridge_port *p, gfp_t gfp) { struct netpoll *np; int err = 0; - np = kzalloc(sizeof(*p->np), GFP_KERNEL); + np = kzalloc(sizeof(*p->np), gfp); err = -ENOMEM; if (!np) goto out; - err = __netpoll_setup(np, p->dev); + err = __netpoll_setup(np, p->dev, gfp); if (err) { kfree(np); goto out; diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index e1144e1617be..171fd6b9bfe6 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -361,7 +361,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) if (err) goto err2; - if (br_netpoll_info(br) && ((err = br_netpoll_enable(p)))) + if (br_netpoll_info(br) && ((err = br_netpoll_enable(p, GFP_KERNEL)))) goto err3; err = netdev_set_master(dev, br->dev); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index a768b2408edf..f507d2af9646 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -316,7 +316,7 @@ static inline void br_netpoll_send_skb(const struct net_bridge_port *p, netpoll_send_skb(np, skb); } -extern int br_netpoll_enable(struct net_bridge_port *p); +extern int br_netpoll_enable(struct net_bridge_port *p, gfp_t gfp); extern void br_netpoll_disable(struct net_bridge_port *p); #else static inline struct netpoll_info *br_netpoll_info(struct net_bridge *br) @@ -329,7 +329,7 @@ static inline void br_netpoll_send_skb(const struct net_bridge_port *p, { } -static inline int br_netpoll_enable(struct net_bridge_port *p) +static inline int br_netpoll_enable(struct net_bridge_port *p, gfp_t gfp) { return 0; } diff --git a/net/core/netpoll.c b/net/core/netpoll.c index b4c90e42b443..37cc854774a4 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -715,7 +715,7 @@ int netpoll_parse_options(struct netpoll *np, char *opt) } EXPORT_SYMBOL(netpoll_parse_options); -int __netpoll_setup(struct netpoll *np, struct net_device *ndev) +int __netpoll_setup(struct netpoll *np, struct net_device *ndev, gfp_t gfp) { struct netpoll_info *npinfo; const struct net_device_ops *ops; @@ -734,7 +734,7 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev) } if (!ndev->npinfo) { - npinfo = kmalloc(sizeof(*npinfo), GFP_KERNEL); + npinfo = kmalloc(sizeof(*npinfo), gfp); if (!npinfo) { err = -ENOMEM; goto out; @@ -752,7 +752,7 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev) ops = np->dev->netdev_ops; if (ops->ndo_netpoll_setup) { - err = ops->ndo_netpoll_setup(ndev, npinfo); + err = ops->ndo_netpoll_setup(ndev, npinfo, gfp); if (err) goto free_npinfo; } @@ -857,7 +857,7 @@ int netpoll_setup(struct netpoll *np) refill_skbs(); rtnl_lock(); - err = __netpoll_setup(np, ndev); + err = __netpoll_setup(np, ndev, GFP_KERNEL); rtnl_unlock(); if (err) -- cgit v1.2.3 From 38e6bc185d9544dfad1774b3f8902a0b061aea25 Mon Sep 17 00:00:00 2001 From: Amerigo Wang Date: Fri, 10 Aug 2012 01:24:38 +0000 Subject: netpoll: make __netpoll_cleanup non-block Like the previous patch, slave_disable_netpoll() and __netpoll_cleanup() may be called with read_lock() held too, so we should make them non-block, by moving the cleanup and kfree() to call_rcu_bh() callbacks. Cc: "David S. Miller" Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 4 +--- include/linux/netpoll.h | 3 +++ net/8021q/vlan_dev.c | 6 +----- net/bridge/br_device.c | 6 +----- net/core/netpoll.c | 42 +++++++++++++++++++++++++++++++---------- 5 files changed, 38 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 8697136e27c0..e42891683e3b 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1257,9 +1257,7 @@ static inline void slave_disable_netpoll(struct slave *slave) return; slave->np = NULL; - synchronize_rcu_bh(); - __netpoll_cleanup(np); - kfree(np); + __netpoll_free_rcu(np); } static inline bool slave_dev_support_netpoll(struct net_device *slave_dev) { diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index bf2d51eec0f3..907812efb4d9 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -23,6 +23,7 @@ struct netpoll { u8 remote_mac[ETH_ALEN]; struct list_head rx; /* rx_np list element */ + struct rcu_head rcu; }; struct netpoll_info { @@ -38,6 +39,7 @@ struct netpoll_info { struct delayed_work tx_work; struct netpoll *netpoll; + struct rcu_head rcu; }; void netpoll_send_udp(struct netpoll *np, const char *msg, int len); @@ -48,6 +50,7 @@ int netpoll_setup(struct netpoll *np); int netpoll_trap(void); void netpoll_set_trap(int trap); void __netpoll_cleanup(struct netpoll *np); +void __netpoll_free_rcu(struct netpoll *np); void netpoll_cleanup(struct netpoll *np); int __netpoll_rx(struct sk_buff *skb); void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb, diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index ee4ae0944cef..b65623f90660 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -704,11 +704,7 @@ static void vlan_dev_netpoll_cleanup(struct net_device *dev) info->netpoll = NULL; - /* Wait for transmitting packets to finish before freeing. */ - synchronize_rcu_bh(); - - __netpoll_cleanup(netpoll); - kfree(netpoll); + __netpoll_free_rcu(netpoll); } #endif /* CONFIG_NET_POLL_CONTROLLER */ diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index ed0e0f9dc788..f41ba4048c9a 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -267,11 +267,7 @@ void br_netpoll_disable(struct net_bridge_port *p) p->np = NULL; - /* Wait for transmitting packets to finish before freeing. */ - synchronize_rcu_bh(); - - __netpoll_cleanup(np); - kfree(np); + __netpoll_free_rcu(np); } #endif diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 37cc854774a4..dc17f1db1479 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -878,6 +878,24 @@ static int __init netpoll_init(void) } core_initcall(netpoll_init); +static void rcu_cleanup_netpoll_info(struct rcu_head *rcu_head) +{ + struct netpoll_info *npinfo = + container_of(rcu_head, struct netpoll_info, rcu); + + skb_queue_purge(&npinfo->arp_tx); + skb_queue_purge(&npinfo->txq); + + /* we can't call cancel_delayed_work_sync here, as we are in softirq */ + cancel_delayed_work(&npinfo->tx_work); + + /* clean after last, unfinished work */ + __skb_queue_purge(&npinfo->txq); + /* now cancel it again */ + cancel_delayed_work(&npinfo->tx_work); + kfree(npinfo); +} + void __netpoll_cleanup(struct netpoll *np) { struct netpoll_info *npinfo; @@ -903,20 +921,24 @@ void __netpoll_cleanup(struct netpoll *np) ops->ndo_netpoll_cleanup(np->dev); RCU_INIT_POINTER(np->dev->npinfo, NULL); + call_rcu_bh(&npinfo->rcu, rcu_cleanup_netpoll_info); + } +} +EXPORT_SYMBOL_GPL(__netpoll_cleanup); - /* avoid racing with NAPI reading npinfo */ - synchronize_rcu_bh(); +static void rcu_cleanup_netpoll(struct rcu_head *rcu_head) +{ + struct netpoll *np = container_of(rcu_head, struct netpoll, rcu); - skb_queue_purge(&npinfo->arp_tx); - skb_queue_purge(&npinfo->txq); - cancel_delayed_work_sync(&npinfo->tx_work); + __netpoll_cleanup(np); + kfree(np); +} - /* clean after last, unfinished work */ - __skb_queue_purge(&npinfo->txq); - kfree(npinfo); - } +void __netpoll_free_rcu(struct netpoll *np) +{ + call_rcu_bh(&np->rcu, rcu_cleanup_netpoll); } -EXPORT_SYMBOL_GPL(__netpoll_cleanup); +EXPORT_SYMBOL_GPL(__netpoll_free_rcu); void netpoll_cleanup(struct netpoll *np) { -- cgit v1.2.3 From 57c5d46191e75312934c00eba65b13a31ca95120 Mon Sep 17 00:00:00 2001 From: Amerigo Wang Date: Fri, 10 Aug 2012 01:24:40 +0000 Subject: netpoll: take rcu_read_lock_bh() in netpoll_rx() In __netpoll_rx(), it dereferences ->npinfo without rcu_dereference_bh(), this patch fixes it by using the 'npinfo' passed from netpoll_rx() where it is already dereferenced with rcu_dereference_bh(). Cc: "David S. Miller" Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/linux/netpoll.h | 4 ++-- net/core/netpoll.c | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index 907812efb4d9..5d881c388273 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -52,7 +52,7 @@ void netpoll_set_trap(int trap); void __netpoll_cleanup(struct netpoll *np); void __netpoll_free_rcu(struct netpoll *np); void netpoll_cleanup(struct netpoll *np); -int __netpoll_rx(struct sk_buff *skb); +int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo); void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb, struct net_device *dev); static inline void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) @@ -77,7 +77,7 @@ static inline bool netpoll_rx(struct sk_buff *skb) spin_lock(&npinfo->rx_lock); /* check rx_flags again with the lock held */ - if (npinfo->rx_flags && __netpoll_rx(skb)) + if (npinfo->rx_flags && __netpoll_rx(skb, npinfo)) ret = true; spin_unlock(&npinfo->rx_lock); diff --git a/net/core/netpoll.c b/net/core/netpoll.c index dc17f1db1479..d055bb01328b 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -543,13 +543,12 @@ static void arp_reply(struct sk_buff *skb) spin_unlock_irqrestore(&npinfo->rx_lock, flags); } -int __netpoll_rx(struct sk_buff *skb) +int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo) { int proto, len, ulen; int hits = 0; const struct iphdr *iph; struct udphdr *uh; - struct netpoll_info *npinfo = skb->dev->npinfo; struct netpoll *np, *tmp; if (list_empty(&npinfo->rx_np)) -- cgit v1.2.3 From 91fe4a4b9e490a24f6702dd8afe72d8afab6fcdb Mon Sep 17 00:00:00 2001 From: Amerigo Wang Date: Fri, 10 Aug 2012 01:24:41 +0000 Subject: netpoll: use netpoll_rx_on() in netpoll_rx() The logic of the code is same, just call netpoll_rx_on(). Cc: "David S. Miller" Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/linux/netpoll.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index 5d881c388273..2d178baa49df 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -63,6 +63,13 @@ static inline void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) #ifdef CONFIG_NETPOLL +static inline int netpoll_rx_on(struct sk_buff *skb) +{ + struct netpoll_info *npinfo = rcu_dereference_bh(skb->dev->npinfo); + + return npinfo && (!list_empty(&npinfo->rx_np) || npinfo->rx_flags); +} + static inline bool netpoll_rx(struct sk_buff *skb) { struct netpoll_info *npinfo; @@ -70,11 +77,11 @@ static inline bool netpoll_rx(struct sk_buff *skb) bool ret = false; local_irq_save(flags); - npinfo = rcu_dereference_bh(skb->dev->npinfo); - if (!npinfo || (list_empty(&npinfo->rx_np) && !npinfo->rx_flags)) + if (!netpoll_rx_on(skb)) goto out; + npinfo = rcu_dereference_bh(skb->dev->npinfo); spin_lock(&npinfo->rx_lock); /* check rx_flags again with the lock held */ if (npinfo->rx_flags && __netpoll_rx(skb, npinfo)) @@ -86,13 +93,6 @@ out: return ret; } -static inline int netpoll_rx_on(struct sk_buff *skb) -{ - struct netpoll_info *npinfo = rcu_dereference_bh(skb->dev->npinfo); - - return npinfo && (!list_empty(&npinfo->rx_np) || npinfo->rx_flags); -} - static inline int netpoll_receive_skb(struct sk_buff *skb) { if (!list_empty(&skb->dev->napi_list)) -- cgit v1.2.3 From 2899656b494dcd118123af1126826b115c8ea6f9 Mon Sep 17 00:00:00 2001 From: Amerigo Wang Date: Fri, 10 Aug 2012 01:24:42 +0000 Subject: netpoll: take rcu_read_lock_bh() in netpoll_send_skb_on_dev() This patch fixes several problems in the call path of netpoll_send_skb_on_dev(): 1. Disable IRQ's before calling netpoll_send_skb_on_dev(). 2. All the callees of netpoll_send_skb_on_dev() should use rcu_dereference_bh() to dereference ->npinfo. 3. Rename arp_reply() to netpoll_arp_reply(), the former is too generic. Cc: "David S. Miller" Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/linux/netpoll.h | 3 +++ net/core/netpoll.c | 31 +++++++++++++++++-------------- 2 files changed, 20 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index 2d178baa49df..61aee86cf21d 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -57,7 +57,10 @@ void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb, struct net_device *dev); static inline void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) { + unsigned long flags; + local_irq_save(flags); netpoll_send_skb_on_dev(np, skb, np->dev); + local_irq_restore(flags); } diff --git a/net/core/netpoll.c b/net/core/netpoll.c index d055bb01328b..174346ac15a0 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -54,7 +54,7 @@ static atomic_t trapped; MAX_UDP_CHUNK) static void zap_completion_queue(void); -static void arp_reply(struct sk_buff *skb); +static void netpoll_arp_reply(struct sk_buff *skb, struct netpoll_info *npinfo); static unsigned int carrier_timeout = 4; module_param(carrier_timeout, uint, 0644); @@ -170,7 +170,8 @@ static void poll_napi(struct net_device *dev) list_for_each_entry(napi, &dev->napi_list, dev_list) { if (napi->poll_owner != smp_processor_id() && spin_trylock(&napi->poll_lock)) { - budget = poll_one_napi(dev->npinfo, napi, budget); + budget = poll_one_napi(rcu_dereference_bh(dev->npinfo), + napi, budget); spin_unlock(&napi->poll_lock); if (!budget) @@ -185,13 +186,14 @@ static void service_arp_queue(struct netpoll_info *npi) struct sk_buff *skb; while ((skb = skb_dequeue(&npi->arp_tx))) - arp_reply(skb); + netpoll_arp_reply(skb, npi); } } static void netpoll_poll_dev(struct net_device *dev) { const struct net_device_ops *ops; + struct netpoll_info *ni = rcu_dereference_bh(dev->npinfo); if (!dev || !netif_running(dev)) return; @@ -206,17 +208,18 @@ static void netpoll_poll_dev(struct net_device *dev) poll_napi(dev); if (dev->flags & IFF_SLAVE) { - if (dev->npinfo) { + if (ni) { struct net_device *bond_dev = dev->master; struct sk_buff *skb; - while ((skb = skb_dequeue(&dev->npinfo->arp_tx))) { + struct netpoll_info *bond_ni = rcu_dereference_bh(bond_dev->npinfo); + while ((skb = skb_dequeue(&ni->arp_tx))) { skb->dev = bond_dev; - skb_queue_tail(&bond_dev->npinfo->arp_tx, skb); + skb_queue_tail(&bond_ni->arp_tx, skb); } } } - service_arp_queue(dev->npinfo); + service_arp_queue(ni); zap_completion_queue(); } @@ -302,6 +305,7 @@ static int netpoll_owner_active(struct net_device *dev) return 0; } +/* call with IRQ disabled */ void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb, struct net_device *dev) { @@ -309,8 +313,11 @@ void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb, unsigned long tries; const struct net_device_ops *ops = dev->netdev_ops; /* It is up to the caller to keep npinfo alive. */ - struct netpoll_info *npinfo = np->dev->npinfo; + struct netpoll_info *npinfo; + + WARN_ON_ONCE(!irqs_disabled()); + npinfo = rcu_dereference_bh(np->dev->npinfo); if (!npinfo || !netif_running(dev) || !netif_device_present(dev)) { __kfree_skb(skb); return; @@ -319,11 +326,9 @@ void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb, /* don't get messages out of order, and no recursion */ if (skb_queue_len(&npinfo->txq) == 0 && !netpoll_owner_active(dev)) { struct netdev_queue *txq; - unsigned long flags; txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb)); - local_irq_save(flags); /* try until next clock tick */ for (tries = jiffies_to_usecs(1)/USEC_PER_POLL; tries > 0; --tries) { @@ -347,10 +352,9 @@ void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb, } WARN_ONCE(!irqs_disabled(), - "netpoll_send_skb(): %s enabled interrupts in poll (%pF)\n", + "netpoll_send_skb_on_dev(): %s enabled interrupts in poll (%pF)\n", dev->name, ops->ndo_start_xmit); - local_irq_restore(flags); } if (status != NETDEV_TX_OK) { @@ -423,9 +427,8 @@ void netpoll_send_udp(struct netpoll *np, const char *msg, int len) } EXPORT_SYMBOL(netpoll_send_udp); -static void arp_reply(struct sk_buff *skb) +static void netpoll_arp_reply(struct sk_buff *skb, struct netpoll_info *npinfo) { - struct netpoll_info *npinfo = skb->dev->npinfo; struct arphdr *arp; unsigned char *arp_ptr; int size, type = ARPOP_REPLY, ptype = ETH_P_ARP; -- cgit v1.2.3 From e15c3c2294605f09f9b336b2f3b97086ab4b8145 Mon Sep 17 00:00:00 2001 From: Amerigo Wang Date: Fri, 10 Aug 2012 01:24:45 +0000 Subject: netpoll: check netpoll tx status on the right device Although this doesn't matter actually, because netpoll_tx_running() doesn't use the parameter, the code will be more readable. For team_dev_queue_xmit() we have to move it down to avoid compile errors. Cc: David Miller Signed-off-by: Jiri Pirko Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 2 +- include/linux/if_team.h | 30 +++++++++++++++--------------- net/bridge/br_forward.c | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index e42891683e3b..d688a8af432c 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -398,7 +398,7 @@ int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, sizeof(qdisc_skb_cb(skb)->slave_dev_queue_mapping)); skb->queue_mapping = qdisc_skb_cb(skb)->slave_dev_queue_mapping; - if (unlikely(netpoll_tx_running(slave_dev))) + if (unlikely(netpoll_tx_running(bond->dev))) bond_netpoll_send_skb(bond_get_slave_by_dev(bond, slave_dev), skb); else dev_queue_xmit(skb); diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 6960fc1841a7..aa2e167e1ef4 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -96,21 +96,6 @@ static inline void team_netpoll_send_skb(struct team_port *port, } #endif -static inline int team_dev_queue_xmit(struct team *team, struct team_port *port, - struct sk_buff *skb) -{ - BUILD_BUG_ON(sizeof(skb->queue_mapping) != - sizeof(qdisc_skb_cb(skb)->slave_dev_queue_mapping)); - skb_set_queue_mapping(skb, qdisc_skb_cb(skb)->slave_dev_queue_mapping); - - skb->dev = port->dev; - if (unlikely(netpoll_tx_running(port->dev))) { - team_netpoll_send_skb(port, skb); - return 0; - } - return dev_queue_xmit(skb); -} - struct team_mode_ops { int (*init)(struct team *team); void (*exit)(struct team *team); @@ -200,6 +185,21 @@ struct team { long mode_priv[TEAM_MODE_PRIV_LONGS]; }; +static inline int team_dev_queue_xmit(struct team *team, struct team_port *port, + struct sk_buff *skb) +{ + BUILD_BUG_ON(sizeof(skb->queue_mapping) != + sizeof(qdisc_skb_cb(skb)->slave_dev_queue_mapping)); + skb_set_queue_mapping(skb, qdisc_skb_cb(skb)->slave_dev_queue_mapping); + + skb->dev = port->dev; + if (unlikely(netpoll_tx_running(team->dev))) { + team_netpoll_send_skb(port, skb); + return 0; + } + return dev_queue_xmit(skb); +} + static inline struct hlist_head *team_port_index_hash(struct team *team, int port_index) { diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index e9466d412707..02015a505d2a 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -65,7 +65,7 @@ static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) { skb->dev = to->dev; - if (unlikely(netpoll_tx_running(to->dev))) { + if (unlikely(netpoll_tx_running(to->br->dev))) { if (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb)) kfree_skb(skb); else { -- cgit v1.2.3 From 77ab8a54d9a8dcc4a46484a04133314f33f2aba6 Mon Sep 17 00:00:00 2001 From: Amerigo Wang Date: Fri, 10 Aug 2012 01:24:46 +0000 Subject: netpoll: convert several functions to bool These functions are just boolean, let them return bool instead of int. Cc: David Miller Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/linux/netpoll.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index 61aee86cf21d..66d5379c305e 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -66,7 +66,7 @@ static inline void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) #ifdef CONFIG_NETPOLL -static inline int netpoll_rx_on(struct sk_buff *skb) +static inline bool netpoll_rx_on(struct sk_buff *skb) { struct netpoll_info *npinfo = rcu_dereference_bh(skb->dev->npinfo); @@ -125,7 +125,7 @@ static inline void netpoll_poll_unlock(void *have) } } -static inline int netpoll_tx_running(struct net_device *dev) +static inline bool netpoll_tx_running(struct net_device *dev) { return irqs_disabled(); } @@ -133,11 +133,11 @@ static inline int netpoll_tx_running(struct net_device *dev) #else static inline bool netpoll_rx(struct sk_buff *skb) { - return 0; + return false; } -static inline int netpoll_rx_on(struct sk_buff *skb) +static inline bool netpoll_rx_on(struct sk_buff *skb) { - return 0; + return false; } static inline int netpoll_receive_skb(struct sk_buff *skb) { @@ -153,9 +153,9 @@ static inline void netpoll_poll_unlock(void *have) static inline void netpoll_netdev_init(struct net_device *dev) { } -static inline int netpoll_tx_running(struct net_device *dev) +static inline bool netpoll_tx_running(struct net_device *dev) { - return 0; + return false; } #endif -- cgit v1.2.3 From 6024935f5ff5f1646bce8404416318e5fd4a0c4a Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 13 Aug 2012 02:49:59 +0000 Subject: llc2: Fix silent failure of llc_station_init() llc_station_init() creates and processes an event skb with no effect other than to change the state from DOWN to UP. Allocation failure is reported, but then ignored by its caller, llc2_init(). Remove this possibility by simply initialising the state as UP. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- include/net/llc.h | 2 +- net/llc/llc_station.c | 19 ++----------------- 2 files changed, 3 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/net/llc.h b/include/net/llc.h index 226c846cab08..f2d0fc570527 100644 --- a/include/net/llc.h +++ b/include/net/llc.h @@ -133,7 +133,7 @@ extern int llc_build_and_send_ui_pkt(struct llc_sap *sap, struct sk_buff *skb, extern void llc_sap_handler(struct llc_sap *sap, struct sk_buff *skb); extern void llc_conn_handler(struct llc_sap *sap, struct sk_buff *skb); -extern int llc_station_init(void); +extern void llc_station_init(void); extern void llc_station_exit(void); #ifdef CONFIG_PROC_FS diff --git a/net/llc/llc_station.c b/net/llc/llc_station.c index 6828e39ec2ec..45ddbb93c5d0 100644 --- a/net/llc/llc_station.c +++ b/net/llc/llc_station.c @@ -687,12 +687,8 @@ static void llc_station_rcv(struct sk_buff *skb) llc_station_state_process(skb); } -int __init llc_station_init(void) +void __init llc_station_init(void) { - int rc = -ENOBUFS; - struct sk_buff *skb; - struct llc_station_state_ev *ev; - skb_queue_head_init(&llc_main_station.mac_pdu_q); skb_queue_head_init(&llc_main_station.ev_q.list); spin_lock_init(&llc_main_station.ev_q.lock); @@ -700,20 +696,9 @@ int __init llc_station_init(void) (unsigned long)&llc_main_station); llc_main_station.ack_timer.expires = jiffies + sysctl_llc_station_ack_timeout; - skb = alloc_skb(0, GFP_ATOMIC); - if (!skb) - goto out; - rc = 0; llc_set_station_handler(llc_station_rcv); - ev = llc_station_ev(skb); - memset(ev, 0, sizeof(*ev)); llc_main_station.maximum_retry = 1; - llc_main_station.state = LLC_STATION_STATE_DOWN; - ev->type = LLC_STATION_EV_TYPE_SIMPLE; - ev->prim_type = LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK; - rc = llc_station_next_state(skb); -out: - return rc; + llc_main_station.state = LLC_STATION_STATE_UP; } void __exit llc_station_exit(void) -- cgit v1.2.3 From 96ec6327144e1ac9e6676e34fae8b49c2102fa5a Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Mon, 13 Aug 2012 05:53:28 +0000 Subject: packet: Diag core and basic socket info dumping The diag module can be built independently from the af_packet.ko one, just like it's done in unix sockets. The core dumping message carries the info available at socket creation time, i.e. family, type and protocol (in the same byte order as shown in the proc file). The socket inode number and cookie is reserved for future per-socket info retrieving. The per-protocol filtering is also reserved for future by requiring the sdiag_protocol to be zero. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/Kbuild | 1 + include/linux/packet_diag.h | 24 ++++++++++ net/packet/Kconfig | 8 ++++ net/packet/Makefile | 2 + net/packet/diag.c | 104 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 139 insertions(+) create mode 100644 include/linux/packet_diag.h create mode 100644 net/packet/diag.c (limited to 'include') diff --git a/include/linux/Kbuild b/include/linux/Kbuild index d9a754474878..d823d603dadd 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -195,6 +195,7 @@ header-y += in_route.h header-y += sock_diag.h header-y += inet_diag.h header-y += unix_diag.h +header-y += packet_diag.h header-y += inotify.h header-y += input.h header-y += ioctl.h diff --git a/include/linux/packet_diag.h b/include/linux/packet_diag.h new file mode 100644 index 000000000000..fa19300ca334 --- /dev/null +++ b/include/linux/packet_diag.h @@ -0,0 +1,24 @@ +#ifndef __PACKET_DIAG_H__ +#define __PACKET_DIAG_H__ + +#include + +struct packet_diag_req { + __u8 sdiag_family; + __u8 sdiag_protocol; + __u16 pad; + __u32 pdiag_ino; + __u32 pdiag_show; + __u32 pdiag_cookie[2]; +}; + +struct packet_diag_msg { + __u8 pdiag_family; + __u8 pdiag_type; + __u16 pdiag_num; + + __u32 pdiag_ino; + __u32 pdiag_cookie[2]; +}; + +#endif diff --git a/net/packet/Kconfig b/net/packet/Kconfig index 0060e3b396b7..cc55b35f80e5 100644 --- a/net/packet/Kconfig +++ b/net/packet/Kconfig @@ -14,3 +14,11 @@ config PACKET be called af_packet. If unsure, say Y. + +config PACKET_DIAG + tristate "Packet: sockets monitoring interface" + depends on PACKET + default n + ---help--- + Support for PF_PACKET sockets monitoring interface used by the ss tool. + If unsure, say Y. diff --git a/net/packet/Makefile b/net/packet/Makefile index 81183eabfdec..9df61347a3c3 100644 --- a/net/packet/Makefile +++ b/net/packet/Makefile @@ -3,3 +3,5 @@ # obj-$(CONFIG_PACKET) += af_packet.o +obj-$(CONFIG_PACKET_DIAG) += af_packet_diag.o +af_packet_diag-y += diag.o diff --git a/net/packet/diag.c b/net/packet/diag.c new file mode 100644 index 000000000000..ff2f7f5bfb8f --- /dev/null +++ b/net/packet/diag.c @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct packet_diag_req *req, + u32 pid, u32 seq, u32 flags, int sk_ino) +{ + struct nlmsghdr *nlh; + struct packet_diag_msg *rp; + const struct packet_sock *po = pkt_sk(sk); + + nlh = nlmsg_put(skb, pid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rp), flags); + if (!nlh) + return -EMSGSIZE; + + rp = nlmsg_data(nlh); + rp->pdiag_family = AF_PACKET; + rp->pdiag_type = sk->sk_type; + rp->pdiag_num = ntohs(po->num); + rp->pdiag_ino = sk_ino; + sock_diag_save_cookie(sk, rp->pdiag_cookie); + + return nlmsg_end(skb, nlh); +} + +static int packet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) +{ + int num = 0, s_num = cb->args[0]; + struct packet_diag_req *req; + struct net *net; + struct sock *sk; + struct hlist_node *node; + + net = sock_net(skb->sk); + req = nlmsg_data(cb->nlh); + + rcu_read_lock(); + sk_for_each_rcu(sk, node, &net->packet.sklist) { + if (!net_eq(sock_net(sk), net)) + continue; + if (num < s_num) + goto next; + + if (sk_diag_fill(sk, skb, req, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + sock_i_ino(sk)) < 0) + goto done; +next: + num++; + } +done: + rcu_read_unlock(); + cb->args[0] = num; + + return skb->len; +} + +static int packet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h) +{ + int hdrlen = sizeof(struct packet_diag_req); + struct net *net = sock_net(skb->sk); + struct packet_diag_req *req; + + if (nlmsg_len(h) < hdrlen) + return -EINVAL; + + req = nlmsg_data(h); + /* Make it possible to support protocol filtering later */ + if (req->sdiag_protocol) + return -EINVAL; + + if (h->nlmsg_flags & NLM_F_DUMP) { + struct netlink_dump_control c = { + .dump = packet_diag_dump, + }; + return netlink_dump_start(net->diag_nlsk, skb, h, &c); + } else + return -EOPNOTSUPP; +} + +static const struct sock_diag_handler packet_diag_handler = { + .family = AF_PACKET, + .dump = packet_diag_handler_dump, +}; + +static int __init packet_diag_init(void) +{ + return sock_diag_register(&packet_diag_handler); +} + +static void __exit packet_diag_exit(void) +{ + sock_diag_unregister(&packet_diag_handler); +} + +module_init(packet_diag_init); +module_exit(packet_diag_exit); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 17 /* AF_PACKET */); -- cgit v1.2.3 From 8a360be0c5f8fe2c46f0a524886fa56596534193 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Mon, 13 Aug 2012 05:55:46 +0000 Subject: packet: Report more packet sk info via diag module This reports in one rtattr message all the other scalar values, that can be set on a packet socket with setsockopt. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/packet_diag.h | 23 +++++++++++++++++++++++ net/packet/diag.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) (limited to 'include') diff --git a/include/linux/packet_diag.h b/include/linux/packet_diag.h index fa19300ca334..3781ea4bc829 100644 --- a/include/linux/packet_diag.h +++ b/include/linux/packet_diag.h @@ -12,6 +12,8 @@ struct packet_diag_req { __u32 pdiag_cookie[2]; }; +#define PACKET_SHOW_INFO 0x00000001 /* Basic packet_sk information */ + struct packet_diag_msg { __u8 pdiag_family; __u8 pdiag_type; @@ -21,4 +23,25 @@ struct packet_diag_msg { __u32 pdiag_cookie[2]; }; +enum { + PACKET_DIAG_INFO, + + PACKET_DIAG_MAX, +}; + +struct packet_diag_info { + __u32 pdi_index; + __u32 pdi_version; + __u32 pdi_reserve; + __u32 pdi_copy_thresh; + __u32 pdi_tstamp; + __u32 pdi_flags; + +#define PDI_RUNNING 0x1 +#define PDI_AUXDATA 0x2 +#define PDI_ORIGDEV 0x4 +#define PDI_VNETHDR 0x8 +#define PDI_LOSS 0x10 +}; + #endif diff --git a/net/packet/diag.c b/net/packet/diag.c index ff2f7f5bfb8f..d5bd0372d004 100644 --- a/net/packet/diag.c +++ b/net/packet/diag.c @@ -7,6 +7,31 @@ #include "internal.h" +static int pdiag_put_info(const struct packet_sock *po, struct sk_buff *nlskb) +{ + struct packet_diag_info pinfo; + + pinfo.pdi_index = po->ifindex; + pinfo.pdi_version = po->tp_version; + pinfo.pdi_reserve = po->tp_reserve; + pinfo.pdi_copy_thresh = po->copy_thresh; + pinfo.pdi_tstamp = po->tp_tstamp; + + pinfo.pdi_flags = 0; + if (po->running) + pinfo.pdi_flags |= PDI_RUNNING; + if (po->auxdata) + pinfo.pdi_flags |= PDI_AUXDATA; + if (po->origdev) + pinfo.pdi_flags |= PDI_ORIGDEV; + if (po->has_vnet_hdr) + pinfo.pdi_flags |= PDI_VNETHDR; + if (po->tp_loss) + pinfo.pdi_flags |= PDI_LOSS; + + return nla_put(nlskb, PACKET_DIAG_INFO, sizeof(pinfo), &pinfo); +} + static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct packet_diag_req *req, u32 pid, u32 seq, u32 flags, int sk_ino) { @@ -25,7 +50,15 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct packet_diag rp->pdiag_ino = sk_ino; sock_diag_save_cookie(sk, rp->pdiag_cookie); + if ((req->pdiag_show & PACKET_SHOW_INFO) && + pdiag_put_info(po, skb)) + goto out_nlmsg_trim; + return nlmsg_end(skb, nlh); + +out_nlmsg_trim: + nlmsg_cancel(skb, nlh); + return -EMSGSIZE; } static int packet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) -- cgit v1.2.3 From eea68e2f1a0061e09265992b91fdc0014930ae92 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Mon, 13 Aug 2012 05:57:44 +0000 Subject: packet: Report socket mclist info via diag module The info is reported as an array of packet_diag_mclist structures. Each includes not only the directly configured values (index, type, etc), but also the "count". Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/packet_diag.h | 10 ++++++++++ net/packet/diag.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) (limited to 'include') diff --git a/include/linux/packet_diag.h b/include/linux/packet_diag.h index 3781ea4bc829..ea2e8923bd7e 100644 --- a/include/linux/packet_diag.h +++ b/include/linux/packet_diag.h @@ -13,6 +13,7 @@ struct packet_diag_req { }; #define PACKET_SHOW_INFO 0x00000001 /* Basic packet_sk information */ +#define PACKET_SHOW_MCLIST 0x00000002 /* A set of packet_diag_mclist-s */ struct packet_diag_msg { __u8 pdiag_family; @@ -25,6 +26,7 @@ struct packet_diag_msg { enum { PACKET_DIAG_INFO, + PACKET_DIAG_MCLIST, PACKET_DIAG_MAX, }; @@ -44,4 +46,12 @@ struct packet_diag_info { #define PDI_LOSS 0x10 }; +struct packet_diag_mclist { + __u32 pdmc_index; + __u32 pdmc_count; + __u16 pdmc_type; + __u16 pdmc_alen; + __u8 pdmc_addr[MAX_ADDR_LEN]; +}; + #endif diff --git a/net/packet/diag.c b/net/packet/diag.c index d5bd0372d004..3dda4ecb9a46 100644 --- a/net/packet/diag.c +++ b/net/packet/diag.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,40 @@ static int pdiag_put_info(const struct packet_sock *po, struct sk_buff *nlskb) return nla_put(nlskb, PACKET_DIAG_INFO, sizeof(pinfo), &pinfo); } +static int pdiag_put_mclist(const struct packet_sock *po, struct sk_buff *nlskb) +{ + struct nlattr *mca; + struct packet_mclist *ml; + + mca = nla_nest_start(nlskb, PACKET_DIAG_MCLIST); + if (!mca) + return -EMSGSIZE; + + rtnl_lock(); + for (ml = po->mclist; ml; ml = ml->next) { + struct packet_diag_mclist *dml; + + dml = nla_reserve_nohdr(nlskb, sizeof(*dml)); + if (!dml) { + rtnl_unlock(); + nla_nest_cancel(nlskb, mca); + return -EMSGSIZE; + } + + dml->pdmc_index = ml->ifindex; + dml->pdmc_type = ml->type; + dml->pdmc_alen = ml->alen; + dml->pdmc_count = ml->count; + BUILD_BUG_ON(sizeof(dml->pdmc_addr) != sizeof(ml->addr)); + memcpy(dml->pdmc_addr, ml->addr, sizeof(ml->addr)); + } + + rtnl_unlock(); + nla_nest_end(nlskb, mca); + + return 0; +} + static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct packet_diag_req *req, u32 pid, u32 seq, u32 flags, int sk_ino) { @@ -54,6 +89,10 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct packet_diag pdiag_put_info(po, skb)) goto out_nlmsg_trim; + if ((req->pdiag_show & PACKET_SHOW_MCLIST) && + pdiag_put_mclist(po, skb)) + goto out_nlmsg_trim; + return nlmsg_end(skb, nlh); out_nlmsg_trim: -- cgit v1.2.3 From 57f5d0d1d9f8e59819cb0ab4b707364c54b5b2d1 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Fri, 27 Jul 2012 19:32:54 -0300 Subject: Bluetooth: Remove some functions from being exported Some connection related functions are only used inside hci_conn.c so no need to have them exported. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 2 -- net/bluetooth/hci_conn.c | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 41d943926d2c..7267dafd7159 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -551,9 +551,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev, return NULL; } -void hci_acl_connect(struct hci_conn *conn); void hci_acl_disconn(struct hci_conn *conn, __u8 reason); -void hci_add_sco(struct hci_conn *conn, __u16 handle); void hci_setup_sync(struct hci_conn *conn, __u16 handle); void hci_sco_setup(struct hci_conn *conn, __u8 status); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 5ad7da217474..724eea980812 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -59,7 +59,7 @@ static void hci_le_connect_cancel(struct hci_conn *conn) hci_send_cmd(conn->hdev, HCI_OP_LE_CREATE_CONN_CANCEL, 0, NULL); } -void hci_acl_connect(struct hci_conn *conn) +static void hci_acl_connect(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; struct inquiry_entry *ie; @@ -129,7 +129,7 @@ void hci_acl_disconn(struct hci_conn *conn, __u8 reason) hci_send_cmd(conn->hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp); } -void hci_add_sco(struct hci_conn *conn, __u16 handle) +static void hci_add_sco(struct hci_conn *conn, __u16 handle) { struct hci_dev *hdev = conn->hdev; struct hci_cp_add_sco cp; -- cgit v1.2.3 From 976d020150456fccbd34103fd117fab910eed09c Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 23 May 2012 17:16:53 -0600 Subject: userns: Convert sock_i_uid to return a kuid_t Acked-by: David S. Miller Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- include/net/sock.h | 2 +- net/core/sock.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index b3730239bf18..65c3d62bfa5a 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1668,7 +1668,7 @@ static inline void sock_graft(struct sock *sk, struct socket *parent) write_unlock_bh(&sk->sk_callback_lock); } -extern int sock_i_uid(struct sock *sk); +extern kuid_t sock_i_uid(struct sock *sk); extern unsigned long sock_i_ino(struct sock *sk); static inline struct dst_entry * diff --git a/net/core/sock.c b/net/core/sock.c index 9c7fe4ff30fc..5c6a435717e0 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1526,12 +1526,12 @@ void sock_edemux(struct sk_buff *skb) } EXPORT_SYMBOL(sock_edemux); -int sock_i_uid(struct sock *sk) +kuid_t sock_i_uid(struct sock *sk) { - int uid; + kuid_t uid; read_lock_bh(&sk->sk_callback_lock); - uid = sk->sk_socket ? SOCK_INODE(sk->sk_socket)->i_uid : 0; + uid = sk->sk_socket ? SOCK_INODE(sk->sk_socket)->i_uid : GLOBAL_ROOT_UID; read_unlock_bh(&sk->sk_callback_lock); return uid; } -- cgit v1.2.3 From adb37c4c67f807f16beb222028fb3ce9a354dc2b Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 23 May 2012 18:01:20 -0600 Subject: userns: Make seq_file's user namespace accessible struct file already has a user namespace associated with it in file->f_cred->user_ns, unfortunately because struct seq_file has no struct file backpointer associated with it, it is difficult to get at the user namespace in seq_file context. Therefore add a helper function seq_user_ns to return the associated user namespace and a user_ns field to struct seq_file to be used in implementing seq_user_ns. Cc: Al Viro Cc: Eric Dumazet Cc: KAMEZAWA Hiroyuki Cc: Alexey Dobriyan Acked-by: David S. Miller Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- fs/seq_file.c | 4 ++++ include/linux/seq_file.h | 14 ++++++++++++++ 2 files changed, 18 insertions(+) (limited to 'include') diff --git a/fs/seq_file.c b/fs/seq_file.c index 14cf9de1dbe1..99dffab4c4e4 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -56,6 +57,9 @@ int seq_open(struct file *file, const struct seq_operations *op) memset(p, 0, sizeof(*p)); mutex_init(&p->lock); p->op = op; +#ifdef CONFIG_USER_NS + p->user_ns = file->f_cred->user_ns; +#endif /* * Wrappers around seq_open(e.g. swaps_open) need to be diff --git a/include/linux/seq_file.h b/include/linux/seq_file.h index 83c44eefe698..68a04a343cad 100644 --- a/include/linux/seq_file.h +++ b/include/linux/seq_file.h @@ -13,6 +13,7 @@ struct file; struct path; struct inode; struct dentry; +struct user_namespace; struct seq_file { char *buf; @@ -25,6 +26,9 @@ struct seq_file { struct mutex lock; const struct seq_operations *op; int poll_event; +#ifdef CONFIG_USER_NS + struct user_namespace *user_ns; +#endif void *private; }; @@ -128,6 +132,16 @@ int seq_put_decimal_ull(struct seq_file *m, char delimiter, int seq_put_decimal_ll(struct seq_file *m, char delimiter, long long num); +static inline struct user_namespace *seq_user_ns(struct seq_file *seq) +{ +#ifdef CONFIG_USER_NS + return seq->user_ns; +#else + extern struct user_namespace init_user_ns; + return &init_user_ns; +#endif +} + #define SEQ_START_TOKEN ((void *)1) /* * Helpers for iteration over list_head-s in seq_files -- cgit v1.2.3 From a7cb5a49bf64ba64864ae16a6be028f8b0d3cc06 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 24 May 2012 01:10:10 -0600 Subject: userns: Print out socket uids in a user namespace aware fashion. Cc: Alexey Kuznetsov Cc: James Morris Cc: Hideaki YOSHIFUJI Cc: Patrick McHardy Cc: Arnaldo Carvalho de Melo Cc: Sridhar Samudrala Acked-by: Vlad Yasevich Acked-by: David S. Miller Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- include/net/tcp.h | 3 ++- init/Kconfig | 6 ------ net/appletalk/atalk_proc.c | 3 ++- net/ipv4/ping.c | 4 +++- net/ipv4/raw.c | 4 +++- net/ipv4/tcp_ipv4.c | 6 +++--- net/ipv4/udp.c | 4 +++- net/ipv6/raw.c | 3 ++- net/ipv6/tcp_ipv6.c | 6 +++--- net/ipv6/udp.c | 3 ++- net/ipx/ipx_proc.c | 3 ++- net/key/af_key.c | 2 +- net/llc/llc_proc.c | 2 +- net/packet/af_packet.c | 2 +- net/phonet/socket.c | 6 ++++-- net/sctp/proc.c | 6 ++++-- 16 files changed, 36 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index e19124b84cd2..91e746736a8f 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1509,7 +1509,8 @@ struct tcp_iter_state { sa_family_t family; enum tcp_seq_states state; struct sock *syn_wait_sk; - int bucket, offset, sbucket, num, uid; + int bucket, offset, sbucket, num; + kuid_t uid; loff_t last_pos; }; diff --git a/init/Kconfig b/init/Kconfig index 80fae193e3a1..25a6ebb50c64 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -942,10 +942,7 @@ config UIDGID_CONVERTED depends on PROC_EVENTS = n # Networking - depends on PACKET = n depends on NET_9P = n - depends on IPX = n - depends on PHONET = n depends on NET_CLS_FLOW = n depends on NETFILTER_XT_MATCH_OWNER = n depends on NETFILTER_XT_MATCH_RECENT = n @@ -953,14 +950,11 @@ config UIDGID_CONVERTED depends on NETFILTER_NETLINK_LOG = n depends on INET = n depends on IPV6 = n - depends on IP_SCTP = n depends on AF_RXRPC = n - depends on LLC2 = n depends on NET_KEY = n depends on INET_DIAG = n depends on DNS_RESOLVER = n depends on AX25 = n - depends on ATALK = n # Filesystems depends on USB_GADGETFS = n diff --git a/net/appletalk/atalk_proc.c b/net/appletalk/atalk_proc.c index b5b1a221c242..c30f3a0717fb 100644 --- a/net/appletalk/atalk_proc.c +++ b/net/appletalk/atalk_proc.c @@ -183,7 +183,8 @@ static int atalk_seq_socket_show(struct seq_file *seq, void *v) ntohs(at->dest_net), at->dest_node, at->dest_port, sk_wmem_alloc_get(s), sk_rmem_alloc_get(s), - s->sk_state, SOCK_INODE(s->sk_socket)->i_uid); + s->sk_state, + from_kuid_munged(seq_user_ns(seq), sock_i_uid(s))); out: return 0; } diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 6232d476f37e..bee5eeb676f8 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -845,7 +845,9 @@ static void ping_format_sock(struct sock *sp, struct seq_file *f, bucket, src, srcp, dest, destp, sp->sk_state, sk_wmem_alloc_get(sp), sk_rmem_alloc_get(sp), - 0, 0L, 0, sock_i_uid(sp), 0, sock_i_ino(sp), + 0, 0L, 0, + from_kuid_munged(seq_user_ns(f), sock_i_uid(sp)), + 0, sock_i_ino(sp), atomic_read(&sp->sk_refcnt), sp, atomic_read(&sp->sk_drops), len); } diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index ff0f071969ea..f2425785d40a 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -992,7 +992,9 @@ static void raw_sock_seq_show(struct seq_file *seq, struct sock *sp, int i) i, src, srcp, dest, destp, sp->sk_state, sk_wmem_alloc_get(sp), sk_rmem_alloc_get(sp), - 0, 0L, 0, sock_i_uid(sp), 0, sock_i_ino(sp), + 0, 0L, 0, + from_kuid_munged(seq_user_ns(seq), sock_i_uid(sp)), + 0, sock_i_ino(sp), atomic_read(&sp->sk_refcnt), sp, atomic_read(&sp->sk_drops)); } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 42b2a6a73092..642be8a4c6a3 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2382,7 +2382,7 @@ void tcp_proc_unregister(struct net *net, struct tcp_seq_afinfo *afinfo) EXPORT_SYMBOL(tcp_proc_unregister); static void get_openreq4(const struct sock *sk, const struct request_sock *req, - struct seq_file *f, int i, int uid, int *len) + struct seq_file *f, int i, kuid_t uid, int *len) { const struct inet_request_sock *ireq = inet_rsk(req); int ttd = req->expires - jiffies; @@ -2399,7 +2399,7 @@ static void get_openreq4(const struct sock *sk, const struct request_sock *req, 1, /* timers active (only the expire timer) */ jiffies_to_clock_t(ttd), req->retrans, - uid, + from_kuid_munged(seq_user_ns(f), uid), 0, /* non standard timer */ 0, /* open_requests have no inode */ atomic_read(&sk->sk_refcnt), @@ -2450,7 +2450,7 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i, int *len) timer_active, jiffies_to_clock_t(timer_expires - jiffies), icsk->icsk_retransmits, - sock_i_uid(sk), + from_kuid_munged(seq_user_ns(f), sock_i_uid(sk)), icsk->icsk_probes_out, sock_i_ino(sk), atomic_read(&sk->sk_refcnt), sk, diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index b4c3582a991f..53b89817c008 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -2110,7 +2110,9 @@ static void udp4_format_sock(struct sock *sp, struct seq_file *f, bucket, src, srcp, dest, destp, sp->sk_state, sk_wmem_alloc_get(sp), sk_rmem_alloc_get(sp), - 0, 0L, 0, sock_i_uid(sp), 0, sock_i_ino(sp), + 0, 0L, 0, + from_kuid_munged(seq_user_ns(f), sock_i_uid(sp)), + 0, sock_i_ino(sp), atomic_read(&sp->sk_refcnt), sp, atomic_read(&sp->sk_drops), len); } diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index ef0579d5bca6..7af88ef01657 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -1251,7 +1251,8 @@ static void raw6_sock_seq_show(struct seq_file *seq, struct sock *sp, int i) sk_wmem_alloc_get(sp), sk_rmem_alloc_get(sp), 0, 0L, 0, - sock_i_uid(sp), 0, + from_kuid_munged(seq_user_ns(seq), sock_i_uid(sp)), + 0, sock_i_ino(sp), atomic_read(&sp->sk_refcnt), sp, atomic_read(&sp->sk_drops)); } diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index c66b90f71c9b..4b5b335ebde1 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1803,7 +1803,7 @@ static void tcp_v6_destroy_sock(struct sock *sk) #ifdef CONFIG_PROC_FS /* Proc filesystem TCPv6 sock list dumping. */ static void get_openreq6(struct seq_file *seq, - const struct sock *sk, struct request_sock *req, int i, int uid) + const struct sock *sk, struct request_sock *req, int i, kuid_t uid) { int ttd = req->expires - jiffies; const struct in6_addr *src = &inet6_rsk(req)->loc_addr; @@ -1827,7 +1827,7 @@ static void get_openreq6(struct seq_file *seq, 1, /* timers active (only the expire timer) */ jiffies_to_clock_t(ttd), req->retrans, - uid, + from_kuid_munged(seq_user_ns(seq), uid), 0, /* non standard timer */ 0, /* open_requests have no inode */ 0, req); @@ -1877,7 +1877,7 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i) timer_active, jiffies_to_clock_t(timer_expires - jiffies), icsk->icsk_retransmits, - sock_i_uid(sp), + from_kuid_munged(seq_user_ns(seq), sock_i_uid(sp)), icsk->icsk_probes_out, sock_i_ino(sp), atomic_read(&sp->sk_refcnt), sp, diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 99d0077b56b8..bbdff07eebe1 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1458,7 +1458,8 @@ static void udp6_sock_seq_show(struct seq_file *seq, struct sock *sp, int bucket sk_wmem_alloc_get(sp), sk_rmem_alloc_get(sp), 0, 0L, 0, - sock_i_uid(sp), 0, + from_kuid_munged(seq_user_ns(seq), sock_i_uid(sp)), + 0, sock_i_ino(sp), atomic_read(&sp->sk_refcnt), sp, atomic_read(&sp->sk_drops)); diff --git a/net/ipx/ipx_proc.c b/net/ipx/ipx_proc.c index f8ba30dfecae..02ff7f2f60d4 100644 --- a/net/ipx/ipx_proc.c +++ b/net/ipx/ipx_proc.c @@ -217,7 +217,8 @@ static int ipx_seq_socket_show(struct seq_file *seq, void *v) seq_printf(seq, "%08X %08X %02X %03d\n", sk_wmem_alloc_get(s), sk_rmem_alloc_get(s), - s->sk_state, SOCK_INODE(s->sk_socket)->i_uid); + s->sk_state, + from_kuid_munged(seq_user_ns(seq), sock_i_uid(s))); out: return 0; } diff --git a/net/key/af_key.c b/net/key/af_key.c index 34e418508a67..0481d4b51476 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -3661,7 +3661,7 @@ static int pfkey_seq_show(struct seq_file *f, void *v) atomic_read(&s->sk_refcnt), sk_rmem_alloc_get(s), sk_wmem_alloc_get(s), - sock_i_uid(s), + from_kuid_munged(seq_user_ns(f), sock_i_uid(s)), sock_i_ino(s) ); return 0; diff --git a/net/llc/llc_proc.c b/net/llc/llc_proc.c index a1839c004357..7b4799cfbf8d 100644 --- a/net/llc/llc_proc.c +++ b/net/llc/llc_proc.c @@ -151,7 +151,7 @@ static int llc_seq_socket_show(struct seq_file *seq, void *v) sk_wmem_alloc_get(sk), sk_rmem_alloc_get(sk) - llc->copied_seq, sk->sk_state, - sk->sk_socket ? SOCK_INODE(sk->sk_socket)->i_uid : -1, + from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)), llc->link); out: return 0; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index ceaca7c134a0..d147317ce9ea 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -3846,7 +3846,7 @@ static int packet_seq_show(struct seq_file *seq, void *v) po->ifindex, po->running, atomic_read(&s->sk_rmem_alloc), - sock_i_uid(s), + from_kuid_munged(seq_user_ns(seq), sock_i_uid(s)), sock_i_ino(s)); } diff --git a/net/phonet/socket.c b/net/phonet/socket.c index 0acc943f713a..b7e982782255 100644 --- a/net/phonet/socket.c +++ b/net/phonet/socket.c @@ -612,7 +612,8 @@ static int pn_sock_seq_show(struct seq_file *seq, void *v) sk->sk_protocol, pn->sobject, pn->dobject, pn->resource, sk->sk_state, sk_wmem_alloc_get(sk), sk_rmem_alloc_get(sk), - sock_i_uid(sk), sock_i_ino(sk), + from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)), + sock_i_ino(sk), atomic_read(&sk->sk_refcnt), sk, atomic_read(&sk->sk_drops), &len); } @@ -796,7 +797,8 @@ static int pn_res_seq_show(struct seq_file *seq, void *v) struct sock *sk = *psk; seq_printf(seq, "%02X %5d %lu%n", - (int) (psk - pnres.sk), sock_i_uid(sk), + (int) (psk - pnres.sk), + from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)), sock_i_ino(sk), &len); } seq_printf(seq, "%*s\n", 63 - len, ""); diff --git a/net/sctp/proc.c b/net/sctp/proc.c index 1e2eee88c3ea..dc12febc977a 100644 --- a/net/sctp/proc.c +++ b/net/sctp/proc.c @@ -216,7 +216,8 @@ static int sctp_eps_seq_show(struct seq_file *seq, void *v) seq_printf(seq, "%8pK %8pK %-3d %-3d %-4d %-5d %5d %5lu ", ep, sk, sctp_sk(sk)->type, sk->sk_state, hash, epb->bind_addr.port, - sock_i_uid(sk), sock_i_ino(sk)); + from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)), + sock_i_ino(sk)); sctp_seq_dump_local_addrs(seq, epb); seq_printf(seq, "\n"); @@ -324,7 +325,8 @@ static int sctp_assocs_seq_show(struct seq_file *seq, void *v) assoc->assoc_id, assoc->sndbuf_used, atomic_read(&assoc->rmem_alloc), - sock_i_uid(sk), sock_i_ino(sk), + from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)), + sock_i_ino(sk), epb->bind_addr.port, assoc->peer.port); seq_printf(seq, " "); -- cgit v1.2.3 From 7064d16e162adf8199f0288b694e6af823ed5431 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 24 May 2012 10:34:21 -0600 Subject: userns: Use kgids for sysctl_ping_group_range - Store sysctl_ping_group_range as a paire of kgid_t values instead of a pair of gid_t values. - Move the kgid conversion work from ping_init_sock into ipv4_ping_group_range - For invalid cases reset to the default disabled state. With the kgid_t conversion made part of the original value sanitation from userspace understand how the code will react becomes clearer and it becomes possible to set the sysctl ping group range from something other than the initial user namespace. Cc: Vasiliy Kulikov Acked-by: David S. Miller Signed-off-by: Eric W. Biederman --- include/net/netns/ipv4.h | 3 ++- init/Kconfig | 1 - net/ipv4/ping.c | 18 ++++++------------ net/ipv4/sysctl_net_ipv4.c | 42 +++++++++++++++++++++++++++--------------- 4 files changed, 35 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 1474dd65c66f..3516dc0cc615 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -5,6 +5,7 @@ #ifndef __NETNS_IPV4_H__ #define __NETNS_IPV4_H__ +#include #include struct tcpm_hash_bucket; @@ -62,7 +63,7 @@ struct netns_ipv4 { int sysctl_icmp_ratemask; int sysctl_icmp_errors_use_inbound_ifaddr; - unsigned int sysctl_ping_group_range[2]; + kgid_t sysctl_ping_group_range[2]; long sysctl_tcp_mem[3]; atomic_t rt_genid; diff --git a/init/Kconfig b/init/Kconfig index 25a6ebb50c64..f857f97bcef3 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -948,7 +948,6 @@ config UIDGID_CONVERTED depends on NETFILTER_XT_MATCH_RECENT = n depends on NETFILTER_XT_TARGET_LOG = n depends on NETFILTER_NETLINK_LOG = n - depends on INET = n depends on IPV6 = n depends on AF_RXRPC = n depends on NET_KEY = n diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index bee5eeb676f8..8f3d05424a3e 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -185,10 +185,10 @@ exit: return sk; } -static void inet_get_ping_group_range_net(struct net *net, gid_t *low, - gid_t *high) +static void inet_get_ping_group_range_net(struct net *net, kgid_t *low, + kgid_t *high) { - gid_t *data = net->ipv4.sysctl_ping_group_range; + kgid_t *data = net->ipv4.sysctl_ping_group_range; unsigned int seq; do { @@ -203,19 +203,13 @@ static void inet_get_ping_group_range_net(struct net *net, gid_t *low, static int ping_init_sock(struct sock *sk) { struct net *net = sock_net(sk); - gid_t group = current_egid(); - gid_t range[2]; + kgid_t group = current_egid(); struct group_info *group_info = get_current_groups(); int i, j, count = group_info->ngroups; kgid_t low, high; - inet_get_ping_group_range_net(net, range, range+1); - low = make_kgid(&init_user_ns, range[0]); - high = make_kgid(&init_user_ns, range[1]); - if (!gid_valid(low) || !gid_valid(high) || gid_lt(high, low)) - return -EACCES; - - if (range[0] <= group && group <= range[1]) + inet_get_ping_group_range_net(net, &low, &high); + if (gid_lte(low, group) && gid_lte(group, high)) return 0; for (i = 0; i < group_info->nblocks; i++) { diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 1b5ce96707a3..3e78c79b5586 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -76,9 +76,9 @@ static int ipv4_local_port_range(ctl_table *table, int write, } -static void inet_get_ping_group_range_table(struct ctl_table *table, gid_t *low, gid_t *high) +static void inet_get_ping_group_range_table(struct ctl_table *table, kgid_t *low, kgid_t *high) { - gid_t *data = table->data; + kgid_t *data = table->data; unsigned int seq; do { seq = read_seqbegin(&sysctl_local_ports.lock); @@ -89,12 +89,12 @@ static void inet_get_ping_group_range_table(struct ctl_table *table, gid_t *low, } /* Update system visible IP port range */ -static void set_ping_group_range(struct ctl_table *table, gid_t range[2]) +static void set_ping_group_range(struct ctl_table *table, kgid_t low, kgid_t high) { - gid_t *data = table->data; + kgid_t *data = table->data; write_seqlock(&sysctl_local_ports.lock); - data[0] = range[0]; - data[1] = range[1]; + data[0] = low; + data[1] = high; write_sequnlock(&sysctl_local_ports.lock); } @@ -103,21 +103,33 @@ static int ipv4_ping_group_range(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { + struct user_namespace *user_ns = current_user_ns(); int ret; - gid_t range[2]; + gid_t urange[2]; + kgid_t low, high; ctl_table tmp = { - .data = &range, - .maxlen = sizeof(range), + .data = &urange, + .maxlen = sizeof(urange), .mode = table->mode, .extra1 = &ip_ping_group_range_min, .extra2 = &ip_ping_group_range_max, }; - inet_get_ping_group_range_table(table, range, range + 1); + inet_get_ping_group_range_table(table, &low, &high); + urange[0] = from_kgid_munged(user_ns, low); + urange[1] = from_kgid_munged(user_ns, high); ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); - if (write && ret == 0) - set_ping_group_range(table, range); + if (write && ret == 0) { + low = make_kgid(user_ns, urange[0]); + high = make_kgid(user_ns, urange[1]); + if (!gid_valid(low) || !gid_valid(high) || + (urange[1] < urange[0]) || gid_lt(high, low)) { + low = make_kgid(&init_user_ns, 1); + high = make_kgid(&init_user_ns, 0); + } + set_ping_group_range(table, low, high); + } return ret; } @@ -786,7 +798,7 @@ static struct ctl_table ipv4_net_table[] = { { .procname = "ping_group_range", .data = &init_net.ipv4.sysctl_ping_group_range, - .maxlen = sizeof(init_net.ipv4.sysctl_ping_group_range), + .maxlen = sizeof(gid_t)*2, .mode = 0644, .proc_handler = ipv4_ping_group_range, }, @@ -830,8 +842,8 @@ static __net_init int ipv4_sysctl_init_net(struct net *net) * Sane defaults - nobody may create ping sockets. * Boot scripts should set this to distro-specific group. */ - net->ipv4.sysctl_ping_group_range[0] = 1; - net->ipv4.sysctl_ping_group_range[1] = 0; + net->ipv4.sysctl_ping_group_range[0] = make_kgid(&init_user_ns, 1); + net->ipv4.sysctl_ping_group_range[1] = make_kgid(&init_user_ns, 0); tcp_init_mem(net); -- cgit v1.2.3 From 4f82f45730c68fdaf9b0472495a965188404866e Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 24 May 2012 10:37:59 -0600 Subject: net ip6 flowlabel: Make owner a union of struct pid * and kuid_t Correct a long standing omission and use struct pid in the owner field of struct ip6_flowlabel when the share type is IPV6_FL_S_PROCESS. This guarantees we don't have issues when pid wraparound occurs. Use a kuid_t in the owner field of struct ip6_flowlabel when the share type is IPV6_FL_S_USER to add user namespace support. In /proc/net/ip6_flowlabel capture the current pid namespace when opening the file and release the pid namespace when the file is closed ensuring we print the pid owner value that is meaning to the reader of the file. Similarly use from_kuid_munged to print uid values that are meaningful to the reader of the file. This requires exporting pid_nr_ns so that ipv6 can continue to built as a module. Yoiks what silliness Acked-by: David S. Miller Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- include/net/ipv6.h | 5 ++++- init/Kconfig | 1 - kernel/pid.c | 1 + net/ipv6/ip6_flowlabel.c | 50 +++++++++++++++++++++++++++++++++++++++++------- 4 files changed, 48 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 01c34b363a34..c8a202436e01 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -222,7 +222,10 @@ struct ip6_flowlabel { struct ipv6_txoptions *opt; unsigned long linger; u8 share; - u32 owner; + union { + struct pid *pid; + kuid_t uid; + } owner; unsigned long lastuse; unsigned long expires; struct net *fl_net; diff --git a/init/Kconfig b/init/Kconfig index f857f97bcef3..64ff9ce59443 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -948,7 +948,6 @@ config UIDGID_CONVERTED depends on NETFILTER_XT_MATCH_RECENT = n depends on NETFILTER_XT_TARGET_LOG = n depends on NETFILTER_NETLINK_LOG = n - depends on IPV6 = n depends on AF_RXRPC = n depends on NET_KEY = n depends on INET_DIAG = n diff --git a/kernel/pid.c b/kernel/pid.c index e86b291ad834..aebd4f5aaf41 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -479,6 +479,7 @@ pid_t pid_nr_ns(struct pid *pid, struct pid_namespace *ns) } return nr; } +EXPORT_SYMBOL_GPL(pid_nr_ns); pid_t pid_vnr(struct pid *pid) { diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index 9772fbd8a3f5..c836a6a20a34 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -90,6 +91,11 @@ static struct ip6_flowlabel *fl_lookup(struct net *net, __be32 label) static void fl_free(struct ip6_flowlabel *fl) { + switch (fl->share) { + case IPV6_FL_S_PROCESS: + put_pid(fl->owner.pid); + break; + } if (fl) { release_net(fl->fl_net); kfree(fl->opt); @@ -394,10 +400,10 @@ fl_create(struct net *net, struct sock *sk, struct in6_flowlabel_req *freq, case IPV6_FL_S_ANY: break; case IPV6_FL_S_PROCESS: - fl->owner = current->pid; + fl->owner.pid = get_task_pid(current, PIDTYPE_PID); break; case IPV6_FL_S_USER: - fl->owner = current_euid(); + fl->owner.uid = current_euid(); break; default: err = -EINVAL; @@ -561,7 +567,10 @@ recheck: err = -EPERM; if (fl1->share == IPV6_FL_S_EXCL || fl1->share != fl->share || - fl1->owner != fl->owner) + ((fl1->share == IPV6_FL_S_PROCESS) && + (fl1->owner.pid == fl->owner.pid)) || + ((fl1->share == IPV6_FL_S_USER) && + uid_eq(fl1->owner.uid, fl->owner.uid))) goto release; err = -EINVAL; @@ -621,6 +630,7 @@ done: struct ip6fl_iter_state { struct seq_net_private p; + struct pid_namespace *pid_ns; int bucket; }; @@ -699,6 +709,7 @@ static void ip6fl_seq_stop(struct seq_file *seq, void *v) static int ip6fl_seq_show(struct seq_file *seq, void *v) { + struct ip6fl_iter_state *state = ip6fl_seq_private(seq); if (v == SEQ_START_TOKEN) seq_printf(seq, "%-5s %-1s %-6s %-6s %-6s %-8s %-32s %s\n", "Label", "S", "Owner", "Users", "Linger", "Expires", "Dst", "Opt"); @@ -708,7 +719,11 @@ static int ip6fl_seq_show(struct seq_file *seq, void *v) "%05X %-1d %-6d %-6d %-6ld %-8ld %pi6 %-4d\n", (unsigned int)ntohl(fl->label), fl->share, - (int)fl->owner, + ((fl->share == IPV6_FL_S_PROCESS) ? + pid_nr_ns(fl->owner.pid, state->pid_ns) : + ((fl->share == IPV6_FL_S_USER) ? + from_kuid_munged(seq_user_ns(seq), fl->owner.uid) : + 0)), atomic_read(&fl->users), fl->linger/HZ, (long)(fl->expires - jiffies)/HZ, @@ -727,8 +742,29 @@ static const struct seq_operations ip6fl_seq_ops = { static int ip6fl_seq_open(struct inode *inode, struct file *file) { - return seq_open_net(inode, file, &ip6fl_seq_ops, - sizeof(struct ip6fl_iter_state)); + struct seq_file *seq; + struct ip6fl_iter_state *state; + int err; + + err = seq_open_net(inode, file, &ip6fl_seq_ops, + sizeof(struct ip6fl_iter_state)); + + if (!err) { + seq = file->private_data; + state = ip6fl_seq_private(seq); + rcu_read_lock(); + state->pid_ns = get_pid_ns(task_active_pid_ns(current)); + rcu_read_unlock(); + } + return err; +} + +static int ip6fl_seq_release(struct inode *inode, struct file *file) +{ + struct seq_file *seq = file->private_data; + struct ip6fl_iter_state *state = ip6fl_seq_private(seq); + put_pid_ns(state->pid_ns); + return seq_release_net(inode, file); } static const struct file_operations ip6fl_seq_fops = { @@ -736,7 +772,7 @@ static const struct file_operations ip6fl_seq_fops = { .open = ip6fl_seq_open, .read = seq_read, .llseek = seq_lseek, - .release = seq_release_net, + .release = ip6fl_seq_release, }; static int __net_init ip6_flowlabel_proc_init(struct net *net) -- cgit v1.2.3 From d13fda8564a67341aad257465cf319bdb2327e33 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 24 May 2012 12:55:00 -0600 Subject: userns: Convert net/ax25 to use kuid_t where appropriate Cc: Ralf Baechle Acked-by: David S. Miller Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- include/net/ax25.h | 4 ++-- init/Kconfig | 1 - net/ax25/ax25_uid.c | 21 ++++++++++++++------- 3 files changed, 16 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/ax25.h b/include/net/ax25.h index 5d2352154cf6..53539acbd81a 100644 --- a/include/net/ax25.h +++ b/include/net/ax25.h @@ -157,7 +157,7 @@ enum { typedef struct ax25_uid_assoc { struct hlist_node uid_node; atomic_t refcount; - uid_t uid; + kuid_t uid; ax25_address call; } ax25_uid_assoc; @@ -434,7 +434,7 @@ extern unsigned long ax25_display_timer(struct timer_list *); /* ax25_uid.c */ extern int ax25_uid_policy; -extern ax25_uid_assoc *ax25_findbyuid(uid_t); +extern ax25_uid_assoc *ax25_findbyuid(kuid_t); extern int __must_check ax25_uid_ioctl(int, struct sockaddr_ax25 *); extern const struct file_operations ax25_uid_fops; extern void ax25_uid_free(void); diff --git a/init/Kconfig b/init/Kconfig index 64ff9ce59443..8447e0ca4186 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -952,7 +952,6 @@ config UIDGID_CONVERTED depends on NET_KEY = n depends on INET_DIAG = n depends on DNS_RESOLVER = n - depends on AX25 = n # Filesystems depends on USB_GADGETFS = n diff --git a/net/ax25/ax25_uid.c b/net/ax25/ax25_uid.c index e3c579ba6325..957999e43ff7 100644 --- a/net/ax25/ax25_uid.c +++ b/net/ax25/ax25_uid.c @@ -51,14 +51,14 @@ int ax25_uid_policy; EXPORT_SYMBOL(ax25_uid_policy); -ax25_uid_assoc *ax25_findbyuid(uid_t uid) +ax25_uid_assoc *ax25_findbyuid(kuid_t uid) { ax25_uid_assoc *ax25_uid, *res = NULL; struct hlist_node *node; read_lock(&ax25_uid_lock); ax25_uid_for_each(ax25_uid, node, &ax25_uid_list) { - if (ax25_uid->uid == uid) { + if (uid_eq(ax25_uid->uid, uid)) { ax25_uid_hold(ax25_uid); res = ax25_uid; break; @@ -84,7 +84,7 @@ int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax) read_lock(&ax25_uid_lock); ax25_uid_for_each(ax25_uid, node, &ax25_uid_list) { if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) { - res = ax25_uid->uid; + res = from_kuid_munged(current_user_ns(), ax25_uid->uid); break; } } @@ -93,9 +93,14 @@ int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax) return res; case SIOCAX25ADDUID: + { + kuid_t sax25_kuid; if (!capable(CAP_NET_ADMIN)) return -EPERM; - user = ax25_findbyuid(sax->sax25_uid); + sax25_kuid = make_kuid(current_user_ns(), sax->sax25_uid); + if (!uid_valid(sax25_kuid)) + return -EINVAL; + user = ax25_findbyuid(sax25_kuid); if (user) { ax25_uid_put(user); return -EEXIST; @@ -106,7 +111,7 @@ int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax) return -ENOMEM; atomic_set(&ax25_uid->refcount, 1); - ax25_uid->uid = sax->sax25_uid; + ax25_uid->uid = sax25_kuid; ax25_uid->call = sax->sax25_call; write_lock(&ax25_uid_lock); @@ -114,7 +119,7 @@ int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax) write_unlock(&ax25_uid_lock); return 0; - + } case SIOCAX25DELUID: if (!capable(CAP_NET_ADMIN)) return -EPERM; @@ -172,7 +177,9 @@ static int ax25_uid_seq_show(struct seq_file *seq, void *v) struct ax25_uid_assoc *pt; pt = hlist_entry(v, struct ax25_uid_assoc, uid_node); - seq_printf(seq, "%6d %s\n", pt->uid, ax2asc(buf, &pt->call)); + seq_printf(seq, "%6d %s\n", + from_kuid_munged(seq_user_ns(seq), pt->uid), + ax2asc(buf, &pt->call)); } return 0; } -- cgit v1.2.3 From 3fbc290540a1ed1a8a076ed8f53bee7a38a9f408 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 24 May 2012 17:21:27 -0600 Subject: netlink: Make the sending netlink socket availabe in NETLINK_CB The sending socket of an skb is already available by it's port id in the NETLINK_CB. If you want to know more like to examine the credentials on the sending socket you have to look up the sending socket by it's port id and all of the needed functions and data structures are static inside of af_netlink.c. So do the simple thing and pass the sending socket to the receivers in the NETLINK_CB. I intend to use this to get the user namespace of the sending socket in inet_diag so that I can report uids in the context of the process who opened the socket, the same way I report uids in the contect of the process who opens files. Acked-by: David S. Miller Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- include/linux/netlink.h | 1 + net/netlink/af_netlink.c | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/netlink.h b/include/linux/netlink.h index f74dd133788f..c9fdde2bc73f 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -165,6 +165,7 @@ struct netlink_skb_parms { struct ucred creds; /* Skb credentials */ __u32 pid; __u32 dst_group; + struct sock *ssk; }; #define NETLINK_CB(skb) (*(struct netlink_skb_parms*)&((skb)->cb)) diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 5463969da45b..7cb7867cc369 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -912,7 +912,8 @@ static void netlink_rcv_wake(struct sock *sk) wake_up_interruptible(&nlk->wait); } -static int netlink_unicast_kernel(struct sock *sk, struct sk_buff *skb) +static int netlink_unicast_kernel(struct sock *sk, struct sk_buff *skb, + struct sock *ssk) { int ret; struct netlink_sock *nlk = nlk_sk(sk); @@ -921,6 +922,7 @@ static int netlink_unicast_kernel(struct sock *sk, struct sk_buff *skb) if (nlk->netlink_rcv != NULL) { ret = skb->len; skb_set_owner_r(skb, sk); + NETLINK_CB(skb).ssk = ssk; nlk->netlink_rcv(skb); consume_skb(skb); } else { @@ -947,7 +949,7 @@ retry: return PTR_ERR(sk); } if (netlink_is_kernel(sk)) - return netlink_unicast_kernel(sk, skb); + return netlink_unicast_kernel(sk, skb, ssk); if (sk_filter(sk, skb)) { err = skb->len; -- cgit v1.2.3 From c336d148adc4181f31741ae066df41429be64b67 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 24 May 2012 17:56:43 -0600 Subject: userns: Implement sk_user_ns Add a helper sk_user_ns to make it easy to find the user namespace of the process that opened a socket. Acked-by: David S. Miller Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- include/net/sock.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index 65c3d62bfa5a..9d43736a869d 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -604,6 +604,15 @@ static inline void sk_add_bind_node(struct sock *sk, #define sk_for_each_bound(__sk, node, list) \ hlist_for_each_entry(__sk, node, list, sk_bind_node) +static inline struct user_namespace *sk_user_ns(struct sock *sk) +{ + /* Careful only use this in a context where these parameters + * can not change and must all be valid, such as recvmsg from + * userspace. + */ + return sk->sk_socket->file->f_cred->user_ns; +} + /* Sock flags */ enum sock_flags { SOCK_DEAD, -- cgit v1.2.3 From d06ca9564350184a19b5aae9ac150f1b1306de29 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 24 May 2012 17:58:08 -0600 Subject: userns: Teach inet_diag to work with user namespaces Compute the user namespace of the socket that we are replying to and translate the kuids of reported sockets into that user namespace. Cc: Andrew Vagin Acked-by: David S. Miller Acked-by: Pavel Emelyanov Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- include/linux/inet_diag.h | 1 + init/Kconfig | 1 - net/ipv4/inet_diag.c | 21 +++++++++++++++------ net/ipv4/udp_diag.c | 5 ++++- 4 files changed, 20 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/inet_diag.h b/include/linux/inet_diag.h index f1362b5447fc..e788c186ed3a 100644 --- a/include/linux/inet_diag.h +++ b/include/linux/inet_diag.h @@ -159,6 +159,7 @@ struct inet_diag_handler { struct inet_connection_sock; int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, struct sk_buff *skb, struct inet_diag_req_v2 *req, + struct user_namespace *user_ns, u32 pid, u32 seq, u16 nlmsg_flags, const struct nlmsghdr *unlh); void inet_diag_dump_icsk(struct inet_hashinfo *h, struct sk_buff *skb, diff --git a/init/Kconfig b/init/Kconfig index 8447e0ca4186..07435e0c3a4b 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -950,7 +950,6 @@ config UIDGID_CONVERTED depends on NETFILTER_NETLINK_LOG = n depends on AF_RXRPC = n depends on NET_KEY = n - depends on INET_DIAG = n depends on DNS_RESOLVER = n # Filesystems diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 570e61f9611f..8bc005b1435f 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -69,6 +69,7 @@ static inline void inet_diag_unlock_handler( int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, struct sk_buff *skb, struct inet_diag_req_v2 *req, + struct user_namespace *user_ns, u32 pid, u32 seq, u16 nlmsg_flags, const struct nlmsghdr *unlh) { @@ -124,7 +125,7 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, } #endif - r->idiag_uid = sock_i_uid(sk); + r->idiag_uid = from_kuid_munged(user_ns, sock_i_uid(sk)); r->idiag_inode = sock_i_ino(sk); if (ext & (1 << (INET_DIAG_MEMINFO - 1))) { @@ -199,11 +200,12 @@ EXPORT_SYMBOL_GPL(inet_sk_diag_fill); static int inet_csk_diag_fill(struct sock *sk, struct sk_buff *skb, struct inet_diag_req_v2 *req, + struct user_namespace *user_ns, u32 pid, u32 seq, u16 nlmsg_flags, const struct nlmsghdr *unlh) { return inet_sk_diag_fill(sk, inet_csk(sk), - skb, req, pid, seq, nlmsg_flags, unlh); + skb, req, user_ns, pid, seq, nlmsg_flags, unlh); } static int inet_twsk_diag_fill(struct inet_timewait_sock *tw, @@ -256,14 +258,16 @@ static int inet_twsk_diag_fill(struct inet_timewait_sock *tw, } static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, - struct inet_diag_req_v2 *r, u32 pid, u32 seq, u16 nlmsg_flags, + struct inet_diag_req_v2 *r, + struct user_namespace *user_ns, + u32 pid, u32 seq, u16 nlmsg_flags, const struct nlmsghdr *unlh) { if (sk->sk_state == TCP_TIME_WAIT) return inet_twsk_diag_fill((struct inet_timewait_sock *)sk, skb, r, pid, seq, nlmsg_flags, unlh); - return inet_csk_diag_fill(sk, skb, r, pid, seq, nlmsg_flags, unlh); + return inet_csk_diag_fill(sk, skb, r, user_ns, pid, seq, nlmsg_flags, unlh); } int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *in_skb, @@ -311,6 +315,7 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *in_s } err = sk_diag_fill(sk, rep, req, + sk_user_ns(NETLINK_CB(in_skb).ssk), NETLINK_CB(in_skb).pid, nlh->nlmsg_seq, 0, nlh); if (err < 0) { @@ -551,6 +556,7 @@ static int inet_csk_diag_dump(struct sock *sk, return 0; return inet_csk_diag_fill(sk, skb, r, + sk_user_ns(NETLINK_CB(cb->skb).ssk), NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh); } @@ -591,7 +597,9 @@ static int inet_twsk_diag_dump(struct inet_timewait_sock *tw, } static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, - struct request_sock *req, u32 pid, u32 seq, + struct request_sock *req, + struct user_namespace *user_ns, + u32 pid, u32 seq, const struct nlmsghdr *unlh) { const struct inet_request_sock *ireq = inet_rsk(req); @@ -625,7 +633,7 @@ static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, r->idiag_expires = jiffies_to_msecs(tmo); r->idiag_rqueue = 0; r->idiag_wqueue = 0; - r->idiag_uid = sock_i_uid(sk); + r->idiag_uid = from_kuid_munged(user_ns, sock_i_uid(sk)); r->idiag_inode = 0; #if IS_ENABLED(CONFIG_IPV6) if (r->idiag_family == AF_INET6) { @@ -702,6 +710,7 @@ static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk, } err = inet_diag_fill_req(skb, sk, req, + sk_user_ns(NETLINK_CB(cb->skb).ssk), NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, cb->nlh); if (err < 0) { diff --git a/net/ipv4/udp_diag.c b/net/ipv4/udp_diag.c index 16d0960062be..d2f336ea82ca 100644 --- a/net/ipv4/udp_diag.c +++ b/net/ipv4/udp_diag.c @@ -24,7 +24,9 @@ static int sk_diag_dump(struct sock *sk, struct sk_buff *skb, if (!inet_diag_bc_sk(bc, sk)) return 0; - return inet_sk_diag_fill(sk, NULL, skb, req, NETLINK_CB(cb->skb).pid, + return inet_sk_diag_fill(sk, NULL, skb, req, + sk_user_ns(NETLINK_CB(cb->skb).ssk), + NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh); } @@ -69,6 +71,7 @@ static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb, goto out; err = inet_sk_diag_fill(sk, NULL, rep, req, + sk_user_ns(NETLINK_CB(in_skb).ssk), NETLINK_CB(in_skb).pid, nlh->nlmsg_seq, 0, nlh); if (err < 0) { -- cgit v1.2.3 From af4c6641f5ad445fe6d0832da42406dbd9a37ce4 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 25 May 2012 13:42:45 -0600 Subject: net sched: Pass the skb into change so it can access NETLINK_CB cls_flow.c plays with uids and gids. Unless I misread that code it is possible for classifiers to depend on the specific uid and gid values. Therefore I need to know the user namespace of the netlink socket that is installing the packet classifiers. Pass in the rtnetlink skb so I can access the NETLINK_CB of the passed packet. In particular I want access to sk_user_ns(NETLINK_CB(in_skb).ssk). Pass in not the user namespace but the incomming rtnetlink skb into the the classifier change routines as that is generally the more useful parameter. Cc: Jamal Hadi Salim Acked-by: David S. Miller Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- include/net/sch_generic.h | 3 ++- net/sched/cls_api.c | 2 +- net/sched/cls_basic.c | 3 ++- net/sched/cls_cgroup.c | 3 ++- net/sched/cls_flow.c | 3 ++- net/sched/cls_fw.c | 3 ++- net/sched/cls_route.c | 3 ++- net/sched/cls_rsvp.h | 3 ++- net/sched/cls_tcindex.c | 3 ++- net/sched/cls_u32.c | 3 ++- 10 files changed, 19 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index d9611e032418..4616f468d599 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -188,7 +188,8 @@ struct tcf_proto_ops { unsigned long (*get)(struct tcf_proto*, u32 handle); void (*put)(struct tcf_proto*, unsigned long); - int (*change)(struct tcf_proto*, unsigned long, + int (*change)(struct sk_buff *, + struct tcf_proto*, unsigned long, u32 handle, struct nlattr **, unsigned long *); int (*delete)(struct tcf_proto*, unsigned long); diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 6dd1131f2ec1..dc3ef5aef355 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -319,7 +319,7 @@ replay: } } - err = tp->ops->change(tp, cl, t->tcm_handle, tca, &fh); + err = tp->ops->change(skb, tp, cl, t->tcm_handle, tca, &fh); if (err == 0) { if (tp_created) { spin_lock_bh(root_lock); diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c index 590960a22a77..344a11b342e5 100644 --- a/net/sched/cls_basic.c +++ b/net/sched/cls_basic.c @@ -162,7 +162,8 @@ errout: return err; } -static int basic_change(struct tcf_proto *tp, unsigned long base, u32 handle, +static int basic_change(struct sk_buff *in_skb, + struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, unsigned long *arg) { int err; diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index 7743ea8d1d38..91de66695b4a 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c @@ -151,7 +151,8 @@ static const struct nla_policy cgroup_policy[TCA_CGROUP_MAX + 1] = { [TCA_CGROUP_EMATCHES] = { .type = NLA_NESTED }, }; -static int cls_cgroup_change(struct tcf_proto *tp, unsigned long base, +static int cls_cgroup_change(struct sk_buff *in_skb, + struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, unsigned long *arg) { diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index ccd08c8dc6a7..ae854f3434b0 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -347,7 +347,8 @@ static const struct nla_policy flow_policy[TCA_FLOW_MAX + 1] = { [TCA_FLOW_PERTURB] = { .type = NLA_U32 }, }; -static int flow_change(struct tcf_proto *tp, unsigned long base, +static int flow_change(struct sk_buff *in_skb, + struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, unsigned long *arg) { diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index 8384a4797240..4075a0aef2aa 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -233,7 +233,8 @@ errout: return err; } -static int fw_change(struct tcf_proto *tp, unsigned long base, +static int fw_change(struct sk_buff *in_skb, + struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, unsigned long *arg) diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index 44f405cb9aaf..c10d57bf98f2 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -427,7 +427,8 @@ errout: return err; } -static int route4_change(struct tcf_proto *tp, unsigned long base, +static int route4_change(struct sk_buff *in_skb, + struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, unsigned long *arg) diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h index 18ab93ec8d7e..494bbb90924a 100644 --- a/net/sched/cls_rsvp.h +++ b/net/sched/cls_rsvp.h @@ -416,7 +416,8 @@ static const struct nla_policy rsvp_policy[TCA_RSVP_MAX + 1] = { [TCA_RSVP_PINFO] = { .len = sizeof(struct tc_rsvp_pinfo) }, }; -static int rsvp_change(struct tcf_proto *tp, unsigned long base, +static int rsvp_change(struct sk_buff *in_skb, + struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, unsigned long *arg) diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c index fe29420d0b0e..a1293b4ab7a1 100644 --- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c @@ -332,7 +332,8 @@ errout: } static int -tcindex_change(struct tcf_proto *tp, unsigned long base, u32 handle, +tcindex_change(struct sk_buff *in_skb, + struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, unsigned long *arg) { struct nlattr *opt = tca[TCA_OPTIONS]; diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index d45373fb00b9..c7c27bc91b5a 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -544,7 +544,8 @@ errout: return err; } -static int u32_change(struct tcf_proto *tp, unsigned long base, u32 handle, +static int u32_change(struct sk_buff *in_skb, + struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, unsigned long *arg) { -- cgit v1.2.3 From f1f4376307ca45558eb22487022aefceed7385e8 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 6 Aug 2012 08:39:38 +0000 Subject: sctp: Make the port hash table use struct net in it's key. - Add struct net into the port hash table hash calculation - Add struct net inot the struct sctp_bind_bucket so there is a memory of which network namespace a port is allocated in. No need for a ref count because sctp_bind_bucket only exists when there are sockets in the hash table and sockets can not change their network namspace, and sockets already ref count their network namespace. - Add struct net into the key comparison when we are testing to see if we have found the port hash table entry we are looking for. With these changes lookups in the port hash table becomes safe to use in multiple network namespaces. Signed-off-by: "Eric W. Biederman" Acked-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 4 ++-- include/net/sctp/structs.h | 1 + net/sctp/socket.c | 22 +++++++++++++--------- 3 files changed, 16 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index ff499640528b..7c0504034583 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -632,9 +632,9 @@ static inline int sctp_sanity_check(void) /* Warning: The following hash functions assume a power of two 'size'. */ /* This is the hash function for the SCTP port hash table. */ -static inline int sctp_phashfn(__u16 lport) +static inline int sctp_phashfn(struct net *net, __u16 lport) { - return lport & (sctp_port_hashsize - 1); + return (net_hash_mix(net) + lport) & (sctp_port_hashsize - 1); } /* This is the hash function for the endpoint hash table. */ diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index fc5e60016e37..c089bb12af77 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -102,6 +102,7 @@ struct sctp_bind_bucket { unsigned short fastreuse; struct hlist_node node; struct hlist_head owner; + struct net *net; }; struct sctp_bind_hashbucket { diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 5e259817a7f3..4316b0f988d4 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -5769,7 +5769,7 @@ static void sctp_unhash(struct sock *sk) * a fastreuse flag (FIXME: NPI ipg). */ static struct sctp_bind_bucket *sctp_bucket_create( - struct sctp_bind_hashbucket *head, unsigned short snum); + struct sctp_bind_hashbucket *head, struct net *, unsigned short snum); static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) { @@ -5799,11 +5799,12 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) rover = low; if (inet_is_reserved_local_port(rover)) continue; - index = sctp_phashfn(rover); + index = sctp_phashfn(sock_net(sk), rover); head = &sctp_port_hashtable[index]; sctp_spin_lock(&head->lock); sctp_for_each_hentry(pp, node, &head->chain) - if (pp->port == rover) + if ((pp->port == rover) && + net_eq(sock_net(sk), pp->net)) goto next; break; next: @@ -5827,10 +5828,10 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) * to the port number (snum) - we detect that with the * port iterator, pp being NULL. */ - head = &sctp_port_hashtable[sctp_phashfn(snum)]; + head = &sctp_port_hashtable[sctp_phashfn(sock_net(sk), snum)]; sctp_spin_lock(&head->lock); sctp_for_each_hentry(pp, node, &head->chain) { - if (pp->port == snum) + if ((pp->port == snum) && net_eq(pp->net, sock_net(sk))) goto pp_found; } } @@ -5881,7 +5882,7 @@ pp_found: pp_not_found: /* If there was a hash table miss, create a new port. */ ret = 1; - if (!pp && !(pp = sctp_bucket_create(head, snum))) + if (!pp && !(pp = sctp_bucket_create(head, sock_net(sk), snum))) goto fail_unlock; /* In either case (hit or miss), make sure fastreuse is 1 only @@ -6113,7 +6114,7 @@ unsigned int sctp_poll(struct file *file, struct socket *sock, poll_table *wait) ********************************************************************/ static struct sctp_bind_bucket *sctp_bucket_create( - struct sctp_bind_hashbucket *head, unsigned short snum) + struct sctp_bind_hashbucket *head, struct net *net, unsigned short snum) { struct sctp_bind_bucket *pp; @@ -6123,6 +6124,7 @@ static struct sctp_bind_bucket *sctp_bucket_create( pp->port = snum; pp->fastreuse = 0; INIT_HLIST_HEAD(&pp->owner); + pp->net = net; hlist_add_head(&pp->node, &head->chain); } return pp; @@ -6142,7 +6144,8 @@ static void sctp_bucket_destroy(struct sctp_bind_bucket *pp) static inline void __sctp_put_port(struct sock *sk) { struct sctp_bind_hashbucket *head = - &sctp_port_hashtable[sctp_phashfn(inet_sk(sk)->inet_num)]; + &sctp_port_hashtable[sctp_phashfn(sock_net(sk), + inet_sk(sk)->inet_num)]; struct sctp_bind_bucket *pp; sctp_spin_lock(&head->lock); @@ -6809,7 +6812,8 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, newsp->hmac = NULL; /* Hook this new socket in to the bind_hash list. */ - head = &sctp_port_hashtable[sctp_phashfn(inet_sk(oldsk)->inet_num)]; + head = &sctp_port_hashtable[sctp_phashfn(sock_net(oldsk), + inet_sk(oldsk)->inet_num)]; sctp_local_bh_disable(); sctp_spin_lock(&head->lock); pp = sctp_sk(oldsk)->bind_hash; -- cgit v1.2.3 From 4cdadcbcb64bdf3ae8bdf3ef5bb2b91c85444cfa Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 6 Aug 2012 08:40:21 +0000 Subject: sctp: Make the endpoint hashtable handle multiple network namespaces - Use struct net in the hash calculation - Use sock_net(endpoint.base.sk) in the endpoint lookups. - On receive calculate the network namespace from skb->dev. Signed-off-by: "Eric W. Biederman" Acked-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 4 ++-- include/net/sctp/structs.h | 2 +- net/sctp/endpointola.c | 4 +++- net/sctp/input.c | 19 ++++++++++++------- 4 files changed, 18 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 7c0504034583..87b119f74c4a 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -638,9 +638,9 @@ static inline int sctp_phashfn(struct net *net, __u16 lport) } /* This is the hash function for the endpoint hash table. */ -static inline int sctp_ep_hashfn(__u16 lport) +static inline int sctp_ep_hashfn(struct net *net, __u16 lport) { - return lport & (sctp_ep_hashsize - 1); + return (net_hash_mix(net) + lport) & (sctp_ep_hashsize - 1); } /* This is the hash function for the association hash table. */ diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index c089bb12af77..9f9de558541f 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1426,7 +1426,7 @@ struct sctp_association *sctp_endpoint_lookup_assoc( int sctp_endpoint_is_peeled_off(struct sctp_endpoint *, const union sctp_addr *); struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *, - const union sctp_addr *); + struct net *, const union sctp_addr *); int sctp_has_association(const union sctp_addr *laddr, const union sctp_addr *paddr); diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index 68a385d7c3bd..50c87b4ad765 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -302,11 +302,13 @@ void sctp_endpoint_put(struct sctp_endpoint *ep) /* Is this the endpoint we are looking for? */ struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *ep, + struct net *net, const union sctp_addr *laddr) { struct sctp_endpoint *retval = NULL; - if (htons(ep->base.bind_addr.port) == laddr->v4.sin_port) { + if ((htons(ep->base.bind_addr.port) == laddr->v4.sin_port) && + net_eq(sock_net(ep->base.sk), net)) { if (sctp_bind_addr_match(&ep->base.bind_addr, laddr, sctp_sk(ep->base.sk))) retval = ep; diff --git a/net/sctp/input.c b/net/sctp/input.c index e64d5210ed13..c0ca893ab1d1 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -70,7 +70,8 @@ static struct sctp_association *__sctp_rcv_lookup(struct sk_buff *skb, const union sctp_addr *laddr, const union sctp_addr *paddr, struct sctp_transport **transportp); -static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(const union sctp_addr *laddr); +static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(struct net *net, + const union sctp_addr *laddr); static struct sctp_association *__sctp_lookup_association( const union sctp_addr *local, const union sctp_addr *peer, @@ -129,6 +130,7 @@ int sctp_rcv(struct sk_buff *skb) union sctp_addr dest; int family; struct sctp_af *af; + struct net *net = dev_net(skb->dev); if (skb->pkt_type!=PACKET_HOST) goto discard_it; @@ -181,7 +183,7 @@ int sctp_rcv(struct sk_buff *skb) asoc = __sctp_rcv_lookup(skb, &src, &dest, &transport); if (!asoc) - ep = __sctp_rcv_lookup_endpoint(&dest); + ep = __sctp_rcv_lookup_endpoint(net, &dest); /* Retrieve the common input handling substructure. */ rcvr = asoc ? &asoc->base : &ep->base; @@ -723,12 +725,13 @@ discard: /* Insert endpoint into the hash table. */ static void __sctp_hash_endpoint(struct sctp_endpoint *ep) { + struct net *net = sock_net(ep->base.sk); struct sctp_ep_common *epb; struct sctp_hashbucket *head; epb = &ep->base; - epb->hashent = sctp_ep_hashfn(epb->bind_addr.port); + epb->hashent = sctp_ep_hashfn(net, epb->bind_addr.port); head = &sctp_ep_hashtable[epb->hashent]; sctp_write_lock(&head->lock); @@ -747,12 +750,13 @@ void sctp_hash_endpoint(struct sctp_endpoint *ep) /* Remove endpoint from the hash table. */ static void __sctp_unhash_endpoint(struct sctp_endpoint *ep) { + struct net *net = sock_net(ep->base.sk); struct sctp_hashbucket *head; struct sctp_ep_common *epb; epb = &ep->base; - epb->hashent = sctp_ep_hashfn(epb->bind_addr.port); + epb->hashent = sctp_ep_hashfn(net, epb->bind_addr.port); head = &sctp_ep_hashtable[epb->hashent]; @@ -770,7 +774,8 @@ void sctp_unhash_endpoint(struct sctp_endpoint *ep) } /* Look up an endpoint. */ -static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(const union sctp_addr *laddr) +static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(struct net *net, + const union sctp_addr *laddr) { struct sctp_hashbucket *head; struct sctp_ep_common *epb; @@ -778,12 +783,12 @@ static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(const union sctp_addr *l struct hlist_node *node; int hash; - hash = sctp_ep_hashfn(ntohs(laddr->v4.sin_port)); + hash = sctp_ep_hashfn(net, ntohs(laddr->v4.sin_port)); head = &sctp_ep_hashtable[hash]; read_lock(&head->lock); sctp_for_each_hentry(epb, node, &head->chain) { ep = sctp_ep(epb); - if (sctp_endpoint_is_match(ep, laddr)) + if (sctp_endpoint_is_match(ep, net, laddr)) goto hit; } -- cgit v1.2.3 From 4110cc255ddec59c79fba4d71cdd948d0a382140 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 6 Aug 2012 08:41:13 +0000 Subject: sctp: Make the association hashtable handle multiple network namespaces - Use struct net in the hash calculation - Use sock_net(association.base.sk) in the association lookups. - On receive calculate the network namespace from skb->dev. - Pass struct net from receive down to the functions that actually do the association lookup. Signed-off-by: "Eric W. Biederman" Acked-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 6 ++--- include/net/sctp/structs.h | 3 ++- net/sctp/associola.c | 4 ++- net/sctp/endpointola.c | 6 +++-- net/sctp/input.c | 64 +++++++++++++++++++++++++++++----------------- net/sctp/ipv6.c | 3 ++- 6 files changed, 54 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 87b119f74c4a..640915a0613d 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -156,7 +156,7 @@ void sctp_hash_established(struct sctp_association *); void sctp_unhash_established(struct sctp_association *); void sctp_hash_endpoint(struct sctp_endpoint *); void sctp_unhash_endpoint(struct sctp_endpoint *); -struct sock *sctp_err_lookup(int family, struct sk_buff *, +struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *, struct sctphdr *, struct sctp_association **, struct sctp_transport **); void sctp_err_finish(struct sock *, struct sctp_association *); @@ -644,9 +644,9 @@ static inline int sctp_ep_hashfn(struct net *net, __u16 lport) } /* This is the hash function for the association hash table. */ -static inline int sctp_assoc_hashfn(__u16 lport, __u16 rport) +static inline int sctp_assoc_hashfn(struct net *net, __u16 lport, __u16 rport) { - int h = (lport << 16) + rport; + int h = (lport << 16) + rport + net_hash_mix(net); h ^= h>>8; return h & (sctp_assoc_hashsize - 1); } diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 9f9de558541f..c0563d1dd7c7 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1427,7 +1427,7 @@ int sctp_endpoint_is_peeled_off(struct sctp_endpoint *, const union sctp_addr *); struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *, struct net *, const union sctp_addr *); -int sctp_has_association(const union sctp_addr *laddr, +int sctp_has_association(struct net *net, const union sctp_addr *laddr, const union sctp_addr *paddr); int sctp_verify_init(const struct sctp_association *asoc, sctp_cid_t, @@ -2014,6 +2014,7 @@ void sctp_assoc_control_transport(struct sctp_association *, sctp_transport_cmd_t, sctp_sn_error_t); struct sctp_transport *sctp_assoc_lookup_tsn(struct sctp_association *, __u32); struct sctp_transport *sctp_assoc_is_match(struct sctp_association *, + struct net *, const union sctp_addr *, const union sctp_addr *); void sctp_assoc_migrate(struct sctp_association *, struct sock *); diff --git a/net/sctp/associola.c b/net/sctp/associola.c index ebaef3ed6065..a3601f35ac15 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -1089,13 +1089,15 @@ out: /* Is this the association we are looking for? */ struct sctp_transport *sctp_assoc_is_match(struct sctp_association *asoc, + struct net *net, const union sctp_addr *laddr, const union sctp_addr *paddr) { struct sctp_transport *transport; if ((htons(asoc->base.bind_addr.port) == laddr->v4.sin_port) && - (htons(asoc->peer.port) == paddr->v4.sin_port)) { + (htons(asoc->peer.port) == paddr->v4.sin_port) && + net_eq(sock_net(asoc->base.sk), net)) { transport = sctp_assoc_lookup_paddr(asoc, paddr); if (!transport) goto out; diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index 50c87b4ad765..6b763939345b 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -345,7 +345,8 @@ static struct sctp_association *__sctp_endpoint_lookup_assoc( rport = ntohs(paddr->v4.sin_port); - hash = sctp_assoc_hashfn(ep->base.bind_addr.port, rport); + hash = sctp_assoc_hashfn(sock_net(ep->base.sk), ep->base.bind_addr.port, + rport); head = &sctp_assoc_hashtable[hash]; read_lock(&head->lock); sctp_for_each_hentry(epb, node, &head->chain) { @@ -388,13 +389,14 @@ int sctp_endpoint_is_peeled_off(struct sctp_endpoint *ep, { struct sctp_sockaddr_entry *addr; struct sctp_bind_addr *bp; + struct net *net = sock_net(ep->base.sk); bp = &ep->base.bind_addr; /* This function is called with the socket lock held, * so the address_list can not change. */ list_for_each_entry(addr, &bp->address_list, list) { - if (sctp_has_association(&addr->a, paddr)) + if (sctp_has_association(net, &addr->a, paddr)) return 1; } diff --git a/net/sctp/input.c b/net/sctp/input.c index c0ca893ab1d1..a7e9a85b5acb 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -66,13 +66,15 @@ /* Forward declarations for internal helpers. */ static int sctp_rcv_ootb(struct sk_buff *); -static struct sctp_association *__sctp_rcv_lookup(struct sk_buff *skb, +static struct sctp_association *__sctp_rcv_lookup(struct net *net, + struct sk_buff *skb, const union sctp_addr *laddr, const union sctp_addr *paddr, struct sctp_transport **transportp); static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(struct net *net, const union sctp_addr *laddr); static struct sctp_association *__sctp_lookup_association( + struct net *net, const union sctp_addr *local, const union sctp_addr *peer, struct sctp_transport **pt); @@ -180,7 +182,7 @@ int sctp_rcv(struct sk_buff *skb) !af->addr_valid(&dest, NULL, skb)) goto discard_it; - asoc = __sctp_rcv_lookup(skb, &src, &dest, &transport); + asoc = __sctp_rcv_lookup(net, skb, &src, &dest, &transport); if (!asoc) ep = __sctp_rcv_lookup_endpoint(net, &dest); @@ -476,7 +478,7 @@ void sctp_icmp_proto_unreachable(struct sock *sk, } /* Common lookup code for icmp/icmpv6 error handler. */ -struct sock *sctp_err_lookup(int family, struct sk_buff *skb, +struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *skb, struct sctphdr *sctphdr, struct sctp_association **app, struct sctp_transport **tpp) @@ -505,7 +507,7 @@ struct sock *sctp_err_lookup(int family, struct sk_buff *skb, /* Look for an association that matches the incoming ICMP error * packet. */ - asoc = __sctp_lookup_association(&saddr, &daddr, &transport); + asoc = __sctp_lookup_association(net, &saddr, &daddr, &transport); if (!asoc) return NULL; @@ -588,6 +590,7 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info) struct inet_sock *inet; sk_buff_data_t saveip, savesctp; int err; + struct net *net = dev_net(skb->dev); if (skb->len < ihlen + 8) { ICMP_INC_STATS_BH(&init_net, ICMP_MIB_INERRORS); @@ -599,7 +602,7 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info) savesctp = skb->transport_header; skb_reset_network_header(skb); skb_set_transport_header(skb, ihlen); - sk = sctp_err_lookup(AF_INET, skb, sctp_hdr(skb), &asoc, &transport); + sk = sctp_err_lookup(net, AF_INET, skb, sctp_hdr(skb), &asoc, &transport); /* Put back, the original values. */ skb->network_header = saveip; skb->transport_header = savesctp; @@ -803,13 +806,15 @@ hit: /* Insert association into the hash table. */ static void __sctp_hash_established(struct sctp_association *asoc) { + struct net *net = sock_net(asoc->base.sk); struct sctp_ep_common *epb; struct sctp_hashbucket *head; epb = &asoc->base; /* Calculate which chain this entry will belong to. */ - epb->hashent = sctp_assoc_hashfn(epb->bind_addr.port, asoc->peer.port); + epb->hashent = sctp_assoc_hashfn(net, epb->bind_addr.port, + asoc->peer.port); head = &sctp_assoc_hashtable[epb->hashent]; @@ -832,12 +837,13 @@ void sctp_hash_established(struct sctp_association *asoc) /* Remove association from the hash table. */ static void __sctp_unhash_established(struct sctp_association *asoc) { + struct net *net = sock_net(asoc->base.sk); struct sctp_hashbucket *head; struct sctp_ep_common *epb; epb = &asoc->base; - epb->hashent = sctp_assoc_hashfn(epb->bind_addr.port, + epb->hashent = sctp_assoc_hashfn(net, epb->bind_addr.port, asoc->peer.port); head = &sctp_assoc_hashtable[epb->hashent]; @@ -860,6 +866,7 @@ void sctp_unhash_established(struct sctp_association *asoc) /* Look up an association. */ static struct sctp_association *__sctp_lookup_association( + struct net *net, const union sctp_addr *local, const union sctp_addr *peer, struct sctp_transport **pt) @@ -874,12 +881,13 @@ static struct sctp_association *__sctp_lookup_association( /* Optimize here for direct hit, only listening connections can * have wildcards anyways. */ - hash = sctp_assoc_hashfn(ntohs(local->v4.sin_port), ntohs(peer->v4.sin_port)); + hash = sctp_assoc_hashfn(net, ntohs(local->v4.sin_port), + ntohs(peer->v4.sin_port)); head = &sctp_assoc_hashtable[hash]; read_lock(&head->lock); sctp_for_each_hentry(epb, node, &head->chain) { asoc = sctp_assoc(epb); - transport = sctp_assoc_is_match(asoc, local, peer); + transport = sctp_assoc_is_match(asoc, net, local, peer); if (transport) goto hit; } @@ -897,27 +905,29 @@ hit: /* Look up an association. BH-safe. */ SCTP_STATIC -struct sctp_association *sctp_lookup_association(const union sctp_addr *laddr, +struct sctp_association *sctp_lookup_association(struct net *net, + const union sctp_addr *laddr, const union sctp_addr *paddr, struct sctp_transport **transportp) { struct sctp_association *asoc; sctp_local_bh_disable(); - asoc = __sctp_lookup_association(laddr, paddr, transportp); + asoc = __sctp_lookup_association(net, laddr, paddr, transportp); sctp_local_bh_enable(); return asoc; } /* Is there an association matching the given local and peer addresses? */ -int sctp_has_association(const union sctp_addr *laddr, +int sctp_has_association(struct net *net, + const union sctp_addr *laddr, const union sctp_addr *paddr) { struct sctp_association *asoc; struct sctp_transport *transport; - if ((asoc = sctp_lookup_association(laddr, paddr, &transport))) { + if ((asoc = sctp_lookup_association(net, laddr, paddr, &transport))) { sctp_association_put(asoc); return 1; } @@ -943,7 +953,8 @@ int sctp_has_association(const union sctp_addr *laddr, * in certain circumstances. * */ -static struct sctp_association *__sctp_rcv_init_lookup(struct sk_buff *skb, +static struct sctp_association *__sctp_rcv_init_lookup(struct net *net, + struct sk_buff *skb, const union sctp_addr *laddr, struct sctp_transport **transportp) { struct sctp_association *asoc; @@ -983,7 +994,7 @@ static struct sctp_association *__sctp_rcv_init_lookup(struct sk_buff *skb, af->from_addr_param(paddr, params.addr, sh->source, 0); - asoc = __sctp_lookup_association(laddr, paddr, &transport); + asoc = __sctp_lookup_association(net, laddr, paddr, &transport); if (asoc) return asoc; } @@ -1006,6 +1017,7 @@ static struct sctp_association *__sctp_rcv_init_lookup(struct sk_buff *skb, * subsequent ASCONF Chunks. If found, proceed to rule D4. */ static struct sctp_association *__sctp_rcv_asconf_lookup( + struct net *net, sctp_chunkhdr_t *ch, const union sctp_addr *laddr, __be16 peer_port, @@ -1025,7 +1037,7 @@ static struct sctp_association *__sctp_rcv_asconf_lookup( af->from_addr_param(&paddr, param, peer_port, 0); - return __sctp_lookup_association(laddr, &paddr, transportp); + return __sctp_lookup_association(net, laddr, &paddr, transportp); } @@ -1038,7 +1050,8 @@ static struct sctp_association *__sctp_rcv_asconf_lookup( * This means that any chunks that can help us identify the association need * to be looked at to find this association. */ -static struct sctp_association *__sctp_rcv_walk_lookup(struct sk_buff *skb, +static struct sctp_association *__sctp_rcv_walk_lookup(struct net *net, + struct sk_buff *skb, const union sctp_addr *laddr, struct sctp_transport **transportp) { @@ -1080,7 +1093,8 @@ static struct sctp_association *__sctp_rcv_walk_lookup(struct sk_buff *skb, case SCTP_CID_ASCONF: if (have_auth || sctp_addip_noauth) - asoc = __sctp_rcv_asconf_lookup(ch, laddr, + asoc = __sctp_rcv_asconf_lookup( + net, ch, laddr, sctp_hdr(skb)->source, transportp); default: @@ -1103,7 +1117,8 @@ static struct sctp_association *__sctp_rcv_walk_lookup(struct sk_buff *skb, * include looking inside of INIT/INIT-ACK chunks or after the AUTH * chunks. */ -static struct sctp_association *__sctp_rcv_lookup_harder(struct sk_buff *skb, +static struct sctp_association *__sctp_rcv_lookup_harder(struct net *net, + struct sk_buff *skb, const union sctp_addr *laddr, struct sctp_transport **transportp) { @@ -1123,11 +1138,11 @@ static struct sctp_association *__sctp_rcv_lookup_harder(struct sk_buff *skb, switch (ch->type) { case SCTP_CID_INIT: case SCTP_CID_INIT_ACK: - return __sctp_rcv_init_lookup(skb, laddr, transportp); + return __sctp_rcv_init_lookup(net, skb, laddr, transportp); break; default: - return __sctp_rcv_walk_lookup(skb, laddr, transportp); + return __sctp_rcv_walk_lookup(net, skb, laddr, transportp); break; } @@ -1136,21 +1151,22 @@ static struct sctp_association *__sctp_rcv_lookup_harder(struct sk_buff *skb, } /* Lookup an association for an inbound skb. */ -static struct sctp_association *__sctp_rcv_lookup(struct sk_buff *skb, +static struct sctp_association *__sctp_rcv_lookup(struct net *net, + struct sk_buff *skb, const union sctp_addr *paddr, const union sctp_addr *laddr, struct sctp_transport **transportp) { struct sctp_association *asoc; - asoc = __sctp_lookup_association(laddr, paddr, transportp); + asoc = __sctp_lookup_association(net, laddr, paddr, transportp); /* Further lookup for INIT/INIT-ACK packets. * SCTP Implementors Guide, 2.18 Handling of address * parameters within the INIT or INIT-ACK. */ if (!asoc) - asoc = __sctp_rcv_lookup_harder(skb, laddr, transportp); + asoc = __sctp_rcv_lookup_harder(net, skb, laddr, transportp); return asoc; } diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index ed7139ea7978..2165a7ed25f1 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -154,6 +154,7 @@ SCTP_STATIC void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, struct ipv6_pinfo *np; sk_buff_data_t saveip, savesctp; int err; + struct net *net = dev_net(skb->dev); idev = in6_dev_get(skb->dev); @@ -162,7 +163,7 @@ SCTP_STATIC void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, savesctp = skb->transport_header; skb_reset_network_header(skb); skb_set_transport_header(skb, offset); - sk = sctp_err_lookup(AF_INET6, skb, sctp_hdr(skb), &asoc, &transport); + sk = sctp_err_lookup(net, AF_INET6, skb, sctp_hdr(skb), &asoc, &transport); /* Put back, the original pointers. */ skb->network_header = saveip; skb->transport_header = savesctp; -- cgit v1.2.3 From 4db67e808640e3934d82ce61ee8e2e89fd877ba8 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 6 Aug 2012 08:42:04 +0000 Subject: sctp: Make the address lists per network namespace - Move the address lists into struct net - Add per network namespace initialization and cleanup - Pass around struct net so it is everywhere I need it. - Rename all of the global variable references into references to the variables moved into struct net Signed-off-by: "Eric W. Biederman" Acked-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/net/net_namespace.h | 4 ++ include/net/netns/sctp.h | 21 +++++++ include/net/sctp/sctp.h | 4 +- include/net/sctp/structs.h | 22 +------ net/sctp/associola.c | 3 +- net/sctp/bind_addr.c | 14 ++--- net/sctp/ipv6.c | 17 +++--- net/sctp/protocol.c | 143 +++++++++++++++++++++++++------------------- net/sctp/socket.c | 7 ++- 9 files changed, 132 insertions(+), 103 deletions(-) create mode 100644 include/net/netns/sctp.h (limited to 'include') diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 97e441945c13..5ae57f1ab755 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) @@ -81,6 +82,9 @@ struct net { #if IS_ENABLED(CONFIG_IPV6) struct netns_ipv6 ipv6; #endif +#if defined(CONFIG_IP_SCTP) || defined(CONFIG_IP_SCTP_MODULE) + struct netns_sctp sctp; +#endif #if defined(CONFIG_IP_DCCP) || defined(CONFIG_IP_DCCP_MODULE) struct netns_dccp dccp; #endif diff --git a/include/net/netns/sctp.h b/include/net/netns/sctp.h new file mode 100644 index 000000000000..cbd684e01cf7 --- /dev/null +++ b/include/net/netns/sctp.h @@ -0,0 +1,21 @@ +#ifndef __NETNS_SCTP_H__ +#define __NETNS_SCTP_H__ + +struct netns_sctp { + /* This is the global local address list. + * We actively maintain this complete list of addresses on + * the system by catching address add/delete events. + * + * It is a list of sctp_sockaddr_entry. + */ + struct list_head local_addr_list; + struct list_head addr_waitq; + struct timer_list addr_wq_timer; + struct list_head auto_asconf_splist; + spinlock_t addr_wq_lock; + + /* Lock that protects the local_addr_list writers */ + spinlock_t local_addr_lock; +}; + +#endif /* __NETNS_SCTP_H__ */ diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 640915a0613d..00c920537d4a 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -115,12 +115,12 @@ * sctp/protocol.c */ extern struct sock *sctp_get_ctl_sock(void); -extern int sctp_copy_local_addr_list(struct sctp_bind_addr *, +extern int sctp_copy_local_addr_list(struct net *, struct sctp_bind_addr *, sctp_scope_t, gfp_t gfp, int flags); extern struct sctp_pf *sctp_get_pf_specific(sa_family_t family); extern int sctp_register_pf(struct sctp_pf *, sa_family_t); -extern void sctp_addr_wq_mgmt(struct sctp_sockaddr_entry *, int); +extern void sctp_addr_wq_mgmt(struct net *, struct sctp_sockaddr_entry *, int); /* * sctp/socket.c diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index c0563d1dd7c7..6bdfcabe560e 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -205,21 +205,7 @@ extern struct sctp_globals { int port_hashsize; struct sctp_bind_hashbucket *port_hashtable; - /* This is the global local address list. - * We actively maintain this complete list of addresses on - * the system by catching address add/delete events. - * - * It is a list of sctp_sockaddr_entry. - */ - struct list_head local_addr_list; int default_auto_asconf; - struct list_head addr_waitq; - struct timer_list addr_wq_timer; - struct list_head auto_asconf_splist; - spinlock_t addr_wq_lock; - - /* Lock that protects the local_addr_list writers */ - spinlock_t addr_list_lock; /* Flag to indicate if addip is enabled. */ int addip_enable; @@ -278,12 +264,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_local_addr_list (sctp_globals.local_addr_list) -#define sctp_local_addr_lock (sctp_globals.addr_list_lock) -#define sctp_auto_asconf_splist (sctp_globals.auto_asconf_splist) -#define sctp_addr_waitq (sctp_globals.addr_waitq) -#define sctp_addr_wq_timer (sctp_globals.addr_wq_timer) -#define sctp_addr_wq_lock (sctp_globals.addr_wq_lock) #define sctp_default_auto_asconf (sctp_globals.default_auto_asconf) #define sctp_scope_policy (sctp_globals.ipv4_scope_policy) #define sctp_addip_enable (sctp_globals.addip_enable) @@ -1241,7 +1221,7 @@ struct sctp_bind_addr { void sctp_bind_addr_init(struct sctp_bind_addr *, __u16 port); void sctp_bind_addr_free(struct sctp_bind_addr *); -int sctp_bind_addr_copy(struct sctp_bind_addr *dest, +int sctp_bind_addr_copy(struct net *net, struct sctp_bind_addr *dest, const struct sctp_bind_addr *src, sctp_scope_t scope, gfp_t gfp, int flags); diff --git a/net/sctp/associola.c b/net/sctp/associola.c index a3601f35ac15..ed4930b31341 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -1544,7 +1544,8 @@ int sctp_assoc_set_bind_addr_from_ep(struct sctp_association *asoc, if (asoc->peer.ipv6_address) flags |= SCTP_ADDR6_PEERSUPP; - return sctp_bind_addr_copy(&asoc->base.bind_addr, + return sctp_bind_addr_copy(sock_net(asoc->base.sk), + &asoc->base.bind_addr, &asoc->ep->base.bind_addr, scope, gfp, flags); } diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index 4ece451c8d27..a85ce4b3e574 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c @@ -52,8 +52,8 @@ #include /* Forward declarations for internal helpers. */ -static int sctp_copy_one_addr(struct sctp_bind_addr *, union sctp_addr *, - sctp_scope_t scope, gfp_t gfp, +static int sctp_copy_one_addr(struct net *, struct sctp_bind_addr *, + union sctp_addr *, sctp_scope_t scope, gfp_t gfp, int flags); static void sctp_bind_addr_clean(struct sctp_bind_addr *); @@ -62,7 +62,7 @@ static void sctp_bind_addr_clean(struct sctp_bind_addr *); /* Copy 'src' to 'dest' taking 'scope' into account. Omit addresses * in 'src' which have a broader scope than 'scope'. */ -int sctp_bind_addr_copy(struct sctp_bind_addr *dest, +int sctp_bind_addr_copy(struct net *net, struct sctp_bind_addr *dest, const struct sctp_bind_addr *src, sctp_scope_t scope, gfp_t gfp, int flags) @@ -75,7 +75,7 @@ int sctp_bind_addr_copy(struct sctp_bind_addr *dest, /* Extract the addresses which are relevant for this scope. */ list_for_each_entry(addr, &src->address_list, list) { - error = sctp_copy_one_addr(dest, &addr->a, scope, + error = sctp_copy_one_addr(net, dest, &addr->a, scope, gfp, flags); if (error < 0) goto out; @@ -87,7 +87,7 @@ int sctp_bind_addr_copy(struct sctp_bind_addr *dest, */ if (list_empty(&dest->address_list) && (SCTP_SCOPE_GLOBAL == scope)) { list_for_each_entry(addr, &src->address_list, list) { - error = sctp_copy_one_addr(dest, &addr->a, + error = sctp_copy_one_addr(net, dest, &addr->a, SCTP_SCOPE_LINK, gfp, flags); if (error < 0) @@ -448,7 +448,7 @@ union sctp_addr *sctp_find_unmatch_addr(struct sctp_bind_addr *bp, } /* Copy out addresses from the global local address list. */ -static int sctp_copy_one_addr(struct sctp_bind_addr *dest, +static int sctp_copy_one_addr(struct net *net, struct sctp_bind_addr *dest, union sctp_addr *addr, sctp_scope_t scope, gfp_t gfp, int flags) @@ -456,7 +456,7 @@ static int sctp_copy_one_addr(struct sctp_bind_addr *dest, int error = 0; if (sctp_is_any(NULL, addr)) { - error = sctp_copy_local_addr_list(dest, scope, gfp, flags); + error = sctp_copy_local_addr_list(net, dest, scope, gfp, flags); } else if (sctp_in_scope(addr, scope)) { /* Now that the address is in scope, check to see if * the address type is supported by local sock as diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 2165a7ed25f1..bbf15341eb2b 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -99,6 +99,7 @@ static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev, struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr; struct sctp_sockaddr_entry *addr = NULL; struct sctp_sockaddr_entry *temp; + struct net *net = dev_net(ifa->idev->dev); int found = 0; switch (ev) { @@ -110,27 +111,27 @@ static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev, addr->a.v6.sin6_addr = ifa->addr; addr->a.v6.sin6_scope_id = ifa->idev->dev->ifindex; addr->valid = 1; - spin_lock_bh(&sctp_local_addr_lock); - list_add_tail_rcu(&addr->list, &sctp_local_addr_list); - sctp_addr_wq_mgmt(addr, SCTP_ADDR_NEW); - spin_unlock_bh(&sctp_local_addr_lock); + spin_lock_bh(&net->sctp.local_addr_lock); + list_add_tail_rcu(&addr->list, &net->sctp.local_addr_list); + sctp_addr_wq_mgmt(net, addr, SCTP_ADDR_NEW); + spin_unlock_bh(&net->sctp.local_addr_lock); } break; case NETDEV_DOWN: - spin_lock_bh(&sctp_local_addr_lock); + spin_lock_bh(&net->sctp.local_addr_lock); list_for_each_entry_safe(addr, temp, - &sctp_local_addr_list, list) { + &net->sctp.local_addr_list, list) { if (addr->a.sa.sa_family == AF_INET6 && ipv6_addr_equal(&addr->a.v6.sin6_addr, &ifa->addr)) { - sctp_addr_wq_mgmt(addr, SCTP_ADDR_DEL); + sctp_addr_wq_mgmt(net, addr, SCTP_ADDR_DEL); found = 1; addr->valid = 0; list_del_rcu(&addr->list); break; } } - spin_unlock_bh(&sctp_local_addr_lock); + spin_unlock_bh(&net->sctp.local_addr_lock); if (found) kfree_rcu(addr, rcu); break; diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 1f89c4e69645..7025d96bae5f 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -201,29 +201,29 @@ static void sctp_v4_copy_addrlist(struct list_head *addrlist, /* Extract our IP addresses from the system and stash them in the * protocol structure. */ -static void sctp_get_local_addr_list(void) +static void sctp_get_local_addr_list(struct net *net) { struct net_device *dev; struct list_head *pos; struct sctp_af *af; rcu_read_lock(); - for_each_netdev_rcu(&init_net, dev) { + for_each_netdev_rcu(net, dev) { __list_for_each(pos, &sctp_address_families) { af = list_entry(pos, struct sctp_af, list); - af->copy_addrlist(&sctp_local_addr_list, dev); + af->copy_addrlist(&net->sctp.local_addr_list, dev); } } rcu_read_unlock(); } /* Free the existing local addresses. */ -static void sctp_free_local_addr_list(void) +static void sctp_free_local_addr_list(struct net *net) { struct sctp_sockaddr_entry *addr; struct list_head *pos, *temp; - list_for_each_safe(pos, temp, &sctp_local_addr_list) { + list_for_each_safe(pos, temp, &net->sctp.local_addr_list) { addr = list_entry(pos, struct sctp_sockaddr_entry, list); list_del(pos); kfree(addr); @@ -231,14 +231,14 @@ static void sctp_free_local_addr_list(void) } /* Copy the local addresses which are valid for 'scope' into 'bp'. */ -int sctp_copy_local_addr_list(struct sctp_bind_addr *bp, sctp_scope_t scope, - gfp_t gfp, int copy_flags) +int sctp_copy_local_addr_list(struct net *net, struct sctp_bind_addr *bp, + sctp_scope_t scope, gfp_t gfp, int copy_flags) { struct sctp_sockaddr_entry *addr; int error = 0; rcu_read_lock(); - list_for_each_entry_rcu(addr, &sctp_local_addr_list, list) { + list_for_each_entry_rcu(addr, &net->sctp.local_addr_list, list) { if (!addr->valid) continue; if (sctp_in_scope(&addr->a, scope)) { @@ -627,14 +627,15 @@ static void sctp_v4_ecn_capable(struct sock *sk) void sctp_addr_wq_timeout_handler(unsigned long arg) { + struct net *net = (struct net *)arg; struct sctp_sockaddr_entry *addrw, *temp; struct sctp_sock *sp; - spin_lock_bh(&sctp_addr_wq_lock); + spin_lock_bh(&net->sctp.addr_wq_lock); - list_for_each_entry_safe(addrw, temp, &sctp_addr_waitq, list) { + list_for_each_entry_safe(addrw, temp, &net->sctp.addr_waitq, list) { SCTP_DEBUG_PRINTK_IPADDR("sctp_addrwq_timo_handler: the first ent in wq %p is ", - " for cmd %d at entry %p\n", &sctp_addr_waitq, &addrw->a, addrw->state, + " for cmd %d at entry %p\n", &net->sctp.addr_waitq, &addrw->a, addrw->state, addrw); #if IS_ENABLED(CONFIG_IPV6) @@ -648,7 +649,7 @@ void sctp_addr_wq_timeout_handler(unsigned long arg) goto free_next; in6 = (struct in6_addr *)&addrw->a.v6.sin6_addr; - if (ipv6_chk_addr(&init_net, in6, NULL, 0) == 0 && + if (ipv6_chk_addr(net, in6, NULL, 0) == 0 && addrw->state == SCTP_ADDR_NEW) { unsigned long timeo_val; @@ -656,12 +657,12 @@ void sctp_addr_wq_timeout_handler(unsigned long arg) SCTP_ADDRESS_TICK_DELAY); timeo_val = jiffies; timeo_val += msecs_to_jiffies(SCTP_ADDRESS_TICK_DELAY); - mod_timer(&sctp_addr_wq_timer, timeo_val); + mod_timer(&net->sctp.addr_wq_timer, timeo_val); break; } } #endif - list_for_each_entry(sp, &sctp_auto_asconf_splist, auto_asconf_list) { + list_for_each_entry(sp, &net->sctp.auto_asconf_splist, auto_asconf_list) { struct sock *sk; sk = sctp_opt2sk(sp); @@ -679,31 +680,32 @@ free_next: list_del(&addrw->list); kfree(addrw); } - spin_unlock_bh(&sctp_addr_wq_lock); + spin_unlock_bh(&net->sctp.addr_wq_lock); } -static void sctp_free_addr_wq(void) +static void sctp_free_addr_wq(struct net *net) { struct sctp_sockaddr_entry *addrw; struct sctp_sockaddr_entry *temp; - spin_lock_bh(&sctp_addr_wq_lock); - del_timer(&sctp_addr_wq_timer); - list_for_each_entry_safe(addrw, temp, &sctp_addr_waitq, list) { + spin_lock_bh(&net->sctp.addr_wq_lock); + del_timer(&net->sctp.addr_wq_timer); + list_for_each_entry_safe(addrw, temp, &net->sctp.addr_waitq, list) { list_del(&addrw->list); kfree(addrw); } - spin_unlock_bh(&sctp_addr_wq_lock); + spin_unlock_bh(&net->sctp.addr_wq_lock); } /* lookup the entry for the same address in the addr_waitq * sctp_addr_wq MUST be locked */ -static struct sctp_sockaddr_entry *sctp_addr_wq_lookup(struct sctp_sockaddr_entry *addr) +static struct sctp_sockaddr_entry *sctp_addr_wq_lookup(struct net *net, + struct sctp_sockaddr_entry *addr) { struct sctp_sockaddr_entry *addrw; - list_for_each_entry(addrw, &sctp_addr_waitq, list) { + list_for_each_entry(addrw, &net->sctp.addr_waitq, list) { if (addrw->a.sa.sa_family != addr->a.sa.sa_family) continue; if (addrw->a.sa.sa_family == AF_INET) { @@ -719,7 +721,7 @@ static struct sctp_sockaddr_entry *sctp_addr_wq_lookup(struct sctp_sockaddr_entr return NULL; } -void sctp_addr_wq_mgmt(struct sctp_sockaddr_entry *addr, int cmd) +void sctp_addr_wq_mgmt(struct net *net, struct sctp_sockaddr_entry *addr, int cmd) { struct sctp_sockaddr_entry *addrw; unsigned long timeo_val; @@ -730,38 +732,38 @@ void sctp_addr_wq_mgmt(struct sctp_sockaddr_entry *addr, int cmd) * new address after a couple of addition and deletion of that address */ - spin_lock_bh(&sctp_addr_wq_lock); + spin_lock_bh(&net->sctp.addr_wq_lock); /* Offsets existing events in addr_wq */ - addrw = sctp_addr_wq_lookup(addr); + addrw = sctp_addr_wq_lookup(net, addr); if (addrw) { if (addrw->state != cmd) { SCTP_DEBUG_PRINTK_IPADDR("sctp_addr_wq_mgmt offsets existing entry for %d ", " in wq %p\n", addrw->state, &addrw->a, - &sctp_addr_waitq); + &net->sctp.addr_waitq); list_del(&addrw->list); kfree(addrw); } - spin_unlock_bh(&sctp_addr_wq_lock); + spin_unlock_bh(&net->sctp.addr_wq_lock); return; } /* OK, we have to add the new address to the wait queue */ addrw = kmemdup(addr, sizeof(struct sctp_sockaddr_entry), GFP_ATOMIC); if (addrw == NULL) { - spin_unlock_bh(&sctp_addr_wq_lock); + spin_unlock_bh(&net->sctp.addr_wq_lock); return; } addrw->state = cmd; - list_add_tail(&addrw->list, &sctp_addr_waitq); + list_add_tail(&addrw->list, &net->sctp.addr_waitq); SCTP_DEBUG_PRINTK_IPADDR("sctp_addr_wq_mgmt add new entry for cmd:%d ", - " in wq %p\n", addrw->state, &addrw->a, &sctp_addr_waitq); + " in wq %p\n", addrw->state, &addrw->a, &net->sctp.addr_waitq); - if (!timer_pending(&sctp_addr_wq_timer)) { + if (!timer_pending(&net->sctp.addr_wq_timer)) { timeo_val = jiffies; timeo_val += msecs_to_jiffies(SCTP_ADDRESS_TICK_DELAY); - mod_timer(&sctp_addr_wq_timer, timeo_val); + mod_timer(&net->sctp.addr_wq_timer, timeo_val); } - spin_unlock_bh(&sctp_addr_wq_lock); + spin_unlock_bh(&net->sctp.addr_wq_lock); } /* Event handler for inet address addition/deletion events. @@ -776,11 +778,9 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev, struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; struct sctp_sockaddr_entry *addr = NULL; struct sctp_sockaddr_entry *temp; + struct net *net = dev_net(ifa->ifa_dev->dev); int found = 0; - if (!net_eq(dev_net(ifa->ifa_dev->dev), &init_net)) - return NOTIFY_DONE; - switch (ev) { case NETDEV_UP: addr = kmalloc(sizeof(struct sctp_sockaddr_entry), GFP_ATOMIC); @@ -789,27 +789,27 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev, addr->a.v4.sin_port = 0; addr->a.v4.sin_addr.s_addr = ifa->ifa_local; addr->valid = 1; - spin_lock_bh(&sctp_local_addr_lock); - list_add_tail_rcu(&addr->list, &sctp_local_addr_list); - sctp_addr_wq_mgmt(addr, SCTP_ADDR_NEW); - spin_unlock_bh(&sctp_local_addr_lock); + spin_lock_bh(&net->sctp.local_addr_lock); + list_add_tail_rcu(&addr->list, &net->sctp.local_addr_list); + sctp_addr_wq_mgmt(net, addr, SCTP_ADDR_NEW); + spin_unlock_bh(&net->sctp.local_addr_lock); } break; case NETDEV_DOWN: - spin_lock_bh(&sctp_local_addr_lock); + spin_lock_bh(&net->sctp.local_addr_lock); list_for_each_entry_safe(addr, temp, - &sctp_local_addr_list, list) { + &net->sctp.local_addr_list, list) { if (addr->a.sa.sa_family == AF_INET && addr->a.v4.sin_addr.s_addr == ifa->ifa_local) { - sctp_addr_wq_mgmt(addr, SCTP_ADDR_DEL); + sctp_addr_wq_mgmt(net, addr, SCTP_ADDR_DEL); found = 1; addr->valid = 0; list_del_rcu(&addr->list); break; } } - spin_unlock_bh(&sctp_local_addr_lock); + spin_unlock_bh(&net->sctp.local_addr_lock); if (found) kfree_rcu(addr, rcu); break; @@ -1194,6 +1194,36 @@ static void sctp_v4_del_protocol(void) unregister_inetaddr_notifier(&sctp_inetaddr_notifier); } +static int sctp_net_init(struct net *net) +{ + /* Initialize the local address list. */ + INIT_LIST_HEAD(&net->sctp.local_addr_list); + spin_lock_init(&net->sctp.local_addr_lock); + sctp_get_local_addr_list(net); + + /* Initialize the address event list */ + INIT_LIST_HEAD(&net->sctp.addr_waitq); + INIT_LIST_HEAD(&net->sctp.auto_asconf_splist); + spin_lock_init(&net->sctp.addr_wq_lock); + net->sctp.addr_wq_timer.expires = 0; + setup_timer(&net->sctp.addr_wq_timer, sctp_addr_wq_timeout_handler, + (unsigned long)net); + + return 0; +} + +static void sctp_net_exit(struct net *net) +{ + /* Free the local address list */ + sctp_free_addr_wq(net); + sctp_free_local_addr_list(net); +} + +static struct pernet_operations sctp_net_ops = { + .init = sctp_net_init, + .exit = sctp_net_exit, +}; + /* Initialize the universe into something sensible. */ SCTP_STATIC __init int sctp_init(void) { @@ -1399,18 +1429,6 @@ SCTP_STATIC __init int sctp_init(void) sctp_v4_pf_init(); sctp_v6_pf_init(); - /* Initialize the local address list. */ - INIT_LIST_HEAD(&sctp_local_addr_list); - spin_lock_init(&sctp_local_addr_lock); - sctp_get_local_addr_list(); - - /* Initialize the address event list */ - INIT_LIST_HEAD(&sctp_addr_waitq); - INIT_LIST_HEAD(&sctp_auto_asconf_splist); - spin_lock_init(&sctp_addr_wq_lock); - sctp_addr_wq_timer.expires = 0; - setup_timer(&sctp_addr_wq_timer, sctp_addr_wq_timeout_handler, 0); - status = sctp_v4_protosw_init(); if (status) @@ -1426,6 +1444,10 @@ SCTP_STATIC __init int sctp_init(void) goto err_ctl_sock_init; } + status = register_pernet_subsys(&sctp_net_ops); + if (status) + goto err_register_pernet_subsys; + status = sctp_v4_add_protocol(); if (status) goto err_add_protocol; @@ -1441,13 +1463,14 @@ out: err_v6_add_protocol: sctp_v4_del_protocol(); err_add_protocol: + unregister_pernet_subsys(&sctp_net_ops); +err_register_pernet_subsys: inet_ctl_sock_destroy(sctp_ctl_sock); err_ctl_sock_init: sctp_v6_protosw_exit(); err_v6_protosw_init: sctp_v4_protosw_exit(); err_protosw_init: - sctp_free_local_addr_list(); sctp_v4_pf_exit(); sctp_v6_pf_exit(); sctp_sysctl_unregister(); @@ -1482,18 +1505,16 @@ SCTP_STATIC __exit void sctp_exit(void) /* Unregister with inet6/inet layers. */ sctp_v6_del_protocol(); sctp_v4_del_protocol(); - sctp_free_addr_wq(); /* Free the control endpoint. */ inet_ctl_sock_destroy(sctp_ctl_sock); + unregister_pernet_subsys(&sctp_net_ops); + /* Free protosw registrations */ sctp_v6_protosw_exit(); sctp_v4_protosw_exit(); - /* Free the local address list. */ - sctp_free_local_addr_list(); - /* Unregister with socket layer. */ sctp_v6_pf_exit(); sctp_v4_pf_exit(); diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 4316b0f988d4..5b6dd0e3d1f6 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -3471,7 +3471,7 @@ static int sctp_setsockopt_auto_asconf(struct sock *sk, char __user *optval, sp->do_auto_asconf = 0; } else if (val && !sp->do_auto_asconf) { list_add_tail(&sp->auto_asconf_list, - &sctp_auto_asconf_splist); + &sock_net(sk)->sctp.auto_asconf_splist); sp->do_auto_asconf = 1; } return 0; @@ -3964,7 +3964,7 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk) sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); if (sctp_default_auto_asconf) { list_add_tail(&sp->auto_asconf_list, - &sctp_auto_asconf_splist); + &sock_net(sk)->sctp.auto_asconf_splist); sp->do_auto_asconf = 1; } else sp->do_auto_asconf = 0; @@ -4653,9 +4653,10 @@ static int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to, union sctp_addr temp; int cnt = 0; int addrlen; + struct net *net = sock_net(sk); rcu_read_lock(); - list_for_each_entry_rcu(addr, &sctp_local_addr_list, list) { + list_for_each_entry_rcu(addr, &net->sctp.local_addr_list, list) { if (!addr->valid) continue; -- cgit v1.2.3 From 2ce955035081112cf1590c961da8d94324142b5e Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 6 Aug 2012 08:43:06 +0000 Subject: sctp: Make the ctl_sock per network namespace - Kill sctp_get_ctl_sock, it is useless now. - Pass struct net where needed so net->sctp.ctl_sock is accessible. Signed-off-by: "Eric W. Biederman" Acked-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/net/netns/sctp.h | 8 ++++++++ include/net/sctp/sctp.h | 1 - net/sctp/input.c | 4 ++-- net/sctp/protocol.c | 47 +++++++++++++++++++---------------------------- net/sctp/sm_statefuns.c | 45 +++++++++++++++++++++++++++++++-------------- 5 files changed, 60 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/include/net/netns/sctp.h b/include/net/netns/sctp.h index cbd684e01cf7..29e36b4b4feb 100644 --- a/include/net/netns/sctp.h +++ b/include/net/netns/sctp.h @@ -1,7 +1,15 @@ #ifndef __NETNS_SCTP_H__ #define __NETNS_SCTP_H__ +struct sock; + struct netns_sctp { + /* This is the global socket data structure used for responding to + * the Out-of-the-blue (OOTB) packets. A control sock will be created + * for this socket at the initialization time. + */ + struct sock *ctl_sock; + /* This is the global local address list. * We actively maintain this complete list of addresses on * the system by catching address add/delete events. diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 00c920537d4a..550a81bbfc71 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -114,7 +114,6 @@ /* * sctp/protocol.c */ -extern struct sock *sctp_get_ctl_sock(void); extern int sctp_copy_local_addr_list(struct net *, struct sctp_bind_addr *, sctp_scope_t, gfp_t gfp, int flags); diff --git a/net/sctp/input.c b/net/sctp/input.c index a7e9a85b5acb..c9a0449bde53 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -204,7 +204,7 @@ int sctp_rcv(struct sk_buff *skb) sctp_endpoint_put(ep); ep = NULL; } - sk = sctp_get_ctl_sock(); + sk = net->sctp.ctl_sock; ep = sctp_sk(sk)->ep; sctp_endpoint_hold(ep); rcvr = &ep->base; @@ -795,7 +795,7 @@ static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(struct net *net, goto hit; } - ep = sctp_sk((sctp_get_ctl_sock()))->ep; + ep = sctp_sk(net->sctp.ctl_sock)->ep; hit: sctp_endpoint_hold(ep); diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 7025d96bae5f..f20bd708e89c 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -78,12 +78,6 @@ struct proc_dir_entry *proc_net_sctp; struct idr sctp_assocs_id; DEFINE_SPINLOCK(sctp_assocs_id_lock); -/* This is the global socket data structure used for responding to - * the Out-of-the-blue (OOTB) packets. A control sock will be created - * for this socket at the initialization time. - */ -static struct sock *sctp_ctl_sock; - static struct sctp_pf *sctp_pf_inet6_specific; static struct sctp_pf *sctp_pf_inet_specific; static struct sctp_af *sctp_af_v4_specific; @@ -96,12 +90,6 @@ long sysctl_sctp_mem[3]; int sysctl_sctp_rmem[3]; int sysctl_sctp_wmem[3]; -/* Return the address of the control sock. */ -struct sock *sctp_get_ctl_sock(void) -{ - return sctp_ctl_sock; -} - /* Set up the proc fs entry for the SCTP protocol. */ static __init int sctp_proc_init(void) { @@ -822,7 +810,7 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev, * Initialize the control inode/socket with a control endpoint data * structure. This endpoint is reserved exclusively for the OOTB processing. */ -static int sctp_ctl_sock_init(void) +static int sctp_ctl_sock_init(struct net *net) { int err; sa_family_t family = PF_INET; @@ -830,14 +818,14 @@ static int sctp_ctl_sock_init(void) if (sctp_get_pf_specific(PF_INET6)) family = PF_INET6; - err = inet_ctl_sock_create(&sctp_ctl_sock, family, - SOCK_SEQPACKET, IPPROTO_SCTP, &init_net); + err = inet_ctl_sock_create(&net->sctp.ctl_sock, family, + SOCK_SEQPACKET, IPPROTO_SCTP, net); /* If IPv6 socket could not be created, try the IPv4 socket */ if (err < 0 && family == PF_INET6) - err = inet_ctl_sock_create(&sctp_ctl_sock, AF_INET, + err = inet_ctl_sock_create(&net->sctp.ctl_sock, AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP, - &init_net); + net); if (err < 0) { pr_err("Failed to create the SCTP control socket\n"); @@ -1196,6 +1184,14 @@ static void sctp_v4_del_protocol(void) static int sctp_net_init(struct net *net) { + int status; + + /* Initialize the control inode/socket for handling OOTB packets. */ + if ((status = sctp_ctl_sock_init(net))) { + pr_err("Failed to initialize the SCTP control sock\n"); + goto err_ctl_sock_init; + } + /* Initialize the local address list. */ INIT_LIST_HEAD(&net->sctp.local_addr_list); spin_lock_init(&net->sctp.local_addr_lock); @@ -1210,6 +1206,9 @@ static int sctp_net_init(struct net *net) (unsigned long)net); return 0; + +err_ctl_sock_init: + return status; } static void sctp_net_exit(struct net *net) @@ -1217,6 +1216,9 @@ static void sctp_net_exit(struct net *net) /* Free the local address list */ sctp_free_addr_wq(net); sctp_free_local_addr_list(net); + + /* Free the control endpoint. */ + inet_ctl_sock_destroy(net->sctp.ctl_sock); } static struct pernet_operations sctp_net_ops = { @@ -1438,12 +1440,6 @@ SCTP_STATIC __init int sctp_init(void) if (status) goto err_v6_protosw_init; - /* Initialize the control inode/socket for handling OOTB packets. */ - if ((status = sctp_ctl_sock_init())) { - pr_err("Failed to initialize the SCTP control sock\n"); - goto err_ctl_sock_init; - } - status = register_pernet_subsys(&sctp_net_ops); if (status) goto err_register_pernet_subsys; @@ -1465,8 +1461,6 @@ err_v6_add_protocol: err_add_protocol: unregister_pernet_subsys(&sctp_net_ops); err_register_pernet_subsys: - inet_ctl_sock_destroy(sctp_ctl_sock); -err_ctl_sock_init: sctp_v6_protosw_exit(); err_v6_protosw_init: sctp_v4_protosw_exit(); @@ -1506,9 +1500,6 @@ SCTP_STATIC __exit void sctp_exit(void) sctp_v6_del_protocol(); sctp_v4_del_protocol(); - /* Free the control endpoint. */ - inet_ctl_sock_destroy(sctp_ctl_sock); - unregister_pernet_subsys(&sctp_net_ops); /* Free protosw registrations */ diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 9fca10357350..f2daf615e8fe 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -74,7 +74,8 @@ static struct sctp_packet *sctp_abort_pkt_new(const struct sctp_endpoint *ep, static int sctp_eat_data(const struct sctp_association *asoc, struct sctp_chunk *chunk, sctp_cmd_seq_t *commands); -static struct sctp_packet *sctp_ootb_pkt_new(const struct sctp_association *asoc, +static struct sctp_packet *sctp_ootb_pkt_new(struct net *net, + const struct sctp_association *asoc, const struct sctp_chunk *chunk); static void sctp_send_stale_cookie_err(const struct sctp_endpoint *ep, const struct sctp_association *asoc, @@ -301,6 +302,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep, struct sctp_chunk *err_chunk; struct sctp_packet *packet; sctp_unrecognized_param_t *unk_param; + struct net *net; int len; /* 6.10 Bundling @@ -318,7 +320,8 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep, /* If the packet is an OOTB packet which is temporarily on the * control endpoint, respond with an ABORT. */ - if (ep == sctp_sk((sctp_get_ctl_sock()))->ep) { + net = sock_net(ep->base.sk); + if (ep == sctp_sk(net->sctp.ctl_sock)->ep) { SCTP_INC_STATS(SCTP_MIB_OUTOFBLUES); return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); } @@ -646,11 +649,13 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep, int error = 0; struct sctp_chunk *err_chk_p; struct sock *sk; + struct net *net; /* If the packet is an OOTB packet which is temporarily on the * control endpoint, respond with an ABORT. */ - if (ep == sctp_sk((sctp_get_ctl_sock()))->ep) { + net = sock_net(ep->base.sk); + if (ep == sctp_sk(net->sctp.ctl_sock)->ep) { SCTP_INC_STATS(SCTP_MIB_OUTOFBLUES); return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); } @@ -1171,7 +1176,7 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const struct sctp_endpoint *ep, /* Helper function to send out an abort for the restart * condition. */ -static int sctp_sf_send_restart_abort(union sctp_addr *ssa, +static int sctp_sf_send_restart_abort(struct net *net, union sctp_addr *ssa, struct sctp_chunk *init, sctp_cmd_seq_t *commands) { @@ -1197,7 +1202,7 @@ static int sctp_sf_send_restart_abort(union sctp_addr *ssa, errhdr->length = htons(len); /* Assign to the control socket. */ - ep = sctp_sk((sctp_get_ctl_sock()))->ep; + ep = sctp_sk(net->sctp.ctl_sock)->ep; /* Association is NULL since this may be a restart attack and we * want to send back the attacker's vtag. @@ -1240,6 +1245,7 @@ static int sctp_sf_check_restart_addrs(const struct sctp_association *new_asoc, struct sctp_chunk *init, sctp_cmd_seq_t *commands) { + struct net *net = sock_net(new_asoc->base.sk); struct sctp_transport *new_addr; int ret = 1; @@ -1258,7 +1264,7 @@ static int sctp_sf_check_restart_addrs(const struct sctp_association *new_asoc, transports) { if (!list_has_sctp_addr(&asoc->peer.transport_addr_list, &new_addr->ipaddr)) { - sctp_sf_send_restart_abort(&new_addr->ipaddr, init, + sctp_sf_send_restart_abort(net, &new_addr->ipaddr, init, commands); ret = 0; break; @@ -1650,10 +1656,11 @@ sctp_disposition_t sctp_sf_do_5_2_3_initack(const struct sctp_endpoint *ep, const sctp_subtype_t type, void *arg, sctp_cmd_seq_t *commands) { + struct net *net = sock_net(ep->base.sk); /* Per the above section, we'll discard the chunk if we have an * endpoint. If this is an OOTB INIT-ACK, treat it as such. */ - if (ep == sctp_sk((sctp_get_ctl_sock()))->ep) + if (ep == sctp_sk(net->sctp.ctl_sock)->ep) return sctp_sf_ootb(ep, asoc, type, arg, commands); else return sctp_sf_discard_chunk(ep, asoc, type, arg, commands); @@ -3163,8 +3170,10 @@ static sctp_disposition_t sctp_sf_tabort_8_4_8(const struct sctp_endpoint *ep, struct sctp_packet *packet = NULL; struct sctp_chunk *chunk = arg; struct sctp_chunk *abort; + struct net *net; - packet = sctp_ootb_pkt_new(asoc, chunk); + net = sock_net(ep->base.sk); + packet = sctp_ootb_pkt_new(net, asoc, chunk); if (packet) { /* Make an ABORT. The T bit will be set if the asoc @@ -3425,8 +3434,10 @@ static sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep, struct sctp_packet *packet = NULL; struct sctp_chunk *chunk = arg; struct sctp_chunk *shut; + struct net *net; - packet = sctp_ootb_pkt_new(asoc, chunk); + net = sock_net(ep->base.sk); + packet = sctp_ootb_pkt_new(net, asoc, chunk); if (packet) { /* Make an SHUTDOWN_COMPLETE. @@ -4262,6 +4273,7 @@ static sctp_disposition_t sctp_sf_abort_violation( struct sctp_packet *packet = NULL; struct sctp_chunk *chunk = arg; struct sctp_chunk *abort = NULL; + struct net *net; /* SCTP-AUTH, Section 6.3: * It should be noted that if the receiver wants to tear @@ -4282,6 +4294,7 @@ static sctp_disposition_t sctp_sf_abort_violation( if (!abort) goto nomem; + net = sock_net(ep->base.sk); if (asoc) { /* Treat INIT-ACK as a special case during COOKIE-WAIT. */ if (chunk->chunk_hdr->type == SCTP_CID_INIT_ACK && @@ -4319,7 +4332,7 @@ static sctp_disposition_t sctp_sf_abort_violation( SCTP_DEC_STATS(SCTP_MIB_CURRESTAB); } } else { - packet = sctp_ootb_pkt_new(asoc, chunk); + packet = sctp_ootb_pkt_new(net, asoc, chunk); if (!packet) goto nomem_pkt; @@ -5825,8 +5838,10 @@ static struct sctp_packet *sctp_abort_pkt_new(const struct sctp_endpoint *ep, { struct sctp_packet *packet; struct sctp_chunk *abort; + struct net *net; - packet = sctp_ootb_pkt_new(asoc, chunk); + net = sock_net(ep->base.sk); + packet = sctp_ootb_pkt_new(net, asoc, chunk); if (packet) { /* Make an ABORT. @@ -5858,7 +5873,8 @@ static struct sctp_packet *sctp_abort_pkt_new(const struct sctp_endpoint *ep, } /* Allocate a packet for responding in the OOTB conditions. */ -static struct sctp_packet *sctp_ootb_pkt_new(const struct sctp_association *asoc, +static struct sctp_packet *sctp_ootb_pkt_new(struct net *net, + const struct sctp_association *asoc, const struct sctp_chunk *chunk) { struct sctp_packet *packet; @@ -5919,7 +5935,7 @@ static struct sctp_packet *sctp_ootb_pkt_new(const struct sctp_association *asoc * the source address. */ sctp_transport_route(transport, (union sctp_addr *)&chunk->dest, - sctp_sk(sctp_get_ctl_sock())); + sctp_sk(net->sctp.ctl_sock)); packet = sctp_packet_init(&transport->packet, transport, sport, dport); packet = sctp_packet_config(packet, vtag, 0); @@ -5946,7 +5962,8 @@ static void sctp_send_stale_cookie_err(const struct sctp_endpoint *ep, struct sctp_packet *packet; if (err_chunk) { - packet = sctp_ootb_pkt_new(asoc, chunk); + struct net *net = sock_net(ep->base.sk); + packet = sctp_ootb_pkt_new(net, asoc, chunk); if (packet) { struct sctp_signed_cookie *cookie; -- cgit v1.2.3 From 13d782f6b4fbbaf9d0380a9947deb45a9de46ae7 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 6 Aug 2012 08:45:15 +0000 Subject: sctp: Make the proc files per network namespace. - Convert all of the files under /proc/net/sctp to be per network namespace. - Don't print anything for /proc/net/sctp/snmp except in the initial network namespaces as the snmp counters still have to be converted to be per network namespace. Signed-off-by: "Eric W. Biederman" Acked-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/net/netns/sctp.h | 5 +++ include/net/sctp/sctp.h | 25 +++++++------- net/sctp/objcnt.c | 8 ++--- net/sctp/proc.c | 59 +++++++++++++++++++++----------- net/sctp/protocol.c | 87 +++++++++++++++++++++--------------------------- 5 files changed, 98 insertions(+), 86 deletions(-) (limited to 'include') diff --git a/include/net/netns/sctp.h b/include/net/netns/sctp.h index 29e36b4b4feb..9c20a82a77ec 100644 --- a/include/net/netns/sctp.h +++ b/include/net/netns/sctp.h @@ -2,8 +2,13 @@ #define __NETNS_SCTP_H__ struct sock; +struct proc_dir_entry; struct netns_sctp { +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *proc_net_sctp; +#endif + /* This is the global socket data structure used for responding to * the Out-of-the-blue (OOTB) packets. A control sock will be created * for this socket at the initialization time. diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 550a81bbfc71..b49588a51d80 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -172,14 +172,14 @@ void sctp_backlog_migrate(struct sctp_association *assoc, /* * sctp/proc.c */ -int sctp_snmp_proc_init(void); -void sctp_snmp_proc_exit(void); -int sctp_eps_proc_init(void); -void sctp_eps_proc_exit(void); -int sctp_assocs_proc_init(void); -void sctp_assocs_proc_exit(void); -int sctp_remaddr_proc_init(void); -void sctp_remaddr_proc_exit(void); +int sctp_snmp_proc_init(struct net *net); +void sctp_snmp_proc_exit(struct net *net); +int sctp_eps_proc_init(struct net *net); +void sctp_eps_proc_exit(struct net *net); +int sctp_assocs_proc_init(struct net *net); +void sctp_assocs_proc_exit(struct net *net); +int sctp_remaddr_proc_init(struct net *net); +void sctp_remaddr_proc_exit(struct net *net); /* @@ -360,16 +360,16 @@ atomic_t sctp_dbg_objcnt_## name = ATOMIC_INIT(0) #define SCTP_DBG_OBJCNT_ENTRY(name) \ {.label= #name, .counter= &sctp_dbg_objcnt_## name} -void sctp_dbg_objcnt_init(void); -void sctp_dbg_objcnt_exit(void); +void sctp_dbg_objcnt_init(struct net *); +void sctp_dbg_objcnt_exit(struct net *); #else #define SCTP_DBG_OBJCNT_INC(name) #define SCTP_DBG_OBJCNT_DEC(name) -static inline void sctp_dbg_objcnt_init(void) { return; } -static inline void sctp_dbg_objcnt_exit(void) { return; } +static inline void sctp_dbg_objcnt_init(struct net *) { return; } +static inline void sctp_dbg_objcnt_exit(struct net *) { return; } #endif /* CONFIG_SCTP_DBG_OBJCOUNT */ @@ -585,7 +585,6 @@ for (pos = chunk->subh.fwdtsn_hdr->skip;\ extern struct proto sctp_prot; extern struct proto sctpv6_prot; -extern struct proc_dir_entry *proc_net_sctp; void sctp_put_port(struct sock *sk); extern struct idr sctp_assocs_id; diff --git a/net/sctp/objcnt.c b/net/sctp/objcnt.c index 8ef8e7d9eb61..fe012c44f8df 100644 --- a/net/sctp/objcnt.c +++ b/net/sctp/objcnt.c @@ -129,20 +129,20 @@ static const struct file_operations sctp_objcnt_ops = { }; /* Initialize the objcount in the proc filesystem. */ -void sctp_dbg_objcnt_init(void) +void sctp_dbg_objcnt_init(struct net *net) { struct proc_dir_entry *ent; ent = proc_create("sctp_dbg_objcnt", 0, - proc_net_sctp, &sctp_objcnt_ops); + net->sctp.proc_net_sctp, &sctp_objcnt_ops); if (!ent) pr_warn("sctp_dbg_objcnt: Unable to create /proc entry.\n"); } /* Cleanup the objcount entry in the proc filesystem. */ -void sctp_dbg_objcnt_exit(void) +void sctp_dbg_objcnt_exit(struct net *net) { - remove_proc_entry("sctp_dbg_objcnt", proc_net_sctp); + remove_proc_entry("sctp_dbg_objcnt", net->sctp.proc_net_sctp); } diff --git a/net/sctp/proc.c b/net/sctp/proc.c index 1e2eee88c3ea..dc79a3aa0382 100644 --- a/net/sctp/proc.c +++ b/net/sctp/proc.c @@ -80,8 +80,12 @@ static const struct snmp_mib sctp_snmp_list[] = { /* Display sctp snmp mib statistics(/proc/net/sctp/snmp). */ static int sctp_snmp_seq_show(struct seq_file *seq, void *v) { + struct net *net = seq->private; int i; + if (!net_eq(net, &init_net)) + return 0; + for (i = 0; sctp_snmp_list[i].name != NULL; i++) seq_printf(seq, "%-32s\t%ld\n", sctp_snmp_list[i].name, snmp_fold_field((void __percpu **)sctp_statistics, @@ -93,7 +97,7 @@ static int sctp_snmp_seq_show(struct seq_file *seq, void *v) /* Initialize the seq file operations for 'snmp' object. */ static int sctp_snmp_seq_open(struct inode *inode, struct file *file) { - return single_open(file, sctp_snmp_seq_show, NULL); + return single_open_net(inode, file, sctp_snmp_seq_show); } static const struct file_operations sctp_snmp_seq_fops = { @@ -105,11 +109,12 @@ static const struct file_operations sctp_snmp_seq_fops = { }; /* Set up the proc fs entry for 'snmp' object. */ -int __init sctp_snmp_proc_init(void) +int __net_init sctp_snmp_proc_init(struct net *net) { struct proc_dir_entry *p; - p = proc_create("snmp", S_IRUGO, proc_net_sctp, &sctp_snmp_seq_fops); + p = proc_create("snmp", S_IRUGO, net->sctp.proc_net_sctp, + &sctp_snmp_seq_fops); if (!p) return -ENOMEM; @@ -117,9 +122,9 @@ int __init sctp_snmp_proc_init(void) } /* Cleanup the proc fs entry for 'snmp' object. */ -void sctp_snmp_proc_exit(void) +void sctp_snmp_proc_exit(struct net *net) { - remove_proc_entry("snmp", proc_net_sctp); + remove_proc_entry("snmp", net->sctp.proc_net_sctp); } /* Dump local addresses of an association/endpoint. */ @@ -197,6 +202,7 @@ static void * sctp_eps_seq_next(struct seq_file *seq, void *v, loff_t *pos) /* Display sctp endpoints (/proc/net/sctp/eps). */ static int sctp_eps_seq_show(struct seq_file *seq, void *v) { + struct seq_net_private *priv = seq->private; struct sctp_hashbucket *head; struct sctp_ep_common *epb; struct sctp_endpoint *ep; @@ -213,6 +219,8 @@ static int sctp_eps_seq_show(struct seq_file *seq, void *v) sctp_for_each_hentry(epb, node, &head->chain) { ep = sctp_ep(epb); sk = epb->sk; + if (!net_eq(sock_net(sk), priv->net)) + continue; seq_printf(seq, "%8pK %8pK %-3d %-3d %-4d %-5d %5d %5lu ", ep, sk, sctp_sk(sk)->type, sk->sk_state, hash, epb->bind_addr.port, @@ -238,7 +246,8 @@ static const struct seq_operations sctp_eps_ops = { /* Initialize the seq file operations for 'eps' object. */ static int sctp_eps_seq_open(struct inode *inode, struct file *file) { - return seq_open(file, &sctp_eps_ops); + return seq_open_net(inode, file, &sctp_eps_ops, + sizeof(struct seq_net_private)); } static const struct file_operations sctp_eps_seq_fops = { @@ -249,11 +258,12 @@ static const struct file_operations sctp_eps_seq_fops = { }; /* Set up the proc fs entry for 'eps' object. */ -int __init sctp_eps_proc_init(void) +int __net_init sctp_eps_proc_init(struct net *net) { struct proc_dir_entry *p; - p = proc_create("eps", S_IRUGO, proc_net_sctp, &sctp_eps_seq_fops); + p = proc_create("eps", S_IRUGO, net->sctp.proc_net_sctp, + &sctp_eps_seq_fops); if (!p) return -ENOMEM; @@ -261,9 +271,9 @@ int __init sctp_eps_proc_init(void) } /* Cleanup the proc fs entry for 'eps' object. */ -void sctp_eps_proc_exit(void) +void sctp_eps_proc_exit(struct net *net) { - remove_proc_entry("eps", proc_net_sctp); + remove_proc_entry("eps", net->sctp.proc_net_sctp); } @@ -300,6 +310,7 @@ static void * sctp_assocs_seq_next(struct seq_file *seq, void *v, loff_t *pos) /* Display sctp associations (/proc/net/sctp/assocs). */ static int sctp_assocs_seq_show(struct seq_file *seq, void *v) { + struct seq_net_private *priv = seq->private; struct sctp_hashbucket *head; struct sctp_ep_common *epb; struct sctp_association *assoc; @@ -316,6 +327,8 @@ static int sctp_assocs_seq_show(struct seq_file *seq, void *v) sctp_for_each_hentry(epb, node, &head->chain) { assoc = sctp_assoc(epb); sk = epb->sk; + if (!net_eq(sock_net(sk), priv->net)) + continue; seq_printf(seq, "%8pK %8pK %-3d %-3d %-2d %-4d " "%4d %8d %8d %7d %5lu %-5d %5d ", @@ -354,7 +367,8 @@ static const struct seq_operations sctp_assoc_ops = { /* Initialize the seq file operations for 'assocs' object. */ static int sctp_assocs_seq_open(struct inode *inode, struct file *file) { - return seq_open(file, &sctp_assoc_ops); + return seq_open_net(inode, file, &sctp_assoc_ops, + sizeof(struct seq_net_private)); } static const struct file_operations sctp_assocs_seq_fops = { @@ -365,11 +379,11 @@ static const struct file_operations sctp_assocs_seq_fops = { }; /* Set up the proc fs entry for 'assocs' object. */ -int __init sctp_assocs_proc_init(void) +int __net_init sctp_assocs_proc_init(struct net *net) { struct proc_dir_entry *p; - p = proc_create("assocs", S_IRUGO, proc_net_sctp, + p = proc_create("assocs", S_IRUGO, net->sctp.proc_net_sctp, &sctp_assocs_seq_fops); if (!p) return -ENOMEM; @@ -378,9 +392,9 @@ int __init sctp_assocs_proc_init(void) } /* Cleanup the proc fs entry for 'assocs' object. */ -void sctp_assocs_proc_exit(void) +void sctp_assocs_proc_exit(struct net *net) { - remove_proc_entry("assocs", proc_net_sctp); + remove_proc_entry("assocs", net->sctp.proc_net_sctp); } static void *sctp_remaddr_seq_start(struct seq_file *seq, loff_t *pos) @@ -412,6 +426,7 @@ static void sctp_remaddr_seq_stop(struct seq_file *seq, void *v) static int sctp_remaddr_seq_show(struct seq_file *seq, void *v) { + struct seq_net_private *priv = seq->private; struct sctp_hashbucket *head; struct sctp_ep_common *epb; struct sctp_association *assoc; @@ -426,6 +441,8 @@ static int sctp_remaddr_seq_show(struct seq_file *seq, void *v) sctp_local_bh_disable(); read_lock(&head->lock); sctp_for_each_hentry(epb, node, &head->chain) { + if (!net_eq(sock_net(epb->sk), priv->net)) + continue; assoc = sctp_assoc(epb); list_for_each_entry(tsp, &assoc->peer.transport_addr_list, transports) { @@ -489,14 +506,15 @@ static const struct seq_operations sctp_remaddr_ops = { }; /* Cleanup the proc fs entry for 'remaddr' object. */ -void sctp_remaddr_proc_exit(void) +void sctp_remaddr_proc_exit(struct net *net) { - remove_proc_entry("remaddr", proc_net_sctp); + remove_proc_entry("remaddr", net->sctp.proc_net_sctp); } static int sctp_remaddr_seq_open(struct inode *inode, struct file *file) { - return seq_open(file, &sctp_remaddr_ops); + return seq_open_net(inode, file, &sctp_remaddr_ops, + sizeof(struct seq_net_private)); } static const struct file_operations sctp_remaddr_seq_fops = { @@ -506,11 +524,12 @@ static const struct file_operations sctp_remaddr_seq_fops = { .release = seq_release, }; -int __init sctp_remaddr_proc_init(void) +int __net_init sctp_remaddr_proc_init(struct net *net) { struct proc_dir_entry *p; - p = proc_create("remaddr", S_IRUGO, proc_net_sctp, &sctp_remaddr_seq_fops); + p = proc_create("remaddr", S_IRUGO, net->sctp.proc_net_sctp, + &sctp_remaddr_seq_fops); if (!p) return -ENOMEM; return 0; diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 48a5989c98ce..de7994edb4ca 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -71,10 +71,6 @@ struct sctp_globals sctp_globals __read_mostly; DEFINE_SNMP_STAT(struct sctp_mib, sctp_statistics) __read_mostly; -#ifdef CONFIG_PROC_FS -struct proc_dir_entry *proc_net_sctp; -#endif - struct idr sctp_assocs_id; DEFINE_SPINLOCK(sctp_assocs_id_lock); @@ -91,60 +87,52 @@ int sysctl_sctp_rmem[3]; int sysctl_sctp_wmem[3]; /* Set up the proc fs entry for the SCTP protocol. */ -static __init int sctp_proc_init(void) +static __net_init int sctp_proc_init(struct net *net) { #ifdef CONFIG_PROC_FS - if (!proc_net_sctp) { - proc_net_sctp = proc_mkdir("sctp", init_net.proc_net); - if (!proc_net_sctp) - goto out_free_percpu; - } - - if (sctp_snmp_proc_init()) + net->sctp.proc_net_sctp = proc_net_mkdir(net, "sctp", net->proc_net); + if (!net->sctp.proc_net_sctp) + goto out_proc_net_sctp; + if (sctp_snmp_proc_init(net)) goto out_snmp_proc_init; - if (sctp_eps_proc_init()) + if (sctp_eps_proc_init(net)) goto out_eps_proc_init; - if (sctp_assocs_proc_init()) + if (sctp_assocs_proc_init(net)) goto out_assocs_proc_init; - if (sctp_remaddr_proc_init()) + if (sctp_remaddr_proc_init(net)) goto out_remaddr_proc_init; return 0; out_remaddr_proc_init: - sctp_assocs_proc_exit(); + sctp_assocs_proc_exit(net); out_assocs_proc_init: - sctp_eps_proc_exit(); + sctp_eps_proc_exit(net); out_eps_proc_init: - sctp_snmp_proc_exit(); + sctp_snmp_proc_exit(net); out_snmp_proc_init: - if (proc_net_sctp) { - proc_net_sctp = NULL; - remove_proc_entry("sctp", init_net.proc_net); - } -out_free_percpu: -#else - return 0; -#endif /* CONFIG_PROC_FS */ + remove_proc_entry("sctp", net->proc_net); + net->sctp.proc_net_sctp = NULL; +out_proc_net_sctp: return -ENOMEM; +#endif /* CONFIG_PROC_FS */ + return 0; } /* Clean up the proc fs entry for the SCTP protocol. * Note: Do not make this __exit as it is used in the init error * path. */ -static void sctp_proc_exit(void) +static void sctp_proc_exit(struct net *net) { #ifdef CONFIG_PROC_FS - sctp_snmp_proc_exit(); - sctp_eps_proc_exit(); - sctp_assocs_proc_exit(); - sctp_remaddr_proc_exit(); - - if (proc_net_sctp) { - proc_net_sctp = NULL; - remove_proc_entry("sctp", init_net.proc_net); - } + sctp_snmp_proc_exit(net); + sctp_eps_proc_exit(net); + sctp_assocs_proc_exit(net); + sctp_remaddr_proc_exit(net); + + remove_proc_entry("sctp", net->proc_net); + net->sctp.proc_net_sctp = NULL; #endif } @@ -1180,6 +1168,13 @@ static int sctp_net_init(struct net *net) { int status; + /* Initialize proc fs directory. */ + status = sctp_proc_init(net); + if (status) + goto err_init_proc; + + sctp_dbg_objcnt_init(net); + /* Initialize the control inode/socket for handling OOTB packets. */ if ((status = sctp_ctl_sock_init(net))) { pr_err("Failed to initialize the SCTP control sock\n"); @@ -1202,6 +1197,9 @@ static int sctp_net_init(struct net *net) return 0; err_ctl_sock_init: + sctp_dbg_objcnt_exit(net); + sctp_proc_exit(net); +err_init_proc: return status; } @@ -1213,6 +1211,10 @@ static void sctp_net_exit(struct net *net) /* Free the control endpoint. */ inet_ctl_sock_destroy(net->sctp.ctl_sock); + + sctp_dbg_objcnt_exit(net); + + sctp_proc_exit(net); } static struct pernet_operations sctp_net_ops = { @@ -1259,14 +1261,6 @@ SCTP_STATIC __init int sctp_init(void) if (status) goto err_percpu_counter_init; - /* Initialize proc fs directory. */ - status = sctp_proc_init(); - if (status) - goto err_init_proc; - - /* Initialize object count debugging. */ - sctp_dbg_objcnt_init(); - /* * 14. Suggested SCTP Protocol Parameter Values */ @@ -1476,9 +1470,6 @@ err_ehash_alloc: get_order(sctp_assoc_hashsize * sizeof(struct sctp_hashbucket))); err_ahash_alloc: - sctp_dbg_objcnt_exit(); - sctp_proc_exit(); -err_init_proc: percpu_counter_destroy(&sctp_sockets_allocated); err_percpu_counter_init: cleanup_sctp_mibs(); @@ -1520,9 +1511,7 @@ SCTP_STATIC __exit void sctp_exit(void) get_order(sctp_port_hashsize * sizeof(struct sctp_bind_hashbucket))); - sctp_dbg_objcnt_exit(); percpu_counter_destroy(&sctp_sockets_allocated); - sctp_proc_exit(); cleanup_sctp_mibs(); rcu_barrier(); /* Wait for completion of call_rcu()'s */ -- cgit v1.2.3 From b01a24078fa3fc4f0f447d1306ce5adc495ead86 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 6 Aug 2012 08:47:55 +0000 Subject: sctp: Make the mib per network namespace Signed-off-by: "Eric W. Biederman" Acked-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/net/netns/sctp.h | 3 + include/net/sctp/sctp.h | 9 ++- net/sctp/associola.c | 2 +- net/sctp/chunk.c | 2 +- net/sctp/endpointola.c | 2 +- net/sctp/input.c | 22 +++---- net/sctp/ipv6.c | 4 +- net/sctp/output.c | 2 +- net/sctp/outqueue.c | 18 +++--- net/sctp/proc.c | 5 +- net/sctp/protocol.c | 27 ++++---- net/sctp/sm_statefuns.c | 163 ++++++++++++++++++++++++++++------------------- net/sctp/ulpqueue.c | 18 ++++-- 13 files changed, 158 insertions(+), 119 deletions(-) (limited to 'include') diff --git a/include/net/netns/sctp.h b/include/net/netns/sctp.h index 9c20a82a77ec..06ccddf9566c 100644 --- a/include/net/netns/sctp.h +++ b/include/net/netns/sctp.h @@ -3,8 +3,11 @@ struct sock; struct proc_dir_entry; +struct sctp_mib; struct netns_sctp { + DEFINE_SNMP_STAT(struct sctp_mib, sctp_statistics); + #ifdef CONFIG_PROC_FS struct proc_dir_entry *proc_net_sctp; #endif diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index b49588a51d80..7dcd4dfd7c3f 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -221,11 +221,10 @@ extern struct kmem_cache *sctp_bucket_cachep __read_mostly; #define sctp_bh_unlock_sock(sk) bh_unlock_sock(sk) /* SCTP SNMP MIB stats handlers */ -DECLARE_SNMP_STAT(struct sctp_mib, sctp_statistics); -#define SCTP_INC_STATS(field) SNMP_INC_STATS(sctp_statistics, field) -#define SCTP_INC_STATS_BH(field) SNMP_INC_STATS_BH(sctp_statistics, field) -#define SCTP_INC_STATS_USER(field) SNMP_INC_STATS_USER(sctp_statistics, field) -#define SCTP_DEC_STATS(field) SNMP_DEC_STATS(sctp_statistics, field) +#define SCTP_INC_STATS(net, field) SNMP_INC_STATS((net)->sctp.sctp_statistics, field) +#define SCTP_INC_STATS_BH(net, field) SNMP_INC_STATS_BH((net)->sctp.sctp_statistics, field) +#define SCTP_INC_STATS_USER(net, field) SNMP_INC_STATS_USER((net)->sctp.sctp_statistics, field) +#define SCTP_DEC_STATS(net, field) SNMP_DEC_STATS((net)->sctp.sctp_statistics, field) #endif /* !TEST_FRAME */ diff --git a/net/sctp/associola.c b/net/sctp/associola.c index ed4930b31341..8a1f27a7a001 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -1150,7 +1150,7 @@ static void sctp_assoc_bh_rcv(struct work_struct *work) if (sctp_chunk_is_data(chunk)) asoc->peer.last_data_from = chunk->transport; else - SCTP_INC_STATS(SCTP_MIB_INCTRLCHUNKS); + SCTP_INC_STATS(sock_net(asoc->base.sk), SCTP_MIB_INCTRLCHUNKS); if (chunk->transport) chunk->transport->last_time_heard = jiffies; diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index 6c8556459a75..7c2df9c33df3 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -257,7 +257,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, offset = 0; if ((whole > 1) || (whole && over)) - SCTP_INC_STATS_USER(SCTP_MIB_FRAGUSRMSGS); + SCTP_INC_STATS_USER(sock_net(asoc->base.sk), SCTP_MIB_FRAGUSRMSGS); /* Create chunks for all the full sized DATA chunks. */ for (i=0, len=first_len; i < whole; i++) { diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index 6b763939345b..3edca80ab3a1 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -478,7 +478,7 @@ normal: if (asoc && sctp_chunk_is_data(chunk)) asoc->peer.last_data_from = chunk->transport; else - SCTP_INC_STATS(SCTP_MIB_INCTRLCHUNKS); + SCTP_INC_STATS(sock_net(ep->base.sk), SCTP_MIB_INCTRLCHUNKS); if (chunk->transport) chunk->transport->last_time_heard = jiffies; diff --git a/net/sctp/input.c b/net/sctp/input.c index c9a0449bde53..530830151f04 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -83,7 +83,7 @@ static int sctp_add_backlog(struct sock *sk, struct sk_buff *skb); /* Calculate the SCTP checksum of an SCTP packet. */ -static inline int sctp_rcv_checksum(struct sk_buff *skb) +static inline int sctp_rcv_checksum(struct net *net, struct sk_buff *skb) { struct sctphdr *sh = sctp_hdr(skb); __le32 cmp = sh->checksum; @@ -99,7 +99,7 @@ static inline int sctp_rcv_checksum(struct sk_buff *skb) if (val != cmp) { /* CRC failure, dump it. */ - SCTP_INC_STATS_BH(SCTP_MIB_CHECKSUMERRORS); + SCTP_INC_STATS_BH(net, SCTP_MIB_CHECKSUMERRORS); return -1; } return 0; @@ -137,7 +137,7 @@ int sctp_rcv(struct sk_buff *skb) if (skb->pkt_type!=PACKET_HOST) goto discard_it; - SCTP_INC_STATS_BH(SCTP_MIB_INSCTPPACKS); + SCTP_INC_STATS_BH(net, SCTP_MIB_INSCTPPACKS); if (skb_linearize(skb)) goto discard_it; @@ -149,7 +149,7 @@ int sctp_rcv(struct sk_buff *skb) if (skb->len < sizeof(struct sctphdr)) goto discard_it; if (!sctp_checksum_disable && !skb_csum_unnecessary(skb) && - sctp_rcv_checksum(skb) < 0) + sctp_rcv_checksum(net, skb) < 0) goto discard_it; skb_pull(skb, sizeof(struct sctphdr)); @@ -220,7 +220,7 @@ int sctp_rcv(struct sk_buff *skb) */ if (!asoc) { if (sctp_rcv_ootb(skb)) { - SCTP_INC_STATS_BH(SCTP_MIB_OUTOFBLUES); + SCTP_INC_STATS_BH(net, SCTP_MIB_OUTOFBLUES); goto discard_release; } } @@ -276,9 +276,9 @@ int sctp_rcv(struct sk_buff *skb) skb = NULL; /* sctp_chunk_free already freed the skb */ goto discard_release; } - SCTP_INC_STATS_BH(SCTP_MIB_IN_PKT_BACKLOG); + SCTP_INC_STATS_BH(net, SCTP_MIB_IN_PKT_BACKLOG); } else { - SCTP_INC_STATS_BH(SCTP_MIB_IN_PKT_SOFTIRQ); + SCTP_INC_STATS_BH(net, SCTP_MIB_IN_PKT_SOFTIRQ); sctp_inq_push(&chunk->rcvr->inqueue, chunk); } @@ -293,7 +293,7 @@ int sctp_rcv(struct sk_buff *skb) return 0; discard_it: - SCTP_INC_STATS_BH(SCTP_MIB_IN_PKT_DISCARDS); + SCTP_INC_STATS_BH(net, SCTP_MIB_IN_PKT_DISCARDS); kfree_skb(skb); return 0; @@ -543,7 +543,7 @@ struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *skb, * servers this needs to be solved differently. */ if (sock_owned_by_user(sk)) - NET_INC_STATS_BH(&init_net, LINUX_MIB_LOCKDROPPEDICMPS); + NET_INC_STATS_BH(net, LINUX_MIB_LOCKDROPPEDICMPS); *app = asoc; *tpp = transport; @@ -593,7 +593,7 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info) struct net *net = dev_net(skb->dev); if (skb->len < ihlen + 8) { - ICMP_INC_STATS_BH(&init_net, ICMP_MIB_INERRORS); + ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS); return; } @@ -607,7 +607,7 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info) skb->network_header = saveip; skb->transport_header = savesctp; if (!sk) { - ICMP_INC_STATS_BH(&init_net, ICMP_MIB_INERRORS); + ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS); return; } /* Warning: The sock lock is held. Remember to call diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index a18cda60e156..ea14cb445295 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -169,7 +169,7 @@ SCTP_STATIC void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, skb->network_header = saveip; skb->transport_header = savesctp; if (!sk) { - ICMP6_INC_STATS_BH(dev_net(skb->dev), idev, ICMP6_MIB_INERRORS); + ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_INERRORS); goto out; } @@ -243,7 +243,7 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport) __func__, skb, skb->len, &fl6.saddr, &fl6.daddr); - SCTP_INC_STATS(SCTP_MIB_OUTSCTPPACKS); + SCTP_INC_STATS(sock_net(sk), SCTP_MIB_OUTSCTPPACKS); if (!(transport->param_flags & SPP_PMTUD_ENABLE)) skb->local_df = 1; diff --git a/net/sctp/output.c b/net/sctp/output.c index 838e18b4d7ea..0c6359bb973c 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -597,7 +597,7 @@ out: return err; no_route: kfree_skb(nskb); - IP_INC_STATS_BH(&init_net, IPSTATS_MIB_OUTNOROUTES); + IP_INC_STATS_BH(sock_net(asoc->base.sk), IPSTATS_MIB_OUTNOROUTES); /* FIXME: Returning the 'err' will effect all the associations * associated with a socket, although only one of the paths of the diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index e7aa177c9522..072bf6ae3c26 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -299,6 +299,7 @@ void sctp_outq_free(struct sctp_outq *q) /* Put a new chunk in an sctp_outq. */ int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk) { + struct net *net = sock_net(q->asoc->base.sk); int error = 0; SCTP_DEBUG_PRINTK("sctp_outq_tail(%p, %p[%s])\n", @@ -337,15 +338,15 @@ int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk) sctp_outq_tail_data(q, chunk); if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) - SCTP_INC_STATS(SCTP_MIB_OUTUNORDERCHUNKS); + SCTP_INC_STATS(net, SCTP_MIB_OUTUNORDERCHUNKS); else - SCTP_INC_STATS(SCTP_MIB_OUTORDERCHUNKS); + SCTP_INC_STATS(net, SCTP_MIB_OUTORDERCHUNKS); q->empty = 0; break; } } else { list_add_tail(&chunk->list, &q->control_chunk_list); - SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS); + SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS); } if (error < 0) @@ -478,11 +479,12 @@ void sctp_retransmit_mark(struct sctp_outq *q, void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport, sctp_retransmit_reason_t reason) { + struct net *net = sock_net(q->asoc->base.sk); int error = 0; switch(reason) { case SCTP_RTXR_T3_RTX: - SCTP_INC_STATS(SCTP_MIB_T3_RETRANSMITS); + SCTP_INC_STATS(net, SCTP_MIB_T3_RETRANSMITS); sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_T3_RTX); /* Update the retran path if the T3-rtx timer has expired for * the current retran path. @@ -493,15 +495,15 @@ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport, transport->asoc->unack_data; break; case SCTP_RTXR_FAST_RTX: - SCTP_INC_STATS(SCTP_MIB_FAST_RETRANSMITS); + SCTP_INC_STATS(net, SCTP_MIB_FAST_RETRANSMITS); sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_FAST_RTX); q->fast_rtx = 1; break; case SCTP_RTXR_PMTUD: - SCTP_INC_STATS(SCTP_MIB_PMTUD_RETRANSMITS); + SCTP_INC_STATS(net, SCTP_MIB_PMTUD_RETRANSMITS); break; case SCTP_RTXR_T1_RTX: - SCTP_INC_STATS(SCTP_MIB_T1_RETRANSMITS); + SCTP_INC_STATS(net, SCTP_MIB_T1_RETRANSMITS); transport->asoc->init_retries++; break; default: @@ -1914,6 +1916,6 @@ static void sctp_generate_fwdtsn(struct sctp_outq *q, __u32 ctsn) if (ftsn_chunk) { list_add_tail(&ftsn_chunk->list, &q->control_chunk_list); - SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS); + SCTP_INC_STATS(sock_net(asoc->base.sk), SCTP_MIB_OUTCTRLCHUNKS); } } diff --git a/net/sctp/proc.c b/net/sctp/proc.c index dc79a3aa0382..3e62ee55228f 100644 --- a/net/sctp/proc.c +++ b/net/sctp/proc.c @@ -83,12 +83,9 @@ static int sctp_snmp_seq_show(struct seq_file *seq, void *v) struct net *net = seq->private; int i; - if (!net_eq(net, &init_net)) - return 0; - for (i = 0; sctp_snmp_list[i].name != NULL; i++) seq_printf(seq, "%-32s\t%ld\n", sctp_snmp_list[i].name, - snmp_fold_field((void __percpu **)sctp_statistics, + snmp_fold_field((void __percpu **)net->sctp.sctp_statistics, sctp_snmp_list[i].entry)); return 0; diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 059c914c09f2..d58db315db85 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -69,7 +69,6 @@ /* Global data structures. */ struct sctp_globals sctp_globals __read_mostly; -DEFINE_SNMP_STAT(struct sctp_mib, sctp_statistics) __read_mostly; struct idr sctp_assocs_id; DEFINE_SPINLOCK(sctp_assocs_id_lock); @@ -961,7 +960,7 @@ static inline int sctp_v4_xmit(struct sk_buff *skb, inet->pmtudisc = transport->param_flags & SPP_PMTUD_ENABLE ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT; - SCTP_INC_STATS(SCTP_MIB_OUTSCTPPACKS); + SCTP_INC_STATS(sock_net(&inet->sk), SCTP_MIB_OUTSCTPPACKS); return ip_queue_xmit(skb, &transport->fl); } @@ -1102,16 +1101,16 @@ int sctp_register_pf(struct sctp_pf *pf, sa_family_t family) return 1; } -static inline int init_sctp_mibs(void) +static inline int init_sctp_mibs(struct net *net) { - return snmp_mib_init((void __percpu **)sctp_statistics, + return snmp_mib_init((void __percpu **)net->sctp.sctp_statistics, sizeof(struct sctp_mib), __alignof__(struct sctp_mib)); } -static inline void cleanup_sctp_mibs(void) +static inline void cleanup_sctp_mibs(struct net *net) { - snmp_mib_free((void __percpu **)sctp_statistics); + snmp_mib_free((void __percpu **)net->sctp.sctp_statistics); } static void sctp_v4_pf_init(void) @@ -1170,6 +1169,11 @@ static int sctp_net_init(struct net *net) { int status; + /* Allocate and initialise sctp mibs. */ + status = init_sctp_mibs(net); + if (status) + goto err_init_mibs; + /* Initialize proc fs directory. */ status = sctp_proc_init(net); if (status) @@ -1202,6 +1206,8 @@ err_ctl_sock_init: sctp_dbg_objcnt_exit(net); sctp_proc_exit(net); err_init_proc: + cleanup_sctp_mibs(net); +err_init_mibs: return status; } @@ -1217,6 +1223,7 @@ static void sctp_net_exit(struct net *net) sctp_dbg_objcnt_exit(net); sctp_proc_exit(net); + cleanup_sctp_mibs(net); } static struct pernet_operations sctp_net_ops = { @@ -1254,11 +1261,6 @@ SCTP_STATIC __init int sctp_init(void) if (!sctp_chunk_cachep) goto err_chunk_cachep; - /* Allocate and initialise sctp mibs. */ - status = init_sctp_mibs(); - if (status) - goto err_init_mibs; - status = percpu_counter_init(&sctp_sockets_allocated, 0); if (status) goto err_percpu_counter_init; @@ -1474,8 +1476,6 @@ err_ehash_alloc: err_ahash_alloc: percpu_counter_destroy(&sctp_sockets_allocated); err_percpu_counter_init: - cleanup_sctp_mibs(); -err_init_mibs: kmem_cache_destroy(sctp_chunk_cachep); err_chunk_cachep: kmem_cache_destroy(sctp_bucket_cachep); @@ -1514,7 +1514,6 @@ SCTP_STATIC __exit void sctp_exit(void) sizeof(struct sctp_bind_hashbucket))); percpu_counter_destroy(&sctp_sockets_allocated); - cleanup_sctp_mibs(); rcu_barrier(); /* Wait for completion of call_rcu()'s */ diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index f2daf615e8fe..bee5e2c288d8 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -213,6 +213,7 @@ sctp_disposition_t sctp_sf_do_4_C(const struct sctp_endpoint *ep, { struct sctp_chunk *chunk = arg; struct sctp_ulpevent *ev; + struct net *net; if (!sctp_vtag_verify_either(chunk, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); @@ -260,8 +261,9 @@ sctp_disposition_t sctp_sf_do_4_C(const struct sctp_endpoint *ep, sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, SCTP_STATE(SCTP_STATE_CLOSED)); - SCTP_INC_STATS(SCTP_MIB_SHUTDOWNS); - SCTP_DEC_STATS(SCTP_MIB_CURRESTAB); + net = sock_net(asoc->base.sk); + SCTP_INC_STATS(net, SCTP_MIB_SHUTDOWNS); + SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB); sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); @@ -322,7 +324,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep, */ net = sock_net(ep->base.sk); if (ep == sctp_sk(net->sctp.ctl_sock)->ep) { - SCTP_INC_STATS(SCTP_MIB_OUTOFBLUES); + SCTP_INC_STATS(net, SCTP_MIB_OUTOFBLUES); return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); } @@ -369,7 +371,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep, if (packet) { sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, SCTP_PACKET(packet)); - SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS); + SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS); return SCTP_DISPOSITION_CONSUME; } else { return SCTP_DISPOSITION_NOMEM; @@ -540,7 +542,7 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep, if (packet) { sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, SCTP_PACKET(packet)); - SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS); + SCTP_INC_STATS(sock_net(ep->base.sk), SCTP_MIB_OUTCTRLCHUNKS); error = SCTP_ERROR_INV_PARAM; } } @@ -559,7 +561,7 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep, if (sctp_auth_recv_cid(SCTP_CID_ABORT, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); - SCTP_INC_STATS(SCTP_MIB_ABORTEDS); + SCTP_INC_STATS(sock_net(ep->base.sk), SCTP_MIB_ABORTEDS); return sctp_stop_t1_and_abort(commands, error, ECONNREFUSED, asoc, chunk->transport); } @@ -656,7 +658,7 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep, */ net = sock_net(ep->base.sk); if (ep == sctp_sk(net->sctp.ctl_sock)->ep) { - SCTP_INC_STATS(SCTP_MIB_OUTOFBLUES); + SCTP_INC_STATS(net, SCTP_MIB_OUTOFBLUES); return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); } @@ -809,8 +811,8 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep, sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc)); sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, SCTP_STATE(SCTP_STATE_ESTABLISHED)); - SCTP_INC_STATS(SCTP_MIB_CURRESTAB); - SCTP_INC_STATS(SCTP_MIB_PASSIVEESTABS); + SCTP_INC_STATS(net, SCTP_MIB_CURRESTAB); + SCTP_INC_STATS(net, SCTP_MIB_PASSIVEESTABS); sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START, SCTP_NULL()); if (new_asoc->autoclose) @@ -868,6 +870,7 @@ sctp_disposition_t sctp_sf_do_5_1E_ca(const struct sctp_endpoint *ep, { struct sctp_chunk *chunk = arg; struct sctp_ulpevent *ev; + struct net *net; if (!sctp_vtag_verify(chunk, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); @@ -897,8 +900,9 @@ sctp_disposition_t sctp_sf_do_5_1E_ca(const struct sctp_endpoint *ep, SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE)); sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, SCTP_STATE(SCTP_STATE_ESTABLISHED)); - SCTP_INC_STATS(SCTP_MIB_CURRESTAB); - SCTP_INC_STATS(SCTP_MIB_ACTIVEESTABS); + net = sock_net(ep->base.sk); + SCTP_INC_STATS(net, SCTP_MIB_CURRESTAB); + SCTP_INC_STATS(net, SCTP_MIB_ACTIVEESTABS); sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START, SCTP_NULL()); if (asoc->autoclose) sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START, @@ -972,13 +976,15 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const struct sctp_endpoint *ep, struct sctp_transport *transport = (struct sctp_transport *) arg; if (asoc->overall_error_count >= asoc->max_retrans) { + struct net *net; sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, SCTP_ERROR(ETIMEDOUT)); /* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */ sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_PERR(SCTP_ERROR_NO_ERROR)); - SCTP_INC_STATS(SCTP_MIB_ABORTEDS); - SCTP_DEC_STATS(SCTP_MIB_CURRESTAB); + net = sock_net(ep->base.sk); + SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); + SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB); return SCTP_DISPOSITION_DELETE_TCB; } @@ -1213,7 +1219,7 @@ static int sctp_sf_send_restart_abort(struct net *net, union sctp_addr *ssa, goto out; sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, SCTP_PACKET(pkt)); - SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS); + SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS); /* Discard the rest of the inbound packet. */ sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET, SCTP_NULL()); @@ -1427,7 +1433,7 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( if (packet) { sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, SCTP_PACKET(packet)); - SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS); + SCTP_INC_STATS(sock_net(ep->base.sk), SCTP_MIB_OUTCTRLCHUNKS); retval = SCTP_DISPOSITION_CONSUME; } else { retval = SCTP_DISPOSITION_NOMEM; @@ -1791,7 +1797,7 @@ static sctp_disposition_t sctp_sf_do_dupcook_b(const struct sctp_endpoint *ep, sctp_add_cmd_sf(commands, SCTP_CMD_UPDATE_ASSOC, SCTP_ASOC(new_asoc)); sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, SCTP_STATE(SCTP_STATE_ESTABLISHED)); - SCTP_INC_STATS(SCTP_MIB_CURRESTAB); + SCTP_INC_STATS(sock_net(new_asoc->base.sk), SCTP_MIB_CURRESTAB); sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START, SCTP_NULL()); repl = sctp_make_cookie_ack(new_asoc, chunk); @@ -1883,7 +1889,7 @@ static sctp_disposition_t sctp_sf_do_dupcook_d(const struct sctp_endpoint *ep, SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE)); sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, SCTP_STATE(SCTP_STATE_ESTABLISHED)); - SCTP_INC_STATS(SCTP_MIB_CURRESTAB); + SCTP_INC_STATS(sock_net(asoc->base.sk), SCTP_MIB_CURRESTAB); sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START, SCTP_NULL()); @@ -2417,6 +2423,7 @@ static sctp_disposition_t __sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep, struct sctp_chunk *chunk = arg; unsigned int len; __be16 error = SCTP_ERROR_NO_ERROR; + struct net *net; /* See if we have an error cause code in the chunk. */ len = ntohs(chunk->chunk_hdr->length); @@ -2433,8 +2440,9 @@ static sctp_disposition_t __sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep, sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, SCTP_ERROR(ECONNRESET)); /* ASSOC_FAILED will DELETE_TCB. */ sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_PERR(error)); - SCTP_INC_STATS(SCTP_MIB_ABORTEDS); - SCTP_DEC_STATS(SCTP_MIB_CURRESTAB); + net = sock_net(ep->base.sk); + SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); + SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB); return SCTP_DISPOSITION_ABORT; } @@ -2521,7 +2529,7 @@ static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands, SCTP_DEBUG_PRINTK("ABORT received (INIT).\n"); sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, SCTP_STATE(SCTP_STATE_CLOSED)); - SCTP_INC_STATS(SCTP_MIB_ABORTEDS); + SCTP_INC_STATS(sock_net(asoc->base.sk), SCTP_MIB_ABORTEDS); sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, SCTP_ERROR(sk_err)); @@ -2904,11 +2912,11 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const struct sctp_endpoint *ep, break; case SCTP_IERROR_HIGH_TSN: case SCTP_IERROR_BAD_STREAM: - SCTP_INC_STATS(SCTP_MIB_IN_DATA_CHUNK_DISCARDS); + SCTP_INC_STATS(sock_net(asoc->base.sk), SCTP_MIB_IN_DATA_CHUNK_DISCARDS); goto discard_noforce; case SCTP_IERROR_DUP_TSN: case SCTP_IERROR_IGNORE_TSN: - SCTP_INC_STATS(SCTP_MIB_IN_DATA_CHUNK_DISCARDS); + SCTP_INC_STATS(sock_net(asoc->base.sk), SCTP_MIB_IN_DATA_CHUNK_DISCARDS); goto discard_force; case SCTP_IERROR_NO_DATA: goto consume; @@ -3197,7 +3205,7 @@ static sctp_disposition_t sctp_sf_tabort_8_4_8(const struct sctp_endpoint *ep, sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, SCTP_PACKET(packet)); - SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS); + SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS); sctp_sf_pdiscard(ep, asoc, type, arg, commands); return SCTP_DISPOSITION_CONSUME; @@ -3260,6 +3268,7 @@ sctp_disposition_t sctp_sf_do_9_2_final(const struct sctp_endpoint *ep, struct sctp_chunk *chunk = arg; struct sctp_chunk *reply; struct sctp_ulpevent *ev; + struct net *net; if (!sctp_vtag_verify(chunk, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); @@ -3299,8 +3308,9 @@ sctp_disposition_t sctp_sf_do_9_2_final(const struct sctp_endpoint *ep, sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, SCTP_STATE(SCTP_STATE_CLOSED)); - SCTP_INC_STATS(SCTP_MIB_SHUTDOWNS); - SCTP_DEC_STATS(SCTP_MIB_CURRESTAB); + net = sock_net(asoc->base.sk); + SCTP_INC_STATS(net, SCTP_MIB_SHUTDOWNS); + SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB); sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); /* ...and remove all record of the association. */ @@ -3346,8 +3356,10 @@ sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep, __u8 *ch_end; int ootb_shut_ack = 0; int ootb_cookie_ack = 0; + struct net *net; - SCTP_INC_STATS(SCTP_MIB_OUTOFBLUES); + net = sock_net(asoc->base.sk); + SCTP_INC_STATS(net, SCTP_MIB_OUTOFBLUES); ch = (sctp_chunkhdr_t *) chunk->chunk_hdr; do { @@ -3461,7 +3473,7 @@ static sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep, sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, SCTP_PACKET(packet)); - SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS); + SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS); /* If the chunk length is invalid, we don't want to process * the reset of the packet. @@ -3508,7 +3520,7 @@ sctp_disposition_t sctp_sf_do_8_5_1_E_sa(const struct sctp_endpoint *ep, * packet and the state function that handles OOTB SHUTDOWN_ACK is * called with a NULL association. */ - SCTP_INC_STATS(SCTP_MIB_OUTOFBLUES); + SCTP_INC_STATS(sock_net(ep->base.sk), SCTP_MIB_OUTOFBLUES); return sctp_sf_shut_8_4_5(ep, NULL, type, arg, commands); } @@ -3699,6 +3711,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep, */ if (ADDIP_SERIAL_gte(rcvd_serial, sent_serial + 1) && !(asoc->addip_last_asconf)) { + struct net *net; abort = sctp_make_abort(asoc, asconf_ack, sizeof(sctp_errhdr_t)); if (abort) { @@ -3716,12 +3729,14 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep, SCTP_ERROR(ECONNABORTED)); sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_PERR(SCTP_ERROR_ASCONF_ACK)); - SCTP_INC_STATS(SCTP_MIB_ABORTEDS); - SCTP_DEC_STATS(SCTP_MIB_CURRESTAB); + net = sock_net(asoc->base.sk); + SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); + SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB); return SCTP_DISPOSITION_ABORT; } if ((rcvd_serial == sent_serial) && asoc->addip_last_asconf) { + struct net *net; sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO)); @@ -3750,8 +3765,9 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep, SCTP_ERROR(ECONNABORTED)); sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_PERR(SCTP_ERROR_ASCONF_ACK)); - SCTP_INC_STATS(SCTP_MIB_ABORTEDS); - SCTP_DEC_STATS(SCTP_MIB_CURRESTAB); + net = sock_net(asoc->base.sk); + SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); + SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB); return SCTP_DISPOSITION_ABORT; } @@ -4222,7 +4238,7 @@ sctp_disposition_t sctp_sf_pdiscard(const struct sctp_endpoint *ep, void *arg, sctp_cmd_seq_t *commands) { - SCTP_INC_STATS(SCTP_MIB_IN_PKT_DISCARDS); + SCTP_INC_STATS(sock_net(asoc->base.sk), SCTP_MIB_IN_PKT_DISCARDS); sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET, SCTP_NULL()); return SCTP_DISPOSITION_CONSUME; @@ -4315,7 +4331,7 @@ static sctp_disposition_t sctp_sf_abort_violation( } sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort)); - SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS); + SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS); if (asoc->state <= SCTP_STATE_COOKIE_ECHOED) { sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, @@ -4329,7 +4345,7 @@ static sctp_disposition_t sctp_sf_abort_violation( SCTP_ERROR(ECONNABORTED)); sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_PERR(SCTP_ERROR_PROTO_VIOLATION)); - SCTP_DEC_STATS(SCTP_MIB_CURRESTAB); + SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB); } } else { packet = sctp_ootb_pkt_new(net, asoc, chunk); @@ -4347,10 +4363,10 @@ static sctp_disposition_t sctp_sf_abort_violation( sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, SCTP_PACKET(packet)); - SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS); + SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS); } - SCTP_INC_STATS(SCTP_MIB_ABORTEDS); + SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); discard: sctp_sf_pdiscard(ep, asoc, SCTP_ST_CHUNK(0), arg, commands); @@ -4410,6 +4426,7 @@ static sctp_disposition_t sctp_sf_violation_paramlen( struct sctp_chunk *chunk = arg; struct sctp_paramhdr *param = ext; struct sctp_chunk *abort = NULL; + struct net *net; if (sctp_auth_recv_cid(SCTP_CID_ABORT, asoc)) goto discard; @@ -4419,15 +4436,16 @@ static sctp_disposition_t sctp_sf_violation_paramlen( if (!abort) goto nomem; + net = sock_net(asoc->base.sk); sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort)); - SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS); + SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS); sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, SCTP_ERROR(ECONNABORTED)); sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_PERR(SCTP_ERROR_PROTO_VIOLATION)); - SCTP_DEC_STATS(SCTP_MIB_CURRESTAB); - SCTP_INC_STATS(SCTP_MIB_ABORTEDS); + SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB); + SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); discard: sctp_sf_pdiscard(ep, asoc, SCTP_ST_CHUNK(0), arg, commands); @@ -4757,6 +4775,7 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort( */ struct sctp_chunk *abort = arg; sctp_disposition_t retval; + struct net *net; retval = SCTP_DISPOSITION_CONSUME; @@ -4772,8 +4791,9 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort( sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_PERR(SCTP_ERROR_USER_ABORT)); - SCTP_INC_STATS(SCTP_MIB_ABORTEDS); - SCTP_DEC_STATS(SCTP_MIB_CURRESTAB); + net = sock_net(asoc->base.sk); + SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); + SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB); return retval; } @@ -4824,13 +4844,15 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_shutdown( void *arg, sctp_cmd_seq_t *commands) { + struct net *net = sock_net(asoc->base.sk); + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, SCTP_STATE(SCTP_STATE_CLOSED)); - SCTP_INC_STATS(SCTP_MIB_SHUTDOWNS); + SCTP_INC_STATS(net, SCTP_MIB_SHUTDOWNS); sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); @@ -4886,6 +4908,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort( { struct sctp_chunk *abort = arg; sctp_disposition_t retval; + struct net *net = sock_net(asoc->base.sk); /* Stop T1-init timer */ sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, @@ -4897,7 +4920,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort( sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, SCTP_STATE(SCTP_STATE_CLOSED)); - SCTP_INC_STATS(SCTP_MIB_ABORTEDS); + SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); /* Even if we can't send the ABORT due to low memory delete the * TCB. This is a departure from our typical NOMEM handling. @@ -5318,8 +5341,9 @@ sctp_disposition_t sctp_sf_do_6_3_3_rtx(const struct sctp_endpoint *ep, sctp_cmd_seq_t *commands) { struct sctp_transport *transport = arg; + struct net *net = sock_net(asoc->base.sk); - SCTP_INC_STATS(SCTP_MIB_T3_RTX_EXPIREDS); + SCTP_INC_STATS(net, SCTP_MIB_T3_RTX_EXPIREDS); if (asoc->overall_error_count >= asoc->max_retrans) { if (asoc->state == SCTP_STATE_SHUTDOWN_PENDING) { @@ -5340,8 +5364,8 @@ sctp_disposition_t sctp_sf_do_6_3_3_rtx(const struct sctp_endpoint *ep, /* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */ sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_PERR(SCTP_ERROR_NO_ERROR)); - SCTP_INC_STATS(SCTP_MIB_ABORTEDS); - SCTP_DEC_STATS(SCTP_MIB_CURRESTAB); + SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); + SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB); return SCTP_DISPOSITION_DELETE_TCB; } } @@ -5403,7 +5427,8 @@ sctp_disposition_t sctp_sf_do_6_2_sack(const struct sctp_endpoint *ep, void *arg, sctp_cmd_seq_t *commands) { - SCTP_INC_STATS(SCTP_MIB_DELAY_SACK_EXPIREDS); + struct net *net = sock_net(asoc->base.sk); + SCTP_INC_STATS(net, SCTP_MIB_DELAY_SACK_EXPIREDS); sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_FORCE()); return SCTP_DISPOSITION_CONSUME; } @@ -5436,9 +5461,10 @@ sctp_disposition_t sctp_sf_t1_init_timer_expire(const struct sctp_endpoint *ep, struct sctp_chunk *repl = NULL; struct sctp_bind_addr *bp; int attempts = asoc->init_err_counter + 1; + struct net *net = sock_net(asoc->base.sk); SCTP_DEBUG_PRINTK("Timer T1 expired (INIT).\n"); - SCTP_INC_STATS(SCTP_MIB_T1_INIT_EXPIREDS); + SCTP_INC_STATS(net, SCTP_MIB_T1_INIT_EXPIREDS); if (attempts <= asoc->max_init_attempts) { bp = (struct sctp_bind_addr *) &asoc->base.bind_addr; @@ -5496,9 +5522,10 @@ sctp_disposition_t sctp_sf_t1_cookie_timer_expire(const struct sctp_endpoint *ep { struct sctp_chunk *repl = NULL; int attempts = asoc->init_err_counter + 1; + struct net *net = sock_net(asoc->base.sk); SCTP_DEBUG_PRINTK("Timer T1 expired (COOKIE-ECHO).\n"); - SCTP_INC_STATS(SCTP_MIB_T1_COOKIE_EXPIREDS); + SCTP_INC_STATS(net, SCTP_MIB_T1_COOKIE_EXPIREDS); if (attempts <= asoc->max_init_attempts) { repl = sctp_make_cookie_echo(asoc, NULL); @@ -5543,9 +5570,10 @@ sctp_disposition_t sctp_sf_t2_timer_expire(const struct sctp_endpoint *ep, sctp_cmd_seq_t *commands) { struct sctp_chunk *reply = NULL; + struct net *net = sock_net(asoc->base.sk); SCTP_DEBUG_PRINTK("Timer T2 expired.\n"); - SCTP_INC_STATS(SCTP_MIB_T2_SHUTDOWN_EXPIREDS); + SCTP_INC_STATS(net, SCTP_MIB_T2_SHUTDOWN_EXPIREDS); ((struct sctp_association *)asoc)->shutdown_retries++; @@ -5555,8 +5583,8 @@ sctp_disposition_t sctp_sf_t2_timer_expire(const struct sctp_endpoint *ep, /* Note: CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */ sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_PERR(SCTP_ERROR_NO_ERROR)); - SCTP_INC_STATS(SCTP_MIB_ABORTEDS); - SCTP_DEC_STATS(SCTP_MIB_CURRESTAB); + SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); + SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB); return SCTP_DISPOSITION_DELETE_TCB; } @@ -5613,8 +5641,9 @@ sctp_disposition_t sctp_sf_t4_timer_expire( { struct sctp_chunk *chunk = asoc->addip_last_asconf; struct sctp_transport *transport = chunk->transport; + struct net *net = sock_net(asoc->base.sk); - SCTP_INC_STATS(SCTP_MIB_T4_RTO_EXPIREDS); + SCTP_INC_STATS(net, SCTP_MIB_T4_RTO_EXPIREDS); /* ADDIP 4.1 B1) Increment the error counters and perform path failure * detection on the appropriate destination address as defined in @@ -5639,8 +5668,8 @@ sctp_disposition_t sctp_sf_t4_timer_expire( SCTP_ERROR(ETIMEDOUT)); sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_PERR(SCTP_ERROR_NO_ERROR)); - SCTP_INC_STATS(SCTP_MIB_ABORTEDS); - SCTP_DEC_STATS(SCTP_MIB_CURRESTAB); + SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); + SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB); return SCTP_DISPOSITION_ABORT; } @@ -5682,9 +5711,10 @@ sctp_disposition_t sctp_sf_t5_timer_expire(const struct sctp_endpoint *ep, sctp_cmd_seq_t *commands) { struct sctp_chunk *reply = NULL; + struct net *net = sock_net(asoc->base.sk); SCTP_DEBUG_PRINTK("Timer T5 expired.\n"); - SCTP_INC_STATS(SCTP_MIB_T5_SHUTDOWN_GUARD_EXPIREDS); + SCTP_INC_STATS(net, SCTP_MIB_T5_SHUTDOWN_GUARD_EXPIREDS); reply = sctp_make_abort(asoc, NULL, 0); if (!reply) @@ -5696,8 +5726,8 @@ sctp_disposition_t sctp_sf_t5_timer_expire(const struct sctp_endpoint *ep, sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_PERR(SCTP_ERROR_NO_ERROR)); - SCTP_INC_STATS(SCTP_MIB_ABORTEDS); - SCTP_DEC_STATS(SCTP_MIB_CURRESTAB); + SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); + SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB); return SCTP_DISPOSITION_DELETE_TCB; nomem: @@ -5716,9 +5746,10 @@ sctp_disposition_t sctp_sf_autoclose_timer_expire( void *arg, sctp_cmd_seq_t *commands) { + struct net *net = sock_net(asoc->base.sk); int disposition; - SCTP_INC_STATS(SCTP_MIB_AUTOCLOSE_EXPIREDS); + SCTP_INC_STATS(net, SCTP_MIB_AUTOCLOSE_EXPIREDS); /* From 9.2 Shutdown of an Association * Upon receipt of the SHUTDOWN primitive from its upper @@ -5976,7 +6007,7 @@ static void sctp_send_stale_cookie_err(const struct sctp_endpoint *ep, sctp_packet_append_chunk(packet, err_chunk); sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, SCTP_PACKET(packet)); - SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS); + SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS); } else sctp_chunk_free (err_chunk); } @@ -5996,6 +6027,7 @@ static int sctp_eat_data(const struct sctp_association *asoc, __u32 tsn; struct sctp_tsnmap *map = (struct sctp_tsnmap *)&asoc->peer.tsn_map; struct sock *sk = asoc->base.sk; + struct net *net; u16 ssn; u16 sid; u8 ordered = 0; @@ -6112,6 +6144,7 @@ static int sctp_eat_data(const struct sctp_association *asoc, * No User Data: This error cause is returned to the originator of a * DATA chunk if a received DATA chunk has no user data. */ + net = sock_net(sk); if (unlikely(0 == datalen)) { err = sctp_make_abort_no_data(asoc, chunk, tsn); if (err) { @@ -6126,8 +6159,8 @@ static int sctp_eat_data(const struct sctp_association *asoc, SCTP_ERROR(ECONNABORTED)); sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_PERR(SCTP_ERROR_NO_DATA)); - SCTP_INC_STATS(SCTP_MIB_ABORTEDS); - SCTP_DEC_STATS(SCTP_MIB_CURRESTAB); + SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); + SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB); return SCTP_IERROR_NO_DATA; } @@ -6137,9 +6170,9 @@ static int sctp_eat_data(const struct sctp_association *asoc, * if we renege and the chunk arrives again. */ if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) - SCTP_INC_STATS(SCTP_MIB_INUNORDERCHUNKS); + SCTP_INC_STATS(net, SCTP_MIB_INUNORDERCHUNKS); else { - SCTP_INC_STATS(SCTP_MIB_INORDERCHUNKS); + SCTP_INC_STATS(net, SCTP_MIB_INORDERCHUNKS); ordered = 1; } diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c index f5a6a4f4faf7..360d8697b95c 100644 --- a/net/sctp/ulpqueue.c +++ b/net/sctp/ulpqueue.c @@ -326,7 +326,9 @@ static void sctp_ulpq_store_reasm(struct sctp_ulpq *ulpq, * payload was fragmented on the way and ip had to reassemble them. * We add the rest of skb's to the first skb's fraglist. */ -static struct sctp_ulpevent *sctp_make_reassembled_event(struct sk_buff_head *queue, struct sk_buff *f_frag, struct sk_buff *l_frag) +static struct sctp_ulpevent *sctp_make_reassembled_event(struct net *net, + struct sk_buff_head *queue, struct sk_buff *f_frag, + struct sk_buff *l_frag) { struct sk_buff *pos; struct sk_buff *new = NULL; @@ -394,7 +396,7 @@ static struct sctp_ulpevent *sctp_make_reassembled_event(struct sk_buff_head *qu } event = sctp_skb2event(f_frag); - SCTP_INC_STATS(SCTP_MIB_REASMUSRMSGS); + SCTP_INC_STATS(net, SCTP_MIB_REASMUSRMSGS); return event; } @@ -493,7 +495,8 @@ static struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_ulpq *ul cevent = sctp_skb2event(pd_first); pd_point = sctp_sk(asoc->base.sk)->pd_point; if (pd_point && pd_point <= pd_len) { - retval = sctp_make_reassembled_event(&ulpq->reasm, + retval = sctp_make_reassembled_event(sock_net(asoc->base.sk), + &ulpq->reasm, pd_first, pd_last); if (retval) @@ -503,7 +506,8 @@ static struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_ulpq *ul done: return retval; found: - retval = sctp_make_reassembled_event(&ulpq->reasm, first_frag, pos); + retval = sctp_make_reassembled_event(sock_net(ulpq->asoc->base.sk), + &ulpq->reasm, first_frag, pos); if (retval) retval->msg_flags |= MSG_EOR; goto done; @@ -563,7 +567,8 @@ static struct sctp_ulpevent *sctp_ulpq_retrieve_partial(struct sctp_ulpq *ulpq) * further. */ done: - retval = sctp_make_reassembled_event(&ulpq->reasm, first_frag, last_frag); + retval = sctp_make_reassembled_event(sock_net(ulpq->asoc->base.sk), + &ulpq->reasm, first_frag, last_frag); if (retval && is_last) retval->msg_flags |= MSG_EOR; @@ -655,7 +660,8 @@ static struct sctp_ulpevent *sctp_ulpq_retrieve_first(struct sctp_ulpq *ulpq) * further. */ done: - retval = sctp_make_reassembled_event(&ulpq->reasm, first_frag, last_frag); + retval = sctp_make_reassembled_event(sock_net(ulpq->asoc->base.sk), + &ulpq->reasm, first_frag, last_frag); return retval; } -- cgit v1.2.3 From ebb7e95d9351f77a8ec1fca20eb645051401b7b2 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 7 Aug 2012 07:23:59 +0000 Subject: sctp: Add infrastructure for per net sysctls Start with an empty sctp_net_table that will be populated as the various tunable sysctls are made per net. Signed-off-by: "Eric W. Biederman" Acked-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/net/netns/sctp.h | 6 +++++- include/net/sctp/sctp.h | 4 ++++ net/sctp/protocol.c | 7 +++++++ net/sctp/sysctl.c | 21 +++++++++++++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/netns/sctp.h b/include/net/netns/sctp.h index 06ccddf9566c..9576b60cbd25 100644 --- a/include/net/netns/sctp.h +++ b/include/net/netns/sctp.h @@ -4,6 +4,7 @@ struct sock; struct proc_dir_entry; struct sctp_mib; +struct ctl_table_header; struct netns_sctp { DEFINE_SNMP_STAT(struct sctp_mib, sctp_statistics); @@ -11,7 +12,9 @@ struct netns_sctp { #ifdef CONFIG_PROC_FS struct proc_dir_entry *proc_net_sctp; #endif - +#ifdef CONFIG_SYSCTL + struct ctl_table_header *sysctl_header; +#endif /* This is the global socket data structure used for responding to * the Out-of-the-blue (OOTB) packets. A control sock will be created * for this socket at the initialization time. @@ -32,6 +35,7 @@ struct netns_sctp { /* Lock that protects the local_addr_list writers */ spinlock_t local_addr_lock; + }; #endif /* __NETNS_SCTP_H__ */ diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 7dcd4dfd7c3f..c07421d1772f 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -375,9 +375,13 @@ static inline void sctp_dbg_objcnt_exit(struct net *) { return; } #if defined CONFIG_SYSCTL void sctp_sysctl_register(void); void sctp_sysctl_unregister(void); +int sctp_sysctl_net_register(struct net *net); +void sctp_sysctl_net_unregister(struct net *net); #else static inline void sctp_sysctl_register(void) { return; } static inline void sctp_sysctl_unregister(void) { return; } +static inline int sctp_sysctl_net_register(struct net *net) { return 0; } +static inline void sctp_sysctl_net_unregister(struct net *net) { return; } #endif /* Size of Supported Address Parameter for 'x' address types. */ diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index d58db315db85..0f2342be61f3 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1169,6 +1169,10 @@ static int sctp_net_init(struct net *net) { int status; + status = sctp_sysctl_net_register(net); + if (status) + goto err_sysctl_register; + /* Allocate and initialise sctp mibs. */ status = init_sctp_mibs(net); if (status) @@ -1208,6 +1212,8 @@ err_ctl_sock_init: err_init_proc: cleanup_sctp_mibs(net); err_init_mibs: + sctp_sysctl_net_unregister(net); +err_sysctl_register: return status; } @@ -1224,6 +1230,7 @@ static void sctp_net_exit(struct net *net) sctp_proc_exit(net); cleanup_sctp_mibs(net); + sctp_sysctl_net_unregister(net); } static struct pernet_operations sctp_net_ops = { diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index 2b2bfe933ff1..bee36c408dde 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -284,6 +284,27 @@ static ctl_table sctp_table[] = { { /* sentinel */ } }; +static ctl_table sctp_net_table[] = { + { /* sentinel */ } +}; + +int sctp_sysctl_net_register(struct net *net) +{ + struct ctl_table *table; + + table = kmemdup(sctp_net_table, sizeof(sctp_net_table), GFP_KERNEL); + if (!table) + return -ENOMEM; + + net->sctp.sysctl_header = register_net_sysctl(net, "net/sctp", table); + return 0; +} + +void sctp_sysctl_net_unregister(struct net *net) +{ + unregister_net_sysctl_table(net->sctp.sysctl_header); +} + static struct ctl_table_header * sctp_sysctl_header; /* Sysctl registration. */ -- cgit v1.2.3 From 55e26eb95a5345a5796babac98de6d6c42771df1 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 7 Aug 2012 07:25:24 +0000 Subject: sctp: Push struct net down to sctp_chunk_event_lookup This trickles up through sctp_sm_lookup_event up to sctp_do_sm and up further into sctp_primitiv_NAME before the code reaches places where struct net can be reliably found. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 12 ++++++------ include/net/sctp/sm.h | 5 +++-- net/sctp/associola.c | 5 +++-- net/sctp/endpointola.c | 4 +++- net/sctp/input.c | 4 +++- net/sctp/primitive.c | 4 ++-- net/sctp/sm_sideeffect.c | 24 ++++++++++++++++-------- net/sctp/sm_statetable.c | 11 +++++++---- net/sctp/socket.c | 27 +++++++++++++++++---------- 9 files changed, 60 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index c07421d1772f..aaa82923bbaa 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -139,12 +139,12 @@ extern int sctp_asconf_mgmt(struct sctp_sock *, struct sctp_sockaddr_entry *); /* * sctp/primitive.c */ -int sctp_primitive_ASSOCIATE(struct sctp_association *, void *arg); -int sctp_primitive_SHUTDOWN(struct sctp_association *, void *arg); -int sctp_primitive_ABORT(struct sctp_association *, void *arg); -int sctp_primitive_SEND(struct sctp_association *, void *arg); -int sctp_primitive_REQUESTHEARTBEAT(struct sctp_association *, void *arg); -int sctp_primitive_ASCONF(struct sctp_association *, void *arg); +int sctp_primitive_ASSOCIATE(struct net *, struct sctp_association *, void *arg); +int sctp_primitive_SHUTDOWN(struct net *, struct sctp_association *, void *arg); +int sctp_primitive_ABORT(struct net *, struct sctp_association *, void *arg); +int sctp_primitive_SEND(struct net *, struct sctp_association *, void *arg); +int sctp_primitive_REQUESTHEARTBEAT(struct net *, struct sctp_association *, void *arg); +int sctp_primitive_ASCONF(struct net *, struct sctp_association *, void *arg); /* * sctp/input.c diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index 9148632b8204..bcef13023ac3 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -178,7 +178,8 @@ sctp_state_fn_t sctp_sf_autoclose_timer_expire; /* Prototypes for utility support functions. */ __u8 sctp_get_chunk_type(struct sctp_chunk *chunk); -const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t, +const sctp_sm_table_entry_t *sctp_sm_lookup_event(struct net *, + sctp_event_t, sctp_state_t, sctp_subtype_t); int sctp_chunk_iif(const struct sctp_chunk *); @@ -268,7 +269,7 @@ void sctp_chunk_assign_ssn(struct sctp_chunk *); /* Prototypes for statetable processing. */ -int sctp_do_sm(sctp_event_t event_type, sctp_subtype_t subtype, +int sctp_do_sm(struct net *net, sctp_event_t event_type, sctp_subtype_t subtype, sctp_state_t state, struct sctp_endpoint *, struct sctp_association *asoc, diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 8a1f27a7a001..6bcbecafe393 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -1118,6 +1118,7 @@ static void sctp_assoc_bh_rcv(struct work_struct *work) struct sctp_association *asoc = container_of(work, struct sctp_association, base.inqueue.immediate); + struct net *net = sock_net(asoc->base.sk); struct sctp_endpoint *ep; struct sctp_chunk *chunk; struct sctp_inq *inqueue; @@ -1150,13 +1151,13 @@ static void sctp_assoc_bh_rcv(struct work_struct *work) if (sctp_chunk_is_data(chunk)) asoc->peer.last_data_from = chunk->transport; else - SCTP_INC_STATS(sock_net(asoc->base.sk), SCTP_MIB_INCTRLCHUNKS); + SCTP_INC_STATS(net, SCTP_MIB_INCTRLCHUNKS); if (chunk->transport) chunk->transport->last_time_heard = jiffies; /* Run through the state machine. */ - error = sctp_do_sm(SCTP_EVENT_T_CHUNK, subtype, + error = sctp_do_sm(net, SCTP_EVENT_T_CHUNK, subtype, state, ep, asoc, chunk, GFP_ATOMIC); /* Check to see if the association is freed in response to diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index 3edca80ab3a1..8315792ef2ba 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -413,6 +413,7 @@ static void sctp_endpoint_bh_rcv(struct work_struct *work) base.inqueue.immediate); struct sctp_association *asoc; struct sock *sk; + struct net *net; struct sctp_transport *transport; struct sctp_chunk *chunk; struct sctp_inq *inqueue; @@ -427,6 +428,7 @@ static void sctp_endpoint_bh_rcv(struct work_struct *work) asoc = NULL; inqueue = &ep->base.inqueue; sk = ep->base.sk; + net = sock_net(sk); while (NULL != (chunk = sctp_inq_pop(inqueue))) { subtype = SCTP_ST_CHUNK(chunk->chunk_hdr->type); @@ -483,7 +485,7 @@ normal: if (chunk->transport) chunk->transport->last_time_heard = jiffies; - error = sctp_do_sm(SCTP_EVENT_T_CHUNK, subtype, state, + error = sctp_do_sm(net, SCTP_EVENT_T_CHUNK, subtype, state, ep, asoc, chunk, GFP_ATOMIC); if (error && chunk) diff --git a/net/sctp/input.c b/net/sctp/input.c index 530830151f04..a2ceb70ee06c 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -466,11 +466,13 @@ void sctp_icmp_proto_unreachable(struct sock *sk, } } else { + struct net *net = sock_net(sk); + if (timer_pending(&t->proto_unreach_timer) && del_timer(&t->proto_unreach_timer)) sctp_association_put(asoc); - sctp_do_sm(SCTP_EVENT_T_OTHER, + sctp_do_sm(net, SCTP_EVENT_T_OTHER, SCTP_ST_OTHER(SCTP_EVENT_ICMP_PROTO_UNREACH), asoc->state, asoc->ep, asoc, t, GFP_ATOMIC); diff --git a/net/sctp/primitive.c b/net/sctp/primitive.c index 534c7eae9d15..794bb14decde 100644 --- a/net/sctp/primitive.c +++ b/net/sctp/primitive.c @@ -57,7 +57,7 @@ #define DECLARE_PRIMITIVE(name) \ /* This is called in the code as sctp_primitive_ ## name. */ \ -int sctp_primitive_ ## name(struct sctp_association *asoc, \ +int sctp_primitive_ ## name(struct net *net, struct sctp_association *asoc, \ void *arg) { \ int error = 0; \ sctp_event_t event_type; sctp_subtype_t subtype; \ @@ -69,7 +69,7 @@ int sctp_primitive_ ## name(struct sctp_association *asoc, \ state = asoc ? asoc->state : SCTP_STATE_CLOSED; \ ep = asoc ? asoc->ep : NULL; \ \ - error = sctp_do_sm(event_type, subtype, state, ep, asoc, \ + error = sctp_do_sm(net, event_type, subtype, state, ep, asoc, \ arg, GFP_KERNEL); \ return error; \ } diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index fe99628e1257..02c4c1c066a7 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -251,6 +251,7 @@ void sctp_generate_t3_rtx_event(unsigned long peer) int error; struct sctp_transport *transport = (struct sctp_transport *) peer; struct sctp_association *asoc = transport->asoc; + struct net *net = sock_net(asoc->base.sk); /* Check whether a task is in the sock. */ @@ -271,7 +272,7 @@ void sctp_generate_t3_rtx_event(unsigned long peer) goto out_unlock; /* Run through the state machine. */ - error = sctp_do_sm(SCTP_EVENT_T_TIMEOUT, + error = sctp_do_sm(net, SCTP_EVENT_T_TIMEOUT, SCTP_ST_TIMEOUT(SCTP_EVENT_TIMEOUT_T3_RTX), asoc->state, asoc->ep, asoc, @@ -291,6 +292,7 @@ out_unlock: static void sctp_generate_timeout_event(struct sctp_association *asoc, sctp_event_timeout_t timeout_type) { + struct net *net = sock_net(asoc->base.sk); int error = 0; sctp_bh_lock_sock(asoc->base.sk); @@ -312,7 +314,7 @@ static void sctp_generate_timeout_event(struct sctp_association *asoc, goto out_unlock; /* Run through the state machine. */ - error = sctp_do_sm(SCTP_EVENT_T_TIMEOUT, + error = sctp_do_sm(net, SCTP_EVENT_T_TIMEOUT, SCTP_ST_TIMEOUT(timeout_type), asoc->state, asoc->ep, asoc, (void *)timeout_type, GFP_ATOMIC); @@ -371,6 +373,7 @@ void sctp_generate_heartbeat_event(unsigned long data) int error = 0; struct sctp_transport *transport = (struct sctp_transport *) data; struct sctp_association *asoc = transport->asoc; + struct net *net = sock_net(asoc->base.sk); sctp_bh_lock_sock(asoc->base.sk); if (sock_owned_by_user(asoc->base.sk)) { @@ -388,7 +391,7 @@ void sctp_generate_heartbeat_event(unsigned long data) if (transport->dead) goto out_unlock; - error = sctp_do_sm(SCTP_EVENT_T_TIMEOUT, + error = sctp_do_sm(net, SCTP_EVENT_T_TIMEOUT, SCTP_ST_TIMEOUT(SCTP_EVENT_TIMEOUT_HEARTBEAT), asoc->state, asoc->ep, asoc, transport, GFP_ATOMIC); @@ -408,6 +411,7 @@ void sctp_generate_proto_unreach_event(unsigned long data) { struct sctp_transport *transport = (struct sctp_transport *) data; struct sctp_association *asoc = transport->asoc; + struct net *net = sock_net(asoc->base.sk); sctp_bh_lock_sock(asoc->base.sk); if (sock_owned_by_user(asoc->base.sk)) { @@ -426,7 +430,7 @@ void sctp_generate_proto_unreach_event(unsigned long data) if (asoc->base.dead) goto out_unlock; - sctp_do_sm(SCTP_EVENT_T_OTHER, + sctp_do_sm(net, SCTP_EVENT_T_OTHER, SCTP_ST_OTHER(SCTP_EVENT_ICMP_PROTO_UNREACH), asoc->state, asoc->ep, asoc, transport, GFP_ATOMIC); @@ -753,8 +757,10 @@ static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds, int err = 0; if (sctp_outq_sack(&asoc->outqueue, sackh)) { + struct net *net = sock_net(asoc->base.sk); + /* There are no more TSNs awaiting SACK. */ - err = sctp_do_sm(SCTP_EVENT_T_OTHER, + err = sctp_do_sm(net, SCTP_EVENT_T_OTHER, SCTP_ST_OTHER(SCTP_EVENT_NO_PENDING_TSN), asoc->state, asoc->ep, asoc, NULL, GFP_ATOMIC); @@ -1042,6 +1048,8 @@ static int sctp_cmd_send_msg(struct sctp_association *asoc, */ static void sctp_cmd_send_asconf(struct sctp_association *asoc) { + struct net *net = sock_net(asoc->base.sk); + /* Send the next asconf chunk from the addip chunk * queue. */ @@ -1053,7 +1061,7 @@ static void sctp_cmd_send_asconf(struct sctp_association *asoc) /* Hold the chunk until an ASCONF_ACK is received. */ sctp_chunk_hold(asconf); - if (sctp_primitive_ASCONF(asoc, asconf)) + if (sctp_primitive_ASCONF(net, asoc, asconf)) sctp_chunk_free(asconf); else asoc->addip_last_asconf = asconf; @@ -1089,7 +1097,7 @@ static void sctp_cmd_send_asconf(struct sctp_association *asoc) * If you want to understand all of lksctp, this is a * good place to start. */ -int sctp_do_sm(sctp_event_t event_type, sctp_subtype_t subtype, +int sctp_do_sm(struct net *net, sctp_event_t event_type, sctp_subtype_t subtype, sctp_state_t state, struct sctp_endpoint *ep, struct sctp_association *asoc, @@ -1110,7 +1118,7 @@ int sctp_do_sm(sctp_event_t event_type, sctp_subtype_t subtype, /* Look up the state function, run it, and then process the * side effects. These three steps are the heart of lksctp. */ - state_fn = sctp_sm_lookup_event(event_type, state, subtype); + state_fn = sctp_sm_lookup_event(net, event_type, state, subtype); sctp_init_cmd_seq(&commands); diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c index 7c211a7f90f4..4a029d798287 100644 --- a/net/sctp/sm_statetable.c +++ b/net/sctp/sm_statetable.c @@ -59,7 +59,8 @@ other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STATES]; static const sctp_sm_table_entry_t timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][SCTP_STATE_NUM_STATES]; -static const sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t cid, +static const sctp_sm_table_entry_t *sctp_chunk_event_lookup(struct net *net, + sctp_cid_t cid, sctp_state_t state); @@ -82,13 +83,14 @@ static const sctp_sm_table_entry_t bug = { rtn; \ }) -const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, +const sctp_sm_table_entry_t *sctp_sm_lookup_event(struct net *net, + sctp_event_t event_type, sctp_state_t state, sctp_subtype_t event_subtype) { switch (event_type) { case SCTP_EVENT_T_CHUNK: - return sctp_chunk_event_lookup(event_subtype.chunk, state); + return sctp_chunk_event_lookup(net, event_subtype.chunk, state); case SCTP_EVENT_T_TIMEOUT: return DO_LOOKUP(SCTP_EVENT_TIMEOUT_MAX, timeout, timeout_event_table); @@ -906,7 +908,8 @@ static const sctp_sm_table_entry_t timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][S TYPE_SCTP_EVENT_TIMEOUT_AUTOCLOSE, }; -static const sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t cid, +static const sctp_sm_table_entry_t *sctp_chunk_event_lookup(struct net *net, + sctp_cid_t cid, sctp_state_t state) { if (state > SCTP_STATE_MAX) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 5b6dd0e3d1f6..a6a4226a922f 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -427,6 +427,7 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len) static int sctp_send_asconf(struct sctp_association *asoc, struct sctp_chunk *chunk) { + struct net *net = sock_net(asoc->base.sk); int retval = 0; /* If there is an outstanding ASCONF chunk, queue it for later @@ -439,7 +440,7 @@ static int sctp_send_asconf(struct sctp_association *asoc, /* Hold the chunk until an ASCONF_ACK is received. */ sctp_chunk_hold(chunk); - retval = sctp_primitive_ASCONF(asoc, chunk); + retval = sctp_primitive_ASCONF(net, asoc, chunk); if (retval) sctp_chunk_free(chunk); else @@ -1050,6 +1051,7 @@ static int __sctp_connect(struct sock* sk, int addrs_size, sctp_assoc_t *assoc_id) { + struct net *net = sock_net(sk); struct sctp_sock *sp; struct sctp_endpoint *ep; struct sctp_association *asoc = NULL; @@ -1200,7 +1202,7 @@ static int __sctp_connect(struct sock* sk, goto out_free; } - err = sctp_primitive_ASSOCIATE(asoc, NULL); + err = sctp_primitive_ASSOCIATE(net, asoc, NULL); if (err < 0) { goto out_free; } @@ -1458,6 +1460,7 @@ SCTP_STATIC int sctp_getsockopt_connectx3(struct sock* sk, int len, */ SCTP_STATIC void sctp_close(struct sock *sk, long timeout) { + struct net *net = sock_net(sk); struct sctp_endpoint *ep; struct sctp_association *asoc; struct list_head *pos, *temp; @@ -1499,9 +1502,9 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout) chunk = sctp_make_abort_user(asoc, NULL, 0); if (chunk) - sctp_primitive_ABORT(asoc, chunk); + sctp_primitive_ABORT(net, asoc, chunk); } else - sctp_primitive_SHUTDOWN(asoc, NULL); + sctp_primitive_SHUTDOWN(net, asoc, NULL); } /* On a TCP-style socket, block for at most linger_time if set. */ @@ -1569,6 +1572,7 @@ SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *, sctp_cmsgs_t *); SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t msg_len) { + struct net *net = sock_net(sk); struct sctp_sock *sp; struct sctp_endpoint *ep; struct sctp_association *new_asoc=NULL, *asoc=NULL; @@ -1714,7 +1718,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, if (sinfo_flags & SCTP_EOF) { SCTP_DEBUG_PRINTK("Shutting down association: %p\n", asoc); - sctp_primitive_SHUTDOWN(asoc, NULL); + sctp_primitive_SHUTDOWN(net, asoc, NULL); err = 0; goto out_unlock; } @@ -1727,7 +1731,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, } SCTP_DEBUG_PRINTK("Aborting association: %p\n", asoc); - sctp_primitive_ABORT(asoc, chunk); + sctp_primitive_ABORT(net, asoc, chunk); err = 0; goto out_unlock; } @@ -1900,7 +1904,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, /* Auto-connect, if we aren't connected already. */ if (sctp_state(asoc, CLOSED)) { - err = sctp_primitive_ASSOCIATE(asoc, NULL); + err = sctp_primitive_ASSOCIATE(net, asoc, NULL); if (err < 0) goto out_free; SCTP_DEBUG_PRINTK("We associated primitively.\n"); @@ -1928,7 +1932,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, * works that way today. Keep it that way or this * breaks. */ - err = sctp_primitive_SEND(asoc, datamsg); + err = sctp_primitive_SEND(net, asoc, datamsg); /* Did the lower layer accept the chunk? */ if (err) sctp_datamsg_free(datamsg); @@ -2320,7 +2324,9 @@ static int sctp_apply_peer_addr_params(struct sctp_paddrparams *params, int error; if (params->spp_flags & SPP_HB_DEMAND && trans) { - error = sctp_primitive_REQUESTHEARTBEAT (trans->asoc, trans); + struct net *net = sock_net(trans->asoc->base.sk); + + error = sctp_primitive_REQUESTHEARTBEAT(net, trans->asoc, trans); if (error) return error; } @@ -4011,6 +4017,7 @@ SCTP_STATIC void sctp_destroy_sock(struct sock *sk) */ SCTP_STATIC void sctp_shutdown(struct sock *sk, int how) { + struct net *net = sock_net(sk); struct sctp_endpoint *ep; struct sctp_association *asoc; @@ -4022,7 +4029,7 @@ SCTP_STATIC void sctp_shutdown(struct sock *sk, int how) if (!list_empty(&ep->asocs)) { asoc = list_entry(ep->asocs.next, struct sctp_association, asocs); - sctp_primitive_SHUTDOWN(asoc, NULL); + sctp_primitive_SHUTDOWN(net, asoc, NULL); } } } -- cgit v1.2.3 From 89bf3450cb9b041b1bb4bcc5e7cbdeab4545b1c1 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 7 Aug 2012 07:26:14 +0000 Subject: sctp: Push struct net down into sctp_transport_init Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 2 +- net/sctp/associola.c | 3 ++- net/sctp/sm_statefuns.c | 2 +- net/sctp/transport.c | 8 +++++--- 4 files changed, 9 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 6bdfcabe560e..88d217941579 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1066,7 +1066,7 @@ struct sctp_transport { __u64 hb_nonce; }; -struct sctp_transport *sctp_transport_new(const union sctp_addr *, +struct sctp_transport *sctp_transport_new(struct net *, const union sctp_addr *, gfp_t); void sctp_transport_set_owner(struct sctp_transport *, struct sctp_association *); diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 6bcbecafe393..93a4513c85e0 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -641,6 +641,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, const gfp_t gfp, const int peer_state) { + struct net *net = sock_net(asoc->base.sk); struct sctp_transport *peer; struct sctp_sock *sp; unsigned short port; @@ -674,7 +675,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, return peer; } - peer = sctp_transport_new(addr, gfp); + peer = sctp_transport_new(net, addr, gfp); if (!peer) return NULL; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index bee5e2c288d8..ff2530c848b0 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -5958,7 +5958,7 @@ static struct sctp_packet *sctp_ootb_pkt_new(struct net *net, } /* Make a transport for the bucket, Eliza... */ - transport = sctp_transport_new(sctp_source(chunk), GFP_ATOMIC); + transport = sctp_transport_new(net, sctp_source(chunk), GFP_ATOMIC); if (!transport) goto nomem; diff --git a/net/sctp/transport.c b/net/sctp/transport.c index c97472b248a2..aada963c9d6b 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -59,7 +59,8 @@ /* 1st Level Abstractions. */ /* Initialize a new transport from provided memory. */ -static struct sctp_transport *sctp_transport_init(struct sctp_transport *peer, +static struct sctp_transport *sctp_transport_init(struct net *net, + struct sctp_transport *peer, const union sctp_addr *addr, gfp_t gfp) { @@ -109,7 +110,8 @@ static struct sctp_transport *sctp_transport_init(struct sctp_transport *peer, } /* Allocate and initialize a new transport. */ -struct sctp_transport *sctp_transport_new(const union sctp_addr *addr, +struct sctp_transport *sctp_transport_new(struct net *net, + const union sctp_addr *addr, gfp_t gfp) { struct sctp_transport *transport; @@ -118,7 +120,7 @@ struct sctp_transport *sctp_transport_new(const union sctp_addr *addr, if (!transport) goto fail; - if (!sctp_transport_init(transport, addr, gfp)) + if (!sctp_transport_init(net, transport, addr, gfp)) goto fail_init; transport->malloced = 1; -- cgit v1.2.3 From e7ff4a7037e6908b7a5f4682945a0b097d5b3535 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 7 Aug 2012 07:27:02 +0000 Subject: sctp: Push struct net down into sctp_in_scope struct net will be needed shortly when the tunables are made per network namespace. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 2 +- net/sctp/bind_addr.c | 4 ++-- net/sctp/protocol.c | 2 +- net/sctp/sm_make_chunk.c | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 88d217941579..b0882f3a3a51 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1248,7 +1248,7 @@ int sctp_raw_to_bind_addrs(struct sctp_bind_addr *bp, __u8 *raw, int len, __u16 port, gfp_t gfp); sctp_scope_t sctp_scope(const union sctp_addr *); -int sctp_in_scope(const union sctp_addr *addr, const sctp_scope_t scope); +int sctp_in_scope(struct net *net, const union sctp_addr *addr, const sctp_scope_t scope); int sctp_is_any(struct sock *sk, const union sctp_addr *addr); int sctp_addr_is_valid(const union sctp_addr *addr); int sctp_is_ep_boundall(struct sock *sk); diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index a85ce4b3e574..23389ba44e39 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c @@ -457,7 +457,7 @@ static int sctp_copy_one_addr(struct net *net, struct sctp_bind_addr *dest, if (sctp_is_any(NULL, addr)) { error = sctp_copy_local_addr_list(net, dest, scope, gfp, flags); - } else if (sctp_in_scope(addr, scope)) { + } else if (sctp_in_scope(net, addr, scope)) { /* Now that the address is in scope, check to see if * the address type is supported by local sock as * well as the remote peer. @@ -494,7 +494,7 @@ int sctp_is_any(struct sock *sk, const union sctp_addr *addr) } /* Is 'addr' valid for 'scope'? */ -int sctp_in_scope(const union sctp_addr *addr, sctp_scope_t scope) +int sctp_in_scope(struct net *net, const union sctp_addr *addr, sctp_scope_t scope) { sctp_scope_t addr_scope = sctp_scope(addr); diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 0f2342be61f3..59965bdea07a 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -210,7 +210,7 @@ int sctp_copy_local_addr_list(struct net *net, struct sctp_bind_addr *bp, list_for_each_entry_rcu(addr, &net->sctp.local_addr_list, list) { if (!addr->valid) continue; - if (sctp_in_scope(&addr->a, scope)) { + if (sctp_in_scope(net, &addr->a, scope)) { /* Now that the address is in scope, check to see if * the address type is really supported by the local * sock as well as the remote peer. diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 479a70ef6ff8..fb12835e95c2 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -2466,6 +2466,7 @@ static int sctp_process_param(struct sctp_association *asoc, const union sctp_addr *peer_addr, gfp_t gfp) { + struct net *net = sock_net(asoc->base.sk); union sctp_addr addr; int i; __u16 sat; @@ -2494,7 +2495,7 @@ do_addr_param: af = sctp_get_af_specific(param_type2af(param.p->type)); af->from_addr_param(&addr, param.addr, htons(asoc->peer.port), 0); scope = sctp_scope(peer_addr); - if (sctp_in_scope(&addr, scope)) + if (sctp_in_scope(net, &addr, scope)) if (!sctp_assoc_add_peer(asoc, &addr, gfp, SCTP_UNCONFIRMED)) return 0; break; -- cgit v1.2.3 From 24cb81a6a91288fcba19548944729ea906eb5e2a Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 7 Aug 2012 07:28:09 +0000 Subject: sctp: Push struct net down into all of the state machine functions There are a handle of state machine functions primarily those dealing with processing INIT packets where there is neither a valid endpoint nor a valid assoication from which to derive a struct net. Therefore add struct net * to the parameter list of sctp_state_fn_t and update all of the state machine functions. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- include/net/sctp/sm.h | 3 +- net/sctp/sm_sideeffect.c | 2 +- net/sctp/sm_statefuns.c | 619 ++++++++++++++++++++++++++--------------------- 3 files changed, 341 insertions(+), 283 deletions(-) (limited to 'include') diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index bcef13023ac3..b5887e1677e4 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -77,7 +77,8 @@ typedef struct { int action; } sctp_sm_command_t; -typedef sctp_disposition_t (sctp_state_fn_t) (const struct sctp_endpoint *, +typedef sctp_disposition_t (sctp_state_fn_t) (struct net *, + const struct sctp_endpoint *, const struct sctp_association *, const sctp_subtype_t type, void *arg, diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 02c4c1c066a7..bcfebb91559d 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -1123,7 +1123,7 @@ int sctp_do_sm(struct net *net, sctp_event_t event_type, sctp_subtype_t subtype, sctp_init_cmd_seq(&commands); DEBUG_PRE; - status = (*state_fn->fn)(ep, asoc, subtype, event_arg, &commands); + status = (*state_fn->fn)(net, ep, asoc, subtype, event_arg, &commands); DEBUG_POST; error = sctp_side_effects(event_type, subtype, state, diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index ff2530c848b0..19f3bff84193 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -66,7 +66,8 @@ #include #include -static struct sctp_packet *sctp_abort_pkt_new(const struct sctp_endpoint *ep, +static struct sctp_packet *sctp_abort_pkt_new(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, struct sctp_chunk *chunk, const void *payload, @@ -77,34 +78,40 @@ static int sctp_eat_data(const struct sctp_association *asoc, static struct sctp_packet *sctp_ootb_pkt_new(struct net *net, const struct sctp_association *asoc, const struct sctp_chunk *chunk); -static void sctp_send_stale_cookie_err(const struct sctp_endpoint *ep, +static void sctp_send_stale_cookie_err(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const struct sctp_chunk *chunk, sctp_cmd_seq_t *commands, struct sctp_chunk *err_chunk); -static sctp_disposition_t sctp_sf_do_5_2_6_stale(const struct sctp_endpoint *ep, +static sctp_disposition_t sctp_sf_do_5_2_6_stale(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, sctp_cmd_seq_t *commands); -static sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep, +static sctp_disposition_t sctp_sf_shut_8_4_5(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, sctp_cmd_seq_t *commands); -static sctp_disposition_t sctp_sf_tabort_8_4_8(const struct sctp_endpoint *ep, +static sctp_disposition_t sctp_sf_tabort_8_4_8(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, sctp_cmd_seq_t *commands); static struct sctp_sackhdr *sctp_sm_pull_sack(struct sctp_chunk *chunk); -static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands, +static sctp_disposition_t sctp_stop_t1_and_abort(struct net *net, + sctp_cmd_seq_t *commands, __be16 error, int sk_err, const struct sctp_association *asoc, struct sctp_transport *transport); static sctp_disposition_t sctp_sf_abort_violation( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, void *arg, @@ -113,6 +120,7 @@ static sctp_disposition_t sctp_sf_abort_violation( const size_t paylen); static sctp_disposition_t sctp_sf_violation_chunklen( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -120,6 +128,7 @@ static sctp_disposition_t sctp_sf_violation_chunklen( sctp_cmd_seq_t *commands); static sctp_disposition_t sctp_sf_violation_paramlen( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -127,6 +136,7 @@ static sctp_disposition_t sctp_sf_violation_paramlen( sctp_cmd_seq_t *commands); static sctp_disposition_t sctp_sf_violation_ctsn( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -134,18 +144,21 @@ static sctp_disposition_t sctp_sf_violation_ctsn( sctp_cmd_seq_t *commands); static sctp_disposition_t sctp_sf_violation_chunk( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, sctp_cmd_seq_t *commands); -static sctp_ierror_t sctp_sf_authenticate(const struct sctp_endpoint *ep, +static sctp_ierror_t sctp_sf_authenticate(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, struct sctp_chunk *chunk); -static sctp_disposition_t __sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep, +static sctp_disposition_t __sctp_sf_do_9_1_abort(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -205,7 +218,8 @@ sctp_chunk_length_valid(struct sctp_chunk *chunk, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_4_C(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_do_4_C(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -213,10 +227,9 @@ sctp_disposition_t sctp_sf_do_4_C(const struct sctp_endpoint *ep, { struct sctp_chunk *chunk = arg; struct sctp_ulpevent *ev; - struct net *net; if (!sctp_vtag_verify_either(chunk, asoc)) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* RFC 2960 6.10 Bundling * @@ -224,11 +237,11 @@ sctp_disposition_t sctp_sf_do_4_C(const struct sctp_endpoint *ep, * SHUTDOWN COMPLETE with any other chunks. */ if (!chunk->singleton) - return sctp_sf_violation_chunk(ep, asoc, type, arg, commands); + return sctp_sf_violation_chunk(net, ep, asoc, type, arg, commands); /* Make sure that the SHUTDOWN_COMPLETE chunk has a valid length. */ if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); /* RFC 2960 10.2 SCTP-to-ULP @@ -261,7 +274,6 @@ sctp_disposition_t sctp_sf_do_4_C(const struct sctp_endpoint *ep, sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, SCTP_STATE(SCTP_STATE_CLOSED)); - net = sock_net(asoc->base.sk); SCTP_INC_STATS(net, SCTP_MIB_SHUTDOWNS); SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB); @@ -292,7 +304,8 @@ sctp_disposition_t sctp_sf_do_4_C(const struct sctp_endpoint *ep, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_do_5_1B_init(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -304,7 +317,6 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep, struct sctp_chunk *err_chunk; struct sctp_packet *packet; sctp_unrecognized_param_t *unk_param; - struct net *net; int len; /* 6.10 Bundling @@ -317,22 +329,21 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep, * with an INIT chunk that is bundled with other chunks. */ if (!chunk->singleton) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* If the packet is an OOTB packet which is temporarily on the * control endpoint, respond with an ABORT. */ - net = sock_net(ep->base.sk); if (ep == sctp_sk(net->sctp.ctl_sock)->ep) { SCTP_INC_STATS(net, SCTP_MIB_OUTOFBLUES); - return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); + return sctp_sf_tabort_8_4_8(net, ep, asoc, type, arg, commands); } /* 3.1 A packet containing an INIT chunk MUST have a zero Verification * Tag. */ if (chunk->sctp_hdr->vtag != 0) - return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); + return sctp_sf_tabort_8_4_8(net, ep, asoc, type, arg, commands); /* Make sure that the INIT chunk has a valid length. * Normally, this would cause an ABORT with a Protocol Violation @@ -340,7 +351,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep, * just discard the packet. */ if (!sctp_chunk_length_valid(chunk, sizeof(sctp_init_chunk_t))) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* If the INIT is coming toward a closing socket, we'll send back * and ABORT. Essentially, this catches the race of INIT being @@ -349,7 +360,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep, * can treat this OOTB */ if (sctp_sstate(ep->base.sk, CLOSING)) - return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); + return sctp_sf_tabort_8_4_8(net, ep, asoc, type, arg, commands); /* Verify the INIT chunk before processing it. */ err_chunk = NULL; @@ -360,7 +371,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep, * Send an ABORT, with causes if there is any. */ if (err_chunk) { - packet = sctp_abort_pkt_new(ep, asoc, arg, + packet = sctp_abort_pkt_new(net, ep, asoc, arg, (__u8 *)(err_chunk->chunk_hdr) + sizeof(sctp_chunkhdr_t), ntohs(err_chunk->chunk_hdr->length) - @@ -377,7 +388,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep, return SCTP_DISPOSITION_NOMEM; } } else { - return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, + return sctp_sf_tabort_8_4_8(net, ep, asoc, type, arg, commands); } } @@ -489,7 +500,8 @@ nomem: * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_do_5_1C_ack(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -501,18 +513,18 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep, struct sctp_packet *packet; if (!sctp_vtag_verify(chunk, asoc)) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* 6.10 Bundling * An endpoint MUST NOT bundle INIT, INIT ACK or * SHUTDOWN COMPLETE with any other chunks. */ if (!chunk->singleton) - return sctp_sf_violation_chunk(ep, asoc, type, arg, commands); + return sctp_sf_violation_chunk(net, ep, asoc, type, arg, commands); /* Make sure that the INIT-ACK chunk has a valid length */ if (!sctp_chunk_length_valid(chunk, sizeof(sctp_initack_chunk_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); /* Grab the INIT header. */ chunk->subh.init_hdr = (sctp_inithdr_t *) chunk->skb->data; @@ -531,7 +543,7 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep, * the association. */ if (err_chunk) { - packet = sctp_abort_pkt_new(ep, asoc, arg, + packet = sctp_abort_pkt_new(net, ep, asoc, arg, (__u8 *)(err_chunk->chunk_hdr) + sizeof(sctp_chunkhdr_t), ntohs(err_chunk->chunk_hdr->length) - @@ -542,7 +554,7 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep, if (packet) { sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, SCTP_PACKET(packet)); - SCTP_INC_STATS(sock_net(ep->base.sk), SCTP_MIB_OUTCTRLCHUNKS); + SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS); error = SCTP_ERROR_INV_PARAM; } } @@ -559,10 +571,10 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep, * was malformed. */ if (sctp_auth_recv_cid(SCTP_CID_ABORT, asoc)) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); - SCTP_INC_STATS(sock_net(ep->base.sk), SCTP_MIB_ABORTEDS); - return sctp_stop_t1_and_abort(commands, error, ECONNREFUSED, + SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); + return sctp_stop_t1_and_abort(net, commands, error, ECONNREFUSED, asoc, chunk->transport); } @@ -638,7 +650,8 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_do_5_1D_ce(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, sctp_cmd_seq_t *commands) @@ -651,15 +664,13 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep, int error = 0; struct sctp_chunk *err_chk_p; struct sock *sk; - struct net *net; /* If the packet is an OOTB packet which is temporarily on the * control endpoint, respond with an ABORT. */ - net = sock_net(ep->base.sk); if (ep == sctp_sk(net->sctp.ctl_sock)->ep) { SCTP_INC_STATS(net, SCTP_MIB_OUTOFBLUES); - return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); + return sctp_sf_tabort_8_4_8(net, ep, asoc, type, arg, commands); } /* Make sure that the COOKIE_ECHO chunk has a valid length. @@ -668,7 +679,7 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep, * in sctp_unpack_cookie(). */ if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t))) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* If the endpoint is not listening or if the number of associations * on the TCP-style socket exceed the max backlog, respond with an @@ -677,7 +688,7 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep, sk = ep->base.sk; if (!sctp_sstate(sk, LISTENING) || (sctp_style(sk, TCP) && sk_acceptq_is_full(sk))) - return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); + return sctp_sf_tabort_8_4_8(net, ep, asoc, type, arg, commands); /* "Decode" the chunk. We have no optional parameters so we * are in good shape. @@ -710,13 +721,13 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep, goto nomem; case -SCTP_IERROR_STALE_COOKIE: - sctp_send_stale_cookie_err(ep, asoc, chunk, commands, + sctp_send_stale_cookie_err(net, ep, asoc, chunk, commands, err_chk_p); - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); case -SCTP_IERROR_BAD_SIG: default: - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); } } @@ -763,14 +774,14 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep, skb_pull(chunk->auth_chunk, sizeof(sctp_chunkhdr_t)); auth.transport = chunk->transport; - ret = sctp_sf_authenticate(ep, new_asoc, type, &auth); + ret = sctp_sf_authenticate(net, ep, new_asoc, type, &auth); /* We can now safely free the auth_chunk clone */ kfree_skb(chunk->auth_chunk); if (ret != SCTP_IERROR_NO_ERROR) { sctp_association_free(new_asoc); - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); } } @@ -863,23 +874,23 @@ nomem: * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_5_1E_ca(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_do_5_1E_ca(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, sctp_cmd_seq_t *commands) { struct sctp_chunk *chunk = arg; struct sctp_ulpevent *ev; - struct net *net; if (!sctp_vtag_verify(chunk, asoc)) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* Verify that the chunk length for the COOKIE-ACK is OK. * If we don't do this, any bundled chunks may be junked. */ if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); /* Reset init error count upon receipt of COOKIE-ACK, @@ -900,7 +911,6 @@ sctp_disposition_t sctp_sf_do_5_1E_ca(const struct sctp_endpoint *ep, SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE)); sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, SCTP_STATE(SCTP_STATE_ESTABLISHED)); - net = sock_net(ep->base.sk); SCTP_INC_STATS(net, SCTP_MIB_CURRESTAB); SCTP_INC_STATS(net, SCTP_MIB_ACTIVEESTABS); sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START, SCTP_NULL()); @@ -967,7 +977,8 @@ static sctp_disposition_t sctp_sf_heartbeat(const struct sctp_endpoint *ep, } /* Generate a HEARTBEAT packet on the given transport. */ -sctp_disposition_t sctp_sf_sendbeat_8_3(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_sendbeat_8_3(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -976,13 +987,11 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const struct sctp_endpoint *ep, struct sctp_transport *transport = (struct sctp_transport *) arg; if (asoc->overall_error_count >= asoc->max_retrans) { - struct net *net; sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, SCTP_ERROR(ETIMEDOUT)); /* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */ sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_PERR(SCTP_ERROR_NO_ERROR)); - net = sock_net(ep->base.sk); SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB); return SCTP_DISPOSITION_DELETE_TCB; @@ -1039,7 +1048,8 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const struct sctp_endpoint *ep, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_beat_8_3(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_beat_8_3(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -1050,11 +1060,11 @@ sctp_disposition_t sctp_sf_beat_8_3(const struct sctp_endpoint *ep, size_t paylen = 0; if (!sctp_vtag_verify(chunk, asoc)) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* Make sure that the HEARTBEAT chunk has a valid length. */ if (!sctp_chunk_length_valid(chunk, sizeof(sctp_heartbeat_chunk_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); /* 8.3 The receiver of the HEARTBEAT should immediately @@ -1106,7 +1116,8 @@ nomem: * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_backbeat_8_3(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_backbeat_8_3(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -1119,12 +1130,12 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const struct sctp_endpoint *ep, unsigned long max_interval; if (!sctp_vtag_verify(chunk, asoc)) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* Make sure that the HEARTBEAT-ACK chunk has a valid length. */ if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t) + sizeof(sctp_sender_hb_info_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); hbinfo = (sctp_sender_hb_info_t *) chunk->skb->data; @@ -1213,7 +1224,7 @@ static int sctp_sf_send_restart_abort(struct net *net, union sctp_addr *ssa, /* Association is NULL since this may be a restart attack and we * want to send back the attacker's vtag. */ - pkt = sctp_abort_pkt_new(ep, NULL, init, errhdr, len); + pkt = sctp_abort_pkt_new(net, ep, NULL, init, errhdr, len); if (!pkt) goto out; @@ -1370,6 +1381,7 @@ static char sctp_tietags_compare(struct sctp_association *new_asoc, * chunk handling. */ static sctp_disposition_t sctp_sf_do_unexpected_init( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -1394,20 +1406,20 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( * with an INIT chunk that is bundled with other chunks. */ if (!chunk->singleton) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* 3.1 A packet containing an INIT chunk MUST have a zero Verification * Tag. */ if (chunk->sctp_hdr->vtag != 0) - return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); + return sctp_sf_tabort_8_4_8(net, ep, asoc, type, arg, commands); /* Make sure that the INIT chunk has a valid length. * In this case, we generate a protocol violation since we have * an association established. */ if (!sctp_chunk_length_valid(chunk, sizeof(sctp_init_chunk_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); /* Grab the INIT header. */ chunk->subh.init_hdr = (sctp_inithdr_t *) chunk->skb->data; @@ -1424,7 +1436,7 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( * Send an ABORT, with causes if there is any. */ if (err_chunk) { - packet = sctp_abort_pkt_new(ep, asoc, arg, + packet = sctp_abort_pkt_new(net, ep, asoc, arg, (__u8 *)(err_chunk->chunk_hdr) + sizeof(sctp_chunkhdr_t), ntohs(err_chunk->chunk_hdr->length) - @@ -1433,14 +1445,14 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( if (packet) { sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, SCTP_PACKET(packet)); - SCTP_INC_STATS(sock_net(ep->base.sk), SCTP_MIB_OUTCTRLCHUNKS); + SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS); retval = SCTP_DISPOSITION_CONSUME; } else { retval = SCTP_DISPOSITION_NOMEM; } goto cleanup; } else { - return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, + return sctp_sf_tabort_8_4_8(net, ep, asoc, type, arg, commands); } } @@ -1582,7 +1594,8 @@ cleanup: * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_5_2_1_siminit(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_do_5_2_1_siminit(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -1591,7 +1604,7 @@ sctp_disposition_t sctp_sf_do_5_2_1_siminit(const struct sctp_endpoint *ep, /* Call helper to do the real work for both simulataneous and * duplicate INIT chunk handling. */ - return sctp_sf_do_unexpected_init(ep, asoc, type, arg, commands); + return sctp_sf_do_unexpected_init(net, ep, asoc, type, arg, commands); } /* @@ -1635,7 +1648,8 @@ sctp_disposition_t sctp_sf_do_5_2_1_siminit(const struct sctp_endpoint *ep, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_5_2_2_dupinit(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_do_5_2_2_dupinit(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -1644,7 +1658,7 @@ sctp_disposition_t sctp_sf_do_5_2_2_dupinit(const struct sctp_endpoint *ep, /* Call helper to do the real work for both simulataneous and * duplicate INIT chunk handling. */ - return sctp_sf_do_unexpected_init(ep, asoc, type, arg, commands); + return sctp_sf_do_unexpected_init(net, ep, asoc, type, arg, commands); } @@ -1657,19 +1671,19 @@ sctp_disposition_t sctp_sf_do_5_2_2_dupinit(const struct sctp_endpoint *ep, * An unexpected INIT ACK usually indicates the processing of an old or * duplicated INIT chunk. */ -sctp_disposition_t sctp_sf_do_5_2_3_initack(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_do_5_2_3_initack(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, sctp_cmd_seq_t *commands) { - struct net *net = sock_net(ep->base.sk); /* Per the above section, we'll discard the chunk if we have an * endpoint. If this is an OOTB INIT-ACK, treat it as such. */ if (ep == sctp_sk(net->sctp.ctl_sock)->ep) - return sctp_sf_ootb(ep, asoc, type, arg, commands); + return sctp_sf_ootb(net, ep, asoc, type, arg, commands); else - return sctp_sf_discard_chunk(ep, asoc, type, arg, commands); + return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands); } /* Unexpected COOKIE-ECHO handler for peer restart (Table 2, action 'A') @@ -1677,7 +1691,8 @@ sctp_disposition_t sctp_sf_do_5_2_3_initack(const struct sctp_endpoint *ep, * Section 5.2.4 * A) In this case, the peer may have restarted. */ -static sctp_disposition_t sctp_sf_do_dupcook_a(const struct sctp_endpoint *ep, +static sctp_disposition_t sctp_sf_do_dupcook_a(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, struct sctp_chunk *chunk, sctp_cmd_seq_t *commands, @@ -1713,7 +1728,7 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(const struct sctp_endpoint *ep, * its peer. */ if (sctp_state(asoc, SHUTDOWN_ACK_SENT)) { - disposition = sctp_sf_do_9_2_reshutack(ep, asoc, + disposition = sctp_sf_do_9_2_reshutack(net, ep, asoc, SCTP_ST_CHUNK(chunk->chunk_hdr->type), chunk, commands); if (SCTP_DISPOSITION_NOMEM == disposition) @@ -1776,7 +1791,8 @@ nomem: * after responding to the local endpoint's INIT */ /* This case represents an initialization collision. */ -static sctp_disposition_t sctp_sf_do_dupcook_b(const struct sctp_endpoint *ep, +static sctp_disposition_t sctp_sf_do_dupcook_b(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, struct sctp_chunk *chunk, sctp_cmd_seq_t *commands, @@ -1797,7 +1813,7 @@ static sctp_disposition_t sctp_sf_do_dupcook_b(const struct sctp_endpoint *ep, sctp_add_cmd_sf(commands, SCTP_CMD_UPDATE_ASSOC, SCTP_ASOC(new_asoc)); sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, SCTP_STATE(SCTP_STATE_ESTABLISHED)); - SCTP_INC_STATS(sock_net(new_asoc->base.sk), SCTP_MIB_CURRESTAB); + SCTP_INC_STATS(net, SCTP_MIB_CURRESTAB); sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START, SCTP_NULL()); repl = sctp_make_cookie_ack(new_asoc, chunk); @@ -1846,7 +1862,8 @@ nomem: * but a new tag of its own. */ /* This case represents an initialization collision. */ -static sctp_disposition_t sctp_sf_do_dupcook_c(const struct sctp_endpoint *ep, +static sctp_disposition_t sctp_sf_do_dupcook_c(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, struct sctp_chunk *chunk, sctp_cmd_seq_t *commands, @@ -1867,7 +1884,8 @@ static sctp_disposition_t sctp_sf_do_dupcook_c(const struct sctp_endpoint *ep, * enter the ESTABLISHED state, if it has not already done so. */ /* This case represents an initialization collision. */ -static sctp_disposition_t sctp_sf_do_dupcook_d(const struct sctp_endpoint *ep, +static sctp_disposition_t sctp_sf_do_dupcook_d(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, struct sctp_chunk *chunk, sctp_cmd_seq_t *commands, @@ -1889,7 +1907,7 @@ static sctp_disposition_t sctp_sf_do_dupcook_d(const struct sctp_endpoint *ep, SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE)); sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, SCTP_STATE(SCTP_STATE_ESTABLISHED)); - SCTP_INC_STATS(sock_net(asoc->base.sk), SCTP_MIB_CURRESTAB); + SCTP_INC_STATS(net, SCTP_MIB_CURRESTAB); sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START, SCTP_NULL()); @@ -1961,7 +1979,8 @@ nomem: * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_do_5_2_4_dupcook(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -1980,7 +1999,7 @@ sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const struct sctp_endpoint *ep, * done later. */ if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); /* "Decode" the chunk. We have no optional parameters so we @@ -2014,12 +2033,12 @@ sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const struct sctp_endpoint *ep, goto nomem; case -SCTP_IERROR_STALE_COOKIE: - sctp_send_stale_cookie_err(ep, asoc, chunk, commands, + sctp_send_stale_cookie_err(net, ep, asoc, chunk, commands, err_chk_p); - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); case -SCTP_IERROR_BAD_SIG: default: - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); } } @@ -2030,27 +2049,27 @@ sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const struct sctp_endpoint *ep, switch (action) { case 'A': /* Association restart. */ - retval = sctp_sf_do_dupcook_a(ep, asoc, chunk, commands, + retval = sctp_sf_do_dupcook_a(net, ep, asoc, chunk, commands, new_asoc); break; case 'B': /* Collision case B. */ - retval = sctp_sf_do_dupcook_b(ep, asoc, chunk, commands, + retval = sctp_sf_do_dupcook_b(net, ep, asoc, chunk, commands, new_asoc); break; case 'C': /* Collision case C. */ - retval = sctp_sf_do_dupcook_c(ep, asoc, chunk, commands, + retval = sctp_sf_do_dupcook_c(net, ep, asoc, chunk, commands, new_asoc); break; case 'D': /* Collision case D. */ - retval = sctp_sf_do_dupcook_d(ep, asoc, chunk, commands, + retval = sctp_sf_do_dupcook_d(net, ep, asoc, chunk, commands, new_asoc); break; default: /* Discard packet for all others. */ - retval = sctp_sf_pdiscard(ep, asoc, type, arg, commands); + retval = sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); break; } @@ -2076,6 +2095,7 @@ nomem: * See sctp_sf_do_9_1_abort(). */ sctp_disposition_t sctp_sf_shutdown_pending_abort( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -2085,7 +2105,7 @@ sctp_disposition_t sctp_sf_shutdown_pending_abort( struct sctp_chunk *chunk = arg; if (!sctp_vtag_verify_either(chunk, asoc)) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* Make sure that the ABORT chunk has a valid length. * Since this is an ABORT chunk, we have to discard it @@ -2098,7 +2118,7 @@ sctp_disposition_t sctp_sf_shutdown_pending_abort( * packet. */ if (!sctp_chunk_length_valid(chunk, sizeof(sctp_abort_chunk_t))) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* ADD-IP: Special case for ABORT chunks * F4) One special consideration is that ABORT Chunks arriving @@ -2107,9 +2127,9 @@ sctp_disposition_t sctp_sf_shutdown_pending_abort( */ if (SCTP_ADDR_DEL == sctp_bind_addr_state(&asoc->base.bind_addr, &chunk->dest)) - return sctp_sf_discard_chunk(ep, asoc, type, arg, commands); + return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands); - return __sctp_sf_do_9_1_abort(ep, asoc, type, arg, commands); + return __sctp_sf_do_9_1_abort(net, ep, asoc, type, arg, commands); } /* @@ -2117,7 +2137,8 @@ sctp_disposition_t sctp_sf_shutdown_pending_abort( * * See sctp_sf_do_9_1_abort(). */ -sctp_disposition_t sctp_sf_shutdown_sent_abort(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_shutdown_sent_abort(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -2126,7 +2147,7 @@ sctp_disposition_t sctp_sf_shutdown_sent_abort(const struct sctp_endpoint *ep, struct sctp_chunk *chunk = arg; if (!sctp_vtag_verify_either(chunk, asoc)) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* Make sure that the ABORT chunk has a valid length. * Since this is an ABORT chunk, we have to discard it @@ -2139,7 +2160,7 @@ sctp_disposition_t sctp_sf_shutdown_sent_abort(const struct sctp_endpoint *ep, * packet. */ if (!sctp_chunk_length_valid(chunk, sizeof(sctp_abort_chunk_t))) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* ADD-IP: Special case for ABORT chunks * F4) One special consideration is that ABORT Chunks arriving @@ -2148,7 +2169,7 @@ sctp_disposition_t sctp_sf_shutdown_sent_abort(const struct sctp_endpoint *ep, */ if (SCTP_ADDR_DEL == sctp_bind_addr_state(&asoc->base.bind_addr, &chunk->dest)) - return sctp_sf_discard_chunk(ep, asoc, type, arg, commands); + return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands); /* Stop the T2-shutdown timer. */ sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, @@ -2158,7 +2179,7 @@ sctp_disposition_t sctp_sf_shutdown_sent_abort(const struct sctp_endpoint *ep, sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD)); - return __sctp_sf_do_9_1_abort(ep, asoc, type, arg, commands); + return __sctp_sf_do_9_1_abort(net, ep, asoc, type, arg, commands); } /* @@ -2167,6 +2188,7 @@ sctp_disposition_t sctp_sf_shutdown_sent_abort(const struct sctp_endpoint *ep, * See sctp_sf_do_9_1_abort(). */ sctp_disposition_t sctp_sf_shutdown_ack_sent_abort( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -2176,7 +2198,7 @@ sctp_disposition_t sctp_sf_shutdown_ack_sent_abort( /* The same T2 timer, so we should be able to use * common function with the SHUTDOWN-SENT state. */ - return sctp_sf_shutdown_sent_abort(ep, asoc, type, arg, commands); + return sctp_sf_shutdown_sent_abort(net, ep, asoc, type, arg, commands); } /* @@ -2193,7 +2215,8 @@ sctp_disposition_t sctp_sf_shutdown_ack_sent_abort( * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_cookie_echoed_err(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_cookie_echoed_err(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -2203,13 +2226,13 @@ sctp_disposition_t sctp_sf_cookie_echoed_err(const struct sctp_endpoint *ep, sctp_errhdr_t *err; if (!sctp_vtag_verify(chunk, asoc)) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* Make sure that the ERROR chunk has a valid length. * The parameter walking depends on this as well. */ if (!sctp_chunk_length_valid(chunk, sizeof(sctp_operr_chunk_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); /* Process the error here */ @@ -2219,7 +2242,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_err(const struct sctp_endpoint *ep, */ sctp_walk_errors(err, chunk->chunk_hdr) { if (SCTP_ERROR_STALE_COOKIE == err->cause) - return sctp_sf_do_5_2_6_stale(ep, asoc, type, + return sctp_sf_do_5_2_6_stale(net, ep, asoc, type, arg, commands); } @@ -2228,7 +2251,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_err(const struct sctp_endpoint *ep, * we are discarding the packet, there should be no adverse * affects. */ - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); } /* @@ -2256,7 +2279,8 @@ sctp_disposition_t sctp_sf_cookie_echoed_err(const struct sctp_endpoint *ep, * * The return value is the disposition of the chunk. */ -static sctp_disposition_t sctp_sf_do_5_2_6_stale(const struct sctp_endpoint *ep, +static sctp_disposition_t sctp_sf_do_5_2_6_stale(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -2378,7 +2402,8 @@ nomem: * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_do_9_1_abort(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -2387,7 +2412,7 @@ sctp_disposition_t sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep, struct sctp_chunk *chunk = arg; if (!sctp_vtag_verify_either(chunk, asoc)) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* Make sure that the ABORT chunk has a valid length. * Since this is an ABORT chunk, we have to discard it @@ -2400,7 +2425,7 @@ sctp_disposition_t sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep, * packet. */ if (!sctp_chunk_length_valid(chunk, sizeof(sctp_abort_chunk_t))) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* ADD-IP: Special case for ABORT chunks * F4) One special consideration is that ABORT Chunks arriving @@ -2409,12 +2434,13 @@ sctp_disposition_t sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep, */ if (SCTP_ADDR_DEL == sctp_bind_addr_state(&asoc->base.bind_addr, &chunk->dest)) - return sctp_sf_discard_chunk(ep, asoc, type, arg, commands); + return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands); - return __sctp_sf_do_9_1_abort(ep, asoc, type, arg, commands); + return __sctp_sf_do_9_1_abort(net, ep, asoc, type, arg, commands); } -static sctp_disposition_t __sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep, +static sctp_disposition_t __sctp_sf_do_9_1_abort(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -2423,7 +2449,6 @@ static sctp_disposition_t __sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep, struct sctp_chunk *chunk = arg; unsigned int len; __be16 error = SCTP_ERROR_NO_ERROR; - struct net *net; /* See if we have an error cause code in the chunk. */ len = ntohs(chunk->chunk_hdr->length); @@ -2432,7 +2457,7 @@ static sctp_disposition_t __sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep, sctp_errhdr_t *err; sctp_walk_errors(err, chunk->chunk_hdr); if ((void *)err != (void *)chunk->chunk_end) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); error = ((sctp_errhdr_t *)chunk->skb->data)->cause; } @@ -2440,7 +2465,6 @@ static sctp_disposition_t __sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep, sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, SCTP_ERROR(ECONNRESET)); /* ASSOC_FAILED will DELETE_TCB. */ sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_PERR(error)); - net = sock_net(ep->base.sk); SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB); @@ -2452,7 +2476,8 @@ static sctp_disposition_t __sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep, * * See sctp_sf_do_9_1_abort() above. */ -sctp_disposition_t sctp_sf_cookie_wait_abort(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_cookie_wait_abort(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -2463,7 +2488,7 @@ sctp_disposition_t sctp_sf_cookie_wait_abort(const struct sctp_endpoint *ep, __be16 error = SCTP_ERROR_NO_ERROR; if (!sctp_vtag_verify_either(chunk, asoc)) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* Make sure that the ABORT chunk has a valid length. * Since this is an ABORT chunk, we have to discard it @@ -2476,27 +2501,28 @@ sctp_disposition_t sctp_sf_cookie_wait_abort(const struct sctp_endpoint *ep, * packet. */ if (!sctp_chunk_length_valid(chunk, sizeof(sctp_abort_chunk_t))) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* See if we have an error cause code in the chunk. */ len = ntohs(chunk->chunk_hdr->length); if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr)) error = ((sctp_errhdr_t *)chunk->skb->data)->cause; - return sctp_stop_t1_and_abort(commands, error, ECONNREFUSED, asoc, + return sctp_stop_t1_and_abort(net, commands, error, ECONNREFUSED, asoc, chunk->transport); } /* * Process an incoming ICMP as an ABORT. (COOKIE-WAIT state) */ -sctp_disposition_t sctp_sf_cookie_wait_icmp_abort(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_cookie_wait_icmp_abort(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, sctp_cmd_seq_t *commands) { - return sctp_stop_t1_and_abort(commands, SCTP_ERROR_NO_ERROR, + return sctp_stop_t1_and_abort(net, commands, SCTP_ERROR_NO_ERROR, ENOPROTOOPT, asoc, (struct sctp_transport *)arg); } @@ -2504,7 +2530,8 @@ sctp_disposition_t sctp_sf_cookie_wait_icmp_abort(const struct sctp_endpoint *ep /* * Process an ABORT. (COOKIE-ECHOED state) */ -sctp_disposition_t sctp_sf_cookie_echoed_abort(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_cookie_echoed_abort(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -2513,7 +2540,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_abort(const struct sctp_endpoint *ep, /* There is a single T1 timer, so we should be able to use * common function with the COOKIE-WAIT state. */ - return sctp_sf_cookie_wait_abort(ep, asoc, type, arg, commands); + return sctp_sf_cookie_wait_abort(net, ep, asoc, type, arg, commands); } /* @@ -2521,7 +2548,8 @@ sctp_disposition_t sctp_sf_cookie_echoed_abort(const struct sctp_endpoint *ep, * * This is common code called by several sctp_sf_*_abort() functions above. */ -static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands, +static sctp_disposition_t sctp_stop_t1_and_abort(struct net *net, + sctp_cmd_seq_t *commands, __be16 error, int sk_err, const struct sctp_association *asoc, struct sctp_transport *transport) @@ -2529,7 +2557,7 @@ static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands, SCTP_DEBUG_PRINTK("ABORT received (INIT).\n"); sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, SCTP_STATE(SCTP_STATE_CLOSED)); - SCTP_INC_STATS(sock_net(asoc->base.sk), SCTP_MIB_ABORTEDS); + SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, SCTP_ERROR(sk_err)); @@ -2572,7 +2600,8 @@ static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_9_2_shutdown(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_do_9_2_shutdown(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -2585,12 +2614,12 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown(const struct sctp_endpoint *ep, __u32 ctsn; if (!sctp_vtag_verify(chunk, asoc)) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* Make sure that the SHUTDOWN chunk has a valid length. */ if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_shutdown_chunk_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); /* Convert the elaborate header. */ @@ -2610,7 +2639,7 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown(const struct sctp_endpoint *ep, * sender with an ABORT. */ if (!TSN_lt(ctsn, asoc->next_tsn)) - return sctp_sf_violation_ctsn(ep, asoc, type, arg, commands); + return sctp_sf_violation_ctsn(net, ep, asoc, type, arg, commands); /* API 5.3.1.5 SCTP_SHUTDOWN_EVENT * When a peer sends a SHUTDOWN, SCTP delivers this notification to @@ -2634,7 +2663,7 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown(const struct sctp_endpoint *ep, disposition = SCTP_DISPOSITION_CONSUME; if (sctp_outq_is_empty(&asoc->outqueue)) { - disposition = sctp_sf_do_9_2_shutdown_ack(ep, asoc, type, + disposition = sctp_sf_do_9_2_shutdown_ack(net, ep, asoc, type, arg, commands); } @@ -2660,7 +2689,8 @@ out: * The Cumulative TSN Ack of the received SHUTDOWN chunk * MUST be processed. */ -sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -2671,12 +2701,12 @@ sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(const struct sctp_endpoint *ep, __u32 ctsn; if (!sctp_vtag_verify(chunk, asoc)) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* Make sure that the SHUTDOWN chunk has a valid length. */ if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_shutdown_chunk_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); sdh = (sctp_shutdownhdr_t *)chunk->skb->data; @@ -2693,7 +2723,7 @@ sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(const struct sctp_endpoint *ep, * sender with an ABORT. */ if (!TSN_lt(ctsn, asoc->next_tsn)) - return sctp_sf_violation_ctsn(ep, asoc, type, arg, commands); + return sctp_sf_violation_ctsn(net, ep, asoc, type, arg, commands); /* verify, by checking the Cumulative TSN Ack field of the * chunk, that all its outstanding DATA chunks have been @@ -2712,7 +2742,8 @@ sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(const struct sctp_endpoint *ep, * that belong to this association, it should discard the INIT chunk and * retransmit the SHUTDOWN ACK chunk. */ -sctp_disposition_t sctp_sf_do_9_2_reshutack(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_do_9_2_reshutack(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -2723,7 +2754,7 @@ sctp_disposition_t sctp_sf_do_9_2_reshutack(const struct sctp_endpoint *ep, /* Make sure that the chunk has a valid length */ if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); /* Since we are not going to really process this INIT, there @@ -2775,7 +2806,8 @@ nomem: * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_ecn_cwr(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_do_ecn_cwr(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -2786,10 +2818,10 @@ sctp_disposition_t sctp_sf_do_ecn_cwr(const struct sctp_endpoint *ep, u32 lowest_tsn; if (!sctp_vtag_verify(chunk, asoc)) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); if (!sctp_chunk_length_valid(chunk, sizeof(sctp_ecne_chunk_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); cwr = (sctp_cwrhdr_t *) chunk->skb->data; @@ -2830,7 +2862,8 @@ sctp_disposition_t sctp_sf_do_ecn_cwr(const struct sctp_endpoint *ep, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_ecne(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_do_ecne(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -2840,10 +2873,10 @@ sctp_disposition_t sctp_sf_do_ecne(const struct sctp_endpoint *ep, struct sctp_chunk *chunk = arg; if (!sctp_vtag_verify(chunk, asoc)) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); if (!sctp_chunk_length_valid(chunk, sizeof(sctp_ecne_chunk_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); ecne = (sctp_ecnehdr_t *) chunk->skb->data; @@ -2886,7 +2919,8 @@ sctp_disposition_t sctp_sf_do_ecne(const struct sctp_endpoint *ep, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_eat_data_6_2(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_eat_data_6_2(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -2899,11 +2933,11 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const struct sctp_endpoint *ep, if (!sctp_vtag_verify(chunk, asoc)) { sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG, SCTP_NULL()); - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); } if (!sctp_chunk_length_valid(chunk, sizeof(sctp_data_chunk_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); error = sctp_eat_data(asoc, chunk, commands ); @@ -2912,16 +2946,16 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const struct sctp_endpoint *ep, break; case SCTP_IERROR_HIGH_TSN: case SCTP_IERROR_BAD_STREAM: - SCTP_INC_STATS(sock_net(asoc->base.sk), SCTP_MIB_IN_DATA_CHUNK_DISCARDS); + SCTP_INC_STATS(net, SCTP_MIB_IN_DATA_CHUNK_DISCARDS); goto discard_noforce; case SCTP_IERROR_DUP_TSN: case SCTP_IERROR_IGNORE_TSN: - SCTP_INC_STATS(sock_net(asoc->base.sk), SCTP_MIB_IN_DATA_CHUNK_DISCARDS); + SCTP_INC_STATS(net, SCTP_MIB_IN_DATA_CHUNK_DISCARDS); goto discard_force; case SCTP_IERROR_NO_DATA: goto consume; case SCTP_IERROR_PROTO_VIOLATION: - return sctp_sf_abort_violation(ep, asoc, chunk, commands, + return sctp_sf_abort_violation(net, ep, asoc, chunk, commands, (u8 *)chunk->subh.data_hdr, sizeof(sctp_datahdr_t)); default: BUG(); @@ -3007,7 +3041,8 @@ consume: * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_eat_data_fast_4_4(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_eat_data_fast_4_4(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -3019,11 +3054,11 @@ sctp_disposition_t sctp_sf_eat_data_fast_4_4(const struct sctp_endpoint *ep, if (!sctp_vtag_verify(chunk, asoc)) { sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG, SCTP_NULL()); - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); } if (!sctp_chunk_length_valid(chunk, sizeof(sctp_data_chunk_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); error = sctp_eat_data(asoc, chunk, commands ); @@ -3037,7 +3072,7 @@ sctp_disposition_t sctp_sf_eat_data_fast_4_4(const struct sctp_endpoint *ep, case SCTP_IERROR_NO_DATA: goto consume; case SCTP_IERROR_PROTO_VIOLATION: - return sctp_sf_abort_violation(ep, asoc, chunk, commands, + return sctp_sf_abort_violation(net, ep, asoc, chunk, commands, (u8 *)chunk->subh.data_hdr, sizeof(sctp_datahdr_t)); default: BUG(); @@ -3097,7 +3132,8 @@ consume: * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_eat_sack_6_2(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_eat_sack_6_2(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -3108,18 +3144,18 @@ sctp_disposition_t sctp_sf_eat_sack_6_2(const struct sctp_endpoint *ep, __u32 ctsn; if (!sctp_vtag_verify(chunk, asoc)) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* Make sure that the SACK chunk has a valid length. */ if (!sctp_chunk_length_valid(chunk, sizeof(sctp_sack_chunk_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); /* Pull the SACK chunk from the data buffer */ sackh = sctp_sm_pull_sack(chunk); /* Was this a bogus SACK? */ if (!sackh) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); chunk->subh.sack_hdr = sackh; ctsn = ntohl(sackh->cum_tsn_ack); @@ -3140,7 +3176,7 @@ sctp_disposition_t sctp_sf_eat_sack_6_2(const struct sctp_endpoint *ep, * sender with an ABORT. */ if (!TSN_lt(ctsn, asoc->next_tsn)) - return sctp_sf_violation_ctsn(ep, asoc, type, arg, commands); + return sctp_sf_violation_ctsn(net, ep, asoc, type, arg, commands); /* Return this SACK for further processing. */ sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_SACK, SCTP_SACKH(sackh)); @@ -3169,7 +3205,8 @@ sctp_disposition_t sctp_sf_eat_sack_6_2(const struct sctp_endpoint *ep, * * The return value is the disposition of the chunk. */ -static sctp_disposition_t sctp_sf_tabort_8_4_8(const struct sctp_endpoint *ep, +static sctp_disposition_t sctp_sf_tabort_8_4_8(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -3178,9 +3215,7 @@ static sctp_disposition_t sctp_sf_tabort_8_4_8(const struct sctp_endpoint *ep, struct sctp_packet *packet = NULL; struct sctp_chunk *chunk = arg; struct sctp_chunk *abort; - struct net *net; - net = sock_net(ep->base.sk); packet = sctp_ootb_pkt_new(net, asoc, chunk); if (packet) { @@ -3207,7 +3242,7 @@ static sctp_disposition_t sctp_sf_tabort_8_4_8(const struct sctp_endpoint *ep, SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS); - sctp_sf_pdiscard(ep, asoc, type, arg, commands); + sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); return SCTP_DISPOSITION_CONSUME; } @@ -3222,7 +3257,8 @@ static sctp_disposition_t sctp_sf_tabort_8_4_8(const struct sctp_endpoint *ep, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_operr_notify(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_operr_notify(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -3232,15 +3268,15 @@ sctp_disposition_t sctp_sf_operr_notify(const struct sctp_endpoint *ep, sctp_errhdr_t *err; if (!sctp_vtag_verify(chunk, asoc)) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* Make sure that the ERROR chunk has a valid length. */ if (!sctp_chunk_length_valid(chunk, sizeof(sctp_operr_chunk_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); sctp_walk_errors(err, chunk->chunk_hdr); if ((void *)err != (void *)chunk->chunk_end) - return sctp_sf_violation_paramlen(ep, asoc, type, arg, + return sctp_sf_violation_paramlen(net, ep, asoc, type, arg, (void *)err, commands); sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_OPERR, @@ -3259,7 +3295,8 @@ sctp_disposition_t sctp_sf_operr_notify(const struct sctp_endpoint *ep, * * The return value is the disposition. */ -sctp_disposition_t sctp_sf_do_9_2_final(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_do_9_2_final(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -3268,14 +3305,13 @@ sctp_disposition_t sctp_sf_do_9_2_final(const struct sctp_endpoint *ep, struct sctp_chunk *chunk = arg; struct sctp_chunk *reply; struct sctp_ulpevent *ev; - struct net *net; if (!sctp_vtag_verify(chunk, asoc)) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* Make sure that the SHUTDOWN_ACK chunk has a valid length. */ if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); /* 10.2 H) SHUTDOWN COMPLETE notification * @@ -3308,7 +3344,6 @@ sctp_disposition_t sctp_sf_do_9_2_final(const struct sctp_endpoint *ep, sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, SCTP_STATE(SCTP_STATE_CLOSED)); - net = sock_net(asoc->base.sk); SCTP_INC_STATS(net, SCTP_MIB_SHUTDOWNS); SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB); sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); @@ -3343,7 +3378,8 @@ nomem: * receiver of the OOTB packet shall discard the OOTB packet and take * no further action. */ -sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_ootb(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -3356,16 +3392,14 @@ sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep, __u8 *ch_end; int ootb_shut_ack = 0; int ootb_cookie_ack = 0; - struct net *net; - net = sock_net(asoc->base.sk); SCTP_INC_STATS(net, SCTP_MIB_OUTOFBLUES); ch = (sctp_chunkhdr_t *) chunk->chunk_hdr; do { /* Report violation if the chunk is less then minimal */ if (ntohs(ch->length) < sizeof(sctp_chunkhdr_t)) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); /* Now that we know we at least have a chunk header, @@ -3380,7 +3414,7 @@ sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep, * sending an ABORT of its own. */ if (SCTP_CID_ABORT == ch->type) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* RFC 8.4, 7) If the packet contains a "Stale cookie" ERROR * or a COOKIE ACK the SCTP Packet should be silently @@ -3402,18 +3436,18 @@ sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep, /* Report violation if chunk len overflows */ ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length)); if (ch_end > skb_tail_pointer(skb)) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); ch = (sctp_chunkhdr_t *) ch_end; } while (ch_end < skb_tail_pointer(skb)); if (ootb_shut_ack) - return sctp_sf_shut_8_4_5(ep, asoc, type, arg, commands); + return sctp_sf_shut_8_4_5(net, ep, asoc, type, arg, commands); else if (ootb_cookie_ack) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); else - return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); + return sctp_sf_tabort_8_4_8(net, ep, asoc, type, arg, commands); } /* @@ -3437,7 +3471,8 @@ sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep, * * The return value is the disposition of the chunk. */ -static sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep, +static sctp_disposition_t sctp_sf_shut_8_4_5(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -3446,9 +3481,7 @@ static sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep, struct sctp_packet *packet = NULL; struct sctp_chunk *chunk = arg; struct sctp_chunk *shut; - struct net *net; - net = sock_net(ep->base.sk); packet = sctp_ootb_pkt_new(net, asoc, chunk); if (packet) { @@ -3479,13 +3512,13 @@ static sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep, * the reset of the packet. */ if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t))) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* We need to discard the rest of the packet to prevent * potential bomming attacks from additional bundled chunks. * This is documented in SCTP Threats ID. */ - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); } return SCTP_DISPOSITION_NOMEM; @@ -3502,7 +3535,8 @@ static sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep, * chunks. --piggy ] * */ -sctp_disposition_t sctp_sf_do_8_5_1_E_sa(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_do_8_5_1_E_sa(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -3512,7 +3546,7 @@ sctp_disposition_t sctp_sf_do_8_5_1_E_sa(const struct sctp_endpoint *ep, /* Make sure that the SHUTDOWN_ACK chunk has a valid length. */ if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); /* Although we do have an association in this case, it corresponds @@ -3520,13 +3554,14 @@ sctp_disposition_t sctp_sf_do_8_5_1_E_sa(const struct sctp_endpoint *ep, * packet and the state function that handles OOTB SHUTDOWN_ACK is * called with a NULL association. */ - SCTP_INC_STATS(sock_net(ep->base.sk), SCTP_MIB_OUTOFBLUES); + SCTP_INC_STATS(net, SCTP_MIB_OUTOFBLUES); - return sctp_sf_shut_8_4_5(ep, NULL, type, arg, commands); + return sctp_sf_shut_8_4_5(net, ep, NULL, type, arg, commands); } /* ADDIP Section 4.2 Upon reception of an ASCONF Chunk. */ -sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_do_asconf(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, sctp_cmd_seq_t *commands) @@ -3542,7 +3577,7 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep, if (!sctp_vtag_verify(chunk, asoc)) { sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG, SCTP_NULL()); - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); } /* ADD-IP: Section 4.1.1 @@ -3552,11 +3587,11 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep, * described in [I-D.ietf-tsvwg-sctp-auth]. */ if (!sctp_addip_noauth && !chunk->auth) - return sctp_sf_discard_chunk(ep, asoc, type, arg, commands); + return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands); /* Make sure that the ASCONF ADDIP chunk has a valid length. */ if (!sctp_chunk_length_valid(chunk, sizeof(sctp_addip_chunk_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); hdr = (sctp_addiphdr_t *)chunk->skb->data; @@ -3565,7 +3600,7 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep, addr_param = (union sctp_addr_param *)hdr->params; length = ntohs(addr_param->p.length); if (length < sizeof(sctp_paramhdr_t)) - return sctp_sf_violation_paramlen(ep, asoc, type, arg, + return sctp_sf_violation_paramlen(net, ep, asoc, type, arg, (void *)addr_param, commands); /* Verify the ASCONF chunk before processing it. */ @@ -3573,7 +3608,7 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep, (sctp_paramhdr_t *)((void *)addr_param + length), (void *)chunk->chunk_end, &err_param)) - return sctp_sf_violation_paramlen(ep, asoc, type, arg, + return sctp_sf_violation_paramlen(net, ep, asoc, type, arg, (void *)err_param, commands); /* ADDIP 5.2 E1) Compare the value of the serial number to the value @@ -3653,7 +3688,8 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep, * When building TLV parameters for the ASCONF Chunk that will add or * delete IP addresses the D0 to D13 rules should be applied: */ -sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_do_asconf_ack(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, sctp_cmd_seq_t *commands) @@ -3668,7 +3704,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep, if (!sctp_vtag_verify(asconf_ack, asoc)) { sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG, SCTP_NULL()); - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); } /* ADD-IP, Section 4.1.2: @@ -3678,11 +3714,11 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep, * described in [I-D.ietf-tsvwg-sctp-auth]. */ if (!sctp_addip_noauth && !asconf_ack->auth) - return sctp_sf_discard_chunk(ep, asoc, type, arg, commands); + return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands); /* Make sure that the ADDIP chunk has a valid length. */ if (!sctp_chunk_length_valid(asconf_ack, sizeof(sctp_addip_chunk_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); addip_hdr = (sctp_addiphdr_t *)asconf_ack->skb->data; @@ -3693,7 +3729,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep, (sctp_paramhdr_t *)addip_hdr->params, (void *)asconf_ack->chunk_end, &err_param)) - return sctp_sf_violation_paramlen(ep, asoc, type, arg, + return sctp_sf_violation_paramlen(net, ep, asoc, type, arg, (void *)err_param, commands); if (last_asconf) { @@ -3711,7 +3747,6 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep, */ if (ADDIP_SERIAL_gte(rcvd_serial, sent_serial + 1) && !(asoc->addip_last_asconf)) { - struct net *net; abort = sctp_make_abort(asoc, asconf_ack, sizeof(sctp_errhdr_t)); if (abort) { @@ -3729,14 +3764,12 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep, SCTP_ERROR(ECONNABORTED)); sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_PERR(SCTP_ERROR_ASCONF_ACK)); - net = sock_net(asoc->base.sk); SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB); return SCTP_DISPOSITION_ABORT; } if ((rcvd_serial == sent_serial) && asoc->addip_last_asconf) { - struct net *net; sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO)); @@ -3765,7 +3798,6 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep, SCTP_ERROR(ECONNABORTED)); sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_PERR(SCTP_ERROR_ASCONF_ACK)); - net = sock_net(asoc->base.sk); SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB); return SCTP_DISPOSITION_ABORT; @@ -3788,7 +3820,8 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_eat_fwd_tsn(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_eat_fwd_tsn(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -3803,12 +3836,12 @@ sctp_disposition_t sctp_sf_eat_fwd_tsn(const struct sctp_endpoint *ep, if (!sctp_vtag_verify(chunk, asoc)) { sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG, SCTP_NULL()); - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); } /* Make sure that the FORWARD_TSN chunk has valid length. */ if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_fwdtsn_chunk))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); fwdtsn_hdr = (struct sctp_fwdtsn_hdr *)chunk->skb->data; @@ -3855,6 +3888,7 @@ discard_noforce: } sctp_disposition_t sctp_sf_eat_fwd_tsn_fast( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -3870,12 +3904,12 @@ sctp_disposition_t sctp_sf_eat_fwd_tsn_fast( if (!sctp_vtag_verify(chunk, asoc)) { sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG, SCTP_NULL()); - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); } /* Make sure that the FORWARD_TSN chunk has a valid length. */ if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_fwdtsn_chunk))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); fwdtsn_hdr = (struct sctp_fwdtsn_hdr *)chunk->skb->data; @@ -3942,7 +3976,8 @@ gen_shutdown: * * The return value is the disposition of the chunk. */ -static sctp_ierror_t sctp_sf_authenticate(const struct sctp_endpoint *ep, +static sctp_ierror_t sctp_sf_authenticate(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, struct sctp_chunk *chunk) @@ -4015,7 +4050,8 @@ nomem: return SCTP_IERROR_NOMEM; } -sctp_disposition_t sctp_sf_eat_auth(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_eat_auth(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -4028,21 +4064,21 @@ sctp_disposition_t sctp_sf_eat_auth(const struct sctp_endpoint *ep, /* Make sure that the peer has AUTH capable */ if (!asoc->peer.auth_capable) - return sctp_sf_unk_chunk(ep, asoc, type, arg, commands); + return sctp_sf_unk_chunk(net, ep, asoc, type, arg, commands); if (!sctp_vtag_verify(chunk, asoc)) { sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG, SCTP_NULL()); - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); } /* Make sure that the AUTH chunk has valid length. */ if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_auth_chunk))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); auth_hdr = (struct sctp_authhdr *)chunk->skb->data; - error = sctp_sf_authenticate(ep, asoc, type, chunk); + error = sctp_sf_authenticate(net, ep, asoc, type, chunk); switch (error) { case SCTP_IERROR_AUTH_BAD_HMAC: /* Generate the ERROR chunk and discard the rest @@ -4059,10 +4095,10 @@ sctp_disposition_t sctp_sf_eat_auth(const struct sctp_endpoint *ep, /* Fall Through */ case SCTP_IERROR_AUTH_BAD_KEYID: case SCTP_IERROR_BAD_SIG: - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); case SCTP_IERROR_PROTO_VIOLATION: - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); case SCTP_IERROR_NOMEM: @@ -4111,7 +4147,8 @@ sctp_disposition_t sctp_sf_eat_auth(const struct sctp_endpoint *ep, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_unk_chunk(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_unk_chunk(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -4124,20 +4161,20 @@ sctp_disposition_t sctp_sf_unk_chunk(const struct sctp_endpoint *ep, SCTP_DEBUG_PRINTK("Processing the unknown chunk id %d.\n", type.chunk); if (!sctp_vtag_verify(unk_chunk, asoc)) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* Make sure that the chunk has a valid length. * Since we don't know the chunk type, we use a general * chunkhdr structure to make a comparison. */ if (!sctp_chunk_length_valid(unk_chunk, sizeof(sctp_chunkhdr_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); switch (type.chunk & SCTP_CID_ACTION_MASK) { case SCTP_CID_ACTION_DISCARD: /* Discard the packet. */ - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); break; case SCTP_CID_ACTION_DISCARD_ERR: /* Generate an ERROR chunk as response. */ @@ -4152,7 +4189,7 @@ sctp_disposition_t sctp_sf_unk_chunk(const struct sctp_endpoint *ep, } /* Discard the packet. */ - sctp_sf_pdiscard(ep, asoc, type, arg, commands); + sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); return SCTP_DISPOSITION_CONSUME; break; case SCTP_CID_ACTION_SKIP: @@ -4194,7 +4231,8 @@ sctp_disposition_t sctp_sf_unk_chunk(const struct sctp_endpoint *ep, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_discard_chunk(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_discard_chunk(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -4207,7 +4245,7 @@ sctp_disposition_t sctp_sf_discard_chunk(const struct sctp_endpoint *ep, * chunkhdr structure to make a comparison. */ if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); SCTP_DEBUG_PRINTK("Chunk %d is discarded\n", type.chunk); @@ -4232,13 +4270,14 @@ sctp_disposition_t sctp_sf_discard_chunk(const struct sctp_endpoint *ep, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_pdiscard(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_pdiscard(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, sctp_cmd_seq_t *commands) { - SCTP_INC_STATS(sock_net(asoc->base.sk), SCTP_MIB_IN_PKT_DISCARDS); + SCTP_INC_STATS(net, SCTP_MIB_IN_PKT_DISCARDS); sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET, SCTP_NULL()); return SCTP_DISPOSITION_CONSUME; @@ -4259,7 +4298,8 @@ sctp_disposition_t sctp_sf_pdiscard(const struct sctp_endpoint *ep, * We simply tag the chunk as a violation. The state machine will log * the violation and continue. */ -sctp_disposition_t sctp_sf_violation(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_violation(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -4269,7 +4309,7 @@ sctp_disposition_t sctp_sf_violation(const struct sctp_endpoint *ep, /* Make sure that the chunk has a valid length. */ if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); return SCTP_DISPOSITION_VIOLATION; @@ -4279,6 +4319,7 @@ sctp_disposition_t sctp_sf_violation(const struct sctp_endpoint *ep, * Common function to handle a protocol violation. */ static sctp_disposition_t sctp_sf_abort_violation( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, void *arg, @@ -4289,7 +4330,6 @@ static sctp_disposition_t sctp_sf_abort_violation( struct sctp_packet *packet = NULL; struct sctp_chunk *chunk = arg; struct sctp_chunk *abort = NULL; - struct net *net; /* SCTP-AUTH, Section 6.3: * It should be noted that if the receiver wants to tear @@ -4310,7 +4350,6 @@ static sctp_disposition_t sctp_sf_abort_violation( if (!abort) goto nomem; - net = sock_net(ep->base.sk); if (asoc) { /* Treat INIT-ACK as a special case during COOKIE-WAIT. */ if (chunk->chunk_hdr->type == SCTP_CID_INIT_ACK && @@ -4369,7 +4408,7 @@ static sctp_disposition_t sctp_sf_abort_violation( SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); discard: - sctp_sf_pdiscard(ep, asoc, SCTP_ST_CHUNK(0), arg, commands); + sctp_sf_pdiscard(net, ep, asoc, SCTP_ST_CHUNK(0), arg, commands); return SCTP_DISPOSITION_ABORT; nomem_pkt: @@ -4398,6 +4437,7 @@ nomem: * Generate an ABORT chunk and terminate the association. */ static sctp_disposition_t sctp_sf_violation_chunklen( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -4406,7 +4446,7 @@ static sctp_disposition_t sctp_sf_violation_chunklen( { static const char err_str[]="The following chunk had invalid length:"; - return sctp_sf_abort_violation(ep, asoc, arg, commands, err_str, + return sctp_sf_abort_violation(net, ep, asoc, arg, commands, err_str, sizeof(err_str)); } @@ -4417,6 +4457,7 @@ static sctp_disposition_t sctp_sf_violation_chunklen( * the length is considered as invalid. */ static sctp_disposition_t sctp_sf_violation_paramlen( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -4426,7 +4467,6 @@ static sctp_disposition_t sctp_sf_violation_paramlen( struct sctp_chunk *chunk = arg; struct sctp_paramhdr *param = ext; struct sctp_chunk *abort = NULL; - struct net *net; if (sctp_auth_recv_cid(SCTP_CID_ABORT, asoc)) goto discard; @@ -4436,7 +4476,6 @@ static sctp_disposition_t sctp_sf_violation_paramlen( if (!abort) goto nomem; - net = sock_net(asoc->base.sk); sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort)); SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS); @@ -4448,7 +4487,7 @@ static sctp_disposition_t sctp_sf_violation_paramlen( SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); discard: - sctp_sf_pdiscard(ep, asoc, SCTP_ST_CHUNK(0), arg, commands); + sctp_sf_pdiscard(net, ep, asoc, SCTP_ST_CHUNK(0), arg, commands); return SCTP_DISPOSITION_ABORT; nomem: return SCTP_DISPOSITION_NOMEM; @@ -4461,6 +4500,7 @@ nomem: * error code. */ static sctp_disposition_t sctp_sf_violation_ctsn( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -4469,7 +4509,7 @@ static sctp_disposition_t sctp_sf_violation_ctsn( { static const char err_str[]="The cumulative tsn ack beyond the max tsn currently sent:"; - return sctp_sf_abort_violation(ep, asoc, arg, commands, err_str, + return sctp_sf_abort_violation(net, ep, asoc, arg, commands, err_str, sizeof(err_str)); } @@ -4480,6 +4520,7 @@ static sctp_disposition_t sctp_sf_violation_ctsn( * on the path and we may not want to continue this communication. */ static sctp_disposition_t sctp_sf_violation_chunk( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -4489,9 +4530,9 @@ static sctp_disposition_t sctp_sf_violation_chunk( static const char err_str[]="The following chunk violates protocol:"; if (!asoc) - return sctp_sf_violation(ep, asoc, type, arg, commands); + return sctp_sf_violation(net, ep, asoc, type, arg, commands); - return sctp_sf_abort_violation(ep, asoc, arg, commands, err_str, + return sctp_sf_abort_violation(net, ep, asoc, arg, commands, err_str, sizeof(err_str)); } /*************************************************************************** @@ -4554,7 +4595,8 @@ static sctp_disposition_t sctp_sf_violation_chunk( * * The return value is a disposition. */ -sctp_disposition_t sctp_sf_do_prm_asoc(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_do_prm_asoc(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -4665,7 +4707,8 @@ nomem: * * The return value is the disposition. */ -sctp_disposition_t sctp_sf_do_prm_send(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_do_prm_send(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -4704,6 +4747,7 @@ sctp_disposition_t sctp_sf_do_prm_send(const struct sctp_endpoint *ep, * The return value is the disposition. */ sctp_disposition_t sctp_sf_do_9_2_prm_shutdown( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -4725,7 +4769,7 @@ sctp_disposition_t sctp_sf_do_9_2_prm_shutdown( disposition = SCTP_DISPOSITION_CONSUME; if (sctp_outq_is_empty(&asoc->outqueue)) { - disposition = sctp_sf_do_9_2_start_shutdown(ep, asoc, type, + disposition = sctp_sf_do_9_2_start_shutdown(net, ep, asoc, type, arg, commands); } return disposition; @@ -4759,6 +4803,7 @@ sctp_disposition_t sctp_sf_do_9_2_prm_shutdown( * The return value is the disposition. */ sctp_disposition_t sctp_sf_do_9_1_prm_abort( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -4775,7 +4820,6 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort( */ struct sctp_chunk *abort = arg; sctp_disposition_t retval; - struct net *net; retval = SCTP_DISPOSITION_CONSUME; @@ -4791,7 +4835,6 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort( sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_PERR(SCTP_ERROR_USER_ABORT)); - net = sock_net(asoc->base.sk); SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB); @@ -4799,7 +4842,8 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort( } /* We tried an illegal operation on an association which is closed. */ -sctp_disposition_t sctp_sf_error_closed(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_error_closed(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -4812,7 +4856,8 @@ sctp_disposition_t sctp_sf_error_closed(const struct sctp_endpoint *ep, /* We tried an illegal operation on an association which is shutting * down. */ -sctp_disposition_t sctp_sf_error_shutdown(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_error_shutdown(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -4838,14 +4883,13 @@ sctp_disposition_t sctp_sf_error_shutdown(const struct sctp_endpoint *ep, * (timers) */ sctp_disposition_t sctp_sf_cookie_wait_prm_shutdown( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, sctp_cmd_seq_t *commands) { - struct net *net = sock_net(asoc->base.sk); - sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); @@ -4874,6 +4918,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_shutdown( * (timers) */ sctp_disposition_t sctp_sf_cookie_echoed_prm_shutdown( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -4882,7 +4927,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_prm_shutdown( /* There is a single T1 timer, so we should be able to use * common function with the COOKIE-WAIT state. */ - return sctp_sf_cookie_wait_prm_shutdown(ep, asoc, type, arg, commands); + return sctp_sf_cookie_wait_prm_shutdown(net, ep, asoc, type, arg, commands); } /* @@ -4900,6 +4945,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_prm_shutdown( * (timers) */ sctp_disposition_t sctp_sf_cookie_wait_prm_abort( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -4908,7 +4954,6 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort( { struct sctp_chunk *abort = arg; sctp_disposition_t retval; - struct net *net = sock_net(asoc->base.sk); /* Stop T1-init timer */ sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, @@ -4950,6 +4995,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort( * (timers) */ sctp_disposition_t sctp_sf_cookie_echoed_prm_abort( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -4959,7 +5005,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_prm_abort( /* There is a single T1 timer, so we should be able to use * common function with the COOKIE-WAIT state. */ - return sctp_sf_cookie_wait_prm_abort(ep, asoc, type, arg, commands); + return sctp_sf_cookie_wait_prm_abort(net, ep, asoc, type, arg, commands); } /* @@ -4975,6 +5021,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_prm_abort( * (timers) */ sctp_disposition_t sctp_sf_shutdown_pending_prm_abort( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -4985,7 +5032,7 @@ sctp_disposition_t sctp_sf_shutdown_pending_prm_abort( sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD)); - return sctp_sf_do_9_1_prm_abort(ep, asoc, type, arg, commands); + return sctp_sf_do_9_1_prm_abort(net, ep, asoc, type, arg, commands); } /* @@ -5001,6 +5048,7 @@ sctp_disposition_t sctp_sf_shutdown_pending_prm_abort( * (timers) */ sctp_disposition_t sctp_sf_shutdown_sent_prm_abort( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -5015,7 +5063,7 @@ sctp_disposition_t sctp_sf_shutdown_sent_prm_abort( sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD)); - return sctp_sf_do_9_1_prm_abort(ep, asoc, type, arg, commands); + return sctp_sf_do_9_1_prm_abort(net, ep, asoc, type, arg, commands); } /* @@ -5031,6 +5079,7 @@ sctp_disposition_t sctp_sf_shutdown_sent_prm_abort( * (timers) */ sctp_disposition_t sctp_sf_shutdown_ack_sent_prm_abort( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -5040,7 +5089,7 @@ sctp_disposition_t sctp_sf_shutdown_ack_sent_prm_abort( /* The same T2 timer, so we should be able to use * common function with the SHUTDOWN-SENT state. */ - return sctp_sf_shutdown_sent_prm_abort(ep, asoc, type, arg, commands); + return sctp_sf_shutdown_sent_prm_abort(net, ep, asoc, type, arg, commands); } /* @@ -5066,6 +5115,7 @@ sctp_disposition_t sctp_sf_shutdown_ack_sent_prm_abort( * association on which a heartbeat should be issued. */ sctp_disposition_t sctp_sf_do_prm_requestheartbeat( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -5097,7 +5147,8 @@ sctp_disposition_t sctp_sf_do_prm_requestheartbeat( * When an endpoint has an ASCONF signaled change to be sent to the * remote endpoint it should do A1 to A9 */ -sctp_disposition_t sctp_sf_do_prm_asconf(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_do_prm_asconf(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -5118,6 +5169,7 @@ sctp_disposition_t sctp_sf_do_prm_asconf(const struct sctp_endpoint *ep, * The return value is the disposition of the primitive. */ sctp_disposition_t sctp_sf_ignore_primitive( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -5139,6 +5191,7 @@ sctp_disposition_t sctp_sf_ignore_primitive( * retransmit, the stack will immediately send up this notification. */ sctp_disposition_t sctp_sf_do_no_pending_tsn( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -5170,6 +5223,7 @@ sctp_disposition_t sctp_sf_do_no_pending_tsn( * The return value is the disposition. */ sctp_disposition_t sctp_sf_do_9_2_start_shutdown( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -5239,6 +5293,7 @@ nomem: * The return value is the disposition. */ sctp_disposition_t sctp_sf_do_9_2_shutdown_ack( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -5257,11 +5312,11 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown_ack( */ if (chunk) { if (!sctp_vtag_verify(chunk, asoc)) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* Make sure that the SHUTDOWN chunk has a valid length. */ if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_shutdown_chunk_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); } @@ -5309,7 +5364,8 @@ nomem: * * The return value is the disposition of the event. */ -sctp_disposition_t sctp_sf_ignore_other(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_ignore_other(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -5334,14 +5390,14 @@ sctp_disposition_t sctp_sf_ignore_other(const struct sctp_endpoint *ep, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_6_3_3_rtx(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_do_6_3_3_rtx(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, sctp_cmd_seq_t *commands) { struct sctp_transport *transport = arg; - struct net *net = sock_net(asoc->base.sk); SCTP_INC_STATS(net, SCTP_MIB_T3_RTX_EXPIREDS); @@ -5421,13 +5477,13 @@ sctp_disposition_t sctp_sf_do_6_3_3_rtx(const struct sctp_endpoint *ep, * allow. However, an SCTP transmitter MUST NOT be more aggressive than * the following algorithms allow. */ -sctp_disposition_t sctp_sf_do_6_2_sack(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_do_6_2_sack(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, sctp_cmd_seq_t *commands) { - struct net *net = sock_net(asoc->base.sk); SCTP_INC_STATS(net, SCTP_MIB_DELAY_SACK_EXPIREDS); sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_FORCE()); return SCTP_DISPOSITION_CONSUME; @@ -5452,7 +5508,8 @@ sctp_disposition_t sctp_sf_do_6_2_sack(const struct sctp_endpoint *ep, * (timers, events) * */ -sctp_disposition_t sctp_sf_t1_init_timer_expire(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_t1_init_timer_expire(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -5461,7 +5518,6 @@ sctp_disposition_t sctp_sf_t1_init_timer_expire(const struct sctp_endpoint *ep, struct sctp_chunk *repl = NULL; struct sctp_bind_addr *bp; int attempts = asoc->init_err_counter + 1; - struct net *net = sock_net(asoc->base.sk); SCTP_DEBUG_PRINTK("Timer T1 expired (INIT).\n"); SCTP_INC_STATS(net, SCTP_MIB_T1_INIT_EXPIREDS); @@ -5514,7 +5570,8 @@ sctp_disposition_t sctp_sf_t1_init_timer_expire(const struct sctp_endpoint *ep, * (timers, events) * */ -sctp_disposition_t sctp_sf_t1_cookie_timer_expire(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_t1_cookie_timer_expire(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -5522,7 +5579,6 @@ sctp_disposition_t sctp_sf_t1_cookie_timer_expire(const struct sctp_endpoint *ep { struct sctp_chunk *repl = NULL; int attempts = asoc->init_err_counter + 1; - struct net *net = sock_net(asoc->base.sk); SCTP_DEBUG_PRINTK("Timer T1 expired (COOKIE-ECHO).\n"); SCTP_INC_STATS(net, SCTP_MIB_T1_COOKIE_EXPIREDS); @@ -5563,14 +5619,14 @@ sctp_disposition_t sctp_sf_t1_cookie_timer_expire(const struct sctp_endpoint *ep * the T2-Shutdown timer, giving its peer ample opportunity to transmit * all of its queued DATA chunks that have not yet been sent. */ -sctp_disposition_t sctp_sf_t2_timer_expire(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_t2_timer_expire(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, sctp_cmd_seq_t *commands) { struct sctp_chunk *reply = NULL; - struct net *net = sock_net(asoc->base.sk); SCTP_DEBUG_PRINTK("Timer T2 expired.\n"); SCTP_INC_STATS(net, SCTP_MIB_T2_SHUTDOWN_EXPIREDS); @@ -5633,6 +5689,7 @@ nomem: * If the T4 RTO timer expires the endpoint should do B1 to B5 */ sctp_disposition_t sctp_sf_t4_timer_expire( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, @@ -5641,7 +5698,6 @@ sctp_disposition_t sctp_sf_t4_timer_expire( { struct sctp_chunk *chunk = asoc->addip_last_asconf; struct sctp_transport *transport = chunk->transport; - struct net *net = sock_net(asoc->base.sk); SCTP_INC_STATS(net, SCTP_MIB_T4_RTO_EXPIREDS); @@ -5704,14 +5760,14 @@ sctp_disposition_t sctp_sf_t4_timer_expire( * At the expiration of this timer the sender SHOULD abort the association * by sending an ABORT chunk. */ -sctp_disposition_t sctp_sf_t5_timer_expire(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_t5_timer_expire(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, sctp_cmd_seq_t *commands) { struct sctp_chunk *reply = NULL; - struct net *net = sock_net(asoc->base.sk); SCTP_DEBUG_PRINTK("Timer T5 expired.\n"); SCTP_INC_STATS(net, SCTP_MIB_T5_SHUTDOWN_GUARD_EXPIREDS); @@ -5740,13 +5796,13 @@ nomem: * the user. So this routine looks same as sctp_sf_do_9_2_prm_shutdown(). */ sctp_disposition_t sctp_sf_autoclose_timer_expire( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, sctp_cmd_seq_t *commands) { - struct net *net = sock_net(asoc->base.sk); int disposition; SCTP_INC_STATS(net, SCTP_MIB_AUTOCLOSE_EXPIREDS); @@ -5764,7 +5820,7 @@ sctp_disposition_t sctp_sf_autoclose_timer_expire( disposition = SCTP_DISPOSITION_CONSUME; if (sctp_outq_is_empty(&asoc->outqueue)) { - disposition = sctp_sf_do_9_2_start_shutdown(ep, asoc, type, + disposition = sctp_sf_do_9_2_start_shutdown(net, ep, asoc, type, arg, commands); } return disposition; @@ -5782,7 +5838,8 @@ sctp_disposition_t sctp_sf_autoclose_timer_expire( * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_not_impl(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_not_impl(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -5799,7 +5856,8 @@ sctp_disposition_t sctp_sf_not_impl(const struct sctp_endpoint *ep, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_bug(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_bug(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -5819,7 +5877,8 @@ sctp_disposition_t sctp_sf_bug(const struct sctp_endpoint *ep, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_timer_ignore(const struct sctp_endpoint *ep, +sctp_disposition_t sctp_sf_timer_ignore(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -5861,7 +5920,8 @@ static struct sctp_sackhdr *sctp_sm_pull_sack(struct sctp_chunk *chunk) /* Create an ABORT packet to be sent as a response, with the specified * error causes. */ -static struct sctp_packet *sctp_abort_pkt_new(const struct sctp_endpoint *ep, +static struct sctp_packet *sctp_abort_pkt_new(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, struct sctp_chunk *chunk, const void *payload, @@ -5869,9 +5929,7 @@ static struct sctp_packet *sctp_abort_pkt_new(const struct sctp_endpoint *ep, { struct sctp_packet *packet; struct sctp_chunk *abort; - struct net *net; - net = sock_net(ep->base.sk); packet = sctp_ootb_pkt_new(net, asoc, chunk); if (packet) { @@ -5984,7 +6042,8 @@ void sctp_ootb_pkt_free(struct sctp_packet *packet) } /* Send a stale cookie error when a invalid COOKIE ECHO chunk is found */ -static void sctp_send_stale_cookie_err(const struct sctp_endpoint *ep, +static void sctp_send_stale_cookie_err(struct net *net, + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const struct sctp_chunk *chunk, sctp_cmd_seq_t *commands, @@ -5993,7 +6052,6 @@ static void sctp_send_stale_cookie_err(const struct sctp_endpoint *ep, struct sctp_packet *packet; if (err_chunk) { - struct net *net = sock_net(ep->base.sk); packet = sctp_ootb_pkt_new(net, asoc, chunk); if (packet) { struct sctp_signed_cookie *cookie; @@ -6027,7 +6085,7 @@ static int sctp_eat_data(const struct sctp_association *asoc, __u32 tsn; struct sctp_tsnmap *map = (struct sctp_tsnmap *)&asoc->peer.tsn_map; struct sock *sk = asoc->base.sk; - struct net *net; + struct net *net = sock_net(sk); u16 ssn; u16 sid; u8 ordered = 0; @@ -6144,7 +6202,6 @@ static int sctp_eat_data(const struct sctp_association *asoc, * No User Data: This error cause is returned to the originator of a * DATA chunk if a received DATA chunk has no user data. */ - net = sock_net(sk); if (unlikely(0 == datalen)) { err = sctp_make_abort_no_data(asoc, chunk, tsn); if (err) { -- cgit v1.2.3 From f53b5b097e58361668b785eff9f7bcd12b4255ec Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 7 Aug 2012 07:29:08 +0000 Subject: sctp: Push struct net down into sctp_verify_ext_param Add struct net as a parameter to sctp_verify_param so it can be passed to sctp_verify_ext_param where struct net will be needed when the sctp tunables become per net tunables. Add struct net as a parameter to sctp_verify_init so struct net can be passed to sctp_verify_param. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 6 +++--- net/sctp/sm_make_chunk.c | 11 ++++++----- net/sctp/sm_statefuns.c | 6 +++--- 3 files changed, 12 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index b0882f3a3a51..18052b421203 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1410,9 +1410,9 @@ struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *, int sctp_has_association(struct net *net, const union sctp_addr *laddr, const union sctp_addr *paddr); -int sctp_verify_init(const struct sctp_association *asoc, sctp_cid_t, - sctp_init_chunk_t *peer_init, struct sctp_chunk *chunk, - struct sctp_chunk **err_chunk); +int sctp_verify_init(struct net *net, const struct sctp_association *asoc, + sctp_cid_t, sctp_init_chunk_t *peer_init, + struct sctp_chunk *chunk, struct sctp_chunk **err_chunk); int sctp_process_init(struct sctp_association *, struct sctp_chunk *chunk, const union sctp_addr *peer, sctp_init_chunk_t *init, gfp_t gfp); diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index fb12835e95c2..a4b096f85a68 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1940,7 +1940,7 @@ static int sctp_process_hn_param(const struct sctp_association *asoc, return 0; } -static int sctp_verify_ext_param(union sctp_params param) +static int sctp_verify_ext_param(struct net *net, union sctp_params param) { __u16 num_ext = ntohs(param.p->length) - sizeof(sctp_paramhdr_t); int have_auth = 0; @@ -2081,7 +2081,8 @@ static sctp_ierror_t sctp_process_unk_param(const struct sctp_association *asoc, * SCTP_IERROR_ERROR - stop processing, trigger an ERROR * SCTP_IERROR_NO_ERROR - continue with the chunk */ -static sctp_ierror_t sctp_verify_param(const struct sctp_association *asoc, +static sctp_ierror_t sctp_verify_param(struct net *net, + const struct sctp_association *asoc, union sctp_params param, sctp_cid_t cid, struct sctp_chunk *chunk, @@ -2110,7 +2111,7 @@ static sctp_ierror_t sctp_verify_param(const struct sctp_association *asoc, break; case SCTP_PARAM_SUPPORTED_EXT: - if (!sctp_verify_ext_param(param)) + if (!sctp_verify_ext_param(net, param)) return SCTP_IERROR_ABORT; break; @@ -2198,7 +2199,7 @@ fallthrough: } /* Verify the INIT packet before we process it. */ -int sctp_verify_init(const struct sctp_association *asoc, +int sctp_verify_init(struct net *net, const struct sctp_association *asoc, sctp_cid_t cid, sctp_init_chunk_t *peer_init, struct sctp_chunk *chunk, @@ -2245,7 +2246,7 @@ int sctp_verify_init(const struct sctp_association *asoc, /* Verify all the variable length parameters */ sctp_walk_params(param, peer_init, init_hdr.params) { - result = sctp_verify_param(asoc, param, cid, chunk, errp); + result = sctp_verify_param(net, asoc, param, cid, chunk, errp); switch (result) { case SCTP_IERROR_ABORT: case SCTP_IERROR_NOMEM: diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 19f3bff84193..e17ada47afc4 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -364,7 +364,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(struct net *net, /* Verify the INIT chunk before processing it. */ err_chunk = NULL; - if (!sctp_verify_init(asoc, chunk->chunk_hdr->type, + if (!sctp_verify_init(net, asoc, chunk->chunk_hdr->type, (sctp_init_chunk_t *)chunk->chunk_hdr, chunk, &err_chunk)) { /* This chunk contains fatal error. It is to be discarded. @@ -531,7 +531,7 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(struct net *net, /* Verify the INIT chunk before processing it. */ err_chunk = NULL; - if (!sctp_verify_init(asoc, chunk->chunk_hdr->type, + if (!sctp_verify_init(net, asoc, chunk->chunk_hdr->type, (sctp_init_chunk_t *)chunk->chunk_hdr, chunk, &err_chunk)) { @@ -1429,7 +1429,7 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( /* Verify the INIT chunk before processing it. */ err_chunk = NULL; - if (!sctp_verify_init(asoc, chunk->chunk_hdr->type, + if (!sctp_verify_init(net, asoc, chunk->chunk_hdr->type, (sctp_init_chunk_t *)chunk->chunk_hdr, chunk, &err_chunk)) { /* This chunk contains fatal error. It is to be discarded. -- cgit v1.2.3 From e1fc3b14f9a90d9591016749289f2c3d7b35fbf4 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 7 Aug 2012 07:29:57 +0000 Subject: sctp: Make sysctl tunables per net Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- include/net/netns/sctp.h | 90 ++++++++++++++++++++++ include/net/sctp/structs.h | 116 ---------------------------- net/sctp/associola.c | 10 ++- net/sctp/auth.c | 20 ++++- net/sctp/bind_addr.c | 2 +- net/sctp/endpointola.c | 9 ++- net/sctp/input.c | 2 +- net/sctp/protocol.c | 128 +++++++++++++++---------------- net/sctp/sm_make_chunk.c | 47 ++++++------ net/sctp/sm_statefuns.c | 4 +- net/sctp/sm_statetable.c | 6 +- net/sctp/socket.c | 65 +++++++++------- net/sctp/sysctl.c | 185 +++++++++++++++++++++++---------------------- net/sctp/transport.c | 15 ++-- 14 files changed, 355 insertions(+), 344 deletions(-) (limited to 'include') diff --git a/include/net/netns/sctp.h b/include/net/netns/sctp.h index 9576b60cbd25..5e5eb1f9f14b 100644 --- a/include/net/netns/sctp.h +++ b/include/net/netns/sctp.h @@ -36,6 +36,96 @@ struct netns_sctp { /* Lock that protects the local_addr_list writers */ spinlock_t local_addr_lock; + /* RFC2960 Section 14. Suggested SCTP Protocol Parameter Values + * + * The following protocol parameters are RECOMMENDED: + * + * RTO.Initial - 3 seconds + * RTO.Min - 1 second + * RTO.Max - 60 seconds + * RTO.Alpha - 1/8 (3 when converted to right shifts.) + * RTO.Beta - 1/4 (2 when converted to right shifts.) + */ + unsigned int rto_initial; + unsigned int rto_min; + unsigned int rto_max; + + /* Note: rto_alpha and rto_beta are really defined as inverse + * powers of two to facilitate integer operations. + */ + int rto_alpha; + int rto_beta; + + /* Max.Burst - 4 */ + int max_burst; + + /* Whether Cookie Preservative is enabled(1) or not(0) */ + int cookie_preserve_enable; + + /* Valid.Cookie.Life - 60 seconds */ + unsigned int valid_cookie_life; + + /* Delayed SACK timeout 200ms default*/ + unsigned int sack_timeout; + + /* HB.interval - 30 seconds */ + unsigned int hb_interval; + + /* Association.Max.Retrans - 10 attempts + * Path.Max.Retrans - 5 attempts (per destination address) + * Max.Init.Retransmits - 8 attempts + */ + int max_retrans_association; + int max_retrans_path; + int max_retrans_init; + /* Potentially-Failed.Max.Retrans sysctl value + * taken from: + * http://tools.ietf.org/html/draft-nishida-tsvwg-sctp-failover-05 + */ + int pf_retrans; + + /* + * Policy for preforming sctp/socket accounting + * 0 - do socket level accounting, all assocs share sk_sndbuf + * 1 - do sctp accounting, each asoc may use sk_sndbuf bytes + */ + int sndbuf_policy; + + /* + * Policy for preforming sctp/socket accounting + * 0 - do socket level accounting, all assocs share sk_rcvbuf + * 1 - do sctp accounting, each asoc may use sk_rcvbuf bytes + */ + int rcvbuf_policy; + + int default_auto_asconf; + + /* Flag to indicate if addip is enabled. */ + int addip_enable; + int addip_noauth; + + /* Flag to indicate if PR-SCTP is enabled. */ + int prsctp_enable; + + /* Flag to idicate if SCTP-AUTH is enabled */ + int auth_enable; + + /* + * Policy to control SCTP IPv4 address scoping + * 0 - Disable IPv4 address scoping + * 1 - Enable IPv4 address scoping + * 2 - Selectively allow only IPv4 private addresses + * 3 - Selectively allow only IPv4 link local address + */ + int scope_policy; + + /* Threshold for rwnd update SACKS. Receive buffer shifted this many + * bits is an indicator of when to send and window update SACK. + */ + int rwnd_upd_shift; + + /* Threshold for autoclose timeout, in seconds. */ + unsigned long max_autoclose; }; #endif /* __NETNS_SCTP_H__ */ diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 18052b421203..0fef00f5d3ce 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -119,69 +119,6 @@ struct sctp_hashbucket { /* The SCTP globals structure. */ extern struct sctp_globals { - /* RFC2960 Section 14. Suggested SCTP Protocol Parameter Values - * - * The following protocol parameters are RECOMMENDED: - * - * RTO.Initial - 3 seconds - * RTO.Min - 1 second - * RTO.Max - 60 seconds - * RTO.Alpha - 1/8 (3 when converted to right shifts.) - * RTO.Beta - 1/4 (2 when converted to right shifts.) - */ - unsigned int rto_initial; - unsigned int rto_min; - unsigned int rto_max; - - /* Note: rto_alpha and rto_beta are really defined as inverse - * powers of two to facilitate integer operations. - */ - int rto_alpha; - int rto_beta; - - /* Max.Burst - 4 */ - int max_burst; - - /* Whether Cookie Preservative is enabled(1) or not(0) */ - int cookie_preserve_enable; - - /* Valid.Cookie.Life - 60 seconds */ - unsigned int valid_cookie_life; - - /* Delayed SACK timeout 200ms default*/ - unsigned int sack_timeout; - - /* HB.interval - 30 seconds */ - unsigned int hb_interval; - - /* Association.Max.Retrans - 10 attempts - * Path.Max.Retrans - 5 attempts (per destination address) - * Max.Init.Retransmits - 8 attempts - */ - int max_retrans_association; - int max_retrans_path; - int max_retrans_init; - - /* Potentially-Failed.Max.Retrans sysctl value - * taken from: - * http://tools.ietf.org/html/draft-nishida-tsvwg-sctp-failover-05 - */ - int pf_retrans; - - /* - * Policy for preforming sctp/socket accounting - * 0 - do socket level accounting, all assocs share sk_sndbuf - * 1 - do sctp accounting, each asoc may use sk_sndbuf bytes - */ - int sndbuf_policy; - - /* - * Policy for preforming sctp/socket accounting - * 0 - do socket level accounting, all assocs share sk_rcvbuf - * 1 - do sctp accounting, each asoc may use sk_rcvbuf bytes - */ - int rcvbuf_policy; - /* The following variables are implementation specific. */ /* Default initialization values to be applied to new associations. */ @@ -205,56 +142,11 @@ extern struct sctp_globals { int port_hashsize; struct sctp_bind_hashbucket *port_hashtable; - int default_auto_asconf; - - /* Flag to indicate if addip is enabled. */ - int addip_enable; - int addip_noauth_enable; - - /* Flag to indicate if PR-SCTP is enabled. */ - int prsctp_enable; - - /* Flag to idicate if SCTP-AUTH is enabled */ - int auth_enable; - - /* - * Policy to control SCTP IPv4 address scoping - * 0 - Disable IPv4 address scoping - * 1 - Enable IPv4 address scoping - * 2 - Selectively allow only IPv4 private addresses - * 3 - Selectively allow only IPv4 link local address - */ - int ipv4_scope_policy; - /* Flag to indicate whether computing and verifying checksum * is disabled. */ bool checksum_disable; - - /* Threshold for rwnd update SACKS. Receive buffer shifted this many - * bits is an indicator of when to send and window update SACK. - */ - int rwnd_update_shift; - - /* Threshold for autoclose timeout, in seconds. */ - unsigned long max_autoclose; } sctp_globals; -#define sctp_rto_initial (sctp_globals.rto_initial) -#define sctp_rto_min (sctp_globals.rto_min) -#define sctp_rto_max (sctp_globals.rto_max) -#define sctp_rto_alpha (sctp_globals.rto_alpha) -#define sctp_rto_beta (sctp_globals.rto_beta) -#define sctp_max_burst (sctp_globals.max_burst) -#define sctp_valid_cookie_life (sctp_globals.valid_cookie_life) -#define sctp_cookie_preserve_enable (sctp_globals.cookie_preserve_enable) -#define sctp_max_retrans_association (sctp_globals.max_retrans_association) -#define sctp_sndbuf_policy (sctp_globals.sndbuf_policy) -#define sctp_rcvbuf_policy (sctp_globals.rcvbuf_policy) -#define sctp_max_retrans_path (sctp_globals.max_retrans_path) -#define sctp_pf_retrans (sctp_globals.pf_retrans) -#define sctp_max_retrans_init (sctp_globals.max_retrans_init) -#define sctp_sack_timeout (sctp_globals.sack_timeout) -#define sctp_hb_interval (sctp_globals.hb_interval) #define sctp_max_instreams (sctp_globals.max_instreams) #define sctp_max_outstreams (sctp_globals.max_outstreams) #define sctp_address_families (sctp_globals.address_families) @@ -264,15 +156,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_default_auto_asconf (sctp_globals.default_auto_asconf) -#define sctp_scope_policy (sctp_globals.ipv4_scope_policy) -#define sctp_addip_enable (sctp_globals.addip_enable) -#define sctp_addip_noauth (sctp_globals.addip_noauth_enable) -#define sctp_prsctp_enable (sctp_globals.prsctp_enable) -#define sctp_auth_enable (sctp_globals.auth_enable) #define sctp_checksum_disable (sctp_globals.checksum_disable) -#define sctp_rwnd_upd_shift (sctp_globals.rwnd_update_shift) -#define sctp_max_autoclose (sctp_globals.max_autoclose) /* SCTP Socket type: UDP or TCP style. */ typedef enum { diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 93a4513c85e0..b1ef3bc301a5 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -82,6 +82,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a sctp_scope_t scope, gfp_t gfp) { + struct net *net = sock_net(sk); struct sctp_sock *sp; int i; sctp_paramhdr_t *p; @@ -124,7 +125,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a * socket values. */ asoc->max_retrans = sp->assocparams.sasoc_asocmaxrxt; - asoc->pf_retrans = sctp_pf_retrans; + asoc->pf_retrans = net->sctp.pf_retrans; asoc->rto_initial = msecs_to_jiffies(sp->rtoinfo.srto_initial); asoc->rto_max = msecs_to_jiffies(sp->rtoinfo.srto_max); @@ -175,7 +176,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a asoc->timeouts[SCTP_EVENT_TIMEOUT_HEARTBEAT] = 0; asoc->timeouts[SCTP_EVENT_TIMEOUT_SACK] = asoc->sackdelay; asoc->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE] = - min_t(unsigned long, sp->autoclose, sctp_max_autoclose) * HZ; + min_t(unsigned long, sp->autoclose, net->sctp.max_autoclose) * HZ; /* Initializes the timers */ for (i = SCTP_EVENT_TIMEOUT_NONE; i < SCTP_NUM_TIMEOUT_TYPES; ++i) @@ -281,7 +282,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a * and will revert old behavior. */ asoc->peer.asconf_capable = 0; - if (sctp_addip_noauth) + if (net->sctp.addip_noauth) asoc->peer.asconf_capable = 1; asoc->asconf_addr_del_pending = NULL; asoc->src_out_of_asoc_ok = 0; @@ -1418,6 +1419,7 @@ void sctp_assoc_sync_pmtu(struct sock *sk, struct sctp_association *asoc) /* Should we send a SACK to update our peer? */ static inline int sctp_peer_needs_update(struct sctp_association *asoc) { + struct net *net = sock_net(asoc->base.sk); switch (asoc->state) { case SCTP_STATE_ESTABLISHED: case SCTP_STATE_SHUTDOWN_PENDING: @@ -1425,7 +1427,7 @@ static inline int sctp_peer_needs_update(struct sctp_association *asoc) case SCTP_STATE_SHUTDOWN_SENT: if ((asoc->rwnd > asoc->a_rwnd) && ((asoc->rwnd - asoc->a_rwnd) >= max_t(__u32, - (asoc->base.sk->sk_rcvbuf >> sctp_rwnd_upd_shift), + (asoc->base.sk->sk_rcvbuf >> net->sctp.rwnd_upd_shift), asoc->pathmtu))) return 1; break; diff --git a/net/sctp/auth.c b/net/sctp/auth.c index bf812048cf6f..aaa6c121ecce 100644 --- a/net/sctp/auth.c +++ b/net/sctp/auth.c @@ -392,13 +392,14 @@ nomem: */ int sctp_auth_asoc_init_active_key(struct sctp_association *asoc, gfp_t gfp) { + struct net *net = sock_net(asoc->base.sk); struct sctp_auth_bytes *secret; struct sctp_shared_key *ep_key; /* If we don't support AUTH, or peer is not capable * we don't need to do anything. */ - if (!sctp_auth_enable || !asoc->peer.auth_capable) + if (!net->sctp.auth_enable || !asoc->peer.auth_capable) return 0; /* If the key_id is non-zero and we couldn't find an @@ -445,11 +446,12 @@ struct sctp_shared_key *sctp_auth_get_shkey( */ int sctp_auth_init_hmacs(struct sctp_endpoint *ep, gfp_t gfp) { + struct net *net = sock_net(ep->base.sk); struct crypto_hash *tfm = NULL; __u16 id; /* if the transforms are already allocted, we are done */ - if (!sctp_auth_enable) { + if (!net->sctp.auth_enable) { ep->auth_hmacs = NULL; return 0; } @@ -674,7 +676,12 @@ static int __sctp_auth_cid(sctp_cid_t chunk, struct sctp_chunks_param *param) /* Check if peer requested that this chunk is authenticated */ int sctp_auth_send_cid(sctp_cid_t chunk, const struct sctp_association *asoc) { - if (!sctp_auth_enable || !asoc || !asoc->peer.auth_capable) + struct net *net; + if (!asoc) + return 0; + + net = sock_net(asoc->base.sk); + if (!net->sctp.auth_enable || !asoc->peer.auth_capable) return 0; return __sctp_auth_cid(chunk, asoc->peer.peer_chunks); @@ -683,7 +690,12 @@ int sctp_auth_send_cid(sctp_cid_t chunk, const struct sctp_association *asoc) /* Check if we requested that peer authenticate this chunk. */ int sctp_auth_recv_cid(sctp_cid_t chunk, const struct sctp_association *asoc) { - if (!sctp_auth_enable || !asoc) + struct net *net; + if (!asoc) + return 0; + + net = sock_net(asoc->base.sk); + if (!net->sctp.auth_enable); return 0; return __sctp_auth_cid(chunk, diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index 23389ba44e39..d886b3bf84f5 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c @@ -512,7 +512,7 @@ int sctp_in_scope(struct net *net, const union sctp_addr *addr, sctp_scope_t sco * Address scoping can be selectively controlled via sysctl * option */ - switch (sctp_scope_policy) { + switch (net->sctp.scope_policy) { case SCTP_SCOPE_POLICY_DISABLE: return 1; case SCTP_SCOPE_POLICY_ENABLE: diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index 8315792ef2ba..1859e2bc83d1 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -65,6 +65,7 @@ static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep, struct sock *sk, gfp_t gfp) { + struct net *net = sock_net(sk); struct sctp_hmac_algo_param *auth_hmacs = NULL; struct sctp_chunks_param *auth_chunks = NULL; struct sctp_shared_key *null_key; @@ -74,7 +75,7 @@ static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep, if (!ep->digest) return NULL; - if (sctp_auth_enable) { + if (net->sctp.auth_enable) { /* Allocate space for HMACS and CHUNKS authentication * variables. There are arrays that we encode directly * into parameters to make the rest of the operations easier. @@ -106,7 +107,7 @@ static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep, /* If the Add-IP functionality is enabled, we must * authenticate, ASCONF and ASCONF-ACK chunks */ - if (sctp_addip_enable) { + if (net->sctp.addip_enable) { auth_chunks->chunks[0] = SCTP_CID_ASCONF; auth_chunks->chunks[1] = SCTP_CID_ASCONF_ACK; auth_chunks->param_hdr.length = @@ -140,14 +141,14 @@ static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep, INIT_LIST_HEAD(&ep->asocs); /* Use SCTP specific send buffer space queues. */ - ep->sndbuf_policy = sctp_sndbuf_policy; + ep->sndbuf_policy = net->sctp.sndbuf_policy; sk->sk_data_ready = sctp_data_ready; sk->sk_write_space = sctp_write_space; sock_set_flag(sk, SOCK_USE_WRITE_QUEUE); /* Get the receive buffer policy for this endpoint */ - ep->rcvbuf_policy = sctp_rcvbuf_policy; + ep->rcvbuf_policy = net->sctp.rcvbuf_policy; /* Initialize the secret key used with cookie. */ get_random_bytes(&ep->secret_key[0], SCTP_SECRET_SIZE); diff --git a/net/sctp/input.c b/net/sctp/input.c index a2ceb70ee06c..25dfe7380479 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -1094,7 +1094,7 @@ static struct sctp_association *__sctp_rcv_walk_lookup(struct net *net, break; case SCTP_CID_ASCONF: - if (have_auth || sctp_addip_noauth) + if (have_auth || net->sctp.addip_noauth) asoc = __sctp_rcv_asconf_lookup( net, ch, laddr, sctp_hdr(skb)->source, diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 59965bdea07a..2d518425d598 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1169,6 +1169,70 @@ static int sctp_net_init(struct net *net) { int status; + /* + * 14. Suggested SCTP Protocol Parameter Values + */ + /* The following protocol parameters are RECOMMENDED: */ + /* RTO.Initial - 3 seconds */ + net->sctp.rto_initial = SCTP_RTO_INITIAL; + /* RTO.Min - 1 second */ + net->sctp.rto_min = SCTP_RTO_MIN; + /* RTO.Max - 60 seconds */ + net->sctp.rto_max = SCTP_RTO_MAX; + /* RTO.Alpha - 1/8 */ + net->sctp.rto_alpha = SCTP_RTO_ALPHA; + /* RTO.Beta - 1/4 */ + net->sctp.rto_beta = SCTP_RTO_BETA; + + /* Valid.Cookie.Life - 60 seconds */ + net->sctp.valid_cookie_life = SCTP_DEFAULT_COOKIE_LIFE; + + /* Whether Cookie Preservative is enabled(1) or not(0) */ + net->sctp.cookie_preserve_enable = 1; + + /* Max.Burst - 4 */ + net->sctp.max_burst = SCTP_DEFAULT_MAX_BURST; + + /* Association.Max.Retrans - 10 attempts + * Path.Max.Retrans - 5 attempts (per destination address) + * Max.Init.Retransmits - 8 attempts + */ + net->sctp.max_retrans_association = 10; + net->sctp.max_retrans_path = 5; + net->sctp.max_retrans_init = 8; + + /* Sendbuffer growth - do per-socket accounting */ + net->sctp.sndbuf_policy = 0; + + /* Rcvbuffer growth - do per-socket accounting */ + net->sctp.rcvbuf_policy = 0; + + /* HB.interval - 30 seconds */ + net->sctp.hb_interval = SCTP_DEFAULT_TIMEOUT_HEARTBEAT; + + /* delayed SACK timeout */ + net->sctp.sack_timeout = SCTP_DEFAULT_TIMEOUT_SACK; + + /* Disable ADDIP by default. */ + net->sctp.addip_enable = 0; + net->sctp.addip_noauth = 0; + net->sctp.default_auto_asconf = 0; + + /* Enable PR-SCTP by default. */ + net->sctp.prsctp_enable = 1; + + /* Disable AUTH by default. */ + net->sctp.auth_enable = 0; + + /* Set SCOPE policy to enabled */ + net->sctp.scope_policy = SCTP_SCOPE_POLICY_ENABLE; + + /* Set the default rwnd update threshold */ + net->sctp.rwnd_upd_shift = SCTP_DEFAULT_RWND_SHIFT; + + /* Initialize maximum autoclose timeout. */ + net->sctp.max_autoclose = INT_MAX / HZ; + status = sctp_sysctl_net_register(net); if (status) goto err_sysctl_register; @@ -1272,59 +1336,12 @@ SCTP_STATIC __init int sctp_init(void) if (status) goto err_percpu_counter_init; - /* - * 14. Suggested SCTP Protocol Parameter Values - */ - /* The following protocol parameters are RECOMMENDED: */ - /* RTO.Initial - 3 seconds */ - sctp_rto_initial = SCTP_RTO_INITIAL; - /* RTO.Min - 1 second */ - sctp_rto_min = SCTP_RTO_MIN; - /* RTO.Max - 60 seconds */ - sctp_rto_max = SCTP_RTO_MAX; - /* RTO.Alpha - 1/8 */ - sctp_rto_alpha = SCTP_RTO_ALPHA; - /* RTO.Beta - 1/4 */ - sctp_rto_beta = SCTP_RTO_BETA; - - /* Valid.Cookie.Life - 60 seconds */ - sctp_valid_cookie_life = SCTP_DEFAULT_COOKIE_LIFE; - - /* Whether Cookie Preservative is enabled(1) or not(0) */ - sctp_cookie_preserve_enable = 1; - - /* Max.Burst - 4 */ - sctp_max_burst = SCTP_DEFAULT_MAX_BURST; - - /* Association.Max.Retrans - 10 attempts - * Path.Max.Retrans - 5 attempts (per destination address) - * Max.Init.Retransmits - 8 attempts - */ - sctp_max_retrans_association = 10; - sctp_max_retrans_path = 5; - sctp_max_retrans_init = 8; - - /* Sendbuffer growth - do per-socket accounting */ - sctp_sndbuf_policy = 0; - - /* Rcvbuffer growth - do per-socket accounting */ - sctp_rcvbuf_policy = 0; - - /* HB.interval - 30 seconds */ - sctp_hb_interval = SCTP_DEFAULT_TIMEOUT_HEARTBEAT; - - /* delayed SACK timeout */ - sctp_sack_timeout = SCTP_DEFAULT_TIMEOUT_SACK; - /* Implementation specific variables. */ /* Initialize default stream count setup information. */ sctp_max_instreams = SCTP_DEFAULT_INSTREAMS; sctp_max_outstreams = SCTP_DEFAULT_OUTSTREAMS; - /* Initialize maximum autoclose timeout. */ - sctp_max_autoclose = INT_MAX / HZ; - /* Initialize handle used for association ids. */ idr_init(&sctp_assocs_id); @@ -1411,23 +1428,6 @@ SCTP_STATIC __init int sctp_init(void) pr_info("Hash tables configured (established %d bind %d)\n", sctp_assoc_hashsize, sctp_port_hashsize); - /* Disable ADDIP by default. */ - sctp_addip_enable = 0; - sctp_addip_noauth = 0; - sctp_default_auto_asconf = 0; - - /* Enable PR-SCTP by default. */ - sctp_prsctp_enable = 1; - - /* Disable AUTH by default. */ - sctp_auth_enable = 0; - - /* Set SCOPE policy to enabled */ - sctp_scope_policy = SCTP_SCOPE_POLICY_ENABLE; - - /* Set the default rwnd update threshold */ - sctp_rwnd_upd_shift = SCTP_DEFAULT_RWND_SHIFT; - sctp_sysctl_register(); INIT_LIST_HEAD(&sctp_address_families); diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index a4b096f85a68..fbe1636309a7 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -198,6 +198,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, const struct sctp_bind_addr *bp, gfp_t gfp, int vparam_len) { + struct net *net = sock_net(asoc->base.sk); sctp_inithdr_t init; union sctp_params addrs; size_t chunksize; @@ -237,7 +238,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, chunksize += WORD_ROUND(SCTP_SAT_LEN(num_types)); chunksize += sizeof(ecap_param); - if (sctp_prsctp_enable) + if (net->sctp.prsctp_enable) chunksize += sizeof(prsctp_param); /* ADDIP: Section 4.2.7: @@ -245,7 +246,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, * the ASCONF,the ASCONF-ACK, and the AUTH chunks in its INIT and * INIT-ACK parameters. */ - if (sctp_addip_enable) { + if (net->sctp.addip_enable) { extensions[num_ext] = SCTP_CID_ASCONF; extensions[num_ext+1] = SCTP_CID_ASCONF_ACK; num_ext += 2; @@ -257,7 +258,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, chunksize += vparam_len; /* Account for AUTH related parameters */ - if (sctp_auth_enable) { + if (net->sctp.auth_enable) { /* Add random parameter length*/ chunksize += sizeof(asoc->c.auth_random); @@ -331,7 +332,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, sctp_addto_param(retval, num_ext, extensions); } - if (sctp_prsctp_enable) + if (net->sctp.prsctp_enable) sctp_addto_chunk(retval, sizeof(prsctp_param), &prsctp_param); if (sp->adaptation_ind) { @@ -342,7 +343,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, } /* Add SCTP-AUTH chunks to the parameter list */ - if (sctp_auth_enable) { + if (net->sctp.auth_enable) { sctp_addto_chunk(retval, sizeof(asoc->c.auth_random), asoc->c.auth_random); if (auth_hmacs) @@ -1964,10 +1965,10 @@ static int sctp_verify_ext_param(struct net *net, union sctp_params param) * only if ADD-IP is turned on and we are not backward-compatible * mode. */ - if (sctp_addip_noauth) + if (net->sctp.addip_noauth) return 1; - if (sctp_addip_enable && !have_auth && have_asconf) + if (net->sctp.addip_enable && !have_auth && have_asconf) return 0; return 1; @@ -1976,13 +1977,14 @@ static int sctp_verify_ext_param(struct net *net, union sctp_params param) static void sctp_process_ext_param(struct sctp_association *asoc, union sctp_params param) { + struct net *net = sock_net(asoc->base.sk); __u16 num_ext = ntohs(param.p->length) - sizeof(sctp_paramhdr_t); int i; for (i = 0; i < num_ext; i++) { switch (param.ext->chunks[i]) { case SCTP_CID_FWD_TSN: - if (sctp_prsctp_enable && + if (net->sctp.prsctp_enable && !asoc->peer.prsctp_capable) asoc->peer.prsctp_capable = 1; break; @@ -1990,12 +1992,12 @@ static void sctp_process_ext_param(struct sctp_association *asoc, /* if the peer reports AUTH, assume that he * supports AUTH. */ - if (sctp_auth_enable) + if (net->sctp.auth_enable) asoc->peer.auth_capable = 1; break; case SCTP_CID_ASCONF: case SCTP_CID_ASCONF_ACK: - if (sctp_addip_enable) + if (net->sctp.addip_enable) asoc->peer.asconf_capable = 1; break; default: @@ -2116,7 +2118,7 @@ static sctp_ierror_t sctp_verify_param(struct net *net, break; case SCTP_PARAM_SET_PRIMARY: - if (sctp_addip_enable) + if (net->sctp.addip_enable) break; goto fallthrough; @@ -2127,12 +2129,12 @@ static sctp_ierror_t sctp_verify_param(struct net *net, break; case SCTP_PARAM_FWD_TSN_SUPPORT: - if (sctp_prsctp_enable) + if (net->sctp.prsctp_enable) break; goto fallthrough; case SCTP_PARAM_RANDOM: - if (!sctp_auth_enable) + if (!net->sctp.auth_enable) goto fallthrough; /* SCTP-AUTH: Secion 6.1 @@ -2149,7 +2151,7 @@ static sctp_ierror_t sctp_verify_param(struct net *net, break; case SCTP_PARAM_CHUNKS: - if (!sctp_auth_enable) + if (!net->sctp.auth_enable) goto fallthrough; /* SCTP-AUTH: Section 3.2 @@ -2165,7 +2167,7 @@ static sctp_ierror_t sctp_verify_param(struct net *net, break; case SCTP_PARAM_HMAC_ALGO: - if (!sctp_auth_enable) + if (!net->sctp.auth_enable) goto fallthrough; hmacs = (struct sctp_hmac_algo_param *)param.p; @@ -2271,6 +2273,7 @@ int sctp_process_init(struct sctp_association *asoc, struct sctp_chunk *chunk, const union sctp_addr *peer_addr, sctp_init_chunk_t *peer_init, gfp_t gfp) { + struct net *net = sock_net(asoc->base.sk); union sctp_params param; struct sctp_transport *transport; struct list_head *pos, *temp; @@ -2327,7 +2330,7 @@ int sctp_process_init(struct sctp_association *asoc, struct sctp_chunk *chunk, * also give us an option to silently ignore the packet, which * is what we'll do here. */ - if (!sctp_addip_noauth && + if (!net->sctp.addip_noauth && (asoc->peer.asconf_capable && !asoc->peer.auth_capable)) { asoc->peer.addip_disabled_mask |= (SCTP_PARAM_ADD_IP | SCTP_PARAM_DEL_IP | @@ -2502,7 +2505,7 @@ do_addr_param: break; case SCTP_PARAM_COOKIE_PRESERVATIVE: - if (!sctp_cookie_preserve_enable) + if (!net->sctp.cookie_preserve_enable) break; stale = ntohl(param.life->lifespan_increment); @@ -2582,7 +2585,7 @@ do_addr_param: break; case SCTP_PARAM_SET_PRIMARY: - if (!sctp_addip_enable) + if (!net->sctp.addip_enable) goto fall_through; addr_param = param.v + sizeof(sctp_addip_param_t); @@ -2609,7 +2612,7 @@ do_addr_param: break; case SCTP_PARAM_FWD_TSN_SUPPORT: - if (sctp_prsctp_enable) { + if (net->sctp.prsctp_enable) { asoc->peer.prsctp_capable = 1; break; } @@ -2617,7 +2620,7 @@ do_addr_param: goto fall_through; case SCTP_PARAM_RANDOM: - if (!sctp_auth_enable) + if (!net->sctp.auth_enable) goto fall_through; /* Save peer's random parameter */ @@ -2630,7 +2633,7 @@ do_addr_param: break; case SCTP_PARAM_HMAC_ALGO: - if (!sctp_auth_enable) + if (!net->sctp.auth_enable) goto fall_through; /* Save peer's HMAC list */ @@ -2646,7 +2649,7 @@ do_addr_param: break; case SCTP_PARAM_CHUNKS: - if (!sctp_auth_enable) + if (!net->sctp.auth_enable) goto fall_through; asoc->peer.peer_chunks = kmemdup(param.p, diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index e17ada47afc4..094813b6c3c3 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -3586,7 +3586,7 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net, * is received unauthenticated it MUST be silently discarded as * described in [I-D.ietf-tsvwg-sctp-auth]. */ - if (!sctp_addip_noauth && !chunk->auth) + if (!net->sctp.addip_noauth && !chunk->auth) return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands); /* Make sure that the ASCONF ADDIP chunk has a valid length. */ @@ -3713,7 +3713,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(struct net *net, * is received unauthenticated it MUST be silently discarded as * described in [I-D.ietf-tsvwg-sctp-auth]. */ - if (!sctp_addip_noauth && !asconf_ack->auth) + if (!net->sctp.addip_noauth && !asconf_ack->auth) return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands); /* Make sure that the ADDIP chunk has a valid length. */ diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c index 4a029d798287..84d98d8a5a74 100644 --- a/net/sctp/sm_statetable.c +++ b/net/sctp/sm_statetable.c @@ -918,12 +918,12 @@ static const sctp_sm_table_entry_t *sctp_chunk_event_lookup(struct net *net, if (cid <= SCTP_CID_BASE_MAX) return &chunk_event_table[cid][state]; - if (sctp_prsctp_enable) { + if (net->sctp.prsctp_enable) { if (cid == SCTP_CID_FWD_TSN) return &prsctp_chunk_event_table[0][state]; } - if (sctp_addip_enable) { + if (net->sctp.addip_enable) { if (cid == SCTP_CID_ASCONF) return &addip_chunk_event_table[0][state]; @@ -931,7 +931,7 @@ static const sctp_sm_table_entry_t *sctp_chunk_event_lookup(struct net *net, return &addip_chunk_event_table[1][state]; } - if (sctp_auth_enable) { + if (net->sctp.auth_enable) { if (cid == SCTP_CID_AUTH) return &auth_chunk_event_table[0][state]; } diff --git a/net/sctp/socket.c b/net/sctp/socket.c index a6a4226a922f..d37d24ff197f 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -516,6 +516,7 @@ static int sctp_send_asconf_add_ip(struct sock *sk, struct sockaddr *addrs, int addrcnt) { + struct net *net = sock_net(sk); struct sctp_sock *sp; struct sctp_endpoint *ep; struct sctp_association *asoc; @@ -530,7 +531,7 @@ static int sctp_send_asconf_add_ip(struct sock *sk, int i; int retval = 0; - if (!sctp_addip_enable) + if (!net->sctp.addip_enable) return retval; sp = sctp_sk(sk); @@ -718,6 +719,7 @@ static int sctp_send_asconf_del_ip(struct sock *sk, struct sockaddr *addrs, int addrcnt) { + struct net *net = sock_net(sk); struct sctp_sock *sp; struct sctp_endpoint *ep; struct sctp_association *asoc; @@ -733,7 +735,7 @@ static int sctp_send_asconf_del_ip(struct sock *sk, int stored = 0; chunk = NULL; - if (!sctp_addip_enable) + if (!net->sctp.addip_enable) return retval; sp = sctp_sk(sk); @@ -3039,6 +3041,7 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char __user *optval, unsigned int optlen) { + struct net *net = sock_net(sk); struct sctp_sock *sp; struct sctp_association *asoc = NULL; struct sctp_setpeerprim prim; @@ -3048,7 +3051,7 @@ static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char __user *optva sp = sctp_sk(sk); - if (!sctp_addip_enable) + if (!net->sctp.addip_enable) return -EPERM; if (optlen != sizeof(struct sctp_setpeerprim)) @@ -3285,9 +3288,10 @@ static int sctp_setsockopt_auth_chunk(struct sock *sk, char __user *optval, unsigned int optlen) { + struct net *net = sock_net(sk); struct sctp_authchunk val; - if (!sctp_auth_enable) + if (!net->sctp.auth_enable) return -EACCES; if (optlen != sizeof(struct sctp_authchunk)) @@ -3317,11 +3321,12 @@ static int sctp_setsockopt_hmac_ident(struct sock *sk, char __user *optval, unsigned int optlen) { + struct net *net = sock_net(sk); struct sctp_hmacalgo *hmacs; u32 idents; int err; - if (!sctp_auth_enable) + if (!net->sctp.auth_enable) return -EACCES; if (optlen < sizeof(struct sctp_hmacalgo)) @@ -3354,11 +3359,12 @@ static int sctp_setsockopt_auth_key(struct sock *sk, char __user *optval, unsigned int optlen) { + struct net *net = sock_net(sk); struct sctp_authkey *authkey; struct sctp_association *asoc; int ret; - if (!sctp_auth_enable) + if (!net->sctp.auth_enable) return -EACCES; if (optlen <= sizeof(struct sctp_authkey)) @@ -3395,10 +3401,11 @@ static int sctp_setsockopt_active_key(struct sock *sk, char __user *optval, unsigned int optlen) { + struct net *net = sock_net(sk); struct sctp_authkeyid val; struct sctp_association *asoc; - if (!sctp_auth_enable) + if (!net->sctp.auth_enable) return -EACCES; if (optlen != sizeof(struct sctp_authkeyid)) @@ -3423,10 +3430,11 @@ static int sctp_setsockopt_del_key(struct sock *sk, char __user *optval, unsigned int optlen) { + struct net *net = sock_net(sk); struct sctp_authkeyid val; struct sctp_association *asoc; - if (!sctp_auth_enable) + if (!net->sctp.auth_enable) return -EACCES; if (optlen != sizeof(struct sctp_authkeyid)) @@ -3849,6 +3857,7 @@ out: */ SCTP_STATIC int sctp_init_sock(struct sock *sk) { + struct net *net = sock_net(sk); struct sctp_endpoint *ep; struct sctp_sock *sp; @@ -3878,7 +3887,7 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk) sp->default_timetolive = 0; sp->default_rcv_context = 0; - sp->max_burst = sctp_max_burst; + sp->max_burst = net->sctp.max_burst; /* Initialize default setup parameters. These parameters * can be modified with the SCTP_INITMSG socket option or @@ -3886,24 +3895,24 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk) */ sp->initmsg.sinit_num_ostreams = sctp_max_outstreams; sp->initmsg.sinit_max_instreams = sctp_max_instreams; - sp->initmsg.sinit_max_attempts = sctp_max_retrans_init; - sp->initmsg.sinit_max_init_timeo = sctp_rto_max; + sp->initmsg.sinit_max_attempts = net->sctp.max_retrans_init; + sp->initmsg.sinit_max_init_timeo = net->sctp.rto_max; /* Initialize default RTO related parameters. These parameters can * be modified for with the SCTP_RTOINFO socket option. */ - sp->rtoinfo.srto_initial = sctp_rto_initial; - sp->rtoinfo.srto_max = sctp_rto_max; - sp->rtoinfo.srto_min = sctp_rto_min; + sp->rtoinfo.srto_initial = net->sctp.rto_initial; + sp->rtoinfo.srto_max = net->sctp.rto_max; + sp->rtoinfo.srto_min = net->sctp.rto_min; /* Initialize default association related parameters. These parameters * can be modified with the SCTP_ASSOCINFO socket option. */ - sp->assocparams.sasoc_asocmaxrxt = sctp_max_retrans_association; + sp->assocparams.sasoc_asocmaxrxt = net->sctp.max_retrans_association; sp->assocparams.sasoc_number_peer_destinations = 0; sp->assocparams.sasoc_peer_rwnd = 0; sp->assocparams.sasoc_local_rwnd = 0; - sp->assocparams.sasoc_cookie_life = sctp_valid_cookie_life; + sp->assocparams.sasoc_cookie_life = net->sctp.valid_cookie_life; /* Initialize default event subscriptions. By default, all the * options are off. @@ -3913,10 +3922,10 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk) /* Default Peer Address Parameters. These defaults can * be modified via SCTP_PEER_ADDR_PARAMS */ - sp->hbinterval = sctp_hb_interval; - sp->pathmaxrxt = sctp_max_retrans_path; + sp->hbinterval = net->sctp.hb_interval; + sp->pathmaxrxt = net->sctp.max_retrans_path; sp->pathmtu = 0; // allow default discovery - sp->sackdelay = sctp_sack_timeout; + sp->sackdelay = net->sctp.sack_timeout; sp->sackfreq = 2; sp->param_flags = SPP_HB_ENABLE | SPP_PMTUD_ENABLE | @@ -3967,10 +3976,10 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk) local_bh_disable(); percpu_counter_inc(&sctp_sockets_allocated); - sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); - if (sctp_default_auto_asconf) { + sock_prot_inuse_add(net, sk->sk_prot, 1); + if (net->sctp.default_auto_asconf) { list_add_tail(&sp->auto_asconf_list, - &sock_net(sk)->sctp.auto_asconf_splist); + &net->sctp.auto_asconf_splist); sp->do_auto_asconf = 1; } else sp->do_auto_asconf = 0; @@ -5307,12 +5316,13 @@ static int sctp_getsockopt_maxburst(struct sock *sk, int len, static int sctp_getsockopt_hmac_ident(struct sock *sk, int len, char __user *optval, int __user *optlen) { + struct net *net = sock_net(sk); struct sctp_hmacalgo __user *p = (void __user *)optval; struct sctp_hmac_algo_param *hmacs; __u16 data_len = 0; u32 num_idents; - if (!sctp_auth_enable) + if (!net->sctp.auth_enable) return -EACCES; hmacs = sctp_sk(sk)->ep->auth_hmacs_list; @@ -5336,10 +5346,11 @@ static int sctp_getsockopt_hmac_ident(struct sock *sk, int len, static int sctp_getsockopt_active_key(struct sock *sk, int len, char __user *optval, int __user *optlen) { + struct net *net = sock_net(sk); struct sctp_authkeyid val; struct sctp_association *asoc; - if (!sctp_auth_enable) + if (!net->sctp.auth_enable) return -EACCES; if (len < sizeof(struct sctp_authkeyid)) @@ -5368,6 +5379,7 @@ static int sctp_getsockopt_active_key(struct sock *sk, int len, static int sctp_getsockopt_peer_auth_chunks(struct sock *sk, int len, char __user *optval, int __user *optlen) { + struct net *net = sock_net(sk); struct sctp_authchunks __user *p = (void __user *)optval; struct sctp_authchunks val; struct sctp_association *asoc; @@ -5375,7 +5387,7 @@ static int sctp_getsockopt_peer_auth_chunks(struct sock *sk, int len, u32 num_chunks = 0; char __user *to; - if (!sctp_auth_enable) + if (!net->sctp.auth_enable) return -EACCES; if (len < sizeof(struct sctp_authchunks)) @@ -5411,6 +5423,7 @@ num: static int sctp_getsockopt_local_auth_chunks(struct sock *sk, int len, char __user *optval, int __user *optlen) { + struct net *net = sock_net(sk); struct sctp_authchunks __user *p = (void __user *)optval; struct sctp_authchunks val; struct sctp_association *asoc; @@ -5418,7 +5431,7 @@ static int sctp_getsockopt_local_auth_chunks(struct sock *sk, int len, u32 num_chunks = 0; char __user *to; - if (!sctp_auth_enable) + if (!net->sctp.auth_enable) return -EACCES; if (len < sizeof(struct sctp_authchunks)) diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index bee36c408dde..70e3ba5cb50b 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -63,9 +63,35 @@ extern int sysctl_sctp_rmem[3]; extern int sysctl_sctp_wmem[3]; static ctl_table sctp_table[] = { + { + .procname = "sctp_mem", + .data = &sysctl_sctp_mem, + .maxlen = sizeof(sysctl_sctp_mem), + .mode = 0644, + .proc_handler = proc_doulongvec_minmax + }, + { + .procname = "sctp_rmem", + .data = &sysctl_sctp_rmem, + .maxlen = sizeof(sysctl_sctp_rmem), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { + .procname = "sctp_wmem", + .data = &sysctl_sctp_wmem, + .maxlen = sizeof(sysctl_sctp_wmem), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + + { /* sentinel */ } +}; + +static ctl_table sctp_net_table[] = { { .procname = "rto_initial", - .data = &sctp_rto_initial, + .data = &init_net.sctp.rto_initial, .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_minmax, @@ -74,7 +100,7 @@ static ctl_table sctp_table[] = { }, { .procname = "rto_min", - .data = &sctp_rto_min, + .data = &init_net.sctp.rto_min, .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_minmax, @@ -83,7 +109,7 @@ static ctl_table sctp_table[] = { }, { .procname = "rto_max", - .data = &sctp_rto_max, + .data = &init_net.sctp.rto_max, .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_minmax, @@ -91,17 +117,22 @@ static ctl_table sctp_table[] = { .extra2 = &timer_max }, { - .procname = "valid_cookie_life", - .data = &sctp_valid_cookie_life, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &one, - .extra2 = &timer_max + .procname = "rto_alpha_exp_divisor", + .data = &init_net.sctp.rto_alpha, + .maxlen = sizeof(int), + .mode = 0444, + .proc_handler = proc_dointvec, + }, + { + .procname = "rto_beta_exp_divisor", + .data = &init_net.sctp.rto_beta, + .maxlen = sizeof(int), + .mode = 0444, + .proc_handler = proc_dointvec, }, { .procname = "max_burst", - .data = &sctp_max_burst, + .data = &init_net.sctp.max_burst, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec_minmax, @@ -109,31 +140,42 @@ static ctl_table sctp_table[] = { .extra2 = &int_max }, { - .procname = "association_max_retrans", - .data = &sctp_max_retrans_association, + .procname = "cookie_preserve_enable", + .data = &init_net.sctp.cookie_preserve_enable, .maxlen = sizeof(int), .mode = 0644, + .proc_handler = proc_dointvec, + }, + { + .procname = "valid_cookie_life", + .data = &init_net.sctp.valid_cookie_life, + .maxlen = sizeof(unsigned int), + .mode = 0644, .proc_handler = proc_dointvec_minmax, - .extra1 = &one, - .extra2 = &int_max + .extra1 = &one, + .extra2 = &timer_max }, { - .procname = "sndbuf_policy", - .data = &sctp_sndbuf_policy, + .procname = "sack_timeout", + .data = &init_net.sctp.sack_timeout, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_dointvec_minmax, + .extra1 = &sack_timer_min, + .extra2 = &sack_timer_max, }, { - .procname = "rcvbuf_policy", - .data = &sctp_rcvbuf_policy, - .maxlen = sizeof(int), + .procname = "hb_interval", + .data = &init_net.sctp.hb_interval, + .maxlen = sizeof(unsigned int), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_dointvec_minmax, + .extra1 = &one, + .extra2 = &timer_max }, { - .procname = "path_max_retrans", - .data = &sctp_max_retrans_path, + .procname = "association_max_retrans", + .data = &init_net.sctp.max_retrans_association, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec_minmax, @@ -141,17 +183,17 @@ static ctl_table sctp_table[] = { .extra2 = &int_max }, { - .procname = "pf_retrans", - .data = &sctp_pf_retrans, + .procname = "path_max_retrans", + .data = &init_net.sctp.max_retrans_path, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec_minmax, - .extra1 = &zero, + .extra1 = &one, .extra2 = &int_max }, { .procname = "max_init_retransmits", - .data = &sctp_max_retrans_init, + .data = &init_net.sctp.max_retrans_init, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec_minmax, @@ -159,103 +201,66 @@ static ctl_table sctp_table[] = { .extra2 = &int_max }, { - .procname = "hb_interval", - .data = &sctp_hb_interval, - .maxlen = sizeof(unsigned int), + .procname = "pf_retrans", + .data = &init_net.sctp.pf_retrans, + .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec_minmax, - .extra1 = &one, - .extra2 = &timer_max + .extra1 = &zero, + .extra2 = &int_max }, { - .procname = "cookie_preserve_enable", - .data = &sctp_cookie_preserve_enable, + .procname = "sndbuf_policy", + .data = &init_net.sctp.sndbuf_policy, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec, }, { - .procname = "rto_alpha_exp_divisor", - .data = &sctp_rto_alpha, - .maxlen = sizeof(int), - .mode = 0444, - .proc_handler = proc_dointvec, - }, - { - .procname = "rto_beta_exp_divisor", - .data = &sctp_rto_beta, - .maxlen = sizeof(int), - .mode = 0444, - .proc_handler = proc_dointvec, - }, - { - .procname = "addip_enable", - .data = &sctp_addip_enable, + .procname = "rcvbuf_policy", + .data = &init_net.sctp.rcvbuf_policy, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec, }, { .procname = "default_auto_asconf", - .data = &sctp_default_auto_asconf, + .data = &init_net.sctp.default_auto_asconf, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec, }, { - .procname = "prsctp_enable", - .data = &sctp_prsctp_enable, + .procname = "addip_enable", + .data = &init_net.sctp.addip_enable, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec, }, { - .procname = "sack_timeout", - .data = &sctp_sack_timeout, + .procname = "addip_noauth_enable", + .data = &init_net.sctp.addip_noauth, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &sack_timer_min, - .extra2 = &sack_timer_max, - }, - { - .procname = "sctp_mem", - .data = &sysctl_sctp_mem, - .maxlen = sizeof(sysctl_sctp_mem), - .mode = 0644, - .proc_handler = proc_doulongvec_minmax - }, - { - .procname = "sctp_rmem", - .data = &sysctl_sctp_rmem, - .maxlen = sizeof(sysctl_sctp_rmem), - .mode = 0644, .proc_handler = proc_dointvec, }, { - .procname = "sctp_wmem", - .data = &sysctl_sctp_wmem, - .maxlen = sizeof(sysctl_sctp_wmem), - .mode = 0644, - .proc_handler = proc_dointvec, - }, - { - .procname = "auth_enable", - .data = &sctp_auth_enable, + .procname = "prsctp_enable", + .data = &init_net.sctp.prsctp_enable, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec, }, { - .procname = "addip_noauth_enable", - .data = &sctp_addip_noauth, + .procname = "auth_enable", + .data = &init_net.sctp.auth_enable, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec, }, { .procname = "addr_scope_policy", - .data = &sctp_scope_policy, + .data = &init_net.sctp.scope_policy, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec_minmax, @@ -264,7 +269,7 @@ static ctl_table sctp_table[] = { }, { .procname = "rwnd_update_shift", - .data = &sctp_rwnd_upd_shift, + .data = &init_net.sctp.rwnd_upd_shift, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec_minmax, @@ -273,7 +278,7 @@ static ctl_table sctp_table[] = { }, { .procname = "max_autoclose", - .data = &sctp_max_autoclose, + .data = &init_net.sctp.max_autoclose, .maxlen = sizeof(unsigned long), .mode = 0644, .proc_handler = &proc_doulongvec_minmax, @@ -284,18 +289,18 @@ static ctl_table sctp_table[] = { { /* sentinel */ } }; -static ctl_table sctp_net_table[] = { - { /* sentinel */ } -}; - int sctp_sysctl_net_register(struct net *net) { struct ctl_table *table; + int i; table = kmemdup(sctp_net_table, sizeof(sctp_net_table), GFP_KERNEL); if (!table) return -ENOMEM; + for (i = 0; table[i].data; i++) + table[i].data += (char *)(&net->sctp) - (char *)&init_net.sctp; + net->sctp.sysctl_header = register_net_sysctl(net, "net/sctp", table); return 0; } diff --git a/net/sctp/transport.c b/net/sctp/transport.c index aada963c9d6b..953c21e4af97 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -77,7 +77,7 @@ static struct sctp_transport *sctp_transport_init(struct net *net, * given destination transport address, set RTO to the protocol * parameter 'RTO.Initial'. */ - peer->rto = msecs_to_jiffies(sctp_rto_initial); + peer->rto = msecs_to_jiffies(net->sctp.rto_initial); peer->last_time_heard = jiffies; peer->last_time_ecne_reduced = jiffies; @@ -87,8 +87,8 @@ static struct sctp_transport *sctp_transport_init(struct net *net, SPP_SACKDELAY_ENABLE; /* Initialize the default path max_retrans. */ - peer->pathmaxrxt = sctp_max_retrans_path; - peer->pf_retrans = sctp_pf_retrans; + peer->pathmaxrxt = net->sctp.max_retrans_path; + peer->pf_retrans = net->sctp.pf_retrans; INIT_LIST_HEAD(&peer->transmitted); INIT_LIST_HEAD(&peer->send_ready); @@ -318,6 +318,7 @@ void sctp_transport_update_rto(struct sctp_transport *tp, __u32 rtt) SCTP_ASSERT(tp->rto_pending, "rto_pending not set", return); if (tp->rttvar || tp->srtt) { + struct net *net = sock_net(tp->asoc->base.sk); /* 6.3.1 C3) When a new RTT measurement R' is made, set * RTTVAR <- (1 - RTO.Beta) * RTTVAR + RTO.Beta * |SRTT - R'| * SRTT <- (1 - RTO.Alpha) * SRTT + RTO.Alpha * R' @@ -329,10 +330,10 @@ void sctp_transport_update_rto(struct sctp_transport *tp, __u32 rtt) * For example, assuming the default value of RTO.Alpha of * 1/8, rto_alpha would be expressed as 3. */ - tp->rttvar = tp->rttvar - (tp->rttvar >> sctp_rto_beta) - + ((abs(tp->srtt - rtt)) >> sctp_rto_beta); - tp->srtt = tp->srtt - (tp->srtt >> sctp_rto_alpha) - + (rtt >> sctp_rto_alpha); + tp->rttvar = tp->rttvar - (tp->rttvar >> net->sctp.rto_beta) + + ((abs(tp->srtt - rtt)) >> net->sctp.rto_beta); + tp->srtt = tp->srtt - (tp->srtt >> net->sctp.rto_alpha) + + (rtt >> net->sctp.rto_alpha); } else { /* 6.3.1 C2) When the first RTT measurement R is made, set * SRTT <- R, RTTVAR <- R/2. -- cgit v1.2.3 From ef706fb55670dfe959b69dde24c2768e4083ef66 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Tue, 14 Aug 2012 19:32:31 -0400 Subject: irqdesc: delete now orphaned references to timer_rand_state In commit c5857ccf293 ("random: remove rand_initialize_irq()") the timer_rand_state was removed from struct irq_desc. Hence we can also remove the forward declaration of it and the kernel doc information now too. Cc: Jiri Kosina Signed-off-by: Paul Gortmaker Signed-off-by: Jiri Kosina --- include/linux/irqdesc.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h index f1e2527006bd..3c32b12b3f0a 100644 --- a/include/linux/irqdesc.h +++ b/include/linux/irqdesc.h @@ -10,12 +10,10 @@ struct irq_affinity_notify; struct proc_dir_entry; -struct timer_rand_state; struct module; /** * struct irq_desc - interrupt descriptor * @irq_data: per irq and chip data passed down to chip functions - * @timer_rand_state: pointer to timer rand state struct * @kstat_irqs: irq stats per cpu * @handle_irq: highlevel irq-events handler * @preflow_handler: handler called before the flow handler (currently used by sparc) -- cgit v1.2.3 From 1f07b62f3205f6ed41759df2892eaf433bc051a1 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Wed, 15 Aug 2012 18:18:11 +0800 Subject: sctp: fix a compile error in sctp.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I got the following compile error: In file included from include/net/sctp/checksum.h:46:0, from net/ipv4/netfilter/nf_nat_proto_sctp.c:14: include/net/sctp/sctp.h: In function ‘sctp_dbg_objcnt_init’: include/net/sctp/sctp.h:370:88: error: parameter name omitted include/net/sctp/sctp.h: In function ‘sctp_dbg_objcnt_exit’: include/net/sctp/sctp.h:371:88: error: parameter name omitted which is caused by commit 13d782f6b4fbbaf9d0380a9947deb45a9de46ae7 Author: Eric W. Biederman Date: Mon Aug 6 08:45:15 2012 +0000 sctp: Make the proc files per network namespace. This patch could fix it. Cc: David S. Miller Cc: "Eric W. Biederman" Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index aaa82923bbaa..9c6414f553f9 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -367,8 +367,8 @@ void sctp_dbg_objcnt_exit(struct net *); #define SCTP_DBG_OBJCNT_INC(name) #define SCTP_DBG_OBJCNT_DEC(name) -static inline void sctp_dbg_objcnt_init(struct net *) { return; } -static inline void sctp_dbg_objcnt_exit(struct net *) { return; } +static inline void sctp_dbg_objcnt_init(struct net *net) { return; } +static inline void sctp_dbg_objcnt_exit(struct net *net) { return; } #endif /* CONFIG_SCTP_DBG_OBJCOUNT */ -- cgit v1.2.3 From 4e8b14526ca7fb046a81c94002c1c43b6fdf0e9b Mon Sep 17 00:00:00 2001 From: John Stultz Date: Wed, 8 Aug 2012 15:36:20 -0400 Subject: time: Improve sanity checking of timekeeping inputs Unexpected behavior could occur if the time is set to a value large enough to overflow a 64bit ktime_t (which is something larger then the year 2262). Also unexpected behavior could occur if large negative offsets are injected via adjtimex. So this patch improves the sanity check timekeeping inputs by improving the timespec_valid() check, and then makes better use of timespec_valid() to make sure we don't set the time to an invalid negative value or one that overflows ktime_t. Note: This does not protect from setting the time close to overflowing ktime_t and then letting natural accumulation cause the overflow. Reported-by: CAI Qian Reported-by: Sasha Levin Signed-off-by: John Stultz Cc: Peter Zijlstra Cc: Prarit Bhargava Cc: Zhouping Liu Cc: Ingo Molnar Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/1344454580-17031-1-git-send-email-john.stultz@linaro.org Signed-off-by: Thomas Gleixner --- include/linux/ktime.h | 7 ------- include/linux/time.h | 22 ++++++++++++++++++++-- kernel/time/timekeeping.c | 26 ++++++++++++++++++++++++-- 3 files changed, 44 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/linux/ktime.h b/include/linux/ktime.h index 603bec2913b0..06177ba10a16 100644 --- a/include/linux/ktime.h +++ b/include/linux/ktime.h @@ -58,13 +58,6 @@ union ktime { typedef union ktime ktime_t; /* Kill this */ -#define KTIME_MAX ((s64)~((u64)1 << 63)) -#if (BITS_PER_LONG == 64) -# define KTIME_SEC_MAX (KTIME_MAX / NSEC_PER_SEC) -#else -# define KTIME_SEC_MAX LONG_MAX -#endif - /* * ktime_t definitions when using the 64-bit scalar representation: */ diff --git a/include/linux/time.h b/include/linux/time.h index c81c5e40fcb5..b0bbd8f0130d 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -107,11 +107,29 @@ static inline struct timespec timespec_sub(struct timespec lhs, return ts_delta; } +#define KTIME_MAX ((s64)~((u64)1 << 63)) +#if (BITS_PER_LONG == 64) +# define KTIME_SEC_MAX (KTIME_MAX / NSEC_PER_SEC) +#else +# define KTIME_SEC_MAX LONG_MAX +#endif + /* * Returns true if the timespec is norm, false if denorm: */ -#define timespec_valid(ts) \ - (((ts)->tv_sec >= 0) && (((unsigned long) (ts)->tv_nsec) < NSEC_PER_SEC)) +static inline bool timespec_valid(const struct timespec *ts) +{ + /* Dates before 1970 are bogus */ + if (ts->tv_sec < 0) + return false; + /* Can't have more nanoseconds then a second */ + if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC) + return false; + /* Disallow values that could overflow ktime_t */ + if ((unsigned long long)ts->tv_sec >= KTIME_SEC_MAX) + return false; + return true; +} extern void read_persistent_clock(struct timespec *ts); extern void read_boot_clock(struct timespec *ts); diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index e16af197a2bc..898bef066a44 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -427,7 +427,7 @@ int do_settimeofday(const struct timespec *tv) struct timespec ts_delta, xt; unsigned long flags; - if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) + if (!timespec_valid(tv)) return -EINVAL; write_seqlock_irqsave(&tk->lock, flags); @@ -463,6 +463,8 @@ int timekeeping_inject_offset(struct timespec *ts) { struct timekeeper *tk = &timekeeper; unsigned long flags; + struct timespec tmp; + int ret = 0; if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC) return -EINVAL; @@ -471,10 +473,17 @@ int timekeeping_inject_offset(struct timespec *ts) timekeeping_forward_now(tk); + /* Make sure the proposed value is valid */ + tmp = timespec_add(tk_xtime(tk), *ts); + if (!timespec_valid(&tmp)) { + ret = -EINVAL; + goto error; + } tk_xtime_add(tk, ts); tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, *ts)); +error: /* even if we error out, we forwarded the time, so call update */ timekeeping_update(tk, true); write_sequnlock_irqrestore(&tk->lock, flags); @@ -482,7 +491,7 @@ int timekeeping_inject_offset(struct timespec *ts) /* signal hrtimers about time change */ clock_was_set(); - return 0; + return ret; } EXPORT_SYMBOL(timekeeping_inject_offset); @@ -649,7 +658,20 @@ void __init timekeeping_init(void) struct timespec now, boot, tmp; read_persistent_clock(&now); + if (!timespec_valid(&now)) { + pr_warn("WARNING: Persistent clock returned invalid value!\n" + " Check your CMOS/BIOS settings.\n"); + now.tv_sec = 0; + now.tv_nsec = 0; + } + read_boot_clock(&boot); + if (!timespec_valid(&boot)) { + pr_warn("WARNING: Boot clock returned invalid value!\n" + " Check your CMOS/BIOS settings.\n"); + boot.tv_sec = 0; + boot.tv_nsec = 0; + } seqlock_init(&tk->lock); -- cgit v1.2.3 From 58569aee5a1a5dcc25c34a0a2ed9a377874e6b05 Mon Sep 17 00:00:00 2001 From: "Arnaud Patard (Rtp)" Date: Thu, 26 Jul 2012 12:15:46 +0200 Subject: ARM: Orion: Set eth packet size csum offload limit The mv643xx ethernet controller limits the packet size for the TX checksum offloading. This patch sets this limits for Kirkwood and Dove which have smaller limits that the default. As a side note, this patch is an updated version of a patch sent some years ago: http://lists.infradead.org/pipermail/linux-arm-kernel/2010-June/017320.html which seems to have been lost. Signed-off-by: Arnaud Patard Signed-off-by: Jason Cooper Cc: --- arch/arm/mach-dove/common.c | 3 ++- arch/arm/mach-kirkwood/common.c | 4 ++-- arch/arm/mach-mv78xx0/common.c | 6 ++++-- arch/arm/mach-orion5x/common.c | 3 ++- arch/arm/plat-orion/common.c | 8 ++++++-- arch/arm/plat-orion/include/plat/common.h | 6 ++++-- include/linux/mv643xx_eth.h | 2 ++ 7 files changed, 22 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-dove/common.c b/arch/arm/mach-dove/common.c index 4db5de54b6a7..6321567d8eaa 100644 --- a/arch/arm/mach-dove/common.c +++ b/arch/arm/mach-dove/common.c @@ -102,7 +102,8 @@ void __init dove_ehci1_init(void) void __init dove_ge00_init(struct mv643xx_eth_platform_data *eth_data) { orion_ge00_init(eth_data, DOVE_GE00_PHYS_BASE, - IRQ_DOVE_GE00_SUM, IRQ_DOVE_GE00_ERR); + IRQ_DOVE_GE00_SUM, IRQ_DOVE_GE00_ERR, + 1600); } /***************************************************************************** diff --git a/arch/arm/mach-kirkwood/common.c b/arch/arm/mach-kirkwood/common.c index c4b64adcbfce..3226077735b1 100644 --- a/arch/arm/mach-kirkwood/common.c +++ b/arch/arm/mach-kirkwood/common.c @@ -301,7 +301,7 @@ void __init kirkwood_ge00_init(struct mv643xx_eth_platform_data *eth_data) { orion_ge00_init(eth_data, GE00_PHYS_BASE, IRQ_KIRKWOOD_GE00_SUM, - IRQ_KIRKWOOD_GE00_ERR); + IRQ_KIRKWOOD_GE00_ERR, 1600); /* The interface forgets the MAC address assigned by u-boot if the clock is turned off, so claim the clk now. */ clk_prepare_enable(ge0); @@ -315,7 +315,7 @@ void __init kirkwood_ge01_init(struct mv643xx_eth_platform_data *eth_data) { orion_ge01_init(eth_data, GE01_PHYS_BASE, IRQ_KIRKWOOD_GE01_SUM, - IRQ_KIRKWOOD_GE01_ERR); + IRQ_KIRKWOOD_GE01_ERR, 1600); clk_prepare_enable(ge1); } diff --git a/arch/arm/mach-mv78xx0/common.c b/arch/arm/mach-mv78xx0/common.c index b4c53b846c9c..3057f7d4329a 100644 --- a/arch/arm/mach-mv78xx0/common.c +++ b/arch/arm/mach-mv78xx0/common.c @@ -213,7 +213,8 @@ void __init mv78xx0_ge00_init(struct mv643xx_eth_platform_data *eth_data) { orion_ge00_init(eth_data, GE00_PHYS_BASE, IRQ_MV78XX0_GE00_SUM, - IRQ_MV78XX0_GE_ERR); + IRQ_MV78XX0_GE_ERR, + MV643XX_TX_CSUM_DEFAULT_LIMIT); } @@ -224,7 +225,8 @@ void __init mv78xx0_ge01_init(struct mv643xx_eth_platform_data *eth_data) { orion_ge01_init(eth_data, GE01_PHYS_BASE, IRQ_MV78XX0_GE01_SUM, - NO_IRQ); + NO_IRQ, + MV643XX_TX_CSUM_DEFAULT_LIMIT); } diff --git a/arch/arm/mach-orion5x/common.c b/arch/arm/mach-orion5x/common.c index 9148b229d0de..410291c67666 100644 --- a/arch/arm/mach-orion5x/common.c +++ b/arch/arm/mach-orion5x/common.c @@ -109,7 +109,8 @@ void __init orion5x_eth_init(struct mv643xx_eth_platform_data *eth_data) { orion_ge00_init(eth_data, ORION5X_ETH_PHYS_BASE, IRQ_ORION5X_ETH_SUM, - IRQ_ORION5X_ETH_ERR); + IRQ_ORION5X_ETH_ERR, + MV643XX_TX_CSUM_DEFAULT_LIMIT); } diff --git a/arch/arm/plat-orion/common.c b/arch/arm/plat-orion/common.c index d245a87dc014..b8b747a9d360 100644 --- a/arch/arm/plat-orion/common.c +++ b/arch/arm/plat-orion/common.c @@ -291,10 +291,12 @@ static struct platform_device orion_ge00 = { void __init orion_ge00_init(struct mv643xx_eth_platform_data *eth_data, unsigned long mapbase, unsigned long irq, - unsigned long irq_err) + unsigned long irq_err, + unsigned int tx_csum_limit) { fill_resources(&orion_ge00_shared, orion_ge00_shared_resources, mapbase + 0x2000, SZ_16K - 1, irq_err); + orion_ge00_shared_data.tx_csum_limit = tx_csum_limit; ge_complete(&orion_ge00_shared_data, orion_ge00_resources, irq, &orion_ge00_shared, eth_data, &orion_ge00); @@ -343,10 +345,12 @@ static struct platform_device orion_ge01 = { void __init orion_ge01_init(struct mv643xx_eth_platform_data *eth_data, unsigned long mapbase, unsigned long irq, - unsigned long irq_err) + unsigned long irq_err, + unsigned int tx_csum_limit) { fill_resources(&orion_ge01_shared, orion_ge01_shared_resources, mapbase + 0x2000, SZ_16K - 1, irq_err); + orion_ge01_shared_data.tx_csum_limit = tx_csum_limit; ge_complete(&orion_ge01_shared_data, orion_ge01_resources, irq, &orion_ge01_shared, eth_data, &orion_ge01); diff --git a/arch/arm/plat-orion/include/plat/common.h b/arch/arm/plat-orion/include/plat/common.h index e00fdb213609..ae2377ef63e5 100644 --- a/arch/arm/plat-orion/include/plat/common.h +++ b/arch/arm/plat-orion/include/plat/common.h @@ -39,12 +39,14 @@ void __init orion_rtc_init(unsigned long mapbase, void __init orion_ge00_init(struct mv643xx_eth_platform_data *eth_data, unsigned long mapbase, unsigned long irq, - unsigned long irq_err); + unsigned long irq_err, + unsigned int tx_csum_limit); void __init orion_ge01_init(struct mv643xx_eth_platform_data *eth_data, unsigned long mapbase, unsigned long irq, - unsigned long irq_err); + unsigned long irq_err, + unsigned int tx_csum_limit); void __init orion_ge10_init(struct mv643xx_eth_platform_data *eth_data, unsigned long mapbase, diff --git a/include/linux/mv643xx_eth.h b/include/linux/mv643xx_eth.h index 51bf8ada6dc0..49258e0ed1c6 100644 --- a/include/linux/mv643xx_eth.h +++ b/include/linux/mv643xx_eth.h @@ -15,6 +15,8 @@ #define MV643XX_ETH_SIZE_REG_4 0x2224 #define MV643XX_ETH_BASE_ADDR_ENABLE_REG 0x2290 +#define MV643XX_TX_CSUM_DEFAULT_LIMIT 0 + struct mv643xx_eth_shared_platform_data { struct mbus_dram_target_info *dram; struct platform_device *shared_smi; -- cgit v1.2.3 From 65e0736bc2ac314bd374e93c24dd0698ac5ee66d Mon Sep 17 00:00:00 2001 From: Fan Du Date: Wed, 15 Aug 2012 10:13:47 +0800 Subject: xfrm: remove redundant parameter "int dir" in struct xfrm_mgr.acquire Sematically speaking, xfrm_mgr.acquire is called when kernel intends to ask user space IKE daemon to negotiate SAs with peers. IOW the direction will *always* be XFRM_POLICY_OUT, so remove int dir for clarity. Signed-off-by: Fan Du Signed-off-by: David S. Miller --- include/net/xfrm.h | 2 +- net/key/af_key.c | 4 ++-- net/xfrm/xfrm_state.c | 2 +- net/xfrm/xfrm_user.c | 9 ++++----- 4 files changed, 8 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 62b619e82a90..5e1662dbb83c 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -571,7 +571,7 @@ struct xfrm_mgr { struct list_head list; char *id; int (*notify)(struct xfrm_state *x, const struct km_event *c); - int (*acquire)(struct xfrm_state *x, struct xfrm_tmpl *, struct xfrm_policy *xp, int dir); + int (*acquire)(struct xfrm_state *x, struct xfrm_tmpl *, struct xfrm_policy *xp); struct xfrm_policy *(*compile_policy)(struct sock *sk, int opt, u8 *data, int len, int *dir); int (*new_mapping)(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport); int (*notify_policy)(struct xfrm_policy *x, int dir, const struct km_event *c); diff --git a/net/key/af_key.c b/net/key/af_key.c index 34e418508a67..ec7d161c129b 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -3024,7 +3024,7 @@ static u32 get_acqseq(void) return res; } -static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *xp, int dir) +static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *xp) { struct sk_buff *skb; struct sadb_msg *hdr; @@ -3105,7 +3105,7 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct pol->sadb_x_policy_len = sizeof(struct sadb_x_policy)/sizeof(uint64_t); pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY; pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC; - pol->sadb_x_policy_dir = dir+1; + pol->sadb_x_policy_dir = XFRM_POLICY_OUT + 1; pol->sadb_x_policy_id = xp->index; /* Set sadb_comb's. */ diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 87cd0e4d4282..7856c33898fa 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1700,7 +1700,7 @@ int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol) read_lock(&xfrm_km_lock); list_for_each_entry(km, &xfrm_km_list, list) { - acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT); + acqret = km->acquire(x, t, pol); if (!acqret) err = acqret; } diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index e75d8e47f35c..ab58034c42d6 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -2567,8 +2567,7 @@ static inline size_t xfrm_acquire_msgsize(struct xfrm_state *x, } static int build_acquire(struct sk_buff *skb, struct xfrm_state *x, - struct xfrm_tmpl *xt, struct xfrm_policy *xp, - int dir) + struct xfrm_tmpl *xt, struct xfrm_policy *xp) { __u32 seq = xfrm_get_acqseq(); struct xfrm_user_acquire *ua; @@ -2583,7 +2582,7 @@ static int build_acquire(struct sk_buff *skb, struct xfrm_state *x, memcpy(&ua->id, &x->id, sizeof(ua->id)); memcpy(&ua->saddr, &x->props.saddr, sizeof(ua->saddr)); memcpy(&ua->sel, &x->sel, sizeof(ua->sel)); - copy_to_user_policy(xp, &ua->policy, dir); + copy_to_user_policy(xp, &ua->policy, XFRM_POLICY_OUT); ua->aalgos = xt->aalgos; ua->ealgos = xt->ealgos; ua->calgos = xt->calgos; @@ -2605,7 +2604,7 @@ static int build_acquire(struct sk_buff *skb, struct xfrm_state *x, } static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt, - struct xfrm_policy *xp, int dir) + struct xfrm_policy *xp) { struct net *net = xs_net(x); struct sk_buff *skb; @@ -2614,7 +2613,7 @@ static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt, if (skb == NULL) return -ENOMEM; - if (build_acquire(skb, x, xt, xp, dir) < 0) + if (build_acquire(skb, x, xt, xp) < 0) BUG(); return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_ACQUIRE, GFP_ATOMIC); -- cgit v1.2.3 From d2323cf77308d6aa13a3a5287310ef93c4919d1e Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 25 Jul 2012 22:54:29 +0200 Subject: onewire: w1-gpio: add ext_pullup_enable pin in platform data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the process of porting boards to devicetree implemenation, we should keep information about external circuitry where they belong - the individual drivers. This patch adds a way to specify a GPIO to drive the (optional) external pull-up logic, rather than using a function pointer for that. Signed-off-by: Daniel Mack Acked-by: Evgeniy Polyakov Acked-by: Ville Syrjälä Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/w1-gpio.c | 18 +++++++++++++++++- include/linux/w1-gpio.h | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c index f01c336233da..6012c4ea3206 100644 --- a/drivers/w1/masters/w1-gpio.c +++ b/drivers/w1/masters/w1-gpio.c @@ -104,6 +104,13 @@ static int __init w1_gpio_probe(struct platform_device *pdev) if (err) goto free_master; + if (gpio_is_valid(pdata->ext_pullup_enable_pin)) { + err = gpio_request_one(pdata->ext_pullup_enable_pin, + GPIOF_INIT_LOW, "w1 pullup"); + if (err < 0) + goto free_gpio; + } + master->data = pdata; master->read_bit = w1_gpio_read_bit; @@ -117,15 +124,21 @@ static int __init w1_gpio_probe(struct platform_device *pdev) err = w1_add_master_device(master); if (err) - goto free_gpio; + goto free_gpio_ext_pu; if (pdata->enable_external_pullup) pdata->enable_external_pullup(1); + if (gpio_is_valid(pdata->ext_pullup_enable_pin)) + gpio_set_value(pdata->ext_pullup_enable_pin, 1); + platform_set_drvdata(pdev, master); return 0; + free_gpio_ext_pu: + if (gpio_is_valid(pdata->ext_pullup_enable_pin)) + gpio_free(pdata->ext_pullup_enable_pin); free_gpio: gpio_free(pdata->pin); free_master: @@ -142,6 +155,9 @@ static int __exit w1_gpio_remove(struct platform_device *pdev) if (pdata->enable_external_pullup) pdata->enable_external_pullup(0); + if (gpio_is_valid(pdata->ext_pullup_enable_pin)) + gpio_set_value(pdata->ext_pullup_enable_pin, 0); + w1_remove_master_device(master); gpio_free(pdata->pin); kfree(master); diff --git a/include/linux/w1-gpio.h b/include/linux/w1-gpio.h index 3adeff82212f..065e3ae79ab0 100644 --- a/include/linux/w1-gpio.h +++ b/include/linux/w1-gpio.h @@ -19,6 +19,7 @@ struct w1_gpio_platform_data { unsigned int pin; unsigned int is_open_drain:1; void (*enable_external_pullup)(int enable); + unsigned int ext_pullup_enable_pin; }; #endif /* _LINUX_W1_GPIO_H */ -- cgit v1.2.3 From ca08649eb5dd30f11a5a8fe8659b48899b7ea6a1 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Thu, 16 Aug 2012 11:31:27 -0400 Subject: Revert "xen PVonHVM: move shared_info to MMIO before kexec" This reverts commit 00e37bdb0113a98408de42db85be002f21dbffd3. During shutdown of PVHVM guests with more than 2VCPUs on certain machines we can hit the race where the replaced shared_info is not replaced fast enough and the PV time clock retries reading the same area over and over without any any success and is stuck in an infinite loop. Acked-by: Olaf Hering Signed-off-by: Konrad Rzeszutek Wilk --- arch/x86/xen/enlighten.c | 118 +++++---------------------------------------- arch/x86/xen/suspend.c | 2 +- arch/x86/xen/xen-ops.h | 2 +- drivers/xen/platform-pci.c | 15 ------ include/xen/events.h | 2 - 5 files changed, 13 insertions(+), 126 deletions(-) (limited to 'include') diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index a6f8acbdfc9a..f1814fc2cb77 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include @@ -1472,130 +1471,38 @@ asmlinkage void __init xen_start_kernel(void) #endif } -#ifdef CONFIG_XEN_PVHVM -/* - * The pfn containing the shared_info is located somewhere in RAM. This - * will cause trouble if the current kernel is doing a kexec boot into a - * new kernel. The new kernel (and its startup code) can not know where - * the pfn is, so it can not reserve the page. The hypervisor will - * continue to update the pfn, and as a result memory corruption occours - * in the new kernel. - * - * One way to work around this issue is to allocate a page in the - * xen-platform pci device's BAR memory range. But pci init is done very - * late and the shared_info page is already in use very early to read - * the pvclock. So moving the pfn from RAM to MMIO is racy because some - * code paths on other vcpus could access the pfn during the small - * window when the old pfn is moved to the new pfn. There is even a - * small window were the old pfn is not backed by a mfn, and during that - * time all reads return -1. - * - * Because it is not known upfront where the MMIO region is located it - * can not be used right from the start in xen_hvm_init_shared_info. - * - * To minimise trouble the move of the pfn is done shortly before kexec. - * This does not eliminate the race because all vcpus are still online - * when the syscore_ops will be called. But hopefully there is no work - * pending at this point in time. Also the syscore_op is run last which - * reduces the risk further. - */ - -static struct shared_info *xen_hvm_shared_info; - -static void xen_hvm_connect_shared_info(unsigned long pfn) +void __ref xen_hvm_init_shared_info(void) { + int cpu; struct xen_add_to_physmap xatp; + static struct shared_info *shared_info_page = 0; + if (!shared_info_page) + shared_info_page = (struct shared_info *) + extend_brk(PAGE_SIZE, PAGE_SIZE); xatp.domid = DOMID_SELF; xatp.idx = 0; xatp.space = XENMAPSPACE_shared_info; - xatp.gpfn = pfn; + xatp.gpfn = __pa(shared_info_page) >> PAGE_SHIFT; if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp)) BUG(); -} -static void xen_hvm_set_shared_info(struct shared_info *sip) -{ - int cpu; - - HYPERVISOR_shared_info = sip; + HYPERVISOR_shared_info = (struct shared_info *)shared_info_page; /* xen_vcpu is a pointer to the vcpu_info struct in the shared_info * page, we use it in the event channel upcall and in some pvclock * related functions. We don't need the vcpu_info placement * optimizations because we don't use any pv_mmu or pv_irq op on * HVM. - * When xen_hvm_set_shared_info is run at boot time only vcpu 0 is - * online but xen_hvm_set_shared_info is run at resume time too and + * When xen_hvm_init_shared_info is run at boot time only vcpu 0 is + * online but xen_hvm_init_shared_info is run at resume time too and * in that case multiple vcpus might be online. */ for_each_online_cpu(cpu) { per_cpu(xen_vcpu, cpu) = &HYPERVISOR_shared_info->vcpu_info[cpu]; } } -/* Reconnect the shared_info pfn to a mfn */ -void xen_hvm_resume_shared_info(void) -{ - xen_hvm_connect_shared_info(__pa(xen_hvm_shared_info) >> PAGE_SHIFT); -} - -#ifdef CONFIG_KEXEC -static struct shared_info *xen_hvm_shared_info_kexec; -static unsigned long xen_hvm_shared_info_pfn_kexec; - -/* Remember a pfn in MMIO space for kexec reboot */ -void __devinit xen_hvm_prepare_kexec(struct shared_info *sip, unsigned long pfn) -{ - xen_hvm_shared_info_kexec = sip; - xen_hvm_shared_info_pfn_kexec = pfn; -} - -static void xen_hvm_syscore_shutdown(void) -{ - struct xen_memory_reservation reservation = { - .domid = DOMID_SELF, - .nr_extents = 1, - }; - unsigned long prev_pfn; - int rc; - - if (!xen_hvm_shared_info_kexec) - return; - - prev_pfn = __pa(xen_hvm_shared_info) >> PAGE_SHIFT; - set_xen_guest_handle(reservation.extent_start, &prev_pfn); - - /* Move pfn to MMIO, disconnects previous pfn from mfn */ - xen_hvm_connect_shared_info(xen_hvm_shared_info_pfn_kexec); - - /* Update pointers, following hypercall is also a memory barrier */ - xen_hvm_set_shared_info(xen_hvm_shared_info_kexec); - - /* Allocate new mfn for previous pfn */ - do { - rc = HYPERVISOR_memory_op(XENMEM_populate_physmap, &reservation); - if (rc == 0) - msleep(123); - } while (rc == 0); - - /* Make sure the previous pfn is really connected to a (new) mfn */ - BUG_ON(rc != 1); -} - -static struct syscore_ops xen_hvm_syscore_ops = { - .shutdown = xen_hvm_syscore_shutdown, -}; -#endif - -/* Use a pfn in RAM, may move to MMIO before kexec. */ -static void __init xen_hvm_init_shared_info(void) -{ - /* Remember pointer for resume */ - xen_hvm_shared_info = extend_brk(PAGE_SIZE, PAGE_SIZE); - xen_hvm_connect_shared_info(__pa(xen_hvm_shared_info) >> PAGE_SHIFT); - xen_hvm_set_shared_info(xen_hvm_shared_info); -} - +#ifdef CONFIG_XEN_PVHVM static void __init init_hvm_pv_info(void) { int major, minor; @@ -1646,9 +1553,6 @@ static void __init xen_hvm_guest_init(void) init_hvm_pv_info(); xen_hvm_init_shared_info(); -#ifdef CONFIG_KEXEC - register_syscore_ops(&xen_hvm_syscore_ops); -#endif if (xen_feature(XENFEAT_hvm_callback_vector)) xen_have_vector_callback = 1; diff --git a/arch/x86/xen/suspend.c b/arch/x86/xen/suspend.c index ae8a00c39de4..45329c8c226e 100644 --- a/arch/x86/xen/suspend.c +++ b/arch/x86/xen/suspend.c @@ -30,7 +30,7 @@ void xen_arch_hvm_post_suspend(int suspend_cancelled) { #ifdef CONFIG_XEN_PVHVM int cpu; - xen_hvm_resume_shared_info(); + xen_hvm_init_shared_info(); xen_callback_vector(); xen_unplug_emulated_devices(); if (xen_feature(XENFEAT_hvm_safe_pvclock)) { diff --git a/arch/x86/xen/xen-ops.h b/arch/x86/xen/xen-ops.h index 1e4329e04e0f..202d4c150154 100644 --- a/arch/x86/xen/xen-ops.h +++ b/arch/x86/xen/xen-ops.h @@ -41,7 +41,7 @@ void xen_enable_syscall(void); void xen_vcpu_restore(void); void xen_callback_vector(void); -void xen_hvm_resume_shared_info(void); +void xen_hvm_init_shared_info(void); void xen_unplug_emulated_devices(void); void __init xen_build_dynamic_phys_to_machine(void); diff --git a/drivers/xen/platform-pci.c b/drivers/xen/platform-pci.c index d4c50d63acbc..97ca359ae2bd 100644 --- a/drivers/xen/platform-pci.c +++ b/drivers/xen/platform-pci.c @@ -101,19 +101,6 @@ static int platform_pci_resume(struct pci_dev *pdev) return 0; } -static void __devinit prepare_shared_info(void) -{ -#ifdef CONFIG_KEXEC - unsigned long addr; - struct shared_info *hvm_shared_info; - - addr = alloc_xen_mmio(PAGE_SIZE); - hvm_shared_info = ioremap(addr, PAGE_SIZE); - memset(hvm_shared_info, 0, PAGE_SIZE); - xen_hvm_prepare_kexec(hvm_shared_info, addr >> PAGE_SHIFT); -#endif -} - static int __devinit platform_pci_init(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -151,8 +138,6 @@ static int __devinit platform_pci_init(struct pci_dev *pdev, platform_mmio = mmio_addr; platform_mmiolen = mmio_len; - prepare_shared_info(); - if (!xen_have_vector_callback) { ret = xen_allocate_irq(pdev); if (ret) { diff --git a/include/xen/events.h b/include/xen/events.h index 9c641deb65d2..04399b28e821 100644 --- a/include/xen/events.h +++ b/include/xen/events.h @@ -58,8 +58,6 @@ void notify_remote_via_irq(int irq); void xen_irq_resume(void); -void xen_hvm_prepare_kexec(struct shared_info *sip, unsigned long pfn); - /* Clear an irq's pending state, in preparation for polling on it */ void xen_clear_irq_pending(int irq); void xen_set_irq_pending(int irq); -- cgit v1.2.3 From 27712b3928bec9b1a889d7f60d718a35ca6c23b3 Mon Sep 17 00:00:00 2001 From: Pavan Savoy Date: Fri, 3 Aug 2012 14:49:40 -0500 Subject: drivers/misc/ti-st: remove sparse warnings remove sparse warnings by assigning right storage specifiers to functions and also clean-up the declarations in the include/linux/ti_wilink_st.h Signed-off-by: Pavan Savoy Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ti-st/st_core.c | 12 +++++++----- drivers/misc/ti-st/st_kim.c | 12 ++++++------ include/linux/ti_wilink_st.h | 3 ++- 3 files changed, 15 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index acfaeeb9e01a..46937b107261 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -30,11 +30,13 @@ #include +extern void st_kim_recv(void *, const unsigned char *, long); +void st_int_recv(void *, const unsigned char *, long); /* function pointer pointing to either, * st_kim_recv during registration to receive fw download responses * st_int_recv after registration to receive proto stack responses */ -void (*st_recv) (void*, const unsigned char*, long); +static void (*st_recv) (void *, const unsigned char *, long); /********************************************************************/ static void add_channel_to_table(struct st_data_s *st_gdata, @@ -100,7 +102,7 @@ int st_int_write(struct st_data_s *st_gdata, * push the skb received to relevant * protocol stacks */ -void st_send_frame(unsigned char chnl_id, struct st_data_s *st_gdata) +static void st_send_frame(unsigned char chnl_id, struct st_data_s *st_gdata) { pr_debug(" %s(prot:%d) ", __func__, chnl_id); @@ -140,7 +142,7 @@ void st_send_frame(unsigned char chnl_id, struct st_data_s *st_gdata) * This function is being called with spin lock held, protocol drivers are * only expected to complete their waits and do nothing more than that. */ -void st_reg_complete(struct st_data_s *st_gdata, char err) +static void st_reg_complete(struct st_data_s *st_gdata, char err) { unsigned char i = 0; pr_info(" %s ", __func__); @@ -379,7 +381,7 @@ done: * completely, return that skb which has the pending data. * In normal cases, return top of txq. */ -struct sk_buff *st_int_dequeue(struct st_data_s *st_gdata) +static struct sk_buff *st_int_dequeue(struct st_data_s *st_gdata) { struct sk_buff *returning_skb; @@ -401,7 +403,7 @@ struct sk_buff *st_int_dequeue(struct st_data_s *st_gdata) * txq and waitq needs protection since the other contexts * may be sending data, waking up chip. */ -void st_int_enqueue(struct st_data_s *st_gdata, struct sk_buff *skb) +static void st_int_enqueue(struct st_data_s *st_gdata, struct sk_buff *skb) { unsigned long flags = 0; diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c index 5c99995f4eac..847406abd0f8 100644 --- a/drivers/misc/ti-st/st_kim.c +++ b/drivers/misc/ti-st/st_kim.c @@ -63,7 +63,7 @@ static struct platform_device *st_get_plat_device(int id) * in case of error don't complete so that waiting for proper * response times out */ -void validate_firmware_response(struct kim_data_s *kim_gdata) +static void validate_firmware_response(struct kim_data_s *kim_gdata) { struct sk_buff *skb = kim_gdata->rx_skb; if (unlikely(skb->data[5] != 0)) { @@ -119,7 +119,7 @@ static inline int kim_check_data_len(struct kim_data_s *kim_gdata, int len) * have been observed to come in bursts of different * tty_receive and hence the logic */ -void kim_int_recv(struct kim_data_s *kim_gdata, +static void kim_int_recv(struct kim_data_s *kim_gdata, const unsigned char *data, long count) { const unsigned char *ptr; @@ -236,7 +236,7 @@ static long read_local_version(struct kim_data_s *kim_gdata, char *bts_scr_name) return 0; } -void skip_change_remote_baud(unsigned char **ptr, long *len) +static void skip_change_remote_baud(unsigned char **ptr, long *len) { unsigned char *nxt_action, *cur_action; cur_action = *ptr; @@ -688,7 +688,7 @@ static const struct file_operations list_debugfs_fops = { * board-*.c file */ -struct dentry *kim_debugfs_dir; +static struct dentry *kim_debugfs_dir; static int kim_probe(struct platform_device *pdev) { long status; @@ -769,7 +769,7 @@ static int kim_remove(struct platform_device *pdev) return 0; } -int kim_suspend(struct platform_device *pdev, pm_message_t state) +static int kim_suspend(struct platform_device *pdev, pm_message_t state) { struct ti_st_plat_data *pdata = pdev->dev.platform_data; @@ -779,7 +779,7 @@ int kim_suspend(struct platform_device *pdev, pm_message_t state) return -EOPNOTSUPP; } -int kim_resume(struct platform_device *pdev) +static int kim_resume(struct platform_device *pdev) { struct ti_st_plat_data *pdata = pdev->dev.platform_data; diff --git a/include/linux/ti_wilink_st.h b/include/linux/ti_wilink_st.h index 3ca0269dd0b5..932b76392248 100644 --- a/include/linux/ti_wilink_st.h +++ b/include/linux/ti_wilink_st.h @@ -281,9 +281,10 @@ struct kim_data_s { long st_kim_start(void *); long st_kim_stop(void *); -void st_kim_recv(void *, const unsigned char *, long count); void st_kim_complete(void *); void kim_st_list_protocols(struct st_data_s *, void *); +void st_kim_recv(void *, const unsigned char *, long); + /* * BTS headers -- cgit v1.2.3 From 689ae231afbac8979f96100b372a5a73458baaa9 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 27 Jul 2012 22:14:59 +0200 Subject: platform: Add support for automatic device IDs Right now we have support for explicit platform device IDs, as well as ID-less platform devices when a given device type can only have one instance. However there are cases where multiple instances of a device type can exist, and their IDs aren't (and can't be) known in advance and do not matter. In that case we need automatic device IDs to avoid device name collisions. I am using magic ID value -2 (PLATFORM_DEVID_AUTO) for this, similar to -1 for ID-less devices. The automatically allocated device IDs are global (to avoid an additional per-driver cost.) We keep note that the ID was automatically allocated so that it can be freed later. Note that we also restore the ID to PLATFORM_DEVID_AUTO on error and device deletion, to avoid avoid unexpected behavior on retry. I don't really expect retries on platform device addition, but better safe than sorry. Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- drivers/base/platform.c | 38 +++++++++++++++++++++++++++++++++++--- include/linux/platform_device.h | 4 ++++ 2 files changed, 39 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index a1a722502587..3f8077ce585c 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -20,9 +20,13 @@ #include #include #include +#include #include "base.h" +/* For automatically allocated device IDs */ +static DEFINE_IDA(platform_devid_ida); + #define to_platform_driver(drv) (container_of((drv), struct platform_driver, \ driver)) @@ -263,7 +267,7 @@ EXPORT_SYMBOL_GPL(platform_device_add_data); */ int platform_device_add(struct platform_device *pdev) { - int i, ret = 0; + int i, ret; if (!pdev) return -EINVAL; @@ -273,10 +277,27 @@ int platform_device_add(struct platform_device *pdev) pdev->dev.bus = &platform_bus_type; - if (pdev->id != -1) + switch (pdev->id) { + default: dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); - else + break; + case PLATFORM_DEVID_NONE: dev_set_name(&pdev->dev, "%s", pdev->name); + break; + case PLATFORM_DEVID_AUTO: + /* + * Automatically allocated device ID. We mark it as such so + * that we remember it must be freed, and we append a suffix + * to avoid namespace collision with explicit IDs. + */ + ret = ida_simple_get(&platform_devid_ida, 0, 0, GFP_KERNEL); + if (ret < 0) + goto err_out; + pdev->id = ret; + pdev->id_auto = true; + dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id); + break; + } for (i = 0; i < pdev->num_resources; i++) { struct resource *p, *r = &pdev->resource[i]; @@ -309,6 +330,11 @@ int platform_device_add(struct platform_device *pdev) return ret; failed: + if (pdev->id_auto) { + ida_simple_remove(&platform_devid_ida, pdev->id); + pdev->id = PLATFORM_DEVID_AUTO; + } + while (--i >= 0) { struct resource *r = &pdev->resource[i]; unsigned long type = resource_type(r); @@ -317,6 +343,7 @@ int platform_device_add(struct platform_device *pdev) release_resource(r); } + err_out: return ret; } EXPORT_SYMBOL_GPL(platform_device_add); @@ -336,6 +363,11 @@ void platform_device_del(struct platform_device *pdev) if (pdev) { device_del(&pdev->dev); + if (pdev->id_auto) { + ida_simple_remove(&platform_devid_ida, pdev->id); + pdev->id = PLATFORM_DEVID_AUTO; + } + for (i = 0; i < pdev->num_resources; i++) { struct resource *r = &pdev->resource[i]; unsigned long type = resource_type(r); diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index 60e9994ef405..5711e9525a2a 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -14,11 +14,15 @@ #include #include +#define PLATFORM_DEVID_NONE (-1) +#define PLATFORM_DEVID_AUTO (-2) + struct mfd_cell; struct platform_device { const char * name; int id; + bool id_auto; struct device dev; u32 num_resources; struct resource * resource; -- cgit v1.2.3 From 980d7929816236476967218645c30acc022042eb Mon Sep 17 00:00:00 2001 From: anish kumar Date: Fri, 10 Aug 2012 00:29:43 -0700 Subject: Extcon: adc_jack: adc-jack driver to support 3.5 pi or simliar devices External connector devices that decides connection information based on ADC values may use adc-jack device driver. The user simply needs to provide a table of adc range and connection states. Then, extcon framework will automatically notify others. Changes in V1: added Lars-Peter Clausen suggested changes: Using macros to get rid of boiler plate code such as devm_kzalloc and module_platform_driver.Other changes suggested are related to coding guidelines. Changes in V2: Removed some unnecessary checks and changed the way we are un-regitering extcon and freeing the irq while removing. Changes in V3: Renamed the files to comply with extcon naming. Changes in V4: Added the cancel_work_sync during removing of driver. Changes in V5: Added the dependency of IIO in Kconfig. Changes in V6: Some nitpicks related to naming. Reviewed-by: Lars-Peter Clausen Signed-off-by: anish kumar Signed-off-by: MyungJoo Ham Signed-off-by: Greg Kroah-Hartman --- drivers/extcon/Kconfig | 6 + drivers/extcon/Makefile | 1 + drivers/extcon/extcon-adc-jack.c | 195 +++++++++++++++++++++++++++++++++ include/linux/extcon/extcon-adc-jack.h | 73 ++++++++++++ 4 files changed, 275 insertions(+) create mode 100644 drivers/extcon/extcon-adc-jack.c create mode 100644 include/linux/extcon/extcon-adc-jack.h (limited to 'include') diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index e175c8ed4ec4..dd5b01b957aa 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -21,6 +21,12 @@ config EXTCON_GPIO Say Y here to enable GPIO based extcon support. Note that GPIO extcon supports single state per extcon instance. +config EXTCON_ADC_JACK + tristate "ADC Jack extcon support" + depends on IIO + help + Say Y here to enable extcon device driver based on ADC values. + config EXTCON_MAX77693 tristate "MAX77693 EXTCON Support" depends on MFD_MAX77693 diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 88961b332348..53d88c1cce36 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_EXTCON) += extcon_class.o obj-$(CONFIG_EXTCON_GPIO) += extcon_gpio.o +obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c new file mode 100644 index 000000000000..da058f7033ef --- /dev/null +++ b/drivers/extcon/extcon-adc-jack.c @@ -0,0 +1,195 @@ +/* + * drivers/extcon/extcon-adc-jack.c + * + * Analog Jack extcon driver with ADC-based detection capability. + * + * Copyright (C) 2012 Samsung Electronics + * MyungJoo Ham + * + * Modified for calling to IIO to get adc by + * + * 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 + +/** + * struct adc_jack_data - internal data for adc_jack device driver + * @edev - extcon device. + * @cable_names - list of supported cables. + * @num_cables - size of cable_names. + * @adc_conditions - list of adc value conditions. + * @num_conditions - size of adc_conditions. + * @irq - irq number of attach/detach event (0 if not exist). + * @handling_delay - interrupt handler will schedule extcon event + * handling at handling_delay jiffies. + * @handler - extcon event handler called by interrupt handler. + * @chan - iio channel being queried. + */ +struct adc_jack_data { + struct extcon_dev edev; + + const char **cable_names; + int num_cables; + struct adc_jack_cond *adc_conditions; + int num_conditions; + + int irq; + unsigned long handling_delay; /* in jiffies */ + struct delayed_work handler; + + struct iio_channel *chan; +}; + +static void adc_jack_handler(struct work_struct *work) +{ + struct adc_jack_data *data = container_of(to_delayed_work(work), + struct adc_jack_data, + handler); + u32 state = 0; + int ret, adc_val; + int i; + + ret = iio_read_channel_raw(data->chan, &adc_val); + if (ret < 0) { + dev_err(data->edev.dev, "read channel() error: %d\n", ret); + return; + } + + /* Get state from adc value with adc_conditions */ + for (i = 0; i < data->num_conditions; i++) { + struct adc_jack_cond *def = &data->adc_conditions[i]; + if (!def->state) + break; + if (def->min_adc <= adc_val && def->max_adc >= adc_val) { + state = def->state; + break; + } + } + /* if no def has met, it means state = 0 (no cables attached) */ + + extcon_set_state(&data->edev, state); +} + +static irqreturn_t adc_jack_irq_thread(int irq, void *_data) +{ + struct adc_jack_data *data = _data; + + schedule_delayed_work(&data->handler, data->handling_delay); + return IRQ_HANDLED; +} + +static int __devinit adc_jack_probe(struct platform_device *pdev) +{ + struct adc_jack_data *data; + struct adc_jack_pdata *pdata = pdev->dev.platform_data; + int i, err = 0; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->edev.name = pdata->name; + + if (pdata->cable_names) + data->edev.supported_cable = pdata->cable_names; + else + data->edev.supported_cable = extcon_cable_name; + + /* Check the length of array and set num_cables */ + for (i = 0; data->edev.supported_cable[i]; i++) + ; + if (i == 0 || i > SUPPORTED_CABLE_MAX) { + err = -EINVAL; + dev_err(&pdev->dev, "error: pdata->cable_names size = %d\n", + i - 1); + goto out; + } + data->num_cables = i; + + if (!pdata->adc_conditions || + !pdata->adc_conditions[0].state) { + err = -EINVAL; + dev_err(&pdev->dev, "error: adc_conditions not defined.\n"); + goto out; + } + data->adc_conditions = pdata->adc_conditions; + + /* Check the length of array and set num_conditions */ + for (i = 0; data->adc_conditions[i].state; i++) + ; + data->num_conditions = i; + + data->chan = iio_channel_get(dev_name(&pdev->dev), + pdata->consumer_channel); + if (IS_ERR(data->chan)) { + err = PTR_ERR(data->chan); + goto out; + } + + data->handling_delay = msecs_to_jiffies(pdata->handling_delay_ms); + + INIT_DELAYED_WORK_DEFERRABLE(&data->handler, adc_jack_handler); + + platform_set_drvdata(pdev, data); + + err = extcon_dev_register(&data->edev, &pdev->dev); + if (err) + goto out; + + data->irq = platform_get_irq(pdev, 0); + if (!data->irq) { + dev_err(&pdev->dev, "platform_get_irq failed\n"); + err = -ENODEV; + goto err_irq; + } + + err = request_any_context_irq(data->irq, adc_jack_irq_thread, + pdata->irq_flags, pdata->name, data); + + if (err) { + dev_err(&pdev->dev, "error: irq %d\n", data->irq); + err = -EINVAL; + goto err_irq; + } + + goto out; + +err_irq: + extcon_dev_unregister(&data->edev); +out: + return err; +} + +static int __devexit adc_jack_remove(struct platform_device *pdev) +{ + struct adc_jack_data *data = platform_get_drvdata(pdev); + + free_irq(data->irq, data); + cancel_work_sync(&data->handler.work); + extcon_dev_unregister(&data->edev); + + return 0; +} + +static struct platform_driver adc_jack_driver = { + .probe = adc_jack_probe, + .remove = __devexit_p(adc_jack_remove), + .driver = { + .name = "adc-jack", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(adc_jack_driver); diff --git a/include/linux/extcon/extcon-adc-jack.h b/include/linux/extcon/extcon-adc-jack.h new file mode 100644 index 000000000000..a1603cfc504f --- /dev/null +++ b/include/linux/extcon/extcon-adc-jack.h @@ -0,0 +1,73 @@ +/* + * include/linux/extcon/extcon-adc-jack.h + * + * Analog Jack extcon driver with ADC-based detection capability. + * + * Copyright (C) 2012 Samsung Electronics + * MyungJoo Ham + * + * 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 _EXTCON_ADC_JACK_H_ +#define _EXTCON_ADC_JACK_H_ __FILE__ + +#include +#include + +/** + * struct adc_jack_cond - condition to use an extcon state + * @state - the corresponding extcon state (if 0, this struct denotes + * the last adc_jack_cond element among the array) + * @min_adc - min adc value for this condition + * @max_adc - max adc value for this condition + * + * For example, if { .state = 0x3, .min_adc = 100, .max_adc = 200}, it means + * that if ADC value is between (inclusive) 100 and 200, than the cable 0 and + * 1 are attached (1<<0 | 1<<1 == 0x3) + * + * Note that you don't need to describe condition for "no cable attached" + * because when no adc_jack_cond is met, state = 0 is automatically chosen. + */ +struct adc_jack_cond { + u32 state; /* extcon state value. 0 if invalid */ + u32 min_adc; + u32 max_adc; +}; + +/** + * struct adc_jack_pdata - platform data for adc jack device. + * @name - name of the extcon device. If null, "adc-jack" is used. + * @consumer_channel - Unique name to identify the channel on the consumer + * side. This typically describes the channels used within + * the consumer. E.g. 'battery_voltage' + * @cable_names - array of cable names ending with null. If the array itself + * if null, extcon standard cable names are chosen. + * @adc_contitions - array of struct adc_jack_cond conditions ending + * with .state = 0 entry. This describes how to decode + * adc values into extcon state. + * @irq_flags - irq flags used for the @irq + * @handling_delay_ms - in some devices, we need to read ADC value some + * milli-seconds after the interrupt occurs. You may + * describe such delays with @handling_delay_ms, which + * is rounded-off by jiffies. + */ +struct adc_jack_pdata { + const char *name; + const char *consumer_channel; + /* + * NULL if standard extcon names are used. + * The last entry should be NULL + */ + const char **cable_names; + /* The last entry's state should be 0 */ + struct adc_jack_cond *adc_conditions; + + unsigned long irq_flags; + unsigned long handling_delay_ms; /* in ms */ +}; + +#endif /* _EXTCON_ADC_JACK_H */ -- cgit v1.2.3 From 3afebf577d4cb21215abb388e80c46c9c387ed0f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 16 Aug 2012 10:40:32 -0700 Subject: Revert "Extcon: adc_jack: adc-jack driver to support 3.5 pi or simliar devices" This reverts commit 980d7929816236476967218645c30acc022042eb as it breaks the build with the error: ERROR: "extcon_cable_name" [drivers/extcon/extcon-adc-jack.ko] undefined! Cc: Lars-Peter Clausen Cc: anish kumar Cc: MyungJoo Ham Signed-off-by: Greg Kroah-Hartman --- drivers/extcon/Kconfig | 6 - drivers/extcon/Makefile | 1 - drivers/extcon/extcon-adc-jack.c | 195 --------------------------------- include/linux/extcon/extcon-adc-jack.h | 73 ------------ 4 files changed, 275 deletions(-) delete mode 100644 drivers/extcon/extcon-adc-jack.c delete mode 100644 include/linux/extcon/extcon-adc-jack.h (limited to 'include') diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index dd5b01b957aa..e175c8ed4ec4 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -21,12 +21,6 @@ config EXTCON_GPIO Say Y here to enable GPIO based extcon support. Note that GPIO extcon supports single state per extcon instance. -config EXTCON_ADC_JACK - tristate "ADC Jack extcon support" - depends on IIO - help - Say Y here to enable extcon device driver based on ADC values. - config EXTCON_MAX77693 tristate "MAX77693 EXTCON Support" depends on MFD_MAX77693 diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 53d88c1cce36..88961b332348 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -4,7 +4,6 @@ obj-$(CONFIG_EXTCON) += extcon_class.o obj-$(CONFIG_EXTCON_GPIO) += extcon_gpio.o -obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c deleted file mode 100644 index da058f7033ef..000000000000 --- a/drivers/extcon/extcon-adc-jack.c +++ /dev/null @@ -1,195 +0,0 @@ -/* - * drivers/extcon/extcon-adc-jack.c - * - * Analog Jack extcon driver with ADC-based detection capability. - * - * Copyright (C) 2012 Samsung Electronics - * MyungJoo Ham - * - * Modified for calling to IIO to get adc by - * - * 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 - -/** - * struct adc_jack_data - internal data for adc_jack device driver - * @edev - extcon device. - * @cable_names - list of supported cables. - * @num_cables - size of cable_names. - * @adc_conditions - list of adc value conditions. - * @num_conditions - size of adc_conditions. - * @irq - irq number of attach/detach event (0 if not exist). - * @handling_delay - interrupt handler will schedule extcon event - * handling at handling_delay jiffies. - * @handler - extcon event handler called by interrupt handler. - * @chan - iio channel being queried. - */ -struct adc_jack_data { - struct extcon_dev edev; - - const char **cable_names; - int num_cables; - struct adc_jack_cond *adc_conditions; - int num_conditions; - - int irq; - unsigned long handling_delay; /* in jiffies */ - struct delayed_work handler; - - struct iio_channel *chan; -}; - -static void adc_jack_handler(struct work_struct *work) -{ - struct adc_jack_data *data = container_of(to_delayed_work(work), - struct adc_jack_data, - handler); - u32 state = 0; - int ret, adc_val; - int i; - - ret = iio_read_channel_raw(data->chan, &adc_val); - if (ret < 0) { - dev_err(data->edev.dev, "read channel() error: %d\n", ret); - return; - } - - /* Get state from adc value with adc_conditions */ - for (i = 0; i < data->num_conditions; i++) { - struct adc_jack_cond *def = &data->adc_conditions[i]; - if (!def->state) - break; - if (def->min_adc <= adc_val && def->max_adc >= adc_val) { - state = def->state; - break; - } - } - /* if no def has met, it means state = 0 (no cables attached) */ - - extcon_set_state(&data->edev, state); -} - -static irqreturn_t adc_jack_irq_thread(int irq, void *_data) -{ - struct adc_jack_data *data = _data; - - schedule_delayed_work(&data->handler, data->handling_delay); - return IRQ_HANDLED; -} - -static int __devinit adc_jack_probe(struct platform_device *pdev) -{ - struct adc_jack_data *data; - struct adc_jack_pdata *pdata = pdev->dev.platform_data; - int i, err = 0; - - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->edev.name = pdata->name; - - if (pdata->cable_names) - data->edev.supported_cable = pdata->cable_names; - else - data->edev.supported_cable = extcon_cable_name; - - /* Check the length of array and set num_cables */ - for (i = 0; data->edev.supported_cable[i]; i++) - ; - if (i == 0 || i > SUPPORTED_CABLE_MAX) { - err = -EINVAL; - dev_err(&pdev->dev, "error: pdata->cable_names size = %d\n", - i - 1); - goto out; - } - data->num_cables = i; - - if (!pdata->adc_conditions || - !pdata->adc_conditions[0].state) { - err = -EINVAL; - dev_err(&pdev->dev, "error: adc_conditions not defined.\n"); - goto out; - } - data->adc_conditions = pdata->adc_conditions; - - /* Check the length of array and set num_conditions */ - for (i = 0; data->adc_conditions[i].state; i++) - ; - data->num_conditions = i; - - data->chan = iio_channel_get(dev_name(&pdev->dev), - pdata->consumer_channel); - if (IS_ERR(data->chan)) { - err = PTR_ERR(data->chan); - goto out; - } - - data->handling_delay = msecs_to_jiffies(pdata->handling_delay_ms); - - INIT_DELAYED_WORK_DEFERRABLE(&data->handler, adc_jack_handler); - - platform_set_drvdata(pdev, data); - - err = extcon_dev_register(&data->edev, &pdev->dev); - if (err) - goto out; - - data->irq = platform_get_irq(pdev, 0); - if (!data->irq) { - dev_err(&pdev->dev, "platform_get_irq failed\n"); - err = -ENODEV; - goto err_irq; - } - - err = request_any_context_irq(data->irq, adc_jack_irq_thread, - pdata->irq_flags, pdata->name, data); - - if (err) { - dev_err(&pdev->dev, "error: irq %d\n", data->irq); - err = -EINVAL; - goto err_irq; - } - - goto out; - -err_irq: - extcon_dev_unregister(&data->edev); -out: - return err; -} - -static int __devexit adc_jack_remove(struct platform_device *pdev) -{ - struct adc_jack_data *data = platform_get_drvdata(pdev); - - free_irq(data->irq, data); - cancel_work_sync(&data->handler.work); - extcon_dev_unregister(&data->edev); - - return 0; -} - -static struct platform_driver adc_jack_driver = { - .probe = adc_jack_probe, - .remove = __devexit_p(adc_jack_remove), - .driver = { - .name = "adc-jack", - .owner = THIS_MODULE, - }, -}; - -module_platform_driver(adc_jack_driver); diff --git a/include/linux/extcon/extcon-adc-jack.h b/include/linux/extcon/extcon-adc-jack.h deleted file mode 100644 index a1603cfc504f..000000000000 --- a/include/linux/extcon/extcon-adc-jack.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * include/linux/extcon/extcon-adc-jack.h - * - * Analog Jack extcon driver with ADC-based detection capability. - * - * Copyright (C) 2012 Samsung Electronics - * MyungJoo Ham - * - * 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 _EXTCON_ADC_JACK_H_ -#define _EXTCON_ADC_JACK_H_ __FILE__ - -#include -#include - -/** - * struct adc_jack_cond - condition to use an extcon state - * @state - the corresponding extcon state (if 0, this struct denotes - * the last adc_jack_cond element among the array) - * @min_adc - min adc value for this condition - * @max_adc - max adc value for this condition - * - * For example, if { .state = 0x3, .min_adc = 100, .max_adc = 200}, it means - * that if ADC value is between (inclusive) 100 and 200, than the cable 0 and - * 1 are attached (1<<0 | 1<<1 == 0x3) - * - * Note that you don't need to describe condition for "no cable attached" - * because when no adc_jack_cond is met, state = 0 is automatically chosen. - */ -struct adc_jack_cond { - u32 state; /* extcon state value. 0 if invalid */ - u32 min_adc; - u32 max_adc; -}; - -/** - * struct adc_jack_pdata - platform data for adc jack device. - * @name - name of the extcon device. If null, "adc-jack" is used. - * @consumer_channel - Unique name to identify the channel on the consumer - * side. This typically describes the channels used within - * the consumer. E.g. 'battery_voltage' - * @cable_names - array of cable names ending with null. If the array itself - * if null, extcon standard cable names are chosen. - * @adc_contitions - array of struct adc_jack_cond conditions ending - * with .state = 0 entry. This describes how to decode - * adc values into extcon state. - * @irq_flags - irq flags used for the @irq - * @handling_delay_ms - in some devices, we need to read ADC value some - * milli-seconds after the interrupt occurs. You may - * describe such delays with @handling_delay_ms, which - * is rounded-off by jiffies. - */ -struct adc_jack_pdata { - const char *name; - const char *consumer_channel; - /* - * NULL if standard extcon names are used. - * The last entry should be NULL - */ - const char **cable_names; - /* The last entry's state should be 0 */ - struct adc_jack_cond *adc_conditions; - - unsigned long irq_flags; - unsigned long handling_delay_ms; /* in ms */ -}; - -#endif /* _EXTCON_ADC_JACK_H */ -- cgit v1.2.3 From f65444187a66bf54af32a10902877dd0326456d1 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Mon, 6 Aug 2012 19:42:32 +0400 Subject: serial: New serial driver MAX310X This driver is a replacement for a MAX3107 driver with a lot of improvements and new features. The main differences from the old version: - Using the regmap. - Using devm_XXX-related functions. - The use of threaded IRQ with IRQF_ONESHOT flag allows the driver to the hardware that supports only level IRQ. - Improved error handling of serial port, improved FIFO handling, improved hardware & software flow control. - Advanced flags allows turn on RS-485 mode (Auto direction control). - Ability to load multiple instances of drivers. - Added support for MAX3108. - GPIO support. - Driver is quite ready for adding I2C support and support other ICs with compatible registers set (MAX3109, MAX14830). Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 13 +- drivers/tty/serial/Makefile | 2 +- drivers/tty/serial/max3107.c | 1215 ------------------------------- drivers/tty/serial/max3107.h | 441 ------------ drivers/tty/serial/max310x.c | 1259 +++++++++++++++++++++++++++++++++ include/linux/platform_data/max310x.h | 67 ++ include/linux/serial_core.h | 4 +- 7 files changed, 1339 insertions(+), 1662 deletions(-) delete mode 100644 drivers/tty/serial/max3107.c delete mode 100644 drivers/tty/serial/max3107.h create mode 100644 drivers/tty/serial/max310x.c create mode 100644 include/linux/platform_data/max310x.h (limited to 'include') diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 00207865ec55..7b3d9de938e0 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -257,12 +257,19 @@ config SERIAL_MAX3100 help MAX3100 chip support -config SERIAL_MAX3107 - tristate "MAX3107 support" +config SERIAL_MAX310X + bool "MAX310X support" depends on SPI select SERIAL_CORE + select REGMAP_SPI if SPI + default n help - MAX3107 chip support + This selects support for an advanced UART from Maxim (Dallas). + Supported ICs are MAX3107, MAX3108. + Each IC contains 128 words each of receive and transmit FIFO + that can be controlled through I2C or high-speed SPI. + + Say Y here if you want to support this ICs. config SERIAL_DZ bool "DECstation DZ serial driver" diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 8a5df3804e5f..2af9e5279dab 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -28,7 +28,7 @@ obj-$(CONFIG_SERIAL_BFIN) += bfin_uart.o obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o obj-$(CONFIG_SERIAL_MAX3100) += max3100.o -obj-$(CONFIG_SERIAL_MAX3107) += max3107.o +obj-$(CONFIG_SERIAL_MAX310X) += max310x.o obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o obj-$(CONFIG_SERIAL_MUX) += mux.o obj-$(CONFIG_SERIAL_68328) += 68328serial.o diff --git a/drivers/tty/serial/max3107.c b/drivers/tty/serial/max3107.c deleted file mode 100644 index 17c7ba805d98..000000000000 --- a/drivers/tty/serial/max3107.c +++ /dev/null @@ -1,1215 +0,0 @@ -/* - * max3107.c - spi uart protocol driver for Maxim 3107 - * Based on max3100.c - * by Christian Pellegrin - * and max3110.c - * by Feng Tang - * - * Copyright (C) Aavamobile 2009 - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * 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 "max3107.h" - -static const struct baud_table brg26_ext[] = { - { 300, MAX3107_BRG26_B300 }, - { 600, MAX3107_BRG26_B600 }, - { 1200, MAX3107_BRG26_B1200 }, - { 2400, MAX3107_BRG26_B2400 }, - { 4800, MAX3107_BRG26_B4800 }, - { 9600, MAX3107_BRG26_B9600 }, - { 19200, MAX3107_BRG26_B19200 }, - { 57600, MAX3107_BRG26_B57600 }, - { 115200, MAX3107_BRG26_B115200 }, - { 230400, MAX3107_BRG26_B230400 }, - { 460800, MAX3107_BRG26_B460800 }, - { 921600, MAX3107_BRG26_B921600 }, - { 0, 0 } -}; - -static const struct baud_table brg13_int[] = { - { 300, MAX3107_BRG13_IB300 }, - { 600, MAX3107_BRG13_IB600 }, - { 1200, MAX3107_BRG13_IB1200 }, - { 2400, MAX3107_BRG13_IB2400 }, - { 4800, MAX3107_BRG13_IB4800 }, - { 9600, MAX3107_BRG13_IB9600 }, - { 19200, MAX3107_BRG13_IB19200 }, - { 57600, MAX3107_BRG13_IB57600 }, - { 115200, MAX3107_BRG13_IB115200 }, - { 230400, MAX3107_BRG13_IB230400 }, - { 460800, MAX3107_BRG13_IB460800 }, - { 921600, MAX3107_BRG13_IB921600 }, - { 0, 0 } -}; - -static u32 get_new_brg(int baud, struct max3107_port *s) -{ - int i; - const struct baud_table *baud_tbl = s->baud_tbl; - - for (i = 0; i < 13; i++) { - if (baud == baud_tbl[i].baud) - return baud_tbl[i].new_brg; - } - - return 0; -} - -/* Perform SPI transfer for write/read of device register(s) */ -int max3107_rw(struct max3107_port *s, u8 *tx, u8 *rx, int len) -{ - struct spi_message spi_msg; - struct spi_transfer spi_xfer; - - /* Initialize SPI ,message */ - spi_message_init(&spi_msg); - - /* Initialize SPI transfer */ - memset(&spi_xfer, 0, sizeof spi_xfer); - spi_xfer.len = len; - spi_xfer.tx_buf = tx; - spi_xfer.rx_buf = rx; - spi_xfer.speed_hz = MAX3107_SPI_SPEED; - - /* Add SPI transfer to SPI message */ - spi_message_add_tail(&spi_xfer, &spi_msg); - -#ifdef DBG_TRACE_SPI_DATA - { - int i; - pr_info("tx len %d:\n", spi_xfer.len); - for (i = 0 ; i < spi_xfer.len && i < 32 ; i++) - pr_info(" %x", ((u8 *)spi_xfer.tx_buf)[i]); - pr_info("\n"); - } -#endif - - /* Perform synchronous SPI transfer */ - if (spi_sync(s->spi, &spi_msg)) { - dev_err(&s->spi->dev, "spi_sync failure\n"); - return -EIO; - } - -#ifdef DBG_TRACE_SPI_DATA - if (spi_xfer.rx_buf) { - int i; - pr_info("rx len %d:\n", spi_xfer.len); - for (i = 0 ; i < spi_xfer.len && i < 32 ; i++) - pr_info(" %x", ((u8 *)spi_xfer.rx_buf)[i]); - pr_info("\n"); - } -#endif - return 0; -} -EXPORT_SYMBOL_GPL(max3107_rw); - -/* Puts received data to circular buffer */ -static void put_data_to_circ_buf(struct max3107_port *s, unsigned char *data, - int len) -{ - struct uart_port *port = &s->port; - struct tty_struct *tty; - - if (!port->state) - return; - - tty = port->state->port.tty; - if (!tty) - return; - - /* Insert received data */ - tty_insert_flip_string(tty, data, len); - /* Update RX counter */ - port->icount.rx += len; -} - -/* Handle data receiving */ -static void max3107_handlerx(struct max3107_port *s, u16 rxlvl) -{ - int i; - int j; - int len; /* SPI transfer buffer length */ - u16 *buf; - u8 *valid_str; - - if (!s->rx_enabled) - /* RX is disabled */ - return; - - if (rxlvl == 0) { - /* RX fifo is empty */ - return; - } else if (rxlvl >= MAX3107_RX_FIFO_SIZE) { - dev_warn(&s->spi->dev, "Possible RX FIFO overrun %d\n", rxlvl); - /* Ensure sanity of RX level */ - rxlvl = MAX3107_RX_FIFO_SIZE; - } - if ((s->rxbuf == 0) || (s->rxstr == 0)) { - dev_warn(&s->spi->dev, "Rx buffer/str isn't ready\n"); - return; - } - buf = s->rxbuf; - valid_str = s->rxstr; - while (rxlvl) { - pr_debug("rxlvl %d\n", rxlvl); - /* Clear buffer */ - memset(buf, 0, sizeof(u16) * (MAX3107_RX_FIFO_SIZE + 2)); - len = 0; - if (s->irqen_reg & MAX3107_IRQ_RXFIFO_BIT) { - /* First disable RX FIFO interrupt */ - pr_debug("Disabling RX INT\n"); - buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); - s->irqen_reg &= ~MAX3107_IRQ_RXFIFO_BIT; - buf[0] |= s->irqen_reg; - len++; - } - /* Just increase the length by amount of words in FIFO since - * buffer was zeroed and SPI transfer of 0x0000 means reading - * from RX FIFO - */ - len += rxlvl; - /* Append RX level query */ - buf[len] = MAX3107_RXFIFOLVL_REG; - len++; - - /* Perform the SPI transfer */ - if (max3107_rw(s, (u8 *)buf, (u8 *)buf, len * 2)) { - dev_err(&s->spi->dev, "SPI transfer for RX h failed\n"); - return; - } - - /* Skip RX FIFO interrupt disabling word if it was added */ - j = ((len - 1) - rxlvl); - /* Read received words */ - for (i = 0; i < rxlvl; i++, j++) - valid_str[i] = (u8)buf[j]; - put_data_to_circ_buf(s, valid_str, rxlvl); - /* Get new RX level */ - rxlvl = (buf[len - 1] & MAX3107_SPI_RX_DATA_MASK); - } - - if (s->rx_enabled) { - /* RX still enabled, re-enable RX FIFO interrupt */ - pr_debug("Enabling RX INT\n"); - buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); - s->irqen_reg |= MAX3107_IRQ_RXFIFO_BIT; - buf[0] |= s->irqen_reg; - if (max3107_rw(s, (u8 *)buf, NULL, 2)) - dev_err(&s->spi->dev, "RX FIFO INT enabling failed\n"); - } - - /* Push the received data to receivers */ - if (s->port.state->port.tty) - tty_flip_buffer_push(s->port.state->port.tty); -} - - -/* Handle data sending */ -static void max3107_handletx(struct max3107_port *s) -{ - struct circ_buf *xmit = &s->port.state->xmit; - int i; - unsigned long flags; - int len; /* SPI transfer buffer length */ - u16 *buf; - - if (!s->tx_fifo_empty) - /* Don't send more data before previous data is sent */ - return; - - if (uart_circ_empty(xmit) || uart_tx_stopped(&s->port)) - /* No data to send or TX is stopped */ - return; - - if (!s->txbuf) { - dev_warn(&s->spi->dev, "Txbuf isn't ready\n"); - return; - } - buf = s->txbuf; - /* Get length of data pending in circular buffer */ - len = uart_circ_chars_pending(xmit); - if (len) { - /* Limit to size of TX FIFO */ - if (len > MAX3107_TX_FIFO_SIZE) - len = MAX3107_TX_FIFO_SIZE; - - pr_debug("txlen %d\n", len); - - /* Update TX counter */ - s->port.icount.tx += len; - - /* TX FIFO will no longer be empty */ - s->tx_fifo_empty = 0; - - i = 0; - if (s->irqen_reg & MAX3107_IRQ_TXEMPTY_BIT) { - /* First disable TX empty interrupt */ - pr_debug("Disabling TE INT\n"); - buf[i] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); - s->irqen_reg &= ~MAX3107_IRQ_TXEMPTY_BIT; - buf[i] |= s->irqen_reg; - i++; - len++; - } - /* Add data to send */ - spin_lock_irqsave(&s->port.lock, flags); - for ( ; i < len ; i++) { - buf[i] = (MAX3107_WRITE_BIT | MAX3107_THR_REG); - buf[i] |= ((u16)xmit->buf[xmit->tail] & - MAX3107_SPI_TX_DATA_MASK); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - } - spin_unlock_irqrestore(&s->port.lock, flags); - if (!(s->irqen_reg & MAX3107_IRQ_TXEMPTY_BIT)) { - /* Enable TX empty interrupt */ - pr_debug("Enabling TE INT\n"); - buf[i] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); - s->irqen_reg |= MAX3107_IRQ_TXEMPTY_BIT; - buf[i] |= s->irqen_reg; - i++; - len++; - } - if (!s->tx_enabled) { - /* Enable TX */ - pr_debug("Enable TX\n"); - buf[i] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); - spin_lock_irqsave(&s->data_lock, flags); - s->mode1_reg &= ~MAX3107_MODE1_TXDIS_BIT; - buf[i] |= s->mode1_reg; - spin_unlock_irqrestore(&s->data_lock, flags); - s->tx_enabled = 1; - i++; - len++; - } - - /* Perform the SPI transfer */ - if (max3107_rw(s, (u8 *)buf, NULL, len*2)) { - dev_err(&s->spi->dev, - "SPI transfer TX handling failed\n"); - return; - } - } - - /* Indicate wake up if circular buffer is getting low on data */ - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&s->port); - -} - -/* Handle interrupts - * Also reads and returns current RX FIFO level - */ -static u16 handle_interrupt(struct max3107_port *s) -{ - u16 buf[4]; /* Buffer for SPI transfers */ - u8 irq_status; - u16 rx_level; - unsigned long flags; - - /* Read IRQ status register */ - buf[0] = MAX3107_IRQSTS_REG; - /* Read status IRQ status register */ - buf[1] = MAX3107_STS_IRQSTS_REG; - /* Read LSR IRQ status register */ - buf[2] = MAX3107_LSR_IRQSTS_REG; - /* Query RX level */ - buf[3] = MAX3107_RXFIFOLVL_REG; - - if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 8)) { - dev_err(&s->spi->dev, - "SPI transfer for INTR handling failed\n"); - return 0; - } - - irq_status = (u8)buf[0]; - pr_debug("IRQSTS %x\n", irq_status); - rx_level = (buf[3] & MAX3107_SPI_RX_DATA_MASK); - - if (irq_status & MAX3107_IRQ_LSR_BIT) { - /* LSR interrupt */ - if (buf[2] & MAX3107_LSR_RXTO_BIT) - /* RX timeout interrupt, - * handled by normal RX handling - */ - pr_debug("RX TO INT\n"); - } - - if (irq_status & MAX3107_IRQ_TXEMPTY_BIT) { - /* Tx empty interrupt, - * disable TX and set tx_fifo_empty flag - */ - pr_debug("TE INT, disabling TX\n"); - buf[0] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); - spin_lock_irqsave(&s->data_lock, flags); - s->mode1_reg |= MAX3107_MODE1_TXDIS_BIT; - buf[0] |= s->mode1_reg; - spin_unlock_irqrestore(&s->data_lock, flags); - if (max3107_rw(s, (u8 *)buf, NULL, 2)) - dev_err(&s->spi->dev, "SPI transfer TX dis failed\n"); - s->tx_enabled = 0; - s->tx_fifo_empty = 1; - } - - if (irq_status & MAX3107_IRQ_RXFIFO_BIT) - /* RX FIFO interrupt, - * handled by normal RX handling - */ - pr_debug("RFIFO INT\n"); - - /* Return RX level */ - return rx_level; -} - -/* Trigger work thread*/ -static void max3107_dowork(struct max3107_port *s) -{ - if (!work_pending(&s->work) && !freezing(current) && !s->suspended) - queue_work(s->workqueue, &s->work); - else - dev_warn(&s->spi->dev, "interrup isn't serviced normally!\n"); -} - -/* Work thread */ -static void max3107_work(struct work_struct *w) -{ - struct max3107_port *s = container_of(w, struct max3107_port, work); - u16 rxlvl = 0; - int len; /* SPI transfer buffer length */ - u16 buf[5]; /* Buffer for SPI transfers */ - unsigned long flags; - - /* Start by reading current RX FIFO level */ - buf[0] = MAX3107_RXFIFOLVL_REG; - if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { - dev_err(&s->spi->dev, "SPI transfer RX lev failed\n"); - rxlvl = 0; - } else { - rxlvl = (buf[0] & MAX3107_SPI_RX_DATA_MASK); - } - - do { - pr_debug("rxlvl %d\n", rxlvl); - - /* Handle RX */ - max3107_handlerx(s, rxlvl); - rxlvl = 0; - - if (s->handle_irq) { - /* Handle pending interrupts - * We also get new RX FIFO level since new data may - * have been received while pushing received data to - * receivers - */ - s->handle_irq = 0; - rxlvl = handle_interrupt(s); - } - - /* Handle TX */ - max3107_handletx(s); - - /* Handle configuration changes */ - len = 0; - spin_lock_irqsave(&s->data_lock, flags); - if (s->mode1_commit) { - pr_debug("mode1_commit\n"); - buf[len] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); - buf[len++] |= s->mode1_reg; - s->mode1_commit = 0; - } - if (s->lcr_commit) { - pr_debug("lcr_commit\n"); - buf[len] = (MAX3107_WRITE_BIT | MAX3107_LCR_REG); - buf[len++] |= s->lcr_reg; - s->lcr_commit = 0; - } - if (s->brg_commit) { - pr_debug("brg_commit\n"); - buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVMSB_REG); - buf[len++] |= ((s->brg_cfg >> 16) & - MAX3107_SPI_TX_DATA_MASK); - buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVLSB_REG); - buf[len++] |= ((s->brg_cfg >> 8) & - MAX3107_SPI_TX_DATA_MASK); - buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGCFG_REG); - buf[len++] |= ((s->brg_cfg) & 0xff); - s->brg_commit = 0; - } - spin_unlock_irqrestore(&s->data_lock, flags); - - if (len > 0) { - if (max3107_rw(s, (u8 *)buf, NULL, len * 2)) - dev_err(&s->spi->dev, - "SPI transfer config failed\n"); - } - - /* Reloop if interrupt handling indicated data in RX FIFO */ - } while (rxlvl); - -} - -/* Set sleep mode */ -static void max3107_set_sleep(struct max3107_port *s, int mode) -{ - u16 buf[1]; /* Buffer for SPI transfer */ - unsigned long flags; - pr_debug("enter, mode %d\n", mode); - - buf[0] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); - spin_lock_irqsave(&s->data_lock, flags); - switch (mode) { - case MAX3107_DISABLE_FORCED_SLEEP: - s->mode1_reg &= ~MAX3107_MODE1_FORCESLEEP_BIT; - break; - case MAX3107_ENABLE_FORCED_SLEEP: - s->mode1_reg |= MAX3107_MODE1_FORCESLEEP_BIT; - break; - case MAX3107_DISABLE_AUTOSLEEP: - s->mode1_reg &= ~MAX3107_MODE1_AUTOSLEEP_BIT; - break; - case MAX3107_ENABLE_AUTOSLEEP: - s->mode1_reg |= MAX3107_MODE1_AUTOSLEEP_BIT; - break; - default: - spin_unlock_irqrestore(&s->data_lock, flags); - dev_warn(&s->spi->dev, "invalid sleep mode\n"); - return; - } - buf[0] |= s->mode1_reg; - spin_unlock_irqrestore(&s->data_lock, flags); - - if (max3107_rw(s, (u8 *)buf, NULL, 2)) - dev_err(&s->spi->dev, "SPI transfer sleep mode failed\n"); - - if (mode == MAX3107_DISABLE_AUTOSLEEP || - mode == MAX3107_DISABLE_FORCED_SLEEP) - msleep(MAX3107_WAKEUP_DELAY); -} - -/* Perform full register initialization */ -static void max3107_register_init(struct max3107_port *s) -{ - u16 buf[11]; /* Buffer for SPI transfers */ - - /* 1. Configure baud rate, 9600 as default */ - s->baud = 9600; - /* the below is default*/ - if (s->ext_clk) { - s->brg_cfg = MAX3107_BRG26_B9600; - s->baud_tbl = (struct baud_table *)brg26_ext; - } else { - s->brg_cfg = MAX3107_BRG13_IB9600; - s->baud_tbl = (struct baud_table *)brg13_int; - } - - if (s->pdata->init) - s->pdata->init(s); - - buf[0] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVMSB_REG) - | ((s->brg_cfg >> 16) & MAX3107_SPI_TX_DATA_MASK); - buf[1] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVLSB_REG) - | ((s->brg_cfg >> 8) & MAX3107_SPI_TX_DATA_MASK); - buf[2] = (MAX3107_WRITE_BIT | MAX3107_BRGCFG_REG) - | ((s->brg_cfg) & 0xff); - - /* 2. Configure LCR register, 8N1 mode by default */ - s->lcr_reg = MAX3107_LCR_WORD_LEN_8; - buf[3] = (MAX3107_WRITE_BIT | MAX3107_LCR_REG) - | s->lcr_reg; - - /* 3. Configure MODE 1 register */ - s->mode1_reg = 0; - /* Enable IRQ pin */ - s->mode1_reg |= MAX3107_MODE1_IRQSEL_BIT; - /* Disable TX */ - s->mode1_reg |= MAX3107_MODE1_TXDIS_BIT; - s->tx_enabled = 0; - /* RX is enabled */ - s->rx_enabled = 1; - buf[4] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG) - | s->mode1_reg; - - /* 4. Configure MODE 2 register */ - buf[5] = (MAX3107_WRITE_BIT | MAX3107_MODE2_REG); - if (s->loopback) { - /* Enable loopback */ - buf[5] |= MAX3107_MODE2_LOOPBACK_BIT; - } - /* Reset FIFOs */ - buf[5] |= MAX3107_MODE2_FIFORST_BIT; - s->tx_fifo_empty = 1; - - /* 5. Configure FIFO trigger level register */ - buf[6] = (MAX3107_WRITE_BIT | MAX3107_FIFOTRIGLVL_REG); - /* RX FIFO trigger for 16 words, TX FIFO trigger not used */ - buf[6] |= (MAX3107_FIFOTRIGLVL_RX(16) | MAX3107_FIFOTRIGLVL_TX(0)); - - /* 6. Configure flow control levels */ - buf[7] = (MAX3107_WRITE_BIT | MAX3107_FLOWLVL_REG); - /* Flow control halt level 96, resume level 48 */ - buf[7] |= (MAX3107_FLOWLVL_RES(48) | MAX3107_FLOWLVL_HALT(96)); - - /* 7. Configure flow control */ - buf[8] = (MAX3107_WRITE_BIT | MAX3107_FLOWCTRL_REG); - /* Enable auto CTS and auto RTS flow control */ - buf[8] |= (MAX3107_FLOWCTRL_AUTOCTS_BIT | MAX3107_FLOWCTRL_AUTORTS_BIT); - - /* 8. Configure RX timeout register */ - buf[9] = (MAX3107_WRITE_BIT | MAX3107_RXTO_REG); - /* Timeout after 48 character intervals */ - buf[9] |= 0x0030; - - /* 9. Configure LSR interrupt enable register */ - buf[10] = (MAX3107_WRITE_BIT | MAX3107_LSR_IRQEN_REG); - /* Enable RX timeout interrupt */ - buf[10] |= MAX3107_LSR_RXTO_BIT; - - /* Perform SPI transfer */ - if (max3107_rw(s, (u8 *)buf, NULL, 22)) - dev_err(&s->spi->dev, "SPI transfer for init failed\n"); - - /* 10. Clear IRQ status register by reading it */ - buf[0] = MAX3107_IRQSTS_REG; - - /* 11. Configure interrupt enable register */ - /* Enable LSR interrupt */ - s->irqen_reg = MAX3107_IRQ_LSR_BIT; - /* Enable RX FIFO interrupt */ - s->irqen_reg |= MAX3107_IRQ_RXFIFO_BIT; - buf[1] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG) - | s->irqen_reg; - - /* 12. Clear FIFO reset that was set in step 6 */ - buf[2] = (MAX3107_WRITE_BIT | MAX3107_MODE2_REG); - if (s->loopback) { - /* Keep loopback enabled */ - buf[2] |= MAX3107_MODE2_LOOPBACK_BIT; - } - - /* Perform SPI transfer */ - if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 6)) - dev_err(&s->spi->dev, "SPI transfer for init failed\n"); - -} - -/* IRQ handler */ -static irqreturn_t max3107_irq(int irqno, void *dev_id) -{ - struct max3107_port *s = dev_id; - - if (irqno != s->spi->irq) { - /* Unexpected IRQ */ - return IRQ_NONE; - } - - /* Indicate irq */ - s->handle_irq = 1; - - /* Trigger work thread */ - max3107_dowork(s); - - return IRQ_HANDLED; -} - -/* HW suspension function - * - * Currently autosleep is used to decrease current consumption, alternative - * approach would be to set the chip to reset mode if UART is not being - * used but that would mess the GPIOs - * - */ -void max3107_hw_susp(struct max3107_port *s, int suspend) -{ - pr_debug("enter, suspend %d\n", suspend); - - if (suspend) { - /* Suspend requested, - * enable autosleep to decrease current consumption - */ - s->suspended = 1; - max3107_set_sleep(s, MAX3107_ENABLE_AUTOSLEEP); - } else { - /* Resume requested, - * disable autosleep - */ - s->suspended = 0; - max3107_set_sleep(s, MAX3107_DISABLE_AUTOSLEEP); - } -} -EXPORT_SYMBOL_GPL(max3107_hw_susp); - -/* Modem status IRQ enabling */ -static void max3107_enable_ms(struct uart_port *port) -{ - /* Modem status not supported */ -} - -/* Data send function */ -static void max3107_start_tx(struct uart_port *port) -{ - struct max3107_port *s = container_of(port, struct max3107_port, port); - - /* Trigger work thread for sending data */ - max3107_dowork(s); -} - -/* Function for checking that there is no pending transfers */ -static unsigned int max3107_tx_empty(struct uart_port *port) -{ - struct max3107_port *s = container_of(port, struct max3107_port, port); - - pr_debug("returning %d\n", - (s->tx_fifo_empty && uart_circ_empty(&s->port.state->xmit))); - return s->tx_fifo_empty && uart_circ_empty(&s->port.state->xmit); -} - -/* Function for stopping RX */ -static void max3107_stop_rx(struct uart_port *port) -{ - struct max3107_port *s = container_of(port, struct max3107_port, port); - unsigned long flags; - - /* Set RX disabled in MODE 1 register */ - spin_lock_irqsave(&s->data_lock, flags); - s->mode1_reg |= MAX3107_MODE1_RXDIS_BIT; - s->mode1_commit = 1; - spin_unlock_irqrestore(&s->data_lock, flags); - /* Set RX disabled */ - s->rx_enabled = 0; - /* Trigger work thread for doing the actual configuration change */ - max3107_dowork(s); -} - -/* Function for returning control pin states */ -static unsigned int max3107_get_mctrl(struct uart_port *port) -{ - /* DCD and DSR are not wired and CTS/RTS is handled automatically - * so just indicate DSR and CAR asserted - */ - return TIOCM_DSR | TIOCM_CAR; -} - -/* Function for setting control pin states */ -static void max3107_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - /* DCD and DSR are not wired and CTS/RTS is hadnled automatically - * so do nothing - */ -} - -/* Function for configuring UART parameters */ -static void max3107_set_termios(struct uart_port *port, - struct ktermios *termios, - struct ktermios *old) -{ - struct max3107_port *s = container_of(port, struct max3107_port, port); - struct tty_struct *tty; - int baud; - u16 new_lcr = 0; - u32 new_brg = 0; - unsigned long flags; - - if (!port->state) - return; - - tty = port->state->port.tty; - if (!tty) - return; - - /* Get new LCR register values */ - /* Word size */ - if ((termios->c_cflag & CSIZE) == CS7) - new_lcr |= MAX3107_LCR_WORD_LEN_7; - else - new_lcr |= MAX3107_LCR_WORD_LEN_8; - - /* Parity */ - if (termios->c_cflag & PARENB) { - new_lcr |= MAX3107_LCR_PARITY_BIT; - if (!(termios->c_cflag & PARODD)) - new_lcr |= MAX3107_LCR_EVENPARITY_BIT; - } - - /* Stop bits */ - if (termios->c_cflag & CSTOPB) { - /* 2 stop bits */ - new_lcr |= MAX3107_LCR_STOPLEN_BIT; - } - - /* Mask termios capabilities we don't support */ - termios->c_cflag &= ~CMSPAR; - - /* Set status ignore mask */ - s->port.ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - s->port.ignore_status_mask |= MAX3107_ALL_ERRORS; - - /* Set low latency to immediately handle pushed data */ - s->port.state->port.tty->low_latency = 1; - - /* Get new baud rate generator configuration */ - baud = tty_get_baud_rate(tty); - - spin_lock_irqsave(&s->data_lock, flags); - new_brg = get_new_brg(baud, s); - /* if can't find the corrent config, use previous */ - if (!new_brg) { - baud = s->baud; - new_brg = s->brg_cfg; - } - spin_unlock_irqrestore(&s->data_lock, flags); - tty_termios_encode_baud_rate(termios, baud, baud); - s->baud = baud; - - /* Update timeout according to new baud rate */ - uart_update_timeout(port, termios->c_cflag, baud); - - spin_lock_irqsave(&s->data_lock, flags); - if (s->lcr_reg != new_lcr) { - s->lcr_reg = new_lcr; - s->lcr_commit = 1; - } - if (s->brg_cfg != new_brg) { - s->brg_cfg = new_brg; - s->brg_commit = 1; - } - spin_unlock_irqrestore(&s->data_lock, flags); - - /* Trigger work thread for doing the actual configuration change */ - max3107_dowork(s); -} - -/* Port shutdown function */ -static void max3107_shutdown(struct uart_port *port) -{ - struct max3107_port *s = container_of(port, struct max3107_port, port); - - if (s->suspended && s->pdata->hw_suspend) - s->pdata->hw_suspend(s, 0); - - /* Free the interrupt */ - free_irq(s->spi->irq, s); - - if (s->workqueue) { - /* Flush and destroy work queue */ - flush_workqueue(s->workqueue); - destroy_workqueue(s->workqueue); - s->workqueue = NULL; - } - - /* Suspend HW */ - if (s->pdata->hw_suspend) - s->pdata->hw_suspend(s, 1); -} - -/* Port startup function */ -static int max3107_startup(struct uart_port *port) -{ - struct max3107_port *s = container_of(port, struct max3107_port, port); - - /* Initialize work queue */ - s->workqueue = create_freezable_workqueue("max3107"); - if (!s->workqueue) { - dev_err(&s->spi->dev, "Workqueue creation failed\n"); - return -EBUSY; - } - INIT_WORK(&s->work, max3107_work); - - /* Setup IRQ */ - if (request_irq(s->spi->irq, max3107_irq, IRQF_TRIGGER_FALLING, - "max3107", s)) { - dev_err(&s->spi->dev, "IRQ reguest failed\n"); - destroy_workqueue(s->workqueue); - s->workqueue = NULL; - return -EBUSY; - } - - /* Resume HW */ - if (s->pdata->hw_suspend) - s->pdata->hw_suspend(s, 0); - - /* Init registers */ - max3107_register_init(s); - - return 0; -} - -/* Port type function */ -static const char *max3107_type(struct uart_port *port) -{ - struct max3107_port *s = container_of(port, struct max3107_port, port); - return s->spi->modalias; -} - -/* Port release function */ -static void max3107_release_port(struct uart_port *port) -{ - /* Do nothing */ -} - -/* Port request function */ -static int max3107_request_port(struct uart_port *port) -{ - /* Do nothing */ - return 0; -} - -/* Port config function */ -static void max3107_config_port(struct uart_port *port, int flags) -{ - struct max3107_port *s = container_of(port, struct max3107_port, port); - s->port.type = PORT_MAX3107; -} - -/* Port verify function */ -static int max3107_verify_port(struct uart_port *port, - struct serial_struct *ser) -{ - if (ser->type == PORT_UNKNOWN || ser->type == PORT_MAX3107) - return 0; - - return -EINVAL; -} - -/* Port stop TX function */ -static void max3107_stop_tx(struct uart_port *port) -{ - /* Do nothing */ -} - -/* Port break control function */ -static void max3107_break_ctl(struct uart_port *port, int break_state) -{ - /* We don't support break control, do nothing */ -} - - -/* Port functions */ -static struct uart_ops max3107_ops = { - .tx_empty = max3107_tx_empty, - .set_mctrl = max3107_set_mctrl, - .get_mctrl = max3107_get_mctrl, - .stop_tx = max3107_stop_tx, - .start_tx = max3107_start_tx, - .stop_rx = max3107_stop_rx, - .enable_ms = max3107_enable_ms, - .break_ctl = max3107_break_ctl, - .startup = max3107_startup, - .shutdown = max3107_shutdown, - .set_termios = max3107_set_termios, - .type = max3107_type, - .release_port = max3107_release_port, - .request_port = max3107_request_port, - .config_port = max3107_config_port, - .verify_port = max3107_verify_port, -}; - -/* UART driver data */ -static struct uart_driver max3107_uart_driver = { - .owner = THIS_MODULE, - .driver_name = "ttyMAX", - .dev_name = "ttyMAX", - .nr = 1, -}; - -static int driver_registered = 0; - - - -/* 'Generic' platform data */ -static struct max3107_plat generic_plat_data = { - .loopback = 0, - .ext_clk = 1, - .hw_suspend = max3107_hw_susp, - .polled_mode = 0, - .poll_time = 0, -}; - - -/*******************************************************************/ - -/** - * max3107_probe - SPI bus probe entry point - * @spi: the spi device - * - * SPI wants us to probe this device and if appropriate claim it. - * Perform any platform specific requirements and then initialise - * the device. - */ - -int max3107_probe(struct spi_device *spi, struct max3107_plat *pdata) -{ - struct max3107_port *s; - u16 buf[2]; /* Buffer for SPI transfers */ - int retval; - - pr_info("enter max3107 probe\n"); - - /* Allocate port structure */ - s = kzalloc(sizeof(*s), GFP_KERNEL); - if (!s) { - pr_err("Allocating port structure failed\n"); - return -ENOMEM; - } - - s->pdata = pdata; - - /* SPI Rx buffer - * +2 for RX FIFO interrupt - * disabling and RX level query - */ - s->rxbuf = kzalloc(sizeof(u16) * (MAX3107_RX_FIFO_SIZE+2), GFP_KERNEL); - if (!s->rxbuf) { - pr_err("Allocating RX buffer failed\n"); - retval = -ENOMEM; - goto err_free4; - } - s->rxstr = kzalloc(sizeof(u8) * MAX3107_RX_FIFO_SIZE, GFP_KERNEL); - if (!s->rxstr) { - pr_err("Allocating RX buffer failed\n"); - retval = -ENOMEM; - goto err_free3; - } - /* SPI Tx buffer - * SPI transfer buffer - * +3 for TX FIFO empty - * interrupt disabling and - * enabling and TX enabling - */ - s->txbuf = kzalloc(sizeof(u16) * MAX3107_TX_FIFO_SIZE + 3, GFP_KERNEL); - if (!s->txbuf) { - pr_err("Allocating TX buffer failed\n"); - retval = -ENOMEM; - goto err_free2; - } - /* Initialize shared data lock */ - spin_lock_init(&s->data_lock); - - /* SPI intializations */ - dev_set_drvdata(&spi->dev, s); - spi->mode = SPI_MODE_0; - spi->dev.platform_data = pdata; - spi->bits_per_word = 16; - s->ext_clk = pdata->ext_clk; - s->loopback = pdata->loopback; - spi_setup(spi); - s->spi = spi; - - /* Check REV ID to ensure we are talking to what we expect */ - buf[0] = MAX3107_REVID_REG; - if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { - dev_err(&s->spi->dev, "SPI transfer for REVID read failed\n"); - retval = -EIO; - goto err_free1; - } - if ((buf[0] & MAX3107_SPI_RX_DATA_MASK) != MAX3107_REVID1 && - (buf[0] & MAX3107_SPI_RX_DATA_MASK) != MAX3107_REVID2) { - dev_err(&s->spi->dev, "REVID %x does not match\n", - (buf[0] & MAX3107_SPI_RX_DATA_MASK)); - retval = -ENODEV; - goto err_free1; - } - - /* Disable all interrupts */ - buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG | 0x0000); - buf[0] |= 0x0000; - - /* Configure clock source */ - buf[1] = (MAX3107_WRITE_BIT | MAX3107_CLKSRC_REG); - if (s->ext_clk) { - /* External clock */ - buf[1] |= MAX3107_CLKSRC_EXTCLK_BIT; - } - - /* PLL bypass ON */ - buf[1] |= MAX3107_CLKSRC_PLLBYP_BIT; - - /* Perform SPI transfer */ - if (max3107_rw(s, (u8 *)buf, NULL, 4)) { - dev_err(&s->spi->dev, "SPI transfer for init failed\n"); - retval = -EIO; - goto err_free1; - } - - /* Register UART driver */ - if (!driver_registered) { - retval = uart_register_driver(&max3107_uart_driver); - if (retval) { - dev_err(&s->spi->dev, "Registering UART driver failed\n"); - goto err_free1; - } - driver_registered = 1; - } - - /* Initialize UART port data */ - s->port.fifosize = 128; - s->port.ops = &max3107_ops; - s->port.line = 0; - s->port.dev = &spi->dev; - s->port.uartclk = 9600; - s->port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; - s->port.irq = s->spi->irq; - s->port.type = PORT_MAX3107; - - /* Add UART port */ - retval = uart_add_one_port(&max3107_uart_driver, &s->port); - if (retval < 0) { - dev_err(&s->spi->dev, "Adding UART port failed\n"); - goto err_free1; - } - - if (pdata->configure) { - retval = pdata->configure(s); - if (retval < 0) - goto err_free1; - } - - /* Go to suspend mode */ - if (pdata->hw_suspend) - pdata->hw_suspend(s, 1); - - return 0; - -err_free1: - kfree(s->txbuf); -err_free2: - kfree(s->rxstr); -err_free3: - kfree(s->rxbuf); -err_free4: - kfree(s); - return retval; -} -EXPORT_SYMBOL_GPL(max3107_probe); - -/* Driver remove function */ -int max3107_remove(struct spi_device *spi) -{ - struct max3107_port *s = dev_get_drvdata(&spi->dev); - - pr_info("enter max3107 remove\n"); - - /* Remove port */ - if (uart_remove_one_port(&max3107_uart_driver, &s->port)) - dev_warn(&s->spi->dev, "Removing UART port failed\n"); - - - /* Free TxRx buffer */ - kfree(s->rxbuf); - kfree(s->rxstr); - kfree(s->txbuf); - - /* Free port structure */ - kfree(s); - - return 0; -} -EXPORT_SYMBOL_GPL(max3107_remove); - -/* Driver suspend function */ -int max3107_suspend(struct spi_device *spi, pm_message_t state) -{ -#ifdef CONFIG_PM - struct max3107_port *s = dev_get_drvdata(&spi->dev); - - pr_debug("enter suspend\n"); - - /* Suspend UART port */ - uart_suspend_port(&max3107_uart_driver, &s->port); - - /* Go to suspend mode */ - if (s->pdata->hw_suspend) - s->pdata->hw_suspend(s, 1); -#endif /* CONFIG_PM */ - return 0; -} -EXPORT_SYMBOL_GPL(max3107_suspend); - -/* Driver resume function */ -int max3107_resume(struct spi_device *spi) -{ -#ifdef CONFIG_PM - struct max3107_port *s = dev_get_drvdata(&spi->dev); - - pr_debug("enter resume\n"); - - /* Resume from suspend */ - if (s->pdata->hw_suspend) - s->pdata->hw_suspend(s, 0); - - /* Resume UART port */ - uart_resume_port(&max3107_uart_driver, &s->port); -#endif /* CONFIG_PM */ - return 0; -} -EXPORT_SYMBOL_GPL(max3107_resume); - -static int max3107_probe_generic(struct spi_device *spi) -{ - return max3107_probe(spi, &generic_plat_data); -} - -/* Spi driver data */ -static struct spi_driver max3107_driver = { - .driver = { - .name = "max3107", - .owner = THIS_MODULE, - }, - .probe = max3107_probe_generic, - .remove = __devexit_p(max3107_remove), - .suspend = max3107_suspend, - .resume = max3107_resume, -}; - -/* Driver init function */ -static int __init max3107_init(void) -{ - pr_info("enter max3107 init\n"); - return spi_register_driver(&max3107_driver); -} - -/* Driver exit function */ -static void __exit max3107_exit(void) -{ - pr_info("enter max3107 exit\n"); - /* Unregister UART driver */ - if (driver_registered) - uart_unregister_driver(&max3107_uart_driver); - spi_unregister_driver(&max3107_driver); -} - -module_init(max3107_init); -module_exit(max3107_exit); - -MODULE_DESCRIPTION("MAX3107 driver"); -MODULE_AUTHOR("Aavamobile"); -MODULE_ALIAS("spi:max3107"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/max3107.h b/drivers/tty/serial/max3107.h deleted file mode 100644 index 8415fc723b96..000000000000 --- a/drivers/tty/serial/max3107.h +++ /dev/null @@ -1,441 +0,0 @@ -/* - * max3107.h - spi uart protocol driver header for Maxim 3107 - * - * Copyright (C) Aavamobile 2009 - * Based on serial_max3100.h by Christian Pellegrin - * - * 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 _MAX3107_H -#define _MAX3107_H - -/* Serial error status definitions */ -#define MAX3107_PARITY_ERROR 1 -#define MAX3107_FRAME_ERROR 2 -#define MAX3107_OVERRUN_ERROR 4 -#define MAX3107_ALL_ERRORS (MAX3107_PARITY_ERROR | \ - MAX3107_FRAME_ERROR | \ - MAX3107_OVERRUN_ERROR) - -/* GPIO definitions */ -#define MAX3107_GPIO_BASE 88 -#define MAX3107_GPIO_COUNT 4 - - -/* GPIO connected to chip's reset pin */ -#define MAX3107_RESET_GPIO 87 - - -/* Chip reset delay */ -#define MAX3107_RESET_DELAY 10 - -/* Chip wakeup delay */ -#define MAX3107_WAKEUP_DELAY 50 - - -/* Sleep mode definitions */ -#define MAX3107_DISABLE_FORCED_SLEEP 0 -#define MAX3107_ENABLE_FORCED_SLEEP 1 -#define MAX3107_DISABLE_AUTOSLEEP 2 -#define MAX3107_ENABLE_AUTOSLEEP 3 - - -/* Definitions for register access with SPI transfers - * - * SPI transfer format: - * - * Master to slave bits xzzzzzzzyyyyyyyy - * Slave to master bits aaaaaaaabbbbbbbb - * - * where: - * x = 0 for reads, 1 for writes - * z = register address - * y = new register value if write, 0 if read - * a = unspecified - * b = register value if read, unspecified if write - */ - -/* SPI speed */ -#define MAX3107_SPI_SPEED (3125000 * 2) - -/* Write bit */ -#define MAX3107_WRITE_BIT (1 << 15) - -/* SPI TX data mask */ -#define MAX3107_SPI_RX_DATA_MASK (0x00ff) - -/* SPI RX data mask */ -#define MAX3107_SPI_TX_DATA_MASK (0x00ff) - -/* Register access masks */ -#define MAX3107_RHR_REG (0x0000) /* RX FIFO */ -#define MAX3107_THR_REG (0x0000) /* TX FIFO */ -#define MAX3107_IRQEN_REG (0x0100) /* IRQ enable */ -#define MAX3107_IRQSTS_REG (0x0200) /* IRQ status */ -#define MAX3107_LSR_IRQEN_REG (0x0300) /* LSR IRQ enable */ -#define MAX3107_LSR_IRQSTS_REG (0x0400) /* LSR IRQ status */ -#define MAX3107_SPCHR_IRQEN_REG (0x0500) /* Special char IRQ enable */ -#define MAX3107_SPCHR_IRQSTS_REG (0x0600) /* Special char IRQ status */ -#define MAX3107_STS_IRQEN_REG (0x0700) /* Status IRQ enable */ -#define MAX3107_STS_IRQSTS_REG (0x0800) /* Status IRQ status */ -#define MAX3107_MODE1_REG (0x0900) /* MODE1 */ -#define MAX3107_MODE2_REG (0x0a00) /* MODE2 */ -#define MAX3107_LCR_REG (0x0b00) /* LCR */ -#define MAX3107_RXTO_REG (0x0c00) /* RX timeout */ -#define MAX3107_HDPIXDELAY_REG (0x0d00) /* Auto transceiver delays */ -#define MAX3107_IRDA_REG (0x0e00) /* IRDA settings */ -#define MAX3107_FLOWLVL_REG (0x0f00) /* Flow control levels */ -#define MAX3107_FIFOTRIGLVL_REG (0x1000) /* FIFO IRQ trigger levels */ -#define MAX3107_TXFIFOLVL_REG (0x1100) /* TX FIFO level */ -#define MAX3107_RXFIFOLVL_REG (0x1200) /* RX FIFO level */ -#define MAX3107_FLOWCTRL_REG (0x1300) /* Flow control */ -#define MAX3107_XON1_REG (0x1400) /* XON1 character */ -#define MAX3107_XON2_REG (0x1500) /* XON2 character */ -#define MAX3107_XOFF1_REG (0x1600) /* XOFF1 character */ -#define MAX3107_XOFF2_REG (0x1700) /* XOFF2 character */ -#define MAX3107_GPIOCFG_REG (0x1800) /* GPIO config */ -#define MAX3107_GPIODATA_REG (0x1900) /* GPIO data */ -#define MAX3107_PLLCFG_REG (0x1a00) /* PLL config */ -#define MAX3107_BRGCFG_REG (0x1b00) /* Baud rate generator conf */ -#define MAX3107_BRGDIVLSB_REG (0x1c00) /* Baud rate divisor LSB */ -#define MAX3107_BRGDIVMSB_REG (0x1d00) /* Baud rate divisor MSB */ -#define MAX3107_CLKSRC_REG (0x1e00) /* Clock source */ -#define MAX3107_REVID_REG (0x1f00) /* Revision identification */ - -/* IRQ register bits */ -#define MAX3107_IRQ_LSR_BIT (1 << 0) /* LSR interrupt */ -#define MAX3107_IRQ_SPCHR_BIT (1 << 1) /* Special char interrupt */ -#define MAX3107_IRQ_STS_BIT (1 << 2) /* Status interrupt */ -#define MAX3107_IRQ_RXFIFO_BIT (1 << 3) /* RX FIFO interrupt */ -#define MAX3107_IRQ_TXFIFO_BIT (1 << 4) /* TX FIFO interrupt */ -#define MAX3107_IRQ_TXEMPTY_BIT (1 << 5) /* TX FIFO empty interrupt */ -#define MAX3107_IRQ_RXEMPTY_BIT (1 << 6) /* RX FIFO empty interrupt */ -#define MAX3107_IRQ_CTS_BIT (1 << 7) /* CTS interrupt */ - -/* LSR register bits */ -#define MAX3107_LSR_RXTO_BIT (1 << 0) /* RX timeout */ -#define MAX3107_LSR_RXOVR_BIT (1 << 1) /* RX overrun */ -#define MAX3107_LSR_RXPAR_BIT (1 << 2) /* RX parity error */ -#define MAX3107_LSR_FRERR_BIT (1 << 3) /* Frame error */ -#define MAX3107_LSR_RXBRK_BIT (1 << 4) /* RX break */ -#define MAX3107_LSR_RXNOISE_BIT (1 << 5) /* RX noise */ -#define MAX3107_LSR_UNDEF6_BIT (1 << 6) /* Undefined/not used */ -#define MAX3107_LSR_CTS_BIT (1 << 7) /* CTS pin state */ - -/* Special character register bits */ -#define MAX3107_SPCHR_XON1_BIT (1 << 0) /* XON1 character */ -#define MAX3107_SPCHR_XON2_BIT (1 << 1) /* XON2 character */ -#define MAX3107_SPCHR_XOFF1_BIT (1 << 2) /* XOFF1 character */ -#define MAX3107_SPCHR_XOFF2_BIT (1 << 3) /* XOFF2 character */ -#define MAX3107_SPCHR_BREAK_BIT (1 << 4) /* RX break */ -#define MAX3107_SPCHR_MULTIDROP_BIT (1 << 5) /* 9-bit multidrop addr char */ -#define MAX3107_SPCHR_UNDEF6_BIT (1 << 6) /* Undefined/not used */ -#define MAX3107_SPCHR_UNDEF7_BIT (1 << 7) /* Undefined/not used */ - -/* Status register bits */ -#define MAX3107_STS_GPIO0_BIT (1 << 0) /* GPIO 0 interrupt */ -#define MAX3107_STS_GPIO1_BIT (1 << 1) /* GPIO 1 interrupt */ -#define MAX3107_STS_GPIO2_BIT (1 << 2) /* GPIO 2 interrupt */ -#define MAX3107_STS_GPIO3_BIT (1 << 3) /* GPIO 3 interrupt */ -#define MAX3107_STS_UNDEF4_BIT (1 << 4) /* Undefined/not used */ -#define MAX3107_STS_CLKREADY_BIT (1 << 5) /* Clock ready */ -#define MAX3107_STS_SLEEP_BIT (1 << 6) /* Sleep interrupt */ -#define MAX3107_STS_UNDEF7_BIT (1 << 7) /* Undefined/not used */ - -/* MODE1 register bits */ -#define MAX3107_MODE1_RXDIS_BIT (1 << 0) /* RX disable */ -#define MAX3107_MODE1_TXDIS_BIT (1 << 1) /* TX disable */ -#define MAX3107_MODE1_TXHIZ_BIT (1 << 2) /* TX pin three-state */ -#define MAX3107_MODE1_RTSHIZ_BIT (1 << 3) /* RTS pin three-state */ -#define MAX3107_MODE1_TRNSCVCTRL_BIT (1 << 4) /* Transceiver ctrl enable */ -#define MAX3107_MODE1_FORCESLEEP_BIT (1 << 5) /* Force sleep mode */ -#define MAX3107_MODE1_AUTOSLEEP_BIT (1 << 6) /* Auto sleep enable */ -#define MAX3107_MODE1_IRQSEL_BIT (1 << 7) /* IRQ pin enable */ - -/* MODE2 register bits */ -#define MAX3107_MODE2_RST_BIT (1 << 0) /* Chip reset */ -#define MAX3107_MODE2_FIFORST_BIT (1 << 1) /* FIFO reset */ -#define MAX3107_MODE2_RXTRIGINV_BIT (1 << 2) /* RX FIFO INT invert */ -#define MAX3107_MODE2_RXEMPTINV_BIT (1 << 3) /* RX FIFO empty INT invert */ -#define MAX3107_MODE2_SPCHR_BIT (1 << 4) /* Special chr detect enable */ -#define MAX3107_MODE2_LOOPBACK_BIT (1 << 5) /* Internal loopback enable */ -#define MAX3107_MODE2_MULTIDROP_BIT (1 << 6) /* 9-bit multidrop enable */ -#define MAX3107_MODE2_ECHOSUPR_BIT (1 << 7) /* ECHO suppression enable */ - -/* LCR register bits */ -#define MAX3107_LCR_LENGTH0_BIT (1 << 0) /* Word length bit 0 */ -#define MAX3107_LCR_LENGTH1_BIT (1 << 1) /* Word length bit 1 - * - * Word length bits table: - * 00 -> 5 bit words - * 01 -> 6 bit words - * 10 -> 7 bit words - * 11 -> 8 bit words - */ -#define MAX3107_LCR_STOPLEN_BIT (1 << 2) /* STOP length bit - * - * STOP length bit table: - * 0 -> 1 stop bit - * 1 -> 1-1.5 stop bits if - * word length is 5, - * 2 stop bits otherwise - */ -#define MAX3107_LCR_PARITY_BIT (1 << 3) /* Parity bit enable */ -#define MAX3107_LCR_EVENPARITY_BIT (1 << 4) /* Even parity bit enable */ -#define MAX3107_LCR_FORCEPARITY_BIT (1 << 5) /* 9-bit multidrop parity */ -#define MAX3107_LCR_TXBREAK_BIT (1 << 6) /* TX break enable */ -#define MAX3107_LCR_RTS_BIT (1 << 7) /* RTS pin control */ -#define MAX3107_LCR_WORD_LEN_5 (0x0000) -#define MAX3107_LCR_WORD_LEN_6 (0x0001) -#define MAX3107_LCR_WORD_LEN_7 (0x0002) -#define MAX3107_LCR_WORD_LEN_8 (0x0003) - - -/* IRDA register bits */ -#define MAX3107_IRDA_IRDAEN_BIT (1 << 0) /* IRDA mode enable */ -#define MAX3107_IRDA_SIR_BIT (1 << 1) /* SIR mode enable */ -#define MAX3107_IRDA_SHORTIR_BIT (1 << 2) /* Short SIR mode enable */ -#define MAX3107_IRDA_MIR_BIT (1 << 3) /* MIR mode enable */ -#define MAX3107_IRDA_RXINV_BIT (1 << 4) /* RX logic inversion enable */ -#define MAX3107_IRDA_TXINV_BIT (1 << 5) /* TX logic inversion enable */ -#define MAX3107_IRDA_UNDEF6_BIT (1 << 6) /* Undefined/not used */ -#define MAX3107_IRDA_UNDEF7_BIT (1 << 7) /* Undefined/not used */ - -/* Flow control trigger level register masks */ -#define MAX3107_FLOWLVL_HALT_MASK (0x000f) /* Flow control halt level */ -#define MAX3107_FLOWLVL_RES_MASK (0x00f0) /* Flow control resume level */ -#define MAX3107_FLOWLVL_HALT(words) ((words/8) & 0x000f) -#define MAX3107_FLOWLVL_RES(words) (((words/8) & 0x000f) << 4) - -/* FIFO interrupt trigger level register masks */ -#define MAX3107_FIFOTRIGLVL_TX_MASK (0x000f) /* TX FIFO trigger level */ -#define MAX3107_FIFOTRIGLVL_RX_MASK (0x00f0) /* RX FIFO trigger level */ -#define MAX3107_FIFOTRIGLVL_TX(words) ((words/8) & 0x000f) -#define MAX3107_FIFOTRIGLVL_RX(words) (((words/8) & 0x000f) << 4) - -/* Flow control register bits */ -#define MAX3107_FLOWCTRL_AUTORTS_BIT (1 << 0) /* Auto RTS flow ctrl enable */ -#define MAX3107_FLOWCTRL_AUTOCTS_BIT (1 << 1) /* Auto CTS flow ctrl enable */ -#define MAX3107_FLOWCTRL_GPIADDR_BIT (1 << 2) /* Enables that GPIO inputs - * are used in conjunction with - * XOFF2 for definition of - * special character */ -#define MAX3107_FLOWCTRL_SWFLOWEN_BIT (1 << 3) /* Auto SW flow ctrl enable */ -#define MAX3107_FLOWCTRL_SWFLOW0_BIT (1 << 4) /* SWFLOW bit 0 */ -#define MAX3107_FLOWCTRL_SWFLOW1_BIT (1 << 5) /* SWFLOW bit 1 - * - * SWFLOW bits 1 & 0 table: - * 00 -> no transmitter flow - * control - * 01 -> receiver compares - * XON2 and XOFF2 - * and controls - * transmitter - * 10 -> receiver compares - * XON1 and XOFF1 - * and controls - * transmitter - * 11 -> receiver compares - * XON1, XON2, XOFF1 and - * XOFF2 and controls - * transmitter - */ -#define MAX3107_FLOWCTRL_SWFLOW2_BIT (1 << 6) /* SWFLOW bit 2 */ -#define MAX3107_FLOWCTRL_SWFLOW3_BIT (1 << 7) /* SWFLOW bit 3 - * - * SWFLOW bits 3 & 2 table: - * 00 -> no received flow - * control - * 01 -> transmitter generates - * XON2 and XOFF2 - * 10 -> transmitter generates - * XON1 and XOFF1 - * 11 -> transmitter generates - * XON1, XON2, XOFF1 and - * XOFF2 - */ - -/* GPIO configuration register bits */ -#define MAX3107_GPIOCFG_GP0OUT_BIT (1 << 0) /* GPIO 0 output enable */ -#define MAX3107_GPIOCFG_GP1OUT_BIT (1 << 1) /* GPIO 1 output enable */ -#define MAX3107_GPIOCFG_GP2OUT_BIT (1 << 2) /* GPIO 2 output enable */ -#define MAX3107_GPIOCFG_GP3OUT_BIT (1 << 3) /* GPIO 3 output enable */ -#define MAX3107_GPIOCFG_GP0OD_BIT (1 << 4) /* GPIO 0 open-drain enable */ -#define MAX3107_GPIOCFG_GP1OD_BIT (1 << 5) /* GPIO 1 open-drain enable */ -#define MAX3107_GPIOCFG_GP2OD_BIT (1 << 6) /* GPIO 2 open-drain enable */ -#define MAX3107_GPIOCFG_GP3OD_BIT (1 << 7) /* GPIO 3 open-drain enable */ - -/* GPIO DATA register bits */ -#define MAX3107_GPIODATA_GP0OUT_BIT (1 << 0) /* GPIO 0 output value */ -#define MAX3107_GPIODATA_GP1OUT_BIT (1 << 1) /* GPIO 1 output value */ -#define MAX3107_GPIODATA_GP2OUT_BIT (1 << 2) /* GPIO 2 output value */ -#define MAX3107_GPIODATA_GP3OUT_BIT (1 << 3) /* GPIO 3 output value */ -#define MAX3107_GPIODATA_GP0IN_BIT (1 << 4) /* GPIO 0 input value */ -#define MAX3107_GPIODATA_GP1IN_BIT (1 << 5) /* GPIO 1 input value */ -#define MAX3107_GPIODATA_GP2IN_BIT (1 << 6) /* GPIO 2 input value */ -#define MAX3107_GPIODATA_GP3IN_BIT (1 << 7) /* GPIO 3 input value */ - -/* PLL configuration register masks */ -#define MAX3107_PLLCFG_PREDIV_MASK (0x003f) /* PLL predivision value */ -#define MAX3107_PLLCFG_PLLFACTOR_MASK (0x00c0) /* PLL multiplication factor */ - -/* Baud rate generator configuration register masks and bits */ -#define MAX3107_BRGCFG_FRACT_MASK (0x000f) /* Fractional portion of - * Baud rate generator divisor - */ -#define MAX3107_BRGCFG_2XMODE_BIT (1 << 4) /* Double baud rate */ -#define MAX3107_BRGCFG_4XMODE_BIT (1 << 5) /* Quadruple baud rate */ -#define MAX3107_BRGCFG_UNDEF6_BIT (1 << 6) /* Undefined/not used */ -#define MAX3107_BRGCFG_UNDEF7_BIT (1 << 7) /* Undefined/not used */ - -/* Clock source register bits */ -#define MAX3107_CLKSRC_INTOSC_BIT (1 << 0) /* Internal osc enable */ -#define MAX3107_CLKSRC_CRYST_BIT (1 << 1) /* Crystal osc enable */ -#define MAX3107_CLKSRC_PLL_BIT (1 << 2) /* PLL enable */ -#define MAX3107_CLKSRC_PLLBYP_BIT (1 << 3) /* PLL bypass */ -#define MAX3107_CLKSRC_EXTCLK_BIT (1 << 4) /* External clock enable */ -#define MAX3107_CLKSRC_UNDEF5_BIT (1 << 5) /* Undefined/not used */ -#define MAX3107_CLKSRC_UNDEF6_BIT (1 << 6) /* Undefined/not used */ -#define MAX3107_CLKSRC_CLK2RTS_BIT (1 << 7) /* Baud clk to RTS pin */ - - -/* HW definitions */ -#define MAX3107_RX_FIFO_SIZE 128 -#define MAX3107_TX_FIFO_SIZE 128 -#define MAX3107_REVID1 0x00a0 -#define MAX3107_REVID2 0x00a1 - - -/* Baud rate generator configuration values for external clock 13MHz */ -#define MAX3107_BRG13_B300 (0x0A9400 | 0x05) -#define MAX3107_BRG13_B600 (0x054A00 | 0x03) -#define MAX3107_BRG13_B1200 (0x02A500 | 0x01) -#define MAX3107_BRG13_B2400 (0x015200 | 0x09) -#define MAX3107_BRG13_B4800 (0x00A900 | 0x04) -#define MAX3107_BRG13_B9600 (0x005400 | 0x0A) -#define MAX3107_BRG13_B19200 (0x002A00 | 0x05) -#define MAX3107_BRG13_B38400 (0x001500 | 0x03) -#define MAX3107_BRG13_B57600 (0x000E00 | 0x02) -#define MAX3107_BRG13_B115200 (0x000700 | 0x01) -#define MAX3107_BRG13_B230400 (0x000300 | 0x08) -#define MAX3107_BRG13_B460800 (0x000100 | 0x0c) -#define MAX3107_BRG13_B921600 (0x000100 | 0x1c) - -/* Baud rate generator configuration values for external clock 26MHz */ -#define MAX3107_BRG26_B300 (0x152800 | 0x0A) -#define MAX3107_BRG26_B600 (0x0A9400 | 0x05) -#define MAX3107_BRG26_B1200 (0x054A00 | 0x03) -#define MAX3107_BRG26_B2400 (0x02A500 | 0x01) -#define MAX3107_BRG26_B4800 (0x015200 | 0x09) -#define MAX3107_BRG26_B9600 (0x00A900 | 0x04) -#define MAX3107_BRG26_B19200 (0x005400 | 0x0A) -#define MAX3107_BRG26_B38400 (0x002A00 | 0x05) -#define MAX3107_BRG26_B57600 (0x001C00 | 0x03) -#define MAX3107_BRG26_B115200 (0x000E00 | 0x02) -#define MAX3107_BRG26_B230400 (0x000700 | 0x01) -#define MAX3107_BRG26_B460800 (0x000300 | 0x08) -#define MAX3107_BRG26_B921600 (0x000100 | 0x0C) - -/* Baud rate generator configuration values for internal clock */ -#define MAX3107_BRG13_IB300 (0x008000 | 0x00) -#define MAX3107_BRG13_IB600 (0x004000 | 0x00) -#define MAX3107_BRG13_IB1200 (0x002000 | 0x00) -#define MAX3107_BRG13_IB2400 (0x001000 | 0x00) -#define MAX3107_BRG13_IB4800 (0x000800 | 0x00) -#define MAX3107_BRG13_IB9600 (0x000400 | 0x00) -#define MAX3107_BRG13_IB19200 (0x000200 | 0x00) -#define MAX3107_BRG13_IB38400 (0x000100 | 0x00) -#define MAX3107_BRG13_IB57600 (0x000000 | 0x0B) -#define MAX3107_BRG13_IB115200 (0x000000 | 0x05) -#define MAX3107_BRG13_IB230400 (0x000000 | 0x03) -#define MAX3107_BRG13_IB460800 (0x000000 | 0x00) -#define MAX3107_BRG13_IB921600 (0x000000 | 0x00) - - -struct baud_table { - int baud; - u32 new_brg; -}; - -struct max3107_port { - /* UART port structure */ - struct uart_port port; - - /* SPI device structure */ - struct spi_device *spi; - -#if defined(CONFIG_GPIOLIB) - /* GPIO chip structure */ - struct gpio_chip chip; -#endif - - /* Workqueue that does all the magic */ - struct workqueue_struct *workqueue; - struct work_struct work; - - /* Lock for shared data */ - spinlock_t data_lock; - - /* Device configuration */ - int ext_clk; /* 1 if external clock used */ - int loopback; /* Current loopback mode state */ - int baud; /* Current baud rate */ - - /* State flags */ - int suspended; /* Indicates suspend mode */ - int tx_fifo_empty; /* Flag for TX FIFO state */ - int rx_enabled; /* Flag for receiver state */ - int tx_enabled; /* Flag for transmitter state */ - - u16 irqen_reg; /* Current IRQ enable register value */ - /* Shared data */ - u16 mode1_reg; /* Current mode1 register value*/ - int mode1_commit; /* Flag for setting new mode1 register value */ - u16 lcr_reg; /* Current LCR register value */ - int lcr_commit; /* Flag for setting new LCR register value */ - u32 brg_cfg; /* Current Baud rate generator config */ - int brg_commit; /* Flag for setting new baud rate generator - * config - */ - struct baud_table *baud_tbl; - int handle_irq; /* Indicates that IRQ should be handled */ - - /* Rx buffer and str*/ - u16 *rxbuf; - u8 *rxstr; - /* Tx buffer*/ - u16 *txbuf; - - struct max3107_plat *pdata; /* Platform data */ -}; - -/* Platform data structure */ -struct max3107_plat { - /* Loopback mode enable */ - int loopback; - /* External clock enable */ - int ext_clk; - /* Called during the register initialisation */ - void (*init)(struct max3107_port *s); - /* Called when the port is found and configured */ - int (*configure)(struct max3107_port *s); - /* HW suspend function */ - void (*hw_suspend) (struct max3107_port *s, int suspend); - /* Polling mode enable */ - int polled_mode; - /* Polling period if polling mode enabled */ - int poll_time; -}; - -extern int max3107_rw(struct max3107_port *s, u8 *tx, u8 *rx, int len); -extern void max3107_hw_susp(struct max3107_port *s, int suspend); -extern int max3107_probe(struct spi_device *spi, struct max3107_plat *pdata); -extern int max3107_remove(struct spi_device *spi); -extern int max3107_suspend(struct spi_device *spi, pm_message_t state); -extern int max3107_resume(struct spi_device *spi); - -#endif /* _LINUX_SERIAL_MAX3107_H */ diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c new file mode 100644 index 000000000000..534e44851b7f --- /dev/null +++ b/drivers/tty/serial/max310x.c @@ -0,0 +1,1259 @@ +/* + * Maxim (Dallas) MAX3107/8 serial driver + * + * Copyright (C) 2012 Alexander Shiyan + * + * Based on max3100.c, by Christian Pellegrin + * Based on max3110.c, by Feng Tang + * Based on max3107.c, by Aavamobile + * + * 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. + */ + +/* TODO: MAX3109 support (Dual) */ +/* TODO: MAX14830 support (Quad) */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX310X_MAJOR 204 +#define MAX310X_MINOR 209 + +/* MAX310X register definitions */ +#define MAX310X_RHR_REG (0x00) /* RX FIFO */ +#define MAX310X_THR_REG (0x00) /* TX FIFO */ +#define MAX310X_IRQEN_REG (0x01) /* IRQ enable */ +#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_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 */ +#define MAX310X_MODE1_REG (0x09) /* MODE1 */ +#define MAX310X_MODE2_REG (0x0a) /* MODE2 */ +#define MAX310X_LCR_REG (0x0b) /* LCR */ +#define MAX310X_RXTO_REG (0x0c) /* RX timeout */ +#define MAX310X_HDPIXDELAY_REG (0x0d) /* Auto transceiver delays */ +#define MAX310X_IRDA_REG (0x0e) /* IRDA settings */ +#define MAX310X_FLOWLVL_REG (0x0f) /* Flow control levels */ +#define MAX310X_FIFOTRIGLVL_REG (0x10) /* FIFO IRQ trigger levels */ +#define MAX310X_TXFIFOLVL_REG (0x11) /* TX FIFO level */ +#define MAX310X_RXFIFOLVL_REG (0x12) /* RX FIFO level */ +#define MAX310X_FLOWCTRL_REG (0x13) /* Flow control */ +#define MAX310X_XON1_REG (0x14) /* XON1 character */ +#define MAX310X_XON2_REG (0x15) /* XON2 character */ +#define MAX310X_XOFF1_REG (0x16) /* XOFF1 character */ +#define MAX310X_XOFF2_REG (0x17) /* XOFF2 character */ +#define MAX310X_GPIOCFG_REG (0x18) /* GPIO config */ +#define MAX310X_GPIODATA_REG (0x19) /* GPIO data */ +#define MAX310X_PLLCFG_REG (0x1a) /* PLL config */ +#define MAX310X_BRGCFG_REG (0x1b) /* Baud rate generator conf */ +#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 */ + +/* IRQ register bits */ +#define MAX310X_IRQ_LSR_BIT (1 << 0) /* LSR interrupt */ +#define MAX310X_IRQ_SPCHR_BIT (1 << 1) /* Special char interrupt */ +#define MAX310X_IRQ_STS_BIT (1 << 2) /* Status interrupt */ +#define MAX310X_IRQ_RXFIFO_BIT (1 << 3) /* RX FIFO interrupt */ +#define MAX310X_IRQ_TXFIFO_BIT (1 << 4) /* TX FIFO interrupt */ +#define MAX310X_IRQ_TXEMPTY_BIT (1 << 5) /* TX FIFO empty interrupt */ +#define MAX310X_IRQ_RXEMPTY_BIT (1 << 6) /* RX FIFO empty interrupt */ +#define MAX310X_IRQ_CTS_BIT (1 << 7) /* CTS interrupt */ + +/* LSR register bits */ +#define MAX310X_LSR_RXTO_BIT (1 << 0) /* RX timeout */ +#define MAX310X_LSR_RXOVR_BIT (1 << 1) /* RX overrun */ +#define MAX310X_LSR_RXPAR_BIT (1 << 2) /* RX parity error */ +#define MAX310X_LSR_FRERR_BIT (1 << 3) /* Frame error */ +#define MAX310X_LSR_RXBRK_BIT (1 << 4) /* RX break */ +#define MAX310X_LSR_RXNOISE_BIT (1 << 5) /* RX noise */ +#define MAX310X_LSR_CTS_BIT (1 << 7) /* CTS pin state */ + +/* Special character register bits */ +#define MAX310X_SPCHR_XON1_BIT (1 << 0) /* XON1 character */ +#define MAX310X_SPCHR_XON2_BIT (1 << 1) /* XON2 character */ +#define MAX310X_SPCHR_XOFF1_BIT (1 << 2) /* XOFF1 character */ +#define MAX310X_SPCHR_XOFF2_BIT (1 << 3) /* XOFF2 character */ +#define MAX310X_SPCHR_BREAK_BIT (1 << 4) /* RX break */ +#define MAX310X_SPCHR_MULTIDROP_BIT (1 << 5) /* 9-bit multidrop addr char */ + +/* Status register bits */ +#define MAX310X_STS_GPIO0_BIT (1 << 0) /* GPIO 0 interrupt */ +#define MAX310X_STS_GPIO1_BIT (1 << 1) /* GPIO 1 interrupt */ +#define MAX310X_STS_GPIO2_BIT (1 << 2) /* GPIO 2 interrupt */ +#define MAX310X_STS_GPIO3_BIT (1 << 3) /* GPIO 3 interrupt */ +#define MAX310X_STS_CLKREADY_BIT (1 << 5) /* Clock ready */ +#define MAX310X_STS_SLEEP_BIT (1 << 6) /* Sleep interrupt */ + +/* MODE1 register bits */ +#define MAX310X_MODE1_RXDIS_BIT (1 << 0) /* RX disable */ +#define MAX310X_MODE1_TXDIS_BIT (1 << 1) /* TX disable */ +#define MAX310X_MODE1_TXHIZ_BIT (1 << 2) /* TX pin three-state */ +#define MAX310X_MODE1_RTSHIZ_BIT (1 << 3) /* RTS pin three-state */ +#define MAX310X_MODE1_TRNSCVCTRL_BIT (1 << 4) /* Transceiver ctrl enable */ +#define MAX310X_MODE1_FORCESLEEP_BIT (1 << 5) /* Force sleep mode */ +#define MAX310X_MODE1_AUTOSLEEP_BIT (1 << 6) /* Auto sleep enable */ +#define MAX310X_MODE1_IRQSEL_BIT (1 << 7) /* IRQ pin enable */ + +/* MODE2 register bits */ +#define MAX310X_MODE2_RST_BIT (1 << 0) /* Chip reset */ +#define MAX310X_MODE2_FIFORST_BIT (1 << 1) /* FIFO reset */ +#define MAX310X_MODE2_RXTRIGINV_BIT (1 << 2) /* RX FIFO INT invert */ +#define MAX310X_MODE2_RXEMPTINV_BIT (1 << 3) /* RX FIFO empty INT invert */ +#define MAX310X_MODE2_SPCHR_BIT (1 << 4) /* Special chr detect enable */ +#define MAX310X_MODE2_LOOPBACK_BIT (1 << 5) /* Internal loopback enable */ +#define MAX310X_MODE2_MULTIDROP_BIT (1 << 6) /* 9-bit multidrop enable */ +#define MAX310X_MODE2_ECHOSUPR_BIT (1 << 7) /* ECHO suppression enable */ + +/* LCR register bits */ +#define MAX310X_LCR_LENGTH0_BIT (1 << 0) /* Word length bit 0 */ +#define MAX310X_LCR_LENGTH1_BIT (1 << 1) /* Word length bit 1 + * + * Word length bits table: + * 00 -> 5 bit words + * 01 -> 6 bit words + * 10 -> 7 bit words + * 11 -> 8 bit words + */ +#define MAX310X_LCR_STOPLEN_BIT (1 << 2) /* STOP length bit + * + * STOP length bit table: + * 0 -> 1 stop bit + * 1 -> 1-1.5 stop bits if + * word length is 5, + * 2 stop bits otherwise + */ +#define MAX310X_LCR_PARITY_BIT (1 << 3) /* Parity bit enable */ +#define MAX310X_LCR_EVENPARITY_BIT (1 << 4) /* Even parity bit enable */ +#define MAX310X_LCR_FORCEPARITY_BIT (1 << 5) /* 9-bit multidrop parity */ +#define MAX310X_LCR_TXBREAK_BIT (1 << 6) /* TX break enable */ +#define MAX310X_LCR_RTS_BIT (1 << 7) /* RTS pin control */ +#define MAX310X_LCR_WORD_LEN_5 (0x00) +#define MAX310X_LCR_WORD_LEN_6 (0x01) +#define MAX310X_LCR_WORD_LEN_7 (0x02) +#define MAX310X_LCR_WORD_LEN_8 (0x03) + +/* IRDA register bits */ +#define MAX310X_IRDA_IRDAEN_BIT (1 << 0) /* IRDA mode enable */ +#define MAX310X_IRDA_SIR_BIT (1 << 1) /* SIR mode enable */ +#define MAX310X_IRDA_SHORTIR_BIT (1 << 2) /* Short SIR mode enable */ +#define MAX310X_IRDA_MIR_BIT (1 << 3) /* MIR mode enable */ +#define MAX310X_IRDA_RXINV_BIT (1 << 4) /* RX logic inversion enable */ +#define MAX310X_IRDA_TXINV_BIT (1 << 5) /* TX logic inversion enable */ + +/* Flow control trigger level register masks */ +#define MAX310X_FLOWLVL_HALT_MASK (0x000f) /* Flow control halt level */ +#define MAX310X_FLOWLVL_RES_MASK (0x00f0) /* Flow control resume level */ +#define MAX310X_FLOWLVL_HALT(words) ((words / 8) & 0x0f) +#define MAX310X_FLOWLVL_RES(words) (((words / 8) & 0x0f) << 4) + +/* FIFO interrupt trigger level register masks */ +#define MAX310X_FIFOTRIGLVL_TX_MASK (0x0f) /* TX FIFO trigger level */ +#define MAX310X_FIFOTRIGLVL_RX_MASK (0xf0) /* RX FIFO trigger level */ +#define MAX310X_FIFOTRIGLVL_TX(words) ((words / 8) & 0x0f) +#define MAX310X_FIFOTRIGLVL_RX(words) (((words / 8) & 0x0f) << 4) + +/* Flow control register bits */ +#define MAX310X_FLOWCTRL_AUTORTS_BIT (1 << 0) /* Auto RTS flow ctrl enable */ +#define MAX310X_FLOWCTRL_AUTOCTS_BIT (1 << 1) /* Auto CTS flow ctrl enable */ +#define MAX310X_FLOWCTRL_GPIADDR_BIT (1 << 2) /* Enables that GPIO inputs + * are used in conjunction with + * XOFF2 for definition of + * special character */ +#define MAX310X_FLOWCTRL_SWFLOWEN_BIT (1 << 3) /* Auto SW flow ctrl enable */ +#define MAX310X_FLOWCTRL_SWFLOW0_BIT (1 << 4) /* SWFLOW bit 0 */ +#define MAX310X_FLOWCTRL_SWFLOW1_BIT (1 << 5) /* SWFLOW bit 1 + * + * SWFLOW bits 1 & 0 table: + * 00 -> no transmitter flow + * control + * 01 -> receiver compares + * XON2 and XOFF2 + * and controls + * transmitter + * 10 -> receiver compares + * XON1 and XOFF1 + * and controls + * transmitter + * 11 -> receiver compares + * XON1, XON2, XOFF1 and + * XOFF2 and controls + * transmitter + */ +#define MAX310X_FLOWCTRL_SWFLOW2_BIT (1 << 6) /* SWFLOW bit 2 */ +#define MAX310X_FLOWCTRL_SWFLOW3_BIT (1 << 7) /* SWFLOW bit 3 + * + * SWFLOW bits 3 & 2 table: + * 00 -> no received flow + * control + * 01 -> transmitter generates + * XON2 and XOFF2 + * 10 -> transmitter generates + * XON1 and XOFF1 + * 11 -> transmitter generates + * XON1, XON2, XOFF1 and + * XOFF2 + */ + +/* GPIO configuration register bits */ +#define MAX310X_GPIOCFG_GP0OUT_BIT (1 << 0) /* GPIO 0 output enable */ +#define MAX310X_GPIOCFG_GP1OUT_BIT (1 << 1) /* GPIO 1 output enable */ +#define MAX310X_GPIOCFG_GP2OUT_BIT (1 << 2) /* GPIO 2 output enable */ +#define MAX310X_GPIOCFG_GP3OUT_BIT (1 << 3) /* GPIO 3 output enable */ +#define MAX310X_GPIOCFG_GP0OD_BIT (1 << 4) /* GPIO 0 open-drain enable */ +#define MAX310X_GPIOCFG_GP1OD_BIT (1 << 5) /* GPIO 1 open-drain enable */ +#define MAX310X_GPIOCFG_GP2OD_BIT (1 << 6) /* GPIO 2 open-drain enable */ +#define MAX310X_GPIOCFG_GP3OD_BIT (1 << 7) /* GPIO 3 open-drain enable */ + +/* GPIO DATA register bits */ +#define MAX310X_GPIODATA_GP0OUT_BIT (1 << 0) /* GPIO 0 output value */ +#define MAX310X_GPIODATA_GP1OUT_BIT (1 << 1) /* GPIO 1 output value */ +#define MAX310X_GPIODATA_GP2OUT_BIT (1 << 2) /* GPIO 2 output value */ +#define MAX310X_GPIODATA_GP3OUT_BIT (1 << 3) /* GPIO 3 output value */ +#define MAX310X_GPIODATA_GP0IN_BIT (1 << 4) /* GPIO 0 input value */ +#define MAX310X_GPIODATA_GP1IN_BIT (1 << 5) /* GPIO 1 input value */ +#define MAX310X_GPIODATA_GP2IN_BIT (1 << 6) /* GPIO 2 input value */ +#define MAX310X_GPIODATA_GP3IN_BIT (1 << 7) /* GPIO 3 input value */ + +/* PLL configuration register masks */ +#define MAX310X_PLLCFG_PREDIV_MASK (0x3f) /* PLL predivision value */ +#define MAX310X_PLLCFG_PLLFACTOR_MASK (0xc0) /* PLL multiplication factor */ + +/* Baud rate generator configuration register bits */ +#define MAX310X_BRGCFG_2XMODE_BIT (1 << 4) /* Double baud rate */ +#define MAX310X_BRGCFG_4XMODE_BIT (1 << 5) /* Quadruple baud rate */ + +/* Clock source register bits */ +#define MAX310X_CLKSRC_CRYST_BIT (1 << 1) /* Crystal osc enable */ +#define MAX310X_CLKSRC_PLL_BIT (1 << 2) /* PLL enable */ +#define MAX310X_CLKSRC_PLLBYP_BIT (1 << 3) /* PLL bypass */ +#define MAX310X_CLKSRC_EXTCLK_BIT (1 << 4) /* External clock enable */ +#define MAX310X_CLKSRC_CLK2RTS_BIT (1 << 7) /* Baud clk to RTS pin */ + +/* Misc definitions */ +#define MAX310X_FIFO_SIZE (128) + +/* 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_port { + struct uart_driver uart; + struct uart_port port; + + const char *name; + int uartclk; + + unsigned int nr_gpio; +#ifdef CONFIG_GPIOLIB + struct gpio_chip gpio; +#endif + + struct regmap *regmap; + struct regmap_config regcfg; + + struct workqueue_struct *wq; + struct work_struct tx_work; + + struct mutex max310x_mutex; + + struct max310x_pdata *pdata; +}; + +static bool max3107_8_reg_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + 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; + } + + return true; +} + +static bool max310x_reg_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX310X_RHR_REG: + 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 MAX310X_GPIODATA_REG: + return true; + default: + break; + } + + return false; +} + +static bool max310x_reg_precious(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX310X_RHR_REG: + case MAX310X_IRQSTS_REG: + case MAX310X_SPCHR_IRQSTS_REG: + case MAX310X_STS_IRQSTS_REG: + return true; + default: + break; + } + + return false; +} + +static void max310x_set_baud(struct max310x_port *s, int baud) +{ + unsigned int mode = 0, div = s->uartclk / baud; + + if (!(div / 16)) { + /* Mode x2 */ + mode = MAX310X_BRGCFG_2XMODE_BIT; + div = (s->uartclk * 2) / baud; + } + + if (!(div / 16)) { + /* Mode x4 */ + mode = MAX310X_BRGCFG_4XMODE_BIT; + div = (s->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; + } + } +} + +static int __devinit max310x_update_best_err(unsigned long f, long *besterr) +{ + /* Use baudrate 115200 for calculate error */ + long err = f % (115200 * 16); + + if ((*besterr < 0) || (*besterr > err)) { + *besterr = err; + return 0; + } + + return 1; +} + +static int __devinit max310x_set_ref_clk(struct max310x_port *s) +{ + unsigned int div, clksrc, pllcfg = 0; + long besterr = -1; + unsigned long fdiv, fmul, bestfreq = s->pdata->frequency; + + /* First, update error without PLL */ + max310x_update_best_err(s->pdata->frequency, &besterr); + + /* Try all possible PLL dividers */ + for (div = 1; (div <= 63) && besterr; div++) { + fdiv = DIV_ROUND_CLOSEST(s->pdata->frequency, div); + + /* Try multiplier 6 */ + fmul = fdiv * 6; + if ((fdiv >= 500000) && (fdiv <= 800000)) + if (!max310x_update_best_err(fmul, &besterr)) { + pllcfg = (0 << 6) | div; + bestfreq = fmul; + } + /* Try multiplier 48 */ + fmul = fdiv * 48; + if ((fdiv >= 850000) && (fdiv <= 1200000)) + if (!max310x_update_best_err(fmul, &besterr)) { + pllcfg = (1 << 6) | div; + bestfreq = fmul; + } + /* Try multiplier 96 */ + fmul = fdiv * 96; + if ((fdiv >= 425000) && (fdiv <= 1000000)) + if (!max310x_update_best_err(fmul, &besterr)) { + pllcfg = (2 << 6) | div; + bestfreq = fmul; + } + /* Try multiplier 144 */ + fmul = fdiv * 144; + if ((fdiv >= 390000) && (fdiv <= 667000)) + if (!max310x_update_best_err(fmul, &besterr)) { + pllcfg = (3 << 6) | div; + bestfreq = fmul; + } + } + + /* Configure clock source */ + if (s->pdata->driver_flags & MAX310X_EXT_CLK) + clksrc = MAX310X_CLKSRC_EXTCLK_BIT; + else + clksrc = MAX310X_CLKSRC_CRYST_BIT; + + /* Configure PLL */ + if (pllcfg) { + clksrc |= MAX310X_CLKSRC_PLL_BIT; + regmap_write(s->regmap, MAX310X_PLLCFG_REG, pllcfg); + } else + clksrc |= MAX310X_CLKSRC_PLLBYP_BIT; + + 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); + + return (int)bestfreq; +} + +static void max310x_handle_rx(struct max310x_port *s, unsigned int rxlen) +{ + unsigned int sts = 0, ch = 0, flag; + struct tty_struct *tty = tty_port_tty_get(&s->port.state->port); + + if (!tty) + return; + + if (unlikely(rxlen >= MAX310X_FIFO_SIZE)) { + dev_warn(s->port.dev, "Possible RX FIFO overrun %d\n", rxlen); + /* Ensure sanity of RX level */ + rxlen = MAX310X_FIFO_SIZE; + } + + 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); + + sts &= MAX310X_LSR_RXPAR_BIT | MAX310X_LSR_FRERR_BIT | + MAX310X_LSR_RXOVR_BIT | MAX310X_LSR_RXBRK_BIT; + + s->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)) + continue; + } else if (sts & MAX310X_LSR_RXPAR_BIT) + s->port.icount.parity++; + else if (sts & MAX310X_LSR_FRERR_BIT) + s->port.icount.frame++; + else if (sts & MAX310X_LSR_RXOVR_BIT) + s->port.icount.overrun++; + + sts &= s->port.read_status_mask; + if (sts & MAX310X_LSR_RXBRK_BIT) + flag = TTY_BREAK; + else if (sts & MAX310X_LSR_RXPAR_BIT) + flag = TTY_PARITY; + else if (sts & MAX310X_LSR_FRERR_BIT) + flag = TTY_FRAME; + else if (sts & MAX310X_LSR_RXOVR_BIT) + flag = TTY_OVERRUN; + } + + if (uart_handle_sysrq_char(s->port, ch)) + continue; + + if (sts & s->port.ignore_status_mask) + continue; + + uart_insert_char(&s->port, sts, MAX310X_LSR_RXOVR_BIT, + ch, flag); + } + + tty_flip_buffer_push(tty); + + tty_kref_put(tty); +} + +static void max310x_handle_tx(struct max310x_port *s) +{ + struct circ_buf *xmit = &s->port.state->xmit; + unsigned int txlen = 0, 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; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(&s->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; + 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; + while (to_send--) { + regmap_write(s->regmap, 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); +} + +static irqreturn_t max310x_ist(int irq, void *dev_id) +{ + struct max310x_port *s = (struct max310x_port *)dev_id; + unsigned int ists = 0, lsr = 0, rxlen = 0; + + mutex_lock(&s->max310x_mutex); + + 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) + 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, + !!(lsr & MAX310X_LSR_CTS_BIT)); + } + + mutex_unlock(&s->max310x_mutex); + + return IRQ_HANDLED; +} + +static void max310x_wq_proc(struct work_struct *ws) +{ + struct max310x_port *s = container_of(ws, struct max310x_port, tx_work); + + mutex_lock(&s->max310x_mutex); + max310x_handle_tx(s); + mutex_unlock(&s->max310x_mutex); +} + +static void max310x_start_tx(struct uart_port *port) +{ + struct max310x_port *s = container_of(port, struct max310x_port, 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 */ +} + +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); + + return val ? 0 : TIOCSER_TEMT; +} + +static void max310x_enable_ms(struct uart_port *port) +{ + /* Modem status not supported */ +} + +static unsigned int max310x_get_mctrl(struct uart_port *port) +{ + /* DCD and DSR are not wired and CTS/RTS is handled automatically + * so just indicate DSR and CAR asserted + */ + return TIOCM_DSR | TIOCM_CAR; +} + +static void max310x_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* DCD and DSR are not wired and CTS/RTS is hadnled automatically + * so do nothing + */ +} + +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); +} + +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) { + case CS5: + lcr = MAX310X_LCR_WORD_LEN_5; + break; + case CS6: + lcr = MAX310X_LCR_WORD_LEN_6; + break; + case CS7: + lcr = MAX310X_LCR_WORD_LEN_7; + break; + case CS8: + default: + lcr = MAX310X_LCR_WORD_LEN_8; + break; + } + + /* Parity */ + if (termios->c_cflag & PARENB) { + lcr |= MAX310X_LCR_PARITY_BIT; + if (!(termios->c_cflag & PARODD)) + lcr |= MAX310X_LCR_EVENPARITY_BIT; + } + + /* Stop bits */ + if (termios->c_cflag & CSTOPB) + lcr |= MAX310X_LCR_STOPLEN_BIT; /* 2 stops */ + + /* Update LCR register */ + regmap_write(s->regmap, MAX310X_LCR_REG, lcr); + + /* Set read status mask */ + port->read_status_mask = MAX310X_LSR_RXOVR_BIT; + if (termios->c_iflag & INPCK) + port->read_status_mask |= MAX310X_LSR_RXPAR_BIT | + MAX310X_LSR_FRERR_BIT; + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= MAX310X_LSR_RXBRK_BIT; + + /* Set status ignore mask */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNBRK) + port->ignore_status_mask |= MAX310X_LSR_RXBRK_BIT; + if (!(termios->c_cflag & CREAD)) + port->ignore_status_mask |= MAX310X_LSR_RXPAR_BIT | + MAX310X_LSR_RXOVR_BIT | + MAX310X_LSR_FRERR_BIT | + 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]); + if (termios->c_cflag & CRTSCTS) + flow |= MAX310X_FLOWCTRL_AUTOCTS_BIT | + MAX310X_FLOWCTRL_AUTORTS_BIT; + if (termios->c_iflag & IXON) + flow |= MAX310X_FLOWCTRL_SWFLOW3_BIT | + MAX310X_FLOWCTRL_SWFLOWEN_BIT; + if (termios->c_iflag & IXOFF) + flow |= MAX310X_FLOWCTRL_SWFLOW1_BIT | + MAX310X_FLOWCTRL_SWFLOWEN_BIT; + regmap_write(s->regmap, MAX310X_FLOWCTRL_REG, flow); + + /* Get baud rate generator configuration */ + baud = uart_get_baud_rate(port, termios, old, + port->uartclk / 16 / 0xffff, + port->uartclk / 4); + + /* Setup baudrate generator */ + max310x_set_baud(s, 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); + + if (s->pdata->suspend) + s->pdata->suspend(0); + + mutex_lock(&s->max310x_mutex); + + /* Configure baud rate, 9600 as default */ + max310x_set_baud(s, 9600); + + /* Configure LCR register, 8N1 mode by default */ + val = MAX310X_LCR_WORD_LEN_8; + regmap_write(s->regmap, MAX310X_LCR_REG, val); + + /* 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); + + /* Configure MODE2 register */ + val = MAX310X_MODE2_RXEMPTINV_BIT; + if (s->pdata->uart_flags[line] & MAX310X_LOOPBACK) + val |= MAX310X_MODE2_LOOPBACK_BIT; + if (s->pdata->uart_flags[line] & MAX310X_ECHO_SUPRESS) + val |= MAX310X_MODE2_ECHOSUPR_BIT; + + /* 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); + + /* 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); + + /* Clear FIFO reset */ + regmap_update_bits(s->regmap, MAX310X_MODE2_REG, + MAX310X_MODE2_FIFORST_BIT, 0); + + /* 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); + + return 0; +} + +static void max310x_shutdown(struct uart_port *port) +{ + struct max310x_port *s = container_of(port, struct max310x_port, port); + + /* Disable all interrupts */ + mutex_lock(&s->max310x_mutex); + regmap_write(s->regmap, MAX310X_IRQEN_REG, 0); + mutex_unlock(&s->max310x_mutex); + + if (s->pdata->suspend) + s->pdata->suspend(1); +} + +static const char *max310x_type(struct uart_port *port) +{ + struct max310x_port *s = container_of(port, struct max310x_port, port); + + return (port->type == PORT_MAX310X) ? s->name : NULL; +} + +static int max310x_request_port(struct uart_port *port) +{ + /* Do nothing */ + 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) +{ + if ((ser->type == PORT_UNKNOWN) || (ser->type == PORT_MAX310X)) + return 0; + if (ser->irq == port->irq) + return 0; + + return -EINVAL; +} + +static 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, + .start_tx = max310x_start_tx, + .stop_rx = max310x_stop_rx, + .enable_ms = max310x_enable_ms, + .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, + .config_port = max310x_config_port, + .verify_port = max310x_verify_port, +}; + +static int max310x_suspend(struct spi_device *spi, pm_message_t state) +{ + int ret; + struct max310x_port *s = dev_get_drvdata(&spi->dev); + + dev_dbg(&spi->dev, "Suspend\n"); + + 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); + + return ret; +} + +static int max310x_resume(struct spi_device *spi) +{ + struct max310x_port *s = dev_get_drvdata(&spi->dev); + + dev_dbg(&spi->dev, "Resume\n"); + + if (s->pdata->suspend) + s->pdata->suspend(0); + + mutex_lock(&s->max310x_mutex); + + /* 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); + + return uart_resume_port(&s->uart, &s->port); +} + +#ifdef CONFIG_GPIOLIB +static int max310x_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + unsigned int val = 0; + struct max310x_port *s = container_of(chip, struct max310x_port, gpio); + + mutex_lock(&s->max310x_mutex); + regmap_read(s->regmap, MAX310X_GPIODATA_REG, &val); + mutex_unlock(&s->max310x_mutex); + + return !!((val >> 4) & (1 << offset)); +} + +static void max310x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct max310x_port *s = container_of(chip, struct max310x_port, gpio); + + mutex_lock(&s->max310x_mutex); + regmap_update_bits(s->regmap, MAX310X_GPIODATA_REG, 1 << offset, value ? + 1 << offset : 0); + mutex_unlock(&s->max310x_mutex); +} + +static int max310x_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct max310x_port *s = container_of(chip, struct max310x_port, gpio); + + mutex_lock(&s->max310x_mutex); + + regmap_update_bits(s->regmap, MAX310X_GPIOCFG_REG, 1 << offset, 0); + + mutex_unlock(&s->max310x_mutex); + + return 0; +} + +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); + + 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); + + 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 __devinit max310x_probe(struct spi_device *spi) +{ + 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; + + /* Check for IRQ */ + if (spi->irq <= 0) { + dev_err(dev, "No IRQ specified\n"); + return -ENOTSUPP; + } + + /* Alloc port structure */ + s = devm_kzalloc(dev, sizeof(struct max310x_port), 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) && + ((pdata->frequency < 500000) || (pdata->frequency > 35000000))) + goto err_freq; + /* Check frequency for quartz */ + if (!(pdata->driver_flags & MAX310X_EXT_CLK) && + ((pdata->frequency < 1000000) || (pdata->frequency > 4000000))) + goto err_freq; + + mutex_init(&s->max310x_mutex); + + /* Setup SPI bus */ + spi->mode = SPI_MODE_0; + spi->bits_per_word = 8; + spi->max_speed_hz = 26000000; + spi_setup(spi); + + /* Setup regmap */ + s->regcfg.reg_bits = 8; + s->regcfg.val_bits = 8; + 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.volatile_reg = max310x_reg_volatile; + s->regcfg.precious_reg = max310x_reg_precious; + s->regmap = devm_regmap_init_spi(spi, &s->regcfg); + 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; + } + + /* 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; + } + + /* 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; + ret = uart_register_driver(&s->uart); + if (ret) { + dev_err(dev, "Registering UART driver failed\n"); + goto err_out; + } + + /* 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); + +#ifdef CONFIG_GPIOLIB + /* Setup GPIO cotroller */ + if (pdata->gpio_base) { + s->gpio.owner = THIS_MODULE; + s->gpio.dev = dev; + s->gpio.label = dev_name(dev); + s->gpio.direction_input = max310x_gpio_direction_input; + 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; + if (gpiochip_add(&s->gpio)) { + /* Indicate that we should not call gpiochip_remove */ + s->gpio.base = 0; + } + } else + dev_info(dev, "GPIO support not enabled\n"); +#endif + + /* Go to suspend mode */ + if (pdata->suspend) + pdata->suspend(1); + + return 0; + +err_freq: + dev_err(dev, "Frequency parameter incorrect\n"); + ret = -EINVAL; + +err_out: + dev_set_drvdata(dev, NULL); + devm_kfree(dev, s); + + return ret; +} + +static int __devexit max310x_remove(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct max310x_port *s = dev_get_drvdata(dev); + + dev_dbg(dev, "Removing port\n"); + + devm_free_irq(dev, s->port.irq, s); + + destroy_workqueue(s->wq); + + uart_remove_one_port(&s->uart, &s->port); + + uart_unregister_driver(&s->uart); + +#ifdef CONFIG_GPIOLIB + if (s->pdata->gpio_base) + gpiochip_remove(&s->gpio); +#endif + + dev_set_drvdata(dev, NULL); + + if (s->pdata->suspend) + s->pdata->suspend(1); + if (s->pdata->exit) + s->pdata->exit(); + + devm_kfree(dev, s); + + return 0; +} + +static const struct spi_device_id max310x_id_table[] = { + { "max3107", MAX310X_TYPE_MAX3107 }, + { "max3108", MAX310X_TYPE_MAX3108 }, +}; +MODULE_DEVICE_TABLE(spi, max310x_id_table); + +static struct spi_driver max310x_driver = { + .driver = { + .name = "max310x", + .owner = THIS_MODULE, + }, + .probe = max310x_probe, + .remove = __devexit_p(max310x_remove), + .suspend = max310x_suspend, + .resume = max310x_resume, + .id_table = max310x_id_table, +}; +module_spi_driver(max310x_driver); + +MODULE_LICENSE("GPL v2"); +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 new file mode 100644 index 000000000000..91648bf5fc5c --- /dev/null +++ b/include/linux/platform_data/max310x.h @@ -0,0 +1,67 @@ +/* + * Maxim (Dallas) MAX3107/8 serial driver + * + * Copyright (C) 2012 Alexander Shiyan + * + * Based on max3100.c, by Christian Pellegrin + * Based on max3110.c, by Feng Tang + * Based on max3107.c, by Aavamobile + * + * 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 _MAX310X_H_ +#define _MAX310X_H_ + +/* + * Example board initialization data: + * + * static struct max310x_pdata max3107_pdata = { + * .driver_flags = MAX310X_EXT_CLK, + * .uart_flags[0] = MAX310X_ECHO_SUPRESS | MAX310X_AUTO_DIR_CTRL, + * .frequency = 3686400, + * .gpio_base = -1, + * }; + * + * static struct spi_board_info spi_device_max3107[] = { + * { + * .modalias = "max3107", + * .irq = IRQ_EINT3, + * .bus_num = 1, + * .chip_select = 1, + * .platform_data = &max3107_pdata, + * }, + * }; + */ + +#define MAX310X_MAX_UARTS 1 + +/* MAX310X platform data structure */ +struct max310x_pdata { + /* Flags global to driver */ + const u8 driver_flags:2; +#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 */ +#define MAX310X_ECHO_SUPRESS (0x00000002) /* Enable echo supress */ +#define MAX310X_AUTO_DIR_CTRL (0x00000004) /* Enable Auto direction + * control (RS-485) + */ + /* Frequency (extrenal clock or crystal) */ + const int frequency; + /* GPIO base number (can be negative) */ + const int gpio_base; + /* Called during startup */ + void (*init)(void); + /* Called before finish */ + void (*exit)(void); + /* Suspend callback */ + void (*suspend)(int do_suspend); +}; + +#endif diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 0253c2022e53..7cf0b68bbe9e 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -193,8 +193,8 @@ /* SH-SCI */ #define PORT_SCIFB 93 -/* MAX3107 */ -#define PORT_MAX3107 94 +/* MAX310X */ +#define PORT_MAX310X 94 /* High Speed UART for Medfield */ #define PORT_MFD 95 -- cgit v1.2.3 From 8857df3aceb7a8eb7558059b7da109e41dd1fb95 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Fri, 20 Jul 2012 09:31:00 +0100 Subject: iio: frequency: ADF4350: Fix potential reference div factor overflow. With small channel spacing values and high reference frequencies it is possible to exceed the range of the 10-bit counter. Workaround by checking the range and widening some constrains. We don't use the REG1_PHASE value in this case the datasheet recommends to set it to 1 if not used. Signed-off-by: Michael Hennerich Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/adf4350.c | 24 +++++++++++++++--------- include/linux/iio/frequency/adf4350.h | 2 ++ 2 files changed, 17 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index 59fbb3ae40e7..e35bb8f6fe75 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -129,7 +129,7 @@ static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq) { struct adf4350_platform_data *pdata = st->pdata; u64 tmp; - u32 div_gcd, prescaler; + u32 div_gcd, prescaler, chspc; u16 mdiv, r_cnt = 0; u8 band_sel_div; @@ -158,14 +158,20 @@ static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq) if (pdata->ref_div_factor) r_cnt = pdata->ref_div_factor - 1; - do { - r_cnt = adf4350_tune_r_cnt(st, r_cnt); + chspc = st->chspc; - st->r1_mod = st->fpfd / st->chspc; - while (st->r1_mod > ADF4350_MAX_MODULUS) { - r_cnt = adf4350_tune_r_cnt(st, r_cnt); - st->r1_mod = st->fpfd / st->chspc; - } + do { + do { + do { + r_cnt = adf4350_tune_r_cnt(st, r_cnt); + st->r1_mod = st->fpfd / chspc; + if (r_cnt > ADF4350_MAX_R_CNT) { + /* try higher spacing values */ + chspc++; + r_cnt = 0; + } + } while ((st->r1_mod > ADF4350_MAX_MODULUS) && r_cnt); + } while (r_cnt == 0); tmp = freq * (u64)st->r1_mod + (st->fpfd > 1); do_div(tmp, st->fpfd); /* Div round closest (n + d/2)/d */ @@ -194,7 +200,7 @@ static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq) st->regs[ADF4350_REG0] = ADF4350_REG0_INT(st->r0_int) | ADF4350_REG0_FRACT(st->r0_fract); - st->regs[ADF4350_REG1] = ADF4350_REG1_PHASE(0) | + st->regs[ADF4350_REG1] = ADF4350_REG1_PHASE(1) | ADF4350_REG1_MOD(st->r1_mod) | prescaler; diff --git a/include/linux/iio/frequency/adf4350.h b/include/linux/iio/frequency/adf4350.h index b76b4a87065e..be91f344d5fc 100644 --- a/include/linux/iio/frequency/adf4350.h +++ b/include/linux/iio/frequency/adf4350.h @@ -87,6 +87,8 @@ #define ADF4350_MAX_BANDSEL_CLK 125000 /* Hz */ #define ADF4350_MAX_FREQ_REFIN 250000000 /* Hz */ #define ADF4350_MAX_MODULUS 4095 +#define ADF4350_MAX_R_CNT 1023 + /** * struct adf4350_platform_data - platform specific information -- cgit v1.2.3 From 1f2b79599ee8f5fc82cc73c6c090eb6cdff881d6 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sat, 4 Aug 2012 12:01:21 +0800 Subject: firmware loader: always let firmware_buf own the pages buffer This patch always let firmware_buf own the pages buffer allocated inside firmware_data_write, and add all instances of firmware_buf into the firmware cache global list. Also introduce one private field in 'struct firmware', so release_firmware will see the instance of firmware_buf associated with the current firmware instance, then just 'free' the instance of firmware_buf. The firmware_buf instance represents one pages buffer for one firmware image, so lots of firmware loading requests can share the same firmware_buf instance if they request the same firmware image file. This patch will make implementation of the following cache_firmware/ uncache_firmware very easy and simple. In fact, the patch improves request_formware/release_firmware: - only request userspace to write firmware image once if several devices share one same firmware image and its drivers call request_firmware concurrently. Signed-off-by: Ming Lei Cc: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 240 ++++++++++++++++++++++++++++++------------ include/linux/firmware.h | 3 + 2 files changed, 174 insertions(+), 69 deletions(-) (limited to 'include') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 5f2076e5d5b1..848ad97e8d79 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -21,6 +21,7 @@ #include #include #include +#include MODULE_AUTHOR("Manuel Estrada Sainz"); MODULE_DESCRIPTION("Multi purpose firmware loading support"); @@ -85,13 +86,17 @@ static inline long firmware_loading_timeout(void) return loading_timeout > 0 ? loading_timeout * HZ : MAX_SCHEDULE_TIMEOUT; } -/* fw_lock could be moved to 'struct firmware_priv' but since it is just - * guarding for corner cases a global lock should be OK */ -static DEFINE_MUTEX(fw_lock); +struct firmware_cache { + /* firmware_buf instance will be added into the below list */ + spinlock_t lock; + struct list_head head; +}; struct firmware_buf { + struct kref ref; + struct list_head list; struct completion completion; - struct firmware *fw; + struct firmware_cache *fwc; unsigned long status; void *data; size_t size; @@ -106,8 +111,94 @@ struct firmware_priv { bool nowait; struct device dev; struct firmware_buf *buf; + struct firmware *fw; }; +#define to_fwbuf(d) container_of(d, struct firmware_buf, ref) + +/* fw_lock could be moved to 'struct firmware_priv' but since it is just + * guarding for corner cases a global lock should be OK */ +static DEFINE_MUTEX(fw_lock); + +static struct firmware_cache fw_cache; + +static struct firmware_buf *__allocate_fw_buf(const char *fw_name, + struct firmware_cache *fwc) +{ + struct firmware_buf *buf; + + buf = kzalloc(sizeof(*buf) + strlen(fw_name) + 1 , GFP_ATOMIC); + + if (!buf) + return buf; + + kref_init(&buf->ref); + strcpy(buf->fw_id, fw_name); + buf->fwc = fwc; + init_completion(&buf->completion); + + pr_debug("%s: fw-%s buf=%p\n", __func__, fw_name, buf); + + return buf; +} + +static int fw_lookup_and_allocate_buf(const char *fw_name, + struct firmware_cache *fwc, + struct firmware_buf **buf) +{ + struct firmware_buf *tmp; + + spin_lock(&fwc->lock); + list_for_each_entry(tmp, &fwc->head, list) + if (!strcmp(tmp->fw_id, fw_name)) { + kref_get(&tmp->ref); + spin_unlock(&fwc->lock); + *buf = tmp; + return 1; + } + + tmp = __allocate_fw_buf(fw_name, fwc); + if (tmp) + list_add(&tmp->list, &fwc->head); + spin_unlock(&fwc->lock); + + *buf = tmp; + + return tmp ? 0 : -ENOMEM; +} + +static void __fw_free_buf(struct kref *ref) +{ + struct firmware_buf *buf = to_fwbuf(ref); + struct firmware_cache *fwc = buf->fwc; + int i; + + pr_debug("%s: fw-%s buf=%p data=%p size=%u\n", + __func__, buf->fw_id, buf, buf->data, + (unsigned int)buf->size); + + spin_lock(&fwc->lock); + list_del(&buf->list); + spin_unlock(&fwc->lock); + + vunmap(buf->data); + for (i = 0; i < buf->nr_pages; i++) + __free_page(buf->pages[i]); + kfree(buf->pages); + kfree(buf); +} + +static void fw_free_buf(struct firmware_buf *buf) +{ + kref_put(&buf->ref, __fw_free_buf); +} + +static void __init fw_cache_init(void) +{ + spin_lock_init(&fw_cache.lock); + INIT_LIST_HEAD(&fw_cache.head); +} + static struct firmware_priv *to_firmware_priv(struct device *dev) { return container_of(dev, struct firmware_priv, dev); @@ -118,7 +209,7 @@ static void fw_load_abort(struct firmware_priv *fw_priv) struct firmware_buf *buf = fw_priv->buf; set_bit(FW_STATUS_ABORT, &buf->status); - complete(&buf->completion); + complete_all(&buf->completion); } static ssize_t firmware_timeout_show(struct class *class, @@ -158,18 +249,6 @@ static struct class_attribute firmware_class_attrs[] = { __ATTR_NULL }; -static void fw_free_buf(struct firmware_buf *buf) -{ - int i; - - if (!buf) - return; - - for (i = 0; i < buf->nr_pages; i++) - __free_page(buf->pages[i]); - kfree(buf->pages); -} - static void fw_dev_release(struct device *dev) { struct firmware_priv *fw_priv = to_firmware_priv(dev); @@ -212,13 +291,8 @@ static ssize_t firmware_loading_show(struct device *dev, /* firmware holds the ownership of pages */ static void firmware_free_data(const struct firmware *fw) { - int i; - vunmap(fw->data); - if (fw->pages) { - for (i = 0; i < PFN_UP(fw->size); i++) - __free_page(fw->pages[i]); - kfree(fw->pages); - } + WARN_ON(!fw->priv); + fw_free_buf(fw->priv); } /* Some architectures don't have PAGE_KERNEL_RO */ @@ -269,7 +343,7 @@ static ssize_t firmware_loading_store(struct device *dev, if (test_bit(FW_STATUS_LOADING, &fw_buf->status)) { set_bit(FW_STATUS_DONE, &fw_buf->status); clear_bit(FW_STATUS_LOADING, &fw_buf->status); - complete(&fw_buf->completion); + complete_all(&fw_buf->completion); break; } /* fallthrough */ @@ -448,7 +522,6 @@ fw_create_instance(struct firmware *firmware, const char *fw_name, struct device *device, bool uevent, bool nowait) { struct firmware_priv *fw_priv; - struct firmware_buf *buf; struct device *f_dev; fw_priv = kzalloc(sizeof(*fw_priv), GFP_KERNEL); @@ -458,21 +531,10 @@ fw_create_instance(struct firmware *firmware, const char *fw_name, goto exit; } - buf = kzalloc(sizeof(*buf) + strlen(fw_name) + 1, GFP_KERNEL); - if (!buf) { - dev_err(device, "%s: kmalloc failed\n", __func__); - kfree(fw_priv); - fw_priv = ERR_PTR(-ENOMEM); - goto exit; - } - - buf->fw = firmware; - fw_priv->buf = buf; fw_priv->nowait = nowait; + fw_priv->fw = firmware; setup_timer(&fw_priv->timeout, firmware_class_timeout, (u_long) fw_priv); - strcpy(buf->fw_id, fw_name); - init_completion(&buf->completion); f_dev = &fw_priv->dev; @@ -484,12 +546,42 @@ exit: return fw_priv; } +/* one pages buffer is mapped/unmapped only once */ +static int fw_map_pages_buf(struct firmware_buf *buf) +{ + buf->data = vmap(buf->pages, buf->nr_pages, 0, PAGE_KERNEL_RO); + if (!buf->data) + return -ENOMEM; + return 0; +} + +/* store the pages buffer info firmware from buf */ +static void fw_set_page_data(struct firmware_buf *buf, struct firmware *fw) +{ + fw->priv = buf; + fw->pages = buf->pages; + fw->size = buf->size; + fw->data = buf->data; + + pr_debug("%s: fw-%s buf=%p data=%p size=%u\n", + __func__, buf->fw_id, buf, buf->data, + (unsigned int)buf->size); +} + +static void _request_firmware_cleanup(const struct firmware **firmware_p) +{ + release_firmware(*firmware_p); + *firmware_p = NULL; +} + static struct firmware_priv * _request_firmware_prepare(const struct firmware **firmware_p, const char *name, struct device *device, bool uevent, bool nowait) { struct firmware *firmware; - struct firmware_priv *fw_priv; + struct firmware_priv *fw_priv = NULL; + struct firmware_buf *buf; + int ret; if (!firmware_p) return ERR_PTR(-EINVAL); @@ -506,35 +598,45 @@ _request_firmware_prepare(const struct firmware **firmware_p, const char *name, return NULL; } - fw_priv = fw_create_instance(firmware, name, device, uevent, nowait); - if (IS_ERR(fw_priv)) { - release_firmware(firmware); + ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf); + if (!ret) + fw_priv = fw_create_instance(firmware, name, device, + uevent, nowait); + + if (IS_ERR(fw_priv) || ret < 0) { + kfree(firmware); *firmware_p = NULL; + return ERR_PTR(-ENOMEM); + } else if (fw_priv) { + fw_priv->buf = buf; + + /* + * bind with 'buf' now to avoid warning in failure path + * of requesting firmware. + */ + firmware->priv = buf; + return fw_priv; } - return fw_priv; -} - -static void _request_firmware_cleanup(const struct firmware **firmware_p) -{ - release_firmware(*firmware_p); - *firmware_p = NULL; -} -/* transfer the ownership of pages to firmware */ -static int fw_set_page_data(struct firmware_buf *buf) -{ - struct firmware *fw = buf->fw; - - buf->data = vmap(buf->pages, buf->nr_pages, 0, PAGE_KERNEL_RO); - if (!buf->data) - return -ENOMEM; - - fw->data = buf->data; - fw->pages = buf->pages; - fw->size = buf->size; - WARN_ON(PFN_UP(fw->size) != buf->nr_pages); + /* share the cached buf, which is inprogessing or completed */ + check_status: + mutex_lock(&fw_lock); + if (test_bit(FW_STATUS_ABORT, &buf->status)) { + fw_priv = ERR_PTR(-ENOENT); + _request_firmware_cleanup(firmware_p); + goto exit; + } else if (test_bit(FW_STATUS_DONE, &buf->status)) { + fw_priv = NULL; + fw_set_page_data(buf, firmware); + goto exit; + } + mutex_unlock(&fw_lock); + wait_for_completion(&buf->completion); + goto check_status; - return 0; +exit: + mutex_unlock(&fw_lock); + return fw_priv; } static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, @@ -585,13 +687,12 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, if (!buf->size || test_bit(FW_STATUS_ABORT, &buf->status)) retval = -ENOENT; - /* transfer pages ownership at the last minute */ if (!retval) - retval = fw_set_page_data(buf); - if (retval) - fw_free_buf(buf); /* free untransfered pages buffer */ + retval = fw_map_pages_buf(buf); + + /* pass the pages buffer to driver at the last minute */ + fw_set_page_data(buf, fw_priv->fw); - kfree(buf); fw_priv->buf = NULL; mutex_unlock(&fw_lock); @@ -753,6 +854,7 @@ request_firmware_nowait( static int __init firmware_class_init(void) { + fw_cache_init(); return class_register(&firmware_class); } diff --git a/include/linux/firmware.h b/include/linux/firmware.h index 1e7c01189fa6..e85b771f3d8c 100644 --- a/include/linux/firmware.h +++ b/include/linux/firmware.h @@ -12,6 +12,9 @@ struct firmware { size_t size; const u8 *data; struct page **pages; + + /* firmware loader private fields */ + void *priv; }; struct module; -- cgit v1.2.3 From 2887b3959c8b2f6ed1f62ce95c0888aedb1ea84b Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sat, 4 Aug 2012 12:01:22 +0800 Subject: firmware loader: introduce cache_firmware and uncache_firmware This patches introduce two kernel APIs of cache_firmware and uncache_firmware, both of which take the firmware file name as the only parameter. So any drivers can call cache_firmware to cache the specified firmware file into kernel memory, and can use the cached firmware in situations which can't request firmware from user space. Signed-off-by: Ming Lei Cc: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 100 ++++++++++++++++++++++++++++++++++++++---- include/linux/firmware.h | 12 +++++ 2 files changed, 104 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 848ad97e8d79..fc119ce6fdb8 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -142,6 +142,17 @@ static struct firmware_buf *__allocate_fw_buf(const char *fw_name, return buf; } +static struct firmware_buf *__fw_lookup_buf(const char *fw_name) +{ + struct firmware_buf *tmp; + struct firmware_cache *fwc = &fw_cache; + + list_for_each_entry(tmp, &fwc->head, list) + if (!strcmp(tmp->fw_id, fw_name)) + return tmp; + return NULL; +} + static int fw_lookup_and_allocate_buf(const char *fw_name, struct firmware_cache *fwc, struct firmware_buf **buf) @@ -149,14 +160,13 @@ static int fw_lookup_and_allocate_buf(const char *fw_name, struct firmware_buf *tmp; spin_lock(&fwc->lock); - list_for_each_entry(tmp, &fwc->head, list) - if (!strcmp(tmp->fw_id, fw_name)) { - kref_get(&tmp->ref); - spin_unlock(&fwc->lock); - *buf = tmp; - return 1; - } - + tmp = __fw_lookup_buf(fw_name); + if (tmp) { + kref_get(&tmp->ref); + spin_unlock(&fwc->lock); + *buf = tmp; + return 1; + } tmp = __allocate_fw_buf(fw_name, fwc); if (tmp) list_add(&tmp->list, &fwc->head); @@ -167,6 +177,18 @@ static int fw_lookup_and_allocate_buf(const char *fw_name, return tmp ? 0 : -ENOMEM; } +static struct firmware_buf *fw_lookup_buf(const char *fw_name) +{ + struct firmware_buf *tmp; + struct firmware_cache *fwc = &fw_cache; + + spin_lock(&fwc->lock); + tmp = __fw_lookup_buf(fw_name); + spin_unlock(&fwc->lock); + + return tmp; +} + static void __fw_free_buf(struct kref *ref) { struct firmware_buf *buf = to_fwbuf(ref); @@ -852,6 +874,66 @@ request_firmware_nowait( return 0; } +/** + * cache_firmware - cache one firmware image in kernel memory space + * @fw_name: the firmware image name + * + * Cache firmware in kernel memory so that drivers can use it when + * system isn't ready for them to request firmware image from userspace. + * Once it returns successfully, driver can use request_firmware or its + * nowait version to get the cached firmware without any interacting + * with userspace + * + * Return 0 if the firmware image has been cached successfully + * Return !0 otherwise + * + */ +int cache_firmware(const char *fw_name) +{ + int ret; + const struct firmware *fw; + + pr_debug("%s: %s\n", __func__, fw_name); + + ret = request_firmware(&fw, fw_name, NULL); + if (!ret) + kfree(fw); + + pr_debug("%s: %s ret=%d\n", __func__, fw_name, ret); + + return ret; +} + +/** + * uncache_firmware - remove one cached firmware image + * @fw_name: the firmware image name + * + * Uncache one firmware image which has been cached successfully + * before. + * + * Return 0 if the firmware cache has been removed successfully + * Return !0 otherwise + * + */ +int uncache_firmware(const char *fw_name) +{ + struct firmware_buf *buf; + struct firmware fw; + + pr_debug("%s: %s\n", __func__, fw_name); + + if (fw_get_builtin_firmware(&fw, fw_name)) + return 0; + + buf = fw_lookup_buf(fw_name); + if (buf) { + fw_free_buf(buf); + return 0; + } + + return -EINVAL; +} + static int __init firmware_class_init(void) { fw_cache_init(); @@ -869,3 +951,5 @@ module_exit(firmware_class_exit); EXPORT_SYMBOL(release_firmware); EXPORT_SYMBOL(request_firmware); EXPORT_SYMBOL(request_firmware_nowait); +EXPORT_SYMBOL_GPL(cache_firmware); +EXPORT_SYMBOL_GPL(uncache_firmware); diff --git a/include/linux/firmware.h b/include/linux/firmware.h index e85b771f3d8c..e4279fedb93a 100644 --- a/include/linux/firmware.h +++ b/include/linux/firmware.h @@ -47,6 +47,8 @@ int request_firmware_nowait( void (*cont)(const struct firmware *fw, void *context)); void release_firmware(const struct firmware *fw); +int cache_firmware(const char *name); +int uncache_firmware(const char *name); #else static inline int request_firmware(const struct firmware **fw, const char *name, @@ -65,6 +67,16 @@ static inline int request_firmware_nowait( static inline void release_firmware(const struct firmware *fw) { } + +static inline int cache_firmware(const char *name) +{ + return -ENOENT; +} + +static inline int uncache_firmware(const char *name) +{ + return -EINVAL; +} #endif #endif -- cgit v1.2.3 From bddb1b9078505bb0e430c87c5015c0963f7a594f Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sat, 4 Aug 2012 12:01:26 +0800 Subject: driver core: devres: introduce devres_for_each_res This patch introduces one devres API of devres_for_each_res so that the device's driver can iterate each resource it has interest in. The firmware loader will use the API to get each firmware name from the device instance. Signed-off-by: Ming Lei Cc: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/base/devres.c | 42 ++++++++++++++++++++++++++++++++++++++++++ include/linux/device.h | 4 ++++ 2 files changed, 46 insertions(+) (limited to 'include') diff --git a/drivers/base/devres.c b/drivers/base/devres.c index 2360adb7a58f..8731979d668a 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -143,6 +143,48 @@ void * devres_alloc(dr_release_t release, size_t size, gfp_t gfp) EXPORT_SYMBOL_GPL(devres_alloc); #endif +/** + * devres_for_each_res - Resource iterator + * @dev: Device to iterate resource from + * @release: Look for resources associated with this release function + * @match: Match function (optional) + * @match_data: Data for the match function + * @fn: Function to be called for each matched resource. + * @data: Data for @fn, the 3rd parameter of @fn + * + * Call @fn for each devres of @dev which is associated with @release + * and for which @match returns 1. + * + * RETURNS: + * void + */ +void devres_for_each_res(struct device *dev, dr_release_t release, + dr_match_t match, void *match_data, + void (*fn)(struct device *, void *, void *), + void *data) +{ + struct devres_node *node; + struct devres_node *tmp; + unsigned long flags; + + if (!fn) + return; + + spin_lock_irqsave(&dev->devres_lock, flags); + list_for_each_entry_safe_reverse(node, tmp, + &dev->devres_head, entry) { + struct devres *dr = container_of(node, struct devres, node); + + if (node->release != release) + continue; + if (match && !match(dev, dr->data, match_data)) + continue; + fn(dev, dr->data, data); + } + spin_unlock_irqrestore(&dev->devres_lock, flags); +} +EXPORT_SYMBOL_GPL(devres_for_each_res); + /** * devres_free - Free device resource data * @res: Pointer to devres data to free diff --git a/include/linux/device.h b/include/linux/device.h index 52a5f15a2223..ecd900663726 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -536,6 +536,10 @@ extern void *__devres_alloc(dr_release_t release, size_t size, gfp_t gfp, #else extern void *devres_alloc(dr_release_t release, size_t size, gfp_t gfp); #endif +extern void devres_for_each_res(struct device *dev, dr_release_t release, + dr_match_t match, void *match_data, + void (*fn)(struct device *, void *, void *), + void *data); extern void devres_free(void *res); extern void devres_add(struct device *dev, void *res); extern void *devres_find(struct device *dev, dr_release_t release, -- cgit v1.2.3 From 9b5957803cb444a99275355eb2309b6fecc63c5f Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Mon, 13 Aug 2012 10:06:51 -0700 Subject: Drivers: hv: Add KVP definitions for IP address injection Add the necessary definitions for supporting the IP injection functionality. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Reviewed-by: Olaf Hering Reviewed-by: Ben Hutchings Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_util.c | 4 +-- include/linux/hyperv.h | 76 ++++++++++++++++++++++++++++++++++++++++++++++-- tools/hv/hv_kvp_daemon.c | 2 +- 3 files changed, 77 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index d3ac6a40118b..a0667de7a04c 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -263,7 +263,7 @@ static int util_probe(struct hv_device *dev, (struct hv_util_service *)dev_id->driver_data; int ret; - srv->recv_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); + srv->recv_buffer = kmalloc(PAGE_SIZE * 2, GFP_KERNEL); if (!srv->recv_buffer) return -ENOMEM; if (srv->util_init) { @@ -274,7 +274,7 @@ static int util_probe(struct hv_device *dev, } } - ret = vmbus_open(dev->channel, 2 * PAGE_SIZE, 2 * PAGE_SIZE, NULL, 0, + ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0, srv->util_cb, dev->channel); if (ret) goto error; diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 68ed7f7e1fc9..11afc4e0849a 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -122,12 +122,53 @@ #define REG_U32 4 #define REG_U64 8 +/* + * As we look at expanding the KVP functionality to include + * IP injection functionality, we need to maintain binary + * compatibility with older daemons. + * + * The KVP opcodes are defined by the host and it was unfortunate + * that I chose to treat the registration operation as part of the + * KVP operations defined by the host. + * Here is the level of compatibility + * (between the user level daemon and the kernel KVP driver) that we + * will implement: + * + * An older daemon will always be supported on a newer driver. + * A given user level daemon will require a minimal version of the + * kernel driver. + * If we cannot handle the version differences, we will fail gracefully + * (this can happen when we have a user level daemon that is more + * advanced than the KVP driver. + * + * We will use values used in this handshake for determining if we have + * workable user level daemon and the kernel driver. We begin by taking the + * registration opcode out of the KVP opcode namespace. We will however, + * maintain compatibility with the existing user-level daemon code. + */ + +/* + * Daemon code not supporting IP injection (legacy daemon). + */ + +#define KVP_OP_REGISTER 4 + +/* + * Daemon code supporting IP injection. + * The KVP opcode field is used to communicate the + * registration information; so define a namespace that + * will be distinct from the host defined KVP opcode. + */ + +#define KVP_OP_REGISTER1 100 + enum hv_kvp_exchg_op { KVP_OP_GET = 0, KVP_OP_SET, KVP_OP_DELETE, KVP_OP_ENUMERATE, - KVP_OP_REGISTER, + KVP_OP_GET_IP_INFO, + KVP_OP_SET_IP_INFO, KVP_OP_COUNT /* Number of operations, must be last. */ }; @@ -140,6 +181,26 @@ enum hv_kvp_exchg_pool { KVP_POOL_COUNT /* Number of pools, must be last. */ }; +#define ADDR_FAMILY_NONE 0x00 +#define ADDR_FAMILY_IPV4 0x01 +#define ADDR_FAMILY_IPV6 0x02 + +#define MAX_ADAPTER_ID_SIZE 128 +#define MAX_IP_ADDR_SIZE 1024 +#define MAX_GATEWAY_SIZE 512 + + +struct hv_kvp_ipaddr_value { + __u16 adapter_id[MAX_ADAPTER_ID_SIZE]; + __u8 addr_family; + __u8 dhcp_enabled; + __u16 ip_addr[MAX_IP_ADDR_SIZE]; + __u16 sub_net[MAX_IP_ADDR_SIZE]; + __u16 gate_way[MAX_GATEWAY_SIZE]; + __u16 dns_addr[MAX_IP_ADDR_SIZE]; +} __attribute__((packed)); + + struct hv_kvp_hdr { __u8 operation; __u8 pool; @@ -181,16 +242,26 @@ struct hv_kvp_register { }; struct hv_kvp_msg { - struct hv_kvp_hdr kvp_hdr; + union { + struct hv_kvp_hdr kvp_hdr; + int error; + }; union { struct hv_kvp_msg_get kvp_get; struct hv_kvp_msg_set kvp_set; struct hv_kvp_msg_delete kvp_delete; struct hv_kvp_msg_enumerate kvp_enum_data; + struct hv_kvp_ipaddr_value kvp_ip_val; struct hv_kvp_register kvp_register; } body; } __attribute__((packed)); +struct hv_kvp_ip_msg { + __u8 operation; + __u8 pool; + struct hv_kvp_ipaddr_value kvp_ip_val; +} __attribute__((packed)); + #ifdef __KERNEL__ #include #include @@ -982,6 +1053,7 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver); #define HV_S_CONT 0x80070103 #define HV_ERROR_NOT_SUPPORTED 0x80070032 #define HV_ERROR_MACHINE_LOCKED 0x800704F7 +#define HV_ERROR_DEVICE_NOT_CONNECTED 0x8007048F /* * While we want to handle util services as regular devices, diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index d9834b362943..8fbcf7b3c69d 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -69,7 +69,7 @@ enum key_index { }; static char kvp_send_buffer[4096]; -static char kvp_recv_buffer[4096]; +static char kvp_recv_buffer[4096 * 2]; static struct sockaddr_nl addr; static char *os_name = ""; -- cgit v1.2.3 From b47a81dcc5a806efb6d970608299129771588289 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Mon, 13 Aug 2012 10:06:52 -0700 Subject: Drivers: hv: kvp: Cleanup error handling in KVP In preparation to implementing IP injection, cleanup the way we propagate and handle errors both in the driver as well as in the user level daemon. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Reviewed-by: Olaf Hering Reviewed-by: Ben Hutchings Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_kvp.c | 114 +++++++++++++++++++++++++++++++++++++---------- include/linux/hyperv.h | 17 ++++--- tools/hv/hv_kvp_daemon.c | 66 +++++++++++++-------------- 3 files changed, 135 insertions(+), 62 deletions(-) (limited to 'include') diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 0012eed6d872..eb4d0730f44d 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -48,13 +48,24 @@ static struct { void *kvp_context; /* for the channel callback */ } kvp_transaction; +/* + * Before we can accept KVP messages from the host, we need + * to handshake with the user level daemon. This state tracks + * if we are in the handshake phase. + */ +static bool in_hand_shake = true; + +/* + * This state maintains the version number registered by the daemon. + */ +static int dm_reg_value; + static void kvp_send_key(struct work_struct *dummy); -#define TIMEOUT_FIRED 1 static void kvp_respond_to_host(char *key, char *value, int error); static void kvp_work_func(struct work_struct *dummy); -static void kvp_register(void); +static void kvp_register(int); static DECLARE_DELAYED_WORK(kvp_work, kvp_work_func); static DECLARE_WORK(kvp_sendkey_work, kvp_send_key); @@ -68,7 +79,7 @@ static u8 *recv_buffer; */ static void -kvp_register(void) +kvp_register(int reg_value) { struct cn_msg *msg; @@ -83,7 +94,7 @@ kvp_register(void) msg->id.idx = CN_KVP_IDX; msg->id.val = CN_KVP_VAL; - kvp_msg->kvp_hdr.operation = KVP_OP_REGISTER; + kvp_msg->kvp_hdr.operation = reg_value; strcpy(version, HV_DRV_VERSION); msg->len = sizeof(struct hv_kvp_msg); cn_netlink_send(msg, 0, GFP_ATOMIC); @@ -97,9 +108,43 @@ kvp_work_func(struct work_struct *dummy) * If the timer fires, the user-mode component has not responded; * process the pending transaction. */ - kvp_respond_to_host("Unknown key", "Guest timed out", TIMEOUT_FIRED); + kvp_respond_to_host("Unknown key", "Guest timed out", HV_E_FAIL); +} + +static int kvp_handle_handshake(struct hv_kvp_msg *msg) +{ + int ret = 1; + + switch (msg->kvp_hdr.operation) { + case KVP_OP_REGISTER: + dm_reg_value = KVP_OP_REGISTER; + pr_info("KVP: IP injection functionality not available\n"); + pr_info("KVP: Upgrade the KVP daemon\n"); + break; + case KVP_OP_REGISTER1: + dm_reg_value = KVP_OP_REGISTER1; + break; + default: + pr_info("KVP: incompatible daemon\n"); + pr_info("KVP: KVP version: %d, Daemon version: %d\n", + KVP_OP_REGISTER1, msg->kvp_hdr.operation); + ret = 0; + } + + if (ret) { + /* + * We have a compatible daemon; complete the handshake. + */ + pr_info("KVP: user-mode registering done.\n"); + kvp_register(dm_reg_value); + kvp_transaction.active = false; + if (kvp_transaction.kvp_context) + hv_kvp_onchannelcallback(kvp_transaction.kvp_context); + } + return ret; } + /* * Callback when data is received from user mode. */ @@ -109,27 +154,52 @@ kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) { struct hv_kvp_msg *message; struct hv_kvp_msg_enumerate *data; + int error = 0; message = (struct hv_kvp_msg *)msg->data; - switch (message->kvp_hdr.operation) { + + /* + * If we are negotiating the version information + * with the daemon; handle that first. + */ + + if (in_hand_shake) { + if (kvp_handle_handshake(message)) + in_hand_shake = false; + return; + } + + /* + * Based on the version of the daemon, we propagate errors from the + * daemon differently. + */ + + data = &message->body.kvp_enum_data; + + switch (dm_reg_value) { case KVP_OP_REGISTER: - pr_info("KVP: user-mode registering done.\n"); - kvp_register(); - kvp_transaction.active = false; - hv_kvp_onchannelcallback(kvp_transaction.kvp_context); + /* + * Null string is used to pass back error condition. + */ + if (data->data.key[0] == 0) + error = HV_S_CONT; break; - default: - data = &message->body.kvp_enum_data; + case KVP_OP_REGISTER1: /* - * Complete the transaction by forwarding the key value - * to the host. But first, cancel the timeout. + * We use the message header information from + * the user level daemon to transmit errors. */ - if (cancel_delayed_work_sync(&kvp_work)) - kvp_respond_to_host(data->data.key, - data->data.value, - !strlen(data->data.key)); + error = message->error; + break; } + + /* + * Complete the transaction by forwarding the key value + * to the host. But first, cancel the timeout. + */ + if (cancel_delayed_work_sync(&kvp_work)) + kvp_respond_to_host(data->data.key, data->data.value, error); } static void @@ -287,6 +357,7 @@ kvp_respond_to_host(char *key, char *value, int error) */ return; + icmsghdrp->status = error; /* * If the error parameter is set, terminate the host's enumeration @@ -294,15 +365,12 @@ kvp_respond_to_host(char *key, char *value, int error) */ if (error) { /* - * Something failed or the we have timedout; - * terminate the current host-side iteration. + * Something failed or we have timedout; + * terminate the current host-side iteration. */ - icmsghdrp->status = HV_S_CONT; goto response_done; } - icmsghdrp->status = HV_S_OK; - kvp_msg = (struct hv_kvp_msg *) &recv_buffer[sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr)]; diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 11afc4e0849a..b587c448e8ab 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -181,6 +181,17 @@ enum hv_kvp_exchg_pool { KVP_POOL_COUNT /* Number of pools, must be last. */ }; +/* + * Some Hyper-V status codes. + */ + +#define HV_S_OK 0x00000000 +#define HV_E_FAIL 0x80004005 +#define HV_S_CONT 0x80070103 +#define HV_ERROR_NOT_SUPPORTED 0x80070032 +#define HV_ERROR_MACHINE_LOCKED 0x800704F7 +#define HV_ERROR_DEVICE_NOT_CONNECTED 0x8007048F + #define ADDR_FAMILY_NONE 0x00 #define ADDR_FAMILY_IPV4 0x01 #define ADDR_FAMILY_IPV6 0x02 @@ -1048,12 +1059,6 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver); #define ICMSGHDRFLAG_REQUEST 2 #define ICMSGHDRFLAG_RESPONSE 4 -#define HV_S_OK 0x00000000 -#define HV_E_FAIL 0x80004005 -#define HV_S_CONT 0x80070103 -#define HV_ERROR_NOT_SUPPORTED 0x80070032 -#define HV_ERROR_MACHINE_LOCKED 0x800704F7 -#define HV_ERROR_DEVICE_NOT_CONNECTED 0x8007048F /* * While we want to handle util services as regular devices, diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 8fbcf7b3c69d..069e2b38decb 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -71,13 +71,14 @@ enum key_index { static char kvp_send_buffer[4096]; static char kvp_recv_buffer[4096 * 2]; static struct sockaddr_nl addr; +static int in_hand_shake = 1; static char *os_name = ""; static char *os_major = ""; static char *os_minor = ""; static char *processor_arch; static char *os_build; -static char *lic_version; +static char *lic_version = "Unknown version"; static struct utsname uts_buf; @@ -394,7 +395,7 @@ static int kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value, return 1; } -static void kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size, +static int kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size, __u8 *value, int value_size) { struct kvp_record *record; @@ -406,16 +407,12 @@ static void kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size, record = kvp_file_info[pool].records; if (index >= kvp_file_info[pool].num_records) { - /* - * This is an invalid index; terminate enumeration; - * - a NULL value will do the trick. - */ - strcpy(value, ""); - return; + return 1; } memcpy(key, record[index].key, key_size); memcpy(value, record[index].value, value_size); + return 0; } @@ -646,6 +643,8 @@ int main(void) char *p; char *key_value; char *key_name; + int op; + int pool; daemon(1, 0); openlog("KVP", 0, LOG_USER); @@ -687,7 +686,7 @@ int main(void) message->id.val = CN_KVP_VAL; hv_msg = (struct hv_kvp_msg *)message->data; - hv_msg->kvp_hdr.operation = KVP_OP_REGISTER; + hv_msg->kvp_hdr.operation = KVP_OP_REGISTER1; message->ack = 0; message->len = sizeof(struct hv_kvp_msg); @@ -721,12 +720,21 @@ int main(void) incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg); hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; - switch (hv_msg->kvp_hdr.operation) { - case KVP_OP_REGISTER: + /* + * We will use the KVP header information to pass back + * the error from this daemon. So, first copy the state + * and set the error code to success. + */ + op = hv_msg->kvp_hdr.operation; + pool = hv_msg->kvp_hdr.pool; + hv_msg->error = HV_S_OK; + + if ((in_hand_shake) && (op == KVP_OP_REGISTER1)) { /* * Driver is registering with us; stash away the version * information. */ + in_hand_shake = 0; p = (char *)hv_msg->body.kvp_register.version; lic_version = malloc(strlen(p) + 1); if (lic_version) { @@ -737,44 +745,39 @@ int main(void) syslog(LOG_ERR, "malloc failed"); } continue; + } - /* - * The current protocol with the kernel component uses a - * NULL key name to pass an error condition. - * For the SET, GET and DELETE operations, - * use the existing protocol to pass back error. - */ - + switch (op) { case KVP_OP_SET: - if (kvp_key_add_or_modify(hv_msg->kvp_hdr.pool, + if (kvp_key_add_or_modify(pool, hv_msg->body.kvp_set.data.key, hv_msg->body.kvp_set.data.key_size, hv_msg->body.kvp_set.data.value, hv_msg->body.kvp_set.data.value_size)) - strcpy(hv_msg->body.kvp_set.data.key, ""); + hv_msg->error = HV_S_CONT; break; case KVP_OP_GET: - if (kvp_get_value(hv_msg->kvp_hdr.pool, + if (kvp_get_value(pool, hv_msg->body.kvp_set.data.key, hv_msg->body.kvp_set.data.key_size, hv_msg->body.kvp_set.data.value, hv_msg->body.kvp_set.data.value_size)) - strcpy(hv_msg->body.kvp_set.data.key, ""); + hv_msg->error = HV_S_CONT; break; case KVP_OP_DELETE: - if (kvp_key_delete(hv_msg->kvp_hdr.pool, + if (kvp_key_delete(pool, hv_msg->body.kvp_delete.key, hv_msg->body.kvp_delete.key_size)) - strcpy(hv_msg->body.kvp_delete.key, ""); + hv_msg->error = HV_S_CONT; break; default: break; } - if (hv_msg->kvp_hdr.operation != KVP_OP_ENUMERATE) + if (op != KVP_OP_ENUMERATE) goto kvp_done; /* @@ -782,13 +785,14 @@ int main(void) * both the key and the value; if not read from the * appropriate pool. */ - if (hv_msg->kvp_hdr.pool != KVP_POOL_AUTO) { - kvp_pool_enumerate(hv_msg->kvp_hdr.pool, + if (pool != KVP_POOL_AUTO) { + if (kvp_pool_enumerate(pool, hv_msg->body.kvp_enum_data.index, hv_msg->body.kvp_enum_data.data.key, HV_KVP_EXCHANGE_MAX_KEY_SIZE, hv_msg->body.kvp_enum_data.data.value, - HV_KVP_EXCHANGE_MAX_VALUE_SIZE); + HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) + hv_msg->error = HV_S_CONT; goto kvp_done; } @@ -841,11 +845,7 @@ int main(void) strcpy(key_name, "ProcessorArchitecture"); break; default: - strcpy(key_value, "Unknown Key"); - /* - * We use a null key name to terminate enumeration. - */ - strcpy(key_name, ""); + hv_msg->error = HV_S_CONT; break; } /* -- cgit v1.2.3 From 1508d8111f0e965ebe06c32dc4d176750eb53c3a Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 16 Aug 2012 08:23:20 -0700 Subject: Drivers: hv: Explicitly size elements of protocol structures Use explicitly sized types in data structures defining the host/guest protocol. Reported-by: Juan Sanchez-Agrelo Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- include/linux/hyperv.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index b587c448e8ab..7585d5533e43 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -487,7 +487,7 @@ struct vmtransfer_page_range { struct vmtransfer_page_packet_header { struct vmpacket_descriptor d; u16 xfer_pageset_id; - bool sender_owns_set; + u8 sender_owns_set; u8 reserved; u32 range_cnt; struct vmtransfer_page_range ranges[1]; @@ -641,7 +641,7 @@ struct vmbus_channel_query_vmbus_version { /* VMBus Version Supported parameters */ struct vmbus_channel_version_supported { struct vmbus_channel_message_header header; - bool version_supported; + u8 version_supported; } __packed; /* Offer Channel parameters */ @@ -650,7 +650,7 @@ struct vmbus_channel_offer_channel { struct vmbus_channel_offer offer; u32 child_relid; u8 monitorid; - bool monitor_allocated; + u8 monitor_allocated; } __packed; /* Rescind Offer parameters */ @@ -786,7 +786,7 @@ struct vmbus_channel_initiate_contact { struct vmbus_channel_version_response { struct vmbus_channel_message_header header; - bool version_supported; + u8 version_supported; } __packed; enum vmbus_channel_state { -- cgit v1.2.3 From 26f944b2716717aa243f16cd2181f80441153f0d Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 16 Aug 2012 18:24:19 +0530 Subject: usb: hcd: use *resource_size_t* for specifying resource data Changed the type of rsrc_start and rsrc_len from u64 to resource_size_t so that it behaves well with resource API's like *resource_size()* on both 32bit and 64bit devices. Signed-off-by: Kishon Vijay Abraham I Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/hcd.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index c5fdb148fc02..608050b2545f 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -135,8 +135,8 @@ struct usb_hcd { unsigned int irq; /* irq allocated */ void __iomem *regs; /* device memory/io */ - u64 rsrc_start; /* memory/io resource start */ - u64 rsrc_len; /* memory/io resource length */ + resource_size_t rsrc_start; /* memory/io resource start */ + resource_size_t rsrc_len; /* memory/io resource length */ unsigned power_budget; /* in mA, 0 = no limit */ /* bandwidth_mutex should be taken before adding or removing -- cgit v1.2.3 From ac5aa7f9e0891a115ab307b4bdde9c55b9232970 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 10 Aug 2012 16:52:58 +0200 Subject: pinctrl: header: trivial: declare struct device As struct device is used as a function argument, it should at least be declared (device.h is not included). Signed-off-by: Richard Genoud Signed-off-by: Linus Walleij --- include/linux/pinctrl/consumer.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/pinctrl/consumer.h b/include/linux/pinctrl/consumer.h index 6dd96fb45482..e9b7f4350844 100644 --- a/include/linux/pinctrl/consumer.h +++ b/include/linux/pinctrl/consumer.h @@ -20,6 +20,7 @@ /* This struct is private to the core and should be regarded as a cookie */ struct pinctrl; struct pinctrl_state; +struct device; #ifdef CONFIG_PINCTRL -- cgit v1.2.3 From dfe3212e0196c01402154971841463d721dea915 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 17 Aug 2012 22:06:59 +0800 Subject: PM / Sleep: introduce dpm_for_each_dev dpm_list and its pm lock provide a good way to iterate all devices in system. Except this way, there is no other easy way to iterate devices in system. firmware loader need to cache firmware images for devices before system sleep, so introduce the function to meet its demand. Reported-by: Fengguang Wu Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman --- drivers/base/power/main.c | 22 ++++++++++++++++++++++ include/linux/pm.h | 5 +++++ 2 files changed, 27 insertions(+) (limited to 'include') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 0113adc310dc..b0b072a88f5f 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -1324,3 +1324,25 @@ int device_pm_wait_for_dev(struct device *subordinate, struct device *dev) return async_error; } EXPORT_SYMBOL_GPL(device_pm_wait_for_dev); + +/** + * dpm_for_each_dev - device iterator. + * @data: data for the callback. + * @fn: function to be called for each device. + * + * Iterate over devices in dpm_list, and call @fn for each device, + * passing it @data. + */ +void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *)) +{ + struct device *dev; + + if (!fn) + return; + + device_pm_lock(); + list_for_each_entry(dev, &dpm_list, power.entry) + fn(dev, data); + device_pm_unlock(); +} +EXPORT_SYMBOL_GPL(dpm_for_each_dev); diff --git a/include/linux/pm.h b/include/linux/pm.h index f067e60a3832..88f034a23f2c 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -638,6 +638,7 @@ extern void __suspend_report_result(const char *function, void *fn, int ret); } while (0) extern int device_pm_wait_for_dev(struct device *sub, struct device *dev); +extern void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *)); extern int pm_generic_prepare(struct device *dev); extern int pm_generic_suspend_late(struct device *dev); @@ -677,6 +678,10 @@ static inline int device_pm_wait_for_dev(struct device *a, struct device *b) return 0; } +static inline void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *)) +{ +} + #define pm_generic_prepare NULL #define pm_generic_suspend NULL #define pm_generic_resume NULL -- cgit v1.2.3 From 8be3d3b2ac87955455c7ce852e0d489cb04fff59 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 3 Aug 2012 17:26:06 +0200 Subject: mmc: spi: Move SSP register definitions into separate file Move the definitions into separate file so separate SPI driver can be implemented. The SSP controller in MXS can act both as a MMC host and as a SPI host. Based on previous attempt by: Fabio Estevam Signed-off-by: Fabio Estevam Signed-off-by: Marek Vasut Acked-by: Chris Ball Acked-by: Shawn Guo Signed-off-by: Mark Brown --- drivers/mmc/host/mxs-mmc.c | 87 ++--------------------------------- include/linux/spi/mxs-spi.h | 109 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 84 deletions(-) create mode 100644 include/linux/spi/mxs-spi.h (limited to 'include') diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index a51f9309ffbb..26c95dc87cb2 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -45,87 +45,10 @@ #include #include #include +#include #define DRIVER_NAME "mxs-mmc" -/* card detect polling timeout */ -#define MXS_MMC_DETECT_TIMEOUT (HZ/2) - -#define ssp_is_old(host) ((host)->devid == IMX23_MMC) - -/* SSP registers */ -#define HW_SSP_CTRL0 0x000 -#define BM_SSP_CTRL0_RUN (1 << 29) -#define BM_SSP_CTRL0_SDIO_IRQ_CHECK (1 << 28) -#define BM_SSP_CTRL0_IGNORE_CRC (1 << 26) -#define BM_SSP_CTRL0_READ (1 << 25) -#define BM_SSP_CTRL0_DATA_XFER (1 << 24) -#define BP_SSP_CTRL0_BUS_WIDTH (22) -#define BM_SSP_CTRL0_BUS_WIDTH (0x3 << 22) -#define BM_SSP_CTRL0_WAIT_FOR_IRQ (1 << 21) -#define BM_SSP_CTRL0_LONG_RESP (1 << 19) -#define BM_SSP_CTRL0_GET_RESP (1 << 17) -#define BM_SSP_CTRL0_ENABLE (1 << 16) -#define BP_SSP_CTRL0_XFER_COUNT (0) -#define BM_SSP_CTRL0_XFER_COUNT (0xffff) -#define HW_SSP_CMD0 0x010 -#define BM_SSP_CMD0_DBL_DATA_RATE_EN (1 << 25) -#define BM_SSP_CMD0_SLOW_CLKING_EN (1 << 22) -#define BM_SSP_CMD0_CONT_CLKING_EN (1 << 21) -#define BM_SSP_CMD0_APPEND_8CYC (1 << 20) -#define BP_SSP_CMD0_BLOCK_SIZE (16) -#define BM_SSP_CMD0_BLOCK_SIZE (0xf << 16) -#define BP_SSP_CMD0_BLOCK_COUNT (8) -#define BM_SSP_CMD0_BLOCK_COUNT (0xff << 8) -#define BP_SSP_CMD0_CMD (0) -#define BM_SSP_CMD0_CMD (0xff) -#define HW_SSP_CMD1 0x020 -#define HW_SSP_XFER_SIZE 0x030 -#define HW_SSP_BLOCK_SIZE 0x040 -#define BP_SSP_BLOCK_SIZE_BLOCK_COUNT (4) -#define BM_SSP_BLOCK_SIZE_BLOCK_COUNT (0xffffff << 4) -#define BP_SSP_BLOCK_SIZE_BLOCK_SIZE (0) -#define BM_SSP_BLOCK_SIZE_BLOCK_SIZE (0xf) -#define HW_SSP_TIMING(h) (ssp_is_old(h) ? 0x050 : 0x070) -#define BP_SSP_TIMING_TIMEOUT (16) -#define BM_SSP_TIMING_TIMEOUT (0xffff << 16) -#define BP_SSP_TIMING_CLOCK_DIVIDE (8) -#define BM_SSP_TIMING_CLOCK_DIVIDE (0xff << 8) -#define BP_SSP_TIMING_CLOCK_RATE (0) -#define BM_SSP_TIMING_CLOCK_RATE (0xff) -#define HW_SSP_CTRL1(h) (ssp_is_old(h) ? 0x060 : 0x080) -#define BM_SSP_CTRL1_SDIO_IRQ (1 << 31) -#define BM_SSP_CTRL1_SDIO_IRQ_EN (1 << 30) -#define BM_SSP_CTRL1_RESP_ERR_IRQ (1 << 29) -#define BM_SSP_CTRL1_RESP_ERR_IRQ_EN (1 << 28) -#define BM_SSP_CTRL1_RESP_TIMEOUT_IRQ (1 << 27) -#define BM_SSP_CTRL1_RESP_TIMEOUT_IRQ_EN (1 << 26) -#define BM_SSP_CTRL1_DATA_TIMEOUT_IRQ (1 << 25) -#define BM_SSP_CTRL1_DATA_TIMEOUT_IRQ_EN (1 << 24) -#define BM_SSP_CTRL1_DATA_CRC_IRQ (1 << 23) -#define BM_SSP_CTRL1_DATA_CRC_IRQ_EN (1 << 22) -#define BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ (1 << 21) -#define BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ_EN (1 << 20) -#define BM_SSP_CTRL1_RECV_TIMEOUT_IRQ (1 << 17) -#define BM_SSP_CTRL1_RECV_TIMEOUT_IRQ_EN (1 << 16) -#define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ (1 << 15) -#define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ_EN (1 << 14) -#define BM_SSP_CTRL1_DMA_ENABLE (1 << 13) -#define BM_SSP_CTRL1_POLARITY (1 << 9) -#define BP_SSP_CTRL1_WORD_LENGTH (4) -#define BM_SSP_CTRL1_WORD_LENGTH (0xf << 4) -#define BP_SSP_CTRL1_SSP_MODE (0) -#define BM_SSP_CTRL1_SSP_MODE (0xf) -#define HW_SSP_SDRESP0(h) (ssp_is_old(h) ? 0x080 : 0x0a0) -#define HW_SSP_SDRESP1(h) (ssp_is_old(h) ? 0x090 : 0x0b0) -#define HW_SSP_SDRESP2(h) (ssp_is_old(h) ? 0x0a0 : 0x0c0) -#define HW_SSP_SDRESP3(h) (ssp_is_old(h) ? 0x0b0 : 0x0d0) -#define HW_SSP_STATUS(h) (ssp_is_old(h) ? 0x0c0 : 0x100) -#define BM_SSP_STATUS_CARD_DETECT (1 << 28) -#define BM_SSP_STATUS_SDIO_IRQ (1 << 17) - -#define BF_SSP(value, field) (((value) << BP_SSP_##field) & BM_SSP_##field) - #define MXS_MMC_IRQ_BITS (BM_SSP_CTRL1_SDIO_IRQ | \ BM_SSP_CTRL1_RESP_ERR_IRQ | \ BM_SSP_CTRL1_RESP_TIMEOUT_IRQ | \ @@ -135,12 +58,8 @@ BM_SSP_CTRL1_RECV_TIMEOUT_IRQ | \ BM_SSP_CTRL1_FIFO_OVERRUN_IRQ) -#define SSP_PIO_NUM 3 - -enum mxs_mmc_id { - IMX23_MMC, - IMX28_MMC, -}; +/* card detect polling timeout */ +#define MXS_MMC_DETECT_TIMEOUT (HZ/2) struct mxs_mmc_host { struct mmc_host *mmc; diff --git a/include/linux/spi/mxs-spi.h b/include/linux/spi/mxs-spi.h new file mode 100644 index 000000000000..b7ccd577f758 --- /dev/null +++ b/include/linux/spi/mxs-spi.h @@ -0,0 +1,109 @@ +/* + * include/linux/spi/mxs-spi.h + * + * Freescale i.MX233/i.MX28 SPI controller register definition + * + * Copyright 2008 Embedded Alley Solutions, Inc. + * Copyright 2009-2011 Freescale Semiconductor, 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. + * + * 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_SPI_MXS_SPI_H__ +#define __LINUX_SPI_MXS_SPI_H__ + +#define ssp_is_old(host) ((host)->devid == IMX23_MMC) + +/* SSP registers */ +#define HW_SSP_CTRL0 0x000 +#define BM_SSP_CTRL0_RUN (1 << 29) +#define BM_SSP_CTRL0_SDIO_IRQ_CHECK (1 << 28) +#define BM_SSP_CTRL0_IGNORE_CRC (1 << 26) +#define BM_SSP_CTRL0_READ (1 << 25) +#define BM_SSP_CTRL0_DATA_XFER (1 << 24) +#define BP_SSP_CTRL0_BUS_WIDTH 22 +#define BM_SSP_CTRL0_BUS_WIDTH (0x3 << 22) +#define BM_SSP_CTRL0_WAIT_FOR_IRQ (1 << 21) +#define BM_SSP_CTRL0_LONG_RESP (1 << 19) +#define BM_SSP_CTRL0_GET_RESP (1 << 17) +#define BM_SSP_CTRL0_ENABLE (1 << 16) +#define BP_SSP_CTRL0_XFER_COUNT 0 +#define BM_SSP_CTRL0_XFER_COUNT 0xffff +#define HW_SSP_CMD0 0x010 +#define BM_SSP_CMD0_DBL_DATA_RATE_EN (1 << 25) +#define BM_SSP_CMD0_SLOW_CLKING_EN (1 << 22) +#define BM_SSP_CMD0_CONT_CLKING_EN (1 << 21) +#define BM_SSP_CMD0_APPEND_8CYC (1 << 20) +#define BP_SSP_CMD0_BLOCK_SIZE 16 +#define BM_SSP_CMD0_BLOCK_SIZE (0xf << 16) +#define BP_SSP_CMD0_BLOCK_COUNT 8 +#define BM_SSP_CMD0_BLOCK_COUNT (0xff << 8) +#define BP_SSP_CMD0_CMD 0 +#define BM_SSP_CMD0_CMD 0xff +#define HW_SSP_CMD1 0x020 +#define HW_SSP_XFER_SIZE 0x030 +#define HW_SSP_BLOCK_SIZE 0x040 +#define BP_SSP_BLOCK_SIZE_BLOCK_COUNT 4 +#define BM_SSP_BLOCK_SIZE_BLOCK_COUNT (0xffffff << 4) +#define BP_SSP_BLOCK_SIZE_BLOCK_SIZE 0 +#define BM_SSP_BLOCK_SIZE_BLOCK_SIZE 0xf +#define HW_SSP_TIMING(h) (ssp_is_old(h) ? 0x050 : 0x070) +#define BP_SSP_TIMING_TIMEOUT 16 +#define BM_SSP_TIMING_TIMEOUT (0xffff << 16) +#define BP_SSP_TIMING_CLOCK_DIVIDE 8 +#define BM_SSP_TIMING_CLOCK_DIVIDE (0xff << 8) +#define BP_SSP_TIMING_CLOCK_RATE 0 +#define BM_SSP_TIMING_CLOCK_RATE 0xff +#define HW_SSP_CTRL1(h) (ssp_is_old(h) ? 0x060 : 0x080) +#define BM_SSP_CTRL1_SDIO_IRQ (1 << 31) +#define BM_SSP_CTRL1_SDIO_IRQ_EN (1 << 30) +#define BM_SSP_CTRL1_RESP_ERR_IRQ (1 << 29) +#define BM_SSP_CTRL1_RESP_ERR_IRQ_EN (1 << 28) +#define BM_SSP_CTRL1_RESP_TIMEOUT_IRQ (1 << 27) +#define BM_SSP_CTRL1_RESP_TIMEOUT_IRQ_EN (1 << 26) +#define BM_SSP_CTRL1_DATA_TIMEOUT_IRQ (1 << 25) +#define BM_SSP_CTRL1_DATA_TIMEOUT_IRQ_EN (1 << 24) +#define BM_SSP_CTRL1_DATA_CRC_IRQ (1 << 23) +#define BM_SSP_CTRL1_DATA_CRC_IRQ_EN (1 << 22) +#define BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ (1 << 21) +#define BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ_EN (1 << 20) +#define BM_SSP_CTRL1_RECV_TIMEOUT_IRQ (1 << 17) +#define BM_SSP_CTRL1_RECV_TIMEOUT_IRQ_EN (1 << 16) +#define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ (1 << 15) +#define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ_EN (1 << 14) +#define BM_SSP_CTRL1_DMA_ENABLE (1 << 13) +#define BM_SSP_CTRL1_POLARITY (1 << 9) +#define BP_SSP_CTRL1_WORD_LENGTH 4 +#define BM_SSP_CTRL1_WORD_LENGTH (0xf << 4) +#define BP_SSP_CTRL1_SSP_MODE 0 +#define BM_SSP_CTRL1_SSP_MODE 0xf +#define HW_SSP_SDRESP0(h) (ssp_is_old(h) ? 0x080 : 0x0a0) +#define HW_SSP_SDRESP1(h) (ssp_is_old(h) ? 0x090 : 0x0b0) +#define HW_SSP_SDRESP2(h) (ssp_is_old(h) ? 0x0a0 : 0x0c0) +#define HW_SSP_SDRESP3(h) (ssp_is_old(h) ? 0x0b0 : 0x0d0) +#define HW_SSP_STATUS(h) (ssp_is_old(h) ? 0x0c0 : 0x100) +#define BM_SSP_STATUS_CARD_DETECT (1 << 28) +#define BM_SSP_STATUS_SDIO_IRQ (1 << 17) + +#define BF_SSP(value, field) (((value) << BP_SSP_##field) & BM_SSP_##field) + +#define SSP_PIO_NUM 3 + +enum mxs_mmc_id { + IMX23_MMC, + IMX28_MMC, +}; + +#endif /* __LINUX_SPI_MXS_SPI_H__ */ -- cgit v1.2.3 From 600a991f98d94c97f72870f85df687c517c3af53 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 3 Aug 2012 17:26:07 +0200 Subject: mmc: spi: Rename IMX2[38]_MMC to IMX2[38]_SSP Since the SSP controller can act as both SPI and MMC host, renaming the enum to properly reflect the naming seems appropriate. Based on previous attempt by: Fabio Estevam Signed-off-by: Fabio Estevam Signed-off-by: Marek Vasut Acked-by: Chris Ball Acked-by: Shawn Guo Signed-off-by: Mark Brown --- drivers/mmc/host/mxs-mmc.c | 18 +++++++++--------- include/linux/spi/mxs-spi.h | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 26c95dc87cb2..e80c2b6b2d7c 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -78,7 +78,7 @@ struct mxs_mmc_host { enum dma_transfer_direction slave_dirn; u32 ssp_pio_words[SSP_PIO_NUM]; - enum mxs_mmc_id devid; + enum mxs_ssp_id devid; unsigned char bus_width; spinlock_t lock; int sdio_irq_en; @@ -601,22 +601,22 @@ static bool mxs_mmc_dma_filter(struct dma_chan *chan, void *param) return true; } -static struct platform_device_id mxs_mmc_ids[] = { +static struct platform_device_id mxs_ssp_ids[] = { { .name = "imx23-mmc", - .driver_data = IMX23_MMC, + .driver_data = IMX23_SSP, }, { .name = "imx28-mmc", - .driver_data = IMX28_MMC, + .driver_data = IMX28_SSP, }, { /* sentinel */ } }; -MODULE_DEVICE_TABLE(platform, mxs_mmc_ids); +MODULE_DEVICE_TABLE(platform, mxs_ssp_ids); static const struct of_device_id mxs_mmc_dt_ids[] = { - { .compatible = "fsl,imx23-mmc", .data = (void *) IMX23_MMC, }, - { .compatible = "fsl,imx28-mmc", .data = (void *) IMX28_MMC, }, + { .compatible = "fsl,imx23-mmc", .data = (void *) IMX23_SSP, }, + { .compatible = "fsl,imx28-mmc", .data = (void *) IMX28_SSP, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mxs_mmc_dt_ids); @@ -655,7 +655,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) } if (np) { - host->devid = (enum mxs_mmc_id) of_id->data; + host->devid = (enum mxs_ssp_id) of_id->data; /* * TODO: This is a temporary solution and should be changed * to use generic DMA binding later when the helpers get in. @@ -829,7 +829,7 @@ static const struct dev_pm_ops mxs_mmc_pm_ops = { static struct platform_driver mxs_mmc_driver = { .probe = mxs_mmc_probe, .remove = mxs_mmc_remove, - .id_table = mxs_mmc_ids, + .id_table = mxs_ssp_ids, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, diff --git a/include/linux/spi/mxs-spi.h b/include/linux/spi/mxs-spi.h index b7ccd577f758..c08aef57c7ce 100644 --- a/include/linux/spi/mxs-spi.h +++ b/include/linux/spi/mxs-spi.h @@ -24,7 +24,7 @@ #ifndef __LINUX_SPI_MXS_SPI_H__ #define __LINUX_SPI_MXS_SPI_H__ -#define ssp_is_old(host) ((host)->devid == IMX23_MMC) +#define ssp_is_old(host) ((host)->devid == IMX23_SSP) /* SSP registers */ #define HW_SSP_CTRL0 0x000 @@ -101,9 +101,9 @@ #define SSP_PIO_NUM 3 -enum mxs_mmc_id { - IMX23_MMC, - IMX28_MMC, +enum mxs_ssp_id { + IMX23_SSP, + IMX28_SSP, }; #endif /* __LINUX_SPI_MXS_SPI_H__ */ -- cgit v1.2.3 From f83b73806579c666fe6f1a4ed4800092c89e81db Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 3 Aug 2012 17:26:08 +0200 Subject: mmc: spi: Add necessary bits into mxs-spi.h Add missing register bits and registers into mxs-spi.h . These will be used by the SPI driver. Based on previous attempt by: Fabio Estevam Signed-off-by: Fabio Estevam Signed-off-by: Marek Vasut Acked-by: Chris Ball Acked-by: Shawn Guo Signed-off-by: Mark Brown --- include/linux/spi/mxs-spi.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'include') diff --git a/include/linux/spi/mxs-spi.h b/include/linux/spi/mxs-spi.h index c08aef57c7ce..7dfa1d7d1a78 100644 --- a/include/linux/spi/mxs-spi.h +++ b/include/linux/spi/mxs-spi.h @@ -30,12 +30,14 @@ #define HW_SSP_CTRL0 0x000 #define BM_SSP_CTRL0_RUN (1 << 29) #define BM_SSP_CTRL0_SDIO_IRQ_CHECK (1 << 28) +#define BM_SSP_CTRL0_LOCK_CS (1 << 27) #define BM_SSP_CTRL0_IGNORE_CRC (1 << 26) #define BM_SSP_CTRL0_READ (1 << 25) #define BM_SSP_CTRL0_DATA_XFER (1 << 24) #define BP_SSP_CTRL0_BUS_WIDTH 22 #define BM_SSP_CTRL0_BUS_WIDTH (0x3 << 22) #define BM_SSP_CTRL0_WAIT_FOR_IRQ (1 << 21) +#define BM_SSP_CTRL0_WAIT_FOR_CMD (1 << 20) #define BM_SSP_CTRL0_LONG_RESP (1 << 19) #define BM_SSP_CTRL0_GET_RESP (1 << 17) #define BM_SSP_CTRL0_ENABLE (1 << 16) @@ -64,8 +66,12 @@ #define BM_SSP_TIMING_TIMEOUT (0xffff << 16) #define BP_SSP_TIMING_CLOCK_DIVIDE 8 #define BM_SSP_TIMING_CLOCK_DIVIDE (0xff << 8) +#define BF_SSP_TIMING_CLOCK_DIVIDE(v) \ + (((v) << 8) & BM_SSP_TIMING_CLOCK_DIVIDE) #define BP_SSP_TIMING_CLOCK_RATE 0 #define BM_SSP_TIMING_CLOCK_RATE 0xff +#define BF_SSP_TIMING_CLOCK_RATE(v) \ + (((v) << 0) & BM_SSP_TIMING_CLOCK_RATE) #define HW_SSP_CTRL1(h) (ssp_is_old(h) ? 0x060 : 0x080) #define BM_SSP_CTRL1_SDIO_IRQ (1 << 31) #define BM_SSP_CTRL1_SDIO_IRQ_EN (1 << 30) @@ -84,11 +90,26 @@ #define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ (1 << 15) #define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ_EN (1 << 14) #define BM_SSP_CTRL1_DMA_ENABLE (1 << 13) +#define BM_SSP_CTRL1_PHASE (1 << 10) #define BM_SSP_CTRL1_POLARITY (1 << 9) #define BP_SSP_CTRL1_WORD_LENGTH 4 #define BM_SSP_CTRL1_WORD_LENGTH (0xf << 4) +#define BF_SSP_CTRL1_WORD_LENGTH(v) \ + (((v) << 4) & BM_SSP_CTRL1_WORD_LENGTH) +#define BV_SSP_CTRL1_WORD_LENGTH__FOUR_BITS 0x3 +#define BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS 0x7 +#define BV_SSP_CTRL1_WORD_LENGTH__SIXTEEN_BITS 0xF #define BP_SSP_CTRL1_SSP_MODE 0 #define BM_SSP_CTRL1_SSP_MODE 0xf +#define BF_SSP_CTRL1_SSP_MODE(v) \ + (((v) << 0) & BM_SSP_CTRL1_SSP_MODE) +#define BV_SSP_CTRL1_SSP_MODE__SPI 0x0 +#define BV_SSP_CTRL1_SSP_MODE__SSI 0x1 +#define BV_SSP_CTRL1_SSP_MODE__SD_MMC 0x3 +#define BV_SSP_CTRL1_SSP_MODE__MS 0x4 + +#define HW_SSP_DATA(h) (ssp_is_old(h) ? 0x070 : 0x090) + #define HW_SSP_SDRESP0(h) (ssp_is_old(h) ? 0x080 : 0x0a0) #define HW_SSP_SDRESP1(h) (ssp_is_old(h) ? 0x090 : 0x0b0) #define HW_SSP_SDRESP2(h) (ssp_is_old(h) ? 0x0a0 : 0x0c0) @@ -96,6 +117,7 @@ #define HW_SSP_STATUS(h) (ssp_is_old(h) ? 0x0c0 : 0x100) #define BM_SSP_STATUS_CARD_DETECT (1 << 28) #define BM_SSP_STATUS_SDIO_IRQ (1 << 17) +#define BM_SSP_STATUS_FIFO_EMPTY (1 << 5) #define BF_SSP(value, field) (((value) << BP_SSP_##field) & BM_SSP_##field) -- cgit v1.2.3 From 829c1bf40b926a86e545733f6252262add3abe39 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 3 Aug 2012 17:26:09 +0200 Subject: mmc: spi: Pull out parts shared between MMC and SPI Abstract out the common part of private data shared between MMC and SPI. These shall later allow to use common clock configuration function. Signed-off-by: Marek Vasut Acked-by: Chris Ball Acked-by: Shawn Guo Signed-off-by: Mark Brown --- drivers/mmc/host/mxs-mmc.c | 107 ++++++++++++++++++++++++-------------------- include/linux/spi/mxs-spi.h | 8 ++++ 2 files changed, 67 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index e80c2b6b2d7c..7b85e035a4cb 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -62,23 +62,20 @@ #define MXS_MMC_DETECT_TIMEOUT (HZ/2) struct mxs_mmc_host { + struct mxs_ssp ssp; + struct mmc_host *mmc; struct mmc_request *mrq; struct mmc_command *cmd; struct mmc_data *data; - void __iomem *base; int dma_channel; - struct clk *clk; - unsigned int clk_rate; - struct dma_chan *dmach; struct mxs_dma_data dma_data; unsigned int dma_dir; enum dma_transfer_direction slave_dirn; u32 ssp_pio_words[SSP_PIO_NUM]; - enum mxs_ssp_id devid; unsigned char bus_width; spinlock_t lock; int sdio_irq_en; @@ -105,16 +102,18 @@ static int mxs_mmc_get_ro(struct mmc_host *mmc) static int mxs_mmc_get_cd(struct mmc_host *mmc) { struct mxs_mmc_host *host = mmc_priv(mmc); + struct mxs_ssp *ssp = &host->ssp; - return !(readl(host->base + HW_SSP_STATUS(host)) & + return !(readl(ssp->base + HW_SSP_STATUS(ssp)) & BM_SSP_STATUS_CARD_DETECT); } static void mxs_mmc_reset(struct mxs_mmc_host *host) { + struct mxs_ssp *ssp = &host->ssp; u32 ctrl0, ctrl1; - stmp_reset_block(host->base); + stmp_reset_block(ssp->base); ctrl0 = BM_SSP_CTRL0_IGNORE_CRC; ctrl1 = BF_SSP(0x3, CTRL1_SSP_MODE) | @@ -130,15 +129,15 @@ static void mxs_mmc_reset(struct mxs_mmc_host *host) writel(BF_SSP(0xffff, TIMING_TIMEOUT) | BF_SSP(2, TIMING_CLOCK_DIVIDE) | BF_SSP(0, TIMING_CLOCK_RATE), - host->base + HW_SSP_TIMING(host)); + ssp->base + HW_SSP_TIMING(ssp)); if (host->sdio_irq_en) { ctrl0 |= BM_SSP_CTRL0_SDIO_IRQ_CHECK; ctrl1 |= BM_SSP_CTRL1_SDIO_IRQ_EN; } - writel(ctrl0, host->base + HW_SSP_CTRL0); - writel(ctrl1, host->base + HW_SSP_CTRL1(host)); + writel(ctrl0, ssp->base + HW_SSP_CTRL0); + writel(ctrl1, ssp->base + HW_SSP_CTRL1(ssp)); } static void mxs_mmc_start_cmd(struct mxs_mmc_host *host, @@ -149,15 +148,16 @@ static void mxs_mmc_request_done(struct mxs_mmc_host *host) struct mmc_command *cmd = host->cmd; struct mmc_data *data = host->data; struct mmc_request *mrq = host->mrq; + struct mxs_ssp *ssp = &host->ssp; if (mmc_resp_type(cmd) & MMC_RSP_PRESENT) { if (mmc_resp_type(cmd) & MMC_RSP_136) { - cmd->resp[3] = readl(host->base + HW_SSP_SDRESP0(host)); - cmd->resp[2] = readl(host->base + HW_SSP_SDRESP1(host)); - cmd->resp[1] = readl(host->base + HW_SSP_SDRESP2(host)); - cmd->resp[0] = readl(host->base + HW_SSP_SDRESP3(host)); + cmd->resp[3] = readl(ssp->base + HW_SSP_SDRESP0(ssp)); + cmd->resp[2] = readl(ssp->base + HW_SSP_SDRESP1(ssp)); + cmd->resp[1] = readl(ssp->base + HW_SSP_SDRESP2(ssp)); + cmd->resp[0] = readl(ssp->base + HW_SSP_SDRESP3(ssp)); } else { - cmd->resp[0] = readl(host->base + HW_SSP_SDRESP0(host)); + cmd->resp[0] = readl(ssp->base + HW_SSP_SDRESP0(ssp)); } } @@ -196,13 +196,14 @@ static irqreturn_t mxs_mmc_irq_handler(int irq, void *dev_id) struct mxs_mmc_host *host = dev_id; struct mmc_command *cmd = host->cmd; struct mmc_data *data = host->data; + struct mxs_ssp *ssp = &host->ssp; u32 stat; spin_lock(&host->lock); - stat = readl(host->base + HW_SSP_CTRL1(host)); + stat = readl(ssp->base + HW_SSP_CTRL1(ssp)); writel(stat & MXS_MMC_IRQ_BITS, - host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_CLR); + ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_CLR); if ((stat & BM_SSP_CTRL1_SDIO_IRQ) && (stat & BM_SSP_CTRL1_SDIO_IRQ_EN)) mmc_signal_sdio_irq(host->mmc); @@ -366,6 +367,8 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host) unsigned int data_size = 0, log2_blksz; unsigned int blocks = data->blocks; + struct mxs_ssp *ssp = &host->ssp; + u32 ignore_crc, get_resp, long_resp, read; u32 ctrl0, cmd0, cmd1, val; @@ -408,15 +411,15 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host) blocks = 1; /* xfer count, block size and count need to be set differently */ - if (ssp_is_old(host)) { + if (ssp_is_old(ssp)) { ctrl0 |= BF_SSP(data_size, CTRL0_XFER_COUNT); cmd0 |= BF_SSP(log2_blksz, CMD0_BLOCK_SIZE) | BF_SSP(blocks - 1, CMD0_BLOCK_COUNT); } else { - writel(data_size, host->base + HW_SSP_XFER_SIZE); + writel(data_size, ssp->base + HW_SSP_XFER_SIZE); writel(BF_SSP(log2_blksz, BLOCK_SIZE_BLOCK_SIZE) | BF_SSP(blocks - 1, BLOCK_SIZE_BLOCK_COUNT), - host->base + HW_SSP_BLOCK_SIZE); + ssp->base + HW_SSP_BLOCK_SIZE); } if ((cmd->opcode == MMC_STOP_TRANSMISSION) || @@ -431,11 +434,11 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host) } /* set the timeout count */ - timeout = mxs_ns_to_ssp_ticks(host->clk_rate, data->timeout_ns); - val = readl(host->base + HW_SSP_TIMING(host)); + timeout = mxs_ns_to_ssp_ticks(ssp->clk_rate, data->timeout_ns); + val = readl(ssp->base + HW_SSP_TIMING(ssp)); val &= ~(BM_SSP_TIMING_TIMEOUT); val |= BF_SSP(timeout, TIMING_TIMEOUT); - writel(val, host->base + HW_SSP_TIMING(host)); + writel(val, ssp->base + HW_SSP_TIMING(ssp)); /* pio */ host->ssp_pio_words[0] = ctrl0; @@ -500,11 +503,12 @@ static void mxs_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) static void mxs_mmc_set_clk_rate(struct mxs_mmc_host *host, unsigned int rate) { + struct mxs_ssp *ssp = &host->ssp; unsigned int ssp_clk, ssp_sck; u32 clock_divide, clock_rate; u32 val; - ssp_clk = clk_get_rate(host->clk); + ssp_clk = clk_get_rate(ssp->clk); for (clock_divide = 2; clock_divide <= 254; clock_divide += 2) { clock_rate = DIV_ROUND_UP(ssp_clk, rate * clock_divide); @@ -521,13 +525,13 @@ static void mxs_mmc_set_clk_rate(struct mxs_mmc_host *host, unsigned int rate) ssp_sck = ssp_clk / clock_divide / (1 + clock_rate); - val = readl(host->base + HW_SSP_TIMING(host)); + val = readl(ssp->base + HW_SSP_TIMING(ssp)); val &= ~(BM_SSP_TIMING_CLOCK_DIVIDE | BM_SSP_TIMING_CLOCK_RATE); val |= BF_SSP(clock_divide, TIMING_CLOCK_DIVIDE); val |= BF_SSP(clock_rate, TIMING_CLOCK_RATE); - writel(val, host->base + HW_SSP_TIMING(host)); + writel(val, ssp->base + HW_SSP_TIMING(ssp)); - host->clk_rate = ssp_sck; + ssp->clk_rate = ssp_sck; dev_dbg(mmc_dev(host->mmc), "%s: clock_divide %d, clock_rate %d, ssp_clk %d, rate_actual %d, rate_requested %d\n", @@ -552,6 +556,7 @@ static void mxs_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) { struct mxs_mmc_host *host = mmc_priv(mmc); + struct mxs_ssp *ssp = &host->ssp; unsigned long flags; spin_lock_irqsave(&host->lock, flags); @@ -560,19 +565,19 @@ static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) if (enable) { writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK, - host->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); writel(BM_SSP_CTRL1_SDIO_IRQ_EN, - host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_SET); + ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_SET); - if (readl(host->base + HW_SSP_STATUS(host)) & + if (readl(ssp->base + HW_SSP_STATUS(ssp)) & BM_SSP_STATUS_SDIO_IRQ) mmc_signal_sdio_irq(host->mmc); } else { writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK, - host->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); writel(BM_SSP_CTRL1_SDIO_IRQ_EN, - host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_CLR); + ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_CLR); } spin_unlock_irqrestore(&host->lock, flags); @@ -635,6 +640,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) dma_cap_mask_t mask; struct regulator *reg_vmmc; enum of_gpio_flags flags; + struct mxs_ssp *ssp; iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); @@ -648,14 +654,16 @@ static int mxs_mmc_probe(struct platform_device *pdev) return -ENOMEM; host = mmc_priv(mmc); - host->base = devm_request_and_ioremap(&pdev->dev, iores); - if (!host->base) { + ssp = &host->ssp; + ssp->dev = &pdev->dev; + ssp->base = devm_request_and_ioremap(&pdev->dev, iores); + if (!ssp->base) { ret = -EADDRNOTAVAIL; goto out_mmc_free; } if (np) { - host->devid = (enum mxs_ssp_id) of_id->data; + ssp->devid = (enum mxs_ssp_id) of_id->data; /* * TODO: This is a temporary solution and should be changed * to use generic DMA binding later when the helpers get in. @@ -668,7 +676,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) goto out_mmc_free; } } else { - host->devid = pdev->id_entry->driver_data; + ssp->devid = pdev->id_entry->driver_data; host->dma_channel = dmares->start; } @@ -691,12 +699,12 @@ static int mxs_mmc_probe(struct platform_device *pdev) goto out_mmc_free; } - host->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(host->clk)) { - ret = PTR_ERR(host->clk); + ssp->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(ssp->clk)) { + ret = PTR_ERR(ssp->clk); goto out_mmc_free; } - clk_prepare_enable(host->clk); + clk_prepare_enable(ssp->clk); mxs_mmc_reset(host); @@ -741,8 +749,8 @@ static int mxs_mmc_probe(struct platform_device *pdev) mmc->max_segs = 52; mmc->max_blk_size = 1 << 0xf; - mmc->max_blk_count = (ssp_is_old(host)) ? 0xff : 0xffffff; - mmc->max_req_size = (ssp_is_old(host)) ? 0xffff : 0xffffffff; + mmc->max_blk_count = (ssp_is_old(ssp)) ? 0xff : 0xffffff; + mmc->max_req_size = (ssp_is_old(ssp)) ? 0xffff : 0xffffffff; mmc->max_seg_size = dma_get_max_seg_size(host->dmach->device->dev); platform_set_drvdata(pdev, mmc); @@ -766,8 +774,8 @@ out_free_dma: if (host->dmach) dma_release_channel(host->dmach); out_clk_put: - clk_disable_unprepare(host->clk); - clk_put(host->clk); + clk_disable_unprepare(ssp->clk); + clk_put(ssp->clk); out_mmc_free: mmc_free_host(mmc); return ret; @@ -777,6 +785,7 @@ static int mxs_mmc_remove(struct platform_device *pdev) { struct mmc_host *mmc = platform_get_drvdata(pdev); struct mxs_mmc_host *host = mmc_priv(mmc); + struct mxs_ssp *ssp = &host->ssp; mmc_remove_host(mmc); @@ -785,8 +794,8 @@ static int mxs_mmc_remove(struct platform_device *pdev) if (host->dmach) dma_release_channel(host->dmach); - clk_disable_unprepare(host->clk); - clk_put(host->clk); + clk_disable_unprepare(ssp->clk); + clk_put(ssp->clk); mmc_free_host(mmc); @@ -798,11 +807,12 @@ static int mxs_mmc_suspend(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct mxs_mmc_host *host = mmc_priv(mmc); + struct mxs_ssp *ssp = &host->ssp; int ret = 0; ret = mmc_suspend_host(mmc); - clk_disable_unprepare(host->clk); + clk_disable_unprepare(ssp->clk); return ret; } @@ -811,9 +821,10 @@ static int mxs_mmc_resume(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct mxs_mmc_host *host = mmc_priv(mmc); + struct mxs_ssp *ssp = &host->ssp; int ret = 0; - clk_prepare_enable(host->clk); + clk_prepare_enable(ssp->clk); ret = mmc_resume_host(mmc); diff --git a/include/linux/spi/mxs-spi.h b/include/linux/spi/mxs-spi.h index 7dfa1d7d1a78..475f69fb896f 100644 --- a/include/linux/spi/mxs-spi.h +++ b/include/linux/spi/mxs-spi.h @@ -128,4 +128,12 @@ enum mxs_ssp_id { IMX28_SSP, }; +struct mxs_ssp { + struct device *dev; + void __iomem *base; + struct clk *clk; + unsigned int clk_rate; + enum mxs_ssp_id devid; +}; + #endif /* __LINUX_SPI_MXS_SPI_H__ */ -- cgit v1.2.3 From 1308239858c33feeeb67003d08c754ee181f33cf Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 3 Aug 2012 17:26:10 +0200 Subject: mmc: spi: Pull out the SSP clock configuration function Pull out the MMC clock configuration function and make it into SSP clock configuration function, so it can be used by the SPI driver too. Signed-off-by: Marek Vasut Acked-by: Chris Ball Acked-by: Shawn Guo Signed-off-by: Mark Brown --- drivers/clk/mxs/Makefile | 2 +- drivers/clk/mxs/clk-ssp.c | 62 +++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/host/mxs-mmc.c | 39 +--------------------------- include/linux/spi/mxs-spi.h | 2 ++ 4 files changed, 66 insertions(+), 39 deletions(-) create mode 100644 drivers/clk/mxs/clk-ssp.c (limited to 'include') diff --git a/drivers/clk/mxs/Makefile b/drivers/clk/mxs/Makefile index 7bedeec08524..a6a22237e860 100644 --- a/drivers/clk/mxs/Makefile +++ b/drivers/clk/mxs/Makefile @@ -2,7 +2,7 @@ # Makefile for mxs specific clk # -obj-y += clk.o clk-pll.o clk-ref.o clk-div.o clk-frac.o +obj-y += clk.o clk-pll.o clk-ref.o clk-div.o clk-frac.o clk-ssp.o obj-$(CONFIG_SOC_IMX23) += clk-imx23.o obj-$(CONFIG_SOC_IMX28) += clk-imx28.o diff --git a/drivers/clk/mxs/clk-ssp.c b/drivers/clk/mxs/clk-ssp.c new file mode 100644 index 000000000000..af7bdbf9ebd7 --- /dev/null +++ b/drivers/clk/mxs/clk-ssp.c @@ -0,0 +1,62 @@ +/* + * Copyright 2012 DENX Software Engineering, GmbH + * + * Pulled from code: + * Portions copyright (C) 2003 Russell King, PXA MMCI Driver + * Portions copyright (C) 2004-2005 Pierre Ossman, W83L51xD SD/MMC driver + * + * Copyright 2008 Embedded Alley Solutions, Inc. + * Copyright 2009-2011 Freescale Semiconductor, Inc. + * + * 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 +#include +#include +#include +#include +#include +#include + +void mxs_ssp_set_clk_rate(struct mxs_ssp *ssp, unsigned int rate) +{ + unsigned int ssp_clk, ssp_sck; + u32 clock_divide, clock_rate; + u32 val; + + ssp_clk = clk_get_rate(ssp->clk); + + for (clock_divide = 2; clock_divide <= 254; clock_divide += 2) { + clock_rate = DIV_ROUND_UP(ssp_clk, rate * clock_divide); + clock_rate = (clock_rate > 0) ? clock_rate - 1 : 0; + if (clock_rate <= 255) + break; + } + + if (clock_divide > 254) { + dev_err(ssp->dev, + "%s: cannot set clock to %d\n", __func__, rate); + return; + } + + ssp_sck = ssp_clk / clock_divide / (1 + clock_rate); + + val = readl(ssp->base + HW_SSP_TIMING(ssp)); + val &= ~(BM_SSP_TIMING_CLOCK_DIVIDE | BM_SSP_TIMING_CLOCK_RATE); + val |= BF_SSP(clock_divide, TIMING_CLOCK_DIVIDE); + val |= BF_SSP(clock_rate, TIMING_CLOCK_RATE); + writel(val, ssp->base + HW_SSP_TIMING(ssp)); + + ssp->clk_rate = ssp_sck; + + dev_dbg(ssp->dev, + "%s: clock_divide %d, clock_rate %d, ssp_clk %d, rate_actual %d, rate_requested %d\n", + __func__, clock_divide, clock_rate, ssp_clk, ssp_sck, rate); +} +EXPORT_SYMBOL_GPL(mxs_ssp_set_clk_rate); diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 7b85e035a4cb..0813340fa29c 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -501,43 +501,6 @@ static void mxs_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) mxs_mmc_start_cmd(host, mrq->cmd); } -static void mxs_mmc_set_clk_rate(struct mxs_mmc_host *host, unsigned int rate) -{ - struct mxs_ssp *ssp = &host->ssp; - unsigned int ssp_clk, ssp_sck; - u32 clock_divide, clock_rate; - u32 val; - - ssp_clk = clk_get_rate(ssp->clk); - - for (clock_divide = 2; clock_divide <= 254; clock_divide += 2) { - clock_rate = DIV_ROUND_UP(ssp_clk, rate * clock_divide); - clock_rate = (clock_rate > 0) ? clock_rate - 1 : 0; - if (clock_rate <= 255) - break; - } - - if (clock_divide > 254) { - dev_err(mmc_dev(host->mmc), - "%s: cannot set clock to %d\n", __func__, rate); - return; - } - - ssp_sck = ssp_clk / clock_divide / (1 + clock_rate); - - val = readl(ssp->base + HW_SSP_TIMING(ssp)); - val &= ~(BM_SSP_TIMING_CLOCK_DIVIDE | BM_SSP_TIMING_CLOCK_RATE); - val |= BF_SSP(clock_divide, TIMING_CLOCK_DIVIDE); - val |= BF_SSP(clock_rate, TIMING_CLOCK_RATE); - writel(val, ssp->base + HW_SSP_TIMING(ssp)); - - ssp->clk_rate = ssp_sck; - - dev_dbg(mmc_dev(host->mmc), - "%s: clock_divide %d, clock_rate %d, ssp_clk %d, rate_actual %d, rate_requested %d\n", - __func__, clock_divide, clock_rate, ssp_clk, ssp_sck, rate); -} - static void mxs_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct mxs_mmc_host *host = mmc_priv(mmc); @@ -550,7 +513,7 @@ static void mxs_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) host->bus_width = 0; if (ios->clock) - mxs_mmc_set_clk_rate(host, ios->clock); + mxs_ssp_set_clk_rate(&host->ssp, ios->clock); } static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) diff --git a/include/linux/spi/mxs-spi.h b/include/linux/spi/mxs-spi.h index 475f69fb896f..d07f8dc7fdd9 100644 --- a/include/linux/spi/mxs-spi.h +++ b/include/linux/spi/mxs-spi.h @@ -136,4 +136,6 @@ struct mxs_ssp { enum mxs_ssp_id devid; }; +void mxs_ssp_set_clk_rate(struct mxs_ssp *ssp, unsigned int rate); + #endif /* __LINUX_SPI_MXS_SPI_H__ */ -- cgit v1.2.3 From 65defb9b3ba67c1d6f88ac62c24644eb23a7b676 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 3 Aug 2012 17:26:12 +0200 Subject: mmc: spi: Pull out common DMA parts from MXS MMC These parts will be used by the MXS SPI driver too. Signed-off-by: Marek Vasut Acked-by: Chris Ball Acked-by: Shawn Guo Signed-off-by: Mark Brown --- drivers/mmc/host/mxs-mmc.c | 88 ++++++++++++++++++++++----------------------- drivers/spi/spi-mxs.c | 1 - include/linux/spi/mxs-spi.h | 9 +++++ 3 files changed, 51 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 0813340fa29c..4da996654772 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -41,7 +41,6 @@ #include #include #include -#include #include #include #include @@ -69,13 +68,6 @@ struct mxs_mmc_host { struct mmc_command *cmd; struct mmc_data *data; - int dma_channel; - struct dma_chan *dmach; - struct mxs_dma_data dma_data; - unsigned int dma_dir; - enum dma_transfer_direction slave_dirn; - u32 ssp_pio_words[SSP_PIO_NUM]; - unsigned char bus_width; spinlock_t lock; int sdio_irq_en; @@ -163,7 +155,7 @@ static void mxs_mmc_request_done(struct mxs_mmc_host *host) if (data) { dma_unmap_sg(mmc_dev(host->mmc), data->sg, - data->sg_len, host->dma_dir); + data->sg_len, ssp->dma_dir); /* * If there was an error on any block, we mark all * data blocks as being in error. @@ -232,6 +224,7 @@ static irqreturn_t mxs_mmc_irq_handler(int irq, void *dev_id) static struct dma_async_tx_descriptor *mxs_mmc_prep_dma( struct mxs_mmc_host *host, unsigned long flags) { + struct mxs_ssp *ssp = &host->ssp; struct dma_async_tx_descriptor *desc; struct mmc_data *data = host->data; struct scatterlist * sgl; @@ -240,24 +233,24 @@ static struct dma_async_tx_descriptor *mxs_mmc_prep_dma( if (data) { /* data */ dma_map_sg(mmc_dev(host->mmc), data->sg, - data->sg_len, host->dma_dir); + data->sg_len, ssp->dma_dir); sgl = data->sg; sg_len = data->sg_len; } else { /* pio */ - sgl = (struct scatterlist *) host->ssp_pio_words; + sgl = (struct scatterlist *) ssp->ssp_pio_words; sg_len = SSP_PIO_NUM; } - desc = dmaengine_prep_slave_sg(host->dmach, - sgl, sg_len, host->slave_dirn, flags); + desc = dmaengine_prep_slave_sg(ssp->dmach, + sgl, sg_len, ssp->slave_dirn, flags); if (desc) { desc->callback = mxs_mmc_dma_irq_callback; desc->callback_param = host; } else { if (data) dma_unmap_sg(mmc_dev(host->mmc), data->sg, - data->sg_len, host->dma_dir); + data->sg_len, ssp->dma_dir); } return desc; @@ -265,6 +258,7 @@ static struct dma_async_tx_descriptor *mxs_mmc_prep_dma( static void mxs_mmc_bc(struct mxs_mmc_host *host) { + struct mxs_ssp *ssp = &host->ssp; struct mmc_command *cmd = host->cmd; struct dma_async_tx_descriptor *desc; u32 ctrl0, cmd0, cmd1; @@ -278,17 +272,17 @@ static void mxs_mmc_bc(struct mxs_mmc_host *host) cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN | BM_SSP_CMD0_SLOW_CLKING_EN; } - host->ssp_pio_words[0] = ctrl0; - host->ssp_pio_words[1] = cmd0; - host->ssp_pio_words[2] = cmd1; - host->dma_dir = DMA_NONE; - host->slave_dirn = DMA_TRANS_NONE; + ssp->ssp_pio_words[0] = ctrl0; + ssp->ssp_pio_words[1] = cmd0; + ssp->ssp_pio_words[2] = cmd1; + ssp->dma_dir = DMA_NONE; + ssp->slave_dirn = DMA_TRANS_NONE; desc = mxs_mmc_prep_dma(host, DMA_CTRL_ACK); if (!desc) goto out; dmaengine_submit(desc); - dma_async_issue_pending(host->dmach); + dma_async_issue_pending(ssp->dmach); return; out: @@ -298,6 +292,7 @@ out: static void mxs_mmc_ac(struct mxs_mmc_host *host) { + struct mxs_ssp *ssp = &host->ssp; struct mmc_command *cmd = host->cmd; struct dma_async_tx_descriptor *desc; u32 ignore_crc, get_resp, long_resp; @@ -319,17 +314,17 @@ static void mxs_mmc_ac(struct mxs_mmc_host *host) cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN | BM_SSP_CMD0_SLOW_CLKING_EN; } - host->ssp_pio_words[0] = ctrl0; - host->ssp_pio_words[1] = cmd0; - host->ssp_pio_words[2] = cmd1; - host->dma_dir = DMA_NONE; - host->slave_dirn = DMA_TRANS_NONE; + ssp->ssp_pio_words[0] = ctrl0; + ssp->ssp_pio_words[1] = cmd0; + ssp->ssp_pio_words[2] = cmd1; + ssp->dma_dir = DMA_NONE; + ssp->slave_dirn = DMA_TRANS_NONE; desc = mxs_mmc_prep_dma(host, DMA_CTRL_ACK); if (!desc) goto out; dmaengine_submit(desc); - dma_async_issue_pending(host->dmach); + dma_async_issue_pending(ssp->dmach); return; out: @@ -441,11 +436,11 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host) writel(val, ssp->base + HW_SSP_TIMING(ssp)); /* pio */ - host->ssp_pio_words[0] = ctrl0; - host->ssp_pio_words[1] = cmd0; - host->ssp_pio_words[2] = cmd1; - host->dma_dir = DMA_NONE; - host->slave_dirn = DMA_TRANS_NONE; + ssp->ssp_pio_words[0] = ctrl0; + ssp->ssp_pio_words[1] = cmd0; + ssp->ssp_pio_words[2] = cmd1; + ssp->dma_dir = DMA_NONE; + ssp->slave_dirn = DMA_TRANS_NONE; desc = mxs_mmc_prep_dma(host, 0); if (!desc) goto out; @@ -453,14 +448,14 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host) /* append data sg */ WARN_ON(host->data != NULL); host->data = data; - host->dma_dir = dma_data_dir; - host->slave_dirn = slave_dirn; + ssp->dma_dir = dma_data_dir; + ssp->slave_dirn = slave_dirn; desc = mxs_mmc_prep_dma(host, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) goto out; dmaengine_submit(desc); - dma_async_issue_pending(host->dmach); + dma_async_issue_pending(ssp->dmach); return; out: dev_warn(mmc_dev(host->mmc), @@ -557,14 +552,15 @@ static const struct mmc_host_ops mxs_mmc_ops = { static bool mxs_mmc_dma_filter(struct dma_chan *chan, void *param) { struct mxs_mmc_host *host = param; + struct mxs_ssp *ssp = &host->ssp; if (!mxs_dma_is_apbh(chan)) return false; - if (chan->chan_id != host->dma_channel) + if (chan->chan_id != ssp->dma_channel) return false; - chan->private = &host->dma_data; + chan->private = &ssp->dma_data; return true; } @@ -632,7 +628,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) * to use generic DMA binding later when the helpers get in. */ ret = of_property_read_u32(np, "fsl,ssp-dma-channel", - &host->dma_channel); + &ssp->dma_channel); if (ret) { dev_err(mmc_dev(host->mmc), "failed to get dma channel\n"); @@ -640,7 +636,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) } } else { ssp->devid = pdev->id_entry->driver_data; - host->dma_channel = dmares->start; + ssp->dma_channel = dmares->start; } host->mmc = mmc; @@ -673,9 +669,9 @@ static int mxs_mmc_probe(struct platform_device *pdev) dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - host->dma_data.chan_irq = irq_dma; - host->dmach = dma_request_channel(mask, mxs_mmc_dma_filter, host); - if (!host->dmach) { + ssp->dma_data.chan_irq = irq_dma; + ssp->dmach = dma_request_channel(mask, mxs_mmc_dma_filter, host); + if (!ssp->dmach) { dev_err(mmc_dev(host->mmc), "%s: failed to request dma\n", __func__); goto out_clk_put; @@ -714,7 +710,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) mmc->max_blk_size = 1 << 0xf; mmc->max_blk_count = (ssp_is_old(ssp)) ? 0xff : 0xffffff; mmc->max_req_size = (ssp_is_old(ssp)) ? 0xffff : 0xffffffff; - mmc->max_seg_size = dma_get_max_seg_size(host->dmach->device->dev); + mmc->max_seg_size = dma_get_max_seg_size(ssp->dmach->device->dev); platform_set_drvdata(pdev, mmc); @@ -734,8 +730,8 @@ static int mxs_mmc_probe(struct platform_device *pdev) return 0; out_free_dma: - if (host->dmach) - dma_release_channel(host->dmach); + if (ssp->dmach) + dma_release_channel(ssp->dmach); out_clk_put: clk_disable_unprepare(ssp->clk); clk_put(ssp->clk); @@ -754,8 +750,8 @@ static int mxs_mmc_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); - if (host->dmach) - dma_release_channel(host->dmach); + if (ssp->dmach) + dma_release_channel(ssp->dmach); clk_disable_unprepare(ssp->clk); clk_put(ssp->clk); diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 7bf826f5af59..0f28afb80310 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #include diff --git a/include/linux/spi/mxs-spi.h b/include/linux/spi/mxs-spi.h index d07f8dc7fdd9..61ae1306db23 100644 --- a/include/linux/spi/mxs-spi.h +++ b/include/linux/spi/mxs-spi.h @@ -24,6 +24,8 @@ #ifndef __LINUX_SPI_MXS_SPI_H__ #define __LINUX_SPI_MXS_SPI_H__ +#include + #define ssp_is_old(host) ((host)->devid == IMX23_SSP) /* SSP registers */ @@ -134,6 +136,13 @@ struct mxs_ssp { struct clk *clk; unsigned int clk_rate; enum mxs_ssp_id devid; + + int dma_channel; + struct dma_chan *dmach; + struct mxs_dma_data dma_data; + unsigned int dma_dir; + enum dma_transfer_direction slave_dirn; + u32 ssp_pio_words[SSP_PIO_NUM]; }; void mxs_ssp_set_clk_rate(struct mxs_ssp *ssp, unsigned int rate); -- cgit v1.2.3 From 8513915accc611e576dbebb93422c257e7e68be8 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 18 Aug 2012 17:43:05 -0700 Subject: ALSA: fix pcm.h kernel-doc warning and notation Fix kernel-doc warning in and add function name to make the kernel-doc notation complete. Warning(include/sound/pcm.h:1081): No description found for parameter 'substream' Signed-off-by: Randy Dunlap Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index c75c0d1a85e2..cdca2ab1e711 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -1075,7 +1075,8 @@ static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max) const char *snd_pcm_format_name(snd_pcm_format_t format); /** - * Get a string naming the direction of a stream + * snd_pcm_stream_str - Get a string naming the direction of a stream + * @substream: the pcm substream instance */ static inline const char *snd_pcm_stream_str(struct snd_pcm_substream *substream) { -- cgit v1.2.3 From 3296193d1421c2d6f9e49e181cecfd917f0f5764 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Tue, 14 Aug 2012 13:20:23 +0000 Subject: dt: introduce for_each_available_child_of_node, of_get_next_available_child Macro for_each_child_of_node() makes it easy to iterate over all of the children for a given device tree node, including those nodes that are marked as unavailable (i.e. status = "disabled"). Introduce for_each_available_child_of_node(), which is like for_each_child_of_node(), but it automatically skips unavailable nodes. This also requires the introduction of helper function of_get_next_available_child(), which returns the next available child node. Signed-off-by: Timur Tabi Signed-off-by: David S. Miller --- drivers/of/base.c | 27 +++++++++++++++++++++++++++ include/linux/of.h | 7 +++++++ 2 files changed, 34 insertions(+) (limited to 'include') diff --git a/drivers/of/base.c b/drivers/of/base.c index c181b94abc36..d4a1c9a043e1 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -363,6 +363,33 @@ struct device_node *of_get_next_child(const struct device_node *node, } EXPORT_SYMBOL(of_get_next_child); +/** + * of_get_next_available_child - Find the next available child node + * @node: parent node + * @prev: previous child of the parent node, or NULL to get first + * + * This function is like of_get_next_child(), except that it + * automatically skips any disabled nodes (i.e. status = "disabled"). + */ +struct device_node *of_get_next_available_child(const struct device_node *node, + struct device_node *prev) +{ + struct device_node *next; + + read_lock(&devtree_lock); + next = prev ? prev->sibling : node->child; + for (; next; next = next->sibling) { + if (!of_device_is_available(next)) + continue; + if (of_node_get(next)) + break; + } + of_node_put(prev); + read_unlock(&devtree_lock); + return next; +} +EXPORT_SYMBOL(of_get_next_available_child); + /** * of_find_node_by_path - Find a node matching a full OF path * @path: The full path to match diff --git a/include/linux/of.h b/include/linux/of.h index 5919ee33f2b7..1b1163225f3b 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -190,10 +190,17 @@ extern struct device_node *of_get_parent(const struct device_node *node); extern struct device_node *of_get_next_parent(struct device_node *node); extern struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev); +extern struct device_node *of_get_next_available_child( + const struct device_node *node, struct device_node *prev); + #define for_each_child_of_node(parent, child) \ for (child = of_get_next_child(parent, NULL); child != NULL; \ child = of_get_next_child(parent, child)) +#define for_each_available_child_of_node(parent, child) \ + for (child = of_get_next_available_child(parent, NULL); child != NULL; \ + child = of_get_next_available_child(parent, child)) + static inline int of_get_child_count(const struct device_node *np) { struct device_node *child; -- cgit v1.2.3 From 16f01365fa01150bf3606fe702a80a03ec87953a Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Thu, 16 Aug 2012 05:34:22 +0000 Subject: packet: Report rings cfg via diag engine One extension bit may result in two nlattrs -- one per ring type. If some ring type is not configured, then the respective nlatts will be empty. The structure reported contains the data, that is given to the corresponding ring setup socket option. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/packet_diag.h | 13 ++++++++++++ net/packet/diag.c | 48 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/packet_diag.h b/include/linux/packet_diag.h index ea2e8923bd7e..34ade828979b 100644 --- a/include/linux/packet_diag.h +++ b/include/linux/packet_diag.h @@ -14,6 +14,7 @@ struct packet_diag_req { #define PACKET_SHOW_INFO 0x00000001 /* Basic packet_sk information */ #define PACKET_SHOW_MCLIST 0x00000002 /* A set of packet_diag_mclist-s */ +#define PACKET_SHOW_RING_CFG 0x00000004 /* Rings configuration parameters */ struct packet_diag_msg { __u8 pdiag_family; @@ -27,6 +28,8 @@ struct packet_diag_msg { enum { PACKET_DIAG_INFO, PACKET_DIAG_MCLIST, + PACKET_DIAG_RX_RING, + PACKET_DIAG_TX_RING, PACKET_DIAG_MAX, }; @@ -54,4 +57,14 @@ struct packet_diag_mclist { __u8 pdmc_addr[MAX_ADDR_LEN]; }; +struct packet_diag_ring { + __u32 pdr_block_size; + __u32 pdr_block_nr; + __u32 pdr_frame_size; + __u32 pdr_frame_nr; + __u32 pdr_retire_tmo; + __u32 pdr_sizeof_priv; + __u32 pdr_features; +}; + #endif diff --git a/net/packet/diag.c b/net/packet/diag.c index 3dda4ecb9a46..e3975e4d458c 100644 --- a/net/packet/diag.c +++ b/net/packet/diag.c @@ -67,12 +67,54 @@ static int pdiag_put_mclist(const struct packet_sock *po, struct sk_buff *nlskb) return 0; } +static int pdiag_put_ring(struct packet_ring_buffer *ring, int ver, int nl_type, + struct sk_buff *nlskb) +{ + struct packet_diag_ring pdr; + + if (!ring->pg_vec || ((ver > TPACKET_V2) && + (nl_type == PACKET_DIAG_TX_RING))) + return 0; + + pdr.pdr_block_size = ring->pg_vec_pages << PAGE_SHIFT; + pdr.pdr_block_nr = ring->pg_vec_len; + pdr.pdr_frame_size = ring->frame_size; + pdr.pdr_frame_nr = ring->frame_max + 1; + + if (ver > TPACKET_V2) { + pdr.pdr_retire_tmo = ring->prb_bdqc.retire_blk_tov; + pdr.pdr_sizeof_priv = ring->prb_bdqc.blk_sizeof_priv; + pdr.pdr_features = ring->prb_bdqc.feature_req_word; + } else { + pdr.pdr_retire_tmo = 0; + pdr.pdr_sizeof_priv = 0; + pdr.pdr_features = 0; + } + + return nla_put(nlskb, nl_type, sizeof(pdr), &pdr); +} + +static int pdiag_put_rings_cfg(struct packet_sock *po, struct sk_buff *skb) +{ + int ret; + + mutex_lock(&po->pg_vec_lock); + ret = pdiag_put_ring(&po->rx_ring, po->tp_version, + PACKET_DIAG_RX_RING, skb); + if (!ret) + ret = pdiag_put_ring(&po->tx_ring, po->tp_version, + PACKET_DIAG_TX_RING, skb); + mutex_unlock(&po->pg_vec_lock); + + return ret; +} + static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct packet_diag_req *req, u32 pid, u32 seq, u32 flags, int sk_ino) { struct nlmsghdr *nlh; struct packet_diag_msg *rp; - const struct packet_sock *po = pkt_sk(sk); + struct packet_sock *po = pkt_sk(sk); nlh = nlmsg_put(skb, pid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rp), flags); if (!nlh) @@ -93,6 +135,10 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct packet_diag pdiag_put_mclist(po, skb)) goto out_nlmsg_trim; + if ((req->pdiag_show & PACKET_SHOW_RING_CFG) && + pdiag_put_rings_cfg(po, skb)) + goto out_nlmsg_trim; + return nlmsg_end(skb, nlh); out_nlmsg_trim: -- cgit v1.2.3 From fff3321d75b1a18231876a1aceb36eacbbf6221e Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Thu, 16 Aug 2012 05:36:48 +0000 Subject: packet: Report fanout status via diag engine Reported value is the same reported by the FANOUT getsockoption, but unlike it, the absent fanout setup results in absent nlattr, rather than in nlattr with zero value. This is done so, since zero fanout report may mean both -- no fanout, and fanout with both id and type zero. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/packet_diag.h | 2 ++ net/packet/af_packet.c | 23 +++-------------------- net/packet/diag.c | 20 ++++++++++++++++++++ net/packet/internal.h | 20 +++++++++++++++++++- 4 files changed, 44 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/linux/packet_diag.h b/include/linux/packet_diag.h index 34ade828979b..93f5fa94a431 100644 --- a/include/linux/packet_diag.h +++ b/include/linux/packet_diag.h @@ -15,6 +15,7 @@ struct packet_diag_req { #define PACKET_SHOW_INFO 0x00000001 /* Basic packet_sk information */ #define PACKET_SHOW_MCLIST 0x00000002 /* A set of packet_diag_mclist-s */ #define PACKET_SHOW_RING_CFG 0x00000004 /* Rings configuration parameters */ +#define PACKET_SHOW_FANOUT 0x00000008 struct packet_diag_msg { __u8 pdiag_family; @@ -30,6 +31,7 @@ enum { PACKET_DIAG_MCLIST, PACKET_DIAG_RX_RING, PACKET_DIAG_TX_RING, + PACKET_DIAG_FANOUT, PACKET_DIAG_MAX, }; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 8a1605ae4029..226b2cdfc339 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -207,24 +207,6 @@ static void prb_fill_vlan_info(struct tpacket_kbdq_core *, struct tpacket3_hdr *); static void packet_flush_mclist(struct sock *sk); -#define PACKET_FANOUT_MAX 256 - -struct packet_fanout { -#ifdef CONFIG_NET_NS - struct net *net; -#endif - unsigned int num_members; - u16 id; - u8 type; - u8 defrag; - atomic_t rr_cur; - struct list_head list; - struct sock *arr[PACKET_FANOUT_MAX]; - spinlock_t lock; - atomic_t sk_ref; - struct packet_type prot_hook ____cacheline_aligned_in_smp; -}; - struct packet_skb_cb { unsigned int origlen; union { @@ -1148,7 +1130,8 @@ static int packet_rcv_fanout(struct sk_buff *skb, struct net_device *dev, return po->prot_hook.func(skb, dev, &po->prot_hook, orig_dev); } -static DEFINE_MUTEX(fanout_mutex); +DEFINE_MUTEX(fanout_mutex); +EXPORT_SYMBOL_GPL(fanout_mutex); static LIST_HEAD(fanout_list); static void __fanout_link(struct sock *sk, struct packet_sock *po) @@ -1260,9 +1243,9 @@ static void fanout_release(struct sock *sk) if (!f) return; + mutex_lock(&fanout_mutex); po->fanout = NULL; - mutex_lock(&fanout_mutex); if (atomic_dec_and_test(&f->sk_ref)) { list_del(&f->list); dev_remove_pack(&f->prot_hook); diff --git a/net/packet/diag.c b/net/packet/diag.c index e3975e4d458c..bc33fbe8a5ef 100644 --- a/net/packet/diag.c +++ b/net/packet/diag.c @@ -109,6 +109,22 @@ static int pdiag_put_rings_cfg(struct packet_sock *po, struct sk_buff *skb) return ret; } +static int pdiag_put_fanout(struct packet_sock *po, struct sk_buff *nlskb) +{ + int ret = 0; + + mutex_lock(&fanout_mutex); + if (po->fanout) { + u32 val; + + val = (u32)po->fanout->id | ((u32)po->fanout->type << 16); + ret = nla_put_u32(nlskb, PACKET_DIAG_FANOUT, val); + } + mutex_unlock(&fanout_mutex); + + return ret; +} + static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct packet_diag_req *req, u32 pid, u32 seq, u32 flags, int sk_ino) { @@ -139,6 +155,10 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct packet_diag pdiag_put_rings_cfg(po, skb)) goto out_nlmsg_trim; + if ((req->pdiag_show & PACKET_SHOW_FANOUT) && + pdiag_put_fanout(po, skb)) + goto out_nlmsg_trim; + return nlmsg_end(skb, nlh); out_nlmsg_trim: diff --git a/net/packet/internal.h b/net/packet/internal.h index 2c5fca28b242..44945f6b7252 100644 --- a/net/packet/internal.h +++ b/net/packet/internal.h @@ -67,7 +67,25 @@ struct packet_ring_buffer { atomic_t pending; }; -struct packet_fanout; +extern struct mutex fanout_mutex; +#define PACKET_FANOUT_MAX 256 + +struct packet_fanout { +#ifdef CONFIG_NET_NS + struct net *net; +#endif + unsigned int num_members; + u16 id; + u8 type; + u8 defrag; + atomic_t rr_cur; + struct list_head list; + struct sock *arr[PACKET_FANOUT_MAX]; + spinlock_t lock; + atomic_t sk_ref; + struct packet_type prot_hook ____cacheline_aligned_in_smp; +}; + struct packet_sock { /* struct sock has to be the first member of packet_sock */ struct sock sk; -- cgit v1.2.3 From 34f256cc7962a44537a0d33877cd93c89873098e Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Thu, 16 Aug 2012 12:09:13 +0000 Subject: tipc: eliminate configuration for maximum number of name subscriptions Gets rid of the need for users to specify the maximum number of name subscriptions supported by TIPC. TIPC now automatically provides support for the maximum number of name subscriptions to 65535. Signed-off-by: Ying Xue Signed-off-by: Jon Maloy Signed-off-by: Paul Gortmaker Signed-off-by: David S. Miller --- include/linux/tipc_config.h | 4 ++-- net/tipc/config.c | 25 +++---------------------- net/tipc/core.c | 2 -- net/tipc/core.h | 4 ++-- net/tipc/subscr.c | 4 ++-- 5 files changed, 9 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/linux/tipc_config.h b/include/linux/tipc_config.h index c98928420100..8dd8305c26c5 100644 --- a/include/linux/tipc_config.h +++ b/include/linux/tipc_config.h @@ -90,7 +90,7 @@ #define TIPC_CMD_GET_REMOTE_MNG 0x4003 /* tx none, rx unsigned */ #define TIPC_CMD_GET_MAX_PORTS 0x4004 /* tx none, rx unsigned */ #define TIPC_CMD_GET_MAX_PUBL 0x4005 /* tx none, rx unsigned */ -#define TIPC_CMD_GET_MAX_SUBSCR 0x4006 /* tx none, rx unsigned */ +#define TIPC_CMD_GET_MAX_SUBSCR 0x4006 /* obsoleted */ #define TIPC_CMD_GET_MAX_ZONES 0x4007 /* obsoleted */ #define TIPC_CMD_GET_MAX_CLUSTERS 0x4008 /* obsoleted */ #define TIPC_CMD_GET_MAX_NODES 0x4009 /* obsoleted */ @@ -116,7 +116,7 @@ #define TIPC_CMD_SET_REMOTE_MNG 0x8003 /* tx unsigned, rx none */ #define TIPC_CMD_SET_MAX_PORTS 0x8004 /* tx unsigned, rx none */ #define TIPC_CMD_SET_MAX_PUBL 0x8005 /* tx unsigned, rx none */ -#define TIPC_CMD_SET_MAX_SUBSCR 0x8006 /* tx unsigned, rx none */ +#define TIPC_CMD_SET_MAX_SUBSCR 0x8006 /* obsoleted */ #define TIPC_CMD_SET_MAX_ZONES 0x8007 /* obsoleted */ #define TIPC_CMD_SET_MAX_CLUSTERS 0x8008 /* obsoleted */ #define TIPC_CMD_SET_MAX_NODES 0x8009 /* obsoleted */ diff --git a/net/tipc/config.c b/net/tipc/config.c index a056a3852f71..7a1275863c8a 100644 --- a/net/tipc/config.c +++ b/net/tipc/config.c @@ -2,7 +2,7 @@ * net/tipc/config.c: TIPC configuration management code * * Copyright (c) 2002-2006, Ericsson AB - * Copyright (c) 2004-2007, 2010-2011, Wind River Systems + * Copyright (c) 2004-2007, 2010-2012, Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -223,21 +223,6 @@ static struct sk_buff *cfg_set_max_publications(void) return tipc_cfg_reply_none(); } -static struct sk_buff *cfg_set_max_subscriptions(void) -{ - u32 value; - - if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED)) - return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); - - value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area)); - if (value < 1 || value > 65535) - return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE - " (max subscriptions must be 1-65535"); - tipc_max_subscriptions = value; - return tipc_cfg_reply_none(); -} - static struct sk_buff *cfg_set_max_ports(void) { u32 value; @@ -360,9 +345,6 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area case TIPC_CMD_SET_MAX_PUBL: rep_tlv_buf = cfg_set_max_publications(); break; - case TIPC_CMD_SET_MAX_SUBSCR: - rep_tlv_buf = cfg_set_max_subscriptions(); - break; case TIPC_CMD_SET_NETID: rep_tlv_buf = cfg_set_netid(); break; @@ -375,9 +357,6 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area case TIPC_CMD_GET_MAX_PUBL: rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_publications); break; - case TIPC_CMD_GET_MAX_SUBSCR: - rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_subscriptions); - break; case TIPC_CMD_GET_NETID: rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_net_id); break; @@ -393,6 +372,8 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area case TIPC_CMD_GET_MAX_CLUSTERS: case TIPC_CMD_SET_MAX_NODES: case TIPC_CMD_GET_MAX_NODES: + case TIPC_CMD_SET_MAX_SUBSCR: + case TIPC_CMD_GET_MAX_SUBSCR: case TIPC_CMD_SET_LOG_SIZE: case TIPC_CMD_DUMP_LOG: rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED diff --git a/net/tipc/core.c b/net/tipc/core.c index b858f2003523..73e5eac20735 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -53,7 +53,6 @@ int tipc_random __read_mostly; /* configurable TIPC parameters */ u32 tipc_own_addr __read_mostly; int tipc_max_ports __read_mostly; -int tipc_max_subscriptions; int tipc_max_publications; int tipc_net_id __read_mostly; int tipc_remote_management __read_mostly; @@ -157,7 +156,6 @@ static int __init tipc_init(void) tipc_own_addr = 0; tipc_remote_management = 1; tipc_max_publications = 10000; - tipc_max_subscriptions = 2000; tipc_max_ports = CONFIG_TIPC_PORTS; tipc_net_id = 4711; diff --git a/net/tipc/core.h b/net/tipc/core.h index 4c5705ac8a5a..ef01412b0989 100644 --- a/net/tipc/core.h +++ b/net/tipc/core.h @@ -60,7 +60,8 @@ #define TIPC_MOD_VER "2.0.0" -#define ULTRA_STRING_MAX_LEN 32768 +#define ULTRA_STRING_MAX_LEN 32768 +#define TIPC_MAX_SUBSCRIPTIONS 65535 struct tipc_msg; /* msg.h */ @@ -76,7 +77,6 @@ int tipc_snprintf(char *buf, int len, const char *fmt, ...); */ extern u32 tipc_own_addr __read_mostly; extern int tipc_max_ports __read_mostly; -extern int tipc_max_subscriptions; extern int tipc_max_publications; extern int tipc_net_id __read_mostly; extern int tipc_remote_management __read_mostly; diff --git a/net/tipc/subscr.c b/net/tipc/subscr.c index 5ed5965eb0be..0f7d0d007e22 100644 --- a/net/tipc/subscr.c +++ b/net/tipc/subscr.c @@ -304,9 +304,9 @@ static struct tipc_subscription *subscr_subscribe(struct tipc_subscr *s, } /* Refuse subscription if global limit exceeded */ - if (atomic_read(&topsrv.subscription_count) >= tipc_max_subscriptions) { + if (atomic_read(&topsrv.subscription_count) >= TIPC_MAX_SUBSCRIPTIONS) { pr_warn("Subscription rejected, limit reached (%u)\n", - tipc_max_subscriptions); + TIPC_MAX_SUBSCRIPTIONS); subscr_terminate(subscriber); return NULL; } -- cgit v1.2.3 From e6a04b1d3ff9d5af219b2fcaebe0ef04733d597c Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Thu, 16 Aug 2012 12:09:14 +0000 Subject: tipc: eliminate configuration for maximum number of name publications Gets rid of the need for users to specify the maximum number of name publications supported by TIPC. TIPC now automatically provides support for the maximum number of name publications to 65535. Signed-off-by: Ying Xue Signed-off-by: Jon Maloy Signed-off-by: Paul Gortmaker Signed-off-by: David S. Miller --- include/linux/tipc_config.h | 4 ++-- net/tipc/config.c | 23 ++--------------------- net/tipc/core.c | 2 -- net/tipc/core.h | 2 +- net/tipc/name_table.c | 4 ++-- 5 files changed, 7 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/include/linux/tipc_config.h b/include/linux/tipc_config.h index 8dd8305c26c5..0b1e3f218a36 100644 --- a/include/linux/tipc_config.h +++ b/include/linux/tipc_config.h @@ -89,7 +89,7 @@ #define TIPC_CMD_GET_REMOTE_MNG 0x4003 /* tx none, rx unsigned */ #define TIPC_CMD_GET_MAX_PORTS 0x4004 /* tx none, rx unsigned */ -#define TIPC_CMD_GET_MAX_PUBL 0x4005 /* tx none, rx unsigned */ +#define TIPC_CMD_GET_MAX_PUBL 0x4005 /* obsoleted */ #define TIPC_CMD_GET_MAX_SUBSCR 0x4006 /* obsoleted */ #define TIPC_CMD_GET_MAX_ZONES 0x4007 /* obsoleted */ #define TIPC_CMD_GET_MAX_CLUSTERS 0x4008 /* obsoleted */ @@ -115,7 +115,7 @@ #define TIPC_CMD_SET_NODE_ADDR 0x8001 /* tx net_addr, rx none */ #define TIPC_CMD_SET_REMOTE_MNG 0x8003 /* tx unsigned, rx none */ #define TIPC_CMD_SET_MAX_PORTS 0x8004 /* tx unsigned, rx none */ -#define TIPC_CMD_SET_MAX_PUBL 0x8005 /* tx unsigned, rx none */ +#define TIPC_CMD_SET_MAX_PUBL 0x8005 /* obsoleted */ #define TIPC_CMD_SET_MAX_SUBSCR 0x8006 /* obsoleted */ #define TIPC_CMD_SET_MAX_ZONES 0x8007 /* obsoleted */ #define TIPC_CMD_SET_MAX_CLUSTERS 0x8008 /* obsoleted */ diff --git a/net/tipc/config.c b/net/tipc/config.c index 7a1275863c8a..f67866c765dd 100644 --- a/net/tipc/config.c +++ b/net/tipc/config.c @@ -208,21 +208,6 @@ static struct sk_buff *cfg_set_remote_mng(void) return tipc_cfg_reply_none(); } -static struct sk_buff *cfg_set_max_publications(void) -{ - u32 value; - - if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED)) - return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); - - value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area)); - if (value < 1 || value > 65535) - return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE - " (max publications must be 1-65535)"); - tipc_max_publications = value; - return tipc_cfg_reply_none(); -} - static struct sk_buff *cfg_set_max_ports(void) { u32 value; @@ -342,9 +327,6 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area case TIPC_CMD_SET_MAX_PORTS: rep_tlv_buf = cfg_set_max_ports(); break; - case TIPC_CMD_SET_MAX_PUBL: - rep_tlv_buf = cfg_set_max_publications(); - break; case TIPC_CMD_SET_NETID: rep_tlv_buf = cfg_set_netid(); break; @@ -354,9 +336,6 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area case TIPC_CMD_GET_MAX_PORTS: rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_ports); break; - case TIPC_CMD_GET_MAX_PUBL: - rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_publications); - break; case TIPC_CMD_GET_NETID: rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_net_id); break; @@ -374,6 +353,8 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area case TIPC_CMD_GET_MAX_NODES: case TIPC_CMD_SET_MAX_SUBSCR: case TIPC_CMD_GET_MAX_SUBSCR: + case TIPC_CMD_SET_MAX_PUBL: + case TIPC_CMD_GET_MAX_PUBL: case TIPC_CMD_SET_LOG_SIZE: case TIPC_CMD_DUMP_LOG: rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED diff --git a/net/tipc/core.c b/net/tipc/core.c index 73e5eac20735..bfe8af88469a 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -53,7 +53,6 @@ int tipc_random __read_mostly; /* configurable TIPC parameters */ u32 tipc_own_addr __read_mostly; int tipc_max_ports __read_mostly; -int tipc_max_publications; int tipc_net_id __read_mostly; int tipc_remote_management __read_mostly; @@ -155,7 +154,6 @@ static int __init tipc_init(void) tipc_own_addr = 0; tipc_remote_management = 1; - tipc_max_publications = 10000; tipc_max_ports = CONFIG_TIPC_PORTS; tipc_net_id = 4711; diff --git a/net/tipc/core.h b/net/tipc/core.h index ef01412b0989..0207db04179a 100644 --- a/net/tipc/core.h +++ b/net/tipc/core.h @@ -62,6 +62,7 @@ #define ULTRA_STRING_MAX_LEN 32768 #define TIPC_MAX_SUBSCRIPTIONS 65535 +#define TIPC_MAX_PUBLICATIONS 65535 struct tipc_msg; /* msg.h */ @@ -77,7 +78,6 @@ int tipc_snprintf(char *buf, int len, const char *fmt, ...); */ extern u32 tipc_own_addr __read_mostly; extern int tipc_max_ports __read_mostly; -extern int tipc_max_publications; extern int tipc_net_id __read_mostly; extern int tipc_remote_management __read_mostly; diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c index 4ebdcc96cb04..98975e80bb51 100644 --- a/net/tipc/name_table.c +++ b/net/tipc/name_table.c @@ -667,9 +667,9 @@ struct publication *tipc_nametbl_publish(u32 type, u32 lower, u32 upper, { struct publication *publ; - if (table.local_publ_count >= tipc_max_publications) { + if (table.local_publ_count >= TIPC_MAX_PUBLICATIONS) { pr_warn("Publication failed, local publication limit reached (%u)\n", - tipc_max_publications); + TIPC_MAX_PUBLICATIONS); return NULL; } -- cgit v1.2.3 From c0de08d04215031d68fa13af36f347a6cfa252ca Mon Sep 17 00:00:00 2001 From: Eric Leblond Date: Thu, 16 Aug 2012 22:02:58 +0000 Subject: af_packet: don't emit packet on orig fanout group If a packet is emitted on one socket in one group of fanout sockets, it is transmitted again. It is thus read again on one of the sockets of the fanout group. This result in a loop for software which generate packets when receiving one. This retransmission is not the intended behavior: a fanout group must behave like a single socket. The packet should not be transmitted on a socket if it originates from a socket belonging to the same fanout group. This patch fixes the issue by changing the transmission check to take fanout group info account. Reported-by: Aleksandr Kotov Signed-off-by: Eric Leblond Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 ++ net/core/dev.c | 16 ++++++++++++++-- net/packet/af_packet.c | 9 +++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 3560d688161e..59dc05f38247 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1522,6 +1522,8 @@ struct packet_type { struct sk_buff **(*gro_receive)(struct sk_buff **head, struct sk_buff *skb); int (*gro_complete)(struct sk_buff *skb); + bool (*id_match)(struct packet_type *ptype, + struct sock *sk); void *af_packet_priv; struct list_head list; }; diff --git a/net/core/dev.c b/net/core/dev.c index a39354ee1432..debd9372472f 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1642,6 +1642,19 @@ static inline int deliver_skb(struct sk_buff *skb, return pt_prev->func(skb, skb->dev, pt_prev, orig_dev); } +static inline bool skb_loop_sk(struct packet_type *ptype, struct sk_buff *skb) +{ + if (ptype->af_packet_priv == NULL) + return false; + + if (ptype->id_match) + return ptype->id_match(ptype, skb->sk); + else if ((struct sock *)ptype->af_packet_priv == skb->sk) + return true; + + return false; +} + /* * Support routine. Sends outgoing frames to any network * taps currently in use. @@ -1659,8 +1672,7 @@ static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) * they originated from - MvS (miquels@drinkel.ow.org) */ if ((ptype->dev == dev || !ptype->dev) && - (ptype->af_packet_priv == NULL || - (struct sock *)ptype->af_packet_priv != skb->sk)) { + (!skb_loop_sk(ptype, skb))) { if (pt_prev) { deliver_skb(skb2, pt_prev, skb->dev); pt_prev = ptype; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 8ac890a1a4c0..aee7196aac36 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1273,6 +1273,14 @@ static void __fanout_unlink(struct sock *sk, struct packet_sock *po) spin_unlock(&f->lock); } +bool match_fanout_group(struct packet_type *ptype, struct sock * sk) +{ + if (ptype->af_packet_priv == (void*)((struct packet_sock *)sk)->fanout) + return true; + + return false; +} + static int fanout_add(struct sock *sk, u16 id, u16 type_flags) { struct packet_sock *po = pkt_sk(sk); @@ -1325,6 +1333,7 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags) match->prot_hook.dev = po->prot_hook.dev; match->prot_hook.func = packet_rcv_fanout; match->prot_hook.af_packet_priv = match; + match->prot_hook.id_match = match_fanout_group; dev_add_pack(&match->prot_hook); list_add(&match->list, &fanout_list); } -- cgit v1.2.3 From 1d76efe1577b4323609b1bcbfafa8b731eda071a Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 17 Aug 2012 04:00:48 +0000 Subject: team: add support for non-ethernet devices This is resolved by two things: 1) allow dev_addr of different length than ETH_ALEN 2) during port add, check for dev->type and change it if necessary Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/Kconfig | 4 +- drivers/net/team/team.c | 89 +++++++++++++++++++++++++-------- drivers/net/team/team_mode_broadcast.c | 8 +-- drivers/net/team/team_mode_roundrobin.c | 8 +-- include/linux/if_team.h | 4 +- 5 files changed, 79 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/drivers/net/team/Kconfig b/drivers/net/team/Kconfig index 6a7260b03a1e..6b08bd419fba 100644 --- a/drivers/net/team/Kconfig +++ b/drivers/net/team/Kconfig @@ -21,7 +21,7 @@ config NET_TEAM_MODE_BROADCAST ---help--- Basic mode where packets are transmitted always by all suitable ports. - All added ports are setup to have team's mac address. + All added ports are setup to have team's device address. To compile this team mode as a module, choose M here: the module will be called team_mode_broadcast. @@ -33,7 +33,7 @@ config NET_TEAM_MODE_ROUNDROBIN Basic mode where port used for transmitting packets is selected in round-robin fashion using packet counter. - All added ports are setup to have team's mac address. + All added ports are setup to have team's device address. To compile this team mode as a module, choose M here: the module will be called team_mode_roundrobin. diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index ba10c469b02b..bc36d13c2675 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -54,29 +54,29 @@ static struct team_port *team_port_get_rtnl(const struct net_device *dev) } /* - * Since the ability to change mac address for open port device is tested in + * Since the ability to change device address for open port device is tested in * team_port_add, this function can be called without control of return value */ -static int __set_port_mac(struct net_device *port_dev, - const unsigned char *dev_addr) +static int __set_port_dev_addr(struct net_device *port_dev, + const unsigned char *dev_addr) { struct sockaddr addr; - memcpy(addr.sa_data, dev_addr, ETH_ALEN); - addr.sa_family = ARPHRD_ETHER; + memcpy(addr.sa_data, dev_addr, port_dev->addr_len); + addr.sa_family = port_dev->type; return dev_set_mac_address(port_dev, &addr); } -static int team_port_set_orig_mac(struct team_port *port) +static int team_port_set_orig_dev_addr(struct team_port *port) { - return __set_port_mac(port->dev, port->orig.dev_addr); + return __set_port_dev_addr(port->dev, port->orig.dev_addr); } -int team_port_set_team_mac(struct team_port *port) +int team_port_set_team_dev_addr(struct team_port *port) { - return __set_port_mac(port->dev, port->team->dev->dev_addr); + return __set_port_dev_addr(port->dev, port->team->dev->dev_addr); } -EXPORT_SYMBOL(team_port_set_team_mac); +EXPORT_SYMBOL(team_port_set_team_dev_addr); static void team_refresh_port_linkup(struct team_port *port) { @@ -965,6 +965,8 @@ static struct netpoll_info *team_netpoll_info(struct team *team) #endif static void __team_port_change_check(struct team_port *port, bool linkup); +static int team_dev_type_check_change(struct net_device *dev, + struct net_device *port_dev); static int team_port_add(struct team *team, struct net_device *port_dev) { @@ -973,9 +975,8 @@ static int team_port_add(struct team *team, struct net_device *port_dev) char *portname = port_dev->name; int err; - if (port_dev->flags & IFF_LOOPBACK || - port_dev->type != ARPHRD_ETHER) { - netdev_err(dev, "Device %s is of an unsupported type\n", + if (port_dev->flags & IFF_LOOPBACK) { + netdev_err(dev, "Device %s is loopback device. Loopback devices can't be added as a team port\n", portname); return -EINVAL; } @@ -986,6 +987,10 @@ static int team_port_add(struct team *team, struct net_device *port_dev) return -EBUSY; } + err = team_dev_type_check_change(dev, port_dev); + if (err) + return err; + if (port_dev->flags & IFF_UP) { netdev_err(dev, "Device %s is up. Set it down before adding it as a team port\n", portname); @@ -1008,7 +1013,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev) goto err_set_mtu; } - memcpy(port->orig.dev_addr, port_dev->dev_addr, ETH_ALEN); + memcpy(port->orig.dev_addr, port_dev->dev_addr, port_dev->addr_len); err = team_port_enter(team, port); if (err) { @@ -1089,7 +1094,7 @@ err_vids_add: err_dev_open: team_port_leave(team, port); - team_port_set_orig_mac(port); + team_port_set_orig_dev_addr(port); err_port_enter: dev_set_mtu(port_dev, port->orig.mtu); @@ -1126,7 +1131,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev) vlan_vids_del_by_dev(port_dev, dev); dev_close(port_dev); team_port_leave(team, port); - team_port_set_orig_mac(port); + team_port_set_orig_dev_addr(port); dev_set_mtu(port_dev, port->orig.mtu); synchronize_rcu(); kfree(port); @@ -1477,17 +1482,18 @@ static void team_set_rx_mode(struct net_device *dev) static int team_set_mac_address(struct net_device *dev, void *p) { + struct sockaddr *addr = p; struct team *team = netdev_priv(dev); struct team_port *port; - int err; - err = eth_mac_addr(dev, p); - if (err) - return err; + if (dev->type == ARPHRD_ETHER && !is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + dev->addr_assign_type &= ~NET_ADDR_RANDOM; rcu_read_lock(); list_for_each_entry_rcu(port, &team->port_list, list) - if (team->ops.port_change_mac) - team->ops.port_change_mac(team, port); + if (team->ops.port_change_dev_addr) + team->ops.port_change_dev_addr(team, port); rcu_read_unlock(); return 0; } @@ -1718,6 +1724,45 @@ static const struct net_device_ops team_netdev_ops = { * rt netlink interface ***********************/ +static void team_setup_by_port(struct net_device *dev, + struct net_device *port_dev) +{ + dev->header_ops = port_dev->header_ops; + dev->type = port_dev->type; + dev->hard_header_len = port_dev->hard_header_len; + dev->addr_len = port_dev->addr_len; + dev->mtu = port_dev->mtu; + memcpy(dev->broadcast, port_dev->broadcast, port_dev->addr_len); + memcpy(dev->dev_addr, port_dev->dev_addr, port_dev->addr_len); + dev->addr_assign_type &= ~NET_ADDR_RANDOM; +} + +static int team_dev_type_check_change(struct net_device *dev, + struct net_device *port_dev) +{ + struct team *team = netdev_priv(dev); + char *portname = port_dev->name; + int err; + + if (dev->type == port_dev->type) + return 0; + if (!list_empty(&team->port_list)) { + netdev_err(dev, "Device %s is of different type\n", portname); + return -EBUSY; + } + err = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, dev); + err = notifier_to_errno(err); + if (err) { + netdev_err(dev, "Refused to change device type\n"); + return err; + } + dev_uc_flush(dev); + dev_mc_flush(dev); + team_setup_by_port(dev, port_dev); + call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev); + return 0; +} + static void team_setup(struct net_device *dev) { ether_setup(dev); diff --git a/drivers/net/team/team_mode_broadcast.c b/drivers/net/team/team_mode_broadcast.c index c96e4d2967f0..9db0171e9366 100644 --- a/drivers/net/team/team_mode_broadcast.c +++ b/drivers/net/team/team_mode_broadcast.c @@ -48,18 +48,18 @@ static bool bc_transmit(struct team *team, struct sk_buff *skb) static int bc_port_enter(struct team *team, struct team_port *port) { - return team_port_set_team_mac(port); + return team_port_set_team_dev_addr(port); } -static void bc_port_change_mac(struct team *team, struct team_port *port) +static void bc_port_change_dev_addr(struct team *team, struct team_port *port) { - team_port_set_team_mac(port); + team_port_set_team_dev_addr(port); } static const struct team_mode_ops bc_mode_ops = { .transmit = bc_transmit, .port_enter = bc_port_enter, - .port_change_mac = bc_port_change_mac, + .port_change_dev_addr = bc_port_change_dev_addr, }; static const struct team_mode bc_mode = { diff --git a/drivers/net/team/team_mode_roundrobin.c b/drivers/net/team/team_mode_roundrobin.c index ad7ed0ec544c..105135aa8f05 100644 --- a/drivers/net/team/team_mode_roundrobin.c +++ b/drivers/net/team/team_mode_roundrobin.c @@ -66,18 +66,18 @@ drop: static int rr_port_enter(struct team *team, struct team_port *port) { - return team_port_set_team_mac(port); + return team_port_set_team_dev_addr(port); } -static void rr_port_change_mac(struct team *team, struct team_port *port) +static void rr_port_change_dev_addr(struct team *team, struct team_port *port) { - team_port_set_team_mac(port); + team_port_set_team_dev_addr(port); } static const struct team_mode_ops rr_mode_ops = { .transmit = rr_transmit, .port_enter = rr_port_enter, - .port_change_mac = rr_port_change_mac, + .port_change_dev_addr = rr_port_change_dev_addr, }; static const struct team_mode rr_mode = { diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 33fcc20b5881..8b000b295730 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -123,7 +123,7 @@ struct team_mode_ops { bool (*transmit)(struct team *team, struct sk_buff *skb); int (*port_enter)(struct team *team, struct team_port *port); void (*port_leave)(struct team *team, struct team_port *port); - void (*port_change_mac)(struct team *team, struct team_port *port); + void (*port_change_dev_addr)(struct team *team, struct team_port *port); void (*port_enabled)(struct team *team, struct team_port *port); void (*port_disabled)(struct team *team, struct team_port *port); }; @@ -238,7 +238,7 @@ static inline struct team_port *team_get_port_by_index_rcu(struct team *team, return NULL; } -extern int team_port_set_team_mac(struct team_port *port); +extern int team_port_set_team_dev_addr(struct team_port *port); extern int team_options_register(struct team *team, const struct team_option *option, size_t option_count); -- cgit v1.2.3 From 9d7b0fc1ef1f17aff57c0dc9a59453d8fca255c3 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Mon, 20 Aug 2012 02:56:56 -0700 Subject: net: ipv6: fix oops in inet_putpeer() Commit 97bab73f (inet: Hide route peer accesses behind helpers.) introduced a bug in xfrm6_policy_destroy(). The xfrm_dst's _rt6i_peer member is not initialized, causing a false positive result from inetpeer_ptr_is_peer(), which in turn causes a NULL pointer dereference in inet_putpeer(). Pid: 314, comm: kworker/0:1 Not tainted 3.6.0-rc1+ #17 To Be Filled By O.E.M. To Be Filled By O.E.M./P4S800D-X EIP: 0060:[] EFLAGS: 00010246 CPU: 0 EIP is at inet_putpeer+0xe/0x16 EAX: 00000000 EBX: f3481700 ECX: 00000000 EDX: 000dd641 ESI: f3481700 EDI: c05e949c EBP: f551def4 ESP: f551def4 DS: 007b ES: 007b FS: 0000 GS: 00e0 SS: 0068 CR0: 8005003b CR2: 00000070 CR3: 3243d000 CR4: 00000750 DR0: 00000000 DR1: 00000000 DR2: 00000000 DR3: 00000000 DR6: ffff0ff0 DR7: 00000400 f551df04 c0423de1 00000000 f3481700 f551df18 c038d5f7 f254b9f8 f551df28 f34f85d8 f551df20 c03ef48d f551df3c c0396870 f30697e8 f24e1738 c05e98f4 f5509540 c05cd2b4 f551df7c c0142d2b c043feb5 f5509540 00000000 c05cd2e8 [] xfrm6_dst_destroy+0x42/0xdb [] dst_destroy+0x1d/0xa4 [] xfrm_bundle_flo_delete+0x2b/0x36 [] flow_cache_gc_task+0x85/0x9f [] process_one_work+0x122/0x441 [] ? apic_timer_interrupt+0x31/0x38 [] ? flow_cache_new_hashrnd+0x2b/0x2b [] worker_thread+0x113/0x3cc Fix by adding a init_dst() callback to struct xfrm_policy_afinfo to properly initialize the dst's peer pointer. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/net/xfrm.h | 2 ++ net/ipv6/xfrm6_policy.c | 8 ++++++++ net/xfrm/xfrm_policy.c | 2 ++ 3 files changed, 12 insertions(+) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 62b619e82a90..976a81abe1a2 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -292,6 +292,8 @@ struct xfrm_policy_afinfo { struct flowi *fl, int reverse); int (*get_tos)(const struct flowi *fl); + void (*init_dst)(struct net *net, + struct xfrm_dst *dst); int (*init_path)(struct xfrm_dst *path, struct dst_entry *dst, int nfheader_len); diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index ef39812107b1..f8c4c08ffb60 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -73,6 +73,13 @@ static int xfrm6_get_tos(const struct flowi *fl) return 0; } +static void xfrm6_init_dst(struct net *net, struct xfrm_dst *xdst) +{ + struct rt6_info *rt = (struct rt6_info *)xdst; + + rt6_init_peer(rt, net->ipv6.peers); +} + static int xfrm6_init_path(struct xfrm_dst *path, struct dst_entry *dst, int nfheader_len) { @@ -286,6 +293,7 @@ static struct xfrm_policy_afinfo xfrm6_policy_afinfo = { .get_saddr = xfrm6_get_saddr, .decode_session = _decode_session6, .get_tos = xfrm6_get_tos, + .init_dst = xfrm6_init_dst, .init_path = xfrm6_init_path, .fill_dst = xfrm6_fill_dst, .blackhole_route = ip6_blackhole_route, diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index c5a5165a5927..5a2aa17e4d3c 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1357,6 +1357,8 @@ static inline struct xfrm_dst *xfrm_alloc_dst(struct net *net, int family) memset(dst + 1, 0, sizeof(*xdst) - sizeof(*dst)); xdst->flo.ops = &xfrm_bundle_fc_ops; + if (afinfo->init_dst) + afinfo->init_dst(net, xdst); } else xdst = ERR_PTR(-ENOBUFS); -- cgit v1.2.3 From baa36046d09ea6dbc122c795566992318663d9eb Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Mon, 18 Jun 2012 17:54:14 +0200 Subject: cputime: Consolidate vtime handling on context switch The archs that implement virtual cputime accounting all flush the cputime of a task when it gets descheduled and sometimes set up some ground initialization for the next task to account its cputime. These archs all put their own hooks in their context switch callbacks and handle the off-case themselves. Consolidate this by creating a new account_switch_vtime() callback called in generic code right after a context switch and that these archs must implement to flush the prev task cputime and initialize the next task cputime related state. Signed-off-by: Frederic Weisbecker Acked-by: Martin Schwidefsky Cc: Tony Luck Cc: Fenghua Yu Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Heiko Carstens Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra --- arch/ia64/include/asm/switch_to.h | 8 -------- arch/ia64/kernel/time.c | 4 ++-- arch/powerpc/include/asm/time.h | 6 ------ arch/powerpc/kernel/process.c | 3 --- arch/powerpc/kernel/time.c | 6 ++++++ arch/s390/include/asm/switch_to.h | 2 -- arch/s390/kernel/vtime.c | 4 ++-- include/linux/kernel_stat.h | 6 ++++++ kernel/sched/core.c | 1 + 9 files changed, 17 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/arch/ia64/include/asm/switch_to.h b/arch/ia64/include/asm/switch_to.h index cb2412fcd17f..d38c7ea5eea5 100644 --- a/arch/ia64/include/asm/switch_to.h +++ b/arch/ia64/include/asm/switch_to.h @@ -30,13 +30,6 @@ extern struct task_struct *ia64_switch_to (void *next_task); extern void ia64_save_extra (struct task_struct *task); extern void ia64_load_extra (struct task_struct *task); -#ifdef CONFIG_VIRT_CPU_ACCOUNTING -extern void ia64_account_on_switch (struct task_struct *prev, struct task_struct *next); -# define IA64_ACCOUNT_ON_SWITCH(p,n) ia64_account_on_switch(p,n) -#else -# define IA64_ACCOUNT_ON_SWITCH(p,n) -#endif - #ifdef CONFIG_PERFMON DECLARE_PER_CPU(unsigned long, pfm_syst_info); # define PERFMON_IS_SYSWIDE() (__get_cpu_var(pfm_syst_info) & 0x1) @@ -49,7 +42,6 @@ extern void ia64_account_on_switch (struct task_struct *prev, struct task_struct || PERFMON_IS_SYSWIDE()) #define __switch_to(prev,next,last) do { \ - IA64_ACCOUNT_ON_SWITCH(prev, next); \ if (IA64_HAS_EXTRA_STATE(prev)) \ ia64_save_extra(prev); \ if (IA64_HAS_EXTRA_STATE(next)) \ diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c index ecc904b33c5f..6247197b9877 100644 --- a/arch/ia64/kernel/time.c +++ b/arch/ia64/kernel/time.c @@ -88,10 +88,10 @@ extern cputime_t cycle_to_cputime(u64 cyc); * accumulated times to the current process, and to prepare accounting on * the next process. */ -void ia64_account_on_switch(struct task_struct *prev, struct task_struct *next) +void account_switch_vtime(struct task_struct *prev) { struct thread_info *pi = task_thread_info(prev); - struct thread_info *ni = task_thread_info(next); + struct thread_info *ni = task_thread_info(current); cputime_t delta_stime, delta_utime; __u64 now; diff --git a/arch/powerpc/include/asm/time.h b/arch/powerpc/include/asm/time.h index 3b4b4a8da922..c1f267694acb 100644 --- a/arch/powerpc/include/asm/time.h +++ b/arch/powerpc/include/asm/time.h @@ -197,12 +197,6 @@ struct cpu_usage { DECLARE_PER_CPU(struct cpu_usage, cpu_usage_array); -#if defined(CONFIG_VIRT_CPU_ACCOUNTING) -#define account_process_vtime(tsk) account_process_tick(tsk, 0) -#else -#define account_process_vtime(tsk) do { } while (0) -#endif - extern void secondary_cpu_time_init(void); DECLARE_PER_CPU(u64, decrementers_next_tb); diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 710f400476de..d73fa999b47b 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -514,9 +514,6 @@ struct task_struct *__switch_to(struct task_struct *prev, local_irq_save(flags); - account_system_vtime(current); - account_process_vtime(current); - /* * We can't take a PMU exception inside _switch() since there is a * window where the kernel stack SLB and the kernel stack are out diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index be171ee73bf8..49da7f06e643 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -366,6 +366,12 @@ void account_process_tick(struct task_struct *tsk, int user_tick) account_user_time(tsk, utime, utimescaled); } +void account_switch_vtime(struct task_struct *prev) +{ + account_system_vtime(prev); + account_process_tick(prev, 0); +} + #else /* ! CONFIG_VIRT_CPU_ACCOUNTING */ #define calc_cputime_factors() #endif diff --git a/arch/s390/include/asm/switch_to.h b/arch/s390/include/asm/switch_to.h index f223068b7822..e7f9b3d04fa1 100644 --- a/arch/s390/include/asm/switch_to.h +++ b/arch/s390/include/asm/switch_to.h @@ -89,12 +89,10 @@ static inline void restore_access_regs(unsigned int *acrs) prev = __switch_to(prev,next); \ } while (0) -extern void account_vtime(struct task_struct *, struct task_struct *); extern void account_tick_vtime(struct task_struct *); #define finish_arch_switch(prev) do { \ set_fs(current->thread.mm_segment); \ - account_vtime(prev, current); \ } while (0) #endif /* __ASM_SWITCH_TO_H */ diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c index 4fc97b40a6e1..449ac22cc71b 100644 --- a/arch/s390/kernel/vtime.c +++ b/arch/s390/kernel/vtime.c @@ -99,7 +99,7 @@ static int do_account_vtime(struct task_struct *tsk, int hardirq_offset) return virt_timer_forward(user + system); } -void account_vtime(struct task_struct *prev, struct task_struct *next) +void account_switch_vtime(struct task_struct *prev) { struct thread_info *ti; @@ -107,7 +107,7 @@ void account_vtime(struct task_struct *prev, struct task_struct *next) ti = task_thread_info(prev); ti->user_timer = S390_lowcore.user_timer; ti->system_timer = S390_lowcore.system_timer; - ti = task_thread_info(next); + ti = task_thread_info(current); S390_lowcore.user_timer = ti->user_timer; S390_lowcore.system_timer = ti->system_timer; } diff --git a/include/linux/kernel_stat.h b/include/linux/kernel_stat.h index 2fbd9053c2df..bbe5d15d6597 100644 --- a/include/linux/kernel_stat.h +++ b/include/linux/kernel_stat.h @@ -130,4 +130,10 @@ extern void account_process_tick(struct task_struct *, int user); extern void account_steal_ticks(unsigned long ticks); extern void account_idle_ticks(unsigned long ticks); +#ifdef CONFIG_VIRT_CPU_ACCOUNTING +extern void account_switch_vtime(struct task_struct *prev); +#else +static inline void account_switch_vtime(struct task_struct *prev) { } +#endif + #endif /* _LINUX_KERNEL_STAT_H */ diff --git a/kernel/sched/core.c b/kernel/sched/core.c index ae3bcaa3afbf..78d9c965433a 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -1796,6 +1796,7 @@ static void finish_task_switch(struct rq *rq, struct task_struct *prev) * Manfred Spraul */ prev_state = prev->state; + account_switch_vtime(prev); finish_arch_switch(prev); #ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW local_irq_disable(); -- cgit v1.2.3 From e687f61eedab8895e5669cb82cebe0253631cd8c Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Sun, 12 Aug 2012 18:24:55 +0200 Subject: mac80211: add supported rates change notification in IBSS In IBSS it is possible that the supported rates set for a station changes over time (e.g. it gets first initialised as an empty set because of no available information about rates and updated later). In this case the driver has to be notified about the change in order to update its internal table accordingly (if needed). This behaviour is needed by all those drivers that handle rc internally but leave stations management to mac80211 Reported-by: Gui Iribarren Signed-off-by: Antonio Quartulli [Johannes - add docs, validate IBSS mode only, fix compilation] Signed-off-by: Johannes Berg --- include/net/mac80211.h | 4 ++++ net/mac80211/driver-ops.h | 3 +++ net/mac80211/ibss.c | 5 ++++- 3 files changed, 11 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 8114f590f715..d9cef31f4cbf 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1897,10 +1897,14 @@ enum ieee80211_frame_release_type { * @IEEE80211_RC_BW_CHANGED: The bandwidth that can be used to transmit * to this station changed. * @IEEE80211_RC_SMPS_CHANGED: The SMPS state of the station changed. + * @IEEE80211_RC_SUPP_RATES_CHANGED: The supported rate set of this peer + * changed (in IBSS mode) due to discovering more information about + * the peer. */ enum ieee80211_rate_control_changed { IEEE80211_RC_BW_CHANGED = BIT(0), IEEE80211_RC_SMPS_CHANGED = BIT(1), + IEEE80211_RC_SUPP_RATES_CHANGED = BIT(2), }; /** diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index a81117a83996..a81154d27291 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -528,6 +528,9 @@ static inline void drv_sta_rc_update(struct ieee80211_local *local, sdata = get_bss_sdata(sdata); check_sdata_in_driver(sdata); + WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED && + sdata->vif.type != NL80211_IFTYPE_ADHOC); + trace_drv_sta_rc_update(local, sdata, sta, changed); if (local->ops->sta_rc_update) local->ops->sta_rc_update(&local->hw, &sdata->vif, diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 1ebda2f9e57b..a9d93285dba7 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -459,8 +459,11 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, } } - if (sta && rates_updated) + if (sta && rates_updated) { + drv_sta_rc_update(local, sdata, &sta->sta, + IEEE80211_RC_SUPP_RATES_CHANGED); rate_control_rate_init(sta); + } rcu_read_unlock(); } -- cgit v1.2.3 From 48613ece3d6a2613caa376f51477435cc080f3cd Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 5 Jul 2012 11:32:16 +0200 Subject: wireless: add radiotap A-MPDU status field Define the A-MPDU status field in radiotap, also update the radiotap parser for it and the MCS field that was apparently missed last time. Signed-off-by: Johannes Berg --- include/net/ieee80211_radiotap.h | 11 +++++++++++ net/wireless/radiotap.c | 2 ++ 2 files changed, 13 insertions(+) (limited to 'include') diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h index 71392545d0a1..7f0df133d119 100644 --- a/include/net/ieee80211_radiotap.h +++ b/include/net/ieee80211_radiotap.h @@ -183,6 +183,9 @@ struct ieee80211_radiotap_header { * Contains a bitmap of known fields/flags, the flags, and * the MCS index. * + * IEEE80211_RADIOTAP_AMPDU_STATUS u32, u16, u8, u8 unitless + * + * Contains the AMPDU information for the subframe. */ enum ieee80211_radiotap_type { IEEE80211_RADIOTAP_TSFT = 0, @@ -205,6 +208,7 @@ enum ieee80211_radiotap_type { IEEE80211_RADIOTAP_DATA_RETRIES = 17, IEEE80211_RADIOTAP_MCS = 19, + IEEE80211_RADIOTAP_AMPDU_STATUS = 20, /* valid in every it_present bitmap, even vendor namespaces */ IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29, @@ -270,6 +274,13 @@ enum ieee80211_radiotap_type { #define IEEE80211_RADIOTAP_MCS_FMT_GF 0x08 #define IEEE80211_RADIOTAP_MCS_FEC_LDPC 0x10 +/* For IEEE80211_RADIOTAP_AMPDU_STATUS */ +#define IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN 0x0001 +#define IEEE80211_RADIOTAP_AMPDU_IS_ZEROLEN 0x0002 +#define IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN 0x0004 +#define IEEE80211_RADIOTAP_AMPDU_IS_LAST 0x0008 +#define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR 0x0010 +#define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN 0x0020 /* helpers */ static inline int ieee80211_get_radiotap_len(unsigned char *data) diff --git a/net/wireless/radiotap.c b/net/wireless/radiotap.c index c4ad7958af52..7d604c06c3dc 100644 --- a/net/wireless/radiotap.c +++ b/net/wireless/radiotap.c @@ -41,6 +41,8 @@ static const struct radiotap_align_size rtap_namespace_sizes[] = { [IEEE80211_RADIOTAP_TX_FLAGS] = { .align = 2, .size = 2, }, [IEEE80211_RADIOTAP_RTS_RETRIES] = { .align = 1, .size = 1, }, [IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, }, + [IEEE80211_RADIOTAP_MCS] = { .align = 1, .size = 3, }, + [IEEE80211_RADIOTAP_AMPDU_STATUS] = { .align = 4, .size = 8, }, /* * add more here as they are defined in radiotap.h */ -- cgit v1.2.3 From 4c29867790131c281ef96af507d85e3e5f829408 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 5 Jul 2012 11:34:31 +0200 Subject: mac80211: support A-MPDU status reporting Support getting A-MPDU status information from the drivers and reporting it to userspace via radiotap in the standard fields. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 53 +++++++++++++++++++++++++++++++++++++------------- net/mac80211/rx.c | 42 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 80 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index d9cef31f4cbf..edc235c51a68 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -676,21 +676,41 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info) * @RX_FLAG_HT_GF: This frame was received in a HT-greenfield transmission, if * the driver fills this value it should add %IEEE80211_RADIOTAP_MCS_HAVE_FMT * to hw.radiotap_mcs_details to advertise that fact + * @RX_FLAG_AMPDU_DETAILS: A-MPDU details are known, in particular the reference + * number (@ampdu_reference) must be populated and be a distinct number for + * each A-MPDU + * @RX_FLAG_AMPDU_REPORT_ZEROLEN: driver reports 0-length subframes + * @RX_FLAG_AMPDU_IS_ZEROLEN: This is a zero-length subframe, for + * monitoring purposes only + * @RX_FLAG_AMPDU_LAST_KNOWN: last subframe is known, should be set on all + * subframes of a single A-MPDU + * @RX_FLAG_AMPDU_IS_LAST: this subframe is the last subframe of the A-MPDU + * @RX_FLAG_AMPDU_DELIM_CRC_ERROR: A delimiter CRC error has been detected + * on this subframe + * @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC + * is stored in the @ampdu_delimiter_crc field) */ enum mac80211_rx_flags { - RX_FLAG_MMIC_ERROR = 1<<0, - RX_FLAG_DECRYPTED = 1<<1, - RX_FLAG_MMIC_STRIPPED = 1<<3, - RX_FLAG_IV_STRIPPED = 1<<4, - RX_FLAG_FAILED_FCS_CRC = 1<<5, - RX_FLAG_FAILED_PLCP_CRC = 1<<6, - RX_FLAG_MACTIME_MPDU = 1<<7, - RX_FLAG_SHORTPRE = 1<<8, - RX_FLAG_HT = 1<<9, - RX_FLAG_40MHZ = 1<<10, - RX_FLAG_SHORT_GI = 1<<11, - RX_FLAG_NO_SIGNAL_VAL = 1<<12, - RX_FLAG_HT_GF = 1<<13, + RX_FLAG_MMIC_ERROR = BIT(0), + RX_FLAG_DECRYPTED = BIT(1), + RX_FLAG_MMIC_STRIPPED = BIT(3), + RX_FLAG_IV_STRIPPED = BIT(4), + RX_FLAG_FAILED_FCS_CRC = BIT(5), + RX_FLAG_FAILED_PLCP_CRC = BIT(6), + RX_FLAG_MACTIME_MPDU = BIT(7), + RX_FLAG_SHORTPRE = BIT(8), + RX_FLAG_HT = BIT(9), + RX_FLAG_40MHZ = BIT(10), + RX_FLAG_SHORT_GI = BIT(11), + RX_FLAG_NO_SIGNAL_VAL = BIT(12), + RX_FLAG_HT_GF = BIT(13), + RX_FLAG_AMPDU_DETAILS = BIT(14), + RX_FLAG_AMPDU_REPORT_ZEROLEN = BIT(15), + RX_FLAG_AMPDU_IS_ZEROLEN = BIT(16), + RX_FLAG_AMPDU_LAST_KNOWN = BIT(17), + RX_FLAG_AMPDU_IS_LAST = BIT(18), + RX_FLAG_AMPDU_DELIM_CRC_ERROR = BIT(19), + RX_FLAG_AMPDU_DELIM_CRC_KNOWN = BIT(20), }; /** @@ -714,17 +734,22 @@ enum mac80211_rx_flags { * HT rates are use (RX_FLAG_HT) * @flag: %RX_FLAG_* * @rx_flags: internal RX flags for mac80211 + * @ampdu_reference: A-MPDU reference number, must be a different value for + * each A-MPDU but the same for each subframe within one A-MPDU + * @ampdu_delimiter_crc: A-MPDU delimiter CRC */ struct ieee80211_rx_status { u64 mactime; u32 device_timestamp; - u16 flag; + u32 ampdu_reference; + u32 flag; u16 freq; u8 rate_idx; u8 rx_flags; u8 band; u8 antenna; s8 signal; + u8 ampdu_delimiter_crc; }; /** diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 0cb4edee6af5..78bf6c7e80c8 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -60,7 +60,9 @@ static inline int should_drop_frame(struct sk_buff *skb, struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC)) + if (status->flag & (RX_FLAG_FAILED_FCS_CRC | + RX_FLAG_FAILED_PLCP_CRC | + RX_FLAG_AMPDU_IS_ZEROLEN)) return 1; if (unlikely(skb->len < 16 + present_fcs_len)) return 1; @@ -91,6 +93,13 @@ ieee80211_rx_radiotap_len(struct ieee80211_local *local, if (status->flag & RX_FLAG_HT) /* HT info */ len += 3; + if (status->flag & RX_FLAG_AMPDU_DETAILS) { + /* padding */ + while (len & 3) + len++; + len += 8; + } + return len; } @@ -215,6 +224,37 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, pos++; *pos++ = status->rate_idx; } + + if (status->flag & RX_FLAG_AMPDU_DETAILS) { + u16 flags = 0; + + /* ensure 4 byte alignment */ + while ((pos - (u8 *)rthdr) & 3) + pos++; + rthdr->it_present |= + cpu_to_le32(1 << IEEE80211_RADIOTAP_AMPDU_STATUS); + put_unaligned_le32(status->ampdu_reference, pos); + pos += 4; + if (status->flag & RX_FLAG_AMPDU_REPORT_ZEROLEN) + flags |= IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN; + if (status->flag & RX_FLAG_AMPDU_IS_ZEROLEN) + flags |= IEEE80211_RADIOTAP_AMPDU_IS_ZEROLEN; + if (status->flag & RX_FLAG_AMPDU_LAST_KNOWN) + flags |= IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN; + if (status->flag & RX_FLAG_AMPDU_IS_LAST) + flags |= IEEE80211_RADIOTAP_AMPDU_IS_LAST; + if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_ERROR) + flags |= IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR; + if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_KNOWN) + flags |= IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN; + put_unaligned_le16(flags, pos); + pos += 2; + if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_KNOWN) + *pos++ = status->ampdu_delimiter_crc; + else + *pos++ = 0; + *pos++ = 0; + } } /* -- cgit v1.2.3 From 98104fdeda63d57631c9f89e90a7b83b58fcee40 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 16 Jun 2012 00:19:54 +0200 Subject: cfg80211: add P2P Device abstraction In order to support using a different MAC address for the P2P Device address we must first have a P2P Device abstraction that can be assigned a MAC address. This abstraction will also be useful to support offloading P2P operations to the device, e.g. periodic listen for discoverability. Currently, the driver is responsible for assigning a MAC address to the P2P Device, but this could be changed by allowing a MAC address to be given to the NEW_INTERFACE command. As it has no associated netdev, a P2P Device can only be identified by its wdev identifier but the previous patches allowed using the wdev identifier in various APIs, e.g. remain-on-channel. Signed-off-by: Johannes Berg --- include/linux/nl80211.h | 30 ++++++++++-- include/net/cfg80211.h | 40 +++++++++++++++- net/mac80211/iface.c | 3 ++ net/mac80211/util.c | 2 + net/wireless/chan.c | 7 ++- net/wireless/core.c | 53 ++++++++++++++++++++- net/wireless/mlme.c | 10 ++-- net/wireless/nl80211.c | 122 +++++++++++++++++++++++++++++++++++++++++++++--- net/wireless/util.c | 18 ++++++- 9 files changed, 265 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 2f3878806403..458416279347 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -565,6 +565,14 @@ * %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ with * %NL80211_ATTR_WIPHY_CHANNEL_TYPE. * + * @NL80211_CMD_START_P2P_DEVICE: Start the given P2P Device, identified by + * its %NL80211_ATTR_WDEV identifier. It must have been created with + * %NL80211_CMD_NEW_INTERFACE previously. After it has been started, the + * P2P Device can be used for P2P operations, e.g. remain-on-channel and + * public action frame TX. + * @NL80211_CMD_STOP_P2P_DEVICE: Stop the given P2P Device, identified by + * its %NL80211_ATTR_WDEV identifier. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -708,6 +716,9 @@ enum nl80211_commands { NL80211_CMD_CH_SWITCH_NOTIFY, + NL80211_CMD_START_P2P_DEVICE, + NL80211_CMD_STOP_P2P_DEVICE, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1575,6 +1586,10 @@ enum nl80211_attrs { * @NL80211_IFTYPE_MESH_POINT: mesh point * @NL80211_IFTYPE_P2P_CLIENT: P2P client * @NL80211_IFTYPE_P2P_GO: P2P group owner + * @NL80211_IFTYPE_P2P_DEVICE: P2P device interface type, this is not a netdev + * and therefore can't be created in the normal ways, use the + * %NL80211_CMD_START_P2P_DEVICE and %NL80211_CMD_STOP_P2P_DEVICE + * commands to create and destroy one * @NL80211_IFTYPE_MAX: highest interface type number currently defined * @NUM_NL80211_IFTYPES: number of defined interface types * @@ -1593,6 +1608,7 @@ enum nl80211_iftype { NL80211_IFTYPE_MESH_POINT, NL80211_IFTYPE_P2P_CLIENT, NL80211_IFTYPE_P2P_GO, + NL80211_IFTYPE_P2P_DEVICE, /* keep last */ NUM_NL80211_IFTYPES, @@ -2994,12 +3010,18 @@ enum nl80211_ap_sme_features { * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested * to work properly to suppport receiving regulatory hints from * cellular base stations. + * @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: If this is set, an active + * P2P Device (%NL80211_IFTYPE_P2P_DEVICE) requires its own channel + * in the interface combinations, even when it's only used for scan + * and remain-on-channel. This could be due to, for example, the + * remain-on-channel implementation requiring a channel context. */ enum nl80211_feature_flags { - NL80211_FEATURE_SK_TX_STATUS = 1 << 0, - NL80211_FEATURE_HT_IBSS = 1 << 1, - NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2, - NL80211_FEATURE_CELL_BASE_REG_HINTS = 1 << 3, + NL80211_FEATURE_SK_TX_STATUS = 1 << 0, + NL80211_FEATURE_HT_IBSS = 1 << 1, + NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2, + NL80211_FEATURE_CELL_BASE_REG_HINTS = 1 << 3, + NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL = 1 << 4, }; /** diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 493fa0c79005..4c518f1f1aca 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1437,7 +1437,8 @@ struct cfg80211_gtk_rekey_data { * @add_virtual_intf: create a new virtual interface with the given name, * must set the struct wireless_dev's iftype. Beware: You must create * the new netdev in the wiphy's network namespace! Returns the struct - * wireless_dev, or an ERR_PTR. + * wireless_dev, or an ERR_PTR. For P2P device wdevs, the driver must + * also set the address member in the wdev. * * @del_virtual_intf: remove the virtual interface * @@ -1616,6 +1617,9 @@ struct cfg80211_gtk_rekey_data { * @get_channel: Get the current operating channel for the virtual interface. * For monitor interfaces, it should return %NULL unless there's a single * current monitoring channel. + * + * @start_p2p_device: Start the given P2P device. + * @stop_p2p_device: Stop the given P2P device. */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); @@ -1832,6 +1836,11 @@ struct cfg80211_ops { (*get_channel)(struct wiphy *wiphy, struct wireless_dev *wdev, enum nl80211_channel_type *type); + + int (*start_p2p_device)(struct wiphy *wiphy, + struct wireless_dev *wdev); + void (*stop_p2p_device)(struct wiphy *wiphy, + struct wireless_dev *wdev); }; /* @@ -2395,6 +2404,8 @@ struct cfg80211_cached_keys; * @cleanup_work: work struct used for cleanup that can't be done directly * @beacon_interval: beacon interval used on this device for transmitting * beacons, 0 when not valid + * @address: The address for this device, valid only if @netdev is %NULL + * @p2p_started: true if this is a P2P Device that has been started */ struct wireless_dev { struct wiphy *wiphy; @@ -2413,7 +2424,9 @@ struct wireless_dev { struct work_struct cleanup_work; - bool use_4addr; + bool use_4addr, p2p_started; + + u8 address[ETH_ALEN] __aligned(sizeof(u16)); /* currently used for IBSS and SME - might be rearranged later */ u8 ssid[IEEE80211_MAX_SSID_LEN]; @@ -2461,6 +2474,13 @@ struct wireless_dev { #endif }; +static inline u8 *wdev_address(struct wireless_dev *wdev) +{ + if (wdev->netdev) + return wdev->netdev->dev_addr; + return wdev->address; +} + /** * wdev_priv - return wiphy priv from wireless_dev * @@ -3528,6 +3548,22 @@ void cfg80211_ch_switch_notify(struct net_device *dev, int freq, */ u32 cfg80211_calculate_bitrate(struct rate_info *rate); +/** + * cfg80211_unregister_wdev - remove the given wdev + * @wdev: struct wireless_dev to remove + * + * Call this function only for wdevs that have no netdev assigned, + * e.g. P2P Devices. It removes the device from the list so that + * it can no longer be used. It is necessary to call this function + * even when cfg80211 requests the removal of the interface by + * calling the del_virtual_intf() callback. The function must also + * be called when the driver wishes to unregister the wdev, e.g. + * when the device is unbound from the driver. + * + * Requires the RTNL to be held. + */ +void cfg80211_unregister_wdev(struct wireless_dev *wdev); + /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index fbab7a84ca21..366d9d3e84c4 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -449,6 +449,7 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) case NUM_NL80211_IFTYPES: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_P2P_DEVICE: /* cannot happen */ WARN_ON(1); break; @@ -1146,6 +1147,8 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_AP_VLAN: break; + case NL80211_IFTYPE_P2P_DEVICE: + /* not yet supported */ case NL80211_IFTYPE_UNSPECIFIED: case NUM_NL80211_IFTYPES: BUG(); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 7dff94e43a0c..9a4e4e30ea6c 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1390,6 +1390,8 @@ int ieee80211_reconfig(struct ieee80211_local *local) case NL80211_IFTYPE_MONITOR: /* ignore virtual */ break; + case NL80211_IFTYPE_P2P_DEVICE: + /* not yet supported */ case NL80211_IFTYPE_UNSPECIFIED: case NUM_NL80211_IFTYPES: case NL80211_IFTYPE_P2P_CLIENT: diff --git a/net/wireless/chan.c b/net/wireless/chan.c index d355f67d0cdd..2f876b9ee344 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -105,7 +105,7 @@ cfg80211_get_chan_state(struct wireless_dev *wdev, ASSERT_WDEV_LOCK(wdev); - if (!netif_running(wdev->netdev)) + if (wdev->netdev && !netif_running(wdev->netdev)) return; switch (wdev->iftype) { @@ -143,6 +143,11 @@ cfg80211_get_chan_state(struct wireless_dev *wdev, case NL80211_IFTYPE_WDS: /* these interface types don't really have a channel */ return; + case NL80211_IFTYPE_P2P_DEVICE: + if (wdev->wiphy->features & + NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL) + *chanmode = CHAN_MODE_EXCLUSIVE; + return; case NL80211_IFTYPE_UNSPECIFIED: case NUM_NL80211_IFTYPES: WARN_ON(1); diff --git a/net/wireless/core.c b/net/wireless/core.c index 31b40cc4a9c3..91b300443f4b 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -230,9 +230,24 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked) rtnl_lock(); mutex_lock(&rdev->devlist_mtx); - list_for_each_entry(wdev, &rdev->wdev_list, list) - if (wdev->netdev) + list_for_each_entry(wdev, &rdev->wdev_list, list) { + if (wdev->netdev) { dev_close(wdev->netdev); + continue; + } + /* otherwise, check iftype */ + switch (wdev->iftype) { + case NL80211_IFTYPE_P2P_DEVICE: + if (!wdev->p2p_started) + break; + rdev->ops->stop_p2p_device(&rdev->wiphy, wdev); + wdev->p2p_started = false; + rdev->opencount--; + break; + default: + break; + } + } mutex_unlock(&rdev->devlist_mtx); rtnl_unlock(); @@ -407,6 +422,11 @@ static int wiphy_verify_combinations(struct wiphy *wiphy) if (WARN_ON(wiphy->software_iftypes & types)) return -EINVAL; + /* Only a single P2P_DEVICE can be allowed */ + if (WARN_ON(types & BIT(NL80211_IFTYPE_P2P_DEVICE) && + c->limits[j].max > 1)) + return -EINVAL; + cnt += c->limits[j].max; /* * Don't advertise an unsupported type @@ -734,6 +754,35 @@ static void wdev_cleanup_work(struct work_struct *work) dev_put(wdev->netdev); } +void cfg80211_unregister_wdev(struct wireless_dev *wdev) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + + ASSERT_RTNL(); + + if (WARN_ON(wdev->netdev)) + return; + + mutex_lock(&rdev->devlist_mtx); + list_del_rcu(&wdev->list); + rdev->devlist_generation++; + + switch (wdev->iftype) { + case NL80211_IFTYPE_P2P_DEVICE: + if (!wdev->p2p_started) + break; + rdev->ops->stop_p2p_device(&rdev->wiphy, wdev); + wdev->p2p_started = false; + rdev->opencount--; + break; + default: + WARN_ON_ONCE(1); + break; + } + mutex_unlock(&rdev->devlist_mtx); +} +EXPORT_SYMBOL(cfg80211_unregister_wdev); + static struct device_type wiphy_type = { .name = "wlan", }; diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 1cdb1d5e6b0f..8fd0242ee169 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -736,7 +736,6 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, const u8 *buf, size_t len, bool no_cck, bool dont_wait_for_ack, u64 *cookie) { - struct net_device *dev = wdev->netdev; const struct ieee80211_mgmt *mgmt; u16 stype; @@ -796,7 +795,7 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_AP_VLAN: - if (!ether_addr_equal(mgmt->bssid, dev->dev_addr)) + if (!ether_addr_equal(mgmt->bssid, wdev_address(wdev))) err = -EINVAL; break; case NL80211_IFTYPE_MESH_POINT: @@ -809,6 +808,11 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, * cfg80211 doesn't track the stations */ break; + case NL80211_IFTYPE_P2P_DEVICE: + /* + * fall through, P2P device only supports + * public action frames + */ default: err = -EOPNOTSUPP; break; @@ -819,7 +823,7 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, return err; } - if (!ether_addr_equal(mgmt->sa, dev->dev_addr)) + if (!ether_addr_equal(mgmt->sa, wdev_address(wdev))) return -EINVAL; /* Transmit the Action frame as requested by user space */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 97026f3b215a..787aeaa902fe 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1100,6 +1100,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, if (nla_put_u32(msg, i, NL80211_CMD_REGISTER_BEACONS)) goto nla_put_failure; } + CMD(start_p2p_device, START_P2P_DEVICE); #ifdef CONFIG_NL80211_TESTMODE CMD(testmode_cmd, TESTMODE); @@ -1748,13 +1749,13 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, if (dev && (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || - nla_put_string(msg, NL80211_ATTR_IFNAME, dev->name) || - nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dev->dev_addr))) + nla_put_string(msg, NL80211_ATTR_IFNAME, dev->name))) goto nla_put_failure; if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFTYPE, wdev->iftype) || nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, wdev_address(wdev)) || nla_put_u32(msg, NL80211_ATTR_GENERATION, rdev->devlist_generation ^ (cfg80211_rdev_list_generation << 2))) @@ -2021,8 +2022,10 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) return PTR_ERR(wdev); } - if (type == NL80211_IFTYPE_MESH_POINT && - info->attrs[NL80211_ATTR_MESH_ID]) { + switch (type) { + case NL80211_IFTYPE_MESH_POINT: + if (!info->attrs[NL80211_ATTR_MESH_ID]) + break; wdev_lock(wdev); BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != IEEE80211_MAX_MESH_ID_LEN); @@ -2031,6 +2034,26 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]), wdev->mesh_id_up_len); wdev_unlock(wdev); + break; + case NL80211_IFTYPE_P2P_DEVICE: + /* + * P2P Device doesn't have a netdev, so doesn't go + * through the netdev notifier and must be added here + */ + mutex_init(&wdev->mtx); + INIT_LIST_HEAD(&wdev->event_list); + spin_lock_init(&wdev->event_lock); + INIT_LIST_HEAD(&wdev->mgmt_registrations); + spin_lock_init(&wdev->mgmt_registrations_lock); + + mutex_lock(&rdev->devlist_mtx); + wdev->identifier = ++rdev->wdev_id; + list_add_rcu(&wdev->list, &rdev->wdev_list); + rdev->devlist_generation++; + mutex_unlock(&rdev->devlist_mtx); + break; + default: + break; } if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0, @@ -6053,6 +6076,7 @@ static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info) case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_P2P_DEVICE: break; default: return -EOPNOTSUPP; @@ -6099,6 +6123,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_P2P_DEVICE: break; default: return -EOPNOTSUPP; @@ -6195,6 +6220,7 @@ static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *in case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_P2P_DEVICE: break; default: return -EOPNOTSUPP; @@ -6810,6 +6836,68 @@ static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info) return 0; } +static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wireless_dev *wdev = info->user_ptr[1]; + int err; + + if (!rdev->ops->start_p2p_device) + return -EOPNOTSUPP; + + if (wdev->iftype != NL80211_IFTYPE_P2P_DEVICE) + return -EOPNOTSUPP; + + if (wdev->p2p_started) + return 0; + + mutex_lock(&rdev->devlist_mtx); + err = cfg80211_can_add_interface(rdev, wdev->iftype); + mutex_unlock(&rdev->devlist_mtx); + if (err) + return err; + + err = rdev->ops->start_p2p_device(&rdev->wiphy, wdev); + if (err) + return err; + + wdev->p2p_started = true; + mutex_lock(&rdev->devlist_mtx); + rdev->opencount++; + mutex_unlock(&rdev->devlist_mtx); + + return 0; +} + +static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wireless_dev *wdev = info->user_ptr[1]; + + if (wdev->iftype != NL80211_IFTYPE_P2P_DEVICE) + return -EOPNOTSUPP; + + if (!rdev->ops->stop_p2p_device) + return -EOPNOTSUPP; + + if (!wdev->p2p_started) + return 0; + + rdev->ops->stop_p2p_device(&rdev->wiphy, wdev); + wdev->p2p_started = false; + + mutex_lock(&rdev->devlist_mtx); + rdev->opencount--; + mutex_unlock(&rdev->devlist_mtx); + + if (WARN_ON(rdev->scan_req && rdev->scan_req->wdev == wdev)) { + rdev->scan_req->aborted = true; + ___cfg80211_scan_done(rdev, true); + } + + return 0; +} + #define NL80211_FLAG_NEED_WIPHY 0x01 #define NL80211_FLAG_NEED_NETDEV 0x02 #define NL80211_FLAG_NEED_RTNL 0x04 @@ -6817,7 +6905,7 @@ static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info) #define NL80211_FLAG_NEED_NETDEV_UP (NL80211_FLAG_NEED_NETDEV |\ NL80211_FLAG_CHECK_NETDEV_UP) #define NL80211_FLAG_NEED_WDEV 0x10 -/* If a netdev is associated, it must be UP */ +/* If a netdev is associated, it must be UP, P2P must be started */ #define NL80211_FLAG_NEED_WDEV_UP (NL80211_FLAG_NEED_WDEV |\ NL80211_FLAG_CHECK_NETDEV_UP) @@ -6878,6 +6966,13 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb, } dev_hold(dev); + } else if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP) { + if (!wdev->p2p_started) { + mutex_unlock(&cfg80211_mutex); + if (rtnl) + rtnl_unlock(); + return -ENETDOWN; + } } cfg80211_lock_rdev(rdev); @@ -7439,7 +7534,22 @@ static struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, - + { + .cmd = NL80211_CMD_START_P2P_DEVICE, + .doit = nl80211_start_p2p_device, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_WDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_STOP_P2P_DEVICE, + .doit = nl80211_stop_p2p_device, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_WDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { diff --git a/net/wireless/util.c b/net/wireless/util.c index ce393dd8c928..d7b672262b5f 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -800,6 +800,10 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, if (otype == NL80211_IFTYPE_AP_VLAN) return -EOPNOTSUPP; + /* cannot change into P2P device type */ + if (ntype == NL80211_IFTYPE_P2P_DEVICE) + return -EOPNOTSUPP; + if (!rdev->ops->change_virtual_intf || !(rdev->wiphy.interface_modes & (1 << ntype))) return -EOPNOTSUPP; @@ -877,6 +881,9 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, case NUM_NL80211_IFTYPES: /* not happening */ break; + case NL80211_IFTYPE_P2P_DEVICE: + WARN_ON(1); + break; } } @@ -1041,8 +1048,15 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, list_for_each_entry(wdev_iter, &rdev->wdev_list, list) { if (wdev_iter == wdev) continue; - if (!netif_running(wdev_iter->netdev)) - continue; + if (wdev_iter->netdev) { + if (!netif_running(wdev_iter->netdev)) + continue; + } else if (wdev_iter->iftype == NL80211_IFTYPE_P2P_DEVICE) { + if (!wdev_iter->p2p_started) + continue; + } else { + WARN_ON(1); + } if (rdev->wiphy.software_iftypes & BIT(wdev_iter->iftype)) continue; -- cgit v1.2.3 From 6d71117a279aa30574a8af6c7348570c292285c2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 19 Jun 2012 17:19:44 +0200 Subject: mac80211: add IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF Some devices like the current iwlwifi implementation require that the P2P interface address match the P2P Device address (only one P2P interface is supported.) Add the HW flag IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF that allows drivers to request that P2P Interfaces added while a P2P Device is active get the same MAC address by default. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 5 +++++ net/mac80211/iface.c | 15 ++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index edc235c51a68..71f8262fc1df 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1241,6 +1241,10 @@ struct ieee80211_tx_control { * queue mapping in order to use different queues (not just one per AC) * for different virtual interfaces. See the doc section on HW queue * control for more details. + * + * @IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF: Use the P2P Device address for any + * P2P Interface. This will be honoured even if more than one interface + * is supported. */ enum ieee80211_hw_flags { IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, @@ -1268,6 +1272,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_AP_LINK_PS = 1<<22, IEEE80211_HW_TX_AMPDU_SETUP_IN_HW = 1<<23, IEEE80211_HW_SCAN_WHILE_IDLE = 1<<24, + IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF = 1<<25, }; /** diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 152aeea14c85..59f8adc2aa5f 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1313,7 +1313,6 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local, local->hw.wiphy->n_addresses <= 1) return; - mutex_lock(&local->iflist_mtx); switch (type) { @@ -1331,6 +1330,19 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local, } /* keep default if no AP interface present */ break; + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + if (local->hw.flags & IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF) { + list_for_each_entry(sdata, &local->interfaces, list) { + if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE) + continue; + if (!ieee80211_sdata_running(sdata)) + continue; + memcpy(perm_addr, sdata->vif.addr, ETH_ALEN); + goto out_unlock; + } + } + /* otherwise fall through */ default: /* assign a new address if possible -- try n_addresses first */ for (i = 0; i < local->hw.wiphy->n_addresses; i++) { @@ -1405,6 +1417,7 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local, break; } + out_unlock: mutex_unlock(&local->iflist_mtx); } -- cgit v1.2.3 From 786e22885d9959fda0473ace5a61cb11620fba9b Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Tue, 24 Jul 2012 17:20:02 +0800 Subject: PCI: Add pcie_flags_reg to cache PCIe capabilities register Since PCI Express Capabilities Register is read only, cache its value into struct pci_dev to avoid repeatedly calling pci_read_config_*(). Signed-off-by: Yijing Wang Signed-off-by: Jiang Liu Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 3 ++- include/linux/pci.h | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 6c143b4497ca..ba4d8550503c 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -929,7 +929,8 @@ void set_pcie_port_type(struct pci_dev *pdev) pdev->is_pcie = 1; pdev->pcie_cap = pos; pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, ®16); - pdev->pcie_type = (reg16 & PCI_EXP_FLAGS_TYPE) >> 4; + pdev->pcie_flags_reg = reg16; + pdev->pcie_type = pci_pcie_type(pdev); pci_read_config_word(pdev, pos + PCI_EXP_DEVCAP, ®16); pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD; } diff --git a/include/linux/pci.h b/include/linux/pci.h index 5faa8310eec9..95662b2f0c3d 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -258,6 +258,7 @@ struct pci_dev { u8 pcie_mpss:3; /* PCI-E Max Payload Size Supported */ u8 rom_base_reg; /* which config register controls the ROM */ u8 pin; /* which interrupt pin this device uses */ + u16 pcie_flags_reg; /* cached PCI-E Capabilities Register */ struct pci_driver *driver; /* which driver has allocated this device */ u64 dma_mask; /* Mask of the bits of bus address this @@ -1650,6 +1651,15 @@ static inline bool pci_is_pcie(struct pci_dev *dev) return !!pci_pcie_cap(dev); } +/** + * pci_pcie_type - get the PCIe device/port type + * @dev: PCI device + */ +static inline int pci_pcie_type(const struct pci_dev *dev) +{ + return (dev->pcie_flags_reg & PCI_EXP_FLAGS_TYPE) >> 4; +} + void pci_request_acs(void); bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags); bool pci_acs_path_enabled(struct pci_dev *start, -- cgit v1.2.3 From 7fda953ffed1b94aa68f80c6c3ab312328aedcb3 Mon Sep 17 00:00:00 2001 From: Lan Tianyu Date: Fri, 17 Aug 2012 16:44:56 +0800 Subject: usb: convert USB_QUIRK_RESET_MORPHS to USB_QUIRK_RESET Since the attribute avoid_reset_quirk is work for all devices including those devices that can't morph, convert USB_QUIRK_RESET_MORPHS to USB_QUIRK_RESET. Signed-off-by: Lan Tianyu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/quirks.c | 2 +- drivers/usb/core/sysfs.c | 6 +++--- drivers/usb/storage/transport.c | 2 +- include/linux/usb/quirks.h | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index f15501f4c585..2dbb5154f356 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -205,7 +205,7 @@ void usb_detect_quirks(struct usb_device *udev) * for all devices. It will affect things like hub resets * and EMF-related port disables. */ - if (!(udev->quirks & USB_QUIRK_RESET_MORPHS)) + if (!(udev->quirks & USB_QUIRK_RESET)) udev->persist_enabled = 1; #endif /* CONFIG_PM */ } diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index d6c49d9ed4e0..818e4a024d0d 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -196,7 +196,7 @@ show_avoid_reset_quirk(struct device *dev, struct device_attribute *attr, char * struct usb_device *udev; udev = to_usb_device(dev); - return sprintf(buf, "%d\n", !!(udev->quirks & USB_QUIRK_RESET_MORPHS)); + return sprintf(buf, "%d\n", !!(udev->quirks & USB_QUIRK_RESET)); } static ssize_t @@ -210,9 +210,9 @@ set_avoid_reset_quirk(struct device *dev, struct device_attribute *attr, return -EINVAL; usb_lock_device(udev); if (val) - udev->quirks |= USB_QUIRK_RESET_MORPHS; + udev->quirks |= USB_QUIRK_RESET; else - udev->quirks &= ~USB_QUIRK_RESET_MORPHS; + udev->quirks &= ~USB_QUIRK_RESET; usb_unlock_device(udev); return count; } diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index c70109e5d60b..c0543c83923e 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -1331,7 +1331,7 @@ int usb_stor_port_reset(struct us_data *us) int result; /*for these devices we must use the class specific method */ - if (us->pusb_dev->quirks & USB_QUIRK_RESET_MORPHS) + if (us->pusb_dev->quirks & USB_QUIRK_RESET) return -EPERM; result = usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf); diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h index 3e93de7ecbc3..52f944dfe2fd 100644 --- a/include/linux/usb/quirks.h +++ b/include/linux/usb/quirks.h @@ -19,8 +19,8 @@ /* device can't handle its Configuration or Interface strings */ #define USB_QUIRK_CONFIG_INTF_STRINGS 0x00000008 -/*device will morph if reset, don't use reset for handling errors */ -#define USB_QUIRK_RESET_MORPHS 0x00000010 +/* device can't be reset(e.g morph devices), don't use reset */ +#define USB_QUIRK_RESET 0x00000010 /* device has more interface descriptions than the bNumInterfaces count, and can't handle talking to these interfaces */ -- cgit v1.2.3 From 606a5020b9bdceb20b4f43e11db0054afa349028 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 20 Aug 2012 14:51:23 -0700 Subject: workqueue: gut flush[_delayed]_work_sync() Now that all workqueues are non-reentrant, flush[_delayed]_work_sync() are equivalent to flush[_delayed]_work(). Drop the separate implementation and make them thin wrappers around flush[_delayed]_work(). * start_flush_work() no longer takes @wait_executing as the only left user - flush_work() - always sets it to %true. * __cancel_work_timer() uses flush_work() instead of wait_on_work(). Signed-off-by: Tejun Heo --- include/linux/workqueue.h | 14 +++++- kernel/workqueue.c | 122 ++++------------------------------------------ 2 files changed, 22 insertions(+), 114 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index b14d5d59af7c..4f9d3bc161a2 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -412,11 +412,9 @@ extern int keventd_up(void); int execute_in_process_context(work_func_t fn, struct execute_work *); extern bool flush_work(struct work_struct *work); -extern bool flush_work_sync(struct work_struct *work); extern bool cancel_work_sync(struct work_struct *work); extern bool flush_delayed_work(struct delayed_work *dwork); -extern bool flush_delayed_work_sync(struct delayed_work *work); extern bool cancel_delayed_work_sync(struct delayed_work *dwork); extern void workqueue_set_max_active(struct workqueue_struct *wq, @@ -456,6 +454,18 @@ static inline bool __cancel_delayed_work(struct delayed_work *work) return ret; } +/* used to be different but now identical to flush_work(), deprecated */ +static inline bool flush_work_sync(struct work_struct *work) +{ + return flush_work(work); +} + +/* used to be different but now identical to flush_delayed_work(), deprecated */ +static inline bool flush_delayed_work_sync(struct delayed_work *dwork) +{ + return flush_delayed_work(dwork); +} + #ifndef CONFIG_SMP static inline long work_on_cpu(unsigned int cpu, long (*fn)(void *), void *arg) { diff --git a/kernel/workqueue.c b/kernel/workqueue.c index c4feef9798ea..5f13a9a2c792 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -2801,8 +2801,7 @@ reflush: } EXPORT_SYMBOL_GPL(drain_workqueue); -static bool start_flush_work(struct work_struct *work, struct wq_barrier *barr, - bool wait_executing) +static bool start_flush_work(struct work_struct *work, struct wq_barrier *barr) { struct worker *worker = NULL; struct global_cwq *gcwq; @@ -2824,13 +2823,12 @@ static bool start_flush_work(struct work_struct *work, struct wq_barrier *barr, cwq = get_work_cwq(work); if (unlikely(!cwq || gcwq != cwq->pool->gcwq)) goto already_gone; - } else if (wait_executing) { + } else { worker = find_worker_executing_work(gcwq, work); if (!worker) goto already_gone; cwq = worker->current_cwq; - } else - goto already_gone; + } insert_wq_barrier(cwq, barr, work, worker); spin_unlock_irq(&gcwq->lock); @@ -2857,15 +2855,8 @@ already_gone: * flush_work - wait for a work to finish executing the last queueing instance * @work: the work to flush * - * Wait until @work has finished execution. This function considers - * only the last queueing instance of @work. If @work has been - * enqueued across different CPUs on a non-reentrant workqueue or on - * multiple workqueues, @work might still be executing on return on - * some of the CPUs from earlier queueing. - * - * If @work was queued only on a non-reentrant, ordered or unbound - * workqueue, @work is guaranteed to be idle on return if it hasn't - * been requeued since flush started. + * Wait until @work has finished execution. @work is guaranteed to be idle + * on return if it hasn't been requeued since flush started. * * RETURNS: * %true if flush_work() waited for the work to finish execution, @@ -2878,85 +2869,15 @@ bool flush_work(struct work_struct *work) lock_map_acquire(&work->lockdep_map); lock_map_release(&work->lockdep_map); - if (start_flush_work(work, &barr, true)) { + if (start_flush_work(work, &barr)) { wait_for_completion(&barr.done); destroy_work_on_stack(&barr.work); return true; - } else - return false; -} -EXPORT_SYMBOL_GPL(flush_work); - -static bool wait_on_cpu_work(struct global_cwq *gcwq, struct work_struct *work) -{ - struct wq_barrier barr; - struct worker *worker; - - spin_lock_irq(&gcwq->lock); - - worker = find_worker_executing_work(gcwq, work); - if (unlikely(worker)) - insert_wq_barrier(worker->current_cwq, &barr, work, worker); - - spin_unlock_irq(&gcwq->lock); - - if (unlikely(worker)) { - wait_for_completion(&barr.done); - destroy_work_on_stack(&barr.work); - return true; - } else + } else { return false; -} - -static bool wait_on_work(struct work_struct *work) -{ - bool ret = false; - int cpu; - - might_sleep(); - - lock_map_acquire(&work->lockdep_map); - lock_map_release(&work->lockdep_map); - - for_each_gcwq_cpu(cpu) - ret |= wait_on_cpu_work(get_gcwq(cpu), work); - return ret; -} - -/** - * flush_work_sync - wait until a work has finished execution - * @work: the work to flush - * - * Wait until @work has finished execution. On return, it's - * guaranteed that all queueing instances of @work which happened - * before this function is called are finished. In other words, if - * @work hasn't been requeued since this function was called, @work is - * guaranteed to be idle on return. - * - * RETURNS: - * %true if flush_work_sync() waited for the work to finish execution, - * %false if it was already idle. - */ -bool flush_work_sync(struct work_struct *work) -{ - struct wq_barrier barr; - bool pending, waited; - - /* we'll wait for executions separately, queue barr only if pending */ - pending = start_flush_work(work, &barr, false); - - /* wait for executions to finish */ - waited = wait_on_work(work); - - /* wait for the pending one */ - if (pending) { - wait_for_completion(&barr.done); - destroy_work_on_stack(&barr.work); } - - return pending || waited; } -EXPORT_SYMBOL_GPL(flush_work_sync); +EXPORT_SYMBOL_GPL(flush_work); static bool __cancel_work_timer(struct work_struct *work, bool is_dwork) { @@ -2970,14 +2891,14 @@ static bool __cancel_work_timer(struct work_struct *work, bool is_dwork) * would be waiting for before retrying. */ if (unlikely(ret == -ENOENT)) - wait_on_work(work); + flush_work(work); } while (unlikely(ret < 0)); /* tell other tasks trying to grab @work to back off */ mark_work_canceling(work); local_irq_restore(flags); - wait_on_work(work); + flush_work(work); clear_work_data(work); return ret; } @@ -3029,29 +2950,6 @@ bool flush_delayed_work(struct delayed_work *dwork) } EXPORT_SYMBOL(flush_delayed_work); -/** - * flush_delayed_work_sync - wait for a dwork to finish - * @dwork: the delayed work to flush - * - * Delayed timer is cancelled and the pending work is queued for - * execution immediately. Other than timer handling, its behavior - * is identical to flush_work_sync(). - * - * RETURNS: - * %true if flush_work_sync() waited for the work to finish execution, - * %false if it was already idle. - */ -bool flush_delayed_work_sync(struct delayed_work *dwork) -{ - local_irq_disable(); - if (del_timer_sync(&dwork->timer)) - __queue_work(dwork->cpu, - get_work_cwq(&dwork->work)->wq, &dwork->work); - local_irq_enable(); - return flush_work_sync(&dwork->work); -} -EXPORT_SYMBOL(flush_delayed_work_sync); - /** * cancel_delayed_work_sync - cancel a delayed work and wait for it to finish * @dwork: the delayed work cancel -- cgit v1.2.3 From ae930e0f4e66fd540c6fbad9f1e2a7743d8b9afe Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 20 Aug 2012 14:51:23 -0700 Subject: workqueue: gut system_nrt[_freezable]_wq() Now that all workqueues are non-reentrant, system[_freezable]_wq() are equivalent to system_nrt[_freezable]_wq(). Replace the latter with wrappers around system[_freezable]_wq(). The wrapping goes through inline functions so that __deprecated can be added easily. Signed-off-by: Tejun Heo --- include/linux/workqueue.h | 23 ++++++++++++++--------- kernel/workqueue.c | 10 +--------- 2 files changed, 15 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 4f9d3bc161a2..855fcdaa2d72 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -297,10 +297,6 @@ enum { * system_long_wq is similar to system_wq but may host long running * works. Queue flushing might take relatively long. * - * system_nrt_wq is non-reentrant and guarantees that any given work - * item is never executed in parallel by multiple CPUs. Queue - * flushing might take relatively long. - * * system_unbound_wq is unbound workqueue. Workers are not bound to * any specific CPU, not concurrency managed, and all queued works are * executed immediately as long as max_active limit is not reached and @@ -308,16 +304,25 @@ enum { * * system_freezable_wq is equivalent to system_wq except that it's * freezable. - * - * system_nrt_freezable_wq is equivalent to system_nrt_wq except that - * it's freezable. */ extern struct workqueue_struct *system_wq; extern struct workqueue_struct *system_long_wq; -extern struct workqueue_struct *system_nrt_wq; extern struct workqueue_struct *system_unbound_wq; extern struct workqueue_struct *system_freezable_wq; -extern struct workqueue_struct *system_nrt_freezable_wq; + +static inline struct workqueue_struct *__system_nrt_wq(void) +{ + return system_wq; +} + +static inline struct workqueue_struct *__system_nrt_freezable_wq(void) +{ + return system_freezable_wq; +} + +/* equivlalent to system_wq and system_freezable_wq, deprecated */ +#define system_nrt_wq __system_nrt_wq() +#define system_nrt_freezable_wq __system_nrt_freezable_wq() extern struct workqueue_struct * __alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active, diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 5f13a9a2c792..85bd3409b9f5 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -274,14 +274,10 @@ struct workqueue_struct *system_highpri_wq __read_mostly; EXPORT_SYMBOL_GPL(system_highpri_wq); struct workqueue_struct *system_long_wq __read_mostly; EXPORT_SYMBOL_GPL(system_long_wq); -struct workqueue_struct *system_nrt_wq __read_mostly; -EXPORT_SYMBOL_GPL(system_nrt_wq); struct workqueue_struct *system_unbound_wq __read_mostly; EXPORT_SYMBOL_GPL(system_unbound_wq); struct workqueue_struct *system_freezable_wq __read_mostly; EXPORT_SYMBOL_GPL(system_freezable_wq); -struct workqueue_struct *system_nrt_freezable_wq __read_mostly; -EXPORT_SYMBOL_GPL(system_nrt_freezable_wq); #define CREATE_TRACE_POINTS #include @@ -3838,16 +3834,12 @@ static int __init init_workqueues(void) system_wq = alloc_workqueue("events", 0, 0); system_highpri_wq = alloc_workqueue("events_highpri", WQ_HIGHPRI, 0); system_long_wq = alloc_workqueue("events_long", 0, 0); - system_nrt_wq = alloc_workqueue("events_nrt", WQ_NON_REENTRANT, 0); system_unbound_wq = alloc_workqueue("events_unbound", WQ_UNBOUND, WQ_UNBOUND_MAX_ACTIVE); system_freezable_wq = alloc_workqueue("events_freezable", WQ_FREEZABLE, 0); - system_nrt_freezable_wq = alloc_workqueue("events_nrt_freezable", - WQ_NON_REENTRANT | WQ_FREEZABLE, 0); BUG_ON(!system_wq || !system_highpri_wq || !system_long_wq || - !system_nrt_wq || !system_unbound_wq || !system_freezable_wq || - !system_nrt_freezable_wq); + !system_unbound_wq || !system_freezable_wq); return 0; } early_initcall(init_workqueues); -- cgit v1.2.3 From 43829731dd372d04d6706c51052b9dabab9ca356 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 20 Aug 2012 14:51:24 -0700 Subject: workqueue: deprecate flush[_delayed]_work_sync() flush[_delayed]_work_sync() are now spurious. Mark them deprecated and convert all users to flush[_delayed]_work(). If you're cc'd and wondering what's going on: Now all workqueues are non-reentrant and the regular flushes guarantee that the work item is not pending or running on any CPU on return, so there's no reason to use the sync flushes at all and they're going away. This patch doesn't make any functional difference. Signed-off-by: Tejun Heo Cc: Russell King Cc: Paul Mundt Cc: Ian Campbell Cc: Jens Axboe Cc: Mattia Dongili Cc: Kent Yoder Cc: David Airlie Cc: Jiri Kosina Cc: Karsten Keil Cc: Bryan Wu Cc: Benjamin Herrenschmidt Cc: Alasdair Kergon Cc: Mauro Carvalho Chehab Cc: Florian Tobias Schandinat Cc: David Woodhouse Cc: "David S. Miller" Cc: linux-wireless@vger.kernel.org Cc: Anton Vorontsov Cc: Sangbeom Kim Cc: "James E.J. Bottomley" Cc: Greg Kroah-Hartman Cc: Eric Van Hensbergen Cc: Takashi Iwai Cc: Steven Whitehouse Cc: Petr Vandrovec Cc: Mark Fasheh Cc: Christoph Hellwig Cc: Avi Kivity --- arch/arm/mach-pxa/sharpsl_pm.c | 4 ++-- arch/arm/plat-omap/mailbox.c | 2 +- arch/sh/drivers/push-switch.c | 2 +- drivers/block/xen-blkfront.c | 4 ++-- drivers/cdrom/gdrom.c | 2 +- drivers/char/sonypi.c | 2 +- drivers/char/tpm/tpm.c | 4 ++-- drivers/gpu/drm/exynos/exynos_drm_g2d.c | 2 +- drivers/gpu/drm/nouveau/nouveau_gpio.c | 2 +- drivers/gpu/drm/radeon/radeon_irq_kms.c | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_fb.c | 2 +- drivers/hid/hid-picolcd.c | 2 +- drivers/input/touchscreen/wm831x-ts.c | 2 +- drivers/isdn/mISDN/hwchannel.c | 4 ++-- drivers/leds/leds-lm3533.c | 6 +++--- drivers/leds/leds-lp8788.c | 2 +- drivers/leds/leds-wm8350.c | 2 +- drivers/macintosh/ams/ams-core.c | 2 +- drivers/md/dm-mpath.c | 2 +- drivers/md/dm-raid1.c | 2 +- drivers/md/dm-stripe.c | 2 +- drivers/media/dvb/dvb-core/dvb_net.c | 4 ++-- drivers/media/dvb/mantis/mantis_evm.c | 2 +- drivers/media/dvb/mantis/mantis_uart.c | 2 +- drivers/media/video/bt8xx/bttv-driver.c | 2 +- drivers/media/video/cx18/cx18-driver.c | 2 +- drivers/media/video/cx231xx/cx231xx-cards.c | 2 +- drivers/media/video/cx23885/cx23885-input.c | 6 +++--- drivers/media/video/cx88/cx88-mpeg.c | 2 +- drivers/media/video/em28xx/em28xx-cards.c | 2 +- drivers/media/video/omap24xxcam.c | 6 +++--- drivers/media/video/saa7134/saa7134-core.c | 2 +- drivers/media/video/saa7134/saa7134-empress.c | 2 +- drivers/media/video/tm6000/tm6000-cards.c | 2 +- drivers/mfd/menelaus.c | 4 ++-- drivers/misc/ioc4.c | 2 +- drivers/mtd/mtdoops.c | 4 ++-- drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c | 2 +- drivers/net/ethernet/neterion/vxge/vxge-main.c | 2 +- drivers/net/ethernet/sun/cassini.c | 2 +- drivers/net/ethernet/sun/niu.c | 2 +- drivers/net/wireless/hostap/hostap_ap.c | 4 ++-- drivers/net/wireless/hostap/hostap_hw.c | 10 +++++----- drivers/power/collie_battery.c | 2 +- drivers/power/tosa_battery.c | 2 +- drivers/power/wm97xx_battery.c | 2 +- drivers/power/z2_battery.c | 2 +- drivers/regulator/core.c | 2 +- drivers/scsi/arcmsr/arcmsr_hba.c | 4 ++-- drivers/scsi/ipr.c | 2 +- drivers/scsi/pmcraid.c | 2 +- drivers/scsi/qla2xxx/qla_target.c | 2 +- drivers/tty/hvc/hvsi.c | 2 +- drivers/tty/ipwireless/hardware.c | 2 +- drivers/tty/ipwireless/network.c | 4 ++-- drivers/tty/serial/kgdboc.c | 2 +- drivers/tty/serial/omap-serial.c | 2 +- drivers/tty/tty_ldisc.c | 6 +++--- drivers/usb/atm/speedtch.c | 2 +- drivers/usb/atm/ueagle-atm.c | 2 +- drivers/usb/gadget/u_ether.c | 2 +- drivers/usb/host/ohci-hcd.c | 2 +- drivers/usb/otg/isp1301_omap.c | 2 +- fs/affs/super.c | 2 +- fs/gfs2/lock_dlm.c | 2 +- fs/gfs2/super.c | 2 +- fs/hfs/inode.c | 2 +- fs/ncpfs/inode.c | 6 +++--- fs/ocfs2/cluster/quorum.c | 2 +- fs/xfs/xfs_super.c | 2 +- fs/xfs/xfs_sync.c | 2 +- include/linux/workqueue.h | 4 ++-- net/9p/trans_fd.c | 2 +- net/dsa/dsa.c | 2 +- sound/i2c/other/ak4113.c | 2 +- sound/i2c/other/ak4114.c | 2 +- sound/pci/oxygen/oxygen_lib.c | 8 ++++---- sound/soc/codecs/wm8350.c | 2 +- sound/soc/codecs/wm8753.c | 2 +- sound/soc/soc-core.c | 6 +++--- virt/kvm/eventfd.c | 2 +- 81 files changed, 111 insertions(+), 111 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-pxa/sharpsl_pm.c b/arch/arm/mach-pxa/sharpsl_pm.c index bdf4cb88ca0a..7875ad6456bd 100644 --- a/arch/arm/mach-pxa/sharpsl_pm.c +++ b/arch/arm/mach-pxa/sharpsl_pm.c @@ -579,8 +579,8 @@ static int sharpsl_ac_check(void) static int sharpsl_pm_suspend(struct platform_device *pdev, pm_message_t state) { sharpsl_pm.flags |= SHARPSL_SUSPENDED; - flush_delayed_work_sync(&toggle_charger); - flush_delayed_work_sync(&sharpsl_bat); + flush_delayed_work(&toggle_charger); + flush_delayed_work(&sharpsl_bat); if (sharpsl_pm.charge_mode == CHRG_ON) sharpsl_pm.flags |= SHARPSL_DO_OFFLINE_CHRG; diff --git a/arch/arm/plat-omap/mailbox.c b/arch/arm/plat-omap/mailbox.c index 5e13c3884aa4..42377ef9ea3d 100644 --- a/arch/arm/plat-omap/mailbox.c +++ b/arch/arm/plat-omap/mailbox.c @@ -310,7 +310,7 @@ static void omap_mbox_fini(struct omap_mbox *mbox) omap_mbox_disable_irq(mbox, IRQ_RX); free_irq(mbox->irq, mbox); tasklet_kill(&mbox->txq->tasklet); - flush_work_sync(&mbox->rxq->work); + flush_work(&mbox->rxq->work); mbox_queue_free(mbox->txq); mbox_queue_free(mbox->rxq); } diff --git a/arch/sh/drivers/push-switch.c b/arch/sh/drivers/push-switch.c index 637b79b09657..5bfb341cc5c4 100644 --- a/arch/sh/drivers/push-switch.c +++ b/arch/sh/drivers/push-switch.c @@ -107,7 +107,7 @@ static int switch_drv_remove(struct platform_device *pdev) device_remove_file(&pdev->dev, &dev_attr_switch); platform_set_drvdata(pdev, NULL); - flush_work_sync(&psw->work); + flush_work(&psw->work); del_timer_sync(&psw->debounce); free_irq(irq, pdev); diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index 2c2d2e5c1597..007db8986e84 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -670,7 +670,7 @@ static void xlvbd_release_gendisk(struct blkfront_info *info) spin_unlock_irqrestore(&info->io_lock, flags); /* Flush gnttab callback work. Must be done with no locks held. */ - flush_work_sync(&info->work); + flush_work(&info->work); del_gendisk(info->gd); @@ -719,7 +719,7 @@ static void blkif_free(struct blkfront_info *info, int suspend) spin_unlock_irq(&info->io_lock); /* Flush gnttab callback work. Must be done with no locks held. */ - flush_work_sync(&info->work); + flush_work(&info->work); /* Free resources associated with old device channel. */ if (info->ring_ref != GRANT_INVALID_REF) { diff --git a/drivers/cdrom/gdrom.c b/drivers/cdrom/gdrom.c index 3ceaf006e7f0..75d485afe56c 100644 --- a/drivers/cdrom/gdrom.c +++ b/drivers/cdrom/gdrom.c @@ -840,7 +840,7 @@ probe_fail_no_mem: static int __devexit remove_gdrom(struct platform_device *devptr) { - flush_work_sync(&work); + flush_work(&work); blk_cleanup_queue(gd.gdrom_rq); free_irq(HW_EVENT_GDROM_CMD, &gd); free_irq(HW_EVENT_GDROM_DMA, &gd); diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index f87780502b41..320debbe32fa 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -1433,7 +1433,7 @@ static int __devexit sonypi_remove(struct platform_device *dev) sonypi_disable(); synchronize_irq(sonypi_device.irq); - flush_work_sync(&sonypi_device.input_work); + flush_work(&sonypi_device.input_work); if (useinput) { input_unregister_device(sonypi_device.input_key_dev); diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 817f0ee202b6..3af9f4d1a23f 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -1172,7 +1172,7 @@ int tpm_release(struct inode *inode, struct file *file) struct tpm_chip *chip = file->private_data; del_singleshot_timer_sync(&chip->user_read_timer); - flush_work_sync(&chip->work); + flush_work(&chip->work); file->private_data = NULL; atomic_set(&chip->data_pending, 0); kfree(chip->data_buffer); @@ -1225,7 +1225,7 @@ ssize_t tpm_read(struct file *file, char __user *buf, int rc; del_singleshot_timer_sync(&chip->user_read_timer); - flush_work_sync(&chip->work); + flush_work(&chip->work); ret_size = atomic_read(&chip->data_pending); atomic_set(&chip->data_pending, 0); if (ret_size > 0) { /* relay data */ diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index d2d88f22a037..a40808e51338 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -908,7 +908,7 @@ static int g2d_suspend(struct device *dev) /* FIXME: good range? */ usleep_range(500, 1000); - flush_work_sync(&g2d->runqueue_work); + flush_work(&g2d->runqueue_work); return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_gpio.c b/drivers/gpu/drm/nouveau/nouveau_gpio.c index 82c19e82ff02..0fe4e17c461d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gpio.c +++ b/drivers/gpu/drm/nouveau/nouveau_gpio.c @@ -302,7 +302,7 @@ nouveau_gpio_isr_del(struct drm_device *dev, int idx, u8 tag, u8 line, spin_unlock_irqrestore(&pgpio->lock, flags); list_for_each_entry_safe(isr, tmp, &tofree, head) { - flush_work_sync(&isr->work); + flush_work(&isr->work); kfree(isr); } } diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c index afaa1727abd2..50b596ec7b7e 100644 --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -277,7 +277,7 @@ void radeon_irq_kms_fini(struct radeon_device *rdev) if (rdev->msi_enabled) pci_disable_msi(rdev->pdev); } - flush_work_sync(&rdev->hotplug_work); + flush_work(&rdev->hotplug_work); } /** diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c index 3c447bf317cb..a32f2e96dd02 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c @@ -594,7 +594,7 @@ int vmw_fb_off(struct vmw_private *vmw_priv) par->dirty.active = false; spin_unlock_irqrestore(&par->dirty.lock, flags); - flush_delayed_work_sync(&info->deferred_work); + flush_delayed_work(&info->deferred_work); par->bo_ptr = NULL; ttm_bo_kunmap(&par->map); diff --git a/drivers/hid/hid-picolcd.c b/drivers/hid/hid-picolcd.c index 27c8ebdfad01..3e90dee30ff6 100644 --- a/drivers/hid/hid-picolcd.c +++ b/drivers/hid/hid-picolcd.c @@ -2737,7 +2737,7 @@ static void __exit picolcd_exit(void) { hid_unregister_driver(&picolcd_driver); #ifdef CONFIG_HID_PICOLCD_FB - flush_work_sync(&picolcd_fb_cleanup); + flush_work(&picolcd_fb_cleanup); WARN_ON(fb_pending); #endif } diff --git a/drivers/input/touchscreen/wm831x-ts.c b/drivers/input/touchscreen/wm831x-ts.c index e83410721e38..52abb98a8ae5 100644 --- a/drivers/input/touchscreen/wm831x-ts.c +++ b/drivers/input/touchscreen/wm831x-ts.c @@ -221,7 +221,7 @@ static void wm831x_ts_input_close(struct input_dev *idev) synchronize_irq(wm831x_ts->pd_irq); /* Make sure the IRQ completion work is quiesced */ - flush_work_sync(&wm831x_ts->pd_data_work); + flush_work(&wm831x_ts->pd_data_work); /* If we ended up with the pen down then make sure we revert back * to pen detection state for the next time we start up. diff --git a/drivers/isdn/mISDN/hwchannel.c b/drivers/isdn/mISDN/hwchannel.c index ef34fd40867c..4b85f7279f18 100644 --- a/drivers/isdn/mISDN/hwchannel.c +++ b/drivers/isdn/mISDN/hwchannel.c @@ -116,7 +116,7 @@ mISDN_freedchannel(struct dchannel *ch) } skb_queue_purge(&ch->squeue); skb_queue_purge(&ch->rqueue); - flush_work_sync(&ch->workq); + flush_work(&ch->workq); return 0; } EXPORT_SYMBOL(mISDN_freedchannel); @@ -157,7 +157,7 @@ mISDN_freebchannel(struct bchannel *ch) mISDN_clear_bchannel(ch); skb_queue_purge(&ch->rqueue); ch->rcount = 0; - flush_work_sync(&ch->workq); + flush_work(&ch->workq); return 0; } EXPORT_SYMBOL(mISDN_freebchannel); diff --git a/drivers/leds/leds-lm3533.c b/drivers/leds/leds-lm3533.c index f56b6e7ffdac..f6837b99908c 100644 --- a/drivers/leds/leds-lm3533.c +++ b/drivers/leds/leds-lm3533.c @@ -737,7 +737,7 @@ err_sysfs_remove: sysfs_remove_group(&led->cdev.dev->kobj, &lm3533_led_attribute_group); err_unregister: led_classdev_unregister(&led->cdev); - flush_work_sync(&led->work); + flush_work(&led->work); return ret; } @@ -751,7 +751,7 @@ static int __devexit lm3533_led_remove(struct platform_device *pdev) lm3533_ctrlbank_disable(&led->cb); sysfs_remove_group(&led->cdev.dev->kobj, &lm3533_led_attribute_group); led_classdev_unregister(&led->cdev); - flush_work_sync(&led->work); + flush_work(&led->work); return 0; } @@ -765,7 +765,7 @@ static void lm3533_led_shutdown(struct platform_device *pdev) lm3533_ctrlbank_disable(&led->cb); lm3533_led_set(&led->cdev, LED_OFF); /* disable blink */ - flush_work_sync(&led->work); + flush_work(&led->work); } static struct platform_driver lm3533_led_driver = { diff --git a/drivers/leds/leds-lp8788.c b/drivers/leds/leds-lp8788.c index 53bd136f1ef0..cb76bc46a021 100644 --- a/drivers/leds/leds-lp8788.c +++ b/drivers/leds/leds-lp8788.c @@ -172,7 +172,7 @@ static int __devexit lp8788_led_remove(struct platform_device *pdev) struct lp8788_led *led = platform_get_drvdata(pdev); led_classdev_unregister(&led->led_dev); - flush_work_sync(&led->work); + flush_work(&led->work); return 0; } diff --git a/drivers/leds/leds-wm8350.c b/drivers/leds/leds-wm8350.c index 918d4baff1c7..4c62113f7a77 100644 --- a/drivers/leds/leds-wm8350.c +++ b/drivers/leds/leds-wm8350.c @@ -275,7 +275,7 @@ static int wm8350_led_remove(struct platform_device *pdev) struct wm8350_led *led = platform_get_drvdata(pdev); led_classdev_unregister(&led->cdev); - flush_work_sync(&led->work); + flush_work(&led->work); wm8350_led_disable(led); regulator_put(led->dcdc); regulator_put(led->isink); diff --git a/drivers/macintosh/ams/ams-core.c b/drivers/macintosh/ams/ams-core.c index 5c6a2d876562..36a4fdddd64a 100644 --- a/drivers/macintosh/ams/ams-core.c +++ b/drivers/macintosh/ams/ams-core.c @@ -226,7 +226,7 @@ void ams_sensor_detach(void) * We do this after ams_info.exit(), because an interrupt might * have arrived before disabling them. */ - flush_work_sync(&ams_info.worker); + flush_work(&ams_info.worker); /* Remove device */ of_device_unregister(ams_info.of_dev); diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index d8abb90a6c2f..bb18eafcc85a 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -944,7 +944,7 @@ static void flush_multipath_work(struct multipath *m) flush_workqueue(kmpath_handlerd); multipath_wait_for_pg_init_completion(m); flush_workqueue(kmultipathd); - flush_work_sync(&m->trigger_event); + flush_work(&m->trigger_event); } static void multipath_dtr(struct dm_target *ti) diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index bc5ddba8045b..fd61f98ee1f6 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -1146,7 +1146,7 @@ static void mirror_dtr(struct dm_target *ti) del_timer_sync(&ms->timer); flush_workqueue(ms->kmirrord_wq); - flush_work_sync(&ms->trigger_event); + flush_work(&ms->trigger_event); dm_kcopyd_client_destroy(ms->kcopyd_client); destroy_workqueue(ms->kmirrord_wq); free_context(ms, ti, ms->nr_mirrors); diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c index a087bf2a8d66..e2f876539743 100644 --- a/drivers/md/dm-stripe.c +++ b/drivers/md/dm-stripe.c @@ -199,7 +199,7 @@ static void stripe_dtr(struct dm_target *ti) for (i = 0; i < sc->stripes; i++) dm_put_device(ti, sc->stripe[i].dev); - flush_work_sync(&sc->trigger_event); + flush_work(&sc->trigger_event); kfree(sc); } diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c index 8766ce8c354d..c2117688aa23 100644 --- a/drivers/media/dvb/dvb-core/dvb_net.c +++ b/drivers/media/dvb/dvb-core/dvb_net.c @@ -1329,8 +1329,8 @@ static int dvb_net_remove_if(struct dvb_net *dvbnet, unsigned long num) return -EBUSY; dvb_net_stop(net); - flush_work_sync(&priv->set_multicast_list_wq); - flush_work_sync(&priv->restart_net_feed_wq); + flush_work(&priv->set_multicast_list_wq); + flush_work(&priv->restart_net_feed_wq); printk("dvb_net: removed network interface %s\n", net->name); unregister_netdev(net); dvbnet->state[num]=0; diff --git a/drivers/media/dvb/mantis/mantis_evm.c b/drivers/media/dvb/mantis/mantis_evm.c index 71ce52875c38..909ff54868a3 100644 --- a/drivers/media/dvb/mantis/mantis_evm.c +++ b/drivers/media/dvb/mantis/mantis_evm.c @@ -111,7 +111,7 @@ void mantis_evmgr_exit(struct mantis_ca *ca) struct mantis_pci *mantis = ca->ca_priv; dprintk(MANTIS_DEBUG, 1, "Mantis Host I/F Event manager exiting"); - flush_work_sync(&ca->hif_evm_work); + flush_work(&ca->hif_evm_work); mantis_hif_exit(ca); mantis_pcmcia_exit(ca); } diff --git a/drivers/media/dvb/mantis/mantis_uart.c b/drivers/media/dvb/mantis/mantis_uart.c index 18340dafa426..85e977861b4a 100644 --- a/drivers/media/dvb/mantis/mantis_uart.c +++ b/drivers/media/dvb/mantis/mantis_uart.c @@ -183,6 +183,6 @@ void mantis_uart_exit(struct mantis_pci *mantis) { /* disable interrupt */ mmwrite(mmread(MANTIS_UART_CTL) & 0xffef, MANTIS_UART_CTL); - flush_work_sync(&mantis->uart_work); + flush_work(&mantis->uart_work); } EXPORT_SYMBOL_GPL(mantis_uart_exit); diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index b58ff87db771..2ce7179a3864 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -196,7 +196,7 @@ static void request_modules(struct bttv *dev) static void flush_request_modules(struct bttv *dev) { - flush_work_sync(&dev->request_module_wk); + flush_work(&dev->request_module_wk); } #else #define request_modules(dev) diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 7e5ffd6f5178..75c890907920 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -272,7 +272,7 @@ static void request_modules(struct cx18 *dev) static void flush_request_modules(struct cx18 *dev) { - flush_work_sync(&dev->request_module_wk); + flush_work(&dev->request_module_wk); } #else #define request_modules(dev) diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/video/cx231xx/cx231xx-cards.c index 02d4d36735d3..b84ebc54d91b 100644 --- a/drivers/media/video/cx231xx/cx231xx-cards.c +++ b/drivers/media/video/cx231xx/cx231xx-cards.c @@ -1002,7 +1002,7 @@ static void request_modules(struct cx231xx *dev) static void flush_request_modules(struct cx231xx *dev) { - flush_work_sync(&dev->request_module_wk); + flush_work(&dev->request_module_wk); } #else #define request_modules(dev) diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c index ce765e3f77bd..bcbf7faf1bab 100644 --- a/drivers/media/video/cx23885/cx23885-input.c +++ b/drivers/media/video/cx23885/cx23885-input.c @@ -231,9 +231,9 @@ static void cx23885_input_ir_stop(struct cx23885_dev *dev) v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms); v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms); } - flush_work_sync(&dev->cx25840_work); - flush_work_sync(&dev->ir_rx_work); - flush_work_sync(&dev->ir_tx_work); + flush_work(&dev->cx25840_work); + flush_work(&dev->ir_rx_work); + flush_work(&dev->ir_tx_work); } static void cx23885_input_ir_close(struct rc_dev *rc) diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index cd5386ee210c..c04fb618e10b 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -70,7 +70,7 @@ static void request_modules(struct cx8802_dev *dev) static void flush_request_modules(struct cx8802_dev *dev) { - flush_work_sync(&dev->request_module_wk); + flush_work(&dev->request_module_wk); } #else #define request_modules(dev) diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index ca62b9981380..f7831e73f077 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -2900,7 +2900,7 @@ static void request_modules(struct em28xx *dev) static void flush_request_modules(struct em28xx *dev) { - flush_work_sync(&dev->request_module_wk); + flush_work(&dev->request_module_wk); } #else #define request_modules(dev) diff --git a/drivers/media/video/omap24xxcam.c b/drivers/media/video/omap24xxcam.c index e5015b0d5508..8d7283bbd431 100644 --- a/drivers/media/video/omap24xxcam.c +++ b/drivers/media/video/omap24xxcam.c @@ -1198,7 +1198,7 @@ static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) atomic_inc(&cam->reset_disable); - flush_work_sync(&cam->sensor_reset_work); + flush_work(&cam->sensor_reset_work); rval = videobuf_streamoff(q); if (!rval) { @@ -1512,7 +1512,7 @@ static int omap24xxcam_release(struct file *file) atomic_inc(&cam->reset_disable); - flush_work_sync(&cam->sensor_reset_work); + flush_work(&cam->sensor_reset_work); /* stop streaming capture */ videobuf_streamoff(&fh->vbq); @@ -1536,7 +1536,7 @@ static int omap24xxcam_release(struct file *file) * not be scheduled anymore since streaming is already * disabled.) */ - flush_work_sync(&cam->sensor_reset_work); + flush_work(&cam->sensor_reset_work); mutex_lock(&cam->mutex); if (atomic_dec_return(&cam->users) == 0) { diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index 5fbb4e49495c..f2b37e05b964 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -170,7 +170,7 @@ static void request_submodules(struct saa7134_dev *dev) static void flush_request_submodules(struct saa7134_dev *dev) { - flush_work_sync(&dev->request_module_wk); + flush_work(&dev->request_module_wk); } #else diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c index dde361a9194e..4df79c656909 100644 --- a/drivers/media/video/saa7134/saa7134-empress.c +++ b/drivers/media/video/saa7134/saa7134-empress.c @@ -556,7 +556,7 @@ static int empress_fini(struct saa7134_dev *dev) if (NULL == dev->empress_dev) return 0; - flush_work_sync(&dev->empress_workqueue); + flush_work(&dev->empress_workqueue); video_unregister_device(dev->empress_dev); dev->empress_dev = NULL; return 0; diff --git a/drivers/media/video/tm6000/tm6000-cards.c b/drivers/media/video/tm6000/tm6000-cards.c index 034659b13174..307d8c5fb7cd 100644 --- a/drivers/media/video/tm6000/tm6000-cards.c +++ b/drivers/media/video/tm6000/tm6000-cards.c @@ -1074,7 +1074,7 @@ static void request_modules(struct tm6000_core *dev) static void flush_request_modules(struct tm6000_core *dev) { - flush_work_sync(&dev->request_module_wk); + flush_work(&dev->request_module_wk); } #else #define request_modules(dev) diff --git a/drivers/mfd/menelaus.c b/drivers/mfd/menelaus.c index cb4910ac4d12..55d589981412 100644 --- a/drivers/mfd/menelaus.c +++ b/drivers/mfd/menelaus.c @@ -1259,7 +1259,7 @@ static int menelaus_probe(struct i2c_client *client, return 0; fail2: free_irq(client->irq, menelaus); - flush_work_sync(&menelaus->work); + flush_work(&menelaus->work); fail1: kfree(menelaus); return err; @@ -1270,7 +1270,7 @@ static int __exit menelaus_remove(struct i2c_client *client) struct menelaus_chip *menelaus = i2c_get_clientdata(client); free_irq(client->irq, menelaus); - flush_work_sync(&menelaus->work); + flush_work(&menelaus->work); kfree(menelaus); the_menelaus = NULL; return 0; diff --git a/drivers/misc/ioc4.c b/drivers/misc/ioc4.c index df03dd3bd0e2..6a7710603a90 100644 --- a/drivers/misc/ioc4.c +++ b/drivers/misc/ioc4.c @@ -487,7 +487,7 @@ static void __exit ioc4_exit(void) { /* Ensure ioc4_load_modules() has completed before exiting */ - flush_work_sync(&ioc4_load_modules_work); + flush_work(&ioc4_load_modules_work); pci_unregister_driver(&ioc4_driver); } diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index 551e316e4454..438737a1f59a 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -387,8 +387,8 @@ static void mtdoops_notify_remove(struct mtd_info *mtd) printk(KERN_WARNING "mtdoops: could not unregister kmsg_dumper\n"); cxt->mtd = NULL; - flush_work_sync(&cxt->work_erase); - flush_work_sync(&cxt->work_write); + flush_work(&cxt->work_erase); + flush_work(&cxt->work_write); } diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c index 6505070abcfa..089ed134369b 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c @@ -1394,7 +1394,7 @@ static int offload_close(struct t3cdev *tdev) sysfs_remove_group(&tdev->lldev->dev.kobj, &offload_attr_group); /* Flush work scheduled while releasing TIDs */ - flush_work_sync(&td->tid_release_task); + flush_work(&td->tid_release_task); tdev->lldev = NULL; cxgb3_set_dummy_ops(tdev); diff --git a/drivers/net/ethernet/neterion/vxge/vxge-main.c b/drivers/net/ethernet/neterion/vxge/vxge-main.c index de2190443510..30ce10bc9149 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-main.c +++ b/drivers/net/ethernet/neterion/vxge/vxge-main.c @@ -3521,7 +3521,7 @@ static void vxge_device_unregister(struct __vxge_hw_device *hldev) strncpy(buf, dev->name, IFNAMSIZ); - flush_work_sync(&vdev->reset_task); + flush_work(&vdev->reset_task); /* in 2.6 will call stop() if device is up */ unregister_netdev(dev); diff --git a/drivers/net/ethernet/sun/cassini.c b/drivers/net/ethernet/sun/cassini.c index ce4df61b4b56..c8251be104d6 100644 --- a/drivers/net/ethernet/sun/cassini.c +++ b/drivers/net/ethernet/sun/cassini.c @@ -3890,7 +3890,7 @@ static int cas_change_mtu(struct net_device *dev, int new_mtu) schedule_work(&cp->reset_task); #endif - flush_work_sync(&cp->reset_task); + flush_work(&cp->reset_task); return 0; } diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c index c2a0fe393267..710f35395195 100644 --- a/drivers/net/ethernet/sun/niu.c +++ b/drivers/net/ethernet/sun/niu.c @@ -9932,7 +9932,7 @@ static int niu_suspend(struct pci_dev *pdev, pm_message_t state) if (!netif_running(dev)) return 0; - flush_work_sync(&np->reset_task); + flush_work(&np->reset_task); niu_netif_stop(np); del_timer_sync(&np->timer); diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/hostap/hostap_ap.c index e1f410277242..c6ea995750db 100644 --- a/drivers/net/wireless/hostap/hostap_ap.c +++ b/drivers/net/wireless/hostap/hostap_ap.c @@ -860,10 +860,10 @@ void hostap_free_data(struct ap_data *ap) return; } - flush_work_sync(&ap->add_sta_proc_queue); + flush_work(&ap->add_sta_proc_queue); #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT - flush_work_sync(&ap->wds_oper_queue); + flush_work(&ap->wds_oper_queue); if (ap->crypt) ap->crypt->deinit(ap->crypt_priv); ap->crypt = ap->crypt_priv = NULL; diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c index 50f87b60b0bd..8e7000fd4414 100644 --- a/drivers/net/wireless/hostap/hostap_hw.c +++ b/drivers/net/wireless/hostap/hostap_hw.c @@ -3311,13 +3311,13 @@ static void prism2_free_local_data(struct net_device *dev) unregister_netdev(local->dev); - flush_work_sync(&local->reset_queue); - flush_work_sync(&local->set_multicast_list_queue); - flush_work_sync(&local->set_tim_queue); + flush_work(&local->reset_queue); + flush_work(&local->set_multicast_list_queue); + flush_work(&local->set_tim_queue); #ifndef PRISM2_NO_STATION_MODES - flush_work_sync(&local->info_queue); + flush_work(&local->info_queue); #endif - flush_work_sync(&local->comms_qual_update); + flush_work(&local->comms_qual_update); lib80211_crypt_info_free(&local->crypt_info); diff --git a/drivers/power/collie_battery.c b/drivers/power/collie_battery.c index 74c6b23aeabf..b19bfe400f8c 100644 --- a/drivers/power/collie_battery.c +++ b/drivers/power/collie_battery.c @@ -290,7 +290,7 @@ static struct gpio collie_batt_gpios[] = { static int collie_bat_suspend(struct ucb1x00_dev *dev, pm_message_t state) { /* flush all pending status updates */ - flush_work_sync(&bat_work); + flush_work(&bat_work); return 0; } diff --git a/drivers/power/tosa_battery.c b/drivers/power/tosa_battery.c index 28bbe7e094e3..51199b5ce221 100644 --- a/drivers/power/tosa_battery.c +++ b/drivers/power/tosa_battery.c @@ -327,7 +327,7 @@ static struct gpio tosa_bat_gpios[] = { static int tosa_bat_suspend(struct platform_device *dev, pm_message_t state) { /* flush all pending status updates */ - flush_work_sync(&bat_work); + flush_work(&bat_work); return 0; } diff --git a/drivers/power/wm97xx_battery.c b/drivers/power/wm97xx_battery.c index d2d4c08c681c..1245fe1f48c3 100644 --- a/drivers/power/wm97xx_battery.c +++ b/drivers/power/wm97xx_battery.c @@ -146,7 +146,7 @@ static irqreturn_t wm97xx_chrg_irq(int irq, void *data) #ifdef CONFIG_PM static int wm97xx_bat_suspend(struct device *dev) { - flush_work_sync(&bat_work); + flush_work(&bat_work); return 0; } diff --git a/drivers/power/z2_battery.c b/drivers/power/z2_battery.c index 8c9a607ea77a..5757d0d6782f 100644 --- a/drivers/power/z2_battery.c +++ b/drivers/power/z2_battery.c @@ -276,7 +276,7 @@ static int z2_batt_suspend(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct z2_charger *charger = i2c_get_clientdata(client); - flush_work_sync(&charger->bat_work); + flush_work(&charger->bat_work); return 0; } diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index f092588a078c..1d2b70017a8d 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -3335,7 +3335,7 @@ void regulator_unregister(struct regulator_dev *rdev) regulator_put(rdev->supply); mutex_lock(®ulator_list_mutex); debugfs_remove_recursive(rdev->debugfs); - flush_work_sync(&rdev->disable_work.work); + flush_work(&rdev->disable_work.work); WARN_ON(rdev->open_count); unset_regulator_supplies(rdev); list_del(&rdev->list); diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index def24a1079ad..33c52bc2c7b4 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -999,7 +999,7 @@ static void arcmsr_remove(struct pci_dev *pdev) int poll_count = 0; arcmsr_free_sysfs_attr(acb); scsi_remove_host(host); - flush_work_sync(&acb->arcmsr_do_message_isr_bh); + flush_work(&acb->arcmsr_do_message_isr_bh); del_timer_sync(&acb->eternal_timer); arcmsr_disable_outbound_ints(acb); arcmsr_stop_adapter_bgrb(acb); @@ -1045,7 +1045,7 @@ static void arcmsr_shutdown(struct pci_dev *pdev) (struct AdapterControlBlock *)host->hostdata; del_timer_sync(&acb->eternal_timer); arcmsr_disable_outbound_ints(acb); - flush_work_sync(&acb->arcmsr_do_message_isr_bh); + flush_work(&acb->arcmsr_do_message_isr_bh); arcmsr_stop_adapter_bgrb(acb); arcmsr_flush_adapter_cache(acb); } diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 467dc38246f9..2ffeb9afd1b7 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -9020,7 +9020,7 @@ static void __ipr_remove(struct pci_dev *pdev) spin_unlock_irqrestore(ioa_cfg->host->host_lock, host_lock_flags); wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload); - flush_work_sync(&ioa_cfg->work_q); + flush_work(&ioa_cfg->work_q); spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags); spin_lock(&ipr_driver_lock); diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index ea8a0b47d66d..af763eab2039 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -5459,7 +5459,7 @@ static void __devexit pmcraid_remove(struct pci_dev *pdev) pmcraid_shutdown(pdev); pmcraid_disable_interrupts(pinstance, ~0); - flush_work_sync(&pinstance->worker_q); + flush_work(&pinstance->worker_q); pmcraid_kill_tasklets(pinstance); pmcraid_unregister_interrupt_handler(pinstance); diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 5b30132960c7..bddc97c5c8e9 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -969,7 +969,7 @@ void qlt_stop_phase1(struct qla_tgt *tgt) spin_unlock_irqrestore(&ha->hardware_lock, flags); mutex_unlock(&ha->tgt.tgt_mutex); - flush_delayed_work_sync(&tgt->sess_del_work); + flush_delayed_work(&tgt->sess_del_work); ql_dbg(ql_dbg_tgt_mgt, vha, 0xf009, "Waiting for sess works (tgt %p)", tgt); diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c index 6f5bc49c441f..1f8e8b37ed23 100644 --- a/drivers/tty/hvc/hvsi.c +++ b/drivers/tty/hvc/hvsi.c @@ -765,7 +765,7 @@ static void hvsi_flush_output(struct hvsi_struct *hp) /* 'writer' could still be pending if it didn't see n_outbuf = 0 yet */ cancel_delayed_work_sync(&hp->writer); - flush_work_sync(&hp->handshaker); + flush_work(&hp->handshaker); /* * it's also possible that our timeout expired and hvsi_write_worker diff --git a/drivers/tty/ipwireless/hardware.c b/drivers/tty/ipwireless/hardware.c index 0aeb5a38d296..b4ba0670dc54 100644 --- a/drivers/tty/ipwireless/hardware.c +++ b/drivers/tty/ipwireless/hardware.c @@ -1729,7 +1729,7 @@ void ipwireless_hardware_free(struct ipw_hardware *hw) ipwireless_stop_interrupts(hw); - flush_work_sync(&hw->work_rx); + flush_work(&hw->work_rx); for (i = 0; i < NL_NUM_OF_ADDRESSES; i++) if (hw->packet_assembler[i] != NULL) diff --git a/drivers/tty/ipwireless/network.c b/drivers/tty/ipwireless/network.c index 57c8b481113f..90ea902ff191 100644 --- a/drivers/tty/ipwireless/network.c +++ b/drivers/tty/ipwireless/network.c @@ -430,8 +430,8 @@ void ipwireless_network_free(struct ipw_network *network) network->shutting_down = 1; ipwireless_ppp_close(network); - flush_work_sync(&network->work_go_online); - flush_work_sync(&network->work_go_offline); + flush_work(&network->work_go_online); + flush_work(&network->work_go_offline); ipwireless_stop_interrupts(network->hardware); ipwireless_associate_network(network->hardware, NULL); diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c index 2b42a01a81c6..ebabf929f460 100644 --- a/drivers/tty/serial/kgdboc.c +++ b/drivers/tty/serial/kgdboc.c @@ -122,7 +122,7 @@ static void kgdboc_unregister_kbd(void) i--; } } - flush_work_sync(&kgdboc_restore_input_work); + flush_work(&kgdboc_restore_input_work); } #else /* ! CONFIG_KDB_KEYBOARD */ #define kgdboc_register_kbd(x) 0 diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index d3cda0cb2df0..0952d71bdf28 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1205,7 +1205,7 @@ static int serial_omap_suspend(struct device *dev) if (up) { uart_suspend_port(&serial_omap_reg, &up->port); - flush_work_sync(&up->qos_work); + flush_work(&up->qos_work); } return 0; diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 6f99c9959f0c..ac5be812dbe3 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -523,9 +523,9 @@ static int tty_ldisc_halt(struct tty_struct *tty) */ static void tty_ldisc_flush_works(struct tty_struct *tty) { - flush_work_sync(&tty->hangup_work); - flush_work_sync(&tty->SAK_work); - flush_work_sync(&tty->buf.work); + flush_work(&tty->hangup_work); + flush_work(&tty->SAK_work); + flush_work(&tty->buf.work); } /** diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c index 975e9c6691d6..807627b36cc8 100644 --- a/drivers/usb/atm/speedtch.c +++ b/drivers/usb/atm/speedtch.c @@ -718,7 +718,7 @@ static void speedtch_atm_stop(struct usbatm_data *usbatm, struct atm_dev *atm_de del_timer_sync(&instance->resubmit_timer); usb_free_urb(int_urb); - flush_work_sync(&instance->status_check_work); + flush_work(&instance->status_check_work); } static int speedtch_pre_reset(struct usb_interface *intf) diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index d7e422dc0ef7..9d1a044504e8 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -2234,7 +2234,7 @@ static void uea_stop(struct uea_softc *sc) usb_free_urb(sc->urb_int); /* flush the work item, when no one can schedule it */ - flush_work_sync(&sc->task); + flush_work(&sc->task); release_firmware(sc->dsp_firm); uea_leaves(INS_TO_USBDEV(sc)); diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index 90e82e288eb9..f1e5fbf5c2c7 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -834,7 +834,7 @@ void gether_cleanup(void) return; unregister_netdev(the_dev->net); - flush_work_sync(&the_dev->work); + flush_work(&the_dev->work); free_netdev(the_dev->net); the_dev = NULL; diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 2b1e8d84c873..2364098ea83c 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -893,7 +893,7 @@ static void ohci_stop (struct usb_hcd *hcd) ohci_dump (ohci, 1); if (quirk_nec(ohci)) - flush_work_sync(&ohci->nec_work); + flush_work(&ohci->nec_work); ohci_usb_reset (ohci); ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index 7a88667742b6..720df35561c9 100644 --- a/drivers/usb/otg/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c @@ -1230,7 +1230,7 @@ static int __exit isp1301_remove(struct i2c_client *i2c) isp->timer.data = 0; set_bit(WORK_STOP, &isp->todo); del_timer_sync(&isp->timer); - flush_work_sync(&isp->work); + flush_work(&isp->work); put_device(&i2c->dev); the_transceiver = NULL; diff --git a/fs/affs/super.c b/fs/affs/super.c index c70f1e5fc024..022cecb0757d 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -551,7 +551,7 @@ affs_remount(struct super_block *sb, int *flags, char *data) return -EINVAL; } - flush_delayed_work_sync(&sbi->sb_work); + flush_delayed_work(&sbi->sb_work); replace_mount_options(sb, new_opts); sbi->s_flags = mount_flags; diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c index 4a38db739ca0..0fb6539b0c8c 100644 --- a/fs/gfs2/lock_dlm.c +++ b/fs/gfs2/lock_dlm.c @@ -1289,7 +1289,7 @@ static void gdlm_unmount(struct gfs2_sbd *sdp) spin_lock(&ls->ls_recover_spin); set_bit(DFL_UNMOUNT, &ls->ls_recover_flags); spin_unlock(&ls->ls_recover_spin); - flush_delayed_work_sync(&sdp->sd_control_work); + flush_delayed_work(&sdp->sd_control_work); /* mounted_lock and control_lock will be purged in dlm recovery */ release: diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index fc3168f47a14..867700aba536 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -1572,7 +1572,7 @@ out: clear_inode(inode); gfs2_dir_hash_inval(ip); ip->i_gl->gl_object = NULL; - flush_delayed_work_sync(&ip->i_gl->gl_work); + flush_delayed_work(&ip->i_gl->gl_work); gfs2_glock_add_to_lru(ip->i_gl); gfs2_glock_put(ip->i_gl); ip->i_gl = NULL; diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index ee1bc55677f1..553909395270 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -644,7 +644,7 @@ static int hfs_file_fsync(struct file *filp, loff_t start, loff_t end, /* sync the superblock to buffers */ sb = inode->i_sb; - flush_delayed_work_sync(&HFS_SB(sb)->mdb_work); + flush_delayed_work(&HFS_SB(sb)->mdb_work); /* .. finally sync the buffers to disk */ err = sync_blockdev(sb->s_bdev); if (!ret) diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 333df07ae3bd..eaa74323663a 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -314,11 +314,11 @@ static void ncp_stop_tasks(struct ncp_server *server) { release_sock(sk); del_timer_sync(&server->timeout_tm); - flush_work_sync(&server->rcv.tq); + flush_work(&server->rcv.tq); if (sk->sk_socket->type == SOCK_STREAM) - flush_work_sync(&server->tx.tq); + flush_work(&server->tx.tq); else - flush_work_sync(&server->timeout_tq); + flush_work(&server->timeout_tq); } static int ncp_show_options(struct seq_file *seq, struct dentry *root) diff --git a/fs/ocfs2/cluster/quorum.c b/fs/ocfs2/cluster/quorum.c index 8f9cea1597af..c19897d0fe14 100644 --- a/fs/ocfs2/cluster/quorum.c +++ b/fs/ocfs2/cluster/quorum.c @@ -327,5 +327,5 @@ void o2quo_exit(void) { struct o2quo_state *qs = &o2quo_state; - flush_work_sync(&qs->qs_work); + flush_work(&qs->qs_work); } diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index bdaf4cb9f4a2..e8e6be439bcd 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -953,7 +953,7 @@ xfs_fs_sync_fs( * We schedule xfssyncd now (now that the disk is * active) instead of later (when it might not be). */ - flush_delayed_work_sync(&mp->m_sync_work); + flush_delayed_work(&mp->m_sync_work); } return 0; diff --git a/fs/xfs/xfs_sync.c b/fs/xfs/xfs_sync.c index 96548176db80..9500caf15acf 100644 --- a/fs/xfs/xfs_sync.c +++ b/fs/xfs/xfs_sync.c @@ -475,7 +475,7 @@ xfs_flush_inodes( struct xfs_mount *mp = ip->i_mount; queue_work(xfs_syncd_wq, &mp->m_flush_work); - flush_work_sync(&mp->m_flush_work); + flush_work(&mp->m_flush_work); } STATIC void diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 855fcdaa2d72..a351be7c3e91 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -460,13 +460,13 @@ static inline bool __cancel_delayed_work(struct delayed_work *work) } /* used to be different but now identical to flush_work(), deprecated */ -static inline bool flush_work_sync(struct work_struct *work) +static inline bool __deprecated flush_work_sync(struct work_struct *work) { return flush_work(work); } /* used to be different but now identical to flush_delayed_work(), deprecated */ -static inline bool flush_delayed_work_sync(struct delayed_work *dwork) +static inline bool __deprecated flush_delayed_work_sync(struct delayed_work *dwork) { return flush_delayed_work(dwork); } diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c index 6449bae15702..505f0ce3f10b 100644 --- a/net/9p/trans_fd.c +++ b/net/9p/trans_fd.c @@ -1083,7 +1083,7 @@ int p9_trans_fd_init(void) void p9_trans_fd_exit(void) { - flush_work_sync(&p9_poll_work); + flush_work(&p9_poll_work); v9fs_unregister_trans(&p9_tcp_trans); v9fs_unregister_trans(&p9_unix_trans); v9fs_unregister_trans(&p9_fd_trans); diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 88e7c2f3fa0d..45295ca09571 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -370,7 +370,7 @@ static int dsa_remove(struct platform_device *pdev) if (dst->link_poll_needed) del_timer_sync(&dst->link_poll_timer); - flush_work_sync(&dst->link_poll_work); + flush_work(&dst->link_poll_work); for (i = 0; i < dst->pd->nr_chips; i++) { struct dsa_switch *ds = dst->ds[i]; diff --git a/sound/i2c/other/ak4113.c b/sound/i2c/other/ak4113.c index dde5c9c92132..ef68d710d08c 100644 --- a/sound/i2c/other/ak4113.c +++ b/sound/i2c/other/ak4113.c @@ -141,7 +141,7 @@ void snd_ak4113_reinit(struct ak4113 *chip) { chip->init = 1; mb(); - flush_delayed_work_sync(&chip->work); + flush_delayed_work(&chip->work); ak4113_init_regs(chip); /* bring up statistics / event queing */ chip->init = 0; diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c index fdf3c1b65e38..816e7d225fb0 100644 --- a/sound/i2c/other/ak4114.c +++ b/sound/i2c/other/ak4114.c @@ -154,7 +154,7 @@ void snd_ak4114_reinit(struct ak4114 *chip) { chip->init = 1; mb(); - flush_delayed_work_sync(&chip->work); + flush_delayed_work(&chip->work); ak4114_init_regs(chip); /* bring up statistics / event queing */ chip->init = 0; diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index ab8738e21ad1..e9fa2d07951d 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -573,8 +573,8 @@ static void oxygen_card_free(struct snd_card *card) oxygen_shutdown(chip); if (chip->irq >= 0) free_irq(chip->irq, chip); - flush_work_sync(&chip->spdif_input_bits_work); - flush_work_sync(&chip->gpio_work); + flush_work(&chip->spdif_input_bits_work); + flush_work(&chip->gpio_work); chip->model.cleanup(chip); kfree(chip->model_data); mutex_destroy(&chip->mutex); @@ -751,8 +751,8 @@ static int oxygen_pci_suspend(struct device *dev) spin_unlock_irq(&chip->reg_lock); synchronize_irq(chip->irq); - flush_work_sync(&chip->spdif_input_bits_work); - flush_work_sync(&chip->gpio_work); + flush_work(&chip->spdif_input_bits_work); + flush_work(&chip->gpio_work); chip->interrupt_mask = saved_interrupt_mask; pci_disable_device(pci); diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index d26c8ae4e6d9..a4cae060bf26 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -1601,7 +1601,7 @@ static int wm8350_codec_remove(struct snd_soc_codec *codec) /* if there was any work waiting then we run it now and * wait for its completion */ - flush_delayed_work_sync(&codec->dapm.delayed_work); + flush_delayed_work(&codec->dapm.delayed_work); wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF); diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index 13bff87ddcf5..2e4a775ae560 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -1509,7 +1509,7 @@ static int wm8753_probe(struct snd_soc_codec *codec) /* power down chip */ static int wm8753_remove(struct snd_soc_codec *codec) { - flush_delayed_work_sync(&codec->dapm.delayed_work); + flush_delayed_work(&codec->dapm.delayed_work); wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index f219b2f7ee68..f6eec782c61e 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -591,7 +591,7 @@ int snd_soc_suspend(struct device *dev) /* close any waiting streams and save state */ for (i = 0; i < card->num_rtd; i++) { - flush_delayed_work_sync(&card->rtd[i].delayed_work); + flush_delayed_work(&card->rtd[i].delayed_work); card->rtd[i].codec->dapm.suspend_bias_level = card->rtd[i].codec->dapm.bias_level; } @@ -1846,7 +1846,7 @@ static int soc_cleanup_card_resources(struct snd_soc_card *card) /* make sure any delayed work runs */ for (i = 0; i < card->num_rtd; i++) { struct snd_soc_pcm_runtime *rtd = &card->rtd[i]; - flush_delayed_work_sync(&rtd->delayed_work); + flush_delayed_work(&rtd->delayed_work); } /* remove auxiliary devices */ @@ -1890,7 +1890,7 @@ int snd_soc_poweroff(struct device *dev) * now, we're shutting down so no imminent restart. */ for (i = 0; i < card->num_rtd; i++) { struct snd_soc_pcm_runtime *rtd = &card->rtd[i]; - flush_delayed_work_sync(&rtd->delayed_work); + flush_delayed_work(&rtd->delayed_work); } snd_soc_dapm_shutdown(card); diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c index 7d7e2aaffece..67a35e90384c 100644 --- a/virt/kvm/eventfd.c +++ b/virt/kvm/eventfd.c @@ -90,7 +90,7 @@ irqfd_shutdown(struct work_struct *work) * We know no new events will be scheduled at this point, so block * until all previously outstanding events have completed */ - flush_work_sync(&irqfd->inject); + flush_work(&irqfd->inject); /* * It is now safe to release the object's resources -- cgit v1.2.3 From 3b07e9ca26866697616097044f25fbe53dbab693 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 20 Aug 2012 14:51:24 -0700 Subject: workqueue: deprecate system_nrt[_freezable]_wq system_nrt[_freezable]_wq are now spurious. Mark them deprecated and convert all users to system[_freezable]_wq. If you're cc'd and wondering what's going on: Now all workqueues are non-reentrant, so there's no reason to use system_nrt[_freezable]_wq. Please use system[_freezable]_wq instead. This patch doesn't make any functional difference. Signed-off-by: Tejun Heo Acked-By: Lai Jiangshan Cc: Jens Axboe Cc: David Airlie Cc: Jiri Kosina Cc: "David S. Miller" Cc: Rusty Russell Cc: "Paul E. McKenney" Cc: David Howells --- block/blk-throttle.c | 7 +++---- block/genhd.c | 10 +++++----- drivers/gpu/drm/drm_crtc_helper.c | 6 +++--- drivers/hid/hid-wiimote-ext.c | 2 +- drivers/mmc/core/host.c | 4 ++-- drivers/net/virtio_net.c | 12 ++++++------ include/linux/workqueue.h | 4 ++-- kernel/srcu.c | 4 ++-- security/keys/gc.c | 8 ++++---- security/keys/key.c | 2 +- 10 files changed, 29 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/block/blk-throttle.c b/block/blk-throttle.c index e287c19908c8..5a58e779912b 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -180,7 +180,7 @@ static inline unsigned int total_nr_queued(struct throtl_data *td) /* * Worker for allocating per cpu stat for tgs. This is scheduled on the - * system_nrt_wq once there are some groups on the alloc_list waiting for + * system_wq once there are some groups on the alloc_list waiting for * allocation. */ static void tg_stats_alloc_fn(struct work_struct *work) @@ -194,8 +194,7 @@ alloc_stats: stats_cpu = alloc_percpu(struct tg_stats_cpu); if (!stats_cpu) { /* allocation failed, try again after some time */ - queue_delayed_work(system_nrt_wq, dwork, - msecs_to_jiffies(10)); + schedule_delayed_work(dwork, msecs_to_jiffies(10)); return; } } @@ -238,7 +237,7 @@ static void throtl_pd_init(struct blkcg_gq *blkg) */ spin_lock_irqsave(&tg_stats_alloc_lock, flags); list_add(&tg->stats_alloc_node, &tg_stats_alloc_list); - queue_delayed_work(system_nrt_wq, &tg_stats_alloc_work, 0); + schedule_delayed_work(&tg_stats_alloc_work, 0); spin_unlock_irqrestore(&tg_stats_alloc_lock, flags); } diff --git a/block/genhd.c b/block/genhd.c index 5d8b44a6442b..a2f3d6a5f55c 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -1490,9 +1490,9 @@ static void __disk_unblock_events(struct gendisk *disk, bool check_now) intv = disk_events_poll_jiffies(disk); set_timer_slack(&ev->dwork.timer, intv / 4); if (check_now) - queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, 0); + queue_delayed_work(system_freezable_wq, &ev->dwork, 0); else if (intv) - queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, intv); + queue_delayed_work(system_freezable_wq, &ev->dwork, intv); out_unlock: spin_unlock_irqrestore(&ev->lock, flags); } @@ -1535,7 +1535,7 @@ void disk_flush_events(struct gendisk *disk, unsigned int mask) spin_lock_irq(&ev->lock); ev->clearing |= mask; if (!ev->block) - mod_delayed_work(system_nrt_freezable_wq, &ev->dwork, 0); + mod_delayed_work(system_freezable_wq, &ev->dwork, 0); spin_unlock_irq(&ev->lock); } @@ -1571,7 +1571,7 @@ unsigned int disk_clear_events(struct gendisk *disk, unsigned int mask) /* uncondtionally schedule event check and wait for it to finish */ disk_block_events(disk); - queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, 0); + queue_delayed_work(system_freezable_wq, &ev->dwork, 0); flush_delayed_work(&ev->dwork); __disk_unblock_events(disk, false); @@ -1608,7 +1608,7 @@ static void disk_events_workfn(struct work_struct *work) intv = disk_events_poll_jiffies(disk); if (!ev->block && intv) - queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, intv); + queue_delayed_work(system_freezable_wq, &ev->dwork, intv); spin_unlock_irq(&ev->lock); diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 3252e7067d8b..8fa9d52820d9 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -968,7 +968,7 @@ static void output_poll_execute(struct work_struct *work) } if (repoll) - queue_delayed_work(system_nrt_wq, delayed_work, DRM_OUTPUT_POLL_PERIOD); + schedule_delayed_work(delayed_work, DRM_OUTPUT_POLL_PERIOD); } void drm_kms_helper_poll_disable(struct drm_device *dev) @@ -993,7 +993,7 @@ void drm_kms_helper_poll_enable(struct drm_device *dev) } if (poll) - queue_delayed_work(system_nrt_wq, &dev->mode_config.output_poll_work, DRM_OUTPUT_POLL_PERIOD); + schedule_delayed_work(&dev->mode_config.output_poll_work, DRM_OUTPUT_POLL_PERIOD); } EXPORT_SYMBOL(drm_kms_helper_poll_enable); @@ -1020,6 +1020,6 @@ void drm_helper_hpd_irq_event(struct drm_device *dev) /* kill timer and schedule immediate execution, this doesn't block */ cancel_delayed_work(&dev->mode_config.output_poll_work); if (drm_kms_helper_poll) - queue_delayed_work(system_nrt_wq, &dev->mode_config.output_poll_work, 0); + schedule_delayed_work(&dev->mode_config.output_poll_work, 0); } EXPORT_SYMBOL(drm_helper_hpd_irq_event); diff --git a/drivers/hid/hid-wiimote-ext.c b/drivers/hid/hid-wiimote-ext.c index 0a1805c9b0e5..d37cd092ffc7 100644 --- a/drivers/hid/hid-wiimote-ext.c +++ b/drivers/hid/hid-wiimote-ext.c @@ -204,7 +204,7 @@ static void wiiext_worker(struct work_struct *work) /* schedule work only once, otherwise mark for reschedule */ static void wiiext_schedule(struct wiimote_ext *ext) { - queue_work(system_nrt_wq, &ext->worker); + schedule_work(&ext->worker); } /* diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 597f189b4427..ee2e16b17017 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -204,8 +204,8 @@ void mmc_host_clk_release(struct mmc_host *host) host->clk_requests--; if (mmc_host_may_gate_card(host->card) && !host->clk_requests) - queue_delayed_work(system_nrt_wq, &host->clk_gate_work, - msecs_to_jiffies(host->clkgate_delay)); + schedule_delayed_work(&host->clk_gate_work, + msecs_to_jiffies(host->clkgate_delay)); spin_unlock_irqrestore(&host->clk_lock, flags); } diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 83d2b0c34c5e..9650c413e11f 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -521,7 +521,7 @@ static void refill_work(struct work_struct *work) /* In theory, this can happen: if we don't get any buffers in * we will *never* try to fill again. */ if (still_empty) - queue_delayed_work(system_nrt_wq, &vi->refill, HZ/2); + schedule_delayed_work(&vi->refill, HZ/2); } static int virtnet_poll(struct napi_struct *napi, int budget) @@ -540,7 +540,7 @@ again: if (vi->num < vi->max / 2) { if (!try_fill_recv(vi, GFP_ATOMIC)) - queue_delayed_work(system_nrt_wq, &vi->refill, 0); + schedule_delayed_work(&vi->refill, 0); } /* Out of packets? */ @@ -745,7 +745,7 @@ static int virtnet_open(struct net_device *dev) /* Make sure we have some buffers: if oom use wq. */ if (!try_fill_recv(vi, GFP_KERNEL)) - queue_delayed_work(system_nrt_wq, &vi->refill, 0); + schedule_delayed_work(&vi->refill, 0); virtnet_napi_enable(vi); return 0; @@ -1020,7 +1020,7 @@ static void virtnet_config_changed(struct virtio_device *vdev) { struct virtnet_info *vi = vdev->priv; - queue_work(system_nrt_wq, &vi->config_work); + schedule_work(&vi->config_work); } static int init_vqs(struct virtnet_info *vi) @@ -1152,7 +1152,7 @@ static int virtnet_probe(struct virtio_device *vdev) otherwise get link status from config. */ if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) { netif_carrier_off(dev); - queue_work(system_nrt_wq, &vi->config_work); + schedule_work(&vi->config_work); } else { vi->status = VIRTIO_NET_S_LINK_UP; netif_carrier_on(dev); @@ -1264,7 +1264,7 @@ static int virtnet_restore(struct virtio_device *vdev) netif_device_attach(vi->dev); if (!try_fill_recv(vi, GFP_KERNEL)) - queue_delayed_work(system_nrt_wq, &vi->refill, 0); + schedule_delayed_work(&vi->refill, 0); mutex_lock(&vi->config_lock); vi->config_enable = true; diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index a351be7c3e91..1ce3fb08308d 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -310,12 +310,12 @@ extern struct workqueue_struct *system_long_wq; extern struct workqueue_struct *system_unbound_wq; extern struct workqueue_struct *system_freezable_wq; -static inline struct workqueue_struct *__system_nrt_wq(void) +static inline struct workqueue_struct * __deprecated __system_nrt_wq(void) { return system_wq; } -static inline struct workqueue_struct *__system_nrt_freezable_wq(void) +static inline struct workqueue_struct * __deprecated __system_nrt_freezable_wq(void) { return system_freezable_wq; } diff --git a/kernel/srcu.c b/kernel/srcu.c index 2095be3318d5..97c465ebd844 100644 --- a/kernel/srcu.c +++ b/kernel/srcu.c @@ -379,7 +379,7 @@ void call_srcu(struct srcu_struct *sp, struct rcu_head *head, rcu_batch_queue(&sp->batch_queue, head); if (!sp->running) { sp->running = true; - queue_delayed_work(system_nrt_wq, &sp->work, 0); + schedule_delayed_work(&sp->work, 0); } spin_unlock_irqrestore(&sp->queue_lock, flags); } @@ -631,7 +631,7 @@ static void srcu_reschedule(struct srcu_struct *sp) } if (pending) - queue_delayed_work(system_nrt_wq, &sp->work, SRCU_INTERVAL); + schedule_delayed_work(&sp->work, SRCU_INTERVAL); } /* diff --git a/security/keys/gc.c b/security/keys/gc.c index 61ab7c82ebb1..d67c97bb1025 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -62,7 +62,7 @@ void key_schedule_gc(time_t gc_at) if (gc_at <= now || test_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags)) { kdebug("IMMEDIATE"); - queue_work(system_nrt_wq, &key_gc_work); + schedule_work(&key_gc_work); } else if (gc_at < key_gc_next_run) { kdebug("DEFERRED"); key_gc_next_run = gc_at; @@ -77,7 +77,7 @@ void key_schedule_gc(time_t gc_at) void key_schedule_gc_links(void) { set_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags); - queue_work(system_nrt_wq, &key_gc_work); + schedule_work(&key_gc_work); } /* @@ -120,7 +120,7 @@ void key_gc_keytype(struct key_type *ktype) set_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags); kdebug("schedule"); - queue_work(system_nrt_wq, &key_gc_work); + schedule_work(&key_gc_work); kdebug("sleep"); wait_on_bit(&key_gc_flags, KEY_GC_REAPING_KEYTYPE, key_gc_wait_bit, @@ -369,7 +369,7 @@ maybe_resched: } if (gc_state & KEY_GC_REAP_AGAIN) - queue_work(system_nrt_wq, &key_gc_work); + schedule_work(&key_gc_work); kleave(" [end %x]", gc_state); return; diff --git a/security/keys/key.c b/security/keys/key.c index 50d96d4e06f2..3cbe3529c418 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -598,7 +598,7 @@ void key_put(struct key *key) key_check(key); if (atomic_dec_and_test(&key->usage)) - queue_work(system_nrt_wq, &key_gc_work); + schedule_work(&key_gc_work); } } EXPORT_SYMBOL(key_put); -- cgit v1.2.3 From 6f6bbc186dc8e4e0c628db7decbd1a5e02cb5fd8 Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Thu, 26 Jul 2012 02:30:53 +0000 Subject: ethtool.h: MDI setting support This change modifies the core ethtool struct to allow a driver to support setting of MDI/MDI-X state for twisted pair wiring. This change uses a previously reserved u8 and should not change any binary compatibility of ethtool. Also as per Ben Hutchings' suggestion, the capabilities are stored in a separate byte so the driver can report if it supports changing settings. see thread: http://kerneltrap.org/mailarchive/linux-netdev/2010/11/17/6289820/thread see ethtool patches titled: ethtool: allow setting MDI-X state Signed-off-by: Jesse Brandeburg CC: Ben Hutchings Tested-by: Aaron Brown aaron.f.brown@intel.com Signed-off-by: Jeff Kirsher --- include/linux/ethtool.h | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 21eff418091b..fcb4f8e60c1c 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -45,8 +45,10 @@ struct ethtool_cmd { * bits) in Mbps. Please use * ethtool_cmd_speed()/_set() to * access it */ - __u8 eth_tp_mdix; - __u8 reserved2; + __u8 eth_tp_mdix; /* twisted pair MDI-X status */ + __u8 eth_tp_mdix_ctrl; /* twisted pair MDI-X control, when set, + * link should be renegotiated if necessary + */ __u32 lp_advertising; /* Features the link partner advertises */ __u32 reserved[2]; }; @@ -1229,10 +1231,13 @@ struct ethtool_ops { #define AUTONEG_DISABLE 0x00 #define AUTONEG_ENABLE 0x01 -/* Mode MDI or MDI-X */ -#define ETH_TP_MDI_INVALID 0x00 -#define ETH_TP_MDI 0x01 -#define ETH_TP_MDI_X 0x02 +/* MDI or MDI-X status/control - if MDI/MDI_X/AUTO is set then + * the driver is required to renegotiate link + */ +#define ETH_TP_MDI_INVALID 0x00 /* status: unknown; control: unsupported */ +#define ETH_TP_MDI 0x01 /* status: MDI; control: force MDI */ +#define ETH_TP_MDI_X 0x02 /* status: MDI-X; control: force MDI-X */ +#define ETH_TP_MDI_AUTO 0x03 /* control: auto-select */ /* Wake-On-Lan options. */ #define WAKE_PHY (1 << 0) -- cgit v1.2.3 From e52b1db37b89b69ceb08b521a808bd2cf4724481 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 8 Aug 2012 11:10:25 -0700 Subject: timer: Generalize timer->base flags handling To prepare for addition of another flag, generalize timer->base flags handling. * Rename from TBASE_*_FLAG to TIMER_* and make them LU constants. * Define and use TIMER_FLAG_MASK for flags masking so that multiple flags can be handled correctly. * Don't dereference timer->base directly even if !tbase_get_deferrable(). All two such places are already passed in @base, so use it instead. * Make sure tvec_base's alignment is large enough for timer->base flags using BUILD_BUG_ON(). Signed-off-by: Tejun Heo Cc: torvalds@linux-foundation.org Cc: peterz@infradead.org Link: http://lkml.kernel.org/r/1344449428-24962-2-git-send-email-tj@kernel.org Signed-off-by: Thomas Gleixner --- include/linux/timer.h | 6 ++++-- kernel/timer.c | 21 +++++++++++++-------- 2 files changed, 17 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/timer.h b/include/linux/timer.h index 6abd9138beda..cbd32ec4dd15 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -58,7 +58,9 @@ extern struct tvec_base boot_tvec_bases; * the timer will be serviced when the CPU eventually wakes up with a * subsequent non-deferrable timer. */ -#define TBASE_DEFERRABLE_FLAG (0x1) +#define TIMER_DEFERRABLE 0x1LU + +#define TIMER_FLAG_MASK 0x1LU #define TIMER_INITIALIZER(_function, _expires, _data) { \ .entry = { .prev = TIMER_ENTRY_STATIC }, \ @@ -72,7 +74,7 @@ extern struct tvec_base boot_tvec_bases; } #define TBASE_MAKE_DEFERRED(ptr) ((struct tvec_base *) \ - ((unsigned char *)(ptr) + TBASE_DEFERRABLE_FLAG)) + ((unsigned char *)(ptr) + TIMER_DEFERRABLE)) #define TIMER_DEFERRED_INITIALIZER(_function, _expires, _data) {\ .entry = { .prev = TIMER_ENTRY_STATIC }, \ diff --git a/kernel/timer.c b/kernel/timer.c index a61c09374eba..cf7af56940b7 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -92,12 +92,12 @@ static DEFINE_PER_CPU(struct tvec_base *, tvec_bases) = &boot_tvec_bases; /* Functions below help us manage 'deferrable' flag */ static inline unsigned int tbase_get_deferrable(struct tvec_base *base) { - return ((unsigned int)(unsigned long)base & TBASE_DEFERRABLE_FLAG); + return ((unsigned int)(unsigned long)base & TIMER_DEFERRABLE); } static inline struct tvec_base *tbase_get_base(struct tvec_base *base) { - return ((struct tvec_base *)((unsigned long)base & ~TBASE_DEFERRABLE_FLAG)); + return ((struct tvec_base *)((unsigned long)base & ~TIMER_FLAG_MASK)); } static inline void timer_set_deferrable(struct timer_list *timer) @@ -108,8 +108,9 @@ static inline void timer_set_deferrable(struct timer_list *timer) static inline void timer_set_base(struct timer_list *timer, struct tvec_base *new_base) { - timer->base = (struct tvec_base *)((unsigned long)(new_base) | - tbase_get_deferrable(timer->base)); + unsigned long flags = (unsigned long)timer->base & TIMER_FLAG_MASK; + + timer->base = (struct tvec_base *)((unsigned long)(new_base) | flags); } static unsigned long round_jiffies_common(unsigned long j, int cpu, @@ -686,7 +687,7 @@ detach_expired_timer(struct timer_list *timer, struct tvec_base *base) { detach_timer(timer, true); if (!tbase_get_deferrable(timer->base)) - timer->base->active_timers--; + base->active_timers--; } static int detach_if_pending(struct timer_list *timer, struct tvec_base *base, @@ -697,7 +698,7 @@ static int detach_if_pending(struct timer_list *timer, struct tvec_base *base, detach_timer(timer, clear_pending); if (!tbase_get_deferrable(timer->base)) { - timer->base->active_timers--; + base->active_timers--; if (timer->expires == base->next_timer) base->next_timer = base->timer_jiffies; } @@ -1800,9 +1801,13 @@ static struct notifier_block __cpuinitdata timers_nb = { void __init init_timers(void) { - int err = timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE, - (void *)(long)smp_processor_id()); + int err; + + /* ensure there are enough low bits for flags in timer->base pointer */ + BUILD_BUG_ON(__alignof__(struct tvec_base) & TIMER_FLAG_MASK); + err = timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE, + (void *)(long)smp_processor_id()); init_timer_stats(); BUG_ON(err != NOTIFY_OK); -- cgit v1.2.3 From 5a9af38d05f6a1bd0d3f1f69a074cdbe9c87e977 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 8 Aug 2012 11:10:26 -0700 Subject: timer: Relocate declarations of init_timer_on_stack_key() init_timer_on_stack_key() is used by init macro definitions. Move init_timer_on_stack_key() and destroy_timer_on_stack() declarations above init macro defs. This will make the next init cleanup patch easier to read. Signed-off-by: Tejun Heo Cc: torvalds@linux-foundation.org Cc: peterz@infradead.org Link: http://lkml.kernel.org/r/1344449428-24962-3-git-send-email-tj@kernel.org Signed-off-by: Thomas Gleixner --- include/linux/timer.h | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/timer.h b/include/linux/timer.h index cbd32ec4dd15..1d364aed1802 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -97,6 +97,21 @@ void init_timer_deferrable_key(struct timer_list *timer, const char *name, struct lock_class_key *key); +#ifdef CONFIG_DEBUG_OBJECTS_TIMERS +extern void init_timer_on_stack_key(struct timer_list *timer, + const char *name, + struct lock_class_key *key); +extern void destroy_timer_on_stack(struct timer_list *timer); +#else +static inline void destroy_timer_on_stack(struct timer_list *timer) { } +static inline void init_timer_on_stack_key(struct timer_list *timer, + const char *name, + struct lock_class_key *key) +{ + init_timer_key(timer, name, key); +} +#endif + #ifdef CONFIG_LOCKDEP #define init_timer(timer) \ do { \ @@ -150,21 +165,6 @@ void init_timer_deferrable_key(struct timer_list *timer, setup_deferrable_timer_on_stack_key((timer), NULL, NULL, (fn), (data)) #endif -#ifdef CONFIG_DEBUG_OBJECTS_TIMERS -extern void init_timer_on_stack_key(struct timer_list *timer, - const char *name, - struct lock_class_key *key); -extern void destroy_timer_on_stack(struct timer_list *timer); -#else -static inline void destroy_timer_on_stack(struct timer_list *timer) { } -static inline void init_timer_on_stack_key(struct timer_list *timer, - const char *name, - struct lock_class_key *key) -{ - init_timer_key(timer, name, key); -} -#endif - static inline void setup_timer_key(struct timer_list * timer, const char *name, struct lock_class_key *key, -- cgit v1.2.3 From fc683995a6c4e604d62ab9a488ac2c1ba94fa868 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 8 Aug 2012 11:10:27 -0700 Subject: timer: Clean up timer initializers Over time, timer initializers became messy with unnecessarily duplicated code which are inconsistently spread across timer.h and timer.c. This patch cleans up timer initializers. * timer.c::__init_timer() is renamed to do_init_timer(). * __TIMER_INITIALIZER() added. It takes @flags and all initializers are wrappers around it. * init_timer[_on_stack]_key() now take @flags. * __init_timer[_on_stack]() added. They take @flags and all init macros are wrappers around them. * __setup_timer[_on_stack]() added. It uses __init_timer() and takes @flags. All setup macros are wrappers around the two. Note that this patch doesn't add missing init/setup combinations - e.g. init_timer_deferrable_on_stack(). Adding missing ones is trivial. Signed-off-by: Tejun Heo Cc: torvalds@linux-foundation.org Cc: peterz@infradead.org Link: http://lkml.kernel.org/r/1344449428-24962-4-git-send-email-tj@kernel.org Signed-off-by: Thomas Gleixner --- include/linux/timer.h | 123 ++++++++++++++++---------------------------------- kernel/timer.c | 56 ++++++----------------- 2 files changed, 53 insertions(+), 126 deletions(-) (limited to 'include') diff --git a/include/linux/timer.h b/include/linux/timer.h index 1d364aed1802..3f95c1fa615e 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -62,136 +62,91 @@ extern struct tvec_base boot_tvec_bases; #define TIMER_FLAG_MASK 0x1LU -#define TIMER_INITIALIZER(_function, _expires, _data) { \ +#define __TIMER_INITIALIZER(_function, _expires, _data, _flags) { \ .entry = { .prev = TIMER_ENTRY_STATIC }, \ .function = (_function), \ .expires = (_expires), \ .data = (_data), \ - .base = &boot_tvec_bases, \ + .base = (void *)((unsigned long)&boot_tvec_bases + (_flags)), \ .slack = -1, \ __TIMER_LOCKDEP_MAP_INITIALIZER( \ __FILE__ ":" __stringify(__LINE__)) \ } -#define TBASE_MAKE_DEFERRED(ptr) ((struct tvec_base *) \ - ((unsigned char *)(ptr) + TIMER_DEFERRABLE)) +#define TIMER_INITIALIZER(_function, _expires, _data) \ + __TIMER_INITIALIZER((_function), (_expires), (_data), 0) -#define TIMER_DEFERRED_INITIALIZER(_function, _expires, _data) {\ - .entry = { .prev = TIMER_ENTRY_STATIC }, \ - .function = (_function), \ - .expires = (_expires), \ - .data = (_data), \ - .base = TBASE_MAKE_DEFERRED(&boot_tvec_bases), \ - __TIMER_LOCKDEP_MAP_INITIALIZER( \ - __FILE__ ":" __stringify(__LINE__)) \ - } +#define TIMER_DEFERRED_INITIALIZER(_function, _expires, _data) \ + __TIMER_INITIALIZER((_function), (_expires), (_data), TIMER_DEFERRABLE) #define DEFINE_TIMER(_name, _function, _expires, _data) \ struct timer_list _name = \ TIMER_INITIALIZER(_function, _expires, _data) -void init_timer_key(struct timer_list *timer, - const char *name, - struct lock_class_key *key); -void init_timer_deferrable_key(struct timer_list *timer, - const char *name, - struct lock_class_key *key); +void init_timer_key(struct timer_list *timer, unsigned int flags, + const char *name, struct lock_class_key *key); #ifdef CONFIG_DEBUG_OBJECTS_TIMERS extern void init_timer_on_stack_key(struct timer_list *timer, - const char *name, + unsigned int flags, const char *name, struct lock_class_key *key); extern void destroy_timer_on_stack(struct timer_list *timer); #else static inline void destroy_timer_on_stack(struct timer_list *timer) { } static inline void init_timer_on_stack_key(struct timer_list *timer, - const char *name, + unsigned int flags, const char *name, struct lock_class_key *key) { - init_timer_key(timer, name, key); + init_timer_key(timer, flags, name, key); } #endif #ifdef CONFIG_LOCKDEP -#define init_timer(timer) \ +#define __init_timer(_timer, _flags) \ do { \ static struct lock_class_key __key; \ - init_timer_key((timer), #timer, &__key); \ + init_timer_key((_timer), (_flags), #_timer, &__key); \ } while (0) -#define init_timer_deferrable(timer) \ +#define __init_timer_on_stack(_timer, _flags) \ do { \ static struct lock_class_key __key; \ - init_timer_deferrable_key((timer), #timer, &__key); \ + init_timer_on_stack_key((_timer), (_flags), #_timer, &__key); \ } while (0) +#else +#define __init_timer(_timer, _flags) \ + init_timer_key((_timer), (_flags), NULL, NULL) +#define __init_timer_on_stack(_timer, _flags) \ + init_timer_on_stack_key((_timer), (_flags), NULL, NULL) +#endif +#define init_timer(timer) \ + __init_timer((timer), 0) +#define init_timer_deferrable(timer) \ + __init_timer((timer), TIMER_DEFERRABLE) #define init_timer_on_stack(timer) \ + __init_timer_on_stack((timer), 0) + +#define __setup_timer(_timer, _fn, _data, _flags) \ do { \ - static struct lock_class_key __key; \ - init_timer_on_stack_key((timer), #timer, &__key); \ + __init_timer((_timer), (_flags)); \ + (_timer)->function = (_fn); \ + (_timer)->data = (_data); \ } while (0) -#define setup_timer(timer, fn, data) \ +#define __setup_timer_on_stack(_timer, _fn, _data, _flags) \ do { \ - static struct lock_class_key __key; \ - setup_timer_key((timer), #timer, &__key, (fn), (data));\ + __init_timer_on_stack((_timer), (_flags)); \ + (_timer)->function = (_fn); \ + (_timer)->data = (_data); \ } while (0) +#define setup_timer(timer, fn, data) \ + __setup_timer((timer), (fn), (data), 0) #define setup_timer_on_stack(timer, fn, data) \ - do { \ - static struct lock_class_key __key; \ - setup_timer_on_stack_key((timer), #timer, &__key, \ - (fn), (data)); \ - } while (0) + __setup_timer_on_stack((timer), (fn), (data), 0) #define setup_deferrable_timer_on_stack(timer, fn, data) \ - do { \ - static struct lock_class_key __key; \ - setup_deferrable_timer_on_stack_key((timer), #timer, \ - &__key, (fn), \ - (data)); \ - } while (0) -#else -#define init_timer(timer)\ - init_timer_key((timer), NULL, NULL) -#define init_timer_deferrable(timer)\ - init_timer_deferrable_key((timer), NULL, NULL) -#define init_timer_on_stack(timer)\ - init_timer_on_stack_key((timer), NULL, NULL) -#define setup_timer(timer, fn, data)\ - setup_timer_key((timer), NULL, NULL, (fn), (data)) -#define setup_timer_on_stack(timer, fn, data)\ - setup_timer_on_stack_key((timer), NULL, NULL, (fn), (data)) -#define setup_deferrable_timer_on_stack(timer, fn, data)\ - setup_deferrable_timer_on_stack_key((timer), NULL, NULL, (fn), (data)) -#endif - -static inline void setup_timer_key(struct timer_list * timer, - const char *name, - struct lock_class_key *key, - void (*function)(unsigned long), - unsigned long data) -{ - timer->function = function; - timer->data = data; - init_timer_key(timer, name, key); -} - -static inline void setup_timer_on_stack_key(struct timer_list *timer, - const char *name, - struct lock_class_key *key, - void (*function)(unsigned long), - unsigned long data) -{ - timer->function = function; - timer->data = data; - init_timer_on_stack_key(timer, name, key); -} - -extern void setup_deferrable_timer_on_stack_key(struct timer_list *timer, - const char *name, - struct lock_class_key *key, - void (*function)(unsigned long), - unsigned long data); + __setup_timer_on_stack((timer), (fn), (data), TIMER_DEFERRABLE) /** * timer_pending - is a timer pending? diff --git a/kernel/timer.c b/kernel/timer.c index cf7af56940b7..8d185a1677cc 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -100,11 +100,6 @@ static inline struct tvec_base *tbase_get_base(struct tvec_base *base) return ((struct tvec_base *)((unsigned long)base & ~TIMER_FLAG_MASK)); } -static inline void timer_set_deferrable(struct timer_list *timer) -{ - timer->base = TBASE_MAKE_DEFERRED(timer->base); -} - static inline void timer_set_base(struct timer_list *timer, struct tvec_base *new_base) { @@ -564,16 +559,14 @@ static inline void debug_timer_assert_init(struct timer_list *timer) debug_object_assert_init(timer, &timer_debug_descr); } -static void __init_timer(struct timer_list *timer, - const char *name, - struct lock_class_key *key); +static void do_init_timer(struct timer_list *timer, unsigned int flags, + const char *name, struct lock_class_key *key); -void init_timer_on_stack_key(struct timer_list *timer, - const char *name, - struct lock_class_key *key) +void init_timer_on_stack_key(struct timer_list *timer, unsigned int flags, + const char *name, struct lock_class_key *key) { debug_object_init_on_stack(timer, &timer_debug_descr); - __init_timer(timer, name, key); + do_init_timer(timer, flags, name, key); } EXPORT_SYMBOL_GPL(init_timer_on_stack_key); @@ -614,12 +607,13 @@ static inline void debug_assert_init(struct timer_list *timer) debug_timer_assert_init(timer); } -static void __init_timer(struct timer_list *timer, - const char *name, - struct lock_class_key *key) +static void do_init_timer(struct timer_list *timer, unsigned int flags, + const char *name, struct lock_class_key *key) { + struct tvec_base *base = __raw_get_cpu_var(tvec_bases); + timer->entry.next = NULL; - timer->base = __raw_get_cpu_var(tvec_bases); + timer->base = (void *)((unsigned long)base | flags); timer->slack = -1; #ifdef CONFIG_TIMER_STATS timer->start_site = NULL; @@ -629,22 +623,10 @@ static void __init_timer(struct timer_list *timer, lockdep_init_map(&timer->lockdep_map, name, key, 0); } -void setup_deferrable_timer_on_stack_key(struct timer_list *timer, - const char *name, - struct lock_class_key *key, - void (*function)(unsigned long), - unsigned long data) -{ - timer->function = function; - timer->data = data; - init_timer_on_stack_key(timer, name, key); - timer_set_deferrable(timer); -} -EXPORT_SYMBOL_GPL(setup_deferrable_timer_on_stack_key); - /** * init_timer_key - initialize a timer * @timer: the timer to be initialized + * @flags: timer flags * @name: name of the timer * @key: lockdep class key of the fake lock used for tracking timer * sync lock dependencies @@ -652,24 +634,14 @@ EXPORT_SYMBOL_GPL(setup_deferrable_timer_on_stack_key); * init_timer_key() must be done to a timer prior calling *any* of the * other timer functions. */ -void init_timer_key(struct timer_list *timer, - const char *name, - struct lock_class_key *key) +void init_timer_key(struct timer_list *timer, unsigned int flags, + const char *name, struct lock_class_key *key) { debug_init(timer); - __init_timer(timer, name, key); + do_init_timer(timer, flags, name, key); } EXPORT_SYMBOL(init_timer_key); -void init_timer_deferrable_key(struct timer_list *timer, - const char *name, - struct lock_class_key *key) -{ - init_timer_key(timer, name, key); - timer_set_deferrable(timer); -} -EXPORT_SYMBOL(init_timer_deferrable_key); - static inline void detach_timer(struct timer_list *timer, bool clear_pending) { struct list_head *entry = &timer->entry; -- cgit v1.2.3 From c5f66e99b7cb091e3d51ae8e8156892e8feb7fa3 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 8 Aug 2012 11:10:28 -0700 Subject: timer: Implement TIMER_IRQSAFE Timer internals are protected with irq-safe locks but timer execution isn't, so a timer being dequeued for execution and its execution aren't atomic against IRQs. This makes it impossible to wait for its completion from IRQ handlers and difficult to shoot down a timer from IRQ handlers. This issue caused some issues for delayed_work interface. Because there's no way to reliably shoot down delayed_work->timer from IRQ handlers, __cancel_delayed_work() can't share the logic to steal the target delayed_work with cancel_delayed_work_sync(), and can only steal delayed_works which are on queued on timer. Similarly, the pending mod_delayed_work() can't be used from IRQ handlers. This patch adds a new timer flag TIMER_IRQSAFE, which makes the timer to be executed without enabling IRQ after dequeueing such that its dequeueing and execution are atomic against IRQ handlers. This makes it safe to wait for the timer's completion from IRQ handlers, for example, using del_timer_sync(). It can never be executing on the local CPU and if executing on other CPUs it won't be interrupted until done. This will enable simplifying delayed_work cancel/mod interface. Signed-off-by: Tejun Heo Cc: torvalds@linux-foundation.org Cc: peterz@infradead.org Link: http://lkml.kernel.org/r/1344449428-24962-5-git-send-email-tj@kernel.org Signed-off-by: Thomas Gleixner --- include/linux/timer.h | 16 ++++++++++++---- kernel/timer.c | 35 ++++++++++++++++++++++++----------- 2 files changed, 36 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/timer.h b/include/linux/timer.h index 3f95c1fa615e..8c5a197e1587 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -49,18 +49,26 @@ extern struct tvec_base boot_tvec_bases; #endif /* - * Note that all tvec_bases are 2 byte aligned and lower bit of - * base in timer_list is guaranteed to be zero. Use the LSB to - * indicate whether the timer is deferrable. + * Note that all tvec_bases are at least 4 byte aligned and lower two bits + * of base in timer_list is guaranteed to be zero. Use them for flags. * * A deferrable timer will work normally when the system is busy, but * will not cause a CPU to come out of idle just to service it; instead, * the timer will be serviced when the CPU eventually wakes up with a * subsequent non-deferrable timer. + * + * An irqsafe timer is executed with IRQ disabled and it's safe to wait for + * the completion of the running instance from IRQ handlers, for example, + * by calling del_timer_sync(). + * + * Note: The irq disabled callback execution is a special case for + * workqueue locking issues. It's not meant for executing random crap + * with interrupts disabled. Abuse is monitored! */ #define TIMER_DEFERRABLE 0x1LU +#define TIMER_IRQSAFE 0x2LU -#define TIMER_FLAG_MASK 0x1LU +#define TIMER_FLAG_MASK 0x3LU #define __TIMER_INITIALIZER(_function, _expires, _data, _flags) { \ .entry = { .prev = TIMER_ENTRY_STATIC }, \ diff --git a/kernel/timer.c b/kernel/timer.c index 8d185a1677cc..706fe4c53e82 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -95,6 +95,11 @@ static inline unsigned int tbase_get_deferrable(struct tvec_base *base) return ((unsigned int)(unsigned long)base & TIMER_DEFERRABLE); } +static inline unsigned int tbase_get_irqsafe(struct tvec_base *base) +{ + return ((unsigned int)(unsigned long)base & TIMER_IRQSAFE); +} + static inline struct tvec_base *tbase_get_base(struct tvec_base *base) { return ((struct tvec_base *)((unsigned long)base & ~TIMER_FLAG_MASK)); @@ -1002,14 +1007,14 @@ EXPORT_SYMBOL(try_to_del_timer_sync); * * Synchronization rules: Callers must prevent restarting of the timer, * otherwise this function is meaningless. It must not be called from - * interrupt contexts. The caller must not hold locks which would prevent - * completion of the timer's handler. The timer's handler must not call - * add_timer_on(). Upon exit the timer is not queued and the handler is - * not running on any CPU. + * interrupt contexts unless the timer is an irqsafe one. The caller must + * not hold locks which would prevent completion of the timer's + * handler. The timer's handler must not call add_timer_on(). Upon exit the + * timer is not queued and the handler is not running on any CPU. * - * Note: You must not hold locks that are held in interrupt context - * while calling this function. Even if the lock has nothing to do - * with the timer in question. Here's why: + * Note: For !irqsafe timers, you must not hold locks that are held in + * interrupt context while calling this function. Even if the lock has + * nothing to do with the timer in question. Here's why: * * CPU0 CPU1 * ---- ---- @@ -1046,7 +1051,7 @@ int del_timer_sync(struct timer_list *timer) * don't use it in hardirq context, because it * could lead to deadlock. */ - WARN_ON(in_irq()); + WARN_ON(in_irq() && !tbase_get_irqsafe(timer->base)); for (;;) { int ret = try_to_del_timer_sync(timer); if (ret >= 0) @@ -1153,19 +1158,27 @@ static inline void __run_timers(struct tvec_base *base) while (!list_empty(head)) { void (*fn)(unsigned long); unsigned long data; + bool irqsafe; timer = list_first_entry(head, struct timer_list,entry); fn = timer->function; data = timer->data; + irqsafe = tbase_get_irqsafe(timer->base); timer_stats_account_timer(timer); base->running_timer = timer; detach_expired_timer(timer, base); - spin_unlock_irq(&base->lock); - call_timer_fn(timer, fn, data); - spin_lock_irq(&base->lock); + if (irqsafe) { + spin_unlock(&base->lock); + call_timer_fn(timer, fn, data); + spin_lock(&base->lock); + } else { + spin_unlock_irq(&base->lock); + call_timer_fn(timer, fn, data); + spin_lock_irq(&base->lock); + } } } base->running_timer = NULL; -- cgit v1.2.3 From cdcba7c6508502cddb07c84a4d14d8f624e8f168 Mon Sep 17 00:00:00 2001 From: Mikel Astiz Date: Thu, 9 Aug 2012 09:52:28 +0200 Subject: Bluetooth: Add more HCI error codes Add more HCI error codes as defined in the specification. Signed-off-by: Mikel Astiz Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 23cf413e2acf..0f28f7052f82 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -302,8 +302,11 @@ enum { /* ---- HCI Error Codes ---- */ #define HCI_ERROR_AUTH_FAILURE 0x05 +#define HCI_ERROR_CONNECTION_TIMEOUT 0x08 #define HCI_ERROR_REJ_BAD_ADDR 0x0f #define HCI_ERROR_REMOTE_USER_TERM 0x13 +#define HCI_ERROR_REMOTE_LOW_RESOURCES 0x14 +#define HCI_ERROR_REMOTE_POWER_OFF 0x15 #define HCI_ERROR_LOCAL_HOST_TERM 0x16 #define HCI_ERROR_PAIRING_NOT_ALLOWED 0x18 -- cgit v1.2.3 From f0d6a0ea330617454032d6e2ed48759858a44427 Mon Sep 17 00:00:00 2001 From: Mikel Astiz Date: Thu, 9 Aug 2012 09:52:30 +0200 Subject: Bluetooth: mgmt: Add device disconnect reason MGMT_EV_DEVICE_DISCONNECTED will now expose the disconnection reason to userland, distinguishing four possible values: 0x00 Reason not known or unspecified 0x01 Connection timeout 0x02 Connection terminated by local host 0x03 Connection terminated by remote host Note that the local/remote distinction just determines which side terminated the low-level connection, regardless of the disconnection of the higher-level profiles. This can sometimes be misleading and thus must be used with care. For example, some hardware combinations would report a locally initiated disconnection even if the user turned Bluetooth off in the remote side. Signed-off-by: Mikel Astiz Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 2 +- include/net/bluetooth/mgmt.h | 9 +++++++++ net/bluetooth/hci_event.c | 26 +++++++++++++++++++++++--- net/bluetooth/mgmt.c | 9 +++++---- 4 files changed, 38 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 7267dafd7159..1bbc1091748c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1002,7 +1002,7 @@ int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u32 flags, u8 *name, u8 name_len, u8 *dev_class); int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type); + u8 link_type, u8 addr_type, u8 reason); int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status); int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 4348ee8bda69..1b48effcd973 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -405,7 +405,16 @@ struct mgmt_ev_device_connected { __u8 eir[0]; } __packed; +#define MGMT_DEV_DISCONN_UNKNOWN 0x00 +#define MGMT_DEV_DISCONN_TIMEOUT 0x01 +#define MGMT_DEV_DISCONN_LOCAL_HOST 0x02 +#define MGMT_DEV_DISCONN_REMOTE 0x03 + #define MGMT_EV_DEVICE_DISCONNECTED 0x000C +struct mgmt_ev_device_disconnected { + struct mgmt_addr_info addr; + __u8 reason; +} __packed; #define MGMT_EV_CONNECT_FAILED 0x000D struct mgmt_ev_connect_failed { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index bfa9bcc0f5ef..48d730228c2f 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -29,6 +29,7 @@ #include #include +#include /* Handle HCI Event packets */ @@ -1875,6 +1876,22 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) } } +static u8 hci_to_mgmt_reason(u8 err) +{ + switch (err) { + case HCI_ERROR_CONNECTION_TIMEOUT: + return MGMT_DEV_DISCONN_TIMEOUT; + case HCI_ERROR_REMOTE_USER_TERM: + case HCI_ERROR_REMOTE_LOW_RESOURCES: + case HCI_ERROR_REMOTE_POWER_OFF: + return MGMT_DEV_DISCONN_REMOTE; + case HCI_ERROR_LOCAL_HOST_TERM: + return MGMT_DEV_DISCONN_LOCAL_HOST; + default: + return MGMT_DEV_DISCONN_UNKNOWN; + } +} + static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_disconn_complete *ev = (void *) skb->data; @@ -1893,12 +1910,15 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) if (test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags) && (conn->type == ACL_LINK || conn->type == LE_LINK)) { - if (ev->status) + if (ev->status) { mgmt_disconnect_failed(hdev, &conn->dst, conn->type, conn->dst_type, ev->status); - else + } else { + u8 reason = hci_to_mgmt_reason(ev->reason); + mgmt_device_disconnected(hdev, &conn->dst, conn->type, - conn->dst_type); + conn->dst_type, reason); + } } if (ev->status == 0) { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index a3329cbd3e4d..05d4b83a0189 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3077,16 +3077,17 @@ static void unpair_device_rsp(struct pending_cmd *cmd, void *data) } int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type) + u8 link_type, u8 addr_type, u8 reason) { - struct mgmt_addr_info ev; + struct mgmt_ev_device_disconnected ev; struct sock *sk = NULL; int err; mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk); - bacpy(&ev.bdaddr, bdaddr); - ev.type = link_to_bdaddr(link_type, addr_type); + bacpy(&ev.addr.bdaddr, bdaddr); + ev.addr.type = link_to_bdaddr(link_type, addr_type); + ev.reason = reason; err = mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev), sk); -- cgit v1.2.3 From 144ad33020a0af66fbb188ef3f13ca91c5326a69 Mon Sep 17 00:00:00 2001 From: Syam Sidhardhan Date: Fri, 27 Jul 2012 23:51:21 +0530 Subject: Bluetooth: Use kref for l2cap channel reference counting This patch changes the struct l2cap_chan and associated code to use kref api for object refcounting and freeing. Suggested-by: Andrei Emeltchenko Signed-off-by: Jaganath Kanakkassery Signed-off-by: Syam Sidhardhan Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 3 +-- net/bluetooth/l2cap_core.c | 15 ++++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index d206296137e2..7ed8e356425a 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -433,11 +433,10 @@ struct l2cap_chan { struct sock *sk; struct l2cap_conn *conn; + struct kref kref; __u8 state; - atomic_t refcnt; - __le16 psm; __u16 dcid; __u16 scid; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index dae895e3ca75..9732f03cfbef 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -406,7 +406,7 @@ struct l2cap_chan *l2cap_chan_create(void) chan->state = BT_OPEN; - atomic_set(&chan->refcnt, 1); + kref_init(&chan->kref); /* This flag is cleared in l2cap_chan_ready() */ set_bit(CONF_NOT_COMPLETE, &chan->conf_state); @@ -416,8 +416,10 @@ struct l2cap_chan *l2cap_chan_create(void) return chan; } -static void l2cap_chan_destroy(struct l2cap_chan *chan) +static void l2cap_chan_destroy(struct kref *kref) { + struct l2cap_chan *chan = container_of(kref, struct l2cap_chan, kref); + BT_DBG("chan %p", chan); write_lock(&chan_list_lock); @@ -429,17 +431,16 @@ static void l2cap_chan_destroy(struct l2cap_chan *chan) void l2cap_chan_hold(struct l2cap_chan *c) { - BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->refcnt)); + BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->kref.refcount)); - atomic_inc(&c->refcnt); + kref_get(&c->kref); } void l2cap_chan_put(struct l2cap_chan *c) { - BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->refcnt)); + BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->kref.refcount)); - if (atomic_dec_and_test(&c->refcnt)) - l2cap_chan_destroy(c); + kref_put(&c->kref, l2cap_chan_destroy); } void l2cap_chan_set_defaults(struct l2cap_chan *chan) -- cgit v1.2.3 From 06d7de831dab8b93adb86e039a2f3d36604a9197 Mon Sep 17 00:00:00 2001 From: AceLan Kao Date: Thu, 26 Jul 2012 09:51:08 +0800 Subject: Revert "rfkill: remove dead code" This reverts commit 2e48928d8a0f38c1b5c81eb3f1294de8a6382c68. Those functions are needed and should not be removed, or there is no way to set the rfkill led trigger name. Signed-off-by: AceLan Kao Signed-off-by: Johannes Berg --- include/linux/rfkill.h | 31 +++++++++++++++++++++++++++++++ net/rfkill/core.c | 14 ++++++++++++++ 2 files changed, 45 insertions(+) (limited to 'include') diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h index 6fdf02737e9d..0ec590bb3611 100644 --- a/include/linux/rfkill.h +++ b/include/linux/rfkill.h @@ -354,6 +354,37 @@ static inline bool rfkill_blocked(struct rfkill *rfkill) } #endif /* RFKILL || RFKILL_MODULE */ + +#ifdef CONFIG_RFKILL_LEDS +/** + * rfkill_get_led_trigger_name - Get the LED trigger name for the button's LED. + * This function might return a NULL pointer if registering of the + * LED trigger failed. Use this as "default_trigger" for the LED. + */ +const char *rfkill_get_led_trigger_name(struct rfkill *rfkill); + +/** + * rfkill_set_led_trigger_name -- set the LED trigger name + * @rfkill: rfkill struct + * @name: LED trigger name + * + * This function sets the LED trigger name of the radio LED + * trigger that rfkill creates. It is optional, but if called + * must be called before rfkill_register() to be effective. + */ +void rfkill_set_led_trigger_name(struct rfkill *rfkill, const char *name); +#else +static inline const char *rfkill_get_led_trigger_name(struct rfkill *rfkill) +{ + return NULL; +} + +static inline void +rfkill_set_led_trigger_name(struct rfkill *rfkill, const char *name) +{ +} +#endif + #endif /* __KERNEL__ */ #endif /* RFKILL_H */ diff --git a/net/rfkill/core.c b/net/rfkill/core.c index 752b72360ebc..c275bad12068 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -150,6 +150,20 @@ static void rfkill_led_trigger_activate(struct led_classdev *led) rfkill_led_trigger_event(rfkill); } +const char *rfkill_get_led_trigger_name(struct rfkill *rfkill) +{ + return rfkill->led_trigger.name; +} +EXPORT_SYMBOL(rfkill_get_led_trigger_name); + +void rfkill_set_led_trigger_name(struct rfkill *rfkill, const char *name) +{ + BUG_ON(!rfkill); + + rfkill->ledtrigname = name; +} +EXPORT_SYMBOL(rfkill_set_led_trigger_name); + static int rfkill_led_trigger_register(struct rfkill *rfkill) { rfkill->led_trigger.name = rfkill->ledtrigname -- cgit v1.2.3 From d57ef3a6a2eeb88df47e892c66692e3f59722ffe Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Fri, 10 Aug 2012 21:23:53 +0200 Subject: bcma: detect and register serial flash device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/bcma/Kconfig | 2 +- drivers/bcma/bcma_private.h | 1 + drivers/bcma/driver_chipcommon_sflash.c | 123 +++++++++++++++++++++++++++- drivers/bcma/main.c | 9 ++ include/linux/bcma/bcma_driver_chipcommon.h | 13 +++ include/linux/bcma/bcma_regs.h | 2 + 6 files changed, 146 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/bcma/Kconfig b/drivers/bcma/Kconfig index 06b3207adebd..cf6e4dd22fd2 100644 --- a/drivers/bcma/Kconfig +++ b/drivers/bcma/Kconfig @@ -48,7 +48,7 @@ config BCMA_DRIVER_MIPS config BCMA_SFLASH bool - depends on BCMA_DRIVER_MIPS && BROKEN + depends on BCMA_DRIVER_MIPS default y config BCMA_NFLASH diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h index 3cf9cc923cd2..94bf07c54a43 100644 --- a/drivers/bcma/bcma_private.h +++ b/drivers/bcma/bcma_private.h @@ -54,6 +54,7 @@ u32 bcma_pmu_get_clockcpu(struct bcma_drv_cc *cc); #ifdef CONFIG_BCMA_SFLASH /* driver_chipcommon_sflash.c */ int bcma_sflash_init(struct bcma_drv_cc *cc); +extern struct platform_device bcma_sflash_dev; #else static inline int bcma_sflash_init(struct bcma_drv_cc *cc) { diff --git a/drivers/bcma/driver_chipcommon_sflash.c b/drivers/bcma/driver_chipcommon_sflash.c index 6e157a58a1d7..2c4eec2ca5a0 100644 --- a/drivers/bcma/driver_chipcommon_sflash.c +++ b/drivers/bcma/driver_chipcommon_sflash.c @@ -5,15 +5,132 @@ * Licensed under the GNU/GPL. See COPYING for details. */ +#include #include -#include -#include #include "bcma_private.h" +static struct resource bcma_sflash_resource = { + .name = "bcma_sflash", + .start = BCMA_SFLASH, + .end = 0, + .flags = IORESOURCE_MEM | IORESOURCE_READONLY, +}; + +struct platform_device bcma_sflash_dev = { + .name = "bcma_sflash", + .resource = &bcma_sflash_resource, + .num_resources = 1, +}; + +struct bcma_sflash_tbl_e { + char *name; + u32 id; + u32 blocksize; + u16 numblocks; +}; + +static struct bcma_sflash_tbl_e bcma_sflash_st_tbl[] = { + { "", 0x14, 0x10000, 32, }, + { 0 }, +}; + +static struct bcma_sflash_tbl_e bcma_sflash_sst_tbl[] = { + { 0 }, +}; + +static struct bcma_sflash_tbl_e bcma_sflash_at_tbl[] = { + { 0 }, +}; + +static void bcma_sflash_cmd(struct bcma_drv_cc *cc, u32 opcode) +{ + int i; + bcma_cc_write32(cc, BCMA_CC_FLASHCTL, + BCMA_CC_FLASHCTL_START | opcode); + for (i = 0; i < 1000; i++) { + if (!(bcma_cc_read32(cc, BCMA_CC_FLASHCTL) & + BCMA_CC_FLASHCTL_BUSY)) + return; + cpu_relax(); + } + bcma_err(cc->core->bus, "SFLASH control command failed (timeout)!\n"); +} + /* Initialize serial flash access */ int bcma_sflash_init(struct bcma_drv_cc *cc) { - bcma_err(cc->core->bus, "Serial flash support is broken\n"); + struct bcma_bus *bus = cc->core->bus; + struct bcma_sflash *sflash = &cc->sflash; + struct bcma_sflash_tbl_e *e; + u32 id, id2; + + switch (cc->capabilities & BCMA_CC_CAP_FLASHT) { + case BCMA_CC_FLASHT_STSER: + bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_DP); + + bcma_cc_write32(cc, BCMA_CC_FLASHADDR, 0); + bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_RES); + id = bcma_cc_read32(cc, BCMA_CC_FLASHDATA); + + bcma_cc_write32(cc, BCMA_CC_FLASHADDR, 1); + bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_RES); + id2 = bcma_cc_read32(cc, BCMA_CC_FLASHDATA); + + switch (id) { + case 0xbf: + for (e = bcma_sflash_sst_tbl; e->name; e++) { + if (e->id == id2) + break; + } + break; + default: + for (e = bcma_sflash_st_tbl; e->name; e++) { + if (e->id == id) + break; + } + break; + } + if (!e->name) { + bcma_err(bus, "Unsupported ST serial flash (id: 0x%X, id2: 0x%X)\n", id, id2); + return -ENOTSUPP; + } + + break; + case BCMA_CC_FLASHT_ATSER: + bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_AT_STATUS); + id = bcma_cc_read32(cc, BCMA_CC_FLASHDATA) & 0x3c; + + for (e = bcma_sflash_at_tbl; e->name; e++) { + if (e->id == id) + break; + } + if (!e->name) { + bcma_err(bus, "Unsupported Atmel serial flash (id: 0x%X)\n", id); + return -ENOTSUPP; + } + + break; + default: + bcma_err(bus, "Unsupported flash type\n"); + return -ENOTSUPP; + } + + sflash->window = BCMA_SFLASH; + sflash->blocksize = e->blocksize; + sflash->numblocks = e->numblocks; + sflash->size = sflash->blocksize * sflash->numblocks; + sflash->present = true; + + bcma_info(bus, "Found %s serial flash (size: %dKiB, blocksize: 0x%X, blocks: %d)\n", + e->name, sflash->size / 1024, sflash->blocksize, + sflash->numblocks); + + /* Prepare platform device, but don't register it yet. It's too early, + * malloc (required by device_private_init) is not available yet. */ + bcma_sflash_dev.resource[0].end = bcma_sflash_dev.resource[0].start + + sflash->size; + bcma_sflash_dev.dev.platform_data = sflash; + return 0; } diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index 758af9ccdef0..a501d259287c 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -7,6 +7,7 @@ #include "bcma_private.h" #include +#include #include #include @@ -136,6 +137,14 @@ static int bcma_register_cores(struct bcma_bus *bus) dev_id++; } +#ifdef CONFIG_BCMA_SFLASH + if (bus->drv_cc.sflash.present) { + err = platform_device_register(&bcma_sflash_dev); + if (err) + bcma_err(bus, "Error registering serial flash\n"); + } +#endif + return 0; } diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h index fcb06fb284eb..bed89694c5f9 100644 --- a/include/linux/bcma/bcma_driver_chipcommon.h +++ b/include/linux/bcma/bcma_driver_chipcommon.h @@ -509,6 +509,16 @@ struct bcma_pflash { u32 window_size; }; +#ifdef CONFIG_BCMA_SFLASH +struct bcma_sflash { + bool present; + u32 window; + u32 blocksize; + u16 numblocks; + u32 size; +}; +#endif + struct bcma_serial_port { void *regs; unsigned long clockspeed; @@ -529,6 +539,9 @@ struct bcma_drv_cc { struct bcma_chipcommon_pmu pmu; #ifdef CONFIG_BCMA_DRIVER_MIPS struct bcma_pflash pflash; +#ifdef CONFIG_BCMA_SFLASH + struct bcma_sflash sflash; +#endif int nr_serial_ports; struct bcma_serial_port serial_ports[4]; diff --git a/include/linux/bcma/bcma_regs.h b/include/linux/bcma/bcma_regs.h index a393e82bf7bf..6c9cb93ae3de 100644 --- a/include/linux/bcma/bcma_regs.h +++ b/include/linux/bcma/bcma_regs.h @@ -85,4 +85,6 @@ * (2 ZettaBytes), high 32 bits */ +#define BCMA_SFLASH 0x1c000000 + #endif /* LINUX_BCMA_REGS_H_ */ -- cgit v1.2.3 From 371a00448f95adaa612cf1a0b31a11e7093bc706 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Sun, 12 Aug 2012 13:08:05 +0200 Subject: bcma: detect and register NAND flash device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/bcma/Kconfig | 2 +- drivers/bcma/bcma_private.h | 1 + drivers/bcma/driver_chipcommon_nflash.c | 28 +++++++++++++++++++++++++--- drivers/bcma/main.c | 8 ++++++++ include/linux/bcma/bcma_driver_chipcommon.h | 13 +++++++++++++ 5 files changed, 48 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/bcma/Kconfig b/drivers/bcma/Kconfig index cf6e4dd22fd2..a533af218368 100644 --- a/drivers/bcma/Kconfig +++ b/drivers/bcma/Kconfig @@ -53,7 +53,7 @@ config BCMA_SFLASH config BCMA_NFLASH bool - depends on BCMA_DRIVER_MIPS && BROKEN + depends on BCMA_DRIVER_MIPS default y config BCMA_DRIVER_GMAC_CMN diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h index 94bf07c54a43..169fc58427d3 100644 --- a/drivers/bcma/bcma_private.h +++ b/drivers/bcma/bcma_private.h @@ -66,6 +66,7 @@ static inline int bcma_sflash_init(struct bcma_drv_cc *cc) #ifdef CONFIG_BCMA_NFLASH /* driver_chipcommon_nflash.c */ int bcma_nflash_init(struct bcma_drv_cc *cc); +extern struct platform_device bcma_nflash_dev; #else static inline int bcma_nflash_init(struct bcma_drv_cc *cc) { diff --git a/drivers/bcma/driver_chipcommon_nflash.c b/drivers/bcma/driver_chipcommon_nflash.c index 574d62435bc2..9042781edec3 100644 --- a/drivers/bcma/driver_chipcommon_nflash.c +++ b/drivers/bcma/driver_chipcommon_nflash.c @@ -5,15 +5,37 @@ * Licensed under the GNU/GPL. See COPYING for details. */ +#include #include -#include -#include #include "bcma_private.h" +struct platform_device bcma_nflash_dev = { + .name = "bcma_nflash", + .num_resources = 0, +}; + /* Initialize NAND flash access */ int bcma_nflash_init(struct bcma_drv_cc *cc) { - bcma_err(cc->core->bus, "NAND flash support is broken\n"); + struct bcma_bus *bus = cc->core->bus; + + if (bus->chipinfo.id != BCMA_CHIP_ID_BCM4706 && + cc->core->id.rev != 0x38) { + bcma_err(bus, "NAND flash on unsupported board!\n"); + return -ENOTSUPP; + } + + if (!(cc->capabilities & BCMA_CC_CAP_NFLASH)) { + bcma_err(bus, "NAND flash not present according to ChipCommon\n"); + return -ENODEV; + } + + cc->nflash.present = true; + + /* Prepare platform device, but don't register it yet. It's too early, + * malloc (required by device_private_init) is not available yet. */ + bcma_nflash_dev.dev.platform_data = &cc->nflash; + return 0; } diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index a501d259287c..a8f570d69075 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -145,6 +145,14 @@ static int bcma_register_cores(struct bcma_bus *bus) } #endif +#ifdef CONFIG_BCMA_NFLASH + if (bus->drv_cc.nflash.present) { + err = platform_device_register(&bcma_nflash_dev); + if (err) + bcma_err(bus, "Error registering NAND flash\n"); + } +#endif + return 0; } diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h index bed89694c5f9..9810d4b29abf 100644 --- a/include/linux/bcma/bcma_driver_chipcommon.h +++ b/include/linux/bcma/bcma_driver_chipcommon.h @@ -519,6 +519,16 @@ struct bcma_sflash { }; #endif +#ifdef CONFIG_BCMA_NFLASH +struct mtd_info; + +struct bcma_nflash { + bool present; + + struct mtd_info *mtd; +}; +#endif + struct bcma_serial_port { void *regs; unsigned long clockspeed; @@ -542,6 +552,9 @@ struct bcma_drv_cc { #ifdef CONFIG_BCMA_SFLASH struct bcma_sflash sflash; #endif +#ifdef CONFIG_BCMA_NFLASH + struct bcma_nflash nflash; +#endif int nr_serial_ports; struct bcma_serial_port serial_ports[4]; -- cgit v1.2.3 From ee64e7f697ad7e5575e6ac8900cfb71975484421 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 21 Aug 2012 13:18:23 -0700 Subject: workqueue: cosmetic whitespace updates for macro definitions Consistently use the last tab position for '\' line continuation in complex macro definitions. This is to help the following patches. This patch is cosmetic. Signed-off-by: Tejun Heo --- include/linux/workqueue.h | 126 +++++++++++++++++++++++----------------------- 1 file changed, 63 insertions(+), 63 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 1ce3fb08308d..26c5b4c63861 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -126,43 +126,43 @@ struct execute_work { #define __WORK_INIT_LOCKDEP_MAP(n, k) #endif -#define __WORK_INITIALIZER(n, f) { \ - .data = WORK_DATA_STATIC_INIT(), \ - .entry = { &(n).entry, &(n).entry }, \ - .func = (f), \ - __WORK_INIT_LOCKDEP_MAP(#n, &(n)) \ +#define __WORK_INITIALIZER(n, f) { \ + .data = WORK_DATA_STATIC_INIT(), \ + .entry = { &(n).entry, &(n).entry }, \ + .func = (f), \ + __WORK_INIT_LOCKDEP_MAP(#n, &(n)) \ } -#define __DELAYED_WORK_INITIALIZER(n, f) { \ - .work = __WORK_INITIALIZER((n).work, (f)), \ - .timer = TIMER_INITIALIZER(delayed_work_timer_fn, \ - 0, (unsigned long)&(n)), \ +#define __DELAYED_WORK_INITIALIZER(n, f) { \ + .work = __WORK_INITIALIZER((n).work, (f)), \ + .timer = TIMER_INITIALIZER(delayed_work_timer_fn, \ + 0, (unsigned long)&(n)), \ } -#define __DEFERRED_WORK_INITIALIZER(n, f) { \ - .work = __WORK_INITIALIZER((n).work, (f)), \ - .timer = TIMER_DEFERRED_INITIALIZER(delayed_work_timer_fn, \ - 0, (unsigned long)&(n)), \ +#define __DEFERRED_WORK_INITIALIZER(n, f) { \ + .work = __WORK_INITIALIZER((n).work, (f)), \ + .timer = TIMER_DEFERRED_INITIALIZER(delayed_work_timer_fn, \ + 0, (unsigned long)&(n)), \ } -#define DECLARE_WORK(n, f) \ +#define DECLARE_WORK(n, f) \ struct work_struct n = __WORK_INITIALIZER(n, f) -#define DECLARE_DELAYED_WORK(n, f) \ +#define DECLARE_DELAYED_WORK(n, f) \ struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f) -#define DECLARE_DEFERRED_WORK(n, f) \ +#define DECLARE_DEFERRED_WORK(n, f) \ struct delayed_work n = __DEFERRED_WORK_INITIALIZER(n, f) /* * initialize a work item's function pointer */ -#define PREPARE_WORK(_work, _func) \ - do { \ - (_work)->func = (_func); \ +#define PREPARE_WORK(_work, _func) \ + do { \ + (_work)->func = (_func); \ } while (0) -#define PREPARE_DELAYED_WORK(_work, _func) \ +#define PREPARE_DELAYED_WORK(_work, _func) \ PREPARE_WORK(&(_work)->work, (_func)) #ifdef CONFIG_DEBUG_OBJECTS_WORK @@ -192,7 +192,7 @@ static inline unsigned int work_static(struct work_struct *work) { return 0; } \ __init_work((_work), _onstack); \ (_work)->data = (atomic_long_t) WORK_DATA_INIT(); \ - lockdep_init_map(&(_work)->lockdep_map, #_work, &__key, 0);\ + lockdep_init_map(&(_work)->lockdep_map, #_work, &__key, 0); \ INIT_LIST_HEAD(&(_work)->entry); \ PREPARE_WORK((_work), (_func)); \ } while (0) @@ -206,38 +206,38 @@ static inline unsigned int work_static(struct work_struct *work) { return 0; } } while (0) #endif -#define INIT_WORK(_work, _func) \ - do { \ - __INIT_WORK((_work), (_func), 0); \ +#define INIT_WORK(_work, _func) \ + do { \ + __INIT_WORK((_work), (_func), 0); \ } while (0) -#define INIT_WORK_ONSTACK(_work, _func) \ - do { \ - __INIT_WORK((_work), (_func), 1); \ +#define INIT_WORK_ONSTACK(_work, _func) \ + do { \ + __INIT_WORK((_work), (_func), 1); \ } while (0) -#define INIT_DELAYED_WORK(_work, _func) \ - do { \ - INIT_WORK(&(_work)->work, (_func)); \ - init_timer(&(_work)->timer); \ - (_work)->timer.function = delayed_work_timer_fn;\ - (_work)->timer.data = (unsigned long)(_work); \ +#define INIT_DELAYED_WORK(_work, _func) \ + do { \ + INIT_WORK(&(_work)->work, (_func)); \ + init_timer(&(_work)->timer); \ + (_work)->timer.function = delayed_work_timer_fn; \ + (_work)->timer.data = (unsigned long)(_work); \ } while (0) -#define INIT_DELAYED_WORK_ONSTACK(_work, _func) \ - do { \ - INIT_WORK_ONSTACK(&(_work)->work, (_func)); \ - init_timer_on_stack(&(_work)->timer); \ - (_work)->timer.function = delayed_work_timer_fn;\ - (_work)->timer.data = (unsigned long)(_work); \ +#define INIT_DELAYED_WORK_ONSTACK(_work, _func) \ + do { \ + INIT_WORK_ONSTACK(&(_work)->work, (_func)); \ + init_timer_on_stack(&(_work)->timer); \ + (_work)->timer.function = delayed_work_timer_fn; \ + (_work)->timer.data = (unsigned long)(_work); \ } while (0) -#define INIT_DELAYED_WORK_DEFERRABLE(_work, _func) \ - do { \ - INIT_WORK(&(_work)->work, (_func)); \ - init_timer_deferrable(&(_work)->timer); \ - (_work)->timer.function = delayed_work_timer_fn;\ - (_work)->timer.data = (unsigned long)(_work); \ +#define INIT_DELAYED_WORK_DEFERRABLE(_work, _func) \ + do { \ + INIT_WORK(&(_work)->work, (_func)); \ + init_timer_deferrable(&(_work)->timer); \ + (_work)->timer.function = delayed_work_timer_fn; \ + (_work)->timer.data = (unsigned long)(_work); \ } while (0) /** @@ -345,22 +345,22 @@ __alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active, * Pointer to the allocated workqueue on success, %NULL on failure. */ #ifdef CONFIG_LOCKDEP -#define alloc_workqueue(fmt, flags, max_active, args...) \ -({ \ - static struct lock_class_key __key; \ - const char *__lock_name; \ - \ - if (__builtin_constant_p(fmt)) \ - __lock_name = (fmt); \ - else \ - __lock_name = #fmt; \ - \ - __alloc_workqueue_key((fmt), (flags), (max_active), \ - &__key, __lock_name, ##args); \ +#define alloc_workqueue(fmt, flags, max_active, args...) \ +({ \ + static struct lock_class_key __key; \ + const char *__lock_name; \ + \ + if (__builtin_constant_p(fmt)) \ + __lock_name = (fmt); \ + else \ + __lock_name = #fmt; \ + \ + __alloc_workqueue_key((fmt), (flags), (max_active), \ + &__key, __lock_name, ##args); \ }) #else -#define alloc_workqueue(fmt, flags, max_active, args...) \ - __alloc_workqueue_key((fmt), (flags), (max_active), \ +#define alloc_workqueue(fmt, flags, max_active, args...) \ + __alloc_workqueue_key((fmt), (flags), (max_active), \ NULL, NULL, ##args) #endif @@ -377,14 +377,14 @@ __alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active, * RETURNS: * Pointer to the allocated workqueue on success, %NULL on failure. */ -#define alloc_ordered_workqueue(fmt, flags, args...) \ +#define alloc_ordered_workqueue(fmt, flags, args...) \ alloc_workqueue(fmt, WQ_UNBOUND | (flags), 1, ##args) -#define create_workqueue(name) \ +#define create_workqueue(name) \ alloc_workqueue((name), WQ_MEM_RECLAIM, 1) -#define create_freezable_workqueue(name) \ +#define create_freezable_workqueue(name) \ alloc_workqueue((name), WQ_FREEZABLE | WQ_UNBOUND | WQ_MEM_RECLAIM, 1) -#define create_singlethread_workqueue(name) \ +#define create_singlethread_workqueue(name) \ alloc_workqueue((name), WQ_UNBOUND | WQ_MEM_RECLAIM, 1) extern void destroy_workqueue(struct workqueue_struct *wq); -- cgit v1.2.3 From 203b42f7317494ae5e5efc7be6fb7f29c927f102 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 21 Aug 2012 13:18:23 -0700 Subject: workqueue: make deferrable delayed_work initializer names consistent Initalizers for deferrable delayed_work are confused. * __DEFERRED_WORK_INITIALIZER() * DECLARE_DEFERRED_WORK() * INIT_DELAYED_WORK_DEFERRABLE() Rename them to * __DEFERRABLE_WORK_INITIALIZER() * DECLARE_DEFERRABLE_WORK() * INIT_DEFERRABLE_WORK() This patch doesn't cause any functional changes. Signed-off-by: Tejun Heo --- arch/powerpc/platforms/cell/cpufreq_spudemand.c | 2 +- drivers/cpufreq/cpufreq_conservative.c | 2 +- drivers/cpufreq/cpufreq_ondemand.c | 2 +- drivers/devfreq/devfreq.c | 2 +- drivers/net/ethernet/mellanox/mlx4/sense.c | 2 +- drivers/power/ab8500_btemp.c | 2 +- drivers/power/ab8500_charger.c | 8 ++++---- drivers/power/ab8500_fg.c | 8 ++++---- drivers/power/abx500_chargalg.c | 4 ++-- drivers/power/max17040_battery.c | 2 +- drivers/video/omap2/displays/panel-taal.c | 6 +++--- drivers/video/omap2/dss/dsi.c | 4 ++-- include/linux/workqueue.h | 8 ++++---- mm/slab.c | 2 +- mm/vmstat.c | 2 +- net/core/neighbour.c | 2 +- net/ipv4/inetpeer.c | 2 +- net/sunrpc/cache.c | 2 +- 18 files changed, 31 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/arch/powerpc/platforms/cell/cpufreq_spudemand.c b/arch/powerpc/platforms/cell/cpufreq_spudemand.c index 23bc9db4317e..82607d621aca 100644 --- a/arch/powerpc/platforms/cell/cpufreq_spudemand.c +++ b/arch/powerpc/platforms/cell/cpufreq_spudemand.c @@ -76,7 +76,7 @@ static void spu_gov_work(struct work_struct *work) static void spu_gov_init_work(struct spu_gov_info_struct *info) { int delay = usecs_to_jiffies(info->poll_int); - INIT_DELAYED_WORK_DEFERRABLE(&info->work, spu_gov_work); + INIT_DEFERRABLE_WORK(&info->work, spu_gov_work); schedule_delayed_work_on(info->policy->cpu, &info->work, delay); } diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index 235a340e81f2..55f0354864e2 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -466,7 +466,7 @@ static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info) delay -= jiffies % delay; dbs_info->enable = 1; - INIT_DELAYED_WORK_DEFERRABLE(&dbs_info->work, do_dbs_timer); + INIT_DEFERRABLE_WORK(&dbs_info->work, do_dbs_timer); schedule_delayed_work_on(dbs_info->cpu, &dbs_info->work, delay); } diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 836e9b062e5e..14c1af5a264f 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -644,7 +644,7 @@ static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info) delay -= jiffies % delay; dbs_info->sample_type = DBS_NORMAL_SAMPLE; - INIT_DELAYED_WORK_DEFERRABLE(&dbs_info->work, do_dbs_timer); + INIT_DEFERRABLE_WORK(&dbs_info->work, do_dbs_timer); schedule_delayed_work_on(dbs_info->cpu, &dbs_info->work, delay); } diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 70c31d43fff3..b146d76f04cf 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -607,7 +607,7 @@ static int __init devfreq_start_polling(void) mutex_lock(&devfreq_list_lock); polling = false; devfreq_wq = create_freezable_workqueue("devfreq_wq"); - INIT_DELAYED_WORK_DEFERRABLE(&devfreq_work, devfreq_monitor); + INIT_DEFERRABLE_WORK(&devfreq_work, devfreq_monitor); mutex_unlock(&devfreq_list_lock); devfreq_monitor(&devfreq_work.work); diff --git a/drivers/net/ethernet/mellanox/mlx4/sense.c b/drivers/net/ethernet/mellanox/mlx4/sense.c index 802498293528..37b23780a4d8 100644 --- a/drivers/net/ethernet/mellanox/mlx4/sense.c +++ b/drivers/net/ethernet/mellanox/mlx4/sense.c @@ -153,5 +153,5 @@ void mlx4_sense_init(struct mlx4_dev *dev) for (port = 1; port <= dev->caps.num_ports; port++) sense->do_sense_port[port] = 1; - INIT_DELAYED_WORK_DEFERRABLE(&sense->sense_poll, mlx4_sense_port); + INIT_DEFERRABLE_WORK(&sense->sense_poll, mlx4_sense_port); } diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c index bba3ccac72fe..3041514f4d3f 100644 --- a/drivers/power/ab8500_btemp.c +++ b/drivers/power/ab8500_btemp.c @@ -1018,7 +1018,7 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev) } /* Init work for measuring temperature periodically */ - INIT_DELAYED_WORK_DEFERRABLE(&di->btemp_periodic_work, + INIT_DEFERRABLE_WORK(&di->btemp_periodic_work, ab8500_btemp_periodic_work); /* Identify the battery */ diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index d4f0c98428cb..0701dbc2b7e1 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -2618,9 +2618,9 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) } /* Init work for HW failure check */ - INIT_DELAYED_WORK_DEFERRABLE(&di->check_hw_failure_work, + INIT_DEFERRABLE_WORK(&di->check_hw_failure_work, ab8500_charger_check_hw_failure_work); - INIT_DELAYED_WORK_DEFERRABLE(&di->check_usbchgnotok_work, + INIT_DEFERRABLE_WORK(&di->check_usbchgnotok_work, ab8500_charger_check_usbchargernotok_work); /* @@ -2632,10 +2632,10 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) * watchdog have to be kicked by the charger driver * when the AC charger is disabled */ - INIT_DELAYED_WORK_DEFERRABLE(&di->kick_wd_work, + INIT_DEFERRABLE_WORK(&di->kick_wd_work, ab8500_charger_kick_watchdog_work); - INIT_DELAYED_WORK_DEFERRABLE(&di->check_vbat_work, + INIT_DEFERRABLE_WORK(&di->check_vbat_work, ab8500_charger_check_vbat_work); /* Init work for charger detection */ diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index bf022255994c..5c9e7c263c38 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -2516,19 +2516,19 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev) INIT_WORK(&di->fg_acc_cur_work, ab8500_fg_acc_cur_work); /* Init work for reinitialising the fg algorithm */ - INIT_DELAYED_WORK_DEFERRABLE(&di->fg_reinit_work, + INIT_DEFERRABLE_WORK(&di->fg_reinit_work, ab8500_fg_reinit_work); /* Work delayed Queue to run the state machine */ - INIT_DELAYED_WORK_DEFERRABLE(&di->fg_periodic_work, + INIT_DEFERRABLE_WORK(&di->fg_periodic_work, ab8500_fg_periodic_work); /* Work to check low battery condition */ - INIT_DELAYED_WORK_DEFERRABLE(&di->fg_low_bat_work, + INIT_DEFERRABLE_WORK(&di->fg_low_bat_work, ab8500_fg_low_bat_work); /* Init work for HW failure check */ - INIT_DELAYED_WORK_DEFERRABLE(&di->fg_check_hw_failure_work, + INIT_DEFERRABLE_WORK(&di->fg_check_hw_failure_work, ab8500_fg_check_hw_failure_work); /* Initialize OVV, and other registers */ diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c index 804b88c760d6..4d302803ffcc 100644 --- a/drivers/power/abx500_chargalg.c +++ b/drivers/power/abx500_chargalg.c @@ -1848,9 +1848,9 @@ static int __devinit abx500_chargalg_probe(struct platform_device *pdev) } /* Init work for chargalg */ - INIT_DELAYED_WORK_DEFERRABLE(&di->chargalg_periodic_work, + INIT_DEFERRABLE_WORK(&di->chargalg_periodic_work, abx500_chargalg_periodic_work); - INIT_DELAYED_WORK_DEFERRABLE(&di->chargalg_wd_work, + INIT_DEFERRABLE_WORK(&di->chargalg_wd_work, abx500_chargalg_wd_work); /* Init work for chargalg */ diff --git a/drivers/power/max17040_battery.c b/drivers/power/max17040_battery.c index c284143cfcd7..58e67830143c 100644 --- a/drivers/power/max17040_battery.c +++ b/drivers/power/max17040_battery.c @@ -232,7 +232,7 @@ static int __devinit max17040_probe(struct i2c_client *client, max17040_reset(client); max17040_get_version(client); - INIT_DELAYED_WORK_DEFERRABLE(&chip->work, max17040_work); + INIT_DEFERRABLE_WORK(&chip->work, max17040_work); schedule_delayed_work(&chip->work, MAX17040_DELAY); return 0; diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c index 3f5acc7771da..6b5e6e0e202f 100644 --- a/drivers/video/omap2/displays/panel-taal.c +++ b/drivers/video/omap2/displays/panel-taal.c @@ -906,7 +906,7 @@ static int taal_probe(struct omap_dss_device *dssdev) r = -ENOMEM; goto err_wq; } - INIT_DELAYED_WORK_DEFERRABLE(&td->esd_work, taal_esd_work); + INIT_DEFERRABLE_WORK(&td->esd_work, taal_esd_work); INIT_DELAYED_WORK(&td->ulps_work, taal_ulps_work); dev_set_drvdata(&dssdev->dev, td); @@ -962,8 +962,8 @@ static int taal_probe(struct omap_dss_device *dssdev) goto err_irq; } - INIT_DELAYED_WORK_DEFERRABLE(&td->te_timeout_work, - taal_te_timeout_work_callback); + INIT_DEFERRABLE_WORK(&td->te_timeout_work, + taal_te_timeout_work_callback); dev_dbg(&dssdev->dev, "Using GPIO TE\n"); } diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index b07e8864f82f..fd40f2625051 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c @@ -4863,8 +4863,8 @@ static int __init omap_dsihw_probe(struct platform_device *dsidev) mutex_init(&dsi->lock); sema_init(&dsi->bus_lock, 1); - INIT_DELAYED_WORK_DEFERRABLE(&dsi->framedone_timeout_work, - dsi_framedone_timeout_work_callback); + INIT_DEFERRABLE_WORK(&dsi->framedone_timeout_work, + dsi_framedone_timeout_work_callback); #ifdef DSI_CATCH_MISSING_TE init_timer(&dsi->te_timer); diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 26c5b4c63861..49a9c51f9ee3 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -139,7 +139,7 @@ struct execute_work { 0, (unsigned long)&(n)), \ } -#define __DEFERRED_WORK_INITIALIZER(n, f) { \ +#define __DEFERRABLE_WORK_INITIALIZER(n, f) { \ .work = __WORK_INITIALIZER((n).work, (f)), \ .timer = TIMER_DEFERRED_INITIALIZER(delayed_work_timer_fn, \ 0, (unsigned long)&(n)), \ @@ -151,8 +151,8 @@ struct execute_work { #define DECLARE_DELAYED_WORK(n, f) \ struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f) -#define DECLARE_DEFERRED_WORK(n, f) \ - struct delayed_work n = __DEFERRED_WORK_INITIALIZER(n, f) +#define DECLARE_DEFERRABLE_WORK(n, f) \ + struct delayed_work n = __DEFERRABLE_WORK_INITIALIZER(n, f) /* * initialize a work item's function pointer @@ -232,7 +232,7 @@ static inline unsigned int work_static(struct work_struct *work) { return 0; } (_work)->timer.data = (unsigned long)(_work); \ } while (0) -#define INIT_DELAYED_WORK_DEFERRABLE(_work, _func) \ +#define INIT_DEFERRABLE_WORK(_work, _func) \ do { \ INIT_WORK(&(_work)->work, (_func)); \ init_timer_deferrable(&(_work)->timer); \ diff --git a/mm/slab.c b/mm/slab.c index f8b0d539b482..35b5cb0da554 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -900,7 +900,7 @@ static void __cpuinit start_cpu_timer(int cpu) */ if (keventd_up() && reap_work->work.func == NULL) { init_reap_node(cpu); - INIT_DELAYED_WORK_DEFERRABLE(reap_work, cache_reap); + INIT_DEFERRABLE_WORK(reap_work, cache_reap); schedule_delayed_work_on(cpu, reap_work, __round_jiffies_relative(HZ, cpu)); } diff --git a/mm/vmstat.c b/mm/vmstat.c index df7a6748231d..b3e3b9d525d0 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -1157,7 +1157,7 @@ static void __cpuinit start_cpu_timer(int cpu) { struct delayed_work *work = &per_cpu(vmstat_work, cpu); - INIT_DELAYED_WORK_DEFERRABLE(work, vmstat_update); + INIT_DEFERRABLE_WORK(work, vmstat_update); schedule_delayed_work_on(cpu, work, __round_jiffies_relative(HZ, cpu)); } diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 117afaf51268..112c6e2266e9 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1545,7 +1545,7 @@ static void neigh_table_init_no_netlink(struct neigh_table *tbl) panic("cannot allocate neighbour cache hashes"); rwlock_init(&tbl->lock); - INIT_DELAYED_WORK_DEFERRABLE(&tbl->gc_work, neigh_periodic_work); + INIT_DEFERRABLE_WORK(&tbl->gc_work, neigh_periodic_work); schedule_delayed_work(&tbl->gc_work, tbl->parms.reachable_time); setup_timer(&tbl->proxy_timer, neigh_proxy_process, (unsigned long)tbl); skb_queue_head_init_class(&tbl->proxy_queue, diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index e1e0a4e8fd34..7b55c8648d4b 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -194,7 +194,7 @@ void __init inet_initpeers(void) 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL); - INIT_DELAYED_WORK_DEFERRABLE(&gc_work, inetpeer_gc_worker); + INIT_DEFERRABLE_WORK(&gc_work, inetpeer_gc_worker); } static int addr_compare(const struct inetpeer_addr *a, diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 2afd2a84dc35..2a68bb3db772 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -1635,7 +1635,7 @@ static int create_cache_proc_entries(struct cache_detail *cd, struct net *net) void __init cache_initialize(void) { - INIT_DELAYED_WORK_DEFERRABLE(&cache_cleaner, do_cache_clean); + INIT_DEFERRABLE_WORK(&cache_cleaner, do_cache_clean); } int cache_register_net(struct cache_detail *cd, struct net *net) -- cgit v1.2.3 From f991b318cc6627a493b0d317a565bb7c3271f36b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 21 Aug 2012 13:18:23 -0700 Subject: workqueue: clean up delayed_work initializers and add missing one Reimplement delayed_work initializers using new timer initializers which take timer flags. This reduces code duplications and will ease further initializer changes. This patch also adds a missing initializer - INIT_DEFERRABLE_WORK_ONSTACK(). Signed-off-by: Tejun Heo --- include/linux/workqueue.h | 48 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 49a9c51f9ee3..e84ebb69607d 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -133,26 +133,20 @@ struct execute_work { __WORK_INIT_LOCKDEP_MAP(#n, &(n)) \ } -#define __DELAYED_WORK_INITIALIZER(n, f) { \ +#define __DELAYED_WORK_INITIALIZER(n, f, tflags) { \ .work = __WORK_INITIALIZER((n).work, (f)), \ - .timer = TIMER_INITIALIZER(delayed_work_timer_fn, \ - 0, (unsigned long)&(n)), \ - } - -#define __DEFERRABLE_WORK_INITIALIZER(n, f) { \ - .work = __WORK_INITIALIZER((n).work, (f)), \ - .timer = TIMER_DEFERRED_INITIALIZER(delayed_work_timer_fn, \ - 0, (unsigned long)&(n)), \ + .timer = __TIMER_INITIALIZER(delayed_work_timer_fn, \ + 0, (unsigned long)&(n), (tflags)), \ } #define DECLARE_WORK(n, f) \ struct work_struct n = __WORK_INITIALIZER(n, f) #define DECLARE_DELAYED_WORK(n, f) \ - struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f) + struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f, 0) #define DECLARE_DEFERRABLE_WORK(n, f) \ - struct delayed_work n = __DEFERRABLE_WORK_INITIALIZER(n, f) + struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f, TIMER_DEFERRABLE) /* * initialize a work item's function pointer @@ -216,29 +210,33 @@ static inline unsigned int work_static(struct work_struct *work) { return 0; } __INIT_WORK((_work), (_func), 1); \ } while (0) -#define INIT_DELAYED_WORK(_work, _func) \ +#define __INIT_DELAYED_WORK(_work, _func, _tflags) \ do { \ INIT_WORK(&(_work)->work, (_func)); \ - init_timer(&(_work)->timer); \ - (_work)->timer.function = delayed_work_timer_fn; \ - (_work)->timer.data = (unsigned long)(_work); \ + __setup_timer(&(_work)->timer, delayed_work_timer_fn, \ + (unsigned long)(_work), (_tflags)); \ } while (0) -#define INIT_DELAYED_WORK_ONSTACK(_work, _func) \ +#define __INIT_DELAYED_WORK_ONSTACK(_work, _func, _tflags) \ do { \ INIT_WORK_ONSTACK(&(_work)->work, (_func)); \ - init_timer_on_stack(&(_work)->timer); \ - (_work)->timer.function = delayed_work_timer_fn; \ - (_work)->timer.data = (unsigned long)(_work); \ + __setup_timer_on_stack(&(_work)->timer, \ + delayed_work_timer_fn, \ + (unsigned long)(_work), \ + (_tflags)); \ } while (0) +#define INIT_DELAYED_WORK(_work, _func) \ + __INIT_DELAYED_WORK(_work, _func, 0) + +#define INIT_DELAYED_WORK_ONSTACK(_work, _func) \ + __INIT_DELAYED_WORK_ONSTACK(_work, _func, 0) + #define INIT_DEFERRABLE_WORK(_work, _func) \ - do { \ - INIT_WORK(&(_work)->work, (_func)); \ - init_timer_deferrable(&(_work)->timer); \ - (_work)->timer.function = delayed_work_timer_fn; \ - (_work)->timer.data = (unsigned long)(_work); \ - } while (0) + __INIT_DELAYED_WORK(_work, _func, TIMER_DEFERRABLE) + +#define INIT_DEFERRABLE_WORK_ONSTACK(_work, _func) \ + __INIT_DELAYED_WORK_ONSTACK(_work, _func, TIMER_DEFERRABLE) /** * work_pending - Find out whether a work item is currently pending -- cgit v1.2.3 From e0aecdd874d78b7129a64b056c20e529e2c916df Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 21 Aug 2012 13:18:24 -0700 Subject: workqueue: use irqsafe timer for delayed_work Up to now, for delayed_works, try_to_grab_pending() couldn't be used from IRQ handlers because IRQs may happen while delayed_work_timer_fn() is in progress leading to indefinite -EAGAIN. This patch makes delayed_work use the new TIMER_IRQSAFE flag for delayed_work->timer. This makes try_to_grab_pending() and thus mod_delayed_work_on() safe to call from IRQ handlers. Signed-off-by: Tejun Heo --- include/linux/workqueue.h | 8 +++++--- kernel/workqueue.c | 20 +++++++++++--------- 2 files changed, 16 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index e84ebb69607d..d86b320319e0 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -136,7 +136,8 @@ struct execute_work { #define __DELAYED_WORK_INITIALIZER(n, f, tflags) { \ .work = __WORK_INITIALIZER((n).work, (f)), \ .timer = __TIMER_INITIALIZER(delayed_work_timer_fn, \ - 0, (unsigned long)&(n), (tflags)), \ + 0, (unsigned long)&(n), \ + (tflags) | TIMER_IRQSAFE), \ } #define DECLARE_WORK(n, f) \ @@ -214,7 +215,8 @@ static inline unsigned int work_static(struct work_struct *work) { return 0; } do { \ INIT_WORK(&(_work)->work, (_func)); \ __setup_timer(&(_work)->timer, delayed_work_timer_fn, \ - (unsigned long)(_work), (_tflags)); \ + (unsigned long)(_work), \ + (_tflags) | TIMER_IRQSAFE); \ } while (0) #define __INIT_DELAYED_WORK_ONSTACK(_work, _func, _tflags) \ @@ -223,7 +225,7 @@ static inline unsigned int work_static(struct work_struct *work) { return 0; } __setup_timer_on_stack(&(_work)->timer, \ delayed_work_timer_fn, \ (unsigned long)(_work), \ - (_tflags)); \ + (_tflags) | TIMER_IRQSAFE); \ } while (0) #define INIT_DELAYED_WORK(_work, _func) \ diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 85bd3409b9f5..b394df8beaee 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -1048,16 +1048,14 @@ static void cwq_dec_nr_in_flight(struct cpu_workqueue_struct *cwq, int color, * for arbitrarily long * * On >= 0 return, the caller owns @work's PENDING bit. To avoid getting - * preempted while holding PENDING and @work off queue, preemption must be - * disabled on entry. This ensures that we don't return -EAGAIN while - * another task is preempted in this function. + * interrupted while holding PENDING and @work off queue, irq must be + * disabled on entry. This, combined with delayed_work->timer being + * irqsafe, ensures that we return -EAGAIN for finite short period of time. * * On successful return, >= 0, irq is disabled and the caller is * responsible for releasing it using local_irq_restore(*@flags). * - * This function is safe to call from any context other than IRQ handler. - * An IRQ handler may run on top of delayed_work_timer_fn() which can make - * this function return -EAGAIN perpetually. + * This function is safe to call from any context including IRQ handler. */ static int try_to_grab_pending(struct work_struct *work, bool is_dwork, unsigned long *flags) @@ -1072,6 +1070,11 @@ static int try_to_grab_pending(struct work_struct *work, bool is_dwork, if (is_dwork) { struct delayed_work *dwork = to_delayed_work(work); + /* + * dwork->timer is irqsafe. If del_timer() fails, it's + * guaranteed that the timer is not queued anywhere and not + * running on the local CPU. + */ if (likely(del_timer(&dwork->timer))) return 1; } @@ -1327,9 +1330,8 @@ void delayed_work_timer_fn(unsigned long __data) struct delayed_work *dwork = (struct delayed_work *)__data; struct cpu_workqueue_struct *cwq = get_work_cwq(&dwork->work); - local_irq_disable(); + /* should have been called from irqsafe timer with irq already off */ __queue_work(dwork->cpu, cwq->wq, &dwork->work); - local_irq_enable(); } EXPORT_SYMBOL_GPL(delayed_work_timer_fn); @@ -1444,7 +1446,7 @@ EXPORT_SYMBOL_GPL(queue_delayed_work); * Returns %false if @dwork was idle and queued, %true if @dwork was * pending and its timer was modified. * - * This function is safe to call from any context other than IRQ handler. + * This function is safe to call from any context including IRQ handler. * See try_to_grab_pending() for details. */ bool mod_delayed_work_on(int cpu, struct workqueue_struct *wq, -- cgit v1.2.3 From 57b30ae77bf00d2318df711ef9a4d2a9be0a3a2a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 21 Aug 2012 13:18:24 -0700 Subject: workqueue: reimplement cancel_delayed_work() using try_to_grab_pending() cancel_delayed_work() can't be called from IRQ handlers due to its use of del_timer_sync() and can't cancel work items which are already transferred from timer to worklist. Also, unlike other flush and cancel functions, a canceled delayed_work would still point to the last associated cpu_workqueue. If the workqueue is destroyed afterwards and the work item is re-used on a different workqueue, the queueing code can oops trying to dereference already freed cpu_workqueue. This patch reimplements cancel_delayed_work() using try_to_grab_pending() and set_work_cpu_and_clear_pending(). This allows the function to be called from IRQ handlers and makes its behavior consistent with other flush / cancel functions. Signed-off-by: Tejun Heo Cc: Linus Torvalds Cc: Ingo Molnar Cc: Andrew Morton --- include/linux/workqueue.h | 17 +---------------- kernel/workqueue.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index d86b320319e0..4898289564ab 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -420,6 +420,7 @@ extern bool flush_work(struct work_struct *work); extern bool cancel_work_sync(struct work_struct *work); extern bool flush_delayed_work(struct delayed_work *dwork); +extern bool cancel_delayed_work(struct delayed_work *dwork); extern bool cancel_delayed_work_sync(struct delayed_work *dwork); extern void workqueue_set_max_active(struct workqueue_struct *wq, @@ -428,22 +429,6 @@ extern bool workqueue_congested(unsigned int cpu, struct workqueue_struct *wq); extern unsigned int work_cpu(struct work_struct *work); extern unsigned int work_busy(struct work_struct *work); -/* - * Kill off a pending schedule_delayed_work(). Note that the work callback - * function may still be running on return from cancel_delayed_work(), unless - * it returns 1 and the work doesn't re-arm itself. Run flush_workqueue() or - * cancel_work_sync() to wait on it. - */ -static inline bool cancel_delayed_work(struct delayed_work *work) -{ - bool ret; - - ret = del_timer_sync(&work->timer); - if (ret) - work_clear_pending(&work->work); - return ret; -} - /* * Like above, but uses del_timer() instead of del_timer_sync(). This means, * if it returns 0 the timer function may be running and the queueing is in diff --git a/kernel/workqueue.c b/kernel/workqueue.c index b394df8beaee..039d0fae171a 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -2948,6 +2948,36 @@ bool flush_delayed_work(struct delayed_work *dwork) } EXPORT_SYMBOL(flush_delayed_work); +/** + * cancel_delayed_work - cancel a delayed work + * @dwork: delayed_work to cancel + * + * Kill off a pending delayed_work. Returns %true if @dwork was pending + * and canceled; %false if wasn't pending. Note that the work callback + * function may still be running on return, unless it returns %true and the + * work doesn't re-arm itself. Explicitly flush or use + * cancel_delayed_work_sync() to wait on it. + * + * This function is safe to call from any context including IRQ handler. + */ +bool cancel_delayed_work(struct delayed_work *dwork) +{ + unsigned long flags; + int ret; + + do { + ret = try_to_grab_pending(&dwork->work, true, &flags); + } while (unlikely(ret == -EAGAIN)); + + if (unlikely(ret < 0)) + return false; + + set_work_cpu_and_clear_pending(&dwork->work, work_cpu(&dwork->work)); + local_irq_restore(flags); + return true; +} +EXPORT_SYMBOL(cancel_delayed_work); + /** * cancel_delayed_work_sync - cancel a delayed work and wait for it to finish * @dwork: the delayed work cancel -- cgit v1.2.3 From 136b5721d75a62a8f02c601c89122e32c1a85a84 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 21 Aug 2012 13:18:24 -0700 Subject: workqueue: deprecate __cancel_delayed_work() Now that cancel_delayed_work() can be safely called from IRQ handlers, there's no reason to use __cancel_delayed_work(). Use cancel_delayed_work() instead of __cancel_delayed_work() and mark the latter deprecated. Signed-off-by: Tejun Heo Acked-by: Jens Axboe Cc: Jiri Kosina Cc: Roland Dreier Cc: Tomi Valkeinen --- block/blk-core.c | 2 +- drivers/block/floppy.c | 2 +- drivers/infiniband/core/mad.c | 2 +- drivers/video/omap2/dss/dsi.c | 2 +- include/linux/workqueue.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index 4b8b606dbb01..dc04a9013027 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -262,7 +262,7 @@ EXPORT_SYMBOL(blk_start_queue); **/ void blk_stop_queue(struct request_queue *q) { - __cancel_delayed_work(&q->delay_work); + cancel_delayed_work(&q->delay_work); queue_flag_set(QUEUE_FLAG_STOPPED, q); } EXPORT_SYMBOL(blk_stop_queue); diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 55a5bc002c06..17c675c52295 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -890,7 +890,7 @@ static void unlock_fdc(void) raw_cmd = NULL; command_status = FD_COMMAND_NONE; - __cancel_delayed_work(&fd_timeout); + cancel_delayed_work(&fd_timeout); do_floppy = NULL; cont = NULL; clear_bit(0, &fdc_busy); diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c index b5938147fc89..dc3fd1e8af07 100644 --- a/drivers/infiniband/core/mad.c +++ b/drivers/infiniband/core/mad.c @@ -2004,7 +2004,7 @@ static void adjust_timeout(struct ib_mad_agent_private *mad_agent_priv) unsigned long delay; if (list_empty(&mad_agent_priv->wait_list)) { - __cancel_delayed_work(&mad_agent_priv->timed_work); + cancel_delayed_work(&mad_agent_priv->timed_work); } else { mad_send_wr = list_entry(mad_agent_priv->wait_list.next, struct ib_mad_send_wr_private, diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index fd40f2625051..05ee04667af1 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c @@ -4306,7 +4306,7 @@ static void dsi_framedone_irq_callback(void *data, u32 mask) * and is sending the data. */ - __cancel_delayed_work(&dsi->framedone_timeout_work); + cancel_delayed_work(&dsi->framedone_timeout_work); dsi_handle_framedone(dsidev, 0); } diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 4898289564ab..2b58905d3504 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -434,7 +434,7 @@ extern unsigned int work_busy(struct work_struct *work); * if it returns 0 the timer function may be running and the queueing is in * progress. */ -static inline bool __cancel_delayed_work(struct delayed_work *work) +static inline bool __deprecated __cancel_delayed_work(struct delayed_work *work) { bool ret; -- cgit v1.2.3 From af74115eed22698f771fec1287a864975c9a6671 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Wed, 15 Aug 2012 21:24:52 -0700 Subject: target: Remove unused se_cmd.cmd_spdtl This was originally for helping fabrics to determine overflow/underflow status, and has been superceeded by SCF_OVERFLOW_BIT + SCF_UNDERFLOW_BIT. Signed-off-by: Roland Dreier Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_transport.c | 2 -- include/target/target_core_base.h | 2 -- 2 files changed, 4 deletions(-) (limited to 'include') diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index ea9a3d2e4f55..4de3186dc44e 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -1165,8 +1165,6 @@ int target_cmd_size_check(struct se_cmd *cmd, unsigned int size) " 0x%02x\n", cmd->se_tfo->get_fabric_name(), cmd->data_length, size, cmd->t_task_cdb[0]); - cmd->cmd_spdtl = size; - if (cmd->data_direction == DMA_TO_DEVICE) { pr_err("Rejecting underflow/overflow" " WRITE data\n"); diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 128ce46fa48a..015cea01ae39 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -503,8 +503,6 @@ struct se_cmd { u32 se_ordered_id; /* Total size in bytes associated with command */ u32 data_length; - /* SCSI Presented Data Transfer Length */ - u32 cmd_spdtl; u32 residual_count; u32 orig_fe_lun; /* Persistent Reservation key */ -- cgit v1.2.3 From e0e3cea46d31d23dc40df0a49a7a2c04fe8edfea Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 21 Aug 2012 06:21:17 +0000 Subject: af_netlink: force credentials passing [CVE-2012-3520] Pablo Neira Ayuso discovered that avahi and potentially NetworkManager accept spoofed Netlink messages because of a kernel bug. The kernel passes all-zero SCM_CREDENTIALS ancillary data to the receiver if the sender did not provide such data, instead of not including any such data at all or including the correct data from the peer (as it is the case with AF_UNIX). This bug was introduced in commit 16e572626961 (af_unix: dont send SCM_CREDENTIALS by default) This patch forces passing credentials for netlink, as before the regression. Another fix would be to not add SCM_CREDENTIALS in netlink messages if not provided by the sender, but it might break some programs. With help from Florian Weimer & Petr Matousek This issue is designated as CVE-2012-3520 Signed-off-by: Eric Dumazet Cc: Petr Matousek Cc: Florian Weimer Cc: Pablo Neira Ayuso Signed-off-by: David S. Miller --- include/net/scm.h | 4 +++- net/netlink/af_netlink.c | 2 +- net/unix/af_unix.c | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/scm.h b/include/net/scm.h index 079d7887dac1..7dc0854f0b38 100644 --- a/include/net/scm.h +++ b/include/net/scm.h @@ -70,9 +70,11 @@ static __inline__ void scm_destroy(struct scm_cookie *scm) } static __inline__ int scm_send(struct socket *sock, struct msghdr *msg, - struct scm_cookie *scm) + struct scm_cookie *scm, bool forcecreds) { memset(scm, 0, sizeof(*scm)); + if (forcecreds) + scm_set_cred(scm, task_tgid(current), current_cred()); unix_get_peersec_dgram(sock, scm); if (msg->msg_controllen <= 0) return 0; diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 5463969da45b..1445d73533ed 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1362,7 +1362,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, if (NULL == siocb->scm) siocb->scm = &scm; - err = scm_send(sock, msg, siocb->scm); + err = scm_send(sock, msg, siocb->scm, true); if (err < 0) return err; diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index e4768c180da2..c5ee4ff61364 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1450,7 +1450,7 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock, if (NULL == siocb->scm) siocb->scm = &tmp_scm; wait_for_unix_gc(); - err = scm_send(sock, msg, siocb->scm); + err = scm_send(sock, msg, siocb->scm, false); if (err < 0) return err; @@ -1619,7 +1619,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, if (NULL == siocb->scm) siocb->scm = &tmp_scm; wait_for_unix_gc(); - err = scm_send(sock, msg, siocb->scm); + err = scm_send(sock, msg, siocb->scm, false); if (err < 0) return err; -- cgit v1.2.3 From 04ccfe77f132b973659f11954443214659014072 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Fri, 17 Aug 2012 14:20:02 +0000 Subject: drm: Remove two unused fields from struct drm_display_mode Signed-off-by: Damien Lespiau Reviewed-by: Jani Nikula Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_modes.c | 3 --- include/drm/drm_crtc.h | 2 -- 2 files changed, 5 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index b7adb4a967fd..28637c181b15 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -706,9 +706,6 @@ void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags) p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal); p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay); p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal); - - p->crtc_hadjusted = false; - p->crtc_vadjusted = false; } EXPORT_SYMBOL(drm_mode_set_crtcinfo); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index a1a0386e0160..ced362533e3c 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -166,8 +166,6 @@ struct drm_display_mode { int crtc_vsync_start; int crtc_vsync_end; int crtc_vtotal; - int crtc_hadjusted; - int crtc_vadjusted; /* Driver private mode info */ int private_size; -- cgit v1.2.3 From c3a5ce0416b6c172a23bc8a3760d8704d3d1535b Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Tue, 21 Aug 2012 16:16:00 -0700 Subject: string: do not export memweight() to userspace Fix the following warning: usr/include/linux/string.h:8: userspace cannot reference function or variable defined in the kernel Signed-off-by: WANG Cong Acked-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/string.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/string.h b/include/linux/string.h index ffe0442e18d2..b9178812d9df 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -144,8 +144,8 @@ static inline bool strstarts(const char *str, const char *prefix) { return strncmp(str, prefix, strlen(prefix)) == 0; } -#endif extern size_t memweight(const void *ptr, size_t bytes); +#endif /* __KERNEL__ */ #endif /* _LINUX_STRING_H_ */ -- cgit v1.2.3 From c67fe3752abe6ab47639e2f9b836900c3dc3da84 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 21 Aug 2012 16:16:17 -0700 Subject: mm: compaction: Abort async compaction if locks are contended or taking too long Jim Schutt reported a problem that pointed at compaction contending heavily on locks. The workload is straight-forward and in his own words; The systems in question have 24 SAS drives spread across 3 HBAs, running 24 Ceph OSD instances, one per drive. FWIW these servers are dual-socket Intel 5675 Xeons w/48 GB memory. I've got ~160 Ceph Linux clients doing dd simultaneously to a Ceph file system backed by 12 of these servers. Early in the test everything looks fine procs -------------------memory------------------ ---swap-- -----io---- --system-- -----cpu------- r b swpd free buff cache si so bi bo in cs us sy id wa st 31 15 0 287216 576 38606628 0 0 2 1158 2 14 1 3 95 0 0 27 15 0 225288 576 38583384 0 0 18 2222016 203357 134876 11 56 17 15 0 28 17 0 219256 576 38544736 0 0 11 2305932 203141 146296 11 49 23 17 0 6 18 0 215596 576 38552872 0 0 7 2363207 215264 166502 12 45 22 20 0 22 18 0 226984 576 38596404 0 0 3 2445741 223114 179527 12 43 23 22 0 and then it goes to pot procs -------------------memory------------------ ---swap-- -----io---- --system-- -----cpu------- r b swpd free buff cache si so bi bo in cs us sy id wa st 163 8 0 464308 576 36791368 0 0 11 22210 866 536 3 13 79 4 0 207 14 0 917752 576 36181928 0 0 712 1345376 134598 47367 7 90 1 2 0 123 12 0 685516 576 36296148 0 0 429 1386615 158494 60077 8 84 5 3 0 123 12 0 598572 576 36333728 0 0 1107 1233281 147542 62351 7 84 5 4 0 622 7 0 660768 576 36118264 0 0 557 1345548 151394 59353 7 85 4 3 0 223 11 0 283960 576 36463868 0 0 46 1107160 121846 33006 6 93 1 1 0 Note that system CPU usage is very high blocks being written out has dropped by 42%. He analysed this with perf and found perf record -g -a sleep 10 perf report --sort symbol --call-graph fractal,5 34.63% [k] _raw_spin_lock_irqsave | |--97.30%-- isolate_freepages | compaction_alloc | unmap_and_move | migrate_pages | compact_zone | compact_zone_order | try_to_compact_pages | __alloc_pages_direct_compact | __alloc_pages_slowpath | __alloc_pages_nodemask | alloc_pages_vma | do_huge_pmd_anonymous_page | handle_mm_fault | do_page_fault | page_fault | | | |--87.39%-- skb_copy_datagram_iovec | | tcp_recvmsg | | inet_recvmsg | | sock_recvmsg | | sys_recvfrom | | system_call | | __recv | | | | | --100.00%-- (nil) | | | --12.61%-- memcpy --2.70%-- [...] There was other data but primarily it is all showing that compaction is contended heavily on the zone->lock and zone->lru_lock. commit [b2eef8c0: mm: compaction: minimise the time IRQs are disabled while isolating pages for migration] noted that it was possible for migration to hold the lru_lock for an excessive amount of time. Very broadly speaking this patch expands the concept. This patch introduces compact_checklock_irqsave() to check if a lock is contended or the process needs to be scheduled. If either condition is true then async compaction is aborted and the caller is informed. The page allocator will fail a THP allocation if compaction failed due to contention. This patch also introduces compact_trylock_irqsave() which will acquire the lock only if it is not contended and the process does not need to schedule. Reported-by: Jim Schutt Tested-by: Jim Schutt Signed-off-by: Mel Gorman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/compaction.h | 4 +- mm/compaction.c | 100 +++++++++++++++++++++++++++++++++++---------- mm/internal.h | 1 + mm/page_alloc.c | 17 +++++--- 4 files changed, 93 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/linux/compaction.h b/include/linux/compaction.h index 133ddcf83397..ef658147e4e8 100644 --- a/include/linux/compaction.h +++ b/include/linux/compaction.h @@ -22,7 +22,7 @@ extern int sysctl_extfrag_handler(struct ctl_table *table, int write, extern int fragmentation_index(struct zone *zone, unsigned int order); extern unsigned long try_to_compact_pages(struct zonelist *zonelist, int order, gfp_t gfp_mask, nodemask_t *mask, - bool sync); + bool sync, bool *contended); extern int compact_pgdat(pg_data_t *pgdat, int order); extern unsigned long compaction_suitable(struct zone *zone, int order); @@ -64,7 +64,7 @@ static inline bool compaction_deferred(struct zone *zone, int order) #else static inline unsigned long try_to_compact_pages(struct zonelist *zonelist, int order, gfp_t gfp_mask, nodemask_t *nodemask, - bool sync) + bool sync, bool *contended) { return COMPACT_CONTINUE; } diff --git a/mm/compaction.c b/mm/compaction.c index bcce7897e17a..7fcd3a52e68d 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -50,6 +50,47 @@ static inline bool migrate_async_suitable(int migratetype) return is_migrate_cma(migratetype) || migratetype == MIGRATE_MOVABLE; } +/* + * Compaction requires the taking of some coarse locks that are potentially + * very heavily contended. Check if the process needs to be scheduled or + * if the lock is contended. For async compaction, back out in the event + * if contention is severe. For sync compaction, schedule. + * + * Returns true if the lock is held. + * Returns false if the lock is released and compaction should abort + */ +static bool compact_checklock_irqsave(spinlock_t *lock, unsigned long *flags, + bool locked, struct compact_control *cc) +{ + if (need_resched() || spin_is_contended(lock)) { + if (locked) { + spin_unlock_irqrestore(lock, *flags); + locked = false; + } + + /* async aborts if taking too long or contended */ + if (!cc->sync) { + if (cc->contended) + *cc->contended = true; + return false; + } + + cond_resched(); + if (fatal_signal_pending(current)) + return false; + } + + if (!locked) + spin_lock_irqsave(lock, *flags); + return true; +} + +static inline bool compact_trylock_irqsave(spinlock_t *lock, + unsigned long *flags, struct compact_control *cc) +{ + return compact_checklock_irqsave(lock, flags, false, cc); +} + /* * Isolate free pages onto a private freelist. Caller must hold zone->lock. * If @strict is true, will abort returning 0 on any invalid PFNs or non-free @@ -173,7 +214,7 @@ isolate_freepages_range(unsigned long start_pfn, unsigned long end_pfn) } /* Update the number of anon and file isolated pages in the zone */ -static void acct_isolated(struct zone *zone, struct compact_control *cc) +static void acct_isolated(struct zone *zone, bool locked, struct compact_control *cc) { struct page *page; unsigned int count[2] = { 0, }; @@ -181,8 +222,14 @@ static void acct_isolated(struct zone *zone, struct compact_control *cc) list_for_each_entry(page, &cc->migratepages, lru) count[!!page_is_file_cache(page)]++; - __mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]); - __mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]); + /* If locked we can use the interrupt unsafe versions */ + if (locked) { + __mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]); + __mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]); + } else { + mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]); + mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]); + } } /* Similar to reclaim, but different enough that they don't share logic */ @@ -228,6 +275,8 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc, struct list_head *migratelist = &cc->migratepages; isolate_mode_t mode = 0; struct lruvec *lruvec; + unsigned long flags; + bool locked; /* * Ensure that there are not too many pages isolated from the LRU @@ -247,25 +296,22 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc, /* Time to isolate some pages for migration */ cond_resched(); - spin_lock_irq(&zone->lru_lock); + spin_lock_irqsave(&zone->lru_lock, flags); + locked = true; for (; low_pfn < end_pfn; low_pfn++) { struct page *page; - bool locked = true; /* give a chance to irqs before checking need_resched() */ if (!((low_pfn+1) % SWAP_CLUSTER_MAX)) { - spin_unlock_irq(&zone->lru_lock); + spin_unlock_irqrestore(&zone->lru_lock, flags); locked = false; } - if (need_resched() || spin_is_contended(&zone->lru_lock)) { - if (locked) - spin_unlock_irq(&zone->lru_lock); - cond_resched(); - spin_lock_irq(&zone->lru_lock); - if (fatal_signal_pending(current)) - break; - } else if (!locked) - spin_lock_irq(&zone->lru_lock); + + /* Check if it is ok to still hold the lock */ + locked = compact_checklock_irqsave(&zone->lru_lock, &flags, + locked, cc); + if (!locked) + break; /* * migrate_pfn does not necessarily start aligned to a @@ -349,9 +395,10 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc, } } - acct_isolated(zone, cc); + acct_isolated(zone, locked, cc); - spin_unlock_irq(&zone->lru_lock); + if (locked) + spin_unlock_irqrestore(&zone->lru_lock, flags); trace_mm_compaction_isolate_migratepages(nr_scanned, nr_isolated); @@ -461,7 +508,16 @@ static void isolate_freepages(struct zone *zone, * are disabled */ isolated = 0; - spin_lock_irqsave(&zone->lock, flags); + + /* + * The zone lock must be held to isolate freepages. This + * unfortunately this is a very coarse lock and can be + * heavily contended if there are parallel allocations + * or parallel compactions. For async compaction do not + * spin on the lock + */ + if (!compact_trylock_irqsave(&zone->lock, &flags, cc)) + break; if (suitable_migration_target(page)) { end_pfn = min(pfn + pageblock_nr_pages, zone_end_pfn); isolated = isolate_freepages_block(pfn, end_pfn, @@ -773,7 +829,7 @@ out: static unsigned long compact_zone_order(struct zone *zone, int order, gfp_t gfp_mask, - bool sync) + bool sync, bool *contended) { struct compact_control cc = { .nr_freepages = 0, @@ -782,6 +838,7 @@ static unsigned long compact_zone_order(struct zone *zone, .migratetype = allocflags_to_migratetype(gfp_mask), .zone = zone, .sync = sync, + .contended = contended, }; INIT_LIST_HEAD(&cc.freepages); INIT_LIST_HEAD(&cc.migratepages); @@ -803,7 +860,7 @@ int sysctl_extfrag_threshold = 500; */ unsigned long try_to_compact_pages(struct zonelist *zonelist, int order, gfp_t gfp_mask, nodemask_t *nodemask, - bool sync) + bool sync, bool *contended) { enum zone_type high_zoneidx = gfp_zone(gfp_mask); int may_enter_fs = gfp_mask & __GFP_FS; @@ -827,7 +884,8 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist, nodemask) { int status; - status = compact_zone_order(zone, order, gfp_mask, sync); + status = compact_zone_order(zone, order, gfp_mask, sync, + contended); rc = max(status, rc); /* If a normal allocation would succeed, stop compacting */ diff --git a/mm/internal.h b/mm/internal.h index 3314f79d775a..b8c91b342e24 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -130,6 +130,7 @@ struct compact_control { int order; /* order a direct compactor needs */ int migratetype; /* MOVABLE, RECLAIMABLE etc */ struct zone *zone; + bool *contended; /* True if a lock was contended */ }; unsigned long diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 07f19248acb5..c66fb875104a 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2102,7 +2102,7 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, struct zonelist *zonelist, enum zone_type high_zoneidx, nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone, int migratetype, bool sync_migration, - bool *deferred_compaction, + bool *contended_compaction, bool *deferred_compaction, unsigned long *did_some_progress) { struct page *page; @@ -2117,7 +2117,8 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, current->flags |= PF_MEMALLOC; *did_some_progress = try_to_compact_pages(zonelist, order, gfp_mask, - nodemask, sync_migration); + nodemask, sync_migration, + contended_compaction); current->flags &= ~PF_MEMALLOC; if (*did_some_progress != COMPACT_SKIPPED) { @@ -2163,7 +2164,7 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, struct zonelist *zonelist, enum zone_type high_zoneidx, nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone, int migratetype, bool sync_migration, - bool *deferred_compaction, + bool *contended_compaction, bool *deferred_compaction, unsigned long *did_some_progress) { return NULL; @@ -2336,6 +2337,7 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, unsigned long did_some_progress; bool sync_migration = false; bool deferred_compaction = false; + bool contended_compaction = false; /* * In the slowpath, we sanity check order to avoid ever trying to @@ -2425,6 +2427,7 @@ rebalance: nodemask, alloc_flags, preferred_zone, migratetype, sync_migration, + &contended_compaction, &deferred_compaction, &did_some_progress); if (page) @@ -2434,10 +2437,11 @@ rebalance: /* * If compaction is deferred for high-order allocations, it is because * sync compaction recently failed. In this is the case and the caller - * has requested the system not be heavily disrupted, fail the - * allocation now instead of entering direct reclaim + * requested a movable allocation that does not heavily disrupt the + * system then fail the allocation instead of entering direct reclaim. */ - if (deferred_compaction && (gfp_mask & __GFP_NO_KSWAPD)) + if ((deferred_compaction || contended_compaction) && + (gfp_mask & __GFP_NO_KSWAPD)) goto nopage; /* Try direct reclaim and then allocating */ @@ -2508,6 +2512,7 @@ rebalance: nodemask, alloc_flags, preferred_zone, migratetype, sync_migration, + &contended_compaction, &deferred_compaction, &did_some_progress); if (page) -- cgit v1.2.3 From c5b3533a82ef4b6ceae81b7675f8d6dadcc6f3ab Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 29 Jul 2012 22:48:32 -0700 Subject: Input: uinput - specify exact bit sizes on userspace APIs Switch to using __u32/__s32 instead of ordinary 'int' in structures forming userspace API. Also internally make request_id unsigned int. Signed-off-by: Dmitry Torokhov --- drivers/input/misc/uinput.c | 11 ++++++----- include/linux/uinput.h | 27 ++++++++++++++------------- 2 files changed, 20 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 6099365102db..b247e1c8e8f6 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -59,7 +59,7 @@ static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned i static bool uinput_request_alloc_id(struct uinput_device *udev, struct uinput_request *request) { - int id; + unsigned int id; bool reserved = false; spin_lock(&udev->requests_lock); @@ -77,10 +77,11 @@ static bool uinput_request_alloc_id(struct uinput_device *udev, return reserved; } -static struct uinput_request *uinput_request_find(struct uinput_device *udev, int id) +static struct uinput_request *uinput_request_find(struct uinput_device *udev, + unsigned int id) { /* Find an input request, by ID. Returns NULL if the ID isn't valid. */ - if (id >= UINPUT_NUM_REQUESTS || id < 0) + if (id >= UINPUT_NUM_REQUESTS) return NULL; return udev->requests[id]; @@ -556,8 +557,8 @@ static int uinput_release(struct inode *inode, struct file *file) #ifdef CONFIG_COMPAT struct uinput_ff_upload_compat { - int request_id; - int retval; + __u32 request_id; + __s32 retval; struct ff_effect_compat effect; struct ff_effect_compat old; }; diff --git a/include/linux/uinput.h b/include/linux/uinput.h index 2aa2881b0df9..c454bbe39ee7 100644 --- a/include/linux/uinput.h +++ b/include/linux/uinput.h @@ -32,6 +32,7 @@ * - first public version */ +#include #include #define UINPUT_VERSION 3 @@ -44,14 +45,14 @@ enum uinput_state { UIST_NEW_DEVICE, UIST_SETUP_COMPLETE, UIST_CREATED }; struct uinput_request { - int id; - int code; /* UI_FF_UPLOAD, UI_FF_ERASE */ + unsigned int id; + unsigned int code; /* UI_FF_UPLOAD, UI_FF_ERASE */ int retval; struct completion done; union { - int effect_id; + unsigned int effect_id; struct { struct ff_effect *effect; struct ff_effect *old; @@ -77,16 +78,16 @@ struct uinput_device { #endif /* __KERNEL__ */ struct uinput_ff_upload { - int request_id; - int retval; + __u32 request_id; + __s32 retval; struct ff_effect effect; struct ff_effect old; }; struct uinput_ff_erase { - int request_id; - int retval; - int effect_id; + __u32 request_id; + __s32 retval; + __u32 effect_id; }; /* ioctl */ @@ -166,11 +167,11 @@ struct uinput_ff_erase { struct uinput_user_dev { char name[UINPUT_MAX_NAME_SIZE]; struct input_id id; - int ff_effects_max; - int absmax[ABS_CNT]; - int absmin[ABS_CNT]; - int absfuzz[ABS_CNT]; - int absflat[ABS_CNT]; + __u32 ff_effects_max; + __s32 absmax[ABS_CNT]; + __s32 absmin[ABS_CNT]; + __s32 absfuzz[ABS_CNT]; + __s32 absflat[ABS_CNT]; }; #endif /* __UINPUT_H_ */ -- cgit v1.2.3 From 1922b0f2758badc0b971d1ebbd300bc6635a6aef Mon Sep 17 00:00:00 2001 From: AnilKumar Ch Date: Mon, 13 Aug 2012 20:36:05 +0530 Subject: mfd: Move tps65217 regulator plat data handling to regulator Regulator platform data handling was mistakenly added to MFD driver. So we will see build errors if we compile MFD drivers without CONFIG_REGULATOR. This patch moves regulator platform data handling from TPS65217 MFD driver to regulator driver. This makes MFD driver independent of REGULATOR framework so build error is fixed if CONFIG_REGULATOR is not set. drivers/built-in.o: In function `tps65217_probe': tps65217.c:(.devinit.text+0x13e37): undefined reference to `of_regulator_match' This patch also fix allocation size of tps65217 platform data. Current implementation allocates a struct tps65217_board for each regulator specified in the device tree. But the structure itself provides array of regulators so one instance of it is sufficient. Signed-off-by: AnilKumar Ch --- drivers/mfd/tps65217.c | 130 +++++++++++---------------------- drivers/regulator/tps65217-regulator.c | 124 +++++++++++++++++++++++++++---- include/linux/mfd/tps65217.h | 12 ++- 3 files changed, 161 insertions(+), 105 deletions(-) (limited to 'include') diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c index 61c097a98f5d..3bc274409b58 100644 --- a/drivers/mfd/tps65217.c +++ b/drivers/mfd/tps65217.c @@ -24,11 +24,18 @@ #include #include #include -#include +#include +#include #include #include +static struct mfd_cell tps65217s[] = { + { + .name = "tps65217-pmic", + }, +}; + /** * tps65217_reg_read: Read a single tps65217 register. * @@ -133,83 +140,48 @@ int tps65217_clear_bits(struct tps65217 *tps, unsigned int reg, } EXPORT_SYMBOL_GPL(tps65217_clear_bits); -#ifdef CONFIG_OF -static struct of_regulator_match reg_matches[] = { - { .name = "dcdc1", .driver_data = (void *)TPS65217_DCDC_1 }, - { .name = "dcdc2", .driver_data = (void *)TPS65217_DCDC_2 }, - { .name = "dcdc3", .driver_data = (void *)TPS65217_DCDC_3 }, - { .name = "ldo1", .driver_data = (void *)TPS65217_LDO_1 }, - { .name = "ldo2", .driver_data = (void *)TPS65217_LDO_2 }, - { .name = "ldo3", .driver_data = (void *)TPS65217_LDO_3 }, - { .name = "ldo4", .driver_data = (void *)TPS65217_LDO_4 }, -}; - -static struct tps65217_board *tps65217_parse_dt(struct i2c_client *client) -{ - struct device_node *node = client->dev.of_node; - struct tps65217_board *pdata; - struct device_node *regs; - int count = ARRAY_SIZE(reg_matches); - int ret, i; - - regs = of_find_node_by_name(node, "regulators"); - if (!regs) - return NULL; - - ret = of_regulator_match(&client->dev, regs, reg_matches, count); - of_node_put(regs); - if ((ret < 0) || (ret > count)) - return NULL; - - count = ret; - pdata = devm_kzalloc(&client->dev, count * sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return NULL; - - for (i = 0; i < count; i++) { - if (!reg_matches[i].init_data || !reg_matches[i].of_node) - continue; - - pdata->tps65217_init_data[i] = reg_matches[i].init_data; - pdata->of_node[i] = reg_matches[i].of_node; - } - - return pdata; -} - -static struct of_device_id tps65217_of_match[] = { - { .compatible = "ti,tps65217", }, - { }, -}; -#else -static struct tps65217_board *tps65217_parse_dt(struct i2c_client *client) -{ - return NULL; -} -#endif - static struct regmap_config tps65217_regmap_config = { .reg_bits = 8, .val_bits = 8, }; +static const struct of_device_id tps65217_of_match[] = { + { .compatible = "ti,tps65217", .data = (void *)TPS65217 }, + { /* sentinel */ }, +}; + static int __devinit tps65217_probe(struct i2c_client *client, const struct i2c_device_id *ids) { struct tps65217 *tps; - struct regulator_init_data *reg_data; - struct tps65217_board *pdata = client->dev.platform_data; - int i, ret; unsigned int version; + unsigned int chip_id = ids->driver_data; + const struct of_device_id *match; + int ret; - if (!pdata && client->dev.of_node) - pdata = tps65217_parse_dt(client); + if (client->dev.of_node) { + match = of_match_device(tps65217_of_match, &client->dev); + if (!match) { + dev_err(&client->dev, + "Failed to find matching dt id\n"); + return -EINVAL; + } + chip_id = (unsigned int)match->data; + } + + if (!chip_id) { + dev_err(&client->dev, "id is null.\n"); + return -ENODEV; + } tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); if (!tps) return -ENOMEM; - tps->pdata = pdata; + i2c_set_clientdata(client, tps); + tps->dev = &client->dev; + tps->id = chip_id; + tps->regmap = devm_regmap_init_i2c(client, &tps65217_regmap_config); if (IS_ERR(tps->regmap)) { ret = PTR_ERR(tps->regmap); @@ -218,8 +190,12 @@ static int __devinit tps65217_probe(struct i2c_client *client, return ret; } - i2c_set_clientdata(client, tps); - tps->dev = &client->dev; + ret = mfd_add_devices(tps->dev, -1, tps65217s, + ARRAY_SIZE(tps65217s), NULL, 0); + if (ret < 0) { + dev_err(tps->dev, "mfd_add_devices failed: %d\n", ret); + return ret; + } ret = tps65217_reg_read(tps, TPS65217_REG_CHIPID, &version); if (ret < 0) { @@ -232,41 +208,21 @@ static int __devinit tps65217_probe(struct i2c_client *client, (version & TPS65217_CHIPID_CHIP_MASK) >> 4, version & TPS65217_CHIPID_REV_MASK); - for (i = 0; i < TPS65217_NUM_REGULATOR; i++) { - struct platform_device *pdev; - - pdev = platform_device_alloc("tps65217-pmic", i); - if (!pdev) { - dev_err(tps->dev, "Cannot create regulator %d\n", i); - continue; - } - - pdev->dev.parent = tps->dev; - pdev->dev.of_node = pdata->of_node[i]; - reg_data = pdata->tps65217_init_data[i]; - platform_device_add_data(pdev, reg_data, sizeof(*reg_data)); - tps->regulator_pdev[i] = pdev; - - platform_device_add(pdev); - } - return 0; } static int __devexit tps65217_remove(struct i2c_client *client) { struct tps65217 *tps = i2c_get_clientdata(client); - int i; - for (i = 0; i < TPS65217_NUM_REGULATOR; i++) - platform_device_unregister(tps->regulator_pdev[i]); + mfd_remove_devices(tps->dev); return 0; } static const struct i2c_device_id tps65217_id_table[] = { - {"tps65217", 0xF0}, - {/* end of list */} + {"tps65217", TPS65217}, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, tps65217_id_table); diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index 6caa222af77a..ab00cab905b7 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -281,37 +282,130 @@ static const struct regulator_desc regulators[] = { NULL), }; +#ifdef CONFIG_OF +static struct of_regulator_match reg_matches[] = { + { .name = "dcdc1", .driver_data = (void *)TPS65217_DCDC_1 }, + { .name = "dcdc2", .driver_data = (void *)TPS65217_DCDC_2 }, + { .name = "dcdc3", .driver_data = (void *)TPS65217_DCDC_3 }, + { .name = "ldo1", .driver_data = (void *)TPS65217_LDO_1 }, + { .name = "ldo2", .driver_data = (void *)TPS65217_LDO_2 }, + { .name = "ldo3", .driver_data = (void *)TPS65217_LDO_3 }, + { .name = "ldo4", .driver_data = (void *)TPS65217_LDO_4 }, +}; + +static struct tps65217_board *tps65217_parse_dt(struct platform_device *pdev) +{ + struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent); + struct device_node *node = tps->dev->of_node; + struct tps65217_board *pdata; + struct device_node *regs; + int i, count; + + regs = of_find_node_by_name(node, "regulators"); + if (!regs) + return NULL; + + count = of_regulator_match(pdev->dev.parent, regs, + reg_matches, TPS65217_NUM_REGULATOR); + of_node_put(regs); + if ((count < 0) || (count > TPS65217_NUM_REGULATOR)) + return NULL; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + for (i = 0; i < count; i++) { + if (!reg_matches[i].init_data || !reg_matches[i].of_node) + continue; + + pdata->tps65217_init_data[i] = reg_matches[i].init_data; + pdata->of_node[i] = reg_matches[i].of_node; + } + + return pdata; +} +#else +static struct tps65217_board *tps65217_parse_dt(struct platform_device *pdev) +{ + return NULL; +} +#endif + static int __devinit tps65217_regulator_probe(struct platform_device *pdev) { + struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent); + struct tps65217_board *pdata = dev_get_platdata(tps->dev); + struct regulator_init_data *reg_data; struct regulator_dev *rdev; - struct tps65217 *tps; - struct tps_info *info = &tps65217_pmic_regs[pdev->id]; struct regulator_config config = { }; + int i, ret; - /* Already set by core driver */ - tps = dev_to_tps65217(pdev->dev.parent); - tps->info[pdev->id] = info; + if (tps->dev->of_node) + pdata = tps65217_parse_dt(pdev); - config.dev = &pdev->dev; - config.of_node = pdev->dev.of_node; - config.init_data = pdev->dev.platform_data; - config.driver_data = tps; + if (!pdata) { + dev_err(&pdev->dev, "Platform data not found\n"); + return -EINVAL; + } - rdev = regulator_register(®ulators[pdev->id], &config); - if (IS_ERR(rdev)) - return PTR_ERR(rdev); + if (tps65217_chip_id(tps) != TPS65217) { + dev_err(&pdev->dev, "Invalid tps chip version\n"); + return -ENODEV; + } - platform_set_drvdata(pdev, rdev); + platform_set_drvdata(pdev, tps); + for (i = 0; i < TPS65217_NUM_REGULATOR; i++) { + + reg_data = pdata->tps65217_init_data[i]; + + /* + * Regulator API handles empty constraints but not NULL + * constraints + */ + if (!reg_data) + continue; + + /* Register the regulators */ + tps->info[i] = &tps65217_pmic_regs[i]; + + config.dev = tps->dev; + config.init_data = reg_data; + config.driver_data = tps; + config.regmap = tps->regmap; + if (tps->dev->of_node) + config.of_node = pdata->of_node[i]; + + rdev = regulator_register(®ulators[i], &config); + if (IS_ERR(rdev)) { + dev_err(tps->dev, "failed to register %s regulator\n", + pdev->name); + ret = PTR_ERR(rdev); + goto err_unregister_regulator; + } + + /* Save regulator for cleanup */ + tps->rdev[i] = rdev; + } return 0; + +err_unregister_regulator: + while (--i >= 0) + regulator_unregister(tps->rdev[i]); + + return ret; } static int __devexit tps65217_regulator_remove(struct platform_device *pdev) { - struct regulator_dev *rdev = platform_get_drvdata(pdev); + struct tps65217 *tps = platform_get_drvdata(pdev); + unsigned int i; + + for (i = 0; i < TPS65217_NUM_REGULATOR; i++) + regulator_unregister(tps->rdev[i]); platform_set_drvdata(pdev, NULL); - regulator_unregister(rdev); return 0; } diff --git a/include/linux/mfd/tps65217.h b/include/linux/mfd/tps65217.h index 12c06870829a..7cd83d826ed8 100644 --- a/include/linux/mfd/tps65217.h +++ b/include/linux/mfd/tps65217.h @@ -22,6 +22,9 @@ #include #include +/* TPS chip id list */ +#define TPS65217 0xF0 + /* I2C ID for TPS65217 part */ #define TPS65217_I2C_ID 0x24 @@ -248,13 +251,11 @@ struct tps_info { struct tps65217 { struct device *dev; struct tps65217_board *pdata; + unsigned int id; struct regulator_desc desc[TPS65217_NUM_REGULATOR]; struct regulator_dev *rdev[TPS65217_NUM_REGULATOR]; struct tps_info *info[TPS65217_NUM_REGULATOR]; struct regmap *regmap; - - /* Client devices */ - struct platform_device *regulator_pdev[TPS65217_NUM_REGULATOR]; }; static inline struct tps65217 *dev_to_tps65217(struct device *dev) @@ -262,6 +263,11 @@ static inline struct tps65217 *dev_to_tps65217(struct device *dev) return dev_get_drvdata(dev); } +static inline int tps65217_chip_id(struct tps65217 *tps65217) +{ + return tps65217->id; +} + int tps65217_reg_read(struct tps65217 *tps, unsigned int reg, unsigned int *val); int tps65217_reg_write(struct tps65217 *tps, unsigned int reg, -- cgit v1.2.3 From 8ad5db8a8ddbe3bd33078863a027011e28f1f4ee Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 17 Aug 2012 20:10:46 -0400 Subject: introduce kref_put_mutex() equivalent of mutex_lock(mutex); if (!kref_put(kref, release)) mutex_unlock(mutex); Signed-off-by: Al Viro --- include/linux/kref.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'include') diff --git a/include/linux/kref.h b/include/linux/kref.h index 9c07dcebded7..65af6887872f 100644 --- a/include/linux/kref.h +++ b/include/linux/kref.h @@ -18,6 +18,7 @@ #include #include #include +#include struct kref { atomic_t refcount; @@ -93,4 +94,21 @@ static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref) { return kref_sub(kref, 1, release); } + +static inline int kref_put_mutex(struct kref *kref, + void (*release)(struct kref *kref), + struct mutex *lock) +{ + WARN_ON(release == NULL); + if (unlikely(!atomic_add_unless(&kref->refcount, -1, 1))) { + mutex_lock(lock); + if (unlikely(!atomic_dec_and_test(&kref->refcount))) { + mutex_unlock(lock); + return 0; + } + release(kref); + return 1; + } + return 0; +} #endif /* _KREF_H_ */ -- cgit v1.2.3 From 657c2077a2dab228fcf28a708df1b1bcf4195803 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 17 Aug 2012 10:07:00 -0600 Subject: PCI: Don't export stop_bus_device and remove_bus_device interfaces The acpiphp hotplug driver was the only user of pci_stop_bus_device() and __pci_remove_bus_device(), and it now uses pci_stop_and_remove_bus_device() instead, so stop exposing these interfaces. This removes these exported symbols: __pci_remove_bus_device pci_stop_bus_device Tested-by: Yijing Wang Signed-off-by: Bjorn Helgaas Acked-by: Yinghai Lu --- drivers/pci/remove.c | 8 ++++---- include/linux/pci.h | 2 -- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 04a4861b4749..534377f967ff 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -79,6 +79,8 @@ void pci_remove_bus(struct pci_bus *pci_bus) EXPORT_SYMBOL(pci_remove_bus); static void __pci_remove_behind_bridge(struct pci_dev *dev); +static void pci_stop_bus_device(struct pci_dev *dev); + /** * pci_stop_and_remove_bus_device - remove a PCI device and any children * @dev: the device to remove @@ -91,7 +93,7 @@ static void __pci_remove_behind_bridge(struct pci_dev *dev); * device lists, remove the /proc entry, and notify userspace * (/sbin/hotplug). */ -void __pci_remove_bus_device(struct pci_dev *dev) +static void __pci_remove_bus_device(struct pci_dev *dev) { if (dev->subordinate) { struct pci_bus *b = dev->subordinate; @@ -103,7 +105,6 @@ void __pci_remove_bus_device(struct pci_dev *dev) pci_destroy_dev(dev); } -EXPORT_SYMBOL(__pci_remove_bus_device); void pci_stop_and_remove_bus_device(struct pci_dev *dev) { @@ -170,7 +171,7 @@ static void pci_stop_bus_devices(struct pci_bus *bus) * and so on). This also stop any subordinate buses and children in a * depth-first manner. */ -void pci_stop_bus_device(struct pci_dev *dev) +static void pci_stop_bus_device(struct pci_dev *dev) { if (dev->subordinate) pci_stop_bus_devices(dev->subordinate); @@ -180,4 +181,3 @@ void pci_stop_bus_device(struct pci_dev *dev) EXPORT_SYMBOL(pci_stop_and_remove_bus_device); EXPORT_SYMBOL(pci_stop_and_remove_behind_bridge); -EXPORT_SYMBOL_GPL(pci_stop_bus_device); diff --git a/include/linux/pci.h b/include/linux/pci.h index 5faa8310eec9..54b5b2b2c2ec 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -734,9 +734,7 @@ u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp); extern struct pci_dev *pci_dev_get(struct pci_dev *dev); extern void pci_dev_put(struct pci_dev *dev); extern void pci_remove_bus(struct pci_bus *b); -extern void __pci_remove_bus_device(struct pci_dev *dev); extern void pci_stop_and_remove_bus_device(struct pci_dev *dev); -extern void pci_stop_bus_device(struct pci_dev *dev); void pci_setup_cardbus(struct pci_bus *bus); extern void pci_sort_breadthfirst(void); #define dev_is_pci(d) ((d)->bus == &pci_bus_type) -- cgit v1.2.3 From 125e14bb35e65b1ddfb7252fa8f6e3c50dbb6db2 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 17 Aug 2012 11:07:49 -0600 Subject: PCI: Remove pci_stop_and_remove_behind_bridge() The PCMCIA CardBus driver was the only user of pci_stop_and_remove_behind_bridge(), and it now uses pci_stop_and_remove_bus_device() instead, so remove this interface. This removes exported symbol pci_stop_and_remove_behind_bridge. Tested-by: Yijing Wang Signed-off-by: Bjorn Helgaas Acked-by: Yinghai Lu --- drivers/pci/remove.c | 25 ------------------------- include/linux/pci.h | 1 - 2 files changed, 26 deletions(-) (limited to 'include') diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 534377f967ff..b18dc2ef09f2 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -121,30 +121,6 @@ static void __pci_remove_behind_bridge(struct pci_dev *dev) __pci_remove_bus_device(pci_dev_b(l)); } -static void pci_stop_behind_bridge(struct pci_dev *dev) -{ - struct list_head *l, *n; - - if (dev->subordinate) - list_for_each_safe(l, n, &dev->subordinate->devices) - pci_stop_bus_device(pci_dev_b(l)); -} - -/** - * pci_stop_and_remove_behind_bridge - stop and remove all devices behind - * a PCI bridge - * @dev: PCI bridge device - * - * Remove all devices on the bus, except for the parent bridge. - * This also removes any child buses, and any devices they may - * contain in a depth-first manner. - */ -void pci_stop_and_remove_behind_bridge(struct pci_dev *dev) -{ - pci_stop_behind_bridge(dev); - __pci_remove_behind_bridge(dev); -} - static void pci_stop_bus_devices(struct pci_bus *bus) { struct list_head *l, *n; @@ -180,4 +156,3 @@ static void pci_stop_bus_device(struct pci_dev *dev) } EXPORT_SYMBOL(pci_stop_and_remove_bus_device); -EXPORT_SYMBOL(pci_stop_and_remove_behind_bridge); diff --git a/include/linux/pci.h b/include/linux/pci.h index 54b5b2b2c2ec..1dce47ca1291 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1011,7 +1011,6 @@ void pci_unregister_driver(struct pci_driver *dev); module_driver(__pci_driver, pci_register_driver, \ pci_unregister_driver) -void pci_stop_and_remove_behind_bridge(struct pci_dev *dev); struct pci_driver *pci_dev_driver(const struct pci_dev *dev); int pci_add_dynid(struct pci_driver *drv, unsigned int vendor, unsigned int device, -- cgit v1.2.3 From c29aabe22eafb4914aecebab6e99623894d81564 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 16 Aug 2012 17:13:00 -0600 Subject: PCI: Remove unused pci_dev_b() All uses of pci_dev_b() have been replaced by list_for_each_entry(), so remove it. Tested-by: Yijing Wang Signed-off-by: Bjorn Helgaas Acked-by: Yinghai Lu --- include/linux/pci.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/pci.h b/include/linux/pci.h index 1dce47ca1291..ed47147ac996 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -369,7 +369,6 @@ static inline struct pci_dev *pci_physfn(struct pci_dev *dev) extern struct pci_dev *alloc_pci_dev(void); -#define pci_dev_b(n) list_entry(n, struct pci_dev, bus_list) #define to_pci_dev(n) container_of(n, struct pci_dev, dev) #define for_each_pci_dev(d) while ((d = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, d)) != NULL) -- cgit v1.2.3 From 1659d129ed014b715b0b2120e6fd929bdd33ed03 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 22 Aug 2012 10:35:40 +0200 Subject: perf tools: Keep the perf_event_attr on version 3 Stashing version 4 under version 3 and removing version 4, because both version changes were within single patchset. Reported-by: Peter Zijlstra Signed-off-by: Jiri Olsa Acked-by: Peter Zijlstra Cc: Arun Sharma Cc: Benjamin Redelings Cc: Corey Ashford Cc: Cyrill Gorcunov Cc: Frank Ch. Eigler Cc: Frederic Weisbecker Cc: H. Peter Anvin Cc: Ingo Molnar Cc: Masami Hiramatsu Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Robert Richter Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Tom Zanussi Cc: Ulrich Drepper Link: http://lkml.kernel.org/r/20120822083540.GB1003@krava.brq.redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- include/linux/perf_event.h | 4 ++-- tools/perf/util/header.c | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 297ca3db6b4a..28f9cee3fbc3 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -205,8 +205,8 @@ enum perf_event_read_format { #define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */ #define PERF_ATTR_SIZE_VER1 72 /* add: config2 */ #define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */ -#define PERF_ATTR_SIZE_VER3 88 /* add: sample_regs_user */ -#define PERF_ATTR_SIZE_VER4 96 /* add: sample_stack_user */ +#define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */ + /* add: sample_stack_user */ /* * Hardware event_id to monitor via a performance monitoring event: diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 63aad3109e34..9696e64c9dbd 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2011,7 +2011,6 @@ static const int attr_file_abi_sizes[] = { [1] = PERF_ATTR_SIZE_VER1, [2] = PERF_ATTR_SIZE_VER2, [3] = PERF_ATTR_SIZE_VER3, - [4] = PERF_ATTR_SIZE_VER4, 0, }; -- cgit v1.2.3 From 3ce8859e2e72713d3619285cab609d05c3591fc4 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 18 Aug 2012 09:06:27 -0700 Subject: spi: Master driver for NXP SC18IS602/603 This driver adds support for NXP SC18IS602/603 I2C to SPI bus bridge. Signed-off-by: Guenter Roeck Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/spi-sc18is602.txt | 23 ++ Documentation/spi/spi-sc18is602 | 36 ++ drivers/spi/Kconfig | 6 + drivers/spi/Makefile | 1 + drivers/spi/spi-sc18is602.c | 364 +++++++++++++++++++++ include/linux/platform_data/sc18is602.h | 19 ++ 6 files changed, 449 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/spi-sc18is602.txt create mode 100644 Documentation/spi/spi-sc18is602 create mode 100644 drivers/spi/spi-sc18is602.c create mode 100644 include/linux/platform_data/sc18is602.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/spi/spi-sc18is602.txt b/Documentation/devicetree/bindings/spi/spi-sc18is602.txt new file mode 100644 index 000000000000..02f9033270a2 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-sc18is602.txt @@ -0,0 +1,23 @@ +NXP SC18IS602/SCIS603 + +Required properties: + - compatible : Should be one of + "nxp,sc18is602" + "nxp,sc18is602b" + "nxp,sc18is603" + - reg: I2C bus address + +Optional properties: + - clock-frequency : external oscillator clock frequency. If not + specified, the SC18IS602 default frequency (7372000) will be used. + +The clock-frequency property is relevant and needed only if the chip has an +external oscillator (SC18IS603). + +Example: + + sc18is603@28 { + compatible = "nxp,sc18is603"; + reg = <0x28>; + clock-frequency = <14744000>; + } diff --git a/Documentation/spi/spi-sc18is602 b/Documentation/spi/spi-sc18is602 new file mode 100644 index 000000000000..a45702865a38 --- /dev/null +++ b/Documentation/spi/spi-sc18is602 @@ -0,0 +1,36 @@ +Kernel driver spi-sc18is602 +=========================== + +Supported chips: + * NXP SI18IS602/602B/603 + Datasheet: http://www.nxp.com/documents/data_sheet/SC18IS602_602B_603.pdf + +Author: + Guenter Roeck + + +Description +----------- + +This driver provides connects a NXP SC18IS602/603 I2C-bus to SPI bridge to the +kernel's SPI core subsystem. + +The driver does not probe for supported chips, since the SI18IS602/603 does not +support Chip ID registers. You will have to instantiate the devices explicitly. +Please see Documentation/i2c/instantiating-devices for details. + + +Usage Notes +----------- + +This driver requires the I2C adapter driver to support raw I2C messages. I2C +adapter drivers which can only handle the SMBus protocol are not supported. + +The maximum SPI message size supported by SC18IS602/603 is 200 bytes. Attempts +to initiate longer transfers will fail with -EINVAL. EEPROM read operations and +similar large accesses have to be split into multiple chunks of no more than +200 bytes per SPI message (128 bytes of data per message is recommended). This +means that programs such as "cp" or "od", which automatically use large block +sizes to access a device, can not be used directly to read data from EEPROM. +Programs such as dd, where the block size can be specified, should be used +instead. diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 5f84b5563c2d..920bb4d22d40 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -325,6 +325,12 @@ config SPI_S3C64XX help SPI driver for Samsung S3C64XX and newer SoCs. +config SPI_SC18IS602 + tristate "NXP SC18IS602/602B/603 I2C to SPI bridge" + depends on I2C + help + SPI driver for NXP SC18IS602/602B/603 I2C to SPI bridge. + config SPI_SH_MSIOF tristate "SuperH MSIOF SPI controller" depends on SUPERH && HAVE_CLK diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3920dcf4c740..7559c984db77 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o spi-s3c24xx-hw-y := spi-s3c24xx.o spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o obj-$(CONFIG_SPI_S3C64XX) += spi-s3c64xx.o +obj-$(CONFIG_SPI_SC18IS602) += spi-sc18is602.o obj-$(CONFIG_SPI_SH) += spi-sh.o obj-$(CONFIG_SPI_SH_HSPI) += spi-sh-hspi.o obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o diff --git a/drivers/spi/spi-sc18is602.c b/drivers/spi/spi-sc18is602.c new file mode 100644 index 000000000000..dd9896423f0b --- /dev/null +++ b/drivers/spi/spi-sc18is602.c @@ -0,0 +1,364 @@ +/* + * NXP SC18IS602/603 SPI driver + * + * Copyright (C) Guenter Roeck + * + * 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 + +enum chips { sc18is602, sc18is602b, sc18is603 }; + +#define SC18IS602_BUFSIZ 200 +#define SC18IS602_CLOCK 7372000 + +#define SC18IS602_MODE_CPHA BIT(2) +#define SC18IS602_MODE_CPOL BIT(3) +#define SC18IS602_MODE_LSB_FIRST BIT(5) +#define SC18IS602_MODE_CLOCK_DIV_4 0x0 +#define SC18IS602_MODE_CLOCK_DIV_16 0x1 +#define SC18IS602_MODE_CLOCK_DIV_64 0x2 +#define SC18IS602_MODE_CLOCK_DIV_128 0x3 + +struct sc18is602 { + struct spi_master *master; + struct device *dev; + u8 ctrl; + u32 freq; + u32 speed; + + /* I2C data */ + struct i2c_client *client; + enum chips id; + u8 buffer[SC18IS602_BUFSIZ + 1]; + int tlen; /* Data queued for tx in buffer */ + int rindex; /* Receive data index in buffer */ +}; + +static int sc18is602_wait_ready(struct sc18is602 *hw, int len) +{ + int i, err; + int usecs = 1000000 * len / hw->speed + 1; + u8 dummy[1]; + + for (i = 0; i < 10; i++) { + err = i2c_master_recv(hw->client, dummy, 1); + if (err >= 0) + return 0; + usleep_range(usecs, usecs * 2); + } + return -ETIMEDOUT; +} + +static int sc18is602_txrx(struct sc18is602 *hw, struct spi_message *msg, + struct spi_transfer *t, bool do_transfer) +{ + unsigned int len = t->len; + int ret; + + if (hw->tlen == 0) { + /* First byte (I2C command) is chip select */ + hw->buffer[0] = 1 << msg->spi->chip_select; + hw->tlen = 1; + hw->rindex = 0; + } + /* + * We can not immediately send data to the chip, since each I2C message + * resembles a full SPI message (from CS active to CS inactive). + * Enqueue messages up to the first read or until do_transfer is true. + */ + if (t->tx_buf) { + memcpy(&hw->buffer[hw->tlen], t->tx_buf, len); + hw->tlen += len; + if (t->rx_buf) + do_transfer = true; + else + hw->rindex = hw->tlen - 1; + } else if (t->rx_buf) { + /* + * For receive-only transfers we still need to perform a dummy + * write to receive data from the SPI chip. + * Read data starts at the end of transmit data (minus 1 to + * account for CS). + */ + hw->rindex = hw->tlen - 1; + memset(&hw->buffer[hw->tlen], 0, len); + hw->tlen += len; + do_transfer = true; + } + + if (do_transfer && hw->tlen > 1) { + ret = sc18is602_wait_ready(hw, SC18IS602_BUFSIZ); + if (ret < 0) + return ret; + ret = i2c_master_send(hw->client, hw->buffer, hw->tlen); + if (ret < 0) + return ret; + if (ret != hw->tlen) + return -EIO; + + if (t->rx_buf) { + int rlen = hw->rindex + len; + + ret = sc18is602_wait_ready(hw, hw->tlen); + if (ret < 0) + return ret; + ret = i2c_master_recv(hw->client, hw->buffer, rlen); + if (ret < 0) + return ret; + if (ret != rlen) + return -EIO; + memcpy(t->rx_buf, &hw->buffer[hw->rindex], len); + } + hw->tlen = 0; + } + return len; +} + +static int sc18is602_setup_transfer(struct sc18is602 *hw, u32 hz, u8 mode) +{ + u8 ctrl = 0; + int ret; + + if (mode & SPI_CPHA) + ctrl |= SC18IS602_MODE_CPHA; + if (mode & SPI_CPOL) + ctrl |= SC18IS602_MODE_CPOL; + if (mode & SPI_LSB_FIRST) + ctrl |= SC18IS602_MODE_LSB_FIRST; + + /* Find the closest clock speed */ + if (hz >= hw->freq / 4) { + ctrl |= SC18IS602_MODE_CLOCK_DIV_4; + hw->speed = hw->freq / 4; + } else if (hz >= hw->freq / 16) { + ctrl |= SC18IS602_MODE_CLOCK_DIV_16; + hw->speed = hw->freq / 16; + } else if (hz >= hw->freq / 64) { + ctrl |= SC18IS602_MODE_CLOCK_DIV_64; + hw->speed = hw->freq / 64; + } else { + ctrl |= SC18IS602_MODE_CLOCK_DIV_128; + hw->speed = hw->freq / 128; + } + + /* + * Don't do anything if the control value did not change. The initial + * value of 0xff for hw->ctrl ensures that the correct mode will be set + * with the first call to this function. + */ + if (ctrl == hw->ctrl) + return 0; + + ret = i2c_smbus_write_byte_data(hw->client, 0xf0, ctrl); + if (ret < 0) + return ret; + + hw->ctrl = ctrl; + + return 0; +} + +static int sc18is602_check_transfer(struct spi_device *spi, + struct spi_transfer *t, int tlen) +{ + int bpw; + uint32_t hz; + + if (t && t->len + tlen > SC18IS602_BUFSIZ) + return -EINVAL; + + bpw = spi->bits_per_word; + if (t && t->bits_per_word) + bpw = t->bits_per_word; + if (bpw != 8) + return -EINVAL; + + hz = spi->max_speed_hz; + if (t && t->speed_hz) + hz = t->speed_hz; + if (hz == 0) + return -EINVAL; + + return 0; +} + +static int sc18is602_transfer_one(struct spi_master *master, + struct spi_message *m) +{ + struct sc18is602 *hw = spi_master_get_devdata(master); + struct spi_device *spi = m->spi; + struct spi_transfer *t; + int status = 0; + + /* SC18IS602 does not support CS2 */ + if (hw->id == sc18is602 && spi->chip_select == 2) { + status = -ENXIO; + goto error; + } + + hw->tlen = 0; + list_for_each_entry(t, &m->transfers, transfer_list) { + u32 hz = t->speed_hz ? : spi->max_speed_hz; + bool do_transfer; + + status = sc18is602_check_transfer(spi, t, hw->tlen); + if (status < 0) + break; + + status = sc18is602_setup_transfer(hw, hz, spi->mode); + if (status < 0) + break; + + do_transfer = t->cs_change || list_is_last(&t->transfer_list, + &m->transfers); + + if (t->len) { + status = sc18is602_txrx(hw, m, t, do_transfer); + if (status < 0) + break; + m->actual_length += status; + } + status = 0; + + if (t->delay_usecs) + udelay(t->delay_usecs); + } +error: + m->status = status; + spi_finalize_current_message(master); + + return status; +} + +static int sc18is602_setup(struct spi_device *spi) +{ + if (!spi->bits_per_word) + spi->bits_per_word = 8; + + if (spi->mode & ~(SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST)) + return -EINVAL; + + return sc18is602_check_transfer(spi, NULL, 0); +} + +static int sc18is602_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *np = dev->of_node; + struct sc18is602_platform_data *pdata = dev_get_platdata(dev); + struct sc18is602 *hw; + struct spi_master *master; + int error; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -ENODEV; + + master = spi_alloc_master(dev, sizeof(struct sc18is602)); + if (!master) + return -ENOMEM; + + hw = spi_master_get_devdata(master); + i2c_set_clientdata(client, hw); + + hw->master = master; + hw->client = client; + hw->dev = dev; + hw->ctrl = 0xff; + + hw->id = id->driver_data; + + switch (hw->id) { + case sc18is602: + case sc18is602b: + master->num_chipselect = 4; + hw->freq = SC18IS602_CLOCK; + break; + case sc18is603: + master->num_chipselect = 2; + if (pdata) { + hw->freq = pdata->clock_frequency; + } else { + const __be32 *val; + int len; + + val = of_get_property(np, "clock-frequency", &len); + if (val && len >= sizeof(__be32)) + hw->freq = be32_to_cpup(val); + } + if (!hw->freq) + hw->freq = SC18IS602_CLOCK; + break; + } + master->bus_num = client->adapter->nr; + master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST; + master->setup = sc18is602_setup; + master->transfer_one_message = sc18is602_transfer_one; + master->dev.of_node = np; + + error = spi_register_master(master); + if (error) + goto error_reg; + + return 0; + +error_reg: + spi_master_put(master); + return error; +} + +static int sc18is602_remove(struct i2c_client *client) +{ + struct sc18is602 *hw = i2c_get_clientdata(client); + struct spi_master *master = hw->master; + + spi_unregister_master(master); + + return 0; +} + +static const struct i2c_device_id sc18is602_id[] = { + { "sc18is602", sc18is602 }, + { "sc18is602b", sc18is602b }, + { "sc18is603", sc18is603 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sc18is602_id); + +static struct i2c_driver sc18is602_driver = { + .driver = { + .name = "sc18is602", + }, + .probe = sc18is602_probe, + .remove = sc18is602_remove, + .id_table = sc18is602_id, +}; + +module_i2c_driver(sc18is602_driver); + +MODULE_DESCRIPTION("SC18IC602/603 SPI Master Driver"); +MODULE_AUTHOR("Guenter Roeck"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/platform_data/sc18is602.h b/include/linux/platform_data/sc18is602.h new file mode 100644 index 000000000000..997b06634152 --- /dev/null +++ b/include/linux/platform_data/sc18is602.h @@ -0,0 +1,19 @@ +/* + * Platform data for NXP SC18IS602/603 + * + * Copyright (C) 2012 Guenter Roeck + * + * 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. + * + * For further information, see the Documentation/spi/sc18is602 file. + */ + +/** + * struct sc18is602_platform_data - sc18is602 info + * @clock_frequency SC18IS603 oscillator frequency + */ +struct sc18is602_platform_data { + u32 clock_frequency; +}; -- cgit v1.2.3 From f6f46de1063c8829713cd9d5b960dd8cb66cde8b Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Wed, 22 Aug 2012 15:49:17 +0200 Subject: spi/pl022: Add chip select handling via GPIO This patch adds the ability for the driver to control the chip select directly. This enables independence from cs_control callbacks. Configurable via platform_data, to be extended as DT in the following patch. Based on the initial patch by Alexandre Pereira da Silva Signed-off-by: Roland Stigge Acked-by: Alexandre Pereira da Silva Reviewed-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 48 ++++++++++++++++++++++++++++++++-------------- include/linux/amba/pl022.h | 2 ++ 2 files changed, 36 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 47c6753fb31b..cf47802f00c9 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -40,6 +40,7 @@ #include #include #include +#include /* * This macro is used to define some register default values. @@ -356,6 +357,8 @@ struct vendor_data { * @sgt_rx: scattertable for the RX transfer * @sgt_tx: scattertable for the TX transfer * @dummypage: a dummy page used for driving data on the bus with DMA + * @cur_cs: current chip select (gpio) + * @chipselects: list of chipselects (gpios) */ struct pl022 { struct amba_device *adev; @@ -389,6 +392,8 @@ struct pl022 { char *dummypage; bool dma_running; #endif + int cur_cs; + int *chipselects; }; /** @@ -433,6 +438,14 @@ static void null_cs_control(u32 command) pr_debug("pl022: dummy chip select control, CS=0x%x\n", command); } +static void pl022_cs_control(struct pl022 *pl022, u32 command) +{ + if (gpio_is_valid(pl022->cur_cs)) + gpio_set_value(pl022->cur_cs, command); + else + pl022->cur_chip->cs_control(command); +} + /** * giveback - current spi_message is over, schedule next message and call * callback of this message. Assumes that caller already @@ -479,7 +492,7 @@ static void giveback(struct pl022 *pl022) if (next_msg && next_msg->spi != pl022->cur_msg->spi) next_msg = NULL; if (!next_msg || pl022->cur_msg->state == STATE_ERROR) - pl022->cur_chip->cs_control(SSP_CHIP_DESELECT); + pl022_cs_control(pl022, SSP_CHIP_DESELECT); else pl022->next_msg_cs_active = true; @@ -818,8 +831,7 @@ static void dma_callback(void *data) /* Update total bytes transferred */ msg->actual_length += pl022->cur_transfer->len; if (pl022->cur_transfer->cs_change) - pl022->cur_chip-> - cs_control(SSP_CHIP_DESELECT); + pl022_cs_control(pl022, SSP_CHIP_DESELECT); /* Move to next transfer */ msg->state = next_transfer(pl022); @@ -1252,8 +1264,7 @@ static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id) /* Update total bytes transferred */ msg->actual_length += pl022->cur_transfer->len; if (pl022->cur_transfer->cs_change) - pl022->cur_chip-> - cs_control(SSP_CHIP_DESELECT); + pl022_cs_control(pl022, SSP_CHIP_DESELECT); /* Move to next transfer */ msg->state = next_transfer(pl022); tasklet_schedule(&pl022->pump_transfers); @@ -1338,7 +1349,7 @@ static void pump_transfers(unsigned long data) /* Reselect chip select only if cs_change was requested */ if (previous->cs_change) - pl022->cur_chip->cs_control(SSP_CHIP_SELECT); + pl022_cs_control(pl022, SSP_CHIP_SELECT); } else { /* STATE_START */ message->state = STATE_RUNNING; @@ -1377,7 +1388,7 @@ static void do_interrupt_dma_transfer(struct pl022 *pl022) /* Enable target chip, if not already active */ if (!pl022->next_msg_cs_active) - pl022->cur_chip->cs_control(SSP_CHIP_SELECT); + pl022_cs_control(pl022, SSP_CHIP_SELECT); if (set_up_next_transfer(pl022, pl022->cur_transfer)) { /* Error path */ @@ -1429,12 +1440,12 @@ static void do_polling_transfer(struct pl022 *pl022) if (previous->delay_usecs) udelay(previous->delay_usecs); if (previous->cs_change) - pl022->cur_chip->cs_control(SSP_CHIP_SELECT); + pl022_cs_control(pl022, SSP_CHIP_SELECT); } else { /* STATE_START */ message->state = STATE_RUNNING; if (!pl022->next_msg_cs_active) - pl022->cur_chip->cs_control(SSP_CHIP_SELECT); + pl022_cs_control(pl022, SSP_CHIP_SELECT); } /* Configuration Changing Per Transfer */ @@ -1466,7 +1477,7 @@ static void do_polling_transfer(struct pl022 *pl022) /* Update total byte transferred */ message->actual_length += pl022->cur_transfer->len; if (pl022->cur_transfer->cs_change) - pl022->cur_chip->cs_control(SSP_CHIP_DESELECT); + pl022_cs_control(pl022, SSP_CHIP_DESELECT); /* Move to next transfer */ message->state = next_transfer(pl022); } @@ -1495,6 +1506,7 @@ static int pl022_transfer_one_message(struct spi_master *master, /* Setup the SPI using the per chip configuration */ pl022->cur_chip = spi_get_ctldata(msg->spi); + pl022->cur_cs = pl022->chipselects[msg->spi->chip_select]; restore_state(pl022); flush(pl022); @@ -1840,8 +1852,9 @@ static int pl022_setup(struct spi_device *spi) chip->xfer_type = chip_info->com_mode; if (!chip_info->cs_control) { chip->cs_control = null_cs_control; - dev_warn(&spi->dev, - "chip select function is NULL for this chip\n"); + if (!gpio_is_valid(pl022->chipselects[spi->chip_select])) + dev_warn(&spi->dev, + "invalid chip select\n"); } else chip->cs_control = chip_info->cs_control; @@ -1993,7 +2006,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) struct pl022_ssp_controller *platform_info = adev->dev.platform_data; struct spi_master *master; struct pl022 *pl022 = NULL; /*Data for this driver */ - int status = 0; + int status = 0, i; dev_info(&adev->dev, "ARM PL022 driver, device ID: 0x%08x\n", adev->periphid); @@ -2004,7 +2017,8 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) } /* Allocate master with space for data */ - master = spi_alloc_master(dev, sizeof(struct pl022)); + master = spi_alloc_master(dev, sizeof(struct pl022) + sizeof(int) * + platform_info->num_chipselect); if (master == NULL) { dev_err(&adev->dev, "probe - cannot alloc SPI master\n"); status = -ENOMEM; @@ -2016,6 +2030,8 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) pl022->master_info = platform_info; pl022->adev = adev; pl022->vendor = id->data; + /* Point chipselects to allocated memory beyond the main struct */ + pl022->chipselects = (int *) pl022 + sizeof(struct pl022); /* * Bus Number Which has been Assigned to this SSP controller @@ -2030,6 +2046,10 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) master->unprepare_transfer_hardware = pl022_unprepare_transfer_hardware; master->rt = platform_info->rt; + if (platform_info->num_chipselect && platform_info->chipselects) + for (i = 0; i < platform_info->num_chipselect; i++) + pl022->chipselects[i] = platform_info->chipselects[i]; + /* * Supports mode 0-3, loopback, and active low CS. Transfers are * always MS bit first on the original pl022. diff --git a/include/linux/amba/pl022.h b/include/linux/amba/pl022.h index fe1d7b283cb6..854b7294f6c6 100644 --- a/include/linux/amba/pl022.h +++ b/include/linux/amba/pl022.h @@ -244,6 +244,7 @@ struct dma_chan; * indicates no delay and the device will be suspended immediately. * @rt: indicates the controller should run the message pump with realtime * priority to minimise the transfer latency on the bus. + * @chipselects: list of chip select gpios */ struct pl022_ssp_controller { u16 bus_id; @@ -254,6 +255,7 @@ struct pl022_ssp_controller { void *dma_tx_param; int autosuspend_delay; bool rt; + int *chipselects; }; /** -- cgit v1.2.3 From 44a9a36f6be43636ac2342c06d9feb60db77826a Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 13 Jul 2012 14:24:59 -0600 Subject: PCI: Add pci_find_next_ext_capability() Some extended capabilities, e.g., the vendor-specific capability, can occur several times. The existing pci_find_ext_capability() only finds the first occurrence. This adds pci_find_next_ext_capability(), which can iterate through all of them. Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 40 ++++++++++++++++++++++++++++++---------- include/linux/pci.h | 1 + 2 files changed, 31 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index f3ea977a5b1b..d34415ba0f64 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -286,20 +286,17 @@ static int pci_pcie_cap2(struct pci_dev *dev) } /** - * pci_find_ext_capability - Find an extended capability + * pci_find_next_ext_capability - Find an extended capability * @dev: PCI device to query + * @start: address at which to start looking (0 to start at beginning of list) * @cap: capability code * - * Returns the address of the requested extended capability structure + * Returns the address of the next matching extended capability structure * within the device's PCI configuration space or 0 if the device does - * not support it. Possible values for @cap: - * - * %PCI_EXT_CAP_ID_ERR Advanced Error Reporting - * %PCI_EXT_CAP_ID_VC Virtual Channel - * %PCI_EXT_CAP_ID_DSN Device Serial Number - * %PCI_EXT_CAP_ID_PWR Power Budgeting + * not support it. Some capabilities can occur several times, e.g., the + * vendor-specific capability, and this provides a way to find them all. */ -int pci_find_ext_capability(struct pci_dev *dev, int cap) +int pci_find_next_ext_capability(struct pci_dev *dev, int start, int cap) { u32 header; int ttl; @@ -311,6 +308,9 @@ int pci_find_ext_capability(struct pci_dev *dev, int cap) if (dev->cfg_size <= PCI_CFG_SPACE_SIZE) return 0; + if (start) + pos = start; + if (pci_read_config_dword(dev, pos, &header) != PCIBIOS_SUCCESSFUL) return 0; @@ -322,7 +322,7 @@ int pci_find_ext_capability(struct pci_dev *dev, int cap) return 0; while (ttl-- > 0) { - if (PCI_EXT_CAP_ID(header) == cap) + if (PCI_EXT_CAP_ID(header) == cap && pos != start) return pos; pos = PCI_EXT_CAP_NEXT(header); @@ -335,6 +335,26 @@ int pci_find_ext_capability(struct pci_dev *dev, int cap) return 0; } +EXPORT_SYMBOL_GPL(pci_find_next_ext_capability); + +/** + * pci_find_ext_capability - Find an extended capability + * @dev: PCI device to query + * @cap: capability code + * + * Returns the address of the requested extended capability structure + * within the device's PCI configuration space or 0 if the device does + * not support it. Possible values for @cap: + * + * %PCI_EXT_CAP_ID_ERR Advanced Error Reporting + * %PCI_EXT_CAP_ID_VC Virtual Channel + * %PCI_EXT_CAP_ID_DSN Device Serial Number + * %PCI_EXT_CAP_ID_PWR Power Budgeting + */ +int pci_find_ext_capability(struct pci_dev *dev, int cap) +{ + return pci_find_next_ext_capability(dev, 0, cap); +} EXPORT_SYMBOL_GPL(pci_find_ext_capability); static int __pci_find_next_ht_cap(struct pci_dev *dev, int pos, int ht_cap) diff --git a/include/linux/pci.h b/include/linux/pci.h index 5faa8310eec9..65c503cdec3b 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -755,6 +755,7 @@ enum pci_lost_interrupt_reason pci_lost_interrupt(struct pci_dev *dev); int pci_find_capability(struct pci_dev *dev, int cap); int pci_find_next_capability(struct pci_dev *dev, u8 pos, int cap); int pci_find_ext_capability(struct pci_dev *dev, int cap); +int pci_find_next_ext_capability(struct pci_dev *dev, int pos, int cap); int pci_find_ht_capability(struct pci_dev *dev, int ht_cap); int pci_find_next_ht_capability(struct pci_dev *dev, int pos, int ht_cap); struct pci_bus *pci_find_next_bus(const struct pci_bus *from); -- cgit v1.2.3 From defb9446fe417f72855bc8bf97aa5d8af076bdf8 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 13 Jul 2012 14:30:21 -0600 Subject: PCI: Add Vendor-Specific Extended Capability header info This adds the fields in the Vendor-Specific Header: ID, Rev, and Length. There may be multiple Vendor-Specific capabilities, so drivers should use the VSEC ID to identify the one of interest. Signed-off-by: Bjorn Helgaas --- include/linux/pci_regs.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index 7fb75b143755..02448b1a0902 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -677,6 +677,12 @@ #define PCI_PWR_CAP_BUDGET(x) ((x) & 1) /* Included in system budget */ #define PCI_EXT_CAP_PWR_SIZEOF 16 +/* Vendor-Specific (VSEC, PCI_EXT_CAP_ID_VNDR) */ +#define PCI_VNDR_HEADER 4 /* Vendor-Specific Header */ +#define PCI_VNDR_HEADER_ID(x) ((x) & 0xffff) +#define PCI_VNDR_HEADER_REV(x) (((x) >> 16) & 0xf) +#define PCI_VNDR_HEADER_LEN(x) (((x) >> 20) & 0xfff) + /* * Hypertransport sub capability types * -- cgit v1.2.3 From 0115e8e30d6fcdd4b8faa30d3ffd90859a591f51 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 22 Aug 2012 17:19:46 +0000 Subject: net: remove delay at device dismantle I noticed extra one second delay in device dismantle, tracked down to a call to dst_dev_event() while some call_rcu() are still in RCU queues. These call_rcu() were posted by rt_free(struct rtable *rt) calls. We then wait a little (but one second) in netdev_wait_allrefs() before kicking again NETDEV_UNREGISTER. As the call_rcu() are now completed, dst_dev_event() can do the needed device swap on busy dst. To solve this problem, add a new NETDEV_UNREGISTER_FINAL, called after a rcu_barrier(), but outside of RTNL lock. Use NETDEV_UNREGISTER_FINAL with care ! Change dst_dev_event() handler to react to NETDEV_UNREGISTER_FINAL Also remove NETDEV_UNREGISTER_BATCH, as its not used anymore after IP cache removal. With help from Gao feng Signed-off-by: Eric Dumazet Cc: Tom Herbert Cc: Mahesh Bandewar Cc: "Eric W. Biederman" Cc: Gao feng Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 +- net/core/dev.c | 22 ++++++++-------------- net/core/dst.c | 2 +- net/core/fib_rules.c | 3 ++- net/core/rtnetlink.c | 2 +- net/ipv4/devinet.c | 6 +++++- net/ipv4/fib_frontend.c | 8 ++++---- net/ipv6/addrconf.c | 6 +++++- 8 files changed, 27 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 4936f09a9333..9ad7fa8c10e0 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1553,7 +1553,7 @@ struct packet_type { #define NETDEV_PRE_TYPE_CHANGE 0x000E #define NETDEV_POST_TYPE_CHANGE 0x000F #define NETDEV_POST_INIT 0x0010 -#define NETDEV_UNREGISTER_BATCH 0x0011 +#define NETDEV_UNREGISTER_FINAL 0x0011 #define NETDEV_RELEASE 0x0012 #define NETDEV_NOTIFY_PEERS 0x0013 #define NETDEV_JOIN 0x0014 diff --git a/net/core/dev.c b/net/core/dev.c index 088923fe4066..0640d2a859c6 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1406,7 +1406,6 @@ rollback: nb->notifier_call(nb, NETDEV_DOWN, dev); } nb->notifier_call(nb, NETDEV_UNREGISTER, dev); - nb->notifier_call(nb, NETDEV_UNREGISTER_BATCH, dev); } } @@ -1448,7 +1447,6 @@ int unregister_netdevice_notifier(struct notifier_block *nb) nb->notifier_call(nb, NETDEV_DOWN, dev); } nb->notifier_call(nb, NETDEV_UNREGISTER, dev); - nb->notifier_call(nb, NETDEV_UNREGISTER_BATCH, dev); } } unlock: @@ -1468,7 +1466,8 @@ EXPORT_SYMBOL(unregister_netdevice_notifier); int call_netdevice_notifiers(unsigned long val, struct net_device *dev) { - ASSERT_RTNL(); + if (val != NETDEV_UNREGISTER_FINAL) + ASSERT_RTNL(); return raw_notifier_call_chain(&netdev_chain, val, dev); } EXPORT_SYMBOL(call_netdevice_notifiers); @@ -5331,10 +5330,6 @@ static void rollback_registered_many(struct list_head *head) netdev_unregister_kobject(dev); } - /* Process any work delayed until the end of the batch */ - dev = list_first_entry(head, struct net_device, unreg_list); - call_netdevice_notifiers(NETDEV_UNREGISTER_BATCH, dev); - synchronize_net(); list_for_each_entry(dev, head, unreg_list) @@ -5787,9 +5782,8 @@ static void netdev_wait_allrefs(struct net_device *dev) /* Rebroadcast unregister notification */ call_netdevice_notifiers(NETDEV_UNREGISTER, dev); - /* don't resend NETDEV_UNREGISTER_BATCH, _BATCH users - * should have already handle it the first time */ - + rcu_barrier(); + call_netdevice_notifiers(NETDEV_UNREGISTER_FINAL, dev); if (test_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state)) { /* We must not have linkwatch events @@ -5851,9 +5845,8 @@ void netdev_run_todo(void) __rtnl_unlock(); - /* Wait for rcu callbacks to finish before attempting to drain - * the device list. This usually avoids a 250ms wait. - */ + + /* Wait for rcu callbacks to finish before next phase */ if (!list_empty(&list)) rcu_barrier(); @@ -5862,6 +5855,8 @@ void netdev_run_todo(void) = list_first_entry(&list, struct net_device, todo_list); list_del(&dev->todo_list); + call_netdevice_notifiers(NETDEV_UNREGISTER_FINAL, dev); + if (unlikely(dev->reg_state != NETREG_UNREGISTERING)) { pr_err("network todo '%s' but state %d\n", dev->name, dev->reg_state); @@ -6256,7 +6251,6 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char the device is just moving and can keep their slaves up. */ call_netdevice_notifiers(NETDEV_UNREGISTER, dev); - call_netdevice_notifiers(NETDEV_UNREGISTER_BATCH, dev); rtmsg_ifinfo(RTM_DELLINK, dev, ~0U); /* diff --git a/net/core/dst.c b/net/core/dst.c index 56d63612e1e4..f6593d238e9a 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -374,7 +374,7 @@ static int dst_dev_event(struct notifier_block *this, unsigned long event, struct dst_entry *dst, *last = NULL; switch (event) { - case NETDEV_UNREGISTER: + case NETDEV_UNREGISTER_FINAL: case NETDEV_DOWN: mutex_lock(&dst_gc_mutex); for (dst = dst_busy_list; dst; dst = dst->next) { diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index ab7db83236c9..585093755c23 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -711,15 +711,16 @@ static int fib_rules_event(struct notifier_block *this, unsigned long event, struct net *net = dev_net(dev); struct fib_rules_ops *ops; - ASSERT_RTNL(); switch (event) { case NETDEV_REGISTER: + ASSERT_RTNL(); list_for_each_entry(ops, &net->rules_ops, list) attach_rules(&ops->rules_list, dev); break; case NETDEV_UNREGISTER: + ASSERT_RTNL(); list_for_each_entry(ops, &net->rules_ops, list) detach_rules(&ops->rules_list, dev); break; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 34d975b0f277..c64efcff8078 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2358,7 +2358,7 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi case NETDEV_PRE_TYPE_CHANGE: case NETDEV_GOING_DOWN: case NETDEV_UNREGISTER: - case NETDEV_UNREGISTER_BATCH: + case NETDEV_UNREGISTER_FINAL: case NETDEV_RELEASE: case NETDEV_JOIN: break; diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index adf273f8ad2e..6a5e6e4b142c 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1147,8 +1147,12 @@ static int inetdev_event(struct notifier_block *this, unsigned long event, void *ptr) { struct net_device *dev = ptr; - struct in_device *in_dev = __in_dev_get_rtnl(dev); + struct in_device *in_dev; + if (event == NETDEV_UNREGISTER_FINAL) + goto out; + + in_dev = __in_dev_get_rtnl(dev); ASSERT_RTNL(); if (!in_dev) { diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 7f073a38c87d..fd7d9ae64f16 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -1041,7 +1041,7 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event, static int fib_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { struct net_device *dev = ptr; - struct in_device *in_dev = __in_dev_get_rtnl(dev); + struct in_device *in_dev; struct net *net = dev_net(dev); if (event == NETDEV_UNREGISTER) { @@ -1050,9 +1050,11 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo return NOTIFY_DONE; } - if (!in_dev) + if (event == NETDEV_UNREGISTER_FINAL) return NOTIFY_DONE; + in_dev = __in_dev_get_rtnl(dev); + switch (event) { case NETDEV_UP: for_ifa(in_dev) { @@ -1071,8 +1073,6 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo case NETDEV_CHANGE: rt_cache_flush(dev_net(dev), 0); break; - case NETDEV_UNREGISTER_BATCH: - break; } return NOTIFY_DONE; } diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 6bc85f7c31e3..e581009cb09e 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -2566,10 +2566,14 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event, void *data) { struct net_device *dev = (struct net_device *) data; - struct inet6_dev *idev = __in6_dev_get(dev); + struct inet6_dev *idev; int run_pending = 0; int err; + if (event == NETDEV_UNREGISTER_FINAL) + return NOTIFY_DONE; + + idev = __in6_dev_get(dev); switch (event) { case NETDEV_REGISTER: if (!idev && dev->mtu >= IPV6_MIN_MTU) { -- cgit v1.2.3 From b32607dd47d456b99f0a16f1c37bc8a0975ffb19 Mon Sep 17 00:00:00 2001 From: "Allan, Bruce W" Date: Mon, 20 Aug 2012 04:55:29 +0000 Subject: mdio: translation of MMD EEE registers to/from ethtool settings The helper functions which translate IEEE MDIO Manageable Device (MMD) Energy-Efficient Ethernet (EEE) registers 3.20, 7.60 and 7.61 to and from the comparable ethtool supported/advertised settings will be needed by drivers other than those in PHYLIB (e.g. e1000e in a follow-on patch). In the same fashion as similar translation functions in linux/mii.h, move these functions from the PHYLIB core to the linux/mdio.h header file so the code will not have to be duplicated in each driver needing MMD-to-ethtool (and vice-versa) translations. The function and some variable names have been renamed to be more descriptive. Not tested on the only hardware that currently calls the related functions, stmmac, because I don't have access to any. Has been compile tested and the translations have been tested on a locally modified version of e1000e. Signed-off-by: Bruce Allan Cc: Giuseppe Cavallaro Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 74 +++++---------------------------------------- include/linux/mdio.h | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 67 deletions(-) (limited to 'include') diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 7ca2ff97c368..ef9ea9248223 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1035,66 +1035,6 @@ static void phy_write_mmd_indirect(struct mii_bus *bus, int prtad, int devad, bus->write(bus, addr, MII_MMD_DATA, data); } -static u32 phy_eee_to_adv(u16 eee_adv) -{ - u32 adv = 0; - - if (eee_adv & MDIO_EEE_100TX) - adv |= ADVERTISED_100baseT_Full; - if (eee_adv & MDIO_EEE_1000T) - adv |= ADVERTISED_1000baseT_Full; - if (eee_adv & MDIO_EEE_10GT) - adv |= ADVERTISED_10000baseT_Full; - if (eee_adv & MDIO_EEE_1000KX) - adv |= ADVERTISED_1000baseKX_Full; - if (eee_adv & MDIO_EEE_10GKX4) - adv |= ADVERTISED_10000baseKX4_Full; - if (eee_adv & MDIO_EEE_10GKR) - adv |= ADVERTISED_10000baseKR_Full; - - return adv; -} - -static u32 phy_eee_to_supported(u16 eee_caported) -{ - u32 supported = 0; - - if (eee_caported & MDIO_EEE_100TX) - supported |= SUPPORTED_100baseT_Full; - if (eee_caported & MDIO_EEE_1000T) - supported |= SUPPORTED_1000baseT_Full; - if (eee_caported & MDIO_EEE_10GT) - supported |= SUPPORTED_10000baseT_Full; - if (eee_caported & MDIO_EEE_1000KX) - supported |= SUPPORTED_1000baseKX_Full; - if (eee_caported & MDIO_EEE_10GKX4) - supported |= SUPPORTED_10000baseKX4_Full; - if (eee_caported & MDIO_EEE_10GKR) - supported |= SUPPORTED_10000baseKR_Full; - - return supported; -} - -static u16 phy_adv_to_eee(u32 adv) -{ - u16 reg = 0; - - if (adv & ADVERTISED_100baseT_Full) - reg |= MDIO_EEE_100TX; - if (adv & ADVERTISED_1000baseT_Full) - reg |= MDIO_EEE_1000T; - if (adv & ADVERTISED_10000baseT_Full) - reg |= MDIO_EEE_10GT; - if (adv & ADVERTISED_1000baseKX_Full) - reg |= MDIO_EEE_1000KX; - if (adv & ADVERTISED_10000baseKX4_Full) - reg |= MDIO_EEE_10GKX4; - if (adv & ADVERTISED_10000baseKR_Full) - reg |= MDIO_EEE_10GKR; - - return reg; -} - /** * phy_init_eee - init and check the EEE feature * @phydev: target phy_device struct @@ -1132,7 +1072,7 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) if (eee_cap < 0) return eee_cap; - cap = phy_eee_to_supported(eee_cap); + cap = mmd_eee_cap_to_ethtool_sup_t(eee_cap); if (!cap) goto eee_exit; @@ -1149,8 +1089,8 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) if (eee_adv < 0) return eee_adv; - adv = phy_eee_to_adv(eee_adv); - lp = phy_eee_to_adv(eee_lp); + adv = mmd_eee_adv_to_ethtool_adv_t(eee_adv); + lp = mmd_eee_adv_to_ethtool_adv_t(eee_lp); idx = phy_find_setting(phydev->speed, phydev->duplex); if ((lp & adv & settings[idx].setting)) goto eee_exit; @@ -1210,21 +1150,21 @@ int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data) MDIO_MMD_PCS, phydev->addr); if (val < 0) return val; - data->supported = phy_eee_to_supported(val); + data->supported = mmd_eee_cap_to_ethtool_sup_t(val); /* Get advertisement EEE */ val = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_ADV, MDIO_MMD_AN, phydev->addr); if (val < 0) return val; - data->advertised = phy_eee_to_adv(val); + data->advertised = mmd_eee_adv_to_ethtool_adv_t(val); /* Get LP advertisement EEE */ val = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_LPABLE, MDIO_MMD_AN, phydev->addr); if (val < 0) return val; - data->lp_advertised = phy_eee_to_adv(val); + data->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val); return 0; } @@ -1241,7 +1181,7 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) { int val; - val = phy_adv_to_eee(data->advertised); + val = ethtool_adv_to_mmd_eee_adv_t(data->advertised); phy_write_mmd_indirect(phydev->bus, MDIO_AN_EEE_ADV, MDIO_MMD_AN, phydev->addr, val); diff --git a/include/linux/mdio.h b/include/linux/mdio.h index 7cccafe50e7b..6c406845f7e2 100644 --- a/include/linux/mdio.h +++ b/include/linux/mdio.h @@ -377,5 +377,88 @@ static inline void mdio45_ethtool_gset(const struct mdio_if_info *mdio, extern int mdio_mii_ioctl(const struct mdio_if_info *mdio, struct mii_ioctl_data *mii_data, int cmd); +/** + * mmd_eee_cap_to_ethtool_sup_t + * @eee_cap: value of the MMD EEE Capability register + * + * A small helper function that translates MMD EEE Capability (3.20) bits + * to ethtool supported settings. + */ +static inline u32 mmd_eee_cap_to_ethtool_sup_t(u16 eee_cap) +{ + u32 supported = 0; + + if (eee_cap & MDIO_EEE_100TX) + supported |= SUPPORTED_100baseT_Full; + if (eee_cap & MDIO_EEE_1000T) + supported |= SUPPORTED_1000baseT_Full; + if (eee_cap & MDIO_EEE_10GT) + supported |= SUPPORTED_10000baseT_Full; + if (eee_cap & MDIO_EEE_1000KX) + supported |= SUPPORTED_1000baseKX_Full; + if (eee_cap & MDIO_EEE_10GKX4) + supported |= SUPPORTED_10000baseKX4_Full; + if (eee_cap & MDIO_EEE_10GKR) + supported |= SUPPORTED_10000baseKR_Full; + + return supported; +} + +/** + * mmd_eee_adv_to_ethtool_adv_t + * @eee_adv: value of the MMD EEE Advertisement/Link Partner Ability registers + * + * A small helper function that translates the MMD EEE Advertisment (7.60) + * and MMD EEE Link Partner Ability (7.61) bits to ethtool advertisement + * settings. + */ +static inline u32 mmd_eee_adv_to_ethtool_adv_t(u16 eee_adv) +{ + u32 adv = 0; + + if (eee_adv & MDIO_EEE_100TX) + adv |= ADVERTISED_100baseT_Full; + if (eee_adv & MDIO_EEE_1000T) + adv |= ADVERTISED_1000baseT_Full; + if (eee_adv & MDIO_EEE_10GT) + adv |= ADVERTISED_10000baseT_Full; + if (eee_adv & MDIO_EEE_1000KX) + adv |= ADVERTISED_1000baseKX_Full; + if (eee_adv & MDIO_EEE_10GKX4) + adv |= ADVERTISED_10000baseKX4_Full; + if (eee_adv & MDIO_EEE_10GKR) + adv |= ADVERTISED_10000baseKR_Full; + + return adv; +} + +/** + * ethtool_adv_to_mmd_eee_adv_t + * @adv: the ethtool advertisement settings + * + * A small helper function that translates ethtool advertisement settings + * to EEE advertisements for the MMD EEE Advertisement (7.60) and + * MMD EEE Link Partner Ability (7.61) registers. + */ +static inline u16 ethtool_adv_to_mmd_eee_adv_t(u32 adv) +{ + u16 reg = 0; + + if (adv & ADVERTISED_100baseT_Full) + reg |= MDIO_EEE_100TX; + if (adv & ADVERTISED_1000baseT_Full) + reg |= MDIO_EEE_1000T; + if (adv & ADVERTISED_10000baseT_Full) + reg |= MDIO_EEE_10GT; + if (adv & ADVERTISED_1000baseKX_Full) + reg |= MDIO_EEE_1000KX; + if (adv & ADVERTISED_10000baseKX4_Full) + reg |= MDIO_EEE_10GKX4; + if (adv & ADVERTISED_10000baseKR_Full) + reg |= MDIO_EEE_10GKR; + + return reg; +} + #endif /* __KERNEL__ */ #endif /* __LINUX_MDIO_H__ */ -- cgit v1.2.3 From 0fa7fa98dbcc2789409ed24e885485e645803d7f Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Tue, 21 Aug 2012 01:06:47 +0000 Subject: packet: Protect packet sk list with mutex (v2) Change since v1: * Fixed inuse counters access spotted by Eric In patch eea68e2f (packet: Report socket mclist info via diag module) I've introduced a "scheduling in atomic" problem in packet diag module -- the socket list is traversed under rcu_read_lock() while performed under it sk mclist access requires rtnl lock (i.e. -- mutex) to be taken. [152363.820563] BUG: scheduling while atomic: crtools/12517/0x10000002 [152363.820573] 4 locks held by crtools/12517: [152363.820581] #0: (sock_diag_mutex){+.+.+.}, at: [] sock_diag_rcv+0x1f/0x3e [152363.820613] #1: (sock_diag_table_mutex){+.+.+.}, at: [] sock_diag_rcv_msg+0xdb/0x11a [152363.820644] #2: (nlk->cb_mutex){+.+.+.}, at: [] netlink_dump+0x23/0x1ab [152363.820693] #3: (rcu_read_lock){.+.+..}, at: [] packet_diag_dump+0x0/0x1af Similar thing was then re-introduced by further packet diag patches (fanount mutex and pgvec mutex for rings) :( Apart from being terribly sorry for the above, I propose to change the packet sk list protection from spinlock to mutex. This lock currently protects two modifications: * sklist * prot inuse counters The sklist modifications can be just reprotected with mutex since they already occur in a sleeping context. The inuse counters modifications are trickier -- the __this_cpu_-s are used inside, thus requiring the caller to handle the potential issues with contexts himself. Since packet sockets' counters are modified in two places only (packet_create and packet_release) we only need to protect the context from being preempted. BH disabling is not required in this case. Signed-off-by: Pavel Emelyanov Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/packet.h | 2 +- net/packet/af_packet.c | 16 +++++++++++----- net/packet/diag.c | 6 +++--- 3 files changed, 15 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/netns/packet.h b/include/net/netns/packet.h index cb4e894c0f8d..4780b080a436 100644 --- a/include/net/netns/packet.h +++ b/include/net/netns/packet.h @@ -8,7 +8,7 @@ #include struct netns_packet { - spinlock_t sklist_lock; + struct mutex sklist_lock; struct hlist_head sklist; }; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index bfaa0a83f0eb..f220c5bdb71f 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -2317,10 +2317,13 @@ static int packet_release(struct socket *sock) net = sock_net(sk); po = pkt_sk(sk); - spin_lock_bh(&net->packet.sklist_lock); + mutex_lock(&net->packet.sklist_lock); sk_del_node_init_rcu(sk); + mutex_unlock(&net->packet.sklist_lock); + + preempt_disable(); sock_prot_inuse_add(net, sk->sk_prot, -1); - spin_unlock_bh(&net->packet.sklist_lock); + preempt_enable(); spin_lock(&po->bind_lock); unregister_prot_hook(sk, false); @@ -2519,10 +2522,13 @@ static int packet_create(struct net *net, struct socket *sock, int protocol, register_prot_hook(sk); } - spin_lock_bh(&net->packet.sklist_lock); + mutex_lock(&net->packet.sklist_lock); sk_add_node_rcu(sk, &net->packet.sklist); + mutex_unlock(&net->packet.sklist_lock); + + preempt_disable(); sock_prot_inuse_add(net, &packet_proto, 1); - spin_unlock_bh(&net->packet.sklist_lock); + preempt_enable(); return 0; out: @@ -3775,7 +3781,7 @@ static const struct file_operations packet_seq_fops = { static int __net_init packet_net_init(struct net *net) { - spin_lock_init(&net->packet.sklist_lock); + mutex_init(&net->packet.sklist_lock); INIT_HLIST_HEAD(&net->packet.sklist); if (!proc_net_fops_create(net, "packet", 0, &packet_seq_fops)) diff --git a/net/packet/diag.c b/net/packet/diag.c index bc33fbe8a5ef..39bce0d50df9 100644 --- a/net/packet/diag.c +++ b/net/packet/diag.c @@ -177,8 +177,8 @@ static int packet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) net = sock_net(skb->sk); req = nlmsg_data(cb->nlh); - rcu_read_lock(); - sk_for_each_rcu(sk, node, &net->packet.sklist) { + mutex_lock(&net->packet.sklist_lock); + sk_for_each(sk, node, &net->packet.sklist) { if (!net_eq(sock_net(sk), net)) continue; if (num < s_num) @@ -192,7 +192,7 @@ next: num++; } done: - rcu_read_unlock(); + mutex_unlock(&net->packet.sklist_lock); cb->args[0] = num; return skb->len; -- cgit v1.2.3 From c7a9b09b1a4a1fbccb2ec409daec95f9068d77c0 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 15 Aug 2012 20:51:54 +0000 Subject: ARM: omap: allow building omap44xx without SMP The new omap4 cpuidle implementation currently requires ARCH_NEEDS_CPU_IDLE_COUPLED, which only works on SMP. This patch makes it possible to build a non-SMP kernel for that platform. This is not normally desired for end-users but can be useful for testing. Without this patch, building rand-0y2jSKT results in: drivers/cpuidle/coupled.c: In function 'cpuidle_coupled_poke': drivers/cpuidle/coupled.c:317:3: error: implicit declaration of function '__smp_call_function_single' [-Werror=implicit-function-declaration] It's not clear if this patch is the best solution for the problem at hand. I have made sure that we can now build the kernel in all configurations, but that does not mean it will actually work on an OMAP44xx. Signed-off-by: Arnd Bergmann Acked-by: Santosh Shilimkar Tested-by: Santosh Shilimkar Cc: Kevin Hilman Cc: Tony Lindgren --- arch/arm/mach-omap2/Kconfig | 2 +- arch/arm/mach-omap2/cpuidle44xx.c | 3 ++- include/linux/cpuidle.h | 4 ++++ 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig index dd2db025f778..66a8be331caf 100644 --- a/arch/arm/mach-omap2/Kconfig +++ b/arch/arm/mach-omap2/Kconfig @@ -62,7 +62,7 @@ config ARCH_OMAP4 select PM_OPP if PM select USB_ARCH_HAS_EHCI if USB_SUPPORT select ARM_CPU_SUSPEND if PM - select ARCH_NEEDS_CPU_IDLE_COUPLED + select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP config SOC_OMAP5 bool "TI OMAP5" diff --git a/arch/arm/mach-omap2/cpuidle44xx.c b/arch/arm/mach-omap2/cpuidle44xx.c index ee05e193fc61..288bee6cbb76 100644 --- a/arch/arm/mach-omap2/cpuidle44xx.c +++ b/arch/arm/mach-omap2/cpuidle44xx.c @@ -238,8 +238,9 @@ int __init omap4_idle_init(void) for_each_cpu(cpu_id, cpu_online_mask) { dev = &per_cpu(omap4_idle_dev, cpu_id); dev->cpu = cpu_id; +#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED dev->coupled_cpus = *cpu_online_mask; - +#endif cpuidle_register_driver(&omap4_idle_driver); if (cpuidle_register_device(dev)) { diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 040b13b5c14a..279b1eaa8b73 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -194,6 +194,10 @@ static inline int cpuidle_play_dead(void) {return -ENODEV; } #ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED void cpuidle_coupled_parallel_barrier(struct cpuidle_device *dev, atomic_t *a); +#else +static inline void cpuidle_coupled_parallel_barrier(struct cpuidle_device *dev, atomic_t *a) +{ +} #endif /****************************** -- cgit v1.2.3 From b2ef39be5744d4d3575474444345e71bd95013ba Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Tue, 24 Jul 2012 17:20:04 +0800 Subject: PCI: Remove unused field pcie_type from struct pci_dev With introduction of pci_pcie_type(), pci_dev->pcie_type field becomes redundant, so remove it. Signed-off-by: Yijing Wang Signed-off-by: Jiang Liu Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 1 - include/linux/pci.h | 1 - 2 files changed, 2 deletions(-) (limited to 'include') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 1d52a43eb086..8bcc985faa16 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -930,7 +930,6 @@ void set_pcie_port_type(struct pci_dev *pdev) pdev->pcie_cap = pos; pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, ®16); pdev->pcie_flags_reg = reg16; - pdev->pcie_type = pci_pcie_type(pdev); pci_read_config_word(pdev, pos + PCI_EXP_DEVCAP, ®16); pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD; } diff --git a/include/linux/pci.h b/include/linux/pci.h index 95662b2f0c3d..9807da507e1f 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -254,7 +254,6 @@ struct pci_dev { u8 revision; /* PCI revision, low byte of class word */ u8 hdr_type; /* PCI header type (`multi' flag masked out) */ u8 pcie_cap; /* PCI-E capability offset */ - u8 pcie_type:4; /* PCI-E device/port type */ u8 pcie_mpss:3; /* PCI-E Max Payload Size Supported */ u8 rom_base_reg; /* which config register controls the ROM */ u8 pin; /* which interrupt pin this device uses */ -- cgit v1.2.3 From 8c0d3a02c1309eb6112d2e7c8172e8ceb26ecfca Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Tue, 24 Jul 2012 17:20:05 +0800 Subject: PCI: Add accessors for PCI Express Capability The PCI Express Capability (PCIe spec r3.0, sec 7.8) comes in two versions, v1 and v2. In v1 Capability structures (PCIe spec r1.0 and r1.1), some fields are optional, so the structure size depends on the device type. This patch adds functions to access this capability so drivers don't have to be aware of the differences between v1 and v2. Note that these new functions apply only to the "PCI Express Capability," not to any of the other "PCI Express Extended Capabilities" (AER, VC, ACS, MFVC, etc.) Function pcie_capability_read_word/dword() reads the PCIe Capabilities register and returns the value in the reference parameter "val". If the PCIe Capabilities register is not implemented on the PCIe device, "val" is set to 0. Function pcie_capability_write_word/dword() writes the value to the specified PCIe Capability register. Function pcie_capability_clear_and_set_word/dword() sets and/or clears bits of a PCIe Capability register. [bhelgaas: changelog, drop "pci_" prefixes, don't export pcie_capability_reg_implemented()] Signed-off-by: Jiang Liu Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas --- drivers/pci/access.c | 202 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 33 ++++++++ include/linux/pci_regs.h | 1 + 3 files changed, 236 insertions(+) (limited to 'include') diff --git a/drivers/pci/access.c b/drivers/pci/access.c index ba91a7e17519..3af0478c057b 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -469,3 +469,205 @@ void pci_cfg_access_unlock(struct pci_dev *dev) raw_spin_unlock_irqrestore(&pci_lock, flags); } EXPORT_SYMBOL_GPL(pci_cfg_access_unlock); + +static inline int pcie_cap_version(const struct pci_dev *dev) +{ + return dev->pcie_flags_reg & PCI_EXP_FLAGS_VERS; +} + +static inline bool pcie_cap_has_devctl(const struct pci_dev *dev) +{ + return true; +} + +static inline bool pcie_cap_has_lnkctl(const struct pci_dev *dev) +{ + int type = pci_pcie_type(dev); + + return pcie_cap_version(dev) > 1 || + type == PCI_EXP_TYPE_ROOT_PORT || + type == PCI_EXP_TYPE_ENDPOINT || + type == PCI_EXP_TYPE_LEG_END; +} + +static inline bool pcie_cap_has_sltctl(const struct pci_dev *dev) +{ + int type = pci_pcie_type(dev); + + return pcie_cap_version(dev) > 1 || + type == PCI_EXP_TYPE_ROOT_PORT || + (type == PCI_EXP_TYPE_DOWNSTREAM && + dev->pcie_flags_reg & PCI_EXP_FLAGS_SLOT); +} + +static inline bool pcie_cap_has_rtctl(const struct pci_dev *dev) +{ + int type = pci_pcie_type(dev); + + return pcie_cap_version(dev) > 1 || + type == PCI_EXP_TYPE_ROOT_PORT || + type == PCI_EXP_TYPE_RC_EC; +} + +static bool pcie_capability_reg_implemented(struct pci_dev *dev, int pos) +{ + if (!pci_is_pcie(dev)) + return false; + + switch (pos) { + case PCI_EXP_FLAGS_TYPE: + return true; + case PCI_EXP_DEVCAP: + case PCI_EXP_DEVCTL: + case PCI_EXP_DEVSTA: + return pcie_cap_has_devctl(dev); + case PCI_EXP_LNKCAP: + case PCI_EXP_LNKCTL: + case PCI_EXP_LNKSTA: + return pcie_cap_has_lnkctl(dev); + case PCI_EXP_SLTCAP: + case PCI_EXP_SLTCTL: + case PCI_EXP_SLTSTA: + return pcie_cap_has_sltctl(dev); + case PCI_EXP_RTCTL: + case PCI_EXP_RTCAP: + case PCI_EXP_RTSTA: + return pcie_cap_has_rtctl(dev); + case PCI_EXP_DEVCAP2: + case PCI_EXP_DEVCTL2: + case PCI_EXP_LNKCAP2: + case PCI_EXP_LNKCTL2: + case PCI_EXP_LNKSTA2: + return pcie_cap_version(dev) > 1; + default: + return false; + } +} + +/* + * Note that these accessor functions are only for the "PCI Express + * Capability" (see PCIe spec r3.0, sec 7.8). They do not apply to the + * other "PCI Express Extended Capabilities" (AER, VC, ACS, MFVC, etc.) + */ +int pcie_capability_read_word(struct pci_dev *dev, int pos, u16 *val) +{ + int ret; + + *val = 0; + if (pos & 1) + return -EINVAL; + + if (pcie_capability_reg_implemented(dev, pos)) { + ret = pci_read_config_word(dev, pci_pcie_cap(dev) + pos, val); + /* + * Reset *val to 0 if pci_read_config_word() fails, it may + * have been written as 0xFFFF if hardware error happens + * during pci_read_config_word(). + */ + if (ret) + *val = 0; + return ret; + } + + /* + * For Functions that do not implement the Slot Capabilities, + * Slot Status, and Slot Control registers, these spaces must + * be hardwired to 0b, with the exception of the Presence Detect + * State bit in the Slot Status register of Downstream Ports, + * which must be hardwired to 1b. (PCIe Base Spec 3.0, sec 7.8) + */ + if (pci_is_pcie(dev) && pos == PCI_EXP_SLTSTA && + pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) { + *val = PCI_EXP_SLTSTA_PDS; + } + + return 0; +} +EXPORT_SYMBOL(pcie_capability_read_word); + +int pcie_capability_read_dword(struct pci_dev *dev, int pos, u32 *val) +{ + int ret; + + *val = 0; + if (pos & 3) + return -EINVAL; + + if (pcie_capability_reg_implemented(dev, pos)) { + ret = pci_read_config_dword(dev, pci_pcie_cap(dev) + pos, val); + /* + * Reset *val to 0 if pci_read_config_dword() fails, it may + * have been written as 0xFFFFFFFF if hardware error happens + * during pci_read_config_dword(). + */ + if (ret) + *val = 0; + return ret; + } + + if (pci_is_pcie(dev) && pos == PCI_EXP_SLTCTL && + pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) { + *val = PCI_EXP_SLTSTA_PDS; + } + + return 0; +} +EXPORT_SYMBOL(pcie_capability_read_dword); + +int pcie_capability_write_word(struct pci_dev *dev, int pos, u16 val) +{ + if (pos & 1) + return -EINVAL; + + if (!pcie_capability_reg_implemented(dev, pos)) + return 0; + + return pci_write_config_word(dev, pci_pcie_cap(dev) + pos, val); +} +EXPORT_SYMBOL(pcie_capability_write_word); + +int pcie_capability_write_dword(struct pci_dev *dev, int pos, u32 val) +{ + if (pos & 3) + return -EINVAL; + + if (!pcie_capability_reg_implemented(dev, pos)) + return 0; + + return pci_write_config_dword(dev, pci_pcie_cap(dev) + pos, val); +} +EXPORT_SYMBOL(pcie_capability_write_dword); + +int pcie_capability_clear_and_set_word(struct pci_dev *dev, int pos, + u16 clear, u16 set) +{ + int ret; + u16 val; + + ret = pcie_capability_read_word(dev, pos, &val); + if (!ret) { + val &= ~clear; + val |= set; + ret = pcie_capability_write_word(dev, pos, val); + } + + return ret; +} +EXPORT_SYMBOL(pcie_capability_clear_and_set_word); + +int pcie_capability_clear_and_set_dword(struct pci_dev *dev, int pos, + u32 clear, u32 set) +{ + int ret; + u32 val; + + ret = pcie_capability_read_dword(dev, pos, &val); + if (!ret) { + val &= ~clear; + val |= set; + ret = pcie_capability_write_dword(dev, pos, val); + } + + return ret; +} +EXPORT_SYMBOL(pcie_capability_clear_and_set_dword); diff --git a/include/linux/pci.h b/include/linux/pci.h index 9807da507e1f..b8667e0548e0 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -816,6 +816,39 @@ static inline int pci_write_config_dword(const struct pci_dev *dev, int where, return pci_bus_write_config_dword(dev->bus, dev->devfn, where, val); } +int pcie_capability_read_word(struct pci_dev *dev, int pos, u16 *val); +int pcie_capability_read_dword(struct pci_dev *dev, int pos, u32 *val); +int pcie_capability_write_word(struct pci_dev *dev, int pos, u16 val); +int pcie_capability_write_dword(struct pci_dev *dev, int pos, u32 val); +int pcie_capability_clear_and_set_word(struct pci_dev *dev, int pos, + u16 clear, u16 set); +int pcie_capability_clear_and_set_dword(struct pci_dev *dev, int pos, + u32 clear, u32 set); + +static inline int pcie_capability_set_word(struct pci_dev *dev, int pos, + u16 set) +{ + return pcie_capability_clear_and_set_word(dev, pos, 0, set); +} + +static inline int pcie_capability_set_dword(struct pci_dev *dev, int pos, + u32 set) +{ + return pcie_capability_clear_and_set_dword(dev, pos, 0, set); +} + +static inline int pcie_capability_clear_word(struct pci_dev *dev, int pos, + u16 clear) +{ + return pcie_capability_clear_and_set_word(dev, pos, clear, 0); +} + +static inline int pcie_capability_clear_dword(struct pci_dev *dev, int pos, + u32 clear) +{ + return pcie_capability_clear_and_set_dword(dev, pos, clear, 0); +} + /* user-space driven config access */ int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val); int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val); diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index 7fb75b143755..3958f70f3202 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -549,6 +549,7 @@ #define PCI_EXP_LNKCAP2_SLS_8_0GB 0x04 /* Current Link Speed 8.0GT/s */ #define PCI_EXP_LNKCAP2_CROSSLINK 0x100 /* 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 */ /* Extended Capabilities (PCI-X 2.0 and Express) */ -- cgit v1.2.3 From f63c45e0e63fd1bccb6d021fe4de20f82114a024 Mon Sep 17 00:00:00 2001 From: Rami Rosen Date: Thu, 23 Aug 2012 02:55:41 +0000 Subject: packet: fix broken build. This patch fixes a broken build due to a missing header: ... CC net/ipv4/proc.o In file included from include/net/net_namespace.h:15, from net/ipv4/proc.c:35: include/net/netns/packet.h:11: error: field 'sklist_lock' has incomplete type ... The lock of netns_packet has been replaced by a recent patch to be a mutex instead of a spinlock, but we need to replace the header file to be linux/mutex.h instead of linux/spinlock.h as well. See commit 0fa7fa98dbcc2789409ed24e885485e645803d7f: packet: Protect packet sk list with mutex (v2) patch, Signed-off-by: Rami Rosen Signed-off-by: David S. Miller --- include/net/netns/packet.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/netns/packet.h b/include/net/netns/packet.h index 4780b080a436..17ec2b95c062 100644 --- a/include/net/netns/packet.h +++ b/include/net/netns/packet.h @@ -5,7 +5,7 @@ #define __NETNS_PACKET_H__ #include -#include +#include struct netns_packet { struct mutex sklist_lock; -- cgit v1.2.3 From 6e20a0a429bd4dc07d6de16d9c247270e04e4aa0 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 14 Jun 2012 19:40:41 -0700 Subject: gpio: pcf857x: enable gpio_to_irq() support pcf857x chip has some pins, but interrupt pin is only 1 pin. And gpiolib requests 1 interrupt per 1 gpio pin. Thus, this patch added demuxed irq to pcf857x driver, and enabled gpio_to_irq(). Signed-off-by: Kuninori Morimoto Signed-off-by: Linus Walleij --- drivers/gpio/gpio-pcf857x.c | 122 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/i2c/pcf857x.h | 3 ++ 2 files changed, 125 insertions(+) (limited to 'include') diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 076e236d0da7..12e3e484d70b 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -23,7 +23,12 @@ #include #include #include +#include +#include +#include #include +#include +#include static const struct i2c_device_id pcf857x_id[] = { @@ -60,7 +65,12 @@ struct pcf857x { struct gpio_chip chip; struct i2c_client *client; struct mutex lock; /* protect 'out' */ + struct work_struct work; /* irq demux work */ + struct irq_domain *irq_domain; /* for irq demux */ + spinlock_t slock; /* protect irq demux */ unsigned out; /* software latch */ + unsigned status; /* current status */ + int irq; /* real irq number */ int (*write)(struct i2c_client *client, unsigned data); int (*read)(struct i2c_client *client); @@ -150,6 +160,100 @@ static void pcf857x_set(struct gpio_chip *chip, unsigned offset, int value) /*-------------------------------------------------------------------------*/ +static int pcf857x_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); + + return irq_create_mapping(gpio->irq_domain, offset); +} + +static void pcf857x_irq_demux_work(struct work_struct *work) +{ + struct pcf857x *gpio = container_of(work, + struct pcf857x, + work); + unsigned long change, i, status, flags; + + status = gpio->read(gpio->client); + + spin_lock_irqsave(&gpio->slock, flags); + + change = gpio->status ^ status; + for_each_set_bit(i, &change, gpio->chip.ngpio) + generic_handle_irq(irq_find_mapping(gpio->irq_domain, i)); + gpio->status = status; + + spin_unlock_irqrestore(&gpio->slock, flags); +} + +static irqreturn_t pcf857x_irq_demux(int irq, void *data) +{ + struct pcf857x *gpio = data; + + /* + * pcf857x can't read/write data here, + * since i2c data access might go to sleep. + */ + schedule_work(&gpio->work); + + return IRQ_HANDLED; +} + +static int pcf857x_irq_domain_map(struct irq_domain *domain, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_and_handler(virq, + &dummy_irq_chip, + handle_level_irq); + return 0; +} + +static struct irq_domain_ops pcf857x_irq_domain_ops = { + .map = pcf857x_irq_domain_map, +}; + +static void pcf857x_irq_domain_cleanup(struct pcf857x *gpio) +{ + if (gpio->irq_domain) + irq_domain_remove(gpio->irq_domain); + + if (gpio->irq) + free_irq(gpio->irq, gpio); +} + +static int pcf857x_irq_domain_init(struct pcf857x *gpio, + struct pcf857x_platform_data *pdata, + struct device *dev) +{ + int status; + + gpio->irq_domain = irq_domain_add_linear(dev->of_node, + gpio->chip.ngpio, + &pcf857x_irq_domain_ops, + NULL); + if (!gpio->irq_domain) + goto fail; + + /* enable real irq */ + status = request_irq(pdata->irq, pcf857x_irq_demux, 0, + dev_name(dev), gpio); + if (status) + goto fail; + + /* enable gpio_to_irq() */ + INIT_WORK(&gpio->work, pcf857x_irq_demux_work); + gpio->chip.to_irq = pcf857x_to_irq; + gpio->irq = pdata->irq; + + return 0; + +fail: + pcf857x_irq_domain_cleanup(gpio); + return -EINVAL; +} + +/*-------------------------------------------------------------------------*/ + static int pcf857x_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -168,6 +272,7 @@ static int pcf857x_probe(struct i2c_client *client, return -ENOMEM; mutex_init(&gpio->lock); + spin_lock_init(&gpio->slock); gpio->chip.base = pdata ? pdata->gpio_base : -1; gpio->chip.can_sleep = 1; @@ -179,6 +284,15 @@ static int pcf857x_probe(struct i2c_client *client, gpio->chip.direction_output = pcf857x_output; gpio->chip.ngpio = id->driver_data; + /* enable gpio_to_irq() if platform has settings */ + if (pdata->irq) { + status = pcf857x_irq_domain_init(gpio, pdata, &client->dev); + if (status < 0) { + dev_err(&client->dev, "irq_domain init failed\n"); + goto fail; + } + } + /* NOTE: the OnSemi jlc1562b is also largely compatible with * these parts, notably for output. It has a low-resolution * DAC instead of pin change IRQs; and its inputs can be the @@ -248,6 +362,7 @@ static int pcf857x_probe(struct i2c_client *client, * all-ones reset state. Otherwise it flags pins to be driven low. */ gpio->out = pdata ? ~pdata->n_latch : ~0; + gpio->status = gpio->out; status = gpiochip_add(&gpio->chip); if (status < 0) @@ -278,6 +393,10 @@ static int pcf857x_probe(struct i2c_client *client, fail: dev_dbg(&client->dev, "probe error %d for '%s'\n", status, client->name); + + if (pdata->irq) + pcf857x_irq_domain_cleanup(gpio); + kfree(gpio); return status; } @@ -299,6 +418,9 @@ static int pcf857x_remove(struct i2c_client *client) } } + if (pdata->irq) + pcf857x_irq_domain_cleanup(gpio); + status = gpiochip_remove(&gpio->chip); if (status == 0) kfree(gpio); diff --git a/include/linux/i2c/pcf857x.h b/include/linux/i2c/pcf857x.h index 0767a2a6b2f1..781e6bd06c34 100644 --- a/include/linux/i2c/pcf857x.h +++ b/include/linux/i2c/pcf857x.h @@ -10,6 +10,7 @@ * @setup: optional callback issued once the GPIOs are valid * @teardown: optional callback issued before the GPIOs are invalidated * @context: optional parameter passed to setup() and teardown() + * @irq: optional interrupt number * * In addition to the I2C_BOARD_INFO() state appropriate to each chip, * the i2c_board_info used with the pcf875x driver must provide its @@ -39,6 +40,8 @@ struct pcf857x_platform_data { int gpio, unsigned ngpio, void *context); void *context; + + int irq; }; #endif /* __LINUX_PCF857X_H */ -- cgit v1.2.3 From 22f5d115a2b087c977128f84ee557ad71530330e Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 14 Aug 2012 10:53:38 +0000 Subject: drm: Initialize object type when using DRM_MODE() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DRM_MODE() macro doesn't initialize the type of the base drm object. When a copy is made of the mode, the object type is overwritten with zero, and the the object can no longer be found by drm_mode_object_find() due to the type check failing. Signed-off-by: Ville Syrjälä Signed-off-by: Dave Airlie --- include/drm/drm_crtc.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index ced362533e3c..bfacf0d5a225 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -118,7 +118,8 @@ enum drm_mode_status { .hdisplay = (hd), .hsync_start = (hss), .hsync_end = (hse), \ .htotal = (ht), .hskew = (hsk), .vdisplay = (vd), \ .vsync_start = (vss), .vsync_end = (vse), .vtotal = (vt), \ - .vscan = (vs), .flags = (f), .vrefresh = 0 + .vscan = (vs), .flags = (f), .vrefresh = 0, \ + .base.type = DRM_MODE_OBJECT_MODE #define CRTC_INTERLACE_HALVE_V 0x1 /* halve V values for interlacing */ -- cgit v1.2.3 From 7c4eaca4162d0b5ad4fb39f974d7ffd71b9daa09 Mon Sep 17 00:00:00 2001 From: Jakob Bornecrantz Date: Thu, 16 Aug 2012 08:29:03 +0000 Subject: drm: Check for invalid cursor flags Signed-off-by: Jakob Bornecrantz Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc.c | 2 +- include/drm/drm_mode.h | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 08a7aa722d6b..6fbfc244748f 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1981,7 +1981,7 @@ int drm_mode_cursor_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - if (!req->flags) + if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags)) return -EINVAL; mutex_lock(&dev->mode_config.mutex); diff --git a/include/drm/drm_mode.h b/include/drm/drm_mode.h index 5581980b14f6..3d6301b6ec16 100644 --- a/include/drm/drm_mode.h +++ b/include/drm/drm_mode.h @@ -359,8 +359,9 @@ struct drm_mode_mode_cmd { struct drm_mode_modeinfo mode; }; -#define DRM_MODE_CURSOR_BO (1<<0) -#define DRM_MODE_CURSOR_MOVE (1<<1) +#define DRM_MODE_CURSOR_BO 0x01 +#define DRM_MODE_CURSOR_MOVE 0x02 +#define DRM_MODE_CURSOR_FLAGS 0x03 /* * depending on the value in flags different members are used. -- cgit v1.2.3 From 8f4cccbbd92f2ad0ddbbc498ef7cee2a1c3defe9 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 20 Aug 2012 22:16:51 +0100 Subject: net: Set device operstate at registration time The operstate of a device is initially IF_OPER_UNKNOWN and is updated asynchronously by linkwatch after each change of carrier state reported by the driver. The default carrier state of a net device is on, and this will never be changed on drivers that do not support carrier detection, thus the operstate remains IF_OPER_UNKNOWN. For devices that do support carrier detection, the driver must set the carrier state to off initially, then poll the hardware state when the device is opened. However, we must not activate linkwatch for a unregistered device, and commit b473001 ('net: Do not fire linkwatch events until the device is registered.') ensured that we don't. But this means that the operstate for many devices that support carrier detection remains IF_OPER_UNKNOWN when it should be IF_OPER_DOWN. The same issue exists with the dormant state. The proper initialisation sequence, avoiding a race with opening of the device, is: rtnl_lock(); rc = register_netdevice(dev); if (rc) goto out_unlock; netif_carrier_off(dev); /* or netif_dormant_on(dev) */ rtnl_unlock(); but it seems silly that this should have to be repeated in so many drivers. Further, the operstate seen immediately after opening the device may still be IF_OPER_UNKNOWN due to the asynchronous nature of linkwatch. Commit 22604c8 ('net: Fix for initial link state in 2.6.28') attempted to fix this by setting the operstate synchronously, but it was reverted as it could lead to deadlock. This initialises the operstate synchronously at registration time only. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + net/core/dev.c | 2 ++ net/core/link_watch.c | 8 ++++++++ 3 files changed, 11 insertions(+) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9ad7fa8c10e0..ccac82e61604 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2227,6 +2227,7 @@ static inline void dev_hold(struct net_device *dev) * kind of lower layer not just hardware media. */ +extern void linkwatch_init_dev(struct net_device *dev); extern void linkwatch_fire_event(struct net_device *dev); extern void linkwatch_forget_dev(struct net_device *dev); diff --git a/net/core/dev.c b/net/core/dev.c index bc857fead8c8..2f25d0cac51c 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5648,6 +5648,8 @@ int register_netdevice(struct net_device *dev) set_bit(__LINK_STATE_PRESENT, &dev->state); + linkwatch_init_dev(dev); + dev_init_scheduler(dev); dev_hold(dev); list_netdevice(dev); diff --git a/net/core/link_watch.c b/net/core/link_watch.c index c3519c6d1b16..a01922219a23 100644 --- a/net/core/link_watch.c +++ b/net/core/link_watch.c @@ -76,6 +76,14 @@ static void rfc2863_policy(struct net_device *dev) } +void linkwatch_init_dev(struct net_device *dev) +{ + /* Handle pre-registration link state changes */ + if (!netif_carrier_ok(dev) || netif_dormant(dev)) + rfc2863_policy(dev); +} + + static bool linkwatch_urgent_event(struct net_device *dev) { if (!netif_running(dev)) -- cgit v1.2.3 From 9b361c13ceeae872161175d39f4798ee345ed10c Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 23 Aug 2012 03:26:52 +0000 Subject: vlan: add helper which can be called to see if device is used by vlan also, remove unused vlan_info definition from header CC: Patrick McHardy Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/linux/if_vlan.h | 9 +++++++-- net/8021q/vlan_core.c | 6 ++++++ 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index a810987cb80e..e6ff12dd717b 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -74,8 +74,6 @@ static inline struct vlan_ethhdr *vlan_eth_hdr(const struct sk_buff *skb) /* found in socket.c */ extern void vlan_ioctl_set(int (*hook)(struct net *, void __user *)); -struct vlan_info; - static inline int is_vlan_dev(struct net_device *dev) { return dev->priv_flags & IFF_802_1Q_VLAN; @@ -101,6 +99,8 @@ extern int vlan_vids_add_by_dev(struct net_device *dev, const struct net_device *by_dev); extern void vlan_vids_del_by_dev(struct net_device *dev, const struct net_device *by_dev); + +extern bool vlan_uses_dev(const struct net_device *dev); #else static inline struct net_device * __vlan_find_dev_deep(struct net_device *real_dev, u16 vlan_id) @@ -151,6 +151,11 @@ static inline void vlan_vids_del_by_dev(struct net_device *dev, const struct net_device *by_dev) { } + +static inline bool vlan_uses_dev(const struct net_device *dev) +{ + return false; +} #endif /** diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index 8ca533c95de0..b258da88f675 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c @@ -368,3 +368,9 @@ void vlan_vids_del_by_dev(struct net_device *dev, vlan_vid_del(dev, vid_info->vid); } EXPORT_SYMBOL(vlan_vids_del_by_dev); + +bool vlan_uses_dev(const struct net_device *dev) +{ + return rtnl_dereference(dev->vlan_info) ? true : false; +} +EXPORT_SYMBOL(vlan_uses_dev); -- cgit v1.2.3 From 38f38657444d15e1a8574eae80ed3de9f501737a Mon Sep 17 00:00:00 2001 From: Aristeu Rozanski Date: Thu, 23 Aug 2012 16:53:28 -0400 Subject: xattr: extract simple_xattr code from tmpfs Extract in-memory xattr APIs from tmpfs. Will be used by cgroup. $ size vmlinux.o text data bss dec hex filename 4658782 880729 5195032 10734543 a3cbcf vmlinux.o $ size vmlinux.o text data bss dec hex filename 4658957 880729 5195032 10734718 a3cc7e vmlinux.o v7: - checkpatch warnings fixed - Implement the changes requested by Hugh Dickins: - make simple_xattrs_init and simple_xattrs_free inline - get rid of locking and list reinitialization in simple_xattrs_free, they're not needed v6: - no changes v5: - no changes v4: - move simple_xattrs_free() to fs/xattr.c v3: - in kmem_xattrs_free(), reinitialize the list - use simple_xattr_* prefix - introduce simple_xattr_add() to prevent direct list usage Original-patch-by: Li Zefan Cc: Li Zefan Cc: Hillf Danton Cc: Lennart Poettering Acked-by: Hugh Dickins Signed-off-by: Li Zefan Signed-off-by: Aristeu Rozanski Signed-off-by: Tejun Heo --- fs/xattr.c | 166 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/shmem_fs.h | 3 +- include/linux/xattr.h | 48 +++++++++++++ mm/shmem.c | 171 ++++------------------------------------------- 4 files changed, 229 insertions(+), 159 deletions(-) (limited to 'include') diff --git a/fs/xattr.c b/fs/xattr.c index 4d45b7189e7e..e17e773517ef 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -791,3 +791,169 @@ EXPORT_SYMBOL(generic_getxattr); EXPORT_SYMBOL(generic_listxattr); EXPORT_SYMBOL(generic_setxattr); EXPORT_SYMBOL(generic_removexattr); + +/* + * Allocate new xattr and copy in the value; but leave the name to callers. + */ +struct simple_xattr *simple_xattr_alloc(const void *value, size_t size) +{ + struct simple_xattr *new_xattr; + size_t len; + + /* wrap around? */ + len = sizeof(*new_xattr) + size; + if (len <= sizeof(*new_xattr)) + return NULL; + + new_xattr = kmalloc(len, GFP_KERNEL); + if (!new_xattr) + return NULL; + + new_xattr->size = size; + memcpy(new_xattr->value, value, size); + return new_xattr; +} + +/* + * xattr GET operation for in-memory/pseudo filesystems + */ +int simple_xattr_get(struct simple_xattrs *xattrs, const char *name, + void *buffer, size_t size) +{ + struct simple_xattr *xattr; + int ret = -ENODATA; + + spin_lock(&xattrs->lock); + list_for_each_entry(xattr, &xattrs->head, list) { + if (strcmp(name, xattr->name)) + continue; + + ret = xattr->size; + if (buffer) { + if (size < xattr->size) + ret = -ERANGE; + else + memcpy(buffer, xattr->value, xattr->size); + } + break; + } + spin_unlock(&xattrs->lock); + return ret; +} + +static int __simple_xattr_set(struct simple_xattrs *xattrs, const char *name, + const void *value, size_t size, int flags) +{ + struct simple_xattr *xattr; + struct simple_xattr *new_xattr = NULL; + int err = 0; + + /* value == NULL means remove */ + if (value) { + new_xattr = simple_xattr_alloc(value, size); + if (!new_xattr) + return -ENOMEM; + + new_xattr->name = kstrdup(name, GFP_KERNEL); + if (!new_xattr->name) { + kfree(new_xattr); + return -ENOMEM; + } + } + + spin_lock(&xattrs->lock); + list_for_each_entry(xattr, &xattrs->head, list) { + if (!strcmp(name, xattr->name)) { + if (flags & XATTR_CREATE) { + xattr = new_xattr; + err = -EEXIST; + } else if (new_xattr) { + list_replace(&xattr->list, &new_xattr->list); + } else { + list_del(&xattr->list); + } + goto out; + } + } + if (flags & XATTR_REPLACE) { + xattr = new_xattr; + err = -ENODATA; + } else { + list_add(&new_xattr->list, &xattrs->head); + xattr = NULL; + } +out: + spin_unlock(&xattrs->lock); + if (xattr) { + kfree(xattr->name); + kfree(xattr); + } + return err; + +} + +/* + * xattr SET operation for in-memory/pseudo filesystems + */ +int simple_xattr_set(struct simple_xattrs *xattrs, const char *name, + const void *value, size_t size, int flags) +{ + if (size == 0) + value = ""; /* empty EA, do not remove */ + return __simple_xattr_set(xattrs, name, value, size, flags); +} + +/* + * xattr REMOVE operation for in-memory/pseudo filesystems + */ +int simple_xattr_remove(struct simple_xattrs *xattrs, const char *name) +{ + return __simple_xattr_set(xattrs, name, NULL, 0, XATTR_REPLACE); +} + +static bool xattr_is_trusted(const char *name) +{ + return !strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN); +} + +/* + * xattr LIST operation for in-memory/pseudo filesystems + */ +ssize_t simple_xattr_list(struct simple_xattrs *xattrs, char *buffer, + size_t size) +{ + bool trusted = capable(CAP_SYS_ADMIN); + struct simple_xattr *xattr; + size_t used = 0; + + spin_lock(&xattrs->lock); + list_for_each_entry(xattr, &xattrs->head, list) { + size_t len; + + /* skip "trusted." attributes for unprivileged callers */ + if (!trusted && xattr_is_trusted(xattr->name)) + continue; + + len = strlen(xattr->name) + 1; + used += len; + if (buffer) { + if (size < used) { + used = -ERANGE; + break; + } + memcpy(buffer, xattr->name, len); + buffer += len; + } + } + spin_unlock(&xattrs->lock); + + return used; +} + +void simple_xattr_list_add(struct simple_xattrs *xattrs, + struct simple_xattr *new_xattr) +{ + spin_lock(&xattrs->lock); + list_add(&new_xattr->list, &xattrs->head); + spin_unlock(&xattrs->lock); +} diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index bef2cf00b3be..30aa0dc60d75 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h @@ -5,6 +5,7 @@ #include #include #include +#include /* inode in-kernel data */ @@ -18,7 +19,7 @@ struct shmem_inode_info { }; struct shared_policy policy; /* NUMA memory alloc policy */ struct list_head swaplist; /* chain of maybes on swap */ - struct list_head xattr_list; /* list of shmem_xattr */ + struct simple_xattrs xattrs; /* list of xattrs */ struct inode vfs_inode; }; diff --git a/include/linux/xattr.h b/include/linux/xattr.h index e5d122031542..2ace7a60316d 100644 --- a/include/linux/xattr.h +++ b/include/linux/xattr.h @@ -59,7 +59,9 @@ #ifdef __KERNEL__ +#include #include +#include struct inode; struct dentry; @@ -96,6 +98,52 @@ ssize_t vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value, size_t size, gfp_t flags); int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name, const char *value, size_t size, gfp_t flags); + +struct simple_xattrs { + struct list_head head; + spinlock_t lock; +}; + +struct simple_xattr { + struct list_head list; + char *name; + size_t size; + char value[0]; +}; + +/* + * initialize the simple_xattrs structure + */ +static inline void simple_xattrs_init(struct simple_xattrs *xattrs) +{ + INIT_LIST_HEAD(&xattrs->head); + spin_lock_init(&xattrs->lock); +} + +/* + * free all the xattrs + */ +static inline void simple_xattrs_free(struct simple_xattrs *xattrs) +{ + struct simple_xattr *xattr, *node; + + list_for_each_entry_safe(xattr, node, &xattrs->head, list) { + kfree(xattr->name); + kfree(xattr); + } +} + +struct simple_xattr *simple_xattr_alloc(const void *value, size_t size); +int simple_xattr_get(struct simple_xattrs *xattrs, const char *name, + void *buffer, size_t size); +int simple_xattr_set(struct simple_xattrs *xattrs, const char *name, + const void *value, size_t size, int flags); +int simple_xattr_remove(struct simple_xattrs *xattrs, const char *name); +ssize_t simple_xattr_list(struct simple_xattrs *xattrs, char *buffer, + size_t size); +void simple_xattr_list_add(struct simple_xattrs *xattrs, + struct simple_xattr *new_xattr); + #endif /* __KERNEL__ */ #endif /* _LINUX_XATTR_H */ diff --git a/mm/shmem.c b/mm/shmem.c index d4e184e2a38e..d3752110c8c7 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -77,13 +77,6 @@ static struct vfsmount *shm_mnt; /* Symlink up to this size is kmalloc'ed instead of using a swappable page */ #define SHORT_SYMLINK_LEN 128 -struct shmem_xattr { - struct list_head list; /* anchored by shmem_inode_info->xattr_list */ - char *name; /* xattr name */ - size_t size; - char value[0]; -}; - /* * shmem_fallocate and shmem_writepage communicate via inode->i_private * (with i_mutex making sure that it has only one user at a time): @@ -636,7 +629,6 @@ static int shmem_setattr(struct dentry *dentry, struct iattr *attr) static void shmem_evict_inode(struct inode *inode) { struct shmem_inode_info *info = SHMEM_I(inode); - struct shmem_xattr *xattr, *nxattr; if (inode->i_mapping->a_ops == &shmem_aops) { shmem_unacct_size(info->flags, inode->i_size); @@ -650,10 +642,7 @@ static void shmem_evict_inode(struct inode *inode) } else kfree(info->symlink); - list_for_each_entry_safe(xattr, nxattr, &info->xattr_list, list) { - kfree(xattr->name); - kfree(xattr); - } + simple_xattrs_free(&info->xattrs); BUG_ON(inode->i_blocks); shmem_free_inode(inode->i_sb); clear_inode(inode); @@ -1377,7 +1366,7 @@ static struct inode *shmem_get_inode(struct super_block *sb, const struct inode spin_lock_init(&info->lock); info->flags = flags & VM_NORESERVE; INIT_LIST_HEAD(&info->swaplist); - INIT_LIST_HEAD(&info->xattr_list); + simple_xattrs_init(&info->xattrs); cache_no_acl(inode); switch (mode & S_IFMT) { @@ -2059,28 +2048,6 @@ static void shmem_put_link(struct dentry *dentry, struct nameidata *nd, void *co * filesystem level, though. */ -/* - * Allocate new xattr and copy in the value; but leave the name to callers. - */ -static struct shmem_xattr *shmem_xattr_alloc(const void *value, size_t size) -{ - struct shmem_xattr *new_xattr; - size_t len; - - /* wrap around? */ - len = sizeof(*new_xattr) + size; - if (len <= sizeof(*new_xattr)) - return NULL; - - new_xattr = kmalloc(len, GFP_KERNEL); - if (!new_xattr) - return NULL; - - new_xattr->size = size; - memcpy(new_xattr->value, value, size); - return new_xattr; -} - /* * Callback for security_inode_init_security() for acquiring xattrs. */ @@ -2090,11 +2057,11 @@ static int shmem_initxattrs(struct inode *inode, { struct shmem_inode_info *info = SHMEM_I(inode); const struct xattr *xattr; - struct shmem_xattr *new_xattr; + struct simple_xattr *new_xattr; size_t len; for (xattr = xattr_array; xattr->name != NULL; xattr++) { - new_xattr = shmem_xattr_alloc(xattr->value, xattr->value_len); + new_xattr = simple_xattr_alloc(xattr->value, xattr->value_len); if (!new_xattr) return -ENOMEM; @@ -2111,91 +2078,12 @@ static int shmem_initxattrs(struct inode *inode, memcpy(new_xattr->name + XATTR_SECURITY_PREFIX_LEN, xattr->name, len); - spin_lock(&info->lock); - list_add(&new_xattr->list, &info->xattr_list); - spin_unlock(&info->lock); + simple_xattr_list_add(&info->xattrs, new_xattr); } return 0; } -static int shmem_xattr_get(struct dentry *dentry, const char *name, - void *buffer, size_t size) -{ - struct shmem_inode_info *info; - struct shmem_xattr *xattr; - int ret = -ENODATA; - - info = SHMEM_I(dentry->d_inode); - - spin_lock(&info->lock); - list_for_each_entry(xattr, &info->xattr_list, list) { - if (strcmp(name, xattr->name)) - continue; - - ret = xattr->size; - if (buffer) { - if (size < xattr->size) - ret = -ERANGE; - else - memcpy(buffer, xattr->value, xattr->size); - } - break; - } - spin_unlock(&info->lock); - return ret; -} - -static int shmem_xattr_set(struct inode *inode, const char *name, - const void *value, size_t size, int flags) -{ - struct shmem_inode_info *info = SHMEM_I(inode); - struct shmem_xattr *xattr; - struct shmem_xattr *new_xattr = NULL; - int err = 0; - - /* value == NULL means remove */ - if (value) { - new_xattr = shmem_xattr_alloc(value, size); - if (!new_xattr) - return -ENOMEM; - - new_xattr->name = kstrdup(name, GFP_KERNEL); - if (!new_xattr->name) { - kfree(new_xattr); - return -ENOMEM; - } - } - - spin_lock(&info->lock); - list_for_each_entry(xattr, &info->xattr_list, list) { - if (!strcmp(name, xattr->name)) { - if (flags & XATTR_CREATE) { - xattr = new_xattr; - err = -EEXIST; - } else if (new_xattr) { - list_replace(&xattr->list, &new_xattr->list); - } else { - list_del(&xattr->list); - } - goto out; - } - } - if (flags & XATTR_REPLACE) { - xattr = new_xattr; - err = -ENODATA; - } else { - list_add(&new_xattr->list, &info->xattr_list); - xattr = NULL; - } -out: - spin_unlock(&info->lock); - if (xattr) - kfree(xattr->name); - kfree(xattr); - return err; -} - static const struct xattr_handler *shmem_xattr_handlers[] = { #ifdef CONFIG_TMPFS_POSIX_ACL &generic_acl_access_handler, @@ -2226,6 +2114,7 @@ static int shmem_xattr_validate(const char *name) static ssize_t shmem_getxattr(struct dentry *dentry, const char *name, void *buffer, size_t size) { + struct shmem_inode_info *info = SHMEM_I(dentry->d_inode); int err; /* @@ -2240,12 +2129,13 @@ static ssize_t shmem_getxattr(struct dentry *dentry, const char *name, if (err) return err; - return shmem_xattr_get(dentry, name, buffer, size); + return simple_xattr_get(&info->xattrs, name, buffer, size); } static int shmem_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { + struct shmem_inode_info *info = SHMEM_I(dentry->d_inode); int err; /* @@ -2260,15 +2150,12 @@ static int shmem_setxattr(struct dentry *dentry, const char *name, if (err) return err; - if (size == 0) - value = ""; /* empty EA, do not remove */ - - return shmem_xattr_set(dentry->d_inode, name, value, size, flags); - + return simple_xattr_set(&info->xattrs, name, value, size, flags); } static int shmem_removexattr(struct dentry *dentry, const char *name) { + struct shmem_inode_info *info = SHMEM_I(dentry->d_inode); int err; /* @@ -2283,45 +2170,13 @@ static int shmem_removexattr(struct dentry *dentry, const char *name) if (err) return err; - return shmem_xattr_set(dentry->d_inode, name, NULL, 0, XATTR_REPLACE); -} - -static bool xattr_is_trusted(const char *name) -{ - return !strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN); + return simple_xattr_remove(&info->xattrs, name); } static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size) { - bool trusted = capable(CAP_SYS_ADMIN); - struct shmem_xattr *xattr; - struct shmem_inode_info *info; - size_t used = 0; - - info = SHMEM_I(dentry->d_inode); - - spin_lock(&info->lock); - list_for_each_entry(xattr, &info->xattr_list, list) { - size_t len; - - /* skip "trusted." attributes for unprivileged callers */ - if (!trusted && xattr_is_trusted(xattr->name)) - continue; - - len = strlen(xattr->name) + 1; - used += len; - if (buffer) { - if (size < used) { - used = -ERANGE; - break; - } - memcpy(buffer, xattr->name, len); - buffer += len; - } - } - spin_unlock(&info->lock); - - return used; + struct shmem_inode_info *info = SHMEM_I(dentry->d_inode); + return simple_xattr_list(&info->xattrs, buffer, size); } #endif /* CONFIG_TMPFS_XATTR */ -- cgit v1.2.3 From 03b1cde6b22f625ae832b939bc7379ec1466aec5 Mon Sep 17 00:00:00 2001 From: Aristeu Rozanski Date: Thu, 23 Aug 2012 16:53:30 -0400 Subject: cgroup: add xattr support This is one of the items in the plumber's wish list. For use cases: >> What would the use case be for this? > > Attaching meta information to services, in an easily discoverable > way. For example, in systemd we create one cgroup for each service, and > could then store data like the main pid of the specific service as an > xattr on the cgroup itself. That way we'd have almost all service state > in the cgroupfs, which would make it possible to terminate systemd and > later restart it without losing any state information. But there's more: > for example, some very peculiar services cannot be terminated on > shutdown (i.e. fakeraid DM stuff) and it would be really nice if the > services in question could just mark that on their cgroup, by setting an > xattr. On the more desktopy side of things there are other > possibilities: for example there are plans defining what an application > is along the lines of a cgroup (i.e. an app being a collection of > processes). With xattrs one could then attach an icon or human readable > program name on the cgroup. > > The key idea is that this would allow attaching runtime meta information > to cgroups and everything they model (services, apps, vms), that doesn't > need any complex userspace infrastructure, has good access control > (i.e. because the file system enforces that anyway, and there's the > "trusted." xattr namespace), notifications (inotify), and can easily be > shared among applications. > > Lennart v7: - no changes v6: - remove user xattr namespace, only allow trusted and security v5: - check for capabilities before setting/removing xattrs v4: - no changes v3: - instead of config option, use mount option to enable xattr support Original-patch-by: Li Zefan Cc: Li Zefan Cc: Tejun Heo Cc: Hugh Dickins Cc: Hillf Danton Cc: Lennart Poettering Signed-off-by: Li Zefan Signed-off-by: Aristeu Rozanski Signed-off-by: Tejun Heo --- include/linux/cgroup.h | 13 +++++-- kernel/cgroup.c | 100 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 103 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index c90eaa803440..145901f5ef99 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -17,6 +17,7 @@ #include #include #include +#include #ifdef CONFIG_CGROUPS @@ -216,6 +217,9 @@ struct cgroup { /* List of events which userspace want to receive */ struct list_head event_list; spinlock_t event_list_lock; + + /* directory xattrs */ + struct simple_xattrs xattrs; }; /* @@ -309,6 +313,9 @@ struct cftype { /* CFTYPE_* flags */ unsigned int flags; + /* file xattrs */ + struct simple_xattrs xattrs; + int (*open)(struct inode *inode, struct file *file); ssize_t (*read)(struct cgroup *cgrp, struct cftype *cft, struct file *file, @@ -394,7 +401,7 @@ struct cftype { */ struct cftype_set { struct list_head node; /* chained at subsys->cftsets */ - const struct cftype *cfts; + struct cftype *cfts; }; struct cgroup_scanner { @@ -406,8 +413,8 @@ struct cgroup_scanner { void *data; }; -int cgroup_add_cftypes(struct cgroup_subsys *ss, const struct cftype *cfts); -int cgroup_rm_cftypes(struct cgroup_subsys *ss, const struct cftype *cfts); +int cgroup_add_cftypes(struct cgroup_subsys *ss, struct cftype *cfts); +int cgroup_rm_cftypes(struct cgroup_subsys *ss, struct cftype *cfts); int cgroup_is_removed(const struct cgroup *cgrp); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 875a7130647c..508b4a97ab19 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -276,7 +276,8 @@ inline int cgroup_is_removed(const struct cgroup *cgrp) /* bits in struct cgroupfs_root flags field */ enum { - ROOT_NOPREFIX, /* mounted subsystems have no named prefix */ + ROOT_NOPREFIX, /* mounted subsystems have no named prefix */ + ROOT_XATTR, /* supports extended attributes */ }; static int cgroup_is_releasable(const struct cgroup *cgrp) @@ -913,15 +914,19 @@ static void cgroup_diput(struct dentry *dentry, struct inode *inode) */ BUG_ON(!list_empty(&cgrp->pidlists)); + simple_xattrs_free(&cgrp->xattrs); + kfree_rcu(cgrp, rcu_head); } else { struct cfent *cfe = __d_cfe(dentry); struct cgroup *cgrp = dentry->d_parent->d_fsdata; + struct cftype *cft = cfe->type; WARN_ONCE(!list_empty(&cfe->node) && cgrp != &cgrp->root->top_cgroup, "cfe still linked for %s\n", cfe->type->name); kfree(cfe); + simple_xattrs_free(&cft->xattrs); } iput(inode); } @@ -1140,6 +1145,8 @@ static int cgroup_show_options(struct seq_file *seq, struct dentry *dentry) seq_printf(seq, ",%s", ss->name); if (test_bit(ROOT_NOPREFIX, &root->flags)) seq_puts(seq, ",noprefix"); + if (test_bit(ROOT_XATTR, &root->flags)) + seq_puts(seq, ",xattr"); if (strlen(root->release_agent_path)) seq_printf(seq, ",release_agent=%s", root->release_agent_path); if (clone_children(&root->top_cgroup)) @@ -1208,6 +1215,10 @@ static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts) opts->clone_children = true; continue; } + if (!strcmp(token, "xattr")) { + set_bit(ROOT_XATTR, &opts->flags); + continue; + } if (!strncmp(token, "release_agent=", 14)) { /* Specifying two release agents is forbidden */ if (opts->release_agent) @@ -1425,6 +1436,7 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp) mutex_init(&cgrp->pidlist_mutex); INIT_LIST_HEAD(&cgrp->event_list); spin_lock_init(&cgrp->event_list_lock); + simple_xattrs_init(&cgrp->xattrs); } static void init_cgroup_root(struct cgroupfs_root *root) @@ -1769,6 +1781,8 @@ static void cgroup_kill_sb(struct super_block *sb) { mutex_unlock(&cgroup_root_mutex); mutex_unlock(&cgroup_mutex); + simple_xattrs_free(&cgrp->xattrs); + kill_litter_super(sb); cgroup_drop_root(root); } @@ -2575,6 +2589,64 @@ static int cgroup_rename(struct inode *old_dir, struct dentry *old_dentry, return simple_rename(old_dir, old_dentry, new_dir, new_dentry); } +static struct simple_xattrs *__d_xattrs(struct dentry *dentry) +{ + if (S_ISDIR(dentry->d_inode->i_mode)) + return &__d_cgrp(dentry)->xattrs; + else + return &__d_cft(dentry)->xattrs; +} + +static inline int xattr_enabled(struct dentry *dentry) +{ + struct cgroupfs_root *root = dentry->d_sb->s_fs_info; + return test_bit(ROOT_XATTR, &root->flags); +} + +static bool is_valid_xattr(const char *name) +{ + if (!strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) || + !strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN)) + return true; + return false; +} + +static int cgroup_setxattr(struct dentry *dentry, const char *name, + const void *val, size_t size, int flags) +{ + if (!xattr_enabled(dentry)) + return -EOPNOTSUPP; + if (!is_valid_xattr(name)) + return -EINVAL; + return simple_xattr_set(__d_xattrs(dentry), name, val, size, flags); +} + +static int cgroup_removexattr(struct dentry *dentry, const char *name) +{ + if (!xattr_enabled(dentry)) + return -EOPNOTSUPP; + if (!is_valid_xattr(name)) + return -EINVAL; + return simple_xattr_remove(__d_xattrs(dentry), name); +} + +static ssize_t cgroup_getxattr(struct dentry *dentry, const char *name, + void *buf, size_t size) +{ + if (!xattr_enabled(dentry)) + return -EOPNOTSUPP; + if (!is_valid_xattr(name)) + return -EINVAL; + return simple_xattr_get(__d_xattrs(dentry), name, buf, size); +} + +static ssize_t cgroup_listxattr(struct dentry *dentry, char *buf, size_t size) +{ + if (!xattr_enabled(dentry)) + return -EOPNOTSUPP; + return simple_xattr_list(__d_xattrs(dentry), buf, size); +} + static const struct file_operations cgroup_file_operations = { .read = cgroup_file_read, .write = cgroup_file_write, @@ -2583,11 +2655,22 @@ static const struct file_operations cgroup_file_operations = { .release = cgroup_file_release, }; +static const struct inode_operations cgroup_file_inode_operations = { + .setxattr = cgroup_setxattr, + .getxattr = cgroup_getxattr, + .listxattr = cgroup_listxattr, + .removexattr = cgroup_removexattr, +}; + static const struct inode_operations cgroup_dir_inode_operations = { .lookup = cgroup_lookup, .mkdir = cgroup_mkdir, .rmdir = cgroup_rmdir, .rename = cgroup_rename, + .setxattr = cgroup_setxattr, + .getxattr = cgroup_getxattr, + .listxattr = cgroup_listxattr, + .removexattr = cgroup_removexattr, }; static struct dentry *cgroup_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) @@ -2635,6 +2718,7 @@ static int cgroup_create_file(struct dentry *dentry, umode_t mode, } else if (S_ISREG(mode)) { inode->i_size = 0; inode->i_fop = &cgroup_file_operations; + inode->i_op = &cgroup_file_inode_operations; } d_instantiate(dentry, inode); dget(dentry); /* Extra count - pin the dentry in core */ @@ -2695,7 +2779,7 @@ static umode_t cgroup_file_mode(const struct cftype *cft) } static int cgroup_add_file(struct cgroup *cgrp, struct cgroup_subsys *subsys, - const struct cftype *cft) + struct cftype *cft) { struct dentry *dir = cgrp->dentry; struct cgroup *parent = __d_cgrp(dir); @@ -2705,6 +2789,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 }; + simple_xattrs_init(&cft->xattrs); + /* does @cft->flags tell us to skip creation on @cgrp? */ if ((cft->flags & CFTYPE_NOT_ON_ROOT) && !cgrp->parent) return 0; @@ -2745,9 +2831,9 @@ out: } static int cgroup_addrm_files(struct cgroup *cgrp, struct cgroup_subsys *subsys, - const struct cftype cfts[], bool is_add) + struct cftype cfts[], bool is_add) { - const struct cftype *cft; + struct cftype *cft; int err, ret = 0; for (cft = cfts; cft->name[0] != '\0'; cft++) { @@ -2781,7 +2867,7 @@ static void cgroup_cfts_prepare(void) } static void cgroup_cfts_commit(struct cgroup_subsys *ss, - const struct cftype *cfts, bool is_add) + struct cftype *cfts, bool is_add) __releases(&cgroup_mutex) __releases(&cgroup_cft_mutex) { LIST_HEAD(pending); @@ -2832,7 +2918,7 @@ static void cgroup_cfts_commit(struct cgroup_subsys *ss, * function currently returns 0 as long as @cfts registration is successful * even if some file creation attempts on existing cgroups fail. */ -int cgroup_add_cftypes(struct cgroup_subsys *ss, const struct cftype *cfts) +int cgroup_add_cftypes(struct cgroup_subsys *ss, struct cftype *cfts) { struct cftype_set *set; @@ -2862,7 +2948,7 @@ EXPORT_SYMBOL_GPL(cgroup_add_cftypes); * Returns 0 on successful unregistration, -ENOENT if @cfts is not * registered with @ss. */ -int cgroup_rm_cftypes(struct cgroup_subsys *ss, const struct cftype *cfts) +int cgroup_rm_cftypes(struct cgroup_subsys *ss, struct cftype *cfts) { struct cftype_set *set; -- cgit v1.2.3 From f9a6aa4303bd15bbdb24d9fe374e4e6850298460 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 6 Aug 2012 18:32:08 +0200 Subject: clk: convert ARM RealView to common clk This converts the ARM RealView machine over to using the common clock. The approach is similar to the one used for the Integrator, and we're reusing the ICST wrapper code. We have to put the clock intialization in the timer init function for the clocks to be available when initializing the timer, keeping them in early_init() is too early for the common clk. Since we now have to go down and compile drivers/clk/versatile a CONFIG_COMMON_CLK_VERSATILE symbol has been added so the proper code gets compiled into the kernel for either machine. A leftover CLK_VERSATILE in the Integrator Kconfig was fixed up to use the new symbol as well. Tested on ARM RealView PB1176. Cc: Pawel Moll Signed-off-by: Linus Walleij Signed-off-by: Mike Turquette --- arch/arm/Kconfig | 7 +- arch/arm/mach-realview/core.c | 106 ------------------------- arch/arm/mach-realview/include/mach/clkdev.h | 16 ---- arch/arm/mach-realview/realview_eb.c | 2 + arch/arm/mach-realview/realview_pb1176.c | 2 + arch/arm/mach-realview/realview_pb11mp.c | 2 + arch/arm/mach-realview/realview_pba8.c | 2 + arch/arm/mach-realview/realview_pbx.c | 2 + drivers/clk/Kconfig | 7 ++ drivers/clk/Makefile | 2 +- drivers/clk/versatile/Makefile | 1 + drivers/clk/versatile/clk-realview.c | 114 +++++++++++++++++++++++++++ include/linux/platform_data/clk-realview.h | 1 + 13 files changed, 137 insertions(+), 127 deletions(-) delete mode 100644 arch/arm/mach-realview/include/mach/clkdev.h create mode 100644 drivers/clk/versatile/clk-realview.c create mode 100644 include/linux/platform_data/clk-realview.h (limited to 'include') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index fb6014868d33..1a01ffa331d0 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -273,7 +273,7 @@ config ARCH_INTEGRATOR select ARM_AMBA select ARCH_HAS_CPUFREQ select COMMON_CLK - select CLK_VERSATILE + select COMMON_CLK_VERSATILE select HAVE_TCM select ICST select GENERIC_CLOCKEVENTS @@ -289,13 +289,12 @@ config ARCH_INTEGRATOR config ARCH_REALVIEW bool "ARM Ltd. RealView family" select ARM_AMBA - select CLKDEV_LOOKUP - select HAVE_MACH_CLKDEV + select COMMON_CLK + select COMMON_CLK_VERSATILE select ICST select GENERIC_CLOCKEVENTS select ARCH_WANT_OPTIONAL_GPIOLIB select PLAT_VERSATILE - select PLAT_VERSATILE_CLOCK select PLAT_VERSATILE_CLCD select ARM_TIMER_SP804 select GPIO_PL061 if GPIOLIB diff --git a/arch/arm/mach-realview/core.c b/arch/arm/mach-realview/core.c index 45868bb43cbd..ff007d15e0ec 100644 --- a/arch/arm/mach-realview/core.c +++ b/arch/arm/mach-realview/core.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include @@ -226,115 +225,10 @@ struct mmci_platform_data realview_mmc1_plat_data = { .cd_invert = true, }; -/* - * Clock handling - */ -static const struct icst_params realview_oscvco_params = { - .ref = 24000000, - .vco_max = ICST307_VCO_MAX, - .vco_min = ICST307_VCO_MIN, - .vd_min = 4 + 8, - .vd_max = 511 + 8, - .rd_min = 1 + 2, - .rd_max = 127 + 2, - .s2div = icst307_s2div, - .idx2s = icst307_idx2s, -}; - -static void realview_oscvco_set(struct clk *clk, struct icst_vco vco) -{ - void __iomem *sys_lock = __io_address(REALVIEW_SYS_BASE) + REALVIEW_SYS_LOCK_OFFSET; - u32 val; - - val = readl(clk->vcoreg) & ~0x7ffff; - val |= vco.v | (vco.r << 9) | (vco.s << 16); - - writel(0xa05f, sys_lock); - writel(val, clk->vcoreg); - writel(0, sys_lock); -} - -static const struct clk_ops oscvco_clk_ops = { - .round = icst_clk_round, - .set = icst_clk_set, - .setvco = realview_oscvco_set, -}; - -static struct clk oscvco_clk = { - .ops = &oscvco_clk_ops, - .params = &realview_oscvco_params, -}; - -/* - * These are fixed clocks. - */ -static struct clk ref24_clk = { - .rate = 24000000, -}; - -static struct clk sp804_clk = { - .rate = 1000000, -}; - -static struct clk dummy_apb_pclk; - -static struct clk_lookup lookups[] = { - { /* Bus clock */ - .con_id = "apb_pclk", - .clk = &dummy_apb_pclk, - }, { /* UART0 */ - .dev_id = "dev:uart0", - .clk = &ref24_clk, - }, { /* UART1 */ - .dev_id = "dev:uart1", - .clk = &ref24_clk, - }, { /* UART2 */ - .dev_id = "dev:uart2", - .clk = &ref24_clk, - }, { /* UART3 */ - .dev_id = "fpga:uart3", - .clk = &ref24_clk, - }, { /* UART3 is on the dev chip in PB1176 */ - .dev_id = "dev:uart3", - .clk = &ref24_clk, - }, { /* UART4 only exists in PB1176 */ - .dev_id = "fpga:uart4", - .clk = &ref24_clk, - }, { /* KMI0 */ - .dev_id = "fpga:kmi0", - .clk = &ref24_clk, - }, { /* KMI1 */ - .dev_id = "fpga:kmi1", - .clk = &ref24_clk, - }, { /* MMC0 */ - .dev_id = "fpga:mmc0", - .clk = &ref24_clk, - }, { /* CLCD is in the PB1176 and EB DevChip */ - .dev_id = "dev:clcd", - .clk = &oscvco_clk, - }, { /* PB:CLCD */ - .dev_id = "issp:clcd", - .clk = &oscvco_clk, - }, { /* SSP */ - .dev_id = "dev:ssp0", - .clk = &ref24_clk, - }, { /* SP804 timers */ - .dev_id = "sp804", - .clk = &sp804_clk, - }, -}; - void __init realview_init_early(void) { void __iomem *sys = __io_address(REALVIEW_SYS_BASE); - if (machine_is_realview_pb1176()) - oscvco_clk.vcoreg = sys + REALVIEW_SYS_OSC0_OFFSET; - else - oscvco_clk.vcoreg = sys + REALVIEW_SYS_OSC4_OFFSET; - - clkdev_add_table(lookups, ARRAY_SIZE(lookups)); - versatile_sched_clock_init(sys + REALVIEW_SYS_24MHz_OFFSET, 24000000); } diff --git a/arch/arm/mach-realview/include/mach/clkdev.h b/arch/arm/mach-realview/include/mach/clkdev.h deleted file mode 100644 index e58d0771b64e..000000000000 --- a/arch/arm/mach-realview/include/mach/clkdev.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef __ASM_MACH_CLKDEV_H -#define __ASM_MACH_CLKDEV_H - -#include - -struct clk { - unsigned long rate; - const struct clk_ops *ops; - const struct icst_params *params; - void __iomem *vcoreg; -}; - -#define __clk_get(clk) ({ 1; }) -#define __clk_put(clk) do { } while (0) - -#endif diff --git a/arch/arm/mach-realview/realview_eb.c b/arch/arm/mach-realview/realview_eb.c index baf382c5e776..a33e33b76733 100644 --- a/arch/arm/mach-realview/realview_eb.c +++ b/arch/arm/mach-realview/realview_eb.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -414,6 +415,7 @@ static void __init realview_eb_timer_init(void) else timer_irq = IRQ_EB_TIMER0_1; + realview_clk_init(__io_address(REALVIEW_SYS_BASE), false); realview_timer_init(timer_irq); realview_eb_twd_init(); } diff --git a/arch/arm/mach-realview/realview_pb1176.c b/arch/arm/mach-realview/realview_pb1176.c index b1d7cafa1a6d..f0298cbc203e 100644 --- a/arch/arm/mach-realview/realview_pb1176.c +++ b/arch/arm/mach-realview/realview_pb1176.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -326,6 +327,7 @@ static void __init realview_pb1176_timer_init(void) timer2_va_base = __io_address(REALVIEW_PB1176_TIMER2_3_BASE); timer3_va_base = __io_address(REALVIEW_PB1176_TIMER2_3_BASE) + 0x20; + realview_clk_init(__io_address(REALVIEW_SYS_BASE), true); realview_timer_init(IRQ_DC1176_TIMER0); } diff --git a/arch/arm/mach-realview/realview_pb11mp.c b/arch/arm/mach-realview/realview_pb11mp.c index a98c536e3327..1f019f76f7b5 100644 --- a/arch/arm/mach-realview/realview_pb11mp.c +++ b/arch/arm/mach-realview/realview_pb11mp.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -312,6 +313,7 @@ static void __init realview_pb11mp_timer_init(void) timer2_va_base = __io_address(REALVIEW_PB11MP_TIMER2_3_BASE); timer3_va_base = __io_address(REALVIEW_PB11MP_TIMER2_3_BASE) + 0x20; + realview_clk_init(__io_address(REALVIEW_SYS_BASE), false); realview_timer_init(IRQ_TC11MP_TIMER0_1); realview_pb11mp_twd_init(); } diff --git a/arch/arm/mach-realview/realview_pba8.c b/arch/arm/mach-realview/realview_pba8.c index 59650174e6ed..5032775dbfee 100644 --- a/arch/arm/mach-realview/realview_pba8.c +++ b/arch/arm/mach-realview/realview_pba8.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -261,6 +262,7 @@ static void __init realview_pba8_timer_init(void) timer2_va_base = __io_address(REALVIEW_PBA8_TIMER2_3_BASE); timer3_va_base = __io_address(REALVIEW_PBA8_TIMER2_3_BASE) + 0x20; + realview_clk_init(__io_address(REALVIEW_SYS_BASE), false); realview_timer_init(IRQ_PBA8_TIMER0_1); } diff --git a/arch/arm/mach-realview/realview_pbx.c b/arch/arm/mach-realview/realview_pbx.c index 3f2f605624e9..de64ba0ddb95 100644 --- a/arch/arm/mach-realview/realview_pbx.c +++ b/arch/arm/mach-realview/realview_pbx.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -320,6 +321,7 @@ static void __init realview_pbx_timer_init(void) timer2_va_base = __io_address(REALVIEW_PBX_TIMER2_3_BASE); timer3_va_base = __io_address(REALVIEW_PBX_TIMER2_3_BASE) + 0x20; + realview_clk_init(__io_address(REALVIEW_SYS_BASE), false); realview_timer_init(IRQ_PBX_TIMER0_1); realview_pbx_twd_init(); } diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 7f0b5ca78516..89b726d1afe5 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -40,4 +40,11 @@ config COMMON_CLK_WM831X Supports the clocking subsystem of the WM831x/2x series of PMICs from Wolfson Microlectronics. +config COMMON_CLK_VERSATILE + tristate "Clock driver for ARM Reference designs" + depends on ARCH_INTEGRATOR || ARCH_REALVIEW + ---help--- + Supports clocking on ARM Reference designs Integrator/AP, + Integrator/CP, RealView PB1176, EB, PB11MP and PBX. + endmenu diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index d4c7253eb307..e30376c4ff5d 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -9,7 +9,7 @@ obj-$(CONFIG_ARCH_MXS) += mxs/ obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/ obj-$(CONFIG_PLAT_SPEAR) += spear/ obj-$(CONFIG_ARCH_U300) += clk-u300.o -obj-$(CONFIG_ARCH_INTEGRATOR) += versatile/ +obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/ obj-$(CONFIG_ARCH_PRIMA2) += clk-prima2.o # Chip specific diff --git a/drivers/clk/versatile/Makefile b/drivers/clk/versatile/Makefile index 50cf6a2ee693..c0a0f6478798 100644 --- a/drivers/clk/versatile/Makefile +++ b/drivers/clk/versatile/Makefile @@ -1,3 +1,4 @@ # Makefile for Versatile-specific clocks obj-$(CONFIG_ICST) += clk-icst.o obj-$(CONFIG_ARCH_INTEGRATOR) += clk-integrator.o +obj-$(CONFIG_ARCH_REALVIEW) += clk-realview.o diff --git a/drivers/clk/versatile/clk-realview.c b/drivers/clk/versatile/clk-realview.c new file mode 100644 index 000000000000..e21a99cef378 --- /dev/null +++ b/drivers/clk/versatile/clk-realview.c @@ -0,0 +1,114 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include "clk-icst.h" + +/* + * Implementation of the ARM RealView clock trees. + */ + +static void __iomem *sys_lock; +static void __iomem *sys_vcoreg; + +/** + * realview_oscvco_get() - get ICST OSC settings for the RealView + */ +static struct icst_vco realview_oscvco_get(void) +{ + u32 val; + struct icst_vco vco; + + val = readl(sys_vcoreg); + vco.v = val & 0x1ff; + vco.r = (val >> 9) & 0x7f; + vco.s = (val >> 16) & 03; + return vco; +} + +static void realview_oscvco_set(struct icst_vco vco) +{ + u32 val; + + val = readl(sys_vcoreg) & ~0x7ffff; + val |= vco.v | (vco.r << 9) | (vco.s << 16); + + /* This magic unlocks the CM VCO so it can be controlled */ + writel(0xa05f, sys_lock); + writel(val, sys_vcoreg); + /* This locks the CM again */ + writel(0, sys_lock); +} + +static const struct icst_params realview_oscvco_params = { + .ref = 24000000, + .vco_max = ICST307_VCO_MAX, + .vco_min = ICST307_VCO_MIN, + .vd_min = 4 + 8, + .vd_max = 511 + 8, + .rd_min = 1 + 2, + .rd_max = 127 + 2, + .s2div = icst307_s2div, + .idx2s = icst307_idx2s, +}; + +static const struct clk_icst_desc __initdata realview_icst_desc = { + .params = &realview_oscvco_params, + .getvco = realview_oscvco_get, + .setvco = realview_oscvco_set, +}; + +/* + * realview_clk_init() - set up the RealView clock tree + */ +void __init realview_clk_init(void __iomem *sysbase, bool is_pb1176) +{ + struct clk *clk; + + sys_lock = sysbase + REALVIEW_SYS_LOCK_OFFSET; + if (is_pb1176) + sys_vcoreg = sysbase + REALVIEW_SYS_OSC0_OFFSET; + else + sys_vcoreg = sysbase + REALVIEW_SYS_OSC4_OFFSET; + + + /* APB clock dummy */ + clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT, 0); + clk_register_clkdev(clk, "apb_pclk", NULL); + + /* 24 MHz clock */ + clk = clk_register_fixed_rate(NULL, "clk24mhz", NULL, CLK_IS_ROOT, + 24000000); + clk_register_clkdev(clk, NULL, "dev:uart0"); + clk_register_clkdev(clk, NULL, "dev:uart1"); + clk_register_clkdev(clk, NULL, "dev:uart2"); + clk_register_clkdev(clk, NULL, "fpga:kmi0"); + clk_register_clkdev(clk, NULL, "fpga:kmi1"); + clk_register_clkdev(clk, NULL, "fpga:mmc0"); + clk_register_clkdev(clk, NULL, "dev:ssp0"); + if (is_pb1176) { + /* + * UART3 is on the dev chip in PB1176 + * UART4 only exists in PB1176 + */ + clk_register_clkdev(clk, NULL, "dev:uart3"); + clk_register_clkdev(clk, NULL, "dev:uart4"); + } else + clk_register_clkdev(clk, NULL, "fpga:uart3"); + + + /* 1 MHz clock */ + clk = clk_register_fixed_rate(NULL, "clk1mhz", NULL, CLK_IS_ROOT, + 1000000); + clk_register_clkdev(clk, NULL, "sp804"); + + /* ICST VCO clock */ + clk = icst_clk_register(NULL, &realview_icst_desc); + clk_register_clkdev(clk, NULL, "dev:clcd"); + clk_register_clkdev(clk, NULL, "issp:clcd"); +} diff --git a/include/linux/platform_data/clk-realview.h b/include/linux/platform_data/clk-realview.h new file mode 100644 index 000000000000..2e426a7dbc51 --- /dev/null +++ b/include/linux/platform_data/clk-realview.h @@ -0,0 +1 @@ +void realview_clk_init(void __iomem *sysbase, bool is_pb1176); -- cgit v1.2.3 From 5f2d04f1f9b52604fca6ee08a77972c0df67e082 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sun, 26 Aug 2012 19:13:55 +0200 Subject: ipv4: fix path MTU discovery with connection tracking IPv4 conntrack defragments incoming packet at the PRE_ROUTING hook and (in case of forwarded packets) refragments them at POST_ROUTING independent of the IP_DF flag. Refragmentation uses the dst_mtu() of the local route without caring about the original fragment sizes, thereby breaking PMTUD. This patch fixes this by keeping track of the largest received fragment with IP_DF set and generates an ICMP fragmentation required error during refragmentation if that size exceeds the MTU. Signed-off-by: Patrick McHardy Acked-by: Eric Dumazet Acked-by: David S. Miller --- include/net/inet_frag.h | 2 ++ include/net/ip.h | 2 ++ net/ipv4/ip_fragment.c | 8 +++++++- net/ipv4/ip_output.c | 4 +++- 4 files changed, 14 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index 2431cf83aeca..5098ee7b7e0e 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -29,6 +29,8 @@ struct inet_frag_queue { #define INET_FRAG_COMPLETE 4 #define INET_FRAG_FIRST_IN 2 #define INET_FRAG_LAST_IN 1 + + u16 max_size; }; #define INETFRAGS_HASHSZ 64 diff --git a/include/net/ip.h b/include/net/ip.h index 5a5d84d3d2c6..0707fb9551aa 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -42,6 +42,8 @@ struct inet_skb_parm { #define IPSKB_XFRM_TRANSFORMED 4 #define IPSKB_FRAG_COMPLETE 8 #define IPSKB_REROUTED 16 + + u16 frag_max_size; }; static inline unsigned int ip_hdrlen(const struct sk_buff *skb) diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 8d07c973409c..fa6a12c51066 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -523,6 +523,10 @@ found: if (offset == 0) qp->q.last_in |= INET_FRAG_FIRST_IN; + if (ip_hdr(skb)->frag_off & htons(IP_DF) && + skb->len + ihl > qp->q.max_size) + qp->q.max_size = skb->len + ihl; + if (qp->q.last_in == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) && qp->q.meat == qp->q.len) return ip_frag_reasm(qp, prev, dev); @@ -646,9 +650,11 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev, head->next = NULL; head->dev = dev; head->tstamp = qp->q.stamp; + IPCB(head)->frag_max_size = qp->q.max_size; iph = ip_hdr(head); - iph->frag_off = 0; + /* max_size != 0 implies at least one fragment had IP_DF set */ + iph->frag_off = qp->q.max_size ? htons(IP_DF) : 0; iph->tot_len = htons(len); iph->tos |= ecn; IP_INC_STATS_BH(net, IPSTATS_MIB_REASMOKS); diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index c196d749daf2..a5beab1dc958 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -467,7 +467,9 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) iph = ip_hdr(skb); - if (unlikely((iph->frag_off & htons(IP_DF)) && !skb->local_df)) { + if (unlikely(((iph->frag_off & htons(IP_DF)) && !skb->local_df) || + (IPCB(skb)->frag_max_size && + IPCB(skb)->frag_max_size > dst_mtu(&rt->dst)))) { IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS); icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(ip_skb_dst_mtu(skb))); -- cgit v1.2.3 From cc110922da7e902b62d18641a370fec01a9fa794 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Thu, 23 Aug 2012 21:32:43 -0300 Subject: Bluetooth: Change signature of smp_conn_security() To make it clear that it may be called from contexts that may not have any knowledge of L2CAP, we change the connection parameter, to receive a hci_conn. This also makes it clear that it is checking the security of the link. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo Padovan --- include/net/bluetooth/smp.h | 2 +- net/bluetooth/l2cap_core.c | 11 ++++++----- net/bluetooth/l2cap_sock.c | 2 +- net/bluetooth/smp.c | 4 ++-- 4 files changed, 10 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h index ca356a734920..8b27927b2a55 100644 --- a/include/net/bluetooth/smp.h +++ b/include/net/bluetooth/smp.h @@ -136,7 +136,7 @@ struct smp_chan { }; /* SMP Commands */ -int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level); +int smp_conn_security(struct hci_conn *hcon, __u8 sec_level); int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb); int smp_distribute_keys(struct l2cap_conn *conn, __u8 force); int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index daa149b7003c..4ea1710a4783 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1199,14 +1199,15 @@ clean: static void l2cap_conn_ready(struct l2cap_conn *conn) { struct l2cap_chan *chan; + struct hci_conn *hcon = conn->hcon; BT_DBG("conn %p", conn); - if (!conn->hcon->out && conn->hcon->type == LE_LINK) + if (!hcon->out && hcon->type == LE_LINK) l2cap_le_conn_ready(conn); - if (conn->hcon->out && conn->hcon->type == LE_LINK) - smp_conn_security(conn, conn->hcon->pending_sec_level); + if (hcon->out && hcon->type == LE_LINK) + smp_conn_security(hcon, hcon->pending_sec_level); mutex_lock(&conn->chan_lock); @@ -1219,8 +1220,8 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) continue; } - if (conn->hcon->type == LE_LINK) { - if (smp_conn_security(conn, chan->sec_level)) + if (hcon->type == LE_LINK) { + if (smp_conn_security(hcon, chan->sec_level)) l2cap_chan_ready(chan); } else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index b94abd30e6f9..45cb0b0dd2c7 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -615,7 +615,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch break; } - if (smp_conn_security(conn, sec.level)) + if (smp_conn_security(conn->hcon, sec.level)) break; sk->sk_state = BT_CONFIG; chan->state = BT_CONFIG; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 98ffc1b6a6fa..8c225ef349cd 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -760,9 +760,9 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) return 0; } -int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) +int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) { - struct hci_conn *hcon = conn->hcon; + struct l2cap_conn *conn = hcon->l2cap_data; struct smp_chan *smp = conn->smp_chan; __u8 authreq; -- cgit v1.2.3 From 2d66f389ccf2c3ffea93c0270ef34186e4995333 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 10 Aug 2012 17:36:00 +0100 Subject: iio: Introduce iio_device_{set,get}_drvdata() Introduce two new helper functions to attach a arbitrary pointer to a IIO device. This is useful to get access to external non-global data from within a IIO device callbacks where only the IIO device is available. Internally these functions use dev_{set,get}_drvdata() on the struct device embedded in the IIO device. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- include/linux/iio/iio.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'include') diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index be82936c4089..b18e74e0bdd5 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -516,6 +516,31 @@ static inline struct iio_dev *iio_device_get(struct iio_dev *indio_dev) return indio_dev ? dev_to_iio_dev(get_device(&indio_dev->dev)) : NULL; } + +/** + * iio_device_set_drvdata() - Set device driver data + * @indio_dev: IIO device structure + * @data: Driver specific data + * + * Allows to attach an arbitrary pointer to an IIO device, which can later be + * retrieved by iio_device_get_drvdata(). + */ +static inline void iio_device_set_drvdata(struct iio_dev *indio_dev, void *data) +{ + dev_set_drvdata(&indio_dev->dev, data); +} + +/** + * iio_device_get_drvdata() - Get device driver data + * @indio_dev: IIO device structure + * + * Returns the data previously set with iio_device_set_drvdata() + */ +static inline void *iio_device_get_drvdata(struct iio_dev *indio_dev) +{ + return dev_get_drvdata(&indio_dev->dev); +} + /* Can we make this smaller? */ #define IIO_ALIGN L1_CACHE_BYTES /** -- cgit v1.2.3 From af3008485ea0372fb9ce1f69f3768617d39eb4e6 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 10 Aug 2012 17:36:00 +0100 Subject: iio:adc: Add common code for ADI Sigma Delta devices Most devices from the Analog Devices Sigma Delta family use a similar scheme for communication with the device. This includes register access, as well as trigger handling. But each device sub-family has different features and different register layouts (some even have no registers at all) and thus it is impractical to try to support all of the devices by the same driver. This patch adds a common base library for Sigma Delta converter devices. It will be used by individual drivers. This code is mostly based on the three existing Sigma Delta drivers the AD7192, AD7780 and AD7793, but has been improved for more robustness and flexibility. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 5 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ad_sigma_delta.c | 558 +++++++++++++++++++++++++++++++++ include/linux/iio/adc/ad_sigma_delta.h | 173 ++++++++++ 4 files changed, 737 insertions(+) create mode 100644 drivers/iio/adc/ad_sigma_delta.c create mode 100644 include/linux/iio/adc/ad_sigma_delta.h (limited to 'include') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 8a78b4f3ef58..a2c50713b0b6 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -3,6 +3,11 @@ # menu "Analog to digital converters" +config AD_SIGMA_DELTA + tristate + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + config AD7266 tristate "Analog Devices AD7265/AD7266 ADC driver" depends on SPI_MASTER diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 52eec254c38c..5989356c5735 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -2,5 +2,6 @@ # Makefile for IIO ADC drivers # +obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o obj-$(CONFIG_AD7266) += ad7266.o obj-$(CONFIG_AT91_ADC) += at91_adc.o diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c new file mode 100644 index 000000000000..ae847c5a1361 --- /dev/null +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -0,0 +1,558 @@ +/* + * Support code for Analog Devices Sigma-Delta ADCs + * + * Copyright 2012 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + + +#define AD_SD_COMM_CHAN_MASK 0x3 + +#define AD_SD_REG_COMM 0x00 +#define AD_SD_REG_DATA 0x03 + +/** + * ad_sd_set_comm() - Set communications register + * + * @sigma_delta: The sigma delta device + * @comm: New value for the communications register + */ +void ad_sd_set_comm(struct ad_sigma_delta *sigma_delta, uint8_t comm) +{ + /* Some variants use the lower two bits of the communications register + * to select the channel */ + sigma_delta->comm = comm & AD_SD_COMM_CHAN_MASK; +} +EXPORT_SYMBOL_GPL(ad_sd_set_comm); + +/** + * ad_sd_write_reg() - Write a register + * + * @sigma_delta: The sigma delta device + * @reg: Address of the register + * @size: Size of the register (0-3) + * @val: Value to write to the register + * + * Returns 0 on success, an error code otherwise. + **/ +int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg, + unsigned int size, unsigned int val) +{ + uint8_t *data = sigma_delta->data; + struct spi_transfer t = { + .tx_buf = data, + .len = size + 1, + .cs_change = sigma_delta->bus_locked, + }; + struct spi_message m; + int ret; + + data[0] = (reg << sigma_delta->info->addr_shift) | sigma_delta->comm; + + switch (size) { + case 3: + data[1] = val >> 16; + data[2] = val >> 8; + data[3] = val; + break; + case 2: + put_unaligned_be16(val, &data[1]); + break; + case 1: + data[1] = val; + break; + case 0: + break; + default: + return -EINVAL; + } + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + + if (sigma_delta->bus_locked) + ret = spi_sync_locked(sigma_delta->spi, &m); + else + ret = spi_sync(sigma_delta->spi, &m); + + return ret; +} +EXPORT_SYMBOL_GPL(ad_sd_write_reg); + +static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta, + unsigned int reg, unsigned int size, uint8_t *val) +{ + uint8_t *data = sigma_delta->data; + int ret; + struct spi_transfer t[] = { + { + .tx_buf = data, + .len = 1, + }, { + .rx_buf = val, + .len = size, + .cs_change = sigma_delta->bus_locked, + }, + }; + struct spi_message m; + + spi_message_init(&m); + + if (sigma_delta->info->has_registers) { + data[0] = reg << sigma_delta->info->addr_shift; + data[0] |= sigma_delta->info->read_mask; + spi_message_add_tail(&t[0], &m); + } + spi_message_add_tail(&t[1], &m); + + if (sigma_delta->bus_locked) + ret = spi_sync_locked(sigma_delta->spi, &m); + else + ret = spi_sync(sigma_delta->spi, &m); + + return ret; +} + +/** + * ad_sd_read_reg() - Read a register + * + * @sigma_delta: The sigma delta device + * @reg: Address of the register + * @size: Size of the register (1-4) + * @val: Read value + * + * Returns 0 on success, an error code otherwise. + **/ +int ad_sd_read_reg(struct ad_sigma_delta *sigma_delta, + unsigned int reg, unsigned int size, unsigned int *val) +{ + int ret; + + ret = ad_sd_read_reg_raw(sigma_delta, reg, size, sigma_delta->data); + if (ret < 0) + goto out; + + switch (size) { + case 4: + *val = get_unaligned_be32(sigma_delta->data); + break; + case 3: + *val = (sigma_delta->data[0] << 16) | + (sigma_delta->data[1] << 8) | + sigma_delta->data[2]; + break; + case 2: + *val = get_unaligned_be16(sigma_delta->data); + break; + case 1: + *val = sigma_delta->data[0]; + break; + default: + ret = -EINVAL; + break; + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(ad_sd_read_reg); + +static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, + unsigned int mode, unsigned int channel) +{ + int ret; + + ret = ad_sigma_delta_set_channel(sigma_delta, channel); + if (ret) + return ret; + + spi_bus_lock(sigma_delta->spi->master); + sigma_delta->bus_locked = true; + INIT_COMPLETION(sigma_delta->completion); + + ret = ad_sigma_delta_set_mode(sigma_delta, mode); + if (ret < 0) + goto out; + + sigma_delta->irq_dis = false; + enable_irq(sigma_delta->spi->irq); + ret = wait_for_completion_timeout(&sigma_delta->completion, 2*HZ); + if (ret == 0) { + sigma_delta->irq_dis = true; + disable_irq_nosync(sigma_delta->spi->irq); + ret = -EIO; + } else { + ret = 0; + } +out: + sigma_delta->bus_locked = false; + spi_bus_unlock(sigma_delta->spi->master); + ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); + + return ret; +} + +/** + * ad_sd_calibrate_all() - Performs channel calibration + * @sigma_delta: The sigma delta device + * @cb: Array of channels and calibration type to perform + * @n: Number of items in cb + * + * Returns 0 on success, an error code otherwise. + **/ +int ad_sd_calibrate_all(struct ad_sigma_delta *sigma_delta, + const struct ad_sd_calib_data *cb, unsigned int n) +{ + unsigned int i; + int ret; + + for (i = 0; i < n; i++) { + ret = ad_sd_calibrate(sigma_delta, cb[i].mode, cb[i].channel); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(ad_sd_calibrate_all); + +/** + * ad_sigma_delta_single_conversion() - Performs a single data conversion + * @indio_dev: The IIO device + * @chan: The conversion is done for this channel + * @val: Pointer to the location where to store the read value + * + * Returns: 0 on success, an error value otherwise. + */ +int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val) +{ + struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); + unsigned int sample, raw_sample; + int ret = 0; + + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + + mutex_lock(&indio_dev->mlock); + ad_sigma_delta_set_channel(sigma_delta, chan->address); + + spi_bus_lock(sigma_delta->spi->master); + sigma_delta->bus_locked = true; + INIT_COMPLETION(sigma_delta->completion); + + ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE); + + sigma_delta->irq_dis = false; + enable_irq(sigma_delta->spi->irq); + ret = wait_for_completion_interruptible_timeout( + &sigma_delta->completion, HZ); + + sigma_delta->bus_locked = false; + spi_bus_unlock(sigma_delta->spi->master); + + if (ret == 0) + ret = -EIO; + if (ret < 0) + goto out; + + ret = ad_sd_read_reg(sigma_delta, AD_SD_REG_DATA, + DIV_ROUND_UP(chan->scan_type.realbits + chan->scan_type.shift, 8), + &raw_sample); + +out: + if (!sigma_delta->irq_dis) { + disable_irq_nosync(sigma_delta->spi->irq); + sigma_delta->irq_dis = true; + } + + ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); + mutex_unlock(&indio_dev->mlock); + + if (ret) + return ret; + + sample = raw_sample >> chan->scan_type.shift; + sample &= (1 << chan->scan_type.realbits) - 1; + *val = sample; + + ret = ad_sigma_delta_postprocess_sample(sigma_delta, raw_sample); + if (ret) + return ret; + + return IIO_VAL_INT; +} +EXPORT_SYMBOL_GPL(ad_sigma_delta_single_conversion); + +static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); + unsigned int channel; + int ret; + + ret = iio_triggered_buffer_postenable(indio_dev); + if (ret < 0) + return ret; + + channel = find_first_bit(indio_dev->active_scan_mask, + indio_dev->masklength); + ret = ad_sigma_delta_set_channel(sigma_delta, + indio_dev->channels[channel].address); + if (ret) + goto err_predisable; + + spi_bus_lock(sigma_delta->spi->master); + sigma_delta->bus_locked = true; + ret = ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_CONTINUOUS); + if (ret) + goto err_unlock; + + sigma_delta->irq_dis = false; + enable_irq(sigma_delta->spi->irq); + + return 0; + +err_unlock: + spi_bus_unlock(sigma_delta->spi->master); +err_predisable: + + return ret; +} + +static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); + + INIT_COMPLETION(sigma_delta->completion); + wait_for_completion_timeout(&sigma_delta->completion, HZ); + + if (!sigma_delta->irq_dis) { + disable_irq_nosync(sigma_delta->spi->irq); + sigma_delta->irq_dis = true; + } + + ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); + + sigma_delta->bus_locked = false; + return spi_bus_unlock(sigma_delta->spi->master); +} + +static irqreturn_t ad_sd_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); + unsigned int reg_size; + uint8_t data[16]; + int ret; + + memset(data, 0x00, 16); + + /* Guaranteed to be aligned with 8 byte boundary */ + if (indio_dev->scan_timestamp) + ((s64 *)data)[1] = pf->timestamp; + + reg_size = indio_dev->channels[0].scan_type.realbits + + indio_dev->channels[0].scan_type.shift; + reg_size = DIV_ROUND_UP(reg_size, 8); + + switch (reg_size) { + case 4: + case 2: + case 1: + ret = ad_sd_read_reg_raw(sigma_delta, AD_SD_REG_DATA, + reg_size, &data[0]); + break; + case 3: + /* We store 24 bit samples in a 32 bit word. Keep the upper + * byte set to zero. */ + ret = ad_sd_read_reg_raw(sigma_delta, AD_SD_REG_DATA, + reg_size, &data[1]); + break; + } + + iio_push_to_buffer(indio_dev->buffer, (uint8_t *)data, pf->timestamp); + + iio_trigger_notify_done(indio_dev->trig); + sigma_delta->irq_dis = false; + enable_irq(sigma_delta->spi->irq); + + return IRQ_HANDLED; +} + +static const struct iio_buffer_setup_ops ad_sd_buffer_setup_ops = { + .preenable = &iio_sw_buffer_preenable, + .postenable = &ad_sd_buffer_postenable, + .predisable = &iio_triggered_buffer_predisable, + .postdisable = &ad_sd_buffer_postdisable, + .validate_scan_mask = &iio_validate_scan_mask_onehot, +}; + +static irqreturn_t ad_sd_data_rdy_trig_poll(int irq, void *private) +{ + struct ad_sigma_delta *sigma_delta = private; + + complete(&sigma_delta->completion); + disable_irq_nosync(irq); + sigma_delta->irq_dis = true; + iio_trigger_poll(sigma_delta->trig, iio_get_time_ns()); + + return IRQ_HANDLED; +} + +/** + * ad_sd_validate_trigger() - validate_trigger callback for ad_sigma_delta devices + * @indio_dev: The IIO device + * @trig: The new trigger + * + * Returns: 0 if the 'trig' matches the trigger registered by the ad_sigma_delta + * device, -EINVAL otherwise. + */ +int ad_sd_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig) +{ + struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); + + if (sigma_delta->trig != trig) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_GPL(ad_sd_validate_trigger); + +static const struct iio_trigger_ops ad_sd_trigger_ops = { + .owner = THIS_MODULE, +}; + +static int ad_sd_probe_trigger(struct iio_dev *indio_dev) +{ + struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); + int ret; + + sigma_delta->trig = iio_trigger_alloc("%s-dev%d", indio_dev->name, + indio_dev->id); + if (sigma_delta->trig == NULL) { + ret = -ENOMEM; + goto error_ret; + } + sigma_delta->trig->ops = &ad_sd_trigger_ops; + init_completion(&sigma_delta->completion); + + ret = request_irq(sigma_delta->spi->irq, + ad_sd_data_rdy_trig_poll, + IRQF_TRIGGER_LOW, + indio_dev->name, + sigma_delta); + if (ret) + goto error_free_trig; + + if (!sigma_delta->irq_dis) { + sigma_delta->irq_dis = true; + disable_irq_nosync(sigma_delta->spi->irq); + } + sigma_delta->trig->dev.parent = &sigma_delta->spi->dev; + sigma_delta->trig->private_data = sigma_delta; + + ret = iio_trigger_register(sigma_delta->trig); + if (ret) + goto error_free_irq; + + /* select default trigger */ + indio_dev->trig = sigma_delta->trig; + + return 0; + +error_free_irq: + free_irq(sigma_delta->spi->irq, sigma_delta); +error_free_trig: + iio_trigger_free(sigma_delta->trig); +error_ret: + return ret; +} + +static void ad_sd_remove_trigger(struct iio_dev *indio_dev) +{ + struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); + + iio_trigger_unregister(sigma_delta->trig); + free_irq(sigma_delta->spi->irq, sigma_delta); + iio_trigger_free(sigma_delta->trig); +} + +/** + * ad_sd_setup_buffer_and_trigger() - + * @indio_dev: The IIO device + */ +int ad_sd_setup_buffer_and_trigger(struct iio_dev *indio_dev) +{ + int ret; + + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + &ad_sd_trigger_handler, &ad_sd_buffer_setup_ops); + if (ret) + return ret; + + ret = ad_sd_probe_trigger(indio_dev); + if (ret) { + iio_triggered_buffer_cleanup(indio_dev); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(ad_sd_setup_buffer_and_trigger); + +/** + * ad_sd_cleanup_buffer_and_trigger() - + * @indio_dev: The IIO device + */ +void ad_sd_cleanup_buffer_and_trigger(struct iio_dev *indio_dev) +{ + ad_sd_remove_trigger(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); +} +EXPORT_SYMBOL_GPL(ad_sd_cleanup_buffer_and_trigger); + +/** + * ad_sd_init() - Initializes a ad_sigma_delta struct + * @sigma_delta: The ad_sigma_delta device + * @indio_dev: The IIO device which the Sigma Delta device is used for + * @spi: The SPI device for the ad_sigma_delta device + * @info: Device specific callbacks and options + * + * This function needs to be called before any other operations are performed on + * the ad_sigma_delta struct. + */ +int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev, + struct spi_device *spi, const struct ad_sigma_delta_info *info) +{ + sigma_delta->spi = spi; + sigma_delta->info = info; + iio_device_set_drvdata(indio_dev, sigma_delta); + + return 0; +} +EXPORT_SYMBOL_GPL(ad_sd_init); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Analog Devices Sigma-Delta ADCs"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/iio/adc/ad_sigma_delta.h b/include/linux/iio/adc/ad_sigma_delta.h new file mode 100644 index 000000000000..2e4eab9868a3 --- /dev/null +++ b/include/linux/iio/adc/ad_sigma_delta.h @@ -0,0 +1,173 @@ +/* + * Support code for Analog Devices Sigma-Delta ADCs + * + * Copyright 2012 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2. + */ +#ifndef __AD_SIGMA_DELTA_H__ +#define __AD_SIGMA_DELTA_H__ + +enum ad_sigma_delta_mode { + AD_SD_MODE_CONTINUOUS = 0, + AD_SD_MODE_SINGLE = 1, + AD_SD_MODE_IDLE = 2, + AD_SD_MODE_POWERDOWN = 3, +}; + +/** + * struct ad_sigma_delta_calib_data - Calibration data for Sigma Delta devices + * @mode: Calibration mode. + * @channel: Calibration channel. + */ +struct ad_sd_calib_data { + unsigned int mode; + unsigned int channel; +}; + +struct ad_sigma_delta; +struct iio_dev; + +/** + * struct ad_sigma_delta_info - Sigma Delta driver specific callbacks and options + * @set_channel: Will be called to select the current channel, may be NULL. + * @set_mode: Will be called to select the current mode, may be NULL. + * @postprocess_sample: Is called for each sampled data word, can be used to + * modify or drop the sample data, it, may be NULL. + * @has_registers: true if the device has writable and readable registers, false + * if there is just one read-only sample data shift register. + * @addr_shift: Shift of the register address in the communications register. + * @read_mask: Mask for the communications register having the read bit set. + */ +struct ad_sigma_delta_info { + int (*set_channel)(struct ad_sigma_delta *, unsigned int channel); + int (*set_mode)(struct ad_sigma_delta *, enum ad_sigma_delta_mode mode); + int (*postprocess_sample)(struct ad_sigma_delta *, unsigned int raw_sample); + bool has_registers; + unsigned int addr_shift; + unsigned int read_mask; +}; + +/** + * struct ad_sigma_delta - Sigma Delta device struct + * @spi: The spi device associated with the Sigma Delta device. + * @trig: The IIO trigger associated with the Sigma Delta device. + * + * Most of the fields are private to the sigma delta library code and should not + * be accessed by individual drivers. + */ +struct ad_sigma_delta { + struct spi_device *spi; + struct iio_trigger *trig; + +/* private: */ + struct completion completion; + bool irq_dis; + + bool bus_locked; + + uint8_t comm; + + const struct ad_sigma_delta_info *info; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + uint8_t data[4] ____cacheline_aligned; +}; + +static inline int ad_sigma_delta_set_channel(struct ad_sigma_delta *sd, + unsigned int channel) +{ + if (sd->info->set_channel) + return sd->info->set_channel(sd, channel); + + return 0; +} + +static inline int ad_sigma_delta_set_mode(struct ad_sigma_delta *sd, + unsigned int mode) +{ + if (sd->info->set_mode) + return sd->info->set_mode(sd, mode); + + return 0; +} + +static inline int ad_sigma_delta_postprocess_sample(struct ad_sigma_delta *sd, + unsigned int raw_sample) +{ + if (sd->info->postprocess_sample) + return sd->info->postprocess_sample(sd, raw_sample); + + return 0; +} + +void ad_sd_set_comm(struct ad_sigma_delta *sigma_delta, uint8_t comm); +int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg, + unsigned int size, unsigned int val); +int ad_sd_read_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg, + unsigned int size, unsigned int *val); + +int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val); +int ad_sd_calibrate_all(struct ad_sigma_delta *sigma_delta, + const struct ad_sd_calib_data *cd, unsigned int n); +int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev, + struct spi_device *spi, const struct ad_sigma_delta_info *info); + +int ad_sd_setup_buffer_and_trigger(struct iio_dev *indio_dev); +void ad_sd_cleanup_buffer_and_trigger(struct iio_dev *indio_dev); + +int ad_sd_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig); + +#define __AD_SD_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift, _extend_name, _type) \ + { \ + .type = (_type), \ + .differential = (_channel2 == -1 ? 0 : 1), \ + .indexed = 1, \ + .channel = (_channel1), \ + .channel2 = (_channel2), \ + .address = (_address), \ + .extend_name = (_extend_name), \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SHARED_BIT | \ + IIO_CHAN_INFO_OFFSET_SEPARATE_BIT, \ + .scan_index = (_si), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (_bits), \ + .storagebits = (_storagebits), \ + .shift = (_shift), \ + .endianness = IIO_BE, \ + }, \ + } + +#define AD_SD_DIFF_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift) \ + __AD_SD_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift, NULL, IIO_VOLTAGE) + +#define AD_SD_SHORTED_CHANNEL(_si, _channel, _address, _bits, \ + _storagebits, _shift) \ + __AD_SD_CHANNEL(_si, _channel, _channel, _address, _bits, \ + _storagebits, _shift, "shorted", IIO_VOLTAGE) + +#define AD_SD_CHANNEL(_si, _channel, _address, _bits, \ + _storagebits, _shift) \ + __AD_SD_CHANNEL(_si, _channel, -1, _address, _bits, \ + _storagebits, _shift, NULL, IIO_VOLTAGE) + +#define AD_SD_TEMP_CHANNEL(_si, _address, _bits, _storagebits, _shift) \ + __AD_SD_CHANNEL(_si, 0, -1, _address, _bits, \ + _storagebits, _shift, NULL, IIO_TEMP) + +#define AD_SD_SUPPLY_CHANNEL(_si, _channel, _address, _bits, _storagebits, \ + _shift) \ + __AD_SD_CHANNEL(_si, _channel, -1, _address, _bits, \ + _storagebits, _shift, "supply", IIO_VOLTAGE) + +#endif -- cgit v1.2.3 From 88238fef16845c18abecb9285c97b0225f71d544 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 17 Aug 2012 16:57:00 +0100 Subject: iio:consumer.h: Fix include guard The symbol name for the #ifndef and the #define of the include guard do not match and thus it becomes quite ineffective. Add the missing '_' to fix this. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- include/linux/iio/consumer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h index e2657e6d4d26..06ab4ec56c37 100644 --- a/include/linux/iio/consumer.h +++ b/include/linux/iio/consumer.h @@ -8,7 +8,7 @@ * the Free Software Foundation. */ #ifndef _IIO_INKERN_CONSUMER_H_ -#define _IIO_INKERN_CONSUMER_H +#define _IIO_INKERN_CONSUMER_H_ #include struct iio_dev; -- cgit v1.2.3 From 08d6005c031631429ed307a28503e00e3970c203 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 17 Aug 2012 16:57:00 +0100 Subject: iio: Add missing include guards to headers Add include guards to the IIO headers where they are missing. This avoids compile errors due to redefined types if a file is included more than once. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- include/linux/iio/kfifo_buf.h | 3 +++ include/linux/iio/machine.h | 5 +++++ include/linux/iio/trigger_consumer.h | 5 +++++ 3 files changed, 13 insertions(+) (limited to 'include') diff --git a/include/linux/iio/kfifo_buf.h b/include/linux/iio/kfifo_buf.h index 014d5a13b32b..25eeac762e84 100644 --- a/include/linux/iio/kfifo_buf.h +++ b/include/linux/iio/kfifo_buf.h @@ -1,3 +1,5 @@ +#ifndef __LINUX_IIO_KFIFO_BUF_H__ +#define __LINUX_IIO_KFIFO_BUF_H__ #include #include @@ -6,3 +8,4 @@ struct iio_buffer *iio_kfifo_allocate(struct iio_dev *indio_dev); void iio_kfifo_free(struct iio_buffer *r); +#endif diff --git a/include/linux/iio/machine.h b/include/linux/iio/machine.h index 400a453ff67b..809a3f08d5a5 100644 --- a/include/linux/iio/machine.h +++ b/include/linux/iio/machine.h @@ -8,6 +8,9 @@ * the Free Software Foundation. */ +#ifndef __LINUX_IIO_MACHINE_H__ +#define __LINUX_IIO_MACHINE_H__ + /** * struct iio_map - description of link between consumer and device channels * @adc_channel_label: Label used to identify the channel on the provider. @@ -22,3 +25,5 @@ struct iio_map { const char *consumer_dev_name; const char *consumer_channel; }; + +#endif diff --git a/include/linux/iio/trigger_consumer.h b/include/linux/iio/trigger_consumer.h index 60d64b356945..762a3d3ee547 100644 --- a/include/linux/iio/trigger_consumer.h +++ b/include/linux/iio/trigger_consumer.h @@ -7,6 +7,9 @@ * the Free Software Foundation. */ +#ifndef __LINUX_IIO_TRIGGER_CONSUMER_H__ +#define __LINUX_IIO_TRIGGER_CONSUMER_H__ + /** * struct iio_poll_func - poll function pair * @@ -50,3 +53,5 @@ void iio_trigger_notify_done(struct iio_trigger *trig); */ int iio_triggered_buffer_postenable(struct iio_dev *indio_dev); int iio_triggered_buffer_predisable(struct iio_dev *indio_dev); + +#endif -- cgit v1.2.3 From 161e7f6d136549d6432218f9f42f88dd443a4719 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 17 Aug 2012 16:57:00 +0100 Subject: iio:trigger_consumer.h: Add missing includes and forward declarations Add includes and forward declarations for types used in this file. This avoids compile errors if the other files have not been included before. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- include/linux/iio/trigger_consumer.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/linux/iio/trigger_consumer.h b/include/linux/iio/trigger_consumer.h index 762a3d3ee547..c4f8c7409666 100644 --- a/include/linux/iio/trigger_consumer.h +++ b/include/linux/iio/trigger_consumer.h @@ -10,6 +10,12 @@ #ifndef __LINUX_IIO_TRIGGER_CONSUMER_H__ #define __LINUX_IIO_TRIGGER_CONSUMER_H__ +#include +#include + +struct iio_dev; +struct iio_trigger; + /** * struct iio_poll_func - poll function pair * -- cgit v1.2.3 From 647c42dfd40fec032a4c8525a755160f0765921f Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Mon, 6 Aug 2012 13:15:09 +0200 Subject: uprobes: Kill uprobes_state->count uprobes_state->count is only needed to avoid the slow path in uprobe_pre_sstep_notifier(). It is also checked in uprobe_munmap() but ironically its only goal to decrement this counter. However, it is very broken. Just some examples: - uprobe_mmap() can race with uprobe_unregister() and wrongly increment the counter if it hits the non-uprobe "int3". Note that install_breakpoint() checks ->consumers first and returns -EEXIST if it is NULL. "atomic_sub() if error" in uprobe_mmap() looks obviously wrong too. - uprobe_munmap() can race with uprobe_register() and wrongly decrement the counter by the same reason. - Suppose an appication tries to increase the mmapped area via sys_mremap(). vma_adjust() does uprobe_munmap(whole_vma) first, this can nullify the counter temporarily and race with another thread which can hit the bp, the application will be killed by SIGTRAP. - Suppose an application mmaps 2 consecutive areas in the same file and one (or both) of these areas has uprobes. In the likely case mmap_region()->vma_merge() suceeds. Like above, this leads to uprobe_munmap/uprobe_mmap from vma_merge()->vma_adjust() but then mmap_region() does another uprobe_mmap(resulting_vma) and doubles the counter. This patch only removes this counter and fixes the compile errors, then we will try to cleanup the changed code and add something else instead. Signed-off-by: Oleg Nesterov Acked-by: Srikar Dronamraju --- include/linux/uprobes.h | 2 +- kernel/events/uprobes.c | 38 ++------------------------------------ 2 files changed, 3 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index efe4b3308c74..03ae547c1c31 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -99,8 +99,8 @@ struct xol_area { struct uprobes_state { struct xol_area *xol_area; - atomic_t count; }; + extern int __weak set_swbp(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); extern int __weak set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr, bool verify); extern bool __weak is_swbp_insn(uprobe_opcode_t *insn); diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 0cefde276641..6f1664d217dc 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -678,18 +678,7 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, uprobe->flags |= UPROBE_COPY_INSN; } - /* - * Ideally, should be updating the probe count after the breakpoint - * has been successfully inserted. However a thread could hit the - * breakpoint we just inserted even before the probe count is - * incremented. If this is the first breakpoint placed, breakpoint - * notifier might ignore uprobes and pass the trap to the thread. - * Hence increment before and decrement on failure. - */ - atomic_inc(&mm->uprobes_state.count); ret = set_swbp(&uprobe->arch, mm, vaddr); - if (ret) - atomic_dec(&mm->uprobes_state.count); return ret; } @@ -697,8 +686,7 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, static void remove_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, unsigned long vaddr) { - if (!set_orig_insn(&uprobe->arch, mm, vaddr, true)) - atomic_dec(&mm->uprobes_state.count); + set_orig_insn(&uprobe->arch, mm, vaddr, true); } /* @@ -1051,13 +1039,6 @@ int uprobe_mmap(struct vm_area_struct *vma) if (!is_swbp_at_addr(vma->vm_mm, vaddr)) continue; - - /* - * Unable to insert a breakpoint, but - * breakpoint lies underneath. Increment the - * probe count. - */ - atomic_inc(&vma->vm_mm->uprobes_state.count); } if (!ret) @@ -1068,9 +1049,6 @@ int uprobe_mmap(struct vm_area_struct *vma) mutex_unlock(uprobes_mmap_hash(inode)); - if (ret) - atomic_sub(count, &vma->vm_mm->uprobes_state.count); - return ret; } @@ -1089,9 +1067,6 @@ void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned lon if (!atomic_read(&vma->vm_mm->mm_users)) /* called by mmput() ? */ return; - if (!atomic_read(&vma->vm_mm->uprobes_state.count)) - return; - inode = vma->vm_file->f_mapping->host; if (!inode) return; @@ -1100,13 +1075,6 @@ void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned lon build_probe_list(inode, vma, start, end, &tmp_list); list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) { - unsigned long vaddr = offset_to_vaddr(vma, uprobe->offset); - /* - * An unregister could have removed the probe before - * unmap. So check before we decrement the count. - */ - if (is_swbp_at_addr(vma->vm_mm, vaddr) == 1) - atomic_dec(&vma->vm_mm->uprobes_state.count); put_uprobe(uprobe); } mutex_unlock(uprobes_mmap_hash(inode)); @@ -1217,7 +1185,6 @@ void uprobe_clear_state(struct mm_struct *mm) void uprobe_reset_state(struct mm_struct *mm) { mm->uprobes_state.xol_area = NULL; - atomic_set(&mm->uprobes_state.count, 0); } /* @@ -1585,8 +1552,7 @@ int uprobe_pre_sstep_notifier(struct pt_regs *regs) { struct uprobe_task *utask; - if (!current->mm || !atomic_read(¤t->mm->uprobes_state.count)) - /* task is currently not uprobed */ + if (!current->mm) return 0; utask = current->utask; -- cgit v1.2.3 From f8ac4ec9c064b330dcc49e03c450fe74298c4622 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 8 Aug 2012 17:11:42 +0200 Subject: uprobes: Introduce MMF_HAS_UPROBES Add the new MMF_HAS_UPROBES flag. It is set by install_breakpoint() and it is copied by dup_mmap(), uprobe_pre_sstep_notifier() checks it to avoid the slow path if the task was never probed. Perhaps it makes sense to check it in valid_vma(is_register => false) as well. This needs the new dup_mmap()->uprobe_dup_mmap() hook. We can't use uprobe_reset_state() or put MMF_HAS_UPROBES into MMF_INIT_MASK, we need oldmm->mmap_sem to avoid the race with uprobe_register() or mmap() from another thread. Currently we never clear this bit, it can be false-positive after uprobe_unregister() or uprobe_munmap() or if dup_mmap() hits the probed VM_DONTCOPY vma. But this is fine correctness-wise and has no effect unless the task hits the non-uprobe breakpoint. Signed-off-by: Oleg Nesterov Acked-by: Srikar Dronamraju --- include/linux/sched.h | 2 ++ include/linux/uprobes.h | 5 +++++ kernel/events/uprobes.c | 22 +++++++++++++++++++++- kernel/fork.c | 1 + 4 files changed, 29 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index b8c86648a2f9..3667c332e61d 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -446,6 +446,8 @@ extern int get_dumpable(struct mm_struct *mm); #define MMF_VM_HUGEPAGE 17 /* set when VM_HUGEPAGE is set on vma */ #define MMF_EXE_FILE_CHANGED 18 /* see prctl_set_mm_exe_file() */ +#define MMF_HAS_UPROBES 19 /* might have uprobes */ + #define MMF_INIT_MASK (MMF_DUMPABLE_MASK | MMF_DUMP_FILTER_MASK) struct sighand_struct { diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index 03ae547c1c31..4a37ab153247 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -108,6 +108,7 @@ extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_con extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); extern int uprobe_mmap(struct vm_area_struct *vma); extern void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end); +extern void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm); extern void uprobe_free_utask(struct task_struct *t); extern void uprobe_copy_process(struct task_struct *t); extern unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs); @@ -138,6 +139,10 @@ static inline void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end) { } +static inline void +uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm) +{ +} static inline void uprobe_notify_resume(struct pt_regs *regs) { } diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 3e2996b809be..33870b17e1dd 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -647,6 +647,7 @@ static int install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, struct vm_area_struct *vma, unsigned long vaddr) { + bool first_uprobe; int ret; /* @@ -678,7 +679,17 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, uprobe->flags |= UPROBE_COPY_INSN; } + /* + * set MMF_HAS_UPROBES in advance for uprobe_pre_sstep_notifier(), + * the task can hit this breakpoint right after __replace_page(). + */ + first_uprobe = !test_bit(MMF_HAS_UPROBES, &mm->flags); + if (first_uprobe) + set_bit(MMF_HAS_UPROBES, &mm->flags); + ret = set_swbp(&uprobe->arch, mm, vaddr); + if (ret && first_uprobe) + clear_bit(MMF_HAS_UPROBES, &mm->flags); return ret; } @@ -1032,6 +1043,9 @@ void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned lon if (!atomic_read(&vma->vm_mm->mm_users)) /* called by mmput() ? */ return; + if (!test_bit(MMF_HAS_UPROBES, &vma->vm_mm->flags)) + return; + /* TODO: unmapping uprobe(s) will need more work */ } @@ -1142,6 +1156,12 @@ void uprobe_reset_state(struct mm_struct *mm) mm->uprobes_state.xol_area = NULL; } +void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm) +{ + if (test_bit(MMF_HAS_UPROBES, &oldmm->flags)) + set_bit(MMF_HAS_UPROBES, &newmm->flags); +} + /* * - search for a free slot. */ @@ -1507,7 +1527,7 @@ int uprobe_pre_sstep_notifier(struct pt_regs *regs) { struct uprobe_task *utask; - if (!current->mm) + if (!current->mm || !test_bit(MMF_HAS_UPROBES, ¤t->mm->flags)) return 0; utask = current->utask; diff --git a/kernel/fork.c b/kernel/fork.c index 912b6f6fe5b8..cbb5f9fcd3e8 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -353,6 +353,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) down_write(&oldmm->mmap_sem); flush_cache_dup_mm(oldmm); + uprobe_dup_mmap(oldmm, mm); /* * Not linked in yet - no deadlock potential: */ -- cgit v1.2.3 From 61559a8165da2b6bab7621ac36379c6280efacb6 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 8 Aug 2012 17:17:46 +0200 Subject: uprobes: Fold uprobe_reset_state() into uprobe_dup_mmap() Now that we have uprobe_dup_mmap() we can fold uprobe_reset_state() into the new hook and remove it. mmput()->uprobe_clear_state() can't be called before dup_mmap(). Signed-off-by: Oleg Nesterov Acked-by: Srikar Dronamraju --- include/linux/uprobes.h | 4 ---- kernel/events/uprobes.c | 10 ++-------- kernel/fork.c | 2 -- 3 files changed, 2 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index 4a37ab153247..30297f95c8af 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -118,7 +118,6 @@ extern void uprobe_notify_resume(struct pt_regs *regs); extern bool uprobe_deny_signal(void); extern bool __weak arch_uprobe_skip_sstep(struct arch_uprobe *aup, struct pt_regs *regs); extern void uprobe_clear_state(struct mm_struct *mm); -extern void uprobe_reset_state(struct mm_struct *mm); #else /* !CONFIG_UPROBES */ struct uprobes_state { }; @@ -163,8 +162,5 @@ static inline void uprobe_copy_process(struct task_struct *t) static inline void uprobe_clear_state(struct mm_struct *mm) { } -static inline void uprobe_reset_state(struct mm_struct *mm) -{ -} #endif /* !CONFIG_UPROBES */ #endif /* _LINUX_UPROBES_H */ diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 33870b17e1dd..610e1c8050cf 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -1148,16 +1148,10 @@ void uprobe_clear_state(struct mm_struct *mm) kfree(area); } -/* - * uprobe_reset_state - Free the area allocated for slots. - */ -void uprobe_reset_state(struct mm_struct *mm) -{ - mm->uprobes_state.xol_area = NULL; -} - void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm) { + newmm->uprobes_state.xol_area = NULL; + if (test_bit(MMF_HAS_UPROBES, &oldmm->flags)) set_bit(MMF_HAS_UPROBES, &newmm->flags); } diff --git a/kernel/fork.c b/kernel/fork.c index cbb5f9fcd3e8..2343c9eaaaf4 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -837,8 +837,6 @@ struct mm_struct *dup_mm(struct task_struct *tsk) #ifdef CONFIG_TRANSPARENT_HUGEPAGE mm->pmd_huge_pte = NULL; #endif - uprobe_reset_state(mm); - if (!mm_init(mm, tsk)) goto fail_nomem; -- cgit v1.2.3 From ded86e7c8fc4404414c4700010c9962ea8bd083a Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 8 Aug 2012 18:07:03 +0200 Subject: uprobes: Remove "verify" argument from set_orig_insn() Nobody does set_orig_insn(verify => false), and I think nobody will. Remove this argument. IIUC set_orig_insn(verify => false) was needed to single-step without xol area. Signed-off-by: Oleg Nesterov Acked-by: Srikar Dronamraju --- include/linux/uprobes.h | 2 +- kernel/events/uprobes.c | 20 +++++++++----------- 2 files changed, 10 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index 30297f95c8af..6d4fe79a1a6a 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -102,7 +102,7 @@ struct uprobes_state { }; extern int __weak set_swbp(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); -extern int __weak set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr, bool verify); +extern int __weak set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); extern bool __weak is_swbp_insn(uprobe_opcode_t *insn); extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 610e1c8050cf..1666632e6edf 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -345,24 +345,22 @@ int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned * @mm: the probed process address space. * @auprobe: arch specific probepoint information. * @vaddr: the virtual address to insert the opcode. - * @verify: if true, verify existance of breakpoint instruction. * * For mm @mm, restore the original opcode (opcode) at @vaddr. * Return 0 (success) or a negative errno. */ int __weak -set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr, bool verify) +set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr) { - if (verify) { - int result; + int result; + + result = is_swbp_at_addr(mm, vaddr); + if (!result) + return -EINVAL; - result = is_swbp_at_addr(mm, vaddr); - if (!result) - return -EINVAL; + if (result != 1) + return result; - if (result != 1) - return result; - } return write_opcode(auprobe, mm, vaddr, *(uprobe_opcode_t *)auprobe->insn); } @@ -697,7 +695,7 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, static void remove_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, unsigned long vaddr) { - set_orig_insn(&uprobe->arch, mm, vaddr, true); + set_orig_insn(&uprobe->arch, mm, vaddr); } /* -- cgit v1.2.3 From b969afc8b719bbe3f0842a694e6bf5e87f08868f Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Thu, 23 Aug 2012 18:14:54 +0200 Subject: ASoC: atmel-ssc: include linux/io.h for raw io Include linux/io.h for raw io operations in atmel-scc header. This fixes the following build error: CC [M] sound/soc/atmel/atmel_ssc_dai.o sound/soc/atmel/atmel_ssc_dai.c: In function 'atmel_ssc_interrupt': sound/soc/atmel/atmel_ssc_dai.c:171: error: implicit declaration of function '__raw_readl' sound/soc/atmel/atmel_ssc_dai.c: In function 'atmel_ssc_shutdown': sound/soc/atmel/atmel_ssc_dai.c:249: error: implicit declaration of function '__raw_writel' Signed-off-by: Joachim Eastwood Signed-off-by: Nicolas Ferre Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Mark Brown --- include/linux/atmel-ssc.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/atmel-ssc.h b/include/linux/atmel-ssc.h index 06023393fba9..4eb31752e2b7 100644 --- a/include/linux/atmel-ssc.h +++ b/include/linux/atmel-ssc.h @@ -3,6 +3,7 @@ #include #include +#include struct ssc_device { struct list_head list; -- cgit v1.2.3 From 2c58e2669f197ab0fd5e7552fe82f7bc7d06b15d Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 5 Aug 2012 10:09:57 +0800 Subject: regulator: max77686: initialize of_node param for regulator register Initialize config.of_node for regulator before registering. This is needed for DT based regulator support. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/max77686.c | 2 ++ include/linux/mfd/max77686.h | 1 + 2 files changed, 3 insertions(+) (limited to 'include') diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c index c564af6f05a3..87544b34628a 100644 --- a/drivers/regulator/max77686.c +++ b/drivers/regulator/max77686.c @@ -265,6 +265,7 @@ static int max77686_pmic_dt_parse_pdata(struct max77686_dev *iodev, rmatch.of_node = NULL; of_regulator_match(iodev->dev, regulators_np, &rmatch, 1); rdata[i].initdata = rmatch.init_data; + rdata[i].of_node = rmatch.of_node; } pdata->regulators = rdata; @@ -325,6 +326,7 @@ static __devinit int max77686_pmic_probe(struct platform_device *pdev) for (i = 0; i < MAX77686_REGULATORS; i++) { config.init_data = pdata->regulators[i].initdata; + config.of_node = pdata->regulators[i].of_node; rdev[i] = regulator_register(®ulators[i], &config); if (IS_ERR(rdev[i])) { diff --git a/include/linux/mfd/max77686.h b/include/linux/mfd/max77686.h index 3d7ae4d7fd36..46c0f320ed76 100644 --- a/include/linux/mfd/max77686.h +++ b/include/linux/mfd/max77686.h @@ -74,6 +74,7 @@ enum max77686_regulators { struct max77686_regulator_data { int id; struct regulator_init_data *initdata; + struct device_node *of_node; }; enum max77686_opmode { -- cgit v1.2.3 From 89009e1888ad9a7f4491b8644884f5339773e643 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 8 Aug 2012 20:17:18 +0800 Subject: regulator: Update comment for set_current_limit callback of struct regulator_ops The regulators should be tending to the maximum in the available range and consumers should specify the widest range possible. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- include/linux/regulator/driver.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index bac4c871f3bd..c10012f0af15 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -58,6 +58,7 @@ enum regulator_status { * regulator_desc.n_voltages. Voltages may be reported in any order. * * @set_current_limit: Configure a limit for a current-limited regulator. + * The driver should select the current closest to max_uA. * @get_current_limit: Get the configured limit for a current-limited regulator. * * @set_mode: Set the configured operating mode for the regulator. -- cgit v1.2.3 From 4cdd34084d539c758d00c5dc7bf95db2e4f2bc70 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sun, 26 Aug 2012 19:13:58 +0200 Subject: netfilter: nf_conntrack_ipv6: improve fragmentation handling The IPv6 conntrack fragmentation currently has a couple of shortcomings. Fragmentes are collected in PREROUTING/OUTPUT, are defragmented, the defragmented packet is then passed to conntrack, the resulting conntrack information is attached to each original fragment and the fragments then continue their way through the stack. Helper invocation occurs in the POSTROUTING hook, at which point only the original fragments are available. The result of this is that fragmented packets are never passed to helpers. This patch improves the situation in the following way: - If a reassembled packet belongs to a connection that has a helper assigned, the reassembled packet is passed through the stack instead of the original fragments. - During defragmentation, the largest received fragment size is stored. On output, the packet is refragmented if required. If the largest received fragment size exceeds the outgoing MTU, a "packet too big" message is generated, thus behaving as if the original fragments were passed through the stack from an outside point of view. - The ipv6_helper() hook function can't receive fragments anymore for connections using a helper, so it is switched to use ipv6_skip_exthdr() instead of the netfilter specific nf_ct_ipv6_skip_exthdr() and the reassembled packets are passed to connection tracking helpers. The result of this is that we can properly track fragmented packets, but still generate ICMPv6 Packet too big messages if we would have before. This patch is also required as a precondition for IPv6 NAT, where NAT helpers might enlarge packets up to a point that they require fragmentation. In that case we can't generate Packet too big messages since the proper MTU can't be calculated in all cases (f.i. when changing textual representation of a variable amount of addresses), so the packet is transparently fragmented iff the original packet or fragments would have fit the outgoing MTU. IPVS parts by Jesper Dangaard Brouer . Signed-off-by: Patrick McHardy --- include/linux/ipv6.h | 1 + net/ipv6/ip6_output.c | 7 +++-- net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 41 ++++++++++++++++++++------ net/ipv6/netfilter/nf_conntrack_reasm.c | 19 ++++++++++-- net/netfilter/ipvs/ip_vs_xmit.c | 9 +++++- 5 files changed, 62 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 879db26ec401..0b94e91ed685 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -256,6 +256,7 @@ struct inet6_skb_parm { #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) __u16 dsthao; #endif + __u16 frag_max_size; #define IP6SKB_XFRM_TRANSFORMED 1 #define IP6SKB_FORWARDED 2 diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 5b2d63ed793e..a4f6263fddca 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -493,7 +493,8 @@ int ip6_forward(struct sk_buff *skb) if (mtu < IPV6_MIN_MTU) mtu = IPV6_MIN_MTU; - if (skb->len > mtu && !skb_is_gso(skb)) { + if ((!skb->local_df && skb->len > mtu && !skb_is_gso(skb)) || + (IP6CB(skb)->frag_max_size && IP6CB(skb)->frag_max_size > mtu)) { /* Again, force OUTPUT device used as source address */ skb->dev = dst->dev; icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); @@ -636,7 +637,9 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) /* We must not fragment if the socket is set to force MTU discovery * or if the skb it not generated by a local socket. */ - if (unlikely(!skb->local_df && skb->len > mtu)) { + if (unlikely(!skb->local_df && skb->len > mtu) || + (IP6CB(skb)->frag_max_size && + IP6CB(skb)->frag_max_size > mtu)) { if (skb->sk && dst_allfrag(skb_dst(skb))) sk_nocaps_add(skb->sk, NETIF_F_GSO_MASK); diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index 4794f96cf2e0..521ddca876f8 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -153,10 +153,10 @@ static unsigned int ipv6_helper(unsigned int hooknum, const struct nf_conn_help *help; const struct nf_conntrack_helper *helper; enum ip_conntrack_info ctinfo; - unsigned int ret, protoff; - unsigned int extoff = (u8 *)(ipv6_hdr(skb) + 1) - skb->data; - unsigned char pnum = ipv6_hdr(skb)->nexthdr; - + unsigned int ret; + __be16 frag_off; + int protoff; + u8 nexthdr; /* This is where we call the helper: as the packet goes out. */ ct = nf_ct_get(skb, &ctinfo); @@ -171,9 +171,10 @@ static unsigned int ipv6_helper(unsigned int hooknum, if (!helper) return NF_ACCEPT; - protoff = nf_ct_ipv6_skip_exthdr(skb, extoff, &pnum, - skb->len - extoff); - if (protoff > skb->len || pnum == NEXTHDR_FRAGMENT) { + nexthdr = ipv6_hdr(skb)->nexthdr; + protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr, + &frag_off); + if (protoff < 0 || (frag_off & htons(~0x7)) != 0) { pr_debug("proto header not found\n"); return NF_ACCEPT; } @@ -199,9 +200,14 @@ static unsigned int ipv6_confirm(unsigned int hooknum, static unsigned int __ipv6_conntrack_in(struct net *net, unsigned int hooknum, struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct sk_buff *reasm = skb->nfct_reasm; + const struct nf_conn_help *help; + struct nf_conn *ct; + enum ip_conntrack_info ctinfo; /* This packet is fragmented and has reassembled packet. */ if (reasm) { @@ -213,6 +219,23 @@ static unsigned int __ipv6_conntrack_in(struct net *net, if (ret != NF_ACCEPT) return ret; } + + /* Conntrack helpers need the entire reassembled packet in the + * POST_ROUTING hook. + */ + ct = nf_ct_get(reasm, &ctinfo); + if (ct != NULL && !nf_ct_is_untracked(ct)) { + help = nfct_help(ct); + if (help && help->helper) { + nf_conntrack_get_reasm(skb); + NF_HOOK_THRESH(NFPROTO_IPV6, hooknum, reasm, + (struct net_device *)in, + (struct net_device *)out, + okfn, NF_IP6_PRI_CONNTRACK + 1); + return NF_DROP_ERR(-ECANCELED); + } + } + nf_conntrack_get(reasm->nfct); skb->nfct = reasm->nfct; skb->nfctinfo = reasm->nfctinfo; @@ -228,7 +251,7 @@ static unsigned int ipv6_conntrack_in(unsigned int hooknum, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return __ipv6_conntrack_in(dev_net(in), hooknum, skb, okfn); + return __ipv6_conntrack_in(dev_net(in), hooknum, skb, in, out, okfn); } static unsigned int ipv6_conntrack_local(unsigned int hooknum, @@ -242,7 +265,7 @@ static unsigned int ipv6_conntrack_local(unsigned int hooknum, net_notice_ratelimited("ipv6_conntrack_local: packet too short\n"); return NF_ACCEPT; } - return __ipv6_conntrack_in(dev_net(out), hooknum, skb, okfn); + return __ipv6_conntrack_in(dev_net(out), hooknum, skb, in, out, okfn); } static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = { diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index c9c78c2e666b..f94fb3ac2a79 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -190,6 +190,7 @@ static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb, const struct frag_hdr *fhdr, int nhoff) { struct sk_buff *prev, *next; + unsigned int payload_len; int offset, end; if (fq->q.last_in & INET_FRAG_COMPLETE) { @@ -197,8 +198,10 @@ static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb, goto err; } + payload_len = ntohs(ipv6_hdr(skb)->payload_len); + offset = ntohs(fhdr->frag_off) & ~0x7; - end = offset + (ntohs(ipv6_hdr(skb)->payload_len) - + end = offset + (payload_len - ((u8 *)(fhdr + 1) - (u8 *)(ipv6_hdr(skb) + 1))); if ((unsigned int)end > IPV6_MAXPLEN) { @@ -307,6 +310,8 @@ found: skb->dev = NULL; fq->q.stamp = skb->tstamp; fq->q.meat += skb->len; + if (payload_len > fq->q.max_size) + fq->q.max_size = payload_len; atomic_add(skb->truesize, &nf_init_frags.mem); /* The first fragment. @@ -412,10 +417,12 @@ nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev) } atomic_sub(head->truesize, &nf_init_frags.mem); + head->local_df = 1; head->next = NULL; head->dev = dev; head->tstamp = fq->q.stamp; ipv6_hdr(head)->payload_len = htons(payload_len); + IP6CB(head)->frag_max_size = sizeof(struct ipv6hdr) + fq->q.max_size; /* Yes, and fold redundant checksum back. 8) */ if (head->ip_summed == CHECKSUM_COMPLETE) @@ -592,6 +599,7 @@ void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb, int (*okfn)(struct sk_buff *)) { struct sk_buff *s, *s2; + unsigned int ret = 0; for (s = NFCT_FRAG6_CB(skb)->orig; s;) { nf_conntrack_put_reasm(s->nfct_reasm); @@ -601,8 +609,13 @@ void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb, s2 = s->next; s->next = NULL; - NF_HOOK_THRESH(NFPROTO_IPV6, hooknum, s, in, out, okfn, - NF_IP6_PRI_CONNTRACK_DEFRAG + 1); + if (ret != -ECANCELED) + ret = NF_HOOK_THRESH(NFPROTO_IPV6, hooknum, s, + in, out, okfn, + NF_IP6_PRI_CONNTRACK_DEFRAG + 1); + else + kfree_skb(s); + s = s2; } nf_conntrack_put_reasm(skb); diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 67a39786b0a1..56f6d5d81a77 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -88,7 +88,14 @@ __ip_vs_dst_check(struct ip_vs_dest *dest, u32 rtos) static inline bool __mtu_check_toobig_v6(const struct sk_buff *skb, u32 mtu) { - if (skb->len > mtu && !skb_is_gso(skb)) { + if (IP6CB(skb)->frag_max_size) { + /* frag_max_size tell us that, this packet have been + * defragmented by netfilter IPv6 conntrack module. + */ + if (IP6CB(skb)->frag_max_size > mtu) + return true; /* largest fragment violate MTU */ + } + else if (skb->len > mtu && !skb_is_gso(skb)) { return true; /* Packet size violate MTU size */ } return false; -- cgit v1.2.3 From 051966c0c644a1c96092d4206e00704ade813c9a Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sun, 26 Aug 2012 19:14:04 +0200 Subject: netfilter: nf_nat: add protoff argument to packet mangling functions For mangling IPv6 packets the protocol header offset needs to be known by the NAT packet mangling functions. Add a so far unused protoff argument and convert the conntrack and NAT helpers to use it in preparation of IPv6 NAT. Signed-off-by: Patrick McHardy --- include/linux/netfilter/nf_conntrack_amanda.h | 1 + include/linux/netfilter/nf_conntrack_ftp.h | 1 + include/linux/netfilter/nf_conntrack_h323.h | 15 +- include/linux/netfilter/nf_conntrack_irc.h | 1 + include/linux/netfilter/nf_conntrack_pptp.h | 2 + include/linux/netfilter/nf_conntrack_sip.h | 12 +- include/net/netfilter/nf_nat_helper.h | 11 +- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 6 +- net/ipv4/netfilter/nf_nat_amanda.c | 3 +- net/ipv4/netfilter/nf_nat_ftp.c | 3 +- net/ipv4/netfilter/nf_nat_h323.c | 48 ++++--- net/ipv4/netfilter/nf_nat_helper.c | 9 +- net/ipv4/netfilter/nf_nat_irc.c | 3 +- net/ipv4/netfilter/nf_nat_pptp.c | 6 +- net/ipv4/netfilter/nf_nat_sip.c | 96 +++++++------ net/netfilter/ipvs/ip_vs_ftp.c | 1 + net/netfilter/nf_conntrack_amanda.c | 5 +- net/netfilter/nf_conntrack_ftp.c | 3 +- net/netfilter/nf_conntrack_h323_main.c | 191 +++++++++++++++---------- net/netfilter/nf_conntrack_irc.c | 3 +- net/netfilter/nf_conntrack_pptp.c | 18 +-- net/netfilter/nf_conntrack_sip.c | 95 +++++++----- 22 files changed, 328 insertions(+), 205 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/nf_conntrack_amanda.h b/include/linux/netfilter/nf_conntrack_amanda.h index 0bb5a6976bf3..4b59a1584959 100644 --- a/include/linux/netfilter/nf_conntrack_amanda.h +++ b/include/linux/netfilter/nf_conntrack_amanda.h @@ -4,6 +4,7 @@ extern unsigned int (*nf_nat_amanda_hook)(struct sk_buff *skb, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned int matchoff, unsigned int matchlen, struct nf_conntrack_expect *exp); diff --git a/include/linux/netfilter/nf_conntrack_ftp.h b/include/linux/netfilter/nf_conntrack_ftp.h index 3e3aa08980c3..28f18df36525 100644 --- a/include/linux/netfilter/nf_conntrack_ftp.h +++ b/include/linux/netfilter/nf_conntrack_ftp.h @@ -34,6 +34,7 @@ struct nf_conntrack_expect; extern unsigned int (*nf_nat_ftp_hook)(struct sk_buff *skb, enum ip_conntrack_info ctinfo, enum nf_ct_ftp_type type, + unsigned int protoff, unsigned int matchoff, unsigned int matchlen, struct nf_conntrack_expect *exp); diff --git a/include/linux/netfilter/nf_conntrack_h323.h b/include/linux/netfilter/nf_conntrack_h323.h index 26f9226ea72b..f381020eee92 100644 --- a/include/linux/netfilter/nf_conntrack_h323.h +++ b/include/linux/netfilter/nf_conntrack_h323.h @@ -36,12 +36,12 @@ extern void nf_conntrack_h245_expect(struct nf_conn *new, struct nf_conntrack_expect *this); extern void nf_conntrack_q931_expect(struct nf_conn *new, struct nf_conntrack_expect *this); -extern int (*set_h245_addr_hook) (struct sk_buff *skb, +extern int (*set_h245_addr_hook) (struct sk_buff *skb, unsigned int protoff, unsigned char **data, int dataoff, H245_TransportAddress *taddr, union nf_inet_addr *addr, __be16 port); -extern int (*set_h225_addr_hook) (struct sk_buff *skb, +extern int (*set_h225_addr_hook) (struct sk_buff *skb, unsigned int protoff, unsigned char **data, int dataoff, TransportAddress *taddr, union nf_inet_addr *addr, @@ -49,40 +49,45 @@ extern int (*set_h225_addr_hook) (struct sk_buff *skb, extern int (*set_sig_addr_hook) (struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, - unsigned char **data, + unsigned int protoff, unsigned char **data, TransportAddress *taddr, int count); extern int (*set_ras_addr_hook) (struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, - unsigned char **data, + unsigned int protoff, unsigned char **data, TransportAddress *taddr, int count); extern int (*nat_rtp_rtcp_hook) (struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, + unsigned int protoff, unsigned char **data, + int dataoff, H245_TransportAddress *taddr, __be16 port, __be16 rtp_port, struct nf_conntrack_expect *rtp_exp, struct nf_conntrack_expect *rtcp_exp); extern int (*nat_t120_hook) (struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, int dataoff, H245_TransportAddress *taddr, __be16 port, struct nf_conntrack_expect *exp); extern int (*nat_h245_hook) (struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, int dataoff, TransportAddress *taddr, __be16 port, struct nf_conntrack_expect *exp); extern int (*nat_callforwarding_hook) (struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, int dataoff, TransportAddress *taddr, __be16 port, struct nf_conntrack_expect *exp); extern int (*nat_q931_hook) (struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, TransportAddress *taddr, int idx, __be16 port, struct nf_conntrack_expect *exp); diff --git a/include/linux/netfilter/nf_conntrack_irc.h b/include/linux/netfilter/nf_conntrack_irc.h index 36282bf71b63..4bb9bae67176 100644 --- a/include/linux/netfilter/nf_conntrack_irc.h +++ b/include/linux/netfilter/nf_conntrack_irc.h @@ -7,6 +7,7 @@ extern unsigned int (*nf_nat_irc_hook)(struct sk_buff *skb, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned int matchoff, unsigned int matchlen, struct nf_conntrack_expect *exp); diff --git a/include/linux/netfilter/nf_conntrack_pptp.h b/include/linux/netfilter/nf_conntrack_pptp.h index 3bbde0c3a8a6..2ab2830316b7 100644 --- a/include/linux/netfilter/nf_conntrack_pptp.h +++ b/include/linux/netfilter/nf_conntrack_pptp.h @@ -303,12 +303,14 @@ struct nf_conntrack_expect; extern int (*nf_nat_pptp_hook_outbound)(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, struct PptpControlHeader *ctlh, union pptp_ctrl_union *pptpReq); extern int (*nf_nat_pptp_hook_inbound)(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, struct PptpControlHeader *ctlh, union pptp_ctrl_union *pptpReq); diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h index 89f2a627f3f0..1afc669a393e 100644 --- a/include/linux/netfilter/nf_conntrack_sip.h +++ b/include/linux/netfilter/nf_conntrack_sip.h @@ -37,10 +37,12 @@ struct sdp_media_type { struct sip_handler { const char *method; unsigned int len; - int (*request)(struct sk_buff *skb, unsigned int dataoff, + int (*request)(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int cseq); - int (*response)(struct sk_buff *skb, unsigned int dataoff, + int (*response)(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int cseq, unsigned int code); }; @@ -105,11 +107,13 @@ enum sdp_header_types { }; extern unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb, + unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen); extern void (*nf_nat_sip_seq_adjust_hook)(struct sk_buff *skb, s16 off); extern unsigned int (*nf_nat_sip_expect_hook)(struct sk_buff *skb, + unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen, @@ -117,6 +121,7 @@ extern unsigned int (*nf_nat_sip_expect_hook)(struct sk_buff *skb, unsigned int matchoff, unsigned int matchlen); extern unsigned int (*nf_nat_sdp_addr_hook)(struct sk_buff *skb, + unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen, @@ -125,6 +130,7 @@ extern unsigned int (*nf_nat_sdp_addr_hook)(struct sk_buff *skb, enum sdp_header_types term, const union nf_inet_addr *addr); extern unsigned int (*nf_nat_sdp_port_hook)(struct sk_buff *skb, + unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen, @@ -132,12 +138,14 @@ extern unsigned int (*nf_nat_sdp_port_hook)(struct sk_buff *skb, unsigned int matchlen, u_int16_t port); extern unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb, + unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int sdpoff, const union nf_inet_addr *addr); extern unsigned int (*nf_nat_sdp_media_hook)(struct sk_buff *skb, + unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen, diff --git a/include/net/netfilter/nf_nat_helper.h b/include/net/netfilter/nf_nat_helper.h index 7d8fb7b46c44..b4d6bfc2af03 100644 --- a/include/net/netfilter/nf_nat_helper.h +++ b/include/net/netfilter/nf_nat_helper.h @@ -10,6 +10,7 @@ struct sk_buff; extern int __nf_nat_mangle_tcp_packet(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, @@ -18,12 +19,13 @@ extern int __nf_nat_mangle_tcp_packet(struct sk_buff *skb, static inline int nf_nat_mangle_tcp_packet(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, unsigned int rep_len) { - return __nf_nat_mangle_tcp_packet(skb, ct, ctinfo, + return __nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff, match_offset, match_len, rep_buffer, rep_len, true); } @@ -31,6 +33,7 @@ static inline int nf_nat_mangle_tcp_packet(struct sk_buff *skb, extern int nf_nat_mangle_udp_packet(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, @@ -41,10 +44,12 @@ extern void nf_nat_set_seq_adjust(struct nf_conn *ct, __be32 seq, s16 off); extern int nf_nat_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, - enum ip_conntrack_info ctinfo); + 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); + 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. */ diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index e7ff2dcab6ce..4ada3295d9a7 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -31,7 +31,8 @@ int (*nf_nat_seq_adjust_hook)(struct sk_buff *skb, struct nf_conn *ct, - enum ip_conntrack_info ctinfo); + enum ip_conntrack_info ctinfo, + unsigned int protoff); EXPORT_SYMBOL_GPL(nf_nat_seq_adjust_hook); static bool ipv4_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff, @@ -149,7 +150,8 @@ static unsigned int ipv4_confirm(unsigned int hooknum, 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)) { + if (!seq_adjust || + !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/ipv4/netfilter/nf_nat_amanda.c b/net/ipv4/netfilter/nf_nat_amanda.c index 3c04d24e2976..75464b62f5f2 100644 --- a/net/ipv4/netfilter/nf_nat_amanda.c +++ b/net/ipv4/netfilter/nf_nat_amanda.c @@ -26,6 +26,7 @@ MODULE_ALIAS("ip_nat_amanda"); static unsigned int help(struct sk_buff *skb, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned int matchoff, unsigned int matchlen, struct nf_conntrack_expect *exp) @@ -61,7 +62,7 @@ static unsigned int help(struct sk_buff *skb, sprintf(buffer, "%u", port); ret = nf_nat_mangle_udp_packet(skb, exp->master, ctinfo, - matchoff, matchlen, + protoff, matchoff, matchlen, buffer, strlen(buffer)); if (ret != NF_ACCEPT) nf_ct_unexpect_related(exp); diff --git a/net/ipv4/netfilter/nf_nat_ftp.c b/net/ipv4/netfilter/nf_nat_ftp.c index e462a957d080..5589f3af4a8e 100644 --- a/net/ipv4/netfilter/nf_nat_ftp.c +++ b/net/ipv4/netfilter/nf_nat_ftp.c @@ -55,6 +55,7 @@ static int nf_nat_ftp_fmt_cmd(enum nf_ct_ftp_type type, static unsigned int nf_nat_ftp(struct sk_buff *skb, enum ip_conntrack_info ctinfo, enum nf_ct_ftp_type type, + unsigned int protoff, unsigned int matchoff, unsigned int matchlen, struct nf_conntrack_expect *exp) @@ -100,7 +101,7 @@ static unsigned int nf_nat_ftp(struct sk_buff *skb, pr_debug("calling nf_nat_mangle_tcp_packet\n"); - if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, matchoff, + if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff, matchoff, matchlen, buffer, buflen)) goto out; diff --git a/net/ipv4/netfilter/nf_nat_h323.c b/net/ipv4/netfilter/nf_nat_h323.c index c6784a18c1c4..d2c228db38b5 100644 --- a/net/ipv4/netfilter/nf_nat_h323.c +++ b/net/ipv4/netfilter/nf_nat_h323.c @@ -21,7 +21,7 @@ #include /****************************************************************************/ -static int set_addr(struct sk_buff *skb, +static int set_addr(struct sk_buff *skb, unsigned int protoff, unsigned char **data, int dataoff, unsigned int addroff, __be32 ip, __be16 port) { @@ -40,7 +40,7 @@ static int set_addr(struct sk_buff *skb, if (ip_hdr(skb)->protocol == IPPROTO_TCP) { if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, - addroff, sizeof(buf), + protoff, addroff, sizeof(buf), (char *) &buf, sizeof(buf))) { net_notice_ratelimited("nf_nat_h323: nf_nat_mangle_tcp_packet error\n"); return -1; @@ -54,7 +54,7 @@ static int set_addr(struct sk_buff *skb, *data = skb->data + ip_hdrlen(skb) + th->doff * 4 + dataoff; } else { if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, - addroff, sizeof(buf), + protoff, addroff, sizeof(buf), (char *) &buf, sizeof(buf))) { net_notice_ratelimited("nf_nat_h323: nf_nat_mangle_udp_packet error\n"); return -1; @@ -69,22 +69,22 @@ static int set_addr(struct sk_buff *skb, } /****************************************************************************/ -static int set_h225_addr(struct sk_buff *skb, +static int set_h225_addr(struct sk_buff *skb, unsigned int protoff, unsigned char **data, int dataoff, TransportAddress *taddr, union nf_inet_addr *addr, __be16 port) { - return set_addr(skb, data, dataoff, taddr->ipAddress.ip, + return set_addr(skb, protoff, data, dataoff, taddr->ipAddress.ip, addr->ip, port); } /****************************************************************************/ -static int set_h245_addr(struct sk_buff *skb, +static int set_h245_addr(struct sk_buff *skb, unsigned protoff, unsigned char **data, int dataoff, H245_TransportAddress *taddr, union nf_inet_addr *addr, __be16 port) { - return set_addr(skb, data, dataoff, + return set_addr(skb, protoff, data, dataoff, taddr->unicastAddress.iPAddress.network, addr->ip, port); } @@ -92,7 +92,7 @@ static int set_h245_addr(struct sk_buff *skb, /****************************************************************************/ static int set_sig_addr(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, - unsigned char **data, + unsigned int protoff, unsigned char **data, TransportAddress *taddr, int count) { const struct nf_ct_h323_master *info = nfct_help_data(ct); @@ -118,7 +118,8 @@ static int set_sig_addr(struct sk_buff *skb, struct nf_conn *ct, &addr.ip, port, &ct->tuplehash[!dir].tuple.dst.u3.ip, info->sig_port[!dir]); - return set_h225_addr(skb, data, 0, &taddr[i], + return set_h225_addr(skb, protoff, data, 0, + &taddr[i], &ct->tuplehash[!dir]. tuple.dst.u3, info->sig_port[!dir]); @@ -129,7 +130,8 @@ static int set_sig_addr(struct sk_buff *skb, struct nf_conn *ct, &addr.ip, port, &ct->tuplehash[!dir].tuple.src.u3.ip, info->sig_port[!dir]); - return set_h225_addr(skb, data, 0, &taddr[i], + return set_h225_addr(skb, protoff, data, 0, + &taddr[i], &ct->tuplehash[!dir]. tuple.src.u3, info->sig_port[!dir]); @@ -143,7 +145,7 @@ static int set_sig_addr(struct sk_buff *skb, struct nf_conn *ct, /****************************************************************************/ static int set_ras_addr(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, - unsigned char **data, + unsigned int protoff, unsigned char **data, TransportAddress *taddr, int count) { int dir = CTINFO2DIR(ctinfo); @@ -159,7 +161,7 @@ static int set_ras_addr(struct sk_buff *skb, struct nf_conn *ct, &addr.ip, ntohs(port), &ct->tuplehash[!dir].tuple.dst.u3.ip, ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.port)); - return set_h225_addr(skb, data, 0, &taddr[i], + return set_h225_addr(skb, protoff, data, 0, &taddr[i], &ct->tuplehash[!dir].tuple.dst.u3, ct->tuplehash[!dir].tuple. dst.u.udp.port); @@ -172,7 +174,7 @@ static int set_ras_addr(struct sk_buff *skb, struct nf_conn *ct, /****************************************************************************/ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, + unsigned int protoff, unsigned char **data, int dataoff, H245_TransportAddress *taddr, __be16 port, __be16 rtp_port, struct nf_conntrack_expect *rtp_exp, @@ -244,7 +246,7 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct, } /* Modify signal */ - if (set_h245_addr(skb, data, dataoff, taddr, + if (set_h245_addr(skb, protoff, data, dataoff, taddr, &ct->tuplehash[!dir].tuple.dst.u3, htons((port & htons(1)) ? nated_port + 1 : nated_port)) == 0) { @@ -275,7 +277,7 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct, /****************************************************************************/ static int nat_t120(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, + unsigned int protoff, unsigned char **data, int dataoff, H245_TransportAddress *taddr, __be16 port, struct nf_conntrack_expect *exp) { @@ -307,7 +309,7 @@ static int nat_t120(struct sk_buff *skb, struct nf_conn *ct, } /* Modify signal */ - if (set_h245_addr(skb, data, dataoff, taddr, + if (set_h245_addr(skb, protoff, data, dataoff, taddr, &ct->tuplehash[!dir].tuple.dst.u3, htons(nated_port)) < 0) { nf_ct_unexpect_related(exp); @@ -326,7 +328,7 @@ static int nat_t120(struct sk_buff *skb, struct nf_conn *ct, /****************************************************************************/ static int nat_h245(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, + unsigned int protoff, unsigned char **data, int dataoff, TransportAddress *taddr, __be16 port, struct nf_conntrack_expect *exp) { @@ -363,7 +365,7 @@ static int nat_h245(struct sk_buff *skb, struct nf_conn *ct, } /* Modify signal */ - if (set_h225_addr(skb, data, dataoff, taddr, + if (set_h225_addr(skb, protoff, data, dataoff, taddr, &ct->tuplehash[!dir].tuple.dst.u3, htons(nated_port)) == 0) { /* Save ports */ @@ -416,7 +418,8 @@ static void ip_nat_q931_expect(struct nf_conn *new, /****************************************************************************/ static int nat_q931(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, - unsigned char **data, TransportAddress *taddr, int idx, + unsigned int protoff, unsigned char **data, + TransportAddress *taddr, int idx, __be16 port, struct nf_conntrack_expect *exp) { struct nf_ct_h323_master *info = nfct_help_data(ct); @@ -453,7 +456,7 @@ static int nat_q931(struct sk_buff *skb, struct nf_conn *ct, } /* Modify signal */ - if (set_h225_addr(skb, data, 0, &taddr[idx], + if (set_h225_addr(skb, protoff, data, 0, &taddr[idx], &ct->tuplehash[!dir].tuple.dst.u3, htons(nated_port)) == 0) { /* Save ports */ @@ -464,7 +467,7 @@ static int nat_q931(struct sk_buff *skb, struct nf_conn *ct, if (idx > 0 && get_h225_addr(ct, *data, &taddr[0], &addr, &port) && (ntohl(addr.ip) & 0xff000000) == 0x7f000000) { - set_h225_addr(skb, data, 0, &taddr[0], + set_h225_addr(skb, protoff, data, 0, &taddr[0], &ct->tuplehash[!dir].tuple.dst.u3, info->sig_port[!dir]); } @@ -507,6 +510,7 @@ static void ip_nat_callforwarding_expect(struct nf_conn *new, /****************************************************************************/ static int nat_callforwarding(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, int dataoff, TransportAddress *taddr, __be16 port, struct nf_conntrack_expect *exp) @@ -541,7 +545,7 @@ static int nat_callforwarding(struct sk_buff *skb, struct nf_conn *ct, } /* Modify signal */ - if (!set_h225_addr(skb, data, dataoff, taddr, + if (!set_h225_addr(skb, protoff, data, dataoff, taddr, &ct->tuplehash[!dir].tuple.dst.u3, htons(nated_port)) == 0) { nf_ct_unexpect_related(exp); diff --git a/net/ipv4/netfilter/nf_nat_helper.c b/net/ipv4/netfilter/nf_nat_helper.c index 2e59ad0b90ca..2fefec5e757c 100644 --- a/net/ipv4/netfilter/nf_nat_helper.c +++ b/net/ipv4/netfilter/nf_nat_helper.c @@ -206,6 +206,7 @@ static void nf_nat_csum(struct sk_buff *skb, const struct iphdr *iph, void *data int __nf_nat_mangle_tcp_packet(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, @@ -257,6 +258,7 @@ int nf_nat_mangle_udp_packet(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, @@ -387,7 +389,8 @@ nf_nat_sack_adjust(struct sk_buff *skb, int nf_nat_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, - enum ip_conntrack_info ctinfo) + enum ip_conntrack_info ctinfo, + unsigned int protoff) { struct tcphdr *tcph; int dir; @@ -401,10 +404,10 @@ nf_nat_seq_adjust(struct sk_buff *skb, this_way = &nat->seq[dir]; other_way = &nat->seq[!dir]; - if (!skb_make_writable(skb, ip_hdrlen(skb) + sizeof(*tcph))) + if (!skb_make_writable(skb, protoff + sizeof(*tcph))) return 0; - tcph = (void *)skb->data + ip_hdrlen(skb); + tcph = (void *)skb->data + protoff; if (after(ntohl(tcph->seq), this_way->correction_pos)) seqoff = this_way->offset_after; else diff --git a/net/ipv4/netfilter/nf_nat_irc.c b/net/ipv4/netfilter/nf_nat_irc.c index 979ae165f4ef..5b0c20a1f08d 100644 --- a/net/ipv4/netfilter/nf_nat_irc.c +++ b/net/ipv4/netfilter/nf_nat_irc.c @@ -29,6 +29,7 @@ MODULE_ALIAS("ip_nat_irc"); static unsigned int help(struct sk_buff *skb, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned int matchoff, unsigned int matchlen, struct nf_conntrack_expect *exp) @@ -66,7 +67,7 @@ static unsigned int help(struct sk_buff *skb, buffer, &ip, port); ret = nf_nat_mangle_tcp_packet(skb, exp->master, ctinfo, - matchoff, matchlen, buffer, + protoff, matchoff, matchlen, buffer, strlen(buffer)); if (ret != NF_ACCEPT) nf_ct_unexpect_related(exp); diff --git a/net/ipv4/netfilter/nf_nat_pptp.c b/net/ipv4/netfilter/nf_nat_pptp.c index 388140881ebe..31ef890d894b 100644 --- a/net/ipv4/netfilter/nf_nat_pptp.c +++ b/net/ipv4/netfilter/nf_nat_pptp.c @@ -113,6 +113,7 @@ static int pptp_outbound_pkt(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, struct PptpControlHeader *ctlh, union pptp_ctrl_union *pptpReq) @@ -175,7 +176,7 @@ pptp_outbound_pkt(struct sk_buff *skb, ntohs(REQ_CID(pptpReq, cid_off)), ntohs(new_callid)); /* mangle packet */ - if (nf_nat_mangle_tcp_packet(skb, ct, ctinfo, + if (nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff, cid_off + sizeof(struct pptp_pkt_hdr) + sizeof(struct PptpControlHeader), sizeof(new_callid), (char *)&new_callid, @@ -216,6 +217,7 @@ static int pptp_inbound_pkt(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, struct PptpControlHeader *ctlh, union pptp_ctrl_union *pptpReq) { @@ -268,7 +270,7 @@ pptp_inbound_pkt(struct sk_buff *skb, pr_debug("altering peer call id from 0x%04x to 0x%04x\n", ntohs(REQ_CID(pptpReq, pcid_off)), ntohs(new_pcid)); - if (nf_nat_mangle_tcp_packet(skb, ct, ctinfo, + if (nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff, pcid_off + sizeof(struct pptp_pkt_hdr) + sizeof(struct PptpControlHeader), sizeof(new_pcid), (char *)&new_pcid, diff --git a/net/ipv4/netfilter/nf_nat_sip.c b/net/ipv4/netfilter/nf_nat_sip.c index 4ad9cf173992..df626af8413c 100644 --- a/net/ipv4/netfilter/nf_nat_sip.c +++ b/net/ipv4/netfilter/nf_nat_sip.c @@ -30,7 +30,8 @@ MODULE_DESCRIPTION("SIP NAT helper"); MODULE_ALIAS("ip_nat_sip"); -static unsigned int mangle_packet(struct sk_buff *skb, unsigned int dataoff, +static unsigned int mangle_packet(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int matchoff, unsigned int matchlen, const char *buffer, unsigned int buflen) @@ -46,7 +47,7 @@ static unsigned int mangle_packet(struct sk_buff *skb, unsigned int dataoff, matchoff += dataoff - baseoff; if (!__nf_nat_mangle_tcp_packet(skb, ct, ctinfo, - matchoff, matchlen, + protoff, matchoff, matchlen, buffer, buflen, false)) return 0; } else { @@ -54,7 +55,7 @@ static unsigned int mangle_packet(struct sk_buff *skb, unsigned int dataoff, matchoff += dataoff - baseoff; if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, - matchoff, matchlen, + protoff, matchoff, matchlen, buffer, buflen)) return 0; } @@ -65,7 +66,8 @@ static unsigned int mangle_packet(struct sk_buff *skb, unsigned int dataoff, return 1; } -static int map_addr(struct sk_buff *skb, unsigned int dataoff, +static int map_addr(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int matchoff, unsigned int matchlen, union nf_inet_addr *addr, __be16 port) @@ -94,11 +96,12 @@ static int map_addr(struct sk_buff *skb, unsigned int dataoff, buflen = sprintf(buffer, "%pI4:%u", &newaddr, ntohs(newport)); - return mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen, - buffer, buflen); + return mangle_packet(skb, protoff, dataoff, dptr, datalen, + matchoff, matchlen, buffer, buflen); } -static int map_sip_addr(struct sk_buff *skb, unsigned int dataoff, +static int map_sip_addr(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen, enum sip_header_types type) { @@ -111,11 +114,12 @@ static int map_sip_addr(struct sk_buff *skb, unsigned int dataoff, if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, type, NULL, &matchoff, &matchlen, &addr, &port) <= 0) return 1; - return map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen, - &addr, port); + return map_addr(skb, protoff, dataoff, dptr, datalen, + matchoff, matchlen, &addr, port); } -static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int dataoff, +static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen) { enum ip_conntrack_info ctinfo; @@ -132,8 +136,8 @@ static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int dataoff, if (ct_sip_parse_request(ct, *dptr, *datalen, &matchoff, &matchlen, &addr, &port) > 0 && - !map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen, - &addr, port)) + !map_addr(skb, protoff, dataoff, dptr, datalen, + matchoff, matchlen, &addr, port)) return NF_DROP; request = 1; } else @@ -164,8 +168,8 @@ static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int dataoff, } olen = *datalen; - if (!map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen, - &addr, port)) + if (!map_addr(skb, protoff, dataoff, dptr, datalen, + matchoff, matchlen, &addr, port)) return NF_DROP; matchend = matchoff + matchlen + *datalen - olen; @@ -179,7 +183,7 @@ static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int dataoff, addr.ip != ct->tuplehash[!dir].tuple.dst.u3.ip) { buflen = sprintf(buffer, "%pI4", &ct->tuplehash[!dir].tuple.dst.u3.ip); - if (!mangle_packet(skb, dataoff, dptr, datalen, + if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, poff, plen, buffer, buflen)) return NF_DROP; } @@ -193,7 +197,7 @@ static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int dataoff, addr.ip != ct->tuplehash[!dir].tuple.src.u3.ip) { buflen = sprintf(buffer, "%pI4", &ct->tuplehash[!dir].tuple.src.u3.ip); - if (!mangle_packet(skb, dataoff, dptr, datalen, + if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, poff, plen, buffer, buflen)) return NF_DROP; } @@ -207,7 +211,7 @@ static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int dataoff, htons(n) != ct->tuplehash[!dir].tuple.src.u.udp.port) { __be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port; buflen = sprintf(buffer, "%u", ntohs(p)); - if (!mangle_packet(skb, dataoff, dptr, datalen, + if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, poff, plen, buffer, buflen)) return NF_DROP; } @@ -221,13 +225,14 @@ next: SIP_HDR_CONTACT, &in_header, &matchoff, &matchlen, &addr, &port) > 0) { - if (!map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen, + if (!map_addr(skb, protoff, dataoff, dptr, datalen, + matchoff, matchlen, &addr, port)) return NF_DROP; } - if (!map_sip_addr(skb, dataoff, dptr, datalen, SIP_HDR_FROM) || - !map_sip_addr(skb, dataoff, dptr, datalen, SIP_HDR_TO)) + if (!map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_FROM) || + !map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_TO)) return NF_DROP; return NF_ACCEPT; @@ -272,7 +277,8 @@ static void ip_nat_sip_expected(struct nf_conn *ct, } } -static unsigned int ip_nat_sip_expect(struct sk_buff *skb, unsigned int dataoff, +static unsigned int ip_nat_sip_expect(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen, struct nf_conntrack_expect *exp, unsigned int matchoff, @@ -326,7 +332,7 @@ static unsigned int ip_nat_sip_expect(struct sk_buff *skb, unsigned int dataoff, if (exp->tuple.dst.u3.ip != exp->saved_ip || exp->tuple.dst.u.udp.port != exp->saved_proto.udp.port) { buflen = sprintf(buffer, "%pI4:%u", &newip, port); - if (!mangle_packet(skb, dataoff, dptr, datalen, + if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, matchoff, matchlen, buffer, buflen)) goto err; } @@ -337,7 +343,8 @@ err: return NF_DROP; } -static int mangle_content_len(struct sk_buff *skb, unsigned int dataoff, +static int mangle_content_len(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen) { enum ip_conntrack_info ctinfo; @@ -359,11 +366,12 @@ static int mangle_content_len(struct sk_buff *skb, unsigned int dataoff, return 0; buflen = sprintf(buffer, "%u", c_len); - return mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen, - buffer, buflen); + return mangle_packet(skb, protoff, dataoff, dptr, datalen, + matchoff, matchlen, buffer, buflen); } -static int mangle_sdp_packet(struct sk_buff *skb, unsigned int dataoff, +static int mangle_sdp_packet(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int sdpoff, enum sdp_header_types type, @@ -377,11 +385,12 @@ static int mangle_sdp_packet(struct sk_buff *skb, unsigned int dataoff, if (ct_sip_get_sdp_header(ct, *dptr, sdpoff, *datalen, type, term, &matchoff, &matchlen) <= 0) return -ENOENT; - return mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen, - buffer, buflen) ? 0 : -EINVAL; + return mangle_packet(skb, protoff, dataoff, dptr, datalen, + matchoff, matchlen, buffer, buflen) ? 0 : -EINVAL; } -static unsigned int ip_nat_sdp_addr(struct sk_buff *skb, unsigned int dataoff, +static unsigned int ip_nat_sdp_addr(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int sdpoff, enum sdp_header_types type, @@ -392,14 +401,15 @@ static unsigned int ip_nat_sdp_addr(struct sk_buff *skb, unsigned int dataoff, unsigned int buflen; buflen = sprintf(buffer, "%pI4", &addr->ip); - if (mangle_sdp_packet(skb, dataoff, dptr, datalen, sdpoff, type, term, - buffer, buflen)) + if (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen, + sdpoff, type, term, buffer, buflen)) return 0; - return mangle_content_len(skb, dataoff, dptr, datalen); + return mangle_content_len(skb, protoff, dataoff, dptr, datalen); } -static unsigned int ip_nat_sdp_port(struct sk_buff *skb, unsigned int dataoff, +static unsigned int ip_nat_sdp_port(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int matchoff, unsigned int matchlen, @@ -409,14 +419,15 @@ static unsigned int ip_nat_sdp_port(struct sk_buff *skb, unsigned int dataoff, unsigned int buflen; buflen = sprintf(buffer, "%u", port); - if (!mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen, - buffer, buflen)) + if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, + matchoff, matchlen, buffer, buflen)) return 0; - return mangle_content_len(skb, dataoff, dptr, datalen); + return mangle_content_len(skb, protoff, dataoff, dptr, datalen); } -static unsigned int ip_nat_sdp_session(struct sk_buff *skb, unsigned int dataoff, +static unsigned int ip_nat_sdp_session(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int sdpoff, const union nf_inet_addr *addr) @@ -426,12 +437,12 @@ static unsigned int ip_nat_sdp_session(struct sk_buff *skb, unsigned int dataoff /* Mangle session description owner and contact addresses */ buflen = sprintf(buffer, "%pI4", &addr->ip); - if (mangle_sdp_packet(skb, dataoff, dptr, datalen, sdpoff, + if (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen, sdpoff, SDP_HDR_OWNER_IP4, SDP_HDR_MEDIA, buffer, buflen)) return 0; - switch (mangle_sdp_packet(skb, dataoff, dptr, datalen, sdpoff, + switch (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen, sdpoff, SDP_HDR_CONNECTION_IP4, SDP_HDR_MEDIA, buffer, buflen)) { case 0: @@ -448,12 +459,13 @@ static unsigned int ip_nat_sdp_session(struct sk_buff *skb, unsigned int dataoff return 0; } - return mangle_content_len(skb, dataoff, dptr, datalen); + return mangle_content_len(skb, protoff, dataoff, dptr, datalen); } /* So, this packet has hit the connection tracking matching code. Mangle it, and change the expectation to match the new version. */ -static unsigned int ip_nat_sdp_media(struct sk_buff *skb, unsigned int dataoff, +static unsigned int ip_nat_sdp_media(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen, struct nf_conntrack_expect *rtp_exp, struct nf_conntrack_expect *rtcp_exp, @@ -514,7 +526,7 @@ static unsigned int ip_nat_sdp_media(struct sk_buff *skb, unsigned int dataoff, /* Update media port. */ if (rtp_exp->tuple.dst.u.udp.port != rtp_exp->saved_proto.udp.port && - !ip_nat_sdp_port(skb, dataoff, dptr, datalen, + !ip_nat_sdp_port(skb, protoff, dataoff, dptr, datalen, mediaoff, medialen, port)) goto err2; diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c index ad70b7e4ac4a..4f53a5f04437 100644 --- a/net/netfilter/ipvs/ip_vs_ftp.c +++ b/net/netfilter/ipvs/ip_vs_ftp.c @@ -268,6 +268,7 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, * packet. */ ret = nf_nat_mangle_tcp_packet(skb, ct, ctinfo, + iph->ihl * 4, start-data, end-start, buf, buf_len); if (ret) { diff --git a/net/netfilter/nf_conntrack_amanda.c b/net/netfilter/nf_conntrack_amanda.c index 184c0dc6e437..e0212b5494b1 100644 --- a/net/netfilter/nf_conntrack_amanda.c +++ b/net/netfilter/nf_conntrack_amanda.c @@ -40,6 +40,7 @@ MODULE_PARM_DESC(ts_algo, "textsearch algorithm to use (default kmp)"); unsigned int (*nf_nat_amanda_hook)(struct sk_buff *skb, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned int matchoff, unsigned int matchlen, struct nf_conntrack_expect *exp) @@ -156,8 +157,8 @@ static int amanda_help(struct sk_buff *skb, nf_nat_amanda = rcu_dereference(nf_nat_amanda_hook); if (nf_nat_amanda && nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK) - ret = nf_nat_amanda(skb, ctinfo, off - dataoff, - len, exp); + ret = nf_nat_amanda(skb, ctinfo, protoff, + off - dataoff, len, exp); else if (nf_ct_expect_related(exp) != 0) ret = NF_DROP; nf_ct_expect_put(exp); diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c index 3e1587e63c03..c0f4a5ba9016 100644 --- a/net/netfilter/nf_conntrack_ftp.c +++ b/net/netfilter/nf_conntrack_ftp.c @@ -48,6 +48,7 @@ module_param(loose, bool, 0600); unsigned int (*nf_nat_ftp_hook)(struct sk_buff *skb, enum ip_conntrack_info ctinfo, enum nf_ct_ftp_type type, + unsigned int protoff, unsigned int matchoff, unsigned int matchlen, struct nf_conntrack_expect *exp); @@ -490,7 +491,7 @@ static int help(struct sk_buff *skb, if (nf_nat_ftp && nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK) ret = nf_nat_ftp(skb, ctinfo, search[dir][i].ftptype, - matchoff, matchlen, exp); + protoff, matchoff, matchlen, exp); else { /* Can't expect this? Best to drop packet now. */ if (nf_ct_expect_related(exp) != 0) diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c index 517c5e3fe7c6..1b30b0dee708 100644 --- a/net/netfilter/nf_conntrack_h323_main.c +++ b/net/netfilter/nf_conntrack_h323_main.c @@ -49,12 +49,12 @@ MODULE_PARM_DESC(callforward_filter, "only create call forwarding expectations " "(determined by routing information)"); /* Hooks for NAT */ -int (*set_h245_addr_hook) (struct sk_buff *skb, +int (*set_h245_addr_hook) (struct sk_buff *skb, unsigned int protoff, unsigned char **data, int dataoff, H245_TransportAddress *taddr, union nf_inet_addr *addr, __be16 port) __read_mostly; -int (*set_h225_addr_hook) (struct sk_buff *skb, +int (*set_h225_addr_hook) (struct sk_buff *skb, unsigned int protoff, unsigned char **data, int dataoff, TransportAddress *taddr, union nf_inet_addr *addr, __be16 port) @@ -62,16 +62,17 @@ int (*set_h225_addr_hook) (struct sk_buff *skb, int (*set_sig_addr_hook) (struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, - unsigned char **data, + unsigned int protoff, unsigned char **data, TransportAddress *taddr, int count) __read_mostly; int (*set_ras_addr_hook) (struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, - unsigned char **data, + unsigned int protoff, unsigned char **data, TransportAddress *taddr, int count) __read_mostly; int (*nat_rtp_rtcp_hook) (struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, int dataoff, H245_TransportAddress *taddr, __be16 port, __be16 rtp_port, @@ -80,24 +81,28 @@ int (*nat_rtp_rtcp_hook) (struct sk_buff *skb, int (*nat_t120_hook) (struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, int dataoff, H245_TransportAddress *taddr, __be16 port, struct nf_conntrack_expect *exp) __read_mostly; int (*nat_h245_hook) (struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, int dataoff, TransportAddress *taddr, __be16 port, struct nf_conntrack_expect *exp) __read_mostly; int (*nat_callforwarding_hook) (struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, int dataoff, TransportAddress *taddr, __be16 port, struct nf_conntrack_expect *exp) __read_mostly; int (*nat_q931_hook) (struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, TransportAddress *taddr, int idx, __be16 port, struct nf_conntrack_expect *exp) __read_mostly; @@ -251,6 +256,7 @@ static int get_h245_addr(struct nf_conn *ct, const unsigned char *data, /****************************************************************************/ static int expect_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, int dataoff, H245_TransportAddress *taddr) { @@ -298,7 +304,7 @@ static int expect_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct, nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK) { /* NAT needed */ - ret = nat_rtp_rtcp(skb, ct, ctinfo, data, dataoff, + ret = nat_rtp_rtcp(skb, ct, ctinfo, protoff, data, dataoff, taddr, port, rtp_port, rtp_exp, rtcp_exp); } else { /* Conntrack only */ if (nf_ct_expect_related(rtp_exp) == 0) { @@ -325,6 +331,7 @@ static int expect_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct, static int expect_t120(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, int dataoff, H245_TransportAddress *taddr) { @@ -357,7 +364,7 @@ static int expect_t120(struct sk_buff *skb, nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK) { /* NAT needed */ - ret = nat_t120(skb, ct, ctinfo, data, dataoff, taddr, + ret = nat_t120(skb, ct, ctinfo, protoff, data, dataoff, taddr, port, exp); } else { /* Conntrack only */ if (nf_ct_expect_related(exp) == 0) { @@ -376,6 +383,7 @@ static int expect_t120(struct sk_buff *skb, static int process_h245_channel(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, int dataoff, H2250LogicalChannelParameters *channel) { @@ -383,7 +391,7 @@ static int process_h245_channel(struct sk_buff *skb, if (channel->options & eH2250LogicalChannelParameters_mediaChannel) { /* RTP */ - ret = expect_rtp_rtcp(skb, ct, ctinfo, data, dataoff, + ret = expect_rtp_rtcp(skb, ct, ctinfo, protoff, data, dataoff, &channel->mediaChannel); if (ret < 0) return -1; @@ -392,7 +400,7 @@ static int process_h245_channel(struct sk_buff *skb, if (channel-> options & eH2250LogicalChannelParameters_mediaControlChannel) { /* RTCP */ - ret = expect_rtp_rtcp(skb, ct, ctinfo, data, dataoff, + ret = expect_rtp_rtcp(skb, ct, ctinfo, protoff, data, dataoff, &channel->mediaControlChannel); if (ret < 0) return -1; @@ -404,6 +412,7 @@ static int process_h245_channel(struct sk_buff *skb, /****************************************************************************/ static int process_olc(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, int dataoff, OpenLogicalChannel *olc) { @@ -414,7 +423,8 @@ static int process_olc(struct sk_buff *skb, struct nf_conn *ct, if (olc->forwardLogicalChannelParameters.multiplexParameters.choice == eOpenLogicalChannel_forwardLogicalChannelParameters_multiplexParameters_h2250LogicalChannelParameters) { - ret = process_h245_channel(skb, ct, ctinfo, data, dataoff, + ret = process_h245_channel(skb, ct, ctinfo, + protoff, data, dataoff, &olc-> forwardLogicalChannelParameters. multiplexParameters. @@ -432,7 +442,8 @@ static int process_olc(struct sk_buff *skb, struct nf_conn *ct, eOpenLogicalChannel_reverseLogicalChannelParameters_multiplexParameters_h2250LogicalChannelParameters)) { ret = - process_h245_channel(skb, ct, ctinfo, data, dataoff, + process_h245_channel(skb, ct, ctinfo, + protoff, data, dataoff, &olc-> reverseLogicalChannelParameters. multiplexParameters. @@ -450,7 +461,7 @@ static int process_olc(struct sk_buff *skb, struct nf_conn *ct, t120.choice == eDataProtocolCapability_separateLANStack && olc->separateStack.networkAddress.choice == eNetworkAccessParameters_networkAddress_localAreaAddress) { - ret = expect_t120(skb, ct, ctinfo, data, dataoff, + ret = expect_t120(skb, ct, ctinfo, protoff, data, dataoff, &olc->separateStack.networkAddress. localAreaAddress); if (ret < 0) @@ -463,7 +474,7 @@ static int process_olc(struct sk_buff *skb, struct nf_conn *ct, /****************************************************************************/ static int process_olca(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, + unsigned int protoff, unsigned char **data, int dataoff, OpenLogicalChannelAck *olca) { H2250LogicalChannelAckParameters *ack; @@ -479,7 +490,8 @@ static int process_olca(struct sk_buff *skb, struct nf_conn *ct, choice == eOpenLogicalChannelAck_reverseLogicalChannelParameters_multiplexParameters_h2250LogicalChannelParameters)) { - ret = process_h245_channel(skb, ct, ctinfo, data, dataoff, + ret = process_h245_channel(skb, ct, ctinfo, + protoff, data, dataoff, &olca-> reverseLogicalChannelParameters. multiplexParameters. @@ -498,7 +510,8 @@ static int process_olca(struct sk_buff *skb, struct nf_conn *ct, if (ack->options & eH2250LogicalChannelAckParameters_mediaChannel) { /* RTP */ - ret = expect_rtp_rtcp(skb, ct, ctinfo, data, dataoff, + ret = expect_rtp_rtcp(skb, ct, ctinfo, + protoff, data, dataoff, &ack->mediaChannel); if (ret < 0) return -1; @@ -507,7 +520,8 @@ static int process_olca(struct sk_buff *skb, struct nf_conn *ct, if (ack->options & eH2250LogicalChannelAckParameters_mediaControlChannel) { /* RTCP */ - ret = expect_rtp_rtcp(skb, ct, ctinfo, data, dataoff, + ret = expect_rtp_rtcp(skb, ct, ctinfo, + protoff, data, dataoff, &ack->mediaControlChannel); if (ret < 0) return -1; @@ -517,7 +531,7 @@ static int process_olca(struct sk_buff *skb, struct nf_conn *ct, if ((olca->options & eOpenLogicalChannelAck_separateStack) && olca->separateStack.networkAddress.choice == eNetworkAccessParameters_networkAddress_localAreaAddress) { - ret = expect_t120(skb, ct, ctinfo, data, dataoff, + ret = expect_t120(skb, ct, ctinfo, protoff, data, dataoff, &olca->separateStack.networkAddress. localAreaAddress); if (ret < 0) @@ -530,14 +544,15 @@ static int process_olca(struct sk_buff *skb, struct nf_conn *ct, /****************************************************************************/ static int process_h245(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, + unsigned int protoff, unsigned char **data, int dataoff, MultimediaSystemControlMessage *mscm) { switch (mscm->choice) { case eMultimediaSystemControlMessage_request: if (mscm->request.choice == eRequestMessage_openLogicalChannel) { - return process_olc(skb, ct, ctinfo, data, dataoff, + return process_olc(skb, ct, ctinfo, + protoff, data, dataoff, &mscm->request.openLogicalChannel); } pr_debug("nf_ct_h323: H.245 Request %d\n", @@ -546,7 +561,8 @@ static int process_h245(struct sk_buff *skb, struct nf_conn *ct, case eMultimediaSystemControlMessage_response: if (mscm->response.choice == eResponseMessage_openLogicalChannelAck) { - return process_olca(skb, ct, ctinfo, data, dataoff, + return process_olca(skb, ct, ctinfo, + protoff, data, dataoff, &mscm->response. openLogicalChannelAck); } @@ -597,7 +613,8 @@ static int h245_help(struct sk_buff *skb, unsigned int protoff, } /* Process H.245 signal */ - if (process_h245(skb, ct, ctinfo, &data, dataoff, &mscm) < 0) + if (process_h245(skb, ct, ctinfo, protoff, + &data, dataoff, &mscm) < 0) goto drop; } @@ -661,7 +678,7 @@ int get_h225_addr(struct nf_conn *ct, unsigned char *data, /****************************************************************************/ static int expect_h245(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, + unsigned int protoff, unsigned char **data, int dataoff, TransportAddress *taddr) { int dir = CTINFO2DIR(ctinfo); @@ -693,7 +710,7 @@ static int expect_h245(struct sk_buff *skb, struct nf_conn *ct, nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK) { /* NAT needed */ - ret = nat_h245(skb, ct, ctinfo, data, dataoff, taddr, + ret = nat_h245(skb, ct, ctinfo, protoff, data, dataoff, taddr, port, exp); } else { /* Conntrack only */ if (nf_ct_expect_related(exp) == 0) { @@ -779,6 +796,7 @@ static int callforward_do_filter(const union nf_inet_addr *src, static int expect_callforwarding(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, int dataoff, TransportAddress *taddr) { @@ -817,7 +835,8 @@ static int expect_callforwarding(struct sk_buff *skb, nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK) { /* Need NAT */ - ret = nat_callforwarding(skb, ct, ctinfo, data, dataoff, + ret = nat_callforwarding(skb, ct, ctinfo, + protoff, data, dataoff, taddr, port, exp); } else { /* Conntrack only */ if (nf_ct_expect_related(exp) == 0) { @@ -835,6 +854,7 @@ static int expect_callforwarding(struct sk_buff *skb, /****************************************************************************/ static int process_setup(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, int dataoff, Setup_UUIE *setup) { @@ -848,7 +868,7 @@ static int process_setup(struct sk_buff *skb, struct nf_conn *ct, pr_debug("nf_ct_q931: Setup\n"); if (setup->options & eSetup_UUIE_h245Address) { - ret = expect_h245(skb, ct, ctinfo, data, dataoff, + ret = expect_h245(skb, ct, ctinfo, protoff, data, dataoff, &setup->h245Address); if (ret < 0) return -1; @@ -864,7 +884,7 @@ static int process_setup(struct sk_buff *skb, struct nf_conn *ct, pr_debug("nf_ct_q931: set destCallSignalAddress %pI6:%hu->%pI6:%hu\n", &addr, ntohs(port), &ct->tuplehash[!dir].tuple.src.u3, ntohs(ct->tuplehash[!dir].tuple.src.u.tcp.port)); - ret = set_h225_addr(skb, data, dataoff, + ret = set_h225_addr(skb, protoff, data, dataoff, &setup->destCallSignalAddress, &ct->tuplehash[!dir].tuple.src.u3, ct->tuplehash[!dir].tuple.src.u.tcp.port); @@ -881,7 +901,7 @@ static int process_setup(struct sk_buff *skb, struct nf_conn *ct, pr_debug("nf_ct_q931: set sourceCallSignalAddress %pI6:%hu->%pI6:%hu\n", &addr, ntohs(port), &ct->tuplehash[!dir].tuple.dst.u3, ntohs(ct->tuplehash[!dir].tuple.dst.u.tcp.port)); - ret = set_h225_addr(skb, data, dataoff, + ret = set_h225_addr(skb, protoff, data, dataoff, &setup->sourceCallSignalAddress, &ct->tuplehash[!dir].tuple.dst.u3, ct->tuplehash[!dir].tuple.dst.u.tcp.port); @@ -891,7 +911,8 @@ static int process_setup(struct sk_buff *skb, struct nf_conn *ct, if (setup->options & eSetup_UUIE_fastStart) { for (i = 0; i < setup->fastStart.count; i++) { - ret = process_olc(skb, ct, ctinfo, data, dataoff, + ret = process_olc(skb, ct, ctinfo, + protoff, data, dataoff, &setup->fastStart.item[i]); if (ret < 0) return -1; @@ -905,6 +926,7 @@ static int process_setup(struct sk_buff *skb, struct nf_conn *ct, static int process_callproceeding(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, int dataoff, CallProceeding_UUIE *callproc) { @@ -914,7 +936,7 @@ static int process_callproceeding(struct sk_buff *skb, pr_debug("nf_ct_q931: CallProceeding\n"); if (callproc->options & eCallProceeding_UUIE_h245Address) { - ret = expect_h245(skb, ct, ctinfo, data, dataoff, + ret = expect_h245(skb, ct, ctinfo, protoff, data, dataoff, &callproc->h245Address); if (ret < 0) return -1; @@ -922,7 +944,8 @@ static int process_callproceeding(struct sk_buff *skb, if (callproc->options & eCallProceeding_UUIE_fastStart) { for (i = 0; i < callproc->fastStart.count; i++) { - ret = process_olc(skb, ct, ctinfo, data, dataoff, + ret = process_olc(skb, ct, ctinfo, + protoff, data, dataoff, &callproc->fastStart.item[i]); if (ret < 0) return -1; @@ -935,6 +958,7 @@ static int process_callproceeding(struct sk_buff *skb, /****************************************************************************/ static int process_connect(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, int dataoff, Connect_UUIE *connect) { @@ -944,7 +968,7 @@ static int process_connect(struct sk_buff *skb, struct nf_conn *ct, pr_debug("nf_ct_q931: Connect\n"); if (connect->options & eConnect_UUIE_h245Address) { - ret = expect_h245(skb, ct, ctinfo, data, dataoff, + ret = expect_h245(skb, ct, ctinfo, protoff, data, dataoff, &connect->h245Address); if (ret < 0) return -1; @@ -952,7 +976,8 @@ static int process_connect(struct sk_buff *skb, struct nf_conn *ct, if (connect->options & eConnect_UUIE_fastStart) { for (i = 0; i < connect->fastStart.count; i++) { - ret = process_olc(skb, ct, ctinfo, data, dataoff, + ret = process_olc(skb, ct, ctinfo, + protoff, data, dataoff, &connect->fastStart.item[i]); if (ret < 0) return -1; @@ -965,6 +990,7 @@ static int process_connect(struct sk_buff *skb, struct nf_conn *ct, /****************************************************************************/ static int process_alerting(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, int dataoff, Alerting_UUIE *alert) { @@ -974,7 +1000,7 @@ static int process_alerting(struct sk_buff *skb, struct nf_conn *ct, pr_debug("nf_ct_q931: Alerting\n"); if (alert->options & eAlerting_UUIE_h245Address) { - ret = expect_h245(skb, ct, ctinfo, data, dataoff, + ret = expect_h245(skb, ct, ctinfo, protoff, data, dataoff, &alert->h245Address); if (ret < 0) return -1; @@ -982,7 +1008,8 @@ static int process_alerting(struct sk_buff *skb, struct nf_conn *ct, if (alert->options & eAlerting_UUIE_fastStart) { for (i = 0; i < alert->fastStart.count; i++) { - ret = process_olc(skb, ct, ctinfo, data, dataoff, + ret = process_olc(skb, ct, ctinfo, + protoff, data, dataoff, &alert->fastStart.item[i]); if (ret < 0) return -1; @@ -995,6 +1022,7 @@ static int process_alerting(struct sk_buff *skb, struct nf_conn *ct, /****************************************************************************/ static int process_facility(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, int dataoff, Facility_UUIE *facility) { @@ -1005,15 +1033,15 @@ static int process_facility(struct sk_buff *skb, struct nf_conn *ct, if (facility->reason.choice == eFacilityReason_callForwarded) { if (facility->options & eFacility_UUIE_alternativeAddress) - return expect_callforwarding(skb, ct, ctinfo, data, - dataoff, + return expect_callforwarding(skb, ct, ctinfo, + protoff, data, dataoff, &facility-> alternativeAddress); return 0; } if (facility->options & eFacility_UUIE_h245Address) { - ret = expect_h245(skb, ct, ctinfo, data, dataoff, + ret = expect_h245(skb, ct, ctinfo, protoff, data, dataoff, &facility->h245Address); if (ret < 0) return -1; @@ -1021,7 +1049,8 @@ static int process_facility(struct sk_buff *skb, struct nf_conn *ct, if (facility->options & eFacility_UUIE_fastStart) { for (i = 0; i < facility->fastStart.count; i++) { - ret = process_olc(skb, ct, ctinfo, data, dataoff, + ret = process_olc(skb, ct, ctinfo, + protoff, data, dataoff, &facility->fastStart.item[i]); if (ret < 0) return -1; @@ -1034,6 +1063,7 @@ static int process_facility(struct sk_buff *skb, struct nf_conn *ct, /****************************************************************************/ static int process_progress(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, int dataoff, Progress_UUIE *progress) { @@ -1043,7 +1073,7 @@ static int process_progress(struct sk_buff *skb, struct nf_conn *ct, pr_debug("nf_ct_q931: Progress\n"); if (progress->options & eProgress_UUIE_h245Address) { - ret = expect_h245(skb, ct, ctinfo, data, dataoff, + ret = expect_h245(skb, ct, ctinfo, protoff, data, dataoff, &progress->h245Address); if (ret < 0) return -1; @@ -1051,7 +1081,8 @@ static int process_progress(struct sk_buff *skb, struct nf_conn *ct, if (progress->options & eProgress_UUIE_fastStart) { for (i = 0; i < progress->fastStart.count; i++) { - ret = process_olc(skb, ct, ctinfo, data, dataoff, + ret = process_olc(skb, ct, ctinfo, + protoff, data, dataoff, &progress->fastStart.item[i]); if (ret < 0) return -1; @@ -1064,7 +1095,8 @@ static int process_progress(struct sk_buff *skb, struct nf_conn *ct, /****************************************************************************/ static int process_q931(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, Q931 *q931) + unsigned int protoff, unsigned char **data, int dataoff, + Q931 *q931) { H323_UU_PDU *pdu = &q931->UUIE.h323_uu_pdu; int i; @@ -1072,28 +1104,29 @@ static int process_q931(struct sk_buff *skb, struct nf_conn *ct, switch (pdu->h323_message_body.choice) { case eH323_UU_PDU_h323_message_body_setup: - ret = process_setup(skb, ct, ctinfo, data, dataoff, + ret = process_setup(skb, ct, ctinfo, protoff, data, dataoff, &pdu->h323_message_body.setup); break; case eH323_UU_PDU_h323_message_body_callProceeding: - ret = process_callproceeding(skb, ct, ctinfo, data, dataoff, + ret = process_callproceeding(skb, ct, ctinfo, + protoff, data, dataoff, &pdu->h323_message_body. callProceeding); break; case eH323_UU_PDU_h323_message_body_connect: - ret = process_connect(skb, ct, ctinfo, data, dataoff, + ret = process_connect(skb, ct, ctinfo, protoff, data, dataoff, &pdu->h323_message_body.connect); break; case eH323_UU_PDU_h323_message_body_alerting: - ret = process_alerting(skb, ct, ctinfo, data, dataoff, + ret = process_alerting(skb, ct, ctinfo, protoff, data, dataoff, &pdu->h323_message_body.alerting); break; case eH323_UU_PDU_h323_message_body_facility: - ret = process_facility(skb, ct, ctinfo, data, dataoff, + ret = process_facility(skb, ct, ctinfo, protoff, data, dataoff, &pdu->h323_message_body.facility); break; case eH323_UU_PDU_h323_message_body_progress: - ret = process_progress(skb, ct, ctinfo, data, dataoff, + ret = process_progress(skb, ct, ctinfo, protoff, data, dataoff, &pdu->h323_message_body.progress); break; default: @@ -1107,7 +1140,8 @@ static int process_q931(struct sk_buff *skb, struct nf_conn *ct, if (pdu->options & eH323_UU_PDU_h245Control) { for (i = 0; i < pdu->h245Control.count; i++) { - ret = process_h245(skb, ct, ctinfo, data, dataoff, + ret = process_h245(skb, ct, ctinfo, + protoff, data, dataoff, &pdu->h245Control.item[i]); if (ret < 0) return -1; @@ -1152,7 +1186,8 @@ static int q931_help(struct sk_buff *skb, unsigned int protoff, } /* Process Q.931 signal */ - if (process_q931(skb, ct, ctinfo, &data, dataoff, &q931) < 0) + if (process_q931(skb, ct, ctinfo, protoff, + &data, dataoff, &q931) < 0) goto drop; } @@ -1249,7 +1284,7 @@ static int set_expect_timeout(struct nf_conntrack_expect *exp, /****************************************************************************/ static int expect_q931(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, - unsigned char **data, + unsigned int protoff, unsigned char **data, TransportAddress *taddr, int count) { struct nf_ct_h323_master *info = nfct_help_data(ct); @@ -1286,7 +1321,8 @@ static int expect_q931(struct sk_buff *skb, struct nf_conn *ct, nat_q931 = rcu_dereference(nat_q931_hook); if (nat_q931 && nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK) { /* Need NAT */ - ret = nat_q931(skb, ct, ctinfo, data, taddr, i, port, exp); + ret = nat_q931(skb, ct, ctinfo, protoff, data, + taddr, i, port, exp); } else { /* Conntrack only */ if (nf_ct_expect_related(exp) == 0) { pr_debug("nf_ct_ras: expect Q.931 "); @@ -1306,6 +1342,7 @@ static int expect_q931(struct sk_buff *skb, struct nf_conn *ct, /****************************************************************************/ static int process_grq(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, GatekeeperRequest *grq) { typeof(set_ras_addr_hook) set_ras_addr; @@ -1315,7 +1352,7 @@ static int process_grq(struct sk_buff *skb, struct nf_conn *ct, set_ras_addr = rcu_dereference(set_ras_addr_hook); if (set_ras_addr && nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK) /* NATed */ - return set_ras_addr(skb, ct, ctinfo, data, + return set_ras_addr(skb, ct, ctinfo, protoff, data, &grq->rasAddress, 1); return 0; } @@ -1323,6 +1360,7 @@ static int process_grq(struct sk_buff *skb, struct nf_conn *ct, /****************************************************************************/ static int process_gcf(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, GatekeeperConfirm *gcf) { int dir = CTINFO2DIR(ctinfo); @@ -1367,6 +1405,7 @@ static int process_gcf(struct sk_buff *skb, struct nf_conn *ct, /****************************************************************************/ static int process_rrq(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, RegistrationRequest *rrq) { struct nf_ct_h323_master *info = nfct_help_data(ct); @@ -1375,7 +1414,7 @@ static int process_rrq(struct sk_buff *skb, struct nf_conn *ct, pr_debug("nf_ct_ras: RRQ\n"); - ret = expect_q931(skb, ct, ctinfo, data, + ret = expect_q931(skb, ct, ctinfo, protoff, data, rrq->callSignalAddress.item, rrq->callSignalAddress.count); if (ret < 0) @@ -1384,7 +1423,7 @@ static int process_rrq(struct sk_buff *skb, struct nf_conn *ct, set_ras_addr = rcu_dereference(set_ras_addr_hook); if (set_ras_addr && nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK) { - ret = set_ras_addr(skb, ct, ctinfo, data, + ret = set_ras_addr(skb, ct, ctinfo, protoff, data, rrq->rasAddress.item, rrq->rasAddress.count); if (ret < 0) @@ -1403,6 +1442,7 @@ static int process_rrq(struct sk_buff *skb, struct nf_conn *ct, /****************************************************************************/ static int process_rcf(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, RegistrationConfirm *rcf) { struct nf_ct_h323_master *info = nfct_help_data(ct); @@ -1416,7 +1456,7 @@ static int process_rcf(struct sk_buff *skb, struct nf_conn *ct, set_sig_addr = rcu_dereference(set_sig_addr_hook); if (set_sig_addr && nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK) { - ret = set_sig_addr(skb, ct, ctinfo, data, + ret = set_sig_addr(skb, ct, ctinfo, protoff, data, rcf->callSignalAddress.item, rcf->callSignalAddress.count); if (ret < 0) @@ -1453,6 +1493,7 @@ static int process_rcf(struct sk_buff *skb, struct nf_conn *ct, /****************************************************************************/ static int process_urq(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, UnregistrationRequest *urq) { struct nf_ct_h323_master *info = nfct_help_data(ct); @@ -1465,7 +1506,7 @@ static int process_urq(struct sk_buff *skb, struct nf_conn *ct, set_sig_addr = rcu_dereference(set_sig_addr_hook); if (set_sig_addr && nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK) { - ret = set_sig_addr(skb, ct, ctinfo, data, + ret = set_sig_addr(skb, ct, ctinfo, protoff, data, urq->callSignalAddress.item, urq->callSignalAddress.count); if (ret < 0) @@ -1486,6 +1527,7 @@ static int process_urq(struct sk_buff *skb, struct nf_conn *ct, /****************************************************************************/ static int process_arq(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, AdmissionRequest *arq) { const struct nf_ct_h323_master *info = nfct_help_data(ct); @@ -1505,7 +1547,7 @@ static int process_arq(struct sk_buff *skb, struct nf_conn *ct, nf_ct_l3num(ct) == NFPROTO_IPV4 && set_h225_addr && ct->status & IPS_NAT_MASK) { /* Answering ARQ */ - return set_h225_addr(skb, data, 0, + return set_h225_addr(skb, protoff, data, 0, &arq->destCallSignalAddress, &ct->tuplehash[!dir].tuple.dst.u3, info->sig_port[!dir]); @@ -1518,7 +1560,7 @@ static int process_arq(struct sk_buff *skb, struct nf_conn *ct, set_h225_addr && nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK) { /* Calling ARQ */ - return set_h225_addr(skb, data, 0, + return set_h225_addr(skb, protoff, data, 0, &arq->srcCallSignalAddress, &ct->tuplehash[!dir].tuple.dst.u3, port); @@ -1530,6 +1572,7 @@ static int process_arq(struct sk_buff *skb, struct nf_conn *ct, /****************************************************************************/ static int process_acf(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, AdmissionConfirm *acf) { int dir = CTINFO2DIR(ctinfo); @@ -1550,7 +1593,7 @@ static int process_acf(struct sk_buff *skb, struct nf_conn *ct, set_sig_addr = rcu_dereference(set_sig_addr_hook); if (set_sig_addr && nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK) - return set_sig_addr(skb, ct, ctinfo, data, + return set_sig_addr(skb, ct, ctinfo, protoff, data, &acf->destCallSignalAddress, 1); return 0; } @@ -1578,6 +1621,7 @@ static int process_acf(struct sk_buff *skb, struct nf_conn *ct, /****************************************************************************/ static int process_lrq(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, LocationRequest *lrq) { typeof(set_ras_addr_hook) set_ras_addr; @@ -1587,7 +1631,7 @@ static int process_lrq(struct sk_buff *skb, struct nf_conn *ct, set_ras_addr = rcu_dereference(set_ras_addr_hook); if (set_ras_addr && nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK) - return set_ras_addr(skb, ct, ctinfo, data, + return set_ras_addr(skb, ct, ctinfo, protoff, data, &lrq->replyAddress, 1); return 0; } @@ -1595,6 +1639,7 @@ static int process_lrq(struct sk_buff *skb, struct nf_conn *ct, /****************************************************************************/ static int process_lcf(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, LocationConfirm *lcf) { int dir = CTINFO2DIR(ctinfo); @@ -1634,6 +1679,7 @@ static int process_lcf(struct sk_buff *skb, struct nf_conn *ct, /****************************************************************************/ static int process_irr(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, InfoRequestResponse *irr) { int ret; @@ -1645,7 +1691,7 @@ static int process_irr(struct sk_buff *skb, struct nf_conn *ct, set_ras_addr = rcu_dereference(set_ras_addr_hook); if (set_ras_addr && nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK) { - ret = set_ras_addr(skb, ct, ctinfo, data, + ret = set_ras_addr(skb, ct, ctinfo, protoff, data, &irr->rasAddress, 1); if (ret < 0) return -1; @@ -1654,7 +1700,7 @@ static int process_irr(struct sk_buff *skb, struct nf_conn *ct, set_sig_addr = rcu_dereference(set_sig_addr_hook); if (set_sig_addr && nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK) { - ret = set_sig_addr(skb, ct, ctinfo, data, + ret = set_sig_addr(skb, ct, ctinfo, protoff, data, irr->callSignalAddress.item, irr->callSignalAddress.count); if (ret < 0) @@ -1667,38 +1713,39 @@ static int process_irr(struct sk_buff *skb, struct nf_conn *ct, /****************************************************************************/ static int process_ras(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned char **data, RasMessage *ras) { switch (ras->choice) { case eRasMessage_gatekeeperRequest: - return process_grq(skb, ct, ctinfo, data, + return process_grq(skb, ct, ctinfo, protoff, data, &ras->gatekeeperRequest); case eRasMessage_gatekeeperConfirm: - return process_gcf(skb, ct, ctinfo, data, + return process_gcf(skb, ct, ctinfo, protoff, data, &ras->gatekeeperConfirm); case eRasMessage_registrationRequest: - return process_rrq(skb, ct, ctinfo, data, + return process_rrq(skb, ct, ctinfo, protoff, data, &ras->registrationRequest); case eRasMessage_registrationConfirm: - return process_rcf(skb, ct, ctinfo, data, + return process_rcf(skb, ct, ctinfo, protoff, data, &ras->registrationConfirm); case eRasMessage_unregistrationRequest: - return process_urq(skb, ct, ctinfo, data, + return process_urq(skb, ct, ctinfo, protoff, data, &ras->unregistrationRequest); case eRasMessage_admissionRequest: - return process_arq(skb, ct, ctinfo, data, + return process_arq(skb, ct, ctinfo, protoff, data, &ras->admissionRequest); case eRasMessage_admissionConfirm: - return process_acf(skb, ct, ctinfo, data, + return process_acf(skb, ct, ctinfo, protoff, data, &ras->admissionConfirm); case eRasMessage_locationRequest: - return process_lrq(skb, ct, ctinfo, data, + return process_lrq(skb, ct, ctinfo, protoff, data, &ras->locationRequest); case eRasMessage_locationConfirm: - return process_lcf(skb, ct, ctinfo, data, + return process_lcf(skb, ct, ctinfo, protoff, data, &ras->locationConfirm); case eRasMessage_infoRequestResponse: - return process_irr(skb, ct, ctinfo, data, + return process_irr(skb, ct, ctinfo, protoff, data, &ras->infoRequestResponse); default: pr_debug("nf_ct_ras: RAS message %d\n", ras->choice); @@ -1738,7 +1785,7 @@ static int ras_help(struct sk_buff *skb, unsigned int protoff, } /* Process RAS message */ - if (process_ras(skb, ct, ctinfo, &data, &ras) < 0) + if (process_ras(skb, ct, ctinfo, protoff, &data, &ras) < 0) goto drop; accept: diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c index e06dc2fab19f..95d097cdb202 100644 --- a/net/netfilter/nf_conntrack_irc.c +++ b/net/netfilter/nf_conntrack_irc.c @@ -33,6 +33,7 @@ static DEFINE_SPINLOCK(irc_buffer_lock); unsigned int (*nf_nat_irc_hook)(struct sk_buff *skb, enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned int matchoff, unsigned int matchlen, struct nf_conntrack_expect *exp) __read_mostly; @@ -206,7 +207,7 @@ static int help(struct sk_buff *skb, unsigned int protoff, nf_nat_irc = rcu_dereference(nf_nat_irc_hook); if (nf_nat_irc && nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK) - ret = nf_nat_irc(skb, ctinfo, + ret = nf_nat_irc(skb, ctinfo, protoff, addr_beg_p - ib_ptr, addr_end_p - addr_beg_p, exp); diff --git a/net/netfilter/nf_conntrack_pptp.c b/net/netfilter/nf_conntrack_pptp.c index 6fed9ec35248..cc7669ef0b95 100644 --- a/net/netfilter/nf_conntrack_pptp.c +++ b/net/netfilter/nf_conntrack_pptp.c @@ -45,14 +45,14 @@ static DEFINE_SPINLOCK(nf_pptp_lock); int (*nf_nat_pptp_hook_outbound)(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, - struct PptpControlHeader *ctlh, + unsigned int protoff, struct PptpControlHeader *ctlh, union pptp_ctrl_union *pptpReq) __read_mostly; EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_outbound); int (*nf_nat_pptp_hook_inbound)(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, - struct PptpControlHeader *ctlh, + unsigned int protoff, struct PptpControlHeader *ctlh, union pptp_ctrl_union *pptpReq) __read_mostly; EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_inbound); @@ -262,7 +262,7 @@ out_unexpect_orig: } static inline int -pptp_inbound_pkt(struct sk_buff *skb, +pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff, struct PptpControlHeader *ctlh, union pptp_ctrl_union *pptpReq, unsigned int reqlen, @@ -376,7 +376,8 @@ pptp_inbound_pkt(struct sk_buff *skb, nf_nat_pptp_inbound = rcu_dereference(nf_nat_pptp_hook_inbound); if (nf_nat_pptp_inbound && ct->status & IPS_NAT_MASK) - return nf_nat_pptp_inbound(skb, ct, ctinfo, ctlh, pptpReq); + return nf_nat_pptp_inbound(skb, ct, ctinfo, + protoff, ctlh, pptpReq); return NF_ACCEPT; invalid: @@ -389,7 +390,7 @@ invalid: } static inline int -pptp_outbound_pkt(struct sk_buff *skb, +pptp_outbound_pkt(struct sk_buff *skb, unsigned int protoff, struct PptpControlHeader *ctlh, union pptp_ctrl_union *pptpReq, unsigned int reqlen, @@ -471,7 +472,8 @@ pptp_outbound_pkt(struct sk_buff *skb, nf_nat_pptp_outbound = rcu_dereference(nf_nat_pptp_hook_outbound); if (nf_nat_pptp_outbound && ct->status & IPS_NAT_MASK) - return nf_nat_pptp_outbound(skb, ct, ctinfo, ctlh, pptpReq); + return nf_nat_pptp_outbound(skb, ct, ctinfo, + protoff, ctlh, pptpReq); return NF_ACCEPT; invalid: @@ -570,11 +572,11 @@ conntrack_pptp_help(struct sk_buff *skb, unsigned int protoff, * established from PNS->PAC. However, RFC makes no guarantee */ if (dir == IP_CT_DIR_ORIGINAL) /* client -> server (PNS -> PAC) */ - ret = pptp_outbound_pkt(skb, ctlh, pptpReq, reqlen, ct, + ret = pptp_outbound_pkt(skb, protoff, ctlh, pptpReq, reqlen, ct, ctinfo); else /* server -> client (PAC -> PNS) */ - ret = pptp_inbound_pkt(skb, ctlh, pptpReq, reqlen, ct, + ret = pptp_inbound_pkt(skb, protoff, ctlh, pptpReq, reqlen, ct, ctinfo); pr_debug("sstate: %d->%d, cstate: %d->%d\n", oldsstate, info->sstate, oldcstate, info->cstate); diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index d08e0baf4640..590f0abaab8c 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -52,8 +52,8 @@ module_param(sip_direct_media, int, 0600); MODULE_PARM_DESC(sip_direct_media, "Expect Media streams between signalling " "endpoints only (default 1)"); -unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb, unsigned int dataoff, - const char **dptr, +unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen) __read_mostly; EXPORT_SYMBOL_GPL(nf_nat_sip_hook); @@ -61,6 +61,7 @@ void (*nf_nat_sip_seq_adjust_hook)(struct sk_buff *skb, s16 off) __read_mostly; EXPORT_SYMBOL_GPL(nf_nat_sip_seq_adjust_hook); unsigned int (*nf_nat_sip_expect_hook)(struct sk_buff *skb, + unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen, @@ -69,7 +70,8 @@ unsigned int (*nf_nat_sip_expect_hook)(struct sk_buff *skb, unsigned int matchlen) __read_mostly; EXPORT_SYMBOL_GPL(nf_nat_sip_expect_hook); -unsigned int (*nf_nat_sdp_addr_hook)(struct sk_buff *skb, unsigned int dataoff, +unsigned int (*nf_nat_sdp_addr_hook)(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int sdpoff, @@ -79,7 +81,8 @@ unsigned int (*nf_nat_sdp_addr_hook)(struct sk_buff *skb, unsigned int dataoff, __read_mostly; EXPORT_SYMBOL_GPL(nf_nat_sdp_addr_hook); -unsigned int (*nf_nat_sdp_port_hook)(struct sk_buff *skb, unsigned int dataoff, +unsigned int (*nf_nat_sdp_port_hook)(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int matchoff, @@ -88,6 +91,7 @@ unsigned int (*nf_nat_sdp_port_hook)(struct sk_buff *skb, unsigned int dataoff, EXPORT_SYMBOL_GPL(nf_nat_sdp_port_hook); unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb, + unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen, @@ -96,7 +100,8 @@ unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb, __read_mostly; EXPORT_SYMBOL_GPL(nf_nat_sdp_session_hook); -unsigned int (*nf_nat_sdp_media_hook)(struct sk_buff *skb, unsigned int dataoff, +unsigned int (*nf_nat_sdp_media_hook)(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen, struct nf_conntrack_expect *rtp_exp, @@ -883,7 +888,8 @@ static void flush_expectations(struct nf_conn *ct, bool media) spin_unlock_bh(&nf_conntrack_lock); } -static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int dataoff, +static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen, union nf_inet_addr *daddr, __be16 port, enum sip_expectation_classes class, @@ -960,7 +966,7 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int dataoff, if (direct_rtp) { nf_nat_sdp_port = rcu_dereference(nf_nat_sdp_port_hook); if (nf_nat_sdp_port && - !nf_nat_sdp_port(skb, dataoff, dptr, datalen, + !nf_nat_sdp_port(skb, protoff, dataoff, dptr, datalen, mediaoff, medialen, ntohs(rtp_port))) goto err1; } @@ -983,7 +989,7 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int dataoff, nf_nat_sdp_media = rcu_dereference(nf_nat_sdp_media_hook); if (nf_nat_sdp_media && nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK && !direct_rtp) - ret = nf_nat_sdp_media(skb, dataoff, dptr, datalen, + ret = nf_nat_sdp_media(skb, protoff, dataoff, dptr, datalen, rtp_exp, rtcp_exp, mediaoff, medialen, daddr); else { @@ -1024,7 +1030,8 @@ static const struct sdp_media_type *sdp_media_type(const char *dptr, return NULL; } -static int process_sdp(struct sk_buff *skb, unsigned int dataoff, +static int process_sdp(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int cseq) { @@ -1098,7 +1105,8 @@ static int process_sdp(struct sk_buff *skb, unsigned int dataoff, else return NF_DROP; - ret = set_expected_rtp_rtcp(skb, dataoff, dptr, datalen, + ret = set_expected_rtp_rtcp(skb, protoff, dataoff, + dptr, datalen, &rtp_addr, htons(port), t->class, mediaoff, medialen); if (ret != NF_ACCEPT) @@ -1107,7 +1115,8 @@ static int process_sdp(struct sk_buff *skb, unsigned int dataoff, /* Update media connection address if present */ if (maddr_len && nf_nat_sdp_addr && nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK) { - ret = nf_nat_sdp_addr(skb, dataoff, dptr, datalen, + ret = nf_nat_sdp_addr(skb, protoff, dataoff, + dptr, datalen, mediaoff, c_hdr, SDP_HDR_MEDIA, &rtp_addr); if (ret != NF_ACCEPT) @@ -1120,12 +1129,13 @@ static int process_sdp(struct sk_buff *skb, unsigned int dataoff, nf_nat_sdp_session = rcu_dereference(nf_nat_sdp_session_hook); if (nf_nat_sdp_session && nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK) - ret = nf_nat_sdp_session(skb, dataoff, dptr, datalen, sdpoff, - &rtp_addr); + ret = nf_nat_sdp_session(skb, protoff, dataoff, + dptr, datalen, sdpoff, &rtp_addr); return ret; } -static int process_invite_response(struct sk_buff *skb, unsigned int dataoff, +static int process_invite_response(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int cseq, unsigned int code) { @@ -1135,13 +1145,14 @@ static int process_invite_response(struct sk_buff *skb, unsigned int dataoff, if ((code >= 100 && code <= 199) || (code >= 200 && code <= 299)) - return process_sdp(skb, dataoff, dptr, datalen, cseq); + return process_sdp(skb, protoff, dataoff, dptr, datalen, cseq); else if (ct_sip_info->invite_cseq == cseq) flush_expectations(ct, true); return NF_ACCEPT; } -static int process_update_response(struct sk_buff *skb, unsigned int dataoff, +static int process_update_response(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int cseq, unsigned int code) { @@ -1151,13 +1162,14 @@ static int process_update_response(struct sk_buff *skb, unsigned int dataoff, if ((code >= 100 && code <= 199) || (code >= 200 && code <= 299)) - return process_sdp(skb, dataoff, dptr, datalen, cseq); + return process_sdp(skb, protoff, dataoff, dptr, datalen, cseq); else if (ct_sip_info->invite_cseq == cseq) flush_expectations(ct, true); return NF_ACCEPT; } -static int process_prack_response(struct sk_buff *skb, unsigned int dataoff, +static int process_prack_response(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int cseq, unsigned int code) { @@ -1167,13 +1179,14 @@ static int process_prack_response(struct sk_buff *skb, unsigned int dataoff, if ((code >= 100 && code <= 199) || (code >= 200 && code <= 299)) - return process_sdp(skb, dataoff, dptr, datalen, cseq); + return process_sdp(skb, protoff, dataoff, dptr, datalen, cseq); else if (ct_sip_info->invite_cseq == cseq) flush_expectations(ct, true); return NF_ACCEPT; } -static int process_invite_request(struct sk_buff *skb, unsigned int dataoff, +static int process_invite_request(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int cseq) { @@ -1183,13 +1196,14 @@ static int process_invite_request(struct sk_buff *skb, unsigned int dataoff, unsigned int ret; flush_expectations(ct, true); - ret = process_sdp(skb, dataoff, dptr, datalen, cseq); + ret = process_sdp(skb, protoff, dataoff, dptr, datalen, cseq); if (ret == NF_ACCEPT) ct_sip_info->invite_cseq = cseq; return ret; } -static int process_bye_request(struct sk_buff *skb, unsigned int dataoff, +static int process_bye_request(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int cseq) { @@ -1204,7 +1218,8 @@ static int process_bye_request(struct sk_buff *skb, unsigned int dataoff, * signalling connections. The expectation is marked inactive and is activated * when receiving a response indicating success from the registrar. */ -static int process_register_request(struct sk_buff *skb, unsigned int dataoff, +static int process_register_request(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int cseq) { @@ -1280,8 +1295,8 @@ static int process_register_request(struct sk_buff *skb, unsigned int dataoff, nf_nat_sip_expect = rcu_dereference(nf_nat_sip_expect_hook); if (nf_nat_sip_expect && nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK) - ret = nf_nat_sip_expect(skb, dataoff, dptr, datalen, exp, - matchoff, matchlen); + ret = nf_nat_sip_expect(skb, protoff, dataoff, dptr, datalen, + exp, matchoff, matchlen); else { if (nf_ct_expect_related(exp) != 0) ret = NF_DROP; @@ -1296,7 +1311,8 @@ store_cseq: return ret; } -static int process_register_response(struct sk_buff *skb, unsigned int dataoff, +static int process_register_response(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int cseq, unsigned int code) { @@ -1378,7 +1394,8 @@ static const struct sip_handler sip_handlers[] = { SIP_HANDLER("REGISTER", process_register_request, process_register_response), }; -static int process_sip_response(struct sk_buff *skb, unsigned int dataoff, +static int process_sip_response(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen) { enum ip_conntrack_info ctinfo; @@ -1409,13 +1426,14 @@ static int process_sip_response(struct sk_buff *skb, unsigned int dataoff, if (*datalen < matchend + handler->len || strnicmp(*dptr + matchend, handler->method, handler->len)) continue; - return handler->response(skb, dataoff, dptr, datalen, + return handler->response(skb, protoff, dataoff, dptr, datalen, cseq, code); } return NF_ACCEPT; } -static int process_sip_request(struct sk_buff *skb, unsigned int dataoff, +static int process_sip_request(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, unsigned int *datalen) { enum ip_conntrack_info ctinfo; @@ -1440,27 +1458,29 @@ static int process_sip_request(struct sk_buff *skb, unsigned int dataoff, if (!cseq) return NF_DROP; - return handler->request(skb, dataoff, dptr, datalen, cseq); + return handler->request(skb, protoff, dataoff, dptr, datalen, + cseq); } return NF_ACCEPT; } static int process_sip_msg(struct sk_buff *skb, struct nf_conn *ct, - unsigned int dataoff, const char **dptr, - unsigned int *datalen) + unsigned int protoff, unsigned int dataoff, + const char **dptr, unsigned int *datalen) { typeof(nf_nat_sip_hook) nf_nat_sip; int ret; if (strnicmp(*dptr, "SIP/2.0 ", strlen("SIP/2.0 ")) != 0) - ret = process_sip_request(skb, dataoff, dptr, datalen); + ret = process_sip_request(skb, protoff, dataoff, dptr, datalen); else - ret = process_sip_response(skb, dataoff, dptr, datalen); + ret = process_sip_response(skb, protoff, dataoff, dptr, datalen); if (ret == NF_ACCEPT && nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK) { nf_nat_sip = rcu_dereference(nf_nat_sip_hook); - if (nf_nat_sip && !nf_nat_sip(skb, dataoff, dptr, datalen)) + if (nf_nat_sip && !nf_nat_sip(skb, protoff, dataoff, + dptr, datalen)) ret = NF_DROP; } @@ -1528,7 +1548,8 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, if (msglen > datalen) return NF_DROP; - ret = process_sip_msg(skb, ct, dataoff, &dptr, &msglen); + ret = process_sip_msg(skb, ct, protoff, dataoff, + &dptr, &msglen); if (ret != NF_ACCEPT) break; diff = msglen - origlen; @@ -1570,7 +1591,7 @@ static int sip_help_udp(struct sk_buff *skb, unsigned int protoff, if (datalen < strlen("SIP/2.0 200")) return NF_ACCEPT; - return process_sip_msg(skb, ct, dataoff, &dptr, &datalen); + return process_sip_msg(skb, ct, protoff, dataoff, &dptr, &datalen); } static struct nf_conntrack_helper sip[MAX_PORTS][4] __read_mostly; -- cgit v1.2.3 From c7232c9979cba684c50b64c513c4a83c9aa70563 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sun, 26 Aug 2012 19:14:06 +0200 Subject: netfilter: add protocol independent NAT core Convert the IPv4 NAT implementation to a protocol independent core and address family specific modules. Signed-off-by: Patrick McHardy --- include/linux/netfilter.h | 14 +- include/linux/netfilter/nf_nat.h | 8 + include/linux/netfilter/nfnetlink_conntrack.h | 6 +- include/linux/netfilter_ipv4.h | 1 - include/net/netfilter/nf_conntrack_expect.h | 2 +- include/net/netfilter/nf_nat.h | 2 +- include/net/netfilter/nf_nat_core.h | 5 +- include/net/netfilter/nf_nat_l3proto.h | 47 ++ include/net/netfilter/nf_nat_l4proto.h | 71 ++ include/net/netfilter/nf_nat_protocol.h | 67 -- include/net/netfilter/nf_nat_rule.h | 15 - include/net/netns/conntrack.h | 4 + include/net/netns/ipv4.h | 2 - net/ipv4/netfilter.c | 37 -- net/ipv4/netfilter/Kconfig | 64 +- net/ipv4/netfilter/Makefile | 11 +- net/ipv4/netfilter/ipt_MASQUERADE.c | 15 +- net/ipv4/netfilter/ipt_NETMAP.c | 15 +- net/ipv4/netfilter/ipt_REDIRECT.c | 15 +- net/ipv4/netfilter/iptable_nat.c | 320 +++++++++ net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 6 - net/ipv4/netfilter/nf_nat_amanda.c | 1 - net/ipv4/netfilter/nf_nat_core.c | 763 ---------------------- net/ipv4/netfilter/nf_nat_ftp.c | 1 - net/ipv4/netfilter/nf_nat_h323.c | 23 +- net/ipv4/netfilter/nf_nat_helper.c | 461 ------------- net/ipv4/netfilter/nf_nat_irc.c | 1 - net/ipv4/netfilter/nf_nat_l3proto_ipv4.c | 281 ++++++++ net/ipv4/netfilter/nf_nat_pptp.c | 15 +- net/ipv4/netfilter/nf_nat_proto_common.c | 114 ---- net/ipv4/netfilter/nf_nat_proto_dccp.c | 106 --- net/ipv4/netfilter/nf_nat_proto_gre.c | 30 +- net/ipv4/netfilter/nf_nat_proto_icmp.c | 24 +- net/ipv4/netfilter/nf_nat_proto_sctp.c | 96 --- net/ipv4/netfilter/nf_nat_proto_tcp.c | 91 --- net/ipv4/netfilter/nf_nat_proto_udp.c | 82 --- net/ipv4/netfilter/nf_nat_proto_udplite.c | 98 --- net/ipv4/netfilter/nf_nat_proto_unknown.c | 52 -- net/ipv4/netfilter/nf_nat_rule.c | 214 ------- net/ipv4/netfilter/nf_nat_sip.c | 19 +- net/ipv4/netfilter/nf_nat_standalone.c | 326 ---------- net/ipv4/netfilter/nf_nat_tftp.c | 1 - net/netfilter/Kconfig | 24 + net/netfilter/Makefile | 11 + net/netfilter/core.c | 5 + net/netfilter/nf_conntrack_core.c | 6 + net/netfilter/nf_conntrack_netlink.c | 35 +- net/netfilter/nf_conntrack_proto_tcp.c | 8 +- net/netfilter/nf_conntrack_sip.c | 6 +- net/netfilter/nf_nat_core.c | 854 +++++++++++++++++++++++++ net/netfilter/nf_nat_helper.c | 435 +++++++++++++ net/netfilter/nf_nat_proto_common.c | 112 ++++ net/netfilter/nf_nat_proto_dccp.c | 116 ++++ net/netfilter/nf_nat_proto_sctp.c | 103 +++ net/netfilter/nf_nat_proto_tcp.c | 85 +++ net/netfilter/nf_nat_proto_udp.c | 76 +++ net/netfilter/nf_nat_proto_udplite.c | 106 +++ net/netfilter/nf_nat_proto_unknown.c | 54 ++ net/netfilter/xt_nat.c | 167 +++++ 59 files changed, 3042 insertions(+), 2687 deletions(-) create mode 100644 include/net/netfilter/nf_nat_l3proto.h create mode 100644 include/net/netfilter/nf_nat_l4proto.h delete mode 100644 include/net/netfilter/nf_nat_protocol.h delete mode 100644 include/net/netfilter/nf_nat_rule.h create mode 100644 net/ipv4/netfilter/iptable_nat.c delete mode 100644 net/ipv4/netfilter/nf_nat_core.c delete mode 100644 net/ipv4/netfilter/nf_nat_helper.c create mode 100644 net/ipv4/netfilter/nf_nat_l3proto_ipv4.c delete mode 100644 net/ipv4/netfilter/nf_nat_proto_common.c delete mode 100644 net/ipv4/netfilter/nf_nat_proto_dccp.c delete mode 100644 net/ipv4/netfilter/nf_nat_proto_sctp.c delete mode 100644 net/ipv4/netfilter/nf_nat_proto_tcp.c delete mode 100644 net/ipv4/netfilter/nf_nat_proto_udp.c delete mode 100644 net/ipv4/netfilter/nf_nat_proto_udplite.c delete mode 100644 net/ipv4/netfilter/nf_nat_proto_unknown.c delete mode 100644 net/ipv4/netfilter/nf_nat_rule.c delete mode 100644 net/ipv4/netfilter/nf_nat_standalone.c create mode 100644 net/netfilter/nf_nat_core.c create mode 100644 net/netfilter/nf_nat_helper.c create mode 100644 net/netfilter/nf_nat_proto_common.c create mode 100644 net/netfilter/nf_nat_proto_dccp.c create mode 100644 net/netfilter/nf_nat_proto_sctp.c create mode 100644 net/netfilter/nf_nat_proto_tcp.c create mode 100644 net/netfilter/nf_nat_proto_udp.c create mode 100644 net/netfilter/nf_nat_proto_udplite.c create mode 100644 net/netfilter/nf_nat_proto_unknown.c create mode 100644 net/netfilter/xt_nat.c (limited to 'include') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index c613cf0d7884..1dcf2a38e51f 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -342,7 +342,7 @@ extern int nf_register_afinfo(const struct nf_afinfo *afinfo); extern void nf_unregister_afinfo(const struct nf_afinfo *afinfo); #include -extern void (*ip_nat_decode_session)(struct sk_buff *, struct flowi *); +extern void (*nf_nat_decode_session_hook)(struct sk_buff *, struct flowi *); static inline void nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, u_int8_t family) @@ -350,13 +350,11 @@ nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, u_int8_t family) #ifdef CONFIG_NF_NAT_NEEDED void (*decodefn)(struct sk_buff *, struct flowi *); - if (family == AF_INET) { - rcu_read_lock(); - decodefn = rcu_dereference(ip_nat_decode_session); - if (decodefn) - decodefn(skb, fl); - rcu_read_unlock(); - } + rcu_read_lock(); + decodefn = rcu_dereference(nf_nat_decode_session_hook); + if (decodefn) + decodefn(skb, fl); + rcu_read_unlock(); #endif } diff --git a/include/linux/netfilter/nf_nat.h b/include/linux/netfilter/nf_nat.h index 8df2d13730b2..bf0cc373ffb6 100644 --- a/include/linux/netfilter/nf_nat.h +++ b/include/linux/netfilter/nf_nat.h @@ -22,4 +22,12 @@ struct nf_nat_ipv4_multi_range_compat { struct nf_nat_ipv4_range range[1]; }; +struct nf_nat_range { + unsigned int flags; + union nf_inet_addr min_addr; + union nf_inet_addr max_addr; + union nf_conntrack_man_proto min_proto; + union nf_conntrack_man_proto max_proto; +}; + #endif /* _NETFILTER_NF_NAT_H */ diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h index f649f7423ca2..68920eab287c 100644 --- a/include/linux/netfilter/nfnetlink_conntrack.h +++ b/include/linux/netfilter/nfnetlink_conntrack.h @@ -142,8 +142,10 @@ enum ctattr_tstamp { enum ctattr_nat { CTA_NAT_UNSPEC, - CTA_NAT_MINIP, - CTA_NAT_MAXIP, + CTA_NAT_V4_MINIP, +#define CTA_NAT_MINIP CTA_NAT_V4_MINIP + CTA_NAT_V4_MAXIP, +#define CTA_NAT_MAXIP CTA_NAT_V4_MAXIP CTA_NAT_PROTO, __CTA_NAT_MAX }; diff --git a/include/linux/netfilter_ipv4.h b/include/linux/netfilter_ipv4.h index e2b12801378d..b962dfc695ae 100644 --- a/include/linux/netfilter_ipv4.h +++ b/include/linux/netfilter_ipv4.h @@ -79,7 +79,6 @@ enum nf_ip_hook_priorities { #ifdef __KERNEL__ extern int ip_route_me_harder(struct sk_buff *skb, unsigned addr_type); -extern int ip_xfrm_me_harder(struct sk_buff *skb); extern __sum16 nf_ip_checksum(struct sk_buff *skb, unsigned int hook, unsigned int dataoff, u_int8_t protocol); #endif /*__KERNEL__*/ diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h index 983f00263243..cc13f377a705 100644 --- a/include/net/netfilter/nf_conntrack_expect.h +++ b/include/net/netfilter/nf_conntrack_expect.h @@ -43,7 +43,7 @@ struct nf_conntrack_expect { unsigned int class; #ifdef CONFIG_NF_NAT_NEEDED - __be32 saved_ip; + union nf_inet_addr saved_addr; /* This is the original per-proto part, used to map the * expected connection the way the recipient expects. */ union nf_conntrack_man_proto saved_proto; diff --git a/include/net/netfilter/nf_nat.h b/include/net/netfilter/nf_nat.h index b4de990b55f1..1752f1339054 100644 --- a/include/net/netfilter/nf_nat.h +++ b/include/net/netfilter/nf_nat.h @@ -50,7 +50,7 @@ struct nf_conn_nat { /* Set up the info structure to map into this range. */ extern unsigned int nf_nat_setup_info(struct nf_conn *ct, - const struct nf_nat_ipv4_range *range, + const struct nf_nat_range *range, enum nf_nat_manip_type maniptype); /* Is this tuple already taken? (not by us)*/ diff --git a/include/net/netfilter/nf_nat_core.h b/include/net/netfilter/nf_nat_core.h index b13d8d18d595..972e1e47ec79 100644 --- a/include/net/netfilter/nf_nat_core.h +++ b/include/net/netfilter/nf_nat_core.h @@ -12,10 +12,7 @@ extern unsigned int nf_nat_packet(struct nf_conn *ct, unsigned int hooknum, struct sk_buff *skb); -extern int nf_nat_icmp_reply_translation(struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - unsigned int hooknum, - struct sk_buff *skb); +extern int nf_xfrm_me_harder(struct sk_buff *skb, unsigned int family); static inline int nf_nat_initialized(struct nf_conn *ct, enum nf_nat_manip_type manip) diff --git a/include/net/netfilter/nf_nat_l3proto.h b/include/net/netfilter/nf_nat_l3proto.h new file mode 100644 index 000000000000..beed96961fa7 --- /dev/null +++ b/include/net/netfilter/nf_nat_l3proto.h @@ -0,0 +1,47 @@ +#ifndef _NF_NAT_L3PROTO_H +#define _NF_NAT_L3PROTO_H + +struct nf_nat_l4proto; +struct nf_nat_l3proto { + u8 l3proto; + + bool (*in_range)(const struct nf_conntrack_tuple *t, + const struct nf_nat_range *range); + + u32 (*secure_port)(const struct nf_conntrack_tuple *t, __be16); + + bool (*manip_pkt)(struct sk_buff *skb, + unsigned int iphdroff, + const struct nf_nat_l4proto *l4proto, + const struct nf_conntrack_tuple *target, + enum nf_nat_manip_type maniptype); + + void (*csum_update)(struct sk_buff *skb, unsigned int iphdroff, + __sum16 *check, + const struct nf_conntrack_tuple *t, + enum nf_nat_manip_type maniptype); + + void (*csum_recalc)(struct sk_buff *skb, u8 proto, + void *data, __sum16 *check, + int datalen, int oldlen); + + void (*decode_session)(struct sk_buff *skb, + const struct nf_conn *ct, + enum ip_conntrack_dir dir, + unsigned long statusbit, + struct flowi *fl); + + int (*nlattr_to_range)(struct nlattr *tb[], + struct nf_nat_range *range); +}; + +extern int nf_nat_l3proto_register(const struct nf_nat_l3proto *); +extern void nf_nat_l3proto_unregister(const struct nf_nat_l3proto *); +extern const struct nf_nat_l3proto *__nf_nat_l3proto_find(u8 l3proto); + +extern int nf_nat_icmp_reply_translation(struct sk_buff *skb, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int hooknum); + +#endif /* _NF_NAT_L3PROTO_H */ diff --git a/include/net/netfilter/nf_nat_l4proto.h b/include/net/netfilter/nf_nat_l4proto.h new file mode 100644 index 000000000000..1f0a4f018fcf --- /dev/null +++ b/include/net/netfilter/nf_nat_l4proto.h @@ -0,0 +1,71 @@ +/* Header for use in defining a given protocol. */ +#ifndef _NF_NAT_L4PROTO_H +#define _NF_NAT_L4PROTO_H +#include +#include + +struct nf_nat_range; +struct nf_nat_l3proto; + +struct nf_nat_l4proto { + /* Protocol number. */ + u8 l4proto; + + /* Translate a packet to the target according to manip type. + * Return true if succeeded. + */ + bool (*manip_pkt)(struct sk_buff *skb, + const struct nf_nat_l3proto *l3proto, + unsigned int iphdroff, unsigned int hdroff, + const struct nf_conntrack_tuple *tuple, + enum nf_nat_manip_type maniptype); + + /* Is the manipable part of the tuple between min and max incl? */ + bool (*in_range)(const struct nf_conntrack_tuple *tuple, + enum nf_nat_manip_type maniptype, + const union nf_conntrack_man_proto *min, + const union nf_conntrack_man_proto *max); + + /* Alter the per-proto part of the tuple (depending on + * maniptype), to give a unique tuple in the given range if + * possible. Per-protocol part of tuple is initialized to the + * incoming packet. + */ + void (*unique_tuple)(const struct nf_nat_l3proto *l3proto, + struct nf_conntrack_tuple *tuple, + const struct nf_nat_range *range, + enum nf_nat_manip_type maniptype, + const struct nf_conn *ct); + + int (*nlattr_to_range)(struct nlattr *tb[], + struct nf_nat_range *range); +}; + +/* Protocol registration. */ +extern int nf_nat_l4proto_register(u8 l3proto, const struct nf_nat_l4proto *l4proto); +extern void nf_nat_l4proto_unregister(u8 l3proto, const struct nf_nat_l4proto *l4proto); + +extern const struct nf_nat_l4proto *__nf_nat_l4proto_find(u8 l3proto, u8 l4proto); + +/* Built-in protocols. */ +extern const struct nf_nat_l4proto nf_nat_l4proto_tcp; +extern const struct nf_nat_l4proto nf_nat_l4proto_udp; +extern const struct nf_nat_l4proto nf_nat_l4proto_icmp; +extern const struct nf_nat_l4proto nf_nat_l4proto_unknown; + +extern bool nf_nat_l4proto_in_range(const struct nf_conntrack_tuple *tuple, + enum nf_nat_manip_type maniptype, + const union nf_conntrack_man_proto *min, + const union nf_conntrack_man_proto *max); + +extern void nf_nat_l4proto_unique_tuple(const struct nf_nat_l3proto *l3proto, + struct nf_conntrack_tuple *tuple, + const struct nf_nat_range *range, + enum nf_nat_manip_type maniptype, + const struct nf_conn *ct, + u16 *rover); + +extern int nf_nat_l4proto_nlattr_to_range(struct nlattr *tb[], + struct nf_nat_range *range); + +#endif /*_NF_NAT_L4PROTO_H*/ diff --git a/include/net/netfilter/nf_nat_protocol.h b/include/net/netfilter/nf_nat_protocol.h deleted file mode 100644 index 7b0b51165f70..000000000000 --- a/include/net/netfilter/nf_nat_protocol.h +++ /dev/null @@ -1,67 +0,0 @@ -/* Header for use in defining a given protocol. */ -#ifndef _NF_NAT_PROTOCOL_H -#define _NF_NAT_PROTOCOL_H -#include -#include - -struct nf_nat_ipv4_range; - -struct nf_nat_protocol { - /* Protocol number. */ - unsigned int protonum; - - /* Translate a packet to the target according to manip type. - Return true if succeeded. */ - bool (*manip_pkt)(struct sk_buff *skb, - unsigned int iphdroff, - const struct nf_conntrack_tuple *tuple, - enum nf_nat_manip_type maniptype); - - /* Is the manipable part of the tuple between min and max incl? */ - bool (*in_range)(const struct nf_conntrack_tuple *tuple, - enum nf_nat_manip_type maniptype, - const union nf_conntrack_man_proto *min, - const union nf_conntrack_man_proto *max); - - /* Alter the per-proto part of the tuple (depending on - maniptype), to give a unique tuple in the given range if - possible. Per-protocol part of tuple is initialized to the - incoming packet. */ - void (*unique_tuple)(struct nf_conntrack_tuple *tuple, - const struct nf_nat_ipv4_range *range, - enum nf_nat_manip_type maniptype, - const struct nf_conn *ct); - - int (*nlattr_to_range)(struct nlattr *tb[], - struct nf_nat_ipv4_range *range); -}; - -/* Protocol registration. */ -extern int nf_nat_protocol_register(const struct nf_nat_protocol *proto); -extern void nf_nat_protocol_unregister(const struct nf_nat_protocol *proto); - -/* Built-in protocols. */ -extern const struct nf_nat_protocol nf_nat_protocol_tcp; -extern const struct nf_nat_protocol nf_nat_protocol_udp; -extern const struct nf_nat_protocol nf_nat_protocol_icmp; -extern const struct nf_nat_protocol nf_nat_unknown_protocol; - -extern int init_protocols(void) __init; -extern void cleanup_protocols(void); -extern const struct nf_nat_protocol *find_nat_proto(u_int16_t protonum); - -extern bool nf_nat_proto_in_range(const struct nf_conntrack_tuple *tuple, - enum nf_nat_manip_type maniptype, - const union nf_conntrack_man_proto *min, - const union nf_conntrack_man_proto *max); - -extern void nf_nat_proto_unique_tuple(struct nf_conntrack_tuple *tuple, - const struct nf_nat_ipv4_range *range, - enum nf_nat_manip_type maniptype, - const struct nf_conn *ct, - u_int16_t *rover); - -extern int nf_nat_proto_nlattr_to_range(struct nlattr *tb[], - struct nf_nat_ipv4_range *range); - -#endif /*_NF_NAT_PROTO_H*/ diff --git a/include/net/netfilter/nf_nat_rule.h b/include/net/netfilter/nf_nat_rule.h deleted file mode 100644 index 2890bdc4cd92..000000000000 --- a/include/net/netfilter/nf_nat_rule.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef _NF_NAT_RULE_H -#define _NF_NAT_RULE_H -#include -#include -#include - -extern int nf_nat_rule_init(void) __init; -extern void nf_nat_rule_cleanup(void); -extern int nf_nat_rule_find(struct sk_buff *skb, - unsigned int hooknum, - const struct net_device *in, - const struct net_device *out, - struct nf_conn *ct); - -#endif /* _NF_NAT_RULE_H */ diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h index 3aecdc7a84fb..a1d83cc8bf85 100644 --- a/include/net/netns/conntrack.h +++ b/include/net/netns/conntrack.h @@ -83,6 +83,10 @@ struct netns_ct { int sysctl_auto_assign_helper; bool auto_assign_helper_warned; struct nf_ip_net nf_ct_proto; +#ifdef CONFIG_NF_NAT_NEEDED + struct hlist_head *nat_bysource; + unsigned int nat_htable_size; +#endif #ifdef CONFIG_SYSCTL struct ctl_table_header *sysctl_header; struct ctl_table_header *acct_sysctl_header; diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 1474dd65c66f..ace280d19a20 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -51,8 +51,6 @@ struct netns_ipv4 { struct xt_table *iptable_security; #endif struct xt_table *nat_table; - struct hlist_head *nat_bysource; - unsigned int nat_htable_size; #endif int sysctl_icmp_echo_ignore_all; diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c index ed1b36783192..f1643c0c3587 100644 --- a/net/ipv4/netfilter.c +++ b/net/ipv4/netfilter.c @@ -72,43 +72,6 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned int addr_type) } EXPORT_SYMBOL(ip_route_me_harder); -#ifdef CONFIG_XFRM -int ip_xfrm_me_harder(struct sk_buff *skb) -{ - struct flowi fl; - unsigned int hh_len; - struct dst_entry *dst; - - if (IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) - return 0; - if (xfrm_decode_session(skb, &fl, AF_INET) < 0) - return -1; - - dst = skb_dst(skb); - if (dst->xfrm) - dst = ((struct xfrm_dst *)dst)->route; - dst_hold(dst); - - dst = xfrm_lookup(dev_net(dst->dev), dst, &fl, skb->sk, 0); - if (IS_ERR(dst)) - return -1; - - skb_dst_drop(skb); - skb_dst_set(skb, dst); - - /* Change in oif may mean change in hh_len. */ - hh_len = skb_dst(skb)->dev->hard_header_len; - if (skb_headroom(skb) < hh_len && - pskb_expand_head(skb, hh_len - skb_headroom(skb), 0, GFP_ATOMIC)) - return -1; - return 0; -} -EXPORT_SYMBOL(ip_xfrm_me_harder); -#endif - -void (*ip_nat_decode_session)(struct sk_buff *, struct flowi *); -EXPORT_SYMBOL(ip_nat_decode_session); - /* * Extra routing may needed on local out, as the QUEUE target never * returns control to the table. diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index fcc543cd987a..b26629681bdb 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig @@ -143,25 +143,22 @@ config IP_NF_TARGET_ULOG To compile it as a module, choose M here. If unsure, say N. # NAT + specific targets: nf_conntrack -config NF_NAT - tristate "Full NAT" +config NF_NAT_IPV4 + tristate "IPv4 NAT" depends on NF_CONNTRACK_IPV4 default m if NETFILTER_ADVANCED=n + select NF_NAT help - The Full NAT option allows masquerading, port forwarding and other + The IPv4 NAT option allows masquerading, port forwarding and other forms of full Network Address Port Translation. It is controlled by the `nat' table in iptables: see the man page for iptables(8). To compile it as a module, choose M here. If unsure, say N. -config NF_NAT_NEEDED - bool - depends on NF_NAT - default y +if NF_NAT_IPV4 config IP_NF_TARGET_MASQUERADE tristate "MASQUERADE target support" - depends on NF_NAT default m if NETFILTER_ADVANCED=n help Masquerading is a special case of NAT: all outgoing connections are @@ -174,7 +171,6 @@ config IP_NF_TARGET_MASQUERADE config IP_NF_TARGET_NETMAP tristate "NETMAP target support" - depends on NF_NAT depends on NETFILTER_ADVANCED help NETMAP is an implementation of static 1:1 NAT mapping of network @@ -185,7 +181,6 @@ config IP_NF_TARGET_NETMAP config IP_NF_TARGET_REDIRECT tristate "REDIRECT target support" - depends on NF_NAT depends on NETFILTER_ADVANCED help REDIRECT is a special case of NAT: all incoming connections are @@ -195,9 +190,11 @@ config IP_NF_TARGET_REDIRECT To compile it as a module, choose M here. If unsure, say N. +endif + config NF_NAT_SNMP_BASIC tristate "Basic SNMP-ALG support" - depends on NF_CONNTRACK_SNMP && NF_NAT + depends on NF_CONNTRACK_SNMP && NF_NAT_IPV4 depends on NETFILTER_ADVANCED default NF_NAT && NF_CONNTRACK_SNMP ---help--- @@ -219,61 +216,46 @@ config NF_NAT_SNMP_BASIC # '&&' (6) # # (6) Returns the result of min(/expr/, /expr/). -config NF_NAT_PROTO_DCCP - tristate - depends on NF_NAT && NF_CT_PROTO_DCCP - default NF_NAT && NF_CT_PROTO_DCCP config NF_NAT_PROTO_GRE tristate - depends on NF_NAT && NF_CT_PROTO_GRE - -config NF_NAT_PROTO_UDPLITE - tristate - depends on NF_NAT && NF_CT_PROTO_UDPLITE - default NF_NAT && NF_CT_PROTO_UDPLITE - -config NF_NAT_PROTO_SCTP - tristate - default NF_NAT && NF_CT_PROTO_SCTP - depends on NF_NAT && NF_CT_PROTO_SCTP - select LIBCRC32C + depends on NF_NAT_IPV4 && NF_CT_PROTO_GRE config NF_NAT_FTP tristate - depends on NF_CONNTRACK && NF_NAT - default NF_NAT && NF_CONNTRACK_FTP + depends on NF_CONNTRACK && NF_NAT_IPV4 + default NF_NAT_IPV4 && NF_CONNTRACK_FTP config NF_NAT_IRC tristate - depends on NF_CONNTRACK && NF_NAT - default NF_NAT && NF_CONNTRACK_IRC + depends on NF_CONNTRACK && NF_NAT_IPV4 + default NF_NAT_IPV4 && NF_CONNTRACK_IRC config NF_NAT_TFTP tristate - depends on NF_CONNTRACK && NF_NAT - default NF_NAT && NF_CONNTRACK_TFTP + depends on NF_CONNTRACK && NF_NAT_IPV4 + default NF_NAT_IPV4 && NF_CONNTRACK_TFTP config NF_NAT_AMANDA tristate - depends on NF_CONNTRACK && NF_NAT - default NF_NAT && NF_CONNTRACK_AMANDA + depends on NF_CONNTRACK && NF_NAT_IPV4 + default NF_NAT_IPV4 && NF_CONNTRACK_AMANDA config NF_NAT_PPTP tristate - depends on NF_CONNTRACK && NF_NAT - default NF_NAT && NF_CONNTRACK_PPTP + depends on NF_CONNTRACK && NF_NAT_IPV4 + default NF_NAT_IPV4 && NF_CONNTRACK_PPTP select NF_NAT_PROTO_GRE config NF_NAT_H323 tristate - depends on NF_CONNTRACK && NF_NAT - default NF_NAT && NF_CONNTRACK_H323 + depends on NF_CONNTRACK && NF_NAT_IPV4 + default NF_NAT_IPV4 && NF_CONNTRACK_H323 config NF_NAT_SIP tristate - depends on NF_CONNTRACK && NF_NAT - default NF_NAT && NF_CONNTRACK_SIP + depends on NF_CONNTRACK && NF_NAT_IPV4 + default NF_NAT_IPV4 && NF_CONNTRACK_SIP # mangle + specific targets config IP_NF_MANGLE diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index c20674dc9452..0ea3acc510e2 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile @@ -10,13 +10,11 @@ nf_conntrack_ipv4-objs += nf_conntrack_l3proto_ipv4_compat.o endif endif -nf_nat-y := nf_nat_core.o nf_nat_helper.o nf_nat_proto_unknown.o nf_nat_proto_common.o nf_nat_proto_tcp.o nf_nat_proto_udp.o nf_nat_proto_icmp.o -iptable_nat-y := nf_nat_rule.o nf_nat_standalone.o - # connection tracking obj-$(CONFIG_NF_CONNTRACK_IPV4) += nf_conntrack_ipv4.o -obj-$(CONFIG_NF_NAT) += nf_nat.o +nf_nat_ipv4-y := nf_nat_l3proto_ipv4.o nf_nat_proto_icmp.o +obj-$(CONFIG_NF_NAT_IPV4) += nf_nat_ipv4.o # defrag obj-$(CONFIG_NF_DEFRAG_IPV4) += nf_defrag_ipv4.o @@ -32,10 +30,7 @@ obj-$(CONFIG_NF_NAT_SNMP_BASIC) += nf_nat_snmp_basic.o obj-$(CONFIG_NF_NAT_TFTP) += nf_nat_tftp.o # NAT protocols (nf_nat) -obj-$(CONFIG_NF_NAT_PROTO_DCCP) += nf_nat_proto_dccp.o obj-$(CONFIG_NF_NAT_PROTO_GRE) += nf_nat_proto_gre.o -obj-$(CONFIG_NF_NAT_PROTO_UDPLITE) += nf_nat_proto_udplite.o -obj-$(CONFIG_NF_NAT_PROTO_SCTP) += nf_nat_proto_sctp.o # generic IP tables obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o @@ -43,7 +38,7 @@ obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o # the three instances of ip_tables obj-$(CONFIG_IP_NF_FILTER) += iptable_filter.o obj-$(CONFIG_IP_NF_MANGLE) += iptable_mangle.o -obj-$(CONFIG_NF_NAT) += iptable_nat.o +obj-$(CONFIG_NF_NAT_IPV4) += iptable_nat.o obj-$(CONFIG_IP_NF_RAW) += iptable_raw.o obj-$(CONFIG_IP_NF_SECURITY) += iptable_security.o diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c index cbb6a1a6f6f7..1c3aa28b51ae 100644 --- a/net/ipv4/netfilter/ipt_MASQUERADE.c +++ b/net/ipv4/netfilter/ipt_MASQUERADE.c @@ -19,9 +19,9 @@ #include #include #include -#include #include #include +#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Netfilter Core Team "); @@ -49,7 +49,7 @@ masquerade_tg(struct sk_buff *skb, const struct xt_action_param *par) struct nf_conn *ct; struct nf_conn_nat *nat; enum ip_conntrack_info ctinfo; - struct nf_nat_ipv4_range newrange; + struct nf_nat_range newrange; const struct nf_nat_ipv4_multi_range_compat *mr; const struct rtable *rt; __be32 newsrc, nh; @@ -80,10 +80,13 @@ masquerade_tg(struct sk_buff *skb, const struct xt_action_param *par) nat->masq_index = par->out->ifindex; /* Transfer from original range. */ - newrange = ((struct nf_nat_ipv4_range) - { mr->range[0].flags | NF_NAT_RANGE_MAP_IPS, - newsrc, newsrc, - mr->range[0].min, mr->range[0].max }); + memset(&newrange.min_addr, 0, sizeof(newrange.min_addr)); + memset(&newrange.max_addr, 0, sizeof(newrange.max_addr)); + newrange.flags = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS; + newrange.min_addr.ip = newsrc; + newrange.max_addr.ip = newsrc; + newrange.min_proto = mr->range[0].min; + newrange.max_proto = mr->range[0].max; /* Hand modified range to generic setup. */ return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_SRC); diff --git a/net/ipv4/netfilter/ipt_NETMAP.c b/net/ipv4/netfilter/ipt_NETMAP.c index b5bfbbabf70d..85028dc0425d 100644 --- a/net/ipv4/netfilter/ipt_NETMAP.c +++ b/net/ipv4/netfilter/ipt_NETMAP.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Svenning Soerensen "); @@ -44,7 +44,7 @@ netmap_tg(struct sk_buff *skb, const struct xt_action_param *par) enum ip_conntrack_info ctinfo; __be32 new_ip, netmask; const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo; - struct nf_nat_ipv4_range newrange; + struct nf_nat_range newrange; NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING || par->hooknum == NF_INET_POST_ROUTING || @@ -61,10 +61,13 @@ netmap_tg(struct sk_buff *skb, const struct xt_action_param *par) new_ip = ip_hdr(skb)->saddr & ~netmask; new_ip |= mr->range[0].min_ip & netmask; - newrange = ((struct nf_nat_ipv4_range) - { mr->range[0].flags | NF_NAT_RANGE_MAP_IPS, - new_ip, new_ip, - mr->range[0].min, mr->range[0].max }); + memset(&newrange.min_addr, 0, sizeof(newrange.min_addr)); + memset(&newrange.max_addr, 0, sizeof(newrange.max_addr)); + newrange.flags = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS; + newrange.min_addr.ip = new_ip; + newrange.max_addr.ip = new_ip; + newrange.min_proto = mr->range[0].min; + newrange.max_proto = mr->range[0].max; /* Hand modified range to generic setup. */ return nf_nat_setup_info(ct, &newrange, HOOK2MANIP(par->hooknum)); diff --git a/net/ipv4/netfilter/ipt_REDIRECT.c b/net/ipv4/netfilter/ipt_REDIRECT.c index 7c0103a5203e..11407d7d2472 100644 --- a/net/ipv4/netfilter/ipt_REDIRECT.c +++ b/net/ipv4/netfilter/ipt_REDIRECT.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Netfilter Core Team "); @@ -48,7 +48,7 @@ redirect_tg(struct sk_buff *skb, const struct xt_action_param *par) enum ip_conntrack_info ctinfo; __be32 newdst; const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo; - struct nf_nat_ipv4_range newrange; + struct nf_nat_range newrange; NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING || par->hooknum == NF_INET_LOCAL_OUT); @@ -76,10 +76,13 @@ redirect_tg(struct sk_buff *skb, const struct xt_action_param *par) } /* Transfer from original range. */ - newrange = ((struct nf_nat_ipv4_range) - { mr->range[0].flags | NF_NAT_RANGE_MAP_IPS, - newdst, newdst, - mr->range[0].min, mr->range[0].max }); + memset(&newrange.min_addr, 0, sizeof(newrange.min_addr)); + memset(&newrange.max_addr, 0, sizeof(newrange.max_addr)); + newrange.flags = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS; + newrange.min_addr.ip = newdst; + newrange.max_addr.ip = newdst; + newrange.min_proto = mr->range[0].min; + newrange.max_proto = mr->range[0].max; /* Hand modified range to generic setup. */ return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST); diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c new file mode 100644 index 000000000000..9e0ffaf1d942 --- /dev/null +++ b/net/ipv4/netfilter/iptable_nat.c @@ -0,0 +1,320 @@ +/* (C) 1999-2001 Paul `Rusty' Russell + * (C) 2002-2006 Netfilter Core Team + * (C) 2011 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 const struct xt_table nf_nat_ipv4_table = { + .name = "nat", + .valid_hooks = (1 << NF_INET_PRE_ROUTING) | + (1 << NF_INET_POST_ROUTING) | + (1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_LOCAL_IN), + .me = THIS_MODULE, + .af = NFPROTO_IPV4, +}; + +static unsigned int alloc_null_binding(struct nf_conn *ct, unsigned int hooknum) +{ + /* Force range to this IP; let proto decide mapping for + * per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED). + */ + struct nf_nat_range range; + + range.flags = 0; + pr_debug("Allocating NULL binding for %p (%pI4)\n", ct, + HOOK2MANIP(hooknum) == NF_NAT_MANIP_SRC ? + &ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip : + &ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip); + + return nf_nat_setup_info(ct, &range, HOOK2MANIP(hooknum)); +} + +static unsigned int nf_nat_rule_find(struct sk_buff *skb, unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + struct nf_conn *ct) +{ + struct net *net = nf_ct_net(ct); + unsigned int ret; + + ret = ipt_do_table(skb, hooknum, in, out, net->ipv4.nat_table); + if (ret == NF_ACCEPT) { + if (!nf_nat_initialized(ct, HOOK2MANIP(hooknum))) + ret = alloc_null_binding(ct, hooknum); + } + return ret; +} + +static unsigned int +nf_nat_ipv4_fn(unsigned int hooknum, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + struct nf_conn *ct; + enum ip_conntrack_info ctinfo; + struct nf_conn_nat *nat; + /* maniptype == SRC for postrouting. */ + enum nf_nat_manip_type maniptype = HOOK2MANIP(hooknum); + + /* We never see fragments: conntrack defrags on pre-routing + * and local-out, and nf_nat_out protects post-routing. + */ + NF_CT_ASSERT(!ip_is_fragment(ip_hdr(skb))); + + ct = nf_ct_get(skb, &ctinfo); + /* Can't track? It's not due to stress, or conntrack would + * have dropped it. Hence it's the user's responsibilty to + * packet filter it out, or implement conntrack/NAT for that + * protocol. 8) --RR + */ + if (!ct) + return NF_ACCEPT; + + /* Don't try to NAT if this packet is not conntracked */ + if (nf_ct_is_untracked(ct)) + return NF_ACCEPT; + + nat = nfct_nat(ct); + if (!nat) { + /* NAT module was loaded late. */ + if (nf_ct_is_confirmed(ct)) + return NF_ACCEPT; + nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); + if (nat == NULL) { + pr_debug("failed to add NAT extension\n"); + return NF_ACCEPT; + } + } + + switch (ctinfo) { + case IP_CT_RELATED: + case IP_CT_RELATED_REPLY: + if (ip_hdr(skb)->protocol == IPPROTO_ICMP) { + if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo, + hooknum)) + return NF_DROP; + else + return NF_ACCEPT; + } + /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */ + case IP_CT_NEW: + /* Seen it before? This can happen for loopback, retrans, + * or local packets. + */ + if (!nf_nat_initialized(ct, maniptype)) { + unsigned int ret; + + ret = nf_nat_rule_find(skb, hooknum, in, out, ct); + if (ret != NF_ACCEPT) + return ret; + } else + pr_debug("Already setup manip %s for ct %p\n", + maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST", + ct); + break; + + default: + /* ESTABLISHED */ + NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED || + ctinfo == IP_CT_ESTABLISHED_REPLY); + } + + return nf_nat_packet(ct, ctinfo, hooknum, skb); +} + +static unsigned int +nf_nat_ipv4_in(unsigned int hooknum, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + unsigned int ret; + __be32 daddr = ip_hdr(skb)->daddr; + + ret = nf_nat_ipv4_fn(hooknum, skb, in, out, okfn); + if (ret != NF_DROP && ret != NF_STOLEN && + daddr != ip_hdr(skb)->daddr) + skb_dst_drop(skb); + + return ret; +} + +static unsigned int +nf_nat_ipv4_out(unsigned int hooknum, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ +#ifdef CONFIG_XFRM + const struct nf_conn *ct; + enum ip_conntrack_info ctinfo; +#endif + unsigned int ret; + + /* root is playing with raw sockets. */ + if (skb->len < sizeof(struct iphdr) || + ip_hdrlen(skb) < sizeof(struct iphdr)) + return NF_ACCEPT; + + ret = nf_nat_ipv4_fn(hooknum, skb, in, out, okfn); +#ifdef CONFIG_XFRM + if (ret != NF_DROP && ret != NF_STOLEN && + !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && + (ct = nf_ct_get(skb, &ctinfo)) != NULL) { + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + + if ((ct->tuplehash[dir].tuple.src.u3.ip != + ct->tuplehash[!dir].tuple.dst.u3.ip) || + (ct->tuplehash[dir].tuple.src.u.all != + ct->tuplehash[!dir].tuple.dst.u.all)) + if (nf_xfrm_me_harder(skb, AF_INET) < 0) + ret = NF_DROP; + } +#endif + return ret; +} + +static unsigned int +nf_nat_ipv4_local_fn(unsigned int hooknum, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + const struct nf_conn *ct; + enum ip_conntrack_info ctinfo; + unsigned int ret; + + /* root is playing with raw sockets. */ + if (skb->len < sizeof(struct iphdr) || + ip_hdrlen(skb) < sizeof(struct iphdr)) + return NF_ACCEPT; + + ret = nf_nat_ipv4_fn(hooknum, skb, in, out, okfn); + if (ret != NF_DROP && ret != NF_STOLEN && + (ct = nf_ct_get(skb, &ctinfo)) != NULL) { + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + + if (ct->tuplehash[dir].tuple.dst.u3.ip != + ct->tuplehash[!dir].tuple.src.u3.ip) { + if (ip_route_me_harder(skb, RTN_UNSPEC)) + ret = NF_DROP; + } +#ifdef CONFIG_XFRM + else if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && + ct->tuplehash[dir].tuple.dst.u.all != + ct->tuplehash[!dir].tuple.src.u.all) + if (nf_xfrm_me_harder(skb, AF_INET) < 0) + ret = NF_DROP; +#endif + } + return ret; +} + +static struct nf_hook_ops nf_nat_ipv4_ops[] __read_mostly = { + /* Before packet filtering, change destination */ + { + .hook = nf_nat_ipv4_in, + .owner = THIS_MODULE, + .pf = NFPROTO_IPV4, + .hooknum = NF_INET_PRE_ROUTING, + .priority = NF_IP_PRI_NAT_DST, + }, + /* After packet filtering, change source */ + { + .hook = nf_nat_ipv4_out, + .owner = THIS_MODULE, + .pf = NFPROTO_IPV4, + .hooknum = NF_INET_POST_ROUTING, + .priority = NF_IP_PRI_NAT_SRC, + }, + /* Before packet filtering, change destination */ + { + .hook = nf_nat_ipv4_local_fn, + .owner = THIS_MODULE, + .pf = NFPROTO_IPV4, + .hooknum = NF_INET_LOCAL_OUT, + .priority = NF_IP_PRI_NAT_DST, + }, + /* After packet filtering, change source */ + { + .hook = nf_nat_ipv4_fn, + .owner = THIS_MODULE, + .pf = NFPROTO_IPV4, + .hooknum = NF_INET_LOCAL_IN, + .priority = NF_IP_PRI_NAT_SRC, + }, +}; + +static int __net_init iptable_nat_net_init(struct net *net) +{ + struct ipt_replace *repl; + + repl = ipt_alloc_initial_table(&nf_nat_ipv4_table); + if (repl == NULL) + return -ENOMEM; + net->ipv4.nat_table = ipt_register_table(net, &nf_nat_ipv4_table, repl); + kfree(repl); + if (IS_ERR(net->ipv4.nat_table)) + return PTR_ERR(net->ipv4.nat_table); + return 0; +} + +static void __net_exit iptable_nat_net_exit(struct net *net) +{ + ipt_unregister_table(net, net->ipv4.nat_table); +} + +static struct pernet_operations iptable_nat_net_ops = { + .init = iptable_nat_net_init, + .exit = iptable_nat_net_exit, +}; + +static int __init iptable_nat_init(void) +{ + int err; + + err = register_pernet_subsys(&iptable_nat_net_ops); + if (err < 0) + goto err1; + + err = nf_register_hooks(nf_nat_ipv4_ops, ARRAY_SIZE(nf_nat_ipv4_ops)); + if (err < 0) + goto err2; + return 0; + +err2: + unregister_pernet_subsys(&iptable_nat_net_ops); +err1: + return err; +} + +static void __exit iptable_nat_exit(void) +{ + nf_unregister_hooks(nf_nat_ipv4_ops, ARRAY_SIZE(nf_nat_ipv4_ops)); + unregister_pernet_subsys(&iptable_nat_net_ops); +} + +module_init(iptable_nat_init); +module_exit(iptable_nat_exit); + +MODULE_LICENSE("GPL"); diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 4ada3295d9a7..fcdd0c2406e6 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -29,12 +29,6 @@ #include #include -int (*nf_nat_seq_adjust_hook)(struct sk_buff *skb, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - unsigned int protoff); -EXPORT_SYMBOL_GPL(nf_nat_seq_adjust_hook); - static bool ipv4_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff, struct nf_conntrack_tuple *tuple) { diff --git a/net/ipv4/netfilter/nf_nat_amanda.c b/net/ipv4/netfilter/nf_nat_amanda.c index 75464b62f5f2..42d337881171 100644 --- a/net/ipv4/netfilter/nf_nat_amanda.c +++ b/net/ipv4/netfilter/nf_nat_amanda.c @@ -16,7 +16,6 @@ #include #include #include -#include #include MODULE_AUTHOR("Brian J. Murrell "); diff --git a/net/ipv4/netfilter/nf_nat_core.c b/net/ipv4/netfilter/nf_nat_core.c deleted file mode 100644 index 44b082fd48ab..000000000000 --- a/net/ipv4/netfilter/nf_nat_core.c +++ /dev/null @@ -1,763 +0,0 @@ -/* NAT for netfilter; shared with compatibility layer. */ - -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2006 Netfilter Core Team - * - * 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 /* For tcp_prot in getorigdst */ -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static DEFINE_SPINLOCK(nf_nat_lock); - -static struct nf_conntrack_l3proto *l3proto __read_mostly; - -#define MAX_IP_NAT_PROTO 256 -static const struct nf_nat_protocol __rcu *nf_nat_protos[MAX_IP_NAT_PROTO] - __read_mostly; - -static inline const struct nf_nat_protocol * -__nf_nat_proto_find(u_int8_t protonum) -{ - return rcu_dereference(nf_nat_protos[protonum]); -} - -/* We keep an extra hash for each conntrack, for fast searching. */ -static inline unsigned int -hash_by_src(const struct net *net, u16 zone, - const struct nf_conntrack_tuple *tuple) -{ - unsigned int hash; - - /* Original src, to ensure we map it consistently if poss. */ - hash = jhash_3words((__force u32)tuple->src.u3.ip, - (__force u32)tuple->src.u.all ^ zone, - tuple->dst.protonum, nf_conntrack_hash_rnd); - return ((u64)hash * net->ipv4.nat_htable_size) >> 32; -} - -/* Is this tuple already taken? (not by us) */ -int -nf_nat_used_tuple(const struct nf_conntrack_tuple *tuple, - const struct nf_conn *ignored_conntrack) -{ - /* Conntrack tracking doesn't keep track of outgoing tuples; only - incoming ones. NAT means they don't have a fixed mapping, - so we invert the tuple and look for the incoming reply. - - We could keep a separate hash if this proves too slow. */ - struct nf_conntrack_tuple reply; - - nf_ct_invert_tuplepr(&reply, tuple); - return nf_conntrack_tuple_taken(&reply, ignored_conntrack); -} -EXPORT_SYMBOL(nf_nat_used_tuple); - -/* If we source map this tuple so reply looks like reply_tuple, will - * that meet the constraints of range. */ -static int -in_range(const struct nf_conntrack_tuple *tuple, - const struct nf_nat_ipv4_range *range) -{ - const struct nf_nat_protocol *proto; - int ret = 0; - - /* If we are supposed to map IPs, then we must be in the - range specified, otherwise let this drag us onto a new src IP. */ - if (range->flags & NF_NAT_RANGE_MAP_IPS) { - if (ntohl(tuple->src.u3.ip) < ntohl(range->min_ip) || - ntohl(tuple->src.u3.ip) > ntohl(range->max_ip)) - return 0; - } - - rcu_read_lock(); - proto = __nf_nat_proto_find(tuple->dst.protonum); - if (!(range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) || - proto->in_range(tuple, NF_NAT_MANIP_SRC, - &range->min, &range->max)) - ret = 1; - rcu_read_unlock(); - - return ret; -} - -static inline int -same_src(const struct nf_conn *ct, - const struct nf_conntrack_tuple *tuple) -{ - const struct nf_conntrack_tuple *t; - - t = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; - return (t->dst.protonum == tuple->dst.protonum && - t->src.u3.ip == tuple->src.u3.ip && - t->src.u.all == tuple->src.u.all); -} - -/* Only called for SRC manip */ -static int -find_appropriate_src(struct net *net, u16 zone, - const struct nf_conntrack_tuple *tuple, - struct nf_conntrack_tuple *result, - const struct nf_nat_ipv4_range *range) -{ - unsigned int h = hash_by_src(net, zone, tuple); - const struct nf_conn_nat *nat; - const struct nf_conn *ct; - const struct hlist_node *n; - - rcu_read_lock(); - hlist_for_each_entry_rcu(nat, n, &net->ipv4.nat_bysource[h], bysource) { - ct = nat->ct; - if (same_src(ct, tuple) && nf_ct_zone(ct) == zone) { - /* Copy source part from reply tuple. */ - nf_ct_invert_tuplepr(result, - &ct->tuplehash[IP_CT_DIR_REPLY].tuple); - result->dst = tuple->dst; - - if (in_range(result, range)) { - rcu_read_unlock(); - return 1; - } - } - } - rcu_read_unlock(); - return 0; -} - -/* For [FUTURE] fragmentation handling, we want the least-used - src-ip/dst-ip/proto triple. Fairness doesn't come into it. Thus - if the range specifies 1.2.3.4 ports 10000-10005 and 1.2.3.5 ports - 1-65535, we don't do pro-rata allocation based on ports; we choose - the ip with the lowest src-ip/dst-ip/proto usage. -*/ -static void -find_best_ips_proto(u16 zone, struct nf_conntrack_tuple *tuple, - const struct nf_nat_ipv4_range *range, - const struct nf_conn *ct, - enum nf_nat_manip_type maniptype) -{ - __be32 *var_ipp; - /* Host order */ - u_int32_t minip, maxip, j; - - /* No IP mapping? Do nothing. */ - if (!(range->flags & NF_NAT_RANGE_MAP_IPS)) - return; - - if (maniptype == NF_NAT_MANIP_SRC) - var_ipp = &tuple->src.u3.ip; - else - var_ipp = &tuple->dst.u3.ip; - - /* Fast path: only one choice. */ - if (range->min_ip == range->max_ip) { - *var_ipp = range->min_ip; - return; - } - - /* Hashing source and destination IPs gives a fairly even - * spread in practice (if there are a small number of IPs - * involved, there usually aren't that many connections - * anyway). The consistency means that servers see the same - * client coming from the same IP (some Internet Banking sites - * like this), even across reboots. */ - minip = ntohl(range->min_ip); - maxip = ntohl(range->max_ip); - j = jhash_2words((__force u32)tuple->src.u3.ip, - range->flags & NF_NAT_RANGE_PERSISTENT ? - 0 : (__force u32)tuple->dst.u3.ip ^ zone, 0); - j = ((u64)j * (maxip - minip + 1)) >> 32; - *var_ipp = htonl(minip + j); -} - -/* Manipulate the tuple into the range given. For NF_INET_POST_ROUTING, - * we change the source to map into the range. For NF_INET_PRE_ROUTING - * and NF_INET_LOCAL_OUT, we change the destination to map into the - * range. It might not be possible to get a unique tuple, but we try. - * At worst (or if we race), we will end up with a final duplicate in - * __ip_conntrack_confirm and drop the packet. */ -static void -get_unique_tuple(struct nf_conntrack_tuple *tuple, - const struct nf_conntrack_tuple *orig_tuple, - const struct nf_nat_ipv4_range *range, - struct nf_conn *ct, - enum nf_nat_manip_type maniptype) -{ - struct net *net = nf_ct_net(ct); - const struct nf_nat_protocol *proto; - u16 zone = nf_ct_zone(ct); - - /* 1) If this srcip/proto/src-proto-part is currently mapped, - and that same mapping gives a unique tuple within the given - range, use that. - - This is only required for source (ie. NAT/masq) mappings. - So far, we don't do local source mappings, so multiple - manips not an issue. */ - if (maniptype == NF_NAT_MANIP_SRC && - !(range->flags & NF_NAT_RANGE_PROTO_RANDOM)) { - /* try the original tuple first */ - if (in_range(orig_tuple, range)) { - if (!nf_nat_used_tuple(orig_tuple, ct)) { - *tuple = *orig_tuple; - return; - } - } else if (find_appropriate_src(net, zone, orig_tuple, tuple, - range)) { - pr_debug("get_unique_tuple: Found current src map\n"); - if (!nf_nat_used_tuple(tuple, ct)) - return; - } - } - - /* 2) Select the least-used IP/proto combination in the given - range. */ - *tuple = *orig_tuple; - find_best_ips_proto(zone, tuple, range, ct, maniptype); - - /* 3) The per-protocol part of the manip is made to map into - the range to make a unique tuple. */ - - rcu_read_lock(); - proto = __nf_nat_proto_find(orig_tuple->dst.protonum); - - /* Only bother mapping if it's not already in range and unique */ - if (!(range->flags & NF_NAT_RANGE_PROTO_RANDOM)) { - if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) { - if (proto->in_range(tuple, maniptype, &range->min, - &range->max) && - (range->min.all == range->max.all || - !nf_nat_used_tuple(tuple, ct))) - goto out; - } else if (!nf_nat_used_tuple(tuple, ct)) { - goto out; - } - } - - /* Last change: get protocol to try to obtain unique tuple. */ - proto->unique_tuple(tuple, range, maniptype, ct); -out: - rcu_read_unlock(); -} - -unsigned int -nf_nat_setup_info(struct nf_conn *ct, - const struct nf_nat_ipv4_range *range, - enum nf_nat_manip_type maniptype) -{ - struct net *net = nf_ct_net(ct); - struct nf_conntrack_tuple curr_tuple, new_tuple; - struct nf_conn_nat *nat; - - /* nat helper or nfctnetlink also setup binding */ - nat = nfct_nat(ct); - if (!nat) { - nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); - if (nat == NULL) { - pr_debug("failed to add NAT extension\n"); - return NF_ACCEPT; - } - } - - NF_CT_ASSERT(maniptype == NF_NAT_MANIP_SRC || - maniptype == NF_NAT_MANIP_DST); - BUG_ON(nf_nat_initialized(ct, maniptype)); - - /* What we've got will look like inverse of reply. Normally - this is what is in the conntrack, except for prior - manipulations (future optimization: if num_manips == 0, - orig_tp = - conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple) */ - nf_ct_invert_tuplepr(&curr_tuple, - &ct->tuplehash[IP_CT_DIR_REPLY].tuple); - - get_unique_tuple(&new_tuple, &curr_tuple, range, ct, maniptype); - - if (!nf_ct_tuple_equal(&new_tuple, &curr_tuple)) { - struct nf_conntrack_tuple reply; - - /* Alter conntrack table so will recognize replies. */ - nf_ct_invert_tuplepr(&reply, &new_tuple); - nf_conntrack_alter_reply(ct, &reply); - - /* Non-atomic: we own this at the moment. */ - if (maniptype == NF_NAT_MANIP_SRC) - ct->status |= IPS_SRC_NAT; - else - ct->status |= IPS_DST_NAT; - } - - if (maniptype == NF_NAT_MANIP_SRC) { - unsigned int srchash; - - srchash = hash_by_src(net, nf_ct_zone(ct), - &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); - spin_lock_bh(&nf_nat_lock); - /* nf_conntrack_alter_reply might re-allocate extension area */ - nat = nfct_nat(ct); - nat->ct = ct; - hlist_add_head_rcu(&nat->bysource, - &net->ipv4.nat_bysource[srchash]); - spin_unlock_bh(&nf_nat_lock); - } - - /* It's done. */ - if (maniptype == NF_NAT_MANIP_DST) - ct->status |= IPS_DST_NAT_DONE; - else - ct->status |= IPS_SRC_NAT_DONE; - - return NF_ACCEPT; -} -EXPORT_SYMBOL(nf_nat_setup_info); - -/* Returns true if succeeded. */ -static bool -manip_pkt(u_int16_t proto, - struct sk_buff *skb, - unsigned int iphdroff, - const struct nf_conntrack_tuple *target, - enum nf_nat_manip_type maniptype) -{ - struct iphdr *iph; - const struct nf_nat_protocol *p; - - if (!skb_make_writable(skb, iphdroff + sizeof(*iph))) - return false; - - iph = (void *)skb->data + iphdroff; - - /* Manipulate protcol part. */ - - /* rcu_read_lock()ed by nf_hook_slow */ - p = __nf_nat_proto_find(proto); - if (!p->manip_pkt(skb, iphdroff, target, maniptype)) - return false; - - iph = (void *)skb->data + iphdroff; - - if (maniptype == NF_NAT_MANIP_SRC) { - csum_replace4(&iph->check, iph->saddr, target->src.u3.ip); - iph->saddr = target->src.u3.ip; - } else { - csum_replace4(&iph->check, iph->daddr, target->dst.u3.ip); - iph->daddr = target->dst.u3.ip; - } - return true; -} - -/* Do packet manipulations according to nf_nat_setup_info. */ -unsigned int nf_nat_packet(struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - unsigned int hooknum, - struct sk_buff *skb) -{ - enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - unsigned long statusbit; - enum nf_nat_manip_type mtype = HOOK2MANIP(hooknum); - - if (mtype == NF_NAT_MANIP_SRC) - statusbit = IPS_SRC_NAT; - else - statusbit = IPS_DST_NAT; - - /* Invert if this is reply dir. */ - if (dir == IP_CT_DIR_REPLY) - statusbit ^= IPS_NAT_MASK; - - /* Non-atomic: these bits don't change. */ - if (ct->status & statusbit) { - struct nf_conntrack_tuple target; - - /* We are aiming to look like inverse of other direction. */ - nf_ct_invert_tuplepr(&target, &ct->tuplehash[!dir].tuple); - - if (!manip_pkt(target.dst.protonum, skb, 0, &target, mtype)) - return NF_DROP; - } - return NF_ACCEPT; -} -EXPORT_SYMBOL_GPL(nf_nat_packet); - -/* Dir is direction ICMP is coming from (opposite to packet it contains) */ -int nf_nat_icmp_reply_translation(struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - unsigned int hooknum, - struct sk_buff *skb) -{ - struct { - struct icmphdr icmp; - struct iphdr ip; - } *inside; - struct nf_conntrack_tuple target; - int hdrlen = ip_hdrlen(skb); - enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - unsigned long statusbit; - enum nf_nat_manip_type manip = HOOK2MANIP(hooknum); - - if (!skb_make_writable(skb, hdrlen + sizeof(*inside))) - return 0; - - inside = (void *)skb->data + hdrlen; - - /* We're actually going to mangle it beyond trivial checksum - adjustment, so make sure the current checksum is correct. */ - if (nf_ip_checksum(skb, hooknum, hdrlen, 0)) - return 0; - - /* Must be RELATED */ - NF_CT_ASSERT(skb->nfctinfo == IP_CT_RELATED || - skb->nfctinfo == IP_CT_RELATED_REPLY); - - /* Redirects on non-null nats must be dropped, else they'll - start talking to each other without our translation, and be - confused... --RR */ - if (inside->icmp.type == ICMP_REDIRECT) { - /* If NAT isn't finished, assume it and drop. */ - if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK) - return 0; - - if (ct->status & IPS_NAT_MASK) - return 0; - } - - if (manip == NF_NAT_MANIP_SRC) - statusbit = IPS_SRC_NAT; - else - statusbit = IPS_DST_NAT; - - /* Invert if this is reply dir. */ - if (dir == IP_CT_DIR_REPLY) - statusbit ^= IPS_NAT_MASK; - - if (!(ct->status & statusbit)) - return 1; - - pr_debug("icmp_reply_translation: translating error %p manip %u " - "dir %s\n", skb, manip, - dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY"); - - /* Change inner back to look like incoming packet. We do the - opposite manip on this hook to normal, because it might not - pass all hooks (locally-generated ICMP). Consider incoming - packet: PREROUTING (DST manip), routing produces ICMP, goes - through POSTROUTING (which must correct the DST manip). */ - if (!manip_pkt(inside->ip.protocol, skb, hdrlen + sizeof(inside->icmp), - &ct->tuplehash[!dir].tuple, !manip)) - return 0; - - if (skb->ip_summed != CHECKSUM_PARTIAL) { - /* Reloading "inside" here since manip_pkt inner. */ - inside = (void *)skb->data + hdrlen; - inside->icmp.checksum = 0; - inside->icmp.checksum = - csum_fold(skb_checksum(skb, hdrlen, - skb->len - hdrlen, 0)); - } - - /* Change outer to look the reply to an incoming packet - * (proto 0 means don't invert per-proto part). */ - nf_ct_invert_tuplepr(&target, &ct->tuplehash[!dir].tuple); - if (!manip_pkt(0, skb, 0, &target, manip)) - return 0; - - return 1; -} -EXPORT_SYMBOL_GPL(nf_nat_icmp_reply_translation); - -/* Protocol registration. */ -int nf_nat_protocol_register(const struct nf_nat_protocol *proto) -{ - int ret = 0; - - spin_lock_bh(&nf_nat_lock); - if (rcu_dereference_protected( - nf_nat_protos[proto->protonum], - lockdep_is_held(&nf_nat_lock) - ) != &nf_nat_unknown_protocol) { - ret = -EBUSY; - goto out; - } - RCU_INIT_POINTER(nf_nat_protos[proto->protonum], proto); - out: - spin_unlock_bh(&nf_nat_lock); - return ret; -} -EXPORT_SYMBOL(nf_nat_protocol_register); - -/* No one stores the protocol anywhere; simply delete it. */ -void nf_nat_protocol_unregister(const struct nf_nat_protocol *proto) -{ - spin_lock_bh(&nf_nat_lock); - RCU_INIT_POINTER(nf_nat_protos[proto->protonum], - &nf_nat_unknown_protocol); - spin_unlock_bh(&nf_nat_lock); - synchronize_rcu(); -} -EXPORT_SYMBOL(nf_nat_protocol_unregister); - -/* No one using conntrack by the time this called. */ -static void nf_nat_cleanup_conntrack(struct nf_conn *ct) -{ - struct nf_conn_nat *nat = nf_ct_ext_find(ct, NF_CT_EXT_NAT); - - if (nat == NULL || nat->ct == NULL) - return; - - NF_CT_ASSERT(nat->ct->status & IPS_SRC_NAT_DONE); - - spin_lock_bh(&nf_nat_lock); - hlist_del_rcu(&nat->bysource); - spin_unlock_bh(&nf_nat_lock); -} - -static void nf_nat_move_storage(void *new, void *old) -{ - struct nf_conn_nat *new_nat = new; - struct nf_conn_nat *old_nat = old; - struct nf_conn *ct = old_nat->ct; - - if (!ct || !(ct->status & IPS_SRC_NAT_DONE)) - return; - - spin_lock_bh(&nf_nat_lock); - hlist_replace_rcu(&old_nat->bysource, &new_nat->bysource); - spin_unlock_bh(&nf_nat_lock); -} - -static struct nf_ct_ext_type nat_extend __read_mostly = { - .len = sizeof(struct nf_conn_nat), - .align = __alignof__(struct nf_conn_nat), - .destroy = nf_nat_cleanup_conntrack, - .move = nf_nat_move_storage, - .id = NF_CT_EXT_NAT, - .flags = NF_CT_EXT_F_PREALLOC, -}; - -#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) - -#include -#include - -static const struct nla_policy protonat_nla_policy[CTA_PROTONAT_MAX+1] = { - [CTA_PROTONAT_PORT_MIN] = { .type = NLA_U16 }, - [CTA_PROTONAT_PORT_MAX] = { .type = NLA_U16 }, -}; - -static int nfnetlink_parse_nat_proto(struct nlattr *attr, - const struct nf_conn *ct, - struct nf_nat_ipv4_range *range) -{ - struct nlattr *tb[CTA_PROTONAT_MAX+1]; - const struct nf_nat_protocol *npt; - int err; - - err = nla_parse_nested(tb, CTA_PROTONAT_MAX, attr, protonat_nla_policy); - if (err < 0) - return err; - - rcu_read_lock(); - npt = __nf_nat_proto_find(nf_ct_protonum(ct)); - if (npt->nlattr_to_range) - err = npt->nlattr_to_range(tb, range); - rcu_read_unlock(); - return err; -} - -static const struct nla_policy nat_nla_policy[CTA_NAT_MAX+1] = { - [CTA_NAT_MINIP] = { .type = NLA_U32 }, - [CTA_NAT_MAXIP] = { .type = NLA_U32 }, - [CTA_NAT_PROTO] = { .type = NLA_NESTED }, -}; - -static int -nfnetlink_parse_nat(const struct nlattr *nat, - const struct nf_conn *ct, struct nf_nat_ipv4_range *range) -{ - struct nlattr *tb[CTA_NAT_MAX+1]; - int err; - - memset(range, 0, sizeof(*range)); - - err = nla_parse_nested(tb, CTA_NAT_MAX, nat, nat_nla_policy); - if (err < 0) - return err; - - if (tb[CTA_NAT_MINIP]) - range->min_ip = nla_get_be32(tb[CTA_NAT_MINIP]); - - if (!tb[CTA_NAT_MAXIP]) - range->max_ip = range->min_ip; - else - range->max_ip = nla_get_be32(tb[CTA_NAT_MAXIP]); - - if (range->min_ip) - range->flags |= NF_NAT_RANGE_MAP_IPS; - - if (!tb[CTA_NAT_PROTO]) - return 0; - - err = nfnetlink_parse_nat_proto(tb[CTA_NAT_PROTO], ct, range); - if (err < 0) - return err; - - return 0; -} - -static int -nfnetlink_parse_nat_setup(struct nf_conn *ct, - enum nf_nat_manip_type manip, - const struct nlattr *attr) -{ - struct nf_nat_ipv4_range range; - - if (nfnetlink_parse_nat(attr, ct, &range) < 0) - return -EINVAL; - if (nf_nat_initialized(ct, manip)) - return -EEXIST; - - return nf_nat_setup_info(ct, &range, manip); -} -#else -static int -nfnetlink_parse_nat_setup(struct nf_conn *ct, - enum nf_nat_manip_type manip, - const struct nlattr *attr) -{ - return -EOPNOTSUPP; -} -#endif - -static int __net_init nf_nat_net_init(struct net *net) -{ - /* Leave them the same for the moment. */ - net->ipv4.nat_htable_size = net->ct.htable_size; - net->ipv4.nat_bysource = nf_ct_alloc_hashtable(&net->ipv4.nat_htable_size, 0); - if (!net->ipv4.nat_bysource) - return -ENOMEM; - return 0; -} - -/* Clear NAT section of all conntracks, in case we're loaded again. */ -static int clean_nat(struct nf_conn *i, void *data) -{ - struct nf_conn_nat *nat = nfct_nat(i); - - if (!nat) - return 0; - memset(nat, 0, sizeof(*nat)); - i->status &= ~(IPS_NAT_MASK | IPS_NAT_DONE_MASK | IPS_SEQ_ADJUST); - return 0; -} - -static void __net_exit nf_nat_net_exit(struct net *net) -{ - nf_ct_iterate_cleanup(net, &clean_nat, NULL); - synchronize_rcu(); - nf_ct_free_hashtable(net->ipv4.nat_bysource, net->ipv4.nat_htable_size); -} - -static struct pernet_operations nf_nat_net_ops = { - .init = nf_nat_net_init, - .exit = nf_nat_net_exit, -}; - -static struct nf_ct_helper_expectfn follow_master_nat = { - .name = "nat-follow-master", - .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) -{ - size_t i; - int ret; - - need_ipv4_conntrack(); - - ret = nf_ct_extend_register(&nat_extend); - if (ret < 0) { - printk(KERN_ERR "nf_nat_core: Unable to register extension\n"); - return ret; - } - - ret = register_pernet_subsys(&nf_nat_net_ops); - if (ret < 0) - goto cleanup_extend; - - /* Sew in builtin protocols. */ - spin_lock_bh(&nf_nat_lock); - for (i = 0; i < MAX_IP_NAT_PROTO; i++) - RCU_INIT_POINTER(nf_nat_protos[i], &nf_nat_unknown_protocol); - RCU_INIT_POINTER(nf_nat_protos[IPPROTO_TCP], &nf_nat_protocol_tcp); - RCU_INIT_POINTER(nf_nat_protos[IPPROTO_UDP], &nf_nat_protocol_udp); - RCU_INIT_POINTER(nf_nat_protos[IPPROTO_ICMP], &nf_nat_protocol_icmp); - spin_unlock_bh(&nf_nat_lock); - - /* Initialize fake conntrack so that NAT will skip it */ - nf_ct_untracked_status_or(IPS_NAT_DONE_MASK); - - l3proto = nf_ct_l3proto_find_get((u_int16_t)AF_INET); - - nf_ct_helper_expectfn_register(&follow_master_nat); - - 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); - return 0; - - cleanup_extend: - nf_ct_extend_unregister(&nat_extend); - return ret; -} - -static void __exit nf_nat_cleanup(void) -{ - unregister_pernet_subsys(&nf_nat_net_ops); - nf_ct_l3proto_put(l3proto); - 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); - synchronize_net(); -} - -MODULE_LICENSE("GPL"); -MODULE_ALIAS("nf-nat-ipv4"); - -module_init(nf_nat_init); -module_exit(nf_nat_cleanup); diff --git a/net/ipv4/netfilter/nf_nat_ftp.c b/net/ipv4/netfilter/nf_nat_ftp.c index 5589f3af4a8e..dd5e387fc03b 100644 --- a/net/ipv4/netfilter/nf_nat_ftp.c +++ b/net/ipv4/netfilter/nf_nat_ftp.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/net/ipv4/netfilter/nf_nat_h323.c b/net/ipv4/netfilter/nf_nat_h323.c index d2c228db38b5..9c3db10b22d3 100644 --- a/net/ipv4/netfilter/nf_nat_h323.c +++ b/net/ipv4/netfilter/nf_nat_h323.c @@ -15,7 +15,6 @@ #include #include -#include #include #include #include @@ -392,7 +391,7 @@ static int nat_h245(struct sk_buff *skb, struct nf_conn *ct, static void ip_nat_q931_expect(struct nf_conn *new, struct nf_conntrack_expect *this) { - struct nf_nat_ipv4_range range; + struct nf_nat_range range; if (this->tuple.src.u3.ip != 0) { /* Only accept calls from GK */ nf_nat_follow_master(new, this); @@ -404,14 +403,15 @@ static void ip_nat_q931_expect(struct nf_conn *new, /* Change src to where master sends to */ range.flags = NF_NAT_RANGE_MAP_IPS; - range.min_ip = range.max_ip = new->tuplehash[!this->dir].tuple.src.u3.ip; + range.min_addr = range.max_addr = + new->tuplehash[!this->dir].tuple.src.u3; nf_nat_setup_info(new, &range, NF_NAT_MANIP_SRC); /* For DST manip, map port here to where it's expected. */ range.flags = (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED); - range.min = range.max = this->saved_proto; - range.min_ip = range.max_ip = - new->master->tuplehash[!this->dir].tuple.src.u3.ip; + range.min_proto = range.max_proto = this->saved_proto; + range.min_addr = range.max_addr = + new->master->tuplehash[!this->dir].tuple.src.u3; nf_nat_setup_info(new, &range, NF_NAT_MANIP_DST); } @@ -490,20 +490,21 @@ static int nat_q931(struct sk_buff *skb, struct nf_conn *ct, static void ip_nat_callforwarding_expect(struct nf_conn *new, struct nf_conntrack_expect *this) { - struct nf_nat_ipv4_range range; + struct nf_nat_range range; /* This must be a fresh one. */ BUG_ON(new->status & IPS_NAT_DONE_MASK); /* Change src to where master sends to */ range.flags = NF_NAT_RANGE_MAP_IPS; - range.min_ip = range.max_ip = new->tuplehash[!this->dir].tuple.src.u3.ip; + range.min_addr = range.max_addr = + new->tuplehash[!this->dir].tuple.src.u3; nf_nat_setup_info(new, &range, NF_NAT_MANIP_SRC); /* For DST manip, map port here to where it's expected. */ range.flags = (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED); - range.min = range.max = this->saved_proto; - range.min_ip = range.max_ip = this->saved_ip; + range.min_proto = range.max_proto = this->saved_proto; + range.min_addr = range.max_addr = this->saved_addr; nf_nat_setup_info(new, &range, NF_NAT_MANIP_DST); } @@ -519,7 +520,7 @@ static int nat_callforwarding(struct sk_buff *skb, struct nf_conn *ct, u_int16_t nated_port; /* Set expectations for NAT */ - exp->saved_ip = exp->tuple.dst.u3.ip; + exp->saved_addr = exp->tuple.dst.u3; exp->tuple.dst.u3.ip = ct->tuplehash[!dir].tuple.dst.u3.ip; exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; exp->expectfn = ip_nat_callforwarding_expect; diff --git a/net/ipv4/netfilter/nf_nat_helper.c b/net/ipv4/netfilter/nf_nat_helper.c deleted file mode 100644 index 2fefec5e757c..000000000000 --- a/net/ipv4/netfilter/nf_nat_helper.c +++ /dev/null @@ -1,461 +0,0 @@ -/* ip_nat_helper.c - generic support functions for NAT helpers - * - * (C) 2000-2002 Harald Welte - * (C) 2003-2006 Netfilter Core Team - * - * 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 -#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); - -static DEFINE_SPINLOCK(nf_nat_seqofs_lock); - -/* 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(&nf_nat_seqofs_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(&nf_nat_seqofs_lock); - - pr_debug("adjust_tcp_sequence: Seq_offset after: "); - DUMP_OFFSET(this_way); -} - -/* Get the offset value, for conntrack */ -s16 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; - - if (!nat) - return 0; - - this_way = &nat->seq[dir]; - spin_lock_bh(&nf_nat_seqofs_lock); - offset = after(seq, this_way->correction_pos) - ? this_way->offset_after : this_way->offset_before; - spin_unlock_bh(&nf_nat_seqofs_lock); - - return offset; -} -EXPORT_SYMBOL_GPL(nf_nat_get_offset); - -/* Frobs data inside this packet, which is linear. */ -static void mangle_contents(struct sk_buff *skb, - unsigned int dataoff, - unsigned int match_offset, - unsigned int match_len, - const char *rep_buffer, - unsigned int rep_len) -{ - unsigned char *data; - - BUG_ON(skb_is_nonlinear(skb)); - data = skb_network_header(skb) + dataoff; - - /* move post-replacement */ - memmove(data + match_offset + rep_len, - data + match_offset + match_len, - skb->tail - (skb->network_header + dataoff + - match_offset + match_len)); - - /* insert data from buffer */ - memcpy(data + match_offset, rep_buffer, rep_len); - - /* update skb info */ - if (rep_len > match_len) { - pr_debug("nf_nat_mangle_packet: Extending packet by " - "%u from %u bytes\n", rep_len - match_len, skb->len); - skb_put(skb, rep_len - match_len); - } else { - pr_debug("nf_nat_mangle_packet: Shrinking packet from " - "%u from %u bytes\n", match_len - rep_len, skb->len); - __skb_trim(skb, skb->len + rep_len - match_len); - } - - /* fix IP hdr checksum information */ - ip_hdr(skb)->tot_len = htons(skb->len); - ip_send_check(ip_hdr(skb)); -} - -/* Unusual, but possible case. */ -static int enlarge_skb(struct sk_buff *skb, unsigned int extra) -{ - if (skb->len + extra > 65535) - return 0; - - if (pskb_expand_head(skb, 0, extra - skb_tailroom(skb), GFP_ATOMIC)) - return 0; - - return 1; -} - -void nf_nat_set_seq_adjust(struct nf_conn *ct, enum ip_conntrack_info ctinfo, - __be32 seq, s16 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); - -static void nf_nat_csum(struct sk_buff *skb, const struct iphdr *iph, void *data, - int datalen, __sum16 *check, int oldlen) -{ - struct rtable *rt = skb_rtable(skb); - - if (skb->ip_summed != CHECKSUM_PARTIAL) { - if (!(rt->rt_flags & RTCF_LOCAL) && - (!skb->dev || skb->dev->features & NETIF_F_V4_CSUM)) { - skb->ip_summed = CHECKSUM_PARTIAL; - skb->csum_start = skb_headroom(skb) + - skb_network_offset(skb) + - iph->ihl * 4; - skb->csum_offset = (void *)check - data; - *check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, - datalen, iph->protocol, 0); - } else { - *check = 0; - *check = csum_tcpudp_magic(iph->saddr, iph->daddr, - datalen, iph->protocol, - csum_partial(data, datalen, - 0)); - if (iph->protocol == IPPROTO_UDP && !*check) - *check = CSUM_MANGLED_0; - } - } else - inet_proto_csum_replace2(check, skb, - htons(oldlen), htons(datalen), 1); -} - -/* Generic function for mangling variable-length address changes inside - * NATed TCP connections (like the PORT XXX,XXX,XXX,XXX,XXX,XXX - * command in FTP). - * - * Takes care about all the nasty sequence number changes, checksumming, - * skb enlargement, ... - * - * */ -int __nf_nat_mangle_tcp_packet(struct sk_buff *skb, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - unsigned int protoff, - unsigned int match_offset, - unsigned int match_len, - const char *rep_buffer, - unsigned int rep_len, bool adjust) -{ - struct iphdr *iph; - struct tcphdr *tcph; - int oldlen, datalen; - - if (!skb_make_writable(skb, skb->len)) - return 0; - - if (rep_len > match_len && - rep_len - match_len > skb_tailroom(skb) && - !enlarge_skb(skb, rep_len - match_len)) - return 0; - - SKB_LINEAR_ASSERT(skb); - - iph = ip_hdr(skb); - tcph = (void *)iph + iph->ihl*4; - - oldlen = skb->len - iph->ihl*4; - mangle_contents(skb, iph->ihl*4 + tcph->doff*4, - match_offset, match_len, rep_buffer, rep_len); - - datalen = skb->len - iph->ihl*4; - nf_nat_csum(skb, iph, tcph, datalen, &tcph->check, oldlen); - - if (adjust && rep_len != match_len) - nf_nat_set_seq_adjust(ct, ctinfo, tcph->seq, - (int)rep_len - (int)match_len); - - return 1; -} -EXPORT_SYMBOL(__nf_nat_mangle_tcp_packet); - -/* Generic function for mangling variable-length address changes inside - * NATed UDP connections (like the CONNECT DATA XXXXX MESG XXXXX INDEX XXXXX - * command in the Amanda protocol) - * - * Takes care about all the nasty sequence number changes, checksumming, - * skb enlargement, ... - * - * XXX - This function could be merged with nf_nat_mangle_tcp_packet which - * should be fairly easy to do. - */ -int -nf_nat_mangle_udp_packet(struct sk_buff *skb, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - unsigned int protoff, - unsigned int match_offset, - unsigned int match_len, - const char *rep_buffer, - unsigned int rep_len) -{ - struct iphdr *iph; - struct udphdr *udph; - int datalen, oldlen; - - if (!skb_make_writable(skb, skb->len)) - return 0; - - if (rep_len > match_len && - rep_len - match_len > skb_tailroom(skb) && - !enlarge_skb(skb, rep_len - match_len)) - return 0; - - iph = ip_hdr(skb); - udph = (void *)iph + iph->ihl*4; - - oldlen = skb->len - iph->ihl*4; - mangle_contents(skb, iph->ihl*4 + sizeof(*udph), - match_offset, match_len, rep_buffer, rep_len); - - /* update the length of the UDP packet */ - datalen = skb->len - iph->ihl*4; - udph->len = htons(datalen); - - /* fix udp checksum if udp checksum was previously calculated */ - if (!udph->check && skb->ip_summed != CHECKSUM_PARTIAL) - return 1; - - nf_nat_csum(skb, iph, udph, datalen, &udph->check, oldlen); - - return 1; -} -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, - 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 = ip_hdrlen(skb) + sizeof(struct tcphdr); - optend = ip_hdrlen(skb) + 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; - s16 seqoff, ackoff; - struct nf_conn_nat *nat = nfct_nat(ct); - struct nf_nat_seq *this_way, *other_way; - - 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; - 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; - - return nf_nat_sack_adjust(skb, tcph, ct, ctinfo); -} - -/* 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, - struct nf_conntrack_expect *exp) -{ - struct nf_nat_ipv4_range range; - - /* This must be a fresh one. */ - BUG_ON(ct->status & IPS_NAT_DONE_MASK); - - /* Change src to where master sends to */ - range.flags = NF_NAT_RANGE_MAP_IPS; - range.min_ip = range.max_ip - = ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip; - nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC); - - /* For DST manip, map port here to where it's expected. */ - range.flags = (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED); - range.min = range.max = exp->saved_proto; - range.min_ip = range.max_ip - = ct->master->tuplehash[!exp->dir].tuple.src.u3.ip; - nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST); -} -EXPORT_SYMBOL(nf_nat_follow_master); diff --git a/net/ipv4/netfilter/nf_nat_irc.c b/net/ipv4/netfilter/nf_nat_irc.c index 5b0c20a1f08d..1ce37f89ec78 100644 --- a/net/ipv4/netfilter/nf_nat_irc.c +++ b/net/ipv4/netfilter/nf_nat_irc.c @@ -17,7 +17,6 @@ #include #include -#include #include #include #include diff --git a/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c b/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c new file mode 100644 index 000000000000..d8b2e14efddc --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c @@ -0,0 +1,281 @@ +/* + * (C) 1999-2001 Paul `Rusty' Russell + * (C) 2002-2006 Netfilter Core Team + * (C) 2011 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 +#include +#include +#include + +static const struct nf_nat_l3proto nf_nat_l3proto_ipv4; + +#ifdef CONFIG_XFRM +static void nf_nat_ipv4_decode_session(struct sk_buff *skb, + const struct nf_conn *ct, + enum ip_conntrack_dir dir, + unsigned long statusbit, + struct flowi *fl) +{ + const struct nf_conntrack_tuple *t = &ct->tuplehash[dir].tuple; + struct flowi4 *fl4 = &fl->u.ip4; + + if (ct->status & statusbit) { + fl4->daddr = t->dst.u3.ip; + if (t->dst.protonum == IPPROTO_TCP || + t->dst.protonum == IPPROTO_UDP || + t->dst.protonum == IPPROTO_UDPLITE || + t->dst.protonum == IPPROTO_DCCP || + t->dst.protonum == IPPROTO_SCTP) + fl4->fl4_dport = t->dst.u.all; + } + + statusbit ^= IPS_NAT_MASK; + + if (ct->status & statusbit) { + fl4->saddr = t->src.u3.ip; + if (t->dst.protonum == IPPROTO_TCP || + t->dst.protonum == IPPROTO_UDP || + t->dst.protonum == IPPROTO_UDPLITE || + t->dst.protonum == IPPROTO_DCCP || + t->dst.protonum == IPPROTO_SCTP) + fl4->fl4_sport = t->src.u.all; + } +} +#endif /* CONFIG_XFRM */ + +static bool nf_nat_ipv4_in_range(const struct nf_conntrack_tuple *t, + const struct nf_nat_range *range) +{ + return ntohl(t->src.u3.ip) >= ntohl(range->min_addr.ip) && + ntohl(t->src.u3.ip) <= ntohl(range->max_addr.ip); +} + +static u32 nf_nat_ipv4_secure_port(const struct nf_conntrack_tuple *t, + __be16 dport) +{ + return secure_ipv4_port_ephemeral(t->src.u3.ip, t->dst.u3.ip, dport); +} + +static bool nf_nat_ipv4_manip_pkt(struct sk_buff *skb, + unsigned int iphdroff, + const struct nf_nat_l4proto *l4proto, + const struct nf_conntrack_tuple *target, + enum nf_nat_manip_type maniptype) +{ + struct iphdr *iph; + unsigned int hdroff; + + if (!skb_make_writable(skb, iphdroff + sizeof(*iph))) + return false; + + iph = (void *)skb->data + iphdroff; + hdroff = iphdroff + iph->ihl * 4; + + if (!l4proto->manip_pkt(skb, &nf_nat_l3proto_ipv4, iphdroff, hdroff, + target, maniptype)) + return false; + iph = (void *)skb->data + iphdroff; + + if (maniptype == NF_NAT_MANIP_SRC) { + csum_replace4(&iph->check, iph->saddr, target->src.u3.ip); + iph->saddr = target->src.u3.ip; + } else { + csum_replace4(&iph->check, iph->daddr, target->dst.u3.ip); + iph->daddr = target->dst.u3.ip; + } + return true; +} + +static void nf_nat_ipv4_csum_update(struct sk_buff *skb, + unsigned int iphdroff, __sum16 *check, + const struct nf_conntrack_tuple *t, + enum nf_nat_manip_type maniptype) +{ + struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff); + __be32 oldip, newip; + + if (maniptype == NF_NAT_MANIP_SRC) { + oldip = iph->saddr; + newip = t->src.u3.ip; + } else { + oldip = iph->daddr; + newip = t->dst.u3.ip; + } + inet_proto_csum_replace4(check, skb, oldip, newip, 1); +} + +static void nf_nat_ipv4_csum_recalc(struct sk_buff *skb, + u8 proto, void *data, __sum16 *check, + int datalen, int oldlen) +{ + const struct iphdr *iph = ip_hdr(skb); + struct rtable *rt = skb_rtable(skb); + + if (skb->ip_summed != CHECKSUM_PARTIAL) { + if (!(rt->rt_flags & RTCF_LOCAL) && + (!skb->dev || skb->dev->features & NETIF_F_V4_CSUM)) { + skb->ip_summed = CHECKSUM_PARTIAL; + skb->csum_start = skb_headroom(skb) + + skb_network_offset(skb) + + ip_hdrlen(skb); + skb->csum_offset = (void *)check - data; + *check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, + datalen, proto, 0); + } else { + *check = 0; + *check = csum_tcpudp_magic(iph->saddr, iph->daddr, + datalen, proto, + csum_partial(data, datalen, + 0)); + if (proto == IPPROTO_UDP && !*check) + *check = CSUM_MANGLED_0; + } + } else + inet_proto_csum_replace2(check, skb, + htons(oldlen), htons(datalen), 1); +} + +static int nf_nat_ipv4_nlattr_to_range(struct nlattr *tb[], + struct nf_nat_range *range) +{ + if (tb[CTA_NAT_V4_MINIP]) { + range->min_addr.ip = nla_get_be32(tb[CTA_NAT_V4_MINIP]); + range->flags |= NF_NAT_RANGE_MAP_IPS; + } + + if (tb[CTA_NAT_V4_MAXIP]) + range->max_addr.ip = nla_get_be32(tb[CTA_NAT_V4_MAXIP]); + else + range->max_addr.ip = range->min_addr.ip; + + return 0; +} + +static const struct nf_nat_l3proto nf_nat_l3proto_ipv4 = { + .l3proto = NFPROTO_IPV4, + .in_range = nf_nat_ipv4_in_range, + .secure_port = nf_nat_ipv4_secure_port, + .manip_pkt = nf_nat_ipv4_manip_pkt, + .csum_update = nf_nat_ipv4_csum_update, + .csum_recalc = nf_nat_ipv4_csum_recalc, + .nlattr_to_range = nf_nat_ipv4_nlattr_to_range, +#ifdef CONFIG_XFRM + .decode_session = nf_nat_ipv4_decode_session, +#endif +}; + +int nf_nat_icmp_reply_translation(struct sk_buff *skb, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int hooknum) +{ + struct { + struct icmphdr icmp; + struct iphdr ip; + } *inside; + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + enum nf_nat_manip_type manip = HOOK2MANIP(hooknum); + unsigned int hdrlen = ip_hdrlen(skb); + const struct nf_nat_l4proto *l4proto; + struct nf_conntrack_tuple target; + unsigned long statusbit; + + NF_CT_ASSERT(ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED_REPLY); + + if (!skb_make_writable(skb, hdrlen + sizeof(*inside))) + return 0; + if (nf_ip_checksum(skb, hooknum, hdrlen, 0)) + return 0; + + inside = (void *)skb->data + hdrlen; + if (inside->icmp.type == ICMP_REDIRECT) { + if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK) + return 0; + if (ct->status & IPS_NAT_MASK) + return 0; + } + + if (manip == NF_NAT_MANIP_SRC) + statusbit = IPS_SRC_NAT; + else + statusbit = IPS_DST_NAT; + + /* Invert if this is reply direction */ + if (dir == IP_CT_DIR_REPLY) + statusbit ^= IPS_NAT_MASK; + + if (!(ct->status & statusbit)) + return 1; + + l4proto = __nf_nat_l4proto_find(NFPROTO_IPV4, inside->ip.protocol); + if (!nf_nat_ipv4_manip_pkt(skb, hdrlen + sizeof(inside->icmp), + l4proto, &ct->tuplehash[!dir].tuple, !manip)) + return 0; + + if (skb->ip_summed != CHECKSUM_PARTIAL) { + /* Reloading "inside" here since manip_pkt may reallocate */ + inside = (void *)skb->data + hdrlen; + inside->icmp.checksum = 0; + inside->icmp.checksum = + csum_fold(skb_checksum(skb, hdrlen, + skb->len - hdrlen, 0)); + } + + /* Change outer to look like the reply to an incoming packet */ + nf_ct_invert_tuplepr(&target, &ct->tuplehash[!dir].tuple); + l4proto = __nf_nat_l4proto_find(NFPROTO_IPV4, 0); + if (!nf_nat_ipv4_manip_pkt(skb, 0, l4proto, &target, manip)) + return 0; + + return 1; +} +EXPORT_SYMBOL_GPL(nf_nat_icmp_reply_translation); + +static int __init nf_nat_l3proto_ipv4_init(void) +{ + int err; + + err = nf_nat_l4proto_register(NFPROTO_IPV4, &nf_nat_l4proto_icmp); + if (err < 0) + goto err1; + err = nf_nat_l3proto_register(&nf_nat_l3proto_ipv4); + if (err < 0) + goto err2; + return err; + +err2: + nf_nat_l4proto_unregister(NFPROTO_IPV4, &nf_nat_l4proto_icmp); +err1: + return err; +} + +static void __exit nf_nat_l3proto_ipv4_exit(void) +{ + nf_nat_l3proto_unregister(&nf_nat_l3proto_ipv4); + nf_nat_l4proto_unregister(NFPROTO_IPV4, &nf_nat_l4proto_icmp); +} + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("nf-nat-" __stringify(AF_INET)); + +module_init(nf_nat_l3proto_ipv4_init); +module_exit(nf_nat_l3proto_ipv4_exit); diff --git a/net/ipv4/netfilter/nf_nat_pptp.c b/net/ipv4/netfilter/nf_nat_pptp.c index 31ef890d894b..a06d7d74817d 100644 --- a/net/ipv4/netfilter/nf_nat_pptp.c +++ b/net/ipv4/netfilter/nf_nat_pptp.c @@ -22,7 +22,6 @@ #include #include -#include #include #include #include @@ -47,7 +46,7 @@ static void pptp_nat_expected(struct nf_conn *ct, struct nf_conntrack_tuple t; const struct nf_ct_pptp_master *ct_pptp_info; const struct nf_nat_pptp *nat_pptp_info; - struct nf_nat_ipv4_range range; + struct nf_nat_range range; ct_pptp_info = nfct_help_data(master); nat_pptp_info = &nfct_nat(master)->help.nat_pptp_info; @@ -89,21 +88,21 @@ static void pptp_nat_expected(struct nf_conn *ct, /* Change src to where master sends to */ range.flags = NF_NAT_RANGE_MAP_IPS; - range.min_ip = range.max_ip - = ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip; + range.min_addr = range.max_addr + = ct->master->tuplehash[!exp->dir].tuple.dst.u3; if (exp->dir == IP_CT_DIR_ORIGINAL) { range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; - range.min = range.max = exp->saved_proto; + range.min_proto = range.max_proto = exp->saved_proto; } nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC); /* For DST manip, map port here to where it's expected. */ range.flags = NF_NAT_RANGE_MAP_IPS; - range.min_ip = range.max_ip - = ct->master->tuplehash[!exp->dir].tuple.src.u3.ip; + range.min_addr = range.max_addr + = ct->master->tuplehash[!exp->dir].tuple.src.u3; if (exp->dir == IP_CT_DIR_REPLY) { range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; - range.min = range.max = exp->saved_proto; + range.min_proto = range.max_proto = exp->saved_proto; } nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST); } diff --git a/net/ipv4/netfilter/nf_nat_proto_common.c b/net/ipv4/netfilter/nf_nat_proto_common.c deleted file mode 100644 index 9993bc93e102..000000000000 --- a/net/ipv4/netfilter/nf_nat_proto_common.c +++ /dev/null @@ -1,114 +0,0 @@ -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2006 Netfilter Core Team - * (C) 2008 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 - -bool nf_nat_proto_in_range(const struct nf_conntrack_tuple *tuple, - enum nf_nat_manip_type maniptype, - const union nf_conntrack_man_proto *min, - const union nf_conntrack_man_proto *max) -{ - __be16 port; - - if (maniptype == NF_NAT_MANIP_SRC) - port = tuple->src.u.all; - else - port = tuple->dst.u.all; - - return ntohs(port) >= ntohs(min->all) && - ntohs(port) <= ntohs(max->all); -} -EXPORT_SYMBOL_GPL(nf_nat_proto_in_range); - -void nf_nat_proto_unique_tuple(struct nf_conntrack_tuple *tuple, - const struct nf_nat_ipv4_range *range, - enum nf_nat_manip_type maniptype, - const struct nf_conn *ct, - u_int16_t *rover) -{ - unsigned int range_size, min, i; - __be16 *portptr; - u_int16_t off; - - if (maniptype == NF_NAT_MANIP_SRC) - portptr = &tuple->src.u.all; - else - portptr = &tuple->dst.u.all; - - /* If no range specified... */ - if (!(range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)) { - /* If it's dst rewrite, can't change port */ - if (maniptype == NF_NAT_MANIP_DST) - return; - - if (ntohs(*portptr) < 1024) { - /* Loose convention: >> 512 is credential passing */ - if (ntohs(*portptr) < 512) { - min = 1; - range_size = 511 - min + 1; - } else { - min = 600; - range_size = 1023 - min + 1; - } - } else { - min = 1024; - range_size = 65535 - 1024 + 1; - } - } else { - min = ntohs(range->min.all); - range_size = ntohs(range->max.all) - min + 1; - } - - if (range->flags & NF_NAT_RANGE_PROTO_RANDOM) - off = secure_ipv4_port_ephemeral(tuple->src.u3.ip, tuple->dst.u3.ip, - maniptype == NF_NAT_MANIP_SRC - ? tuple->dst.u.all - : tuple->src.u.all); - else - off = *rover; - - for (i = 0; ; ++off) { - *portptr = htons(min + off % range_size); - if (++i != range_size && nf_nat_used_tuple(tuple, ct)) - continue; - if (!(range->flags & NF_NAT_RANGE_PROTO_RANDOM)) - *rover = off; - return; - } - return; -} -EXPORT_SYMBOL_GPL(nf_nat_proto_unique_tuple); - -#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) -int nf_nat_proto_nlattr_to_range(struct nlattr *tb[], - struct nf_nat_ipv4_range *range) -{ - if (tb[CTA_PROTONAT_PORT_MIN]) { - range->min.all = nla_get_be16(tb[CTA_PROTONAT_PORT_MIN]); - range->max.all = range->min.tcp.port; - range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED; - } - if (tb[CTA_PROTONAT_PORT_MAX]) { - range->max.all = nla_get_be16(tb[CTA_PROTONAT_PORT_MAX]); - range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED; - } - return 0; -} -EXPORT_SYMBOL_GPL(nf_nat_proto_nlattr_to_range); -#endif diff --git a/net/ipv4/netfilter/nf_nat_proto_dccp.c b/net/ipv4/netfilter/nf_nat_proto_dccp.c deleted file mode 100644 index 3f67138d187c..000000000000 --- a/net/ipv4/netfilter/nf_nat_proto_dccp.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * DCCP NAT protocol helper - * - * Copyright (c) 2005, 2006. 2008 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 u_int16_t dccp_port_rover; - -static void -dccp_unique_tuple(struct nf_conntrack_tuple *tuple, - const struct nf_nat_ipv4_range *range, - enum nf_nat_manip_type maniptype, - const struct nf_conn *ct) -{ - nf_nat_proto_unique_tuple(tuple, range, maniptype, ct, - &dccp_port_rover); -} - -static bool -dccp_manip_pkt(struct sk_buff *skb, - unsigned int iphdroff, - const struct nf_conntrack_tuple *tuple, - enum nf_nat_manip_type maniptype) -{ - const struct iphdr *iph = (const void *)(skb->data + iphdroff); - struct dccp_hdr *hdr; - unsigned int hdroff = iphdroff + iph->ihl * 4; - __be32 oldip, newip; - __be16 *portptr, oldport, newport; - int hdrsize = 8; /* DCCP connection tracking guarantees this much */ - - if (skb->len >= hdroff + sizeof(struct dccp_hdr)) - hdrsize = sizeof(struct dccp_hdr); - - if (!skb_make_writable(skb, hdroff + hdrsize)) - return false; - - iph = (struct iphdr *)(skb->data + iphdroff); - hdr = (struct dccp_hdr *)(skb->data + hdroff); - - if (maniptype == NF_NAT_MANIP_SRC) { - oldip = iph->saddr; - newip = tuple->src.u3.ip; - newport = tuple->src.u.dccp.port; - portptr = &hdr->dccph_sport; - } else { - oldip = iph->daddr; - newip = tuple->dst.u3.ip; - newport = tuple->dst.u.dccp.port; - portptr = &hdr->dccph_dport; - } - - oldport = *portptr; - *portptr = newport; - - if (hdrsize < sizeof(*hdr)) - return true; - - inet_proto_csum_replace4(&hdr->dccph_checksum, skb, oldip, newip, 1); - inet_proto_csum_replace2(&hdr->dccph_checksum, skb, oldport, newport, - 0); - return true; -} - -static const struct nf_nat_protocol nf_nat_protocol_dccp = { - .protonum = IPPROTO_DCCP, - .manip_pkt = dccp_manip_pkt, - .in_range = nf_nat_proto_in_range, - .unique_tuple = dccp_unique_tuple, -#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) - .nlattr_to_range = nf_nat_proto_nlattr_to_range, -#endif -}; - -static int __init nf_nat_proto_dccp_init(void) -{ - return nf_nat_protocol_register(&nf_nat_protocol_dccp); -} - -static void __exit nf_nat_proto_dccp_fini(void) -{ - nf_nat_protocol_unregister(&nf_nat_protocol_dccp); -} - -module_init(nf_nat_proto_dccp_init); -module_exit(nf_nat_proto_dccp_fini); - -MODULE_AUTHOR("Patrick McHardy "); -MODULE_DESCRIPTION("DCCP NAT protocol helper"); -MODULE_LICENSE("GPL"); diff --git a/net/ipv4/netfilter/nf_nat_proto_gre.c b/net/ipv4/netfilter/nf_nat_proto_gre.c index 46ba0b9ab985..ea44f02563b5 100644 --- a/net/ipv4/netfilter/nf_nat_proto_gre.c +++ b/net/ipv4/netfilter/nf_nat_proto_gre.c @@ -28,8 +28,7 @@ #include #include -#include -#include +#include #include MODULE_LICENSE("GPL"); @@ -38,8 +37,9 @@ MODULE_DESCRIPTION("Netfilter NAT protocol helper module for GRE"); /* generate unique tuple ... */ static void -gre_unique_tuple(struct nf_conntrack_tuple *tuple, - const struct nf_nat_ipv4_range *range, +gre_unique_tuple(const struct nf_nat_l3proto *l3proto, + struct nf_conntrack_tuple *tuple, + const struct nf_nat_range *range, enum nf_nat_manip_type maniptype, const struct nf_conn *ct) { @@ -62,8 +62,8 @@ gre_unique_tuple(struct nf_conntrack_tuple *tuple, min = 1; range_size = 0xffff; } else { - min = ntohs(range->min.gre.key); - range_size = ntohs(range->max.gre.key) - min + 1; + min = ntohs(range->min_proto.gre.key); + range_size = ntohs(range->max_proto.gre.key) - min + 1; } pr_debug("min = %u, range_size = %u\n", min, range_size); @@ -80,14 +80,14 @@ gre_unique_tuple(struct nf_conntrack_tuple *tuple, /* manipulate a GRE packet according to maniptype */ static bool -gre_manip_pkt(struct sk_buff *skb, unsigned int iphdroff, +gre_manip_pkt(struct sk_buff *skb, + const struct nf_nat_l3proto *l3proto, + unsigned int iphdroff, unsigned int hdroff, const struct nf_conntrack_tuple *tuple, enum nf_nat_manip_type maniptype) { const struct gre_hdr *greh; struct gre_hdr_pptp *pgreh; - const struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff); - unsigned int hdroff = iphdroff + iph->ihl * 4; /* pgreh includes two optional 32bit fields which are not required * to be there. That's where the magic '8' comes from */ @@ -117,24 +117,24 @@ gre_manip_pkt(struct sk_buff *skb, unsigned int iphdroff, return true; } -static const struct nf_nat_protocol gre = { - .protonum = IPPROTO_GRE, +static const struct nf_nat_l4proto gre = { + .l4proto = IPPROTO_GRE, .manip_pkt = gre_manip_pkt, - .in_range = nf_nat_proto_in_range, + .in_range = nf_nat_l4proto_in_range, .unique_tuple = gre_unique_tuple, #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) - .nlattr_to_range = nf_nat_proto_nlattr_to_range, + .nlattr_to_range = nf_nat_l4proto_nlattr_to_range, #endif }; static int __init nf_nat_proto_gre_init(void) { - return nf_nat_protocol_register(&gre); + return nf_nat_l4proto_register(NFPROTO_IPV4, &gre); } static void __exit nf_nat_proto_gre_fini(void) { - nf_nat_protocol_unregister(&gre); + nf_nat_l4proto_unregister(NFPROTO_IPV4, &gre); } module_init(nf_nat_proto_gre_init); diff --git a/net/ipv4/netfilter/nf_nat_proto_icmp.c b/net/ipv4/netfilter/nf_nat_proto_icmp.c index b35172851bae..eb303471bcf6 100644 --- a/net/ipv4/netfilter/nf_nat_proto_icmp.c +++ b/net/ipv4/netfilter/nf_nat_proto_icmp.c @@ -15,8 +15,7 @@ #include #include #include -#include -#include +#include static bool icmp_in_range(const struct nf_conntrack_tuple *tuple, @@ -29,8 +28,9 @@ icmp_in_range(const struct nf_conntrack_tuple *tuple, } static void -icmp_unique_tuple(struct nf_conntrack_tuple *tuple, - const struct nf_nat_ipv4_range *range, +icmp_unique_tuple(const struct nf_nat_l3proto *l3proto, + struct nf_conntrack_tuple *tuple, + const struct nf_nat_range *range, enum nf_nat_manip_type maniptype, const struct nf_conn *ct) { @@ -38,13 +38,14 @@ icmp_unique_tuple(struct nf_conntrack_tuple *tuple, unsigned int range_size; unsigned int i; - range_size = ntohs(range->max.icmp.id) - ntohs(range->min.icmp.id) + 1; + range_size = ntohs(range->max_proto.icmp.id) - + ntohs(range->min_proto.icmp.id) + 1; /* If no range specified... */ if (!(range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)) range_size = 0xFFFF; for (i = 0; ; ++id) { - tuple->src.u.icmp.id = htons(ntohs(range->min.icmp.id) + + tuple->src.u.icmp.id = htons(ntohs(range->min_proto.icmp.id) + (id % range_size)); if (++i == range_size || !nf_nat_used_tuple(tuple, ct)) return; @@ -54,13 +55,12 @@ icmp_unique_tuple(struct nf_conntrack_tuple *tuple, static bool icmp_manip_pkt(struct sk_buff *skb, - unsigned int iphdroff, + const struct nf_nat_l3proto *l3proto, + unsigned int iphdroff, unsigned int hdroff, const struct nf_conntrack_tuple *tuple, enum nf_nat_manip_type maniptype) { - const struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff); struct icmphdr *hdr; - unsigned int hdroff = iphdroff + iph->ihl*4; if (!skb_make_writable(skb, hdroff + sizeof(*hdr))) return false; @@ -72,12 +72,12 @@ icmp_manip_pkt(struct sk_buff *skb, return true; } -const struct nf_nat_protocol nf_nat_protocol_icmp = { - .protonum = IPPROTO_ICMP, +const struct nf_nat_l4proto nf_nat_l4proto_icmp = { + .l4proto = IPPROTO_ICMP, .manip_pkt = icmp_manip_pkt, .in_range = icmp_in_range, .unique_tuple = icmp_unique_tuple, #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) - .nlattr_to_range = nf_nat_proto_nlattr_to_range, + .nlattr_to_range = nf_nat_l4proto_nlattr_to_range, #endif }; diff --git a/net/ipv4/netfilter/nf_nat_proto_sctp.c b/net/ipv4/netfilter/nf_nat_proto_sctp.c deleted file mode 100644 index 3cce9b6c1c29..000000000000 --- a/net/ipv4/netfilter/nf_nat_proto_sctp.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2008 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 - -static u_int16_t nf_sctp_port_rover; - -static void -sctp_unique_tuple(struct nf_conntrack_tuple *tuple, - const struct nf_nat_ipv4_range *range, - enum nf_nat_manip_type maniptype, - const struct nf_conn *ct) -{ - nf_nat_proto_unique_tuple(tuple, range, maniptype, ct, - &nf_sctp_port_rover); -} - -static bool -sctp_manip_pkt(struct sk_buff *skb, - unsigned int iphdroff, - const struct nf_conntrack_tuple *tuple, - enum nf_nat_manip_type maniptype) -{ - const struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff); - struct sk_buff *frag; - sctp_sctphdr_t *hdr; - unsigned int hdroff = iphdroff + iph->ihl*4; - __be32 oldip, newip; - __be32 crc32; - - if (!skb_make_writable(skb, hdroff + sizeof(*hdr))) - return false; - - iph = (struct iphdr *)(skb->data + iphdroff); - hdr = (struct sctphdr *)(skb->data + hdroff); - - if (maniptype == NF_NAT_MANIP_SRC) { - /* Get rid of src ip and src pt */ - oldip = iph->saddr; - newip = tuple->src.u3.ip; - hdr->source = tuple->src.u.sctp.port; - } else { - /* Get rid of dst ip and dst pt */ - oldip = iph->daddr; - newip = tuple->dst.u3.ip; - 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); - crc32 = sctp_end_cksum(crc32); - hdr->checksum = crc32; - - return true; -} - -static const struct nf_nat_protocol nf_nat_protocol_sctp = { - .protonum = IPPROTO_SCTP, - .manip_pkt = sctp_manip_pkt, - .in_range = nf_nat_proto_in_range, - .unique_tuple = sctp_unique_tuple, -#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) - .nlattr_to_range = nf_nat_proto_nlattr_to_range, -#endif -}; - -static int __init nf_nat_proto_sctp_init(void) -{ - return nf_nat_protocol_register(&nf_nat_protocol_sctp); -} - -static void __exit nf_nat_proto_sctp_exit(void) -{ - nf_nat_protocol_unregister(&nf_nat_protocol_sctp); -} - -module_init(nf_nat_proto_sctp_init); -module_exit(nf_nat_proto_sctp_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("SCTP NAT protocol helper"); -MODULE_AUTHOR("Patrick McHardy "); diff --git a/net/ipv4/netfilter/nf_nat_proto_tcp.c b/net/ipv4/netfilter/nf_nat_proto_tcp.c deleted file mode 100644 index 9fb4b4e72bbf..000000000000 --- a/net/ipv4/netfilter/nf_nat_proto_tcp.c +++ /dev/null @@ -1,91 +0,0 @@ -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2006 Netfilter Core Team - * - * 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 - -static u_int16_t tcp_port_rover; - -static void -tcp_unique_tuple(struct nf_conntrack_tuple *tuple, - const struct nf_nat_ipv4_range *range, - enum nf_nat_manip_type maniptype, - const struct nf_conn *ct) -{ - nf_nat_proto_unique_tuple(tuple, range, maniptype, ct, &tcp_port_rover); -} - -static bool -tcp_manip_pkt(struct sk_buff *skb, - unsigned int iphdroff, - const struct nf_conntrack_tuple *tuple, - enum nf_nat_manip_type maniptype) -{ - const struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff); - struct tcphdr *hdr; - unsigned int hdroff = iphdroff + iph->ihl*4; - __be32 oldip, newip; - __be16 *portptr, newport, oldport; - int hdrsize = 8; /* TCP connection tracking guarantees this much */ - - /* this could be a inner header returned in icmp packet; in such - cases we cannot update the checksum field since it is outside of - the 8 bytes of transport layer headers we are guaranteed */ - if (skb->len >= hdroff + sizeof(struct tcphdr)) - hdrsize = sizeof(struct tcphdr); - - if (!skb_make_writable(skb, hdroff + hdrsize)) - return false; - - iph = (struct iphdr *)(skb->data + iphdroff); - hdr = (struct tcphdr *)(skb->data + hdroff); - - if (maniptype == NF_NAT_MANIP_SRC) { - /* Get rid of src ip and src pt */ - oldip = iph->saddr; - newip = tuple->src.u3.ip; - newport = tuple->src.u.tcp.port; - portptr = &hdr->source; - } else { - /* Get rid of dst ip and dst pt */ - oldip = iph->daddr; - newip = tuple->dst.u3.ip; - newport = tuple->dst.u.tcp.port; - portptr = &hdr->dest; - } - - oldport = *portptr; - *portptr = newport; - - if (hdrsize < sizeof(*hdr)) - return true; - - inet_proto_csum_replace4(&hdr->check, skb, oldip, newip, 1); - inet_proto_csum_replace2(&hdr->check, skb, oldport, newport, 0); - return true; -} - -const struct nf_nat_protocol nf_nat_protocol_tcp = { - .protonum = IPPROTO_TCP, - .manip_pkt = tcp_manip_pkt, - .in_range = nf_nat_proto_in_range, - .unique_tuple = tcp_unique_tuple, -#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) - .nlattr_to_range = nf_nat_proto_nlattr_to_range, -#endif -}; diff --git a/net/ipv4/netfilter/nf_nat_proto_udp.c b/net/ipv4/netfilter/nf_nat_proto_udp.c deleted file mode 100644 index 9883336e628f..000000000000 --- a/net/ipv4/netfilter/nf_nat_proto_udp.c +++ /dev/null @@ -1,82 +0,0 @@ -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2006 Netfilter Core Team - * - * 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 - -static u_int16_t udp_port_rover; - -static void -udp_unique_tuple(struct nf_conntrack_tuple *tuple, - const struct nf_nat_ipv4_range *range, - enum nf_nat_manip_type maniptype, - const struct nf_conn *ct) -{ - nf_nat_proto_unique_tuple(tuple, range, maniptype, ct, &udp_port_rover); -} - -static bool -udp_manip_pkt(struct sk_buff *skb, - unsigned int iphdroff, - const struct nf_conntrack_tuple *tuple, - enum nf_nat_manip_type maniptype) -{ - const struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff); - struct udphdr *hdr; - unsigned int hdroff = iphdroff + iph->ihl*4; - __be32 oldip, newip; - __be16 *portptr, newport; - - if (!skb_make_writable(skb, hdroff + sizeof(*hdr))) - return false; - - iph = (struct iphdr *)(skb->data + iphdroff); - hdr = (struct udphdr *)(skb->data + hdroff); - - if (maniptype == NF_NAT_MANIP_SRC) { - /* Get rid of src ip and src pt */ - oldip = iph->saddr; - newip = tuple->src.u3.ip; - newport = tuple->src.u.udp.port; - portptr = &hdr->source; - } else { - /* Get rid of dst ip and dst pt */ - oldip = iph->daddr; - newip = tuple->dst.u3.ip; - newport = tuple->dst.u.udp.port; - portptr = &hdr->dest; - } - if (hdr->check || skb->ip_summed == CHECKSUM_PARTIAL) { - inet_proto_csum_replace4(&hdr->check, skb, oldip, newip, 1); - inet_proto_csum_replace2(&hdr->check, skb, *portptr, newport, - 0); - if (!hdr->check) - hdr->check = CSUM_MANGLED_0; - } - *portptr = newport; - return true; -} - -const struct nf_nat_protocol nf_nat_protocol_udp = { - .protonum = IPPROTO_UDP, - .manip_pkt = udp_manip_pkt, - .in_range = nf_nat_proto_in_range, - .unique_tuple = udp_unique_tuple, -#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) - .nlattr_to_range = nf_nat_proto_nlattr_to_range, -#endif -}; diff --git a/net/ipv4/netfilter/nf_nat_proto_udplite.c b/net/ipv4/netfilter/nf_nat_proto_udplite.c deleted file mode 100644 index d24d10a7beb2..000000000000 --- a/net/ipv4/netfilter/nf_nat_proto_udplite.c +++ /dev/null @@ -1,98 +0,0 @@ -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2006 Netfilter Core Team - * (C) 2008 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 - -static u_int16_t udplite_port_rover; - -static void -udplite_unique_tuple(struct nf_conntrack_tuple *tuple, - const struct nf_nat_ipv4_range *range, - enum nf_nat_manip_type maniptype, - const struct nf_conn *ct) -{ - nf_nat_proto_unique_tuple(tuple, range, maniptype, ct, - &udplite_port_rover); -} - -static bool -udplite_manip_pkt(struct sk_buff *skb, - unsigned int iphdroff, - const struct nf_conntrack_tuple *tuple, - enum nf_nat_manip_type maniptype) -{ - const struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff); - struct udphdr *hdr; - unsigned int hdroff = iphdroff + iph->ihl*4; - __be32 oldip, newip; - __be16 *portptr, newport; - - if (!skb_make_writable(skb, hdroff + sizeof(*hdr))) - return false; - - iph = (struct iphdr *)(skb->data + iphdroff); - hdr = (struct udphdr *)(skb->data + hdroff); - - if (maniptype == NF_NAT_MANIP_SRC) { - /* Get rid of src ip and src pt */ - oldip = iph->saddr; - newip = tuple->src.u3.ip; - newport = tuple->src.u.udp.port; - portptr = &hdr->source; - } else { - /* Get rid of dst ip and dst pt */ - oldip = iph->daddr; - newip = tuple->dst.u3.ip; - newport = tuple->dst.u.udp.port; - portptr = &hdr->dest; - } - - inet_proto_csum_replace4(&hdr->check, skb, oldip, newip, 1); - inet_proto_csum_replace2(&hdr->check, skb, *portptr, newport, 0); - if (!hdr->check) - hdr->check = CSUM_MANGLED_0; - - *portptr = newport; - return true; -} - -static const struct nf_nat_protocol nf_nat_protocol_udplite = { - .protonum = IPPROTO_UDPLITE, - .manip_pkt = udplite_manip_pkt, - .in_range = nf_nat_proto_in_range, - .unique_tuple = udplite_unique_tuple, -#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) - .nlattr_to_range = nf_nat_proto_nlattr_to_range, -#endif -}; - -static int __init nf_nat_proto_udplite_init(void) -{ - return nf_nat_protocol_register(&nf_nat_protocol_udplite); -} - -static void __exit nf_nat_proto_udplite_fini(void) -{ - nf_nat_protocol_unregister(&nf_nat_protocol_udplite); -} - -module_init(nf_nat_proto_udplite_init); -module_exit(nf_nat_proto_udplite_fini); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("UDP-Lite NAT protocol helper"); -MODULE_AUTHOR("Patrick McHardy "); diff --git a/net/ipv4/netfilter/nf_nat_proto_unknown.c b/net/ipv4/netfilter/nf_nat_proto_unknown.c deleted file mode 100644 index e0afe8112b1c..000000000000 --- a/net/ipv4/netfilter/nf_nat_proto_unknown.c +++ /dev/null @@ -1,52 +0,0 @@ -/* The "unknown" protocol. This is what is used for protocols we - * don't understand. It's returned by ip_ct_find_proto(). - */ - -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2006 Netfilter Core Team - * - * 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 bool unknown_in_range(const struct nf_conntrack_tuple *tuple, - enum nf_nat_manip_type manip_type, - const union nf_conntrack_man_proto *min, - const union nf_conntrack_man_proto *max) -{ - return true; -} - -static void unknown_unique_tuple(struct nf_conntrack_tuple *tuple, - const struct nf_nat_ipv4_range *range, - enum nf_nat_manip_type maniptype, - const struct nf_conn *ct) -{ - /* Sorry: we can't help you; if it's not unique, we can't frob - anything. */ - return; -} - -static bool -unknown_manip_pkt(struct sk_buff *skb, - unsigned int iphdroff, - const struct nf_conntrack_tuple *tuple, - enum nf_nat_manip_type maniptype) -{ - return true; -} - -const struct nf_nat_protocol nf_nat_unknown_protocol = { - .manip_pkt = unknown_manip_pkt, - .in_range = unknown_in_range, - .unique_tuple = unknown_unique_tuple, -}; diff --git a/net/ipv4/netfilter/nf_nat_rule.c b/net/ipv4/netfilter/nf_nat_rule.c deleted file mode 100644 index d2a9dc314e0e..000000000000 --- a/net/ipv4/netfilter/nf_nat_rule.c +++ /dev/null @@ -1,214 +0,0 @@ -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2006 Netfilter Core Team - * - * 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. - */ - -/* Everything about the rules for NAT. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define NAT_VALID_HOOKS ((1 << NF_INET_PRE_ROUTING) | \ - (1 << NF_INET_POST_ROUTING) | \ - (1 << NF_INET_LOCAL_OUT) | \ - (1 << NF_INET_LOCAL_IN)) - -static const struct xt_table nat_table = { - .name = "nat", - .valid_hooks = NAT_VALID_HOOKS, - .me = THIS_MODULE, - .af = NFPROTO_IPV4, -}; - -/* Source NAT */ -static unsigned int -ipt_snat_target(struct sk_buff *skb, const struct xt_action_param *par) -{ - struct nf_conn *ct; - enum ip_conntrack_info ctinfo; - const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo; - - NF_CT_ASSERT(par->hooknum == NF_INET_POST_ROUTING || - par->hooknum == NF_INET_LOCAL_IN); - - ct = nf_ct_get(skb, &ctinfo); - - /* Connection must be valid and new. */ - NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || - ctinfo == IP_CT_RELATED_REPLY)); - NF_CT_ASSERT(par->out != NULL); - - return nf_nat_setup_info(ct, &mr->range[0], NF_NAT_MANIP_SRC); -} - -static unsigned int -ipt_dnat_target(struct sk_buff *skb, const struct xt_action_param *par) -{ - struct nf_conn *ct; - enum ip_conntrack_info ctinfo; - const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo; - - NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING || - par->hooknum == NF_INET_LOCAL_OUT); - - ct = nf_ct_get(skb, &ctinfo); - - /* Connection must be valid and new. */ - NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); - - return nf_nat_setup_info(ct, &mr->range[0], NF_NAT_MANIP_DST); -} - -static int ipt_snat_checkentry(const struct xt_tgchk_param *par) -{ - const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo; - - /* Must be a valid range */ - if (mr->rangesize != 1) { - pr_info("SNAT: multiple ranges no longer supported\n"); - return -EINVAL; - } - return 0; -} - -static int ipt_dnat_checkentry(const struct xt_tgchk_param *par) -{ - const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo; - - /* Must be a valid range */ - if (mr->rangesize != 1) { - pr_info("DNAT: multiple ranges no longer supported\n"); - return -EINVAL; - } - return 0; -} - -static unsigned int -alloc_null_binding(struct nf_conn *ct, unsigned int hooknum) -{ - /* Force range to this IP; let proto decide mapping for - per-proto parts (hence not NF_NAT_RANGE_PROTO_SPECIFIED). - */ - struct nf_nat_ipv4_range range; - - range.flags = 0; - pr_debug("Allocating NULL binding for %p (%pI4)\n", ct, - HOOK2MANIP(hooknum) == NF_NAT_MANIP_SRC ? - &ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip : - &ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip); - - return nf_nat_setup_info(ct, &range, HOOK2MANIP(hooknum)); -} - -int nf_nat_rule_find(struct sk_buff *skb, - unsigned int hooknum, - const struct net_device *in, - const struct net_device *out, - struct nf_conn *ct) -{ - struct net *net = nf_ct_net(ct); - int ret; - - ret = ipt_do_table(skb, hooknum, in, out, net->ipv4.nat_table); - - if (ret == NF_ACCEPT) { - if (!nf_nat_initialized(ct, HOOK2MANIP(hooknum))) - /* NUL mapping */ - ret = alloc_null_binding(ct, hooknum); - } - return ret; -} - -static struct xt_target ipt_snat_reg __read_mostly = { - .name = "SNAT", - .target = ipt_snat_target, - .targetsize = sizeof(struct nf_nat_ipv4_multi_range_compat), - .table = "nat", - .hooks = (1 << NF_INET_POST_ROUTING) | (1 << NF_INET_LOCAL_IN), - .checkentry = ipt_snat_checkentry, - .family = AF_INET, -}; - -static struct xt_target ipt_dnat_reg __read_mostly = { - .name = "DNAT", - .target = ipt_dnat_target, - .targetsize = sizeof(struct nf_nat_ipv4_multi_range_compat), - .table = "nat", - .hooks = (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_OUT), - .checkentry = ipt_dnat_checkentry, - .family = AF_INET, -}; - -static int __net_init nf_nat_rule_net_init(struct net *net) -{ - struct ipt_replace *repl; - - repl = ipt_alloc_initial_table(&nat_table); - if (repl == NULL) - return -ENOMEM; - net->ipv4.nat_table = ipt_register_table(net, &nat_table, repl); - kfree(repl); - if (IS_ERR(net->ipv4.nat_table)) - return PTR_ERR(net->ipv4.nat_table); - return 0; -} - -static void __net_exit nf_nat_rule_net_exit(struct net *net) -{ - ipt_unregister_table(net, net->ipv4.nat_table); -} - -static struct pernet_operations nf_nat_rule_net_ops = { - .init = nf_nat_rule_net_init, - .exit = nf_nat_rule_net_exit, -}; - -int __init nf_nat_rule_init(void) -{ - int ret; - - ret = register_pernet_subsys(&nf_nat_rule_net_ops); - if (ret != 0) - goto out; - ret = xt_register_target(&ipt_snat_reg); - if (ret != 0) - goto unregister_table; - - ret = xt_register_target(&ipt_dnat_reg); - if (ret != 0) - goto unregister_snat; - - return ret; - - unregister_snat: - xt_unregister_target(&ipt_snat_reg); - unregister_table: - unregister_pernet_subsys(&nf_nat_rule_net_ops); - out: - return ret; -} - -void nf_nat_rule_cleanup(void) -{ - xt_unregister_target(&ipt_dnat_reg); - xt_unregister_target(&ipt_snat_reg); - unregister_pernet_subsys(&nf_nat_rule_net_ops); -} diff --git a/net/ipv4/netfilter/nf_nat_sip.c b/net/ipv4/netfilter/nf_nat_sip.c index df626af8413c..47a47186a791 100644 --- a/net/ipv4/netfilter/nf_nat_sip.c +++ b/net/ipv4/netfilter/nf_nat_sip.c @@ -19,7 +19,6 @@ #include #include -#include #include #include #include @@ -255,15 +254,15 @@ static void ip_nat_sip_seq_adjust(struct sk_buff *skb, s16 off) static void ip_nat_sip_expected(struct nf_conn *ct, struct nf_conntrack_expect *exp) { - struct nf_nat_ipv4_range range; + struct nf_nat_range range; /* This must be a fresh one. */ BUG_ON(ct->status & IPS_NAT_DONE_MASK); /* For DST manip, map port here to where it's expected. */ range.flags = (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED); - range.min = range.max = exp->saved_proto; - range.min_ip = range.max_ip = exp->saved_ip; + range.min_proto = range.max_proto = exp->saved_proto; + range.min_addr = range.max_addr = exp->saved_addr; nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST); /* Change src to where master sends to, but only if the connection @@ -271,8 +270,8 @@ static void ip_nat_sip_expected(struct nf_conn *ct, if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip == ct->master->tuplehash[exp->dir].tuple.src.u3.ip) { range.flags = NF_NAT_RANGE_MAP_IPS; - range.min_ip = range.max_ip - = ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip; + range.min_addr = range.max_addr + = ct->master->tuplehash[!exp->dir].tuple.dst.u3; nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC); } } @@ -307,7 +306,7 @@ static unsigned int ip_nat_sip_expect(struct sk_buff *skb, unsigned int protoff, else port = ntohs(exp->tuple.dst.u.udp.port); - exp->saved_ip = exp->tuple.dst.u3.ip; + exp->saved_addr = exp->tuple.dst.u3; exp->tuple.dst.u3.ip = newip; exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port; exp->dir = !dir; @@ -329,7 +328,7 @@ static unsigned int ip_nat_sip_expect(struct sk_buff *skb, unsigned int protoff, if (port == 0) return NF_DROP; - if (exp->tuple.dst.u3.ip != exp->saved_ip || + if (exp->tuple.dst.u3.ip != exp->saved_addr.ip || exp->tuple.dst.u.udp.port != exp->saved_proto.udp.port) { buflen = sprintf(buffer, "%pI4:%u", &newip, port); if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, @@ -485,13 +484,13 @@ static unsigned int ip_nat_sdp_media(struct sk_buff *skb, unsigned int protoff, else rtp_addr->ip = ct->tuplehash[!dir].tuple.dst.u3.ip; - rtp_exp->saved_ip = rtp_exp->tuple.dst.u3.ip; + rtp_exp->saved_addr = rtp_exp->tuple.dst.u3; rtp_exp->tuple.dst.u3.ip = rtp_addr->ip; rtp_exp->saved_proto.udp.port = rtp_exp->tuple.dst.u.udp.port; rtp_exp->dir = !dir; rtp_exp->expectfn = ip_nat_sip_expected; - rtcp_exp->saved_ip = rtcp_exp->tuple.dst.u3.ip; + rtcp_exp->saved_addr = rtcp_exp->tuple.dst.u3; rtcp_exp->tuple.dst.u3.ip = rtp_addr->ip; rtcp_exp->saved_proto.udp.port = rtcp_exp->tuple.dst.u.udp.port; rtcp_exp->dir = !dir; diff --git a/net/ipv4/netfilter/nf_nat_standalone.c b/net/ipv4/netfilter/nf_nat_standalone.c deleted file mode 100644 index 3828a4229822..000000000000 --- a/net/ipv4/netfilter/nf_nat_standalone.c +++ /dev/null @@ -1,326 +0,0 @@ -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2006 Netfilter Core Team - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_XFRM -static void nat_decode_session(struct sk_buff *skb, struct flowi *fl) -{ - struct flowi4 *fl4 = &fl->u.ip4; - const struct nf_conn *ct; - const struct nf_conntrack_tuple *t; - enum ip_conntrack_info ctinfo; - enum ip_conntrack_dir dir; - unsigned long statusbit; - - ct = nf_ct_get(skb, &ctinfo); - if (ct == NULL) - return; - dir = CTINFO2DIR(ctinfo); - t = &ct->tuplehash[dir].tuple; - - if (dir == IP_CT_DIR_ORIGINAL) - statusbit = IPS_DST_NAT; - else - statusbit = IPS_SRC_NAT; - - if (ct->status & statusbit) { - fl4->daddr = t->dst.u3.ip; - if (t->dst.protonum == IPPROTO_TCP || - t->dst.protonum == IPPROTO_UDP || - t->dst.protonum == IPPROTO_UDPLITE || - t->dst.protonum == IPPROTO_DCCP || - t->dst.protonum == IPPROTO_SCTP) - fl4->fl4_dport = t->dst.u.tcp.port; - } - - statusbit ^= IPS_NAT_MASK; - - if (ct->status & statusbit) { - fl4->saddr = t->src.u3.ip; - if (t->dst.protonum == IPPROTO_TCP || - t->dst.protonum == IPPROTO_UDP || - t->dst.protonum == IPPROTO_UDPLITE || - t->dst.protonum == IPPROTO_DCCP || - t->dst.protonum == IPPROTO_SCTP) - fl4->fl4_sport = t->src.u.tcp.port; - } -} -#endif - -static unsigned int -nf_nat_fn(unsigned int hooknum, - struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ - struct nf_conn *ct; - enum ip_conntrack_info ctinfo; - struct nf_conn_nat *nat; - /* maniptype == SRC for postrouting. */ - enum nf_nat_manip_type maniptype = HOOK2MANIP(hooknum); - - /* We never see fragments: conntrack defrags on pre-routing - and local-out, and nf_nat_out protects post-routing. */ - NF_CT_ASSERT(!ip_is_fragment(ip_hdr(skb))); - - ct = nf_ct_get(skb, &ctinfo); - /* Can't track? It's not due to stress, or conntrack would - have dropped it. Hence it's the user's responsibilty to - packet filter it out, or implement conntrack/NAT for that - protocol. 8) --RR */ - if (!ct) - return NF_ACCEPT; - - /* Don't try to NAT if this packet is not conntracked */ - if (nf_ct_is_untracked(ct)) - return NF_ACCEPT; - - nat = nfct_nat(ct); - if (!nat) { - /* NAT module was loaded late. */ - if (nf_ct_is_confirmed(ct)) - return NF_ACCEPT; - nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); - if (nat == NULL) { - pr_debug("failed to add NAT extension\n"); - return NF_ACCEPT; - } - } - - switch (ctinfo) { - case IP_CT_RELATED: - case IP_CT_RELATED_REPLY: - if (ip_hdr(skb)->protocol == IPPROTO_ICMP) { - if (!nf_nat_icmp_reply_translation(ct, ctinfo, - hooknum, skb)) - return NF_DROP; - else - return NF_ACCEPT; - } - /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */ - case IP_CT_NEW: - - /* Seen it before? This can happen for loopback, retrans, - or local packets.. */ - if (!nf_nat_initialized(ct, maniptype)) { - unsigned int ret; - - ret = nf_nat_rule_find(skb, hooknum, in, out, ct); - if (ret != NF_ACCEPT) - return ret; - } else - pr_debug("Already setup manip %s for ct %p\n", - maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST", - ct); - break; - - default: - /* ESTABLISHED */ - NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED || - ctinfo == IP_CT_ESTABLISHED_REPLY); - } - - return nf_nat_packet(ct, ctinfo, hooknum, skb); -} - -static unsigned int -nf_nat_in(unsigned int hooknum, - struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ - unsigned int ret; - __be32 daddr = ip_hdr(skb)->daddr; - - ret = nf_nat_fn(hooknum, skb, in, out, okfn); - if (ret != NF_DROP && ret != NF_STOLEN && - daddr != ip_hdr(skb)->daddr) - skb_dst_drop(skb); - - return ret; -} - -static unsigned int -nf_nat_out(unsigned int hooknum, - struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ -#ifdef CONFIG_XFRM - const struct nf_conn *ct; - enum ip_conntrack_info ctinfo; -#endif - unsigned int ret; - - /* root is playing with raw sockets. */ - if (skb->len < sizeof(struct iphdr) || - ip_hdrlen(skb) < sizeof(struct iphdr)) - return NF_ACCEPT; - - ret = nf_nat_fn(hooknum, skb, in, out, okfn); -#ifdef CONFIG_XFRM - if (ret != NF_DROP && ret != NF_STOLEN && - (ct = nf_ct_get(skb, &ctinfo)) != NULL) { - enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - - if ((ct->tuplehash[dir].tuple.src.u3.ip != - ct->tuplehash[!dir].tuple.dst.u3.ip) || - (ct->tuplehash[dir].tuple.src.u.all != - ct->tuplehash[!dir].tuple.dst.u.all) - ) - return ip_xfrm_me_harder(skb) == 0 ? ret : NF_DROP; - } -#endif - return ret; -} - -static unsigned int -nf_nat_local_fn(unsigned int hooknum, - struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ - const struct nf_conn *ct; - enum ip_conntrack_info ctinfo; - unsigned int ret; - - /* root is playing with raw sockets. */ - if (skb->len < sizeof(struct iphdr) || - ip_hdrlen(skb) < sizeof(struct iphdr)) - return NF_ACCEPT; - - ret = nf_nat_fn(hooknum, skb, in, out, okfn); - if (ret != NF_DROP && ret != NF_STOLEN && - (ct = nf_ct_get(skb, &ctinfo)) != NULL) { - enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - - if (ct->tuplehash[dir].tuple.dst.u3.ip != - ct->tuplehash[!dir].tuple.src.u3.ip) { - if (ip_route_me_harder(skb, RTN_UNSPEC)) - ret = NF_DROP; - } -#ifdef CONFIG_XFRM - else if (ct->tuplehash[dir].tuple.dst.u.all != - ct->tuplehash[!dir].tuple.src.u.all) - if (ip_xfrm_me_harder(skb)) - ret = NF_DROP; -#endif - } - return ret; -} - -/* We must be after connection tracking and before packet filtering. */ - -static struct nf_hook_ops nf_nat_ops[] __read_mostly = { - /* Before packet filtering, change destination */ - { - .hook = nf_nat_in, - .owner = THIS_MODULE, - .pf = NFPROTO_IPV4, - .hooknum = NF_INET_PRE_ROUTING, - .priority = NF_IP_PRI_NAT_DST, - }, - /* After packet filtering, change source */ - { - .hook = nf_nat_out, - .owner = THIS_MODULE, - .pf = NFPROTO_IPV4, - .hooknum = NF_INET_POST_ROUTING, - .priority = NF_IP_PRI_NAT_SRC, - }, - /* Before packet filtering, change destination */ - { - .hook = nf_nat_local_fn, - .owner = THIS_MODULE, - .pf = NFPROTO_IPV4, - .hooknum = NF_INET_LOCAL_OUT, - .priority = NF_IP_PRI_NAT_DST, - }, - /* After packet filtering, change source */ - { - .hook = nf_nat_fn, - .owner = THIS_MODULE, - .pf = NFPROTO_IPV4, - .hooknum = NF_INET_LOCAL_IN, - .priority = NF_IP_PRI_NAT_SRC, - }, -}; - -static int __init nf_nat_standalone_init(void) -{ - int ret = 0; - - need_ipv4_conntrack(); - -#ifdef CONFIG_XFRM - BUG_ON(ip_nat_decode_session != NULL); - RCU_INIT_POINTER(ip_nat_decode_session, nat_decode_session); -#endif - ret = nf_nat_rule_init(); - if (ret < 0) { - pr_err("nf_nat_init: can't setup rules.\n"); - goto cleanup_decode_session; - } - ret = nf_register_hooks(nf_nat_ops, ARRAY_SIZE(nf_nat_ops)); - if (ret < 0) { - pr_err("nf_nat_init: can't register hooks.\n"); - goto cleanup_rule_init; - } - return ret; - - cleanup_rule_init: - nf_nat_rule_cleanup(); - cleanup_decode_session: -#ifdef CONFIG_XFRM - RCU_INIT_POINTER(ip_nat_decode_session, NULL); - synchronize_net(); -#endif - return ret; -} - -static void __exit nf_nat_standalone_fini(void) -{ - nf_unregister_hooks(nf_nat_ops, ARRAY_SIZE(nf_nat_ops)); - nf_nat_rule_cleanup(); -#ifdef CONFIG_XFRM - RCU_INIT_POINTER(ip_nat_decode_session, NULL); - synchronize_net(); -#endif - /* Conntrack caches are unregistered in nf_conntrack_cleanup */ -} - -module_init(nf_nat_standalone_init); -module_exit(nf_nat_standalone_fini); - -MODULE_LICENSE("GPL"); -MODULE_ALIAS("ip_nat"); diff --git a/net/ipv4/netfilter/nf_nat_tftp.c b/net/ipv4/netfilter/nf_nat_tftp.c index 9dbb8d284f99..ccabbda71a3e 100644 --- a/net/ipv4/netfilter/nf_nat_tftp.c +++ b/net/ipv4/netfilter/nf_nat_tftp.c @@ -11,7 +11,6 @@ #include #include #include -#include #include MODULE_AUTHOR("Magnus Boden "); diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index c19b214ffd57..91adddae20a4 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -356,6 +356,30 @@ config NETFILTER_NETLINK_QUEUE_CT If this option is enabled, NFQUEUE can include Connection Tracking information together with the packet is the enqueued via NFNETLINK. +config NF_NAT + tristate + +config NF_NAT_NEEDED + bool + depends on NF_NAT + default y + +config NF_NAT_PROTO_DCCP + tristate + depends on NF_NAT && NF_CT_PROTO_DCCP + default NF_NAT && NF_CT_PROTO_DCCP + +config NF_NAT_PROTO_UDPLITE + tristate + depends on NF_NAT && NF_CT_PROTO_UDPLITE + default NF_NAT && NF_CT_PROTO_UDPLITE + +config NF_NAT_PROTO_SCTP + tristate + default NF_NAT && NF_CT_PROTO_SCTP + depends on NF_NAT && NF_CT_PROTO_SCTP + select LIBCRC32C + endif # NF_CONNTRACK # transparent proxy support diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 1c5160f2278e..09c9451bc510 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -43,6 +43,17 @@ obj-$(CONFIG_NF_CONNTRACK_SANE) += nf_conntrack_sane.o obj-$(CONFIG_NF_CONNTRACK_SIP) += nf_conntrack_sip.o obj-$(CONFIG_NF_CONNTRACK_TFTP) += nf_conntrack_tftp.o +nf_nat-y := nf_nat_core.o nf_nat_proto_unknown.o nf_nat_proto_common.o \ + nf_nat_proto_udp.o nf_nat_proto_tcp.o nf_nat_helper.o + +obj-$(CONFIG_NF_NAT) += nf_nat.o +obj-$(CONFIG_NF_NAT) += xt_nat.o + +# NAT protocols (nf_nat) +obj-$(CONFIG_NF_NAT_PROTO_DCCP) += nf_nat_proto_dccp.o +obj-$(CONFIG_NF_NAT_PROTO_UDPLITE) += nf_nat_proto_udplite.o +obj-$(CONFIG_NF_NAT_PROTO_SCTP) += nf_nat_proto_sctp.o + # transparent proxy support obj-$(CONFIG_NETFILTER_TPROXY) += nf_tproxy_core.o diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 8f4b0b2b6f80..e61b3ac9591b 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -275,6 +275,11 @@ EXPORT_SYMBOL_GPL(nfq_ct_nat_hook); #endif /* CONFIG_NF_CONNTRACK */ +#ifdef CONFIG_NF_NAT_NEEDED +void (*nf_nat_decode_session_hook)(struct sk_buff *, struct flowi *); +EXPORT_SYMBOL(nf_nat_decode_session_hook); +#endif + #ifdef CONFIG_PROC_FS struct proc_dir_entry *proc_net_netfilter; EXPORT_SYMBOL(proc_net_netfilter); diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index cf4875565d67..f83e79defed9 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -55,6 +55,12 @@ int (*nfnetlink_parse_nat_setup_hook)(struct nf_conn *ct, const struct nlattr *attr) __read_mostly; EXPORT_SYMBOL_GPL(nfnetlink_parse_nat_setup_hook); +int (*nf_nat_seq_adjust_hook)(struct sk_buff *skb, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int protoff); +EXPORT_SYMBOL_GPL(nf_nat_seq_adjust_hook); + DEFINE_SPINLOCK(nf_conntrack_lock); EXPORT_SYMBOL_GPL(nf_conntrack_lock); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index da4fc37a8578..966f5133a384 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -45,7 +45,7 @@ #include #ifdef CONFIG_NF_NAT_NEEDED #include -#include +#include #include #endif @@ -1096,13 +1096,14 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct, const struct nlattr *attr) { typeof(nfnetlink_parse_nat_setup_hook) parse_nat_setup; + int err; parse_nat_setup = rcu_dereference(nfnetlink_parse_nat_setup_hook); if (!parse_nat_setup) { #ifdef CONFIG_MODULES rcu_read_unlock(); nfnl_unlock(); - if (request_module("nf-nat-ipv4") < 0) { + if (request_module("nf-nat") < 0) { nfnl_lock(); rcu_read_lock(); return -EOPNOTSUPP; @@ -1115,7 +1116,26 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct, return -EOPNOTSUPP; } - return parse_nat_setup(ct, manip, attr); + err = parse_nat_setup(ct, manip, attr); + if (err == -EAGAIN) { +#ifdef CONFIG_MODULES + rcu_read_unlock(); + spin_unlock_bh(&nf_conntrack_lock); + nfnl_unlock(); + if (request_module("nf-nat-%u", nf_ct_l3num(ct)) < 0) { + nfnl_lock(); + spin_lock_bh(&nf_conntrack_lock); + rcu_read_lock(); + return -EOPNOTSUPP; + } + nfnl_lock(); + spin_lock_bh(&nf_conntrack_lock); + rcu_read_lock(); +#else + err = -EOPNOTSUPP; +#endif + } + return err; } #endif @@ -1979,6 +1999,8 @@ nla_put_failure: return -1; } +static const union nf_inet_addr any_addr; + static int ctnetlink_exp_dump_expect(struct sk_buff *skb, const struct nf_conntrack_expect *exp) @@ -2005,7 +2027,8 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb, goto nla_put_failure; #ifdef CONFIG_NF_NAT_NEEDED - if (exp->saved_ip || exp->saved_proto.all) { + if (!nf_inet_addr_cmp(&exp->saved_addr, &any_addr) || + exp->saved_proto.all) { nest_parms = nla_nest_start(skb, CTA_EXPECT_NAT | NLA_F_NESTED); if (!nest_parms) goto nla_put_failure; @@ -2014,7 +2037,7 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb, goto nla_put_failure; nat_tuple.src.l3num = nf_ct_l3num(master); - nat_tuple.src.u3.ip = exp->saved_ip; + nat_tuple.src.u3 = exp->saved_addr; nat_tuple.dst.protonum = nf_ct_protonum(master); nat_tuple.src.u = exp->saved_proto; @@ -2410,7 +2433,7 @@ ctnetlink_parse_expect_nat(const struct nlattr *attr, if (err < 0) return err; - exp->saved_ip = nat_tuple.src.u3.ip; + exp->saved_addr = nat_tuple.src.u3; exp->saved_proto = nat_tuple.src.u; exp->dir = ntohl(nla_get_be32(tb[CTA_EXPECT_NAT_DIR])); diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index a5ac11ebef33..9c2cc716f4a5 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -505,10 +505,10 @@ static inline s16 nat_offset(const struct nf_conn *ct, return get_offset != NULL ? get_offset(ct, dir, seq) : 0; } -#define NAT_OFFSET(pf, ct, dir, seq) \ - (pf == NFPROTO_IPV4 ? nat_offset(ct, dir, seq) : 0) +#define NAT_OFFSET(ct, dir, seq) \ + (nat_offset(ct, dir, seq)) #else -#define NAT_OFFSET(pf, ct, dir, seq) 0 +#define NAT_OFFSET(ct, dir, seq) 0 #endif static bool tcp_in_window(const struct nf_conn *ct, @@ -541,7 +541,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(pf, ct, !dir, ack - 1); + receiver_offset = NAT_OFFSET(ct, !dir, ack - 1); ack -= receiver_offset; sack -= receiver_offset; diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 590f0abaab8c..d5174902db37 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -946,11 +946,11 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, break; #ifdef CONFIG_NF_NAT_NEEDED if (exp->tuple.src.l3num == AF_INET && !direct_rtp && - (exp->saved_ip != exp->tuple.dst.u3.ip || + (exp->saved_addr.ip != exp->tuple.dst.u3.ip || exp->saved_proto.udp.port != exp->tuple.dst.u.udp.port) && ct->status & IPS_NAT_MASK) { - daddr->ip = exp->saved_ip; - tuple.dst.u3.ip = exp->saved_ip; + daddr->ip = exp->saved_addr.ip; + tuple.dst.u3.ip = exp->saved_addr.ip; tuple.dst.u.udp.port = exp->saved_proto.udp.port; direct_rtp = 1; } else diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c new file mode 100644 index 000000000000..c577b753fb9a --- /dev/null +++ b/net/netfilter/nf_nat_core.c @@ -0,0 +1,854 @@ +/* + * (C) 1999-2001 Paul `Rusty' Russell + * (C) 2002-2006 Netfilter Core Team + * (C) 2011 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 +#include +#include +#include +#include +#include +#include + +static DEFINE_SPINLOCK(nf_nat_lock); + +static DEFINE_MUTEX(nf_nat_proto_mutex); +static const struct nf_nat_l3proto __rcu *nf_nat_l3protos[NFPROTO_NUMPROTO] + __read_mostly; +static const struct nf_nat_l4proto __rcu **nf_nat_l4protos[NFPROTO_NUMPROTO] + __read_mostly; + + +inline const struct nf_nat_l3proto * +__nf_nat_l3proto_find(u8 family) +{ + return rcu_dereference(nf_nat_l3protos[family]); +} + +inline const struct nf_nat_l4proto * +__nf_nat_l4proto_find(u8 family, u8 protonum) +{ + return rcu_dereference(nf_nat_l4protos[family][protonum]); +} +EXPORT_SYMBOL_GPL(__nf_nat_l4proto_find); + +#ifdef CONFIG_XFRM +static void __nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl) +{ + const struct nf_nat_l3proto *l3proto; + const struct nf_conn *ct; + enum ip_conntrack_info ctinfo; + enum ip_conntrack_dir dir; + unsigned long statusbit; + u8 family; + + ct = nf_ct_get(skb, &ctinfo); + if (ct == NULL) + return; + + family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num; + rcu_read_lock(); + l3proto = __nf_nat_l3proto_find(family); + if (l3proto == NULL) + goto out; + + dir = CTINFO2DIR(ctinfo); + if (dir == IP_CT_DIR_ORIGINAL) + statusbit = IPS_DST_NAT; + else + statusbit = IPS_SRC_NAT; + + l3proto->decode_session(skb, ct, dir, statusbit, fl); +out: + rcu_read_unlock(); +} + +int nf_xfrm_me_harder(struct sk_buff *skb, unsigned int family) +{ + struct flowi fl; + unsigned int hh_len; + struct dst_entry *dst; + + if (xfrm_decode_session(skb, &fl, family) < 0) + return -1; + + dst = skb_dst(skb); + if (dst->xfrm) + dst = ((struct xfrm_dst *)dst)->route; + dst_hold(dst); + + dst = xfrm_lookup(dev_net(dst->dev), dst, &fl, skb->sk, 0); + if (IS_ERR(dst)) + return -1; + + skb_dst_drop(skb); + skb_dst_set(skb, dst); + + /* Change in oif may mean change in hh_len. */ + hh_len = skb_dst(skb)->dev->hard_header_len; + if (skb_headroom(skb) < hh_len && + pskb_expand_head(skb, hh_len - skb_headroom(skb), 0, GFP_ATOMIC)) + return -1; + return 0; +} +EXPORT_SYMBOL(nf_xfrm_me_harder); +#endif /* CONFIG_XFRM */ + +/* We keep an extra hash for each conntrack, for fast searching. */ +static inline unsigned int +hash_by_src(const struct net *net, u16 zone, + const struct nf_conntrack_tuple *tuple) +{ + unsigned int hash; + + /* Original src, to ensure we map it consistently if poss. */ + hash = jhash2((u32 *)&tuple->src, sizeof(tuple->src) / sizeof(u32), + tuple->dst.protonum ^ zone ^ nf_conntrack_hash_rnd); + return ((u64)hash * net->ct.nat_htable_size) >> 32; +} + +/* Is this tuple already taken? (not by us) */ +int +nf_nat_used_tuple(const struct nf_conntrack_tuple *tuple, + const struct nf_conn *ignored_conntrack) +{ + /* Conntrack tracking doesn't keep track of outgoing tuples; only + * incoming ones. NAT means they don't have a fixed mapping, + * so we invert the tuple and look for the incoming reply. + * + * We could keep a separate hash if this proves too slow. + */ + struct nf_conntrack_tuple reply; + + nf_ct_invert_tuplepr(&reply, tuple); + return nf_conntrack_tuple_taken(&reply, ignored_conntrack); +} +EXPORT_SYMBOL(nf_nat_used_tuple); + +/* If we source map this tuple so reply looks like reply_tuple, will + * that meet the constraints of range. + */ +static int in_range(const struct nf_nat_l3proto *l3proto, + const struct nf_nat_l4proto *l4proto, + const struct nf_conntrack_tuple *tuple, + const struct nf_nat_range *range) +{ + /* If we are supposed to map IPs, then we must be in the + * range specified, otherwise let this drag us onto a new src IP. + */ + if (range->flags & NF_NAT_RANGE_MAP_IPS && + !l3proto->in_range(tuple, range)) + return 0; + + if (!(range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) || + l4proto->in_range(tuple, NF_NAT_MANIP_SRC, + &range->min_proto, &range->max_proto)) + return 1; + + return 0; +} + +static inline int +same_src(const struct nf_conn *ct, + const struct nf_conntrack_tuple *tuple) +{ + const struct nf_conntrack_tuple *t; + + t = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; + return (t->dst.protonum == tuple->dst.protonum && + nf_inet_addr_cmp(&t->src.u3, &tuple->src.u3) && + t->src.u.all == tuple->src.u.all); +} + +/* Only called for SRC manip */ +static int +find_appropriate_src(struct net *net, u16 zone, + const struct nf_nat_l3proto *l3proto, + const struct nf_nat_l4proto *l4proto, + const struct nf_conntrack_tuple *tuple, + struct nf_conntrack_tuple *result, + const struct nf_nat_range *range) +{ + unsigned int h = hash_by_src(net, zone, tuple); + const struct nf_conn_nat *nat; + const struct nf_conn *ct; + const struct hlist_node *n; + + hlist_for_each_entry_rcu(nat, n, &net->ct.nat_bysource[h], bysource) { + ct = nat->ct; + if (same_src(ct, tuple) && nf_ct_zone(ct) == zone) { + /* Copy source part from reply tuple. */ + nf_ct_invert_tuplepr(result, + &ct->tuplehash[IP_CT_DIR_REPLY].tuple); + result->dst = tuple->dst; + + if (in_range(l3proto, l4proto, result, range)) { + rcu_read_unlock(); + return 1; + } + } + } + return 0; +} + +/* For [FUTURE] fragmentation handling, we want the least-used + * src-ip/dst-ip/proto triple. Fairness doesn't come into it. Thus + * if the range specifies 1.2.3.4 ports 10000-10005 and 1.2.3.5 ports + * 1-65535, we don't do pro-rata allocation based on ports; we choose + * the ip with the lowest src-ip/dst-ip/proto usage. + */ +static void +find_best_ips_proto(u16 zone, struct nf_conntrack_tuple *tuple, + const struct nf_nat_range *range, + const struct nf_conn *ct, + enum nf_nat_manip_type maniptype) +{ + union nf_inet_addr *var_ipp; + unsigned int i, max; + /* Host order */ + u32 minip, maxip, j, dist; + bool full_range; + + /* No IP mapping? Do nothing. */ + if (!(range->flags & NF_NAT_RANGE_MAP_IPS)) + return; + + if (maniptype == NF_NAT_MANIP_SRC) + var_ipp = &tuple->src.u3; + else + var_ipp = &tuple->dst.u3; + + /* Fast path: only one choice. */ + if (nf_inet_addr_cmp(&range->min_addr, &range->max_addr)) { + *var_ipp = range->min_addr; + return; + } + + if (nf_ct_l3num(ct) == NFPROTO_IPV4) + max = sizeof(var_ipp->ip) / sizeof(u32) - 1; + else + max = sizeof(var_ipp->ip6) / sizeof(u32) - 1; + + /* Hashing source and destination IPs gives a fairly even + * spread in practice (if there are a small number of IPs + * involved, there usually aren't that many connections + * anyway). The consistency means that servers see the same + * client coming from the same IP (some Internet Banking sites + * like this), even across reboots. + */ + j = jhash2((u32 *)&tuple->src.u3, sizeof(tuple->src.u3), + range->flags & NF_NAT_RANGE_PERSISTENT ? + 0 : (__force u32)tuple->dst.u3.all[max] ^ zone); + + full_range = false; + for (i = 0; i <= max; i++) { + /* If first bytes of the address are at the maximum, use the + * distance. Otherwise use the full range. + */ + if (!full_range) { + minip = ntohl((__force __be32)range->min_addr.all[i]); + maxip = ntohl((__force __be32)range->max_addr.all[i]); + dist = maxip - minip + 1; + } else { + minip = 0; + dist = ~0; + } + + var_ipp->all[i] = (__force __u32) + htonl(minip + (((u64)j * dist) >> 32)); + if (var_ipp->all[i] != range->max_addr.all[i]) + full_range = true; + + if (!(range->flags & NF_NAT_RANGE_PERSISTENT)) + j ^= (__force u32)tuple->dst.u3.all[i]; + } +} + +/* Manipulate the tuple into the range given. For NF_INET_POST_ROUTING, + * we change the source to map into the range. For NF_INET_PRE_ROUTING + * and NF_INET_LOCAL_OUT, we change the destination to map into the + * range. It might not be possible to get a unique tuple, but we try. + * At worst (or if we race), we will end up with a final duplicate in + * __ip_conntrack_confirm and drop the packet. */ +static void +get_unique_tuple(struct nf_conntrack_tuple *tuple, + const struct nf_conntrack_tuple *orig_tuple, + const struct nf_nat_range *range, + struct nf_conn *ct, + enum nf_nat_manip_type maniptype) +{ + const struct nf_nat_l3proto *l3proto; + const struct nf_nat_l4proto *l4proto; + struct net *net = nf_ct_net(ct); + u16 zone = nf_ct_zone(ct); + + rcu_read_lock(); + l3proto = __nf_nat_l3proto_find(orig_tuple->src.l3num); + l4proto = __nf_nat_l4proto_find(orig_tuple->src.l3num, + orig_tuple->dst.protonum); + + /* 1) If this srcip/proto/src-proto-part is currently mapped, + * and that same mapping gives a unique tuple within the given + * range, use that. + * + * This is only required for source (ie. NAT/masq) mappings. + * So far, we don't do local source mappings, so multiple + * manips not an issue. + */ + if (maniptype == NF_NAT_MANIP_SRC && + !(range->flags & NF_NAT_RANGE_PROTO_RANDOM)) { + /* try the original tuple first */ + if (in_range(l3proto, l4proto, orig_tuple, range)) { + if (!nf_nat_used_tuple(orig_tuple, ct)) { + *tuple = *orig_tuple; + goto out; + } + } else if (find_appropriate_src(net, zone, l3proto, l4proto, + orig_tuple, tuple, range)) { + pr_debug("get_unique_tuple: Found current src map\n"); + if (!nf_nat_used_tuple(tuple, ct)) + goto out; + } + } + + /* 2) Select the least-used IP/proto combination in the given range */ + *tuple = *orig_tuple; + find_best_ips_proto(zone, tuple, range, ct, maniptype); + + /* 3) The per-protocol part of the manip is made to map into + * the range to make a unique tuple. + */ + + /* Only bother mapping if it's not already in range and unique */ + if (!(range->flags & NF_NAT_RANGE_PROTO_RANDOM)) { + if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) { + if (l4proto->in_range(tuple, maniptype, + &range->min_proto, + &range->max_proto) && + (range->min_proto.all == range->max_proto.all || + !nf_nat_used_tuple(tuple, ct))) + goto out; + } else if (!nf_nat_used_tuple(tuple, ct)) { + goto out; + } + } + + /* Last change: get protocol to try to obtain unique tuple. */ + l4proto->unique_tuple(l3proto, tuple, range, maniptype, ct); +out: + rcu_read_unlock(); +} + +unsigned int +nf_nat_setup_info(struct nf_conn *ct, + const struct nf_nat_range *range, + enum nf_nat_manip_type maniptype) +{ + struct net *net = nf_ct_net(ct); + struct nf_conntrack_tuple curr_tuple, new_tuple; + struct nf_conn_nat *nat; + + /* nat helper or nfctnetlink also setup binding */ + nat = nfct_nat(ct); + if (!nat) { + nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); + if (nat == NULL) { + pr_debug("failed to add NAT extension\n"); + return NF_ACCEPT; + } + } + + NF_CT_ASSERT(maniptype == NF_NAT_MANIP_SRC || + maniptype == NF_NAT_MANIP_DST); + BUG_ON(nf_nat_initialized(ct, maniptype)); + + /* What we've got will look like inverse of reply. Normally + * this is what is in the conntrack, except for prior + * manipulations (future optimization: if num_manips == 0, + * orig_tp = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple) + */ + nf_ct_invert_tuplepr(&curr_tuple, + &ct->tuplehash[IP_CT_DIR_REPLY].tuple); + + get_unique_tuple(&new_tuple, &curr_tuple, range, ct, maniptype); + + if (!nf_ct_tuple_equal(&new_tuple, &curr_tuple)) { + struct nf_conntrack_tuple reply; + + /* Alter conntrack table so will recognize replies. */ + nf_ct_invert_tuplepr(&reply, &new_tuple); + nf_conntrack_alter_reply(ct, &reply); + + /* Non-atomic: we own this at the moment. */ + if (maniptype == NF_NAT_MANIP_SRC) + ct->status |= IPS_SRC_NAT; + else + ct->status |= IPS_DST_NAT; + } + + if (maniptype == NF_NAT_MANIP_SRC) { + unsigned int srchash; + + srchash = hash_by_src(net, nf_ct_zone(ct), + &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + spin_lock_bh(&nf_nat_lock); + /* nf_conntrack_alter_reply might re-allocate extension aera */ + nat = nfct_nat(ct); + nat->ct = ct; + hlist_add_head_rcu(&nat->bysource, + &net->ct.nat_bysource[srchash]); + spin_unlock_bh(&nf_nat_lock); + } + + /* It's done. */ + if (maniptype == NF_NAT_MANIP_DST) + ct->status |= IPS_DST_NAT_DONE; + else + ct->status |= IPS_SRC_NAT_DONE; + + return NF_ACCEPT; +} +EXPORT_SYMBOL(nf_nat_setup_info); + +/* Do packet manipulations according to nf_nat_setup_info. */ +unsigned int nf_nat_packet(struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff *skb) +{ + const struct nf_nat_l3proto *l3proto; + const struct nf_nat_l4proto *l4proto; + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + unsigned long statusbit; + enum nf_nat_manip_type mtype = HOOK2MANIP(hooknum); + + if (mtype == NF_NAT_MANIP_SRC) + statusbit = IPS_SRC_NAT; + else + statusbit = IPS_DST_NAT; + + /* Invert if this is reply dir. */ + if (dir == IP_CT_DIR_REPLY) + statusbit ^= IPS_NAT_MASK; + + /* Non-atomic: these bits don't change. */ + if (ct->status & statusbit) { + struct nf_conntrack_tuple target; + + /* We are aiming to look like inverse of other direction. */ + nf_ct_invert_tuplepr(&target, &ct->tuplehash[!dir].tuple); + + l3proto = __nf_nat_l3proto_find(target.src.l3num); + l4proto = __nf_nat_l4proto_find(target.src.l3num, + target.dst.protonum); + if (!l3proto->manip_pkt(skb, 0, l4proto, &target, mtype)) + return NF_DROP; + } + return NF_ACCEPT; +} +EXPORT_SYMBOL_GPL(nf_nat_packet); + +struct nf_nat_proto_clean { + u8 l3proto; + u8 l4proto; + bool hash; +}; + +/* Clear NAT section of all conntracks, in case we're loaded again. */ +static int nf_nat_proto_clean(struct nf_conn *i, void *data) +{ + const struct nf_nat_proto_clean *clean = data; + struct nf_conn_nat *nat = nfct_nat(i); + + if (!nat) + return 0; + if ((clean->l3proto && nf_ct_l3num(i) != clean->l3proto) || + (clean->l4proto && nf_ct_protonum(i) != clean->l4proto)) + return 0; + + if (clean->hash) { + spin_lock_bh(&nf_nat_lock); + hlist_del_rcu(&nat->bysource); + spin_unlock_bh(&nf_nat_lock); + } else { + memset(nat, 0, sizeof(*nat)); + i->status &= ~(IPS_NAT_MASK | IPS_NAT_DONE_MASK | + IPS_SEQ_ADJUST); + } + return 0; +} + +static void nf_nat_l4proto_clean(u8 l3proto, u8 l4proto) +{ + struct nf_nat_proto_clean clean = { + .l3proto = l3proto, + .l4proto = l4proto, + }; + struct net *net; + + rtnl_lock(); + /* Step 1 - remove from bysource hash */ + clean.hash = true; + for_each_net(net) + nf_ct_iterate_cleanup(net, nf_nat_proto_clean, &clean); + synchronize_rcu(); + + /* Step 2 - clean NAT section */ + clean.hash = false; + for_each_net(net) + nf_ct_iterate_cleanup(net, nf_nat_proto_clean, &clean); + rtnl_unlock(); +} + +static void nf_nat_l3proto_clean(u8 l3proto) +{ + struct nf_nat_proto_clean clean = { + .l3proto = l3proto, + }; + struct net *net; + + rtnl_lock(); + /* Step 1 - remove from bysource hash */ + clean.hash = true; + for_each_net(net) + nf_ct_iterate_cleanup(net, nf_nat_proto_clean, &clean); + synchronize_rcu(); + + /* Step 2 - clean NAT section */ + clean.hash = false; + for_each_net(net) + nf_ct_iterate_cleanup(net, nf_nat_proto_clean, &clean); + rtnl_unlock(); +} + +/* Protocol registration. */ +int nf_nat_l4proto_register(u8 l3proto, const struct nf_nat_l4proto *l4proto) +{ + const struct nf_nat_l4proto **l4protos; + unsigned int i; + int ret = 0; + + mutex_lock(&nf_nat_proto_mutex); + if (nf_nat_l4protos[l3proto] == NULL) { + l4protos = kmalloc(IPPROTO_MAX * sizeof(struct nf_nat_l4proto *), + GFP_KERNEL); + if (l4protos == NULL) { + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < IPPROTO_MAX; i++) + RCU_INIT_POINTER(l4protos[i], &nf_nat_l4proto_unknown); + + /* Before making proto_array visible to lockless readers, + * we must make sure its content is committed to memory. + */ + smp_wmb(); + + nf_nat_l4protos[l3proto] = l4protos; + } + + if (rcu_dereference_protected( + nf_nat_l4protos[l3proto][l4proto->l4proto], + lockdep_is_held(&nf_nat_proto_mutex) + ) != &nf_nat_l4proto_unknown) { + ret = -EBUSY; + goto out; + } + RCU_INIT_POINTER(nf_nat_l4protos[l3proto][l4proto->l4proto], l4proto); + out: + mutex_unlock(&nf_nat_proto_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(nf_nat_l4proto_register); + +/* No one stores the protocol anywhere; simply delete it. */ +void nf_nat_l4proto_unregister(u8 l3proto, const struct nf_nat_l4proto *l4proto) +{ + mutex_lock(&nf_nat_proto_mutex); + RCU_INIT_POINTER(nf_nat_l4protos[l3proto][l4proto->l4proto], + &nf_nat_l4proto_unknown); + mutex_unlock(&nf_nat_proto_mutex); + synchronize_rcu(); + + nf_nat_l4proto_clean(l3proto, l4proto->l4proto); +} +EXPORT_SYMBOL_GPL(nf_nat_l4proto_unregister); + +int nf_nat_l3proto_register(const struct nf_nat_l3proto *l3proto) +{ + int err; + + err = nf_ct_l3proto_try_module_get(l3proto->l3proto); + if (err < 0) + return err; + + mutex_lock(&nf_nat_proto_mutex); + RCU_INIT_POINTER(nf_nat_l4protos[l3proto->l3proto][IPPROTO_TCP], + &nf_nat_l4proto_tcp); + RCU_INIT_POINTER(nf_nat_l4protos[l3proto->l3proto][IPPROTO_UDP], + &nf_nat_l4proto_udp); + mutex_unlock(&nf_nat_proto_mutex); + + RCU_INIT_POINTER(nf_nat_l3protos[l3proto->l3proto], l3proto); + return 0; +} +EXPORT_SYMBOL_GPL(nf_nat_l3proto_register); + +void nf_nat_l3proto_unregister(const struct nf_nat_l3proto *l3proto) +{ + mutex_lock(&nf_nat_proto_mutex); + RCU_INIT_POINTER(nf_nat_l3protos[l3proto->l3proto], NULL); + mutex_unlock(&nf_nat_proto_mutex); + synchronize_rcu(); + + nf_nat_l3proto_clean(l3proto->l3proto); + nf_ct_l3proto_module_put(l3proto->l3proto); +} +EXPORT_SYMBOL_GPL(nf_nat_l3proto_unregister); + +/* No one using conntrack by the time this called. */ +static void nf_nat_cleanup_conntrack(struct nf_conn *ct) +{ + struct nf_conn_nat *nat = nf_ct_ext_find(ct, NF_CT_EXT_NAT); + + if (nat == NULL || nat->ct == NULL) + return; + + NF_CT_ASSERT(nat->ct->status & IPS_SRC_NAT_DONE); + + spin_lock_bh(&nf_nat_lock); + hlist_del_rcu(&nat->bysource); + spin_unlock_bh(&nf_nat_lock); +} + +static void nf_nat_move_storage(void *new, void *old) +{ + struct nf_conn_nat *new_nat = new; + struct nf_conn_nat *old_nat = old; + struct nf_conn *ct = old_nat->ct; + + if (!ct || !(ct->status & IPS_SRC_NAT_DONE)) + return; + + spin_lock_bh(&nf_nat_lock); + hlist_replace_rcu(&old_nat->bysource, &new_nat->bysource); + spin_unlock_bh(&nf_nat_lock); +} + +static struct nf_ct_ext_type nat_extend __read_mostly = { + .len = sizeof(struct nf_conn_nat), + .align = __alignof__(struct nf_conn_nat), + .destroy = nf_nat_cleanup_conntrack, + .move = nf_nat_move_storage, + .id = NF_CT_EXT_NAT, + .flags = NF_CT_EXT_F_PREALLOC, +}; + +#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) + +#include +#include + +static const struct nla_policy protonat_nla_policy[CTA_PROTONAT_MAX+1] = { + [CTA_PROTONAT_PORT_MIN] = { .type = NLA_U16 }, + [CTA_PROTONAT_PORT_MAX] = { .type = NLA_U16 }, +}; + +static int nfnetlink_parse_nat_proto(struct nlattr *attr, + const struct nf_conn *ct, + struct nf_nat_range *range) +{ + struct nlattr *tb[CTA_PROTONAT_MAX+1]; + const struct nf_nat_l4proto *l4proto; + int err; + + err = nla_parse_nested(tb, CTA_PROTONAT_MAX, attr, protonat_nla_policy); + if (err < 0) + return err; + + l4proto = __nf_nat_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct)); + if (l4proto->nlattr_to_range) + err = l4proto->nlattr_to_range(tb, range); + + return err; +} + +static const struct nla_policy nat_nla_policy[CTA_NAT_MAX+1] = { + [CTA_NAT_V4_MINIP] = { .type = NLA_U32 }, + [CTA_NAT_V4_MAXIP] = { .type = NLA_U32 }, + [CTA_NAT_PROTO] = { .type = NLA_NESTED }, +}; + +static int +nfnetlink_parse_nat(const struct nlattr *nat, + const struct nf_conn *ct, struct nf_nat_range *range) +{ + const struct nf_nat_l3proto *l3proto; + struct nlattr *tb[CTA_NAT_MAX+1]; + int err; + + memset(range, 0, sizeof(*range)); + + err = nla_parse_nested(tb, CTA_NAT_MAX, nat, nat_nla_policy); + if (err < 0) + return err; + + rcu_read_lock(); + l3proto = __nf_nat_l3proto_find(nf_ct_l3num(ct)); + if (l3proto == NULL) { + err = -EAGAIN; + goto out; + } + err = l3proto->nlattr_to_range(tb, range); + if (err < 0) + goto out; + + if (!tb[CTA_NAT_PROTO]) + goto out; + + err = nfnetlink_parse_nat_proto(tb[CTA_NAT_PROTO], ct, range); +out: + rcu_read_unlock(); + return err; +} + +static int +nfnetlink_parse_nat_setup(struct nf_conn *ct, + enum nf_nat_manip_type manip, + const struct nlattr *attr) +{ + struct nf_nat_range range; + int err; + + err = nfnetlink_parse_nat(attr, ct, &range); + if (err < 0) + return err; + if (nf_nat_initialized(ct, manip)) + return -EEXIST; + + return nf_nat_setup_info(ct, &range, manip); +} +#else +static int +nfnetlink_parse_nat_setup(struct nf_conn *ct, + enum nf_nat_manip_type manip, + const struct nlattr *attr) +{ + return -EOPNOTSUPP; +} +#endif + +static int __net_init nf_nat_net_init(struct net *net) +{ + /* Leave them the same for the moment. */ + net->ct.nat_htable_size = net->ct.htable_size; + net->ct.nat_bysource = nf_ct_alloc_hashtable(&net->ct.nat_htable_size, 0); + if (!net->ct.nat_bysource) + return -ENOMEM; + return 0; +} + +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_clean, &clean); + synchronize_rcu(); + nf_ct_free_hashtable(net->ct.nat_bysource, net->ct.nat_htable_size); +} + +static struct pernet_operations nf_nat_net_ops = { + .init = nf_nat_net_init, + .exit = nf_nat_net_exit, +}; + +static struct nf_ct_helper_expectfn follow_master_nat = { + .name = "nat-follow-master", + .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; + + ret = nf_ct_extend_register(&nat_extend); + if (ret < 0) { + printk(KERN_ERR "nf_nat_core: Unable to register extension\n"); + return ret; + } + + ret = register_pernet_subsys(&nf_nat_net_ops); + if (ret < 0) + goto cleanup_extend; + + nf_ct_helper_expectfn_register(&follow_master_nat); + + /* 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); +#endif + return 0; + + cleanup_extend: + nf_ct_extend_unregister(&nat_extend); + return ret; +} + +static void __exit nf_nat_cleanup(void) +{ + unsigned int i; + + 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 + for (i = 0; i < NFPROTO_NUMPROTO; i++) + kfree(nf_nat_l4protos[i]); + synchronize_net(); +} + +MODULE_LICENSE("GPL"); + +module_init(nf_nat_init); +module_exit(nf_nat_cleanup); diff --git a/net/netfilter/nf_nat_helper.c b/net/netfilter/nf_nat_helper.c new file mode 100644 index 000000000000..23c2b38676a6 --- /dev/null +++ b/net/netfilter/nf_nat_helper.c @@ -0,0 +1,435 @@ +/* nf_nat_helper.c - generic support functions for NAT helpers + * + * (C) 2000-2002 Harald Welte + * (C) 2003-2006 Netfilter Core Team + * + * 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 +#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); + +static DEFINE_SPINLOCK(nf_nat_seqofs_lock); + +/* 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(&nf_nat_seqofs_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(&nf_nat_seqofs_lock); + + pr_debug("adjust_tcp_sequence: Seq_offset after: "); + DUMP_OFFSET(this_way); +} + +/* Get the offset value, for conntrack */ +s16 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; + + if (!nat) + return 0; + + this_way = &nat->seq[dir]; + spin_lock_bh(&nf_nat_seqofs_lock); + offset = after(seq, this_way->correction_pos) + ? this_way->offset_after : this_way->offset_before; + spin_unlock_bh(&nf_nat_seqofs_lock); + + return offset; +} + +/* Frobs data inside this packet, which is linear. */ +static void mangle_contents(struct sk_buff *skb, + unsigned int dataoff, + unsigned int match_offset, + unsigned int match_len, + const char *rep_buffer, + unsigned int rep_len) +{ + unsigned char *data; + + BUG_ON(skb_is_nonlinear(skb)); + data = skb_network_header(skb) + dataoff; + + /* move post-replacement */ + memmove(data + match_offset + rep_len, + data + match_offset + match_len, + skb->tail - (skb->network_header + dataoff + + match_offset + match_len)); + + /* insert data from buffer */ + memcpy(data + match_offset, rep_buffer, rep_len); + + /* update skb info */ + if (rep_len > match_len) { + pr_debug("nf_nat_mangle_packet: Extending packet by " + "%u from %u bytes\n", rep_len - match_len, skb->len); + skb_put(skb, rep_len - match_len); + } else { + pr_debug("nf_nat_mangle_packet: Shrinking packet from " + "%u from %u bytes\n", match_len - rep_len, skb->len); + __skb_trim(skb, skb->len + rep_len - match_len); + } + + if (nf_ct_l3num((struct nf_conn *)skb->nfct) == NFPROTO_IPV4) { + /* fix IP hdr checksum information */ + ip_hdr(skb)->tot_len = htons(skb->len); + ip_send_check(ip_hdr(skb)); + } else + ipv6_hdr(skb)->payload_len = + htons(skb->len - sizeof(struct ipv6hdr)); +} + +/* Unusual, but possible case. */ +static int enlarge_skb(struct sk_buff *skb, unsigned int extra) +{ + if (skb->len + extra > 65535) + return 0; + + if (pskb_expand_head(skb, 0, extra - skb_tailroom(skb), GFP_ATOMIC)) + return 0; + + return 1; +} + +void nf_nat_set_seq_adjust(struct nf_conn *ct, enum ip_conntrack_info ctinfo, + __be32 seq, s16 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). + * + * Takes care about all the nasty sequence number changes, checksumming, + * skb enlargement, ... + * + * */ +int __nf_nat_mangle_tcp_packet(struct sk_buff *skb, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int protoff, + unsigned int match_offset, + unsigned int match_len, + const char *rep_buffer, + unsigned int rep_len, bool adjust) +{ + const struct nf_nat_l3proto *l3proto; + struct tcphdr *tcph; + int oldlen, datalen; + + if (!skb_make_writable(skb, skb->len)) + return 0; + + if (rep_len > match_len && + rep_len - match_len > skb_tailroom(skb) && + !enlarge_skb(skb, rep_len - match_len)) + return 0; + + SKB_LINEAR_ASSERT(skb); + + tcph = (void *)skb->data + protoff; + + oldlen = skb->len - protoff; + mangle_contents(skb, protoff + tcph->doff*4, + match_offset, match_len, rep_buffer, rep_len); + + datalen = skb->len - protoff; + + l3proto = __nf_nat_l3proto_find(nf_ct_l3num(ct)); + l3proto->csum_recalc(skb, IPPROTO_TCP, tcph, &tcph->check, + datalen, oldlen); + + if (adjust && rep_len != match_len) + nf_nat_set_seq_adjust(ct, ctinfo, tcph->seq, + (int)rep_len - (int)match_len); + + return 1; +} +EXPORT_SYMBOL(__nf_nat_mangle_tcp_packet); + +/* Generic function for mangling variable-length address changes inside + * NATed UDP connections (like the CONNECT DATA XXXXX MESG XXXXX INDEX XXXXX + * command in the Amanda protocol) + * + * Takes care about all the nasty sequence number changes, checksumming, + * skb enlargement, ... + * + * XXX - This function could be merged with nf_nat_mangle_tcp_packet which + * should be fairly easy to do. + */ +int +nf_nat_mangle_udp_packet(struct sk_buff *skb, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int protoff, + unsigned int match_offset, + unsigned int match_len, + const char *rep_buffer, + unsigned int rep_len) +{ + const struct nf_nat_l3proto *l3proto; + struct udphdr *udph; + int datalen, oldlen; + + if (!skb_make_writable(skb, skb->len)) + return 0; + + if (rep_len > match_len && + rep_len - match_len > skb_tailroom(skb) && + !enlarge_skb(skb, rep_len - match_len)) + return 0; + + udph = (void *)skb->data + protoff; + + oldlen = skb->len - protoff; + mangle_contents(skb, protoff + sizeof(*udph), + match_offset, match_len, rep_buffer, rep_len); + + /* update the length of the UDP packet */ + datalen = skb->len - protoff; + udph->len = htons(datalen); + + /* fix udp checksum if udp checksum was previously calculated */ + if (!udph->check && skb->ip_summed != CHECKSUM_PARTIAL) + return 1; + + l3proto = __nf_nat_l3proto_find(nf_ct_l3num(ct)); + l3proto->csum_recalc(skb, IPPROTO_UDP, udph, &udph->check, + datalen, oldlen); + + return 1; +} +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; + s16 seqoff, ackoff; + struct nf_conn_nat *nat = nfct_nat(ct); + struct nf_nat_seq *this_way, *other_way; + + 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; + 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; + + return nf_nat_sack_adjust(skb, protoff, tcph, ct, ctinfo); +} + +/* 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, + struct nf_conntrack_expect *exp) +{ + struct nf_nat_range range; + + /* This must be a fresh one. */ + BUG_ON(ct->status & IPS_NAT_DONE_MASK); + + /* Change src to where master sends to */ + range.flags = NF_NAT_RANGE_MAP_IPS; + range.min_addr = range.max_addr + = ct->master->tuplehash[!exp->dir].tuple.dst.u3; + nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC); + + /* For DST manip, map port here to where it's expected. */ + range.flags = (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED); + range.min_proto = range.max_proto = exp->saved_proto; + range.min_addr = range.max_addr + = ct->master->tuplehash[!exp->dir].tuple.src.u3; + nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST); +} +EXPORT_SYMBOL(nf_nat_follow_master); diff --git a/net/netfilter/nf_nat_proto_common.c b/net/netfilter/nf_nat_proto_common.c new file mode 100644 index 000000000000..9baaf734c142 --- /dev/null +++ b/net/netfilter/nf_nat_proto_common.c @@ -0,0 +1,112 @@ +/* (C) 1999-2001 Paul `Rusty' Russell + * (C) 2002-2006 Netfilter Core Team + * (C) 2008 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 + +bool nf_nat_l4proto_in_range(const struct nf_conntrack_tuple *tuple, + enum nf_nat_manip_type maniptype, + const union nf_conntrack_man_proto *min, + const union nf_conntrack_man_proto *max) +{ + __be16 port; + + if (maniptype == NF_NAT_MANIP_SRC) + port = tuple->src.u.all; + else + port = tuple->dst.u.all; + + return ntohs(port) >= ntohs(min->all) && + ntohs(port) <= ntohs(max->all); +} +EXPORT_SYMBOL_GPL(nf_nat_l4proto_in_range); + +void nf_nat_l4proto_unique_tuple(const struct nf_nat_l3proto *l3proto, + struct nf_conntrack_tuple *tuple, + const struct nf_nat_range *range, + enum nf_nat_manip_type maniptype, + const struct nf_conn *ct, + u16 *rover) +{ + unsigned int range_size, min, i; + __be16 *portptr; + u_int16_t off; + + if (maniptype == NF_NAT_MANIP_SRC) + portptr = &tuple->src.u.all; + else + portptr = &tuple->dst.u.all; + + /* If no range specified... */ + if (!(range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)) { + /* If it's dst rewrite, can't change port */ + if (maniptype == NF_NAT_MANIP_DST) + return; + + if (ntohs(*portptr) < 1024) { + /* Loose convention: >> 512 is credential passing */ + if (ntohs(*portptr) < 512) { + min = 1; + range_size = 511 - min + 1; + } else { + min = 600; + range_size = 1023 - min + 1; + } + } else { + min = 1024; + range_size = 65535 - 1024 + 1; + } + } else { + min = ntohs(range->min_proto.all); + range_size = ntohs(range->max_proto.all) - min + 1; + } + + if (range->flags & NF_NAT_RANGE_PROTO_RANDOM) + off = l3proto->secure_port(tuple, maniptype == NF_NAT_MANIP_SRC + ? tuple->dst.u.all + : tuple->src.u.all); + else + off = *rover; + + for (i = 0; ; ++off) { + *portptr = htons(min + off % range_size); + if (++i != range_size && nf_nat_used_tuple(tuple, ct)) + continue; + if (!(range->flags & NF_NAT_RANGE_PROTO_RANDOM)) + *rover = off; + return; + } + return; +} +EXPORT_SYMBOL_GPL(nf_nat_l4proto_unique_tuple); + +#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) +int nf_nat_l4proto_nlattr_to_range(struct nlattr *tb[], + struct nf_nat_range *range) +{ + if (tb[CTA_PROTONAT_PORT_MIN]) { + range->min_proto.all = nla_get_be16(tb[CTA_PROTONAT_PORT_MIN]); + range->max_proto.all = range->min_proto.all; + range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED; + } + if (tb[CTA_PROTONAT_PORT_MAX]) { + range->max_proto.all = nla_get_be16(tb[CTA_PROTONAT_PORT_MAX]); + range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED; + } + return 0; +} +EXPORT_SYMBOL_GPL(nf_nat_l4proto_nlattr_to_range); +#endif diff --git a/net/netfilter/nf_nat_proto_dccp.c b/net/netfilter/nf_nat_proto_dccp.c new file mode 100644 index 000000000000..c8be2cdac0bf --- /dev/null +++ b/net/netfilter/nf_nat_proto_dccp.c @@ -0,0 +1,116 @@ +/* + * DCCP NAT protocol helper + * + * Copyright (c) 2005, 2006, 2008 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 u_int16_t dccp_port_rover; + +static void +dccp_unique_tuple(const struct nf_nat_l3proto *l3proto, + struct nf_conntrack_tuple *tuple, + const struct nf_nat_range *range, + enum nf_nat_manip_type maniptype, + const struct nf_conn *ct) +{ + nf_nat_l4proto_unique_tuple(l3proto, tuple, range, maniptype, ct, + &dccp_port_rover); +} + +static bool +dccp_manip_pkt(struct sk_buff *skb, + const struct nf_nat_l3proto *l3proto, + unsigned int iphdroff, unsigned int hdroff, + const struct nf_conntrack_tuple *tuple, + enum nf_nat_manip_type maniptype) +{ + struct dccp_hdr *hdr; + __be16 *portptr, oldport, newport; + int hdrsize = 8; /* DCCP connection tracking guarantees this much */ + + if (skb->len >= hdroff + sizeof(struct dccp_hdr)) + hdrsize = sizeof(struct dccp_hdr); + + if (!skb_make_writable(skb, hdroff + hdrsize)) + return false; + + hdr = (struct dccp_hdr *)(skb->data + hdroff); + + if (maniptype == NF_NAT_MANIP_SRC) { + newport = tuple->src.u.dccp.port; + portptr = &hdr->dccph_sport; + } else { + newport = tuple->dst.u.dccp.port; + portptr = &hdr->dccph_dport; + } + + oldport = *portptr; + *portptr = newport; + + if (hdrsize < sizeof(*hdr)) + return true; + + l3proto->csum_update(skb, iphdroff, &hdr->dccph_checksum, + tuple, maniptype); + inet_proto_csum_replace2(&hdr->dccph_checksum, skb, oldport, newport, + 0); + return true; +} + +static const struct nf_nat_l4proto nf_nat_l4proto_dccp = { + .l4proto = IPPROTO_DCCP, + .manip_pkt = dccp_manip_pkt, + .in_range = nf_nat_l4proto_in_range, + .unique_tuple = dccp_unique_tuple, +#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) + .nlattr_to_range = nf_nat_l4proto_nlattr_to_range, +#endif +}; + +static int __init nf_nat_proto_dccp_init(void) +{ + int err; + + err = nf_nat_l4proto_register(NFPROTO_IPV4, &nf_nat_l4proto_dccp); + if (err < 0) + goto err1; + err = nf_nat_l4proto_register(NFPROTO_IPV6, &nf_nat_l4proto_dccp); + if (err < 0) + goto err2; + return 0; + +err2: + nf_nat_l4proto_unregister(NFPROTO_IPV4, &nf_nat_l4proto_dccp); +err1: + return err; +} + +static void __exit nf_nat_proto_dccp_fini(void) +{ + nf_nat_l4proto_unregister(NFPROTO_IPV6, &nf_nat_l4proto_dccp); + nf_nat_l4proto_unregister(NFPROTO_IPV4, &nf_nat_l4proto_dccp); + +} + +module_init(nf_nat_proto_dccp_init); +module_exit(nf_nat_proto_dccp_fini); + +MODULE_AUTHOR("Patrick McHardy "); +MODULE_DESCRIPTION("DCCP NAT protocol helper"); +MODULE_LICENSE("GPL"); diff --git a/net/netfilter/nf_nat_proto_sctp.c b/net/netfilter/nf_nat_proto_sctp.c new file mode 100644 index 000000000000..e64faa5ca893 --- /dev/null +++ b/net/netfilter/nf_nat_proto_sctp.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2008 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 + +static u_int16_t nf_sctp_port_rover; + +static void +sctp_unique_tuple(const struct nf_nat_l3proto *l3proto, + struct nf_conntrack_tuple *tuple, + const struct nf_nat_range *range, + enum nf_nat_manip_type maniptype, + const struct nf_conn *ct) +{ + nf_nat_l4proto_unique_tuple(l3proto, tuple, range, maniptype, ct, + &nf_sctp_port_rover); +} + +static bool +sctp_manip_pkt(struct sk_buff *skb, + const struct nf_nat_l3proto *l3proto, + unsigned int iphdroff, unsigned int hdroff, + const struct nf_conntrack_tuple *tuple, + enum nf_nat_manip_type maniptype) +{ + struct sk_buff *frag; + sctp_sctphdr_t *hdr; + __be32 crc32; + + if (!skb_make_writable(skb, hdroff + sizeof(*hdr))) + return false; + + hdr = (struct sctphdr *)(skb->data + hdroff); + + if (maniptype == NF_NAT_MANIP_SRC) { + /* Get rid of src port */ + hdr->source = tuple->src.u.sctp.port; + } else { + /* Get rid of dst port */ + 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); + crc32 = sctp_end_cksum(crc32); + hdr->checksum = crc32; + + return true; +} + +static const struct nf_nat_l4proto nf_nat_l4proto_sctp = { + .l4proto = IPPROTO_SCTP, + .manip_pkt = sctp_manip_pkt, + .in_range = nf_nat_l4proto_in_range, + .unique_tuple = sctp_unique_tuple, +#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) + .nlattr_to_range = nf_nat_l4proto_nlattr_to_range, +#endif +}; + +static int __init nf_nat_proto_sctp_init(void) +{ + int err; + + err = nf_nat_l4proto_register(NFPROTO_IPV4, &nf_nat_l4proto_sctp); + if (err < 0) + goto err1; + err = nf_nat_l4proto_register(NFPROTO_IPV6, &nf_nat_l4proto_sctp); + if (err < 0) + goto err2; + return 0; + +err2: + nf_nat_l4proto_unregister(NFPROTO_IPV4, &nf_nat_l4proto_sctp); +err1: + return err; +} + +static void __exit nf_nat_proto_sctp_exit(void) +{ + nf_nat_l4proto_unregister(NFPROTO_IPV6, &nf_nat_l4proto_sctp); + nf_nat_l4proto_unregister(NFPROTO_IPV4, &nf_nat_l4proto_sctp); +} + +module_init(nf_nat_proto_sctp_init); +module_exit(nf_nat_proto_sctp_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SCTP NAT protocol helper"); +MODULE_AUTHOR("Patrick McHardy "); diff --git a/net/netfilter/nf_nat_proto_tcp.c b/net/netfilter/nf_nat_proto_tcp.c new file mode 100644 index 000000000000..83ec8a6e4c36 --- /dev/null +++ b/net/netfilter/nf_nat_proto_tcp.c @@ -0,0 +1,85 @@ +/* (C) 1999-2001 Paul `Rusty' Russell + * (C) 2002-2006 Netfilter Core Team + * + * 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 + +static u16 tcp_port_rover; + +static void +tcp_unique_tuple(const struct nf_nat_l3proto *l3proto, + struct nf_conntrack_tuple *tuple, + const struct nf_nat_range *range, + enum nf_nat_manip_type maniptype, + const struct nf_conn *ct) +{ + nf_nat_l4proto_unique_tuple(l3proto, tuple, range, maniptype, ct, + &tcp_port_rover); +} + +static bool +tcp_manip_pkt(struct sk_buff *skb, + const struct nf_nat_l3proto *l3proto, + unsigned int iphdroff, unsigned int hdroff, + const struct nf_conntrack_tuple *tuple, + enum nf_nat_manip_type maniptype) +{ + struct tcphdr *hdr; + __be16 *portptr, newport, oldport; + int hdrsize = 8; /* TCP connection tracking guarantees this much */ + + /* this could be a inner header returned in icmp packet; in such + cases we cannot update the checksum field since it is outside of + the 8 bytes of transport layer headers we are guaranteed */ + if (skb->len >= hdroff + sizeof(struct tcphdr)) + hdrsize = sizeof(struct tcphdr); + + if (!skb_make_writable(skb, hdroff + hdrsize)) + return false; + + hdr = (struct tcphdr *)(skb->data + hdroff); + + if (maniptype == NF_NAT_MANIP_SRC) { + /* Get rid of src port */ + newport = tuple->src.u.tcp.port; + portptr = &hdr->source; + } else { + /* Get rid of dst port */ + newport = tuple->dst.u.tcp.port; + portptr = &hdr->dest; + } + + oldport = *portptr; + *portptr = newport; + + if (hdrsize < sizeof(*hdr)) + return true; + + l3proto->csum_update(skb, iphdroff, &hdr->check, tuple, maniptype); + inet_proto_csum_replace2(&hdr->check, skb, oldport, newport, 0); + return true; +} + +const struct nf_nat_l4proto nf_nat_l4proto_tcp = { + .l4proto = IPPROTO_TCP, + .manip_pkt = tcp_manip_pkt, + .in_range = nf_nat_l4proto_in_range, + .unique_tuple = tcp_unique_tuple, +#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) + .nlattr_to_range = nf_nat_l4proto_nlattr_to_range, +#endif +}; diff --git a/net/netfilter/nf_nat_proto_udp.c b/net/netfilter/nf_nat_proto_udp.c new file mode 100644 index 000000000000..7df613fb34a2 --- /dev/null +++ b/net/netfilter/nf_nat_proto_udp.c @@ -0,0 +1,76 @@ +/* (C) 1999-2001 Paul `Rusty' Russell + * (C) 2002-2006 Netfilter Core Team + * + * 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 u16 udp_port_rover; + +static void +udp_unique_tuple(const struct nf_nat_l3proto *l3proto, + struct nf_conntrack_tuple *tuple, + const struct nf_nat_range *range, + enum nf_nat_manip_type maniptype, + const struct nf_conn *ct) +{ + nf_nat_l4proto_unique_tuple(l3proto, tuple, range, maniptype, ct, + &udp_port_rover); +} + +static bool +udp_manip_pkt(struct sk_buff *skb, + const struct nf_nat_l3proto *l3proto, + unsigned int iphdroff, unsigned int hdroff, + const struct nf_conntrack_tuple *tuple, + enum nf_nat_manip_type maniptype) +{ + struct udphdr *hdr; + __be16 *portptr, newport; + + if (!skb_make_writable(skb, hdroff + sizeof(*hdr))) + return false; + hdr = (struct udphdr *)(skb->data + hdroff); + + if (maniptype == NF_NAT_MANIP_SRC) { + /* Get rid of src port */ + newport = tuple->src.u.udp.port; + portptr = &hdr->source; + } else { + /* Get rid of dst port */ + newport = tuple->dst.u.udp.port; + portptr = &hdr->dest; + } + if (hdr->check || skb->ip_summed == CHECKSUM_PARTIAL) { + l3proto->csum_update(skb, iphdroff, &hdr->check, + tuple, maniptype); + inet_proto_csum_replace2(&hdr->check, skb, *portptr, newport, + 0); + if (!hdr->check) + hdr->check = CSUM_MANGLED_0; + } + *portptr = newport; + return true; +} + +const struct nf_nat_l4proto nf_nat_l4proto_udp = { + .l4proto = IPPROTO_UDP, + .manip_pkt = udp_manip_pkt, + .in_range = nf_nat_l4proto_in_range, + .unique_tuple = udp_unique_tuple, +#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) + .nlattr_to_range = nf_nat_l4proto_nlattr_to_range, +#endif +}; diff --git a/net/netfilter/nf_nat_proto_udplite.c b/net/netfilter/nf_nat_proto_udplite.c new file mode 100644 index 000000000000..776a0d1317b1 --- /dev/null +++ b/net/netfilter/nf_nat_proto_udplite.c @@ -0,0 +1,106 @@ +/* (C) 1999-2001 Paul `Rusty' Russell + * (C) 2002-2006 Netfilter Core Team + * (C) 2008 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 + +static u16 udplite_port_rover; + +static void +udplite_unique_tuple(const struct nf_nat_l3proto *l3proto, + struct nf_conntrack_tuple *tuple, + const struct nf_nat_range *range, + enum nf_nat_manip_type maniptype, + const struct nf_conn *ct) +{ + nf_nat_l4proto_unique_tuple(l3proto, tuple, range, maniptype, ct, + &udplite_port_rover); +} + +static bool +udplite_manip_pkt(struct sk_buff *skb, + const struct nf_nat_l3proto *l3proto, + unsigned int iphdroff, unsigned int hdroff, + const struct nf_conntrack_tuple *tuple, + enum nf_nat_manip_type maniptype) +{ + struct udphdr *hdr; + __be16 *portptr, newport; + + if (!skb_make_writable(skb, hdroff + sizeof(*hdr))) + return false; + + hdr = (struct udphdr *)(skb->data + hdroff); + + if (maniptype == NF_NAT_MANIP_SRC) { + /* Get rid of source port */ + newport = tuple->src.u.udp.port; + portptr = &hdr->source; + } else { + /* Get rid of dst port */ + newport = tuple->dst.u.udp.port; + portptr = &hdr->dest; + } + + l3proto->csum_update(skb, iphdroff, &hdr->check, tuple, maniptype); + inet_proto_csum_replace2(&hdr->check, skb, *portptr, newport, 0); + if (!hdr->check) + hdr->check = CSUM_MANGLED_0; + + *portptr = newport; + return true; +} + +static const struct nf_nat_l4proto nf_nat_l4proto_udplite = { + .l4proto = IPPROTO_UDPLITE, + .manip_pkt = udplite_manip_pkt, + .in_range = nf_nat_l4proto_in_range, + .unique_tuple = udplite_unique_tuple, +#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) + .nlattr_to_range = nf_nat_l4proto_nlattr_to_range, +#endif +}; + +static int __init nf_nat_proto_udplite_init(void) +{ + int err; + + err = nf_nat_l4proto_register(NFPROTO_IPV4, &nf_nat_l4proto_udplite); + if (err < 0) + goto err1; + err = nf_nat_l4proto_register(NFPROTO_IPV6, &nf_nat_l4proto_udplite); + if (err < 0) + goto err2; + return 0; + +err2: + nf_nat_l4proto_unregister(NFPROTO_IPV4, &nf_nat_l4proto_udplite); +err1: + return err; +} + +static void __exit nf_nat_proto_udplite_fini(void) +{ + nf_nat_l4proto_unregister(NFPROTO_IPV6, &nf_nat_l4proto_udplite); + nf_nat_l4proto_unregister(NFPROTO_IPV4, &nf_nat_l4proto_udplite); +} + +module_init(nf_nat_proto_udplite_init); +module_exit(nf_nat_proto_udplite_fini); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("UDP-Lite NAT protocol helper"); +MODULE_AUTHOR("Patrick McHardy "); diff --git a/net/netfilter/nf_nat_proto_unknown.c b/net/netfilter/nf_nat_proto_unknown.c new file mode 100644 index 000000000000..6e494d584412 --- /dev/null +++ b/net/netfilter/nf_nat_proto_unknown.c @@ -0,0 +1,54 @@ +/* The "unknown" protocol. This is what is used for protocols we + * don't understand. It's returned by ip_ct_find_proto(). + */ + +/* (C) 1999-2001 Paul `Rusty' Russell + * (C) 2002-2006 Netfilter Core Team + * + * 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 + +static bool unknown_in_range(const struct nf_conntrack_tuple *tuple, + enum nf_nat_manip_type manip_type, + const union nf_conntrack_man_proto *min, + const union nf_conntrack_man_proto *max) +{ + return true; +} + +static void unknown_unique_tuple(const struct nf_nat_l3proto *l3proto, + struct nf_conntrack_tuple *tuple, + const struct nf_nat_range *range, + enum nf_nat_manip_type maniptype, + const struct nf_conn *ct) +{ + /* Sorry: we can't help you; if it's not unique, we can't frob + * anything. + */ + return; +} + +static bool +unknown_manip_pkt(struct sk_buff *skb, + const struct nf_nat_l3proto *l3proto, + unsigned int iphdroff, unsigned int hdroff, + const struct nf_conntrack_tuple *tuple, + enum nf_nat_manip_type maniptype) +{ + return true; +} + +const struct nf_nat_l4proto nf_nat_l4proto_unknown = { + .manip_pkt = unknown_manip_pkt, + .in_range = unknown_in_range, + .unique_tuple = unknown_unique_tuple, +}; diff --git a/net/netfilter/xt_nat.c b/net/netfilter/xt_nat.c new file mode 100644 index 000000000000..7521368a6034 --- /dev/null +++ b/net/netfilter/xt_nat.c @@ -0,0 +1,167 @@ +/* + * (C) 1999-2001 Paul `Rusty' Russell + * (C) 2002-2006 Netfilter Core Team + * (C) 2011 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 + +static int xt_nat_checkentry_v0(const struct xt_tgchk_param *par) +{ + const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo; + + if (mr->rangesize != 1) { + pr_info("%s: multiple ranges no longer supported\n", + par->target->name); + return -EINVAL; + } + return 0; +} + +static void xt_nat_convert_range(struct nf_nat_range *dst, + const struct nf_nat_ipv4_range *src) +{ + memset(&dst->min_addr, 0, sizeof(dst->min_addr)); + memset(&dst->max_addr, 0, sizeof(dst->max_addr)); + + dst->flags = src->flags; + dst->min_addr.ip = src->min_ip; + dst->max_addr.ip = src->max_ip; + dst->min_proto = src->min; + dst->max_proto = src->max; +} + +static unsigned int +xt_snat_target_v0(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo; + struct nf_nat_range range; + enum ip_conntrack_info ctinfo; + struct nf_conn *ct; + + ct = nf_ct_get(skb, &ctinfo); + NF_CT_ASSERT(ct != NULL && + (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || + ctinfo == IP_CT_RELATED_REPLY)); + + xt_nat_convert_range(&range, &mr->range[0]); + return nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC); +} + +static unsigned int +xt_dnat_target_v0(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo; + struct nf_nat_range range; + enum ip_conntrack_info ctinfo; + struct nf_conn *ct; + + ct = nf_ct_get(skb, &ctinfo); + NF_CT_ASSERT(ct != NULL && + (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); + + xt_nat_convert_range(&range, &mr->range[0]); + return nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST); +} + +static unsigned int +xt_snat_target_v1(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct nf_nat_range *range = par->targinfo; + enum ip_conntrack_info ctinfo; + struct nf_conn *ct; + + ct = nf_ct_get(skb, &ctinfo); + NF_CT_ASSERT(ct != NULL && + (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || + ctinfo == IP_CT_RELATED_REPLY)); + + return nf_nat_setup_info(ct, range, NF_NAT_MANIP_SRC); +} + +static unsigned int +xt_dnat_target_v1(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct nf_nat_range *range = par->targinfo; + enum ip_conntrack_info ctinfo; + struct nf_conn *ct; + + ct = nf_ct_get(skb, &ctinfo); + NF_CT_ASSERT(ct != NULL && + (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); + + return nf_nat_setup_info(ct, range, NF_NAT_MANIP_DST); +} + +static struct xt_target xt_nat_target_reg[] __read_mostly = { + { + .name = "SNAT", + .revision = 0, + .checkentry = xt_nat_checkentry_v0, + .target = xt_snat_target_v0, + .targetsize = sizeof(struct nf_nat_ipv4_multi_range_compat), + .family = NFPROTO_IPV4, + .table = "nat", + .hooks = (1 << NF_INET_POST_ROUTING) | + (1 << NF_INET_LOCAL_OUT), + .me = THIS_MODULE, + }, + { + .name = "DNAT", + .revision = 0, + .checkentry = xt_nat_checkentry_v0, + .target = xt_dnat_target_v0, + .targetsize = sizeof(struct nf_nat_ipv4_multi_range_compat), + .family = NFPROTO_IPV4, + .table = "nat", + .hooks = (1 << NF_INET_PRE_ROUTING) | + (1 << NF_INET_LOCAL_IN), + .me = THIS_MODULE, + }, + { + .name = "SNAT", + .revision = 1, + .target = xt_snat_target_v1, + .targetsize = sizeof(struct nf_nat_range), + .table = "nat", + .hooks = (1 << NF_INET_POST_ROUTING) | + (1 << NF_INET_LOCAL_OUT), + .me = THIS_MODULE, + }, + { + .name = "DNAT", + .revision = 1, + .target = xt_dnat_target_v1, + .targetsize = sizeof(struct nf_nat_range), + .table = "nat", + .hooks = (1 << NF_INET_PRE_ROUTING) | + (1 << NF_INET_LOCAL_IN), + .me = THIS_MODULE, + }, +}; + +static int __init xt_nat_init(void) +{ + return xt_register_targets(xt_nat_target_reg, + ARRAY_SIZE(xt_nat_target_reg)); +} + +static void __exit xt_nat_exit(void) +{ + xt_unregister_targets(xt_nat_target_reg, ARRAY_SIZE(xt_nat_target_reg)); +} + +module_init(xt_nat_init); +module_exit(xt_nat_exit); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ipt_SNAT"); +MODULE_ALIAS("ipt_DNAT"); -- cgit v1.2.3 From 2cf545e835aae92173ef0b1f4af385e9c40f21e8 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sun, 26 Aug 2012 19:14:10 +0200 Subject: net: core: add function for incremental IPv6 pseudo header checksum updates Add inet_proto_csum_replace16 for incrementally updating IPv6 pseudo header checksums for IPv6 NAT. Signed-off-by: Patrick McHardy Acked-by: David S. Miller --- include/net/checksum.h | 3 +++ net/core/utils.c | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+) (limited to 'include') diff --git a/include/net/checksum.h b/include/net/checksum.h index ba55d8b8c87c..600d1d705bb8 100644 --- a/include/net/checksum.h +++ b/include/net/checksum.h @@ -109,6 +109,9 @@ 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); static inline void inet_proto_csum_replace2(__sum16 *sum, struct sk_buff *skb, __be16 from, __be16 to, diff --git a/net/core/utils.c b/net/core/utils.c index 39895a65e54a..f5613d569c23 100644 --- a/net/core/utils.c +++ b/net/core/utils.c @@ -294,6 +294,26 @@ void inet_proto_csum_replace4(__sum16 *sum, struct sk_buff *skb, } EXPORT_SYMBOL(inet_proto_csum_replace4); +void inet_proto_csum_replace16(__sum16 *sum, struct sk_buff *skb, + const __be32 *from, const __be32 *to, + int pseudohdr) +{ + __be32 diff[] = { + ~from[0], ~from[1], ~from[2], ~from[3], + to[0], to[1], to[2], to[3], + }; + if (skb->ip_summed != CHECKSUM_PARTIAL) { + *sum = csum_fold(csum_partial(diff, sizeof(diff), + ~csum_unfold(*sum))); + if (skb->ip_summed == CHECKSUM_COMPLETE && pseudohdr) + skb->csum = ~csum_partial(diff, sizeof(diff), + ~skb->csum); + } else if (pseudohdr) + *sum = ~csum_fold(csum_partial(diff, sizeof(diff), + csum_unfold(*sum))); +} +EXPORT_SYMBOL(inet_proto_csum_replace16); + int mac_pton(const char *s, u8 *mac) { int i; -- cgit v1.2.3 From 58a317f1061c894d2344c0b6a18ab4a64b69b815 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sun, 26 Aug 2012 19:14:12 +0200 Subject: netfilter: ipv6: add IPv6 NAT support Signed-off-by: Patrick McHardy --- include/linux/netfilter/nfnetlink_conntrack.h | 2 + include/net/netfilter/nf_nat_l3proto.h | 5 + include/net/netfilter/nf_nat_l4proto.h | 1 + include/net/netns/ipv6.h | 1 + net/core/secure_seq.c | 1 + net/ipv6/netfilter/Kconfig | 12 + net/ipv6/netfilter/Makefile | 4 + net/ipv6/netfilter/ip6table_nat.c | 321 +++++++++++++++++++++++++ net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 37 ++- net/ipv6/netfilter/nf_nat_l3proto_ipv6.c | 287 ++++++++++++++++++++++ net/ipv6/netfilter/nf_nat_proto_icmpv6.c | 90 +++++++ net/netfilter/nf_nat_core.c | 2 + net/netfilter/xt_nat.c | 3 + 13 files changed, 764 insertions(+), 2 deletions(-) create mode 100644 net/ipv6/netfilter/ip6table_nat.c create mode 100644 net/ipv6/netfilter/nf_nat_l3proto_ipv6.c create mode 100644 net/ipv6/netfilter/nf_nat_proto_icmpv6.c (limited to 'include') diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h index 68920eab287c..43bfe3e1685b 100644 --- a/include/linux/netfilter/nfnetlink_conntrack.h +++ b/include/linux/netfilter/nfnetlink_conntrack.h @@ -147,6 +147,8 @@ enum ctattr_nat { CTA_NAT_V4_MAXIP, #define CTA_NAT_MAXIP CTA_NAT_V4_MAXIP CTA_NAT_PROTO, + CTA_NAT_V6_MINIP, + CTA_NAT_V6_MAXIP, __CTA_NAT_MAX }; #define CTA_NAT_MAX (__CTA_NAT_MAX - 1) diff --git a/include/net/netfilter/nf_nat_l3proto.h b/include/net/netfilter/nf_nat_l3proto.h index beed96961fa7..bd3b97e02c82 100644 --- a/include/net/netfilter/nf_nat_l3proto.h +++ b/include/net/netfilter/nf_nat_l3proto.h @@ -43,5 +43,10 @@ extern int nf_nat_icmp_reply_translation(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, unsigned int hooknum); +extern int nf_nat_icmpv6_reply_translation(struct sk_buff *skb, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + unsigned int hdrlen); #endif /* _NF_NAT_L3PROTO_H */ diff --git a/include/net/netfilter/nf_nat_l4proto.h b/include/net/netfilter/nf_nat_l4proto.h index 1f0a4f018fcf..24feb68d1bcc 100644 --- a/include/net/netfilter/nf_nat_l4proto.h +++ b/include/net/netfilter/nf_nat_l4proto.h @@ -51,6 +51,7 @@ extern const struct nf_nat_l4proto *__nf_nat_l4proto_find(u8 l3proto, u8 l4proto extern const struct nf_nat_l4proto nf_nat_l4proto_tcp; extern const struct nf_nat_l4proto nf_nat_l4proto_udp; extern const struct nf_nat_l4proto nf_nat_l4proto_icmp; +extern const struct nf_nat_l4proto nf_nat_l4proto_icmpv6; extern const struct nf_nat_l4proto nf_nat_l4proto_unknown; extern bool nf_nat_l4proto_in_range(const struct nf_conntrack_tuple *tuple, diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index df0a5456a3fd..0318104a9458 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -42,6 +42,7 @@ struct netns_ipv6 { #ifdef CONFIG_SECURITY struct xt_table *ip6table_security; #endif + struct xt_table *ip6table_nat; #endif struct rt6_info *ip6_null_entry; struct rt6_statistics *rt6_stats; diff --git a/net/core/secure_seq.c b/net/core/secure_seq.c index 99b2596531bb..e61a8bb7fce7 100644 --- a/net/core/secure_seq.c +++ b/net/core/secure_seq.c @@ -76,6 +76,7 @@ u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr, return hash[0]; } +EXPORT_SYMBOL(secure_ipv6_port_ephemeral); #endif #ifdef CONFIG_INET diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index 10135342799e..b27e0ad4b738 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig @@ -25,6 +25,18 @@ config NF_CONNTRACK_IPV6 To compile it as a module, choose M here. If unsure, say N. +config NF_NAT_IPV6 + tristate "IPv6 NAT" + depends on NF_CONNTRACK_IPV6 + depends on NETFILTER_ADVANCED + select NF_NAT + help + The IPv6 NAT option allows masquerading, port forwarding and other + forms of full Network Address Port Translation. It is controlled by + the `nat' table in ip6tables, see the man page for ip6tables(8). + + To compile it as a module, choose M here. If unsure, say N. + config IP6_NF_IPTABLES tristate "IP6 tables support (required for filtering)" depends on INET && IPV6 diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile index 534d3f216f7b..76779376da4c 100644 --- a/net/ipv6/netfilter/Makefile +++ b/net/ipv6/netfilter/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_IP6_NF_FILTER) += ip6table_filter.o obj-$(CONFIG_IP6_NF_MANGLE) += ip6table_mangle.o obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o obj-$(CONFIG_IP6_NF_SECURITY) += ip6table_security.o +obj-$(CONFIG_NF_NAT_IPV6) += ip6table_nat.o # objects for l3 independent conntrack nf_conntrack_ipv6-y := nf_conntrack_l3proto_ipv6.o nf_conntrack_proto_icmpv6.o @@ -15,6 +16,9 @@ nf_conntrack_ipv6-y := nf_conntrack_l3proto_ipv6.o nf_conntrack_proto_icmpv6.o # l3 independent conntrack obj-$(CONFIG_NF_CONNTRACK_IPV6) += nf_conntrack_ipv6.o nf_defrag_ipv6.o +nf_nat_ipv6-y := nf_nat_l3proto_ipv6.o nf_nat_proto_icmpv6.o +obj-$(CONFIG_NF_NAT_IPV6) += nf_nat_ipv6.o + # defrag nf_defrag_ipv6-y := nf_defrag_ipv6_hooks.o nf_conntrack_reasm.o obj-$(CONFIG_NF_DEFRAG_IPV6) += nf_defrag_ipv6.o diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c new file mode 100644 index 000000000000..e418bd6350a4 --- /dev/null +++ b/net/ipv6/netfilter/ip6table_nat.c @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2011 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. + * + * Based on Rusty Russell's IPv4 NAT code. Development of IPv6 NAT + * funded by Astaro. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static const struct xt_table nf_nat_ipv6_table = { + .name = "nat", + .valid_hooks = (1 << NF_INET_PRE_ROUTING) | + (1 << NF_INET_POST_ROUTING) | + (1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_LOCAL_IN), + .me = THIS_MODULE, + .af = NFPROTO_IPV6, +}; + +static unsigned int alloc_null_binding(struct nf_conn *ct, unsigned int hooknum) +{ + /* Force range to this IP; let proto decide mapping for + * per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED). + */ + struct nf_nat_range range; + + range.flags = 0; + pr_debug("Allocating NULL binding for %p (%pI6)\n", ct, + HOOK2MANIP(hooknum) == NF_NAT_MANIP_SRC ? + &ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip6 : + &ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip6); + + return nf_nat_setup_info(ct, &range, HOOK2MANIP(hooknum)); +} + +static unsigned int nf_nat_rule_find(struct sk_buff *skb, unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + struct nf_conn *ct) +{ + struct net *net = nf_ct_net(ct); + unsigned int ret; + + ret = ip6t_do_table(skb, hooknum, in, out, net->ipv6.ip6table_nat); + if (ret == NF_ACCEPT) { + if (!nf_nat_initialized(ct, HOOK2MANIP(hooknum))) + ret = alloc_null_binding(ct, hooknum); + } + return ret; +} + +static unsigned int +nf_nat_ipv6_fn(unsigned int hooknum, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + struct nf_conn *ct; + enum ip_conntrack_info ctinfo; + struct nf_conn_nat *nat; + enum nf_nat_manip_type maniptype = HOOK2MANIP(hooknum); + __be16 frag_off; + int hdrlen; + u8 nexthdr; + + ct = nf_ct_get(skb, &ctinfo); + /* Can't track? It's not due to stress, or conntrack would + * have dropped it. Hence it's the user's responsibilty to + * packet filter it out, or implement conntrack/NAT for that + * protocol. 8) --RR + */ + if (!ct) + return NF_ACCEPT; + + /* Don't try to NAT if this packet is not conntracked */ + if (nf_ct_is_untracked(ct)) + return NF_ACCEPT; + + nat = nfct_nat(ct); + if (!nat) { + /* NAT module was loaded late. */ + if (nf_ct_is_confirmed(ct)) + return NF_ACCEPT; + nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); + if (nat == NULL) { + pr_debug("failed to add NAT extension\n"); + return NF_ACCEPT; + } + } + + switch (ctinfo) { + case IP_CT_RELATED: + case IP_CT_RELATED_REPLY: + nexthdr = ipv6_hdr(skb)->nexthdr; + hdrlen = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), + &nexthdr, &frag_off); + + if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) { + if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo, + hooknum, hdrlen)) + return NF_DROP; + else + return NF_ACCEPT; + } + /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */ + case IP_CT_NEW: + /* Seen it before? This can happen for loopback, retrans, + * or local packets. + */ + if (!nf_nat_initialized(ct, maniptype)) { + unsigned int ret; + + ret = nf_nat_rule_find(skb, hooknum, in, out, ct); + if (ret != NF_ACCEPT) + return ret; + } else + pr_debug("Already setup manip %s for ct %p\n", + maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST", + ct); + break; + + default: + /* ESTABLISHED */ + NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED || + ctinfo == IP_CT_ESTABLISHED_REPLY); + } + + return nf_nat_packet(ct, ctinfo, hooknum, skb); +} + +static unsigned int +nf_nat_ipv6_in(unsigned int hooknum, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + unsigned int ret; + struct in6_addr daddr = ipv6_hdr(skb)->daddr; + + ret = nf_nat_ipv6_fn(hooknum, skb, in, out, okfn); + if (ret != NF_DROP && ret != NF_STOLEN && + ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr)) + skb_dst_drop(skb); + + return ret; +} + +static unsigned int +nf_nat_ipv6_out(unsigned int hooknum, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ +#ifdef CONFIG_XFRM + const struct nf_conn *ct; + enum ip_conntrack_info ctinfo; +#endif + unsigned int ret; + + /* root is playing with raw sockets. */ + if (skb->len < sizeof(struct ipv6hdr)) + return NF_ACCEPT; + + ret = nf_nat_ipv6_fn(hooknum, skb, in, out, okfn); +#ifdef CONFIG_XFRM + if (ret != NF_DROP && ret != NF_STOLEN && + !(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && + (ct = nf_ct_get(skb, &ctinfo)) != NULL) { + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + + if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, + &ct->tuplehash[!dir].tuple.dst.u3) || + (ct->tuplehash[dir].tuple.src.u.all != + ct->tuplehash[!dir].tuple.dst.u.all)) + if (nf_xfrm_me_harder(skb, AF_INET6) < 0) + ret = NF_DROP; + } +#endif + return ret; +} + +static unsigned int +nf_nat_ipv6_local_fn(unsigned int hooknum, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + const struct nf_conn *ct; + enum ip_conntrack_info ctinfo; + unsigned int ret; + + /* root is playing with raw sockets. */ + if (skb->len < sizeof(struct ipv6hdr)) + return NF_ACCEPT; + + ret = nf_nat_ipv6_fn(hooknum, skb, in, out, okfn); + if (ret != NF_DROP && ret != NF_STOLEN && + (ct = nf_ct_get(skb, &ctinfo)) != NULL) { + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + + if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, + &ct->tuplehash[!dir].tuple.src.u3)) { + if (ip6_route_me_harder(skb)) + ret = NF_DROP; + } +#ifdef CONFIG_XFRM + else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && + ct->tuplehash[dir].tuple.dst.u.all != + ct->tuplehash[!dir].tuple.src.u.all) + if (nf_xfrm_me_harder(skb, AF_INET6)) + ret = NF_DROP; +#endif + } + return ret; +} + +static struct nf_hook_ops nf_nat_ipv6_ops[] __read_mostly = { + /* Before packet filtering, change destination */ + { + .hook = nf_nat_ipv6_in, + .owner = THIS_MODULE, + .pf = NFPROTO_IPV6, + .hooknum = NF_INET_PRE_ROUTING, + .priority = NF_IP6_PRI_NAT_DST, + }, + /* After packet filtering, change source */ + { + .hook = nf_nat_ipv6_out, + .owner = THIS_MODULE, + .pf = NFPROTO_IPV6, + .hooknum = NF_INET_POST_ROUTING, + .priority = NF_IP6_PRI_NAT_SRC, + }, + /* Before packet filtering, change destination */ + { + .hook = nf_nat_ipv6_local_fn, + .owner = THIS_MODULE, + .pf = NFPROTO_IPV6, + .hooknum = NF_INET_LOCAL_OUT, + .priority = NF_IP6_PRI_NAT_DST, + }, + /* After packet filtering, change source */ + { + .hook = nf_nat_ipv6_fn, + .owner = THIS_MODULE, + .pf = NFPROTO_IPV6, + .hooknum = NF_INET_LOCAL_IN, + .priority = NF_IP6_PRI_NAT_SRC, + }, +}; + +static int __net_init ip6table_nat_net_init(struct net *net) +{ + struct ip6t_replace *repl; + + repl = ip6t_alloc_initial_table(&nf_nat_ipv6_table); + if (repl == NULL) + return -ENOMEM; + net->ipv6.ip6table_nat = ip6t_register_table(net, &nf_nat_ipv6_table, repl); + kfree(repl); + if (IS_ERR(net->ipv6.ip6table_nat)) + return PTR_ERR(net->ipv6.ip6table_nat); + return 0; +} + +static void __net_exit ip6table_nat_net_exit(struct net *net) +{ + ip6t_unregister_table(net, net->ipv6.ip6table_nat); +} + +static struct pernet_operations ip6table_nat_net_ops = { + .init = ip6table_nat_net_init, + .exit = ip6table_nat_net_exit, +}; + +static int __init ip6table_nat_init(void) +{ + int err; + + err = register_pernet_subsys(&ip6table_nat_net_ops); + if (err < 0) + goto err1; + + err = nf_register_hooks(nf_nat_ipv6_ops, ARRAY_SIZE(nf_nat_ipv6_ops)); + if (err < 0) + goto err2; + return 0; + +err2: + unregister_pernet_subsys(&ip6table_nat_net_ops); +err1: + return err; +} + +static void __exit ip6table_nat_exit(void) +{ + nf_unregister_hooks(nf_nat_ipv6_ops, ARRAY_SIZE(nf_nat_ipv6_ops)); + unregister_pernet_subsys(&ip6table_nat_net_ops); +} + +module_init(ip6table_nat_init); +module_exit(ip6table_nat_exit); + +MODULE_LICENSE("GPL"); diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index dcf6010f68d9..8860d23e61cf 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 @@ -142,6 +143,36 @@ static unsigned int ipv6_confirm(unsigned int hooknum, const struct net_device *out, int (*okfn)(struct sk_buff *)) { + struct nf_conn *ct; + enum ip_conntrack_info ctinfo; + unsigned char pnum = ipv6_hdr(skb)->nexthdr; + int protoff; + __be16 frag_off; + + ct = nf_ct_get(skb, &ctinfo); + if (!ct || ctinfo == IP_CT_RELATED_REPLY) + goto out; + + protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &pnum, + &frag_off); + if (protoff < 0 || (frag_off & htons(~0x7)) != 0) { + pr_debug("proto header not found\n"); + goto out; + } + + /* 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)) { + NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop); + return NF_DROP; + } + } +out: /* We've seen it coming out the other side: confirm it */ return nf_conntrack_confirm(skb); } @@ -170,12 +201,14 @@ static unsigned int __ipv6_conntrack_in(struct net *net, } /* Conntrack helpers need the entire reassembled packet in the - * POST_ROUTING hook. + * POST_ROUTING hook. In case of unconfirmed connections NAT + * might reassign a helper, so the entire packet is also + * required. */ ct = nf_ct_get(reasm, &ctinfo); if (ct != NULL && !nf_ct_is_untracked(ct)) { help = nfct_help(ct); - if (help && help->helper) { + if ((help && help->helper) || !nf_ct_is_confirmed(ct)) { nf_conntrack_get_reasm(skb); NF_HOOK_THRESH(NFPROTO_IPV6, hooknum, reasm, (struct net_device *)in, diff --git a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c new file mode 100644 index 000000000000..81a2d1c3da8e --- /dev/null +++ b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2011 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. + * + * Development of IPv6 NAT funded by Astaro. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static const struct nf_nat_l3proto nf_nat_l3proto_ipv6; + +#ifdef CONFIG_XFRM +static void nf_nat_ipv6_decode_session(struct sk_buff *skb, + const struct nf_conn *ct, + enum ip_conntrack_dir dir, + unsigned long statusbit, + struct flowi *fl) +{ + const struct nf_conntrack_tuple *t = &ct->tuplehash[dir].tuple; + struct flowi6 *fl6 = &fl->u.ip6; + + if (ct->status & statusbit) { + fl6->daddr = t->dst.u3.in6; + if (t->dst.protonum == IPPROTO_TCP || + t->dst.protonum == IPPROTO_UDP || + t->dst.protonum == IPPROTO_UDPLITE || + t->dst.protonum == IPPROTO_DCCP || + t->dst.protonum == IPPROTO_SCTP) + fl6->fl6_dport = t->dst.u.all; + } + + statusbit ^= IPS_NAT_MASK; + + if (ct->status & statusbit) { + fl6->saddr = t->src.u3.in6; + if (t->dst.protonum == IPPROTO_TCP || + t->dst.protonum == IPPROTO_UDP || + t->dst.protonum == IPPROTO_UDPLITE || + t->dst.protonum == IPPROTO_DCCP || + t->dst.protonum == IPPROTO_SCTP) + fl6->fl6_sport = t->src.u.all; + } +} +#endif + +static bool nf_nat_ipv6_in_range(const struct nf_conntrack_tuple *t, + const struct nf_nat_range *range) +{ + return ipv6_addr_cmp(&t->src.u3.in6, &range->min_addr.in6) >= 0 && + ipv6_addr_cmp(&t->src.u3.in6, &range->max_addr.in6) <= 0; +} + +static u32 nf_nat_ipv6_secure_port(const struct nf_conntrack_tuple *t, + __be16 dport) +{ + return secure_ipv6_port_ephemeral(t->src.u3.ip6, t->dst.u3.ip6, dport); +} + +static bool nf_nat_ipv6_manip_pkt(struct sk_buff *skb, + unsigned int iphdroff, + const struct nf_nat_l4proto *l4proto, + const struct nf_conntrack_tuple *target, + enum nf_nat_manip_type maniptype) +{ + struct ipv6hdr *ipv6h; + __be16 frag_off; + int hdroff; + u8 nexthdr; + + if (!skb_make_writable(skb, iphdroff + sizeof(*ipv6h))) + return false; + + ipv6h = (void *)skb->data + iphdroff; + nexthdr = ipv6h->nexthdr; + hdroff = ipv6_skip_exthdr(skb, iphdroff + sizeof(*ipv6h), + &nexthdr, &frag_off); + if (hdroff < 0) + goto manip_addr; + + if ((frag_off & htons(~0x7)) == 0 && + !l4proto->manip_pkt(skb, &nf_nat_l3proto_ipv6, iphdroff, hdroff, + target, maniptype)) + return false; +manip_addr: + if (maniptype == NF_NAT_MANIP_SRC) + ipv6h->saddr = target->src.u3.in6; + else + ipv6h->daddr = target->dst.u3.in6; + + return true; +} + +static void nf_nat_ipv6_csum_update(struct sk_buff *skb, + unsigned int iphdroff, __sum16 *check, + const struct nf_conntrack_tuple *t, + enum nf_nat_manip_type maniptype) +{ + const struct ipv6hdr *ipv6h = (struct ipv6hdr *)(skb->data + iphdroff); + const struct in6_addr *oldip, *newip; + + if (maniptype == NF_NAT_MANIP_SRC) { + oldip = &ipv6h->saddr; + newip = &t->src.u3.in6; + } else { + oldip = &ipv6h->daddr; + newip = &t->dst.u3.in6; + } + inet_proto_csum_replace16(check, skb, oldip->s6_addr32, + newip->s6_addr32, 1); +} + +static void nf_nat_ipv6_csum_recalc(struct sk_buff *skb, + u8 proto, void *data, __sum16 *check, + int datalen, int oldlen) +{ + const struct ipv6hdr *ipv6h = ipv6_hdr(skb); + struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); + + if (skb->ip_summed != CHECKSUM_PARTIAL) { + if (!(rt->rt6i_flags & RTF_LOCAL) && + (!skb->dev || skb->dev->features & NETIF_F_V6_CSUM)) { + skb->ip_summed = CHECKSUM_PARTIAL; + skb->csum_start = skb_headroom(skb) + + skb_network_offset(skb) + + (data - (void *)skb->data); + skb->csum_offset = (void *)check - data; + *check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, + datalen, proto, 0); + } else { + *check = 0; + *check = csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, + datalen, proto, + csum_partial(data, datalen, + 0)); + if (proto == IPPROTO_UDP && !*check) + *check = CSUM_MANGLED_0; + } + } else + inet_proto_csum_replace2(check, skb, + htons(oldlen), htons(datalen), 1); +} + +static int nf_nat_ipv6_nlattr_to_range(struct nlattr *tb[], + struct nf_nat_range *range) +{ + if (tb[CTA_NAT_V6_MINIP]) { + nla_memcpy(&range->min_addr.ip6, tb[CTA_NAT_V6_MINIP], + sizeof(struct in6_addr)); + range->flags |= NF_NAT_RANGE_MAP_IPS; + } + + if (tb[CTA_NAT_V6_MAXIP]) + nla_memcpy(&range->max_addr.ip6, tb[CTA_NAT_V6_MAXIP], + sizeof(struct in6_addr)); + else + range->max_addr = range->min_addr; + + return 0; +} + +static const struct nf_nat_l3proto nf_nat_l3proto_ipv6 = { + .l3proto = NFPROTO_IPV6, + .secure_port = nf_nat_ipv6_secure_port, + .in_range = nf_nat_ipv6_in_range, + .manip_pkt = nf_nat_ipv6_manip_pkt, + .csum_update = nf_nat_ipv6_csum_update, + .csum_recalc = nf_nat_ipv6_csum_recalc, + .nlattr_to_range = nf_nat_ipv6_nlattr_to_range, +#ifdef CONFIG_XFRM + .decode_session = nf_nat_ipv6_decode_session, +#endif +}; + +int nf_nat_icmpv6_reply_translation(struct sk_buff *skb, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + unsigned int hdrlen) +{ + struct { + struct icmp6hdr icmp6; + struct ipv6hdr ip6; + } *inside; + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + enum nf_nat_manip_type manip = HOOK2MANIP(hooknum); + const struct nf_nat_l4proto *l4proto; + struct nf_conntrack_tuple target; + unsigned long statusbit; + + NF_CT_ASSERT(ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED_REPLY); + + if (!skb_make_writable(skb, hdrlen + sizeof(*inside))) + return 0; + if (nf_ip6_checksum(skb, hooknum, hdrlen, IPPROTO_ICMPV6)) + return 0; + + inside = (void *)skb->data + hdrlen; + if (inside->icmp6.icmp6_type == NDISC_REDIRECT) { + if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK) + return 0; + if (ct->status & IPS_NAT_MASK) + return 0; + } + + if (manip == NF_NAT_MANIP_SRC) + statusbit = IPS_SRC_NAT; + else + statusbit = IPS_DST_NAT; + + /* Invert if this is reply direction */ + if (dir == IP_CT_DIR_REPLY) + statusbit ^= IPS_NAT_MASK; + + if (!(ct->status & statusbit)) + return 1; + + l4proto = __nf_nat_l4proto_find(NFPROTO_IPV6, inside->ip6.nexthdr); + if (!nf_nat_ipv6_manip_pkt(skb, hdrlen + sizeof(inside->icmp6), + l4proto, &ct->tuplehash[!dir].tuple, !manip)) + return 0; + + if (skb->ip_summed != CHECKSUM_PARTIAL) { + struct ipv6hdr *ipv6h = ipv6_hdr(skb); + inside = (void *)skb->data + hdrlen; + inside->icmp6.icmp6_cksum = 0; + inside->icmp6.icmp6_cksum = + csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, + skb->len - hdrlen, IPPROTO_ICMPV6, + csum_partial(&inside->icmp6, + skb->len - hdrlen, 0)); + } + + nf_ct_invert_tuplepr(&target, &ct->tuplehash[!dir].tuple); + l4proto = __nf_nat_l4proto_find(NFPROTO_IPV6, IPPROTO_ICMPV6); + if (!nf_nat_ipv6_manip_pkt(skb, 0, l4proto, &target, manip)) + return 0; + + return 1; +} +EXPORT_SYMBOL_GPL(nf_nat_icmpv6_reply_translation); + +static int __init nf_nat_l3proto_ipv6_init(void) +{ + int err; + + err = nf_nat_l4proto_register(NFPROTO_IPV6, &nf_nat_l4proto_icmpv6); + if (err < 0) + goto err1; + err = nf_nat_l3proto_register(&nf_nat_l3proto_ipv6); + if (err < 0) + goto err2; + return err; + +err2: + nf_nat_l4proto_unregister(NFPROTO_IPV6, &nf_nat_l4proto_icmpv6); +err1: + return err; +} + +static void __exit nf_nat_l3proto_ipv6_exit(void) +{ + nf_nat_l3proto_unregister(&nf_nat_l3proto_ipv6); + nf_nat_l4proto_unregister(NFPROTO_IPV6, &nf_nat_l4proto_icmpv6); +} + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("nf-nat-" __stringify(AF_INET6)); + +module_init(nf_nat_l3proto_ipv6_init); +module_exit(nf_nat_l3proto_ipv6_exit); diff --git a/net/ipv6/netfilter/nf_nat_proto_icmpv6.c b/net/ipv6/netfilter/nf_nat_proto_icmpv6.c new file mode 100644 index 000000000000..5d6da784305b --- /dev/null +++ b/net/ipv6/netfilter/nf_nat_proto_icmpv6.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2011 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. + * + * Based on Rusty Russell's IPv4 ICMP NAT code. Development of IPv6 + * NAT funded by Astaro. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +static bool +icmpv6_in_range(const struct nf_conntrack_tuple *tuple, + enum nf_nat_manip_type maniptype, + const union nf_conntrack_man_proto *min, + const union nf_conntrack_man_proto *max) +{ + return ntohs(tuple->src.u.icmp.id) >= ntohs(min->icmp.id) && + ntohs(tuple->src.u.icmp.id) <= ntohs(max->icmp.id); +} + +static void +icmpv6_unique_tuple(const struct nf_nat_l3proto *l3proto, + struct nf_conntrack_tuple *tuple, + const struct nf_nat_range *range, + enum nf_nat_manip_type maniptype, + const struct nf_conn *ct) +{ + static u16 id; + unsigned int range_size; + unsigned int i; + + range_size = ntohs(range->max_proto.icmp.id) - + ntohs(range->min_proto.icmp.id) + 1; + + if (!(range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)) + range_size = 0xffff; + + for (i = 0; ; ++id) { + tuple->src.u.icmp.id = htons(ntohs(range->min_proto.icmp.id) + + (id % range_size)); + if (++i == range_size || !nf_nat_used_tuple(tuple, ct)) + return; + } +} + +static bool +icmpv6_manip_pkt(struct sk_buff *skb, + const struct nf_nat_l3proto *l3proto, + unsigned int iphdroff, unsigned int hdroff, + const struct nf_conntrack_tuple *tuple, + enum nf_nat_manip_type maniptype) +{ + struct icmp6hdr *hdr; + + if (!skb_make_writable(skb, hdroff + sizeof(*hdr))) + return false; + + hdr = (struct icmp6hdr *)(skb->data + hdroff); + l3proto->csum_update(skb, iphdroff, &hdr->icmp6_cksum, + tuple, maniptype); + if (hdr->icmp6_code == ICMPV6_ECHO_REQUEST || + hdr->icmp6_code == ICMPV6_ECHO_REPLY) { + inet_proto_csum_replace2(&hdr->icmp6_cksum, skb, + hdr->icmp6_identifier, + tuple->src.u.icmp.id, 0); + hdr->icmp6_identifier = tuple->src.u.icmp.id; + } + return true; +} + +const struct nf_nat_l4proto nf_nat_l4proto_icmpv6 = { + .l4proto = IPPROTO_ICMPV6, + .manip_pkt = icmpv6_manip_pkt, + .in_range = icmpv6_in_range, + .unique_tuple = icmpv6_unique_tuple, +#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) + .nlattr_to_range = nf_nat_l4proto_nlattr_to_range, +#endif +}; diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index c577b753fb9a..29d445235199 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -696,6 +696,8 @@ static int nfnetlink_parse_nat_proto(struct nlattr *attr, static const struct nla_policy nat_nla_policy[CTA_NAT_MAX+1] = { [CTA_NAT_V4_MINIP] = { .type = NLA_U32 }, [CTA_NAT_V4_MAXIP] = { .type = NLA_U32 }, + [CTA_NAT_V6_MINIP] = { .len = sizeof(struct in6_addr) }, + [CTA_NAT_V6_MAXIP] = { .len = sizeof(struct in6_addr) }, [CTA_NAT_PROTO] = { .type = NLA_NESTED }, }; diff --git a/net/netfilter/xt_nat.c b/net/netfilter/xt_nat.c index 7521368a6034..81aafa8e4fef 100644 --- a/net/netfilter/xt_nat.c +++ b/net/netfilter/xt_nat.c @@ -163,5 +163,8 @@ module_init(xt_nat_init); module_exit(xt_nat_exit); MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); MODULE_ALIAS("ipt_SNAT"); MODULE_ALIAS("ipt_DNAT"); +MODULE_ALIAS("ip6t_SNAT"); +MODULE_ALIAS("ip6t_DNAT"); -- cgit v1.2.3 From b3f644fc8232ca761da0b5c5ccb6f30b423c4302 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sun, 26 Aug 2012 19:14:14 +0200 Subject: netfilter: ip6tables: add MASQUERADE target Signed-off-by: Patrick McHardy --- include/net/addrconf.h | 2 +- include/net/netfilter/nf_nat.h | 4 +- net/ipv4/netfilter/ipt_MASQUERADE.c | 3 +- net/ipv6/addrconf.c | 2 +- net/ipv6/netfilter/Kconfig | 12 ++++ net/ipv6/netfilter/Makefile | 1 + net/ipv6/netfilter/ip6t_MASQUERADE.c | 135 +++++++++++++++++++++++++++++++++++ 7 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 net/ipv6/netfilter/ip6t_MASQUERADE.c (limited to 'include') diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 089a09d001d1..9e63e76b20e7 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -78,7 +78,7 @@ extern struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, int strict); extern int ipv6_dev_get_saddr(struct net *net, - struct net_device *dev, + const struct net_device *dev, const struct in6_addr *daddr, unsigned int srcprefs, struct in6_addr *saddr); diff --git a/include/net/netfilter/nf_nat.h b/include/net/netfilter/nf_nat.h index 1752f1339054..bd8eea720f2e 100644 --- a/include/net/netfilter/nf_nat.h +++ b/include/net/netfilter/nf_nat.h @@ -43,7 +43,9 @@ struct nf_conn_nat { struct nf_conn *ct; union nf_conntrack_nat_help help; #if defined(CONFIG_IP_NF_TARGET_MASQUERADE) || \ - defined(CONFIG_IP_NF_TARGET_MASQUERADE_MODULE) + defined(CONFIG_IP_NF_TARGET_MASQUERADE_MODULE) || \ + defined(CONFIG_IP6_NF_TARGET_MASQUERADE) || \ + defined(CONFIG_IP6_NF_TARGET_MASQUERADE_MODULE) int masq_index; #endif }; diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c index 1c3aa28b51ae..5d5d4d1be9c2 100644 --- a/net/ipv4/netfilter/ipt_MASQUERADE.c +++ b/net/ipv4/netfilter/ipt_MASQUERADE.c @@ -99,7 +99,8 @@ device_cmp(struct nf_conn *i, void *ifindex) if (!nat) return 0; - + if (nf_ct_l3num(i) != NFPROTO_IPV4) + return 0; return nat->masq_index == (int)(long)ifindex; } diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index e581009cb09e..19d4bffda9d7 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -1093,7 +1093,7 @@ out: return ret; } -int ipv6_dev_get_saddr(struct net *net, struct net_device *dst_dev, +int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev, const struct in6_addr *daddr, unsigned int prefs, struct in6_addr *saddr) { diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index b27e0ad4b738..54a5032ea3df 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig @@ -144,6 +144,18 @@ config IP6_NF_TARGET_HL (e.g. when running oldconfig). It selects CONFIG_NETFILTER_XT_TARGET_HL. +config IP6_NF_TARGET_MASQUERADE + tristate "MASQUERADE target support" + depends on NF_NAT_IPV6 + help + Masquerading is a special case of NAT: all outgoing connections are + changed to seem to come from a particular interface's address, and + if the interface goes down, those connections are lost. This is + only useful for dialup accounts with dynamic IP address (ie. your IP + address will be different on next dialup). + + To compile it as a module, choose M here. If unsure, say N. + config IP6_NF_FILTER tristate "Packet filtering" default m if NETFILTER_ADVANCED=n diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile index 76779376da4c..068bad199587 100644 --- a/net/ipv6/netfilter/Makefile +++ b/net/ipv6/netfilter/Makefile @@ -34,4 +34,5 @@ obj-$(CONFIG_IP6_NF_MATCH_RPFILTER) += ip6t_rpfilter.o obj-$(CONFIG_IP6_NF_MATCH_RT) += ip6t_rt.o # targets +obj-$(CONFIG_IP6_NF_TARGET_MASQUERADE) += ip6t_MASQUERADE.o obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o diff --git a/net/ipv6/netfilter/ip6t_MASQUERADE.c b/net/ipv6/netfilter/ip6t_MASQUERADE.c new file mode 100644 index 000000000000..60e9053bab05 --- /dev/null +++ b/net/ipv6/netfilter/ip6t_MASQUERADE.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2011 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. + * + * Based on Rusty Russell's IPv6 MASQUERADE target. Development of IPv6 + * NAT funded by Astaro. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned int +masquerade_tg6(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct nf_nat_range *range = par->targinfo; + enum ip_conntrack_info ctinfo; + struct in6_addr src; + struct nf_conn *ct; + struct nf_nat_range newrange; + + ct = nf_ct_get(skb, &ctinfo); + NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || + ctinfo == IP_CT_RELATED_REPLY)); + + if (ipv6_dev_get_saddr(dev_net(par->out), par->out, + &ipv6_hdr(skb)->daddr, 0, &src) < 0) + return NF_DROP; + + nfct_nat(ct)->masq_index = par->out->ifindex; + + newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS; + newrange.min_addr.in6 = src; + newrange.max_addr.in6 = src; + newrange.min_proto = range->min_proto; + newrange.max_proto = range->max_proto; + + return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_SRC); +} + +static int masquerade_tg6_checkentry(const struct xt_tgchk_param *par) +{ + const struct nf_nat_range *range = par->targinfo; + + if (range->flags & NF_NAT_RANGE_MAP_IPS) + return -EINVAL; + return 0; +} + +static int device_cmp(struct nf_conn *ct, void *ifindex) +{ + const struct nf_conn_nat *nat = nfct_nat(ct); + + if (!nat) + return 0; + if (nf_ct_l3num(ct) != NFPROTO_IPV6) + return 0; + return nat->masq_index == (int)(long)ifindex; +} + +static int masq_device_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + const struct net_device *dev = ptr; + struct net *net = dev_net(dev); + + if (event == NETDEV_DOWN) + nf_ct_iterate_cleanup(net, device_cmp, + (void *)(long)dev->ifindex); + + return NOTIFY_DONE; +} + +static struct notifier_block masq_dev_notifier = { + .notifier_call = masq_device_event, +}; + +static int masq_inet_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct inet6_ifaddr *ifa = ptr; + + return masq_device_event(this, event, ifa->idev->dev); +} + +static struct notifier_block masq_inet_notifier = { + .notifier_call = masq_inet_event, +}; + +static struct xt_target masquerade_tg6_reg __read_mostly = { + .name = "MASQUERADE", + .family = NFPROTO_IPV6, + .checkentry = masquerade_tg6_checkentry, + .target = masquerade_tg6, + .targetsize = sizeof(struct nf_nat_range), + .table = "nat", + .hooks = 1 << NF_INET_POST_ROUTING, + .me = THIS_MODULE, +}; + +static int __init masquerade_tg6_init(void) +{ + int err; + + err = xt_register_target(&masquerade_tg6_reg); + if (err == 0) { + register_netdevice_notifier(&masq_dev_notifier); + register_inet6addr_notifier(&masq_inet_notifier); + } + + return err; +} +static void __exit masquerade_tg6_exit(void) +{ + unregister_inet6addr_notifier(&masq_inet_notifier); + unregister_netdevice_notifier(&masq_dev_notifier); + xt_unregister_target(&masquerade_tg6_reg); +} + +module_init(masquerade_tg6_init); +module_exit(masquerade_tg6_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); +MODULE_DESCRIPTION("Xtables: automatic address SNAT"); -- cgit v1.2.3 From 9a664821068739dbc8eac13770e28167b46a0c0f Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sun, 26 Aug 2012 19:14:25 +0200 Subject: netfilter: nf_nat: support IPv6 in SIP NAT helper Add IPv6 support to the SIP NAT helper. There are no functional differences to IPv4 NAT, just different formats for addresses. Signed-off-by: Patrick McHardy --- include/linux/netfilter/nf_conntrack_sip.h | 9 +- net/ipv4/netfilter/Kconfig | 5 - net/ipv4/netfilter/Makefile | 1 - net/ipv4/netfilter/nf_nat_sip.c | 580 --------------------------- net/netfilter/Kconfig | 5 + net/netfilter/Makefile | 1 + net/netfilter/nf_conntrack_sip.c | 68 ++-- net/netfilter/nf_nat_sip.c | 609 +++++++++++++++++++++++++++++ 8 files changed, 653 insertions(+), 625 deletions(-) delete mode 100644 net/ipv4/netfilter/nf_nat_sip.c create mode 100644 net/netfilter/nf_nat_sip.c (limited to 'include') diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h index 1afc669a393e..387bdd02945d 100644 --- a/include/linux/netfilter/nf_conntrack_sip.h +++ b/include/linux/netfilter/nf_conntrack_sip.h @@ -99,10 +99,8 @@ enum sip_header_types { enum sdp_header_types { SDP_HDR_UNSPEC, SDP_HDR_VERSION, - SDP_HDR_OWNER_IP4, - SDP_HDR_CONNECTION_IP4, - SDP_HDR_OWNER_IP6, - SDP_HDR_CONNECTION_IP6, + SDP_HDR_OWNER, + SDP_HDR_CONNECTION, SDP_HDR_MEDIA, }; @@ -111,7 +109,8 @@ extern unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb, unsigned int dataoff, const char **dptr, unsigned int *datalen); -extern void (*nf_nat_sip_seq_adjust_hook)(struct sk_buff *skb, s16 off); +extern void (*nf_nat_sip_seq_adjust_hook)(struct sk_buff *skb, + unsigned int protoff, s16 off); extern unsigned int (*nf_nat_sip_expect_hook)(struct sk_buff *skb, unsigned int protoff, unsigned int dataoff, diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index 52c4a87007aa..30197f8003be 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig @@ -242,11 +242,6 @@ config NF_NAT_H323 depends on NF_CONNTRACK && NF_NAT_IPV4 default NF_NAT_IPV4 && NF_CONNTRACK_H323 -config NF_NAT_SIP - tristate - depends on NF_CONNTRACK && NF_NAT_IPV4 - default NF_NAT_IPV4 && NF_CONNTRACK_SIP - # mangle + specific targets config IP_NF_MANGLE tristate "Packet mangling" diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index 8baa496f6a4e..8914abffc96d 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile @@ -23,7 +23,6 @@ obj-$(CONFIG_NF_DEFRAG_IPV4) += nf_defrag_ipv4.o obj-$(CONFIG_NF_NAT_H323) += nf_nat_h323.o obj-$(CONFIG_NF_NAT_IRC) += nf_nat_irc.o obj-$(CONFIG_NF_NAT_PPTP) += nf_nat_pptp.o -obj-$(CONFIG_NF_NAT_SIP) += nf_nat_sip.o obj-$(CONFIG_NF_NAT_SNMP_BASIC) += nf_nat_snmp_basic.o obj-$(CONFIG_NF_NAT_TFTP) += nf_nat_tftp.o diff --git a/net/ipv4/netfilter/nf_nat_sip.c b/net/ipv4/netfilter/nf_nat_sip.c deleted file mode 100644 index 47a47186a791..000000000000 --- a/net/ipv4/netfilter/nf_nat_sip.c +++ /dev/null @@ -1,580 +0,0 @@ -/* SIP extension for NAT alteration. - * - * (C) 2005 by Christian Hentschel - * based on RR's ip_nat_ftp.c and other modules. - * (C) 2007 United Security Providers - * (C) 2007, 2008 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 - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Christian Hentschel "); -MODULE_DESCRIPTION("SIP NAT helper"); -MODULE_ALIAS("ip_nat_sip"); - - -static unsigned int mangle_packet(struct sk_buff *skb, unsigned int protoff, - unsigned int dataoff, - const char **dptr, unsigned int *datalen, - unsigned int matchoff, unsigned int matchlen, - const char *buffer, unsigned int buflen) -{ - enum ip_conntrack_info ctinfo; - struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - struct tcphdr *th; - unsigned int baseoff; - - if (nf_ct_protonum(ct) == IPPROTO_TCP) { - th = (struct tcphdr *)(skb->data + ip_hdrlen(skb)); - baseoff = ip_hdrlen(skb) + th->doff * 4; - matchoff += dataoff - baseoff; - - if (!__nf_nat_mangle_tcp_packet(skb, ct, ctinfo, - protoff, matchoff, matchlen, - buffer, buflen, false)) - return 0; - } else { - baseoff = ip_hdrlen(skb) + sizeof(struct udphdr); - matchoff += dataoff - baseoff; - - if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, - protoff, matchoff, matchlen, - buffer, buflen)) - return 0; - } - - /* Reload data pointer and adjust datalen value */ - *dptr = skb->data + dataoff; - *datalen += buflen - matchlen; - return 1; -} - -static int map_addr(struct sk_buff *skb, unsigned int protoff, - unsigned int dataoff, - const char **dptr, unsigned int *datalen, - unsigned int matchoff, unsigned int matchlen, - union nf_inet_addr *addr, __be16 port) -{ - enum ip_conntrack_info ctinfo; - struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; - unsigned int buflen; - __be32 newaddr; - __be16 newport; - - if (ct->tuplehash[dir].tuple.src.u3.ip == addr->ip && - ct->tuplehash[dir].tuple.src.u.udp.port == port) { - newaddr = ct->tuplehash[!dir].tuple.dst.u3.ip; - newport = ct->tuplehash[!dir].tuple.dst.u.udp.port; - } else if (ct->tuplehash[dir].tuple.dst.u3.ip == addr->ip && - ct->tuplehash[dir].tuple.dst.u.udp.port == port) { - newaddr = ct->tuplehash[!dir].tuple.src.u3.ip; - newport = ct->tuplehash[!dir].tuple.src.u.udp.port; - } else - return 1; - - if (newaddr == addr->ip && newport == port) - return 1; - - buflen = sprintf(buffer, "%pI4:%u", &newaddr, ntohs(newport)); - - return mangle_packet(skb, protoff, dataoff, dptr, datalen, - matchoff, matchlen, buffer, buflen); -} - -static int map_sip_addr(struct sk_buff *skb, unsigned int protoff, - unsigned int dataoff, - const char **dptr, unsigned int *datalen, - enum sip_header_types type) -{ - enum ip_conntrack_info ctinfo; - struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - unsigned int matchlen, matchoff; - union nf_inet_addr addr; - __be16 port; - - if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, type, NULL, - &matchoff, &matchlen, &addr, &port) <= 0) - return 1; - return map_addr(skb, protoff, dataoff, dptr, datalen, - matchoff, matchlen, &addr, port); -} - -static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int protoff, - unsigned int dataoff, - const char **dptr, unsigned int *datalen) -{ - enum ip_conntrack_info ctinfo; - struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - unsigned int coff, matchoff, matchlen; - enum sip_header_types hdr; - union nf_inet_addr addr; - __be16 port; - int request, in_header; - - /* Basic rules: requests and responses. */ - if (strnicmp(*dptr, "SIP/2.0", strlen("SIP/2.0")) != 0) { - if (ct_sip_parse_request(ct, *dptr, *datalen, - &matchoff, &matchlen, - &addr, &port) > 0 && - !map_addr(skb, protoff, dataoff, dptr, datalen, - matchoff, matchlen, &addr, port)) - return NF_DROP; - request = 1; - } else - request = 0; - - if (nf_ct_protonum(ct) == IPPROTO_TCP) - hdr = SIP_HDR_VIA_TCP; - else - hdr = SIP_HDR_VIA_UDP; - - /* Translate topmost Via header and parameters */ - if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, - hdr, NULL, &matchoff, &matchlen, - &addr, &port) > 0) { - unsigned int olen, matchend, poff, plen, buflen, n; - char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; - - /* We're only interested in headers related to this - * connection */ - if (request) { - if (addr.ip != ct->tuplehash[dir].tuple.src.u3.ip || - port != ct->tuplehash[dir].tuple.src.u.udp.port) - goto next; - } else { - if (addr.ip != ct->tuplehash[dir].tuple.dst.u3.ip || - port != ct->tuplehash[dir].tuple.dst.u.udp.port) - goto next; - } - - olen = *datalen; - if (!map_addr(skb, protoff, dataoff, dptr, datalen, - matchoff, matchlen, &addr, port)) - return NF_DROP; - - matchend = matchoff + matchlen + *datalen - olen; - - /* The maddr= parameter (RFC 2361) specifies where to send - * the reply. */ - if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen, - "maddr=", &poff, &plen, - &addr, true) > 0 && - addr.ip == ct->tuplehash[dir].tuple.src.u3.ip && - addr.ip != ct->tuplehash[!dir].tuple.dst.u3.ip) { - buflen = sprintf(buffer, "%pI4", - &ct->tuplehash[!dir].tuple.dst.u3.ip); - if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, - poff, plen, buffer, buflen)) - return NF_DROP; - } - - /* The received= parameter (RFC 2361) contains the address - * from which the server received the request. */ - if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen, - "received=", &poff, &plen, - &addr, false) > 0 && - addr.ip == ct->tuplehash[dir].tuple.dst.u3.ip && - addr.ip != ct->tuplehash[!dir].tuple.src.u3.ip) { - buflen = sprintf(buffer, "%pI4", - &ct->tuplehash[!dir].tuple.src.u3.ip); - if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, - poff, plen, buffer, buflen)) - return NF_DROP; - } - - /* The rport= parameter (RFC 3581) contains the port number - * from which the server received the request. */ - if (ct_sip_parse_numerical_param(ct, *dptr, matchend, *datalen, - "rport=", &poff, &plen, - &n) > 0 && - htons(n) == ct->tuplehash[dir].tuple.dst.u.udp.port && - htons(n) != ct->tuplehash[!dir].tuple.src.u.udp.port) { - __be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port; - buflen = sprintf(buffer, "%u", ntohs(p)); - if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, - poff, plen, buffer, buflen)) - return NF_DROP; - } - } - -next: - /* Translate Contact headers */ - coff = 0; - in_header = 0; - while (ct_sip_parse_header_uri(ct, *dptr, &coff, *datalen, - SIP_HDR_CONTACT, &in_header, - &matchoff, &matchlen, - &addr, &port) > 0) { - if (!map_addr(skb, protoff, dataoff, dptr, datalen, - matchoff, matchlen, - &addr, port)) - return NF_DROP; - } - - if (!map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_FROM) || - !map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_TO)) - return NF_DROP; - - return NF_ACCEPT; -} - -static void ip_nat_sip_seq_adjust(struct sk_buff *skb, s16 off) -{ - enum ip_conntrack_info ctinfo; - struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - const struct tcphdr *th; - - if (nf_ct_protonum(ct) != IPPROTO_TCP || off == 0) - return; - - th = (struct tcphdr *)(skb->data + ip_hdrlen(skb)); - nf_nat_set_seq_adjust(ct, ctinfo, th->seq, off); -} - -/* Handles expected signalling connections and media streams */ -static void ip_nat_sip_expected(struct nf_conn *ct, - struct nf_conntrack_expect *exp) -{ - struct nf_nat_range range; - - /* This must be a fresh one. */ - BUG_ON(ct->status & IPS_NAT_DONE_MASK); - - /* For DST manip, map port here to where it's expected. */ - range.flags = (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED); - range.min_proto = range.max_proto = exp->saved_proto; - range.min_addr = range.max_addr = exp->saved_addr; - nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST); - - /* Change src to where master sends to, but only if the connection - * actually came from the same source. */ - if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip == - ct->master->tuplehash[exp->dir].tuple.src.u3.ip) { - range.flags = NF_NAT_RANGE_MAP_IPS; - range.min_addr = range.max_addr - = ct->master->tuplehash[!exp->dir].tuple.dst.u3; - nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC); - } -} - -static unsigned int ip_nat_sip_expect(struct sk_buff *skb, unsigned int protoff, - unsigned int dataoff, - const char **dptr, unsigned int *datalen, - struct nf_conntrack_expect *exp, - unsigned int matchoff, - unsigned int matchlen) -{ - enum ip_conntrack_info ctinfo; - struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - __be32 newip; - u_int16_t port; - char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; - unsigned int buflen; - - /* Connection will come from reply */ - if (ct->tuplehash[dir].tuple.src.u3.ip == ct->tuplehash[!dir].tuple.dst.u3.ip) - newip = exp->tuple.dst.u3.ip; - else - newip = ct->tuplehash[!dir].tuple.dst.u3.ip; - - /* If the signalling port matches the connection's source port in the - * original direction, try to use the destination port in the opposite - * direction. */ - if (exp->tuple.dst.u.udp.port == - ct->tuplehash[dir].tuple.src.u.udp.port) - port = ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.port); - else - port = ntohs(exp->tuple.dst.u.udp.port); - - exp->saved_addr = exp->tuple.dst.u3; - exp->tuple.dst.u3.ip = newip; - exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port; - exp->dir = !dir; - exp->expectfn = ip_nat_sip_expected; - - for (; port != 0; port++) { - int ret; - - exp->tuple.dst.u.udp.port = htons(port); - ret = nf_ct_expect_related(exp); - if (ret == 0) - break; - else if (ret != -EBUSY) { - port = 0; - break; - } - } - - if (port == 0) - return NF_DROP; - - if (exp->tuple.dst.u3.ip != exp->saved_addr.ip || - exp->tuple.dst.u.udp.port != exp->saved_proto.udp.port) { - buflen = sprintf(buffer, "%pI4:%u", &newip, port); - if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, - matchoff, matchlen, buffer, buflen)) - goto err; - } - return NF_ACCEPT; - -err: - nf_ct_unexpect_related(exp); - return NF_DROP; -} - -static int mangle_content_len(struct sk_buff *skb, unsigned int protoff, - unsigned int dataoff, - const char **dptr, unsigned int *datalen) -{ - enum ip_conntrack_info ctinfo; - struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - unsigned int matchoff, matchlen; - char buffer[sizeof("65536")]; - int buflen, c_len; - - /* Get actual SDP length */ - if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen, - SDP_HDR_VERSION, SDP_HDR_UNSPEC, - &matchoff, &matchlen) <= 0) - return 0; - c_len = *datalen - matchoff + strlen("v="); - - /* Now, update SDP length */ - if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CONTENT_LENGTH, - &matchoff, &matchlen) <= 0) - return 0; - - buflen = sprintf(buffer, "%u", c_len); - return mangle_packet(skb, protoff, dataoff, dptr, datalen, - matchoff, matchlen, buffer, buflen); -} - -static int mangle_sdp_packet(struct sk_buff *skb, unsigned int protoff, - unsigned int dataoff, - const char **dptr, unsigned int *datalen, - unsigned int sdpoff, - enum sdp_header_types type, - enum sdp_header_types term, - char *buffer, int buflen) -{ - enum ip_conntrack_info ctinfo; - struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - unsigned int matchlen, matchoff; - - if (ct_sip_get_sdp_header(ct, *dptr, sdpoff, *datalen, type, term, - &matchoff, &matchlen) <= 0) - return -ENOENT; - return mangle_packet(skb, protoff, dataoff, dptr, datalen, - matchoff, matchlen, buffer, buflen) ? 0 : -EINVAL; -} - -static unsigned int ip_nat_sdp_addr(struct sk_buff *skb, unsigned int protoff, - unsigned int dataoff, - const char **dptr, unsigned int *datalen, - unsigned int sdpoff, - enum sdp_header_types type, - enum sdp_header_types term, - const union nf_inet_addr *addr) -{ - char buffer[sizeof("nnn.nnn.nnn.nnn")]; - unsigned int buflen; - - buflen = sprintf(buffer, "%pI4", &addr->ip); - if (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen, - sdpoff, type, term, buffer, buflen)) - return 0; - - return mangle_content_len(skb, protoff, dataoff, dptr, datalen); -} - -static unsigned int ip_nat_sdp_port(struct sk_buff *skb, unsigned int protoff, - unsigned int dataoff, - const char **dptr, unsigned int *datalen, - unsigned int matchoff, - unsigned int matchlen, - u_int16_t port) -{ - char buffer[sizeof("nnnnn")]; - unsigned int buflen; - - buflen = sprintf(buffer, "%u", port); - if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, - matchoff, matchlen, buffer, buflen)) - return 0; - - return mangle_content_len(skb, protoff, dataoff, dptr, datalen); -} - -static unsigned int ip_nat_sdp_session(struct sk_buff *skb, unsigned int protoff, - unsigned int dataoff, - const char **dptr, unsigned int *datalen, - unsigned int sdpoff, - const union nf_inet_addr *addr) -{ - char buffer[sizeof("nnn.nnn.nnn.nnn")]; - unsigned int buflen; - - /* Mangle session description owner and contact addresses */ - buflen = sprintf(buffer, "%pI4", &addr->ip); - if (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen, sdpoff, - SDP_HDR_OWNER_IP4, SDP_HDR_MEDIA, - buffer, buflen)) - return 0; - - switch (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen, sdpoff, - SDP_HDR_CONNECTION_IP4, SDP_HDR_MEDIA, - buffer, buflen)) { - case 0: - /* - * RFC 2327: - * - * Session description - * - * c=* (connection information - not required if included in all media) - */ - case -ENOENT: - break; - default: - return 0; - } - - return mangle_content_len(skb, protoff, dataoff, dptr, datalen); -} - -/* So, this packet has hit the connection tracking matching code. - Mangle it, and change the expectation to match the new version. */ -static unsigned int ip_nat_sdp_media(struct sk_buff *skb, unsigned int protoff, - unsigned int dataoff, - const char **dptr, unsigned int *datalen, - struct nf_conntrack_expect *rtp_exp, - struct nf_conntrack_expect *rtcp_exp, - unsigned int mediaoff, - unsigned int medialen, - union nf_inet_addr *rtp_addr) -{ - enum ip_conntrack_info ctinfo; - struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - u_int16_t port; - - /* Connection will come from reply */ - if (ct->tuplehash[dir].tuple.src.u3.ip == - ct->tuplehash[!dir].tuple.dst.u3.ip) - rtp_addr->ip = rtp_exp->tuple.dst.u3.ip; - else - rtp_addr->ip = ct->tuplehash[!dir].tuple.dst.u3.ip; - - rtp_exp->saved_addr = rtp_exp->tuple.dst.u3; - rtp_exp->tuple.dst.u3.ip = rtp_addr->ip; - rtp_exp->saved_proto.udp.port = rtp_exp->tuple.dst.u.udp.port; - rtp_exp->dir = !dir; - rtp_exp->expectfn = ip_nat_sip_expected; - - rtcp_exp->saved_addr = rtcp_exp->tuple.dst.u3; - rtcp_exp->tuple.dst.u3.ip = rtp_addr->ip; - rtcp_exp->saved_proto.udp.port = rtcp_exp->tuple.dst.u.udp.port; - rtcp_exp->dir = !dir; - rtcp_exp->expectfn = ip_nat_sip_expected; - - /* Try to get same pair of ports: if not, try to change them. */ - for (port = ntohs(rtp_exp->tuple.dst.u.udp.port); - port != 0; port += 2) { - int ret; - - rtp_exp->tuple.dst.u.udp.port = htons(port); - ret = nf_ct_expect_related(rtp_exp); - if (ret == -EBUSY) - continue; - else if (ret < 0) { - port = 0; - break; - } - rtcp_exp->tuple.dst.u.udp.port = htons(port + 1); - ret = nf_ct_expect_related(rtcp_exp); - if (ret == 0) - break; - else if (ret != -EBUSY) { - nf_ct_unexpect_related(rtp_exp); - port = 0; - break; - } - } - - if (port == 0) - goto err1; - - /* Update media port. */ - if (rtp_exp->tuple.dst.u.udp.port != rtp_exp->saved_proto.udp.port && - !ip_nat_sdp_port(skb, protoff, dataoff, dptr, datalen, - mediaoff, medialen, port)) - goto err2; - - return NF_ACCEPT; - -err2: - nf_ct_unexpect_related(rtp_exp); - nf_ct_unexpect_related(rtcp_exp); -err1: - return NF_DROP; -} - -static struct nf_ct_helper_expectfn sip_nat = { - .name = "sip", - .expectfn = ip_nat_sip_expected, -}; - -static void __exit nf_nat_sip_fini(void) -{ - RCU_INIT_POINTER(nf_nat_sip_hook, NULL); - RCU_INIT_POINTER(nf_nat_sip_seq_adjust_hook, NULL); - RCU_INIT_POINTER(nf_nat_sip_expect_hook, NULL); - RCU_INIT_POINTER(nf_nat_sdp_addr_hook, NULL); - RCU_INIT_POINTER(nf_nat_sdp_port_hook, NULL); - RCU_INIT_POINTER(nf_nat_sdp_session_hook, NULL); - RCU_INIT_POINTER(nf_nat_sdp_media_hook, NULL); - nf_ct_helper_expectfn_unregister(&sip_nat); - synchronize_rcu(); -} - -static int __init nf_nat_sip_init(void) -{ - BUG_ON(nf_nat_sip_hook != NULL); - BUG_ON(nf_nat_sip_seq_adjust_hook != NULL); - BUG_ON(nf_nat_sip_expect_hook != NULL); - BUG_ON(nf_nat_sdp_addr_hook != NULL); - BUG_ON(nf_nat_sdp_port_hook != NULL); - BUG_ON(nf_nat_sdp_session_hook != NULL); - BUG_ON(nf_nat_sdp_media_hook != NULL); - RCU_INIT_POINTER(nf_nat_sip_hook, ip_nat_sip); - RCU_INIT_POINTER(nf_nat_sip_seq_adjust_hook, ip_nat_sip_seq_adjust); - RCU_INIT_POINTER(nf_nat_sip_expect_hook, ip_nat_sip_expect); - RCU_INIT_POINTER(nf_nat_sdp_addr_hook, ip_nat_sdp_addr); - RCU_INIT_POINTER(nf_nat_sdp_port_hook, ip_nat_sdp_port); - RCU_INIT_POINTER(nf_nat_sdp_session_hook, ip_nat_sdp_session); - RCU_INIT_POINTER(nf_nat_sdp_media_hook, ip_nat_sdp_media); - nf_ct_helper_expectfn_register(&sip_nat); - return 0; -} - -module_init(nf_nat_sip_init); -module_exit(nf_nat_sip_fini); diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 2eee9f1f99ef..bf3e4649efb2 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -390,6 +390,11 @@ config NF_NAT_FTP depends on NF_CONNTRACK && NF_NAT default NF_NAT && NF_CONNTRACK_FTP +config NF_NAT_SIP + tristate + depends on NF_CONNTRACK && NF_NAT + default NF_NAT && NF_CONNTRACK_SIP + endif # NF_CONNTRACK # transparent proxy support diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 7d6e1ea14c9b..7d6d1a035f31 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_NF_NAT_PROTO_SCTP) += nf_nat_proto_sctp.o # NAT helpers obj-$(CONFIG_NF_NAT_AMANDA) += nf_nat_amanda.o obj-$(CONFIG_NF_NAT_FTP) += nf_nat_ftp.o +obj-$(CONFIG_NF_NAT_SIP) += nf_nat_sip.o # transparent proxy support obj-$(CONFIG_NETFILTER_TPROXY) += nf_tproxy_core.o diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index d5174902db37..df8f4f284481 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -57,7 +57,8 @@ unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb, unsigned int protoff, unsigned int *datalen) __read_mostly; EXPORT_SYMBOL_GPL(nf_nat_sip_hook); -void (*nf_nat_sip_seq_adjust_hook)(struct sk_buff *skb, s16 off) __read_mostly; +void (*nf_nat_sip_seq_adjust_hook)(struct sk_buff *skb, unsigned int protoff, + s16 off) __read_mostly; EXPORT_SYMBOL_GPL(nf_nat_sip_seq_adjust_hook); unsigned int (*nf_nat_sip_expect_hook)(struct sk_buff *skb, @@ -742,13 +743,18 @@ static int sdp_addr_len(const struct nf_conn *ct, const char *dptr, * be tolerant and also accept records terminated with a single newline * character". We handle both cases. */ -static const struct sip_header ct_sdp_hdrs[] = { - [SDP_HDR_VERSION] = SDP_HDR("v=", NULL, digits_len), - [SDP_HDR_OWNER_IP4] = SDP_HDR("o=", "IN IP4 ", sdp_addr_len), - [SDP_HDR_CONNECTION_IP4] = SDP_HDR("c=", "IN IP4 ", sdp_addr_len), - [SDP_HDR_OWNER_IP6] = SDP_HDR("o=", "IN IP6 ", sdp_addr_len), - [SDP_HDR_CONNECTION_IP6] = SDP_HDR("c=", "IN IP6 ", sdp_addr_len), - [SDP_HDR_MEDIA] = SDP_HDR("m=", NULL, media_len), +static const struct sip_header ct_sdp_hdrs_v4[] = { + [SDP_HDR_VERSION] = SDP_HDR("v=", NULL, digits_len), + [SDP_HDR_OWNER] = SDP_HDR("o=", "IN IP4 ", sdp_addr_len), + [SDP_HDR_CONNECTION] = SDP_HDR("c=", "IN IP4 ", sdp_addr_len), + [SDP_HDR_MEDIA] = SDP_HDR("m=", NULL, media_len), +}; + +static const struct sip_header ct_sdp_hdrs_v6[] = { + [SDP_HDR_VERSION] = SDP_HDR("v=", NULL, digits_len), + [SDP_HDR_OWNER] = SDP_HDR("o=", "IN IP6 ", sdp_addr_len), + [SDP_HDR_CONNECTION] = SDP_HDR("c=", "IN IP6 ", sdp_addr_len), + [SDP_HDR_MEDIA] = SDP_HDR("m=", NULL, media_len), }; /* Linear string search within SDP header values */ @@ -774,11 +780,14 @@ int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr, enum sdp_header_types term, unsigned int *matchoff, unsigned int *matchlen) { - const struct sip_header *hdr = &ct_sdp_hdrs[type]; - const struct sip_header *thdr = &ct_sdp_hdrs[term]; + const struct sip_header *hdrs, *hdr, *thdr; const char *start = dptr, *limit = dptr + datalen; int shift = 0; + hdrs = nf_ct_l3num(ct) == NFPROTO_IPV4 ? ct_sdp_hdrs_v4 : ct_sdp_hdrs_v6; + hdr = &hdrs[type]; + thdr = &hdrs[term]; + for (dptr += dataoff; dptr < limit; dptr++) { /* Find beginning of line */ if (*dptr != '\r' && *dptr != '\n') @@ -945,12 +954,12 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, exp->class != class) break; #ifdef CONFIG_NF_NAT_NEEDED - if (exp->tuple.src.l3num == AF_INET && !direct_rtp && - (exp->saved_addr.ip != exp->tuple.dst.u3.ip || + if (!direct_rtp && + (!nf_inet_addr_cmp(&exp->saved_addr, &exp->tuple.dst.u3) || exp->saved_proto.udp.port != exp->tuple.dst.u.udp.port) && ct->status & IPS_NAT_MASK) { - daddr->ip = exp->saved_addr.ip; - tuple.dst.u3.ip = exp->saved_addr.ip; + *daddr = exp->saved_addr; + tuple.dst.u3 = exp->saved_addr; tuple.dst.u.udp.port = exp->saved_proto.udp.port; direct_rtp = 1; } else @@ -987,8 +996,7 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, IPPROTO_UDP, NULL, &rtcp_port); nf_nat_sdp_media = rcu_dereference(nf_nat_sdp_media_hook); - if (nf_nat_sdp_media && nf_ct_l3num(ct) == NFPROTO_IPV4 && - ct->status & IPS_NAT_MASK && !direct_rtp) + if (nf_nat_sdp_media && ct->status & IPS_NAT_MASK && !direct_rtp) ret = nf_nat_sdp_media(skb, protoff, dataoff, dptr, datalen, rtp_exp, rtcp_exp, mediaoff, medialen, daddr); @@ -1044,15 +1052,12 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff, unsigned int i; union nf_inet_addr caddr, maddr, rtp_addr; unsigned int port; - enum sdp_header_types c_hdr; const struct sdp_media_type *t; int ret = NF_ACCEPT; typeof(nf_nat_sdp_addr_hook) nf_nat_sdp_addr; typeof(nf_nat_sdp_session_hook) nf_nat_sdp_session; nf_nat_sdp_addr = rcu_dereference(nf_nat_sdp_addr_hook); - c_hdr = nf_ct_l3num(ct) == AF_INET ? SDP_HDR_CONNECTION_IP4 : - SDP_HDR_CONNECTION_IP6; /* Find beginning of session description */ if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen, @@ -1066,7 +1071,7 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff, * the end of the session description. */ caddr_len = 0; if (ct_sip_parse_sdp_addr(ct, *dptr, sdpoff, *datalen, - c_hdr, SDP_HDR_MEDIA, + SDP_HDR_CONNECTION, SDP_HDR_MEDIA, &matchoff, &matchlen, &caddr) > 0) caddr_len = matchlen; @@ -1096,7 +1101,7 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff, /* The media description overrides the session description. */ maddr_len = 0; if (ct_sip_parse_sdp_addr(ct, *dptr, mediaoff, *datalen, - c_hdr, SDP_HDR_MEDIA, + SDP_HDR_CONNECTION, SDP_HDR_MEDIA, &matchoff, &matchlen, &maddr) > 0) { maddr_len = matchlen; memcpy(&rtp_addr, &maddr, sizeof(rtp_addr)); @@ -1113,11 +1118,10 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff, return ret; /* Update media connection address if present */ - if (maddr_len && nf_nat_sdp_addr && - nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK) { + if (maddr_len && nf_nat_sdp_addr && ct->status & IPS_NAT_MASK) { ret = nf_nat_sdp_addr(skb, protoff, dataoff, - dptr, datalen, - mediaoff, c_hdr, SDP_HDR_MEDIA, + dptr, datalen, mediaoff, + SDP_HDR_CONNECTION, SDP_HDR_MEDIA, &rtp_addr); if (ret != NF_ACCEPT) return ret; @@ -1127,8 +1131,7 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff, /* Update session connection and owner addresses */ nf_nat_sdp_session = rcu_dereference(nf_nat_sdp_session_hook); - if (nf_nat_sdp_session && nf_ct_l3num(ct) == NFPROTO_IPV4 && - ct->status & IPS_NAT_MASK) + if (nf_nat_sdp_session && ct->status & IPS_NAT_MASK) ret = nf_nat_sdp_session(skb, protoff, dataoff, dptr, datalen, sdpoff, &rtp_addr); @@ -1293,8 +1296,7 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff, exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE; nf_nat_sip_expect = rcu_dereference(nf_nat_sip_expect_hook); - if (nf_nat_sip_expect && nf_ct_l3num(ct) == NFPROTO_IPV4 && - ct->status & IPS_NAT_MASK) + if (nf_nat_sip_expect && ct->status & IPS_NAT_MASK) ret = nf_nat_sip_expect(skb, protoff, dataoff, dptr, datalen, exp, matchoff, matchlen); else { @@ -1476,8 +1478,7 @@ static int process_sip_msg(struct sk_buff *skb, struct nf_conn *ct, else ret = process_sip_response(skb, protoff, dataoff, dptr, datalen); - if (ret == NF_ACCEPT && nf_ct_l3num(ct) == NFPROTO_IPV4 && - ct->status & IPS_NAT_MASK) { + if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) { nf_nat_sip = rcu_dereference(nf_nat_sip_hook); if (nf_nat_sip && !nf_nat_sip(skb, protoff, dataoff, dptr, datalen)) @@ -1560,11 +1561,10 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, datalen = datalen + diff - msglen; } - if (ret == NF_ACCEPT && nf_ct_l3num(ct) == NFPROTO_IPV4 && - ct->status & IPS_NAT_MASK) { + if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) { nf_nat_sip_seq_adjust = rcu_dereference(nf_nat_sip_seq_adjust_hook); if (nf_nat_sip_seq_adjust) - nf_nat_sip_seq_adjust(skb, tdiff); + nf_nat_sip_seq_adjust(skb, protoff, tdiff); } return ret; diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c new file mode 100644 index 000000000000..f4db3a7bd285 --- /dev/null +++ b/net/netfilter/nf_nat_sip.c @@ -0,0 +1,609 @@ +/* SIP extension for NAT alteration. + * + * (C) 2005 by Christian Hentschel + * based on RR's ip_nat_ftp.c and other modules. + * (C) 2007 United Security Providers + * (C) 2007, 2008, 2011, 2012 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 + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Christian Hentschel "); +MODULE_DESCRIPTION("SIP NAT helper"); +MODULE_ALIAS("ip_nat_sip"); + + +static unsigned int mangle_packet(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, + const char **dptr, unsigned int *datalen, + unsigned int matchoff, unsigned int matchlen, + const char *buffer, unsigned int buflen) +{ + enum ip_conntrack_info ctinfo; + struct nf_conn *ct = nf_ct_get(skb, &ctinfo); + struct tcphdr *th; + unsigned int baseoff; + + if (nf_ct_protonum(ct) == IPPROTO_TCP) { + th = (struct tcphdr *)(skb->data + protoff); + baseoff = protoff + th->doff * 4; + matchoff += dataoff - baseoff; + + if (!__nf_nat_mangle_tcp_packet(skb, ct, ctinfo, + protoff, matchoff, matchlen, + buffer, buflen, false)) + return 0; + } else { + baseoff = protoff + sizeof(struct udphdr); + matchoff += dataoff - baseoff; + + if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, + protoff, matchoff, matchlen, + buffer, buflen)) + return 0; + } + + /* Reload data pointer and adjust datalen value */ + *dptr = skb->data + dataoff; + *datalen += buflen - matchlen; + return 1; +} + +static int sip_sprintf_addr(const struct nf_conn *ct, char *buffer, + const union nf_inet_addr *addr, bool delim) +{ + if (nf_ct_l3num(ct) == NFPROTO_IPV4) + return sprintf(buffer, "%pI4", &addr->ip); + else { + if (delim) + return sprintf(buffer, "[%pI6c]", &addr->ip6); + else + return sprintf(buffer, "%pI6c", &addr->ip6); + } +} + +static int sip_sprintf_addr_port(const struct nf_conn *ct, char *buffer, + const union nf_inet_addr *addr, u16 port) +{ + if (nf_ct_l3num(ct) == NFPROTO_IPV4) + return sprintf(buffer, "%pI4:%u", &addr->ip, port); + else + return sprintf(buffer, "[%pI6c]:%u", &addr->ip6, port); +} + +static int map_addr(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, + const char **dptr, unsigned int *datalen, + unsigned int matchoff, unsigned int matchlen, + union nf_inet_addr *addr, __be16 port) +{ + enum ip_conntrack_info ctinfo; + struct nf_conn *ct = nf_ct_get(skb, &ctinfo); + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn")]; + unsigned int buflen; + union nf_inet_addr newaddr; + __be16 newport; + + if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, addr) && + ct->tuplehash[dir].tuple.src.u.udp.port == port) { + newaddr = ct->tuplehash[!dir].tuple.dst.u3; + newport = ct->tuplehash[!dir].tuple.dst.u.udp.port; + } else if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, addr) && + ct->tuplehash[dir].tuple.dst.u.udp.port == port) { + newaddr = ct->tuplehash[!dir].tuple.src.u3; + newport = ct->tuplehash[!dir].tuple.src.u.udp.port; + } else + return 1; + + if (nf_inet_addr_cmp(&newaddr, addr) && newport == port) + return 1; + + buflen = sip_sprintf_addr_port(ct, buffer, &newaddr, ntohs(newport)); + return mangle_packet(skb, protoff, dataoff, dptr, datalen, + matchoff, matchlen, buffer, buflen); +} + +static int map_sip_addr(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, + const char **dptr, unsigned int *datalen, + enum sip_header_types type) +{ + enum ip_conntrack_info ctinfo; + struct nf_conn *ct = nf_ct_get(skb, &ctinfo); + unsigned int matchlen, matchoff; + union nf_inet_addr addr; + __be16 port; + + if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, type, NULL, + &matchoff, &matchlen, &addr, &port) <= 0) + return 1; + return map_addr(skb, protoff, dataoff, dptr, datalen, + matchoff, matchlen, &addr, port); +} + +static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, + const char **dptr, unsigned int *datalen) +{ + enum ip_conntrack_info ctinfo; + struct nf_conn *ct = nf_ct_get(skb, &ctinfo); + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + unsigned int coff, matchoff, matchlen; + enum sip_header_types hdr; + union nf_inet_addr addr; + __be16 port; + int request, in_header; + + /* Basic rules: requests and responses. */ + if (strnicmp(*dptr, "SIP/2.0", strlen("SIP/2.0")) != 0) { + if (ct_sip_parse_request(ct, *dptr, *datalen, + &matchoff, &matchlen, + &addr, &port) > 0 && + !map_addr(skb, protoff, dataoff, dptr, datalen, + matchoff, matchlen, &addr, port)) + return NF_DROP; + request = 1; + } else + request = 0; + + if (nf_ct_protonum(ct) == IPPROTO_TCP) + hdr = SIP_HDR_VIA_TCP; + else + hdr = SIP_HDR_VIA_UDP; + + /* Translate topmost Via header and parameters */ + if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, + hdr, NULL, &matchoff, &matchlen, + &addr, &port) > 0) { + unsigned int olen, matchend, poff, plen, buflen, n; + char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn")]; + + /* We're only interested in headers related to this + * connection */ + if (request) { + if (!nf_inet_addr_cmp(&addr, + &ct->tuplehash[dir].tuple.src.u3) || + port != ct->tuplehash[dir].tuple.src.u.udp.port) + goto next; + } else { + if (!nf_inet_addr_cmp(&addr, + &ct->tuplehash[dir].tuple.dst.u3) || + port != ct->tuplehash[dir].tuple.dst.u.udp.port) + goto next; + } + + olen = *datalen; + if (!map_addr(skb, protoff, dataoff, dptr, datalen, + matchoff, matchlen, &addr, port)) + return NF_DROP; + + matchend = matchoff + matchlen + *datalen - olen; + + /* The maddr= parameter (RFC 2361) specifies where to send + * the reply. */ + if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen, + "maddr=", &poff, &plen, + &addr, true) > 0 && + nf_inet_addr_cmp(&addr, &ct->tuplehash[dir].tuple.src.u3) && + !nf_inet_addr_cmp(&addr, &ct->tuplehash[!dir].tuple.dst.u3)) { + buflen = sip_sprintf_addr(ct, buffer, + &ct->tuplehash[!dir].tuple.dst.u3, + true); + if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, + poff, plen, buffer, buflen)) + return NF_DROP; + } + + /* The received= parameter (RFC 2361) contains the address + * from which the server received the request. */ + if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen, + "received=", &poff, &plen, + &addr, false) > 0 && + nf_inet_addr_cmp(&addr, &ct->tuplehash[dir].tuple.dst.u3) && + !nf_inet_addr_cmp(&addr, &ct->tuplehash[!dir].tuple.src.u3)) { + buflen = sip_sprintf_addr(ct, buffer, + &ct->tuplehash[!dir].tuple.src.u3, + false); + if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, + poff, plen, buffer, buflen)) + return NF_DROP; + } + + /* The rport= parameter (RFC 3581) contains the port number + * from which the server received the request. */ + if (ct_sip_parse_numerical_param(ct, *dptr, matchend, *datalen, + "rport=", &poff, &plen, + &n) > 0 && + htons(n) == ct->tuplehash[dir].tuple.dst.u.udp.port && + htons(n) != ct->tuplehash[!dir].tuple.src.u.udp.port) { + __be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port; + buflen = sprintf(buffer, "%u", ntohs(p)); + if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, + poff, plen, buffer, buflen)) + return NF_DROP; + } + } + +next: + /* Translate Contact headers */ + coff = 0; + in_header = 0; + while (ct_sip_parse_header_uri(ct, *dptr, &coff, *datalen, + SIP_HDR_CONTACT, &in_header, + &matchoff, &matchlen, + &addr, &port) > 0) { + if (!map_addr(skb, protoff, dataoff, dptr, datalen, + matchoff, matchlen, + &addr, port)) + return NF_DROP; + } + + if (!map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_FROM) || + !map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_TO)) + return NF_DROP; + + return NF_ACCEPT; +} + +static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff, + s16 off) +{ + enum ip_conntrack_info ctinfo; + struct nf_conn *ct = nf_ct_get(skb, &ctinfo); + const struct tcphdr *th; + + if (nf_ct_protonum(ct) != IPPROTO_TCP || off == 0) + return; + + th = (struct tcphdr *)(skb->data + protoff); + nf_nat_set_seq_adjust(ct, ctinfo, th->seq, off); +} + +/* Handles expected signalling connections and media streams */ +static void nf_nat_sip_expected(struct nf_conn *ct, + struct nf_conntrack_expect *exp) +{ + struct nf_nat_range range; + + /* This must be a fresh one. */ + BUG_ON(ct->status & IPS_NAT_DONE_MASK); + + /* For DST manip, map port here to where it's expected. */ + range.flags = (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED); + range.min_proto = range.max_proto = exp->saved_proto; + range.min_addr = range.max_addr = exp->saved_addr; + nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST); + + /* Change src to where master sends to, but only if the connection + * actually came from the same source. */ + if (nf_inet_addr_cmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3, + &ct->master->tuplehash[exp->dir].tuple.src.u3)) { + range.flags = NF_NAT_RANGE_MAP_IPS; + range.min_addr = range.max_addr + = ct->master->tuplehash[!exp->dir].tuple.dst.u3; + nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC); + } +} + +static unsigned int nf_nat_sip_expect(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, + const char **dptr, unsigned int *datalen, + struct nf_conntrack_expect *exp, + unsigned int matchoff, + unsigned int matchlen) +{ + enum ip_conntrack_info ctinfo; + struct nf_conn *ct = nf_ct_get(skb, &ctinfo); + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + union nf_inet_addr newaddr; + u_int16_t port; + char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn")]; + unsigned int buflen; + + /* Connection will come from reply */ + if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, + &ct->tuplehash[!dir].tuple.dst.u3)) + newaddr = exp->tuple.dst.u3; + else + newaddr = ct->tuplehash[!dir].tuple.dst.u3; + + /* If the signalling port matches the connection's source port in the + * original direction, try to use the destination port in the opposite + * direction. */ + if (exp->tuple.dst.u.udp.port == + ct->tuplehash[dir].tuple.src.u.udp.port) + port = ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.port); + else + port = ntohs(exp->tuple.dst.u.udp.port); + + exp->saved_addr = exp->tuple.dst.u3; + exp->tuple.dst.u3 = newaddr; + exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port; + exp->dir = !dir; + exp->expectfn = nf_nat_sip_expected; + + for (; port != 0; port++) { + int ret; + + exp->tuple.dst.u.udp.port = htons(port); + ret = nf_ct_expect_related(exp); + if (ret == 0) + break; + else if (ret != -EBUSY) { + port = 0; + break; + } + } + + if (port == 0) + return NF_DROP; + + if (!nf_inet_addr_cmp(&exp->tuple.dst.u3, &exp->saved_addr) || + exp->tuple.dst.u.udp.port != exp->saved_proto.udp.port) { + buflen = sip_sprintf_addr_port(ct, buffer, &newaddr, port); + if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, + matchoff, matchlen, buffer, buflen)) + goto err; + } + return NF_ACCEPT; + +err: + nf_ct_unexpect_related(exp); + return NF_DROP; +} + +static int mangle_content_len(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, + const char **dptr, unsigned int *datalen) +{ + enum ip_conntrack_info ctinfo; + struct nf_conn *ct = nf_ct_get(skb, &ctinfo); + unsigned int matchoff, matchlen; + char buffer[sizeof("65536")]; + int buflen, c_len; + + /* Get actual SDP length */ + if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen, + SDP_HDR_VERSION, SDP_HDR_UNSPEC, + &matchoff, &matchlen) <= 0) + return 0; + c_len = *datalen - matchoff + strlen("v="); + + /* Now, update SDP length */ + if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CONTENT_LENGTH, + &matchoff, &matchlen) <= 0) + return 0; + + buflen = sprintf(buffer, "%u", c_len); + return mangle_packet(skb, protoff, dataoff, dptr, datalen, + matchoff, matchlen, buffer, buflen); +} + +static int mangle_sdp_packet(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, + const char **dptr, unsigned int *datalen, + unsigned int sdpoff, + enum sdp_header_types type, + enum sdp_header_types term, + char *buffer, int buflen) +{ + enum ip_conntrack_info ctinfo; + struct nf_conn *ct = nf_ct_get(skb, &ctinfo); + unsigned int matchlen, matchoff; + + if (ct_sip_get_sdp_header(ct, *dptr, sdpoff, *datalen, type, term, + &matchoff, &matchlen) <= 0) + return -ENOENT; + return mangle_packet(skb, protoff, dataoff, dptr, datalen, + matchoff, matchlen, buffer, buflen) ? 0 : -EINVAL; +} + +static unsigned int nf_nat_sdp_addr(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, + const char **dptr, unsigned int *datalen, + unsigned int sdpoff, + enum sdp_header_types type, + enum sdp_header_types term, + const union nf_inet_addr *addr) +{ + enum ip_conntrack_info ctinfo; + struct nf_conn *ct = nf_ct_get(skb, &ctinfo); + char buffer[INET6_ADDRSTRLEN]; + unsigned int buflen; + + buflen = sip_sprintf_addr(ct, buffer, addr, false); + if (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen, + sdpoff, type, term, buffer, buflen)) + return 0; + + return mangle_content_len(skb, protoff, dataoff, dptr, datalen); +} + +static unsigned int nf_nat_sdp_port(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, + const char **dptr, unsigned int *datalen, + unsigned int matchoff, + unsigned int matchlen, + u_int16_t port) +{ + char buffer[sizeof("nnnnn")]; + unsigned int buflen; + + buflen = sprintf(buffer, "%u", port); + if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, + matchoff, matchlen, buffer, buflen)) + return 0; + + return mangle_content_len(skb, protoff, dataoff, dptr, datalen); +} + +static unsigned int nf_nat_sdp_session(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, + const char **dptr, unsigned int *datalen, + unsigned int sdpoff, + const union nf_inet_addr *addr) +{ + enum ip_conntrack_info ctinfo; + struct nf_conn *ct = nf_ct_get(skb, &ctinfo); + char buffer[INET6_ADDRSTRLEN]; + unsigned int buflen; + + /* Mangle session description owner and contact addresses */ + buflen = sip_sprintf_addr(ct, buffer, addr, false); + if (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen, sdpoff, + SDP_HDR_OWNER, SDP_HDR_MEDIA, buffer, buflen)) + return 0; + + switch (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen, sdpoff, + SDP_HDR_CONNECTION, SDP_HDR_MEDIA, + buffer, buflen)) { + case 0: + /* + * RFC 2327: + * + * Session description + * + * c=* (connection information - not required if included in all media) + */ + case -ENOENT: + break; + default: + return 0; + } + + return mangle_content_len(skb, protoff, dataoff, dptr, datalen); +} + +/* So, this packet has hit the connection tracking matching code. + Mangle it, and change the expectation to match the new version. */ +static unsigned int nf_nat_sdp_media(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, + const char **dptr, unsigned int *datalen, + struct nf_conntrack_expect *rtp_exp, + struct nf_conntrack_expect *rtcp_exp, + unsigned int mediaoff, + unsigned int medialen, + union nf_inet_addr *rtp_addr) +{ + enum ip_conntrack_info ctinfo; + struct nf_conn *ct = nf_ct_get(skb, &ctinfo); + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + u_int16_t port; + + /* Connection will come from reply */ + if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, + &ct->tuplehash[!dir].tuple.dst.u3)) + *rtp_addr = rtp_exp->tuple.dst.u3; + else + *rtp_addr = ct->tuplehash[!dir].tuple.dst.u3; + + rtp_exp->saved_addr = rtp_exp->tuple.dst.u3; + rtp_exp->tuple.dst.u3 = *rtp_addr; + rtp_exp->saved_proto.udp.port = rtp_exp->tuple.dst.u.udp.port; + rtp_exp->dir = !dir; + rtp_exp->expectfn = nf_nat_sip_expected; + + rtcp_exp->saved_addr = rtcp_exp->tuple.dst.u3; + rtcp_exp->tuple.dst.u3 = *rtp_addr; + rtcp_exp->saved_proto.udp.port = rtcp_exp->tuple.dst.u.udp.port; + rtcp_exp->dir = !dir; + rtcp_exp->expectfn = nf_nat_sip_expected; + + /* Try to get same pair of ports: if not, try to change them. */ + for (port = ntohs(rtp_exp->tuple.dst.u.udp.port); + port != 0; port += 2) { + int ret; + + rtp_exp->tuple.dst.u.udp.port = htons(port); + ret = nf_ct_expect_related(rtp_exp); + if (ret == -EBUSY) + continue; + else if (ret < 0) { + port = 0; + break; + } + rtcp_exp->tuple.dst.u.udp.port = htons(port + 1); + ret = nf_ct_expect_related(rtcp_exp); + if (ret == 0) + break; + else if (ret != -EBUSY) { + nf_ct_unexpect_related(rtp_exp); + port = 0; + break; + } + } + + if (port == 0) + goto err1; + + /* Update media port. */ + if (rtp_exp->tuple.dst.u.udp.port != rtp_exp->saved_proto.udp.port && + !nf_nat_sdp_port(skb, protoff, dataoff, dptr, datalen, + mediaoff, medialen, port)) + goto err2; + + return NF_ACCEPT; + +err2: + nf_ct_unexpect_related(rtp_exp); + nf_ct_unexpect_related(rtcp_exp); +err1: + return NF_DROP; +} + +static struct nf_ct_helper_expectfn sip_nat = { + .name = "sip", + .expectfn = nf_nat_sip_expected, +}; + +static void __exit nf_nat_sip_fini(void) +{ + RCU_INIT_POINTER(nf_nat_sip_hook, NULL); + RCU_INIT_POINTER(nf_nat_sip_seq_adjust_hook, NULL); + RCU_INIT_POINTER(nf_nat_sip_expect_hook, NULL); + RCU_INIT_POINTER(nf_nat_sdp_addr_hook, NULL); + RCU_INIT_POINTER(nf_nat_sdp_port_hook, NULL); + RCU_INIT_POINTER(nf_nat_sdp_session_hook, NULL); + RCU_INIT_POINTER(nf_nat_sdp_media_hook, NULL); + nf_ct_helper_expectfn_unregister(&sip_nat); + synchronize_rcu(); +} + +static int __init nf_nat_sip_init(void) +{ + BUG_ON(nf_nat_sip_hook != NULL); + BUG_ON(nf_nat_sip_seq_adjust_hook != NULL); + BUG_ON(nf_nat_sip_expect_hook != NULL); + BUG_ON(nf_nat_sdp_addr_hook != NULL); + BUG_ON(nf_nat_sdp_port_hook != NULL); + BUG_ON(nf_nat_sdp_session_hook != NULL); + BUG_ON(nf_nat_sdp_media_hook != NULL); + RCU_INIT_POINTER(nf_nat_sip_hook, nf_nat_sip); + RCU_INIT_POINTER(nf_nat_sip_seq_adjust_hook, nf_nat_sip_seq_adjust); + RCU_INIT_POINTER(nf_nat_sip_expect_hook, nf_nat_sip_expect); + RCU_INIT_POINTER(nf_nat_sdp_addr_hook, nf_nat_sdp_addr); + RCU_INIT_POINTER(nf_nat_sdp_port_hook, nf_nat_sdp_port); + RCU_INIT_POINTER(nf_nat_sdp_session_hook, nf_nat_sdp_session); + RCU_INIT_POINTER(nf_nat_sdp_media_hook, nf_nat_sdp_media); + nf_ct_helper_expectfn_register(&sip_nat); + return 0; +} + +module_init(nf_nat_sip_init); +module_exit(nf_nat_sip_fini); -- cgit v1.2.3 From 8a91bb0c304b0853f8c59b1b48c7822c52362cba Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sun, 26 Aug 2012 19:14:31 +0200 Subject: netfilter: ip6tables: add stateless IPv6-to-IPv6 Network Prefix Translation target Signed-off-by: Patrick McHardy --- include/linux/netfilter_ipv6/Kbuild | 1 + include/linux/netfilter_ipv6/ip6t_NPT.h | 16 ++++ net/ipv6/netfilter/Kconfig | 9 ++ net/ipv6/netfilter/Makefile | 1 + net/ipv6/netfilter/ip6t_NPT.c | 165 ++++++++++++++++++++++++++++++++ 5 files changed, 192 insertions(+) create mode 100644 include/linux/netfilter_ipv6/ip6t_NPT.h create mode 100644 net/ipv6/netfilter/ip6t_NPT.c (limited to 'include') diff --git a/include/linux/netfilter_ipv6/Kbuild b/include/linux/netfilter_ipv6/Kbuild index bd095bc075e9..b88c0058bf73 100644 --- a/include/linux/netfilter_ipv6/Kbuild +++ b/include/linux/netfilter_ipv6/Kbuild @@ -1,6 +1,7 @@ header-y += ip6_tables.h header-y += ip6t_HL.h header-y += ip6t_LOG.h +header-y += ip6t_NPT.h header-y += ip6t_REJECT.h header-y += ip6t_ah.h header-y += ip6t_frag.h diff --git a/include/linux/netfilter_ipv6/ip6t_NPT.h b/include/linux/netfilter_ipv6/ip6t_NPT.h new file mode 100644 index 000000000000..f763355481b5 --- /dev/null +++ b/include/linux/netfilter_ipv6/ip6t_NPT.h @@ -0,0 +1,16 @@ +#ifndef __NETFILTER_IP6T_NPT +#define __NETFILTER_IP6T_NPT + +#include +#include + +struct ip6t_npt_tginfo { + union nf_inet_addr src_pfx; + union nf_inet_addr dst_pfx; + __u8 src_pfx_len; + __u8 dst_pfx_len; + /* Used internally by the kernel */ + __sum16 adjustment; +}; + +#endif /* __NETFILTER_IP6T_NPT */ diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index 7bdf73be9a99..3b73254d7bf1 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig @@ -177,6 +177,15 @@ config IP6_NF_TARGET_REDIRECT To compile it as a module, choose M here. If unsure, say N. +config IP6_NF_TARGET_NPT + tristate "NPT (Network Prefix translation) target support" + depends on NETFILTER_ADVANCED + help + This option adds the `SNPT' and `DNPT' target, which perform + stateless IPv6-to-IPv6 Network Prefix Translation per RFC 6296. + + To compile it as a module, choose M here. If unsure, say N. + config IP6_NF_FILTER tristate "Packet filtering" default m if NETFILTER_ADVANCED=n diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile index 0864ce601651..5752132ca159 100644 --- a/net/ipv6/netfilter/Makefile +++ b/net/ipv6/netfilter/Makefile @@ -36,5 +36,6 @@ obj-$(CONFIG_IP6_NF_MATCH_RT) += ip6t_rt.o # targets obj-$(CONFIG_IP6_NF_TARGET_MASQUERADE) += ip6t_MASQUERADE.o obj-$(CONFIG_IP6_NF_TARGET_NETMAP) += ip6t_NETMAP.o +obj-$(CONFIG_IP6_NF_TARGET_NPT) += ip6t_NPT.o obj-$(CONFIG_IP6_NF_TARGET_REDIRECT) += ip6t_REDIRECT.o obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o diff --git a/net/ipv6/netfilter/ip6t_NPT.c b/net/ipv6/netfilter/ip6t_NPT.c new file mode 100644 index 000000000000..e9486915eff6 --- /dev/null +++ b/net/ipv6/netfilter/ip6t_NPT.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2011, 2012 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 + +static __sum16 csum16_complement(__sum16 a) +{ + return (__force __sum16)(0xffff - (__force u16)a); +} + +static __sum16 csum16_add(__sum16 a, __sum16 b) +{ + u16 sum; + + sum = (__force u16)a + (__force u16)b; + sum += (__force u16)a < (__force u16)b; + return (__force __sum16)sum; +} + +static __sum16 csum16_sub(__sum16 a, __sum16 b) +{ + return csum16_add(a, csum16_complement(b)); +} + +static int ip6t_npt_checkentry(const struct xt_tgchk_param *par) +{ + struct ip6t_npt_tginfo *npt = par->targinfo; + __sum16 src_sum = 0, dst_sum = 0; + unsigned int i; + + if (npt->src_pfx_len > 64 || npt->dst_pfx_len > 64) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(npt->src_pfx.in6.s6_addr16); i++) { + src_sum = csum16_add(src_sum, + (__force __sum16)npt->src_pfx.in6.s6_addr16[i]); + dst_sum = csum16_add(dst_sum, + (__force __sum16)npt->dst_pfx.in6.s6_addr16[i]); + } + + npt->adjustment = csum16_sub(src_sum, dst_sum); + return 0; +} + +static bool ip6t_npt_map_pfx(const struct ip6t_npt_tginfo *npt, + struct in6_addr *addr) +{ + unsigned int pfx_len; + unsigned int i, idx; + __be32 mask; + __sum16 sum; + + pfx_len = max(npt->src_pfx_len, npt->dst_pfx_len); + for (i = 0; i < pfx_len; i += 32) { + if (pfx_len - i >= 32) + mask = 0; + else + mask = htonl(~((1 << (pfx_len - i)) - 1)); + + idx = i / 32; + addr->s6_addr32[idx] &= mask; + addr->s6_addr32[idx] |= npt->dst_pfx.in6.s6_addr32[idx]; + } + + if (pfx_len <= 48) + idx = 3; + else { + for (idx = 4; idx < ARRAY_SIZE(addr->s6_addr16); idx++) { + if ((__force __sum16)addr->s6_addr16[idx] != + CSUM_MANGLED_0) + break; + } + if (idx == ARRAY_SIZE(addr->s6_addr16)) + return false; + } + + sum = csum16_add((__force __sum16)addr->s6_addr16[idx], + npt->adjustment); + if (sum == CSUM_MANGLED_0) + sum = 0; + *(__force __sum16 *)&addr->s6_addr16[idx] = sum; + + return true; +} + +static unsigned int +ip6t_snpt_tg(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct ip6t_npt_tginfo *npt = par->targinfo; + + if (!ip6t_npt_map_pfx(npt, &ipv6_hdr(skb)->saddr)) { + icmpv6_send(skb, ICMPV6_PARAMPROB, ICMPV6_HDR_FIELD, + offsetof(struct ipv6hdr, saddr)); + return NF_DROP; + } + return XT_CONTINUE; +} + +static unsigned int +ip6t_dnpt_tg(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct ip6t_npt_tginfo *npt = par->targinfo; + + if (!ip6t_npt_map_pfx(npt, &ipv6_hdr(skb)->daddr)) { + icmpv6_send(skb, ICMPV6_PARAMPROB, ICMPV6_HDR_FIELD, + offsetof(struct ipv6hdr, daddr)); + return NF_DROP; + } + return XT_CONTINUE; +} + +static struct xt_target ip6t_npt_target_reg[] __read_mostly = { + { + .name = "SNPT", + .target = ip6t_snpt_tg, + .targetsize = sizeof(struct ip6t_npt_tginfo), + .checkentry = ip6t_npt_checkentry, + .family = NFPROTO_IPV6, + .hooks = (1 << NF_INET_LOCAL_IN) | + (1 << NF_INET_POST_ROUTING), + .me = THIS_MODULE, + }, + { + .name = "DNPT", + .target = ip6t_dnpt_tg, + .targetsize = sizeof(struct ip6t_npt_tginfo), + .checkentry = ip6t_npt_checkentry, + .family = NFPROTO_IPV6, + .hooks = (1 << NF_INET_PRE_ROUTING) | + (1 << NF_INET_LOCAL_OUT), + .me = THIS_MODULE, + }, +}; + +static int __init ip6t_npt_init(void) +{ + return xt_register_targets(ip6t_npt_target_reg, + ARRAY_SIZE(ip6t_npt_target_reg)); +} + +static void __exit ip6t_npt_exit(void) +{ + xt_unregister_targets(ip6t_npt_target_reg, + ARRAY_SIZE(ip6t_npt_target_reg)); +} + +module_init(ip6t_npt_init); +module_exit(ip6t_npt_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("IPv6-to-IPv6 Network Prefix Translation (RFC 6296)"); +MODULE_AUTHOR("Patrick McHardy "); +MODULE_ALIAS("ip6t_SNPT"); +MODULE_ALIAS("ip6t_DNPT"); -- cgit v1.2.3 From f9dc9ac51610a35629c8211b6b8c9b0c65cf0e1d Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 24 Aug 2012 01:58:59 +0000 Subject: of/mdio: Add dummy functions in of_mdio.h. This patch adds dummy functions in of_mdio.h, so that driver need not ifdef there code with CONFIG_OF. Signed-off-by: Srinivas Kandagatla Signed-off-by: David S. Miller --- include/linux/of_mdio.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'include') diff --git a/include/linux/of_mdio.h b/include/linux/of_mdio.h index 912c27a0f7ee..6ef49b803efb 100644 --- a/include/linux/of_mdio.h +++ b/include/linux/of_mdio.h @@ -12,6 +12,7 @@ #include #include +#ifdef CONFIG_OF extern int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np); extern struct phy_device *of_phy_find_device(struct device_node *phy_np); extern struct phy_device *of_phy_connect(struct net_device *dev, @@ -24,4 +25,36 @@ extern struct phy_device *of_phy_connect_fixed_link(struct net_device *dev, extern struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np); +#else /* CONFIG_OF */ +int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) +{ + return -ENOSYS; +} + +struct phy_device *of_phy_find_device(struct device_node *phy_np) +{ + return NULL; +} + +struct phy_device *of_phy_connect(struct net_device *dev, + struct device_node *phy_np, + void (*hndlr)(struct net_device *), + u32 flags, phy_interface_t iface) +{ + return NULL; +} + +struct phy_device *of_phy_connect_fixed_link(struct net_device *dev, + void (*hndlr)(struct net_device *), + phy_interface_t iface) +{ + return NULL; +} + +struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np) +{ + return NULL; +} +#endif /* CONFIG_OF */ + #endif /* __LINUX_OF_MDIO_H */ -- cgit v1.2.3 From 5c879d2094946081af934739850c7260e8b25d3c Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Sun, 26 Aug 2012 00:35:45 +0000 Subject: bnx2x: fix 57840_MF pci id Commit c3def943c7117d42caaed3478731ea7c3c87190e have added support for new pci ids of the 57840 board, while failing to change the obsolete value in 'pci_ids.h'. This patch does so, allowing the probe of such devices. Signed-off-by: Yuval Mintz Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- include/linux/pci_ids.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index fc3526077348..6b4565c440c8 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2149,7 +2149,7 @@ #define PCI_DEVICE_ID_TIGON3_5704S 0x16a8 #define PCI_DEVICE_ID_NX2_57800_VF 0x16a9 #define PCI_DEVICE_ID_NX2_5706S 0x16aa -#define PCI_DEVICE_ID_NX2_57840_MF 0x16ab +#define PCI_DEVICE_ID_NX2_57840_MF 0x16a4 #define PCI_DEVICE_ID_NX2_5708S 0x16ac #define PCI_DEVICE_ID_NX2_57840_VF 0x16ad #define PCI_DEVICE_ID_NX2_57810_MF 0x16ae -- cgit v1.2.3 From 36ac914ba2a5f1a14c5e0bf5d33bc20016d50940 Mon Sep 17 00:00:00 2001 From: Xiaofan Tian Date: Thu, 30 Aug 2012 17:03:35 +0800 Subject: regmap: irq: Add mask invert flag for enable register Currently, regmap will write 1 to mask_base to mask an interrupt and write 0 to unmask it. But some chips do not have an interrupt mask register, and only have interrupt enable register. Then we should write 0 to disable interrupt and 1 to enable. So add an mask_invert flag to handle this. If it is not set, behavior is same as previous. If set it to 1, the mask value will be inverted before written to mask_base Signed-off-by: Xiaofan Tian Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 12 ++++++++++-- include/linux/regmap.h | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index bb5e10305197..094f9988fdf2 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -78,7 +78,11 @@ static void regmap_irq_sync_unlock(struct irq_data *data) for (i = 0; i < d->chip->num_regs; i++) { reg = d->chip->mask_base + (i * map->reg_stride * d->irq_reg_stride); - ret = regmap_update_bits(d->map, reg, + if (d->chip->mask_invert) + ret = regmap_update_bits(d->map, reg, + d->mask_buf_def[i], ~d->mask_buf[i]); + else + ret = regmap_update_bits(d->map, reg, d->mask_buf_def[i], d->mask_buf[i]); if (ret != 0) dev_err(d->map->dev, "Failed to sync masks in %x\n", @@ -338,7 +342,11 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, d->mask_buf[i] = d->mask_buf_def[i]; reg = chip->mask_base + (i * map->reg_stride * d->irq_reg_stride); - ret = regmap_update_bits(map, reg, + if (chip->mask_invert) + ret = regmap_update_bits(map, reg, + d->mask_buf[i], ~d->mask_buf[i]); + else + ret = regmap_update_bits(map, reg, d->mask_buf[i], d->mask_buf[i]); if (ret != 0) { dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 9c4c9c673f38..e3bcc3f4dcb8 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -300,6 +300,7 @@ struct regmap_irq_chip { unsigned int ack_base; unsigned int wake_base; unsigned int irq_reg_stride; + unsigned int mask_invert; bool runtime_pm; int num_regs; -- cgit v1.2.3 From 85b8614d722389202af298e1bf8a599c431fef19 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Fri, 24 Aug 2012 20:46:18 +0200 Subject: usb: gadget: get rid of USB_GADGET_{DUAL,SUPER}SPEED This commit removes USB_GADGET_DUALSPEED and USB_GADGET_SUPERSPEED Kconfig options. Since now kernel allows many UDC drivers to be compiled, those options may turn to no longer be valid. For instance, if someone decides to build UDC that supports super speed and UDC that supports high speed only, the latter will be "assumed" to support super speed since USB_GADGET_SUPERSPEED will be selected by the former. The test of whether CONFIG_USB_GADGET_*SPEED was defined was just an optimisation which removed otherwise dead code (ie. if UDC is not dual speed, there is no need to handle cases that can happen if speed is high). This commit removes those checks. Signed-off-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/chipidea/Kconfig | 1 - drivers/usb/dwc3/Kconfig | 2 -- drivers/usb/gadget/Kconfig | 25 ------------------------- drivers/usb/gadget/composite.c | 9 +-------- drivers/usb/gadget/inode.c | 15 ++++----------- drivers/usb/gadget/u_ether.c | 7 ------- drivers/usb/musb/Kconfig | 1 - include/linux/usb/gadget.h | 19 ++----------------- 8 files changed, 7 insertions(+), 72 deletions(-) (limited to 'include') diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig index 8337fb5d988d..272109ff5aa3 100644 --- a/drivers/usb/chipidea/Kconfig +++ b/drivers/usb/chipidea/Kconfig @@ -13,7 +13,6 @@ if USB_CHIPIDEA config USB_CHIPIDEA_UDC bool "ChipIdea device controller" depends on USB_GADGET - select USB_GADGET_DUALSPEED help Say Y here to enable device controller functionality of the ChipIdea driver. diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index d13c60f42139..f6a6e070c2ac 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -2,8 +2,6 @@ config USB_DWC3 tristate "DesignWare USB3 DRD Core Support" depends on (USB && USB_GADGET) select USB_OTG_UTILS - select USB_GADGET_DUALSPEED - select USB_GADGET_SUPERSPEED 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/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 51ab5fd5d468..2ba0d0e2eed6 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -154,7 +154,6 @@ config USB_LPC32XX config USB_ATMEL_USBA tristate "Atmel USBA" - select USB_GADGET_DUALSPEED depends on AVR32 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45 help USBA is the integrated high-speed USB Device controller on @@ -163,7 +162,6 @@ config USB_ATMEL_USBA config USB_FSL_USB2 tristate "Freescale Highspeed USB DR Peripheral Controller" depends on FSL_SOC || ARCH_MXC - select USB_GADGET_DUALSPEED select USB_FSL_MPH_DR_OF if OF help Some of Freescale PowerPC and i.MX processors have a High Speed @@ -179,7 +177,6 @@ config USB_FSL_USB2 config USB_FUSB300 tristate "Faraday FUSB300 USB Peripheral Controller" depends on !PHYS_ADDR_T_64BIT - select USB_GADGET_DUALSPEED help Faraday usb device controller FUSB300 driver @@ -227,7 +224,6 @@ config USB_PXA25X_SMALL config USB_R8A66597 tristate "Renesas R8A66597 USB Peripheral Controller" - select USB_GADGET_DUALSPEED help R8A66597 is a discrete USB host and peripheral controller chip that supports both full and high speed USB 2.0 data transfers. @@ -240,7 +236,6 @@ config USB_R8A66597 config USB_RENESAS_USBHS_UDC tristate 'Renesas USBHS controller' depends on USB_RENESAS_USBHS - select USB_GADGET_DUALSPEED help Renesas USBHS is a discrete USB host and peripheral controller chip that supports both full and high speed USB 2.0 data transfers. @@ -268,7 +263,6 @@ config USB_PXA27X config USB_S3C_HSOTG tristate "S3C HS/OtG USB Device controller" depends on S3C_DEV_USB_HSOTG - select USB_GADGET_DUALSPEED help The Samsung S3C64XX USB2.0 high-speed gadget controller integrated into the S3C64XX series SoC. @@ -305,7 +299,6 @@ config USB_S3C2410_DEBUG config USB_S3C_HSUDC tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller" depends on ARCH_S3C24XX - select USB_GADGET_DUALSPEED help Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC integrated with dual speed USB 2.0 device controller. It has @@ -315,7 +308,6 @@ config USB_S3C_HSUDC config USB_MV_UDC tristate "Marvell USB2.0 Device Controller" - select USB_GADGET_DUALSPEED help Marvell Socs (including PXA and MMP series) include a high speed USB2.0 OTG controller, which can be configured as high speed or @@ -338,14 +330,12 @@ config USB_MV_U3D config USB_GADGET_MUSB_HDRC tristate "Inventra HDRC USB Peripheral (TI, ADI, ...)" depends on USB_MUSB_HDRC - select USB_GADGET_DUALSPEED help This OTG-capable silicon IP is used in dual designs including the TI DaVinci, OMAP 243x, OMAP 343x, TUSB 6010, and ADI Blackfin config USB_M66592 tristate "Renesas M66592 USB Peripheral Controller" - select USB_GADGET_DUALSPEED help M66592 is a discrete USB peripheral controller chip that supports both full and high speed USB 2.0 data transfers. @@ -362,7 +352,6 @@ config USB_M66592 config USB_AMD5536UDC tristate "AMD5536 UDC" depends on PCI - select USB_GADGET_DUALSPEED help The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge. It is a USB Highspeed DMA capable USB device controller. Beside ep0 @@ -389,7 +378,6 @@ config USB_FSL_QE config USB_NET2272 tristate "PLX NET2272" - select USB_GADGET_DUALSPEED help PLX NET2272 is a USB peripheral controller which supports both full and high speed USB 2.0 data transfers. @@ -413,7 +401,6 @@ config USB_NET2272_DMA config USB_NET2280 tristate "NetChip 228x" depends on PCI - select USB_GADGET_DUALSPEED help NetChip 2280 / 2282 is a PCI based USB peripheral controller which supports both full and high speed USB 2.0 data transfers. @@ -443,7 +430,6 @@ config USB_GOKU config USB_EG20T tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC" depends on PCI - select USB_GADGET_DUALSPEED help This is a USB device driver for EG20T PCH. EG20T PCH is the platform controller hub that is used in Intel's @@ -470,8 +456,6 @@ config USB_EG20T config USB_DUMMY_HCD tristate "Dummy HCD (DEVELOPMENT)" depends on USB=y || (USB=m && USB_GADGET=m) - select USB_GADGET_DUALSPEED - select USB_GADGET_SUPERSPEED help This host controller driver emulates USB, looping all data transfer requests back to a USB "gadget driver" in the same host. The host @@ -496,15 +480,6 @@ config USB_DUMMY_HCD endmenu -# Selected by UDC drivers that support high-speed operation. -config USB_GADGET_DUALSPEED - bool - -# Selected by UDC drivers that support super-speed opperation -config USB_GADGET_SUPERSPEED - bool - depends on USB_GADGET_DUALSPEED - # # USB Gadget Drivers # diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 2cb1030203b5..91411a6d741b 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1586,12 +1586,6 @@ composite_resume(struct usb_gadget *gadget) /*-------------------------------------------------------------------------*/ static struct usb_gadget_driver composite_driver = { -#ifdef CONFIG_USB_GADGET_SUPERSPEED - .max_speed = USB_SPEED_SUPER, -#else - .max_speed = USB_SPEED_HIGH, -#endif - .unbind = composite_unbind, .setup = composite_setup, @@ -1636,8 +1630,7 @@ int usb_composite_probe(struct usb_composite_driver *driver, driver->iProduct = driver->name; composite_driver.function = (char *) driver->name; composite_driver.driver.name = driver->name; - composite_driver.max_speed = - min_t(u8, composite_driver.max_speed, driver->max_speed); + composite_driver.max_speed = driver->max_speed; composite = driver; composite_gadget_bind = bind; diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index e58b16442971..ae13a106fb96 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -828,7 +828,6 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) if (value == 0) data->state = STATE_EP_ENABLED; break; -#ifdef CONFIG_USB_GADGET_DUALSPEED case USB_SPEED_HIGH: /* fails if caller didn't provide that descriptor... */ ep->desc = &data->hs_desc; @@ -836,7 +835,6 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) if (value == 0) data->state = STATE_EP_ENABLED; break; -#endif default: DBG(data->dev, "unconnected, %s init abandoned\n", data->name); @@ -1324,7 +1322,6 @@ static const struct file_operations ep0_io_operations = { * Unrecognized ep0 requests may be handled in user space. */ -#ifdef CONFIG_USB_GADGET_DUALSPEED static void make_qualifier (struct dev_data *dev) { struct usb_qualifier_descriptor qual; @@ -1347,7 +1344,6 @@ static void make_qualifier (struct dev_data *dev) memcpy (dev->rbuf, &qual, sizeof qual); } -#endif static int config_buf (struct dev_data *dev, u8 type, unsigned index) @@ -1427,7 +1423,6 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) dev->dev->bMaxPacketSize0 = dev->gadget->ep0->maxpacket; req->buf = dev->dev; break; -#ifdef CONFIG_USB_GADGET_DUALSPEED case USB_DT_DEVICE_QUALIFIER: if (!dev->hs_config) break; @@ -1437,7 +1432,6 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) break; case USB_DT_OTHER_SPEED_CONFIG: // FALLTHROUGH -#endif case USB_DT_CONFIG: value = config_buf (dev, w_value >> 8, @@ -1763,11 +1757,6 @@ gadgetfs_suspend (struct usb_gadget *gadget) } static struct usb_gadget_driver gadgetfs_driver = { -#ifdef CONFIG_USB_GADGET_DUALSPEED - .max_speed = USB_SPEED_HIGH, -#else - .max_speed = USB_SPEED_FULL, -#endif .function = (char *) driver_desc, .unbind = gadgetfs_unbind, .setup = gadgetfs_setup, @@ -1900,6 +1889,10 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) /* triggers gadgetfs_bind(); then we can enumerate. */ spin_unlock_irq (&dev->lock); + if (dev->hs_config) + gadgetfs_driver.max_speed = USB_SPEED_HIGH; + else + gadgetfs_driver.max_speed = USB_SPEED_FULL; value = usb_gadget_probe_driver(&gadgetfs_driver, gadgetfs_bind); if (value != 0) { kfree (dev->buf); diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index 90e82e288eb9..1154a99dc8db 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -83,17 +83,10 @@ struct eth_dev { #define DEFAULT_QLEN 2 /* double buffering by default */ - -#ifdef CONFIG_USB_GADGET_DUALSPEED - static unsigned qmult = 5; module_param(qmult, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(qmult, "queue length multiplier at high/super speed"); -#else /* full speed (low speed doesn't do bulk) */ -#define qmult 1 -#endif - /* for dual-speed hardware, use deeper queues at high/super speed */ static inline int qlen(struct usb_gadget *gadget) { diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index ef0c3f9f0947..1b9f005ae9a5 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -12,7 +12,6 @@ config USB_MUSB_HDRC select TWL4030_USB if MACH_OMAP_3430SDP select TWL6030_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA select USB_OTG_UTILS - select USB_GADGET_DUALSPEED help Say Y here if your system has a dual role high speed USB controller based on the Mentor Graphics silicon IP. Then diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 9517466ababb..d05b220f0fd3 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -558,14 +558,7 @@ static inline struct usb_gadget *dev_to_usb_gadget(struct device *dev) */ static inline int gadget_is_dualspeed(struct usb_gadget *g) { -#ifdef CONFIG_USB_GADGET_DUALSPEED - /* runtime test would check "g->max_speed" ... that might be - * useful to work around hardware bugs, but is mostly pointless - */ - return 1; -#else - return 0; -#endif + return g->max_speed >= USB_SPEED_HIGH; } /** @@ -575,15 +568,7 @@ static inline int gadget_is_dualspeed(struct usb_gadget *g) */ static inline int gadget_is_superspeed(struct usb_gadget *g) { -#ifdef CONFIG_USB_GADGET_SUPERSPEED - /* - * runtime test would check "g->max_speed" ... that might be - * useful to work around hardware bugs, but is mostly pointless - */ - return 1; -#else - return 0; -#endif + return g->max_speed >= USB_SPEED_SUPER; } /** -- cgit v1.2.3 From 5b423f6a40a0327f9d40bc8b97ce9be266f74368 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 29 Aug 2012 16:25:49 +0000 Subject: netfilter: nf_conntrack: fix racy timer handling with reliable events Existing code assumes that del_timer returns true for alive conntrack entries. However, this is not true if reliable events are enabled. In that case, del_timer may return true for entries that were just inserted in the dying list. Note that packets / ctnetlink may hold references to conntrack entries that were just inserted to such list. This patch fixes the issue by adding an independent timer for event delivery. This increases the size of the ecache extension. Still we can revisit this later and use variable size extensions to allocate this area on demand. Tested-by: Oliver Smith Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_ecache.h | 1 + net/netfilter/nf_conntrack_core.c | 16 +++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_ecache.h b/include/net/netfilter/nf_conntrack_ecache.h index e1ce1048fe5f..4a045cda9c60 100644 --- a/include/net/netfilter/nf_conntrack_ecache.h +++ b/include/net/netfilter/nf_conntrack_ecache.h @@ -18,6 +18,7 @@ struct nf_conntrack_ecache { u16 ctmask; /* bitmask of ct events to be delivered */ u16 expmask; /* bitmask of expect events to be delivered */ u32 pid; /* netlink pid of destroyer */ + struct timer_list timeout; }; static inline struct nf_conntrack_ecache * diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index cf4875565d67..2ceec64b19f9 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -249,12 +249,15 @@ static void death_by_event(unsigned long ul_conntrack) { struct nf_conn *ct = (void *)ul_conntrack; struct net *net = nf_ct_net(ct); + struct nf_conntrack_ecache *ecache = nf_ct_ecache_find(ct); + + BUG_ON(ecache == NULL); if (nf_conntrack_event(IPCT_DESTROY, ct) < 0) { /* bad luck, let's retry again */ - ct->timeout.expires = jiffies + + ecache->timeout.expires = jiffies + (random32() % net->ct.sysctl_events_retry_timeout); - add_timer(&ct->timeout); + add_timer(&ecache->timeout); return; } /* we've got the event delivered, now it's dying */ @@ -268,6 +271,9 @@ static void death_by_event(unsigned long ul_conntrack) void nf_ct_insert_dying_list(struct nf_conn *ct) { struct net *net = nf_ct_net(ct); + struct nf_conntrack_ecache *ecache = nf_ct_ecache_find(ct); + + BUG_ON(ecache == NULL); /* add this conntrack to the dying list */ spin_lock_bh(&nf_conntrack_lock); @@ -275,10 +281,10 @@ void nf_ct_insert_dying_list(struct nf_conn *ct) &net->ct.dying); spin_unlock_bh(&nf_conntrack_lock); /* set a new timer to retry event delivery */ - setup_timer(&ct->timeout, death_by_event, (unsigned long)ct); - ct->timeout.expires = jiffies + + setup_timer(&ecache->timeout, death_by_event, (unsigned long)ct); + ecache->timeout.expires = jiffies + (random32() % net->ct.sysctl_events_retry_timeout); - add_timer(&ct->timeout); + add_timer(&ecache->timeout); } EXPORT_SYMBOL_GPL(nf_ct_insert_dying_list); -- cgit v1.2.3 From 380a0e6f72e8d0211f48c24f293a366a53b374d7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 31 Aug 2012 10:31:53 -0700 Subject: regulator: Clarify documentation for regmap in the config Signed-off-by: Mark Brown --- include/linux/regulator/driver.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index bac4c871f3bd..4a9ca04a13ef 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -221,7 +221,8 @@ struct regulator_desc { * @driver_data: private regulator data * @of_node: OpenFirmware node to parse for device tree bindings (may be * NULL). - * @regmap: regmap to use for core regmap helpers + * @regmap: regmap to use for core regmap helpers if dev_get_regulator() is + * insufficient. * @ena_gpio: GPIO controlling regulator enable. * @ena_gpio_invert: Sense for GPIO enable control. * @ena_gpio_flags: Flags to use when calling gpio_request_one() -- cgit v1.2.3 From 6c9ff979d1921e9fd05d89e1383121c2503759b9 Mon Sep 17 00:00:00 2001 From: Alex Bergmann Date: Fri, 31 Aug 2012 02:48:31 +0000 Subject: tcp: Increase timeout for SYN segments Commit 9ad7c049 ("tcp: RFC2988bis + taking RTT sample from 3WHS for the passive open side") changed the initRTO from 3secs to 1sec in accordance to RFC6298 (former RFC2988bis). This reduced the time till the last SYN retransmission packet gets sent from 93secs to 31secs. RFC1122 is stating that the retransmission should be done for at least 3 minutes, but this seems to be quite high. "However, the values of R1 and R2 may be different for SYN and data segments. In particular, R2 for a SYN segment MUST be set large enough to provide retransmission of the segment for at least 3 minutes. The application can close the connection (i.e., give up on the open attempt) sooner, of course." This patch increases the value of TCP_SYN_RETRIES to the value of 6, providing a retransmission window of 63secs. The comments for SYN and SYNACK retries have also been updated to describe the current settings. The same goes for the documentation file "Documentation/networking/ip-sysctl.txt". Signed-off-by: Alexander Bergmann Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 8 ++++++-- include/net/tcp.h | 18 ++++++++++++++---- 2 files changed, 20 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index ca447b35b833..d64e53124b8c 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -439,7 +439,9 @@ tcp_stdurg - BOOLEAN tcp_synack_retries - INTEGER Number of times SYNACKs for a passive TCP connection attempt will be retransmitted. Should not be higher than 255. Default value - is 5, which corresponds to ~180seconds. + is 5, which corresponds to 31seconds till the last retransmission + with the current initial RTO of 1second. With this the final timeout + for a passive TCP connection will happen after 63seconds. tcp_syncookies - BOOLEAN Only valid when the kernel was compiled with CONFIG_SYNCOOKIES @@ -478,7 +480,9 @@ tcp_fastopen - INTEGER tcp_syn_retries - INTEGER Number of times initial SYNs for an active TCP connection attempt will be retransmitted. Should not be higher than 255. Default value - is 5, which corresponds to ~180seconds. + is 6, which corresponds to 63seconds till the last restransmission + with the current initial RTO of 1second. With this the final timeout + for an active TCP connection attempt will happen after 127seconds. tcp_timestamps - BOOLEAN Enable timestamps as defined in RFC1323. diff --git a/include/net/tcp.h b/include/net/tcp.h index 9a0021d16d91..0fca06f16463 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -98,11 +98,21 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo); * 15 is ~13-30min depending on RTO. */ -#define TCP_SYN_RETRIES 5 /* number of times to retry active opening a - * connection: ~180sec is RFC minimum */ +#define TCP_SYN_RETRIES 6 /* This is how many retries are done + * when active opening a connection. + * RFC1122 says the minimum retry MUST + * be at least 180secs. Nevertheless + * this value is corresponding to + * 63secs of retransmission with the + * current initial RTO. + */ -#define TCP_SYNACK_RETRIES 5 /* number of times to retry passive opening a - * connection: ~180sec is RFC minimum */ +#define TCP_SYNACK_RETRIES 5 /* This is how may retries are done + * when passive opening a connection. + * This is corresponding to 31secs of + * retransmission with the current + * initial RTO. + */ #define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to destroy TIME-WAIT * state, about 60 seconds */ -- cgit v1.2.3 From d56631a66c0d0c9d662abfb38cd1f6326eeebd7c Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 30 Aug 2012 05:50:43 +0000 Subject: net:stmmac: Remove bus_id from mdio platform data. This patch removes bus_id from mdio platform data, The reason to remove bus_id is, stmmac mdio bus_id is always same as stmmac bus-id, so there is no point in passing this in different variable. Also stmmac ethernet driver connects to phy with bus_id passed its platform data. So, having single bus-id is much simpler. Signed-off-by: Srinivas Kandagatla Signed-off-by: David S. Miller --- Documentation/networking/stmmac.txt | 5 ----- drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 8 +++----- drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c | 1 - include/linux/stmmac.h | 1 - 4 files changed, 3 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/Documentation/networking/stmmac.txt b/Documentation/networking/stmmac.txt index c676b9cedbd0..ef9ee71b4d7f 100644 --- a/Documentation/networking/stmmac.txt +++ b/Documentation/networking/stmmac.txt @@ -173,7 +173,6 @@ Where: For MDIO bus The we have: struct stmmac_mdio_bus_data { - int bus_id; int (*phy_reset)(void *priv); unsigned int phy_mask; int *irqs; @@ -181,7 +180,6 @@ For MDIO bus The we have: }; Where: - o bus_id: bus identifier; o phy_reset: hook to reset the phy device attached to the bus. o phy_mask: phy mask passed when register the MDIO bus within the driver. o irqs: list of IRQs, one per PHY. @@ -230,9 +228,6 @@ there are two MAC cores: one MAC is for MDIO Bus/PHY emulation with fixed_link support. static struct stmmac_mdio_bus_data stmmac1_mdio_bus = { - .bus_id = 1, - | - |-> phy device on the bus_id 1 .phy_reset = phy_reset; | |-> function to provide the phy_reset on this board diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index e1f3d04a8c90..0376a5e6b2bf 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -177,7 +177,7 @@ int stmmac_mdio_register(struct net_device *ndev) new_bus->write = &stmmac_mdio_write; new_bus->reset = &stmmac_mdio_reset; snprintf(new_bus->id, MII_BUS_ID_SIZE, "%s-%x", - new_bus->name, mdio_bus_data->bus_id); + new_bus->name, priv->plat->bus_id); new_bus->priv = ndev; new_bus->irq = irqlist; new_bus->phy_mask = mdio_bus_data->phy_mask; @@ -213,12 +213,10 @@ int stmmac_mdio_register(struct net_device *ndev) * and no PHY number was provided to the MAC, * use the one probed here. */ - if ((priv->plat->bus_id == mdio_bus_data->bus_id) && - (priv->plat->phy_addr == -1)) + if (priv->plat->phy_addr == -1) priv->plat->phy_addr = addr; - act = (priv->plat->bus_id == mdio_bus_data->bus_id) && - (priv->plat->phy_addr == addr); + act = (priv->plat->phy_addr == addr); switch (phydev->irq) { case PHY_POLL: irq_str = "POLL"; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c index 13afb8edfadc..1f069b0f6af5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c @@ -40,7 +40,6 @@ static void stmmac_default_data(void) plat_dat.has_gmac = 1; plat_dat.force_sf_dma_mode = 1; - mdio_data.bus_id = 1; mdio_data.phy_reset = NULL; mdio_data.phy_mask = 0; plat_dat.mdio_bus_data = &mdio_data; diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index b69bdb1e08b6..a1547ea3920d 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -76,7 +76,6 @@ /* Platfrom data for platform device structure's platform_data field */ struct stmmac_mdio_bus_data { - int bus_id; int (*phy_reset)(void *priv); unsigned int phy_mask; int *irqs; -- cgit v1.2.3 From 1046716368979dee857a2b8a91c4a8833f21b9cb Mon Sep 17 00:00:00 2001 From: Jerry Chu Date: Fri, 31 Aug 2012 12:29:11 +0000 Subject: tcp: TCP Fast Open Server - header & support functions This patch adds all the necessary data structure and support functions to implement TFO server side. It also documents a number of flags for the sysctl_tcp_fastopen knob, and adds a few Linux extension MIBs. In addition, it includes the following: 1. a new TCP_FASTOPEN socket option an application must call to supply a max backlog allowed in order to enable TFO on its listener. 2. A number of key data structures: "fastopen_rsk" in tcp_sock - for a big socket to access its request_sock for retransmission and ack processing purpose. It is non-NULL iff 3WHS not completed. "fastopenq" in request_sock_queue - points to a per Fast Open listener data structure "fastopen_queue" to keep track of qlen (# of outstanding Fast Open requests) and max_qlen, among other things. "listener" in tcp_request_sock - to point to the original listener for book-keeping purpose, i.e., to maintain qlen against max_qlen as part of defense against IP spoofing attack. 3. various data structure and functions, many in tcp_fastopen.c, to support server side Fast Open cookie operations, including /proc/sys/net/ipv4/tcp_fastopen_key to allow manual rekeying. Signed-off-by: H.K. Jerry Chu Cc: Yuchung Cheng Cc: Neal Cardwell Cc: Eric Dumazet Cc: Tom Herbert Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 29 +++++++++--- include/linux/snmp.h | 4 ++ include/linux/tcp.h | 45 ++++++++++++++++-- include/net/request_sock.h | 36 +++++++++++++++ include/net/tcp.h | 46 ++++++++++++++++--- net/ipv4/proc.c | 4 ++ net/ipv4/sysctl_net_ipv4.c | 45 ++++++++++++++++++ net/ipv4/tcp_fastopen.c | 83 +++++++++++++++++++++++++++++++++- net/ipv4/tcp_input.c | 4 +- 9 files changed, 276 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index d64e53124b8c..c7fc10724948 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -467,16 +467,31 @@ tcp_syncookies - BOOLEAN tcp_fastopen - INTEGER Enable TCP Fast Open feature (draft-ietf-tcpm-fastopen) to send data in the opening SYN packet. To use this feature, the client application - must not use connect(). Instead, it should use sendmsg() or sendto() - with MSG_FASTOPEN flag which performs a TCP handshake automatically. - - The values (bitmap) are: - 1: Enables sending data in the opening SYN on the client - 5: Enables sending data in the opening SYN on the client regardless - of cookie availability. + must use sendmsg() or sendto() with MSG_FASTOPEN flag rather than + connect() to perform a TCP handshake automatically. + + The values (bitmap) are + 1: Enables sending data in the opening SYN on the client. + 2: Enables TCP Fast Open on the server side, i.e., allowing data in + a SYN packet to be accepted and passed to the application before + 3-way hand shake finishes. + 4: Send data in the opening SYN regardless of cookie availability and + without a cookie option. + 0x100: Accept SYN data w/o validating the cookie. + 0x200: Accept data-in-SYN w/o any cookie option present. + 0x400/0x800: Enable Fast Open on all listeners regardless of the + TCP_FASTOPEN socket option. The two different flags designate two + different ways of setting max_qlen without the TCP_FASTOPEN socket + option. Default: 0 + Note that the client & server side Fast Open flags (1 and 2 + respectively) must be also enabled before the rest of flags can take + effect. + + See include/net/tcp.h and the code for more details. + tcp_syn_retries - INTEGER Number of times initial SYNs for an active TCP connection attempt will be retransmitted. Should not be higher than 255. Default value diff --git a/include/linux/snmp.h b/include/linux/snmp.h index ad6e3a6bf9fb..fdfba235f9f1 100644 --- a/include/linux/snmp.h +++ b/include/linux/snmp.h @@ -241,6 +241,10 @@ enum LINUX_MIB_TCPCHALLENGEACK, /* TCPChallengeACK */ LINUX_MIB_TCPSYNCHALLENGE, /* TCPSYNChallenge */ LINUX_MIB_TCPFASTOPENACTIVE, /* TCPFastOpenActive */ + LINUX_MIB_TCPFASTOPENPASSIVE, /* TCPFastOpenPassive*/ + LINUX_MIB_TCPFASTOPENPASSIVEFAIL, /* TCPFastOpenPassiveFail */ + LINUX_MIB_TCPFASTOPENLISTENOVERFLOW, /* TCPFastOpenListenOverflow */ + LINUX_MIB_TCPFASTOPENCOOKIEREQD, /* TCPFastOpenCookieReqd */ __LINUX_MIB_MAX }; diff --git a/include/linux/tcp.h b/include/linux/tcp.h index eb125a4c30b3..ae46df590629 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -110,6 +110,7 @@ enum { #define TCP_REPAIR_QUEUE 20 #define TCP_QUEUE_SEQ 21 #define TCP_REPAIR_OPTIONS 22 +#define TCP_FASTOPEN 23 /* Enable FastOpen on listeners */ struct tcp_repair_opt { __u32 opt_code; @@ -246,6 +247,7 @@ static inline unsigned int tcp_optlen(const struct sk_buff *skb) /* TCP Fast Open */ #define TCP_FASTOPEN_COOKIE_MIN 4 /* Min Fast Open Cookie size in bytes */ #define TCP_FASTOPEN_COOKIE_MAX 16 /* Max Fast Open Cookie size in bytes */ +#define TCP_FASTOPEN_COOKIE_SIZE 8 /* the size employed by this impl. */ /* TCP Fast Open Cookie as stored in memory */ struct tcp_fastopen_cookie { @@ -312,9 +314,14 @@ struct tcp_request_sock { /* Only used by TCP MD5 Signature so far. */ const struct tcp_request_sock_ops *af_specific; #endif + struct sock *listener; /* needed for TFO */ u32 rcv_isn; u32 snt_isn; u32 snt_synack; /* synack sent time */ + u32 rcv_nxt; /* the ack # by SYNACK. For + * FastOpen it's the seq# + * after data-in-SYN. + */ }; static inline struct tcp_request_sock *tcp_rsk(const struct request_sock *req) @@ -505,14 +512,18 @@ struct tcp_sock { struct tcp_md5sig_info __rcu *md5sig_info; #endif -/* TCP fastopen related information */ - struct tcp_fastopen_request *fastopen_req; - /* When the cookie options are generated and exchanged, then this * object holds a reference to them (cookie_values->kref). Also * contains related tcp_cookie_transactions fields. */ struct tcp_cookie_values *cookie_values; + +/* TCP fastopen related information */ + struct tcp_fastopen_request *fastopen_req; + /* fastopen_rsk points to request_sock that resulted in this big + * socket. Used to retransmit SYNACKs etc. + */ + struct request_sock *fastopen_rsk; }; enum tsq_flags { @@ -552,6 +563,34 @@ static inline struct tcp_timewait_sock *tcp_twsk(const struct sock *sk) return (struct tcp_timewait_sock *)sk; } +static inline bool tcp_passive_fastopen(const struct sock *sk) +{ + return (sk->sk_state == TCP_SYN_RECV && + tcp_sk(sk)->fastopen_rsk != NULL); +} + +static inline bool fastopen_cookie_present(struct tcp_fastopen_cookie *foc) +{ + return foc->len != -1; +} + +static inline int fastopen_init_queue(struct sock *sk, int backlog) +{ + struct request_sock_queue *queue = + &inet_csk(sk)->icsk_accept_queue; + + if (queue->fastopenq == NULL) { + queue->fastopenq = kzalloc( + sizeof(struct fastopen_queue), + sk->sk_allocation); + if (queue->fastopenq == NULL) + return -ENOMEM; + spin_lock_init(&queue->fastopenq->lock); + } + queue->fastopenq->max_qlen = backlog; + return 0; +} + #endif /* __KERNEL__ */ #endif /* _LINUX_TCP_H */ diff --git a/include/net/request_sock.h b/include/net/request_sock.h index 4c0766e201e3..c3cdd6c9f448 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -106,6 +106,34 @@ struct listen_sock { struct request_sock *syn_table[0]; }; +/* + * For a TCP Fast Open listener - + * lock - protects the access to all the reqsk, which is co-owned by + * the listener and the child socket. + * qlen - pending TFO requests (still in TCP_SYN_RECV). + * max_qlen - max TFO reqs allowed before TFO is disabled. + * + * XXX (TFO) - ideally these fields can be made as part of "listen_sock" + * structure above. But there is some implementation difficulty due to + * listen_sock being part of request_sock_queue hence will be freed when + * a listener is stopped. But TFO related fields may continue to be + * accessed even after a listener is closed, until its sk_refcnt drops + * to 0 implying no more outstanding TFO reqs. One solution is to keep + * listen_opt around until sk_refcnt drops to 0. But there is some other + * complexity that needs to be resolved. E.g., a listener can be disabled + * temporarily through shutdown()->tcp_disconnect(), and re-enabled later. + */ +struct fastopen_queue { + struct request_sock *rskq_rst_head; /* Keep track of past TFO */ + struct request_sock *rskq_rst_tail; /* requests that caused RST. + * This is part of the defense + * against spoofing attack. + */ + spinlock_t lock; + int qlen; /* # of pending (TCP_SYN_RECV) reqs */ + int max_qlen; /* != 0 iff TFO is currently enabled */ +}; + /** struct request_sock_queue - queue of request_socks * * @rskq_accept_head - FIFO head of established children @@ -129,6 +157,12 @@ struct request_sock_queue { u8 rskq_defer_accept; /* 3 bytes hole, try to pack */ struct listen_sock *listen_opt; + struct fastopen_queue *fastopenq; /* This is non-NULL iff TFO has been + * enabled on this listener. Check + * max_qlen != 0 in fastopen_queue + * to determine if TFO is enabled + * right at this moment. + */ }; extern int reqsk_queue_alloc(struct request_sock_queue *queue, @@ -136,6 +170,8 @@ extern int reqsk_queue_alloc(struct request_sock_queue *queue, extern void __reqsk_queue_destroy(struct request_sock_queue *queue); extern void reqsk_queue_destroy(struct request_sock_queue *queue); +extern void reqsk_fastopen_remove(struct sock *sk, + struct request_sock *req, bool reset); static inline struct request_sock * reqsk_queue_yank_acceptq(struct request_sock_queue *queue) diff --git a/include/net/tcp.h b/include/net/tcp.h index 0fca06f16463..9f8821e3293a 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -224,8 +224,24 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo); /* Bit Flags for sysctl_tcp_fastopen */ #define TFO_CLIENT_ENABLE 1 +#define TFO_SERVER_ENABLE 2 #define TFO_CLIENT_NO_COOKIE 4 /* Data in SYN w/o cookie option */ +/* Process SYN data but skip cookie validation */ +#define TFO_SERVER_COOKIE_NOT_CHKED 0x100 +/* Accept SYN data w/o any cookie option */ +#define TFO_SERVER_COOKIE_NOT_REQD 0x200 + +/* Force enable TFO on all listeners, i.e., not requiring the + * TCP_FASTOPEN socket option. SOCKOPT1/2 determine how to set max_qlen. + */ +#define TFO_SERVER_WO_SOCKOPT1 0x400 +#define TFO_SERVER_WO_SOCKOPT2 0x800 +/* Always create TFO child sockets on a TFO listener even when + * cookie/data not present. (For testing purpose!) + */ +#define TFO_SERVER_ALWAYS 0x1000 + extern struct inet_timewait_death_row tcp_death_row; /* sysctl variables for tcp */ @@ -421,12 +437,6 @@ extern void tcp_metrics_init(void); extern bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst, bool paws_check); extern bool tcp_remember_stamp(struct sock *sk); extern bool tcp_tw_remember_stamp(struct inet_timewait_sock *tw); -extern void tcp_fastopen_cache_get(struct sock *sk, u16 *mss, - struct tcp_fastopen_cookie *cookie, - int *syn_loss, unsigned long *last_syn_loss); -extern void tcp_fastopen_cache_set(struct sock *sk, u16 mss, - struct tcp_fastopen_cookie *cookie, - bool syn_lost); extern void tcp_fetch_timewait_stamp(struct sock *sk, struct dst_entry *dst); extern void tcp_disable_fack(struct tcp_sock *tp); extern void tcp_close(struct sock *sk, long timeout); @@ -537,6 +547,7 @@ extern void tcp_send_delayed_ack(struct sock *sk); extern void tcp_cwnd_application_limited(struct sock *sk); extern void tcp_resume_early_retransmit(struct sock *sk); extern void tcp_rearm_rto(struct sock *sk); +extern void tcp_reset(struct sock *sk); /* tcp_timer.c */ extern void tcp_init_xmit_timers(struct sock *); @@ -586,6 +597,7 @@ 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) { @@ -1104,6 +1116,7 @@ static inline void tcp_openreq_init(struct request_sock *req, req->rcv_wnd = 0; /* So that tcp_send_synack() knows! */ req->cookie_ts = 0; tcp_rsk(req)->rcv_isn = TCP_SKB_CB(skb)->seq; + tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->seq + 1; req->mss = rx_opt->mss_clamp; req->ts_recent = rx_opt->saw_tstamp ? rx_opt->rcv_tsval : 0; ireq->tstamp_ok = rx_opt->tstamp_ok; @@ -1308,15 +1321,34 @@ extern int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *, const struct sk_buff extern int tcp_md5_hash_key(struct tcp_md5sig_pool *hp, const struct tcp_md5sig_key *key); +/* From tcp_fastopen.c */ +extern void tcp_fastopen_cache_get(struct sock *sk, u16 *mss, + struct tcp_fastopen_cookie *cookie, + int *syn_loss, unsigned long *last_syn_loss); +extern void tcp_fastopen_cache_set(struct sock *sk, u16 mss, + struct tcp_fastopen_cookie *cookie, + bool syn_lost); struct tcp_fastopen_request { /* Fast Open cookie. Size 0 means a cookie request */ struct tcp_fastopen_cookie cookie; struct msghdr *data; /* data in MSG_FASTOPEN */ u16 copied; /* queued in tcp_connect() */ }; - 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); + +#define TCP_FASTOPEN_KEY_LENGTH 16 + +/* Fastopen key context */ +struct tcp_fastopen_context { + struct crypto_cipher __rcu *tfm; + __u8 key[TCP_FASTOPEN_KEY_LENGTH]; + struct rcu_head rcu; +}; + /* write queue abstraction */ static inline void tcp_write_queue_purge(struct sock *sk) { diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 957acd12250b..8de53e1ddd54 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -263,6 +263,10 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("TCPChallengeACK", LINUX_MIB_TCPCHALLENGEACK), SNMP_MIB_ITEM("TCPSYNChallenge", LINUX_MIB_TCPSYNCHALLENGE), SNMP_MIB_ITEM("TCPFastOpenActive", LINUX_MIB_TCPFASTOPENACTIVE), + SNMP_MIB_ITEM("TCPFastOpenPassive", LINUX_MIB_TCPFASTOPENPASSIVE), + SNMP_MIB_ITEM("TCPFastOpenPassiveFail", LINUX_MIB_TCPFASTOPENPASSIVEFAIL), + SNMP_MIB_ITEM("TCPFastOpenListenOverflow", LINUX_MIB_TCPFASTOPENLISTENOVERFLOW), + SNMP_MIB_ITEM("TCPFastOpenCookieReqd", LINUX_MIB_TCPFASTOPENCOOKIEREQD), SNMP_MIB_SENTINEL }; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 3e78c79b5586..9205e492dc9d 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -232,6 +232,45 @@ static int ipv4_tcp_mem(ctl_table *ctl, int write, return 0; } +int proc_tcp_fastopen_key(ctl_table *ctl, int write, void __user *buffer, + size_t *lenp, loff_t *ppos) +{ + ctl_table tbl = { .maxlen = (TCP_FASTOPEN_KEY_LENGTH * 2 + 10) }; + struct tcp_fastopen_context *ctxt; + int ret; + u32 user_key[4]; /* 16 bytes, matching TCP_FASTOPEN_KEY_LENGTH */ + + tbl.data = kmalloc(tbl.maxlen, GFP_KERNEL); + if (!tbl.data) + return -ENOMEM; + + rcu_read_lock(); + ctxt = rcu_dereference(tcp_fastopen_ctx); + if (ctxt) + memcpy(user_key, ctxt->key, TCP_FASTOPEN_KEY_LENGTH); + rcu_read_unlock(); + + snprintf(tbl.data, tbl.maxlen, "%08x-%08x-%08x-%08x", + user_key[0], user_key[1], user_key[2], user_key[3]); + ret = proc_dostring(&tbl, write, buffer, lenp, ppos); + + if (write && ret == 0) { + if (sscanf(tbl.data, "%x-%x-%x-%x", user_key, user_key + 1, + user_key + 2, user_key + 3) != 4) { + ret = -EINVAL; + goto bad_key; + } + tcp_fastopen_reset_cipher(user_key, TCP_FASTOPEN_KEY_LENGTH); + } + +bad_key: + pr_debug("proc FO key set 0x%x-%x-%x-%x <- 0x%s: %u\n", + user_key[0], user_key[1], user_key[2], user_key[3], + (char *)tbl.data, ret); + kfree(tbl.data); + return ret; +} + static struct ctl_table ipv4_table[] = { { .procname = "tcp_timestamps", @@ -385,6 +424,12 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "tcp_fastopen_key", + .mode = 0600, + .maxlen = ((TCP_FASTOPEN_KEY_LENGTH * 2) + 10), + .proc_handler = proc_tcp_fastopen_key, + }, { .procname = "tcp_tw_recycle", .data = &tcp_death_row.sysctl_tw_recycle, diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index a7f729c409d7..8f7ef0ad80e5 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -1,10 +1,91 @@ +#include #include #include +#include +#include +#include +#include +#include +#include -int sysctl_tcp_fastopen; +int sysctl_tcp_fastopen __read_mostly; + +struct tcp_fastopen_context __rcu *tcp_fastopen_ctx; + +static DEFINE_SPINLOCK(tcp_fastopen_ctx_lock); + +static void tcp_fastopen_ctx_free(struct rcu_head *head) +{ + struct tcp_fastopen_context *ctx = + container_of(head, struct tcp_fastopen_context, rcu); + crypto_free_cipher(ctx->tfm); + kfree(ctx); +} + +int tcp_fastopen_reset_cipher(void *key, unsigned int len) +{ + int err; + struct tcp_fastopen_context *ctx, *octx; + + ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + ctx->tfm = crypto_alloc_cipher("aes", 0, 0); + + if (IS_ERR(ctx->tfm)) { + err = PTR_ERR(ctx->tfm); +error: kfree(ctx); + pr_err("TCP: TFO aes cipher alloc error: %d\n", err); + return err; + } + err = crypto_cipher_setkey(ctx->tfm, key, len); + if (err) { + pr_err("TCP: TFO cipher key error: %d\n", err); + crypto_free_cipher(ctx->tfm); + goto error; + } + memcpy(ctx->key, key, len); + + spin_lock(&tcp_fastopen_ctx_lock); + + octx = rcu_dereference_protected(tcp_fastopen_ctx, + lockdep_is_held(&tcp_fastopen_ctx_lock)); + rcu_assign_pointer(tcp_fastopen_ctx, ctx); + spin_unlock(&tcp_fastopen_ctx_lock); + + if (octx) + call_rcu(&octx->rcu, tcp_fastopen_ctx_free); + return err; +} + +/* Computes the fastopen cookie for the peer. + * The peer address 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) +{ + __be32 peer_addr[4] = { addr, 0, 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); + foc->len = TCP_FASTOPEN_COOKIE_SIZE; + } + rcu_read_unlock(); +} static int __init tcp_fastopen_init(void) { + __u8 key[TCP_FASTOPEN_KEY_LENGTH]; + + get_random_bytes(key, sizeof(key)); + tcp_fastopen_reset_cipher(key, sizeof(key)); return 0; } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index ce4ffe9ed556..d47d5fe8f3f0 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -378,7 +378,7 @@ static void tcp_fixup_rcvbuf(struct sock *sk) /* 4. Try to fixup all. It is made immediately after connection enters * established state. */ -static void tcp_init_buffer_space(struct sock *sk) +void tcp_init_buffer_space(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); int maxwin; @@ -4038,7 +4038,7 @@ static inline bool tcp_sequence(const struct tcp_sock *tp, u32 seq, u32 end_seq) } /* When we get a reset we do this. */ -static void tcp_reset(struct sock *sk) +void tcp_reset(struct sock *sk) { /* We want the right error as BSD sees it (and indeed as we do). */ switch (sk->sk_state) { -- cgit v1.2.3 From 8336886f786fdacbc19b719c1f7ea91eb70706d4 Mon Sep 17 00:00:00 2001 From: Jerry Chu Date: Fri, 31 Aug 2012 12:29:12 +0000 Subject: tcp: TCP Fast Open Server - support TFO listeners This patch builds on top of the previous patch to add the support for TFO listeners. This includes - 1. allocating, properly initializing, and managing the per listener fastopen_queue structure when TFO is enabled 2. changes to the inet_csk_accept code to support TFO. E.g., the request_sock can no longer be freed upon accept(), not until 3WHS finishes 3. allowing a TCP_SYN_RECV socket to properly poll() and sendmsg() if it's a TFO socket 4. properly closing a TFO listener, and a TFO socket before 3WHS finishes 5. supporting TCP_FASTOPEN socket option 6. modifying tcp_check_req() to use to check a TFO socket as well as request_sock 7. supporting TCP's TFO cookie option 8. adding a new SYN-ACK retransmit handler to use the timer directly off the TFO socket rather than the listener socket. Note that TFO server side will not retransmit anything other than SYN-ACK until the 3WHS is completed. The patch also contains an important function "reqsk_fastopen_remove()" to manage the somewhat complex relation between a listener, its request_sock, and the corresponding child socket. See the comment above the function for the detail. Signed-off-by: H.K. Jerry Chu Cc: Yuchung Cheng Cc: Neal Cardwell Cc: Eric Dumazet Cc: Tom Herbert Signed-off-by: David S. Miller --- include/net/request_sock.h | 13 ------ include/net/tcp.h | 6 ++- net/core/request_sock.c | 95 +++++++++++++++++++++++++++++++++++++++++ net/ipv4/af_inet.c | 28 +++++++++++- net/ipv4/inet_connection_sock.c | 57 ++++++++++++++++++++++--- net/ipv4/syncookies.c | 1 + net/ipv4/tcp.c | 49 ++++++++++++++++++--- net/ipv4/tcp_ipv4.c | 4 +- net/ipv4/tcp_minisocks.c | 61 +++++++++++++++++++++----- net/ipv4/tcp_output.c | 21 ++++++--- net/ipv4/tcp_timer.c | 39 ++++++++++++++++- net/ipv6/syncookies.c | 1 + net/ipv6/tcp_ipv6.c | 5 ++- 13 files changed, 330 insertions(+), 50 deletions(-) (limited to 'include') diff --git a/include/net/request_sock.h b/include/net/request_sock.h index c3cdd6c9f448..b01d8dd9ee7c 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -226,19 +226,6 @@ static inline struct request_sock *reqsk_queue_remove(struct request_sock_queue return req; } -static inline struct sock *reqsk_queue_get_child(struct request_sock_queue *queue, - struct sock *parent) -{ - struct request_sock *req = reqsk_queue_remove(queue); - struct sock *child = req->sk; - - WARN_ON(child == NULL); - - sk_acceptq_removed(parent); - __reqsk_free(req); - return child; -} - static inline int reqsk_queue_removed(struct request_sock_queue *queue, struct request_sock *req) { diff --git a/include/net/tcp.h b/include/net/tcp.h index 9f8821e3293a..1421b02a7905 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -424,7 +424,8 @@ extern enum tcp_tw_status tcp_timewait_state_process(struct inet_timewait_sock * const struct tcphdr *th); extern struct sock * tcp_check_req(struct sock *sk,struct sk_buff *skb, struct request_sock *req, - struct request_sock **prev); + struct request_sock **prev, + bool fastopen); extern int tcp_child_process(struct sock *parent, struct sock *child, struct sk_buff *skb); extern bool tcp_use_frto(struct sock *sk); @@ -478,7 +479,8 @@ extern int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, extern int tcp_connect(struct sock *sk); extern struct sk_buff * tcp_make_synack(struct sock *sk, struct dst_entry *dst, struct request_sock *req, - struct request_values *rvp); + struct request_values *rvp, + struct tcp_fastopen_cookie *foc); extern int tcp_disconnect(struct sock *sk, int flags); void tcp_connect_init(struct sock *sk); diff --git a/net/core/request_sock.c b/net/core/request_sock.c index 9b570a6a33c5..c31d9e8668c3 100644 --- a/net/core/request_sock.c +++ b/net/core/request_sock.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -130,3 +131,97 @@ void reqsk_queue_destroy(struct request_sock_queue *queue) kfree(lopt); } +/* + * This function is called to set a Fast Open socket's "fastopen_rsk" field + * to NULL when a TFO socket no longer needs to access the request_sock. + * This happens only after 3WHS has been either completed or aborted (e.g., + * RST is received). + * + * Before TFO, a child socket is created only after 3WHS is completed, + * hence it never needs to access the request_sock. things get a lot more + * complex with TFO. A child socket, accepted or not, has to access its + * request_sock for 3WHS processing, e.g., to retransmit SYN-ACK pkts, + * until 3WHS is either completed or aborted. Afterwards the req will stay + * until either the child socket is accepted, or in the rare case when the + * listener is closed before the child is accepted. + * + * In short, a request socket is only freed after BOTH 3WHS has completed + * (or aborted) and the child socket has been accepted (or listener closed). + * When a child socket is accepted, its corresponding req->sk is set to + * NULL since it's no longer needed. More importantly, "req->sk == NULL" + * will be used by the code below to determine if a child socket has been + * accepted or not, and the check is protected by the fastopenq->lock + * described below. + * + * Note that fastopen_rsk is only accessed from the child socket's context + * with its socket lock held. But a request_sock (req) can be accessed by + * both its child socket through fastopen_rsk, and a listener socket through + * icsk_accept_queue.rskq_accept_head. To protect the access a simple spin + * lock per listener "icsk->icsk_accept_queue.fastopenq->lock" is created. + * only in the rare case when both the listener and the child locks are held, + * e.g., in inet_csk_listen_stop() do we not need to acquire the lock. + * The lock also protects other fields such as fastopenq->qlen, which is + * decremented by this function when fastopen_rsk is no longer needed. + * + * Note that another solution was to simply use the existing socket lock + * from the listener. But first socket lock is difficult to use. It is not + * a simple spin lock - one must consider sock_owned_by_user() and arrange + * to use sk_add_backlog() stuff. But what really makes it infeasible is the + * locking hierarchy violation. E.g., inet_csk_listen_stop() may try to + * acquire a child's lock while holding listener's socket lock. A corner + * case might also exist in tcp_v4_hnd_req() that will trigger this locking + * order. + * + * When a TFO req is created, it needs to sock_hold its listener to prevent + * the latter data structure from going away. + * + * This function also sets "treq->listener" to NULL and unreference listener + * socket. treq->listener is used by the listener so it is protected by the + * fastopenq->lock in this function. + */ +void reqsk_fastopen_remove(struct sock *sk, struct request_sock *req, + bool reset) +{ + struct sock *lsk = tcp_rsk(req)->listener; + struct fastopen_queue *fastopenq = + inet_csk(lsk)->icsk_accept_queue.fastopenq; + + BUG_ON(!spin_is_locked(&sk->sk_lock.slock) && !sock_owned_by_user(sk)); + + tcp_sk(sk)->fastopen_rsk = NULL; + spin_lock_bh(&fastopenq->lock); + fastopenq->qlen--; + tcp_rsk(req)->listener = NULL; + if (req->sk) /* the child socket hasn't been accepted yet */ + goto out; + + if (!reset || lsk->sk_state != TCP_LISTEN) { + /* If the listener has been closed don't bother with the + * special RST handling below. + */ + spin_unlock_bh(&fastopenq->lock); + sock_put(lsk); + reqsk_free(req); + return; + } + /* Wait for 60secs before removing a req that has triggered RST. + * This is a simple defense against TFO spoofing attack - by + * counting the req against fastopen.max_qlen, and disabling + * TFO when the qlen exceeds max_qlen. + * + * For more details see CoNext'11 "TCP Fast Open" paper. + */ + req->expires = jiffies + 60*HZ; + if (fastopenq->rskq_rst_head == NULL) + fastopenq->rskq_rst_head = req; + else + fastopenq->rskq_rst_tail->dl_next = req; + + req->dl_next = NULL; + fastopenq->rskq_rst_tail = req; + fastopenq->qlen++; +out: + spin_unlock_bh(&fastopenq->lock); + sock_put(lsk); + return; +} diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 6681ccf5c3ee..4f70ef0b946d 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -149,6 +149,11 @@ void inet_sock_destruct(struct sock *sk) pr_err("Attempt to release alive inet socket %p\n", sk); return; } + if (sk->sk_type == SOCK_STREAM) { + struct fastopen_queue *fastopenq = + inet_csk(sk)->icsk_accept_queue.fastopenq; + kfree(fastopenq); + } WARN_ON(atomic_read(&sk->sk_rmem_alloc)); WARN_ON(atomic_read(&sk->sk_wmem_alloc)); @@ -212,6 +217,26 @@ int inet_listen(struct socket *sock, int backlog) * we can only allow the backlog to be adjusted. */ if (old_state != TCP_LISTEN) { + /* Check special setups for testing purpose to enable TFO w/o + * requiring TCP_FASTOPEN sockopt. + * Note that only TCP sockets (SOCK_STREAM) will reach here. + * Also fastopenq may already been allocated because this + * socket was in TCP_LISTEN state previously but was + * shutdown() (rather than close()). + */ + if ((sysctl_tcp_fastopen & TFO_SERVER_ENABLE) != 0 && + inet_csk(sk)->icsk_accept_queue.fastopenq == NULL) { + if ((sysctl_tcp_fastopen & TFO_SERVER_WO_SOCKOPT1) != 0) + err = fastopen_init_queue(sk, backlog); + else if ((sysctl_tcp_fastopen & + TFO_SERVER_WO_SOCKOPT2) != 0) + err = fastopen_init_queue(sk, + ((uint)sysctl_tcp_fastopen) >> 16); + else + err = 0; + if (err) + goto out; + } err = inet_csk_listen_start(sk, backlog); if (err) goto out; @@ -701,7 +726,8 @@ int inet_accept(struct socket *sock, struct socket *newsock, int flags) sock_rps_record_flow(sk2); WARN_ON(!((1 << sk2->sk_state) & - (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT | TCPF_CLOSE))); + (TCPF_ESTABLISHED | TCPF_SYN_RECV | + TCPF_CLOSE_WAIT | TCPF_CLOSE))); sock_graft(sk2, newsock); diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 7f75f21d7b83..8464b79c493f 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -283,7 +283,9 @@ static int inet_csk_wait_for_connect(struct sock *sk, long timeo) struct sock *inet_csk_accept(struct sock *sk, int flags, int *err) { struct inet_connection_sock *icsk = inet_csk(sk); + struct request_sock_queue *queue = &icsk->icsk_accept_queue; struct sock *newsk; + struct request_sock *req; int error; lock_sock(sk); @@ -296,7 +298,7 @@ struct sock *inet_csk_accept(struct sock *sk, int flags, int *err) goto out_err; /* Find already established connection */ - if (reqsk_queue_empty(&icsk->icsk_accept_queue)) { + if (reqsk_queue_empty(queue)) { long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); /* If this is a non blocking socket don't sleep */ @@ -308,14 +310,32 @@ struct sock *inet_csk_accept(struct sock *sk, int flags, int *err) if (error) goto out_err; } - - newsk = reqsk_queue_get_child(&icsk->icsk_accept_queue, sk); - WARN_ON(newsk->sk_state == TCP_SYN_RECV); + req = reqsk_queue_remove(queue); + newsk = req->sk; + + sk_acceptq_removed(sk); + if (sk->sk_type == SOCK_STREAM && queue->fastopenq != NULL) { + spin_lock_bh(&queue->fastopenq->lock); + if (tcp_rsk(req)->listener) { + /* We are still waiting for the final ACK from 3WHS + * so can't free req now. Instead, we set req->sk to + * NULL to signify that the child socket is taken + * so reqsk_fastopen_remove() will free the req + * when 3WHS finishes (or is aborted). + */ + req->sk = NULL; + req = NULL; + } + spin_unlock_bh(&queue->fastopenq->lock); + } out: release_sock(sk); + if (req) + __reqsk_free(req); return newsk; out_err: newsk = NULL; + req = NULL; *err = error; goto out; } @@ -720,13 +740,14 @@ EXPORT_SYMBOL_GPL(inet_csk_listen_start); void inet_csk_listen_stop(struct sock *sk) { struct inet_connection_sock *icsk = inet_csk(sk); + struct request_sock_queue *queue = &icsk->icsk_accept_queue; struct request_sock *acc_req; struct request_sock *req; inet_csk_delete_keepalive_timer(sk); /* make all the listen_opt local to us */ - acc_req = reqsk_queue_yank_acceptq(&icsk->icsk_accept_queue); + acc_req = reqsk_queue_yank_acceptq(queue); /* Following specs, it would be better either to send FIN * (and enter FIN-WAIT-1, it is normal close) @@ -736,7 +757,7 @@ void inet_csk_listen_stop(struct sock *sk) * To be honest, we are not able to make either * of the variants now. --ANK */ - reqsk_queue_destroy(&icsk->icsk_accept_queue); + reqsk_queue_destroy(queue); while ((req = acc_req) != NULL) { struct sock *child = req->sk; @@ -754,6 +775,19 @@ void inet_csk_listen_stop(struct sock *sk) percpu_counter_inc(sk->sk_prot->orphan_count); + if (sk->sk_type == SOCK_STREAM && tcp_rsk(req)->listener) { + BUG_ON(tcp_sk(child)->fastopen_rsk != req); + BUG_ON(sk != tcp_rsk(req)->listener); + + /* Paranoid, to prevent race condition if + * an inbound pkt destined for child is + * blocked by sock lock in tcp_v4_rcv(). + * Also to satisfy an assertion in + * tcp_v4_destroy_sock(). + */ + tcp_sk(child)->fastopen_rsk = NULL; + sock_put(sk); + } inet_csk_destroy_sock(child); bh_unlock_sock(child); @@ -763,6 +797,17 @@ void inet_csk_listen_stop(struct sock *sk) sk_acceptq_removed(sk); __reqsk_free(req); } + if (queue->fastopenq != NULL) { + /* Free all the reqs queued in rskq_rst_head. */ + spin_lock_bh(&queue->fastopenq->lock); + acc_req = queue->fastopenq->rskq_rst_head; + queue->fastopenq->rskq_rst_head = NULL; + spin_unlock_bh(&queue->fastopenq->lock); + while ((req = acc_req) != NULL) { + acc_req = req->dl_next; + __reqsk_free(req); + } + } WARN_ON(sk->sk_ack_backlog); } EXPORT_SYMBOL_GPL(inet_csk_listen_stop); diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 650e1528e1e6..ba48e799b031 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -319,6 +319,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, ireq->tstamp_ok = tcp_opt.saw_tstamp; req->ts_recent = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsval : 0; treq->snt_synack = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsecr : 0; + treq->listener = NULL; /* We throwed the options of the initial SYN away, so we hope * the ACK carries the same options again (see RFC1122 4.2.3.8) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 2109ff4a1daf..df83d744e380 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -486,8 +486,9 @@ unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait) if (sk->sk_shutdown & RCV_SHUTDOWN) mask |= POLLIN | POLLRDNORM | POLLRDHUP; - /* Connected? */ - if ((1 << sk->sk_state) & ~(TCPF_SYN_SENT | TCPF_SYN_RECV)) { + /* Connected or passive Fast Open socket? */ + if (sk->sk_state != TCP_SYN_SENT && + (sk->sk_state != TCP_SYN_RECV || tp->fastopen_rsk != NULL)) { int target = sock_rcvlowat(sk, 0, INT_MAX); if (tp->urg_seq == tp->copied_seq && @@ -840,10 +841,15 @@ static ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffse ssize_t copied; long timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT); - /* Wait for a connection to finish. */ - if ((1 << sk->sk_state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) + /* Wait for a connection to finish. One exception is TCP Fast Open + * (passive side) where data is allowed to be sent before a connection + * is fully established. + */ + if (((1 << sk->sk_state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) && + !tcp_passive_fastopen(sk)) { if ((err = sk_stream_wait_connect(sk, &timeo)) != 0) goto out_err; + } clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); @@ -1042,10 +1048,15 @@ int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT); - /* Wait for a connection to finish. */ - if ((1 << sk->sk_state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) + /* Wait for a connection to finish. One exception is TCP Fast Open + * (passive side) where data is allowed to be sent before a connection + * is fully established. + */ + if (((1 << sk->sk_state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) && + !tcp_passive_fastopen(sk)) { if ((err = sk_stream_wait_connect(sk, &timeo)) != 0) goto do_error; + } if (unlikely(tp->repair)) { if (tp->repair_queue == TCP_RECV_QUEUE) { @@ -2144,6 +2155,10 @@ void tcp_close(struct sock *sk, long timeout) * they look as CLOSING or LAST_ACK for Linux) * Probably, I missed some more holelets. * --ANK + * XXX (TFO) - To start off we don't support SYN+ACK+FIN + * in a single packet! (May consider it later but will + * probably need API support or TCP_CORK SYN-ACK until + * data is written and socket is closed.) */ tcp_send_fin(sk); } @@ -2215,8 +2230,16 @@ adjudge_to_death: } } - if (sk->sk_state == TCP_CLOSE) + if (sk->sk_state == TCP_CLOSE) { + struct request_sock *req = tcp_sk(sk)->fastopen_rsk; + /* We could get here with a non-NULL req if the socket is + * aborted (e.g., closed with unread data) before 3WHS + * finishes. + */ + if (req != NULL) + reqsk_fastopen_remove(sk, req, false); inet_csk_destroy_sock(sk); + } /* Otherwise, socket is reprieved until protocol close. */ out: @@ -2688,6 +2711,14 @@ static int do_tcp_setsockopt(struct sock *sk, int level, else icsk->icsk_user_timeout = msecs_to_jiffies(val); break; + + case TCP_FASTOPEN: + if (val >= 0 && ((1 << sk->sk_state) & (TCPF_CLOSE | + TCPF_LISTEN))) + err = fastopen_init_queue(sk, val); + else + err = -EINVAL; + break; default: err = -ENOPROTOOPT; break; @@ -3501,11 +3532,15 @@ EXPORT_SYMBOL(tcp_cookie_generator); void tcp_done(struct sock *sk) { + struct request_sock *req = tcp_sk(sk)->fastopen_rsk; + if (sk->sk_state == TCP_SYN_SENT || sk->sk_state == TCP_SYN_RECV) TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_ATTEMPTFAILS); tcp_set_state(sk, TCP_CLOSE); tcp_clear_xmit_timers(sk); + if (req != NULL) + reqsk_fastopen_remove(sk, req, false); sk->sk_shutdown = SHUTDOWN_MASK; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 36f02f954ac1..bb148dee1edd 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -839,7 +839,7 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, if (!dst && (dst = inet_csk_route_req(sk, &fl4, req)) == NULL) return -1; - skb = tcp_make_synack(sk, dst, req, rvp); + skb = tcp_make_synack(sk, dst, req, rvp, NULL); if (skb) { __tcp_v4_send_check(skb, ireq->loc_addr, ireq->rmt_addr); @@ -1554,7 +1554,7 @@ static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb) struct request_sock *req = inet_csk_search_req(sk, &prev, th->source, iph->saddr, iph->daddr); if (req) - return tcp_check_req(sk, skb, req, prev); + return tcp_check_req(sk, skb, req, prev, false); nsk = inet_lookup_established(sock_net(sk), &tcp_hashinfo, iph->saddr, th->source, iph->daddr, th->dest, inet_iif(skb)); diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 6ff7f10dce9d..e965319d610b 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -507,6 +507,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, newicsk->icsk_ack.last_seg_size = skb->len - newtp->tcp_header_len; newtp->rx_opt.mss_clamp = req->mss; TCP_ECN_openreq_child(newtp, req); + newtp->fastopen_rsk = NULL; TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_PASSIVEOPENS); } @@ -515,13 +516,18 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, EXPORT_SYMBOL(tcp_create_openreq_child); /* - * Process an incoming packet for SYN_RECV sockets represented - * as a request_sock. + * Process an incoming packet for SYN_RECV sockets represented as a + * request_sock. Normally sk is the listener socket but for TFO it + * points to the child socket. + * + * XXX (TFO) - The current impl contains a special check for ack + * validation and inside tcp_v4_reqsk_send_ack(). Can we do better? */ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, struct request_sock *req, - struct request_sock **prev) + struct request_sock **prev, + bool fastopen) { struct tcp_options_received tmp_opt; const u8 *hash_location; @@ -530,6 +536,8 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, __be32 flg = tcp_flag_word(th) & (TCP_FLAG_RST|TCP_FLAG_SYN|TCP_FLAG_ACK); bool paws_reject = false; + BUG_ON(fastopen == (sk->sk_state == TCP_LISTEN)); + tmp_opt.saw_tstamp = 0; if (th->doff > (sizeof(struct tcphdr)>>2)) { tcp_parse_options(skb, &tmp_opt, &hash_location, 0, NULL); @@ -565,6 +573,9 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, * * Enforce "SYN-ACK" according to figure 8, figure 6 * of RFC793, fixed by RFC1122. + * + * Note that even if there is new data in the SYN packet + * they will be thrown away too. */ req->rsk_ops->rtx_syn_ack(sk, req, NULL); return NULL; @@ -622,9 +633,12 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, * sent (the segment carries an unacceptable ACK) ... * a reset is sent." * - * Invalid ACK: reset will be sent by listening socket + * Invalid ACK: reset will be sent by listening socket. + * Note that the ACK validity check for a Fast Open socket is done + * elsewhere and is checked directly against the child socket rather + * than req because user data may have been sent out. */ - if ((flg & TCP_FLAG_ACK) && + if ((flg & TCP_FLAG_ACK) && !fastopen && (TCP_SKB_CB(skb)->ack_seq != tcp_rsk(req)->snt_isn + 1 + tcp_s_data_size(tcp_sk(sk)))) return sk; @@ -637,7 +651,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, /* RFC793: "first check sequence number". */ if (paws_reject || !tcp_in_window(TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq, - tcp_rsk(req)->rcv_isn + 1, tcp_rsk(req)->rcv_isn + 1 + req->rcv_wnd)) { + tcp_rsk(req)->rcv_nxt, tcp_rsk(req)->rcv_nxt + req->rcv_wnd)) { /* Out of window: send ACK and drop. */ if (!(flg & TCP_FLAG_RST)) req->rsk_ops->send_ack(sk, skb, req); @@ -648,7 +662,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, /* In sequence, PAWS is OK. */ - if (tmp_opt.saw_tstamp && !after(TCP_SKB_CB(skb)->seq, tcp_rsk(req)->rcv_isn + 1)) + if (tmp_opt.saw_tstamp && !after(TCP_SKB_CB(skb)->seq, tcp_rsk(req)->rcv_nxt)) req->ts_recent = tmp_opt.rcv_tsval; if (TCP_SKB_CB(skb)->seq == tcp_rsk(req)->rcv_isn) { @@ -667,10 +681,19 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, /* ACK sequence verified above, just make sure ACK is * set. If ACK not set, just silently drop the packet. + * + * XXX (TFO) - if we ever allow "data after SYN", the + * following check needs to be removed. */ if (!(flg & TCP_FLAG_ACK)) return NULL; + /* For Fast Open no more processing is needed (sk is the + * child socket). + */ + if (fastopen) + return sk; + /* While TCP_DEFER_ACCEPT is active, drop bare ACK. */ if (req->retrans < inet_csk(sk)->icsk_accept_queue.rskq_defer_accept && TCP_SKB_CB(skb)->end_seq == tcp_rsk(req)->rcv_isn + 1) { @@ -706,11 +729,21 @@ listen_overflow: } embryonic_reset: - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_EMBRYONICRSTS); - if (!(flg & TCP_FLAG_RST)) + if (!(flg & TCP_FLAG_RST)) { + /* Received a bad SYN pkt - for TFO We try not to reset + * the local connection unless it's really necessary to + * avoid becoming vulnerable to outside attack aiming at + * resetting legit local connections. + */ req->rsk_ops->send_reset(sk, skb); - - inet_csk_reqsk_queue_drop(sk, req, prev); + } else if (fastopen) { /* received a valid RST pkt */ + reqsk_fastopen_remove(sk, req, true); + tcp_reset(sk); + } + if (!fastopen) { + inet_csk_reqsk_queue_drop(sk, req, prev); + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_EMBRYONICRSTS); + } return NULL; } EXPORT_SYMBOL(tcp_check_req); @@ -719,6 +752,12 @@ EXPORT_SYMBOL(tcp_check_req); * Queue segment on the new socket if the new socket is active, * otherwise we just shortcircuit this and continue with * the new socket. + * + * For the vast majority of cases child->sk_state will be TCP_SYN_RECV + * when entering. But other states are possible due to a race condition + * where after __inet_lookup_established() fails but before the listener + * locked is obtained, other packets cause the same connection to + * be created. */ int tcp_child_process(struct sock *parent, struct sock *child, diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index d04632673a9e..9383b51f3efc 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -702,7 +702,8 @@ static unsigned int tcp_synack_options(struct sock *sk, unsigned int mss, struct sk_buff *skb, struct tcp_out_options *opts, struct tcp_md5sig_key **md5, - struct tcp_extend_values *xvp) + struct tcp_extend_values *xvp, + struct tcp_fastopen_cookie *foc) { struct inet_request_sock *ireq = inet_rsk(req); unsigned int remaining = MAX_TCP_OPTION_SPACE; @@ -747,7 +748,15 @@ static unsigned int tcp_synack_options(struct sock *sk, if (unlikely(!ireq->tstamp_ok)) remaining -= TCPOLEN_SACKPERM_ALIGNED; } - + if (foc != NULL) { + u32 need = TCPOLEN_EXP_FASTOPEN_BASE + foc->len; + need = (need + 3) & ~3U; /* Align to 32 bits */ + if (remaining >= need) { + opts->options |= OPTION_FAST_OPEN_COOKIE; + opts->fastopen_cookie = foc; + remaining -= need; + } + } /* Similar rationale to tcp_syn_options() applies here, too. * If the options fit, the same options should fit now! */ @@ -2658,7 +2667,8 @@ int tcp_send_synack(struct sock *sk) */ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, struct request_sock *req, - struct request_values *rvp) + struct request_values *rvp, + struct tcp_fastopen_cookie *foc) { struct tcp_out_options opts; struct tcp_extend_values *xvp = tcp_xv(rvp); @@ -2718,7 +2728,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, #endif TCP_SKB_CB(skb)->when = tcp_time_stamp; tcp_header_size = tcp_synack_options(sk, req, mss, - skb, &opts, &md5, xvp) + skb, &opts, &md5, xvp, foc) + sizeof(*th); skb_push(skb, tcp_header_size); @@ -2772,7 +2782,8 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, } th->seq = htonl(TCP_SKB_CB(skb)->seq); - th->ack_seq = htonl(tcp_rsk(req)->rcv_isn + 1); + /* XXX data is queued and acked as is. No buffer/window check */ + th->ack_seq = htonl(tcp_rsk(req)->rcv_nxt); /* RFC1323: The window in SYN & SYN/ACK segments is never scaled. */ th->window = htons(min(req->rcv_wnd, 65535U)); diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index b774a03bd1dc..fc04711e80c8 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -304,6 +304,35 @@ static void tcp_probe_timer(struct sock *sk) } } +/* + * Timer for Fast Open socket to retransmit SYNACK. Note that the + * sk here is the child socket, not the parent (listener) socket. + */ +static void tcp_fastopen_synack_timer(struct sock *sk) +{ + struct inet_connection_sock *icsk = inet_csk(sk); + int max_retries = icsk->icsk_syn_retries ? : + sysctl_tcp_synack_retries + 1; /* add one more retry for fastopen */ + struct request_sock *req; + + req = tcp_sk(sk)->fastopen_rsk; + req->rsk_ops->syn_ack_timeout(sk, req); + + if (req->retrans >= max_retries) { + tcp_write_err(sk); + return; + } + /* XXX (TFO) - Unlike regular SYN-ACK retransmit, we ignore error + * returned from rtx_syn_ack() to make it more persistent like + * regular retransmit because if the child socket has been accepted + * it's not good to give up too easily. + */ + req->rsk_ops->rtx_syn_ack(sk, req, NULL); + req->retrans++; + inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, + TCP_TIMEOUT_INIT << req->retrans, TCP_RTO_MAX); +} + /* * The TCP retransmit timer. */ @@ -317,7 +346,15 @@ void tcp_retransmit_timer(struct sock *sk) tcp_resume_early_retransmit(sk); return; } - + if (tp->fastopen_rsk) { + BUG_ON(sk->sk_state != TCP_SYN_RECV && + sk->sk_state != TCP_FIN_WAIT1); + tcp_fastopen_synack_timer(sk); + /* Before we receive ACK to our SYN-ACK don't retransmit + * anything else (e.g., data or FIN segments). + */ + return; + } if (!tp->packets_out) goto out; diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index bb46061c813a..182ab9a85d6c 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -190,6 +190,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) ireq = inet_rsk(req); ireq6 = inet6_rsk(req); treq = tcp_rsk(req); + treq->listener = NULL; if (security_inet_conn_request(sk, skb, req)) goto out_free; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index f99b81d53cca..09078b9bc6f6 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -475,7 +475,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst, if (!dst && (dst = inet6_csk_route_req(sk, fl6, req)) == NULL) goto done; - skb = tcp_make_synack(sk, dst, req, rvp); + skb = tcp_make_synack(sk, dst, req, rvp, NULL); if (skb) { __tcp_v6_send_check(skb, &treq->loc_addr, &treq->rmt_addr); @@ -987,7 +987,7 @@ static struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb) &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, inet6_iif(skb)); if (req) - return tcp_check_req(sk, skb, req, prev); + return tcp_check_req(sk, skb, req, prev, false); nsk = __inet6_lookup_established(sock_net(sk), &tcp_hashinfo, &ipv6_hdr(skb)->saddr, th->source, @@ -1179,6 +1179,7 @@ have_isn: want_cookie) goto drop_and_free; + tcp_rsk(req)->listener = NULL; inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT); return 0; -- cgit v1.2.3 From 5a4432b9544bc84168267f8e51798f7fe9b1af24 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Mon, 13 Aug 2012 21:00:25 +0900 Subject: mfd: printk/comment fixes Correct spelling typo in drivers/mfd Signed-off-by: Masanari Iida Signed-off-by: Jiri Kosina --- drivers/mfd/ab8500-gpadc.c | 2 +- drivers/mfd/rc5t583.c | 2 +- drivers/mfd/rdc321x-southbridge.c | 2 +- drivers/mfd/tps65911-comparator.c | 2 +- include/linux/mfd/max8998.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c index b86fd8e1ec3f..f4a43de1f40c 100644 --- a/drivers/mfd/ab8500-gpadc.c +++ b/drivers/mfd/ab8500-gpadc.c @@ -342,7 +342,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel) /* * Delay might be needed for ABB8500 cut 3.0, if not, remove - * when hardware will be availible + * when hardware will be available */ msleep(1); break; diff --git a/drivers/mfd/rc5t583.c b/drivers/mfd/rc5t583.c index cdc1df7fa0e9..da151018e1a6 100644 --- a/drivers/mfd/rc5t583.c +++ b/drivers/mfd/rc5t583.c @@ -281,7 +281,7 @@ static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c, if (i2c->irq) { ret = rc5t583_irq_init(rc5t583, i2c->irq, pdata->irq_base); - /* Still continue with waring if irq init fails */ + /* Still continue with warning, if irq init fails */ if (ret) dev_warn(&i2c->dev, "IRQ init failed: %d\n", ret); else diff --git a/drivers/mfd/rdc321x-southbridge.c b/drivers/mfd/rdc321x-southbridge.c index 685d61e431ad..7b46d6617459 100644 --- a/drivers/mfd/rdc321x-southbridge.c +++ b/drivers/mfd/rdc321x-southbridge.c @@ -1,5 +1,5 @@ /* - * RDC321x MFD southbrige driver + * RDC321x MFD southbridge driver * * Copyright (C) 2007-2010 Florian Fainelli * Copyright (C) 2010 Bernhard Loos diff --git a/drivers/mfd/tps65911-comparator.c b/drivers/mfd/tps65911-comparator.c index 5a62e6bf89ae..0b6e361432c4 100644 --- a/drivers/mfd/tps65911-comparator.c +++ b/drivers/mfd/tps65911-comparator.c @@ -136,7 +136,7 @@ static __devinit int tps65911_comparator_probe(struct platform_device *pdev) ret = comp_threshold_set(tps65910, COMP2, pdata->vmbch2_threshold); if (ret < 0) { - dev_err(&pdev->dev, "cannot set COMP2 theshold\n"); + dev_err(&pdev->dev, "cannot set COMP2 threshold\n"); return ret; } diff --git a/include/linux/mfd/max8998.h b/include/linux/mfd/max8998.h index f4f0dfa4698a..6823548d0c0a 100644 --- a/include/linux/mfd/max8998.h +++ b/include/linux/mfd/max8998.h @@ -67,7 +67,7 @@ struct max8998_regulator_data { /** * struct max8998_board - packages regulator init data * @regulators: array of defined regulators - * @num_regulators: number of regultors used + * @num_regulators: number of regulators used * @irq_base: base IRQ number for max8998, required for IRQs * @ono: power onoff IRQ number for max8998 * @buck_voltage_lock: Do NOT change the values of the following six -- cgit v1.2.3 From cee58483cf56e0ba355fdd97ff5e8925329aa936 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 31 Aug 2012 13:30:06 -0400 Subject: time: Move ktime_t overflow checking into timespec_valid_strict Andreas Bombe reported that the added ktime_t overflow checking added to timespec_valid in commit 4e8b14526ca7 ("time: Improve sanity checking of timekeeping inputs") was causing problems with X.org because it caused timeouts larger then KTIME_T to be invalid. Previously, these large timeouts would be clamped to KTIME_MAX and would never expire, which is valid. This patch splits the ktime_t overflow checking into a new timespec_valid_strict function, and converts the timekeeping codes internal checking to use this more strict function. Reported-and-tested-by: Andreas Bombe Cc: Zhouping Liu Cc: Ingo Molnar Cc: Prarit Bhargava Cc: Thomas Gleixner Cc: stable@vger.kernel.org Signed-off-by: John Stultz Signed-off-by: Linus Torvalds --- include/linux/time.h | 7 +++++++ kernel/time/timekeeping.c | 10 +++++----- 2 files changed, 12 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/time.h b/include/linux/time.h index b0bbd8f0130d..b51e664c83e7 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -125,6 +125,13 @@ static inline bool timespec_valid(const struct timespec *ts) /* Can't have more nanoseconds then a second */ if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC) return false; + return true; +} + +static inline bool timespec_valid_strict(const struct timespec *ts) +{ + if (!timespec_valid(ts)) + return false; /* Disallow values that could overflow ktime_t */ if ((unsigned long long)ts->tv_sec >= KTIME_SEC_MAX) return false; diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 0c1485e42be6..34e5eac81424 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -428,7 +428,7 @@ int do_settimeofday(const struct timespec *tv) struct timespec ts_delta, xt; unsigned long flags; - if (!timespec_valid(tv)) + if (!timespec_valid_strict(tv)) return -EINVAL; write_seqlock_irqsave(&tk->lock, flags); @@ -476,7 +476,7 @@ int timekeeping_inject_offset(struct timespec *ts) /* Make sure the proposed value is valid */ tmp = timespec_add(tk_xtime(tk), *ts); - if (!timespec_valid(&tmp)) { + if (!timespec_valid_strict(&tmp)) { ret = -EINVAL; goto error; } @@ -659,7 +659,7 @@ void __init timekeeping_init(void) struct timespec now, boot, tmp; read_persistent_clock(&now); - if (!timespec_valid(&now)) { + if (!timespec_valid_strict(&now)) { pr_warn("WARNING: Persistent clock returned invalid value!\n" " Check your CMOS/BIOS settings.\n"); now.tv_sec = 0; @@ -667,7 +667,7 @@ void __init timekeeping_init(void) } read_boot_clock(&boot); - if (!timespec_valid(&boot)) { + if (!timespec_valid_strict(&boot)) { pr_warn("WARNING: Boot clock returned invalid value!\n" " Check your CMOS/BIOS settings.\n"); boot.tv_sec = 0; @@ -713,7 +713,7 @@ static struct timespec timekeeping_suspend_time; static void __timekeeping_inject_sleeptime(struct timekeeper *tk, struct timespec *delta) { - if (!timespec_valid(delta)) { + if (!timespec_valid_strict(delta)) { printk(KERN_WARNING "__timekeeping_inject_sleeptime: Invalid " "sleep delta value!\n"); return; -- cgit v1.2.3 From 4907cb7b193a4f91c1fd30cf679c035e3644c64d Mon Sep 17 00:00:00 2001 From: Anatol Pomozov Date: Sat, 1 Sep 2012 10:31:09 -0700 Subject: treewide: fix comment/printk/variable typos Signed-off-by: Anatol Pomozov Signed-off-by: Jiri Kosina --- drivers/dma/intel_mid_dma.c | 2 +- drivers/net/ethernet/3com/typhoon.c | 2 +- drivers/net/ethernet/broadcom/bnx2x/bnx2x.h | 2 +- drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h | 2 +- drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c | 6 +++--- drivers/net/wireless/rtlwifi/rtl8192de/fw.c | 6 +++--- drivers/scsi/bfa/bfa_ioc.c | 2 +- drivers/scsi/bfa/bfa_ioc.h | 2 +- drivers/scsi/isci/init.c | 2 +- drivers/scsi/isci/request.c | 2 +- drivers/scsi/isci/task.c | 2 +- drivers/scsi/lpfc/lpfc_init.c | 2 +- drivers/scsi/lpfc/lpfc_sli.c | 4 ++-- drivers/video/exynos/exynos_mipi_dsi.c | 2 +- drivers/w1/masters/ds1wm.c | 2 +- fs/ext2/balloc.c | 2 +- fs/ext3/balloc.c | 2 +- fs/ext3/inode.c | 2 +- fs/ext4/inode.c | 14 +++++++------- fs/ext4/mballoc.c | 2 +- include/trace/define_trace.h | 2 +- net/8021q/vlanproc.c | 2 +- 22 files changed, 33 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c index 222e907bfaaa..02b21d7d38e5 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -427,7 +427,7 @@ DMA engine callback Functions*/ * intel_mid_dma_tx_submit - callback to submit DMA transaction * @tx: dma engine descriptor * - * Submit the DMA trasaction for this descriptor, start if ch idle + * Submit the DMA transaction for this descriptor, start if ch idle */ static dma_cookie_t intel_mid_dma_tx_submit(struct dma_async_tx_descriptor *tx) { diff --git a/drivers/net/ethernet/3com/typhoon.c b/drivers/net/ethernet/3com/typhoon.c index b15366635147..bb9670f29b59 100644 --- a/drivers/net/ethernet/3com/typhoon.c +++ b/drivers/net/ethernet/3com/typhoon.c @@ -364,7 +364,7 @@ typhoon_inc_rxfree_index(u32 *index, const int count) static inline void typhoon_inc_tx_index(u32 *index, const int count) { - /* if we start using the Hi Tx ring, this needs updateing */ + /* if we start using the Hi Tx ring, this needs updating */ typhoon_inc_index(index, count, TXLO_ENTRIES); } diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index 77bcd4cb4ffb..dfa3ee2ddba4 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -1458,7 +1458,7 @@ struct bnx2x { int fw_stats_req_sz; /* - * FW statistics data shortcut (points at the begining of + * FW statistics data shortcut (points at the beginning of * fw_stats buffer + fw_stats_req_sz). */ struct bnx2x_fw_stats_data *fw_stats_data; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h index f83e033da6da..acf2fe4ca608 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h @@ -1321,7 +1321,7 @@ void bnx2x_init_mcast_obj(struct bnx2x *bp, * the current command will be enqueued to the tail of the * pending commands list. * - * Return: 0 is operation was sucessfull and there are no pending completions, + * Return: 0 is operation was successfull and there are no pending completions, * negative if there were errors, positive if there are pending * completions. */ diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c index 44febfde9493..8a7b864faca3 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c +++ b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c @@ -315,7 +315,7 @@ static void _rtl92c_fill_h2c_command(struct ieee80211_hw *hw, u16 box_reg = 0, box_extreg = 0; u8 u1b_tmp; bool isfw_read = false; - bool bwrite_sucess = false; + bool bwrite_success = false; u8 wait_h2c_limmit = 100; u8 wait_writeh2c_limmit = 100; u8 boxcontent[4], boxextcontent[2]; @@ -354,7 +354,7 @@ static void _rtl92c_fill_h2c_command(struct ieee80211_hw *hw, } } - while (!bwrite_sucess) { + while (!bwrite_success) { wait_writeh2c_limmit--; if (wait_writeh2c_limmit == 0) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, @@ -491,7 +491,7 @@ static void _rtl92c_fill_h2c_command(struct ieee80211_hw *hw, break; } - bwrite_sucess = true; + bwrite_success = true; rtlhal->last_hmeboxnum = boxnum + 1; if (rtlhal->last_hmeboxnum == 4) diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/fw.c b/drivers/net/wireless/rtlwifi/rtl8192de/fw.c index 895ae6c1f354..eb22dccc418b 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/fw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/fw.c @@ -365,7 +365,7 @@ static void _rtl92d_fill_h2c_command(struct ieee80211_hw *hw, u8 u1b_tmp; bool isfw_read = false; u8 buf_index = 0; - bool bwrite_sucess = false; + bool bwrite_success = false; u8 wait_h2c_limmit = 100; u8 wait_writeh2c_limmit = 100; u8 boxcontent[4], boxextcontent[2]; @@ -408,7 +408,7 @@ static void _rtl92d_fill_h2c_command(struct ieee80211_hw *hw, break; } } - while (!bwrite_sucess) { + while (!bwrite_success) { wait_writeh2c_limmit--; if (wait_writeh2c_limmit == 0) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, @@ -515,7 +515,7 @@ static void _rtl92d_fill_h2c_command(struct ieee80211_hw *hw, "switch case not processed\n"); break; } - bwrite_sucess = true; + bwrite_success = true; rtlhal->last_hmeboxnum = boxnum + 1; if (rtlhal->last_hmeboxnum == 4) rtlhal->last_hmeboxnum = 0; diff --git a/drivers/scsi/bfa/bfa_ioc.c b/drivers/scsi/bfa/bfa_ioc.c index 14e6284e48e4..6faff0053370 100644 --- a/drivers/scsi/bfa/bfa_ioc.c +++ b/drivers/scsi/bfa/bfa_ioc.c @@ -5587,7 +5587,7 @@ static bfa_status_t bfa_dconf_flash_write(struct bfa_dconf_mod_s *dconf); static void bfa_dconf_init_cb(void *arg, bfa_status_t status); /* - * Begining state of dconf module. Waiting for an event to start. + * Beginning state of dconf module. Waiting for an event to start. */ static void bfa_dconf_sm_uninit(struct bfa_dconf_mod_s *dconf, enum bfa_dconf_event event) diff --git a/drivers/scsi/bfa/bfa_ioc.h b/drivers/scsi/bfa/bfa_ioc.h index 1a99d4b5b50f..7b916e04ca56 100644 --- a/drivers/scsi/bfa/bfa_ioc.h +++ b/drivers/scsi/bfa/bfa_ioc.h @@ -530,7 +530,7 @@ struct bfa_diag_results_fwping { struct bfa_diag_qtest_result_s { u32 status; - u16 count; /* sucessful queue test count */ + u16 count; /* successful queue test count */ u8 queue; u8 rsvd; /* 64-bit align */ }; diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index 47e28b555029..8912567988cf 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -219,7 +219,7 @@ static struct sas_domain_function_template isci_transport_ops = { * @isci_host: This parameter specifies the lldd specific wrapper for the * libsas sas_ha struct. * - * This method returns an error code indicating sucess or failure. The user + * This method returns an error code indicating success or failure. The user * should check for possible memory allocation error return otherwise, a zero * indicates success. */ diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index 7a0431c73493..c1bafc3f3fb1 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -2240,7 +2240,7 @@ static enum sci_status atapi_data_tc_completion_handler(struct isci_request *ire status = ireq->sci_status; sci_change_state(&idev->sm, SCI_STP_DEV_ATAPI_ERROR); } else { - /* If receiving any non-sucess TC status, no UF + /* If receiving any non-success TC status, no UF * received yet, then an UF for the status fis * is coming after (XXX: suspect this is * actually a protocol error or a bug like the diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 6bc74eb012c9..b6f19a1db780 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -532,7 +532,7 @@ int isci_task_abort_task(struct sas_task *task) /* The request has already completed and there * is nothing to do here other than to set the task * done bit, and indicate that the task abort function - * was sucessful. + * was successful. */ spin_lock_irqsave(&task->task_state_lock, flags); task->task_state_flags |= SAS_TASK_STATE_DONE; diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 411ed48d79da..be43b4f6bd70 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -6470,7 +6470,7 @@ out_error: * we just use some constant number as place holder. * * Return codes - * 0 - sucessful + * 0 - successful * -ENOMEM - No availble memory * -EIO - The mailbox failed to complete successfully. **/ diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index b4720a109817..7983abbb7aaf 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -4739,7 +4739,7 @@ lpfc_sli4_read_rev(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq, * is attached to. * * Return codes - * 0 - sucessful + * 0 - successful * otherwise - failed to retrieve physical port name **/ static int @@ -15118,7 +15118,7 @@ lpfc_check_next_fcf_pri_level(struct lpfc_hba *phba) /* * if next_fcf_pri was not set above and the list is not empty then * we have failed flogis on all of them. So reset flogi failed - * and start at the begining. + * and start at the beginning. */ if (!next_fcf_pri && !list_empty(&phba->fcf.fcf_pri_list)) { list_for_each_entry(fcf_pri, &phba->fcf.fcf_pri_list, list) { diff --git a/drivers/video/exynos/exynos_mipi_dsi.c b/drivers/video/exynos/exynos_mipi_dsi.c index 9908e75ae761..752a12e625e4 100644 --- a/drivers/video/exynos/exynos_mipi_dsi.c +++ b/drivers/video/exynos/exynos_mipi_dsi.c @@ -461,7 +461,7 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev) done: platform_set_drvdata(pdev, dsim); - dev_dbg(&pdev->dev, "%s() completed sucessfuly (%s mode)\n", __func__, + dev_dbg(&pdev->dev, "%s() completed successfully (%s mode)\n", __func__, dsim_config->e_interface == DSIM_COMMAND ? "CPU" : "RGB"); return 0; diff --git a/drivers/w1/masters/ds1wm.c b/drivers/w1/masters/ds1wm.c index a0c8965c1a79..649eb0b2d3ff 100644 --- a/drivers/w1/masters/ds1wm.c +++ b/drivers/w1/masters/ds1wm.c @@ -347,7 +347,7 @@ static void ds1wm_search(void *data, struct w1_master *master_dev, "pass: %d entering ASM\n", pass); ds1wm_write_register(ds1wm_data, DS1WM_CMD, DS1WM_CMD_SRA); dev_dbg(&ds1wm_data->pdev->dev, - "pass: %d begining nibble loop\n", pass); + "pass: %d beginning nibble loop\n", pass); r_prime = 0; d = 0; diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 1c3613998862..905166a3ffb2 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -479,7 +479,7 @@ void ext2_discard_reservation(struct inode *inode) /** * ext2_free_blocks() -- Free given blocks and update quota and i_blocks * @inode: inode - * @block: start physcial block to free + * @block: start physical block to free * @count: number of blocks to free */ void ext2_free_blocks (struct inode * inode, unsigned long block, diff --git a/fs/ext3/balloc.c b/fs/ext3/balloc.c index 25cd60892116..7c6a31d44daf 100644 --- a/fs/ext3/balloc.c +++ b/fs/ext3/balloc.c @@ -483,7 +483,7 @@ void ext3_discard_reservation(struct inode *inode) * ext3_free_blocks_sb() -- Free given blocks and update quota * @handle: handle to this transaction * @sb: super block - * @block: start physcial block to free + * @block: start physical block to free * @count: number of blocks to free * @pdquot_freed_blocks: pointer to quota */ diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 9a4a5c48b1c9..3aff616d4d03 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -3196,7 +3196,7 @@ out_brelse: * * - Within generic_file_write() for O_SYNC files. * Here, there will be no transaction running. We wait for any running - * trasnaction to commit. + * transaction to commit. * * - Within sys_sync(), kupdate and such. * We wait on commit, if tol to. diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 02bc8cbe7281..189dae6b0964 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -3227,7 +3227,7 @@ int ext4_discard_partial_page_buffers(handle_t *handle, * handle: The journal handle * inode: The files inode * page: A locked page that contains the offset "from" - * from: The starting byte offset (from the begining of the file) + * from: The starting byte offset (from the beginning of the file) * to begin discarding * len: The length of bytes to discard * flags: Optional flags that may be used: @@ -3235,11 +3235,11 @@ int ext4_discard_partial_page_buffers(handle_t *handle, * EXT4_DISCARD_PARTIAL_PG_ZERO_UNMAPPED * Only zero the regions of the page whose buffer heads * have already been unmapped. This flag is appropriate - * for updateing the contents of a page whose blocks may + * for updating the contents of a page whose blocks may * have already been released, and we only want to zero * out the regions that correspond to those released blocks. * - * Returns zero on sucess or negative on failure. + * Returns zero on success or negative on failure. */ static int ext4_discard_partial_page_buffers_no_lock(handle_t *handle, struct inode *inode, struct page *page, loff_t from, @@ -3400,7 +3400,7 @@ int ext4_can_truncate(struct inode *inode) * @offset: The offset where the hole will begin * @len: The length of the hole * - * Returns: 0 on sucess or negative on failure + * Returns: 0 on success or negative on failure */ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length) @@ -3922,7 +3922,7 @@ static int ext4_inode_blocks_set(handle_t *handle, if (i_blocks <= ~0U) { /* - * i_blocks can be represnted in a 32 bit variable + * i_blocks can be represented in a 32 bit variable * as multiple of 512 bytes */ raw_inode->i_blocks_lo = cpu_to_le32(i_blocks); @@ -4083,7 +4083,7 @@ out_brelse: * * - Within generic_file_write() for O_SYNC files. * Here, there will be no transaction running. We wait for any running - * trasnaction to commit. + * transaction to commit. * * - Within sys_sync(), kupdate and such. * We wait on commit, if tol to. @@ -4327,7 +4327,7 @@ static int ext4_index_trans_blocks(struct inode *inode, int nrblocks, int chunk) * worse case, the indexs blocks spread over different block groups * * If datablocks are discontiguous, they are possible to spread over - * different block groups too. If they are contiuguous, with flexbg, + * different block groups too. If they are contiguous, with flexbg, * they could still across block group boundary. * * Also account for superblock, inode, quota and xattr blocks diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 1cd6994fc446..86831411e98b 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -4705,7 +4705,7 @@ error_return: * ext4_group_add_blocks() -- Add given blocks to an existing group * @handle: handle to this transaction * @sb: super block - * @block: start physcial block to add to the block group + * @block: start physical block to add to the block group * @count: number of blocks to free * * This marks the blocks as free in the bitmap and buddy. diff --git a/include/trace/define_trace.h b/include/trace/define_trace.h index b0b4eb24d592..1905ca8dd399 100644 --- a/include/trace/define_trace.h +++ b/include/trace/define_trace.h @@ -1,5 +1,5 @@ /* - * Trace files that want to automate creationg of all tracepoints defined + * Trace files that want to automate creation of all tracepoints defined * in their file should include this file. The following are macros that the * trace file may define: * diff --git a/net/8021q/vlanproc.c b/net/8021q/vlanproc.c index c718fd3664b6..4de77ea5fa37 100644 --- a/net/8021q/vlanproc.c +++ b/net/8021q/vlanproc.c @@ -105,7 +105,7 @@ static const struct file_operations vlandev_fops = { }; /* - * Proc filesystem derectory entries. + * Proc filesystem directory entries. */ /* Strings */ -- cgit v1.2.3 From b6d86d3d6d6e4c9b588d81615c81b5a8292b62ed Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 24 Aug 2012 17:25:01 -0700 Subject: linux/kernel.h: Fix DIV_ROUND_CLOSEST to support negative dividends DIV_ROUND_CLOSEST returns a bad result for negative dividends: DIV_ROUND_CLOSEST(-2, 2) = 0 Most of the time this does not matter. However, in the hardware monitoring subsystem, DIV_ROUND_CLOSEST is sometimes used on integers which can be negative (such as temperatures). Signed-off-by: Guenter Roeck Acked-by: Jean Delvare --- include/linux/kernel.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 604382143bcf..594b419b7d20 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -82,10 +82,18 @@ __x - (__x % (y)); \ } \ ) + +/* + * Divide positive or negative dividend by positive divisor and round + * to closest integer. Result is undefined for negative divisors. + */ #define DIV_ROUND_CLOSEST(x, divisor)( \ { \ - typeof(divisor) __divisor = divisor; \ - (((x) + ((__divisor) / 2)) / (__divisor)); \ + typeof(x) __x = x; \ + typeof(divisor) __d = divisor; \ + (((typeof(x))-1) >= 0 || (__x) >= 0) ? \ + (((__x) + ((__d) / 2)) / (__d)) : \ + (((__x) - ((__d) / 2)) / (__d)); \ } \ ) -- cgit v1.2.3 From e60bc2df9ba5574af87a8d0fa249ceac9fa1d3bd Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 30 Jul 2012 18:38:33 +0200 Subject: pinctrl: use ERR_CAST instead of ERR_PTR(PTR_ERR(...)) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This doesn't change semantics, just looks nicer. Signed-off-by: Uwe Kleine-König Signed-off-by: Linus Walleij --- include/linux/pinctrl/consumer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/pinctrl/consumer.h b/include/linux/pinctrl/consumer.h index e9b7f4350844..d2816454c263 100644 --- a/include/linux/pinctrl/consumer.h +++ b/include/linux/pinctrl/consumer.h @@ -140,7 +140,7 @@ static inline struct pinctrl * __must_check devm_pinctrl_get_select( s = pinctrl_lookup_state(p, name); if (IS_ERR(s)) { devm_pinctrl_put(p); - return ERR_PTR(PTR_ERR(s)); + return ERR_CAST(s); } ret = pinctrl_select_state(p, s); -- cgit v1.2.3 From 84b5ee939eba0115739c19c0e01ea903b029c9da Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 28 Aug 2012 00:53:15 +0000 Subject: netfilter: nf_conntrack: add nf_ct_timeout_lookup This patch adds the new nf_ct_timeout_lookup function to encapsulate the timeout policy attachment that is called in the nf_conntrack_in path. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_timeout.h | 20 ++++++++++++++++++++ net/netfilter/nf_conntrack_core.c | 7 +------ 2 files changed, 21 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_timeout.h b/include/net/netfilter/nf_conntrack_timeout.h index 34ec89f8dbf9..e41e472d08f2 100644 --- a/include/net/netfilter/nf_conntrack_timeout.h +++ b/include/net/netfilter/nf_conntrack_timeout.h @@ -55,6 +55,26 @@ struct nf_conn_timeout *nf_ct_timeout_ext_add(struct nf_conn *ct, #endif }; +static inline unsigned int * +nf_ct_timeout_lookup(struct net *net, struct nf_conn *ct, + struct nf_conntrack_l4proto *l4proto) +{ +#ifdef CONFIG_NF_CONNTRACK_TIMEOUT + struct nf_conn_timeout *timeout_ext; + unsigned int *timeouts; + + timeout_ext = nf_ct_timeout_find(ct); + if (timeout_ext) + timeouts = NF_CT_TIMEOUT_EXT_DATA(timeout_ext); + else + timeouts = l4proto->get_timeouts(net); + + return timeouts; +#else + return l4proto->get_timeouts(net); +#endif +} + #ifdef CONFIG_NF_CONNTRACK_TIMEOUT extern int nf_conntrack_timeout_init(struct net *net); extern void nf_conntrack_timeout_fini(struct net *net); diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index f83e79defed9..c9bb994ae9ba 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -930,7 +930,6 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, enum ip_conntrack_info ctinfo; struct nf_conntrack_l3proto *l3proto; struct nf_conntrack_l4proto *l4proto; - struct nf_conn_timeout *timeout_ext; unsigned int *timeouts; unsigned int dataoff; u_int8_t protonum; @@ -997,11 +996,7 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, NF_CT_ASSERT(skb->nfct); /* Decide what timeout policy we want to apply to this flow. */ - timeout_ext = nf_ct_timeout_find(ct); - if (timeout_ext) - timeouts = NF_CT_TIMEOUT_EXT_DATA(timeout_ext); - else - timeouts = l4proto->get_timeouts(net); + timeouts = nf_ct_timeout_lookup(net, ct, l4proto); ret = l4proto->packet(ct, skb, dataoff, ctinfo, pf, hooknum, timeouts); if (ret <= 0) { -- cgit v1.2.3 From 684bad1107571d35610a674c61b3544efb5a5b13 Mon Sep 17 00:00:00 2001 From: Yuchung Cheng Date: Sun, 2 Sep 2012 17:38:04 +0000 Subject: tcp: use PRR to reduce cwin in CWR state Use proportional rate reduction (PRR) algorithm to reduce cwnd in CWR state, in addition to Recovery state. Retire the current rate-halving in CWR. When losses are detected via ACKs in CWR state, the sender enters Recovery state but the cwnd reduction continues and does not restart. Rename and refactor cwnd reduction functions since both CWR and Recovery use the same algorithm: tcp_init_cwnd_reduction() is new and initiates reduction state variables. tcp_cwnd_reduction() is previously tcp_update_cwnd_in_recovery(). tcp_ends_cwnd_reduction() is previously tcp_complete_cwr(). The rate halving functions and logic such as tcp_cwnd_down(), tcp_min_cwnd(), and the cwnd moderation inside tcp_enter_cwr() are removed. The unused parameter, flag, in tcp_cwnd_reduction() is also removed. Signed-off-by: Yuchung Cheng Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- include/net/tcp.h | 10 ++++- net/ipv4/tcp_input.c | 119 +++++++++++++++++--------------------------------- net/ipv4/tcp_output.c | 6 +-- 3 files changed, 52 insertions(+), 83 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 1421b02a7905..a8cb00c0c6d9 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -913,15 +913,21 @@ static inline bool tcp_in_initial_slowstart(const struct tcp_sock *tp) return tp->snd_ssthresh >= TCP_INFINITE_SSTHRESH; } +static inline bool tcp_in_cwnd_reduction(const struct sock *sk) +{ + return (TCPF_CA_CWR | TCPF_CA_Recovery) & + (1 << inet_csk(sk)->icsk_ca_state); +} + /* If cwnd > ssthresh, we may raise ssthresh to be half-way to cwnd. - * The exception is rate halving phase, when cwnd is decreasing towards + * The exception is cwnd reduction phase, when cwnd is decreasing towards * ssthresh. */ static inline __u32 tcp_current_ssthresh(const struct sock *sk) { const struct tcp_sock *tp = tcp_sk(sk); - if ((1 << inet_csk(sk)->icsk_ca_state) & (TCPF_CA_CWR | TCPF_CA_Recovery)) + if (tcp_in_cwnd_reduction(sk)) return tp->snd_ssthresh; else return max(tp->snd_ssthresh, diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 38589e464e63..e2bec815ff23 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2470,35 +2470,6 @@ static inline void tcp_moderate_cwnd(struct tcp_sock *tp) tp->snd_cwnd_stamp = tcp_time_stamp; } -/* Lower bound on congestion window is slow start threshold - * unless congestion avoidance choice decides to overide it. - */ -static inline u32 tcp_cwnd_min(const struct sock *sk) -{ - const struct tcp_congestion_ops *ca_ops = inet_csk(sk)->icsk_ca_ops; - - return ca_ops->min_cwnd ? ca_ops->min_cwnd(sk) : tcp_sk(sk)->snd_ssthresh; -} - -/* Decrease cwnd each second ack. */ -static void tcp_cwnd_down(struct sock *sk, int flag) -{ - struct tcp_sock *tp = tcp_sk(sk); - int decr = tp->snd_cwnd_cnt + 1; - - if ((flag & (FLAG_ANY_PROGRESS | FLAG_DSACKING_ACK)) || - (tcp_is_reno(tp) && !(flag & FLAG_NOT_DUP))) { - tp->snd_cwnd_cnt = decr & 1; - decr >>= 1; - - if (decr && tp->snd_cwnd > tcp_cwnd_min(sk)) - tp->snd_cwnd -= decr; - - tp->snd_cwnd = min(tp->snd_cwnd, tcp_packets_in_flight(tp) + 1); - tp->snd_cwnd_stamp = tcp_time_stamp; - } -} - /* Nothing was retransmitted or returned timestamp is less * than timestamp of the first retransmission. */ @@ -2700,9 +2671,8 @@ static bool tcp_try_undo_loss(struct sock *sk) return false; } -/* This function implements the PRR algorithm, specifcally the PRR-SSRB - * (proportional rate reduction with slow start reduction bound) as described in - * http://www.ietf.org/id/draft-mathis-tcpm-proportional-rate-reduction-01.txt. +/* The cwnd reduction in CWR and Recovery use the PRR algorithm + * https://datatracker.ietf.org/doc/draft-ietf-tcpm-proportional-rate-reduction/ * It computes the number of packets to send (sndcnt) based on packets newly * delivered: * 1) If the packets in flight is larger than ssthresh, PRR spreads the @@ -2711,13 +2681,29 @@ static bool tcp_try_undo_loss(struct sock *sk) * losses and/or application stalls), do not perform any further cwnd * reductions, but instead slow start up to ssthresh. */ -static void tcp_update_cwnd_in_recovery(struct sock *sk, int newly_acked_sacked, - int fast_rexmit, int flag) +static void tcp_init_cwnd_reduction(struct sock *sk, const bool set_ssthresh) +{ + struct tcp_sock *tp = tcp_sk(sk); + + tp->high_seq = tp->snd_nxt; + tp->bytes_acked = 0; + tp->snd_cwnd_cnt = 0; + tp->prior_cwnd = tp->snd_cwnd; + tp->prr_delivered = 0; + tp->prr_out = 0; + if (set_ssthresh) + tp->snd_ssthresh = inet_csk(sk)->icsk_ca_ops->ssthresh(sk); + TCP_ECN_queue_cwr(tp); +} + +static void tcp_cwnd_reduction(struct sock *sk, int newly_acked_sacked, + int fast_rexmit) { struct tcp_sock *tp = tcp_sk(sk); int sndcnt = 0; int delta = tp->snd_ssthresh - tcp_packets_in_flight(tp); + tp->prr_delivered += newly_acked_sacked; if (tcp_packets_in_flight(tp) > tp->snd_ssthresh) { u64 dividend = (u64)tp->snd_ssthresh * tp->prr_delivered + tp->prior_cwnd - 1; @@ -2732,43 +2718,29 @@ static void tcp_update_cwnd_in_recovery(struct sock *sk, int newly_acked_sacked, tp->snd_cwnd = tcp_packets_in_flight(tp) + sndcnt; } -static inline void tcp_complete_cwr(struct sock *sk) +static inline void tcp_end_cwnd_reduction(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); - /* Do not moderate cwnd if it's already undone in cwr or recovery. */ - if (tp->undo_marker) { - if (inet_csk(sk)->icsk_ca_state == TCP_CA_CWR) { - tp->snd_cwnd = min(tp->snd_cwnd, tp->snd_ssthresh); - tp->snd_cwnd_stamp = tcp_time_stamp; - } else if (tp->snd_ssthresh < TCP_INFINITE_SSTHRESH) { - /* PRR algorithm. */ - tp->snd_cwnd = tp->snd_ssthresh; - tp->snd_cwnd_stamp = tcp_time_stamp; - } + /* Reset cwnd to ssthresh in CWR or Recovery (unless it's undone) */ + if (inet_csk(sk)->icsk_ca_state == TCP_CA_CWR || + (tp->undo_marker && tp->snd_ssthresh < TCP_INFINITE_SSTHRESH)) { + tp->snd_cwnd = tp->snd_ssthresh; + tp->snd_cwnd_stamp = tcp_time_stamp; } tcp_ca_event(sk, CA_EVENT_COMPLETE_CWR); } -/* Set slow start threshold and cwnd not falling to slow start */ +/* Enter CWR state. Disable cwnd undo since congestion is proven with ECN */ void tcp_enter_cwr(struct sock *sk, const int set_ssthresh) { struct tcp_sock *tp = tcp_sk(sk); - const struct inet_connection_sock *icsk = inet_csk(sk); tp->prior_ssthresh = 0; tp->bytes_acked = 0; - if (icsk->icsk_ca_state < TCP_CA_CWR) { + if (inet_csk(sk)->icsk_ca_state < TCP_CA_CWR) { tp->undo_marker = 0; - if (set_ssthresh) - tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh(sk); - tp->snd_cwnd = min(tp->snd_cwnd, - tcp_packets_in_flight(tp) + 1U); - tp->snd_cwnd_cnt = 0; - tp->high_seq = tp->snd_nxt; - tp->snd_cwnd_stamp = tcp_time_stamp; - TCP_ECN_queue_cwr(tp); - + tcp_init_cwnd_reduction(sk, set_ssthresh); tcp_set_ca_state(sk, TCP_CA_CWR); } } @@ -2787,7 +2759,7 @@ static void tcp_try_keep_open(struct sock *sk) } } -static void tcp_try_to_open(struct sock *sk, int flag) +static void tcp_try_to_open(struct sock *sk, int flag, int newly_acked_sacked) { struct tcp_sock *tp = tcp_sk(sk); @@ -2804,7 +2776,7 @@ static void tcp_try_to_open(struct sock *sk, int flag) if (inet_csk(sk)->icsk_ca_state != TCP_CA_Open) tcp_moderate_cwnd(tp); } else { - tcp_cwnd_down(sk, flag); + tcp_cwnd_reduction(sk, newly_acked_sacked, 0); } } @@ -2898,7 +2870,6 @@ static void tcp_enter_recovery(struct sock *sk, bool ece_ack) NET_INC_STATS_BH(sock_net(sk), mib_idx); - tp->high_seq = tp->snd_nxt; tp->prior_ssthresh = 0; tp->undo_marker = tp->snd_una; tp->undo_retrans = tp->retrans_out; @@ -2906,15 +2877,8 @@ static void tcp_enter_recovery(struct sock *sk, bool ece_ack) if (inet_csk(sk)->icsk_ca_state < TCP_CA_CWR) { if (!ece_ack) tp->prior_ssthresh = tcp_current_ssthresh(sk); - tp->snd_ssthresh = inet_csk(sk)->icsk_ca_ops->ssthresh(sk); - TCP_ECN_queue_cwr(tp); + tcp_init_cwnd_reduction(sk, true); } - - tp->bytes_acked = 0; - tp->snd_cwnd_cnt = 0; - tp->prior_cwnd = tp->snd_cwnd; - tp->prr_delivered = 0; - tp->prr_out = 0; tcp_set_ca_state(sk, TCP_CA_Recovery); } @@ -2974,7 +2938,7 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked, /* CWR is to be held something *above* high_seq * is ACKed for CWR bit to reach receiver. */ if (tp->snd_una != tp->high_seq) { - tcp_complete_cwr(sk); + tcp_end_cwnd_reduction(sk); tcp_set_ca_state(sk, TCP_CA_Open); } break; @@ -2984,7 +2948,7 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked, tcp_reset_reno_sack(tp); if (tcp_try_undo_recovery(sk)) return; - tcp_complete_cwr(sk); + tcp_end_cwnd_reduction(sk); break; } } @@ -3025,7 +2989,7 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked, tcp_try_undo_dsack(sk); if (!tcp_time_to_recover(sk, flag)) { - tcp_try_to_open(sk, flag); + tcp_try_to_open(sk, flag, newly_acked_sacked); return; } @@ -3047,8 +3011,7 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked, if (do_lost || (tcp_is_fack(tp) && tcp_head_timedout(sk))) tcp_update_scoreboard(sk, fast_rexmit); - tp->prr_delivered += newly_acked_sacked; - tcp_update_cwnd_in_recovery(sk, newly_acked_sacked, fast_rexmit, flag); + tcp_cwnd_reduction(sk, newly_acked_sacked, fast_rexmit); tcp_xmit_retransmit_queue(sk); } @@ -3394,7 +3357,7 @@ static inline bool tcp_may_raise_cwnd(const struct sock *sk, const int flag) { const struct tcp_sock *tp = tcp_sk(sk); return (!(flag & FLAG_ECE) || tp->snd_cwnd < tp->snd_ssthresh) && - !((1 << inet_csk(sk)->icsk_ca_state) & (TCPF_CA_Recovery | TCPF_CA_CWR)); + !tcp_in_cwnd_reduction(sk); } /* Check that window update is acceptable. @@ -3462,9 +3425,9 @@ static void tcp_conservative_spur_to_response(struct tcp_sock *tp) } /* A conservative spurious RTO response algorithm: reduce cwnd using - * rate halving and continue in congestion avoidance. + * PRR and continue in congestion avoidance. */ -static void tcp_ratehalving_spur_to_response(struct sock *sk) +static void tcp_cwr_spur_to_response(struct sock *sk) { tcp_enter_cwr(sk, 0); } @@ -3472,7 +3435,7 @@ static void tcp_ratehalving_spur_to_response(struct sock *sk) static void tcp_undo_spur_to_response(struct sock *sk, int flag) { if (flag & FLAG_ECE) - tcp_ratehalving_spur_to_response(sk); + tcp_cwr_spur_to_response(sk); else tcp_undo_cwr(sk, true); } @@ -3579,7 +3542,7 @@ static bool tcp_process_frto(struct sock *sk, int flag) tcp_conservative_spur_to_response(tp); break; default: - tcp_ratehalving_spur_to_response(sk); + tcp_cwr_spur_to_response(sk); break; } tp->frto_counter = 0; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 9383b51f3efc..cfe6ffe1c177 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2037,10 +2037,10 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, if (push_one) break; } - if (inet_csk(sk)->icsk_ca_state == TCP_CA_Recovery) - tp->prr_out += sent_pkts; if (likely(sent_pkts)) { + if (tcp_in_cwnd_reduction(sk)) + tp->prr_out += sent_pkts; tcp_cwnd_validate(sk); return false; } @@ -2542,7 +2542,7 @@ begin_fwd: } NET_INC_STATS_BH(sock_net(sk), mib_idx); - if (inet_csk(sk)->icsk_ca_state == TCP_CA_Recovery) + if (tcp_in_cwnd_reduction(sk)) tp->prr_out += tcp_skb_pcount(skb); if (skb == tcp_write_queue_head(sk)) -- cgit v1.2.3 From c3668a0f8097af2f24a5fd67695f4ee830f99eda Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Sun, 26 Aug 2012 13:43:00 +0100 Subject: iio: document missing elements Signed-off-by: Peter Meerwald Signed-off-by: Jonathan Cameron --- include/linux/iio/iio.h | 4 ++++ include/linux/iio/trigger.h | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index b18e74e0bdd5..2c395a898193 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -229,6 +229,7 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, * @indexed: Specify the channel has a numerical index. If not, * the channel index number will be suppressed for sysfs * attributes but not for event codes. + * @output: Channel is output. * @differential: Channel is differential. */ struct iio_chan_spec { @@ -312,6 +313,9 @@ struct iio_dev; * Meaning is event dependent. * @validate_trigger: function to validate the trigger when the * current trigger gets changed. + * @update_scan_mode: function to configure device and scan buffer when + * channels have changed + * @debugfs_reg_access: function to read or write register value of device **/ struct iio_info { struct module *driver_module; diff --git a/include/linux/iio/trigger.h b/include/linux/iio/trigger.h index a9819940a84c..f0af2673891d 100644 --- a/include/linux/iio/trigger.h +++ b/include/linux/iio/trigger.h @@ -39,7 +39,7 @@ struct iio_trigger_ops { /** * struct iio_trigger - industrial I/O trigger device - * + * @ops: [DRIVER] operations structure * @id: [INTERN] unique id number * @name: [DRIVER] unique name * @dev: [DRIVER] associated device (if relevant) @@ -104,7 +104,8 @@ void iio_trigger_unregister(struct iio_trigger *trig_info); /** * iio_trigger_poll() - called on a trigger occurring - * @trig: trigger which occurred + * @trig: trigger which occurred + * @time: timestamp when trigger occurred * * Typically called in relevant hardware interrupt handler. **/ -- cgit v1.2.3 From d25b3808db3a03deb12ffc0660c757b4a619f262 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Sun, 26 Aug 2012 13:43:00 +0100 Subject: iio: fix typos Signed-off-by: Peter Meerwald Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 2 +- include/linux/iio/iio.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 2ec266ef41a3..47c1ffab38ad 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -729,7 +729,7 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev) attrcount = attrcount_orig; /* * New channel registration method - relies on the fact a group does - * not need to be initialized if it is name is NULL. + * not need to be initialized if its name is NULL. */ if (indio_dev->channels) for (i = 0; i < indio_dev->num_channels; i++) { diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 2c395a898193..677e3d8105fa 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -164,7 +164,7 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, * IIO_ENUM() - Initialize enum extended channel attribute * @_name: Attribute name * @_shared: Whether the attribute is shared between all channels - * @_e: Pointer to a iio_enum struct + * @_e: Pointer to an iio_enum struct * * This should usually be used together with IIO_ENUM_AVAILABLE() */ @@ -180,9 +180,9 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, /** * IIO_ENUM_AVAILABLE() - Initialize enum available extended channel attribute * @_name: Attribute name ("_available" will be appended to the name) - * @_e: Pointer to a iio_enum struct + * @_e: Pointer to an iio_enum struct * - * Creates a read only attribute which list all the available enum items in a + * Creates a read only attribute which lists all the available enum items in a * space separated list. This should usually be used together with IIO_ENUM() */ #define IIO_ENUM_AVAILABLE(_name, _e) \ -- cgit v1.2.3 From 99698b45670a37b5304d5c6a743c8e96baa9ed8f Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Sun, 26 Aug 2012 13:43:00 +0100 Subject: iio: whitespace cleanup and removal of semicolon after functions Signed-off-by: Peter Meerwald Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 10 +++++----- drivers/iio/industrialio-core.c | 2 +- include/linux/iio/iio.h | 8 ++++---- include/linux/iio/trigger.h | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 4add9bb40eeb..774891cf8139 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -422,7 +422,7 @@ ssize_t iio_buffer_store_enable(struct device *dev, ret = indio_dev->setup_ops->preenable(indio_dev); if (ret) { printk(KERN_ERR - "Buffer not started:" + "Buffer not started: " "buffer preenable failed\n"); goto error_ret; } @@ -431,12 +431,12 @@ ssize_t iio_buffer_store_enable(struct device *dev, ret = buffer->access->request_update(buffer); if (ret) { printk(KERN_INFO - "Buffer not started:" + "Buffer not started: " "buffer parameter update failed\n"); goto error_ret; } } - /* Definitely possible for devices to support both of these.*/ + /* Definitely possible for devices to support both of these. */ if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) { if (!indio_dev->trig) { printk(KERN_INFO @@ -456,7 +456,7 @@ ssize_t iio_buffer_store_enable(struct device *dev, ret = indio_dev->setup_ops->postenable(indio_dev); if (ret) { printk(KERN_INFO - "Buffer not started:" + "Buffer not started: " "postenable failed\n"); indio_dev->currentmode = previous_mode; if (indio_dev->setup_ops->postdisable) @@ -657,7 +657,7 @@ EXPORT_SYMBOL_GPL(iio_scan_mask_query); /** * struct iio_demux_table() - table describing demux memcpy ops * @from: index to copy from - * @to: index to copy to + * @to: index to copy to * @length: how many bytes to copy * @l: list head used for management */ diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 47c1ffab38ad..a288792e2d3a 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -29,7 +29,7 @@ #include #include -/* IDA to assign each registered device a unique id*/ +/* IDA to assign each registered device a unique id */ static DEFINE_IDA(iio_ida); static dev_t iio_devt; diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 677e3d8105fa..057d60382eac 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -371,10 +371,10 @@ struct iio_info { * scan mask is valid for the device. */ struct iio_buffer_setup_ops { - int (*preenable)(struct iio_dev *); - int (*postenable)(struct iio_dev *); - int (*predisable)(struct iio_dev *); - int (*postdisable)(struct iio_dev *); + int (*preenable)(struct iio_dev *); + int (*postenable)(struct iio_dev *); + int (*predisable)(struct iio_dev *); + int (*postdisable)(struct iio_dev *); bool (*validate_scan_mask)(struct iio_dev *indio_dev, const unsigned long *scan_mask); }; diff --git a/include/linux/iio/trigger.h b/include/linux/iio/trigger.h index f0af2673891d..20239da1d0f7 100644 --- a/include/linux/iio/trigger.h +++ b/include/linux/iio/trigger.h @@ -29,7 +29,7 @@ struct iio_subirq { * instances of a given device. **/ struct iio_trigger_ops { - struct module *owner; + struct module *owner; int (*set_trigger_state)(struct iio_trigger *trig, bool state); int (*try_reenable)(struct iio_trigger *trig); int (*validate_device)(struct iio_trigger *trig, @@ -76,19 +76,19 @@ struct iio_trigger { static inline struct iio_trigger *to_iio_trigger(struct device *d) { return container_of(d, struct iio_trigger, dev); -}; +} static inline void iio_trigger_put(struct iio_trigger *trig) { module_put(trig->ops->owner); put_device(&trig->dev); -}; +} static inline void iio_trigger_get(struct iio_trigger *trig) { get_device(&trig->dev); __module_get(trig->ops->owner); -}; +} /** * iio_trigger_register() - register a trigger with the IIO core -- cgit v1.2.3 From a6fa941d94b411bbd2b6421ffbde6db3c93e65ab Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 20 Aug 2012 14:59:25 +0100 Subject: perf_event: Switch to internal refcount, fix race with close() Don't mess with file refcounts (or keep a reference to file, for that matter) in perf_event. Use explicit refcount of its own instead. Deal with the race between the final reference to event going away and new children getting created for it by use of atomic_long_inc_not_zero() in inherit_event(); just have the latter free what it had allocated and return NULL, that works out just fine (children of siblings of something doomed are created as singletons, same as if the child of leader had been created and immediately killed). Signed-off-by: Al Viro Cc: stable@kernel.org Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20120820135925.GG23464@ZenIV.linux.org.uk Signed-off-by: Ingo Molnar --- include/linux/perf_event.h | 2 +- kernel/events/core.c | 62 ++++++++++++++++++++++++---------------------- 2 files changed, 34 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 7602ccb3f40e..ad04dfcd6f35 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -926,7 +926,7 @@ struct perf_event { struct hw_perf_event hw; struct perf_event_context *ctx; - struct file *filp; + atomic_long_t refcount; /* * These accumulate total time (in nanoseconds) that children diff --git a/kernel/events/core.c b/kernel/events/core.c index b7935fcec7d9..efef4282b8e8 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -2935,12 +2935,12 @@ EXPORT_SYMBOL_GPL(perf_event_release_kernel); /* * Called when the last reference to the file is gone. */ -static int perf_release(struct inode *inode, struct file *file) +static void put_event(struct perf_event *event) { - struct perf_event *event = file->private_data; struct task_struct *owner; - file->private_data = NULL; + if (!atomic_long_dec_and_test(&event->refcount)) + return; rcu_read_lock(); owner = ACCESS_ONCE(event->owner); @@ -2975,7 +2975,13 @@ static int perf_release(struct inode *inode, struct file *file) put_task_struct(owner); } - return perf_event_release_kernel(event); + perf_event_release_kernel(event); +} + +static int perf_release(struct inode *inode, struct file *file) +{ + put_event(file->private_data); + return 0; } u64 perf_event_read_value(struct perf_event *event, u64 *enabled, u64 *running) @@ -3227,7 +3233,7 @@ unlock: static const struct file_operations perf_fops; -static struct perf_event *perf_fget_light(int fd, int *fput_needed) +static struct file *perf_fget_light(int fd, int *fput_needed) { struct file *file; @@ -3241,7 +3247,7 @@ static struct perf_event *perf_fget_light(int fd, int *fput_needed) return ERR_PTR(-EBADF); } - return file->private_data; + return file; } static int perf_event_set_output(struct perf_event *event, @@ -3273,19 +3279,21 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case PERF_EVENT_IOC_SET_OUTPUT: { + struct file *output_file = NULL; struct perf_event *output_event = NULL; int fput_needed = 0; int ret; if (arg != -1) { - output_event = perf_fget_light(arg, &fput_needed); - if (IS_ERR(output_event)) - return PTR_ERR(output_event); + output_file = perf_fget_light(arg, &fput_needed); + if (IS_ERR(output_file)) + return PTR_ERR(output_file); + output_event = output_file->private_data; } ret = perf_event_set_output(event, output_event); if (output_event) - fput_light(output_event->filp, fput_needed); + fput_light(output_file, fput_needed); return ret; } @@ -5950,6 +5958,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, mutex_init(&event->mmap_mutex); + atomic_long_set(&event->refcount, 1); event->cpu = cpu; event->attr = *attr; event->group_leader = group_leader; @@ -6260,12 +6269,12 @@ SYSCALL_DEFINE5(perf_event_open, return event_fd; if (group_fd != -1) { - group_leader = perf_fget_light(group_fd, &fput_needed); - if (IS_ERR(group_leader)) { - err = PTR_ERR(group_leader); + group_file = perf_fget_light(group_fd, &fput_needed); + if (IS_ERR(group_file)) { + err = PTR_ERR(group_file); goto err_fd; } - group_file = group_leader->filp; + group_leader = group_file->private_data; if (flags & PERF_FLAG_FD_OUTPUT) output_event = group_leader; if (flags & PERF_FLAG_FD_NO_GROUP) @@ -6402,7 +6411,6 @@ SYSCALL_DEFINE5(perf_event_open, put_ctx(gctx); } - event->filp = event_file; WARN_ON_ONCE(ctx->parent_ctx); mutex_lock(&ctx->mutex); @@ -6496,7 +6504,6 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu, goto err_free; } - event->filp = NULL; WARN_ON_ONCE(ctx->parent_ctx); mutex_lock(&ctx->mutex); perf_install_in_context(ctx, event, cpu); @@ -6578,7 +6585,7 @@ static void sync_child_event(struct perf_event *child_event, * Release the parent event, if this was the last * reference to it. */ - fput(parent_event->filp); + put_event(parent_event); } static void @@ -6654,9 +6661,8 @@ static void perf_event_exit_task_context(struct task_struct *child, int ctxn) * * __perf_event_exit_task() * sync_child_event() - * fput(parent_event->filp) - * perf_release() - * mutex_lock(&ctx->mutex) + * put_event() + * mutex_lock(&ctx->mutex) * * But since its the parent context it won't be the same instance. */ @@ -6724,7 +6730,7 @@ static void perf_free_event(struct perf_event *event, list_del_init(&event->child_list); mutex_unlock(&parent->child_mutex); - fput(parent->filp); + put_event(parent); perf_group_detach(event); list_del_event(event, ctx); @@ -6804,6 +6810,12 @@ inherit_event(struct perf_event *parent_event, NULL, NULL); if (IS_ERR(child_event)) return child_event; + + if (!atomic_long_inc_not_zero(&parent_event->refcount)) { + free_event(child_event); + return NULL; + } + get_ctx(child_ctx); /* @@ -6844,14 +6856,6 @@ inherit_event(struct perf_event *parent_event, add_event_to_ctx(child_event, child_ctx); raw_spin_unlock_irqrestore(&child_ctx->lock, flags); - /* - * Get a reference to the parent filp - we will fput it - * when the child event exits. This is safe to do because - * we are in the parent and we know that the filp still - * exists and has a nonzero count: - */ - atomic_long_inc(&parent_event->filp->f_count); - /* * Link this into the parent event's child list */ -- cgit v1.2.3 From 500ad2d8b01390c98bc6dce068bccfa9534b8212 Mon Sep 17 00:00:00 2001 From: "K.Prasad" Date: Thu, 2 Aug 2012 13:46:35 +0530 Subject: perf/hwpb: Invoke __perf_event_disable() if interrupts are already disabled While debugging a warning message on PowerPC while using hardware breakpoints, it was discovered that when perf_event_disable is invoked through hw_breakpoint_handler function with interrupts disabled, a subsequent IPI in the code path would trigger a WARN_ON_ONCE message in smp_call_function_single function. This patch calls __perf_event_disable() when interrupts are already disabled, instead of perf_event_disable(). Reported-by: Edjunior Barbosa Machado Signed-off-by: K.Prasad [naveen.n.rao@linux.vnet.ibm.com: v3: Check to make sure we target current task] Signed-off-by: Naveen N. Rao Acked-by: Frederic Weisbecker Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20120802081635.5811.17737.stgit@localhost.localdomain [ Fixed build error on MIPS. ] Signed-off-by: Ingo Molnar --- include/linux/perf_event.h | 2 ++ kernel/events/core.c | 2 +- kernel/events/hw_breakpoint.c | 11 ++++++++++- 3 files changed, 13 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index ad04dfcd6f35..33ed9d605f91 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -1296,6 +1296,7 @@ extern int perf_swevent_get_recursion_context(void); extern void perf_swevent_put_recursion_context(int rctx); extern void perf_event_enable(struct perf_event *event); extern void perf_event_disable(struct perf_event *event); +extern int __perf_event_disable(void *info); extern void perf_event_task_tick(void); #else static inline void @@ -1334,6 +1335,7 @@ static inline int perf_swevent_get_recursion_context(void) { return -1; } static inline void perf_swevent_put_recursion_context(int rctx) { } static inline void perf_event_enable(struct perf_event *event) { } static inline void perf_event_disable(struct perf_event *event) { } +static inline int __perf_event_disable(void *info) { return -1; } static inline void perf_event_task_tick(void) { } #endif diff --git a/kernel/events/core.c b/kernel/events/core.c index efef4282b8e8..7fee567153f0 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -1253,7 +1253,7 @@ retry: /* * Cross CPU call to disable a performance event */ -static int __perf_event_disable(void *info) +int __perf_event_disable(void *info) { struct perf_event *event = info; struct perf_event_context *ctx = event->ctx; diff --git a/kernel/events/hw_breakpoint.c b/kernel/events/hw_breakpoint.c index bb38c4d3ee12..9a7b487c6fe2 100644 --- a/kernel/events/hw_breakpoint.c +++ b/kernel/events/hw_breakpoint.c @@ -453,7 +453,16 @@ int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *att int old_type = bp->attr.bp_type; int err = 0; - perf_event_disable(bp); + /* + * modify_user_hw_breakpoint can be invoked with IRQs disabled and hence it + * will not be possible to raise IPIs that invoke __perf_event_disable. + * So call the function directly after making sure we are targeting the + * current task. + */ + if (irqs_disabled() && bp->ctx && bp->ctx->task == current) + __perf_event_disable(bp); + else + perf_event_disable(bp); bp->attr.bp_addr = attr->bp_addr; bp->attr.bp_type = attr->bp_type; -- cgit v1.2.3 From 3550ccdb9d8d350e526b809bf3dd92b550a74fe1 Mon Sep 17 00:00:00 2001 From: Ian Chen Date: Wed, 29 Aug 2012 15:05:36 +0900 Subject: mmc: card: Skip secure erase on MoviNAND; causes unrecoverable corruption. For several MoviNAND eMMC parts, there are known issues with secure erase and secure trim. For these specific MoviNAND devices, we skip these operations. Specifically, there is a bug in the eMMC firmware that causes unrecoverable corruption when the MMC is erased with MMC_CAP_ERASE enabled. References: http://forum.xda-developers.com/showthread.php?t=1644364 https://plus.google.com/111398485184813224730/posts/21pTYfTsCkB#111398485184813224730/posts/21pTYfTsCkB Signed-off-by: Ian Chen Reviewed-by: Namjae Jeon Acked-by: Jaehoon Chung Reviewed-by: Linus Walleij Cc: stable [3.0+] Signed-off-by: Chris Ball --- drivers/mmc/card/block.c | 26 +++++++++++++++++++++++++- include/linux/mmc/card.h | 1 + 2 files changed, 26 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index f1c84decb192..172a768036d8 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1411,7 +1411,8 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) /* complete ongoing async transfer before issuing discard */ if (card->host->areq) mmc_blk_issue_rw_rq(mq, NULL); - if (req->cmd_flags & REQ_SECURE) + if (req->cmd_flags & REQ_SECURE && + !(card->quirks & MMC_QUIRK_SEC_ERASE_TRIM_BROKEN)) ret = mmc_blk_issue_secdiscard_rq(mq, req); else ret = mmc_blk_issue_discard_rq(mq, req); @@ -1716,6 +1717,7 @@ force_ro_fail: #define CID_MANFID_SANDISK 0x2 #define CID_MANFID_TOSHIBA 0x11 #define CID_MANFID_MICRON 0x13 +#define CID_MANFID_SAMSUNG 0x15 static const struct mmc_fixup blk_fixups[] = { @@ -1752,6 +1754,28 @@ static const struct mmc_fixup blk_fixups[] = MMC_FIXUP(CID_NAME_ANY, CID_MANFID_MICRON, 0x200, add_quirk_mmc, MMC_QUIRK_LONG_READ_TIME), + /* + * On these Samsung MoviNAND parts, performing secure erase or + * secure trim can result in unrecoverable corruption due to a + * firmware bug. + */ + MMC_FIXUP("M8G2FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), + MMC_FIXUP("MAG4FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), + MMC_FIXUP("MBG8FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), + MMC_FIXUP("MCGAFA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), + MMC_FIXUP("VAL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), + MMC_FIXUP("VYL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), + MMC_FIXUP("KYL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), + MMC_FIXUP("VZL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), + END_FIXUP }; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 111aca5e97f3..4b27f9f503e4 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -239,6 +239,7 @@ struct mmc_card { #define MMC_QUIRK_BLK_NO_CMD23 (1<<7) /* Avoid CMD23 for regular multiblock */ #define MMC_QUIRK_BROKEN_BYTE_MODE_512 (1<<8) /* Avoid sending 512 bytes in */ #define MMC_QUIRK_LONG_READ_TIME (1<<9) /* Data read time > CSD says */ +#define MMC_QUIRK_SEC_ERASE_TRIM_BROKEN (1<<10) /* Skip secure for erase/trim */ /* byte mode */ unsigned int poweroff_notify_state; /* eMMC4.5 notify feature */ #define MMC_NO_POWER_NOTIFICATION 0 -- cgit v1.2.3 From 3b59df46a449ec9975146d71318c4777ad086744 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Tue, 4 Sep 2012 00:03:29 +0000 Subject: xfrm: Workaround incompatibility of ESN and async crypto ESN for esp is defined in RFC 4303. This RFC assumes that the sequence number counters are always up to date. However, this is not true if an async crypto algorithm is employed. If the sequence number counters are not up to date on sequence number check, we may incorrectly update the upper 32 bit of the sequence number. This leads to a DOS. We workaround this by comparing the upper sequence number, (used for authentication) with the upper sequence number computed after the async processing. We drop the packet if these numbers are different. To do this, we introduce a recheck function that does this check in the ESN case. Signed-off-by: Steffen Klassert Acked-by: Herbert Xu Signed-off-by: David S. Miller --- include/net/xfrm.h | 3 +++ net/xfrm/xfrm_input.c | 2 +- net/xfrm/xfrm_replay.c | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 976a81abe1a2..639dd1316d37 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -273,6 +273,9 @@ struct xfrm_replay { int (*check)(struct xfrm_state *x, struct sk_buff *skb, __be32 net_seq); + int (*recheck)(struct xfrm_state *x, + struct sk_buff *skb, + __be32 net_seq); void (*notify)(struct xfrm_state *x, int event); int (*overflow)(struct xfrm_state *x, struct sk_buff *skb); }; diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index 54a0dc2e2f8d..ab2bb42fe094 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c @@ -212,7 +212,7 @@ resume: /* only the first xfrm gets the encap type */ encap_type = 0; - if (async && x->repl->check(x, skb, seq)) { + if (async && x->repl->recheck(x, skb, seq)) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATESEQERROR); goto drop_unlock; } diff --git a/net/xfrm/xfrm_replay.c b/net/xfrm/xfrm_replay.c index 2f6d11d04a2b..3efb07d3eb27 100644 --- a/net/xfrm/xfrm_replay.c +++ b/net/xfrm/xfrm_replay.c @@ -420,6 +420,18 @@ err: return -EINVAL; } +static int xfrm_replay_recheck_esn(struct xfrm_state *x, + struct sk_buff *skb, __be32 net_seq) +{ + if (unlikely(XFRM_SKB_CB(skb)->seq.input.hi != + htonl(xfrm_replay_seqhi(x, net_seq)))) { + x->stats.replay_window++; + return -EINVAL; + } + + return xfrm_replay_check_esn(x, skb, net_seq); +} + static void xfrm_replay_advance_esn(struct xfrm_state *x, __be32 net_seq) { unsigned int bitnr, nr, i; @@ -479,6 +491,7 @@ static void xfrm_replay_advance_esn(struct xfrm_state *x, __be32 net_seq) static struct xfrm_replay xfrm_replay_legacy = { .advance = xfrm_replay_advance, .check = xfrm_replay_check, + .recheck = xfrm_replay_check, .notify = xfrm_replay_notify, .overflow = xfrm_replay_overflow, }; @@ -486,6 +499,7 @@ static struct xfrm_replay xfrm_replay_legacy = { static struct xfrm_replay xfrm_replay_bmp = { .advance = xfrm_replay_advance_bmp, .check = xfrm_replay_check_bmp, + .recheck = xfrm_replay_check_bmp, .notify = xfrm_replay_notify_bmp, .overflow = xfrm_replay_overflow_bmp, }; @@ -493,6 +507,7 @@ static struct xfrm_replay xfrm_replay_bmp = { static struct xfrm_replay xfrm_replay_esn = { .advance = xfrm_replay_advance_esn, .check = xfrm_replay_check_esn, + .recheck = xfrm_replay_recheck_esn, .notify = xfrm_replay_notify_bmp, .overflow = xfrm_replay_overflow_esn, }; -- cgit v1.2.3 From c3f52af3e03013db5237e339c817beaae5ec9e3a Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 3 Sep 2012 14:56:02 -0400 Subject: NFS: Fix the initialisation of the readdir 'cookieverf' array When the NFS_COOKIEVERF helper macro was converted into a static inline function in commit 99fadcd764 (nfs: convert NFS_*(inode) helpers to static inline), we broke the initialisation of the readdir cookies, since that depended on doing a memset with an argument of 'sizeof(NFS_COOKIEVERF(inode))' which therefore changed from sizeof(be32 cookieverf[2]) to sizeof(be32 *). At this point, NFS_COOKIEVERF seems to be more of an obfuscation than a helper, so the best thing would be to just get rid of it. Also see: https://bugzilla.kernel.org/show_bug.cgi?id=46881 Reported-by: Andi Kleen Reported-by: David Binderman Signed-off-by: Trond Myklebust Cc: stable@vger.kernel.org --- fs/nfs/inode.c | 2 +- fs/nfs/nfs3proc.c | 2 +- fs/nfs/nfs4proc.c | 4 ++-- include/linux/nfs_fs.h | 5 ----- 4 files changed, 4 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index c6e895f0fbf3..9b47610338f5 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -154,7 +154,7 @@ static void nfs_zap_caches_locked(struct inode *inode) nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); nfsi->attrtimeo_timestamp = jiffies; - memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode))); + memset(NFS_I(inode)->cookieverf, 0, sizeof(NFS_I(inode)->cookieverf)); if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE; else diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index d6b3b5f2d779..69322096c325 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -643,7 +643,7 @@ nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, u64 cookie, struct page **pages, unsigned int count, int plus) { struct inode *dir = dentry->d_inode; - __be32 *verf = NFS_COOKIEVERF(dir); + __be32 *verf = NFS_I(dir)->cookieverf; struct nfs3_readdirargs arg = { .fh = NFS_FH(dir), .cookie = cookie, diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 635274140b18..86b4c7361036 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3215,11 +3215,11 @@ static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, dentry->d_parent->d_name.name, dentry->d_name.name, (unsigned long long)cookie); - nfs4_setup_readdir(cookie, NFS_COOKIEVERF(dir), dentry, &args); + nfs4_setup_readdir(cookie, NFS_I(dir)->cookieverf, dentry, &args); res.pgbase = args.pgbase; status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &msg, &args.seq_args, &res.seq_res, 0); if (status >= 0) { - memcpy(NFS_COOKIEVERF(dir), res.verifier.data, NFS4_VERIFIER_SIZE); + memcpy(NFS_I(dir)->cookieverf, res.verifier.data, NFS4_VERIFIER_SIZE); status += args.pgbase; } diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 1f8fc7f9bcd8..4b03f56e280e 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -265,11 +265,6 @@ static inline const struct nfs_rpc_ops *NFS_PROTO(const struct inode *inode) return NFS_SERVER(inode)->nfs_client->rpc_ops; } -static inline __be32 *NFS_COOKIEVERF(const struct inode *inode) -{ - return NFS_I(inode)->cookieverf; -} - static inline unsigned NFS_MINATTRTIMEO(const struct inode *inode) { struct nfs_server *nfss = NFS_SERVER(inode); -- cgit v1.2.3 From 849e0ad257d259b8443c63d74e3bcc32ebf336af Mon Sep 17 00:00:00 2001 From: Jens Taprogge Date: Tue, 4 Sep 2012 17:01:13 +0200 Subject: Staging: ipack: implement ipack device table. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The modaliases look like ipack:fXvNdM, where X is the format version (8 bit) and N and M are the vendor and device ID represented as 32 bit hexadecimal numbers each. Using 32 bits allows us to define IPACK_ANY_ID as (~0) without interfering with the valid ids. The resulting modalias string for ipoctal.ko looks like this (once ipoctal provides a device table): alias: ipack:f01v000000F0d00000048* alias: ipack:f01v000000F0d0000002A* alias: ipack:f01v000000F0d00000022* (output from modinfo) Signed-off-by: Jens Taprogge Signed-off-by: Samuel Iglesias Gonsálvez Signed-off-by: Greg Kroah-Hartman --- drivers/staging/ipack/ipack.h | 26 ++++++++++++++++++++++++++ drivers/staging/ipack/ipack_ids.h | 27 +++++++++++++++++++++++++++ include/linux/mod_devicetable.h | 7 +++++++ scripts/mod/file2alias.c | 15 +++++++++++++++ 4 files changed, 75 insertions(+) create mode 100644 drivers/staging/ipack/ipack_ids.h (limited to 'include') diff --git a/drivers/staging/ipack/ipack.h b/drivers/staging/ipack/ipack.h index e3609b16368b..703142d1859b 100644 --- a/drivers/staging/ipack/ipack.h +++ b/drivers/staging/ipack/ipack.h @@ -9,6 +9,7 @@ * Software Foundation; version 2 of the License. */ +#include #include #define IPACK_IDPROM_OFFSET_I 0x01 @@ -95,6 +96,7 @@ struct ipack_driver_ops { */ struct ipack_driver { struct device_driver driver; + const struct ipack_device_id *id_table; struct ipack_driver_ops *ops; }; @@ -169,3 +171,27 @@ void ipack_driver_unregister(struct ipack_driver *edrv); */ struct ipack_device *ipack_device_register(struct ipack_bus_device *bus, int slot, int irqv); void ipack_device_unregister(struct ipack_device *dev); + +/** + * DEFINE_IPACK_DEVICE_TABLE - macro used to describe a IndustryPack table + * @_table: device table name + * + * This macro is used to create a struct ipack_device_id array (a device table) + * in a generic manner. + */ +#define DEFINE_IPACK_DEVICE_TABLE(_table) \ + const struct ipack_device_id _table[] __devinitconst + +/** + * IPACK_DEVICE - macro used to describe a specific IndustryPack device + * @_format: the format version (currently either 1 or 2, 8 bit value) + * @vend: the 8 or 24 bit IndustryPack Vendor ID + * @dev: the 8 or 16 bit IndustryPack Device ID + * + * This macro is used to create a struct ipack_device_id that matches a specific + * device. + */ +#define IPACK_DEVICE(_format, vend, dev) \ + .format = (_format), \ + .vendor = (vend), \ + .device = (dev) diff --git a/drivers/staging/ipack/ipack_ids.h b/drivers/staging/ipack/ipack_ids.h new file mode 100644 index 000000000000..ba85ec53b2c3 --- /dev/null +++ b/drivers/staging/ipack/ipack_ids.h @@ -0,0 +1,27 @@ +/* + * IndustryPack Fromat, Vendor and Device IDs. + */ + +/* ID section format versions */ +#define IPACK_ID_VERSION_INVALID 0x00 +#define IPACK_ID_VERSION_1 0x01 +#define IPACK_ID_VERSION_2 0x02 + +/* Vendors and devices. Sort key: vendor first, device next. */ +#define IPACK1_VENDOR_ID_RESERVED1 0x00 +#define IPACK1_VENDOR_ID_RESERVED2 0xFF +#define IPACK1_VENDOR_ID_UNREGISTRED01 0x01 +#define IPACK1_VENDOR_ID_UNREGISTRED02 0x02 +#define IPACK1_VENDOR_ID_UNREGISTRED03 0x03 +#define IPACK1_VENDOR_ID_UNREGISTRED04 0x04 +#define IPACK1_VENDOR_ID_UNREGISTRED05 0x05 +#define IPACK1_VENDOR_ID_UNREGISTRED06 0x06 +#define IPACK1_VENDOR_ID_UNREGISTRED07 0x07 +#define IPACK1_VENDOR_ID_UNREGISTRED08 0x08 +#define IPACK1_VENDOR_ID_UNREGISTRED09 0x09 +#define IPACK1_VENDOR_ID_UNREGISTRED10 0x0A +#define IPACK1_VENDOR_ID_UNREGISTRED11 0x0B +#define IPACK1_VENDOR_ID_UNREGISTRED12 0x0C +#define IPACK1_VENDOR_ID_UNREGISTRED13 0x0D +#define IPACK1_VENDOR_ID_UNREGISTRED14 0x0E +#define IPACK1_VENDOR_ID_UNREGISTRED15 0x0F diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 6955045199b0..999c4c25fbf7 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -600,4 +600,11 @@ struct x86_cpu_id { #define X86_MODEL_ANY 0 #define X86_FEATURE_ANY 0 /* Same as FPU, you can't test for that */ +#define IPACK_ANY_ID (~0) +struct ipack_device_id { + __u8 format; /* Format version or IPACK_ANY_ID */ + __u32 vendor; /* Vendor ID or IPACK_ANY_ID */ + __u32 device; /* Device ID or IPACK_ANY_ID */ +}; + #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 7ed6864ef65b..3c22bda8fdf3 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -966,6 +966,21 @@ static int do_isapnp_entry(const char *filename, } ADD_TO_DEVTABLE("isapnp", struct isapnp_device_id, do_isapnp_entry); +/* Looks like: "ipack:fNvNdN". */ +static int do_ipack_entry(const char *filename, + struct ipack_device_id *id, char *alias) +{ + id->vendor = TO_NATIVE(id->vendor); + id->device = TO_NATIVE(id->device); + strcpy(alias, "ipack:"); + ADD(alias, "f", id->format != IPACK_ANY_ID, id->format); + ADD(alias, "v", id->vendor != IPACK_ANY_ID, id->vendor); + ADD(alias, "d", id->device != IPACK_ANY_ID, id->device); + add_wildcard(alias); + return 1; +} +ADD_TO_DEVTABLE("ipack", struct ipack_device_id, do_ipack_entry); + /* * Append a match expression for a single masked hex digit. * outp points to a pointer to the character at which to append. -- cgit v1.2.3 From a85442ade272121927a56e02f7dfde1127482df2 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 4 Sep 2012 20:27:38 -0700 Subject: Input: tegra - move platform data header Move the Tegra KBC platform data header out of arch/arm/mach-tegra, as a pre-requisite of single zImage. Signed-off-by: Stephen Warren Signed-off-by: Dmitry Torokhov --- arch/arm/mach-tegra/include/mach/kbc.h | 62 ---------------------------------- drivers/input/keyboard/tegra-kbc.c | 2 +- include/linux/input/tegra_kbc.h | 62 ++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 63 deletions(-) delete mode 100644 arch/arm/mach-tegra/include/mach/kbc.h create mode 100644 include/linux/input/tegra_kbc.h (limited to 'include') diff --git a/arch/arm/mach-tegra/include/mach/kbc.h b/arch/arm/mach-tegra/include/mach/kbc.h deleted file mode 100644 index a13025612939..000000000000 --- a/arch/arm/mach-tegra/include/mach/kbc.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Platform definitions for tegra-kbc keyboard input driver - * - * Copyright (c) 2010-2011, NVIDIA Corporation. - * - * 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 ASMARM_ARCH_TEGRA_KBC_H -#define ASMARM_ARCH_TEGRA_KBC_H - -#include -#include - -#define KBC_MAX_GPIO 24 -#define KBC_MAX_KPENT 8 - -#define KBC_MAX_ROW 16 -#define KBC_MAX_COL 8 -#define KBC_MAX_KEY (KBC_MAX_ROW * KBC_MAX_COL) - -enum tegra_pin_type { - PIN_CFG_IGNORE, - PIN_CFG_COL, - PIN_CFG_ROW, -}; - -struct tegra_kbc_pin_cfg { - enum tegra_pin_type type; - unsigned char num; -}; - -struct tegra_kbc_wake_key { - u8 row:4; - u8 col:4; -}; - -struct tegra_kbc_platform_data { - unsigned int debounce_cnt; - unsigned int repeat_cnt; - - struct tegra_kbc_pin_cfg pin_cfg[KBC_MAX_GPIO]; - const struct matrix_keymap_data *keymap_data; - - u32 wakeup_key; - bool wakeup; - bool use_fn_map; - bool use_ghost_filter; -}; -#endif diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index 4ffe64d53107..c7ae23ead575 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -29,8 +29,8 @@ #include #include #include +#include #include -#include #define KBC_MAX_DEBOUNCE_CNT 0x3ffu diff --git a/include/linux/input/tegra_kbc.h b/include/linux/input/tegra_kbc.h new file mode 100644 index 000000000000..a13025612939 --- /dev/null +++ b/include/linux/input/tegra_kbc.h @@ -0,0 +1,62 @@ +/* + * Platform definitions for tegra-kbc keyboard input driver + * + * Copyright (c) 2010-2011, NVIDIA Corporation. + * + * 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 ASMARM_ARCH_TEGRA_KBC_H +#define ASMARM_ARCH_TEGRA_KBC_H + +#include +#include + +#define KBC_MAX_GPIO 24 +#define KBC_MAX_KPENT 8 + +#define KBC_MAX_ROW 16 +#define KBC_MAX_COL 8 +#define KBC_MAX_KEY (KBC_MAX_ROW * KBC_MAX_COL) + +enum tegra_pin_type { + PIN_CFG_IGNORE, + PIN_CFG_COL, + PIN_CFG_ROW, +}; + +struct tegra_kbc_pin_cfg { + enum tegra_pin_type type; + unsigned char num; +}; + +struct tegra_kbc_wake_key { + u8 row:4; + u8 col:4; +}; + +struct tegra_kbc_platform_data { + unsigned int debounce_cnt; + unsigned int repeat_cnt; + + struct tegra_kbc_pin_cfg pin_cfg[KBC_MAX_GPIO]; + const struct matrix_keymap_data *keymap_data; + + u32 wakeup_key; + bool wakeup; + bool use_fn_map; + bool use_ghost_filter; +}; +#endif -- cgit v1.2.3 From de4217d90fed1b1714a270ceb5d092f7314e8bda Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Tue, 4 Sep 2012 14:25:58 +0530 Subject: usb: otg: Move phy interface to separate file. As otg.h is containing lots of phy interface related stuff, moving all phy interface related stuff to new file named phy.h Signed-off-by: Venu Byravarasu Signed-off-by: Felipe Balbi --- include/linux/usb/otg.h | 198 +-------------------------------------------- include/linux/usb/phy.h | 208 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+), 197 deletions(-) create mode 100644 include/linux/usb/phy.h (limited to 'include') diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 16dc520dfbad..65a2b6afe020 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -9,7 +9,7 @@ #ifndef __LINUX_USB_OTG_H #define __LINUX_USB_OTG_H -#include +#include /* OTG defines lots of enumeration states before device reset */ enum usb_otg_state { @@ -35,31 +35,6 @@ enum usb_otg_state { OTG_STATE_A_VBUS_ERR, }; -enum usb_phy_events { - USB_EVENT_NONE, /* no events or cable disconnected */ - USB_EVENT_VBUS, /* vbus valid event */ - USB_EVENT_ID, /* id was grounded */ - USB_EVENT_CHARGER, /* usb dedicated charger */ - USB_EVENT_ENUMERATED, /* gadget driver enumerated */ -}; - -/* associate a type with PHY */ -enum usb_phy_type { - USB_PHY_TYPE_UNDEFINED, - USB_PHY_TYPE_USB2, - USB_PHY_TYPE_USB3, -}; - -struct usb_phy; - -/* for transceivers connected thru an ULPI interface, the user must - * provide access ops - */ -struct usb_phy_io_ops { - int (*read)(struct usb_phy *x, u32 reg); - int (*write)(struct usb_phy *x, u32 val, u32 reg); -}; - struct usb_otg { u8 default_a; @@ -85,120 +60,9 @@ struct usb_otg { }; -/* - * the otg driver needs to interact with both device side and host side - * usb controllers. it decides which controller is active at a given - * moment, using the transceiver, ID signal, HNP and sometimes static - * configuration information (including "board isn't wired for otg"). - */ -struct usb_phy { - struct device *dev; - const char *label; - unsigned int flags; - - enum usb_phy_type type; - enum usb_otg_state state; - enum usb_phy_events last_event; - - struct usb_otg *otg; - - struct device *io_dev; - struct usb_phy_io_ops *io_ops; - void __iomem *io_priv; - - /* for notification of usb_phy_events */ - struct atomic_notifier_head notifier; - - /* to pass extra port status to the root hub */ - u16 port_status; - u16 port_change; - - /* to support controllers that have multiple transceivers */ - struct list_head head; - - /* initialize/shutdown the OTG controller */ - int (*init)(struct usb_phy *x); - void (*shutdown)(struct usb_phy *x); - - /* effective for B devices, ignored for A-peripheral */ - int (*set_power)(struct usb_phy *x, - unsigned mA); - - /* for non-OTG B devices: set transceiver into suspend mode */ - int (*set_suspend)(struct usb_phy *x, - int suspend); - - /* notify phy connect status change */ - int (*notify_connect)(struct usb_phy *x, int port); - int (*notify_disconnect)(struct usb_phy *x, int port); -}; - - -/* for board-specific init logic */ -extern int usb_add_phy(struct usb_phy *, enum usb_phy_type type); -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) - return x->io_ops->read(x, reg); - - return -EINVAL; -} - -static inline int usb_phy_io_write(struct usb_phy *x, u32 val, u32 reg) -{ - if (x->io_ops && x->io_ops->write) - return x->io_ops->write(x, val, reg); - - return -EINVAL; -} - -static inline int -usb_phy_init(struct usb_phy *x) -{ - if (x->init) - return x->init(x); - - return 0; -} - -static inline void -usb_phy_shutdown(struct usb_phy *x) -{ - if (x->shutdown) - x->shutdown(x); -} - -/* for usb host and peripheral controller drivers */ #ifdef CONFIG_USB_OTG_UTILS -extern struct usb_phy *usb_get_phy(enum usb_phy_type type); -extern struct usb_phy *devm_usb_get_phy(struct device *dev, - enum usb_phy_type type); -extern void usb_put_phy(struct usb_phy *); -extern void devm_usb_put_phy(struct device *dev, struct usb_phy *x); extern const char *otg_state_string(enum usb_otg_state state); #else -static inline struct usb_phy *usb_get_phy(enum usb_phy_type type) -{ - return NULL; -} - -static inline struct usb_phy *devm_usb_get_phy(struct device *dev, - enum usb_phy_type type) -{ - return NULL; -} - -static inline void usb_put_phy(struct usb_phy *x) -{ -} - -static inline void devm_usb_put_phy(struct device *dev, struct usb_phy *x) -{ -} - static inline const char *otg_state_string(enum usb_otg_state state) { return NULL; @@ -247,42 +111,6 @@ otg_set_peripheral(struct usb_otg *otg, struct usb_gadget *periph) return -ENOTSUPP; } -static inline int -usb_phy_set_power(struct usb_phy *x, unsigned mA) -{ - if (x && x->set_power) - return x->set_power(x, mA); - return 0; -} - -/* Context: can sleep */ -static inline int -usb_phy_set_suspend(struct usb_phy *x, int suspend) -{ - if (x->set_suspend != NULL) - return x->set_suspend(x, suspend); - else - return 0; -} - -static inline int -usb_phy_notify_connect(struct usb_phy *x, int port) -{ - if (x->notify_connect) - return x->notify_connect(x, port); - else - return 0; -} - -static inline int -usb_phy_notify_disconnect(struct usb_phy *x, int port) -{ - if (x->notify_disconnect) - return x->notify_disconnect(x, port); - else - return 0; -} - static inline int otg_start_srp(struct usb_otg *otg) { @@ -292,31 +120,7 @@ otg_start_srp(struct usb_otg *otg) return -ENOTSUPP; } -/* notifiers */ -static inline int -usb_register_notifier(struct usb_phy *x, struct notifier_block *nb) -{ - return atomic_notifier_chain_register(&x->notifier, nb); -} - -static inline void -usb_unregister_notifier(struct usb_phy *x, struct notifier_block *nb) -{ - atomic_notifier_chain_unregister(&x->notifier, nb); -} - /* for OTG controller drivers (and maybe other stuff) */ extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num); -static inline const char *usb_phy_type_string(enum usb_phy_type type) -{ - switch (type) { - case USB_PHY_TYPE_USB2: - return "USB2 PHY"; - case USB_PHY_TYPE_USB3: - return "USB3 PHY"; - default: - return "UNKNOWN PHY TYPE"; - } -} #endif /* __LINUX_USB_OTG_H */ diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h new file mode 100644 index 000000000000..88fc16062e77 --- /dev/null +++ b/include/linux/usb/phy.h @@ -0,0 +1,208 @@ +/* USB OTG (On The Go) defines */ +/* + * + * These APIs may be used between USB controllers. USB device drivers + * (for either host or peripheral roles) don't use these calls; they + * continue to use just usb_device and usb_gadget. + */ + +#ifndef __LINUX_USB_PHY_H +#define __LINUX_USB_PHY_H + +#include + +enum usb_phy_events { + USB_EVENT_NONE, /* no events or cable disconnected */ + USB_EVENT_VBUS, /* vbus valid event */ + USB_EVENT_ID, /* id was grounded */ + USB_EVENT_CHARGER, /* usb dedicated charger */ + USB_EVENT_ENUMERATED, /* gadget driver enumerated */ +}; + +/* associate a type with PHY */ +enum usb_phy_type { + USB_PHY_TYPE_UNDEFINED, + USB_PHY_TYPE_USB2, + USB_PHY_TYPE_USB3, +}; + +struct usb_phy; +struct usb_otg; + +/* for transceivers connected thru an ULPI interface, the user must + * provide access ops + */ +struct usb_phy_io_ops { + int (*read)(struct usb_phy *x, u32 reg); + int (*write)(struct usb_phy *x, u32 val, u32 reg); +}; + +struct usb_phy { + struct device *dev; + const char *label; + unsigned int flags; + + enum usb_phy_type type; + enum usb_phy_events last_event; + + struct usb_otg *otg; + + struct device *io_dev; + struct usb_phy_io_ops *io_ops; + void __iomem *io_priv; + + /* for notification of usb_phy_events */ + struct atomic_notifier_head notifier; + + /* to pass extra port status to the root hub */ + u16 port_status; + u16 port_change; + + /* to support controllers that have multiple transceivers */ + struct list_head head; + + /* initialize/shutdown the OTG controller */ + int (*init)(struct usb_phy *x); + void (*shutdown)(struct usb_phy *x); + + /* effective for B devices, ignored for A-peripheral */ + int (*set_power)(struct usb_phy *x, + unsigned mA); + + /* for non-OTG B devices: set transceiver into suspend mode */ + int (*set_suspend)(struct usb_phy *x, + int suspend); + + /* notify phy connect status change */ + int (*notify_connect)(struct usb_phy *x, int port); + int (*notify_disconnect)(struct usb_phy *x, int port); +}; + + +/* for board-specific init logic */ +extern int usb_add_phy(struct usb_phy *, enum usb_phy_type type); +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) + return x->io_ops->read(x, reg); + + return -EINVAL; +} + +static inline int usb_phy_io_write(struct usb_phy *x, u32 val, u32 reg) +{ + if (x->io_ops && x->io_ops->write) + return x->io_ops->write(x, val, reg); + + return -EINVAL; +} + +static inline int +usb_phy_init(struct usb_phy *x) +{ + if (x->init) + return x->init(x); + + return 0; +} + +static inline void +usb_phy_shutdown(struct usb_phy *x) +{ + if (x->shutdown) + x->shutdown(x); +} + +/* for usb host and peripheral controller drivers */ +#ifdef CONFIG_USB_OTG_UTILS +extern struct usb_phy *usb_get_phy(enum usb_phy_type type); +extern struct usb_phy *devm_usb_get_phy(struct device *dev, + enum usb_phy_type type); +extern void usb_put_phy(struct usb_phy *); +extern void devm_usb_put_phy(struct device *dev, struct usb_phy *x); +#else +static inline struct usb_phy *usb_get_phy(enum usb_phy_type type) +{ + return NULL; +} + +static inline struct usb_phy *devm_usb_get_phy(struct device *dev, + enum usb_phy_type type) +{ + return NULL; +} + +static inline void usb_put_phy(struct usb_phy *x) +{ +} + +static inline void devm_usb_put_phy(struct device *dev, struct usb_phy *x) +{ +} + +#endif + +static inline int +usb_phy_set_power(struct usb_phy *x, unsigned mA) +{ + if (x && x->set_power) + return x->set_power(x, mA); + return 0; +} + +/* Context: can sleep */ +static inline int +usb_phy_set_suspend(struct usb_phy *x, int suspend) +{ + if (x->set_suspend != NULL) + return x->set_suspend(x, suspend); + else + return 0; +} + +static inline int +usb_phy_notify_connect(struct usb_phy *x, int port) +{ + if (x->notify_connect) + return x->notify_connect(x, port); + else + return 0; +} + +static inline int +usb_phy_notify_disconnect(struct usb_phy *x, int port) +{ + if (x->notify_disconnect) + return x->notify_disconnect(x, port); + else + return 0; +} + +/* notifiers */ +static inline int +usb_register_notifier(struct usb_phy *x, struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&x->notifier, nb); +} + +static inline void +usb_unregister_notifier(struct usb_phy *x, struct notifier_block *nb) +{ + atomic_notifier_chain_unregister(&x->notifier, nb); +} + +static inline const char *usb_phy_type_string(enum usb_phy_type type) +{ + switch (type) { + case USB_PHY_TYPE_USB2: + return "USB2 PHY"; + case USB_PHY_TYPE_USB3: + return "USB3 PHY"; + default: + return "UNKNOWN PHY TYPE"; + } +} +#endif /* __LINUX_USB_PHY_H */ -- cgit v1.2.3 From 1ba8216f0bc02af6ba70d1108d60eb1b064395e4 Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Wed, 5 Sep 2012 18:50:23 +0530 Subject: usb: move phy driver from mach-tegra to drivers/usb As part of this patch: 1. Moved existing tegra phy driver to drivers/USB directory. 2. Added standard USB phy driver APIs to tegra phy driver. Signed-off-by: Venu Byravarasu Tested-by: Stephen Warren Signed-off-by: Felipe Balbi --- arch/arm/mach-tegra/Makefile | 1 - arch/arm/mach-tegra/devices.c | 2 +- arch/arm/mach-tegra/devices.h | 2 +- arch/arm/mach-tegra/include/mach/usb_phy.h | 86 --- arch/arm/mach-tegra/usb_phy.c | 817 ---------------------------- drivers/usb/host/ehci-tegra.c | 14 +- drivers/usb/phy/Makefile | 1 + drivers/usb/phy/tegra_usb_phy.c | 839 +++++++++++++++++++++++++++++ include/linux/usb/tegra_usb_phy.h | 80 +++ 9 files changed, 930 insertions(+), 912 deletions(-) delete mode 100644 arch/arm/mach-tegra/include/mach/usb_phy.h delete mode 100644 arch/arm/mach-tegra/usb_phy.c create mode 100644 drivers/usb/phy/tegra_usb_phy.c create mode 100644 include/linux/usb/tegra_usb_phy.h (limited to 'include') diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index c3d7303b9ac8..0e82b7f34fc1 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -21,7 +21,6 @@ obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o obj-$(CONFIG_TEGRA_SYSTEM_DMA) += dma.o obj-$(CONFIG_CPU_FREQ) += cpu-tegra.o obj-$(CONFIG_TEGRA_PCI) += pcie.o -obj-$(CONFIG_USB_SUPPORT) += usb_phy.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += board-dt-tegra20.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += board-dt-tegra30.o diff --git a/arch/arm/mach-tegra/devices.c b/arch/arm/mach-tegra/devices.c index c70e65ffa36b..d8ab76276397 100644 --- a/arch/arm/mach-tegra/devices.c +++ b/arch/arm/mach-tegra/devices.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include "gpio-names.h" #include "devices.h" diff --git a/arch/arm/mach-tegra/devices.h b/arch/arm/mach-tegra/devices.h index 4f5052726495..906a61f340c8 100644 --- a/arch/arm/mach-tegra/devices.h +++ b/arch/arm/mach-tegra/devices.h @@ -22,7 +22,7 @@ #include #include -#include +#include extern struct tegra_ulpi_config tegra_ehci2_ulpi_phy_config; diff --git a/arch/arm/mach-tegra/include/mach/usb_phy.h b/arch/arm/mach-tegra/include/mach/usb_phy.h deleted file mode 100644 index 935ce9f65590..000000000000 --- a/arch/arm/mach-tegra/include/mach/usb_phy.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * arch/arm/mach-tegra/include/mach/usb_phy.h - * - * 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 __MACH_USB_PHY_H -#define __MACH_USB_PHY_H - -#include -#include - -struct tegra_utmip_config { - u8 hssync_start_delay; - u8 elastic_limit; - u8 idle_wait_delay; - u8 term_range_adj; - u8 xcvr_setup; - u8 xcvr_lsfslew; - 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, - TEGRA_USB_PHY_PORT_SPEED_HIGH, -}; - -enum tegra_usb_phy_mode { - TEGRA_USB_PHY_MODE_DEVICE, - TEGRA_USB_PHY_MODE_HOST, -}; - -struct tegra_xtal_freq; - -struct tegra_usb_phy { - int instance; - const struct tegra_xtal_freq *freq; - void __iomem *regs; - void __iomem *pad_regs; - struct clk *clk; - struct clk *pll_u; - struct clk *pad_clk; - enum tegra_usb_phy_mode mode; - void *config; - struct usb_phy *ulpi; -}; - -struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance, - void __iomem *regs, void *config, enum tegra_usb_phy_mode phy_mode); - -int tegra_usb_phy_power_on(struct tegra_usb_phy *phy); - -void tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy); - -void tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy); - -void tegra_usb_phy_power_off(struct tegra_usb_phy *phy); - -void tegra_usb_phy_preresume(struct tegra_usb_phy *phy); - -void tegra_usb_phy_postresume(struct tegra_usb_phy *phy); - -void tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy, - enum tegra_usb_phy_port_speed port_speed); - -void tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy); - -void tegra_usb_phy_close(struct tegra_usb_phy *phy); - -#endif /* __MACH_USB_PHY_H */ diff --git a/arch/arm/mach-tegra/usb_phy.c b/arch/arm/mach-tegra/usb_phy.c deleted file mode 100644 index 022b33a05c3a..000000000000 --- a/arch/arm/mach-tegra/usb_phy.c +++ /dev/null @@ -1,817 +0,0 @@ -/* - * arch/arm/mach-tegra/usb_phy.c - * - * Copyright (C) 2010 Google, Inc. - * - * Author: - * Erik Gilling - * Benoit Goby - * - * 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. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define ULPI_VIEWPORT 0x170 - -#define USB_PORTSC1 0x184 -#define USB_PORTSC1_PTS(x) (((x) & 0x3) << 30) -#define USB_PORTSC1_PSPD(x) (((x) & 0x3) << 26) -#define USB_PORTSC1_PHCD (1 << 23) -#define USB_PORTSC1_WKOC (1 << 22) -#define USB_PORTSC1_WKDS (1 << 21) -#define USB_PORTSC1_WKCN (1 << 20) -#define USB_PORTSC1_PTC(x) (((x) & 0xf) << 16) -#define USB_PORTSC1_PP (1 << 12) -#define USB_PORTSC1_SUSP (1 << 7) -#define USB_PORTSC1_PE (1 << 2) -#define USB_PORTSC1_CCS (1 << 0) - -#define USB_SUSP_CTRL 0x400 -#define USB_WAKE_ON_CNNT_EN_DEV (1 << 3) -#define USB_WAKE_ON_DISCON_EN_DEV (1 << 4) -#define USB_SUSP_CLR (1 << 5) -#define USB_PHY_CLK_VALID (1 << 7) -#define UTMIP_RESET (1 << 11) -#define UHSIC_RESET (1 << 11) -#define UTMIP_PHY_ENABLE (1 << 12) -#define ULPI_PHY_ENABLE (1 << 13) -#define USB_SUSP_SET (1 << 14) -#define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16) - -#define USB1_LEGACY_CTRL 0x410 -#define USB1_NO_LEGACY_MODE (1 << 0) -#define USB1_VBUS_SENSE_CTL_MASK (3 << 1) -#define USB1_VBUS_SENSE_CTL_VBUS_WAKEUP (0 << 1) -#define USB1_VBUS_SENSE_CTL_AB_SESS_VLD_OR_VBUS_WAKEUP \ - (1 << 1) -#define USB1_VBUS_SENSE_CTL_AB_SESS_VLD (2 << 1) -#define USB1_VBUS_SENSE_CTL_A_SESS_VLD (3 << 1) - -#define ULPI_TIMING_CTRL_0 0x424 -#define ULPI_OUTPUT_PINMUX_BYP (1 << 10) -#define ULPI_CLKOUT_PINMUX_BYP (1 << 11) - -#define ULPI_TIMING_CTRL_1 0x428 -#define ULPI_DATA_TRIMMER_LOAD (1 << 0) -#define ULPI_DATA_TRIMMER_SEL(x) (((x) & 0x7) << 1) -#define ULPI_STPDIRNXT_TRIMMER_LOAD (1 << 16) -#define ULPI_STPDIRNXT_TRIMMER_SEL(x) (((x) & 0x7) << 17) -#define ULPI_DIR_TRIMMER_LOAD (1 << 24) -#define ULPI_DIR_TRIMMER_SEL(x) (((x) & 0x7) << 25) - -#define UTMIP_PLL_CFG1 0x804 -#define UTMIP_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0) -#define UTMIP_PLLU_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 27) - -#define UTMIP_XCVR_CFG0 0x808 -#define UTMIP_XCVR_SETUP(x) (((x) & 0xf) << 0) -#define UTMIP_XCVR_LSRSLEW(x) (((x) & 0x3) << 8) -#define UTMIP_XCVR_LSFSLEW(x) (((x) & 0x3) << 10) -#define UTMIP_FORCE_PD_POWERDOWN (1 << 14) -#define UTMIP_FORCE_PD2_POWERDOWN (1 << 16) -#define UTMIP_FORCE_PDZI_POWERDOWN (1 << 18) -#define UTMIP_XCVR_HSSLEW_MSB(x) (((x) & 0x7f) << 25) - -#define UTMIP_BIAS_CFG0 0x80c -#define UTMIP_OTGPD (1 << 11) -#define UTMIP_BIASPD (1 << 10) - -#define UTMIP_HSRX_CFG0 0x810 -#define UTMIP_ELASTIC_LIMIT(x) (((x) & 0x1f) << 10) -#define UTMIP_IDLE_WAIT(x) (((x) & 0x1f) << 15) - -#define UTMIP_HSRX_CFG1 0x814 -#define UTMIP_HS_SYNC_START_DLY(x) (((x) & 0x1f) << 1) - -#define UTMIP_TX_CFG0 0x820 -#define UTMIP_FS_PREABMLE_J (1 << 19) -#define UTMIP_HS_DISCON_DISABLE (1 << 8) - -#define UTMIP_MISC_CFG0 0x824 -#define UTMIP_DPDM_OBSERVE (1 << 26) -#define UTMIP_DPDM_OBSERVE_SEL(x) (((x) & 0xf) << 27) -#define UTMIP_DPDM_OBSERVE_SEL_FS_J UTMIP_DPDM_OBSERVE_SEL(0xf) -#define UTMIP_DPDM_OBSERVE_SEL_FS_K UTMIP_DPDM_OBSERVE_SEL(0xe) -#define UTMIP_DPDM_OBSERVE_SEL_FS_SE1 UTMIP_DPDM_OBSERVE_SEL(0xd) -#define UTMIP_DPDM_OBSERVE_SEL_FS_SE0 UTMIP_DPDM_OBSERVE_SEL(0xc) -#define UTMIP_SUSPEND_EXIT_ON_EDGE (1 << 22) - -#define UTMIP_MISC_CFG1 0x828 -#define UTMIP_PLL_ACTIVE_DLY_COUNT(x) (((x) & 0x1f) << 18) -#define UTMIP_PLLU_STABLE_COUNT(x) (((x) & 0xfff) << 6) - -#define UTMIP_DEBOUNCE_CFG0 0x82c -#define UTMIP_BIAS_DEBOUNCE_A(x) (((x) & 0xffff) << 0) - -#define UTMIP_BAT_CHRG_CFG0 0x830 -#define UTMIP_PD_CHRG (1 << 0) - -#define UTMIP_SPARE_CFG0 0x834 -#define FUSE_SETUP_SEL (1 << 3) - -#define UTMIP_XCVR_CFG1 0x838 -#define UTMIP_FORCE_PDDISC_POWERDOWN (1 << 0) -#define UTMIP_FORCE_PDCHRP_POWERDOWN (1 << 2) -#define UTMIP_FORCE_PDDR_POWERDOWN (1 << 4) -#define UTMIP_XCVR_TERM_RANGE_ADJ(x) (((x) & 0xf) << 18) - -#define UTMIP_BIAS_CFG1 0x83c -#define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3) - -static DEFINE_SPINLOCK(utmip_pad_lock); -static int utmip_pad_count; - -struct tegra_xtal_freq { - int freq; - u8 enable_delay; - u8 stable_count; - u8 active_delay; - u8 xtal_freq_count; - u16 debounce; -}; - -static const struct tegra_xtal_freq tegra_freq_table[] = { - { - .freq = 12000000, - .enable_delay = 0x02, - .stable_count = 0x2F, - .active_delay = 0x04, - .xtal_freq_count = 0x76, - .debounce = 0x7530, - }, - { - .freq = 13000000, - .enable_delay = 0x02, - .stable_count = 0x33, - .active_delay = 0x05, - .xtal_freq_count = 0x7F, - .debounce = 0x7EF4, - }, - { - .freq = 19200000, - .enable_delay = 0x03, - .stable_count = 0x4B, - .active_delay = 0x06, - .xtal_freq_count = 0xBB, - .debounce = 0xBB80, - }, - { - .freq = 26000000, - .enable_delay = 0x04, - .stable_count = 0x66, - .active_delay = 0x09, - .xtal_freq_count = 0xFE, - .debounce = 0xFDE8, - }, -}; - -static struct tegra_utmip_config utmip_default[] = { - [0] = { - .hssync_start_delay = 9, - .idle_wait_delay = 17, - .elastic_limit = 16, - .term_range_adj = 6, - .xcvr_setup = 9, - .xcvr_lsfslew = 1, - .xcvr_lsrslew = 1, - }, - [2] = { - .hssync_start_delay = 9, - .idle_wait_delay = 17, - .elastic_limit = 16, - .term_range_adj = 6, - .xcvr_setup = 9, - .xcvr_lsfslew = 2, - .xcvr_lsrslew = 2, - }, -}; - -static inline bool phy_is_ulpi(struct tegra_usb_phy *phy) -{ - return (phy->instance == 1); -} - -static int utmip_pad_open(struct tegra_usb_phy *phy) -{ - phy->pad_clk = clk_get_sys("utmip-pad", NULL); - if (IS_ERR(phy->pad_clk)) { - pr_err("%s: can't get utmip pad clock\n", __func__); - return PTR_ERR(phy->pad_clk); - } - - if (phy->instance == 0) { - phy->pad_regs = phy->regs; - } else { - phy->pad_regs = ioremap(TEGRA_USB_BASE, TEGRA_USB_SIZE); - if (!phy->pad_regs) { - pr_err("%s: can't remap usb registers\n", __func__); - clk_put(phy->pad_clk); - return -ENOMEM; - } - } - return 0; -} - -static void utmip_pad_close(struct tegra_usb_phy *phy) -{ - if (phy->instance != 0) - iounmap(phy->pad_regs); - clk_put(phy->pad_clk); -} - -static void utmip_pad_power_on(struct tegra_usb_phy *phy) -{ - unsigned long val, flags; - void __iomem *base = phy->pad_regs; - - clk_prepare_enable(phy->pad_clk); - - spin_lock_irqsave(&utmip_pad_lock, flags); - - if (utmip_pad_count++ == 0) { - val = readl(base + UTMIP_BIAS_CFG0); - val &= ~(UTMIP_OTGPD | UTMIP_BIASPD); - writel(val, base + UTMIP_BIAS_CFG0); - } - - spin_unlock_irqrestore(&utmip_pad_lock, flags); - - clk_disable_unprepare(phy->pad_clk); -} - -static int utmip_pad_power_off(struct tegra_usb_phy *phy) -{ - unsigned long val, flags; - void __iomem *base = phy->pad_regs; - - if (!utmip_pad_count) { - pr_err("%s: utmip pad already powered off\n", __func__); - return -EINVAL; - } - - clk_prepare_enable(phy->pad_clk); - - spin_lock_irqsave(&utmip_pad_lock, flags); - - if (--utmip_pad_count == 0) { - val = readl(base + UTMIP_BIAS_CFG0); - val |= UTMIP_OTGPD | UTMIP_BIASPD; - writel(val, base + UTMIP_BIAS_CFG0); - } - - spin_unlock_irqrestore(&utmip_pad_lock, flags); - - clk_disable_unprepare(phy->pad_clk); - - return 0; -} - -static int utmi_wait_register(void __iomem *reg, u32 mask, u32 result) -{ - unsigned long timeout = 2000; - do { - if ((readl(reg) & mask) == result) - return 0; - udelay(1); - timeout--; - } while (timeout); - return -1; -} - -static void utmi_phy_clk_disable(struct tegra_usb_phy *phy) -{ - unsigned long val; - void __iomem *base = phy->regs; - - if (phy->instance == 0) { - val = readl(base + USB_SUSP_CTRL); - val |= USB_SUSP_SET; - writel(val, base + USB_SUSP_CTRL); - - udelay(10); - - val = readl(base + USB_SUSP_CTRL); - val &= ~USB_SUSP_SET; - writel(val, base + USB_SUSP_CTRL); - } - - if (phy->instance == 2) { - val = readl(base + USB_PORTSC1); - val |= USB_PORTSC1_PHCD; - writel(val, base + USB_PORTSC1); - } - - if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) < 0) - pr_err("%s: timeout waiting for phy to stabilize\n", __func__); -} - -static void utmi_phy_clk_enable(struct tegra_usb_phy *phy) -{ - unsigned long val; - void __iomem *base = phy->regs; - - if (phy->instance == 0) { - val = readl(base + USB_SUSP_CTRL); - val |= USB_SUSP_CLR; - writel(val, base + USB_SUSP_CTRL); - - udelay(10); - - val = readl(base + USB_SUSP_CTRL); - val &= ~USB_SUSP_CLR; - writel(val, base + USB_SUSP_CTRL); - } - - if (phy->instance == 2) { - val = readl(base + USB_PORTSC1); - val &= ~USB_PORTSC1_PHCD; - writel(val, base + USB_PORTSC1); - } - - if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, - USB_PHY_CLK_VALID)) - pr_err("%s: timeout waiting for phy to stabilize\n", __func__); -} - -static int utmi_phy_power_on(struct tegra_usb_phy *phy) -{ - unsigned long val; - void __iomem *base = phy->regs; - struct tegra_utmip_config *config = phy->config; - - val = readl(base + USB_SUSP_CTRL); - val |= UTMIP_RESET; - writel(val, base + USB_SUSP_CTRL); - - if (phy->instance == 0) { - val = readl(base + USB1_LEGACY_CTRL); - val |= USB1_NO_LEGACY_MODE; - writel(val, base + USB1_LEGACY_CTRL); - } - - val = readl(base + UTMIP_TX_CFG0); - val &= ~UTMIP_FS_PREABMLE_J; - writel(val, base + UTMIP_TX_CFG0); - - val = readl(base + UTMIP_HSRX_CFG0); - val &= ~(UTMIP_IDLE_WAIT(~0) | UTMIP_ELASTIC_LIMIT(~0)); - val |= UTMIP_IDLE_WAIT(config->idle_wait_delay); - val |= UTMIP_ELASTIC_LIMIT(config->elastic_limit); - writel(val, base + UTMIP_HSRX_CFG0); - - val = readl(base + UTMIP_HSRX_CFG1); - val &= ~UTMIP_HS_SYNC_START_DLY(~0); - val |= UTMIP_HS_SYNC_START_DLY(config->hssync_start_delay); - writel(val, base + UTMIP_HSRX_CFG1); - - val = readl(base + UTMIP_DEBOUNCE_CFG0); - val &= ~UTMIP_BIAS_DEBOUNCE_A(~0); - val |= UTMIP_BIAS_DEBOUNCE_A(phy->freq->debounce); - writel(val, base + UTMIP_DEBOUNCE_CFG0); - - val = readl(base + UTMIP_MISC_CFG0); - 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->mode == TEGRA_USB_PHY_MODE_DEVICE) { - 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); - } - - utmip_pad_power_on(phy); - - val = readl(base + UTMIP_XCVR_CFG0); - val &= ~(UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN | - UTMIP_FORCE_PDZI_POWERDOWN | UTMIP_XCVR_SETUP(~0) | - UTMIP_XCVR_LSFSLEW(~0) | UTMIP_XCVR_LSRSLEW(~0) | - UTMIP_XCVR_HSSLEW_MSB(~0)); - val |= UTMIP_XCVR_SETUP(config->xcvr_setup); - val |= UTMIP_XCVR_LSFSLEW(config->xcvr_lsfslew); - val |= UTMIP_XCVR_LSRSLEW(config->xcvr_lsrslew); - writel(val, base + UTMIP_XCVR_CFG0); - - val = readl(base + UTMIP_XCVR_CFG1); - val &= ~(UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN | - UTMIP_FORCE_PDDR_POWERDOWN | UTMIP_XCVR_TERM_RANGE_ADJ(~0)); - val |= UTMIP_XCVR_TERM_RANGE_ADJ(config->term_range_adj); - writel(val, base + UTMIP_XCVR_CFG1); - - val = readl(base + UTMIP_BAT_CHRG_CFG0); - val &= ~UTMIP_PD_CHRG; - writel(val, base + UTMIP_BAT_CHRG_CFG0); - - val = readl(base + UTMIP_BIAS_CFG1); - val &= ~UTMIP_BIAS_PDTRK_COUNT(~0); - val |= UTMIP_BIAS_PDTRK_COUNT(0x5); - writel(val, base + UTMIP_BIAS_CFG1); - - if (phy->instance == 0) { - val = readl(base + UTMIP_SPARE_CFG0); - if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) - val &= ~FUSE_SETUP_SEL; - else - val |= FUSE_SETUP_SEL; - writel(val, base + UTMIP_SPARE_CFG0); - } - - if (phy->instance == 2) { - val = readl(base + USB_SUSP_CTRL); - val |= UTMIP_PHY_ENABLE; - writel(val, base + USB_SUSP_CTRL); - } - - val = readl(base + USB_SUSP_CTRL); - val &= ~UTMIP_RESET; - writel(val, base + USB_SUSP_CTRL); - - if (phy->instance == 0) { - val = readl(base + USB1_LEGACY_CTRL); - val &= ~USB1_VBUS_SENSE_CTL_MASK; - val |= USB1_VBUS_SENSE_CTL_A_SESS_VLD; - writel(val, base + USB1_LEGACY_CTRL); - - val = readl(base + USB_SUSP_CTRL); - val &= ~USB_SUSP_SET; - writel(val, base + USB_SUSP_CTRL); - } - - utmi_phy_clk_enable(phy); - - if (phy->instance == 2) { - val = readl(base + USB_PORTSC1); - val &= ~USB_PORTSC1_PTS(~0); - writel(val, base + USB_PORTSC1); - } - - return 0; -} - -static void utmi_phy_power_off(struct tegra_usb_phy *phy) -{ - unsigned long val; - void __iomem *base = phy->regs; - - utmi_phy_clk_disable(phy); - - if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) { - val = readl(base + USB_SUSP_CTRL); - val &= ~USB_WAKEUP_DEBOUNCE_COUNT(~0); - val |= USB_WAKE_ON_CNNT_EN_DEV | USB_WAKEUP_DEBOUNCE_COUNT(5); - writel(val, base + USB_SUSP_CTRL); - } - - val = readl(base + USB_SUSP_CTRL); - val |= UTMIP_RESET; - writel(val, base + USB_SUSP_CTRL); - - val = readl(base + UTMIP_BAT_CHRG_CFG0); - val |= UTMIP_PD_CHRG; - writel(val, base + UTMIP_BAT_CHRG_CFG0); - - val = readl(base + UTMIP_XCVR_CFG0); - val |= UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN | - UTMIP_FORCE_PDZI_POWERDOWN; - writel(val, base + UTMIP_XCVR_CFG0); - - val = readl(base + UTMIP_XCVR_CFG1); - val |= UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN | - UTMIP_FORCE_PDDR_POWERDOWN; - writel(val, base + UTMIP_XCVR_CFG1); - - utmip_pad_power_off(phy); -} - -static void utmi_phy_preresume(struct tegra_usb_phy *phy) -{ - unsigned long val; - void __iomem *base = phy->regs; - - val = readl(base + UTMIP_TX_CFG0); - val |= UTMIP_HS_DISCON_DISABLE; - writel(val, base + UTMIP_TX_CFG0); -} - -static void utmi_phy_postresume(struct tegra_usb_phy *phy) -{ - unsigned long val; - void __iomem *base = phy->regs; - - val = readl(base + UTMIP_TX_CFG0); - val &= ~UTMIP_HS_DISCON_DISABLE; - writel(val, base + UTMIP_TX_CFG0); -} - -static void utmi_phy_restore_start(struct tegra_usb_phy *phy, - enum tegra_usb_phy_port_speed port_speed) -{ - unsigned long val; - void __iomem *base = phy->regs; - - val = readl(base + UTMIP_MISC_CFG0); - val &= ~UTMIP_DPDM_OBSERVE_SEL(~0); - if (port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW) - val |= UTMIP_DPDM_OBSERVE_SEL_FS_K; - else - val |= UTMIP_DPDM_OBSERVE_SEL_FS_J; - writel(val, base + UTMIP_MISC_CFG0); - udelay(1); - - val = readl(base + UTMIP_MISC_CFG0); - val |= UTMIP_DPDM_OBSERVE; - writel(val, base + UTMIP_MISC_CFG0); - udelay(10); -} - -static void utmi_phy_restore_end(struct tegra_usb_phy *phy) -{ - unsigned long val; - void __iomem *base = phy->regs; - - val = readl(base + UTMIP_MISC_CFG0); - val &= ~UTMIP_DPDM_OBSERVE; - writel(val, base + UTMIP_MISC_CFG0); - udelay(10); -} - -static int ulpi_phy_power_on(struct tegra_usb_phy *phy) -{ - int ret; - unsigned long val; - void __iomem *base = phy->regs; - struct tegra_ulpi_config *config = phy->config; - - gpio_direction_output(config->reset_gpio, 0); - msleep(5); - gpio_direction_output(config->reset_gpio, 1); - - clk_prepare_enable(phy->clk); - msleep(1); - - val = readl(base + USB_SUSP_CTRL); - val |= UHSIC_RESET; - writel(val, base + USB_SUSP_CTRL); - - val = readl(base + ULPI_TIMING_CTRL_0); - val |= ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP; - writel(val, base + ULPI_TIMING_CTRL_0); - - val = readl(base + USB_SUSP_CTRL); - val |= ULPI_PHY_ENABLE; - writel(val, base + USB_SUSP_CTRL); - - val = 0; - writel(val, base + ULPI_TIMING_CTRL_1); - - val |= ULPI_DATA_TRIMMER_SEL(4); - val |= ULPI_STPDIRNXT_TRIMMER_SEL(4); - val |= ULPI_DIR_TRIMMER_SEL(4); - writel(val, base + ULPI_TIMING_CTRL_1); - udelay(10); - - val |= ULPI_DATA_TRIMMER_LOAD; - val |= ULPI_STPDIRNXT_TRIMMER_LOAD; - val |= ULPI_DIR_TRIMMER_LOAD; - writel(val, base + ULPI_TIMING_CTRL_1); - - /* Fix VbusInvalid due to floating VBUS */ - ret = usb_phy_io_write(phy->ulpi, 0x40, 0x08); - if (ret) { - pr_err("%s: ulpi write failed\n", __func__); - return ret; - } - - ret = usb_phy_io_write(phy->ulpi, 0x80, 0x0B); - if (ret) { - pr_err("%s: ulpi write failed\n", __func__); - return ret; - } - - val = readl(base + USB_PORTSC1); - val |= USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN; - writel(val, base + USB_PORTSC1); - - val = readl(base + USB_SUSP_CTRL); - val |= USB_SUSP_CLR; - writel(val, base + USB_SUSP_CTRL); - udelay(100); - - val = readl(base + USB_SUSP_CTRL); - val &= ~USB_SUSP_CLR; - writel(val, base + USB_SUSP_CTRL); - - return 0; -} - -static void ulpi_phy_power_off(struct tegra_usb_phy *phy) -{ - unsigned long val; - void __iomem *base = phy->regs; - struct tegra_ulpi_config *config = phy->config; - - /* Clear WKCN/WKDS/WKOC wake-on events that can cause the USB - * Controller to immediately bring the ULPI PHY out of low power - */ - val = readl(base + USB_PORTSC1); - val &= ~(USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN); - writel(val, base + USB_PORTSC1); - - gpio_direction_output(config->reset_gpio, 0); - clk_disable(phy->clk); -} - -struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance, - void __iomem *regs, void *config, enum tegra_usb_phy_mode phy_mode) -{ - struct tegra_usb_phy *phy; - struct tegra_ulpi_config *ulpi_config; - unsigned long parent_rate; - int i; - int err; - - phy = kmalloc(sizeof(struct tegra_usb_phy), GFP_KERNEL); - if (!phy) - return ERR_PTR(-ENOMEM); - - phy->instance = instance; - phy->regs = regs; - phy->config = config; - phy->mode = phy_mode; - - if (!phy->config) { - if (phy_is_ulpi(phy)) { - pr_err("%s: ulpi phy configuration missing", __func__); - err = -EINVAL; - goto err0; - } else { - phy->config = &utmip_default[instance]; - } - } - - phy->pll_u = clk_get_sys(NULL, "pll_u"); - if (IS_ERR(phy->pll_u)) { - pr_err("Can't get pll_u clock\n"); - err = PTR_ERR(phy->pll_u); - goto err0; - } - clk_prepare_enable(phy->pll_u); - - parent_rate = clk_get_rate(clk_get_parent(phy->pll_u)); - for (i = 0; i < ARRAY_SIZE(tegra_freq_table); i++) { - if (tegra_freq_table[i].freq == parent_rate) { - phy->freq = &tegra_freq_table[i]; - break; - } - } - if (!phy->freq) { - pr_err("invalid pll_u parent rate %ld\n", parent_rate); - err = -EINVAL; - goto err1; - } - - if (phy_is_ulpi(phy)) { - ulpi_config = config; - phy->clk = clk_get_sys(NULL, ulpi_config->clk); - if (IS_ERR(phy->clk)) { - pr_err("%s: can't get ulpi clock\n", __func__); - err = -ENXIO; - goto err1; - } - if (!gpio_is_valid(ulpi_config->reset_gpio)) - ulpi_config->reset_gpio = - of_get_named_gpio(dev->of_node, - "nvidia,phy-reset-gpio", 0); - if (!gpio_is_valid(ulpi_config->reset_gpio)) { - pr_err("%s: invalid reset gpio: %d\n", __func__, - ulpi_config->reset_gpio); - err = -EINVAL; - goto err1; - } - gpio_request(ulpi_config->reset_gpio, "ulpi_phy_reset_b"); - gpio_direction_output(ulpi_config->reset_gpio, 0); - phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0); - phy->ulpi->io_priv = regs + ULPI_VIEWPORT; - } else { - err = utmip_pad_open(phy); - if (err < 0) - goto err1; - } - - return phy; - -err1: - clk_disable_unprepare(phy->pll_u); - clk_put(phy->pll_u); -err0: - kfree(phy); - return ERR_PTR(err); -} -EXPORT_SYMBOL_GPL(tegra_usb_phy_open); - -int tegra_usb_phy_power_on(struct tegra_usb_phy *phy) -{ - if (phy_is_ulpi(phy)) - return ulpi_phy_power_on(phy); - else - return utmi_phy_power_on(phy); -} -EXPORT_SYMBOL_GPL(tegra_usb_phy_power_on); - -void tegra_usb_phy_power_off(struct tegra_usb_phy *phy) -{ - if (phy_is_ulpi(phy)) - ulpi_phy_power_off(phy); - else - utmi_phy_power_off(phy); -} -EXPORT_SYMBOL_GPL(tegra_usb_phy_power_off); - -void tegra_usb_phy_preresume(struct tegra_usb_phy *phy) -{ - if (!phy_is_ulpi(phy)) - utmi_phy_preresume(phy); -} -EXPORT_SYMBOL_GPL(tegra_usb_phy_preresume); - -void tegra_usb_phy_postresume(struct tegra_usb_phy *phy) -{ - if (!phy_is_ulpi(phy)) - utmi_phy_postresume(phy); -} -EXPORT_SYMBOL_GPL(tegra_usb_phy_postresume); - -void tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy, - enum tegra_usb_phy_port_speed port_speed) -{ - if (!phy_is_ulpi(phy)) - utmi_phy_restore_start(phy, port_speed); -} -EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_start); - -void tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy) -{ - if (!phy_is_ulpi(phy)) - utmi_phy_restore_end(phy); -} -EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_end); - -void tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy) -{ - if (!phy_is_ulpi(phy)) - utmi_phy_clk_disable(phy); -} -EXPORT_SYMBOL_GPL(tegra_usb_phy_clk_disable); - -void tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy) -{ - if (!phy_is_ulpi(phy)) - utmi_phy_clk_enable(phy); -} -EXPORT_SYMBOL_GPL(tegra_usb_phy_clk_enable); - -void tegra_usb_phy_close(struct tegra_usb_phy *phy) -{ - if (phy_is_ulpi(phy)) - clk_put(phy->clk); - else - utmip_pad_close(phy); - clk_disable_unprepare(phy->pll_u); - clk_put(phy->pll_u); - kfree(phy); -} -EXPORT_SYMBOL_GPL(tegra_usb_phy_close); diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 950e95efa381..65408f7ae92b 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -27,7 +27,7 @@ #include #include -#include +#include #include #define TEGRA_USB_DMA_ALIGN 32 @@ -49,7 +49,7 @@ static void tegra_ehci_power_up(struct usb_hcd *hcd) clk_prepare_enable(tegra->emc_clk); clk_prepare_enable(tegra->clk); - tegra_usb_phy_power_on(tegra->phy); + usb_phy_set_suspend(&tegra->phy->u_phy, 0); tegra->host_resumed = 1; } @@ -58,7 +58,7 @@ static void tegra_ehci_power_down(struct usb_hcd *hcd) struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); tegra->host_resumed = 0; - tegra_usb_phy_power_off(tegra->phy); + usb_phy_set_suspend(&tegra->phy->u_phy, 1); clk_disable_unprepare(tegra->clk); clk_disable_unprepare(tegra->emc_clk); } @@ -715,7 +715,9 @@ static int tegra_ehci_probe(struct platform_device *pdev) goto fail_phy; } - err = tegra_usb_phy_power_on(tegra->phy); + usb_phy_init(&tegra->phy->u_phy); + + err = usb_phy_set_suspend(&tegra->phy->u_phy, 0); if (err) { dev_err(&pdev->dev, "Failed to power on the phy\n"); goto fail; @@ -762,7 +764,7 @@ fail: usb_put_phy(tegra->transceiver); } #endif - tegra_usb_phy_close(tegra->phy); + usb_phy_shutdown(&tegra->phy->u_phy); fail_phy: iounmap(hcd->regs); fail_io: @@ -801,7 +803,7 @@ static int tegra_ehci_remove(struct platform_device *pdev) usb_remove_hcd(hcd); usb_put_hcd(hcd); - tegra_usb_phy_close(tegra->phy); + usb_phy_shutdown(&tegra->phy->u_phy); iounmap(hcd->regs); clk_disable_unprepare(tegra->clk); diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index cf38f08c818f..bb948fb8654c 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -6,3 +6,4 @@ ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG obj-$(CONFIG_USB_ISP1301) += isp1301.o obj-$(CONFIG_MV_U3D_PHY) += mv_u3d_phy.o +obj-$(CONFIG_USB_EHCI_TEGRA) += tegra_usb_phy.o diff --git a/drivers/usb/phy/tegra_usb_phy.c b/drivers/usb/phy/tegra_usb_phy.c new file mode 100644 index 000000000000..4739903245e8 --- /dev/null +++ b/drivers/usb/phy/tegra_usb_phy.c @@ -0,0 +1,839 @@ +/* + * Copyright (C) 2010 Google, Inc. + * + * Author: + * Erik Gilling + * Benoit Goby + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ULPI_VIEWPORT 0x170 + +#define USB_PORTSC1 0x184 +#define USB_PORTSC1_PTS(x) (((x) & 0x3) << 30) +#define USB_PORTSC1_PSPD(x) (((x) & 0x3) << 26) +#define USB_PORTSC1_PHCD (1 << 23) +#define USB_PORTSC1_WKOC (1 << 22) +#define USB_PORTSC1_WKDS (1 << 21) +#define USB_PORTSC1_WKCN (1 << 20) +#define USB_PORTSC1_PTC(x) (((x) & 0xf) << 16) +#define USB_PORTSC1_PP (1 << 12) +#define USB_PORTSC1_SUSP (1 << 7) +#define USB_PORTSC1_PE (1 << 2) +#define USB_PORTSC1_CCS (1 << 0) + +#define USB_SUSP_CTRL 0x400 +#define USB_WAKE_ON_CNNT_EN_DEV (1 << 3) +#define USB_WAKE_ON_DISCON_EN_DEV (1 << 4) +#define USB_SUSP_CLR (1 << 5) +#define USB_PHY_CLK_VALID (1 << 7) +#define UTMIP_RESET (1 << 11) +#define UHSIC_RESET (1 << 11) +#define UTMIP_PHY_ENABLE (1 << 12) +#define ULPI_PHY_ENABLE (1 << 13) +#define USB_SUSP_SET (1 << 14) +#define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16) + +#define USB1_LEGACY_CTRL 0x410 +#define USB1_NO_LEGACY_MODE (1 << 0) +#define USB1_VBUS_SENSE_CTL_MASK (3 << 1) +#define USB1_VBUS_SENSE_CTL_VBUS_WAKEUP (0 << 1) +#define USB1_VBUS_SENSE_CTL_AB_SESS_VLD_OR_VBUS_WAKEUP \ + (1 << 1) +#define USB1_VBUS_SENSE_CTL_AB_SESS_VLD (2 << 1) +#define USB1_VBUS_SENSE_CTL_A_SESS_VLD (3 << 1) + +#define ULPI_TIMING_CTRL_0 0x424 +#define ULPI_OUTPUT_PINMUX_BYP (1 << 10) +#define ULPI_CLKOUT_PINMUX_BYP (1 << 11) + +#define ULPI_TIMING_CTRL_1 0x428 +#define ULPI_DATA_TRIMMER_LOAD (1 << 0) +#define ULPI_DATA_TRIMMER_SEL(x) (((x) & 0x7) << 1) +#define ULPI_STPDIRNXT_TRIMMER_LOAD (1 << 16) +#define ULPI_STPDIRNXT_TRIMMER_SEL(x) (((x) & 0x7) << 17) +#define ULPI_DIR_TRIMMER_LOAD (1 << 24) +#define ULPI_DIR_TRIMMER_SEL(x) (((x) & 0x7) << 25) + +#define UTMIP_PLL_CFG1 0x804 +#define UTMIP_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0) +#define UTMIP_PLLU_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 27) + +#define UTMIP_XCVR_CFG0 0x808 +#define UTMIP_XCVR_SETUP(x) (((x) & 0xf) << 0) +#define UTMIP_XCVR_LSRSLEW(x) (((x) & 0x3) << 8) +#define UTMIP_XCVR_LSFSLEW(x) (((x) & 0x3) << 10) +#define UTMIP_FORCE_PD_POWERDOWN (1 << 14) +#define UTMIP_FORCE_PD2_POWERDOWN (1 << 16) +#define UTMIP_FORCE_PDZI_POWERDOWN (1 << 18) +#define UTMIP_XCVR_HSSLEW_MSB(x) (((x) & 0x7f) << 25) + +#define UTMIP_BIAS_CFG0 0x80c +#define UTMIP_OTGPD (1 << 11) +#define UTMIP_BIASPD (1 << 10) + +#define UTMIP_HSRX_CFG0 0x810 +#define UTMIP_ELASTIC_LIMIT(x) (((x) & 0x1f) << 10) +#define UTMIP_IDLE_WAIT(x) (((x) & 0x1f) << 15) + +#define UTMIP_HSRX_CFG1 0x814 +#define UTMIP_HS_SYNC_START_DLY(x) (((x) & 0x1f) << 1) + +#define UTMIP_TX_CFG0 0x820 +#define UTMIP_FS_PREABMLE_J (1 << 19) +#define UTMIP_HS_DISCON_DISABLE (1 << 8) + +#define UTMIP_MISC_CFG0 0x824 +#define UTMIP_DPDM_OBSERVE (1 << 26) +#define UTMIP_DPDM_OBSERVE_SEL(x) (((x) & 0xf) << 27) +#define UTMIP_DPDM_OBSERVE_SEL_FS_J UTMIP_DPDM_OBSERVE_SEL(0xf) +#define UTMIP_DPDM_OBSERVE_SEL_FS_K UTMIP_DPDM_OBSERVE_SEL(0xe) +#define UTMIP_DPDM_OBSERVE_SEL_FS_SE1 UTMIP_DPDM_OBSERVE_SEL(0xd) +#define UTMIP_DPDM_OBSERVE_SEL_FS_SE0 UTMIP_DPDM_OBSERVE_SEL(0xc) +#define UTMIP_SUSPEND_EXIT_ON_EDGE (1 << 22) + +#define UTMIP_MISC_CFG1 0x828 +#define UTMIP_PLL_ACTIVE_DLY_COUNT(x) (((x) & 0x1f) << 18) +#define UTMIP_PLLU_STABLE_COUNT(x) (((x) & 0xfff) << 6) + +#define UTMIP_DEBOUNCE_CFG0 0x82c +#define UTMIP_BIAS_DEBOUNCE_A(x) (((x) & 0xffff) << 0) + +#define UTMIP_BAT_CHRG_CFG0 0x830 +#define UTMIP_PD_CHRG (1 << 0) + +#define UTMIP_SPARE_CFG0 0x834 +#define FUSE_SETUP_SEL (1 << 3) + +#define UTMIP_XCVR_CFG1 0x838 +#define UTMIP_FORCE_PDDISC_POWERDOWN (1 << 0) +#define UTMIP_FORCE_PDCHRP_POWERDOWN (1 << 2) +#define UTMIP_FORCE_PDDR_POWERDOWN (1 << 4) +#define UTMIP_XCVR_TERM_RANGE_ADJ(x) (((x) & 0xf) << 18) + +#define UTMIP_BIAS_CFG1 0x83c +#define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3) + +static DEFINE_SPINLOCK(utmip_pad_lock); +static int utmip_pad_count; + +struct tegra_xtal_freq { + int freq; + u8 enable_delay; + u8 stable_count; + u8 active_delay; + u8 xtal_freq_count; + u16 debounce; +}; + +static const struct tegra_xtal_freq tegra_freq_table[] = { + { + .freq = 12000000, + .enable_delay = 0x02, + .stable_count = 0x2F, + .active_delay = 0x04, + .xtal_freq_count = 0x76, + .debounce = 0x7530, + }, + { + .freq = 13000000, + .enable_delay = 0x02, + .stable_count = 0x33, + .active_delay = 0x05, + .xtal_freq_count = 0x7F, + .debounce = 0x7EF4, + }, + { + .freq = 19200000, + .enable_delay = 0x03, + .stable_count = 0x4B, + .active_delay = 0x06, + .xtal_freq_count = 0xBB, + .debounce = 0xBB80, + }, + { + .freq = 26000000, + .enable_delay = 0x04, + .stable_count = 0x66, + .active_delay = 0x09, + .xtal_freq_count = 0xFE, + .debounce = 0xFDE8, + }, +}; + +static struct tegra_utmip_config utmip_default[] = { + [0] = { + .hssync_start_delay = 9, + .idle_wait_delay = 17, + .elastic_limit = 16, + .term_range_adj = 6, + .xcvr_setup = 9, + .xcvr_lsfslew = 1, + .xcvr_lsrslew = 1, + }, + [2] = { + .hssync_start_delay = 9, + .idle_wait_delay = 17, + .elastic_limit = 16, + .term_range_adj = 6, + .xcvr_setup = 9, + .xcvr_lsfslew = 2, + .xcvr_lsrslew = 2, + }, +}; + +static inline bool phy_is_ulpi(struct tegra_usb_phy *phy) +{ + return (phy->instance == 1); +} + +static int utmip_pad_open(struct tegra_usb_phy *phy) +{ + phy->pad_clk = clk_get_sys("utmip-pad", NULL); + if (IS_ERR(phy->pad_clk)) { + pr_err("%s: can't get utmip pad clock\n", __func__); + return PTR_ERR(phy->pad_clk); + } + + if (phy->instance == 0) { + phy->pad_regs = phy->regs; + } else { + phy->pad_regs = ioremap(TEGRA_USB_BASE, TEGRA_USB_SIZE); + if (!phy->pad_regs) { + pr_err("%s: can't remap usb registers\n", __func__); + clk_put(phy->pad_clk); + return -ENOMEM; + } + } + return 0; +} + +static void utmip_pad_close(struct tegra_usb_phy *phy) +{ + if (phy->instance != 0) + iounmap(phy->pad_regs); + clk_put(phy->pad_clk); +} + +static void utmip_pad_power_on(struct tegra_usb_phy *phy) +{ + unsigned long val, flags; + void __iomem *base = phy->pad_regs; + + clk_prepare_enable(phy->pad_clk); + + spin_lock_irqsave(&utmip_pad_lock, flags); + + if (utmip_pad_count++ == 0) { + val = readl(base + UTMIP_BIAS_CFG0); + val &= ~(UTMIP_OTGPD | UTMIP_BIASPD); + writel(val, base + UTMIP_BIAS_CFG0); + } + + spin_unlock_irqrestore(&utmip_pad_lock, flags); + + clk_disable_unprepare(phy->pad_clk); +} + +static int utmip_pad_power_off(struct tegra_usb_phy *phy) +{ + unsigned long val, flags; + void __iomem *base = phy->pad_regs; + + if (!utmip_pad_count) { + pr_err("%s: utmip pad already powered off\n", __func__); + return -EINVAL; + } + + clk_prepare_enable(phy->pad_clk); + + spin_lock_irqsave(&utmip_pad_lock, flags); + + if (--utmip_pad_count == 0) { + val = readl(base + UTMIP_BIAS_CFG0); + val |= UTMIP_OTGPD | UTMIP_BIASPD; + writel(val, base + UTMIP_BIAS_CFG0); + } + + spin_unlock_irqrestore(&utmip_pad_lock, flags); + + clk_disable_unprepare(phy->pad_clk); + + return 0; +} + +static int utmi_wait_register(void __iomem *reg, u32 mask, u32 result) +{ + unsigned long timeout = 2000; + do { + if ((readl(reg) & mask) == result) + return 0; + udelay(1); + timeout--; + } while (timeout); + return -1; +} + +static void utmi_phy_clk_disable(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + + if (phy->instance == 0) { + val = readl(base + USB_SUSP_CTRL); + val |= USB_SUSP_SET; + writel(val, base + USB_SUSP_CTRL); + + udelay(10); + + val = readl(base + USB_SUSP_CTRL); + val &= ~USB_SUSP_SET; + writel(val, base + USB_SUSP_CTRL); + } + + if (phy->instance == 2) { + val = readl(base + USB_PORTSC1); + val |= USB_PORTSC1_PHCD; + writel(val, base + USB_PORTSC1); + } + + if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) < 0) + pr_err("%s: timeout waiting for phy to stabilize\n", __func__); +} + +static void utmi_phy_clk_enable(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + + if (phy->instance == 0) { + val = readl(base + USB_SUSP_CTRL); + val |= USB_SUSP_CLR; + writel(val, base + USB_SUSP_CTRL); + + udelay(10); + + val = readl(base + USB_SUSP_CTRL); + val &= ~USB_SUSP_CLR; + writel(val, base + USB_SUSP_CTRL); + } + + if (phy->instance == 2) { + val = readl(base + USB_PORTSC1); + val &= ~USB_PORTSC1_PHCD; + writel(val, base + USB_PORTSC1); + } + + if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, + USB_PHY_CLK_VALID)) + pr_err("%s: timeout waiting for phy to stabilize\n", __func__); +} + +static int utmi_phy_power_on(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + struct tegra_utmip_config *config = phy->config; + + val = readl(base + USB_SUSP_CTRL); + val |= UTMIP_RESET; + writel(val, base + USB_SUSP_CTRL); + + if (phy->instance == 0) { + val = readl(base + USB1_LEGACY_CTRL); + val |= USB1_NO_LEGACY_MODE; + writel(val, base + USB1_LEGACY_CTRL); + } + + val = readl(base + UTMIP_TX_CFG0); + val &= ~UTMIP_FS_PREABMLE_J; + writel(val, base + UTMIP_TX_CFG0); + + val = readl(base + UTMIP_HSRX_CFG0); + val &= ~(UTMIP_IDLE_WAIT(~0) | UTMIP_ELASTIC_LIMIT(~0)); + val |= UTMIP_IDLE_WAIT(config->idle_wait_delay); + val |= UTMIP_ELASTIC_LIMIT(config->elastic_limit); + writel(val, base + UTMIP_HSRX_CFG0); + + val = readl(base + UTMIP_HSRX_CFG1); + val &= ~UTMIP_HS_SYNC_START_DLY(~0); + val |= UTMIP_HS_SYNC_START_DLY(config->hssync_start_delay); + writel(val, base + UTMIP_HSRX_CFG1); + + val = readl(base + UTMIP_DEBOUNCE_CFG0); + val &= ~UTMIP_BIAS_DEBOUNCE_A(~0); + val |= UTMIP_BIAS_DEBOUNCE_A(phy->freq->debounce); + writel(val, base + UTMIP_DEBOUNCE_CFG0); + + val = readl(base + UTMIP_MISC_CFG0); + 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->mode == TEGRA_USB_PHY_MODE_DEVICE) { + 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); + } + + utmip_pad_power_on(phy); + + val = readl(base + UTMIP_XCVR_CFG0); + val &= ~(UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN | + UTMIP_FORCE_PDZI_POWERDOWN | UTMIP_XCVR_SETUP(~0) | + UTMIP_XCVR_LSFSLEW(~0) | UTMIP_XCVR_LSRSLEW(~0) | + UTMIP_XCVR_HSSLEW_MSB(~0)); + val |= UTMIP_XCVR_SETUP(config->xcvr_setup); + val |= UTMIP_XCVR_LSFSLEW(config->xcvr_lsfslew); + val |= UTMIP_XCVR_LSRSLEW(config->xcvr_lsrslew); + writel(val, base + UTMIP_XCVR_CFG0); + + val = readl(base + UTMIP_XCVR_CFG1); + val &= ~(UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN | + UTMIP_FORCE_PDDR_POWERDOWN | UTMIP_XCVR_TERM_RANGE_ADJ(~0)); + val |= UTMIP_XCVR_TERM_RANGE_ADJ(config->term_range_adj); + writel(val, base + UTMIP_XCVR_CFG1); + + val = readl(base + UTMIP_BAT_CHRG_CFG0); + val &= ~UTMIP_PD_CHRG; + writel(val, base + UTMIP_BAT_CHRG_CFG0); + + val = readl(base + UTMIP_BIAS_CFG1); + val &= ~UTMIP_BIAS_PDTRK_COUNT(~0); + val |= UTMIP_BIAS_PDTRK_COUNT(0x5); + writel(val, base + UTMIP_BIAS_CFG1); + + if (phy->instance == 0) { + val = readl(base + UTMIP_SPARE_CFG0); + if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) + val &= ~FUSE_SETUP_SEL; + else + val |= FUSE_SETUP_SEL; + writel(val, base + UTMIP_SPARE_CFG0); + } + + if (phy->instance == 2) { + val = readl(base + USB_SUSP_CTRL); + val |= UTMIP_PHY_ENABLE; + writel(val, base + USB_SUSP_CTRL); + } + + val = readl(base + USB_SUSP_CTRL); + val &= ~UTMIP_RESET; + writel(val, base + USB_SUSP_CTRL); + + if (phy->instance == 0) { + val = readl(base + USB1_LEGACY_CTRL); + val &= ~USB1_VBUS_SENSE_CTL_MASK; + val |= USB1_VBUS_SENSE_CTL_A_SESS_VLD; + writel(val, base + USB1_LEGACY_CTRL); + + val = readl(base + USB_SUSP_CTRL); + val &= ~USB_SUSP_SET; + writel(val, base + USB_SUSP_CTRL); + } + + utmi_phy_clk_enable(phy); + + if (phy->instance == 2) { + val = readl(base + USB_PORTSC1); + val &= ~USB_PORTSC1_PTS(~0); + writel(val, base + USB_PORTSC1); + } + + return 0; +} + +static int utmi_phy_power_off(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + + utmi_phy_clk_disable(phy); + + if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) { + val = readl(base + USB_SUSP_CTRL); + val &= ~USB_WAKEUP_DEBOUNCE_COUNT(~0); + val |= USB_WAKE_ON_CNNT_EN_DEV | USB_WAKEUP_DEBOUNCE_COUNT(5); + writel(val, base + USB_SUSP_CTRL); + } + + val = readl(base + USB_SUSP_CTRL); + val |= UTMIP_RESET; + writel(val, base + USB_SUSP_CTRL); + + val = readl(base + UTMIP_BAT_CHRG_CFG0); + val |= UTMIP_PD_CHRG; + writel(val, base + UTMIP_BAT_CHRG_CFG0); + + val = readl(base + UTMIP_XCVR_CFG0); + val |= UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN | + UTMIP_FORCE_PDZI_POWERDOWN; + writel(val, base + UTMIP_XCVR_CFG0); + + val = readl(base + UTMIP_XCVR_CFG1); + val |= UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN | + UTMIP_FORCE_PDDR_POWERDOWN; + writel(val, base + UTMIP_XCVR_CFG1); + + return utmip_pad_power_off(phy); +} + +static void utmi_phy_preresume(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + + val = readl(base + UTMIP_TX_CFG0); + val |= UTMIP_HS_DISCON_DISABLE; + writel(val, base + UTMIP_TX_CFG0); +} + +static void utmi_phy_postresume(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + + val = readl(base + UTMIP_TX_CFG0); + val &= ~UTMIP_HS_DISCON_DISABLE; + writel(val, base + UTMIP_TX_CFG0); +} + +static void utmi_phy_restore_start(struct tegra_usb_phy *phy, + enum tegra_usb_phy_port_speed port_speed) +{ + unsigned long val; + void __iomem *base = phy->regs; + + val = readl(base + UTMIP_MISC_CFG0); + val &= ~UTMIP_DPDM_OBSERVE_SEL(~0); + if (port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW) + val |= UTMIP_DPDM_OBSERVE_SEL_FS_K; + else + val |= UTMIP_DPDM_OBSERVE_SEL_FS_J; + writel(val, base + UTMIP_MISC_CFG0); + udelay(1); + + val = readl(base + UTMIP_MISC_CFG0); + val |= UTMIP_DPDM_OBSERVE; + writel(val, base + UTMIP_MISC_CFG0); + udelay(10); +} + +static void utmi_phy_restore_end(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + + val = readl(base + UTMIP_MISC_CFG0); + val &= ~UTMIP_DPDM_OBSERVE; + writel(val, base + UTMIP_MISC_CFG0); + udelay(10); +} + +static int ulpi_phy_power_on(struct tegra_usb_phy *phy) +{ + int ret; + unsigned long val; + void __iomem *base = phy->regs; + struct tegra_ulpi_config *config = phy->config; + + gpio_direction_output(config->reset_gpio, 0); + msleep(5); + gpio_direction_output(config->reset_gpio, 1); + + clk_prepare_enable(phy->clk); + msleep(1); + + val = readl(base + USB_SUSP_CTRL); + val |= UHSIC_RESET; + writel(val, base + USB_SUSP_CTRL); + + val = readl(base + ULPI_TIMING_CTRL_0); + val |= ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP; + writel(val, base + ULPI_TIMING_CTRL_0); + + val = readl(base + USB_SUSP_CTRL); + val |= ULPI_PHY_ENABLE; + writel(val, base + USB_SUSP_CTRL); + + val = 0; + writel(val, base + ULPI_TIMING_CTRL_1); + + val |= ULPI_DATA_TRIMMER_SEL(4); + val |= ULPI_STPDIRNXT_TRIMMER_SEL(4); + val |= ULPI_DIR_TRIMMER_SEL(4); + writel(val, base + ULPI_TIMING_CTRL_1); + udelay(10); + + val |= ULPI_DATA_TRIMMER_LOAD; + val |= ULPI_STPDIRNXT_TRIMMER_LOAD; + val |= ULPI_DIR_TRIMMER_LOAD; + writel(val, base + ULPI_TIMING_CTRL_1); + + /* Fix VbusInvalid due to floating VBUS */ + ret = usb_phy_io_write(phy->ulpi, 0x40, 0x08); + if (ret) { + pr_err("%s: ulpi write failed\n", __func__); + return ret; + } + + ret = usb_phy_io_write(phy->ulpi, 0x80, 0x0B); + if (ret) { + pr_err("%s: ulpi write failed\n", __func__); + return ret; + } + + val = readl(base + USB_PORTSC1); + val |= USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN; + writel(val, base + USB_PORTSC1); + + val = readl(base + USB_SUSP_CTRL); + val |= USB_SUSP_CLR; + writel(val, base + USB_SUSP_CTRL); + udelay(100); + + val = readl(base + USB_SUSP_CTRL); + val &= ~USB_SUSP_CLR; + writel(val, base + USB_SUSP_CTRL); + + return 0; +} + +static int ulpi_phy_power_off(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + struct tegra_ulpi_config *config = phy->config; + + /* Clear WKCN/WKDS/WKOC wake-on events that can cause the USB + * Controller to immediately bring the ULPI PHY out of low power + */ + val = readl(base + USB_PORTSC1); + val &= ~(USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN); + writel(val, base + USB_PORTSC1); + + clk_disable(phy->clk); + return gpio_direction_output(config->reset_gpio, 0); +} + +static int tegra_phy_init(struct usb_phy *x) +{ + struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); + struct tegra_ulpi_config *ulpi_config; + int err; + + if (phy_is_ulpi(phy)) { + ulpi_config = phy->config; + phy->clk = clk_get_sys(NULL, ulpi_config->clk); + if (IS_ERR(phy->clk)) { + pr_err("%s: can't get ulpi clock\n", __func__); + err = -ENXIO; + goto err1; + } + if (!gpio_is_valid(ulpi_config->reset_gpio)) + ulpi_config->reset_gpio = + of_get_named_gpio(phy->dev->of_node, + "nvidia,phy-reset-gpio", 0); + if (!gpio_is_valid(ulpi_config->reset_gpio)) { + pr_err("%s: invalid reset gpio: %d\n", __func__, + ulpi_config->reset_gpio); + err = -EINVAL; + goto err1; + } + gpio_request(ulpi_config->reset_gpio, "ulpi_phy_reset_b"); + gpio_direction_output(ulpi_config->reset_gpio, 0); + phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0); + phy->ulpi->io_priv = phy->regs + ULPI_VIEWPORT; + } else { + err = utmip_pad_open(phy); + if (err < 0) + goto err1; + } + return 0; +err1: + clk_disable_unprepare(phy->pll_u); + clk_put(phy->pll_u); + return err; +} + +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 (phy_is_ulpi(phy)) + clk_put(phy->clk); + else + utmip_pad_close(phy); + clk_disable_unprepare(phy->pll_u); + clk_put(phy->pll_u); + kfree(phy); +} + +static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy) +{ + if (phy_is_ulpi(phy)) + return ulpi_phy_power_on(phy); + else + return utmi_phy_power_on(phy); +} + +static int tegra_usb_phy_power_off(struct tegra_usb_phy *phy) +{ + if (phy_is_ulpi(phy)) + return ulpi_phy_power_off(phy); + else + return utmi_phy_power_off(phy); +} + +static int tegra_usb_phy_suspend(struct usb_phy *x, int suspend) +{ + struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); + if (suspend) + return tegra_usb_phy_power_off(phy); + else + return tegra_usb_phy_power_on(phy); +} + +struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance, + void __iomem *regs, void *config, enum tegra_usb_phy_mode phy_mode) +{ + struct tegra_usb_phy *phy; + unsigned long parent_rate; + int i; + int err; + + phy = kmalloc(sizeof(struct tegra_usb_phy), GFP_KERNEL); + if (!phy) + return ERR_PTR(-ENOMEM); + + phy->instance = instance; + phy->regs = regs; + phy->config = config; + phy->mode = phy_mode; + phy->dev = dev; + + if (!phy->config) { + if (phy_is_ulpi(phy)) { + pr_err("%s: ulpi phy configuration missing", __func__); + err = -EINVAL; + goto err0; + } else { + phy->config = &utmip_default[instance]; + } + } + + phy->pll_u = clk_get_sys(NULL, "pll_u"); + if (IS_ERR(phy->pll_u)) { + pr_err("Can't get pll_u clock\n"); + err = PTR_ERR(phy->pll_u); + goto err0; + } + clk_prepare_enable(phy->pll_u); + + parent_rate = clk_get_rate(clk_get_parent(phy->pll_u)); + for (i = 0; i < ARRAY_SIZE(tegra_freq_table); i++) { + if (tegra_freq_table[i].freq == parent_rate) { + phy->freq = &tegra_freq_table[i]; + break; + } + } + if (!phy->freq) { + pr_err("invalid pll_u parent rate %ld\n", parent_rate); + err = -EINVAL; + goto err1; + } + + phy->u_phy.init = tegra_phy_init; + phy->u_phy.shutdown = tegra_usb_phy_close; + phy->u_phy.set_suspend = tegra_usb_phy_suspend; + + return phy; + +err1: + clk_disable_unprepare(phy->pll_u); + clk_put(phy->pll_u); +err0: + kfree(phy); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(tegra_usb_phy_open); + +void tegra_usb_phy_preresume(struct tegra_usb_phy *phy) +{ + if (!phy_is_ulpi(phy)) + utmi_phy_preresume(phy); +} +EXPORT_SYMBOL_GPL(tegra_usb_phy_preresume); + +void tegra_usb_phy_postresume(struct tegra_usb_phy *phy) +{ + if (!phy_is_ulpi(phy)) + utmi_phy_postresume(phy); +} +EXPORT_SYMBOL_GPL(tegra_usb_phy_postresume); + +void tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy, + enum tegra_usb_phy_port_speed port_speed) +{ + if (!phy_is_ulpi(phy)) + utmi_phy_restore_start(phy, port_speed); +} +EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_start); + +void tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy) +{ + if (!phy_is_ulpi(phy)) + utmi_phy_restore_end(phy); +} +EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_end); + +void tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy) +{ + if (!phy_is_ulpi(phy)) + utmi_phy_clk_disable(phy); +} +EXPORT_SYMBOL_GPL(tegra_usb_phy_clk_disable); + +void tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy) +{ + if (!phy_is_ulpi(phy)) + utmi_phy_clk_enable(phy); +} +EXPORT_SYMBOL_GPL(tegra_usb_phy_clk_enable); diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h new file mode 100644 index 000000000000..176b1ca06ae4 --- /dev/null +++ b/include/linux/usb/tegra_usb_phy.h @@ -0,0 +1,80 @@ +/* + * 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_PHY_H +#define __TEGRA_USB_PHY_H + +#include +#include + +struct tegra_utmip_config { + u8 hssync_start_delay; + u8 elastic_limit; + u8 idle_wait_delay; + u8 term_range_adj; + u8 xcvr_setup; + u8 xcvr_lsfslew; + 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, + TEGRA_USB_PHY_PORT_SPEED_HIGH, +}; + +enum tegra_usb_phy_mode { + TEGRA_USB_PHY_MODE_DEVICE, + TEGRA_USB_PHY_MODE_HOST, +}; + +struct tegra_xtal_freq; + +struct tegra_usb_phy { + int instance; + const struct tegra_xtal_freq *freq; + void __iomem *regs; + void __iomem *pad_regs; + struct clk *clk; + struct clk *pll_u; + struct clk *pad_clk; + enum tegra_usb_phy_mode mode; + void *config; + struct usb_phy *ulpi; + struct usb_phy u_phy; + struct device *dev; +}; + +struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance, + void __iomem *regs, void *config, enum tegra_usb_phy_mode phy_mode); + +void tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy); + +void tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy); + +void tegra_usb_phy_preresume(struct tegra_usb_phy *phy); + +void tegra_usb_phy_postresume(struct tegra_usb_phy *phy); + +void tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy, + enum tegra_usb_phy_port_speed port_speed); + +void tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy); + +#endif /* __TEGRA_USB_PHY_H */ -- cgit v1.2.3 From c9a0a3025262c6395ac1464999cade56cddac3d6 Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Sun, 26 Aug 2012 14:21:47 -0400 Subject: cfg80211: add kerneldoc entry for "vht_cap" Signed-off-by: Robert P. J. Day Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 4c518f1f1aca..60597cf7b83a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -243,6 +243,7 @@ struct ieee80211_sta_vht_cap { * rates" IE, i.e. CCK rates first, then OFDM. * @n_bitrates: Number of bitrates in @bitrates * @ht_cap: HT capabilities in this band + * @vht_cap: VHT capabilities in this band */ struct ieee80211_supported_band { struct ieee80211_channel *channels; -- cgit v1.2.3 From d23ff701643a4a725e2c7a8ba2d567d39daa29ea Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Tue, 4 Sep 2012 11:03:15 +0000 Subject: tcp: add generic netlink support for tcp_metrics Add support for genl "tcp_metrics". No locking is changed, only that now we can unlink and delete entries after grace period. We implement get/del for single entry and dump to support show/flush filtering in user space. Del without address attribute causes flush for all addresses, sadly under genl_mutex. v2: - remove rcu_assign_pointer as suggested by Eric Dumazet, it is not needed because there are no other writes under lock - move the flushing code in tcp_metrics_flush_all v3: - remove synchronize_rcu on flush as suggested by Eric Dumazet Signed-off-by: Julian Anastasov Signed-off-by: David S. Miller --- include/linux/Kbuild | 1 + include/linux/tcp_metrics.h | 54 +++++++ net/ipv4/tcp_metrics.c | 354 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 396 insertions(+), 13 deletions(-) create mode 100644 include/linux/tcp_metrics.h (limited to 'include') diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 1f2c1c787f17..90da0af28352 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -363,6 +363,7 @@ header-y += sysctl.h header-y += sysinfo.h header-y += taskstats.h header-y += tcp.h +header-y += tcp_metrics.h header-y += telephony.h header-y += termios.h header-y += time.h diff --git a/include/linux/tcp_metrics.h b/include/linux/tcp_metrics.h new file mode 100644 index 000000000000..cb5157b55f32 --- /dev/null +++ b/include/linux/tcp_metrics.h @@ -0,0 +1,54 @@ +/* tcp_metrics.h - TCP Metrics Interface */ + +#ifndef _LINUX_TCP_METRICS_H +#define _LINUX_TCP_METRICS_H + +#include + +/* NETLINK_GENERIC related info + */ +#define TCP_METRICS_GENL_NAME "tcp_metrics" +#define TCP_METRICS_GENL_VERSION 0x1 + +enum tcp_metric_index { + TCP_METRIC_RTT, + TCP_METRIC_RTTVAR, + TCP_METRIC_SSTHRESH, + TCP_METRIC_CWND, + TCP_METRIC_REORDERING, + + /* Always last. */ + __TCP_METRIC_MAX, +}; + +#define TCP_METRIC_MAX (__TCP_METRIC_MAX - 1) + +enum { + TCP_METRICS_ATTR_UNSPEC, + TCP_METRICS_ATTR_ADDR_IPV4, /* u32 */ + TCP_METRICS_ATTR_ADDR_IPV6, /* binary */ + TCP_METRICS_ATTR_AGE, /* msecs */ + TCP_METRICS_ATTR_TW_TSVAL, /* u32, raw, rcv tsval */ + TCP_METRICS_ATTR_TW_TS_STAMP, /* s32, sec age */ + TCP_METRICS_ATTR_VALS, /* nested +1, u32 */ + TCP_METRICS_ATTR_FOPEN_MSS, /* u16 */ + TCP_METRICS_ATTR_FOPEN_SYN_DROPS, /* u16, count of drops */ + TCP_METRICS_ATTR_FOPEN_SYN_DROP_TS, /* msecs age */ + TCP_METRICS_ATTR_FOPEN_COOKIE, /* binary */ + + __TCP_METRICS_ATTR_MAX, +}; + +#define TCP_METRICS_ATTR_MAX (__TCP_METRICS_ATTR_MAX - 1) + +enum { + TCP_METRICS_CMD_UNSPEC, + TCP_METRICS_CMD_GET, + TCP_METRICS_CMD_DEL, + + __TCP_METRICS_CMD_MAX, +}; + +#define TCP_METRICS_CMD_MAX (__TCP_METRICS_CMD_MAX - 1) + +#endif /* _LINUX_TCP_METRICS_H */ diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index 0abe67bb4d3a..988edb63ee73 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -17,20 +18,10 @@ #include #include #include +#include int sysctl_tcp_nometrics_save __read_mostly; -enum tcp_metric_index { - TCP_METRIC_RTT, - TCP_METRIC_RTTVAR, - TCP_METRIC_SSTHRESH, - TCP_METRIC_CWND, - TCP_METRIC_REORDERING, - - /* Always last. */ - TCP_METRIC_MAX, -}; - struct tcp_fastopen_metrics { u16 mss; u16 syn_loss:10; /* Recurring Fast Open SYN losses */ @@ -45,8 +36,10 @@ struct tcp_metrics_block { u32 tcpm_ts; u32 tcpm_ts_stamp; u32 tcpm_lock; - u32 tcpm_vals[TCP_METRIC_MAX]; + u32 tcpm_vals[TCP_METRIC_MAX + 1]; struct tcp_fastopen_metrics tcpm_fastopen; + + struct rcu_head rcu_head; }; static bool tcp_metric_locked(struct tcp_metrics_block *tm, @@ -690,6 +683,325 @@ void tcp_fastopen_cache_set(struct sock *sk, u16 mss, rcu_read_unlock(); } +static struct genl_family tcp_metrics_nl_family = { + .id = GENL_ID_GENERATE, + .hdrsize = 0, + .name = TCP_METRICS_GENL_NAME, + .version = TCP_METRICS_GENL_VERSION, + .maxattr = TCP_METRICS_ATTR_MAX, + .netnsok = true, +}; + +static struct nla_policy tcp_metrics_nl_policy[TCP_METRICS_ATTR_MAX + 1] = { + [TCP_METRICS_ATTR_ADDR_IPV4] = { .type = NLA_U32, }, + [TCP_METRICS_ATTR_ADDR_IPV6] = { .type = NLA_BINARY, + .len = sizeof(struct in6_addr), }, + /* Following attributes are not received for GET/DEL, + * we keep them for reference + */ +#if 0 + [TCP_METRICS_ATTR_AGE] = { .type = NLA_MSECS, }, + [TCP_METRICS_ATTR_TW_TSVAL] = { .type = NLA_U32, }, + [TCP_METRICS_ATTR_TW_TS_STAMP] = { .type = NLA_S32, }, + [TCP_METRICS_ATTR_VALS] = { .type = NLA_NESTED, }, + [TCP_METRICS_ATTR_FOPEN_MSS] = { .type = NLA_U16, }, + [TCP_METRICS_ATTR_FOPEN_SYN_DROPS] = { .type = NLA_U16, }, + [TCP_METRICS_ATTR_FOPEN_SYN_DROP_TS] = { .type = NLA_MSECS, }, + [TCP_METRICS_ATTR_FOPEN_COOKIE] = { .type = NLA_BINARY, + .len = TCP_FASTOPEN_COOKIE_MAX, }, +#endif +}; + +/* Add attributes, caller cancels its header on failure */ +static int tcp_metrics_fill_info(struct sk_buff *msg, + struct tcp_metrics_block *tm) +{ + struct nlattr *nest; + int i; + + switch (tm->tcpm_addr.family) { + case AF_INET: + if (nla_put_be32(msg, TCP_METRICS_ATTR_ADDR_IPV4, + tm->tcpm_addr.addr.a4) < 0) + goto nla_put_failure; + break; + case AF_INET6: + if (nla_put(msg, TCP_METRICS_ATTR_ADDR_IPV6, 16, + tm->tcpm_addr.addr.a6) < 0) + goto nla_put_failure; + break; + default: + return -EAFNOSUPPORT; + } + + if (nla_put_msecs(msg, TCP_METRICS_ATTR_AGE, + jiffies - tm->tcpm_stamp) < 0) + goto nla_put_failure; + if (tm->tcpm_ts_stamp) { + if (nla_put_s32(msg, TCP_METRICS_ATTR_TW_TS_STAMP, + (s32) (get_seconds() - tm->tcpm_ts_stamp)) < 0) + goto nla_put_failure; + if (nla_put_u32(msg, TCP_METRICS_ATTR_TW_TSVAL, + tm->tcpm_ts) < 0) + goto nla_put_failure; + } + + { + int n = 0; + + nest = nla_nest_start(msg, TCP_METRICS_ATTR_VALS); + if (!nest) + goto nla_put_failure; + for (i = 0; i < TCP_METRIC_MAX + 1; i++) { + if (!tm->tcpm_vals[i]) + continue; + if (nla_put_u32(msg, i + 1, tm->tcpm_vals[i]) < 0) + goto nla_put_failure; + n++; + } + if (n) + nla_nest_end(msg, nest); + else + nla_nest_cancel(msg, nest); + } + + { + struct tcp_fastopen_metrics tfom_copy[1], *tfom; + unsigned int seq; + + do { + seq = read_seqbegin(&fastopen_seqlock); + tfom_copy[0] = tm->tcpm_fastopen; + } while (read_seqretry(&fastopen_seqlock, seq)); + + tfom = tfom_copy; + if (tfom->mss && + nla_put_u16(msg, TCP_METRICS_ATTR_FOPEN_MSS, + tfom->mss) < 0) + goto nla_put_failure; + if (tfom->syn_loss && + (nla_put_u16(msg, TCP_METRICS_ATTR_FOPEN_SYN_DROPS, + tfom->syn_loss) < 0 || + nla_put_msecs(msg, TCP_METRICS_ATTR_FOPEN_SYN_DROP_TS, + jiffies - tfom->last_syn_loss) < 0)) + goto nla_put_failure; + if (tfom->cookie.len > 0 && + nla_put(msg, TCP_METRICS_ATTR_FOPEN_COOKIE, + tfom->cookie.len, tfom->cookie.val) < 0) + goto nla_put_failure; + } + + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + +static int tcp_metrics_dump_info(struct sk_buff *skb, + struct netlink_callback *cb, + struct tcp_metrics_block *tm) +{ + void *hdr; + + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, + &tcp_metrics_nl_family, NLM_F_MULTI, + TCP_METRICS_CMD_GET); + if (!hdr) + return -EMSGSIZE; + + if (tcp_metrics_fill_info(skb, tm) < 0) + goto nla_put_failure; + + return genlmsg_end(skb, hdr); + +nla_put_failure: + genlmsg_cancel(skb, hdr); + return -EMSGSIZE; +} + +static int tcp_metrics_nl_dump(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct net *net = sock_net(skb->sk); + unsigned int max_rows = 1U << net->ipv4.tcp_metrics_hash_log; + unsigned int row, s_row = cb->args[0]; + int s_col = cb->args[1], col = s_col; + + for (row = s_row; row < max_rows; row++, s_col = 0) { + struct tcp_metrics_block *tm; + struct tcpm_hash_bucket *hb = net->ipv4.tcp_metrics_hash + row; + + rcu_read_lock(); + for (col = 0, tm = rcu_dereference(hb->chain); tm; + tm = rcu_dereference(tm->tcpm_next), col++) { + if (col < s_col) + continue; + if (tcp_metrics_dump_info(skb, cb, tm) < 0) { + rcu_read_unlock(); + goto done; + } + } + rcu_read_unlock(); + } + +done: + cb->args[0] = row; + cb->args[1] = col; + return skb->len; +} + +static int parse_nl_addr(struct genl_info *info, struct inetpeer_addr *addr, + unsigned int *hash, int optional) +{ + struct nlattr *a; + + a = info->attrs[TCP_METRICS_ATTR_ADDR_IPV4]; + if (a) { + addr->family = AF_INET; + addr->addr.a4 = nla_get_be32(a); + *hash = (__force unsigned int) addr->addr.a4; + return 0; + } + a = info->attrs[TCP_METRICS_ATTR_ADDR_IPV6]; + if (a) { + if (nla_len(a) != sizeof(sizeof(struct in6_addr))) + return -EINVAL; + addr->family = AF_INET6; + memcpy(addr->addr.a6, nla_data(a), sizeof(addr->addr.a6)); + *hash = ipv6_addr_hash((struct in6_addr *) addr->addr.a6); + return 0; + } + return optional ? 1 : -EAFNOSUPPORT; +} + +static int tcp_metrics_nl_cmd_get(struct sk_buff *skb, struct genl_info *info) +{ + struct tcp_metrics_block *tm; + struct inetpeer_addr addr; + unsigned int hash; + struct sk_buff *msg; + struct net *net = genl_info_net(info); + void *reply; + int ret; + + ret = parse_nl_addr(info, &addr, &hash, 0); + if (ret < 0) + return ret; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + reply = genlmsg_put_reply(msg, info, &tcp_metrics_nl_family, 0, + info->genlhdr->cmd); + if (!reply) + goto nla_put_failure; + + hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); + ret = -ESRCH; + rcu_read_lock(); + for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; + tm = rcu_dereference(tm->tcpm_next)) { + if (addr_same(&tm->tcpm_addr, &addr)) { + ret = tcp_metrics_fill_info(msg, tm); + break; + } + } + rcu_read_unlock(); + if (ret < 0) + goto out_free; + + genlmsg_end(msg, reply); + return genlmsg_reply(msg, info); + +nla_put_failure: + ret = -EMSGSIZE; + +out_free: + nlmsg_free(msg); + return ret; +} + +#define deref_locked_genl(p) \ + rcu_dereference_protected(p, lockdep_genl_is_held() && \ + lockdep_is_held(&tcp_metrics_lock)) + +#define deref_genl(p) rcu_dereference_protected(p, lockdep_genl_is_held()) + +static int tcp_metrics_flush_all(struct net *net) +{ + unsigned int max_rows = 1U << net->ipv4.tcp_metrics_hash_log; + struct tcpm_hash_bucket *hb = net->ipv4.tcp_metrics_hash; + struct tcp_metrics_block *tm; + unsigned int row; + + for (row = 0; row < max_rows; row++, hb++) { + spin_lock_bh(&tcp_metrics_lock); + tm = deref_locked_genl(hb->chain); + if (tm) + hb->chain = NULL; + spin_unlock_bh(&tcp_metrics_lock); + while (tm) { + struct tcp_metrics_block *next; + + next = deref_genl(tm->tcpm_next); + kfree_rcu(tm, rcu_head); + tm = next; + } + } + return 0; +} + +static int tcp_metrics_nl_cmd_del(struct sk_buff *skb, struct genl_info *info) +{ + struct tcpm_hash_bucket *hb; + struct tcp_metrics_block *tm; + struct tcp_metrics_block __rcu **pp; + struct inetpeer_addr addr; + unsigned int hash; + struct net *net = genl_info_net(info); + int ret; + + ret = parse_nl_addr(info, &addr, &hash, 1); + if (ret < 0) + return ret; + if (ret > 0) + return tcp_metrics_flush_all(net); + + hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); + hb = net->ipv4.tcp_metrics_hash + hash; + pp = &hb->chain; + spin_lock_bh(&tcp_metrics_lock); + for (tm = deref_locked_genl(*pp); tm; + pp = &tm->tcpm_next, tm = deref_locked_genl(*pp)) { + if (addr_same(&tm->tcpm_addr, &addr)) { + *pp = tm->tcpm_next; + break; + } + } + spin_unlock_bh(&tcp_metrics_lock); + if (!tm) + return -ESRCH; + kfree_rcu(tm, rcu_head); + return 0; +} + +static struct genl_ops tcp_metrics_nl_ops[] = { + { + .cmd = TCP_METRICS_CMD_GET, + .doit = tcp_metrics_nl_cmd_get, + .dumpit = tcp_metrics_nl_dump, + .policy = tcp_metrics_nl_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = TCP_METRICS_CMD_DEL, + .doit = tcp_metrics_nl_cmd_del, + .policy = tcp_metrics_nl_policy, + .flags = GENL_ADMIN_PERM, + }, +}; + static unsigned int tcpmhash_entries; static int __init set_tcpmhash_entries(char *str) { @@ -753,5 +1065,21 @@ static __net_initdata struct pernet_operations tcp_net_metrics_ops = { void __init tcp_metrics_init(void) { - register_pernet_subsys(&tcp_net_metrics_ops); + int ret; + + ret = register_pernet_subsys(&tcp_net_metrics_ops); + if (ret < 0) + goto cleanup; + ret = genl_register_family_with_ops(&tcp_metrics_nl_family, + tcp_metrics_nl_ops, + ARRAY_SIZE(tcp_metrics_nl_ops)); + if (ret < 0) + goto cleanup_subsys; + return; + +cleanup_subsys: + unregister_pernet_subsys(&tcp_net_metrics_ops); + +cleanup: + return; } -- cgit v1.2.3 From c338bb0380b1cd64ae566d1ea7d1bfd6c811297d Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Thu, 23 Aug 2012 09:11:54 +0900 Subject: extcon: fixing typos Signed-off-by: Peter Meerwald Signed-off-by: Myungjoo Ham Signed-off-by: Greg Kroah-Hartman --- drivers/extcon/extcon-class.c | 8 ++++---- include/linux/extcon.h | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon-class.c index 481cfa0f2118..946a3188b2b7 100644 --- a/drivers/extcon/extcon-class.c +++ b/drivers/extcon/extcon-class.c @@ -443,7 +443,7 @@ static int _call_per_cable(struct notifier_block *nb, unsigned long val, /** * extcon_register_interest() - Register a notifier for a state change of a - * specific cable, not a entier set of cables of a + * specific cable, not an entier set of cables of a * extcon device. * @obj: an empty extcon_specific_cable_nb object to be returned. * @extcon_name: the name of extcon device. @@ -499,7 +499,7 @@ int extcon_unregister_interest(struct extcon_specific_cable_nb *obj) } /** - * extcon_register_notifier() - Register a notifee to get notified by + * extcon_register_notifier() - Register a notifiee to get notified by * any attach status changes from the extcon. * @edev: the extcon device. * @nb: a notifier block to be registered. @@ -516,7 +516,7 @@ int extcon_register_notifier(struct extcon_dev *edev, EXPORT_SYMBOL_GPL(extcon_register_notifier); /** - * extcon_unregister_notifier() - Unregister a notifee from the extcon device. + * extcon_unregister_notifier() - Unregister a notifiee from the extcon device. * @edev: the extcon device. * @nb: a registered notifier block to be unregistered. */ @@ -806,7 +806,7 @@ EXPORT_SYMBOL_GPL(extcon_dev_register); /** * extcon_dev_unregister() - Unregister the extcon device. - * @edev: the extcon device instance to be unregitered. + * @edev: the extcon device instance to be unregistered. * * Note that this does not call kfree(edev) because edev was not allocated * by this class. diff --git a/include/linux/extcon.h b/include/linux/extcon.h index cdd401477656..7443a560c9d0 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -30,19 +30,19 @@ /* * The standard cable name is to help support general notifier - * and notifee device drivers to share the common names. + * and notifiee device drivers to share the common names. * Please use standard cable names unless your notifier device has * a very unique and abnormal cable or * the cable type is supposed to be used with only one unique - * pair of notifier/notifee devices. + * pair of notifier/notifiee devices. * * Please add any other "standard" cables used with extcon dev. * * You may add a dot and number to specify version or specification * of the specific cable if it is required. (e.g., "Fast-charger.18" * and "Fast-charger.10" for 1.8A and 1.0A chargers) - * However, the notifee and notifier should be able to handle such - * string and if the notifee can negotiate the protocol or idenify, + * However, the notifiee and notifier should be able to handle such + * string and if the notifiee can negotiate the protocol or identify, * you don't need such convention. This convention is helpful when * notifier can distinguish but notifiee cannot. */ @@ -76,7 +76,7 @@ struct extcon_cable; * struct extcon_dev - An extcon device represents one external connector. * @name The name of this extcon device. Parent device name is used * if NULL. - * @supported_cable Array of supported cable name ending with NULL. + * @supported_cable Array of supported cable names ending with NULL. * If supported_cable is NULL, cable name related APIs * are disabled. * @mutually_exclusive Array of mutually exclusive set of cables that cannot @@ -95,7 +95,7 @@ struct extcon_cable; * @state Attach/detach state of this extcon. Do not provide at * register-time * @nh Notifier for the state change events from this extcon - * @entry To support list of extcon devices so that uses can search + * @entry To support list of extcon devices so that users can search * for extcon devices based on the extcon name. * @lock * @max_supported Internal value to store the number of cables. @@ -199,7 +199,7 @@ extern int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state); /* * get/set_cable_state access each bit of the 32b encoded state value. * They are used to access the status of each cable based on the cable_name - * or cable_index, which is retrived by extcon_find_cable_index + * or cable_index, which is retrieved by extcon_find_cable_index */ extern int extcon_find_cable_index(struct extcon_dev *sdev, const char *cable_name); @@ -226,9 +226,9 @@ extern int extcon_unregister_interest(struct extcon_specific_cable_nb *nb); /* * Following APIs are to monitor every action of a notifier. - * Registerer gets notified for every external port of a connection device. + * Registrar gets notified for every external port of a connection device. * Probably this could be used to debug an action of notifier; however, - * we do not recommend to use this at normal 'notifiee' device drivers who + * we do not recommend to use this for normal 'notifiee' device drivers who * want to be notified by a specific external port of the notifier. */ extern int extcon_register_notifier(struct extcon_dev *edev, -- cgit v1.2.3 From 19939860dcae5a3b2e11318eb7d65b4db2e55e2b Mon Sep 17 00:00:00 2001 From: anish kumar Date: Fri, 17 Aug 2012 09:57:22 -0700 Subject: extcon: adc_jack: adc-jack driver to support 3.5 pi or simliar devices External connector devices that decides connection information based on ADC values may use adc-jack device driver. The user simply needs to provide a table of adc range and connection states. Then, extcon framework will automatically notify others. Changes in V1: added Lars-Peter Clausen suggested changes: Using macros to get rid of boiler plate code such as devm_kzalloc and module_platform_driver.Other changes suggested are related to coding guidelines. Changes in V2: Removed some unnecessary checks and changed the way we are un-regitering extcon and freeing the irq while removing. Changes in V3: Renamed the files to comply with extcon naming. Changes in V4: Added the cancel_work_sync during removing of driver. Changes in V5: Added the dependency of IIO in Kconfig. Changes in V6: Some nitpicks related to naming. Changes in this version: V6 patch version patch broke the build: ERROR: "extcon_cable_name" [drivers/extcon/extcon-adc-jack.ko] undefined! Fixed it in this version. Acked-by: Jonathan Cameron Reviewed-by: Lars-Peter Clausen Signed-off-by: anish kumar Signed-off-by: MyungJoo Ham Signed-off-by: Greg Kroah-Hartman --- drivers/extcon/Kconfig | 6 + drivers/extcon/Makefile | 1 + drivers/extcon/extcon-adc-jack.c | 198 +++++++++++++++++++++++++++++++++ include/linux/extcon/extcon-adc-jack.h | 71 ++++++++++++ 4 files changed, 276 insertions(+) create mode 100644 drivers/extcon/extcon-adc-jack.c create mode 100644 include/linux/extcon/extcon-adc-jack.h (limited to 'include') diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index e175c8ed4ec4..dd5b01b957aa 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -21,6 +21,12 @@ config EXTCON_GPIO Say Y here to enable GPIO based extcon support. Note that GPIO extcon supports single state per extcon instance. +config EXTCON_ADC_JACK + tristate "ADC Jack extcon support" + depends on IIO + help + Say Y here to enable extcon device driver based on ADC values. + config EXTCON_MAX77693 tristate "MAX77693 EXTCON Support" depends on MFD_MAX77693 diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 9c0682daefb8..f98a3c4d46e0 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_EXTCON) += extcon-class.o obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o +obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c new file mode 100644 index 000000000000..60ac3fbb4cde --- /dev/null +++ b/drivers/extcon/extcon-adc-jack.c @@ -0,0 +1,198 @@ +/* + * drivers/extcon/extcon-adc-jack.c + * + * Analog Jack extcon driver with ADC-based detection capability. + * + * Copyright (C) 2012 Samsung Electronics + * MyungJoo Ham + * + * Modified for calling to IIO to get adc by + * + * 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 + +/** + * struct adc_jack_data - internal data for adc_jack device driver + * @edev - extcon device. + * @cable_names - list of supported cables. + * @num_cables - size of cable_names. + * @adc_conditions - list of adc value conditions. + * @num_conditions - size of adc_conditions. + * @irq - irq number of attach/detach event (0 if not exist). + * @handling_delay - interrupt handler will schedule extcon event + * handling at handling_delay jiffies. + * @handler - extcon event handler called by interrupt handler. + * @chan - iio channel being queried. + */ +struct adc_jack_data { + struct extcon_dev edev; + + const char **cable_names; + int num_cables; + struct adc_jack_cond *adc_conditions; + int num_conditions; + + int irq; + unsigned long handling_delay; /* in jiffies */ + struct delayed_work handler; + + struct iio_channel *chan; +}; + +static void adc_jack_handler(struct work_struct *work) +{ + struct adc_jack_data *data = container_of(to_delayed_work(work), + struct adc_jack_data, + handler); + u32 state = 0; + int ret, adc_val; + int i; + + ret = iio_read_channel_raw(data->chan, &adc_val); + if (ret < 0) { + dev_err(data->edev.dev, "read channel() error: %d\n", ret); + return; + } + + /* Get state from adc value with adc_conditions */ + for (i = 0; i < data->num_conditions; i++) { + struct adc_jack_cond *def = &data->adc_conditions[i]; + if (!def->state) + break; + if (def->min_adc <= adc_val && def->max_adc >= adc_val) { + state = def->state; + break; + } + } + /* if no def has met, it means state = 0 (no cables attached) */ + + extcon_set_state(&data->edev, state); +} + +static irqreturn_t adc_jack_irq_thread(int irq, void *_data) +{ + struct adc_jack_data *data = _data; + + schedule_delayed_work(&data->handler, data->handling_delay); + return IRQ_HANDLED; +} + +static int __devinit adc_jack_probe(struct platform_device *pdev) +{ + struct adc_jack_data *data; + struct adc_jack_pdata *pdata = pdev->dev.platform_data; + int i, err = 0; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->edev.name = pdata->name; + + if (!pdata->cable_names) { + err = -EINVAL; + dev_err(&pdev->dev, "error: cable_names not defined.\n"); + goto out; + } + + data->edev.supported_cable = pdata->cable_names; + + /* Check the length of array and set num_cables */ + for (i = 0; data->edev.supported_cable[i]; i++) + ; + if (i == 0 || i > SUPPORTED_CABLE_MAX) { + err = -EINVAL; + dev_err(&pdev->dev, "error: pdata->cable_names size = %d\n", + i - 1); + goto out; + } + data->num_cables = i; + + if (!pdata->adc_conditions || + !pdata->adc_conditions[0].state) { + err = -EINVAL; + dev_err(&pdev->dev, "error: adc_conditions not defined.\n"); + goto out; + } + data->adc_conditions = pdata->adc_conditions; + + /* Check the length of array and set num_conditions */ + for (i = 0; data->adc_conditions[i].state; i++) + ; + data->num_conditions = i; + + data->chan = iio_channel_get(dev_name(&pdev->dev), + pdata->consumer_channel); + if (IS_ERR(data->chan)) { + err = PTR_ERR(data->chan); + goto out; + } + + data->handling_delay = msecs_to_jiffies(pdata->handling_delay_ms); + + INIT_DELAYED_WORK_DEFERRABLE(&data->handler, adc_jack_handler); + + platform_set_drvdata(pdev, data); + + err = extcon_dev_register(&data->edev, &pdev->dev); + if (err) + goto out; + + data->irq = platform_get_irq(pdev, 0); + if (!data->irq) { + dev_err(&pdev->dev, "platform_get_irq failed\n"); + err = -ENODEV; + goto err_irq; + } + + err = request_any_context_irq(data->irq, adc_jack_irq_thread, + pdata->irq_flags, pdata->name, data); + + if (err) { + dev_err(&pdev->dev, "error: irq %d\n", data->irq); + err = -EINVAL; + goto err_irq; + } + + goto out; + +err_irq: + extcon_dev_unregister(&data->edev); +out: + return err; +} + +static int __devexit adc_jack_remove(struct platform_device *pdev) +{ + struct adc_jack_data *data = platform_get_drvdata(pdev); + + free_irq(data->irq, data); + cancel_work_sync(&data->handler.work); + extcon_dev_unregister(&data->edev); + + return 0; +} + +static struct platform_driver adc_jack_driver = { + .probe = adc_jack_probe, + .remove = __devexit_p(adc_jack_remove), + .driver = { + .name = "adc-jack", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(adc_jack_driver); diff --git a/include/linux/extcon/extcon-adc-jack.h b/include/linux/extcon/extcon-adc-jack.h new file mode 100644 index 000000000000..20e9eef25d4c --- /dev/null +++ b/include/linux/extcon/extcon-adc-jack.h @@ -0,0 +1,71 @@ +/* + * include/linux/extcon/extcon-adc-jack.h + * + * Analog Jack extcon driver with ADC-based detection capability. + * + * Copyright (C) 2012 Samsung Electronics + * MyungJoo Ham + * + * 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 _EXTCON_ADC_JACK_H_ +#define _EXTCON_ADC_JACK_H_ __FILE__ + +#include +#include + +/** + * struct adc_jack_cond - condition to use an extcon state + * @state - the corresponding extcon state (if 0, this struct denotes + * the last adc_jack_cond element among the array) + * @min_adc - min adc value for this condition + * @max_adc - max adc value for this condition + * + * For example, if { .state = 0x3, .min_adc = 100, .max_adc = 200}, it means + * that if ADC value is between (inclusive) 100 and 200, than the cable 0 and + * 1 are attached (1<<0 | 1<<1 == 0x3) + * + * Note that you don't need to describe condition for "no cable attached" + * because when no adc_jack_cond is met, state = 0 is automatically chosen. + */ +struct adc_jack_cond { + u32 state; /* extcon state value. 0 if invalid */ + u32 min_adc; + u32 max_adc; +}; + +/** + * struct adc_jack_pdata - platform data for adc jack device. + * @name - name of the extcon device. If null, "adc-jack" is used. + * @consumer_channel - Unique name to identify the channel on the consumer + * side. This typically describes the channels used within + * the consumer. E.g. 'battery_voltage' + * @cable_names - array of cable names ending with null. + * @adc_contitions - array of struct adc_jack_cond conditions ending + * with .state = 0 entry. This describes how to decode + * adc values into extcon state. + * @irq_flags - irq flags used for the @irq + * @handling_delay_ms - in some devices, we need to read ADC value some + * milli-seconds after the interrupt occurs. You may + * describe such delays with @handling_delay_ms, which + * is rounded-off by jiffies. + */ +struct adc_jack_pdata { + const char *name; + const char *consumer_channel; + /* + * The last entry should be NULL + */ + const char **cable_names; + /* The last entry's state should be 0 */ + struct adc_jack_cond *adc_conditions; + + unsigned long irq_flags; + unsigned long handling_delay_ms; /* in ms */ +}; + +#endif /* _EXTCON_ADC_JACK_H */ -- cgit v1.2.3 From ba3fe7aba2067212a8c3baffc18f9209fcbe9d34 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 4 Sep 2012 16:35:08 +0100 Subject: tty: move the async flags from the serial code into the tty includes These are used with the tty_port flags which are tty generic so move the flags into a more sensible place. This then makes it possible to add helpers such as those suggested by Huang Shijie. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- include/linux/Kbuild | 1 + include/linux/serial.h | 75 ++------------------------------------------- include/linux/tty.h | 1 + include/linux/tty_flags.h | 78 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 72 deletions(-) create mode 100644 include/linux/tty_flags.h (limited to 'include') diff --git a/include/linux/Kbuild b/include/linux/Kbuild index e9f560a40b8b..c57e064666e4 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -369,6 +369,7 @@ header-y += tipc.h header-y += tipc_config.h header-y += toshiba.h header-y += tty.h +header-y += tty_flags.h header-y += types.h header-y += udf_fs_i.h header-y += udp.h diff --git a/include/linux/serial.h b/include/linux/serial.h index 90e9f981358a..3504f428ce66 100644 --- a/include/linux/serial.h +++ b/include/linux/serial.h @@ -12,9 +12,12 @@ #include +#include + #ifdef __KERNEL__ #include + /* * Counters of the input lines (CTS, DSR, RI, CD) interrupts */ @@ -94,78 +97,6 @@ struct serial_uart_config { #define UART_STARTECH 0x04 #define UART_NATSEMI 0x08 -/* - * Definitions for async_struct (and serial_struct) flags field - * - * Define ASYNCB_* for convenient use with {test,set,clear}_bit. - */ -#define ASYNCB_HUP_NOTIFY 0 /* Notify getty on hangups and closes - * on the callout port */ -#define ASYNCB_FOURPORT 1 /* Set OU1, OUT2 per AST Fourport settings */ -#define ASYNCB_SAK 2 /* Secure Attention Key (Orange book) */ -#define ASYNCB_SPLIT_TERMIOS 3 /* Separate termios for dialin/callout */ -#define ASYNCB_SPD_HI 4 /* Use 56000 instead of 38400 bps */ -#define ASYNCB_SPD_VHI 5 /* Use 115200 instead of 38400 bps */ -#define ASYNCB_SKIP_TEST 6 /* Skip UART test during autoconfiguration */ -#define ASYNCB_AUTO_IRQ 7 /* Do automatic IRQ during - * autoconfiguration */ -#define ASYNCB_SESSION_LOCKOUT 8 /* Lock out cua opens based on session */ -#define ASYNCB_PGRP_LOCKOUT 9 /* Lock out cua opens based on pgrp */ -#define ASYNCB_CALLOUT_NOHUP 10 /* Don't do hangups for cua device */ -#define ASYNCB_HARDPPS_CD 11 /* Call hardpps when CD goes high */ -#define ASYNCB_SPD_SHI 12 /* Use 230400 instead of 38400 bps */ -#define ASYNCB_LOW_LATENCY 13 /* Request low latency behaviour */ -#define ASYNCB_BUGGY_UART 14 /* This is a buggy UART, skip some safety - * checks. Note: can be dangerous! */ -#define ASYNCB_AUTOPROBE 15 /* Port was autoprobed by PCI or PNP code */ -#define ASYNCB_LAST_USER 15 - -/* Internal flags used only by kernel */ -#define ASYNCB_INITIALIZED 31 /* Serial port was initialized */ -#define ASYNCB_SUSPENDED 30 /* Serial port is suspended */ -#define ASYNCB_NORMAL_ACTIVE 29 /* Normal device is active */ -#define ASYNCB_BOOT_AUTOCONF 28 /* Autoconfigure port on bootup */ -#define ASYNCB_CLOSING 27 /* Serial port is closing */ -#define ASYNCB_CTS_FLOW 26 /* Do CTS flow control */ -#define ASYNCB_CHECK_CD 25 /* i.e., CLOCAL */ -#define ASYNCB_SHARE_IRQ 24 /* for multifunction cards, no longer used */ -#define ASYNCB_CONS_FLOW 23 /* flow control for console */ -#define ASYNCB_FIRST_KERNEL 22 - -#define ASYNC_HUP_NOTIFY (1U << ASYNCB_HUP_NOTIFY) -#define ASYNC_SUSPENDED (1U << ASYNCB_SUSPENDED) -#define ASYNC_FOURPORT (1U << ASYNCB_FOURPORT) -#define ASYNC_SAK (1U << ASYNCB_SAK) -#define ASYNC_SPLIT_TERMIOS (1U << ASYNCB_SPLIT_TERMIOS) -#define ASYNC_SPD_HI (1U << ASYNCB_SPD_HI) -#define ASYNC_SPD_VHI (1U << ASYNCB_SPD_VHI) -#define ASYNC_SKIP_TEST (1U << ASYNCB_SKIP_TEST) -#define ASYNC_AUTO_IRQ (1U << ASYNCB_AUTO_IRQ) -#define ASYNC_SESSION_LOCKOUT (1U << ASYNCB_SESSION_LOCKOUT) -#define ASYNC_PGRP_LOCKOUT (1U << ASYNCB_PGRP_LOCKOUT) -#define ASYNC_CALLOUT_NOHUP (1U << ASYNCB_CALLOUT_NOHUP) -#define ASYNC_HARDPPS_CD (1U << ASYNCB_HARDPPS_CD) -#define ASYNC_SPD_SHI (1U << ASYNCB_SPD_SHI) -#define ASYNC_LOW_LATENCY (1U << ASYNCB_LOW_LATENCY) -#define ASYNC_BUGGY_UART (1U << ASYNCB_BUGGY_UART) -#define ASYNC_AUTOPROBE (1U << ASYNCB_AUTOPROBE) - -#define ASYNC_FLAGS ((1U << (ASYNCB_LAST_USER + 1)) - 1) -#define ASYNC_USR_MASK (ASYNC_SPD_MASK|ASYNC_CALLOUT_NOHUP| \ - ASYNC_LOW_LATENCY) -#define ASYNC_SPD_CUST (ASYNC_SPD_HI|ASYNC_SPD_VHI) -#define ASYNC_SPD_WARP (ASYNC_SPD_HI|ASYNC_SPD_SHI) -#define ASYNC_SPD_MASK (ASYNC_SPD_HI|ASYNC_SPD_VHI|ASYNC_SPD_SHI) - -#define ASYNC_INITIALIZED (1U << ASYNCB_INITIALIZED) -#define ASYNC_NORMAL_ACTIVE (1U << ASYNCB_NORMAL_ACTIVE) -#define ASYNC_BOOT_AUTOCONF (1U << ASYNCB_BOOT_AUTOCONF) -#define ASYNC_CLOSING (1U << ASYNCB_CLOSING) -#define ASYNC_CTS_FLOW (1U << ASYNCB_CTS_FLOW) -#define ASYNC_CHECK_CD (1U << ASYNCB_CHECK_CD) -#define ASYNC_SHARE_IRQ (1U << ASYNCB_SHARE_IRQ) -#define ASYNC_CONS_FLOW (1U << ASYNCB_CONS_FLOW) -#define ASYNC_INTERNAL_FLAGS (~((1U << ASYNCB_FIRST_KERNEL) - 1)) /* * Multiport serial configuration structure --- external structure diff --git a/include/linux/tty.h b/include/linux/tty.h index 69a787fdfa9c..dbebd1e56bc1 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -43,6 +43,7 @@ #include #include #include +#include diff --git a/include/linux/tty_flags.h b/include/linux/tty_flags.h new file mode 100644 index 000000000000..eefcb483a2c0 --- /dev/null +++ b/include/linux/tty_flags.h @@ -0,0 +1,78 @@ +#ifndef _LINUX_TTY_FLAGS_H +#define _LINUX_TTY_FLAGS_H + +/* + * Definitions for async_struct (and serial_struct) flags field also + * shared by the tty_port flags structures. + * + * Define ASYNCB_* for convenient use with {test,set,clear}_bit. + */ +#define ASYNCB_HUP_NOTIFY 0 /* Notify getty on hangups and closes + * on the callout port */ +#define ASYNCB_FOURPORT 1 /* Set OU1, OUT2 per AST Fourport settings */ +#define ASYNCB_SAK 2 /* Secure Attention Key (Orange book) */ +#define ASYNCB_SPLIT_TERMIOS 3 /* Separate termios for dialin/callout */ +#define ASYNCB_SPD_HI 4 /* Use 56000 instead of 38400 bps */ +#define ASYNCB_SPD_VHI 5 /* Use 115200 instead of 38400 bps */ +#define ASYNCB_SKIP_TEST 6 /* Skip UART test during autoconfiguration */ +#define ASYNCB_AUTO_IRQ 7 /* Do automatic IRQ during + * autoconfiguration */ +#define ASYNCB_SESSION_LOCKOUT 8 /* Lock out cua opens based on session */ +#define ASYNCB_PGRP_LOCKOUT 9 /* Lock out cua opens based on pgrp */ +#define ASYNCB_CALLOUT_NOHUP 10 /* Don't do hangups for cua device */ +#define ASYNCB_HARDPPS_CD 11 /* Call hardpps when CD goes high */ +#define ASYNCB_SPD_SHI 12 /* Use 230400 instead of 38400 bps */ +#define ASYNCB_LOW_LATENCY 13 /* Request low latency behaviour */ +#define ASYNCB_BUGGY_UART 14 /* This is a buggy UART, skip some safety + * checks. Note: can be dangerous! */ +#define ASYNCB_AUTOPROBE 15 /* Port was autoprobed by PCI or PNP code */ +#define ASYNCB_LAST_USER 15 + +/* Internal flags used only by kernel */ +#define ASYNCB_INITIALIZED 31 /* Serial port was initialized */ +#define ASYNCB_SUSPENDED 30 /* Serial port is suspended */ +#define ASYNCB_NORMAL_ACTIVE 29 /* Normal device is active */ +#define ASYNCB_BOOT_AUTOCONF 28 /* Autoconfigure port on bootup */ +#define ASYNCB_CLOSING 27 /* Serial port is closing */ +#define ASYNCB_CTS_FLOW 26 /* Do CTS flow control */ +#define ASYNCB_CHECK_CD 25 /* i.e., CLOCAL */ +#define ASYNCB_SHARE_IRQ 24 /* for multifunction cards, no longer used */ +#define ASYNCB_CONS_FLOW 23 /* flow control for console */ +#define ASYNCB_FIRST_KERNEL 22 + +#define ASYNC_HUP_NOTIFY (1U << ASYNCB_HUP_NOTIFY) +#define ASYNC_SUSPENDED (1U << ASYNCB_SUSPENDED) +#define ASYNC_FOURPORT (1U << ASYNCB_FOURPORT) +#define ASYNC_SAK (1U << ASYNCB_SAK) +#define ASYNC_SPLIT_TERMIOS (1U << ASYNCB_SPLIT_TERMIOS) +#define ASYNC_SPD_HI (1U << ASYNCB_SPD_HI) +#define ASYNC_SPD_VHI (1U << ASYNCB_SPD_VHI) +#define ASYNC_SKIP_TEST (1U << ASYNCB_SKIP_TEST) +#define ASYNC_AUTO_IRQ (1U << ASYNCB_AUTO_IRQ) +#define ASYNC_SESSION_LOCKOUT (1U << ASYNCB_SESSION_LOCKOUT) +#define ASYNC_PGRP_LOCKOUT (1U << ASYNCB_PGRP_LOCKOUT) +#define ASYNC_CALLOUT_NOHUP (1U << ASYNCB_CALLOUT_NOHUP) +#define ASYNC_HARDPPS_CD (1U << ASYNCB_HARDPPS_CD) +#define ASYNC_SPD_SHI (1U << ASYNCB_SPD_SHI) +#define ASYNC_LOW_LATENCY (1U << ASYNCB_LOW_LATENCY) +#define ASYNC_BUGGY_UART (1U << ASYNCB_BUGGY_UART) +#define ASYNC_AUTOPROBE (1U << ASYNCB_AUTOPROBE) + +#define ASYNC_FLAGS ((1U << (ASYNCB_LAST_USER + 1)) - 1) +#define ASYNC_USR_MASK (ASYNC_SPD_MASK|ASYNC_CALLOUT_NOHUP| \ + ASYNC_LOW_LATENCY) +#define ASYNC_SPD_CUST (ASYNC_SPD_HI|ASYNC_SPD_VHI) +#define ASYNC_SPD_WARP (ASYNC_SPD_HI|ASYNC_SPD_SHI) +#define ASYNC_SPD_MASK (ASYNC_SPD_HI|ASYNC_SPD_VHI|ASYNC_SPD_SHI) + +#define ASYNC_INITIALIZED (1U << ASYNCB_INITIALIZED) +#define ASYNC_NORMAL_ACTIVE (1U << ASYNCB_NORMAL_ACTIVE) +#define ASYNC_BOOT_AUTOCONF (1U << ASYNCB_BOOT_AUTOCONF) +#define ASYNC_CLOSING (1U << ASYNCB_CLOSING) +#define ASYNC_CTS_FLOW (1U << ASYNCB_CTS_FLOW) +#define ASYNC_CHECK_CD (1U << ASYNCB_CHECK_CD) +#define ASYNC_SHARE_IRQ (1U << ASYNCB_SHARE_IRQ) +#define ASYNC_CONS_FLOW (1U << ASYNCB_CONS_FLOW) +#define ASYNC_INTERNAL_FLAGS (~((1U << ASYNCB_FIRST_KERNEL) - 1)) + +#endif -- cgit v1.2.3 From 4b71598b6a1e72d11a657ccd67bdb14f1c269186 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 20 Aug 2012 19:56:28 -0400 Subject: serial: diminish usage of struct serial_uart_config This structure might have made sense many years ago, but at this point it is only used in one specific driver, and referenced in stale comments elsewhere. Rather than change the sunsu.c driver, simply move the struct to be within the exclusive domain of that driver, so it won't get inadvertently picked up and used by other serial drivers going forward. The comments referencing the now driver specific struct are updated accordingly. Note that 8250.c has a struct that is similar in usage, with the name serial8250_config; but is 100% independent and untouched here. Cc: "David S. Miller" Signed-off-by: Paul Gortmaker Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/staging/speakup/serialio.h | 3 +-- drivers/tty/serial/8250/8250.h | 3 --- drivers/tty/serial/sunsu.c | 6 ++++++ include/linux/serial.h | 6 ------ 4 files changed, 7 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/staging/speakup/serialio.h b/drivers/staging/speakup/serialio.h index 614271f9b99f..55d68b5ad165 100644 --- a/drivers/staging/speakup/serialio.h +++ b/drivers/staging/speakup/serialio.h @@ -1,8 +1,7 @@ #ifndef _SPEAKUP_SERIAL_H #define _SPEAKUP_SERIAL_H -#include /* for rs_table, serial constants & - serial_uart_config */ +#include /* for rs_table, serial constants */ #include /* for more serial constants */ #ifndef __sparc__ #include diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 972b5212b58c..0c5e908df0b5 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -26,9 +26,6 @@ struct old_serial_port { unsigned long irqflags; }; -/* - * This replaces serial_uart_config in include/linux/serial.h - */ struct serial8250_config { const char *name; unsigned short fifo_size; diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index d9190957a5f4..b97913dcdbff 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -58,6 +58,12 @@ enum su_type { SU_PORT_NONE, SU_PORT_MS, SU_PORT_KBD, SU_PORT_PORT }; static char *su_typev[] = { "su(???)", "su(mouse)", "su(kbd)", "su(serial)" }; +struct serial_uart_config { + char *name; + int dfl_xmit_fifo_size; + int flags; +}; + /* * Here we define the default xmit fifo size used for each type of UART. */ diff --git a/include/linux/serial.h b/include/linux/serial.h index 3504f428ce66..861e51de476b 100644 --- a/include/linux/serial.h +++ b/include/linux/serial.h @@ -86,12 +86,6 @@ struct serial_struct { #define SERIAL_IO_HUB6 1 #define SERIAL_IO_MEM 2 -struct serial_uart_config { - char *name; - int dfl_xmit_fifo_size; - int flags; -}; - #define UART_CLEAR_FIFO 0x01 #define UART_USE_FIFO 0x02 #define UART_STARTECH 0x04 -- cgit v1.2.3 From 1d65c0b12656d9f3bc29bb19f2d7441832433f03 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 25 Aug 2012 19:24:19 +0400 Subject: serial: New serial driver SCCNXP This driver is a replacement for a SC26XX driver with a lot of improvements and new features. The main differences from the SC26XX driver: - Removed dependency on MIPS. Driver can be used on any platform. - Added support for SCC2681, SCC2691, SCC2692, SC28L91, SC28L92, SC28L202, SCC68681 and SCC68692 ICs. - Using devm_-related functions. - Improved error handling of serial port, improved FIFO handling. - Ability to load multiple instances of drivers. To avoid the possibility of regression, driver SC26XX left in the system to confirm the stability of the driver on platforms where it is being used. Signed-off-by: Alexander Shiyan Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 18 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/sccnxp.c | 985 +++++++++++++++++++++++++++++++++++ include/linux/platform_data/sccnxp.h | 93 ++++ 4 files changed, 1097 insertions(+) create mode 100644 drivers/tty/serial/sccnxp.c create mode 100644 include/linux/platform_data/sccnxp.h (limited to 'include') diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 04b9e13045d7..26907cf25744 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1130,6 +1130,24 @@ config SERIAL_SC26XX_CONSOLE help Support for Console on SC2681/SC2692 serial ports. +config SERIAL_SCCNXP + bool "SCCNXP serial port support" + depends on !SERIAL_SC26XX + select SERIAL_CORE + default n + help + This selects support for an advanced UART from NXP (Philips). + Supported ICs are SCC2681, SCC2691, SCC2692, SC28L91, SC28L92, + SC28L202, SCC68681 and SCC68692. + Positioned as a replacement for the driver SC26XX. + +config SERIAL_SCCNXP_CONSOLE + bool "Console on SCCNXP serial port" + depends on SERIAL_SCCNXP + select SERIAL_CORE_CONSOLE + help + Support for console on SCCNXP serial ports. + config SERIAL_BFIN_SPORT tristate "Blackfin SPORT emulate UART" depends on BLACKFIN diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 2af9e5279dab..ce88667cfd17 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_SERIAL_MPSC) += mpsc.o obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o obj-$(CONFIG_SERIAL_SC26XX) += sc26xx.o +obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o obj-$(CONFIG_SERIAL_JSM) += jsm/ obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.o diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c new file mode 100644 index 000000000000..29dda9ba6f0f --- /dev/null +++ b/drivers/tty/serial/sccnxp.c @@ -0,0 +1,985 @@ +/* + * NXP (Philips) SCC+++(SCN+++) serial driver + * + * Copyright (C) 2012 Alexander Shiyan + * + * Based on sc26xx.c, by Thomas Bogendörfer (tsbogend@alpha.franken.de) + * + * 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_SCCNXP_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SCCNXP_NAME "uart-sccnxp" +#define SCCNXP_MAJOR 204 +#define SCCNXP_MINOR 205 + +#define SCCNXP_MR_REG (0x00) +# define MR0_BAUD_NORMAL (0 << 0) +# define MR0_BAUD_EXT1 (1 << 0) +# define MR0_BAUD_EXT2 (5 << 0) +# define MR0_FIFO (1 << 3) +# define MR0_TXLVL (1 << 4) +# define MR1_BITS_5 (0 << 0) +# define MR1_BITS_6 (1 << 0) +# define MR1_BITS_7 (2 << 0) +# define MR1_BITS_8 (3 << 0) +# define MR1_PAR_EVN (0 << 2) +# define MR1_PAR_ODD (1 << 2) +# define MR1_PAR_NO (4 << 2) +# define MR2_STOP1 (7 << 0) +# define MR2_STOP2 (0xf << 0) +#define SCCNXP_SR_REG (0x01) +#define SCCNXP_CSR_REG SCCNXP_SR_REG +# define SR_RXRDY (1 << 0) +# define SR_FULL (1 << 1) +# define SR_TXRDY (1 << 2) +# define SR_TXEMT (1 << 3) +# define SR_OVR (1 << 4) +# define SR_PE (1 << 5) +# define SR_FE (1 << 6) +# define SR_BRK (1 << 7) +#define SCCNXP_CR_REG (0x02) +# define CR_RX_ENABLE (1 << 0) +# define CR_RX_DISABLE (1 << 1) +# define CR_TX_ENABLE (1 << 2) +# define CR_TX_DISABLE (1 << 3) +# define CR_CMD_MRPTR1 (0x01 << 4) +# define CR_CMD_RX_RESET (0x02 << 4) +# define CR_CMD_TX_RESET (0x03 << 4) +# define CR_CMD_STATUS_RESET (0x04 << 4) +# define CR_CMD_BREAK_RESET (0x05 << 4) +# define CR_CMD_START_BREAK (0x06 << 4) +# define CR_CMD_STOP_BREAK (0x07 << 4) +# define CR_CMD_MRPTR0 (0x0b << 4) +#define SCCNXP_RHR_REG (0x03) +#define SCCNXP_THR_REG SCCNXP_RHR_REG +#define SCCNXP_IPCR_REG (0x04) +#define SCCNXP_ACR_REG SCCNXP_IPCR_REG +# define ACR_BAUD0 (0 << 7) +# define ACR_BAUD1 (1 << 7) +# define ACR_TIMER_MODE (6 << 4) +#define SCCNXP_ISR_REG (0x05) +#define SCCNXP_IMR_REG SCCNXP_ISR_REG +# define IMR_TXRDY (1 << 0) +# define IMR_RXRDY (1 << 1) +# define ISR_TXRDY(x) (1 << ((x * 4) + 0)) +# define ISR_RXRDY(x) (1 << ((x * 4) + 1)) +#define SCCNXP_IPR_REG (0x0d) +#define SCCNXP_OPCR_REG SCCNXP_IPR_REG +#define SCCNXP_SOP_REG (0x0e) +#define SCCNXP_ROP_REG (0x0f) + +/* Route helpers */ +#define MCTRL_MASK(sig) (0xf << (sig)) +#define MCTRL_IBIT(cfg, sig) ((((cfg) >> (sig)) & 0xf) - LINE_IP0) +#define MCTRL_OBIT(cfg, sig) ((((cfg) >> (sig)) & 0xf) - LINE_OP0) + +/* Supported chip types */ +enum { + SCCNXP_TYPE_SC2681 = 2681, + SCCNXP_TYPE_SC2691 = 2691, + SCCNXP_TYPE_SC2692 = 2692, + SCCNXP_TYPE_SC2891 = 2891, + SCCNXP_TYPE_SC2892 = 2892, + SCCNXP_TYPE_SC28202 = 28202, + SCCNXP_TYPE_SC68681 = 68681, + SCCNXP_TYPE_SC68692 = 68692, +}; + +struct sccnxp_port { + struct uart_driver uart; + struct uart_port port[SCCNXP_MAX_UARTS]; + + const char *name; + int irq; + + u8 imr; + u8 addr_mask; + int freq_std; + + int flags; +#define SCCNXP_HAVE_IO 0x00000001 +#define SCCNXP_HAVE_MR0 0x00000002 + +#ifdef CONFIG_SERIAL_SCCNXP_CONSOLE + struct console console; +#endif + + struct mutex sccnxp_mutex; + + struct sccnxp_pdata pdata; +}; + +static inline u8 sccnxp_raw_read(void __iomem *base, u8 reg, u8 shift) +{ + return readb(base + (reg << shift)); +} + +static inline void sccnxp_raw_write(void __iomem *base, u8 reg, u8 shift, u8 v) +{ + writeb(v, base + (reg << shift)); +} + +static inline u8 sccnxp_read(struct uart_port *port, u8 reg) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + return sccnxp_raw_read(port->membase, reg & s->addr_mask, + port->regshift); +} + +static inline void sccnxp_write(struct uart_port *port, u8 reg, u8 v) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + sccnxp_raw_write(port->membase, reg & s->addr_mask, port->regshift, v); +} + +static inline u8 sccnxp_port_read(struct uart_port *port, u8 reg) +{ + return sccnxp_read(port, (port->line << 3) + reg); +} + +static inline void sccnxp_port_write(struct uart_port *port, u8 reg, u8 v) +{ + sccnxp_write(port, (port->line << 3) + reg, v); +} + +static int sccnxp_update_best_err(int a, int b, int *besterr) +{ + int err = abs(a - b); + + if ((*besterr < 0) || (*besterr > err)) { + *besterr = err; + return 0; + } + + return 1; +} + +struct baud_table { + u8 csr; + u8 acr; + u8 mr0; + int baud; +}; + +const struct baud_table baud_std[] = { + { 0, ACR_BAUD0, MR0_BAUD_NORMAL, 50, }, + { 0, ACR_BAUD1, MR0_BAUD_NORMAL, 75, }, + { 1, ACR_BAUD0, MR0_BAUD_NORMAL, 110, }, + { 2, ACR_BAUD0, MR0_BAUD_NORMAL, 134, }, + { 3, ACR_BAUD1, MR0_BAUD_NORMAL, 150, }, + { 3, ACR_BAUD0, MR0_BAUD_NORMAL, 200, }, + { 4, ACR_BAUD0, MR0_BAUD_NORMAL, 300, }, + { 0, ACR_BAUD1, MR0_BAUD_EXT1, 450, }, + { 1, ACR_BAUD0, MR0_BAUD_EXT2, 880, }, + { 3, ACR_BAUD1, MR0_BAUD_EXT1, 900, }, + { 5, ACR_BAUD0, MR0_BAUD_NORMAL, 600, }, + { 7, ACR_BAUD0, MR0_BAUD_NORMAL, 1050, }, + { 2, ACR_BAUD0, MR0_BAUD_EXT2, 1076, }, + { 6, ACR_BAUD0, MR0_BAUD_NORMAL, 1200, }, + { 10, ACR_BAUD1, MR0_BAUD_NORMAL, 1800, }, + { 7, ACR_BAUD1, MR0_BAUD_NORMAL, 2000, }, + { 8, ACR_BAUD0, MR0_BAUD_NORMAL, 2400, }, + { 5, ACR_BAUD1, MR0_BAUD_EXT1, 3600, }, + { 9, ACR_BAUD0, MR0_BAUD_NORMAL, 4800, }, + { 10, ACR_BAUD0, MR0_BAUD_NORMAL, 7200, }, + { 11, ACR_BAUD0, MR0_BAUD_NORMAL, 9600, }, + { 8, ACR_BAUD0, MR0_BAUD_EXT1, 14400, }, + { 12, ACR_BAUD1, MR0_BAUD_NORMAL, 19200, }, + { 9, ACR_BAUD0, MR0_BAUD_EXT1, 28800, }, + { 12, ACR_BAUD0, MR0_BAUD_NORMAL, 38400, }, + { 11, ACR_BAUD0, MR0_BAUD_EXT1, 57600, }, + { 12, ACR_BAUD1, MR0_BAUD_EXT1, 115200, }, + { 12, ACR_BAUD0, MR0_BAUD_EXT1, 230400, }, + { 0, 0, 0, 0 } +}; + +static void sccnxp_set_baud(struct uart_port *port, int baud) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + int div_std, tmp_baud, bestbaud = baud, besterr = -1; + u8 i, acr = 0, csr = 0, mr0 = 0; + + /* Find best baud from table */ + for (i = 0; baud_std[i].baud && besterr; i++) { + if (baud_std[i].mr0 && !(s->flags & SCCNXP_HAVE_MR0)) + continue; + div_std = DIV_ROUND_CLOSEST(s->freq_std, baud_std[i].baud); + tmp_baud = DIV_ROUND_CLOSEST(port->uartclk, div_std); + if (!sccnxp_update_best_err(baud, tmp_baud, &besterr)) { + acr = baud_std[i].acr; + csr = baud_std[i].csr; + mr0 = baud_std[i].mr0; + bestbaud = tmp_baud; + } + } + + if (s->flags & SCCNXP_HAVE_MR0) { + /* Enable FIFO, set half level for TX */ + mr0 |= MR0_FIFO | MR0_TXLVL; + /* Update MR0 */ + sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_MRPTR0); + sccnxp_port_write(port, SCCNXP_MR_REG, mr0); + } + + sccnxp_port_write(port, SCCNXP_ACR_REG, acr | ACR_TIMER_MODE); + sccnxp_port_write(port, SCCNXP_CSR_REG, (csr << 4) | csr); + + dev_dbg(port->dev, "Baudrate desired: %i, calculated: %i\n", + baud, bestbaud); +} + +static void sccnxp_enable_irq(struct uart_port *port, int mask) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + s->imr |= mask << (port->line * 4); + sccnxp_write(port, SCCNXP_IMR_REG, s->imr); +} + +static void sccnxp_disable_irq(struct uart_port *port, int mask) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + s->imr &= ~(mask << (port->line * 4)); + sccnxp_write(port, SCCNXP_IMR_REG, s->imr); +} + +static void sccnxp_set_bit(struct uart_port *port, int sig, int state) +{ + u8 bitmask; + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(sig)) { + bitmask = 1 << MCTRL_OBIT(s->pdata.mctrl_cfg[port->line], sig); + if (state) + sccnxp_write(port, SCCNXP_SOP_REG, bitmask); + else + sccnxp_write(port, SCCNXP_ROP_REG, bitmask); + } +} + +static void sccnxp_handle_rx(struct uart_port *port) +{ + u8 sr; + unsigned int ch, flag; + struct tty_struct *tty = tty_port_tty_get(&port->state->port); + + if (!tty) + return; + + for (;;) { + sr = sccnxp_port_read(port, SCCNXP_SR_REG); + if (!(sr & SR_RXRDY)) + break; + sr &= SR_PE | SR_FE | SR_OVR | SR_BRK; + + ch = sccnxp_port_read(port, SCCNXP_RHR_REG); + + port->icount.rx++; + flag = TTY_NORMAL; + + if (unlikely(sr)) { + if (sr & SR_BRK) { + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } else if (sr & SR_PE) + port->icount.parity++; + else if (sr & SR_FE) + port->icount.frame++; + else if (sr & SR_OVR) + port->icount.overrun++; + + sr &= port->read_status_mask; + if (sr & SR_BRK) + flag = TTY_BREAK; + else if (sr & SR_PE) + flag = TTY_PARITY; + else if (sr & SR_FE) + flag = TTY_FRAME; + else if (sr & SR_OVR) + flag = TTY_OVERRUN; + } + + if (uart_handle_sysrq_char(port, ch)) + continue; + + if (sr & port->ignore_status_mask) + continue; + + uart_insert_char(port, sr, SR_OVR, ch, flag); + } + + tty_flip_buffer_push(tty); + + tty_kref_put(tty); +} + +static void sccnxp_handle_tx(struct uart_port *port) +{ + u8 sr; + struct circ_buf *xmit = &port->state->xmit; + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + if (unlikely(port->x_char)) { + sccnxp_port_write(port, SCCNXP_THR_REG, port->x_char); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + /* Disable TX if FIFO is empty */ + if (sccnxp_port_read(port, SCCNXP_SR_REG) & SR_TXEMT) { + sccnxp_disable_irq(port, IMR_TXRDY); + + /* Set direction to input */ + if (s->flags & SCCNXP_HAVE_IO) + sccnxp_set_bit(port, DIR_OP, 0); + } + return; + } + + while (!uart_circ_empty(xmit)) { + sr = sccnxp_port_read(port, SCCNXP_SR_REG); + if (!(sr & SR_TXRDY)) + break; + + sccnxp_port_write(port, SCCNXP_THR_REG, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); +} + +static irqreturn_t sccnxp_ist(int irq, void *dev_id) +{ + int i; + u8 isr; + struct sccnxp_port *s = (struct sccnxp_port *)dev_id; + + mutex_lock(&s->sccnxp_mutex); + + for (;;) { + isr = sccnxp_read(&s->port[0], SCCNXP_ISR_REG); + isr &= s->imr; + if (!isr) + break; + + dev_dbg(s->port[0].dev, "IRQ status: 0x%02x\n", isr); + + for (i = 0; i < s->uart.nr; i++) { + if (isr & ISR_RXRDY(i)) + sccnxp_handle_rx(&s->port[i]); + if (isr & ISR_TXRDY(i)) + sccnxp_handle_tx(&s->port[i]); + } + } + + mutex_unlock(&s->sccnxp_mutex); + + return IRQ_HANDLED; +} + +static void sccnxp_start_tx(struct uart_port *port) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + mutex_lock(&s->sccnxp_mutex); + + /* Set direction to output */ + if (s->flags & SCCNXP_HAVE_IO) + sccnxp_set_bit(port, DIR_OP, 1); + + sccnxp_enable_irq(port, IMR_TXRDY); + + mutex_unlock(&s->sccnxp_mutex); +} + +static void sccnxp_stop_tx(struct uart_port *port) +{ + /* Do nothing */ +} + +static void sccnxp_stop_rx(struct uart_port *port) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + mutex_lock(&s->sccnxp_mutex); + sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE); + mutex_unlock(&s->sccnxp_mutex); +} + +static unsigned int sccnxp_tx_empty(struct uart_port *port) +{ + u8 val; + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + mutex_lock(&s->sccnxp_mutex); + val = sccnxp_port_read(port, SCCNXP_SR_REG); + mutex_unlock(&s->sccnxp_mutex); + + return (val & SR_TXEMT) ? TIOCSER_TEMT : 0; +} + +static void sccnxp_enable_ms(struct uart_port *port) +{ + /* Do nothing */ +} + +static void sccnxp_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + if (!(s->flags & SCCNXP_HAVE_IO)) + return; + + mutex_lock(&s->sccnxp_mutex); + + sccnxp_set_bit(port, DTR_OP, mctrl & TIOCM_DTR); + sccnxp_set_bit(port, RTS_OP, mctrl & TIOCM_RTS); + + mutex_unlock(&s->sccnxp_mutex); +} + +static unsigned int sccnxp_get_mctrl(struct uart_port *port) +{ + u8 bitmask, ipr; + struct sccnxp_port *s = dev_get_drvdata(port->dev); + unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR; + + if (!(s->flags & SCCNXP_HAVE_IO)) + return mctrl; + + mutex_lock(&s->sccnxp_mutex); + + ipr = ~sccnxp_read(port, SCCNXP_IPCR_REG); + + if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(DSR_IP)) { + bitmask = 1 << MCTRL_IBIT(s->pdata.mctrl_cfg[port->line], + DSR_IP); + mctrl &= ~TIOCM_DSR; + mctrl |= (ipr & bitmask) ? TIOCM_DSR : 0; + } + if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(CTS_IP)) { + bitmask = 1 << MCTRL_IBIT(s->pdata.mctrl_cfg[port->line], + CTS_IP); + mctrl &= ~TIOCM_CTS; + mctrl |= (ipr & bitmask) ? TIOCM_CTS : 0; + } + if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(DCD_IP)) { + bitmask = 1 << MCTRL_IBIT(s->pdata.mctrl_cfg[port->line], + DCD_IP); + mctrl &= ~TIOCM_CAR; + mctrl |= (ipr & bitmask) ? TIOCM_CAR : 0; + } + if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(RNG_IP)) { + bitmask = 1 << MCTRL_IBIT(s->pdata.mctrl_cfg[port->line], + RNG_IP); + mctrl &= ~TIOCM_RNG; + mctrl |= (ipr & bitmask) ? TIOCM_RNG : 0; + } + + mutex_unlock(&s->sccnxp_mutex); + + return mctrl; +} + +static void sccnxp_break_ctl(struct uart_port *port, int break_state) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + mutex_lock(&s->sccnxp_mutex); + sccnxp_port_write(port, SCCNXP_CR_REG, break_state ? + CR_CMD_START_BREAK : CR_CMD_STOP_BREAK); + mutex_unlock(&s->sccnxp_mutex); +} + +static void sccnxp_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + u8 mr1, mr2; + int baud; + + mutex_lock(&s->sccnxp_mutex); + + /* Mask termios capabilities we don't support */ + termios->c_cflag &= ~CMSPAR; + termios->c_iflag &= ~(IXON | IXOFF | IXANY); + + /* Disable RX & TX, reset break condition, status and FIFOs */ + sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_RX_RESET | + CR_RX_DISABLE | CR_TX_DISABLE); + sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_TX_RESET); + sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_STATUS_RESET); + sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_BREAK_RESET); + + /* Word size */ + switch (termios->c_cflag & CSIZE) { + case CS5: + mr1 = MR1_BITS_5; + break; + case CS6: + mr1 = MR1_BITS_6; + break; + case CS7: + mr1 = MR1_BITS_7; + break; + default: + case CS8: + mr1 = MR1_BITS_8; + break; + } + + /* Parity */ + if (termios->c_cflag & PARENB) { + if (termios->c_cflag & PARODD) + mr1 |= MR1_PAR_ODD; + } else + mr1 |= MR1_PAR_NO; + + /* Stop bits */ + mr2 = (termios->c_cflag & CSTOPB) ? MR2_STOP2 : MR2_STOP1; + + /* Update desired format */ + sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_MRPTR1); + sccnxp_port_write(port, SCCNXP_MR_REG, mr1); + sccnxp_port_write(port, SCCNXP_MR_REG, mr2); + + /* Set read status mask */ + port->read_status_mask = SR_OVR; + if (termios->c_iflag & INPCK) + port->read_status_mask |= SR_PE | SR_FE; + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= SR_BRK; + + /* Set status ignore mask */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNBRK) + port->ignore_status_mask |= SR_BRK; + if (!(termios->c_cflag & CREAD)) + port->ignore_status_mask |= SR_PE | SR_OVR | SR_FE | SR_BRK; + + /* Setup baudrate */ + baud = uart_get_baud_rate(port, termios, old, 50, + (s->flags & SCCNXP_HAVE_MR0) ? + 230400 : 38400); + sccnxp_set_baud(port, baud); + + /* Update timeout according to new baud rate */ + uart_update_timeout(port, termios->c_cflag, baud); + + /* Enable RX & TX */ + sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_ENABLE | CR_TX_ENABLE); + + mutex_unlock(&s->sccnxp_mutex); +} + +static int sccnxp_startup(struct uart_port *port) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + mutex_lock(&s->sccnxp_mutex); + + if (s->flags & SCCNXP_HAVE_IO) { + /* Outputs are controlled manually */ + sccnxp_write(port, SCCNXP_OPCR_REG, 0); + } + + /* Reset break condition, status and FIFOs */ + sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_RX_RESET); + sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_TX_RESET); + sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_STATUS_RESET); + sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_BREAK_RESET); + + /* Enable RX & TX */ + sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_ENABLE | CR_TX_ENABLE); + + /* Enable RX interrupt */ + sccnxp_enable_irq(port, IMR_RXRDY); + + mutex_unlock(&s->sccnxp_mutex); + + return 0; +} + +static void sccnxp_shutdown(struct uart_port *port) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + mutex_lock(&s->sccnxp_mutex); + + /* Disable interrupts */ + sccnxp_disable_irq(port, IMR_TXRDY | IMR_RXRDY); + + /* Disable TX & RX */ + sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE | CR_TX_DISABLE); + + /* Leave direction to input */ + if (s->flags & SCCNXP_HAVE_IO) + sccnxp_set_bit(port, DIR_OP, 0); + + mutex_unlock(&s->sccnxp_mutex); +} + +static const char *sccnxp_type(struct uart_port *port) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + return (port->type == PORT_SC26XX) ? s->name : NULL; +} + +static void sccnxp_release_port(struct uart_port *port) +{ + /* Do nothing */ +} + +static int sccnxp_request_port(struct uart_port *port) +{ + /* Do nothing */ + return 0; +} + +static void sccnxp_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) + port->type = PORT_SC26XX; +} + +static int sccnxp_verify_port(struct uart_port *port, struct serial_struct *s) +{ + if ((s->type == PORT_UNKNOWN) || (s->type == PORT_SC26XX)) + return 0; + if (s->irq == port->irq) + return 0; + + return -EINVAL; +} + +static const struct uart_ops sccnxp_ops = { + .tx_empty = sccnxp_tx_empty, + .set_mctrl = sccnxp_set_mctrl, + .get_mctrl = sccnxp_get_mctrl, + .stop_tx = sccnxp_stop_tx, + .start_tx = sccnxp_start_tx, + .stop_rx = sccnxp_stop_rx, + .enable_ms = sccnxp_enable_ms, + .break_ctl = sccnxp_break_ctl, + .startup = sccnxp_startup, + .shutdown = sccnxp_shutdown, + .set_termios = sccnxp_set_termios, + .type = sccnxp_type, + .release_port = sccnxp_release_port, + .request_port = sccnxp_request_port, + .config_port = sccnxp_config_port, + .verify_port = sccnxp_verify_port, +}; + +#ifdef CONFIG_SERIAL_SCCNXP_CONSOLE +static void sccnxp_console_putchar(struct uart_port *port, int c) +{ + int tryes = 100000; + + while (tryes--) { + if (sccnxp_port_read(port, SCCNXP_SR_REG) & SR_TXRDY) { + sccnxp_port_write(port, SCCNXP_THR_REG, c); + break; + } + barrier(); + } +} + +static void sccnxp_console_write(struct console *co, const char *c, unsigned n) +{ + struct sccnxp_port *s = (struct sccnxp_port *)co->data; + struct uart_port *port = &s->port[co->index]; + + mutex_lock(&s->sccnxp_mutex); + uart_console_write(port, c, n, sccnxp_console_putchar); + mutex_unlock(&s->sccnxp_mutex); +} + +static int sccnxp_console_setup(struct console *co, char *options) +{ + struct sccnxp_port *s = (struct sccnxp_port *)co->data; + struct uart_port *port = &s->port[(co->index > 0) ? co->index : 0]; + int baud = 9600, bits = 8, parity = 'n', flow = 'n'; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} +#endif + +static int __devinit 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; + struct sccnxp_port *s; + void __iomem *membase; + + if (!res) { + dev_err(&pdev->dev, "Missing memory resource data\n"); + return -EADDRNOTAVAIL; + } + + dev_set_name(&pdev->dev, SCCNXP_NAME); + + s = devm_kzalloc(&pdev->dev, sizeof(struct sccnxp_port), GFP_KERNEL); + if (!s) { + dev_err(&pdev->dev, "Error allocating port structure\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, s); + + mutex_init(&s->sccnxp_mutex); + + /* Individual chip settings */ + switch (chiptype) { + case SCCNXP_TYPE_SC2681: + s->name = "SC2681"; + s->uart.nr = 2; + s->freq_std = 3686400; + s->addr_mask = 0x0f; + s->flags = SCCNXP_HAVE_IO; + fifosize = 3; + freq_min = 1000000; + freq_max = 4000000; + break; + case SCCNXP_TYPE_SC2691: + s->name = "SC2691"; + s->uart.nr = 1; + s->freq_std = 3686400; + s->addr_mask = 0x07; + s->flags = 0; + fifosize = 3; + freq_min = 1000000; + freq_max = 4000000; + break; + case SCCNXP_TYPE_SC2692: + s->name = "SC2692"; + s->uart.nr = 2; + s->freq_std = 3686400; + s->addr_mask = 0x0f; + s->flags = SCCNXP_HAVE_IO; + fifosize = 3; + freq_min = 1000000; + freq_max = 4000000; + break; + case SCCNXP_TYPE_SC2891: + s->name = "SC2891"; + s->uart.nr = 1; + s->freq_std = 3686400; + s->addr_mask = 0x0f; + s->flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0; + fifosize = 16; + freq_min = 100000; + freq_max = 8000000; + break; + case SCCNXP_TYPE_SC2892: + s->name = "SC2892"; + s->uart.nr = 2; + s->freq_std = 3686400; + s->addr_mask = 0x0f; + s->flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0; + fifosize = 16; + freq_min = 100000; + freq_max = 8000000; + break; + case SCCNXP_TYPE_SC28202: + s->name = "SC28202"; + s->uart.nr = 2; + s->freq_std = 14745600; + s->addr_mask = 0x7f; + s->flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0; + fifosize = 256; + freq_min = 1000000; + freq_max = 50000000; + break; + case SCCNXP_TYPE_SC68681: + s->name = "SC68681"; + s->uart.nr = 2; + s->freq_std = 3686400; + s->addr_mask = 0x0f; + s->flags = SCCNXP_HAVE_IO; + fifosize = 3; + freq_min = 1000000; + freq_max = 4000000; + break; + case SCCNXP_TYPE_SC68692: + s->name = "SC68692"; + s->uart.nr = 2; + s->freq_std = 3686400; + s->addr_mask = 0x0f; + s->flags = SCCNXP_HAVE_IO; + fifosize = 3; + freq_min = 1000000; + freq_max = 4000000; + break; + default: + dev_err(&pdev->dev, "Unsupported chip type %i\n", chiptype); + ret = -ENOTSUPP; + goto err_out; + } + + if (!pdata) { + dev_warn(&pdev->dev, + "No platform data supplied, using defaults\n"); + s->pdata.frequency = s->freq_std; + } else + memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata)); + + s->irq = platform_get_irq(pdev, 0); + if (s->irq <= 0) { + dev_err(&pdev->dev, "Missing irq resource data\n"); + ret = -ENXIO; + goto err_out; + } + + /* 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; + } + + membase = devm_request_and_ioremap(&pdev->dev, res); + if (!membase) { + dev_err(&pdev->dev, "Failed to ioremap\n"); + ret = -EIO; + goto err_out; + } + + s->uart.owner = THIS_MODULE; + s->uart.dev_name = "ttySC"; + s->uart.major = SCCNXP_MAJOR; + s->uart.minor = SCCNXP_MINOR; +#ifdef CONFIG_SERIAL_SCCNXP_CONSOLE + s->uart.cons = &s->console; + s->uart.cons->device = uart_console_device; + s->uart.cons->write = sccnxp_console_write; + s->uart.cons->setup = sccnxp_console_setup; + s->uart.cons->flags = CON_PRINTBUFFER; + s->uart.cons->index = -1; + s->uart.cons->data = s; + strcpy(s->uart.cons->name, "ttySC"); +#endif + ret = uart_register_driver(&s->uart); + if (ret) { + dev_err(&pdev->dev, "Registering UART driver failed\n"); + goto err_out; + } + + for (i = 0; i < s->uart.nr; i++) { + s->port[i].line = i; + s->port[i].dev = &pdev->dev; + s->port[i].irq = s->irq; + s->port[i].type = PORT_SC26XX; + s->port[i].fifosize = fifosize; + s->port[i].flags = UPF_SKIP_TEST | UPF_FIXED_TYPE; + s->port[i].iotype = UPIO_MEM; + 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].ops = &sccnxp_ops; + uart_add_one_port(&s->uart, &s->port[i]); + /* Set direction to input */ + if (s->flags & SCCNXP_HAVE_IO) + sccnxp_set_bit(&s->port[i], DIR_OP, 0); + } + + /* Disable interrupts */ + s->imr = 0; + sccnxp_write(&s->port[0], SCCNXP_IMR_REG, 0); + + /* Board specific configure */ + if (s->pdata.init) + s->pdata.init(); + + ret = devm_request_threaded_irq(&pdev->dev, s->irq, NULL, sccnxp_ist, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(&pdev->dev), s); + if (!ret) + return 0; + + dev_err(&pdev->dev, "Unable to reguest IRQ %i\n", s->irq); + +err_out: + platform_set_drvdata(pdev, NULL); + + return ret; +} + +static int __devexit sccnxp_remove(struct platform_device *pdev) +{ + int i; + struct sccnxp_port *s = platform_get_drvdata(pdev); + + devm_free_irq(&pdev->dev, s->irq, s); + + for (i = 0; i < s->uart.nr; i++) + uart_remove_one_port(&s->uart, &s->port[i]); + + uart_unregister_driver(&s->uart); + platform_set_drvdata(pdev, NULL); + + if (s->pdata.exit) + s->pdata.exit(); + + return 0; +} + +static const struct platform_device_id sccnxp_id_table[] = { + { "sc2681", SCCNXP_TYPE_SC2681 }, + { "sc2691", SCCNXP_TYPE_SC2691 }, + { "sc2692", SCCNXP_TYPE_SC2692 }, + { "sc2891", SCCNXP_TYPE_SC2891 }, + { "sc2892", SCCNXP_TYPE_SC2892 }, + { "sc28202", SCCNXP_TYPE_SC28202 }, + { "sc68681", SCCNXP_TYPE_SC68681 }, + { "sc68692", SCCNXP_TYPE_SC68692 }, +}; +MODULE_DEVICE_TABLE(platform, sccnxp_id_table); + +static struct platform_driver sccnxp_uart_driver = { + .driver = { + .name = SCCNXP_NAME, + .owner = THIS_MODULE, + }, + .probe = sccnxp_probe, + .remove = __devexit_p(sccnxp_remove), + .id_table = sccnxp_id_table, +}; +module_platform_driver(sccnxp_uart_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Alexander Shiyan "); +MODULE_DESCRIPTION("SCCNXP serial driver"); diff --git a/include/linux/platform_data/sccnxp.h b/include/linux/platform_data/sccnxp.h new file mode 100644 index 000000000000..7311ccd3217f --- /dev/null +++ b/include/linux/platform_data/sccnxp.h @@ -0,0 +1,93 @@ +/* + * NXP (Philips) SCC+++(SCN+++) serial driver + * + * Copyright (C) 2012 Alexander Shiyan + * + * Based on sc26xx.c, by Thomas Bogendörfer (tsbogend@alpha.franken.de) + * + * 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 __SCCNXP_H +#define __SCCNXP_H + +#define SCCNXP_MAX_UARTS 2 + +/* Output lines */ +#define LINE_OP0 1 +#define LINE_OP1 2 +#define LINE_OP2 3 +#define LINE_OP3 4 +#define LINE_OP4 5 +#define LINE_OP5 6 +#define LINE_OP6 7 +#define LINE_OP7 8 + +/* Input lines */ +#define LINE_IP0 9 +#define LINE_IP1 10 +#define LINE_IP2 11 +#define LINE_IP3 12 +#define LINE_IP4 13 +#define LINE_IP5 14 +#define LINE_IP6 15 + +/* Signals */ +#define DTR_OP 0 /* DTR */ +#define RTS_OP 4 /* RTS */ +#define DSR_IP 8 /* DSR */ +#define CTS_IP 12 /* CTS */ +#define DCD_IP 16 /* DCD */ +#define RNG_IP 20 /* RNG */ + +#define DIR_OP 24 /* Special signal for control RS-485. + * Goes high when transmit, + * then goes low. + */ + +/* Routing control signal 'sig' to line 'line' */ +#define MCTRL_SIG(sig, line) ((line) << (sig)) + +/* + * Example board initialization data: + * + * static struct resource sc2892_resources[] = { + * DEFINE_RES_MEM(UART_PHYS_START, 0x10), + * DEFINE_RES_IRQ(IRQ_EXT2), + * }; + * + * 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), + * }; + * + * static struct platform_device sc2892 = { + * .name = "sc2892", + * .id = -1, + * .resource = sc2892_resources, + * .num_resources = ARRAY_SIZE(sc2892_resources), + * .dev = { + * .platform_data = &sc2892_info, + * }, + * }; + */ + +/* 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 */ + const u32 mctrl_cfg[SCCNXP_MAX_UARTS]; + /* Called during startup */ + void (*init)(void); + /* Called before finish */ + void (*exit)(void); +}; + +#endif -- cgit v1.2.3 From f21ec3d2d46e5f2ffc06f31fe2704fdcea7a58f3 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Wed, 22 Aug 2012 22:13:36 -0400 Subject: serial: add a new helper function In most of the time, the driver needs to check if the cts flow control is enabled. But now, the driver checks the ASYNC_CTS_FLOW flag manually, which is not a grace way. So add a new wraper function to make the code tidy and clean. Signed-off-by: Huang Shijie Signed-off-by: Greg Kroah-Hartman --- drivers/char/pcmcia/synclink_cs.c | 2 +- drivers/tty/amiserial.c | 2 +- drivers/tty/cyclades.c | 2 +- drivers/tty/isicom.c | 2 +- drivers/tty/mxser.c | 2 +- drivers/tty/serial/mxs-auart.c | 2 +- drivers/tty/serial/serial_core.c | 4 ++-- drivers/tty/synclink.c | 2 +- drivers/tty/synclink_gt.c | 2 +- drivers/tty/synclinkmp.c | 2 +- include/linux/tty.h | 6 ++++++ net/irda/ircomm/ircomm_tty.c | 4 ++-- net/irda/ircomm/ircomm_tty_attach.c | 2 +- 13 files changed, 20 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 5db08c78beb5..3f57d5de3957 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -1050,7 +1050,7 @@ static void cts_change(MGSLPC_INFO *info, struct tty_struct *tty) wake_up_interruptible(&info->status_event_wait_q); wake_up_interruptible(&info->event_wait_q); - if (info->port.flags & ASYNC_CTS_FLOW) { + if (tty_port_cts_enabled(&info->port)) { if (tty->hw_stopped) { if (info->serial_signals & SerialSignal_CTS) { if (debug_level >= DEBUG_LEVEL_ISR) diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 2b7535d42e05..42d0a2581a87 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -420,7 +420,7 @@ static void check_modem_status(struct serial_state *info) tty_hangup(port->tty); } } - if (port->flags & ASYNC_CTS_FLOW) { + if (tty_port_cts_enabled(port)) { if (port->tty->hw_stopped) { if (!(status & SER_CTS)) { #if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index e3954dae5064..0a6a0bc1b598 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -727,7 +727,7 @@ static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, else tty_hangup(tty); } - if ((mdm_change & CyCTS) && (info->port.flags & ASYNC_CTS_FLOW)) { + if ((mdm_change & CyCTS) && tty_port_cts_enabled(&info->port)) { if (tty->hw_stopped) { if (mdm_status & CyCTS) { /* cy_start isn't used diff --git a/drivers/tty/isicom.c b/drivers/tty/isicom.c index 99cf22e5f2b6..d7492e183607 100644 --- a/drivers/tty/isicom.c +++ b/drivers/tty/isicom.c @@ -600,7 +600,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) port->status &= ~ISI_DCD; } - if (port->port.flags & ASYNC_CTS_FLOW) { + if (tty_port_cts_enabled(&port->port)) { if (tty->hw_stopped) { if (header & ISI_CTS) { port->port.tty->hw_stopped = 0; diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index bb2da4ca8257..cfda47dabd28 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -830,7 +830,7 @@ static void mxser_check_modem_status(struct tty_struct *tty, wake_up_interruptible(&port->port.open_wait); } - if (port->port.flags & ASYNC_CTS_FLOW) { + if (tty_port_cts_enabled(&port->port)) { if (tty->hw_stopped) { if (status & UART_MSR_CTS) { tty->hw_stopped = 0; diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 3a667eed63d6..dafeef2bfb49 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -262,7 +262,7 @@ static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl) ctrl &= ~AUART_CTRL2_RTSEN; if (mctrl & TIOCM_RTS) { - if (u->state->port.flags & ASYNC_CTS_FLOW) + if (tty_port_cts_enabled(&u->state->port)) ctrl |= AUART_CTRL2_RTSEN; } diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index bb5f23603836..137b25ce39a7 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -176,7 +176,7 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR); } - if (port->flags & ASYNC_CTS_FLOW) { + if (tty_port_cts_enabled(port)) { spin_lock_irq(&uport->lock); if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) tty->hw_stopped = 1; @@ -2509,7 +2509,7 @@ void uart_handle_cts_change(struct uart_port *uport, unsigned int status) uport->icount.cts++; - if (port->flags & ASYNC_CTS_FLOW) { + if (tty_port_cts_enabled(port)) { if (tty->hw_stopped) { if (status) { tty->hw_stopped = 0; diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index 666aa1455fc7..70e3a525bc82 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -1359,7 +1359,7 @@ static void mgsl_isr_io_pin( struct mgsl_struct *info ) } } - if ( (info->port.flags & ASYNC_CTS_FLOW) && + if (tty_port_cts_enabled(&info->port) && (status & MISCSTATUS_CTS_LATCHED) ) { if (info->port.tty->hw_stopped) { if (status & MISCSTATUS_CTS) { diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index 45f6136f4e51..b38e954eedd3 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -2053,7 +2053,7 @@ static void cts_change(struct slgt_info *info, unsigned short status) wake_up_interruptible(&info->event_wait_q); info->pending_bh |= BH_STATUS; - if (info->port.flags & ASYNC_CTS_FLOW) { + if (tty_port_cts_enabled(&info->port)) { if (info->port.tty) { if (info->port.tty->hw_stopped) { if (info->signals & SerialSignal_CTS) { diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index 53429c890a89..f17d9f3d84a2 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -2500,7 +2500,7 @@ static void isr_io_pin( SLMP_INFO *info, u16 status ) } } - if ( (info->port.flags & ASYNC_CTS_FLOW) && + if (tty_port_cts_enabled(&info->port) && (status & MISCSTATUS_CTS_LATCHED) ) { if ( info->port.tty ) { if (info->port.tty->hw_stopped) { diff --git a/include/linux/tty.h b/include/linux/tty.h index dbebd1e56bc1..9892121354cd 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -514,6 +514,12 @@ static inline struct tty_port *tty_port_get(struct tty_port *port) return port; } +/* If the cts flow control is enabled, return true. */ +static inline bool tty_port_cts_enabled(struct tty_port *port) +{ + return port->flags & ASYNC_CTS_FLOW; +} + extern struct tty_struct *tty_port_tty_get(struct tty_port *port); extern void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty); extern int tty_port_carrier_raised(struct tty_port *port); diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c index 96689906b93c..95a3a7a336ba 100644 --- a/net/irda/ircomm/ircomm_tty.c +++ b/net/irda/ircomm/ircomm_tty.c @@ -1070,7 +1070,7 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self) goto put; } } - if (tty && self->port.flags & ASYNC_CTS_FLOW) { + if (tty && tty_port_cts_enabled(&self->port)) { if (tty->hw_stopped) { if (status & IRCOMM_CTS) { IRDA_DEBUG(2, @@ -1313,7 +1313,7 @@ static void ircomm_tty_line_info(struct ircomm_tty_cb *self, struct seq_file *m) seq_puts(m, "Flags:"); sep = ' '; - if (self->port.flags & ASYNC_CTS_FLOW) { + if (tty_port_cts_enabled(&self->port)) { seq_printf(m, "%cASYNC_CTS_FLOW", sep); sep = '|'; } diff --git a/net/irda/ircomm/ircomm_tty_attach.c b/net/irda/ircomm/ircomm_tty_attach.c index 3ab70e7a0715..edab393e0c82 100644 --- a/net/irda/ircomm/ircomm_tty_attach.c +++ b/net/irda/ircomm/ircomm_tty_attach.c @@ -578,7 +578,7 @@ void ircomm_tty_link_established(struct ircomm_tty_cb *self) * will have to wait for the peer device (DCE) to raise the CTS * line. */ - if ((self->port.flags & ASYNC_CTS_FLOW) && + if (tty_port_cts_enabled(&self->port) && ((self->settings.dce & IRCOMM_CTS) == 0)) { IRDA_DEBUG(0, "%s(), waiting for CTS ...\n", __func__ ); goto put; -- cgit v1.2.3 From 23d3b8bfb8eb20e7d96afa09991e6a5ed1c83164 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 5 Sep 2012 01:02:56 +0000 Subject: net: qdisc busylock needs lockdep annotations It seems we need to provide ability for stacked devices to use specific lock_class_key for sch->busylock We could instead default l2tpeth tx_queue_len to 0 (no qdisc), but a user might use a qdisc anyway. (So same fixes are probably needed on non LLTX stacked drivers) Noticed while stressing L2TPV3 setup : ====================================================== [ INFO: possible circular locking dependency detected ] 3.6.0-rc3+ #788 Not tainted ------------------------------------------------------- netperf/4660 is trying to acquire lock: (l2tpsock){+.-...}, at: [] l2tp_xmit_skb+0x172/0xa50 [l2tp_core] but task is already holding lock: (&(&sch->busylock)->rlock){+.-...}, at: [] dev_queue_xmit+0xd75/0xe00 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 (&(&sch->busylock)->rlock){+.-...}: [] lock_acquire+0x90/0x200 [] _raw_spin_lock_irqsave+0x4c/0x60 [] __wake_up+0x32/0x70 [] tty_wakeup+0x3e/0x80 [] pty_write+0x73/0x80 [] tty_put_char+0x3c/0x40 [] process_echoes+0x142/0x330 [] n_tty_receive_buf+0x8fb/0x1230 [] flush_to_ldisc+0x142/0x1c0 [] process_one_work+0x198/0x760 [] worker_thread+0x186/0x4b0 [] kthread+0x93/0xa0 [] kernel_thread_helper+0x4/0x10 -> #0 (l2tpsock){+.-...}: [] __lock_acquire+0x1628/0x1b10 [] lock_acquire+0x90/0x200 [] _raw_spin_lock+0x41/0x50 [] l2tp_xmit_skb+0x172/0xa50 [l2tp_core] [] l2tp_eth_dev_xmit+0x32/0x60 [l2tp_eth] [] dev_hard_start_xmit+0x502/0xa70 [] sch_direct_xmit+0xfe/0x290 [] dev_queue_xmit+0x1e5/0xe00 [] ip_finish_output+0x3d0/0x890 [] ip_output+0x59/0xf0 [] ip_local_out+0x2d/0xa0 [] ip_queue_xmit+0x1c3/0x680 [] tcp_transmit_skb+0x402/0xa60 [] tcp_write_xmit+0x1f4/0xa30 [] tcp_push_one+0x30/0x40 [] tcp_sendmsg+0xe82/0x1040 [] inet_sendmsg+0x125/0x230 [] sock_sendmsg+0xdc/0xf0 [] sys_sendto+0xfe/0x130 [] system_call_fastpath+0x16/0x1b Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(&(&sch->busylock)->rlock); lock(l2tpsock); lock(&(&sch->busylock)->rlock); lock(l2tpsock); *** DEADLOCK *** 5 locks held by netperf/4660: #0: (sk_lock-AF_INET){+.+.+.}, at: [] tcp_sendmsg+0x2c/0x1040 #1: (rcu_read_lock){.+.+..}, at: [] ip_queue_xmit+0x0/0x680 #2: (rcu_read_lock_bh){.+....}, at: [] ip_finish_output+0x135/0x890 #3: (rcu_read_lock_bh){.+....}, at: [] dev_queue_xmit+0x0/0xe00 #4: (&(&sch->busylock)->rlock){+.-...}, at: [] dev_queue_xmit+0xd75/0xe00 stack backtrace: Pid: 4660, comm: netperf Not tainted 3.6.0-rc3+ #788 Call Trace: [] print_circular_bug+0x1fb/0x20c [] __lock_acquire+0x1628/0x1b10 [] ? check_usage+0x9b/0x4d0 [] ? __lock_acquire+0x2e4/0x1b10 [] lock_acquire+0x90/0x200 [] ? l2tp_xmit_skb+0x172/0xa50 [l2tp_core] [] _raw_spin_lock+0x41/0x50 [] ? l2tp_xmit_skb+0x172/0xa50 [l2tp_core] [] l2tp_xmit_skb+0x172/0xa50 [l2tp_core] [] l2tp_eth_dev_xmit+0x32/0x60 [l2tp_eth] [] dev_hard_start_xmit+0x502/0xa70 [] ? dev_hard_start_xmit+0x5e/0xa70 [] ? dev_queue_xmit+0x141/0xe00 [] sch_direct_xmit+0xfe/0x290 [] dev_queue_xmit+0x1e5/0xe00 [] ? dev_hard_start_xmit+0xa70/0xa70 [] ip_finish_output+0x3d0/0x890 [] ? ip_finish_output+0x135/0x890 [] ip_output+0x59/0xf0 [] ip_local_out+0x2d/0xa0 [] ip_queue_xmit+0x1c3/0x680 [] ? ip_local_out+0xa0/0xa0 [] tcp_transmit_skb+0x402/0xa60 [] ? tcp_md5_do_lookup+0x18e/0x1a0 [] tcp_write_xmit+0x1f4/0xa30 [] tcp_push_one+0x30/0x40 [] tcp_sendmsg+0xe82/0x1040 [] inet_sendmsg+0x125/0x230 [] ? inet_create+0x6b0/0x6b0 [] ? sock_update_classid+0xc2/0x3b0 [] ? sock_update_classid+0x130/0x3b0 [] sock_sendmsg+0xdc/0xf0 [] ? fget_light+0x3f9/0x4f0 [] sys_sendto+0xfe/0x130 [] ? trace_hardirqs_on+0xd/0x10 [] ? _raw_spin_unlock_irq+0x30/0x50 [] ? finish_task_switch+0x83/0xf0 [] ? finish_task_switch+0x46/0xf0 [] ? sysret_check+0x1b/0x56 [] system_call_fastpath+0x16/0x1b Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 ++ net/l2tp/l2tp_eth.c | 3 ++- net/sched/sch_generic.c | 9 ++++++++- 3 files changed, 12 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index ccac82e61604..ae3153c0db0a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1322,6 +1322,8 @@ struct net_device { /* phy device may attach itself for hardware timestamping */ struct phy_device *phydev; + struct lock_class_key *qdisc_tx_busylock; + /* group the device belongs to */ int group; diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c index f9ee74deeac2..ba89997bcd2e 100644 --- a/net/l2tp/l2tp_eth.c +++ b/net/l2tp/l2tp_eth.c @@ -67,6 +67,7 @@ static inline struct l2tp_eth_net *l2tp_eth_pernet(struct net *net) return net_generic(net, l2tp_eth_net_id); } +static struct lock_class_key l2tp_eth_tx_busylock; static int l2tp_eth_dev_init(struct net_device *dev) { struct l2tp_eth *priv = netdev_priv(dev); @@ -74,7 +75,7 @@ static int l2tp_eth_dev_init(struct net_device *dev) priv->dev = dev; eth_hw_addr_random(dev); memset(&dev->broadcast[0], 0xff, 6); - + dev->qdisc_tx_busylock = &l2tp_eth_tx_busylock; return 0; } diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 6c4d5fe53ce8..aefc1504dc88 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -527,6 +527,8 @@ struct Qdisc_ops pfifo_fast_ops __read_mostly = { }; EXPORT_SYMBOL(pfifo_fast_ops); +static struct lock_class_key qdisc_tx_busylock; + struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue, struct Qdisc_ops *ops) { @@ -534,6 +536,7 @@ struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue, struct Qdisc *sch; unsigned int size = QDISC_ALIGN(sizeof(*sch)) + ops->priv_size; int err = -ENOBUFS; + struct net_device *dev = dev_queue->dev; p = kzalloc_node(size, GFP_KERNEL, netdev_queue_numa_node_read(dev_queue)); @@ -553,12 +556,16 @@ struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue, } INIT_LIST_HEAD(&sch->list); skb_queue_head_init(&sch->q); + spin_lock_init(&sch->busylock); + lockdep_set_class(&sch->busylock, + dev->qdisc_tx_busylock ?: &qdisc_tx_busylock); + sch->ops = ops; sch->enqueue = ops->enqueue; sch->dequeue = ops->dequeue; sch->dev_queue = dev_queue; - dev_hold(qdisc_dev(sch)); + dev_hold(dev); atomic_set(&sch->refcnt, 1); return sch; -- cgit v1.2.3 From ef2c7d7b59708d54213c7556a82d14de9a7e4475 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Wed, 5 Sep 2012 02:12:42 +0000 Subject: ipv6: fix handling of blackhole and prohibit routes When adding a blackhole or a prohibit route, they were handling like classic routes. Moreover, it was only possible to add this kind of routes by specifying an interface. Bug already reported here: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=498498 Before the patch: $ ip route add blackhole 2001::1/128 RTNETLINK answers: No such device $ ip route add blackhole 2001::1/128 dev eth0 $ ip -6 route | grep 2001 2001::1 dev eth0 metric 1024 After: $ ip route add blackhole 2001::1/128 $ ip -6 route | grep 2001 blackhole 2001::1 dev lo metric 1024 error -22 v2: wrong patch v3: add a field fc_type in struct fib6_config to store RTN_* type Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- include/net/ip6_fib.h | 1 + net/ipv6/route.c | 32 ++++++++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index 0fedbd8d747a..cd64cf35a49f 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -37,6 +37,7 @@ struct fib6_config { int fc_ifindex; u32 fc_flags; u32 fc_protocol; + u32 fc_type; /* only 8 bits are used */ struct in6_addr fc_dst; struct in6_addr fc_src; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 0ddf2d132e7f..fa264447a751 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1463,8 +1463,18 @@ int ip6_route_add(struct fib6_config *cfg) } rt->dst.output = ip6_pkt_discard_out; rt->dst.input = ip6_pkt_discard; - rt->dst.error = -ENETUNREACH; rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; + switch (cfg->fc_type) { + case RTN_BLACKHOLE: + rt->dst.error = -EINVAL; + break; + case RTN_PROHIBIT: + rt->dst.error = -EACCES; + break; + default: + rt->dst.error = -ENETUNREACH; + break; + } goto install_route; } @@ -2261,8 +2271,11 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, cfg->fc_src_len = rtm->rtm_src_len; cfg->fc_flags = RTF_UP; cfg->fc_protocol = rtm->rtm_protocol; + cfg->fc_type = rtm->rtm_type; - if (rtm->rtm_type == RTN_UNREACHABLE) + if (rtm->rtm_type == RTN_UNREACHABLE || + rtm->rtm_type == RTN_BLACKHOLE || + rtm->rtm_type == RTN_PROHIBIT) cfg->fc_flags |= RTF_REJECT; if (rtm->rtm_type == RTN_LOCAL) @@ -2391,8 +2404,19 @@ static int rt6_fill_node(struct net *net, rtm->rtm_table = table; if (nla_put_u32(skb, RTA_TABLE, table)) goto nla_put_failure; - if (rt->rt6i_flags & RTF_REJECT) - rtm->rtm_type = RTN_UNREACHABLE; + if (rt->rt6i_flags & RTF_REJECT) { + switch (rt->dst.error) { + case -EINVAL: + rtm->rtm_type = RTN_BLACKHOLE; + break; + case -EACCES: + rtm->rtm_type = RTN_PROHIBIT; + break; + default: + rtm->rtm_type = RTN_UNREACHABLE; + break; + } + } else if (rt->rt6i_flags & RTF_LOCAL) rtm->rtm_type = RTN_LOCAL; else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK)) -- cgit v1.2.3 From 3735ba8db8e6ea22ad3ff524328926d8d780a884 Mon Sep 17 00:00:00 2001 From: Shengzhou Liu Date: Wed, 22 Aug 2012 18:17:00 +0800 Subject: powerpc/usb: fix bug of CPU hang when missing USB PHY clock when missing USB PHY clock, kernel booting up will hang during USB initialization. We should check USBGP[PHY_CLK_VALID] bit to avoid CPU hanging in this case. Signed-off-by: Shengzhou Liu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-fsl.c | 58 +++++++++++++++++++++++++++++++-------------- drivers/usb/host/ehci-fsl.h | 1 + include/linux/fsl_devices.h | 1 + 3 files changed, 42 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index b7451b29c5ac..11ff4b4dc7ad 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -210,11 +210,11 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd, usb_put_hcd(hcd); } -static void ehci_fsl_setup_phy(struct usb_hcd *hcd, +static int ehci_fsl_setup_phy(struct usb_hcd *hcd, enum fsl_usb2_phy_modes phy_mode, unsigned int port_offset) { - u32 portsc, temp; + u32 portsc; struct ehci_hcd *ehci = hcd_to_ehci(hcd); void __iomem *non_ehci = hcd->regs; struct device *dev = hcd->self.controller; @@ -232,9 +232,15 @@ static void ehci_fsl_setup_phy(struct usb_hcd *hcd, case FSL_USB2_PHY_ULPI: if (pdata->controller_ver) { /* controller version 1.6 or above */ - temp = in_be32(non_ehci + FSL_SOC_USB_CTRL); - out_be32(non_ehci + FSL_SOC_USB_CTRL, temp | - USB_CTRL_USB_EN | ULPI_PHY_CLK_SEL); + setbits32(non_ehci + FSL_SOC_USB_CTRL, + ULPI_PHY_CLK_SEL); + /* + * Due to controller issue of PHY_CLK_VALID in ULPI + * mode, we set USB_CTRL_USB_EN before checking + * PHY_CLK_VALID, otherwise PHY_CLK_VALID doesn't work. + */ + clrsetbits_be32(non_ehci + FSL_SOC_USB_CTRL, + UTMI_PHY_EN, USB_CTRL_USB_EN); } portsc |= PORT_PTS_ULPI; break; @@ -247,9 +253,7 @@ static void ehci_fsl_setup_phy(struct usb_hcd *hcd, case FSL_USB2_PHY_UTMI: if (pdata->controller_ver) { /* controller version 1.6 or above */ - temp = in_be32(non_ehci + FSL_SOC_USB_CTRL); - out_be32(non_ehci + FSL_SOC_USB_CTRL, temp | - UTMI_PHY_EN | USB_CTRL_USB_EN); + setbits32(non_ehci + FSL_SOC_USB_CTRL, UTMI_PHY_EN); mdelay(FSL_UTMI_PHY_DLY); /* Delay for UTMI PHY CLK to become stable - 10ms*/ } @@ -262,23 +266,34 @@ static void ehci_fsl_setup_phy(struct usb_hcd *hcd, case FSL_USB2_PHY_NONE: break; } + + if ((pdata->controller_ver) && ((phy_mode == FSL_USB2_PHY_ULPI) || + (phy_mode == FSL_USB2_PHY_UTMI))) { + /* check PHY_CLK_VALID to get phy clk valid */ + if (!spin_event_timeout(in_be32(non_ehci + FSL_SOC_USB_CTRL) & + PHY_CLK_VALID, FSL_USB_PHY_CLK_TIMEOUT, 0)) { + printk(KERN_WARNING "fsl-ehci: USB PHY clock invalid\n"); + return -EINVAL; + } + } + ehci_writel(ehci, portsc, &ehci->regs->port_status[port_offset]); + + if (phy_mode != FSL_USB2_PHY_ULPI) + setbits32(non_ehci + FSL_SOC_USB_CTRL, USB_CTRL_USB_EN); + + return 0; } -static void ehci_fsl_usb_setup(struct ehci_hcd *ehci) +static int ehci_fsl_usb_setup(struct ehci_hcd *ehci) { struct usb_hcd *hcd = ehci_to_hcd(ehci); struct fsl_usb2_platform_data *pdata; void __iomem *non_ehci = hcd->regs; - u32 temp; pdata = hcd->self.controller->platform_data; - /* Enable PHY interface in the control reg. */ if (pdata->have_sysif_regs) { - temp = in_be32(non_ehci + FSL_SOC_USB_CTRL); - out_be32(non_ehci + FSL_SOC_USB_CTRL, temp | 0x00000004); - /* * Turn on cache snooping hardware, since some PowerPC platforms * wholly rely on hardware to deal with cache coherent @@ -293,7 +308,8 @@ static void ehci_fsl_usb_setup(struct ehci_hcd *ehci) if ((pdata->operating_mode == FSL_USB2_DR_HOST) || (pdata->operating_mode == FSL_USB2_DR_OTG)) - ehci_fsl_setup_phy(hcd, pdata->phy_mode, 0); + if (ehci_fsl_setup_phy(hcd, pdata->phy_mode, 0)) + return -EINVAL; if (pdata->operating_mode == FSL_USB2_MPH_HOST) { unsigned int chip, rev, svr; @@ -307,9 +323,12 @@ static void ehci_fsl_usb_setup(struct ehci_hcd *ehci) ehci->has_fsl_port_bug = 1; if (pdata->port_enables & FSL_USB2_PORT0_ENABLED) - ehci_fsl_setup_phy(hcd, pdata->phy_mode, 0); + if (ehci_fsl_setup_phy(hcd, pdata->phy_mode, 0)) + return -EINVAL; + if (pdata->port_enables & FSL_USB2_PORT1_ENABLED) - ehci_fsl_setup_phy(hcd, pdata->phy_mode, 1); + if (ehci_fsl_setup_phy(hcd, pdata->phy_mode, 1)) + return -EINVAL; } if (pdata->have_sysif_regs) { @@ -322,12 +341,15 @@ static void ehci_fsl_usb_setup(struct ehci_hcd *ehci) #endif out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001); } + + return 0; } /* called after powerup, by probe or system-pm "wakeup" */ static int ehci_fsl_reinit(struct ehci_hcd *ehci) { - ehci_fsl_usb_setup(ehci); + if (ehci_fsl_usb_setup(ehci)) + return -EINVAL; ehci_port_power(ehci, 0); return 0; diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h index 88403684d10b..dbd292e9f0a7 100644 --- a/drivers/usb/host/ehci-fsl.h +++ b/drivers/usb/host/ehci-fsl.h @@ -61,4 +61,5 @@ #define PLL_RESET (1<<8) #define UTMI_PHY_EN (1<<9) #define ULPI_PHY_CLK_SEL (1<<10) +#define PHY_CLK_VALID (1<<17) #endif /* _EHCI_FSL_H */ diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index 15be561e7397..ccfc4bb3dc48 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -19,6 +19,7 @@ #define FSL_UTMI_PHY_DLY 10 /*As per P1010RM, delay for UTMI PHY CLK to become stable - 10ms*/ +#define FSL_USB_PHY_CLK_TIMEOUT 1000 /* uSec */ #define FSL_USB_VER_OLD 0 #define FSL_USB_VER_1_6 1 #define FSL_USB_VER_2_2 2 -- cgit v1.2.3 From 9394b80c35760d13492a3a895add2891bc64bf86 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 4 Sep 2012 14:43:39 -0600 Subject: regulator: tps6586x: add support for SYS rail Device have SYS rail which is always ON. It is system power bus. LDO5 and LDO_RTC get powered through this rail internally. Add support for this rail and make the LDO5/LDO_RTC supply by it. Update document accordingly. [swarren: Instantiate the sys regulator from board-harmony-power.c to avoid regression.] Signed-off-by: Laxman Dewangan Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/tps6586x.txt | 65 +++++++++++++--------- arch/arm/mach-tegra/board-harmony-power.c | 12 +++- drivers/mfd/tps6586x.c | 13 +++++ drivers/regulator/tps6586x-regulator.c | 20 ++++++- include/linux/mfd/tps6586x.h | 1 + 5 files changed, 81 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/regulator/tps6586x.txt b/Documentation/devicetree/bindings/regulator/tps6586x.txt index da80c2ae0915..a2436e1edfc1 100644 --- a/Documentation/devicetree/bindings/regulator/tps6586x.txt +++ b/Documentation/devicetree/bindings/regulator/tps6586x.txt @@ -8,7 +8,8 @@ Required properties: - gpio-controller: mark the device as a GPIO controller - regulators: list of regulators provided by this controller, must have property "regulator-compatible" to match their hardware counterparts: - sm[0-2], ldo[0-9] and ldo_rtc + sys, sm[0-2], ldo[0-9] and ldo_rtc +- sys-supply: The input supply for SYS. - vin-sm0-supply: The input supply for the SM0. - vin-sm1-supply: The input supply for the SM1. - vin-sm2-supply: The input supply for the SM2. @@ -20,6 +21,9 @@ Required properties: Each regulator is defined using the standard binding for regulators. +Note: LDO5 and LDO_RTC is supplied by SYS regulator internally and driver + take care of making proper parent child relationship. + Example: pmu: tps6586x@34 { @@ -30,6 +34,7 @@ Example: #gpio-cells = <2>; gpio-controller; + sys-supply = <&some_reg>; vin-sm0-supply = <&some_reg>; vin-sm1-supply = <&some_reg>; vin-sm2-supply = <&some_reg>; @@ -43,8 +48,16 @@ Example: #address-cells = <1>; #size-cells = <0>; - sm0_reg: regulator@0 { + sys_reg: regulator@0 { reg = <0>; + regulator-compatible = "sys"; + regulator-name = "vdd_sys"; + regulator-boot-on; + regulator-always-on; + }; + + sm0_reg: regulator@1 { + reg = <1>; regulator-compatible = "sm0"; regulator-min-microvolt = < 725000>; regulator-max-microvolt = <1500000>; @@ -52,8 +65,8 @@ Example: regulator-always-on; }; - sm1_reg: regulator@1 { - reg = <1>; + sm1_reg: regulator@2 { + reg = <2>; regulator-compatible = "sm1"; regulator-min-microvolt = < 725000>; regulator-max-microvolt = <1500000>; @@ -61,8 +74,8 @@ Example: regulator-always-on; }; - sm2_reg: regulator@2 { - reg = <2>; + sm2_reg: regulator@3 { + reg = <3>; regulator-compatible = "sm2"; regulator-min-microvolt = <3000000>; regulator-max-microvolt = <4550000>; @@ -70,72 +83,72 @@ Example: regulator-always-on; }; - ldo0_reg: regulator@3 { - reg = <3>; + ldo0_reg: regulator@4 { + reg = <4>; regulator-compatible = "ldo0"; regulator-name = "PCIE CLK"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; }; - ldo1_reg: regulator@4 { - reg = <4>; + ldo1_reg: regulator@5 { + reg = <5>; regulator-compatible = "ldo1"; regulator-min-microvolt = < 725000>; regulator-max-microvolt = <1500000>; }; - ldo2_reg: regulator@5 { - reg = <5>; + ldo2_reg: regulator@6 { + reg = <6>; regulator-compatible = "ldo2"; regulator-min-microvolt = < 725000>; regulator-max-microvolt = <1500000>; }; - ldo3_reg: regulator@6 { - reg = <6>; + ldo3_reg: regulator@7 { + reg = <7>; regulator-compatible = "ldo3"; regulator-min-microvolt = <1250000>; regulator-max-microvolt = <3300000>; }; - ldo4_reg: regulator@7 { - reg = <7>; + ldo4_reg: regulator@8 { + reg = <8>; regulator-compatible = "ldo4"; regulator-min-microvolt = <1700000>; regulator-max-microvolt = <2475000>; }; - ldo5_reg: regulator@8 { - reg = <8>; + ldo5_reg: regulator@9 { + reg = <9>; regulator-compatible = "ldo5"; regulator-min-microvolt = <1250000>; regulator-max-microvolt = <3300000>; }; - ldo6_reg: regulator@9 { - reg = <9>; + ldo6_reg: regulator@10 { + reg = <10>; regulator-compatible = "ldo6"; regulator-min-microvolt = <1250000>; regulator-max-microvolt = <3300000>; }; - ldo7_reg: regulator@10 { - reg = <10>; + ldo7_reg: regulator@11 { + reg = <11>; regulator-compatible = "ldo7"; regulator-min-microvolt = <1250000>; regulator-max-microvolt = <3300000>; }; - ldo8_reg: regulator@11 { - reg = <11>; + ldo8_reg: regulator@12 { + reg = <12>; regulator-compatible = "ldo8"; regulator-min-microvolt = <1250000>; regulator-max-microvolt = <3300000>; }; - ldo9_reg: regulator@12 { - reg = <12>; + ldo9_reg: regulator@13 { + reg = <13>; regulator-compatible = "ldo9"; regulator-min-microvolt = <1250000>; regulator-max-microvolt = <3300000>; diff --git a/arch/arm/mach-tegra/board-harmony-power.c b/arch/arm/mach-tegra/board-harmony-power.c index b7344beec102..94486e7e9dfd 100644 --- a/arch/arm/mach-tegra/board-harmony-power.c +++ b/arch/arm/mach-tegra/board-harmony-power.c @@ -67,6 +67,13 @@ static struct regulator_init_data ldo0_data = { }, \ } +static struct regulator_init_data sys_data = { + .supply_regulator = "vdd_5v0", + .constraints = { + .name = "vdd_sys", + }, +}; + HARMONY_REGULATOR_INIT(sm0, "vdd_sm0", "vdd_sys", 725, 1500, 1); HARMONY_REGULATOR_INIT(sm1, "vdd_sm1", "vdd_sys", 725, 1500, 1); HARMONY_REGULATOR_INIT(sm2, "vdd_sm2", "vdd_sys", 3000, 4550, 1); @@ -74,7 +81,7 @@ HARMONY_REGULATOR_INIT(ldo1, "vdd_ldo1", "vdd_sm2", 725, 1500, 1); HARMONY_REGULATOR_INIT(ldo2, "vdd_ldo2", "vdd_sm2", 725, 1500, 0); HARMONY_REGULATOR_INIT(ldo3, "vdd_ldo3", "vdd_sm2", 1250, 3300, 1); HARMONY_REGULATOR_INIT(ldo4, "vdd_ldo4", "vdd_sm2", 1700, 2475, 1); -HARMONY_REGULATOR_INIT(ldo5, "vdd_ldo5", NULL, 1250, 3300, 1); +HARMONY_REGULATOR_INIT(ldo5, "vdd_ldo5", "vdd_sys", 1250, 3300, 1); HARMONY_REGULATOR_INIT(ldo6, "vdd_ldo6", "vdd_sm2", 1250, 3300, 0); HARMONY_REGULATOR_INIT(ldo7, "vdd_ldo7", "vdd_sm2", 1250, 3300, 0); HARMONY_REGULATOR_INIT(ldo8, "vdd_ldo8", "vdd_sm2", 1250, 3300, 0); @@ -88,6 +95,7 @@ HARMONY_REGULATOR_INIT(ldo9, "vdd_ldo9", "vdd_sm2", 1250, 3300, 1); } static struct tps6586x_subdev_info tps_devs[] = { + TPS_REG(SYS, &sys_data), TPS_REG(SM_0, &sm0_data), TPS_REG(SM_1, &sm1_data), TPS_REG(SM_2, &sm2_data), @@ -120,7 +128,7 @@ static struct i2c_board_info __initdata harmony_regulators[] = { int __init harmony_regulator_init(void) { - regulator_register_always_on(0, "vdd_sys", + regulator_register_always_on(0, "vdd_5v0", NULL, 0, 5000000); if (machine_is_harmony()) { diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c index 353c34812120..380a3c886d31 100644 --- a/drivers/mfd/tps6586x.c +++ b/drivers/mfd/tps6586x.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -346,6 +347,7 @@ failed: #ifdef CONFIG_OF static struct of_regulator_match tps6586x_matches[] = { + { .name = "sys", .driver_data = (void *)TPS6586X_ID_SYS }, { .name = "sm0", .driver_data = (void *)TPS6586X_ID_SM_0 }, { .name = "sm1", .driver_data = (void *)TPS6586X_ID_SM_1 }, { .name = "sm2", .driver_data = (void *)TPS6586X_ID_SM_2 }, @@ -369,6 +371,7 @@ static struct tps6586x_platform_data *tps6586x_parse_dt(struct i2c_client *clien struct tps6586x_platform_data *pdata; struct tps6586x_subdev_info *devs; struct device_node *regs; + const char *sys_rail_name = NULL; unsigned int count; unsigned int i, j; int err; @@ -391,12 +394,22 @@ static struct tps6586x_platform_data *tps6586x_parse_dt(struct i2c_client *clien return NULL; for (i = 0, j = 0; i < num && j < count; i++) { + struct regulator_init_data *reg_idata; + if (!tps6586x_matches[i].init_data) continue; + reg_idata = tps6586x_matches[i].init_data; devs[j].name = "tps6586x-regulator"; devs[j].platform_data = tps6586x_matches[i].init_data; devs[j].id = (int)tps6586x_matches[i].driver_data; + if (devs[j].id == TPS6586X_ID_SYS) + sys_rail_name = reg_idata->constraints.name; + + if ((devs[j].id == TPS6586X_ID_LDO_5) || + (devs[j].id == TPS6586X_ID_LDO_RTC)) + reg_idata->supply_regulator = sys_rail_name; + devs[j].of_node = tps6586x_matches[i].of_node; j++; } diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c index 19241fc30050..82125269b667 100644 --- a/drivers/regulator/tps6586x-regulator.c +++ b/drivers/regulator/tps6586x-regulator.c @@ -162,6 +162,9 @@ static struct regulator_ops tps6586x_regulator_ops = { .disable = tps6586x_regulator_disable, }; +static struct regulator_ops tps6586x_sys_regulator_ops = { +}; + static const unsigned int tps6586x_ldo0_voltages[] = { 1200000, 1500000, 1800000, 2500000, 2700000, 2850000, 3100000, 3300000, }; @@ -230,15 +233,28 @@ static const unsigned int tps6586x_dvm_voltages[] = { TPS6586X_REGULATOR_DVM_GOREG(goreg, gobit) \ } +#define TPS6586X_SYS_REGULATOR() \ +{ \ + .desc = { \ + .supply_name = "sys", \ + .name = "REG-SYS", \ + .ops = &tps6586x_sys_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = TPS6586X_ID_SYS, \ + .owner = THIS_MODULE, \ + }, \ +} + static struct tps6586x_regulator tps6586x_regulator[] = { + TPS6586X_SYS_REGULATOR(), TPS6586X_LDO(LDO_0, "vinldo01", ldo0, SUPPLYV1, 5, 3, ENC, 0, END, 0), TPS6586X_LDO(LDO_3, "vinldo23", ldo, SUPPLYV4, 0, 3, ENC, 2, END, 2), - TPS6586X_LDO(LDO_5, NULL, ldo, SUPPLYV6, 0, 3, ENE, 6, ENE, 6), + TPS6586X_LDO(LDO_5, "REG-SYS", ldo, SUPPLYV6, 0, 3, ENE, 6, ENE, 6), TPS6586X_LDO(LDO_6, "vinldo678", ldo, SUPPLYV3, 0, 3, ENC, 4, END, 4), TPS6586X_LDO(LDO_7, "vinldo678", ldo, SUPPLYV3, 3, 3, ENC, 5, END, 5), TPS6586X_LDO(LDO_8, "vinldo678", ldo, SUPPLYV2, 5, 3, ENC, 6, END, 6), TPS6586X_LDO(LDO_9, "vinldo9", ldo, SUPPLYV6, 3, 3, ENE, 7, ENE, 7), - TPS6586X_LDO(LDO_RTC, NULL, ldo, SUPPLYV4, 3, 3, V4, 7, V4, 7), + TPS6586X_LDO(LDO_RTC, "REG-SYS", ldo, SUPPLYV4, 3, 3, V4, 7, V4, 7), TPS6586X_LDO(LDO_1, "vinldo01", dvm, SUPPLYV1, 0, 5, ENC, 1, END, 1), TPS6586X_LDO(SM_2, "vin-sm2", sm2, SUPPLYV2, 0, 5, ENC, 7, END, 7), diff --git a/include/linux/mfd/tps6586x.h b/include/linux/mfd/tps6586x.h index f350fd0ba1df..94514710a03f 100644 --- a/include/linux/mfd/tps6586x.h +++ b/include/linux/mfd/tps6586x.h @@ -14,6 +14,7 @@ #define TPS6586X_SLEW_RATE_MASK 0x07 enum { + TPS6586X_ID_SYS, TPS6586X_ID_SM_0, TPS6586X_ID_SM_1, TPS6586X_ID_SM_2, -- cgit v1.2.3 From d5829eac5f7cfff89c6d1cf11717eee97cf030d0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 5 Sep 2012 17:09:15 +0200 Subject: target: fix use-after-free with PSCSI sense data The pointer to the sense buffer is fetched by transport_get_sense_data, but this is called by target_complete_ok_work long after pscsi_req_done has freed the struct that contains it. Pass instead the fabric's sense buffer to transport_complete, and copy the data to it directly in transport_complete. Setting SCF_TRANSPORT_TASK_SENSE also becomes a duty of transport_complete. Signed-off-by: Paolo Bonzini Cc: stable@vger.kernel.org Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_pscsi.c | 21 +++++++------------- drivers/target/target_core_transport.c | 36 +++++++++++++--------------------- include/target/target_core_backend.h | 4 +++- 3 files changed, 24 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c index 5552fa7426bc..5f7151d90344 100644 --- a/drivers/target/target_core_pscsi.c +++ b/drivers/target/target_core_pscsi.c @@ -667,7 +667,8 @@ static void pscsi_free_device(void *p) kfree(pdv); } -static int pscsi_transport_complete(struct se_cmd *cmd, struct scatterlist *sg) +static void pscsi_transport_complete(struct se_cmd *cmd, struct scatterlist *sg, + unsigned char *sense_buffer) { struct pscsi_dev_virt *pdv = cmd->se_dev->dev_ptr; struct scsi_device *sd = pdv->pdv_sd; @@ -679,7 +680,7 @@ static int pscsi_transport_complete(struct se_cmd *cmd, struct scatterlist *sg) * not been allocated because TCM is handling the emulation directly. */ if (!pt) - return 0; + return; cdb = &pt->pscsi_cdb[0]; result = pt->pscsi_result; @@ -750,10 +751,10 @@ after_mode_sense: } after_mode_select: - if (status_byte(result) & CHECK_CONDITION) - return 1; - - return 0; + if (sense_buffer && (status_byte(result) & CHECK_CONDITION)) { + memcpy(sense_buffer, pt->pscsi_sense, TRANSPORT_SENSE_BUFFER); + cmd->se_cmd_flags |= SCF_TRANSPORT_TASK_SENSE; + } } enum { @@ -1184,13 +1185,6 @@ fail: return -ENOMEM; } -static unsigned char *pscsi_get_sense_buffer(struct se_cmd *cmd) -{ - struct pscsi_plugin_task *pt = cmd->priv; - - return pt->pscsi_sense; -} - /* pscsi_get_device_rev(): * * @@ -1273,7 +1267,6 @@ static struct se_subsystem_api pscsi_template = { .check_configfs_dev_params = pscsi_check_configfs_dev_params, .set_configfs_dev_params = pscsi_set_configfs_dev_params, .show_configfs_dev_params = pscsi_show_configfs_dev_params, - .get_sense_buffer = pscsi_get_sense_buffer, .get_device_rev = pscsi_get_device_rev, .get_device_type = pscsi_get_device_type, .get_blocks = pscsi_get_blocks, diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 040f05fde4d6..8a29e3fd0195 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -568,38 +568,31 @@ static void target_complete_failure_work(struct work_struct *work) } /* - * Used to obtain Sense Data from underlying Linux/SCSI struct scsi_cmnd + * Used when asking transport to copy Sense Data from the underlying + * Linux/SCSI struct scsi_cmnd */ -static void transport_get_sense_data(struct se_cmd *cmd) +static unsigned char *transport_get_sense_buffer(struct se_cmd *cmd) { - unsigned char *buffer = cmd->sense_buffer, *sense_buffer = NULL; + unsigned char *buffer = cmd->sense_buffer; struct se_device *dev = cmd->se_dev; - unsigned long flags; u32 offset = 0; WARN_ON(!cmd->se_lun); if (!dev) - return; - - spin_lock_irqsave(&cmd->t_state_lock, flags); - if (cmd->se_cmd_flags & SCF_SENT_CHECK_CONDITION) { - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - return; - } + return NULL; - sense_buffer = dev->transport->get_sense_buffer(cmd); - spin_unlock_irqrestore(&cmd->t_state_lock, flags); + if (cmd->se_cmd_flags & SCF_SENT_CHECK_CONDITION) + return NULL; offset = cmd->se_tfo->set_fabric_sense_len(cmd, TRANSPORT_SENSE_BUFFER); - memcpy(&buffer[offset], sense_buffer, TRANSPORT_SENSE_BUFFER); - /* Automatically padded */ cmd->scsi_sense_length = TRANSPORT_SENSE_BUFFER + offset; - pr_debug("HBA_[%u]_PLUG[%s]: Set SAM STATUS: 0x%02x and sense\n", + pr_debug("HBA_[%u]_PLUG[%s]: Requesting sense for SAM STATUS: 0x%02x\n", dev->se_hba->hba_id, dev->transport->name, cmd->scsi_status); + return &buffer[offset]; } void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status) @@ -615,11 +608,11 @@ void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status) cmd->transport_state &= ~CMD_T_BUSY; if (dev && dev->transport->transport_complete) { - if (dev->transport->transport_complete(cmd, - cmd->t_data_sg) != 0) { - cmd->se_cmd_flags |= SCF_TRANSPORT_TASK_SENSE; + dev->transport->transport_complete(cmd, + cmd->t_data_sg, + transport_get_sense_buffer(cmd)); + if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) success = 1; - } } /* @@ -1987,12 +1980,11 @@ static void target_complete_ok_work(struct work_struct *work) schedule_work(&cmd->se_dev->qf_work_queue); /* - * Check if we need to retrieve a sense buffer from + * Check if we need to send a sense buffer from * the struct se_cmd in question. */ if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) { WARN_ON(!cmd->scsi_status); - transport_get_sense_data(cmd); ret = transport_send_check_condition_and_sense( cmd, 0, 1); if (ret == -EAGAIN || ret == -ENOMEM) diff --git a/include/target/target_core_backend.h b/include/target/target_core_backend.h index f1405d335a96..941c84bf1065 100644 --- a/include/target/target_core_backend.h +++ b/include/target/target_core_backend.h @@ -23,7 +23,9 @@ struct se_subsystem_api { struct se_device *(*create_virtdevice)(struct se_hba *, struct se_subsystem_dev *, void *); void (*free_device)(void *); - int (*transport_complete)(struct se_cmd *cmd, struct scatterlist *); + void (*transport_complete)(struct se_cmd *cmd, + struct scatterlist *, + unsigned char *); int (*parse_cdb)(struct se_cmd *cmd); ssize_t (*check_configfs_dev_params)(struct se_hba *, -- cgit v1.2.3 From f61870ee6f8cc77a844e22f26c58028078df7167 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 28 Aug 2012 22:37:13 +0200 Subject: usb: remove libusual The "Low Performance USB Block driver" has been removed which a user of libusual. Now we have only the usb-storage driver as the only driver in tree. This makes libusual needless. This patch removes libusal, fixes up all users. The usual-table is now linked into usb-storage. usb_usual.h remains in public include directory because some staging users seem to need it. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- drivers/staging/keucr/usb.c | 2 +- drivers/usb/storage/Kconfig | 14 --- drivers/usb/storage/Makefile | 9 +- drivers/usb/storage/alauda.c | 2 +- drivers/usb/storage/cypress_atacb.c | 2 +- drivers/usb/storage/datafab.c | 2 +- drivers/usb/storage/ene_ub6250.c | 2 +- drivers/usb/storage/freecom.c | 2 +- drivers/usb/storage/isd200.c | 5 +- drivers/usb/storage/jumpshot.c | 2 +- drivers/usb/storage/karma.c | 2 +- drivers/usb/storage/libusual.c | 243 ------------------------------------ drivers/usb/storage/onetouch.c | 2 +- drivers/usb/storage/realtek_cr.c | 2 +- drivers/usb/storage/sddr09.c | 2 +- drivers/usb/storage/sddr55.c | 2 +- drivers/usb/storage/shuttle_usbat.c | 2 +- drivers/usb/storage/unusual_devs.h | 36 +++--- drivers/usb/storage/usb.c | 17 +-- drivers/usb/storage/usual-tables.c | 18 +-- include/linux/usb_usual.h | 24 ---- 21 files changed, 42 insertions(+), 350 deletions(-) delete mode 100644 drivers/usb/storage/libusual.c (limited to 'include') diff --git a/drivers/staging/keucr/usb.c b/drivers/staging/keucr/usb.c index 483303402735..55a0b82c6391 100644 --- a/drivers/staging/keucr/usb.c +++ b/drivers/staging/keucr/usb.c @@ -320,7 +320,7 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id) us->subclass = idesc->bInterfaceSubClass; us->protocol = idesc->bInterfaceProtocol; - us->fflags = USB_US_ORIG_FLAGS(id->driver_info); + us->fflags = id->driver_info; us->Power_IsResum = false; if (us->fflags & US_FL_IGNORE_DEVICE) diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig index 7691c866637b..0ae7bb64b5ea 100644 --- a/drivers/usb/storage/Kconfig +++ b/drivers/usb/storage/Kconfig @@ -213,17 +213,3 @@ config USB_UAS say 'Y' or 'M' here and the kernel will use the right driver. If you compile this driver as a module, it will be named uas. - -config USB_LIBUSUAL - bool "The shared table of common (or usual) storage devices" - depends on USB - help - This module contains a table of common (or usual) devices - for usb-storage and ub drivers, and allows to switch binding - of these devices without rebuilding modules. - - Typical syntax of /etc/modprobe.d/*conf is: - - options libusual bias="ub" - - If unsure, say N. diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile index 82e6416a2d47..4cd55481b309 100644 --- a/drivers/usb/storage/Makefile +++ b/drivers/usb/storage/Makefile @@ -12,16 +12,9 @@ obj-$(CONFIG_USB_STORAGE) += usb-storage.o usb-storage-y := scsiglue.o protocol.o transport.o usb.o usb-storage-y += initializers.o sierra_ms.o option_ms.o - +usb-storage-y += usual-tables.o usb-storage-$(CONFIG_USB_STORAGE_DEBUG) += debug.o -ifeq ($(CONFIG_USB_LIBUSUAL),) - usb-storage-y += usual-tables.o -else - obj-$(CONFIG_USB) += usb-libusual.o - usb-libusual-y := libusual.o usual-tables.o -endif - obj-$(CONFIG_USB_STORAGE_ALAUDA) += ums-alauda.o obj-$(CONFIG_USB_STORAGE_CYPRESS_ATACB) += ums-cypress.o obj-$(CONFIG_USB_STORAGE_DATAFAB) += ums-datafab.o diff --git a/drivers/usb/storage/alauda.c b/drivers/usb/storage/alauda.c index bab8c8fe8290..be5564cc8e01 100644 --- a/drivers/usb/storage/alauda.c +++ b/drivers/usb/storage/alauda.c @@ -137,7 +137,7 @@ static int init_alauda(struct us_data *us); vendorName, productName, useProtocol, useTransport, \ initFunction, flags) \ { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ - .driver_info = (flags)|(USB_US_TYPE_STOR<<24) } + .driver_info = (flags) } static struct usb_device_id alauda_usb_ids[] = { # include "unusual_alauda.h" diff --git a/drivers/usb/storage/cypress_atacb.c b/drivers/usb/storage/cypress_atacb.c index 5fe451d16e68..070b5c0ebbf9 100644 --- a/drivers/usb/storage/cypress_atacb.c +++ b/drivers/usb/storage/cypress_atacb.c @@ -41,7 +41,7 @@ MODULE_LICENSE("GPL"); vendorName, productName, useProtocol, useTransport, \ initFunction, flags) \ { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ - .driver_info = (flags)|(USB_US_TYPE_STOR<<24) } + .driver_info = (flags) } static struct usb_device_id cypress_usb_ids[] = { # include "unusual_cypress.h" diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c index 35e9c51e6696..494fee5af41d 100644 --- a/drivers/usb/storage/datafab.c +++ b/drivers/usb/storage/datafab.c @@ -86,7 +86,7 @@ static int datafab_determine_lun(struct us_data *us, vendorName, productName, useProtocol, useTransport, \ initFunction, flags) \ { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ - .driver_info = (flags)|(USB_US_TYPE_STOR<<24) } + .driver_info = (flags) } static struct usb_device_id datafab_usb_ids[] = { # include "unusual_datafab.h" diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index 95edee53d860..118b134a1dad 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -52,7 +52,7 @@ MODULE_FIRMWARE(MS_RW_FIRMWARE); vendorName, productName, useProtocol, useTransport, \ initFunction, flags) \ { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ - .driver_info = (flags)|(USB_US_TYPE_STOR<<24) } + .driver_info = (flags)} static struct usb_device_id ene_ub6250_usb_ids[] = { # include "unusual_ene_ub6250.h" diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c index 042cf9ef3153..e6df087dca9d 100644 --- a/drivers/usb/storage/freecom.c +++ b/drivers/usb/storage/freecom.c @@ -117,7 +117,7 @@ static int init_freecom(struct us_data *us); vendorName, productName, useProtocol, useTransport, \ initFunction, flags) \ { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ - .driver_info = (flags)|(USB_US_TYPE_STOR<<24) } + .driver_info = (flags) } static struct usb_device_id freecom_usb_ids[] = { # include "unusual_freecom.h" diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index 31fa24e7e68a..ecea47877364 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -74,7 +74,7 @@ static int isd200_Initialization(struct us_data *us); vendorName, productName, useProtocol, useTransport, \ initFunction, flags) \ { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ - .driver_info = (flags)|(USB_US_TYPE_STOR<<24) } + .driver_info = (flags) } static struct usb_device_id isd200_usb_ids[] = { # include "unusual_isd200.h" @@ -83,7 +83,6 @@ static struct usb_device_id isd200_usb_ids[] = { MODULE_DEVICE_TABLE(usb, isd200_usb_ids); #undef UNUSUAL_DEV -#undef USUAL_DEV /* * The flags table @@ -105,8 +104,6 @@ static struct us_unusual_dev isd200_unusual_dev_list[] = { }; #undef UNUSUAL_DEV -#undef USUAL_DEV - /* Timeout defines (in Seconds) */ diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c index e3b97383186a..ddc78780b1ad 100644 --- a/drivers/usb/storage/jumpshot.c +++ b/drivers/usb/storage/jumpshot.c @@ -69,7 +69,7 @@ MODULE_LICENSE("GPL"); vendorName, productName, useProtocol, useTransport, \ initFunction, flags) \ { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ - .driver_info = (flags)|(USB_US_TYPE_STOR<<24) } + .driver_info = (flags) } static struct usb_device_id jumpshot_usb_ids[] = { # include "unusual_jumpshot.h" diff --git a/drivers/usb/storage/karma.c b/drivers/usb/storage/karma.c index a8708eae9788..f085ffb606c8 100644 --- a/drivers/usb/storage/karma.c +++ b/drivers/usb/storage/karma.c @@ -57,7 +57,7 @@ static int rio_karma_init(struct us_data *us); vendorName, productName, useProtocol, useTransport, \ initFunction, flags) \ { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ - .driver_info = (flags)|(USB_US_TYPE_STOR<<24) } + .driver_info = (flags) } static struct usb_device_id karma_usb_ids[] = { # include "unusual_karma.h" diff --git a/drivers/usb/storage/libusual.c b/drivers/usb/storage/libusual.c deleted file mode 100644 index fe3ffe1459b2..000000000000 --- a/drivers/usb/storage/libusual.c +++ /dev/null @@ -1,243 +0,0 @@ -/* - * libusual - * - * The libusual contains the table of devices common for ub and usb-storage. - */ -#include -#include -#include -#include -#include -#include -#include - -/* - */ -#define USU_MOD_FL_THREAD 1 /* Thread is running */ -#define USU_MOD_FL_PRESENT 2 /* The module is loaded */ - -struct mod_status { - unsigned long fls; -}; - -static struct mod_status stat[3]; -static DEFINE_SPINLOCK(usu_lock); - -/* - */ -#define USB_US_DEFAULT_BIAS USB_US_TYPE_STOR -static atomic_t usu_bias = ATOMIC_INIT(USB_US_DEFAULT_BIAS); - -#define BIAS_NAME_SIZE (sizeof("usb-storage")) -static const char *bias_names[3] = { "none", "usb-storage", "ub" }; - -static DEFINE_MUTEX(usu_probe_mutex); -static DECLARE_COMPLETION(usu_end_notify); -static atomic_t total_threads = ATOMIC_INIT(0); - -static int usu_probe_thread(void *arg); - -/* - * @type: the module type as an integer - */ -void usb_usual_set_present(int type) -{ - struct mod_status *st; - unsigned long flags; - - if (type <= 0 || type >= 3) - return; - st = &stat[type]; - spin_lock_irqsave(&usu_lock, flags); - st->fls |= USU_MOD_FL_PRESENT; - spin_unlock_irqrestore(&usu_lock, flags); -} -EXPORT_SYMBOL_GPL(usb_usual_set_present); - -void usb_usual_clear_present(int type) -{ - struct mod_status *st; - unsigned long flags; - - if (type <= 0 || type >= 3) - return; - st = &stat[type]; - spin_lock_irqsave(&usu_lock, flags); - st->fls &= ~USU_MOD_FL_PRESENT; - spin_unlock_irqrestore(&usu_lock, flags); -} -EXPORT_SYMBOL_GPL(usb_usual_clear_present); - -/* - * Match the calling driver type against the table. - * Returns: 0 if the device matches. - */ -int usb_usual_check_type(const struct usb_device_id *id, int caller_type) -{ - int id_type = USB_US_TYPE(id->driver_info); - - if (caller_type <= 0 || caller_type >= 3) - return -EINVAL; - - /* Drivers grab fixed assignment devices */ - if (id_type == caller_type) - return 0; - /* Drivers grab devices biased to them */ - if (id_type == USB_US_TYPE_NONE && caller_type == atomic_read(&usu_bias)) - return 0; - return -ENODEV; -} -EXPORT_SYMBOL_GPL(usb_usual_check_type); - -/* - */ -static int usu_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - int rc; - unsigned long type; - struct task_struct* task; - unsigned long flags; - - type = USB_US_TYPE(id->driver_info); - if (type == 0) - type = atomic_read(&usu_bias); - - spin_lock_irqsave(&usu_lock, flags); - if ((stat[type].fls & (USU_MOD_FL_THREAD|USU_MOD_FL_PRESENT)) != 0) { - spin_unlock_irqrestore(&usu_lock, flags); - return -ENXIO; - } - stat[type].fls |= USU_MOD_FL_THREAD; - spin_unlock_irqrestore(&usu_lock, flags); - - task = kthread_run(usu_probe_thread, (void*)type, "libusual_%ld", type); - if (IS_ERR(task)) { - rc = PTR_ERR(task); - printk(KERN_WARNING "libusual: " - "Unable to start the thread for %s: %d\n", - bias_names[type], rc); - spin_lock_irqsave(&usu_lock, flags); - stat[type].fls &= ~USU_MOD_FL_THREAD; - spin_unlock_irqrestore(&usu_lock, flags); - return rc; /* Not being -ENXIO causes a message printed */ - } - atomic_inc(&total_threads); - - return -ENXIO; -} - -static void usu_disconnect(struct usb_interface *intf) -{ - ; /* We should not be here. */ -} - -static struct usb_driver usu_driver = { - .name = "libusual", - .probe = usu_probe, - .disconnect = usu_disconnect, - .id_table = usb_storage_usb_ids, -}; - -/* - * A whole new thread for a purpose of request_module seems quite stupid. - * The request_module forks once inside again. However, if we attempt - * to load a storage module from our own modprobe thread, that module - * references our symbols, which cannot be resolved until our module is - * initialized. I wish there was a way to wait for the end of initialization. - * The module notifier reports MODULE_STATE_COMING only. - * So, we wait until module->init ends as the next best thing. - */ -static int usu_probe_thread(void *arg) -{ - int type = (unsigned long) arg; - struct mod_status *st = &stat[type]; - int rc; - unsigned long flags; - - mutex_lock(&usu_probe_mutex); - rc = request_module(bias_names[type]); - spin_lock_irqsave(&usu_lock, flags); - if (rc == 0 && (st->fls & USU_MOD_FL_PRESENT) == 0) { - /* - * This should not happen, but let us keep tabs on it. - */ - printk(KERN_NOTICE "libusual: " - "modprobe for %s succeeded, but module is not present\n", - bias_names[type]); - } - st->fls &= ~USU_MOD_FL_THREAD; - spin_unlock_irqrestore(&usu_lock, flags); - mutex_unlock(&usu_probe_mutex); - - complete_and_exit(&usu_end_notify, 0); -} - -/* - */ -static int __init usb_usual_init(void) -{ - int rc; - - mutex_lock(&usu_probe_mutex); - rc = usb_register(&usu_driver); - mutex_unlock(&usu_probe_mutex); - return rc; -} - -static void __exit usb_usual_exit(void) -{ - /* - * We do not check for any drivers present, because - * they keep us pinned with symbol references. - */ - - usb_deregister(&usu_driver); - - while (atomic_read(&total_threads) > 0) { - wait_for_completion(&usu_end_notify); - atomic_dec(&total_threads); - } -} - -/* - * Validate and accept the bias parameter. - */ -static int usu_set_bias(const char *bias_s, struct kernel_param *kp) -{ - int i; - int len; - int bias_n = 0; - - len = strlen(bias_s); - if (len == 0) - return -EDOM; - if (bias_s[len-1] == '\n') - --len; - - for (i = 1; i < 3; i++) { - if (strncmp(bias_s, bias_names[i], len) == 0) { - bias_n = i; - break; - } - } - if (bias_n == 0) - return -EINVAL; - - atomic_set(&usu_bias, bias_n); - return 0; -} - -static int usu_get_bias(char *buffer, struct kernel_param *kp) -{ - return strlen(strcpy(buffer, bias_names[atomic_read(&usu_bias)])); -} - -module_init(usb_usual_init); -module_exit(usb_usual_exit); - -module_param_call(bias, usu_set_bias, usu_get_bias, NULL, S_IRUGO|S_IWUSR); -__MODULE_PARM_TYPE(bias, "string"); -MODULE_PARM_DESC(bias, "Bias to usb-storage or ub"); - -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c index 886567a3806d..cb79de61f4c8 100644 --- a/drivers/usb/storage/onetouch.c +++ b/drivers/usb/storage/onetouch.c @@ -67,7 +67,7 @@ struct usb_onetouch { vendorName, productName, useProtocol, useTransport, \ initFunction, flags) \ { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ - .driver_info = (flags)|(USB_US_TYPE_STOR<<24) } + .driver_info = (flags) } static struct usb_device_id onetouch_usb_ids[] = { # include "unusual_onetouch.h" diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c index 63cf2822e299..d36446dd7ae8 100644 --- a/drivers/usb/storage/realtek_cr.c +++ b/drivers/usb/storage/realtek_cr.c @@ -172,7 +172,7 @@ static int init_realtek_cr(struct us_data *us); initFunction, flags) \ {\ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ - .driver_info = (flags)|(USB_US_TYPE_STOR<<24)\ + .driver_info = (flags) \ } static const struct usb_device_id realtek_cr_ids[] = { diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c index 3252a62b31bc..7bd54e0d5120 100644 --- a/drivers/usb/storage/sddr09.c +++ b/drivers/usb/storage/sddr09.c @@ -69,7 +69,7 @@ static int usb_stor_sddr09_init(struct us_data *us); vendorName, productName, useProtocol, useTransport, \ initFunction, flags) \ { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ - .driver_info = (flags)|(USB_US_TYPE_STOR<<24) } + .driver_info = (flags) } static struct usb_device_id sddr09_usb_ids[] = { # include "unusual_sddr09.h" diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c index c144078065a7..d278c5a99b7a 100644 --- a/drivers/usb/storage/sddr55.c +++ b/drivers/usb/storage/sddr55.c @@ -46,7 +46,7 @@ MODULE_LICENSE("GPL"); vendorName, productName, useProtocol, useTransport, \ initFunction, flags) \ { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ - .driver_info = (flags)|(USB_US_TYPE_STOR<<24) } + .driver_info = (flags) } static struct usb_device_id sddr55_usb_ids[] = { # include "unusual_sddr55.h" diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c index fa1ceebc465c..daf2fc58ae02 100644 --- a/drivers/usb/storage/shuttle_usbat.c +++ b/drivers/usb/storage/shuttle_usbat.c @@ -168,7 +168,7 @@ static int init_usbat_flash(struct us_data *us); vendorName, productName, useProtocol, useTransport, \ initFunction, flags) \ { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ - .driver_info = (flags)|(USB_US_TYPE_STOR<<24) } + .driver_info = (flags) } static struct usb_device_id usbat_usb_ids[] = { # include "unusual_usbat.h" diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 62a31bea0634..779cd954abcb 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -2038,25 +2038,25 @@ UNUSUAL_DEV( 0xed10, 0x7636, 0x0001, 0x0001, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NOT_LOCKABLE ), /* Control/Bulk transport for all SubClass values */ -USUAL_DEV(USB_SC_RBC, USB_PR_CB, USB_US_TYPE_STOR), -USUAL_DEV(USB_SC_8020, USB_PR_CB, USB_US_TYPE_STOR), -USUAL_DEV(USB_SC_QIC, USB_PR_CB, USB_US_TYPE_STOR), -USUAL_DEV(USB_SC_UFI, USB_PR_CB, USB_US_TYPE_STOR), -USUAL_DEV(USB_SC_8070, USB_PR_CB, USB_US_TYPE_STOR), -USUAL_DEV(USB_SC_SCSI, USB_PR_CB, USB_US_TYPE_STOR), +USUAL_DEV(USB_SC_RBC, USB_PR_CB), +USUAL_DEV(USB_SC_8020, USB_PR_CB), +USUAL_DEV(USB_SC_QIC, USB_PR_CB), +USUAL_DEV(USB_SC_UFI, USB_PR_CB), +USUAL_DEV(USB_SC_8070, USB_PR_CB), +USUAL_DEV(USB_SC_SCSI, USB_PR_CB), /* Control/Bulk/Interrupt transport for all SubClass values */ -USUAL_DEV(USB_SC_RBC, USB_PR_CBI, USB_US_TYPE_STOR), -USUAL_DEV(USB_SC_8020, USB_PR_CBI, USB_US_TYPE_STOR), -USUAL_DEV(USB_SC_QIC, USB_PR_CBI, USB_US_TYPE_STOR), -USUAL_DEV(USB_SC_UFI, USB_PR_CBI, USB_US_TYPE_STOR), -USUAL_DEV(USB_SC_8070, USB_PR_CBI, USB_US_TYPE_STOR), -USUAL_DEV(USB_SC_SCSI, USB_PR_CBI, USB_US_TYPE_STOR), +USUAL_DEV(USB_SC_RBC, USB_PR_CBI), +USUAL_DEV(USB_SC_8020, USB_PR_CBI), +USUAL_DEV(USB_SC_QIC, USB_PR_CBI), +USUAL_DEV(USB_SC_UFI, USB_PR_CBI), +USUAL_DEV(USB_SC_8070, USB_PR_CBI), +USUAL_DEV(USB_SC_SCSI, USB_PR_CBI), /* Bulk-only transport for all SubClass values */ -USUAL_DEV(USB_SC_RBC, USB_PR_BULK, USB_US_TYPE_STOR), -USUAL_DEV(USB_SC_8020, USB_PR_BULK, USB_US_TYPE_STOR), -USUAL_DEV(USB_SC_QIC, USB_PR_BULK, USB_US_TYPE_STOR), -USUAL_DEV(USB_SC_UFI, USB_PR_BULK, USB_US_TYPE_STOR), -USUAL_DEV(USB_SC_8070, USB_PR_BULK, USB_US_TYPE_STOR), -USUAL_DEV(USB_SC_SCSI, USB_PR_BULK, 0), +USUAL_DEV(USB_SC_RBC, USB_PR_BULK), +USUAL_DEV(USB_SC_8020, USB_PR_BULK), +USUAL_DEV(USB_SC_QIC, USB_PR_BULK), +USUAL_DEV(USB_SC_UFI, USB_PR_BULK), +USUAL_DEV(USB_SC_8070, USB_PR_BULK), +USUAL_DEV(USB_SC_SCSI, USB_PR_BULK), diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index d012fe4329e7..12aa72630aed 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -114,7 +114,7 @@ MODULE_PARM_DESC(quirks, "supplemental list of device IDs and their quirks"); #define COMPLIANT_DEV UNUSUAL_DEV -#define USUAL_DEV(use_protocol, use_transport, use_type) \ +#define USUAL_DEV(use_protocol, use_transport) \ { \ .useProtocol = use_protocol, \ .useTransport = use_transport, \ @@ -126,7 +126,7 @@ static struct us_unusual_dev us_unusual_dev_list[] = { }; static struct us_unusual_dev for_dynamic_ids = - USUAL_DEV(USB_SC_SCSI, USB_PR_BULK, 0); + USUAL_DEV(USB_SC_SCSI, USB_PR_BULK); #undef UNUSUAL_DEV #undef COMPLIANT_DEV @@ -564,7 +564,7 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id, us->protocol = (unusual_dev->useTransport == USB_PR_DEVICE) ? idesc->bInterfaceProtocol : unusual_dev->useTransport; - us->fflags = USB_US_ORIG_FLAGS(id->driver_info); + us->fflags = id->driver_info; adjust_quirks(us); if (us->fflags & US_FL_IGNORE_DEVICE) { @@ -1041,13 +1041,10 @@ static int storage_probe(struct usb_interface *intf, int size; /* - * If libusual is configured, let it decide whether a standard - * device should be handled by usb-storage or by ub. * If the device isn't standard (is handled by a subdriver * module) then don't accept it. */ - if (usb_usual_check_type(id, USB_US_TYPE_STOR) || - usb_usual_ignore_device(intf)) + if (usb_usual_ignore_device(intf)) return -ENXIO; /* @@ -1105,10 +1102,8 @@ static int __init usb_stor_init(void) /* register the driver, return usb_register return code if error */ retval = usb_register(&usb_storage_driver); - if (retval == 0) { + if (retval == 0) pr_info("USB Mass Storage support registered.\n"); - usb_usual_set_present(USB_US_TYPE_STOR); - } return retval; } @@ -1122,8 +1117,6 @@ static void __exit usb_stor_exit(void) */ US_DEBUGP("-- calling usb_deregister()\n"); usb_deregister(&usb_storage_driver) ; - - usb_usual_clear_present(USB_US_TYPE_STOR); } module_init(usb_stor_init); diff --git a/drivers/usb/storage/usual-tables.c b/drivers/usb/storage/usual-tables.c index b96927914f89..b78a526910fb 100644 --- a/drivers/usb/storage/usual-tables.c +++ b/drivers/usb/storage/usual-tables.c @@ -33,32 +33,24 @@ #define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ vendorName, productName, useProtocol, useTransport, \ initFunction, flags) \ -{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ - .driver_info = (flags)|(USB_US_TYPE_STOR<<24) } - -#define COMPLIANT_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ - vendorName, productName, useProtocol, useTransport, \ - initFunction, flags) \ { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ .driver_info = (flags) } -#define USUAL_DEV(useProto, useTrans, useType) \ -{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, useProto, useTrans), \ - .driver_info = ((useType)<<24) } +#define COMPLIANT_DEV UNUSUAL_DEV + +#define USUAL_DEV(useProto, useTrans) \ +{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, useProto, useTrans) } struct usb_device_id usb_storage_usb_ids[] = { # include "unusual_devs.h" { } /* Terminating entry */ }; -EXPORT_SYMBOL_GPL(usb_storage_usb_ids); - MODULE_DEVICE_TABLE(usb, usb_storage_usb_ids); #undef UNUSUAL_DEV #undef COMPLIANT_DEV #undef USUAL_DEV - /* * The table of devices to ignore */ @@ -95,7 +87,6 @@ static struct ignore_entry ignore_ids[] = { #undef UNUSUAL_DEV - /* Return an error if a device is in the ignore_ids list */ int usb_usual_ignore_device(struct usb_interface *intf) { @@ -115,4 +106,3 @@ int usb_usual_ignore_device(struct usb_interface *intf) } return 0; } -EXPORT_SYMBOL_GPL(usb_usual_ignore_device); diff --git a/include/linux/usb_usual.h b/include/linux/usb_usual.h index e84e769aaddc..bf99cd01be20 100644 --- a/include/linux/usb_usual.h +++ b/include/linux/usb_usual.h @@ -72,33 +72,9 @@ enum { US_DO_ALL_FLAGS }; #undef US_FLAG -/* - * The bias field for libusual and friends. - */ -#define USB_US_TYPE_NONE 0 -#define USB_US_TYPE_STOR 1 /* usb-storage */ -#define USB_US_TYPE_UB 2 /* ub */ - -#define USB_US_TYPE(flags) (((flags) >> 24) & 0xFF) -#define USB_US_ORIG_FLAGS(flags) ((flags) & 0x00FFFFFF) - #include -/* - */ extern int usb_usual_ignore_device(struct usb_interface *intf); extern struct usb_device_id usb_storage_usb_ids[]; -#ifdef CONFIG_USB_LIBUSUAL - -extern void usb_usual_set_present(int type); -extern void usb_usual_clear_present(int type); -extern int usb_usual_check_type(const struct usb_device_id *, int type); -#else - -#define usb_usual_set_present(t) do { } while(0) -#define usb_usual_clear_present(t) do { } while(0) -#define usb_usual_check_type(id, t) (0) -#endif /* CONFIG_USB_LIBUSUAL */ - #endif /* __LINUX_USB_USUAL_H */ -- cgit v1.2.3 From a4c3ddec5c5293953d8472eb151c48a3205b738b Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Thu, 6 Sep 2012 10:42:15 +0530 Subject: usb: phy: fix build break During phy interface separation from otg.h, as the enum "usb_otg_state" was having multiple otg states info and removal of member 'state' of this enum type from usb_phy struct did not generate any compilation issues, I removed member state from struct usb_phy. As this is causing build break in musb code, adding member 'state' to usb_phy structure. Signed-off-by: Venu Byravarasu Signed-off-by: Felipe Balbi --- include/linux/usb/otg.h | 24 ------------------------ include/linux/usb/phy.h | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 65a2b6afe020..e8a5fe87c6bd 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -11,30 +11,6 @@ #include -/* OTG defines lots of enumeration states before device reset */ -enum usb_otg_state { - OTG_STATE_UNDEFINED = 0, - - /* single-role peripheral, and dual-role default-b */ - OTG_STATE_B_IDLE, - OTG_STATE_B_SRP_INIT, - OTG_STATE_B_PERIPHERAL, - - /* extra dual-role default-b states */ - OTG_STATE_B_WAIT_ACON, - OTG_STATE_B_HOST, - - /* dual-role default-a */ - OTG_STATE_A_IDLE, - OTG_STATE_A_WAIT_VRISE, - OTG_STATE_A_WAIT_BCON, - OTG_STATE_A_HOST, - OTG_STATE_A_SUSPEND, - OTG_STATE_A_PERIPHERAL, - OTG_STATE_A_WAIT_VFALL, - OTG_STATE_A_VBUS_ERR, -}; - struct usb_otg { u8 default_a; diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index 88fc16062e77..06b5bae35b29 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -26,6 +26,30 @@ enum usb_phy_type { USB_PHY_TYPE_USB3, }; +/* OTG defines lots of enumeration states before device reset */ +enum usb_otg_state { + OTG_STATE_UNDEFINED = 0, + + /* single-role peripheral, and dual-role default-b */ + OTG_STATE_B_IDLE, + OTG_STATE_B_SRP_INIT, + OTG_STATE_B_PERIPHERAL, + + /* extra dual-role default-b states */ + OTG_STATE_B_WAIT_ACON, + OTG_STATE_B_HOST, + + /* dual-role default-a */ + OTG_STATE_A_IDLE, + OTG_STATE_A_WAIT_VRISE, + OTG_STATE_A_WAIT_BCON, + OTG_STATE_A_HOST, + OTG_STATE_A_SUSPEND, + OTG_STATE_A_PERIPHERAL, + OTG_STATE_A_WAIT_VFALL, + OTG_STATE_A_VBUS_ERR, +}; + struct usb_phy; struct usb_otg; @@ -43,6 +67,7 @@ struct usb_phy { unsigned int flags; enum usb_phy_type type; + enum usb_otg_state state; enum usb_phy_events last_event; struct usb_otg *otg; -- cgit v1.2.3 From ad0ed62f340a03acca5ae3e7430a08bf289aaffe Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 24 Jul 2012 17:38:43 +0200 Subject: wireless: remove obsolete chan no/center freq conversion functions There are a number of functions that shouldn't really be used when modern functions that take the band are available in cfg80211. Remove these, but for now keep * ieee80211_freq_to_dsss_chan and * ieee80211_dsss_chan_to_freq as they're used in older drivers. Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 80 ----------------------------------------------- 1 file changed, 80 deletions(-) (limited to 'include') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index e02fc682bb68..2385119f8bb0 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1934,36 +1934,6 @@ static inline bool ieee80211_is_public_action(struct ieee80211_hdr *hdr, return mgmt->u.action.category == WLAN_CATEGORY_PUBLIC; } -/** - * ieee80211_fhss_chan_to_freq - get channel frequency - * @channel: the FHSS channel - * - * Convert IEEE802.11 FHSS channel to frequency (MHz) - * Ref IEEE 802.11-2007 section 14.6 - */ -static inline int ieee80211_fhss_chan_to_freq(int channel) -{ - if ((channel > 1) && (channel < 96)) - return channel + 2400; - else - return -1; -} - -/** - * ieee80211_freq_to_fhss_chan - get channel - * @freq: the channels frequency - * - * Convert frequency (MHz) to IEEE802.11 FHSS channel - * Ref IEEE 802.11-2007 section 14.6 - */ -static inline int ieee80211_freq_to_fhss_chan(int freq) -{ - if ((freq > 2401) && (freq < 2496)) - return freq - 2400; - else - return -1; -} - /** * ieee80211_dsss_chan_to_freq - get channel center frequency * @channel: the DSSS channel @@ -2000,56 +1970,6 @@ static inline int ieee80211_freq_to_dsss_chan(int freq) return -1; } -/* Convert IEEE802.11 HR DSSS channel to frequency (MHz) and back - * Ref IEEE 802.11-2007 section 18.4.6.2 - * - * The channels and frequencies are the same as those defined for DSSS - */ -#define ieee80211_hr_chan_to_freq(chan) ieee80211_dsss_chan_to_freq(chan) -#define ieee80211_freq_to_hr_chan(freq) ieee80211_freq_to_dsss_chan(freq) - -/* Convert IEEE802.11 ERP channel to frequency (MHz) and back - * Ref IEEE 802.11-2007 section 19.4.2 - */ -#define ieee80211_erp_chan_to_freq(chan) ieee80211_hr_chan_to_freq(chan) -#define ieee80211_freq_to_erp_chan(freq) ieee80211_freq_to_hr_chan(freq) - -/** - * ieee80211_ofdm_chan_to_freq - get channel center frequency - * @s_freq: starting frequency == (dotChannelStartingFactor/2) MHz - * @channel: the OFDM channel - * - * Convert IEEE802.11 OFDM channel to center frequency (MHz) - * Ref IEEE 802.11-2007 section 17.3.8.3.2 - */ -static inline int ieee80211_ofdm_chan_to_freq(int s_freq, int channel) -{ - if ((channel > 0) && (channel <= 200) && - (s_freq >= 4000)) - return s_freq + (channel * 5); - else - return -1; -} - -/** - * ieee80211_freq_to_ofdm_channel - get channel - * @s_freq: starting frequency == (dotChannelStartingFactor/2) MHz - * @freq: the frequency - * - * Convert frequency (MHz) to IEEE802.11 OFDM channel - * Ref IEEE 802.11-2007 section 17.3.8.3.2 - * - * This routine selects the channel with the closest center frequency. - */ -static inline int ieee80211_freq_to_ofdm_chan(int s_freq, int freq) -{ - if ((freq > (s_freq + 2)) && (freq <= (s_freq + 1202)) && - (s_freq >= 4000)) - return (freq + 2 - s_freq) / 5; - else - return -1; -} - /** * ieee80211_tu_to_usec - convert time units (TU) to microseconds * @tu: the TUs -- cgit v1.2.3 From 1f1ea6c2d9d8c0be9ec56454b05315273b5de8ce Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sun, 26 Aug 2012 11:44:43 -0700 Subject: NFSv4: Fix buffer overflow checking in __nfs4_get_acl_uncached Pass the checks made by decode_getacl back to __nfs4_get_acl_uncached so that it knows if the acl has been truncated. The current overflow checking is broken, resulting in Oopses on user-triggered nfs4_getfacl calls, and is opaque to the point where several attempts at fixing it have failed. This patch tries to clean up the code in addition to fixing the Oopses by ensuring that the overflow checks are performed in a single place (decode_getacl). If the overflow check failed, we will still be able to report the acl length, but at least we will no longer attempt to cache the acl or copy the truncated contents to user space. Reported-by: Sachin Prabhu Signed-off-by: Trond Myklebust Tested-by: Sachin Prabhu --- fs/nfs/nfs4proc.c | 31 ++++++++++++------------------- fs/nfs/nfs4xdr.c | 14 +++++--------- include/linux/nfs_xdr.h | 2 +- 3 files changed, 18 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 6b94f2d52532..1e50326d00dd 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3739,7 +3739,7 @@ static void nfs4_write_cached_acl(struct inode *inode, struct page **pages, size struct nfs4_cached_acl *acl; size_t buflen = sizeof(*acl) + acl_len; - if (pages && buflen <= PAGE_SIZE) { + if (buflen <= PAGE_SIZE) { acl = kmalloc(buflen, GFP_KERNEL); if (acl == NULL) goto out; @@ -3784,7 +3784,6 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu }; unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE); int ret = -ENOMEM, i; - size_t acl_len = 0; /* As long as we're doing a round trip to the server anyway, * let's be prepared for a page of acl data. */ @@ -3807,11 +3806,6 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu args.acl_len = npages * PAGE_SIZE; args.acl_pgbase = 0; - /* Let decode_getfacl know not to fail if the ACL data is larger than - * the page we send as a guess */ - if (buf == NULL) - res.acl_flags |= NFS4_ACL_LEN_REQUEST; - dprintk("%s buf %p buflen %zu npages %d args.acl_len %zu\n", __func__, buf, buflen, npages, args.acl_len); ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode), @@ -3819,20 +3813,19 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu if (ret) goto out_free; - acl_len = res.acl_len; - if (acl_len > args.acl_len) - nfs4_write_cached_acl(inode, NULL, 0, acl_len); - else - nfs4_write_cached_acl(inode, pages, res.acl_data_offset, - acl_len); - if (buf) { + /* Handle the case where the passed-in buffer is too short */ + if (res.acl_flags & NFS4_ACL_TRUNC) { + /* Did the user only issue a request for the acl length? */ + if (buf == NULL) + goto out_ok; ret = -ERANGE; - if (acl_len > buflen) - goto out_free; - _copy_from_pages(buf, pages, res.acl_data_offset, - acl_len); + goto out_free; } - ret = acl_len; + nfs4_write_cached_acl(inode, pages, res.acl_data_offset, res.acl_len); + if (buf) + _copy_from_pages(buf, pages, res.acl_data_offset, res.acl_len); +out_ok: + ret = res.acl_len; out_free: for (i = 0; i < npages; i++) if (pages[i]) diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 1bfbd67c556d..541e796e6db5 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -5072,18 +5072,14 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req, * are stored with the acl data to handle the problem of * variable length bitmaps.*/ res->acl_data_offset = xdr_stream_pos(xdr) - pg_offset; - - /* We ignore &savep and don't do consistency checks on - * the attr length. Let userspace figure it out.... */ res->acl_len = attrlen; - if (attrlen > (xdr->nwords << 2)) { - if (res->acl_flags & NFS4_ACL_LEN_REQUEST) { - /* getxattr interface called with a NULL buf */ - goto out; - } + + /* Check for receive buffer overflow */ + if (res->acl_len > (xdr->nwords << 2) || + res->acl_len + res->acl_data_offset > xdr->buf->page_len) { + res->acl_flags |= NFS4_ACL_TRUNC; dprintk("NFS: acl reply: attrlen %u > page_len %u\n", attrlen, xdr->nwords << 2); - return -EINVAL; } } else status = -EOPNOTSUPP; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index ac7c8ae254f2..be9cf3c7e79e 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -652,7 +652,7 @@ struct nfs_getaclargs { }; /* getxattr ACL interface flags */ -#define NFS4_ACL_LEN_REQUEST 0x0001 /* zero length getxattr buffer */ +#define NFS4_ACL_TRUNC 0x0001 /* ACL was truncated */ struct nfs_getaclres { size_t acl_len; size_t acl_data_offset; -- cgit v1.2.3 From a0f38b87de1df05acf2e3cc23ee2f02a18d80c85 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 6 Sep 2012 15:45:22 +0300 Subject: serial: add OMAP-specific defines OMAP has some extra Interrupt types which can be really useful for SW. Let's define them so we can later use those in OMAP's serial driver. Tested-by: Shubhrajyoti D Acked-by: Santosh Shilimkar Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- include/linux/serial_reg.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/serial_reg.h b/include/linux/serial_reg.h index 8ce70d76f836..5ed325e88a81 100644 --- a/include/linux/serial_reg.h +++ b/include/linux/serial_reg.h @@ -40,6 +40,10 @@ #define UART_IIR_BUSY 0x07 /* DesignWare APB Busy Detect */ +#define UART_IIR_RX_TIMEOUT 0x0c /* OMAP RX Timeout interrupt */ +#define UART_IIR_XOFF 0x10 /* OMAP XOFF/Special Character */ +#define UART_IIR_CTS_RTS_DSR 0x20 /* OMAP CTS/RTS/DSR Change */ + #define UART_FCR 2 /* Out: FIFO Control Register */ #define UART_FCR_ENABLE_FIFO 0x01 /* Enable the FIFO */ #define UART_FCR_CLEAR_RCVR 0x02 /* Clear the RCVR FIFO */ -- cgit v1.2.3 From 6915c0e487c822e2436683e14302c0b8a6155cc7 Mon Sep 17 00:00:00 2001 From: Tomas Hlavacek Date: Thu, 6 Sep 2012 03:17:18 +0200 Subject: tty: uartclk value from serial_core exposed to sysfs Added file /sys/devices/.../tty/ttySX/uartclk to allow reading uartclk value in struct uart_port in serial_core via sysfs. tty_register_device() has been generalized and refactored in order to add support for setting drvdata and attribute_group to the device. Signed-off-by: Tomas Hlavacek Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-tty | 9 +++++ drivers/tty/serial/serial_core.c | 34 ++++++++++++++++-- drivers/tty/tty_io.c | 69 +++++++++++++++++++++++++++++++------ include/linux/tty.h | 4 +++ 4 files changed, 104 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-tty b/Documentation/ABI/testing/sysfs-tty index b138b663bf54..0c430150d929 100644 --- a/Documentation/ABI/testing/sysfs-tty +++ b/Documentation/ABI/testing/sysfs-tty @@ -17,3 +17,12 @@ Description: device, like 'tty1'. The file supports poll() to detect virtual console switches. + +What: /sys/class/tty/ttyS0/uartclk +Date: Sep 2012 +Contact: Tomas Hlavacek +Description: + Shows the current uartclk value associated with the + UART port in serial_core, that is bound to TTY like ttyS0. + uartclk = 16 * baud_base + diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 19993d89db37..f629bdf2a8cf 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2309,6 +2309,36 @@ struct tty_driver *uart_console_device(struct console *co, int *index) return p->tty_driver; } +static ssize_t uart_get_attr_uartclk(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + + struct tty_port *port = dev_get_drvdata(dev); + struct uart_state *state = container_of(port, struct uart_state, port); + mutex_lock(&state->port.mutex); + ret = snprintf(buf, PAGE_SIZE, "%d\n", state->uart_port->uartclk); + mutex_unlock(&state->port.mutex); + + return ret; +} + +static DEVICE_ATTR(uartclk, S_IRUSR | S_IRGRP, uart_get_attr_uartclk, NULL); + +static struct attribute *tty_dev_attrs[] = { + &dev_attr_uartclk.attr, + NULL, + }; + +static struct attribute_group tty_dev_attr_group = { + .attrs = tty_dev_attrs, + }; + +static const struct attribute_group *tty_dev_attr_groups[] = { + &tty_dev_attr_group, + NULL + }; + /** * uart_add_one_port - attach a driver-defined port structure * @drv: pointer to the uart low level driver structure for this port @@ -2362,8 +2392,8 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) * Register the port whether it's detected or not. This allows * setserial to be used to alter this ports parameters. */ - tty_dev = tty_port_register_device(port, drv->tty_driver, uport->line, - uport->dev); + tty_dev = tty_register_device_attr(drv->tty_driver, uport->line, + uport->dev, port, tty_dev_attr_groups); if (likely(!IS_ERR(tty_dev))) { device_set_wakeup_capable(tty_dev, 1); } else { diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index d3bf91a29303..dcb30d55d39c 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -3041,9 +3041,39 @@ static int tty_cdev_add(struct tty_driver *driver, dev_t dev, struct device *tty_register_device(struct tty_driver *driver, unsigned index, struct device *device) { - struct device *ret; + return tty_register_device_attr(driver, index, device, NULL, NULL); +} +EXPORT_SYMBOL(tty_register_device); + +/** + * tty_register_device_attr - register a tty device + * @driver: the tty driver that describes the tty device + * @index: the index in the tty driver for this tty device + * @device: a struct device that is associated with this tty device. + * This field is optional, if there is no known struct device + * for this tty device it can be set to NULL safely. + * @drvdata: Driver data to be set to device. + * @attr_grp: Attribute group to be set on device. + * + * Returns a pointer to the struct device for this tty device + * (or ERR_PTR(-EFOO) on error). + * + * This call is required to be made to register an individual tty device + * if the tty driver's flags have the TTY_DRIVER_DYNAMIC_DEV bit set. If + * that bit is not set, this function should not be called by a tty + * driver. + * + * Locking: ?? + */ +struct device *tty_register_device_attr(struct tty_driver *driver, + unsigned index, struct device *device, + void *drvdata, + const struct attribute_group **attr_grp) +{ char name[64]; - dev_t dev = MKDEV(driver->major, driver->minor_start) + index; + dev_t devt = MKDEV(driver->major, driver->minor_start) + index; + struct device *dev = NULL; + int retval = -ENODEV; bool cdev = false; if (index >= driver->num) { @@ -3058,19 +3088,38 @@ struct device *tty_register_device(struct tty_driver *driver, unsigned index, tty_line_name(driver, index, name); if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) { - int error = tty_cdev_add(driver, dev, index, 1); - if (error) - return ERR_PTR(error); + retval = tty_cdev_add(driver, devt, index, 1); + if (retval) + goto error; cdev = true; } - ret = device_create(tty_class, device, dev, NULL, name); - if (IS_ERR(ret) && cdev) - cdev_del(&driver->cdevs[index]); + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + retval = -ENOMEM; + goto error; + } - return ret; + dev->devt = devt; + dev->class = tty_class; + dev->parent = device; + dev_set_name(dev, "%s", name); + dev->groups = attr_grp; + dev_set_drvdata(dev, drvdata); + + retval = device_register(dev); + if (retval) + goto error; + + return dev; + +error: + put_device(dev); + if (cdev) + cdev_del(&driver->cdevs[index]); + return ERR_PTR(retval); } -EXPORT_SYMBOL(tty_register_device); +EXPORT_SYMBOL_GPL(tty_register_device_attr); /** * tty_unregister_device - unregister a tty device diff --git a/include/linux/tty.h b/include/linux/tty.h index 9892121354cd..599d60347bf0 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -412,6 +412,10 @@ extern int tty_register_driver(struct tty_driver *driver); extern int tty_unregister_driver(struct tty_driver *driver); extern struct device *tty_register_device(struct tty_driver *driver, unsigned index, struct device *dev); +extern struct device *tty_register_device_attr(struct tty_driver *driver, + unsigned index, struct device *device, + void *drvdata, + const struct attribute_group **attr_grp); extern void tty_unregister_device(struct tty_driver *driver, unsigned index); extern int tty_read_raw_data(struct tty_struct *tty, unsigned char *bufp, int buflen); -- cgit v1.2.3 From 60e233a56609fd963c59e99bd75c663d63fa91b6 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Sun, 2 Sep 2012 15:41:34 +0200 Subject: kobject: fix oops with "input0: bad kobj_uevent_env content in show_uevent()" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fengguang Wu writes: > After the __devinit* removal series, I can still get kernel panic in > show_uevent(). So there are more sources of bug.. > > Debug patch: > > @@ -343,8 +343,11 @@ static ssize_t show_uevent(struct device > goto out; > > /* copy keys to file */ > - for (i = 0; i < env->envp_idx; i++) > + dev_err(dev, "uevent %d env[%d]: %s/.../%s\n", env->buflen, env->envp_idx, top_kobj->name, dev->kobj.name); > + for (i = 0; i < env->envp_idx; i++) { > + printk(KERN_ERR "uevent %d env[%d]: %s\n", (int)count, i, env->envp[i]); > count += sprintf(&buf[count], "%s\n", env->envp[i]); > + } > > Oops message, the env[] is again not properly initilized: > > [ 44.068623] input input0: uevent 61 env[805306368]: input0/.../input0 > [ 44.069552] uevent 0 env[0]: (null) This is a completely different CONFIG_HOTPLUG problem, only demonstrating another reason why CONFIG_HOTPLUG should go away. I had a hard time trying to disable it anyway ;-) The problem this time is lots of code assuming that a call to add_uevent_var() will guarantee that env->buflen > 0. This is not true if CONFIG_HOTPLUG is unset. So things like this end up overwriting env->envp_idx because the array index is -1: if (add_uevent_var(env, "MODALIAS=")) return -ENOMEM; len = input_print_modalias(&env->buf[env->buflen - 1], sizeof(env->buf) - env->buflen, dev, 0); Don't know what the best action is, given that there seem to be a *lot* of this around the kernel. This patch "fixes" the problem for me, but I don't know if it can be considered an appropriate fix. [ It is the correct fix for now, for 3.7 forcing CONFIG_HOTPLUG to always be on is the longterm fix, but it's too late for 3.6 and older kernels to resolve this that way - gregkh ] Reported-by: Fengguang Wu Signed-off-by: Bjørn Mork Tested-by: Fengguang Wu Cc: stable Signed-off-by: Greg Kroah-Hartman --- include/linux/kobject.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/kobject.h b/include/linux/kobject.h index fc615a97e2d3..1e57449395b1 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -224,7 +224,7 @@ static inline int kobject_uevent_env(struct kobject *kobj, static inline __printf(2, 3) int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...) -{ return 0; } +{ return -ENOMEM; } static inline int kobject_action_type(const char *buf, size_t count, enum kobject_action *type) -- cgit v1.2.3 From 657b306a7bdfca4ae1514b533a0e7c3c6d26dbc6 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 6 Sep 2012 20:27:06 +0530 Subject: usb: phy: add a new driver for omap usb2 phy All phy related programming like enabling/disabling the clocks, powering on/off the phy is taken care of by this driver. It is also used for OTG related functionality like srp. This also includes device tree support for usb2 phy driver and the documentation with device tree binding information is updated. Currently writing to control module register is taken care in this driver which will be removed once the control module driver is in place. Cc: Felipe Balbi Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- Documentation/devicetree/bindings/usb/usb-phy.txt | 17 ++ drivers/usb/phy/Kconfig | 9 + drivers/usb/phy/Makefile | 1 + drivers/usb/phy/omap-usb2.c | 271 ++++++++++++++++++++++ include/linux/usb/omap_usb.h | 46 ++++ include/linux/usb/phy_companion.h | 34 +++ 6 files changed, 378 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/usb-phy.txt create mode 100644 drivers/usb/phy/omap-usb2.c create mode 100644 include/linux/usb/omap_usb.h create mode 100644 include/linux/usb/phy_companion.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/usb/usb-phy.txt b/Documentation/devicetree/bindings/usb/usb-phy.txt new file mode 100644 index 000000000000..80d4148cb661 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/usb-phy.txt @@ -0,0 +1,17 @@ +USB PHY + +OMAP USB2 PHY + +Required properties: + - compatible: Should be "ti,omap-usb2" + - reg : Address and length of the register set for the device. Also +add the address of control module dev conf register until a driver for +control module is added + +This is usually a subnode of ocp2scp to which it is connected. + +usb2phy@4a0ad080 { + compatible = "ti,omap-usb2"; + reg = <0x4a0ad080 0x58>, + <0x4a002300 0x4>; +}; diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 2838adb225e8..63c339b3e676 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -4,6 +4,15 @@ comment "USB Physical Layer drivers" depends on USB || USB_GADGET +config OMAP_USB2 + tristate "OMAP USB2 PHY Driver" + select USB_OTG_UTILS + help + Enable this to support the transceiver that is part of SOC. This + driver takes care of all the PHY functionality apart from comparator. + The USB OTG controller communicates with the comparator using this + driver. + config USB_ISP1301 tristate "NXP ISP1301 USB transceiver support" depends on USB || USB_GADGET diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index bb948fb8654c..b069f29f1225 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -4,6 +4,7 @@ ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG +obj-$(CONFIG_OMAP_USB2) += omap-usb2.o obj-$(CONFIG_USB_ISP1301) += isp1301.o obj-$(CONFIG_MV_U3D_PHY) += mv_u3d_phy.o obj-$(CONFIG_USB_EHCI_TEGRA) += tegra_usb_phy.o diff --git a/drivers/usb/phy/omap-usb2.c b/drivers/usb/phy/omap-usb2.c new file mode 100644 index 000000000000..15ab3d6f2e8c --- /dev/null +++ b/drivers/usb/phy/omap-usb2.c @@ -0,0 +1,271 @@ +/* + * omap-usb2.c - USB PHY, talking to musb controller in OMAP. + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.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. + * + * Author: Kishon Vijay Abraham I + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * omap_usb2_set_comparator - links the comparator present in the sytem with + * this phy + * @comparator - the companion phy(comparator) for this phy + * + * The phy companion driver should call this API passing the phy_companion + * filled with set_vbus and start_srp to be used by usb phy. + * + * For use by phy companion driver + */ +int omap_usb2_set_comparator(struct phy_companion *comparator) +{ + struct omap_usb *phy; + struct usb_phy *x = usb_get_phy(USB_PHY_TYPE_USB2); + + if (IS_ERR(x)) + return -ENODEV; + + phy = phy_to_omapusb(x); + phy->comparator = comparator; + return 0; +} +EXPORT_SYMBOL_GPL(omap_usb2_set_comparator); + +/** + * omap_usb_phy_power - power on/off the phy using control module reg + * @phy: struct omap_usb * + * @on: 0 or 1, based on powering on or off the PHY + * + * XXX: Remove this function once control module driver gets merged + */ +static void omap_usb_phy_power(struct omap_usb *phy, int on) +{ + u32 val; + + if (on) { + val = readl(phy->control_dev); + if (val & PHY_PD) { + writel(~PHY_PD, phy->control_dev); + /* XXX: add proper documentation for this delay */ + mdelay(200); + } + } else { + writel(PHY_PD, phy->control_dev); + } +} + +static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled) +{ + struct omap_usb *phy = phy_to_omapusb(otg->phy); + + if (!phy->comparator) + return -ENODEV; + + return phy->comparator->set_vbus(phy->comparator, enabled); +} + +static int omap_usb_start_srp(struct usb_otg *otg) +{ + struct omap_usb *phy = phy_to_omapusb(otg->phy); + + if (!phy->comparator) + return -ENODEV; + + return phy->comparator->start_srp(phy->comparator); +} + +static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + struct usb_phy *phy = otg->phy; + + otg->host = host; + if (!host) + phy->state = OTG_STATE_UNDEFINED; + + return 0; +} + +static int omap_usb_set_peripheral(struct usb_otg *otg, + struct usb_gadget *gadget) +{ + struct usb_phy *phy = otg->phy; + + otg->gadget = gadget; + if (!gadget) + phy->state = OTG_STATE_UNDEFINED; + + return 0; +} + +static int omap_usb2_suspend(struct usb_phy *x, int suspend) +{ + u32 ret; + struct omap_usb *phy = phy_to_omapusb(x); + + if (suspend && !phy->is_suspended) { + omap_usb_phy_power(phy, 0); + pm_runtime_put_sync(phy->dev); + phy->is_suspended = 1; + } else if (!suspend && phy->is_suspended) { + ret = pm_runtime_get_sync(phy->dev); + if (ret < 0) { + dev_err(phy->dev, "get_sync failed with err %d\n", + ret); + return ret; + } + omap_usb_phy_power(phy, 1); + phy->is_suspended = 0; + } + + return 0; +} + +static int __devinit omap_usb2_probe(struct platform_device *pdev) +{ + struct omap_usb *phy; + struct usb_otg *otg; + struct resource *res; + + phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); + if (!phy) { + dev_err(&pdev->dev, "unable to allocate memory for USB2 PHY\n"); + return -ENOMEM; + } + + otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); + if (!otg) { + dev_err(&pdev->dev, "unable to allocate memory for USB OTG\n"); + return -ENOMEM; + } + + phy->dev = &pdev->dev; + + phy->phy.dev = phy->dev; + phy->phy.label = "omap-usb2"; + phy->phy.set_suspend = omap_usb2_suspend; + phy->phy.otg = otg; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + + phy->control_dev = devm_request_and_ioremap(&pdev->dev, res); + if (phy->control_dev == NULL) { + dev_err(&pdev->dev, "Failed to obtain io memory\n"); + return -ENXIO; + } + + phy->is_suspended = 1; + omap_usb_phy_power(phy, 0); + + otg->set_host = omap_usb_set_host; + otg->set_peripheral = omap_usb_set_peripheral; + otg->set_vbus = omap_usb_set_vbus; + otg->start_srp = omap_usb_start_srp; + otg->phy = &phy->phy; + + phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k"); + if (IS_ERR(phy->wkupclk)) { + dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n"); + return PTR_ERR(phy->wkupclk); + } + clk_prepare(phy->wkupclk); + + usb_add_phy(&phy->phy, USB_PHY_TYPE_USB2); + + platform_set_drvdata(pdev, phy); + + pm_runtime_enable(phy->dev); + + return 0; +} + +static int __devexit omap_usb2_remove(struct platform_device *pdev) +{ + struct omap_usb *phy = platform_get_drvdata(pdev); + + clk_unprepare(phy->wkupclk); + usb_remove_phy(&phy->phy); + + return 0; +} + +#ifdef CONFIG_PM_RUNTIME + +static int omap_usb2_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omap_usb *phy = platform_get_drvdata(pdev); + + clk_disable(phy->wkupclk); + + return 0; +} + +static int omap_usb2_runtime_resume(struct device *dev) +{ + u32 ret = 0; + struct platform_device *pdev = to_platform_device(dev); + struct omap_usb *phy = platform_get_drvdata(pdev); + + ret = clk_enable(phy->wkupclk); + if (ret < 0) + dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret); + + return ret; +} + +static const struct dev_pm_ops omap_usb2_pm_ops = { + SET_RUNTIME_PM_OPS(omap_usb2_runtime_suspend, omap_usb2_runtime_resume, + NULL) +}; + +#define DEV_PM_OPS (&omap_usb2_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif + +#ifdef CONFIG_OF +static const struct of_device_id omap_usb2_id_table[] = { + { .compatible = "ti,omap-usb2" }, + {} +}; +MODULE_DEVICE_TABLE(of, omap_usb2_id_table); +#endif + +static struct platform_driver omap_usb2_driver = { + .probe = omap_usb2_probe, + .remove = __devexit_p(omap_usb2_remove), + .driver = { + .name = "omap-usb2", + .owner = THIS_MODULE, + .pm = DEV_PM_OPS, + .of_match_table = of_match_ptr(omap_usb2_id_table), + }, +}; + +module_platform_driver(omap_usb2_driver); + +MODULE_ALIAS("platform: omap_usb2"); +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("OMAP USB2 phy driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/usb/omap_usb.h b/include/linux/usb/omap_usb.h new file mode 100644 index 000000000000..0ea17f8ae820 --- /dev/null +++ b/include/linux/usb/omap_usb.h @@ -0,0 +1,46 @@ +/* + * omap_usb.h -- omap usb2 phy header file + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.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. + * + * Author: Kishon Vijay Abraham I + * + * 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 __DRIVERS_OMAP_USB2_H +#define __DRIVERS_OMAP_USB2_H + +#include + +struct omap_usb { + struct usb_phy phy; + struct phy_companion *comparator; + struct device *dev; + u32 __iomem *control_dev; + struct clk *wkupclk; + u8 is_suspended:1; +}; + +#define PHY_PD 0x1 + +#define phy_to_omapusb(x) container_of((x), struct omap_usb, phy) + +#if defined(CONFIG_OMAP_USB2) || defined(CONFIG_OMAP_USB2_MODULE) +extern int omap_usb2_set_comparator(struct phy_companion *comparator); +#else +static inline int omap_usb2_set_comparator(struct phy_companion *comparator) +{ + return -ENODEV; +} +#endif + +#endif /* __DRIVERS_OMAP_USB_H */ diff --git a/include/linux/usb/phy_companion.h b/include/linux/usb/phy_companion.h new file mode 100644 index 000000000000..edd2ec23d282 --- /dev/null +++ b/include/linux/usb/phy_companion.h @@ -0,0 +1,34 @@ +/* + * phy-companion.h -- phy companion to indicate the comparator part of PHY + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.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. + * + * Author: Kishon Vijay Abraham I + * + * 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 __DRIVERS_PHY_COMPANION_H +#define __DRIVERS_PHY_COMPANION_H + +#include + +/* phy_companion to take care of VBUS, ID and srp capabilities */ +struct phy_companion { + + /* effective for A-peripheral, ignored for B devices */ + int (*set_vbus)(struct phy_companion *x, bool enabled); + + /* for B devices only: start session with A-Host */ + int (*start_srp)(struct phy_companion *x); +}; + +#endif /* __DRIVERS_PHY_COMPANION_H */ -- cgit v1.2.3 From 7c9ab035acb4088dbbf1fec2f478a3a9e47ba15b Mon Sep 17 00:00:00 2001 From: srinivas pandruvada Date: Wed, 5 Sep 2012 13:56:00 +0100 Subject: iio: core: Add hysteresis in channel spec Added hysteresis to the list of channel info enumeration, shared /separate bit defines and to postfix channel info strings. Signed-off-by: srinivas pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 1 + include/linux/iio/iio.h | 5 +++++ 2 files changed, 6 insertions(+) (limited to 'include') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index fa3b9f5e6c45..0499330d6e98 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -99,6 +99,7 @@ static const char * const iio_chan_info_postfix[] = { [IIO_CHAN_INFO_FREQUENCY] = "frequency", [IIO_CHAN_INFO_PHASE] = "phase", [IIO_CHAN_INFO_HARDWAREGAIN] = "hardwaregain", + [IIO_CHAN_INFO_HYSTERESIS] = "hysteresis", }; const struct iio_chan_spec diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 057d60382eac..30affa533a1f 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -35,6 +35,7 @@ enum iio_chan_info_enum { IIO_CHAN_INFO_FREQUENCY, IIO_CHAN_INFO_PHASE, IIO_CHAN_INFO_HARDWAREGAIN, + IIO_CHAN_INFO_HYSTERESIS, }; #define IIO_CHAN_INFO_SHARED_BIT(type) BIT(type*2) @@ -100,6 +101,10 @@ enum iio_chan_info_enum { IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_HARDWAREGAIN) #define IIO_CHAN_INFO_HARDWAREGAIN_SHARED_BIT \ IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_HARDWAREGAIN) +#define IIO_CHAN_INFO_HYSTERESIS_SEPARATE_BIT \ + IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_HYSTERESIS) +#define IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT \ + IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_HYSTERESIS) enum iio_endian { IIO_CPU, -- cgit v1.2.3 From 401ca24fb34aee0cedf9c4fef361e533224f15a1 Mon Sep 17 00:00:00 2001 From: srinivas pandruvada Date: Wed, 5 Sep 2012 13:56:00 +0100 Subject: HID: sensors: introduce sensor framework Adding processing for HID Sensor usage table as defined by HID 1.12, Request #: HUTRR39, dated 05 May, 2011. This driver uses HID driver framework to register, send and receive events. This uses MFD framework, so that actual processing for a specific usage id can be done in a different driver. For example an accelerometer driver can be a separate driver and use the interface provided by this driver to register for events. Signed-off-by: srinivas pandruvada Signed-off-by: Jiri Kosina Signed-off-by: Jonathan Cameron --- drivers/hid/Kconfig | 14 + drivers/hid/Makefile | 1 + drivers/hid/hid-sensor-hub.c | 695 +++++++++++++++++++++++++++++++++++++++++ include/linux/hid-sensor-hub.h | 160 ++++++++++ include/linux/hid-sensor-ids.h | 112 +++++++ 5 files changed, 982 insertions(+) create mode 100644 drivers/hid/hid-sensor-hub.c create mode 100644 include/linux/hid-sensor-hub.h create mode 100644 include/linux/hid-sensor-ids.h (limited to 'include') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index fbf49503508d..f8d314060853 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -690,6 +690,20 @@ config HID_ZYDACRON ---help--- Support for Zydacron remote control. +config HID_SENSOR_HUB + tristate "HID Sensors framework support" + depends on USB_HID + select MFD_CORE + default n + -- help--- + Support for HID Sensor framework. This creates a MFD instance + for a sensor hub and identifies all the sensors connected to it. + Each sensor is registered as a MFD cell, so that sensor specific + processing can be done in a separate driver. Each sensor + drivers can use the service provided by this driver to register + for events and handle data streams. Each sensor driver can format + data and present to user mode using input or IIO interface. + endmenu endif # HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index f975485f88b2..1173d7af20e8 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -91,6 +91,7 @@ obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o obj-$(CONFIG_HID_WACOM) += hid-wacom.o obj-$(CONFIG_HID_WALTOP) += hid-waltop.o obj-$(CONFIG_HID_WIIMOTE) += hid-wiimote.o +obj-$(CONFIG_HID_SENSOR_HUB) += hid-sensor-hub.o obj-$(CONFIG_USB_HID) += usbhid/ obj-$(CONFIG_USB_MOUSE) += usbhid/ diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c new file mode 100644 index 000000000000..34a35ba95fc1 --- /dev/null +++ b/drivers/hid/hid-sensor-hub.c @@ -0,0 +1,695 @@ +/* + * HID Sensors Driver + * Copyright (c) 2012, Intel Corporation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +#include +#include +#include +#include "usbhid/usbhid.h" +#include +#include +#include +#include +#include +#include +#include "hid-ids.h" + +/** + * struct sensor_hub_pending - Synchronous read pending information + * @status: Pending status true/false. + * @ready: Completion synchronization data. + * @usage_id: Usage id for physical device, E.g. Gyro usage id. + * @attr_usage_id: Usage Id of a field, E.g. X-AXIS for a gyro. + * @raw_size: Response size for a read request. + * @raw_data: Place holder for received response. + */ +struct sensor_hub_pending { + bool status; + struct completion ready; + u32 usage_id; + u32 attr_usage_id; + int raw_size; + u8 *raw_data; +}; + +/** + * struct sensor_hub_data - Hold a instance data for a HID hub device + * @hsdev: Stored hid instance for current hub device. + * @mutex: Mutex to serialize synchronous request. + * @lock: Spin lock to protect pending request structure. + * @pending: Holds information of pending sync read request. + * @dyn_callback_list: Holds callback function + * @dyn_callback_lock: spin lock to protect callback list + * @hid_sensor_hub_client_devs: Stores all MFD cells for a hub instance. + * @hid_sensor_client_cnt: Number of MFD cells, (no of sensors attached). + */ +struct sensor_hub_data { + struct hid_sensor_hub_device *hsdev; + struct mutex mutex; + spinlock_t lock; + struct sensor_hub_pending pending; + struct list_head dyn_callback_list; + spinlock_t dyn_callback_lock; + struct mfd_cell *hid_sensor_hub_client_devs; + int hid_sensor_client_cnt; +}; + +/** + * struct hid_sensor_hub_callbacks_list - Stores callback list + * @list: list head. + * @usage_id: usage id for a physical device. + * @usage_callback: Stores registered callback functions. + * @priv: Private data for a physical device. + */ +struct hid_sensor_hub_callbacks_list { + struct list_head list; + u32 usage_id; + struct hid_sensor_hub_callbacks *usage_callback; + void *priv; +}; + +static int sensor_hub_check_for_sensor_page(struct hid_device *hdev) +{ + int i; + int ret = -EINVAL; + + for (i = 0; i < hdev->maxcollection; i++) { + struct hid_collection *col = &hdev->collection[i]; + if (col->type == HID_COLLECTION_PHYSICAL && + (col->usage & HID_USAGE_PAGE) == HID_UP_SENSOR) { + ret = 0; + break; + } + } + + return ret; +} + +static struct hid_report *sensor_hub_report(int id, struct hid_device *hdev, + int dir) +{ + struct hid_report *report; + + list_for_each_entry(report, &hdev->report_enum[dir].report_list, list) { + if (report->id == id) + return report; + } + hid_warn(hdev, "No report with id 0x%x found\n", id); + + return NULL; +} + +static int sensor_hub_get_physical_device_count( + struct hid_report_enum *report_enum) +{ + struct hid_report *report; + struct hid_field *field; + int cnt = 0; + + list_for_each_entry(report, &report_enum->report_list, list) { + field = report->field[0]; + if (report->maxfield && field && + field->physical) + cnt++; + } + + return cnt; +} + +static void sensor_hub_fill_attr_info( + struct hid_sensor_hub_attribute_info *info, + s32 index, s32 report_id, s32 units, s32 unit_expo, s32 size) +{ + info->index = index; + info->report_id = report_id; + info->units = units; + info->unit_expo = unit_expo; + info->size = size/8; +} + +static struct hid_sensor_hub_callbacks *sensor_hub_get_callback( + struct hid_device *hdev, + u32 usage_id, void **priv) +{ + struct hid_sensor_hub_callbacks_list *callback; + struct sensor_hub_data *pdata = hid_get_drvdata(hdev); + + spin_lock(&pdata->dyn_callback_lock); + list_for_each_entry(callback, &pdata->dyn_callback_list, list) + if (callback->usage_id == usage_id) { + *priv = callback->priv; + spin_unlock(&pdata->dyn_callback_lock); + return callback->usage_callback; + } + spin_unlock(&pdata->dyn_callback_lock); + + return NULL; +} + +int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev, + u32 usage_id, + struct hid_sensor_hub_callbacks *usage_callback) +{ + struct hid_sensor_hub_callbacks_list *callback; + struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev); + + spin_lock(&pdata->dyn_callback_lock); + list_for_each_entry(callback, &pdata->dyn_callback_list, list) + if (callback->usage_id == usage_id) { + spin_unlock(&pdata->dyn_callback_lock); + return -EINVAL; + } + callback = kzalloc(sizeof(*callback), GFP_KERNEL); + if (!callback) { + spin_unlock(&pdata->dyn_callback_lock); + return -ENOMEM; + } + callback->usage_callback = usage_callback; + callback->usage_id = usage_id; + callback->priv = NULL; + list_add_tail(&callback->list, &pdata->dyn_callback_list); + spin_unlock(&pdata->dyn_callback_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(sensor_hub_register_callback); + +int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev, + u32 usage_id) +{ + struct hid_sensor_hub_callbacks_list *callback; + struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev); + + spin_lock(&pdata->dyn_callback_lock); + list_for_each_entry(callback, &pdata->dyn_callback_list, list) + if (callback->usage_id == usage_id) { + list_del(&callback->list); + kfree(callback); + break; + } + spin_unlock(&pdata->dyn_callback_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(sensor_hub_remove_callback); + +int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, + u32 field_index, s32 value) +{ + struct hid_report *report; + struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); + int ret = 0; + + if (report_id < 0) + return -EINVAL; + + mutex_lock(&data->mutex); + report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT); + if (!report || (field_index >= report->maxfield)) { + ret = -EINVAL; + goto done_proc; + } + hid_set_field(report->field[field_index], 0, value); + usbhid_submit_report(hsdev->hdev, report, USB_DIR_OUT); + usbhid_wait_io(hsdev->hdev); + +done_proc: + mutex_unlock(&data->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(sensor_hub_set_feature); + +int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, + u32 field_index, s32 *value) +{ + struct hid_report *report; + struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); + int ret = 0; + + if (report_id < 0) + return -EINVAL; + + mutex_lock(&data->mutex); + report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT); + if (!report || (field_index >= report->maxfield)) { + ret = -EINVAL; + goto done_proc; + } + usbhid_submit_report(hsdev->hdev, report, USB_DIR_IN); + usbhid_wait_io(hsdev->hdev); + *value = report->field[field_index]->value[0]; + +done_proc: + mutex_unlock(&data->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(sensor_hub_get_feature); + + +int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev, + u32 usage_id, + u32 attr_usage_id, u32 report_id) +{ + struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); + unsigned long flags; + struct hid_report *report; + int ret_val = 0; + + if (report_id < 0) + return -EINVAL; + + mutex_lock(&data->mutex); + memset(&data->pending, 0, sizeof(data->pending)); + init_completion(&data->pending.ready); + data->pending.usage_id = usage_id; + data->pending.attr_usage_id = attr_usage_id; + data->pending.raw_size = 0; + + spin_lock_irqsave(&data->lock, flags); + data->pending.status = true; + report = sensor_hub_report(report_id, hsdev->hdev, HID_INPUT_REPORT); + if (!report) { + spin_unlock_irqrestore(&data->lock, flags); + goto err_free; + } + usbhid_submit_report(hsdev->hdev, report, USB_DIR_IN); + spin_unlock_irqrestore(&data->lock, flags); + wait_for_completion_interruptible_timeout(&data->pending.ready, HZ*5); + switch (data->pending.raw_size) { + case 1: + ret_val = *(u8 *)data->pending.raw_data; + break; + case 2: + ret_val = *(u16 *)data->pending.raw_data; + break; + case 4: + ret_val = *(u32 *)data->pending.raw_data; + break; + default: + ret_val = 0; + } + kfree(data->pending.raw_data); + +err_free: + data->pending.status = false; + mutex_unlock(&data->mutex); + + return ret_val; +} +EXPORT_SYMBOL_GPL(sensor_hub_input_attr_get_raw_value); + +int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev, + u8 type, + u32 usage_id, + u32 attr_usage_id, + struct hid_sensor_hub_attribute_info *info) +{ + int ret = -1; + int i, j; + int collection_index = -1; + struct hid_report *report; + struct hid_field *field; + struct hid_report_enum *report_enum; + struct hid_device *hdev = hsdev->hdev; + + /* Initialize with defaults */ + info->usage_id = usage_id; + info->attrib_id = attr_usage_id; + info->report_id = -1; + info->index = -1; + info->units = -1; + info->unit_expo = -1; + + for (i = 0; i < hdev->maxcollection; ++i) { + struct hid_collection *collection = &hdev->collection[i]; + if (usage_id == collection->usage) { + collection_index = i; + break; + } + } + if (collection_index == -1) + goto err_ret; + + report_enum = &hdev->report_enum[type]; + list_for_each_entry(report, &report_enum->report_list, list) { + for (i = 0; i < report->maxfield; ++i) { + field = report->field[i]; + if (field->physical == usage_id && + field->logical == attr_usage_id) { + sensor_hub_fill_attr_info(info, i, report->id, + field->unit, field->unit_exponent, + field->report_size); + ret = 0; + } else { + for (j = 0; j < field->maxusage; ++j) { + if (field->usage[j].hid == + attr_usage_id && + field->usage[j].collection_index == + collection_index) { + sensor_hub_fill_attr_info(info, + i, report->id, + field->unit, + field->unit_exponent, + field->report_size); + ret = 0; + break; + } + } + } + if (ret == 0) + break; + } + } + +err_ret: + return ret; +} +EXPORT_SYMBOL_GPL(sensor_hub_input_get_attribute_info); + +#ifdef CONFIG_PM +static int sensor_hub_suspend(struct hid_device *hdev, pm_message_t message) +{ + struct sensor_hub_data *pdata = hid_get_drvdata(hdev); + struct hid_sensor_hub_callbacks_list *callback; + + hid_dbg(hdev, " sensor_hub_suspend\n"); + spin_lock(&pdata->dyn_callback_lock); + list_for_each_entry(callback, &pdata->dyn_callback_list, list) { + if (callback->usage_callback->suspend) + callback->usage_callback->suspend( + pdata->hsdev, callback->priv); + } + spin_unlock(&pdata->dyn_callback_lock); + + return 0; +} + +static int sensor_hub_resume(struct hid_device *hdev) +{ + struct sensor_hub_data *pdata = hid_get_drvdata(hdev); + struct hid_sensor_hub_callbacks_list *callback; + + hid_dbg(hdev, " sensor_hub_resume\n"); + spin_lock(&pdata->dyn_callback_lock); + list_for_each_entry(callback, &pdata->dyn_callback_list, list) { + if (callback->usage_callback->resume) + callback->usage_callback->resume( + pdata->hsdev, callback->priv); + } + spin_unlock(&pdata->dyn_callback_lock); + + return 0; +} + +static int sensor_hub_reset_resume(struct hid_device *hdev) +{ + return 0; +} +#endif +/* + * Handle raw report as sent by device + */ +static int sensor_hub_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *raw_data, int size) +{ + int i; + u8 *ptr; + int sz; + struct sensor_hub_data *pdata = hid_get_drvdata(hdev); + unsigned long flags; + struct hid_sensor_hub_callbacks *callback = NULL; + struct hid_collection *collection = NULL; + void *priv = NULL; + + hid_dbg(hdev, "sensor_hub_raw_event report id:0x%x size:%d type:%d\n", + report->id, size, report->type); + hid_dbg(hdev, "maxfield:%d\n", report->maxfield); + if (report->type != HID_INPUT_REPORT) + return 1; + + ptr = raw_data; + ptr++; /*Skip report id*/ + + if (!report) + goto err_report; + + spin_lock_irqsave(&pdata->lock, flags); + + for (i = 0; i < report->maxfield; ++i) { + + hid_dbg(hdev, "%d collection_index:%x hid:%x sz:%x\n", + i, report->field[i]->usage->collection_index, + report->field[i]->usage->hid, + report->field[i]->report_size/8); + + sz = report->field[i]->report_size/8; + if (pdata->pending.status && pdata->pending.attr_usage_id == + report->field[i]->usage->hid) { + hid_dbg(hdev, "data was pending ...\n"); + pdata->pending.raw_data = kmalloc(sz, GFP_KERNEL); + if (pdata->pending.raw_data) { + memcpy(pdata->pending.raw_data, ptr, sz); + pdata->pending.raw_size = sz; + } else + pdata->pending.raw_size = 0; + complete(&pdata->pending.ready); + } + collection = &hdev->collection[ + report->field[i]->usage->collection_index]; + hid_dbg(hdev, "collection->usage %x\n", + collection->usage); + callback = sensor_hub_get_callback(pdata->hsdev->hdev, + report->field[i]->physical, + &priv); + if (callback && callback->capture_sample) { + if (report->field[i]->logical) + callback->capture_sample(pdata->hsdev, + report->field[i]->logical, sz, ptr, + callback->pdev); + else + callback->capture_sample(pdata->hsdev, + report->field[i]->usage->hid, sz, ptr, + callback->pdev); + } + ptr += sz; + } + if (callback && collection && callback->send_event) + callback->send_event(pdata->hsdev, collection->usage, + callback->pdev); + spin_unlock_irqrestore(&pdata->lock, flags); + +err_report: + return 1; +} + +static int sensor_hub_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int ret; + struct sensor_hub_data *sd; + int i; + char *name; + struct hid_report *report; + struct hid_report_enum *report_enum; + struct hid_field *field; + int dev_cnt; + + sd = kzalloc(sizeof(struct sensor_hub_data), GFP_KERNEL); + if (!sd) { + hid_err(hdev, "cannot allocate Sensor data\n"); + return -ENOMEM; + } + sd->hsdev = kzalloc(sizeof(struct hid_sensor_hub_device), GFP_KERNEL); + if (!sd->hsdev) { + hid_err(hdev, "cannot allocate hid_sensor_hub_device\n"); + ret = -ENOMEM; + goto err_free_hub; + } + hid_set_drvdata(hdev, sd); + sd->hsdev->hdev = hdev; + sd->hsdev->vendor_id = hdev->vendor; + sd->hsdev->product_id = hdev->product; + spin_lock_init(&sd->lock); + spin_lock_init(&sd->dyn_callback_lock); + mutex_init(&sd->mutex); + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "parse failed\n"); + goto err_free; + } + if (sensor_hub_check_for_sensor_page(hdev) < 0) { + hid_err(hdev, "sensor page not found\n"); + goto err_free; + } + INIT_LIST_HEAD(&hdev->inputs); + + hdev->claimed = HID_CLAIMED_INPUT; + ret = hid_hw_start(hdev, 0); + if (ret) { + hid_err(hdev, "hw start failed\n"); + goto err_free; + } + ret = hid_hw_open(hdev); + if (ret) { + hid_err(hdev, "failed to open input interrupt pipe\n"); + goto err_stop_hw; + } + + INIT_LIST_HEAD(&sd->dyn_callback_list); + sd->hid_sensor_client_cnt = 0; + report_enum = &hdev->report_enum[HID_INPUT_REPORT]; + + dev_cnt = sensor_hub_get_physical_device_count(report_enum); + if (dev_cnt > HID_MAX_PHY_DEVICES) { + hid_err(hdev, "Invalid Physical device count\n"); + ret = -EINVAL; + goto err_close; + } + sd->hid_sensor_hub_client_devs = kzalloc(dev_cnt * + sizeof(struct mfd_cell), + GFP_KERNEL); + if (sd->hid_sensor_hub_client_devs == NULL) { + hid_err(hdev, + "Failed to allocate memory for mfd cells\n"); + ret = -ENOMEM; + goto err_close; + } + list_for_each_entry(report, &report_enum->report_list, list) { + hid_dbg(hdev, "Report id:%x\n", report->id); + field = report->field[0]; + if (report->maxfield && field && + field->physical) { + name = kasprintf(GFP_KERNEL, "HID-SENSOR-%x", + field->physical); + if (name == NULL) { + hid_err(hdev, + "Failed MFD device name\n"); + ret = -ENOMEM; + goto err_free_cells; + } + sd->hid_sensor_hub_client_devs[ + sd->hid_sensor_client_cnt].name = name; + sd->hid_sensor_hub_client_devs[ + sd->hid_sensor_client_cnt].platform_data = + sd->hsdev; + sd->hid_sensor_hub_client_devs[ + sd->hid_sensor_client_cnt].pdata_size = + sizeof(*sd->hsdev); + hid_dbg(hdev, "Adding %s:%p\n", name, sd); + sd->hid_sensor_client_cnt++; + } + } + ret = mfd_add_devices(&hdev->dev, 0, sd->hid_sensor_hub_client_devs, + sd->hid_sensor_client_cnt, NULL, 0); + if (ret < 0) + goto err_free_names; + + return ret; + +err_free_names: + for (i = 0; i < sd->hid_sensor_client_cnt ; ++i) + kfree(sd->hid_sensor_hub_client_devs[i].name); +err_free_cells: + kfree(sd->hid_sensor_hub_client_devs); +err_close: + hid_hw_stop(hdev); + hid_hw_close(hdev); +err_stop_hw: + hid_hw_stop(hdev); +err_free: + kfree(sd->hsdev); +err_free_hub: + kfree(sd); + + return ret; +} + +static void sensor_hub_remove(struct hid_device *hdev) +{ + struct sensor_hub_data *data = hid_get_drvdata(hdev); + unsigned long flags; + int i; + + hid_dbg(hdev, " hardware removed\n"); + hdev->claimed &= ~HID_CLAIMED_INPUT; + hid_hw_stop(hdev); + hid_hw_close(hdev); + spin_lock_irqsave(&data->lock, flags); + if (data->pending.status) + complete(&data->pending.ready); + spin_unlock_irqrestore(&data->lock, flags); + mfd_remove_devices(&hdev->dev); + for (i = 0; i < data->hid_sensor_client_cnt ; ++i) + kfree(data->hid_sensor_hub_client_devs[i].name); + kfree(data->hid_sensor_hub_client_devs); + hid_set_drvdata(hdev, NULL); + mutex_destroy(&data->mutex); + kfree(data->hsdev); + kfree(data); +} + +static const struct hid_device_id sensor_hub_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086, + USB_DEVICE_ID_SENSOR_HUB_1020) }, + { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, + USB_DEVICE_ID_SENSOR_HUB_1020) }, + { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086, + USB_DEVICE_ID_SENSOR_HUB_09FA) }, + { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, + USB_DEVICE_ID_SENSOR_HUB_09FA) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM, + USB_DEVICE_ID_SENSOR_HUB_7014) }, + { } +}; +MODULE_DEVICE_TABLE(hid, sensor_hub_devices); + +static const struct hid_usage_id sensor_hub_grabbed_usages[] = { + { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID }, + { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1 } +}; + +static struct hid_driver sensor_hub_driver = { + .name = "hid-sensor-hub", + .id_table = sensor_hub_devices, + .probe = sensor_hub_probe, + .remove = sensor_hub_remove, + .raw_event = sensor_hub_raw_event, +#ifdef CONFIG_PM + .suspend = sensor_hub_suspend, + .resume = sensor_hub_resume, + .reset_resume = sensor_hub_reset_resume, +#endif +}; + +static int __init sensor_hub_init(void) +{ + return hid_register_driver(&sensor_hub_driver); +} + +static void __exit sensor_hub_exit(void) +{ + hid_unregister_driver(&sensor_hub_driver); +} + +module_init(sensor_hub_init); +module_exit(sensor_hub_exit); + +MODULE_DESCRIPTION("HID Sensor Hub driver"); +MODULE_AUTHOR("Srinivas Pandruvada "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/hid-sensor-hub.h b/include/linux/hid-sensor-hub.h new file mode 100644 index 000000000000..0aa5f4c42ae6 --- /dev/null +++ b/include/linux/hid-sensor-hub.h @@ -0,0 +1,160 @@ +/* + * HID Sensors Driver + * Copyright (c) 2012, Intel Corporation. + * + * 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. + * + * 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 _HID_SENSORS_HUB_H +#define _HID_SENSORS_HUB_H + +#include +#include + +/** + * struct hid_sensor_hub_attribute_info - Attribute info + * @usage_id: Parent usage id of a physical device. + * @attrib_id: Attribute id for this attribute. + * @report_id: Report id in which this information resides. + * @index: Field index in the report. + * @units: Measurment unit for this attribute. + * @unit_expo: Exponent used in the data. + * @size: Size in bytes for data size. + */ +struct hid_sensor_hub_attribute_info { + u32 usage_id; + u32 attrib_id; + s32 report_id; + s32 index; + s32 units; + s32 unit_expo; + s32 size; +}; + +/** + * struct hid_sensor_hub_device - Stores the hub instance data + * @hdev: Stores the hid instance. + * @vendor_id: Vendor id of hub device. + * @product_id: Product id of hub device. + */ +struct hid_sensor_hub_device { + struct hid_device *hdev; + u32 vendor_id; + u32 product_id; +}; + +/** + * struct hid_sensor_hub_callbacks - Client callback functions + * @pdev: Platform device instance of the client driver. + * @suspend: Suspend callback. + * @resume: Resume callback. + * @capture_sample: Callback to get a sample. + * @send_event: Send notification to indicate all samples are + * captured, process and send event + */ +struct hid_sensor_hub_callbacks { + struct platform_device *pdev; + int (*suspend)(struct hid_sensor_hub_device *hsdev, void *priv); + int (*resume)(struct hid_sensor_hub_device *hsdev, void *priv); + int (*capture_sample)(struct hid_sensor_hub_device *hsdev, + u32 usage_id, size_t raw_len, char *raw_data, + void *priv); + int (*send_event)(struct hid_sensor_hub_device *hsdev, u32 usage_id, + void *priv); +}; + +/* Registration functions */ + +/** +* sensor_hub_register_callback() - Register client callbacks +* @hsdev: Hub device instance. +* @usage_id: Usage id of the client (E.g. 0x200076 for Gyro). +* @usage_callback: Callback function storage +* +* Used to register callbacks by client processing drivers. Sensor +* hub core driver will call these callbacks to offload processing +* of data streams and notifications. +*/ +int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev, + u32 usage_id, + struct hid_sensor_hub_callbacks *usage_callback); + +/** +* sensor_hub_remove_callback() - Remove client callbacks +* @hsdev: Hub device instance. +* @usage_id: Usage id of the client (E.g. 0x200076 for Gyro). +* +* If there is a callback registred, this call will remove that +* callbacks, so that it will stop data and event notifications. +*/ +int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev, + u32 usage_id); + + +/* Hid sensor hub core interfaces */ + +/** +* sensor_hub_input_get_attribute_info() - Get an attribute information +* @hsdev: Hub device instance. +* @type: Type of this attribute, input/output/feature +* @usage_id: Attribute usage id of parent physical device as per spec +* @attr_usage_id: Attribute usage id as per spec +* @info: return information about attribute after parsing report +* +* Parses report and returns the attribute information such as report id, +* field index, units and exponet etc. +*/ +int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev, + u8 type, + u32 usage_id, u32 attr_usage_id, + struct hid_sensor_hub_attribute_info *info); + +/** +* sensor_hub_input_attr_get_raw_value() - Synchronous read request +* @usage_id: Attribute usage id of parent physical device as per spec +* @attr_usage_id: Attribute usage id as per spec +* @report_id: Report id to look for +* +* Issues a synchronous read request for an input attribute. Returns +* data upto 32 bits. Since client can get events, so this call should +* not be used for data paths, this will impact performance. +*/ + +int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev, + u32 usage_id, + u32 attr_usage_id, u32 report_id); +/** +* sensor_hub_set_feature() - Feature set request +* @report_id: Report id to look for +* @field_index: Field index inside a report +* @value: Value to set +* +* Used to set a field in feature report. For example this can set polling +* interval, sensitivity, activate/deactivate state. +*/ +int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, + u32 field_index, s32 value); + +/** +* sensor_hub_get_feature() - Feature get request +* @report_id: Report id to look for +* @field_index: Field index inside a report +* @value: Place holder for return value +* +* Used to get a field in feature report. For example this can get polling +* interval, sensitivity, activate/deactivate state. +*/ +int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, + u32 field_index, s32 *value); +#endif diff --git a/include/linux/hid-sensor-ids.h b/include/linux/hid-sensor-ids.h new file mode 100644 index 000000000000..ca8d7e94eb3c --- /dev/null +++ b/include/linux/hid-sensor-ids.h @@ -0,0 +1,112 @@ +/* + * HID Sensors Driver + * Copyright (c) 2012, Intel Corporation. + * + * 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. + * + * 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 _HID_SENSORS_IDS_H +#define _HID_SENSORS_IDS_H + +#define HID_UP_SENSOR 0x00200000 +#define HID_MAX_PHY_DEVICES 0xFF + +/* Accel 3D (200073) */ +#define HID_USAGE_SENSOR_ACCEL_3D 0x200073 +#define HID_USAGE_SENSOR_ACCEL_X_AXIS 0x200453 +#define HID_USAGE_SENSOR_ACCEL_Y_AXIS 0x200454 +#define HID_USAGE_SENSOR_ACCEL_Z_AXIS 0x200455 + +/* ALS (200041) */ +#define HID_USAGE_SENSOR_ALS 0x200041 +#define HID_USAGE_SENSOR_LIGHT_ILLUM 0x2004d1 + +/* Gyro 3D: (200076) */ +#define HID_USAGE_SENSOR_GYRO_3D 0x200076 +#define HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS 0x200457 +#define HID_USAGE_SENSOR_ANGL_VELOCITY_Y_AXIS 0x200458 +#define HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS 0x200459 + +/*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 +#define HID_USAGE_SENSOR_ORIENT_MAGN_HEADING_Y 0x200473 +#define HID_USAGE_SENSOR_ORIENT_MAGN_HEADING_Z 0x200474 + +#define HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH 0x200475 +#define HID_USAGE_SENSOR_ORIENT_COMP_TRUE_NORTH 0x200476 +#define HID_USAGE_SENSOR_ORIENT_MAGN_NORTH 0x200477 +#define HID_USAGE_SENSOR_ORIENT_TRUE_NORTH 0x200478 + +#define HID_USAGE_SENSOR_ORIENT_DISTANCE 0x200479 +#define HID_USAGE_SENSOR_ORIENT_DISTANCE_X 0x20047A +#define HID_USAGE_SENSOR_ORIENT_DISTANCE_Y 0x20047B +#define HID_USAGE_SENSOR_ORIENT_DISTANCE_Z 0x20047C +#define HID_USAGE_SENSOR_ORIENT_DISTANCE_OUT_OF_RANGE 0x20047D +#define HID_USAGE_SENSOR_ORIENT_TILT 0x20047E +#define HID_USAGE_SENSOR_ORIENT_TILT_X 0x20047F +#define HID_USAGE_SENSOR_ORIENT_TILT_Y 0x200480 +#define HID_USAGE_SENSOR_ORIENT_TILT_Z 0x200481 +#define HID_USAGE_SENSOR_ORIENT_ROTATION_MATRIX 0x200482 +#define HID_USAGE_SENSOR_ORIENT_QUATERNION 0x200483 +#define HID_USAGE_SENSOR_ORIENT_MAGN_FLUX 0x200484 + +#define HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS 0x200485 +#define HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Y_AXIS 0x200486 +#define HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Z_AXIS 0x200487 + +/* Units */ +#define HID_USAGE_SENSOR_UNITS_NOT_SPECIFIED 0x00 +#define HID_USAGE_SENSOR_UNITS_LUX 0x01 +#define HID_USAGE_SENSOR_UNITS_KELVIN 0x01000100 +#define HID_USAGE_SENSOR_UNITS_FAHRENHEIT 0x03000100 +#define HID_USAGE_SENSOR_UNITS_PASCAL 0xF1E1 +#define HID_USAGE_SENSOR_UNITS_NEWTON 0x11E1 +#define HID_USAGE_SENSOR_UNITS_METERS_PER_SECOND 0x11F0 +#define HID_USAGE_SENSOR_UNITS_METERS_PER_SEC_SQRD 0x11E0 +#define HID_USAGE_SENSOR_UNITS_FARAD 0xE14F2000 +#define HID_USAGE_SENSOR_UNITS_AMPERE 0x01001000 +#define HID_USAGE_SENSOR_UNITS_WATT 0x21d1 +#define HID_USAGE_SENSOR_UNITS_HENRY 0x21E1E000 +#define HID_USAGE_SENSOR_UNITS_OHM 0x21D1E000 +#define HID_USAGE_SENSOR_UNITS_VOLT 0x21D1F000 +#define HID_USAGE_SENSOR_UNITS_HERTZ 0x01F0 +#define HID_USAGE_SENSOR_UNITS_DEGREES_PER_SEC_SQRD 0x14E0 +#define HID_USAGE_SENSOR_UNITS_RADIANS 0x12 +#define HID_USAGE_SENSOR_UNITS_RADIANS_PER_SECOND 0x12F0 +#define HID_USAGE_SENSOR_UNITS_RADIANS_PER_SEC_SQRD 0x12E0 +#define HID_USAGE_SENSOR_UNITS_SECOND 0x0110 +#define HID_USAGE_SENSOR_UNITS_GAUSS 0x01E1F000 +#define HID_USAGE_SENSOR_UNITS_GRAM 0x0101 +#define HID_USAGE_SENSOR_UNITS_CENTIMETER 0x11 +#define HID_USAGE_SENSOR_UNITS_G 0x1A +#define HID_USAGE_SENSOR_UNITS_MILLISECOND 0x19 +#define HID_USAGE_SENSOR_UNITS_PERCENT 0x17 +#define HID_USAGE_SENSOR_UNITS_DEGREES 0x14 +#define HID_USAGE_SENSOR_UNITS_DEGREES_PER_SECOND 0x15 + +/* Common selectors */ +#define HID_USAGE_SENSOR_PROP_REPORT_INTERVAL 0x20030E +#define HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS 0x20030F +#define HID_USAGE_SENSOR_PROP_SENSITIVITY_RANGE_PCT 0x200310 +#define HID_USAGE_SENSOR_PROP_SENSITIVITY_REL_PCT 0x200311 +#define HID_USAGE_SENSOR_PROP_ACCURACY 0x200312 +#define HID_USAGE_SENSOR_PROP_RESOLUTION 0x200313 +#define HID_USAGE_SENSOR_PROP_RANGE_MAXIMUM 0x200314 +#define HID_USAGE_SENSOR_PROP_RANGE_MINIMUM 0x200315 +#define HID_USAGE_SENSOR_PROP_REPORT_STATE 0x200316 +#define HID_USAGE_SENSOR_PROY_POWER_STATE 0x200319 + +#endif -- cgit v1.2.3 From b1b799164afb22711e6bee718f2a5ee669bb9517 Mon Sep 17 00:00:00 2001 From: Tomas Hlavacek Date: Thu, 6 Sep 2012 23:17:47 +0200 Subject: tty_register_device_attr updated for tty-next Added tty_device_create_release() and bound to dev->release in tty_register_device_attr(). Added tty_port_register_device_attr() and used in uart_add_one_port() instead of tty_register_device_attr(). Signed-off-by: Tomas Hlavacek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 8 ++++---- drivers/tty/tty_io.c | 7 +++++++ drivers/tty/tty_port.c | 24 ++++++++++++++++++++++++ include/linux/tty.h | 4 ++++ 4 files changed, 39 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index f629bdf2a8cf..046279ce3e8d 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2313,9 +2313,9 @@ static ssize_t uart_get_attr_uartclk(struct device *dev, struct device_attribute *attr, char *buf) { int ret; - struct tty_port *port = dev_get_drvdata(dev); struct uart_state *state = container_of(port, struct uart_state, port); + mutex_lock(&state->port.mutex); ret = snprintf(buf, PAGE_SIZE, "%d\n", state->uart_port->uartclk); mutex_unlock(&state->port.mutex); @@ -2330,7 +2330,7 @@ static struct attribute *tty_dev_attrs[] = { NULL, }; -static struct attribute_group tty_dev_attr_group = { +static const struct attribute_group tty_dev_attr_group = { .attrs = tty_dev_attrs, }; @@ -2392,8 +2392,8 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) * Register the port whether it's detected or not. This allows * setserial to be used to alter this ports parameters. */ - tty_dev = tty_register_device_attr(drv->tty_driver, uport->line, - uport->dev, port, tty_dev_attr_groups); + tty_dev = tty_port_register_device_attr(port, drv->tty_driver, + uport->line, uport->dev, port, tty_dev_attr_groups); if (likely(!IS_ERR(tty_dev))) { device_set_wakeup_capable(tty_dev, 1); } else { diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index dcb30d55d39c..8a5a8b064616 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -3045,6 +3045,12 @@ struct device *tty_register_device(struct tty_driver *driver, unsigned index, } EXPORT_SYMBOL(tty_register_device); +static void tty_device_create_release(struct device *dev) +{ + pr_debug("device: '%s': %s\n", dev_name(dev), __func__); + kfree(dev); +} + /** * tty_register_device_attr - register a tty device * @driver: the tty driver that describes the tty device @@ -3103,6 +3109,7 @@ struct device *tty_register_device_attr(struct tty_driver *driver, dev->devt = devt; dev->class = tty_class; dev->parent = device; + dev->release = tty_device_create_release; dev_set_name(dev, "%s", name); dev->groups = attr_grp; dev_set_drvdata(dev, drvdata); diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 96302f4c7079..d7bdd8d0c23f 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -73,6 +73,30 @@ struct device *tty_port_register_device(struct tty_port *port, } EXPORT_SYMBOL_GPL(tty_port_register_device); +/** + * tty_port_register_device_attr - register tty device + * @port: tty_port of the device + * @driver: tty_driver for this device + * @index: index of the tty + * @device: parent if exists, otherwise NULL + * @drvdata: Driver data to be set to device. + * @attr_grp: Attribute group to be set on device. + * + * It is the same as tty_register_device_attr except the provided @port is + * linked to a concrete tty specified by @index. Use this or tty_port_install + * (or both). Call tty_port_link_device as a last resort. + */ +struct device *tty_port_register_device_attr(struct tty_port *port, + struct tty_driver *driver, unsigned index, + struct device *device, void *drvdata, + const struct attribute_group **attr_grp) +{ + tty_port_link_device(port, driver, index); + return tty_register_device_attr(driver, index, device, drvdata, + attr_grp); +} +EXPORT_SYMBOL_GPL(tty_port_register_device_attr); + int tty_port_alloc_xmit_buf(struct tty_port *port) { /* We may sleep in get_zeroed_page() */ diff --git a/include/linux/tty.h b/include/linux/tty.h index 599d60347bf0..1509b86825d8 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -507,6 +507,10 @@ extern void tty_port_link_device(struct tty_port *port, extern struct device *tty_port_register_device(struct tty_port *port, struct tty_driver *driver, unsigned index, struct device *device); +extern struct device *tty_port_register_device_attr(struct tty_port *port, + struct tty_driver *driver, unsigned index, + struct device *device, void *drvdata, + const struct attribute_group **attr_grp); extern int tty_port_alloc_xmit_buf(struct tty_port *port); extern void tty_port_free_xmit_buf(struct tty_port *port); extern void tty_port_put(struct tty_port *port); -- cgit v1.2.3 From bce5afd8d960e78892669b68751547015646d5e4 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 27 Aug 2012 15:45:51 +0200 Subject: clk: ux500: First version of clock definitions for ux500 In this first version of the clock definitions, the structure for ux500 are set. Support for u8500, u9540 and u8540 are prepared. Signed-off-by: Ulf Hansson Acked-by: Linus Walleij Signed-off-by: Mike Turquette --- drivers/clk/ux500/Makefile | 5 +++++ drivers/clk/ux500/u8500_clk.c | 21 +++++++++++++++++++++ drivers/clk/ux500/u8540_clk.c | 21 +++++++++++++++++++++ drivers/clk/ux500/u9540_clk.c | 21 +++++++++++++++++++++ include/linux/platform_data/clk-ux500.h | 17 +++++++++++++++++ 5 files changed, 85 insertions(+) create mode 100644 drivers/clk/ux500/u8500_clk.c create mode 100644 drivers/clk/ux500/u8540_clk.c create mode 100644 drivers/clk/ux500/u9540_clk.c create mode 100644 include/linux/platform_data/clk-ux500.h (limited to 'include') diff --git a/drivers/clk/ux500/Makefile b/drivers/clk/ux500/Makefile index a3ccd1b4cfcd..858fbfe66281 100644 --- a/drivers/clk/ux500/Makefile +++ b/drivers/clk/ux500/Makefile @@ -5,3 +5,8 @@ # Clock types obj-y += clk-prcc.o obj-y += clk-prcmu.o + +# Clock definitions +obj-y += u8500_clk.o +obj-y += u9540_clk.o +obj-y += u8540_clk.o diff --git a/drivers/clk/ux500/u8500_clk.c b/drivers/clk/ux500/u8500_clk.c new file mode 100644 index 000000000000..2bc4901599ed --- /dev/null +++ b/drivers/clk/ux500/u8500_clk.c @@ -0,0 +1,21 @@ +/* + * Clock definitions for u8500 platform. + * + * Copyright (C) 2012 ST-Ericsson SA + * Author: Ulf Hansson + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include + +#include "clk.h" + +void u8500_clk_init(void) +{ + /* register clocks here */ +} diff --git a/drivers/clk/ux500/u8540_clk.c b/drivers/clk/ux500/u8540_clk.c new file mode 100644 index 000000000000..10adfd2ead21 --- /dev/null +++ b/drivers/clk/ux500/u8540_clk.c @@ -0,0 +1,21 @@ +/* + * Clock definitions for u8540 platform. + * + * Copyright (C) 2012 ST-Ericsson SA + * Author: Ulf Hansson + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include + +#include "clk.h" + +void u8540_clk_init(void) +{ + /* register clocks here */ +} diff --git a/drivers/clk/ux500/u9540_clk.c b/drivers/clk/ux500/u9540_clk.c new file mode 100644 index 000000000000..dbc0191e16c8 --- /dev/null +++ b/drivers/clk/ux500/u9540_clk.c @@ -0,0 +1,21 @@ +/* + * Clock definitions for u9540 platform. + * + * Copyright (C) 2012 ST-Ericsson SA + * Author: Ulf Hansson + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include + +#include "clk.h" + +void u9540_clk_init(void) +{ + /* register clocks here */ +} diff --git a/include/linux/platform_data/clk-ux500.h b/include/linux/platform_data/clk-ux500.h new file mode 100644 index 000000000000..3af0da1f3be5 --- /dev/null +++ b/include/linux/platform_data/clk-ux500.h @@ -0,0 +1,17 @@ +/* + * Clock definitions for ux500 platforms + * + * Copyright (C) 2012 ST-Ericsson SA + * Author: Ulf Hansson + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#ifndef __CLK_UX500_H +#define __CLK_UX500_H + +void u8500_clk_init(void); +void u9540_clk_init(void); +void u8540_clk_init(void); + +#endif /* __CLK_UX500_H */ -- cgit v1.2.3 From a093bde2b45a0a745f12c018e2d13c027d58641f Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 31 Aug 2012 14:21:28 +0200 Subject: clk: Provide option for clk_get_rate to issue hw for new rate By using CLK_GET_RATE_NOCACHE flag, we tell the clk_get_rate API to issue the hw for an updated clock rate. This can be used for a clock which rate may be updated without a client necessary modifying it. Signed-off-by: Ulf Hansson Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 43 ++++++++++++++++++++++++------------------- include/linux/clk-provider.h | 1 + 2 files changed, 25 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index efdfd009c270..d9cbae06549f 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -557,25 +557,6 @@ int clk_enable(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_enable); -/** - * clk_get_rate - return the rate of clk - * @clk: the clk whose rate is being returned - * - * Simply returns the cached rate of the clk. Does not query the hardware. If - * clk is NULL then returns 0. - */ -unsigned long clk_get_rate(struct clk *clk) -{ - unsigned long rate; - - mutex_lock(&prepare_lock); - rate = __clk_get_rate(clk); - mutex_unlock(&prepare_lock); - - return rate; -} -EXPORT_SYMBOL_GPL(clk_get_rate); - /** * __clk_round_rate - round the given rate for a clk * @clk: round the rate of this clock @@ -701,6 +682,30 @@ static void __clk_recalc_rates(struct clk *clk, unsigned long msg) __clk_recalc_rates(child, msg); } +/** + * clk_get_rate - return the rate of clk + * @clk: the clk whose rate is being returned + * + * Simply returns the cached rate of the clk, unless CLK_GET_RATE_NOCACHE flag + * is set, which means a recalc_rate will be issued. + * If clk is NULL then returns 0. + */ +unsigned long clk_get_rate(struct clk *clk) +{ + unsigned long rate; + + mutex_lock(&prepare_lock); + + if (clk && (clk->flags & CLK_GET_RATE_NOCACHE)) + __clk_recalc_rates(clk, 0); + + rate = __clk_get_rate(clk); + mutex_unlock(&prepare_lock); + + return rate; +} +EXPORT_SYMBOL_GPL(clk_get_rate); + /** * __clk_speculate_rates * @clk: first clk in the subtree diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 77335fac943e..1b15307cd466 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -26,6 +26,7 @@ #define CLK_IGNORE_UNUSED BIT(3) /* do not gate even if unused */ #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 */ struct clk_hw; -- cgit v1.2.3 From 20aee5b6d7738206bfd37b352a97c75627d6fa6d Mon Sep 17 00:00:00 2001 From: Michel Jaouen Date: Fri, 31 Aug 2012 14:21:30 +0200 Subject: mfd: dbx500: Provide a more accurate smp_twd clock The local timer clock is based on ARM subsystem clock. This patch obtains a more exact value of that clock by reading PRCMU registers. Using this increases the accuracy of the local timer events. Signed-off-by: Ulf Hansson Signed-off-by: Rickard Andersson Signed-off-by: Michel Jaouen Acked-by: Linus Walleij Signed-off-by: Mike Turquette --- drivers/mfd/db8500-prcmu.c | 42 ++++++++++++++++++++++++++++++++++++++++ drivers/mfd/dbx500-prcmu-regs.h | 4 +++- include/linux/mfd/dbx500-prcmu.h | 1 + 3 files changed, 46 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 7040a0081130..6b37e2d6ed8f 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -418,6 +418,9 @@ static struct { static atomic_t ac_wake_req_state = ATOMIC_INIT(0); +/* Functions definition */ +static void compute_armss_rate(void); + /* Spinlocks */ static DEFINE_SPINLOCK(prcmu_lock); static DEFINE_SPINLOCK(clkout_lock); @@ -517,6 +520,7 @@ static struct dsiescclk dsiescclk[3] = { } }; + /* * Used by MCDE to setup all necessary PRCMU registers */ @@ -1013,6 +1017,7 @@ int db8500_prcmu_set_arm_opp(u8 opp) (mb1_transfer.ack.arm_opp != opp)) r = -EIO; + compute_armss_rate(); mutex_unlock(&mb1_transfer.lock); return r; @@ -1612,6 +1617,7 @@ static unsigned long pll_rate(void __iomem *reg, unsigned long src_rate, if ((branch == PLL_FIX) || ((branch == PLL_DIV) && (val & PRCM_PLL_FREQ_DIV2EN) && ((reg == PRCM_PLLSOC0_FREQ) || + (reg == PRCM_PLLARM_FREQ) || (reg == PRCM_PLLDDR_FREQ)))) div *= 2; @@ -1661,6 +1667,39 @@ static unsigned long clock_rate(u8 clock) else return 0; } +static unsigned long latest_armss_rate; +static unsigned long armss_rate(void) +{ + return latest_armss_rate; +} + +static void compute_armss_rate(void) +{ + u32 r; + unsigned long rate; + + r = readl(PRCM_ARM_CHGCLKREQ); + + if (r & PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ) { + /* External ARMCLKFIX clock */ + + rate = pll_rate(PRCM_PLLDDR_FREQ, ROOT_CLOCK_RATE, PLL_FIX); + + /* Check PRCM_ARM_CHGCLKREQ divider */ + if (!(r & PRCM_ARM_CHGCLKREQ_PRCM_ARM_DIVSEL)) + rate /= 2; + + /* Check PRCM_ARMCLKFIX_MGT divider */ + r = readl(PRCM_ARMCLKFIX_MGT); + r &= PRCM_CLK_MGT_CLKPLLDIV_MASK; + rate /= r; + + } else {/* ARM PLL */ + rate = pll_rate(PRCM_PLLARM_FREQ, ROOT_CLOCK_RATE, PLL_DIV); + } + + latest_armss_rate = rate; +} static unsigned long dsiclk_rate(u8 n) { @@ -1707,6 +1746,8 @@ unsigned long prcmu_clock_rate(u8 clock) return pll_rate(PRCM_PLLSOC0_FREQ, ROOT_CLOCK_RATE, PLL_RAW); else if (clock == PRCMU_PLLSOC1) return pll_rate(PRCM_PLLSOC1_FREQ, ROOT_CLOCK_RATE, PLL_RAW); + else if (clock == PRCMU_ARMSS) + return armss_rate(); else if (clock == PRCMU_PLLDDR) return pll_rate(PRCM_PLLDDR_FREQ, ROOT_CLOCK_RATE, PLL_RAW); else if (clock == PRCMU_PLLDSI) @@ -2693,6 +2734,7 @@ void __init db8500_prcmu_early_init(void) handle_simple_irq); set_irq_flags(irq, IRQF_VALID); } + compute_armss_rate(); } static void __init init_prcm_registers(void) diff --git a/drivers/mfd/dbx500-prcmu-regs.h b/drivers/mfd/dbx500-prcmu-regs.h index 23108a6e3167..79c76ebdba52 100644 --- a/drivers/mfd/dbx500-prcmu-regs.h +++ b/drivers/mfd/dbx500-prcmu-regs.h @@ -61,7 +61,8 @@ #define PRCM_PLLARM_LOCKP_PRCM_PLLARM_LOCKP3 0x2 #define PRCM_ARM_CHGCLKREQ (_PRCMU_BASE + 0x114) -#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ 0x1 +#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ BIT(0) +#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_DIVSEL BIT(16) #define PRCM_PLLARM_ENABLE (_PRCMU_BASE + 0x98) #define PRCM_PLLARM_ENABLE_PRCM_PLLARM_ENABLE 0x1 @@ -140,6 +141,7 @@ /* PRCMU clock/PLL/reset registers */ #define PRCM_PLLSOC0_FREQ (_PRCMU_BASE + 0x080) #define PRCM_PLLSOC1_FREQ (_PRCMU_BASE + 0x084) +#define PRCM_PLLARM_FREQ (_PRCMU_BASE + 0x088) #define PRCM_PLLDDR_FREQ (_PRCMU_BASE + 0x08C) #define PRCM_PLL_FREQ_D_SHIFT 0 #define PRCM_PLL_FREQ_D_MASK BITS(0, 7) diff --git a/include/linux/mfd/dbx500-prcmu.h b/include/linux/mfd/dbx500-prcmu.h index 5b90e94399e1..c410d99bd667 100644 --- a/include/linux/mfd/dbx500-prcmu.h +++ b/include/linux/mfd/dbx500-prcmu.h @@ -136,6 +136,7 @@ enum prcmu_clock { PRCMU_TIMCLK, PRCMU_PLLSOC0, PRCMU_PLLSOC1, + PRCMU_ARMSS, PRCMU_PLLDDR, PRCMU_PLLDSI, PRCMU_DSI0CLK, -- cgit v1.2.3 From 1efdb69b0bb41dec8ee3e2cac0a0f167837d0919 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 7 Feb 2012 16:54:11 -0800 Subject: userns: Convert ipc to use kuid and kgid where appropriate - Store the ipc owner and creator with a kuid - Store the ipc group and the crators group with a kgid. - Add error handling to ipc_update_perms, allowing it to fail if the uids and gids can not be converted to kuids or kgids. - Modify the proc files to display the ipc creator and owner in the user namespace of the opener of the proc file. Signed-off-by: Eric W. Biederman --- include/linux/ipc.h | 9 +++++---- init/Kconfig | 1 - ipc/msg.c | 14 +++++++++----- ipc/sem.c | 13 ++++++++----- ipc/shm.c | 19 +++++++++++-------- ipc/util.c | 35 +++++++++++++++++++++-------------- ipc/util.h | 2 +- 7 files changed, 55 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/include/linux/ipc.h b/include/linux/ipc.h index 30e816148df4..ca833fdc3138 100644 --- a/include/linux/ipc.h +++ b/include/linux/ipc.h @@ -79,6 +79,7 @@ struct ipc_kludge { #ifdef __KERNEL__ #include +#include #define IPCMNI 32768 /* <= MAX_INT limit for ipc arrays (including sysctl changes) */ @@ -89,10 +90,10 @@ struct kern_ipc_perm int deleted; int id; key_t key; - uid_t uid; - gid_t gid; - uid_t cuid; - gid_t cgid; + kuid_t uid; + kgid_t gid; + kuid_t cuid; + kgid_t cgid; umode_t mode; unsigned long seq; void *security; diff --git a/init/Kconfig b/init/Kconfig index 7d4422c92cca..d09738dee238 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -925,7 +925,6 @@ config UIDGID_CONVERTED # List of kernel pieces that need user namespace work # Features - depends on SYSVIPC = n depends on IMA = n depends on EVM = n depends on KEYS = n diff --git a/ipc/msg.c b/ipc/msg.c index 7385de25788a..a71af5a65abf 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -443,9 +443,12 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, goto out_unlock; } + err = ipc_update_perm(&msqid64.msg_perm, ipcp); + if (err) + goto out_unlock; + msq->q_qbytes = msqid64.msg_qbytes; - ipc_update_perm(&msqid64.msg_perm, ipcp); msq->q_ctime = get_seconds(); /* sleeping receivers might be excluded by * stricter permissions. @@ -922,6 +925,7 @@ out: #ifdef CONFIG_PROC_FS static int sysvipc_msg_proc_show(struct seq_file *s, void *it) { + struct user_namespace *user_ns = seq_user_ns(s); struct msg_queue *msq = it; return seq_printf(s, @@ -933,10 +937,10 @@ static int sysvipc_msg_proc_show(struct seq_file *s, void *it) msq->q_qnum, msq->q_lspid, msq->q_lrpid, - msq->q_perm.uid, - msq->q_perm.gid, - msq->q_perm.cuid, - msq->q_perm.cgid, + from_kuid_munged(user_ns, msq->q_perm.uid), + from_kgid_munged(user_ns, msq->q_perm.gid), + from_kuid_munged(user_ns, msq->q_perm.cuid), + from_kgid_munged(user_ns, msq->q_perm.cgid), msq->q_stime, msq->q_rtime, msq->q_ctime); diff --git a/ipc/sem.c b/ipc/sem.c index 5215a81420df..58d31f1c1eb5 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -1104,7 +1104,9 @@ static int semctl_down(struct ipc_namespace *ns, int semid, freeary(ns, ipcp); goto out_up; case IPC_SET: - ipc_update_perm(&semid64.sem_perm, ipcp); + err = ipc_update_perm(&semid64.sem_perm, ipcp); + if (err) + goto out_unlock; sma->sem_ctime = get_seconds(); break; default: @@ -1677,6 +1679,7 @@ void exit_sem(struct task_struct *tsk) #ifdef CONFIG_PROC_FS static int sysvipc_sem_proc_show(struct seq_file *s, void *it) { + struct user_namespace *user_ns = seq_user_ns(s); struct sem_array *sma = it; return seq_printf(s, @@ -1685,10 +1688,10 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it) sma->sem_perm.id, sma->sem_perm.mode, sma->sem_nsems, - sma->sem_perm.uid, - sma->sem_perm.gid, - sma->sem_perm.cuid, - sma->sem_perm.cgid, + from_kuid_munged(user_ns, sma->sem_perm.uid), + from_kgid_munged(user_ns, sma->sem_perm.gid), + from_kuid_munged(user_ns, sma->sem_perm.cuid), + from_kgid_munged(user_ns, sma->sem_perm.cgid), sma->sem_otime, sma->sem_ctime); } diff --git a/ipc/shm.c b/ipc/shm.c index 00faa05cf72a..dff40c9f73c9 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -758,7 +758,9 @@ static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd, do_shm_rmid(ns, ipcp); goto out_up; case IPC_SET: - ipc_update_perm(&shmid64.shm_perm, ipcp); + err = ipc_update_perm(&shmid64.shm_perm, ipcp); + if (err) + goto out_unlock; shp->shm_ctim = get_seconds(); break; default: @@ -893,10 +895,10 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) audit_ipc_obj(&(shp->shm_perm)); if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) { - uid_t euid = current_euid(); + kuid_t euid = current_euid(); err = -EPERM; - if (euid != shp->shm_perm.uid && - euid != shp->shm_perm.cuid) + if (!uid_eq(euid, shp->shm_perm.uid) && + !uid_eq(euid, shp->shm_perm.cuid)) goto out_unlock; if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK)) goto out_unlock; @@ -1220,6 +1222,7 @@ SYSCALL_DEFINE1(shmdt, char __user *, shmaddr) #ifdef CONFIG_PROC_FS static int sysvipc_shm_proc_show(struct seq_file *s, void *it) { + struct user_namespace *user_ns = seq_user_ns(s); struct shmid_kernel *shp = it; unsigned long rss = 0, swp = 0; @@ -1242,10 +1245,10 @@ static int sysvipc_shm_proc_show(struct seq_file *s, void *it) shp->shm_cprid, shp->shm_lprid, shp->shm_nattch, - shp->shm_perm.uid, - shp->shm_perm.gid, - shp->shm_perm.cuid, - shp->shm_perm.cgid, + from_kuid_munged(user_ns, shp->shm_perm.uid), + from_kgid_munged(user_ns, shp->shm_perm.gid), + from_kuid_munged(user_ns, shp->shm_perm.cuid), + from_kgid_munged(user_ns, shp->shm_perm.cgid), shp->shm_atim, shp->shm_dtim, shp->shm_ctim, diff --git a/ipc/util.c b/ipc/util.c index eb07fd356f27..72fd0785ac94 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -249,8 +249,8 @@ int ipc_get_maxid(struct ipc_ids *ids) int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size) { - uid_t euid; - gid_t egid; + kuid_t euid; + kgid_t egid; int id, err; if (size > IPCMNI) @@ -606,14 +606,14 @@ void ipc_rcu_putref(void *ptr) int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flag) { - uid_t euid = current_euid(); + kuid_t euid = current_euid(); int requested_mode, granted_mode; audit_ipc_obj(ipcp); requested_mode = (flag >> 6) | (flag >> 3) | flag; granted_mode = ipcp->mode; - if (euid == ipcp->cuid || - euid == ipcp->uid) + if (uid_eq(euid, ipcp->cuid) || + uid_eq(euid, ipcp->uid)) granted_mode >>= 6; else if (in_group_p(ipcp->cgid) || in_group_p(ipcp->gid)) granted_mode >>= 3; @@ -643,10 +643,10 @@ int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flag) void kernel_to_ipc64_perm (struct kern_ipc_perm *in, struct ipc64_perm *out) { out->key = in->key; - out->uid = in->uid; - out->gid = in->gid; - out->cuid = in->cuid; - out->cgid = in->cgid; + out->uid = from_kuid_munged(current_user_ns(), in->uid); + out->gid = from_kgid_munged(current_user_ns(), in->gid); + out->cuid = from_kuid_munged(current_user_ns(), in->cuid); + out->cgid = from_kgid_munged(current_user_ns(), in->cgid); out->mode = in->mode; out->seq = in->seq; } @@ -747,12 +747,19 @@ int ipcget(struct ipc_namespace *ns, struct ipc_ids *ids, * @in: the permission given as input. * @out: the permission of the ipc to set. */ -void ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out) +int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out) { - out->uid = in->uid; - out->gid = in->gid; + kuid_t uid = make_kuid(current_user_ns(), in->uid); + kgid_t gid = make_kgid(current_user_ns(), in->gid); + if (!uid_valid(uid) || !gid_valid(gid)) + return -EINVAL; + + out->uid = uid; + out->gid = gid; out->mode = (out->mode & ~S_IRWXUGO) | (in->mode & S_IRWXUGO); + + return 0; } /** @@ -777,7 +784,7 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, struct ipc64_perm *perm, int extra_perm) { struct kern_ipc_perm *ipcp; - uid_t euid; + kuid_t euid; int err; down_write(&ids->rw_mutex); @@ -793,7 +800,7 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, perm->gid, perm->mode); euid = current_euid(); - if (euid == ipcp->cuid || euid == ipcp->uid || + if (uid_eq(euid, ipcp->cuid) || uid_eq(euid, ipcp->uid) || ns_capable(ns->user_ns, CAP_SYS_ADMIN)) return ipcp; diff --git a/ipc/util.h b/ipc/util.h index 850ef3e962cb..c8fe2f7631e9 100644 --- a/ipc/util.h +++ b/ipc/util.h @@ -125,7 +125,7 @@ struct kern_ipc_perm *ipc_lock(struct ipc_ids *, int); void kernel_to_ipc64_perm(struct kern_ipc_perm *in, struct ipc64_perm *out); void ipc64_perm_to_ipc_perm(struct ipc64_perm *in, struct ipc_perm *out); -void ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out); +int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out); struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, struct ipc_ids *ids, int id, int cmd, struct ipc64_perm *perm, int extra_perm); -- cgit v1.2.3 From 5948ae27fb4f0e87ce8543ca1934599b83929a3e Mon Sep 17 00:00:00 2001 From: Jens Taprogge Date: Fri, 7 Sep 2012 10:29:19 +0200 Subject: staging/ipack: Fix bug introduced by IPack device matching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ~0 can not be casted to u8. Instead of using the IPACK_ANY_ID for the format field we introduce a new IPACK_ANY_FORMAT specifically for that field and defined as 0xff. Reported-by: Dan Carpenter Signed-off-by: Jens Taprogge Acked-by: Samuel Iglesias Gonsálvez Signed-off-by: Greg Kroah-Hartman --- drivers/staging/ipack/ipack.c | 3 ++- include/linux/mod_devicetable.h | 1 + scripts/mod/file2alias.c | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/staging/ipack/ipack.c b/drivers/staging/ipack/ipack.c index b3736c0a1086..659aadcac04d 100644 --- a/drivers/staging/ipack/ipack.c +++ b/drivers/staging/ipack/ipack.c @@ -31,7 +31,8 @@ static inline const struct ipack_device_id * ipack_match_one_device(const struct ipack_device_id *id, const struct ipack_device *device) { - if ((id->format == IPACK_ANY_ID || id->format == device->id_format) && + if ((id->format == IPACK_ANY_FORMAT || + id->format == device->id_format) && (id->vendor == IPACK_ANY_ID || id->vendor == device->id_vendor) && (id->device == IPACK_ANY_ID || id->device == device->id_device)) return id; diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 999c4c25fbf7..70c6a359b2f4 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -600,6 +600,7 @@ struct x86_cpu_id { #define X86_MODEL_ANY 0 #define X86_FEATURE_ANY 0 /* Same as FPU, you can't test for that */ +#define IPACK_ANY_FORMAT 0xff #define IPACK_ANY_ID (~0) struct ipack_device_id { __u8 format; /* Format version or IPACK_ANY_ID */ diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 3c22bda8fdf3..df4fc23dd836 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -973,7 +973,7 @@ static int do_ipack_entry(const char *filename, id->vendor = TO_NATIVE(id->vendor); id->device = TO_NATIVE(id->device); strcpy(alias, "ipack:"); - ADD(alias, "f", id->format != IPACK_ANY_ID, id->format); + ADD(alias, "f", id->format != IPACK_ANY_FORMAT, id->format); ADD(alias, "v", id->vendor != IPACK_ANY_ID, id->vendor); ADD(alias, "d", id->device != IPACK_ANY_ID, id->device); add_wildcard(alias); -- cgit v1.2.3 From f39c1bfb5a03e2d255451bff05be0d7255298fa4 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 7 Sep 2012 11:08:50 -0400 Subject: SUNRPC: Fix a UDP transport regression Commit 43cedbf0e8dfb9c5610eb7985d5f21263e313802 (SUNRPC: Ensure that we grab the XPRT_LOCK before calling xprt_alloc_slot) is causing hangs in the case of NFS over UDP mounts. Since neither the UDP or the RDMA transport mechanism use dynamic slot allocation, we can skip grabbing the socket lock for those transports. Add a new rpc_xprt_op to allow switching between the TCP and UDP/RDMA case. Note that the NFSv4.1 back channel assigns the slot directly through rpc_run_bc_task, so we can ignore that case. Reported-by: Dick Streefland Signed-off-by: Trond Myklebust Cc: stable@vger.kernel.org [>= 3.1] --- include/linux/sunrpc/xprt.h | 3 +++ net/sunrpc/xprt.c | 34 ++++++++++++++++++++-------------- net/sunrpc/xprtrdma/transport.c | 1 + net/sunrpc/xprtsock.c | 3 +++ 4 files changed, 27 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index cff40aa7db62..bf8c49ff7530 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -114,6 +114,7 @@ struct rpc_xprt_ops { void (*set_buffer_size)(struct rpc_xprt *xprt, size_t sndsize, size_t rcvsize); int (*reserve_xprt)(struct rpc_xprt *xprt, struct rpc_task *task); void (*release_xprt)(struct rpc_xprt *xprt, struct rpc_task *task); + void (*alloc_slot)(struct rpc_xprt *xprt, struct rpc_task *task); void (*rpcbind)(struct rpc_task *task); void (*set_port)(struct rpc_xprt *xprt, unsigned short port); void (*connect)(struct rpc_task *task); @@ -281,6 +282,8 @@ void xprt_connect(struct rpc_task *task); void xprt_reserve(struct rpc_task *task); int xprt_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task); int xprt_reserve_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task); +void xprt_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task); +void xprt_lock_and_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task); int xprt_prepare_transmit(struct rpc_task *task); void xprt_transmit(struct rpc_task *task); void xprt_end_transmit(struct rpc_task *task); diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index a5a402a7d21f..5d7f61d7559c 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -969,11 +969,11 @@ static bool xprt_dynamic_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req) return false; } -static void xprt_alloc_slot(struct rpc_task *task) +void xprt_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task) { - struct rpc_xprt *xprt = task->tk_xprt; struct rpc_rqst *req; + spin_lock(&xprt->reserve_lock); if (!list_empty(&xprt->free)) { req = list_entry(xprt->free.next, struct rpc_rqst, rq_list); list_del(&req->rq_list); @@ -994,12 +994,29 @@ static void xprt_alloc_slot(struct rpc_task *task) default: task->tk_status = -EAGAIN; } + spin_unlock(&xprt->reserve_lock); return; out_init_req: task->tk_status = 0; task->tk_rqstp = req; xprt_request_init(task, xprt); + spin_unlock(&xprt->reserve_lock); +} +EXPORT_SYMBOL_GPL(xprt_alloc_slot); + +void xprt_lock_and_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task) +{ + /* Note: grabbing the xprt_lock_write() ensures that we throttle + * new slot allocation if the transport is congested (i.e. when + * reconnecting a stream transport or when out of socket write + * buffer space). + */ + if (xprt_lock_write(xprt, task)) { + xprt_alloc_slot(xprt, task); + xprt_release_write(xprt, task); + } } +EXPORT_SYMBOL_GPL(xprt_lock_and_alloc_slot); static void xprt_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req) { @@ -1083,20 +1100,9 @@ void xprt_reserve(struct rpc_task *task) if (task->tk_rqstp != NULL) return; - /* Note: grabbing the xprt_lock_write() here is not strictly needed, - * but ensures that we throttle new slot allocation if the transport - * is congested (e.g. if reconnecting or if we're out of socket - * write buffer space). - */ task->tk_timeout = 0; task->tk_status = -EAGAIN; - if (!xprt_lock_write(xprt, task)) - return; - - spin_lock(&xprt->reserve_lock); - xprt_alloc_slot(task); - spin_unlock(&xprt->reserve_lock); - xprt_release_write(xprt, task); + xprt->ops->alloc_slot(xprt, task); } static inline __be32 xprt_alloc_xid(struct rpc_xprt *xprt) diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c index 06cdbff79e4a..5d9202dc7cb1 100644 --- a/net/sunrpc/xprtrdma/transport.c +++ b/net/sunrpc/xprtrdma/transport.c @@ -713,6 +713,7 @@ static void xprt_rdma_print_stats(struct rpc_xprt *xprt, struct seq_file *seq) static struct rpc_xprt_ops xprt_rdma_procs = { .reserve_xprt = xprt_rdma_reserve_xprt, .release_xprt = xprt_release_xprt_cong, /* sunrpc/xprt.c */ + .alloc_slot = xprt_alloc_slot, .release_request = xprt_release_rqst_cong, /* ditto */ .set_retrans_timeout = xprt_set_retrans_timeout_def, /* ditto */ .rpcbind = rpcb_getport_async, /* sunrpc/rpcb_clnt.c */ diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 400567243f84..a35b8e52e551 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -2473,6 +2473,7 @@ static void bc_destroy(struct rpc_xprt *xprt) static struct rpc_xprt_ops xs_local_ops = { .reserve_xprt = xprt_reserve_xprt, .release_xprt = xs_tcp_release_xprt, + .alloc_slot = xprt_alloc_slot, .rpcbind = xs_local_rpcbind, .set_port = xs_local_set_port, .connect = xs_connect, @@ -2489,6 +2490,7 @@ static struct rpc_xprt_ops xs_udp_ops = { .set_buffer_size = xs_udp_set_buffer_size, .reserve_xprt = xprt_reserve_xprt_cong, .release_xprt = xprt_release_xprt_cong, + .alloc_slot = xprt_alloc_slot, .rpcbind = rpcb_getport_async, .set_port = xs_set_port, .connect = xs_connect, @@ -2506,6 +2508,7 @@ static struct rpc_xprt_ops xs_udp_ops = { static struct rpc_xprt_ops xs_tcp_ops = { .reserve_xprt = xprt_reserve_xprt, .release_xprt = xs_tcp_release_xprt, + .alloc_slot = xprt_lock_and_alloc_slot, .rpcbind = rpcb_getport_async, .set_port = xs_set_port, .connect = xs_connect, -- cgit v1.2.3 From a8edc3bf05a3465726afdf635a820761fae0d50b Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Wed, 5 Sep 2012 22:50:48 +0000 Subject: net/mlx4_core: Put Firmware flow steering structures in common header files To allow for usage of the flow steering Firmware structures in more locations over the driver, such as the resource tracker, move them from mcg.c to common header files. Signed-off-by: Hadar Hen Zion Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/mcg.c | 90 +++---------------------------- drivers/net/ethernet/mellanox/mlx4/mlx4.h | 76 ++++++++++++++++++++++++++ include/linux/mlx4/device.h | 2 + 3 files changed, 85 insertions(+), 83 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c index a018ea2a43de..2673f3ba935f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c @@ -650,13 +650,6 @@ static int find_entry(struct mlx4_dev *dev, u8 port, return err; } -struct mlx4_net_trans_rule_hw_ctrl { - __be32 ctrl; - __be32 vf_vep_port; - __be32 qpn; - __be32 reserved; -}; - static void trans_rule_ctrl_to_hw(struct mlx4_net_trans_rule *ctrl, struct mlx4_net_trans_rule_hw_ctrl *hw) { @@ -680,87 +673,18 @@ static void trans_rule_ctrl_to_hw(struct mlx4_net_trans_rule *ctrl, hw->qpn = cpu_to_be32(ctrl->qpn); } -struct mlx4_net_trans_rule_hw_ib { - u8 size; - u8 rsvd1; - __be16 id; - u32 rsvd2; - __be32 qpn; - __be32 qpn_mask; - u8 dst_gid[16]; - u8 dst_gid_msk[16]; -} __packed; - -struct mlx4_net_trans_rule_hw_eth { - u8 size; - u8 rsvd; - __be16 id; - u8 rsvd1[6]; - u8 dst_mac[6]; - u16 rsvd2; - u8 dst_mac_msk[6]; - u16 rsvd3; - u8 src_mac[6]; - u16 rsvd4; - u8 src_mac_msk[6]; - u8 rsvd5; - u8 ether_type_enable; - __be16 ether_type; - __be16 vlan_id_msk; - __be16 vlan_id; -} __packed; - -struct mlx4_net_trans_rule_hw_tcp_udp { - u8 size; - u8 rsvd; - __be16 id; - __be16 rsvd1[3]; - __be16 dst_port; - __be16 rsvd2; - __be16 dst_port_msk; - __be16 rsvd3; - __be16 src_port; - __be16 rsvd4; - __be16 src_port_msk; -} __packed; - -struct mlx4_net_trans_rule_hw_ipv4 { - u8 size; - u8 rsvd; - __be16 id; - __be32 rsvd1; - __be32 dst_ip; - __be32 dst_ip_msk; - __be32 src_ip; - __be32 src_ip_msk; -} __packed; - -struct _rule_hw { - union { - struct { - u8 size; - u8 rsvd; - __be16 id; - }; - struct mlx4_net_trans_rule_hw_eth eth; - struct mlx4_net_trans_rule_hw_ib ib; - struct mlx4_net_trans_rule_hw_ipv4 ipv4; - struct mlx4_net_trans_rule_hw_tcp_udp tcp_udp; - }; +const u16 __sw_id_hw[] = { + [MLX4_NET_TRANS_RULE_ID_ETH] = 0xE001, + [MLX4_NET_TRANS_RULE_ID_IB] = 0xE005, + [MLX4_NET_TRANS_RULE_ID_IPV6] = 0xE003, + [MLX4_NET_TRANS_RULE_ID_IPV4] = 0xE002, + [MLX4_NET_TRANS_RULE_ID_TCP] = 0xE004, + [MLX4_NET_TRANS_RULE_ID_UDP] = 0xE006 }; static int parse_trans_rule(struct mlx4_dev *dev, struct mlx4_spec_list *spec, struct _rule_hw *rule_hw) { - static const u16 __sw_id_hw[] = { - [MLX4_NET_TRANS_RULE_ID_ETH] = 0xE001, - [MLX4_NET_TRANS_RULE_ID_IB] = 0xE005, - [MLX4_NET_TRANS_RULE_ID_IPV6] = 0xE003, - [MLX4_NET_TRANS_RULE_ID_IPV4] = 0xE002, - [MLX4_NET_TRANS_RULE_ID_TCP] = 0xE004, - [MLX4_NET_TRANS_RULE_ID_UDP] = 0xE006 - }; - static const size_t __rule_hw_sz[] = { [MLX4_NET_TRANS_RULE_ID_ETH] = sizeof(struct mlx4_net_trans_rule_hw_eth), diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 4d9df8f2a126..dba69d98734a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -690,6 +690,82 @@ struct mlx4_steer { struct list_head steer_entries[MLX4_NUM_STEERS]; }; +struct mlx4_net_trans_rule_hw_ctrl { + __be32 ctrl; + __be32 vf_vep_port; + __be32 qpn; + __be32 reserved; +}; + +struct mlx4_net_trans_rule_hw_ib { + u8 size; + u8 rsvd1; + __be16 id; + u32 rsvd2; + __be32 qpn; + __be32 qpn_mask; + u8 dst_gid[16]; + u8 dst_gid_msk[16]; +} __packed; + +struct mlx4_net_trans_rule_hw_eth { + u8 size; + u8 rsvd; + __be16 id; + u8 rsvd1[6]; + u8 dst_mac[6]; + u16 rsvd2; + u8 dst_mac_msk[6]; + u16 rsvd3; + u8 src_mac[6]; + u16 rsvd4; + u8 src_mac_msk[6]; + u8 rsvd5; + u8 ether_type_enable; + __be16 ether_type; + __be16 vlan_id_msk; + __be16 vlan_id; +} __packed; + +struct mlx4_net_trans_rule_hw_tcp_udp { + u8 size; + u8 rsvd; + __be16 id; + __be16 rsvd1[3]; + __be16 dst_port; + __be16 rsvd2; + __be16 dst_port_msk; + __be16 rsvd3; + __be16 src_port; + __be16 rsvd4; + __be16 src_port_msk; +} __packed; + +struct mlx4_net_trans_rule_hw_ipv4 { + u8 size; + u8 rsvd; + __be16 id; + __be32 rsvd1; + __be32 dst_ip; + __be32 dst_ip_msk; + __be32 src_ip; + __be32 src_ip_msk; +} __packed; + +struct _rule_hw { + union { + struct { + u8 size; + u8 rsvd; + __be16 id; + }; + struct mlx4_net_trans_rule_hw_eth eth; + struct mlx4_net_trans_rule_hw_ib ib; + struct mlx4_net_trans_rule_hw_ipv4 ipv4; + struct mlx4_net_trans_rule_hw_tcp_udp tcp_udp; + }; +}; + struct mlx4_priv { struct mlx4_dev dev; diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index bd6c9fcdf2dd..244ba902ab72 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -796,6 +796,8 @@ enum mlx4_net_trans_rule_id { MLX4_NET_TRANS_RULE_NUM, /* should be last */ }; +extern const u16 __sw_id_hw[]; + enum mlx4_net_trans_promisc_mode { MLX4_FS_PROMISC_NONE = 0, MLX4_FS_PROMISC_UPLINK, -- cgit v1.2.3 From 7fb40f87c4195ec1728527f30bc744c47a45b366 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Wed, 5 Sep 2012 22:50:49 +0000 Subject: net/mlx4_core: Add security check / enforcement for flow steering rules set for VMs Since VFs may be mapped to VMs which aren't trusted entities, flow steering rules attached through the wrapper on behalf of VFs must be checked to make sure that their L2 specification relate to MAC address assigned to that VF, and add L2 specification if its missing. Signed-off-by: Hadar Hen Zion Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlx4/resource_tracker.c | 116 +++++++++++++++++++++ include/linux/mlx4/device.h | 11 ++ 2 files changed, 127 insertions(+) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 94ceddd17ab2..293c9e820c49 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -42,6 +42,7 @@ #include #include #include +#include #include "mlx4.h" #include "fw.h" @@ -2776,18 +2777,133 @@ ex_put: return err; } +/* + * MAC validation for Flow Steering rules. + * VF can attach rules only with a mac address which is assigned to it. + */ +static int validate_eth_header_mac(int slave, struct _rule_hw *eth_header, + struct list_head *rlist) +{ + struct mac_res *res, *tmp; + __be64 be_mac; + + /* make sure it isn't multicast or broadcast mac*/ + if (!is_multicast_ether_addr(eth_header->eth.dst_mac) && + !is_broadcast_ether_addr(eth_header->eth.dst_mac)) { + list_for_each_entry_safe(res, tmp, rlist, list) { + be_mac = cpu_to_be64(res->mac << 16); + if (!memcmp(&be_mac, eth_header->eth.dst_mac, ETH_ALEN)) + return 0; + } + pr_err("MAC %pM doesn't belong to VF %d, Steering rule rejected\n", + eth_header->eth.dst_mac, slave); + return -EINVAL; + } + return 0; +} + +/* + * In case of missing eth header, append eth header with a MAC address + * assigned to the VF. + */ +static int add_eth_header(struct mlx4_dev *dev, int slave, + struct mlx4_cmd_mailbox *inbox, + struct list_head *rlist, int header_id) +{ + struct mac_res *res, *tmp; + u8 port; + struct mlx4_net_trans_rule_hw_ctrl *ctrl; + struct mlx4_net_trans_rule_hw_eth *eth_header; + struct mlx4_net_trans_rule_hw_ipv4 *ip_header; + struct mlx4_net_trans_rule_hw_tcp_udp *l4_header; + __be64 be_mac = 0; + __be64 mac_msk = cpu_to_be64(MLX4_MAC_MASK << 16); + + ctrl = (struct mlx4_net_trans_rule_hw_ctrl *)inbox->buf; + port = be32_to_cpu(ctrl->vf_vep_port) & 0xff; + eth_header = (struct mlx4_net_trans_rule_hw_eth *)(ctrl + 1); + + /* Clear a space in the inbox for eth header */ + switch (header_id) { + case MLX4_NET_TRANS_RULE_ID_IPV4: + ip_header = + (struct mlx4_net_trans_rule_hw_ipv4 *)(eth_header + 1); + memmove(ip_header, eth_header, + sizeof(*ip_header) + sizeof(*l4_header)); + break; + case MLX4_NET_TRANS_RULE_ID_TCP: + case MLX4_NET_TRANS_RULE_ID_UDP: + l4_header = (struct mlx4_net_trans_rule_hw_tcp_udp *) + (eth_header + 1); + memmove(l4_header, eth_header, sizeof(*l4_header)); + break; + default: + return -EINVAL; + } + list_for_each_entry_safe(res, tmp, rlist, list) { + if (port == res->port) { + be_mac = cpu_to_be64(res->mac << 16); + break; + } + } + if (!be_mac) { + pr_err("Failed adding eth header to FS rule, Can't find matching MAC for port %d .\n", + port); + return -EINVAL; + } + + memset(eth_header, 0, sizeof(*eth_header)); + eth_header->size = sizeof(*eth_header) >> 2; + eth_header->id = cpu_to_be16(__sw_id_hw[MLX4_NET_TRANS_RULE_ID_ETH]); + memcpy(eth_header->dst_mac, &be_mac, ETH_ALEN); + memcpy(eth_header->dst_mac_msk, &mac_msk, ETH_ALEN); + + return 0; + +} + int mlx4_QP_FLOW_STEERING_ATTACH_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) { + + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_resource_tracker *tracker = &priv->mfunc.master.res_tracker; + struct list_head *rlist = &tracker->slave_list[slave].res_list[RES_MAC]; int err; + struct mlx4_net_trans_rule_hw_ctrl *ctrl; + struct _rule_hw *rule_header; + int header_id; if (dev->caps.steering_mode != MLX4_STEERING_MODE_DEVICE_MANAGED) return -EOPNOTSUPP; + ctrl = (struct mlx4_net_trans_rule_hw_ctrl *)inbox->buf; + rule_header = (struct _rule_hw *)(ctrl + 1); + header_id = map_hw_to_sw_id(be16_to_cpu(rule_header->id)); + + switch (header_id) { + case MLX4_NET_TRANS_RULE_ID_ETH: + if (validate_eth_header_mac(slave, rule_header, rlist)) + return -EINVAL; + break; + case MLX4_NET_TRANS_RULE_ID_IPV4: + case MLX4_NET_TRANS_RULE_ID_TCP: + case MLX4_NET_TRANS_RULE_ID_UDP: + pr_warn("Can't attach FS rule without L2 headers, adding L2 header.\n"); + if (add_eth_header(dev, slave, inbox, rlist, header_id)) + return -EINVAL; + vhcr->in_modifier += + sizeof(struct mlx4_net_trans_rule_hw_eth) >> 2; + break; + default: + pr_err("Corrupted mailbox.\n"); + return -EINVAL; + } + err = mlx4_cmd_imm(dev, inbox->dma, &vhcr->out_param, vhcr->in_modifier, 0, MLX4_QP_FLOW_STEERING_ATTACH, MLX4_CMD_TIME_CLASS_A, diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 244ba902ab72..6e1b0f973a03 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -798,6 +798,17 @@ enum mlx4_net_trans_rule_id { extern const u16 __sw_id_hw[]; +static inline int map_hw_to_sw_id(u16 header_id) +{ + + int i; + for (i = 0; i < MLX4_NET_TRANS_RULE_NUM; i++) { + if (header_id == __sw_id_hw[i]) + return i; + } + return -EINVAL; +} + enum mlx4_net_trans_promisc_mode { MLX4_FS_PROMISC_NONE = 0, MLX4_FS_PROMISC_UPLINK, -- cgit v1.2.3 From 32a8811ff164f882712c17946e58e52444f464a7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 7 Sep 2012 17:30:36 +0200 Subject: target: support zero allocation length in REQUEST SENSE Similar to INQUIRY and MODE SENSE, construct the sense data in a buffer and later copy it to the scatterlist. Do not do anything, but still clear a pending unit attention condition, if the allocation length is zero. However, SPC tells us that "If a REQUEST SENSE command is terminated with CHECK CONDITION status [and] the REQUEST SENSE command was received on an I_T nexus with a pending unit attention condition (i.e., before the device server reports CHECK CONDITION status), then the device server shall not clear the pending unit attention condition." Do the transport_kmap_data_sg early to detect this case. It also tells us "Device servers shall not adjust the additional sense length to reflect truncation if the allocation length is less than the sense data available", so do not do that! Note that the err variable is write-only. Signed-off-by: Paolo Bonzini Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_spc.c | 35 ++++++++++++++++++----------------- include/target/target_core_base.h | 1 + 2 files changed, 19 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c index 4c861de538c9..388a922c8f6d 100644 --- a/drivers/target/target_core_spc.c +++ b/drivers/target/target_core_spc.c @@ -877,9 +877,11 @@ static int spc_emulate_modesense(struct se_cmd *cmd) static int spc_emulate_request_sense(struct se_cmd *cmd) { unsigned char *cdb = cmd->t_task_cdb; - unsigned char *buf; + unsigned char *rbuf; u8 ua_asc = 0, ua_ascq = 0; - int err = 0; + unsigned char buf[SE_SENSE_BUF]; + + memset(buf, 0, SE_SENSE_BUF); if (cdb[1] & 0x01) { pr_err("REQUEST_SENSE description emulation not" @@ -888,20 +890,21 @@ static int spc_emulate_request_sense(struct se_cmd *cmd) return -ENOSYS; } - buf = transport_kmap_data_sg(cmd); - - if (!core_scsi3_ua_clear_for_request_sense(cmd, &ua_asc, &ua_ascq)) { + rbuf = transport_kmap_data_sg(cmd); + if (cmd->scsi_sense_reason != 0) { + /* + * Out of memory. We will fail with CHECK CONDITION, so + * we must not clear the unit attention condition. + */ + target_complete_cmd(cmd, CHECK_CONDITION); + return 0; + } else if (!core_scsi3_ua_clear_for_request_sense(cmd, &ua_asc, &ua_ascq)) { /* * CURRENT ERROR, UNIT ATTENTION */ buf[0] = 0x70; buf[SPC_SENSE_KEY_OFFSET] = UNIT_ATTENTION; - if (cmd->data_length < 18) { - buf[7] = 0x00; - err = -EINVAL; - goto end; - } /* * The Additional Sense Code (ASC) from the UNIT ATTENTION */ @@ -915,11 +918,6 @@ static int spc_emulate_request_sense(struct se_cmd *cmd) buf[0] = 0x70; buf[SPC_SENSE_KEY_OFFSET] = NO_SENSE; - if (cmd->data_length < 18) { - buf[7] = 0x00; - err = -EINVAL; - goto end; - } /* * NO ADDITIONAL SENSE INFORMATION */ @@ -927,8 +925,11 @@ static int spc_emulate_request_sense(struct se_cmd *cmd) buf[7] = 0x0A; } -end: - transport_kunmap_data_sg(cmd); + if (rbuf) { + memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length)); + transport_kunmap_data_sg(cmd); + } + target_complete_cmd(cmd, GOOD); return 0; } diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 015cea01ae39..5be89373ceac 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -121,6 +121,7 @@ #define SE_INQUIRY_BUF 512 #define SE_MODE_PAGE_BUF 512 +#define SE_SENSE_BUF 96 /* struct se_hba->hba_flags */ enum hba_flags_table { -- cgit v1.2.3 From dbe9a4173ea53b72b2c35d19f676a85b69f1c9fe Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 6 Sep 2012 18:20:01 +0000 Subject: scm: Don't use struct ucred in NETLINK_CB and struct scm_cookie. Passing uids and gids on NETLINK_CB from a process in one user namespace to a process in another user namespace can result in the wrong uid or gid being presented to userspace. Avoid that problem by passing kuids and kgids instead. - define struct scm_creds for use in scm_cookie and netlink_skb_parms that holds uid and gid information in kuid_t and kgid_t. - Modify scm_set_cred to fill out scm_creds by heand instead of using cred_to_ucred to fill out struct ucred. This conversion ensures userspace does not get incorrect uid or gid values to look at. - Modify scm_recv to convert from struct scm_creds to struct ucred before copying credential values to userspace. - Modify __scm_send to populate struct scm_creds on in the scm_cookie, instead of just copying struct ucred from userspace. - Modify netlink_sendmsg to copy scm_creds instead of struct ucred into the NETLINK_CB. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- include/linux/netlink.h | 3 ++- include/net/scm.h | 23 +++++++++++++++++++---- net/core/scm.c | 17 +++++++++++------ net/netlink/af_netlink.c | 2 +- 4 files changed, 33 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/netlink.h b/include/linux/netlink.h index c9fdde2bc73f..df73cf4b0290 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -153,6 +153,7 @@ struct nlattr { #include #include +#include struct net; @@ -162,7 +163,7 @@ static inline struct nlmsghdr *nlmsg_hdr(const struct sk_buff *skb) } struct netlink_skb_parms { - struct ucred creds; /* Skb credentials */ + struct scm_creds creds; /* Skb credentials */ __u32 pid; __u32 dst_group; struct sock *ssk; diff --git a/include/net/scm.h b/include/net/scm.h index 7dc0854f0b38..456695f5cbc4 100644 --- a/include/net/scm.h +++ b/include/net/scm.h @@ -12,6 +12,12 @@ */ #define SCM_MAX_FD 253 +struct scm_creds { + u32 pid; + kuid_t uid; + kgid_t gid; +}; + struct scm_fp_list { short count; short max; @@ -22,7 +28,7 @@ struct scm_cookie { struct pid *pid; /* Skb credentials */ const struct cred *cred; struct scm_fp_list *fp; /* Passed files */ - struct ucred creds; /* Skb credentials */ + struct scm_creds creds; /* Skb credentials */ #ifdef CONFIG_SECURITY_NETWORK u32 secid; /* Passed security ID */ #endif @@ -49,7 +55,9 @@ static __inline__ void scm_set_cred(struct scm_cookie *scm, { scm->pid = get_pid(pid); scm->cred = cred ? get_cred(cred) : NULL; - cred_to_ucred(pid, cred, &scm->creds); + scm->creds.pid = pid_vnr(pid); + scm->creds.uid = cred ? cred->euid : INVALID_UID; + scm->creds.gid = cred ? cred->egid : INVALID_GID; } static __inline__ void scm_destroy_cred(struct scm_cookie *scm) @@ -112,8 +120,15 @@ static __inline__ void scm_recv(struct socket *sock, struct msghdr *msg, return; } - if (test_bit(SOCK_PASSCRED, &sock->flags)) - put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, sizeof(scm->creds), &scm->creds); + if (test_bit(SOCK_PASSCRED, &sock->flags)) { + struct user_namespace *current_ns = current_user_ns(); + struct ucred ucreds = { + .pid = scm->creds.pid, + .uid = from_kuid_munged(current_ns, scm->creds.uid), + .gid = from_kgid_munged(current_ns, scm->creds.gid), + }; + put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, sizeof(ucreds), &ucreds); + } scm_destroy_cred(scm); diff --git a/net/core/scm.c b/net/core/scm.c index 6ab491d6c26f..9c1c63da3ca8 100644 --- a/net/core/scm.c +++ b/net/core/scm.c @@ -155,19 +155,21 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p) break; case SCM_CREDENTIALS: { + struct ucred creds; kuid_t uid; kgid_t gid; if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct ucred))) goto error; - memcpy(&p->creds, CMSG_DATA(cmsg), sizeof(struct ucred)); - err = scm_check_creds(&p->creds); + memcpy(&creds, CMSG_DATA(cmsg), sizeof(struct ucred)); + err = scm_check_creds(&creds); if (err) goto error; - if (!p->pid || pid_vnr(p->pid) != p->creds.pid) { + p->creds.pid = creds.pid; + if (!p->pid || pid_vnr(p->pid) != creds.pid) { struct pid *pid; err = -ESRCH; - pid = find_get_pid(p->creds.pid); + pid = find_get_pid(creds.pid); if (!pid) goto error; put_pid(p->pid); @@ -175,11 +177,14 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p) } err = -EINVAL; - uid = make_kuid(current_user_ns(), p->creds.uid); - gid = make_kgid(current_user_ns(), p->creds.gid); + uid = make_kuid(current_user_ns(), creds.uid); + gid = make_kgid(current_user_ns(), creds.gid); if (!uid_valid(uid) || !gid_valid(gid)) goto error; + p->creds.uid = uid; + p->creds.gid = gid; + if (!p->cred || !uid_eq(p->cred->euid, uid) || !gid_eq(p->cred->egid, gid)) { diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 382119917166..f530b1ca1773 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1399,7 +1399,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, NETLINK_CB(skb).pid = nlk->pid; NETLINK_CB(skb).dst_group = dst_group; - memcpy(NETLINK_CREDS(skb), &siocb->scm->creds, sizeof(struct ucred)); + NETLINK_CB(skb).creds = siocb->scm->creds; err = -EFAULT; if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { -- cgit v1.2.3 From 4ccfe6d4109252dfadcd6885f33ed600ee03dbf8 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Fri, 7 Sep 2012 00:45:29 +0000 Subject: ipv4/route: arg delay is useless in rt_cache_flush() Since route cache deletion (89aef8921bfbac22f), delay is no more used. Remove it. Signed-off-by: Nicolas Dichtel Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/route.h | 2 +- net/ipv4/arp.c | 2 +- net/ipv4/devinet.c | 6 +++--- net/ipv4/fib_frontend.c | 20 ++++++++++---------- net/ipv4/fib_rules.c | 2 +- net/ipv4/fib_trie.c | 6 +++--- net/ipv4/route.c | 19 +++---------------- 7 files changed, 22 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/include/net/route.h b/include/net/route.h index 776a27f1ab78..da22243d2760 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -108,7 +108,7 @@ extern struct ip_rt_acct __percpu *ip_rt_acct; struct in_device; extern int ip_rt_init(void); -extern void rt_cache_flush(struct net *net, int how); +extern void rt_cache_flush(struct net *net); extern void rt_flush_dev(struct net_device *dev); extern struct rtable *__ip_route_output_key(struct net *, struct flowi4 *flp); extern struct rtable *ip_route_output_flow(struct net *, struct flowi4 *flp, diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 77e87aff419a..47800459e4cb 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -1225,7 +1225,7 @@ static int arp_netdev_event(struct notifier_block *this, unsigned long event, switch (event) { case NETDEV_CHANGEADDR: neigh_changeaddr(&arp_tbl, dev); - rt_cache_flush(dev_net(dev), 0); + rt_cache_flush(dev_net(dev)); break; default: break; diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index adf273f8ad2e..f14eff506743 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1500,7 +1500,7 @@ static int devinet_conf_proc(ctl_table *ctl, int write, if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 || i == IPV4_DEVCONF_ROUTE_LOCALNET - 1) if ((new_value == 0) && (old_value != 0)) - rt_cache_flush(net, 0); + rt_cache_flush(net); } return ret; @@ -1534,7 +1534,7 @@ static int devinet_sysctl_forward(ctl_table *ctl, int write, dev_disable_lro(idev->dev); } rtnl_unlock(); - rt_cache_flush(net, 0); + rt_cache_flush(net); } } @@ -1551,7 +1551,7 @@ static int ipv4_doint_and_flush(ctl_table *ctl, int write, struct net *net = ctl->extra2; if (write && *valp != val) - rt_cache_flush(net, 0); + rt_cache_flush(net); return ret; } diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index acdee325d972..8a7cd795a96e 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -148,7 +148,7 @@ static void fib_flush(struct net *net) } if (flushed) - rt_cache_flush(net, -1); + rt_cache_flush(net); } /* @@ -999,11 +999,11 @@ static void nl_fib_lookup_exit(struct net *net) net->ipv4.fibnl = NULL; } -static void fib_disable_ip(struct net_device *dev, int force, int delay) +static void fib_disable_ip(struct net_device *dev, int force) { if (fib_sync_down_dev(dev, force)) fib_flush(dev_net(dev)); - rt_cache_flush(dev_net(dev), delay); + rt_cache_flush(dev_net(dev)); arp_ifdown(dev); } @@ -1020,7 +1020,7 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event, fib_sync_up(dev); #endif atomic_inc(&net->ipv4.dev_addr_genid); - rt_cache_flush(dev_net(dev), -1); + rt_cache_flush(dev_net(dev)); break; case NETDEV_DOWN: fib_del_ifaddr(ifa, NULL); @@ -1029,9 +1029,9 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event, /* Last address was deleted from this interface. * Disable IP. */ - fib_disable_ip(dev, 1, 0); + fib_disable_ip(dev, 1); } else { - rt_cache_flush(dev_net(dev), -1); + rt_cache_flush(dev_net(dev)); } break; } @@ -1045,7 +1045,7 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo struct net *net = dev_net(dev); if (event == NETDEV_UNREGISTER) { - fib_disable_ip(dev, 2, -1); + fib_disable_ip(dev, 2); rt_flush_dev(dev); return NOTIFY_DONE; } @@ -1061,14 +1061,14 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo fib_sync_up(dev); #endif atomic_inc(&net->ipv4.dev_addr_genid); - rt_cache_flush(net, -1); + rt_cache_flush(net); break; case NETDEV_DOWN: - fib_disable_ip(dev, 0, 0); + fib_disable_ip(dev, 0); break; case NETDEV_CHANGEMTU: case NETDEV_CHANGE: - rt_cache_flush(net, 0); + rt_cache_flush(net); break; } return NOTIFY_DONE; diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index a83d74e498d2..274309d3aded 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -259,7 +259,7 @@ static size_t fib4_rule_nlmsg_payload(struct fib_rule *rule) static void fib4_rule_flush_cache(struct fib_rules_ops *ops) { - rt_cache_flush(ops->fro_net, -1); + rt_cache_flush(ops->fro_net); } static const struct fib_rules_ops __net_initdata fib4_rules_ops_template = { diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 3c820dae235e..8d766106b540 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1286,7 +1286,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg) fib_release_info(fi_drop); if (state & FA_S_ACCESSED) - rt_cache_flush(cfg->fc_nlinfo.nl_net, -1); + rt_cache_flush(cfg->fc_nlinfo.nl_net); rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, tb->tb_id, &cfg->fc_nlinfo, NLM_F_REPLACE); @@ -1333,7 +1333,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg) list_add_tail_rcu(&new_fa->fa_list, (fa ? &fa->fa_list : fa_head)); - rt_cache_flush(cfg->fc_nlinfo.nl_net, -1); + rt_cache_flush(cfg->fc_nlinfo.nl_net); rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, tb->tb_id, &cfg->fc_nlinfo, 0); succeeded: @@ -1711,7 +1711,7 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg) trie_leaf_remove(t, l); if (fa->fa_state & FA_S_ACCESSED) - rt_cache_flush(cfg->fc_nlinfo.nl_net, -1); + rt_cache_flush(cfg->fc_nlinfo.nl_net); fib_release_info(fa->fa_info); alias_free_mem_rcu(fa); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index dc9549b5eb1c..ab1e7c7c38fc 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -461,11 +461,7 @@ static void rt_cache_invalidate(struct net *net) atomic_add(shuffle + 1U, &net->ipv4.rt_genid); } -/* - * delay < 0 : invalidate cache (fast : entries will be deleted later) - * delay >= 0 : invalidate & flush cache (can be long) - */ -void rt_cache_flush(struct net *net, int delay) +void rt_cache_flush(struct net *net) { rt_cache_invalidate(net); } @@ -2345,7 +2341,7 @@ int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb) void ip_rt_multicast_event(struct in_device *in_dev) { - rt_cache_flush(dev_net(in_dev->dev), 0); + rt_cache_flush(dev_net(in_dev->dev)); } #ifdef CONFIG_SYSCTL @@ -2354,16 +2350,7 @@ static int ipv4_sysctl_rtcache_flush(ctl_table *__ctl, int write, size_t *lenp, loff_t *ppos) { if (write) { - int flush_delay; - ctl_table ctl; - struct net *net; - - memcpy(&ctl, __ctl, sizeof(ctl)); - ctl.data = &flush_delay; - proc_dointvec(&ctl, write, buffer, lenp, ppos); - - net = (struct net *)__ctl->extra1; - rt_cache_flush(net, flush_delay); + rt_cache_flush((struct net *)__ctl->extra1); return 0; } -- cgit v1.2.3 From 220a60a425146b0e37998cc0b3082f0541aad866 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 3 Sep 2012 11:34:58 +0100 Subject: pps/ptp: Allow PHC devices to adjust PPS events for known delay Initial version by Stuart Hodgson Some PHC device drivers may deliver PPS events with a significant and variable delay, but still be able to measure precisely what that delay is. Add a pps_sub_ts() function for subtracting a delay from the timestamp(s) in a PPS event, and a PTP event type (PTP_CLOCK_PPSUSR) for which the caller provides a complete PPS event. Signed-off-by: Ben Hutchings --- drivers/ptp/ptp_clock.c | 5 +++++ include/linux/pps_kernel.h | 9 +++++++++ include/linux/ptp_clock_kernel.h | 10 ++++++++-- 3 files changed, 22 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c index 1e528b539a07..966875dcda56 100644 --- a/drivers/ptp/ptp_clock.c +++ b/drivers/ptp/ptp_clock.c @@ -300,6 +300,11 @@ void ptp_clock_event(struct ptp_clock *ptp, struct ptp_clock_event *event) pps_get_ts(&evt); pps_event(ptp->pps_source, &evt, PTP_PPS_EVENT, NULL); break; + + case PTP_CLOCK_PPSUSR: + pps_event(ptp->pps_source, &event->pps_times, + PTP_PPS_EVENT, NULL); + break; } } EXPORT_SYMBOL(ptp_clock_event); diff --git a/include/linux/pps_kernel.h b/include/linux/pps_kernel.h index 94048547f29a..0cc45ae1afd5 100644 --- a/include/linux/pps_kernel.h +++ b/include/linux/pps_kernel.h @@ -116,5 +116,14 @@ static inline void pps_get_ts(struct pps_event_time *ts) #endif /* CONFIG_NTP_PPS */ +/* Subtract known time delay from PPS event time(s) */ +static inline void pps_sub_ts(struct pps_event_time *ts, struct timespec delta) +{ + ts->ts_real = timespec_sub(ts->ts_real, delta); +#ifdef CONFIG_NTP_PPS + ts->ts_raw = timespec_sub(ts->ts_raw, delta); +#endif +} + #endif /* LINUX_PPS_KERNEL_H */ diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h index 945704c2ed65..a644b29f1161 100644 --- a/include/linux/ptp_clock_kernel.h +++ b/include/linux/ptp_clock_kernel.h @@ -21,6 +21,7 @@ #ifndef _PTP_CLOCK_KERNEL_H_ #define _PTP_CLOCK_KERNEL_H_ +#include #include @@ -110,6 +111,7 @@ enum ptp_clock_events { PTP_CLOCK_ALARM, PTP_CLOCK_EXTTS, PTP_CLOCK_PPS, + PTP_CLOCK_PPSUSR, }; /** @@ -117,13 +119,17 @@ enum ptp_clock_events { * * @type: One of the ptp_clock_events enumeration values. * @index: Identifies the source of the event. - * @timestamp: When the event occured. + * @timestamp: When the event occurred (%PTP_CLOCK_EXTTS only). + * @pps_times: When the event occurred (%PTP_CLOCK_PPSUSR only). */ struct ptp_clock_event { int type; int index; - u64 timestamp; + union { + u64 timestamp; + struct pps_event_time pps_times; + }; }; /** -- cgit v1.2.3 From 494530284f16298050ab99f54b7b12dd7d1418a1 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 7 Sep 2012 09:33:14 -0700 Subject: PCI: Make pci_error_handlers const Since pci_error_handlers is just a function table make it const. Signed-off-by: Stephen Hemminger Signed-off-by: Bjorn Helgaas Acked-by: Linas Vepstas --- drivers/pci/pcie/aer/aerdrv.c | 2 +- drivers/pci/pcie/aer/aerdrv_core.c | 8 ++++---- drivers/pci/pcie/portdrv_pci.c | 10 +++++----- include/linux/pci.h | 2 +- include/linux/pcieport_if.h | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index 58ad7917553c..7131644e3ae6 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -48,7 +48,7 @@ static pci_ers_result_t aer_error_detected(struct pci_dev *dev, static void aer_error_resume(struct pci_dev *dev); static pci_ers_result_t aer_root_reset(struct pci_dev *dev); -static struct pci_error_handlers aer_error_handlers = { +static const struct pci_error_handlers aer_error_handlers = { .error_detected = aer_error_detected, .resume = aer_error_resume, }; diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 0ca053538146..dc901771d34b 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -240,7 +240,7 @@ static bool find_source_device(struct pci_dev *parent, static int report_error_detected(struct pci_dev *dev, void *data) { pci_ers_result_t vote; - struct pci_error_handlers *err_handler; + const struct pci_error_handlers *err_handler; struct aer_broadcast_data *result_data; result_data = (struct aer_broadcast_data *) data; @@ -274,7 +274,7 @@ static int report_error_detected(struct pci_dev *dev, void *data) static int report_mmio_enabled(struct pci_dev *dev, void *data) { pci_ers_result_t vote; - struct pci_error_handlers *err_handler; + const struct pci_error_handlers *err_handler; struct aer_broadcast_data *result_data; result_data = (struct aer_broadcast_data *) data; @@ -292,7 +292,7 @@ static int report_mmio_enabled(struct pci_dev *dev, void *data) static int report_slot_reset(struct pci_dev *dev, void *data) { pci_ers_result_t vote; - struct pci_error_handlers *err_handler; + const struct pci_error_handlers *err_handler; struct aer_broadcast_data *result_data; result_data = (struct aer_broadcast_data *) data; @@ -309,7 +309,7 @@ static int report_slot_reset(struct pci_dev *dev, void *data) static int report_resume(struct pci_dev *dev, void *data) { - struct pci_error_handlers *err_handler; + const struct pci_error_handlers *err_handler; dev->error_state = pci_channel_io_normal; diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 3a7eefcb270a..943443510089 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -371,11 +371,11 @@ static const struct pci_device_id port_pci_ids[] = { { }; MODULE_DEVICE_TABLE(pci, port_pci_ids); -static struct pci_error_handlers pcie_portdrv_err_handler = { - .error_detected = pcie_portdrv_error_detected, - .mmio_enabled = pcie_portdrv_mmio_enabled, - .slot_reset = pcie_portdrv_slot_reset, - .resume = pcie_portdrv_err_resume, +static const struct pci_error_handlers pcie_portdrv_err_handler = { + .error_detected = pcie_portdrv_error_detected, + .mmio_enabled = pcie_portdrv_mmio_enabled, + .slot_reset = pcie_portdrv_slot_reset, + .resume = pcie_portdrv_err_resume, }; static struct pci_driver pcie_portdriver = { diff --git a/include/linux/pci.h b/include/linux/pci.h index 5faa8310eec9..f0f2b80e5e7f 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -596,7 +596,7 @@ struct pci_driver { int (*resume_early) (struct pci_dev *dev); int (*resume) (struct pci_dev *dev); /* Device woken up */ void (*shutdown) (struct pci_dev *dev); - struct pci_error_handlers *err_handler; + const struct pci_error_handlers *err_handler; struct device_driver driver; struct pci_dynids dynids; }; diff --git a/include/linux/pcieport_if.h b/include/linux/pcieport_if.h index 6775532b92a9..e6f91b1406d8 100644 --- a/include/linux/pcieport_if.h +++ b/include/linux/pcieport_if.h @@ -49,7 +49,7 @@ struct pcie_port_service_driver { int (*resume) (struct pcie_device *dev); /* Service Error Recovery Handler */ - struct pci_error_handlers *err_handler; + const struct pci_error_handlers *err_handler; /* Link Reset Capability - AER service driver specific */ pci_ers_result_t (*reset_link) (struct pci_dev *dev); -- cgit v1.2.3 From 494bfec99922d54054d2d0873f1017680cfc3f13 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 22 Aug 2012 21:36:27 +0800 Subject: clk: add of_clk_src_onecell_get() support For those SoCs that have hundreds of clock outputs, their clock DT bindings could reasonably define #clock-cells as 1 and require the client device specify the index of the clock it consumes in the cell of its "clocks" phandle. Add a generic of_clk_src_onecell_get() function for this purpose. Signed-off-by: Shawn Guo Reviewed-by: Rob Herring Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 14 ++++++++++++++ include/linux/clk-provider.h | 5 +++++ 2 files changed, 19 insertions(+) (limited to 'include') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index d9cbae06549f..56e4495ebeb1 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1587,6 +1587,20 @@ struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec, } EXPORT_SYMBOL_GPL(of_clk_src_simple_get); +struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data) +{ + struct clk_onecell_data *clk_data = data; + unsigned int idx = clkspec->args[0]; + + if (idx >= clk_data->clk_num) { + pr_err("%s: invalid clock index %d\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return clk_data->clks[idx]; +} +EXPORT_SYMBOL_GPL(of_clk_src_onecell_get); + /** * of_clk_add_provider() - Register a clock provider for a node * @np: Device node pointer associated with clock provider diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 1b15307cd466..c12731582920 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -361,6 +361,11 @@ int of_clk_add_provider(struct device_node *np, void of_clk_del_provider(struct device_node *np); struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec, void *data); +struct clk_onecell_data { + struct clk **clks; + unsigned int clk_num; +}; +struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data); const char *of_clk_get_parent_name(struct device_node *np, int index); void of_clk_init(const struct of_device_id *matches); -- cgit v1.2.3 From ce56ade6ae74e604a4b5d6ea5b1d58960fa8e7aa Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 4 Sep 2012 13:38:00 +0100 Subject: iio: Drop timestamp parameter from buffer store_to callback Drop timestamp parameter from buffer store_to callback and subsequently from iio_push_to_buffer. The timestamp parameter is unused and it seems likely that it will stay unused in the future, so it should be safe to remove it. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/accel/hid-sensor-accel-3d.c | 3 +-- drivers/iio/adc/ad7266.c | 2 +- drivers/iio/adc/ad_sigma_delta.c | 2 +- drivers/iio/adc/at91_adc.c | 2 +- drivers/iio/gyro/hid-sensor-gyro-3d.c | 3 +-- drivers/iio/industrialio-buffer.c | 5 ++--- drivers/iio/kfifo_buf.c | 3 +-- drivers/iio/light/adjd_s311.c | 2 +- drivers/iio/light/hid-sensor-als.c | 3 +-- drivers/iio/magnetometer/hid-sensor-magn-3d.c | 3 +-- drivers/staging/iio/accel/adis16201_ring.c | 2 +- drivers/staging/iio/accel/adis16203_ring.c | 2 +- drivers/staging/iio/accel/adis16204_ring.c | 2 +- drivers/staging/iio/accel/adis16209_ring.c | 2 +- drivers/staging/iio/accel/adis16240_ring.c | 2 +- drivers/staging/iio/accel/lis3l02dq_ring.c | 2 +- drivers/staging/iio/adc/ad7298_ring.c | 2 +- drivers/staging/iio/adc/ad7476_ring.c | 2 +- drivers/staging/iio/adc/ad7606_ring.c | 2 +- drivers/staging/iio/adc/ad7887_ring.c | 2 +- drivers/staging/iio/adc/ad799x_ring.c | 2 +- drivers/staging/iio/adc/max1363_ring.c | 2 +- drivers/staging/iio/adc/mxs-lradc.c | 2 +- drivers/staging/iio/gyro/adis16260_ring.c | 2 +- drivers/staging/iio/iio_simple_dummy_buffer.c | 2 +- drivers/staging/iio/impedance-analyzer/ad5933.c | 2 +- drivers/staging/iio/imu/adis16400_ring.c | 2 +- drivers/staging/iio/meter/ade7758_ring.c | 2 +- drivers/staging/iio/ring_sw.c | 7 +++---- include/linux/iio/buffer.h | 6 ++---- 30 files changed, 34 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index c593eb239270..314a4057879e 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -198,7 +198,6 @@ static const struct iio_info accel_3d_info = { static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) { struct iio_buffer *buffer = indio_dev->buffer; - s64 timestamp = iio_get_time_ns(); int datum_sz; dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); @@ -212,7 +211,7 @@ static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) datum_sz); return; } - iio_push_to_buffer(buffer, (u8 *)data, timestamp); + iio_push_to_buffer(buffer, (u8 *)data); } /* Callback handler to send event after all samples are received and captured */ diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c index 5c3f1ba5a06d..b11f214779a2 100644 --- a/drivers/iio/adc/ad7266.c +++ b/drivers/iio/adc/ad7266.c @@ -99,7 +99,7 @@ static irqreturn_t ad7266_trigger_handler(int irq, void *p) if (ret == 0) { if (indio_dev->scan_timestamp) ((s64 *)st->data)[1] = pf->timestamp; - iio_push_to_buffer(buffer, (u8 *)st->data, pf->timestamp); + iio_push_to_buffer(buffer, (u8 *)st->data); } iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index ae847c5a1361..67baa1363d7a 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -391,7 +391,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p) break; } - iio_push_to_buffer(indio_dev->buffer, (uint8_t *)data, pf->timestamp); + iio_push_to_buffer(indio_dev->buffer, (uint8_t *)data); iio_trigger_notify_done(indio_dev->trig); sigma_delta->irq_dis = false; diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index c1e4690f188d..bc10091fe76c 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -82,7 +82,7 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p) *timestamp = pf->timestamp; } - buffer->access->store_to(buffer, (u8 *)st->buffer, pf->timestamp); + buffer->access->store_to(buffer, (u8 *)st->buffer); iio_trigger_notify_done(idev->trig); st->irq_enabled = true; diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c index 94a4740135e7..4c56ada51c39 100644 --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -198,7 +198,6 @@ static const struct iio_info gyro_3d_info = { static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) { struct iio_buffer *buffer = indio_dev->buffer; - s64 timestamp = iio_get_time_ns(); int datum_sz; dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); @@ -212,7 +211,7 @@ static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) datum_sz); return; } - iio_push_to_buffer(buffer, (u8 *)data, timestamp); + iio_push_to_buffer(buffer, (u8 *)data); } /* Callback handler to send event after all samples are received and captured */ diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 774891cf8139..d4ad37455a67 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -682,12 +682,11 @@ static unsigned char *iio_demux(struct iio_buffer *buffer, return buffer->demux_bounce; } -int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data, - s64 timestamp) +int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data) { unsigned char *dataout = iio_demux(buffer, data); - return buffer->access->store_to(buffer, dataout, timestamp); + return buffer->access->store_to(buffer, dataout); } EXPORT_SYMBOL_GPL(iio_push_to_buffer); diff --git a/drivers/iio/kfifo_buf.c b/drivers/iio/kfifo_buf.c index 63da42498a9a..5bc5c860e9ca 100644 --- a/drivers/iio/kfifo_buf.c +++ b/drivers/iio/kfifo_buf.c @@ -95,8 +95,7 @@ static int iio_set_length_kfifo(struct iio_buffer *r, int length) } static int iio_store_to_kfifo(struct iio_buffer *r, - u8 *data, - s64 timestamp) + u8 *data) { int ret; struct iio_kfifo *kf = iio_to_kfifo(r); diff --git a/drivers/iio/light/adjd_s311.c b/drivers/iio/light/adjd_s311.c index 9a99f43094f0..164b62b91a4b 100644 --- a/drivers/iio/light/adjd_s311.c +++ b/drivers/iio/light/adjd_s311.c @@ -187,7 +187,7 @@ static irqreturn_t adjd_s311_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *(s64 *)((u8 *)data->buffer + ALIGN(len, sizeof(s64))) = time_ns; - iio_push_to_buffer(buffer, (u8 *)data->buffer, time_ns); + iio_push_to_buffer(buffer, (u8 *)data->buffer); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index b3c8e91b2c8d..96e3691e42c4 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -177,7 +177,6 @@ static const struct iio_info als_info = { static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) { struct iio_buffer *buffer = indio_dev->buffer; - s64 timestamp = iio_get_time_ns(); int datum_sz; dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); @@ -191,7 +190,7 @@ static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) datum_sz); return; } - iio_push_to_buffer(buffer, (u8 *)data, timestamp); + iio_push_to_buffer(buffer, (u8 *)data); } /* Callback handler to send event after all samples are received and captured */ diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c index 397704eded97..c4f0d274f577 100644 --- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c +++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c @@ -199,7 +199,6 @@ static const struct iio_info magn_3d_info = { static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) { struct iio_buffer *buffer = indio_dev->buffer; - s64 timestamp = iio_get_time_ns(); int datum_sz; dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); @@ -213,7 +212,7 @@ static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) datum_sz); return; } - iio_push_to_buffer(buffer, (u8 *)data, timestamp); + iio_push_to_buffer(buffer, (u8 *)data); } /* Callback handler to send event after all samples are received and captured */ diff --git a/drivers/staging/iio/accel/adis16201_ring.c b/drivers/staging/iio/accel/adis16201_ring.c index 884dcf89bce3..97c09f0c26ae 100644 --- a/drivers/staging/iio/accel/adis16201_ring.c +++ b/drivers/staging/iio/accel/adis16201_ring.c @@ -82,7 +82,7 @@ static irqreturn_t adis16201_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp; - iio_push_to_buffer(indio_dev->buffer, (u8 *)data, pf->timestamp); + iio_push_to_buffer(indio_dev->buffer, (u8 *)data); kfree(data); done: diff --git a/drivers/staging/iio/accel/adis16203_ring.c b/drivers/staging/iio/accel/adis16203_ring.c index a7ff80490c47..7507e1a04591 100644 --- a/drivers/staging/iio/accel/adis16203_ring.c +++ b/drivers/staging/iio/accel/adis16203_ring.c @@ -81,7 +81,7 @@ static irqreturn_t adis16203_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp; - iio_push_to_buffer(indio_dev->buffer, (u8 *)data, pf->timestamp); + iio_push_to_buffer(indio_dev->buffer, (u8 *)data); kfree(data); done: diff --git a/drivers/staging/iio/accel/adis16204_ring.c b/drivers/staging/iio/accel/adis16204_ring.c index 16e36e3e9d33..4c976bec986b 100644 --- a/drivers/staging/iio/accel/adis16204_ring.c +++ b/drivers/staging/iio/accel/adis16204_ring.c @@ -78,7 +78,7 @@ static irqreturn_t adis16204_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp; - iio_push_to_buffer(indio_dev->buffer, (u8 *)data, pf->timestamp); + iio_push_to_buffer(indio_dev->buffer, (u8 *)data); kfree(data); done: diff --git a/drivers/staging/iio/accel/adis16209_ring.c b/drivers/staging/iio/accel/adis16209_ring.c index 5cfeff40fcf6..f939e29d6c82 100644 --- a/drivers/staging/iio/accel/adis16209_ring.c +++ b/drivers/staging/iio/accel/adis16209_ring.c @@ -78,7 +78,7 @@ static irqreturn_t adis16209_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp; - iio_push_to_buffer(indio_dev->buffer, (u8 *)data, pf->timestamp); + iio_push_to_buffer(indio_dev->buffer, (u8 *)data); kfree(data); done: diff --git a/drivers/staging/iio/accel/adis16240_ring.c b/drivers/staging/iio/accel/adis16240_ring.c index d5ec43e877b5..caff8e25e0a2 100644 --- a/drivers/staging/iio/accel/adis16240_ring.c +++ b/drivers/staging/iio/accel/adis16240_ring.c @@ -76,7 +76,7 @@ static irqreturn_t adis16240_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp; - iio_push_to_buffer(indio_dev->buffer, (u8 *)data, pf->timestamp); + iio_push_to_buffer(indio_dev->buffer, (u8 *)data); kfree(data); done: diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c index 9332762da718..247605ce5051 100644 --- a/drivers/staging/iio/accel/lis3l02dq_ring.c +++ b/drivers/staging/iio/accel/lis3l02dq_ring.c @@ -152,7 +152,7 @@ static irqreturn_t lis3l02dq_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *(s64 *)((u8 *)data + ALIGN(len, sizeof(s64))) = pf->timestamp; - iio_push_to_buffer(indio_dev->buffer, (u8 *)data, pf->timestamp); + iio_push_to_buffer(indio_dev->buffer, (u8 *)data); kfree(data); done: diff --git a/drivers/staging/iio/adc/ad7298_ring.c b/drivers/staging/iio/adc/ad7298_ring.c index 9fce4045cbb9..c2906a85fedb 100644 --- a/drivers/staging/iio/adc/ad7298_ring.c +++ b/drivers/staging/iio/adc/ad7298_ring.c @@ -93,7 +93,7 @@ static irqreturn_t ad7298_trigger_handler(int irq, void *p) indio_dev->masklength); i++) buf[i] = be16_to_cpu(st->rx_buf[i]); - iio_push_to_buffer(indio_dev->buffer, (u8 *)buf, time_ns); + iio_push_to_buffer(indio_dev->buffer, (u8 *)buf); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/staging/iio/adc/ad7476_ring.c b/drivers/staging/iio/adc/ad7476_ring.c index 3741ac6131db..940681f29b87 100644 --- a/drivers/staging/iio/adc/ad7476_ring.c +++ b/drivers/staging/iio/adc/ad7476_ring.c @@ -44,7 +44,7 @@ static irqreturn_t ad7476_trigger_handler(int irq, void *p) memcpy(rxbuf + indio_dev->scan_bytes - sizeof(s64), &time_ns, sizeof(time_ns)); - iio_push_to_buffer(indio_dev->buffer, rxbuf, time_ns); + iio_push_to_buffer(indio_dev->buffer, rxbuf); done: iio_trigger_notify_done(indio_dev->trig); kfree(rxbuf); diff --git a/drivers/staging/iio/adc/ad7606_ring.c b/drivers/staging/iio/adc/ad7606_ring.c index f4b009ee6635..ba04d0ffd4f4 100644 --- a/drivers/staging/iio/adc/ad7606_ring.c +++ b/drivers/staging/iio/adc/ad7606_ring.c @@ -83,7 +83,7 @@ static void ad7606_poll_bh_to_ring(struct work_struct *work_s) if (indio_dev->scan_timestamp) *((s64 *)(buf + indio_dev->scan_bytes - sizeof(s64))) = time_ns; - iio_push_to_buffer(indio_dev->buffer, buf, time_ns); + iio_push_to_buffer(indio_dev->buffer, buf); done: gpio_set_value(st->pdata->gpio_convst, 0); iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/staging/iio/adc/ad7887_ring.c b/drivers/staging/iio/adc/ad7887_ring.c index a4ff4935982d..b39923bbeedc 100644 --- a/drivers/staging/iio/adc/ad7887_ring.c +++ b/drivers/staging/iio/adc/ad7887_ring.c @@ -95,7 +95,7 @@ static irqreturn_t ad7887_trigger_handler(int irq, void *p) memcpy(buf + indio_dev->scan_bytes - sizeof(s64), &time_ns, sizeof(time_ns)); - iio_push_to_buffer(indio_dev->buffer, buf, time_ns); + iio_push_to_buffer(indio_dev->buffer, buf); done: kfree(buf); iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/staging/iio/adc/ad799x_ring.c b/drivers/staging/iio/adc/ad799x_ring.c index 84275c3c6b26..86026d9b20bc 100644 --- a/drivers/staging/iio/adc/ad799x_ring.c +++ b/drivers/staging/iio/adc/ad799x_ring.c @@ -77,7 +77,7 @@ static irqreturn_t ad799x_trigger_handler(int irq, void *p) memcpy(rxbuf + indio_dev->scan_bytes - sizeof(s64), &time_ns, sizeof(time_ns)); - iio_push_to_buffer(indio_dev->buffer, rxbuf, time_ns); + iio_push_to_buffer(indio_dev->buffer, rxbuf); done: kfree(rxbuf); out: diff --git a/drivers/staging/iio/adc/max1363_ring.c b/drivers/staging/iio/adc/max1363_ring.c index 774ae1b63550..5f74f3b7671a 100644 --- a/drivers/staging/iio/adc/max1363_ring.c +++ b/drivers/staging/iio/adc/max1363_ring.c @@ -80,7 +80,7 @@ static irqreturn_t max1363_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) memcpy(rxbuf + d_size - sizeof(s64), &time_ns, sizeof(time_ns)); - iio_push_to_buffer(indio_dev->buffer, rxbuf, time_ns); + iio_push_to_buffer(indio_dev->buffer, rxbuf); done_free: kfree(rxbuf); diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c index ae549e572e5d..ca7c1fa88e71 100644 --- a/drivers/staging/iio/adc/mxs-lradc.c +++ b/drivers/staging/iio/adc/mxs-lradc.c @@ -256,7 +256,7 @@ static irqreturn_t mxs_lradc_trigger_handler(int irq, void *p) *timestamp = pf->timestamp; } - iio_push_to_buffer(buffer, (u8 *)lradc->buffer, pf->timestamp); + iio_push_to_buffer(buffer, (u8 *)lradc->buffer); iio_trigger_notify_done(iio->trig); diff --git a/drivers/staging/iio/gyro/adis16260_ring.c b/drivers/staging/iio/gyro/adis16260_ring.c index e6e234503824..e294cb49736d 100644 --- a/drivers/staging/iio/gyro/adis16260_ring.c +++ b/drivers/staging/iio/gyro/adis16260_ring.c @@ -81,7 +81,7 @@ static irqreturn_t adis16260_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp; - iio_push_to_buffer(indio_dev->buffer, (u8 *)data, pf->timestamp); + iio_push_to_buffer(indio_dev->buffer, (u8 *)data); kfree(data); done: diff --git a/drivers/staging/iio/iio_simple_dummy_buffer.c b/drivers/staging/iio/iio_simple_dummy_buffer.c index e08021362ecf..1fd38095b2ca 100644 --- a/drivers/staging/iio/iio_simple_dummy_buffer.c +++ b/drivers/staging/iio/iio_simple_dummy_buffer.c @@ -87,7 +87,7 @@ static irqreturn_t iio_simple_dummy_trigger_h(int irq, void *p) if (indio_dev->scan_timestamp) *(s64 *)((u8 *)data + ALIGN(len, sizeof(s64))) = iio_get_time_ns(); - iio_push_to_buffer(buffer, (u8 *)data, pf->timestamp); + iio_push_to_buffer(buffer, (u8 *)data); kfree(data); diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c index 39a9be8b7d8e..de21d47f33e9 100644 --- a/drivers/staging/iio/impedance-analyzer/ad5933.c +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c @@ -678,7 +678,7 @@ static void ad5933_work(struct work_struct *work) buf[0] = be16_to_cpu(buf[0]); } /* save datum to the ring */ - iio_push_to_buffer(ring, (u8 *)buf, iio_get_time_ns()); + iio_push_to_buffer(ring, (u8 *)buf); } else { /* no data available - try again later */ schedule_delayed_work(&st->work, st->poll_time_jiffies); diff --git a/drivers/staging/iio/imu/adis16400_ring.c b/drivers/staging/iio/imu/adis16400_ring.c index 6b717886b5b4..260bdd1a4681 100644 --- a/drivers/staging/iio/imu/adis16400_ring.c +++ b/drivers/staging/iio/imu/adis16400_ring.c @@ -150,7 +150,7 @@ static irqreturn_t adis16400_trigger_handler(int irq, void *p) /* Guaranteed to be aligned with 8 byte boundary */ if (ring->scan_timestamp) *((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp; - iio_push_to_buffer(ring, (u8 *) data, pf->timestamp); + iio_push_to_buffer(ring, (u8 *) data); done: kfree(data); diff --git a/drivers/staging/iio/meter/ade7758_ring.c b/drivers/staging/iio/meter/ade7758_ring.c index 23c107a09ceb..9e49baccf660 100644 --- a/drivers/staging/iio/meter/ade7758_ring.c +++ b/drivers/staging/iio/meter/ade7758_ring.c @@ -73,7 +73,7 @@ static irqreturn_t ade7758_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) dat64[1] = pf->timestamp; - iio_push_to_buffer(indio_dev->buffer, (u8 *)dat64, pf->timestamp); + iio_push_to_buffer(indio_dev->buffer, (u8 *)dat64); iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/staging/iio/ring_sw.c b/drivers/staging/iio/ring_sw.c index f61c8fdaab06..3a45f9a52de8 100644 --- a/drivers/staging/iio/ring_sw.c +++ b/drivers/staging/iio/ring_sw.c @@ -65,7 +65,7 @@ static inline void __iio_free_sw_ring_buffer(struct iio_sw_ring_buffer *ring) /* Lock always held if their is a chance this may be called */ /* Only one of these per ring may run concurrently - enforced by drivers */ static int iio_store_to_sw_ring(struct iio_sw_ring_buffer *ring, - unsigned char *data, s64 timestamp) + unsigned char *data) { int ret = 0; unsigned char *temp_ptr, *change_test_ptr; @@ -256,11 +256,10 @@ error_ret: } static int iio_store_to_sw_rb(struct iio_buffer *r, - u8 *data, - s64 timestamp) + u8 *data) { struct iio_sw_ring_buffer *ring = iio_to_sw_ring(r); - return iio_store_to_sw_ring(ring, data, timestamp); + return iio_store_to_sw_ring(ring, data); } static int iio_request_update_sw_rb(struct iio_buffer *r) diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h index 8ba516fc2ec6..c629b3a1d9a9 100644 --- a/include/linux/iio/buffer.h +++ b/include/linux/iio/buffer.h @@ -36,7 +36,7 @@ struct iio_buffer; * any of them not existing. **/ struct iio_buffer_access_funcs { - int (*store_to)(struct iio_buffer *buffer, u8 *data, s64 timestamp); + int (*store_to)(struct iio_buffer *buffer, u8 *data); int (*read_first_n)(struct iio_buffer *buffer, size_t n, char __user *buf); @@ -118,10 +118,8 @@ int iio_scan_mask_set(struct iio_dev *indio_dev, * iio_push_to_buffer() - push to a registered buffer. * @buffer: IIO buffer structure for device * @data: the data to push to the buffer - * @timestamp: timestamp to associate with the data */ -int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data, - s64 timestamp); +int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data); int iio_update_demux(struct iio_dev *indio_dev); -- cgit v1.2.3 From 7b123c85bbb3fadbd02b82d77d5aee0c399b0e06 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 4 Sep 2012 16:26:00 +0100 Subject: staging:iio:adc: Add AD7791 driver This patch adds support for the Analog Devices AD7787, AD7788, AD7789, AD7790 and AD7791 Sigma Delta Analog-to-Digital converters. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 12 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ad7791.c | 460 +++++++++++++++++++++++++++++++++++ include/linux/platform_data/ad7791.h | 17 ++ 4 files changed, 490 insertions(+) create mode 100644 drivers/iio/adc/ad7791.c create mode 100644 include/linux/platform_data/ad7791.h (limited to 'include') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index a2c50713b0b6..d0ae71ec2aa0 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -18,6 +18,18 @@ config AD7266 Say yes here to build support for Analog Devices AD7265 and AD7266 ADCs. +config AD7791 + tristate "Analog Devices AD7791 ADC driver" + depends on SPI + select AD_SIGMA_DELTA + help + Say yes here to build support for Analog Devices AD7787, AD7788, AD7789, + AD7790 and AD7791 SPI analog to digital converters (ADC). If unsure, say + N (but it is safe to say "Y"). + + To compile this driver as a module, choose M here: the module will be + called ad7791. + config AT91_ADC tristate "Atmel AT91 ADC" depends on ARCH_AT91 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 5989356c5735..f187ff6c2a16 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -4,4 +4,5 @@ obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o obj-$(CONFIG_AD7266) += ad7266.o +obj-$(CONFIG_AD7791) += ad7791.o obj-$(CONFIG_AT91_ADC) += at91_adc.o diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c new file mode 100644 index 000000000000..e93740843b2b --- /dev/null +++ b/drivers/iio/adc/ad7791.c @@ -0,0 +1,460 @@ +/* + * AD7787/AD7788/AD7789/AD7790/AD7791 SPI ADC driver + * + * Copyright 2012 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define AD7791_REG_COMM 0x0 /* For writes */ +#define AD7791_REG_STATUS 0x0 /* For reads */ +#define AD7791_REG_MODE 0x1 +#define AD7791_REG_FILTER 0x2 +#define AD7791_REG_DATA 0x3 + +#define AD7791_MODE_CONTINUOUS 0x00 +#define AD7791_MODE_SINGLE 0x02 +#define AD7791_MODE_POWERDOWN 0x03 + +#define AD7791_CH_AIN1P_AIN1N 0x00 +#define AD7791_CH_AIN2 0x01 +#define AD7791_CH_AIN1N_AIN1N 0x02 +#define AD7791_CH_AVDD_MONITOR 0x03 + +#define AD7791_FILTER_CLK_DIV_1 (0x0 << 4) +#define AD7791_FILTER_CLK_DIV_2 (0x1 << 4) +#define AD7791_FILTER_CLK_DIV_4 (0x2 << 4) +#define AD7791_FILTER_CLK_DIV_8 (0x3 << 4) +#define AD7791_FILTER_CLK_MASK (0x3 << 4) +#define AD7791_FILTER_RATE_120 0x0 +#define AD7791_FILTER_RATE_100 0x1 +#define AD7791_FILTER_RATE_33_3 0x2 +#define AD7791_FILTER_RATE_20 0x3 +#define AD7791_FILTER_RATE_16_6 0x4 +#define AD7791_FILTER_RATE_16_7 0x5 +#define AD7791_FILTER_RATE_13_3 0x6 +#define AD7791_FILTER_RATE_9_5 0x7 +#define AD7791_FILTER_RATE_MASK 0x7 + +#define AD7791_MODE_BUFFER BIT(1) +#define AD7791_MODE_UNIPOLAR BIT(2) +#define AD7791_MODE_BURNOUT BIT(3) +#define AD7791_MODE_SEL_MASK (0x3 << 6) +#define AD7791_MODE_SEL(x) ((x) << 6) + +#define DECLARE_AD7787_CHANNELS(name, bits, storagebits) \ +const struct iio_chan_spec name[] = { \ + AD_SD_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \ + (bits), (storagebits), 0), \ + AD_SD_CHANNEL(1, 1, AD7791_CH_AIN2, (bits), (storagebits), 0), \ + AD_SD_SHORTED_CHANNEL(2, 0, AD7791_CH_AIN1N_AIN1N, \ + (bits), (storagebits), 0), \ + AD_SD_SUPPLY_CHANNEL(3, 2, AD7791_CH_AVDD_MONITOR, \ + (bits), (storagebits), 0), \ + IIO_CHAN_SOFT_TIMESTAMP(4), \ +} + +#define DECLARE_AD7791_CHANNELS(name, bits, storagebits) \ +const struct iio_chan_spec name[] = { \ + AD_SD_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \ + (bits), (storagebits), 0), \ + AD_SD_SHORTED_CHANNEL(1, 0, AD7791_CH_AIN1N_AIN1N, \ + (bits), (storagebits), 0), \ + AD_SD_SUPPLY_CHANNEL(2, 1, AD7791_CH_AVDD_MONITOR, \ + (bits), (storagebits), 0), \ + IIO_CHAN_SOFT_TIMESTAMP(3), \ +} + +static DECLARE_AD7787_CHANNELS(ad7787_channels, 24, 32); +static DECLARE_AD7791_CHANNELS(ad7790_channels, 16, 16); +static DECLARE_AD7791_CHANNELS(ad7791_channels, 24, 32); + +enum { + AD7787, + AD7788, + AD7789, + AD7790, + AD7791, +}; + +enum ad7791_chip_info_flags { + AD7791_FLAG_HAS_FILTER = (1 << 0), + AD7791_FLAG_HAS_BUFFER = (1 << 1), + AD7791_FLAG_HAS_UNIPOLAR = (1 << 2), + AD7791_FLAG_HAS_BURNOUT = (1 << 3), +}; + +struct ad7791_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; + enum ad7791_chip_info_flags flags; +}; + +static const struct ad7791_chip_info ad7791_chip_infos[] = { + [AD7787] = { + .channels = ad7787_channels, + .num_channels = ARRAY_SIZE(ad7787_channels), + .flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER | + AD7791_FLAG_HAS_UNIPOLAR | AD7791_FLAG_HAS_BURNOUT, + }, + [AD7788] = { + .channels = ad7790_channels, + .num_channels = ARRAY_SIZE(ad7790_channels), + .flags = AD7791_FLAG_HAS_UNIPOLAR, + }, + [AD7789] = { + .channels = ad7791_channels, + .num_channels = ARRAY_SIZE(ad7791_channels), + .flags = AD7791_FLAG_HAS_UNIPOLAR, + }, + [AD7790] = { + .channels = ad7790_channels, + .num_channels = ARRAY_SIZE(ad7790_channels), + .flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER | + AD7791_FLAG_HAS_BURNOUT, + }, + [AD7791] = { + .channels = ad7791_channels, + .num_channels = ARRAY_SIZE(ad7791_channels), + .flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER | + AD7791_FLAG_HAS_UNIPOLAR | AD7791_FLAG_HAS_BURNOUT, + }, +}; + +struct ad7791_state { + struct ad_sigma_delta sd; + uint8_t mode; + uint8_t filter; + + struct regulator *reg; + const struct ad7791_chip_info *info; +}; + +static struct ad7791_state *ad_sigma_delta_to_ad7791(struct ad_sigma_delta *sd) +{ + return container_of(sd, struct ad7791_state, sd); +} + +static int ad7791_set_channel(struct ad_sigma_delta *sd, unsigned int channel) +{ + ad_sd_set_comm(sd, channel); + + return 0; +} + +static int ad7791_set_mode(struct ad_sigma_delta *sd, + enum ad_sigma_delta_mode mode) +{ + struct ad7791_state *st = ad_sigma_delta_to_ad7791(sd); + + switch (mode) { + case AD_SD_MODE_CONTINUOUS: + mode = AD7791_MODE_CONTINUOUS; + break; + case AD_SD_MODE_SINGLE: + mode = AD7791_MODE_SINGLE; + break; + case AD_SD_MODE_IDLE: + case AD_SD_MODE_POWERDOWN: + mode = AD7791_MODE_POWERDOWN; + break; + } + + st->mode &= ~AD7791_MODE_SEL_MASK; + st->mode |= AD7791_MODE_SEL(mode); + + return ad_sd_write_reg(sd, AD7791_REG_MODE, sizeof(st->mode), st->mode); +} + +static const struct ad_sigma_delta_info ad7791_sigma_delta_info = { + .set_channel = ad7791_set_channel, + .set_mode = ad7791_set_mode, + .has_registers = true, + .addr_shift = 4, + .read_mask = BIT(3), +}; + +static int ad7791_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val, int *val2, long info) +{ + struct ad7791_state *st = iio_priv(indio_dev); + bool unipolar = !!(st->mode & AD7791_MODE_UNIPOLAR); + unsigned long long scale_pv; + + switch (info) { + case IIO_CHAN_INFO_RAW: + return ad_sigma_delta_single_conversion(indio_dev, chan, val); + case IIO_CHAN_INFO_OFFSET: + /** + * Unipolar: 0 to VREF + * Bipolar -VREF to VREF + **/ + if (unipolar) + *val = 0; + else + *val = -(1 << (chan->scan_type.realbits - 1)); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* The monitor channel uses an internal reference. */ + if (chan->address == AD7791_CH_AVDD_MONITOR) { + scale_pv = 5850000000000ULL; + } else { + int voltage_uv; + + voltage_uv = regulator_get_voltage(st->reg); + if (voltage_uv < 0) + return voltage_uv; + scale_pv = (unsigned long long)voltage_uv * 1000000; + } + if (unipolar) + scale_pv >>= chan->scan_type.realbits; + else + scale_pv >>= chan->scan_type.realbits - 1; + *val2 = do_div(scale_pv, 1000000000); + *val = scale_pv; + + return IIO_VAL_INT_PLUS_NANO; + } + + return -EINVAL; +} + +static const char * const ad7791_sample_freq_avail[] = { + [AD7791_FILTER_RATE_120] = "120", + [AD7791_FILTER_RATE_100] = "100", + [AD7791_FILTER_RATE_33_3] = "33.3", + [AD7791_FILTER_RATE_20] = "20", + [AD7791_FILTER_RATE_16_6] = "16.6", + [AD7791_FILTER_RATE_16_7] = "16.7", + [AD7791_FILTER_RATE_13_3] = "13.3", + [AD7791_FILTER_RATE_9_5] = "9.5", +}; + +static ssize_t ad7791_read_frequency(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7791_state *st = iio_priv(indio_dev); + unsigned int rate = st->filter & AD7791_FILTER_RATE_MASK; + + return sprintf(buf, "%s\n", ad7791_sample_freq_avail[rate]); +} + +static ssize_t ad7791_write_frequency(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7791_state *st = iio_priv(indio_dev); + int i, ret; + + mutex_lock(&indio_dev->mlock); + if (iio_buffer_enabled(indio_dev)) { + mutex_unlock(&indio_dev->mlock); + return -EBUSY; + } + mutex_unlock(&indio_dev->mlock); + + ret = -EINVAL; + + for (i = 0; i < ARRAY_SIZE(ad7791_sample_freq_avail); i++) { + if (sysfs_streq(ad7791_sample_freq_avail[i], buf)) { + + mutex_lock(&indio_dev->mlock); + st->filter &= ~AD7791_FILTER_RATE_MASK; + st->filter |= i; + ad_sd_write_reg(&st->sd, AD7791_REG_FILTER, + sizeof(st->filter), st->filter); + mutex_unlock(&indio_dev->mlock); + ret = 0; + break; + } + } + + return ret ? ret : len; +} + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, + ad7791_read_frequency, + ad7791_write_frequency); + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("120 100 33.3 20 16.7 16.6 13.3 9.5"); + +static struct attribute *ad7791_attributes[] = { + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group ad7791_attribute_group = { + .attrs = ad7791_attributes, +}; + +static const struct iio_info ad7791_info = { + .read_raw = &ad7791_read_raw, + .attrs = &ad7791_attribute_group, + .validate_trigger = ad_sd_validate_trigger, + .driver_module = THIS_MODULE, +}; + +static const struct iio_info ad7791_no_filter_info = { + .read_raw = &ad7791_read_raw, + .validate_trigger = ad_sd_validate_trigger, + .driver_module = THIS_MODULE, +}; + +static int __devinit ad7791_setup(struct ad7791_state *st, + struct ad7791_platform_data *pdata) +{ + /* Set to poweron-reset default values */ + st->mode = AD7791_MODE_BUFFER; + st->filter = AD7791_FILTER_RATE_16_6; + + if (!pdata) + return 0; + + if ((st->info->flags & AD7791_FLAG_HAS_BUFFER) && !pdata->buffered) + st->mode &= ~AD7791_MODE_BUFFER; + + if ((st->info->flags & AD7791_FLAG_HAS_BURNOUT) && + pdata->burnout_current) + st->mode |= AD7791_MODE_BURNOUT; + + if ((st->info->flags & AD7791_FLAG_HAS_UNIPOLAR) && pdata->unipolar) + st->mode |= AD7791_MODE_UNIPOLAR; + + return ad_sd_write_reg(&st->sd, AD7791_REG_MODE, sizeof(st->mode), + st->mode); +} + +static int __devinit ad7791_probe(struct spi_device *spi) +{ + struct ad7791_platform_data *pdata = spi->dev.platform_data; + struct iio_dev *indio_dev; + struct ad7791_state *st; + int ret; + + if (!spi->irq) { + dev_err(&spi->dev, "Missing IRQ.\n"); + return -ENXIO; + } + + indio_dev = iio_device_alloc(sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->reg = regulator_get(&spi->dev, "refin"); + if (IS_ERR(st->reg)) { + ret = PTR_ERR(st->reg); + goto err_iio_free; + } + + ret = regulator_enable(st->reg); + if (ret) + goto error_put_reg; + + st->info = &ad7791_chip_infos[spi_get_device_id(spi)->driver_data]; + ad_sd_init(&st->sd, indio_dev, spi, &ad7791_sigma_delta_info); + + spi_set_drvdata(spi, indio_dev); + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->info->channels; + indio_dev->num_channels = st->info->num_channels; + if (st->info->flags & AD7791_FLAG_HAS_FILTER) + indio_dev->info = &ad7791_info; + else + indio_dev->info = &ad7791_no_filter_info; + + ret = ad_sd_setup_buffer_and_trigger(indio_dev); + if (ret) + goto error_disable_reg; + + ret = ad7791_setup(st, pdata); + if (ret) + goto error_remove_trigger; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_remove_trigger; + + return 0; + +error_remove_trigger: + ad_sd_cleanup_buffer_and_trigger(indio_dev); +error_disable_reg: + regulator_disable(st->reg); +error_put_reg: + regulator_put(st->reg); +err_iio_free: + iio_device_free(indio_dev); + + return ret; +} + +static int __devexit ad7791_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad7791_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + ad_sd_cleanup_buffer_and_trigger(indio_dev); + + regulator_disable(st->reg); + regulator_put(st->reg); + + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad7791_spi_ids[] = { + { "ad7787", AD7787 }, + { "ad7788", AD7788 }, + { "ad7789", AD7789 }, + { "ad7790", AD7790 }, + { "ad7791", AD7791 }, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7791_spi_ids); + +static struct spi_driver ad7791_driver = { + .driver = { + .name = "ad7791", + .owner = THIS_MODULE, + }, + .probe = ad7791_probe, + .remove = __devexit_p(ad7791_remove), + .id_table = ad7791_spi_ids, +}; +module_spi_driver(ad7791_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Analog Device AD7787/AD7788/AD7789/AD7790/AD7791 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/ad7791.h b/include/linux/platform_data/ad7791.h new file mode 100644 index 000000000000..f9e4db1b82ae --- /dev/null +++ b/include/linux/platform_data/ad7791.h @@ -0,0 +1,17 @@ +#ifndef __LINUX_PLATFORM_DATA_AD7791__ +#define __LINUX_PLATFORM_DATA_AD7791__ + +/** + * struct ad7791_platform_data - AD7791 device platform data + * @buffered: If set to true configure the device for buffered input mode. + * @burnout_current: If set to true the 100mA burnout current is enabled. + * @unipolar: If set to true sample in unipolar mode, if set to false sample in + * bipolar mode. + */ +struct ad7791_platform_data { + bool buffered; + bool burnout_current; + bool unipolar; +}; + +#endif -- cgit v1.2.3 From 6b536b5e5e1da32f3ba1e3f42c7bf2f80d37dc6b Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 31 Aug 2012 16:39:28 +0300 Subject: Bluetooth: Remove unneeded zero init hdev is allocated with kzalloc so zero initialization is not needed. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 9 --------- net/bluetooth/hci_core.c | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 1bbc1091748c..fa807a3fd23b 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -428,15 +428,6 @@ static inline bool hci_conn_ssp_enabled(struct hci_conn *conn) test_bit(HCI_CONN_SSP_ENABLED, &conn->flags); } -static inline void hci_conn_hash_init(struct hci_dev *hdev) -{ - struct hci_conn_hash *h = &hdev->conn_hash; - INIT_LIST_HEAD(&h->list); - h->acl_num = 0; - h->sco_num = 0; - h->le_num = 0; -} - static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c) { struct hci_conn_hash *h = &hdev->conn_hash; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index fa974a19d365..86abe721f484 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1652,6 +1652,7 @@ struct hci_dev *hci_alloc_dev(void) INIT_LIST_HEAD(&hdev->link_keys); INIT_LIST_HEAD(&hdev->long_term_keys); INIT_LIST_HEAD(&hdev->remote_oob_data); + INIT_LIST_HEAD(&hdev->conn_hash.list); INIT_WORK(&hdev->rx_work, hci_rx_work); INIT_WORK(&hdev->cmd_work, hci_cmd_work); @@ -1674,7 +1675,6 @@ struct hci_dev *hci_alloc_dev(void) hci_init_sysfs(hdev); discovery_init(hdev); - hci_conn_hash_init(hdev); return hdev; } -- cgit v1.2.3 From 9472007c62ecc8f21daa2e1e252bf73b67e535fc Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 6 Sep 2012 15:05:43 +0300 Subject: Bluetooth: trivial: Make hci_chan_del return void Return code is not needed in hci_chan_del Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_conn.c | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index fa807a3fd23b..4704ca4b8767 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -552,7 +552,7 @@ void hci_conn_hash_flush(struct hci_dev *hdev); void hci_conn_check_pending(struct hci_dev *hdev); struct hci_chan *hci_chan_create(struct hci_conn *conn); -int hci_chan_del(struct hci_chan *chan); +void hci_chan_del(struct hci_chan *chan); void hci_chan_list_flush(struct hci_conn *conn); struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 3e65c021df50..59f0344406c8 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -935,7 +935,7 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn) return chan; } -int hci_chan_del(struct hci_chan *chan) +void hci_chan_del(struct hci_chan *chan) { struct hci_conn *conn = chan->conn; struct hci_dev *hdev = conn->hdev; @@ -948,8 +948,6 @@ int hci_chan_del(struct hci_chan *chan) skb_queue_purge(&chan->data_q); kfree(chan); - - return 0; } void hci_chan_list_flush(struct hci_conn *conn) -- cgit v1.2.3 From 376261ae3627b03574994496f70f95d5538795d5 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 6 Sep 2012 15:05:45 +0300 Subject: Bluetooth: debug: Print refcnt for hci_dev Add debug output for HCI kref. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4704ca4b8767..6a3337e9c42c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -603,11 +603,17 @@ static inline void hci_conn_put(struct hci_conn *conn) /* ----- HCI Devices ----- */ static inline void hci_dev_put(struct hci_dev *d) { + BT_DBG("%s orig refcnt %d", d->name, + atomic_read(&d->dev.kobj.kref.refcount)); + put_device(&d->dev); } static inline struct hci_dev *hci_dev_hold(struct hci_dev *d) { + BT_DBG("%s orig refcnt %d", d->name, + atomic_read(&d->dev.kobj.kref.refcount)); + get_device(&d->dev); return d; } -- cgit v1.2.3 From 9785e10aedfa0fad5c1aac709dce5ada1b123783 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 8 Sep 2012 02:53:53 +0000 Subject: netlink: kill netlink_set_nonroot Replace netlink_set_nonroot by one new field `flags' in struct netlink_kernel_cfg that is passed to netlink_kernel_create. This patch also renames NL_NONROOT_* to NL_CFG_F_NONROOT_* since now the flags field in nl_table is generic (so we can add more flags if needed in the future). Also adjust all callers in the net-next tree to use these flags instead of netlink_set_nonroot. Signed-off-by: Pablo Neira Ayuso Signed-off-by: David S. Miller --- include/linux/netlink.h | 9 ++++----- lib/kobject_uevent.c | 2 +- net/core/rtnetlink.c | 2 +- net/netlink/af_netlink.c | 28 +++++++++++++--------------- net/netlink/genetlink.c | 3 +-- security/selinux/netlink.c | 2 +- 6 files changed, 21 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/linux/netlink.h b/include/linux/netlink.h index df73cf4b0290..8719a4e235a5 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -176,12 +176,16 @@ struct netlink_skb_parms { extern void netlink_table_grab(void); extern void netlink_table_ungrab(void); +#define NL_CFG_F_NONROOT_RECV (1 << 0) +#define NL_CFG_F_NONROOT_SEND (1 << 1) + /* optional Netlink kernel configuration parameters */ struct netlink_kernel_cfg { unsigned int groups; void (*input)(struct sk_buff *skb); struct mutex *cb_mutex; void (*bind)(int group); + unsigned int flags; }; extern struct sock *netlink_kernel_create(struct net *net, int unit, @@ -260,11 +264,6 @@ extern int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, const struct nlmsghdr *nlh, struct netlink_dump_control *control); - -#define NL_NONROOT_RECV 0x1 -#define NL_NONROOT_SEND 0x2 -extern void netlink_set_nonroot(int protocol, unsigned flag); - #endif /* __KERNEL__ */ #endif /* __LINUX_NETLINK_H */ diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index 0401d2916d9f..c2e97787d01e 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c @@ -375,6 +375,7 @@ static int uevent_net_init(struct net *net) struct uevent_sock *ue_sk; struct netlink_kernel_cfg cfg = { .groups = 1, + .flags = NL_CFG_F_NONROOT_RECV, }; ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL); @@ -422,7 +423,6 @@ static struct pernet_operations uevent_net_ops = { static int __init kobject_uevent_init(void) { - netlink_set_nonroot(NETLINK_KOBJECT_UEVENT, NL_NONROOT_RECV); return register_pernet_subsys(&uevent_net_ops); } diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index c64efcff8078..a71806eb9cc6 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2381,6 +2381,7 @@ static int __net_init rtnetlink_net_init(struct net *net) .groups = RTNLGRP_MAX, .input = rtnetlink_rcv, .cb_mutex = &rtnl_mutex, + .flags = NL_CFG_F_NONROOT_RECV, }; sk = netlink_kernel_create(net, NETLINK_ROUTE, THIS_MODULE, &cfg); @@ -2416,7 +2417,6 @@ void __init rtnetlink_init(void) if (register_pernet_subsys(&rtnetlink_net_ops)) panic("rtnetlink_init: cannot initialize rtnetlink\n"); - netlink_set_nonroot(NETLINK_ROUTE, NL_NONROOT_RECV); register_netdevice_notifier(&rtnetlink_dev_notifier); rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink, diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index f530b1ca1773..b74540ce3c14 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -121,7 +121,7 @@ struct netlink_table { struct nl_pid_hash hash; struct hlist_head mc_list; struct listeners __rcu *listeners; - unsigned int nl_nonroot; + unsigned int flags; unsigned int groups; struct mutex *cb_mutex; struct module *module; @@ -536,6 +536,8 @@ static int netlink_release(struct socket *sock) if (--nl_table[sk->sk_protocol].registered == 0) { kfree(nl_table[sk->sk_protocol].listeners); nl_table[sk->sk_protocol].module = NULL; + nl_table[sk->sk_protocol].bind = NULL; + nl_table[sk->sk_protocol].flags = 0; nl_table[sk->sk_protocol].registered = 0; } } else if (nlk->subscriptions) { @@ -596,7 +598,7 @@ retry: static inline int netlink_capable(const struct socket *sock, unsigned int flag) { - return (nl_table[sock->sk->sk_protocol].nl_nonroot & flag) || + return (nl_table[sock->sk->sk_protocol].flags & flag) || capable(CAP_NET_ADMIN); } @@ -659,7 +661,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, /* Only superuser is allowed to listen multicasts */ if (nladdr->nl_groups) { - if (!netlink_capable(sock, NL_NONROOT_RECV)) + if (!netlink_capable(sock, NL_CFG_F_NONROOT_RECV)) return -EPERM; err = netlink_realloc_groups(sk); if (err) @@ -721,7 +723,7 @@ static int netlink_connect(struct socket *sock, struct sockaddr *addr, return -EINVAL; /* Only superuser is allowed to send multicasts */ - if (nladdr->nl_groups && !netlink_capable(sock, NL_NONROOT_SEND)) + if (nladdr->nl_groups && !netlink_capable(sock, NL_CFG_F_NONROOT_SEND)) return -EPERM; if (!nlk->pid) @@ -1244,7 +1246,7 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname, break; case NETLINK_ADD_MEMBERSHIP: case NETLINK_DROP_MEMBERSHIP: { - if (!netlink_capable(sock, NL_NONROOT_RECV)) + if (!netlink_capable(sock, NL_CFG_F_NONROOT_RECV)) return -EPERM; err = netlink_realloc_groups(sk); if (err) @@ -1376,7 +1378,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, dst_group = ffs(addr->nl_groups); err = -EPERM; if ((dst_group || dst_pid) && - !netlink_capable(sock, NL_NONROOT_SEND)) + !netlink_capable(sock, NL_CFG_F_NONROOT_SEND)) goto out; } else { dst_pid = nlk->dst_pid; @@ -1580,7 +1582,10 @@ netlink_kernel_create(struct net *net, int unit, rcu_assign_pointer(nl_table[unit].listeners, listeners); nl_table[unit].cb_mutex = cb_mutex; nl_table[unit].module = module; - nl_table[unit].bind = cfg ? cfg->bind : NULL; + if (cfg) { + nl_table[unit].bind = cfg->bind; + nl_table[unit].flags = cfg->flags; + } nl_table[unit].registered = 1; } else { kfree(listeners); @@ -1679,13 +1684,6 @@ void netlink_clear_multicast_users(struct sock *ksk, unsigned int group) netlink_table_ungrab(); } -void netlink_set_nonroot(int protocol, unsigned int flags) -{ - if ((unsigned int)protocol < MAX_LINKS) - nl_table[protocol].nl_nonroot = flags; -} -EXPORT_SYMBOL(netlink_set_nonroot); - struct nlmsghdr * __nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len, int flags) { @@ -2150,7 +2148,7 @@ static void __init netlink_add_usersock_entry(void) rcu_assign_pointer(nl_table[NETLINK_USERSOCK].listeners, listeners); nl_table[NETLINK_USERSOCK].module = THIS_MODULE; nl_table[NETLINK_USERSOCK].registered = 1; - nl_table[NETLINK_USERSOCK].nl_nonroot = NL_NONROOT_SEND; + nl_table[NETLINK_USERSOCK].flags = NL_CFG_F_NONROOT_SEND; netlink_table_ungrab(); } diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index fda497412fc3..c1b71aef9f71 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -918,6 +918,7 @@ static int __net_init genl_pernet_init(struct net *net) struct netlink_kernel_cfg cfg = { .input = genl_rcv, .cb_mutex = &genl_mutex, + .flags = NL_CFG_F_NONROOT_RECV, }; /* we'll bump the group number right afterwards */ @@ -955,8 +956,6 @@ static int __init genl_init(void) if (err < 0) goto problem; - netlink_set_nonroot(NETLINK_GENERIC, NL_NONROOT_RECV); - err = register_pernet_subsys(&genl_pernet_ops); if (err) goto problem; diff --git a/security/selinux/netlink.c b/security/selinux/netlink.c index 8a77725423e0..0d2cd11f3c22 100644 --- a/security/selinux/netlink.c +++ b/security/selinux/netlink.c @@ -113,13 +113,13 @@ static int __init selnl_init(void) { struct netlink_kernel_cfg cfg = { .groups = SELNLGRP_MAX, + .flags = NL_CFG_F_NONROOT_RECV, }; selnl = netlink_kernel_create(&init_net, NETLINK_SELINUX, THIS_MODULE, &cfg); if (selnl == NULL) panic("SELinux: Cannot create netlink socket."); - netlink_set_nonroot(NETLINK_SELINUX, NL_NONROOT_RECV); return 0; } -- cgit v1.2.3 From 9f00d9776bc5beb92e8bfc884a7e96ddc5589e2e Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 8 Sep 2012 02:53:54 +0000 Subject: netlink: hide struct module parameter in netlink_kernel_create This patch defines netlink_kernel_create as a wrapper function of __netlink_kernel_create to hide the struct module *me parameter (which seems to be THIS_MODULE in all existing netlink subsystems). Suggested by David S. Miller. Signed-off-by: Pablo Neira Ayuso Signed-off-by: David S. Miller --- crypto/crypto_user.c | 3 +-- drivers/connector/connector.c | 3 +-- drivers/infiniband/core/netlink.c | 2 +- drivers/scsi/scsi_netlink.c | 2 +- drivers/scsi/scsi_transport_iscsi.c | 3 +-- drivers/staging/gdm72xx/netlink_k.c | 2 +- include/linux/netlink.h | 13 ++++++++++--- kernel/audit.c | 3 +-- lib/kobject_uevent.c | 3 +-- net/bridge/netfilter/ebt_ulog.c | 3 +-- net/core/rtnetlink.c | 2 +- net/core/sock_diag.c | 3 +-- net/decnet/netfilter/dn_rtmsg.c | 3 +-- net/ipv4/fib_frontend.c | 2 +- net/ipv4/netfilter/ipt_ULOG.c | 3 +-- net/netfilter/nfnetlink.c | 2 +- net/netlink/af_netlink.c | 8 +++----- net/netlink/genetlink.c | 3 +-- net/xfrm/xfrm_user.c | 2 +- security/selinux/netlink.c | 3 +-- 20 files changed, 31 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/crypto/crypto_user.c b/crypto/crypto_user.c index ba2c611154af..165914e63ef2 100644 --- a/crypto/crypto_user.c +++ b/crypto/crypto_user.c @@ -500,8 +500,7 @@ static int __init crypto_user_init(void) .input = crypto_netlink_rcv, }; - crypto_nlsk = netlink_kernel_create(&init_net, NETLINK_CRYPTO, - THIS_MODULE, &cfg); + crypto_nlsk = netlink_kernel_create(&init_net, NETLINK_CRYPTO, &cfg); if (!crypto_nlsk) return -ENOMEM; diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c index 82fa4f0f91d6..965b7811e04f 100644 --- a/drivers/connector/connector.c +++ b/drivers/connector/connector.c @@ -264,8 +264,7 @@ static int __devinit cn_init(void) .input = dev->input, }; - dev->nls = netlink_kernel_create(&init_net, NETLINK_CONNECTOR, - THIS_MODULE, &cfg); + dev->nls = netlink_kernel_create(&init_net, NETLINK_CONNECTOR, &cfg); if (!dev->nls) return -EIO; diff --git a/drivers/infiniband/core/netlink.c b/drivers/infiniband/core/netlink.c index 3ae2bfd31015..fe10a949aef9 100644 --- a/drivers/infiniband/core/netlink.c +++ b/drivers/infiniband/core/netlink.c @@ -177,7 +177,7 @@ int __init ibnl_init(void) .input = ibnl_rcv, }; - nls = netlink_kernel_create(&init_net, NETLINK_RDMA, THIS_MODULE, &cfg); + nls = netlink_kernel_create(&init_net, NETLINK_RDMA, &cfg); if (!nls) { pr_warn("Failed to create netlink socket\n"); return -ENOMEM; diff --git a/drivers/scsi/scsi_netlink.c b/drivers/scsi/scsi_netlink.c index 8818dd681c19..3252bc9625ee 100644 --- a/drivers/scsi/scsi_netlink.c +++ b/drivers/scsi/scsi_netlink.c @@ -501,7 +501,7 @@ scsi_netlink_init(void) } scsi_nl_sock = netlink_kernel_create(&init_net, NETLINK_SCSITRANSPORT, - THIS_MODULE, &cfg); + &cfg); if (!scsi_nl_sock) { printk(KERN_ERR "%s: register of receive handler failed\n", __func__); diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index fa1dfaa83e32..519bd5303f3f 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -2969,8 +2969,7 @@ static __init int iscsi_transport_init(void) if (err) goto unregister_conn_class; - nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, - THIS_MODULE, &cfg); + nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, &cfg); if (!nls) { err = -ENOBUFS; goto unregister_session_class; diff --git a/drivers/staging/gdm72xx/netlink_k.c b/drivers/staging/gdm72xx/netlink_k.c index 3abb31df8f28..2109cab0a14c 100644 --- a/drivers/staging/gdm72xx/netlink_k.c +++ b/drivers/staging/gdm72xx/netlink_k.c @@ -95,7 +95,7 @@ struct sock *netlink_init(int unit, void (*cb)(struct net_device *dev, u16 type, init_MUTEX(&netlink_mutex); #endif - sock = netlink_kernel_create(&init_net, unit, THIS_MODULE, &cfg); + sock = netlink_kernel_create(&init_net, unit, &cfg); if (sock) rcv_cb = cb; diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 8719a4e235a5..cd17dda5a987 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -153,6 +153,7 @@ struct nlattr { #include #include +#include #include struct net; @@ -188,9 +189,15 @@ struct netlink_kernel_cfg { unsigned int flags; }; -extern struct sock *netlink_kernel_create(struct net *net, int unit, - struct module *module, - struct netlink_kernel_cfg *cfg); +extern struct sock *__netlink_kernel_create(struct net *net, int unit, + struct module *module, + struct netlink_kernel_cfg *cfg); +static inline struct sock * +netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg) +{ + return __netlink_kernel_create(net, unit, THIS_MODULE, cfg); +} + extern void netlink_kernel_release(struct sock *sk); extern int __netlink_change_ngroups(struct sock *sk, unsigned int groups); extern int netlink_change_ngroups(struct sock *sk, unsigned int groups); diff --git a/kernel/audit.c b/kernel/audit.c index ea3b7b6191c7..a24aafa850ae 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -971,8 +971,7 @@ static int __init audit_init(void) printk(KERN_INFO "audit: initializing netlink socket (%s)\n", audit_default ? "enabled" : "disabled"); - audit_sock = netlink_kernel_create(&init_net, NETLINK_AUDIT, - THIS_MODULE, &cfg); + audit_sock = netlink_kernel_create(&init_net, NETLINK_AUDIT, &cfg); if (!audit_sock) audit_panic("cannot initialize netlink socket"); else diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index c2e97787d01e..52e5abbc41db 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c @@ -382,8 +382,7 @@ static int uevent_net_init(struct net *net) if (!ue_sk) return -ENOMEM; - ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT, - THIS_MODULE, &cfg); + ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT, &cfg); if (!ue_sk->sk) { printk(KERN_ERR "kobject_uevent: unable to create netlink socket!\n"); diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c index 19063473c71f..3476ec469740 100644 --- a/net/bridge/netfilter/ebt_ulog.c +++ b/net/bridge/netfilter/ebt_ulog.c @@ -298,8 +298,7 @@ static int __init ebt_ulog_init(void) spin_lock_init(&ulog_buffers[i].lock); } - ebtulognl = netlink_kernel_create(&init_net, NETLINK_NFLOG, - THIS_MODULE, &cfg); + ebtulognl = netlink_kernel_create(&init_net, NETLINK_NFLOG, &cfg); if (!ebtulognl) ret = -ENOMEM; else if ((ret = xt_register_target(&ebt_ulog_tg_reg)) != 0) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index a71806eb9cc6..508c5df4a09c 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2384,7 +2384,7 @@ static int __net_init rtnetlink_net_init(struct net *net) .flags = NL_CFG_F_NONROOT_RECV, }; - sk = netlink_kernel_create(net, NETLINK_ROUTE, THIS_MODULE, &cfg); + sk = netlink_kernel_create(net, NETLINK_ROUTE, &cfg); if (!sk) return -ENOMEM; net->rtnl = sk; diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c index 9d8755e4a7a5..602cd637182e 100644 --- a/net/core/sock_diag.c +++ b/net/core/sock_diag.c @@ -172,8 +172,7 @@ static int __net_init diag_net_init(struct net *net) .input = sock_diag_rcv, }; - net->diag_nlsk = netlink_kernel_create(net, NETLINK_SOCK_DIAG, - THIS_MODULE, &cfg); + net->diag_nlsk = netlink_kernel_create(net, NETLINK_SOCK_DIAG, &cfg); return net->diag_nlsk == NULL ? -ENOMEM : 0; } diff --git a/net/decnet/netfilter/dn_rtmsg.c b/net/decnet/netfilter/dn_rtmsg.c index 11db0ecf342f..dfe42012a044 100644 --- a/net/decnet/netfilter/dn_rtmsg.c +++ b/net/decnet/netfilter/dn_rtmsg.c @@ -130,8 +130,7 @@ static int __init dn_rtmsg_init(void) .input = dnrmg_receive_user_skb, }; - dnrmg = netlink_kernel_create(&init_net, - NETLINK_DNRTMSG, THIS_MODULE, &cfg); + dnrmg = netlink_kernel_create(&init_net, NETLINK_DNRTMSG, &cfg); if (dnrmg == NULL) { printk(KERN_ERR "dn_rtmsg: Cannot create netlink socket"); return -ENOMEM; diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 8a7cd795a96e..dc1f10ed1872 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -986,7 +986,7 @@ static int __net_init nl_fib_lookup_init(struct net *net) .input = nl_fib_input, }; - sk = netlink_kernel_create(net, NETLINK_FIB_LOOKUP, THIS_MODULE, &cfg); + sk = netlink_kernel_create(net, NETLINK_FIB_LOOKUP, &cfg); if (sk == NULL) return -EAFNOSUPPORT; net->ipv4.fibnl = sk; diff --git a/net/ipv4/netfilter/ipt_ULOG.c b/net/ipv4/netfilter/ipt_ULOG.c index 1109f7f6c254..b5ef3cba2250 100644 --- a/net/ipv4/netfilter/ipt_ULOG.c +++ b/net/ipv4/netfilter/ipt_ULOG.c @@ -396,8 +396,7 @@ static int __init ulog_tg_init(void) for (i = 0; i < ULOG_MAXNLGROUPS; i++) setup_timer(&ulog_buffers[i].timer, ulog_timer, i); - nflognl = netlink_kernel_create(&init_net, NETLINK_NFLOG, - THIS_MODULE, &cfg); + nflognl = netlink_kernel_create(&init_net, NETLINK_NFLOG, &cfg); if (!nflognl) return -ENOMEM; diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index a26503342e71..ffb92c03a358 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -241,7 +241,7 @@ static int __net_init nfnetlink_net_init(struct net *net) #endif }; - nfnl = netlink_kernel_create(net, NETLINK_NETFILTER, THIS_MODULE, &cfg); + nfnl = netlink_kernel_create(net, NETLINK_NETFILTER, &cfg); if (!nfnl) return -ENOMEM; net->nfnl_stash = nfnl; diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index b74540ce3c14..4d348e97e131 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1526,9 +1526,8 @@ static void netlink_data_ready(struct sock *sk, int len) */ struct sock * -netlink_kernel_create(struct net *net, int unit, - struct module *module, - struct netlink_kernel_cfg *cfg) +__netlink_kernel_create(struct net *net, int unit, struct module *module, + struct netlink_kernel_cfg *cfg) { struct socket *sock; struct sock *sk; @@ -1603,8 +1602,7 @@ out_sock_release_nosk: sock_release(sock); return NULL; } -EXPORT_SYMBOL(netlink_kernel_create); - +EXPORT_SYMBOL(__netlink_kernel_create); void netlink_kernel_release(struct sock *sk) diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index c1b71aef9f71..19288b7d6135 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -922,8 +922,7 @@ static int __net_init genl_pernet_init(struct net *net) }; /* we'll bump the group number right afterwards */ - net->genl_sock = netlink_kernel_create(net, NETLINK_GENERIC, - THIS_MODULE, &cfg); + net->genl_sock = netlink_kernel_create(net, NETLINK_GENERIC, &cfg); if (!net->genl_sock && net_eq(net, &init_net)) panic("GENL: Cannot initialize generic netlink\n"); diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index ab58034c42d6..354070adb5ef 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -2963,7 +2963,7 @@ static int __net_init xfrm_user_net_init(struct net *net) .input = xfrm_netlink_rcv, }; - nlsk = netlink_kernel_create(net, NETLINK_XFRM, THIS_MODULE, &cfg); + nlsk = netlink_kernel_create(net, NETLINK_XFRM, &cfg); if (nlsk == NULL) return -ENOMEM; net->xfrm.nlsk_stash = nlsk; /* Don't set to NULL */ diff --git a/security/selinux/netlink.c b/security/selinux/netlink.c index 0d2cd11f3c22..14d810ead420 100644 --- a/security/selinux/netlink.c +++ b/security/selinux/netlink.c @@ -116,8 +116,7 @@ static int __init selnl_init(void) .flags = NL_CFG_F_NONROOT_RECV, }; - selnl = netlink_kernel_create(&init_net, NETLINK_SELINUX, - THIS_MODULE, &cfg); + selnl = netlink_kernel_create(&init_net, NETLINK_SELINUX, &cfg); if (selnl == NULL) panic("SELinux: Cannot create netlink socket."); return 0; -- cgit v1.2.3 From 49d8c59927e7887ea168a040e41c22937e10c30b Mon Sep 17 00:00:00 2001 From: Yunfan Zhang Date: Sat, 8 Sep 2012 03:52:18 -0700 Subject: regulator: Fairchild fan53555 support This driver supports Fairchild FAN53555 Digitally Programmable TinyBuck Regulator. The FAN53555 is a step-down switching voltage regulator that delivers a digitally programmable output from an input voltage supply of 2.5V to 5.5V. The output voltage is programmed through an I2C interface. Signed-off-by: Yunfan Zhang Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 11 ++ drivers/regulator/Makefile | 1 + drivers/regulator/fan53555.c | 325 +++++++++++++++++++++++++++++++++++++ include/linux/regulator/fan53555.h | 60 +++++++ 4 files changed, 397 insertions(+) create mode 100644 drivers/regulator/fan53555.c create mode 100644 include/linux/regulator/fan53555.h (limited to 'include') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 1c6320c131f4..a80abc4b158a 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -110,6 +110,17 @@ config REGULATOR_DA9052 This driver supports the voltage regulators of DA9052-BC and DA9053-AA/Bx PMIC. +config REGULATOR_FAN53555 + tristate "Fairchild FAN53555 Regulator" + depends on I2C + select REGMAP_I2C + help + This driver supports Fairchild FAN53555 Digitally Programmable + TinyBuck Regulator. The FAN53555 is a step-down switching voltage + regulator that delivers a digitally programmable output from an + input voltage supply of 2.5V to 5.5V. The output voltage is + programmed through an I2C interface. + config REGULATOR_ANATOP tristate "Freescale i.MX on-chip ANATOP LDO regulators" depends on MFD_ANATOP diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 3a0dbc5de66e..e431eed8a878 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_REGULATOR_DA903X) += da903x.o obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o +obj-$(CONFIG_REGULATOR_FAN53555) += fan53555.o obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c new file mode 100644 index 000000000000..890f2b34b39a --- /dev/null +++ b/drivers/regulator/fan53555.c @@ -0,0 +1,325 @@ +/* + * FAN53555 Fairchild Digitally Programmable TinyBuck Regulator Driver. + * + * Supported Part Numbers: + * FAN53555UC00X/01X/03X/04X/05X + * + * Copyright (c) 2012 Marvell Technology Ltd. + * Yunfan Zhang + * + * This package 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 + +/* Voltage setting */ +#define FAN53555_VSEL0 0x00 +#define FAN53555_VSEL1 0x01 +/* Control register */ +#define FAN53555_CONTROL 0x02 +/* IC Type */ +#define FAN53555_ID1 0x03 +/* IC mask version */ +#define FAN53555_ID2 0x04 +/* Monitor register */ +#define FAN53555_MONITOR 0x05 + +/* VSEL bit definitions */ +#define VSEL_BUCK_EN (1 << 7) +#define VSEL_MODE (1 << 6) +#define VSEL_NSEL_MASK 0x3F +/* Chip ID and Verison */ +#define DIE_ID 0x0F /* ID1 */ +#define DIE_REV 0x0F /* ID2 */ +/* Control bit definitions */ +#define CTL_OUTPUT_DISCHG (1 << 7) +#define CTL_SLEW_MASK (0x7 << 4) +#define CTL_SLEW_SHIFT 4 +#define CTL_RESET (1 << 2) + +#define FAN53555_NVOLTAGES 64 /* Numbers of voltages */ + +/* IC Type */ +enum { + FAN53555_CHIP_ID_00 = 0, + FAN53555_CHIP_ID_01, + FAN53555_CHIP_ID_02, + FAN53555_CHIP_ID_03, + FAN53555_CHIP_ID_04, + FAN53555_CHIP_ID_05, +}; + +struct fan53555_device_info { + struct regmap *regmap; + struct device *dev; + struct regulator_desc desc; + struct regulator_dev *rdev; + struct regulator_init_data *regulator; + /* IC Type and Rev */ + int chip_id; + int chip_rev; + /* Voltage setting register */ + unsigned int vol_reg; + unsigned int sleep_reg; + /* Voltage range and step(linear) */ + unsigned int vsel_min; + unsigned int vsel_max; + unsigned int vsel_step; + /* Voltage slew rate limiting */ + unsigned int slew_rate; + /* Sleep voltage cache */ + unsigned int sleep_vol_cache; +}; + +static int fan53555_set_suspend_voltage(struct regulator_dev *rdev, int uV) +{ + struct fan53555_device_info *di = rdev_get_drvdata(rdev); + int ret; + + if (di->sleep_vol_cache == uV) + return 0; + ret = regulator_map_voltage_linear(rdev, uV, uV); + if (ret < 0) + return -EINVAL; + ret = regmap_update_bits(di->regmap, di->sleep_reg, + VSEL_NSEL_MASK, ret); + if (ret < 0) + return -EINVAL; + /* Cache the sleep voltage setting. + * Might not be the real voltage which is rounded */ + di->sleep_vol_cache = uV; + + return 0; +} + +static int fan53555_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct fan53555_device_info *di = rdev_get_drvdata(rdev); + + switch (mode) { + case REGULATOR_MODE_FAST: + regmap_update_bits(di->regmap, di->vol_reg, + VSEL_MODE, VSEL_MODE); + break; + case REGULATOR_MODE_NORMAL: + regmap_update_bits(di->regmap, di->vol_reg, VSEL_MODE, 0); + break; + default: + return -EINVAL; + } + return 0; +} + +static unsigned int fan53555_get_mode(struct regulator_dev *rdev) +{ + struct fan53555_device_info *di = rdev_get_drvdata(rdev); + unsigned int val; + int ret = 0; + + ret = regmap_read(di->regmap, di->vol_reg, &val); + if (ret < 0) + return ret; + if (val & VSEL_MODE) + return REGULATOR_MODE_FAST; + else + return REGULATOR_MODE_NORMAL; +} + +static struct regulator_ops fan53555_regulator_ops = { + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .map_voltage = regulator_map_voltage_linear, + .list_voltage = regulator_list_voltage_linear, + .set_suspend_voltage = fan53555_set_suspend_voltage, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_mode = fan53555_set_mode, + .get_mode = fan53555_get_mode, +}; + +/* For 00,01,03,05 options: + * VOUT = 0.60V + NSELx * 10mV, from 0.60 to 1.23V. + * For 04 option: + * VOUT = 0.603V + NSELx * 12.826mV, from 0.603 to 1.411V. + * */ +static int fan53555_device_setup(struct fan53555_device_info *di, + struct fan53555_platform_data *pdata) +{ + unsigned int reg, data, mask; + + /* Setup voltage control register */ + switch (pdata->sleep_vsel_id) { + case FAN53555_VSEL_ID_0: + di->sleep_reg = FAN53555_VSEL0; + di->vol_reg = FAN53555_VSEL1; + break; + case FAN53555_VSEL_ID_1: + di->sleep_reg = FAN53555_VSEL1; + di->vol_reg = FAN53555_VSEL0; + break; + default: + dev_err(di->dev, "Invalid VSEL ID!\n"); + return -EINVAL; + } + /* Init voltage range and step */ + switch (di->chip_id) { + case FAN53555_CHIP_ID_00: + case FAN53555_CHIP_ID_01: + case FAN53555_CHIP_ID_03: + case FAN53555_CHIP_ID_05: + di->vsel_min = 600000; + di->vsel_max = 1230000; + di->vsel_step = 10000; + break; + case FAN53555_CHIP_ID_04: + di->vsel_min = 603000; + di->vsel_max = 1411000; + di->vsel_step = 12826; + break; + default: + dev_err(di->dev, + "Chip ID[%d]\n not supported!\n", di->chip_id); + return -EINVAL; + } + /* Init slew rate */ + if (pdata->slew_rate & 0x7) + di->slew_rate = pdata->slew_rate; + else + di->slew_rate = FAN53555_SLEW_RATE_64MV; + reg = FAN53555_CONTROL; + data = di->slew_rate << CTL_SLEW_SHIFT; + mask = CTL_SLEW_MASK; + return regmap_update_bits(di->regmap, reg, mask, data); +} + +static int fan53555_regulator_register(struct fan53555_device_info *di, + struct regulator_config *config) +{ + struct regulator_desc *rdesc = &di->desc; + + rdesc->name = "fan53555-reg"; + rdesc->ops = &fan53555_regulator_ops; + rdesc->type = REGULATOR_VOLTAGE; + rdesc->n_voltages = FAN53555_NVOLTAGES; + rdesc->enable_reg = di->vol_reg; + rdesc->enable_mask = VSEL_BUCK_EN; + rdesc->min_uV = di->vsel_min; + rdesc->uV_step = di->vsel_step; + rdesc->vsel_reg = di->vol_reg; + rdesc->vsel_mask = VSEL_NSEL_MASK; + rdesc->owner = THIS_MODULE; + + di->rdev = regulator_register(&di->desc, config); + if (IS_ERR(di->rdev)) + return PTR_ERR(di->rdev); + return 0; + +} + +static struct regmap_config fan53555_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int __devinit fan53555_regulator_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct fan53555_device_info *di; + struct fan53555_platform_data *pdata; + struct regulator_config config = { }; + unsigned int val; + int ret; + + pdata = client->dev.platform_data; + if (!pdata || !pdata->regulator) { + dev_err(&client->dev, "Platform data not found!\n"); + return -ENODEV; + } + + di = devm_kzalloc(&client->dev, sizeof(struct fan53555_device_info), + GFP_KERNEL); + if (!di) { + dev_err(&client->dev, "Failed to allocate device info data!\n"); + return -ENOMEM; + } + di->regmap = devm_regmap_init_i2c(client, &fan53555_regmap_config); + if (IS_ERR(di->regmap)) { + dev_err(&client->dev, "Failed to allocate regmap!\n"); + return PTR_ERR(di->regmap); + } + di->dev = &client->dev; + di->regulator = pdata->regulator; + i2c_set_clientdata(client, di); + /* Get chip ID */ + ret = regmap_read(di->regmap, FAN53555_ID1, &val); + if (ret < 0) { + dev_err(&client->dev, "Failed to get chip ID!\n"); + return -ENODEV; + } + di->chip_id = val & DIE_ID; + /* Get chip revision */ + ret = regmap_read(di->regmap, FAN53555_ID2, &val); + if (ret < 0) { + dev_err(&client->dev, "Failed to get chip Rev!\n"); + return -ENODEV; + } + di->chip_rev = val & DIE_REV; + dev_info(&client->dev, "FAN53555 Option[%d] Rev[%d] Detected!\n", + di->chip_id, di->chip_rev); + /* Device init */ + ret = fan53555_device_setup(di, pdata); + if (ret < 0) { + dev_err(&client->dev, "Failed to setup device!\n"); + return ret; + } + /* Register regulator */ + config.dev = di->dev; + config.init_data = di->regulator; + config.regmap = di->regmap; + config.driver_data = di; + ret = fan53555_regulator_register(di, &config); + if (ret < 0) + dev_err(&client->dev, "Failed to register regulator!\n"); + return ret; + +} + +static int __devexit fan53555_regulator_remove(struct i2c_client *client) +{ + struct fan53555_device_info *di = i2c_get_clientdata(client); + + regulator_unregister(di->rdev); + return 0; +} + +static const struct i2c_device_id fan53555_id[] = { + {"fan53555", -1}, + { }, +}; + +static struct i2c_driver fan53555_regulator_driver = { + .driver = { + .name = "fan53555-regulator", + }, + .probe = fan53555_regulator_probe, + .remove = __devexit_p(fan53555_regulator_remove), + .id_table = fan53555_id, +}; + +module_i2c_driver(fan53555_regulator_driver); + +MODULE_AUTHOR("Yunfan Zhang "); +MODULE_DESCRIPTION("FAN53555 regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/regulator/fan53555.h b/include/linux/regulator/fan53555.h new file mode 100644 index 000000000000..5c45c85d52ca --- /dev/null +++ b/include/linux/regulator/fan53555.h @@ -0,0 +1,60 @@ +/* + * fan53555.h - Fairchild Regulator FAN53555 Driver + * + * Copyright (C) 2012 Marvell Technology Ltd. + * Yunfan Zhang + * + * This package 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 __FAN53555_H__ + +/* VSEL ID */ +enum { + FAN53555_VSEL_ID_0 = 0, + FAN53555_VSEL_ID_1, +}; + +/* Transition slew rate limiting from a low to high voltage. + * ----------------------- + * Bin |Slew Rate(mV/uS) + * ------|---------------- + * 000 | 64.00 + * ------|---------------- + * 001 | 32.00 + * ------|---------------- + * 010 | 16.00 + * ------|---------------- + * 011 | 8.00 + * ------|---------------- + * 100 | 4.00 + * ------|---------------- + * 101 | 2.00 + * ------|---------------- + * 110 | 1.00 + * ------|---------------- + * 111 | 0.50 + * ----------------------- + */ +enum { + FAN53555_SLEW_RATE_64MV = 0, + FAN53555_SLEW_RATE_32MV, + FAN53555_SLEW_RATE_16MV, + FAN53555_SLEW_RATE_8MV, + FAN53555_SLEW_RATE_4MV, + FAN53555_SLEW_RATE_2MV, + FAN53555_SLEW_RATE_1MV, + FAN53555_SLEW_RATE_0_5MV, +}; + +struct fan53555_platform_data { + struct regulator_init_data *regulator; + unsigned int slew_rate; + /* Sleep VSEL ID */ + unsigned int sleep_vsel_id; +}; + +#endif /* __FAN53555_H__ */ -- cgit v1.2.3 From f59c8f9fe689790248ae7aa7426579982050638c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 31 Aug 2012 10:36:37 -0700 Subject: regulator: core: Support bypass mode Many regulators support a bypass mode where they simply switch their input supply to the output. This is mainly used in low power retention states where power consumption is extremely low so higher voltage or less clean supplies can be used. Support this by providing ops for the drivers and a consumer API which allows the device to be put into bypass mode if all consumers enable it and the machine enables permission for this. This is not supported as a mode since the existing modes are rarely used due to fuzzy definition and mostly redundant with modern hardware which is able to respond promptly to load changes. Signed-off-by: Mark Brown Reviewed-by: Graeme Gregory --- Documentation/ABI/testing/sysfs-class-regulator | 21 ++++++ drivers/regulator/core.c | 85 +++++++++++++++++++++++++ include/linux/regulator/consumer.h | 8 +++ include/linux/regulator/driver.h | 10 +++ include/linux/regulator/machine.h | 2 + 5 files changed, 126 insertions(+) (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-class-regulator b/Documentation/ABI/testing/sysfs-class-regulator index e091fa873792..bc578bc60628 100644 --- a/Documentation/ABI/testing/sysfs-class-regulator +++ b/Documentation/ABI/testing/sysfs-class-regulator @@ -349,3 +349,24 @@ Description: This will be one of the same strings reported by the "state" attribute. + +What: /sys/class/regulator/.../bypass +Date: September 2012 +KernelVersion: 3.7 +Contact: Mark Brown +Description: + Some regulator directories will contain a field called + bypass. This indicates if the device is in bypass mode. + + This will be one of the following strings: + + 'enabled' + 'disabled' + 'unknown' + + 'enabled' means the regulator is in bypass mode. + + 'disabled' means that the regulator is regulating. + + 'unknown' means software cannot determine the state, or + the reported state is invalid. diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 48385318175a..64e16053975e 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -77,6 +77,7 @@ struct regulator { struct device *dev; struct list_head list; unsigned int always_on:1; + unsigned int bypass:1; int uA_load; int min_uV; int max_uV; @@ -394,6 +395,9 @@ static ssize_t regulator_status_show(struct device *dev, case REGULATOR_STATUS_STANDBY: label = "standby"; break; + case REGULATOR_STATUS_BYPASS: + label = "bypass"; + break; case REGULATOR_STATUS_UNDEFINED: label = "undefined"; break; @@ -585,6 +589,27 @@ static ssize_t regulator_suspend_standby_state_show(struct device *dev, static DEVICE_ATTR(suspend_standby_state, 0444, regulator_suspend_standby_state_show, NULL); +static ssize_t regulator_bypass_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + const char *report; + bool bypass; + int ret; + + ret = rdev->desc->ops->get_bypass(rdev, &bypass); + + if (ret != 0) + report = "unknown"; + else if (bypass) + report = "enabled"; + else + report = "disabled"; + + return sprintf(buf, "%s\n", report); +} +static DEVICE_ATTR(bypass, 0444, + regulator_bypass_show, NULL); /* * These are the only attributes are present for all regulators. @@ -2673,6 +2698,59 @@ out: } EXPORT_SYMBOL_GPL(regulator_set_optimum_mode); +/** + * regulator_allow_bypass - allow the regulator to go into bypass mode + * + * @regulator: Regulator to configure + * @allow: enable or disable bypass mode + * + * Allow the regulator to go into bypass mode if all other consumers + * for the regulator also enable bypass mode and the machine + * constraints allow this. Bypass mode means that the regulator is + * simply passing the input directly to the output with no regulation. + */ +int regulator_allow_bypass(struct regulator *regulator, bool enable) +{ + struct regulator_dev *rdev = regulator->rdev; + int ret = 0; + + if (!rdev->desc->ops->set_bypass) + return 0; + + if (rdev->constraints && + !(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_BYPASS)) + return 0; + + mutex_lock(&rdev->mutex); + + if (enable && !regulator->bypass) { + rdev->bypass_count++; + + if (rdev->bypass_count == rdev->open_count) { + ret = rdev->desc->ops->set_bypass(rdev, enable); + if (ret != 0) + rdev->bypass_count--; + } + + } else if (!enable && regulator->bypass) { + rdev->bypass_count--; + + if (rdev->bypass_count != rdev->open_count) { + ret = rdev->desc->ops->set_bypass(rdev, enable); + if (ret != 0) + rdev->bypass_count++; + } + } + + if (ret == 0) + regulator->bypass = enable; + + mutex_unlock(&rdev->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_allow_bypass); + /** * regulator_register_notifier - register regulator event notifier * @regulator: regulator source @@ -3036,6 +3114,11 @@ static int add_regulator_attributes(struct regulator_dev *rdev) if (status < 0) return status; } + if (ops->get_bypass) { + status = device_create_file(dev, &dev_attr_bypass); + if (status < 0) + return status; + } /* some attributes are type-specific */ if (rdev->desc->type == REGULATOR_CURRENT) { @@ -3124,6 +3207,8 @@ static void rdev_init_debugfs(struct regulator_dev *rdev) &rdev->use_count); debugfs_create_u32("open_count", 0444, rdev->debugfs, &rdev->open_count); + debugfs_create_u32("bypass_count", 0444, rdev->debugfs, + &rdev->bypass_count); } /** diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index da339fd8c755..ea3e35816621 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -177,6 +177,8 @@ int regulator_set_mode(struct regulator *regulator, unsigned int mode); unsigned int regulator_get_mode(struct regulator *regulator); int regulator_set_optimum_mode(struct regulator *regulator, int load_uA); +int regulator_allow_bypass(struct regulator *regulator, bool allow); + /* regulator notifier block */ int regulator_register_notifier(struct regulator *regulator, struct notifier_block *nb); @@ -328,6 +330,12 @@ static inline int regulator_set_optimum_mode(struct regulator *regulator, return REGULATOR_MODE_NORMAL; } +static inline int regulator_allow_bypass(struct regulator *regulator, + bool allow) +{ + return 0; +} + static inline int regulator_register_notifier(struct regulator *regulator, struct notifier_block *nb) { diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index bac4c871f3bd..c9869cfbf261 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -32,6 +32,8 @@ enum regulator_status { REGULATOR_STATUS_NORMAL, REGULATOR_STATUS_IDLE, REGULATOR_STATUS_STANDBY, + /* The regulator is enabled but not regulating */ + REGULATOR_STATUS_BYPASS, /* in case that any other status doesn't apply */ REGULATOR_STATUS_UNDEFINED, }; @@ -67,6 +69,9 @@ enum regulator_status { * @get_optimum_mode: Get the most efficient operating mode for the regulator * when running with the specified parameters. * + * @set_bypass: Set the regulator in bypass mode. + * @get_bypass: Get the regulator bypass mode state. + * * @enable_time: Time taken for the regulator voltage output voltage to * stabilise after being enabled, in microseconds. * @set_ramp_delay: Set the ramp delay for the regulator. The driver should @@ -133,6 +138,10 @@ struct regulator_ops { unsigned int (*get_optimum_mode) (struct regulator_dev *, int input_uV, int output_uV, int load_uA); + /* control and report on bypass mode */ + int (*set_bypass)(struct regulator_dev *dev, bool enable); + int (*get_bypass)(struct regulator_dev *dev, bool *enable); + /* the operations below are for configuration of regulator state when * its parent PMIC enters a global STANDBY/HIBERNATE state */ @@ -253,6 +262,7 @@ struct regulator_dev { int exclusive; u32 use_count; u32 open_count; + u32 bypass_count; /* lists we belong to */ struct list_head list; /* list of all regulators */ diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index 40dd0a394cfa..36adbc82de6a 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -32,6 +32,7 @@ struct regulator; * board/machine. * STATUS: Regulator can be enabled and disabled. * DRMS: Dynamic Regulator Mode Switching is enabled for this regulator. + * BYPASS: Regulator can be put into bypass mode */ #define REGULATOR_CHANGE_VOLTAGE 0x1 @@ -39,6 +40,7 @@ struct regulator; #define REGULATOR_CHANGE_MODE 0x4 #define REGULATOR_CHANGE_STATUS 0x8 #define REGULATOR_CHANGE_DRMS 0x10 +#define REGULATOR_CHANGE_BYPASS 0x20 /** * struct regulator_state - regulator state during low power system states -- cgit v1.2.3 From df36793115b4f68181877a1c89bac54feadd965d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 27 Aug 2012 16:04:23 -0700 Subject: regulator: core: Provide regmap get/set bypass operations Signed-off-by: Mark Brown --- drivers/regulator/core.c | 41 ++++++++++++++++++++++++++++++++++++++++ include/linux/regulator/driver.h | 4 ++++ 2 files changed, 45 insertions(+) (limited to 'include') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 64e16053975e..419805cdd9d7 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2698,6 +2698,47 @@ out: } EXPORT_SYMBOL_GPL(regulator_set_optimum_mode); +/** + * regulator_set_bypass_regmap - Default set_bypass() using regmap + * + * @rdev: device to operate on. + * @enable: state to set. + */ +int regulator_set_bypass_regmap(struct regulator_dev *rdev, bool enable) +{ + unsigned int val; + + if (enable) + val = rdev->desc->bypass_mask; + else + val = 0; + + return regmap_update_bits(rdev->regmap, rdev->desc->bypass_reg, + rdev->desc->bypass_mask, val); +} +EXPORT_SYMBOL_GPL(regulator_set_bypass_regmap); + +/** + * regulator_get_bypass_regmap - Default get_bypass() using regmap + * + * @rdev: device to operate on. + * @enable: current state. + */ +int regulator_get_bypass_regmap(struct regulator_dev *rdev, bool *enable) +{ + unsigned int val; + int ret; + + ret = regmap_read(rdev->regmap, rdev->desc->bypass_reg, &val); + if (ret != 0) + return ret; + + *enable = val & rdev->desc->bypass_mask; + + return 0; +} +EXPORT_SYMBOL_GPL(regulator_get_bypass_regmap); + /** * regulator_allow_bypass - allow the regulator to go into bypass mode * diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index c9869cfbf261..7274a469e8d9 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -214,6 +214,8 @@ struct regulator_desc { unsigned int vsel_mask; unsigned int enable_reg; unsigned int enable_mask; + unsigned int bypass_reg; + unsigned int bypass_mask; unsigned int enable_time; }; @@ -320,6 +322,8 @@ int regulator_disable_regmap(struct regulator_dev *rdev); int regulator_set_voltage_time_sel(struct regulator_dev *rdev, unsigned int old_selector, unsigned int new_selector); +int regulator_set_bypass_regmap(struct regulator_dev *rdev, bool enable); +int regulator_get_bypass_regmap(struct regulator_dev *rdev, bool *enable); void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data); -- cgit v1.2.3 From e548c49e6dc6b08b59042930a2e90c69c13c9293 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 4 Sep 2012 17:08:23 +0200 Subject: mac80211: add key flag for management keys Mark keys that might be used to receive management frames so drivers can fall back on software crypto for them if they don't support hardware offload. As the new flag is only set correctly for RX keys and the existing IEEE80211_KEY_FLAG_SW_MGMT flag can only affect TX, also rename the latter to IEEE80211_KEY_FLAG_SW_MGMT_TX. Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath5k/mac80211-ops.c | 2 +- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 2 +- drivers/net/wireless/ath/ath9k/main.c | 2 +- include/net/mac80211.h | 12 ++++++++-- net/mac80211/cfg.c | 32 +++++++++++++++++++++++++++ net/mac80211/tx.c | 2 +- 6 files changed, 46 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index c89fa6ead615..155c70e4c26c 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -524,7 +524,7 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (key->cipher == WLAN_CIPHER_SUITE_TKIP) key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; if (key->cipher == WLAN_CIPHER_SUITE_CCMP) - key->flags |= IEEE80211_KEY_FLAG_SW_MGMT; + key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; ret = 0; } break; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index c32f6e3ffb18..a6bb6e3698ca 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1449,7 +1449,7 @@ static int ath9k_htc_set_key(struct ieee80211_hw *hw, key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; if (priv->ah->sw_mgmt_crypto && key->cipher == WLAN_CIPHER_SUITE_CCMP) - key->flags |= IEEE80211_KEY_FLAG_SW_MGMT; + key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; ret = 0; } break; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 4d8dc9ff5a75..06c628e85a43 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1441,7 +1441,7 @@ static int ath9k_set_key(struct ieee80211_hw *hw, key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; if (sc->sc_ah->sw_mgmt_crypto && key->cipher == WLAN_CIPHER_SUITE_CCMP) - key->flags |= IEEE80211_KEY_FLAG_SW_MGMT; + key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; ret = 0; } break; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 71f8262fc1df..82558c8decf8 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -973,21 +973,29 @@ static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif) * generation in software. * @IEEE80211_KEY_FLAG_PAIRWISE: Set by mac80211, this flag indicates * that the key is pairwise rather then a shared key. - * @IEEE80211_KEY_FLAG_SW_MGMT: This flag should be set by the driver for a + * @IEEE80211_KEY_FLAG_SW_MGMT_TX: This flag should be set by the driver for a * CCMP key if it requires CCMP encryption of management frames (MFP) to * be done in software. * @IEEE80211_KEY_FLAG_PUT_IV_SPACE: This flag should be set by the driver * if space should be prepared for the IV, but the IV * itself should not be generated. Do not set together with * @IEEE80211_KEY_FLAG_GENERATE_IV on the same key. + * @IEEE80211_KEY_FLAG_RX_MGMT: This key will be used to decrypt received + * management frames. The flag can help drivers that have a hardware + * crypto implementation that doesn't deal with management frames + * properly by allowing them to not upload the keys to hardware and + * fall back to software crypto. Note that this flag deals only with + * RX, if your crypto engine can't deal with TX you can also set the + * %IEEE80211_KEY_FLAG_SW_MGMT_TX flag to encrypt such frames in SW. */ enum ieee80211_key_flags { IEEE80211_KEY_FLAG_WMM_STA = 1<<0, IEEE80211_KEY_FLAG_GENERATE_IV = 1<<1, IEEE80211_KEY_FLAG_GENERATE_MMIC= 1<<2, IEEE80211_KEY_FLAG_PAIRWISE = 1<<3, - IEEE80211_KEY_FLAG_SW_MGMT = 1<<4, + IEEE80211_KEY_FLAG_SW_MGMT_TX = 1<<4, IEEE80211_KEY_FLAG_PUT_IV_SPACE = 1<<5, + IEEE80211_KEY_FLAG_RX_MGMT = 1<<6, }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 03fe6d1cff42..00e31b488adc 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -170,6 +170,38 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, } } + switch (sdata->vif.type) { + case NL80211_IFTYPE_STATION: + if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED) + key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT; + break; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + /* Keys without a station are used for TX only */ + if (key->sta && test_sta_flag(key->sta, WLAN_STA_MFP)) + key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT; + break; + case NL80211_IFTYPE_ADHOC: + /* no MFP (yet) */ + break; + case NL80211_IFTYPE_MESH_POINT: +#ifdef CONFIG_MAC80211_MESH + if (sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE) + key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT; + break; +#endif + case NL80211_IFTYPE_WDS: + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_P2P_DEVICE: + case NL80211_IFTYPE_UNSPECIFIED: + case NUM_NL80211_IFTYPES: + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + /* shouldn't happen */ + WARN_ON_ONCE(1); + break; + } + err = ieee80211_key_link(key, sdata, sta); if (err) ieee80211_key_free(sdata->local, key); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 29eb4e678235..e0e0d1d0e830 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -580,7 +580,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) tx->key = NULL; else skip_hw = (tx->key->conf.flags & - IEEE80211_KEY_FLAG_SW_MGMT) && + IEEE80211_KEY_FLAG_SW_MGMT_TX) && ieee80211_is_mgmt(hdr->frame_control); break; case WLAN_CIPHER_SUITE_AES_CMAC: -- cgit v1.2.3 From fac3a43e0ab20dbf5e845c6221ead0d073984f41 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 6 Sep 2012 20:11:01 +0200 Subject: usb: gadget: move bind callback into driver struct usb_composite_driver It was moved to be an argument in 07a18bd716ed5 ("usb gadget: don't save bind callback in struct usb_composite_driver"). The reason was to avoid the section missmatch. The warning was shown because ->bind is marked as __init becuase it is a one time init. The warning can be also suppresed by whitelisting the variable i.e. rename it to lets say _probe. Acked-by: Michal Nazarewicz Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/gadget/composite.c | 10 ++++++---- include/linux/usb/composite.h | 13 +++++++++---- 2 files changed, 15 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 91411a6d741b..402e5bd8b3e5 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -32,7 +32,6 @@ #define USB_BUFSIZ 1024 static struct usb_composite_driver *composite; -static int (*composite_gadget_bind)(struct usb_composite_dev *cdev); /* Some systems will need runtime overrides for the product identifiers * published in the device descriptor, either numbers or strings or both. @@ -1468,7 +1467,7 @@ static int composite_bind(struct usb_gadget *gadget) * serial number), register function drivers, potentially update * power state and consumption, etc */ - status = composite_gadget_bind(cdev); + status = composite->bind(cdev); if (status < 0) goto fail; @@ -1621,7 +1620,9 @@ static struct usb_gadget_driver composite_driver = { int usb_composite_probe(struct usb_composite_driver *driver, int (*bind)(struct usb_composite_dev *cdev)) { - if (!driver || !driver->dev || !bind || composite) + if (!driver || !driver->dev || composite) + return -EINVAL; + if (!bind && !driver->bind) return -EINVAL; if (!driver->name) @@ -1632,7 +1633,8 @@ int usb_composite_probe(struct usb_composite_driver *driver, composite_driver.driver.name = driver->name; composite_driver.max_speed = driver->max_speed; composite = driver; - composite_gadget_bind = bind; + if (!driver->bind) + driver->bind = bind; return usb_gadget_probe_driver(&composite_driver, composite_bind); } diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 9d8c3b634493..3153f73ae083 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -257,12 +257,16 @@ void usb_remove_config(struct usb_composite_dev *, * not set. * @dev: Template descriptor for the device, including default device * identifiers. - * @strings: tables of strings, keyed by identifiers assigned during bind() + * @strings: tables of strings, keyed by identifiers assigned during @bind * and language IDs provided in control requests * @max_speed: Highest speed the driver supports. * @needs_serial: set to 1 if the gadget needs userspace to provide * a serial number. If one is not provided, warning will be printed. - * @unbind: Reverses bind; called as a side effect of unregistering + * @bind: (REQUIRED) Used to allocate resources that are shared across the + * whole device, such as string IDs, and add its configurations using + * @usb_add_config(). This may fail by returning a negative errno + * value; it should return zero on successful initialization. + * @unbind: Reverses @bind; called as a side effect of unregistering * this driver. * @disconnect: optional driver disconnect method * @suspend: Notifies when the host stops sending USB traffic, @@ -271,9 +275,9 @@ void usb_remove_config(struct usb_composite_dev *, * before function notifications * * Devices default to reporting self powered operation. Devices which rely - * on bus powered operation should report this in their @bind() method. + * on bus powered operation should report this in their @bind method. * - * Before returning from bind, various fields in the template descriptor + * Before returning from @bind, various fields in the template descriptor * may be overridden. These include the idVendor/idProduct/bcdDevice values * normally to bind the appropriate host side driver, and the three strings * (iManufacturer, iProduct, iSerialNumber) normally used to provide user @@ -291,6 +295,7 @@ struct usb_composite_driver { enum usb_device_speed max_speed; unsigned needs_serial:1; + int (*bind)(struct usb_composite_dev *cdev); int (*unbind)(struct usb_composite_dev *); void (*disconnect)(struct usb_composite_dev *); -- cgit v1.2.3 From 03e42bd5937c4c24e411690ab165627e93c258b5 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 6 Sep 2012 20:11:04 +0200 Subject: usb: gadget: move bind() callback back to struct usb_composite_driver This partly reverts 07a18bd7 ("usb gadget: don't save bind callback in struct usb_composite_driver") and fixes new drivers. The section missmatch problems was solved by whitelisting structs in question via __ref. Cc: devel@driverdev.osuosl.org Acked-by: Michal Nazarewicz Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/staging/ccg/ccg.c | 3 ++- drivers/usb/gadget/acm_ms.c | 3 ++- drivers/usb/gadget/audio.c | 3 ++- drivers/usb/gadget/cdc2.c | 3 ++- drivers/usb/gadget/composite.c | 9 ++------- drivers/usb/gadget/ether.c | 3 ++- drivers/usb/gadget/g_ffs.c | 3 ++- drivers/usb/gadget/gmidi.c | 3 ++- drivers/usb/gadget/hid.c | 3 ++- drivers/usb/gadget/mass_storage.c | 3 ++- drivers/usb/gadget/multi.c | 3 ++- drivers/usb/gadget/ncm.c | 3 ++- drivers/usb/gadget/nokia.c | 3 ++- drivers/usb/gadget/printer.c | 3 ++- drivers/usb/gadget/serial.c | 3 ++- drivers/usb/gadget/tcm_usb_gadget.c | 3 ++- drivers/usb/gadget/webcam.c | 3 ++- drivers/usb/gadget/zero.c | 3 ++- include/linux/usb/composite.h | 3 +-- 19 files changed, 37 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/drivers/staging/ccg/ccg.c b/drivers/staging/ccg/ccg.c index 6a7aab8d9bf5..eadda5574b2a 100644 --- a/drivers/staging/ccg/ccg.c +++ b/drivers/staging/ccg/ccg.c @@ -1162,6 +1162,7 @@ static int ccg_usb_unbind(struct usb_composite_dev *cdev) static struct usb_composite_driver ccg_usb_driver = { .name = "configurable_usb", .dev = &device_desc, + .bind = ccg_bind, .unbind = ccg_usb_unbind, .needs_serial = true, .iManufacturer = "Linux Foundation", @@ -1275,7 +1276,7 @@ static int __init init(void) composite_driver.setup = ccg_setup; composite_driver.disconnect = ccg_disconnect; - err = usb_composite_probe(&ccg_usb_driver, ccg_bind); + err = usb_composite_probe(&ccg_usb_driver); if (err) { class_destroy(ccg_class); kfree(dev); diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/acm_ms.c index dc5cd51de7d4..65a2f3cbcde3 100644 --- a/drivers/usb/gadget/acm_ms.c +++ b/drivers/usb/gadget/acm_ms.c @@ -237,6 +237,7 @@ static __refdata struct usb_composite_driver acm_ms_driver = { .dev = &device_desc, .max_speed = USB_SPEED_SUPER, .strings = dev_strings, + .bind = acm_ms_bind, .unbind = __exit_p(acm_ms_unbind), }; @@ -246,7 +247,7 @@ MODULE_LICENSE("GPL v2"); static int __init init(void) { - return usb_composite_probe(&acm_ms_driver, acm_ms_bind); + return usb_composite_probe(&acm_ms_driver); } module_init(init); diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c index e539490e7733..dd339bc5b40c 100644 --- a/drivers/usb/gadget/audio.c +++ b/drivers/usb/gadget/audio.c @@ -203,12 +203,13 @@ static __refdata struct usb_composite_driver audio_driver = { .dev = &device_desc, .strings = audio_strings, .max_speed = USB_SPEED_HIGH, + .bind = audio_bind, .unbind = __exit_p(audio_unbind), }; static int __init init(void) { - return usb_composite_probe(&audio_driver, audio_bind); + return usb_composite_probe(&audio_driver); } module_init(init); diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index 00b65ac7a2ef..b7d984b54ca9 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -237,6 +237,7 @@ static __refdata struct usb_composite_driver cdc_driver = { .dev = &device_desc, .strings = dev_strings, .max_speed = USB_SPEED_HIGH, + .bind = cdc_bind, .unbind = __exit_p(cdc_unbind), }; @@ -246,7 +247,7 @@ MODULE_LICENSE("GPL"); static int __init init(void) { - return usb_composite_probe(&cdc_driver, cdc_bind); + return usb_composite_probe(&cdc_driver); } module_init(init); diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 402e5bd8b3e5..071d15c44116 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1617,12 +1617,9 @@ static struct usb_gadget_driver composite_driver = { * while it was binding. That would usually be done in order to wait for * some userspace participation. */ -int usb_composite_probe(struct usb_composite_driver *driver, - int (*bind)(struct usb_composite_dev *cdev)) +int usb_composite_probe(struct usb_composite_driver *driver) { - if (!driver || !driver->dev || composite) - return -EINVAL; - if (!bind && !driver->bind) + if (!driver || !driver->dev || composite || !driver->bind) return -EINVAL; if (!driver->name) @@ -1633,8 +1630,6 @@ int usb_composite_probe(struct usb_composite_driver *driver, composite_driver.driver.name = driver->name; composite_driver.max_speed = driver->max_speed; composite = driver; - if (!driver->bind) - driver->bind = bind; return usb_gadget_probe_driver(&composite_driver, composite_bind); } diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 49a7dac06b1d..4580ec09cc53 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -393,6 +393,7 @@ static __refdata struct usb_composite_driver eth_driver = { .dev = &device_desc, .strings = dev_strings, .max_speed = USB_SPEED_SUPER, + .bind = eth_bind, .unbind = __exit_p(eth_unbind), }; @@ -402,7 +403,7 @@ MODULE_LICENSE("GPL"); static int __init init(void) { - return usb_composite_probe(ð_driver, eth_bind); + return usb_composite_probe(ð_driver); } module_init(init); diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index d1312c404afa..da9809f55cb5 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -168,6 +168,7 @@ static __refdata struct usb_composite_driver gfs_driver = { .dev = &gfs_dev_desc, .strings = gfs_dev_strings, .max_speed = USB_SPEED_HIGH, + .bind = gfs_bind, .unbind = gfs_unbind, .iProduct = DRIVER_DESC, }; @@ -268,7 +269,7 @@ static int functionfs_ready_callback(struct ffs_data *ffs) } gfs_registered = true; - ret = usb_composite_probe(&gfs_driver, gfs_bind); + ret = usb_composite_probe(&gfs_driver); if (unlikely(ret < 0)) gfs_registered = false; diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index ae46f69b718a..1e3cd378a2d7 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -189,12 +189,13 @@ static __refdata struct usb_composite_driver midi_driver = { .dev = &device_desc, .strings = dev_strings, .max_speed = USB_SPEED_HIGH, + .bind = midi_bind, .unbind = __exit_p(midi_unbind), }; static int __init midi_init(void) { - return usb_composite_probe(&midi_driver, midi_bind); + return usb_composite_probe(&midi_driver); } module_init(midi_init); diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/hid.c index 34e139e700b4..8502e56d73bc 100644 --- a/drivers/usb/gadget/hid.c +++ b/drivers/usb/gadget/hid.c @@ -247,6 +247,7 @@ static __refdata struct usb_composite_driver hidg_driver = { .dev = &device_desc, .strings = dev_strings, .max_speed = USB_SPEED_HIGH, + .bind = hid_bind, .unbind = __exit_p(hid_unbind), }; @@ -272,7 +273,7 @@ static int __init hidg_init(void) if (status < 0) return status; - status = usb_composite_probe(&hidg_driver, hid_bind); + status = usb_composite_probe(&hidg_driver); if (status < 0) platform_driver_unregister(&hidg_plat_driver); diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c index 5df117e3f78d..480edbc368e5 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/mass_storage.c @@ -162,6 +162,7 @@ static __refdata struct usb_composite_driver msg_driver = { .iProduct = DRIVER_DESC, .max_speed = USB_SPEED_SUPER, .needs_serial = 1, + .bind = msg_bind, }; MODULE_DESCRIPTION(DRIVER_DESC); @@ -170,7 +171,7 @@ MODULE_LICENSE("GPL"); static int __init msg_init(void) { - return usb_composite_probe(&msg_driver, msg_bind); + return usb_composite_probe(&msg_driver); } module_init(msg_init); diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 7e5852a28a93..13db7ccb9571 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -343,6 +343,7 @@ static __refdata struct usb_composite_driver multi_driver = { .dev = &device_desc, .strings = dev_strings, .max_speed = USB_SPEED_HIGH, + .bind = multi_bind, .unbind = __exit_p(multi_unbind), .iProduct = DRIVER_DESC, .needs_serial = 1, @@ -351,7 +352,7 @@ static __refdata struct usb_composite_driver multi_driver = { static int __init multi_init(void) { - return usb_composite_probe(&multi_driver, multi_bind); + return usb_composite_probe(&multi_driver); } module_init(multi_init); diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c index 5079bf659e31..9a20057896cd 100644 --- a/drivers/usb/gadget/ncm.c +++ b/drivers/usb/gadget/ncm.c @@ -220,6 +220,7 @@ static __refdata struct usb_composite_driver ncm_driver = { .dev = &device_desc, .strings = dev_strings, .max_speed = USB_SPEED_HIGH, + .bind = gncm_bind, .unbind = __exit_p(gncm_unbind), }; @@ -229,7 +230,7 @@ MODULE_LICENSE("GPL"); static int __init init(void) { - return usb_composite_probe(&ncm_driver, gncm_bind); + return usb_composite_probe(&ncm_driver); } module_init(init); diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index 936d0afc4527..a5b5e7162fef 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -242,12 +242,13 @@ static __refdata struct usb_composite_driver nokia_driver = { .dev = &device_desc, .strings = dev_strings, .max_speed = USB_SPEED_HIGH, + .bind = nokia_bind, .unbind = __exit_p(nokia_unbind), }; static int __init nokia_init(void) { - return usb_composite_probe(&nokia_driver, nokia_bind); + return usb_composite_probe(&nokia_driver); } module_init(nokia_init); diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index 0aab51766ce6..33c0c07493e8 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -1274,6 +1274,7 @@ static __refdata struct usb_composite_driver printer_driver = { .dev = &device_desc, .strings = dev_strings, .max_speed = USB_SPEED_HIGH, + .bind = printer_bind, .unbind = printer_unbind, }; @@ -1297,7 +1298,7 @@ init(void) return status; } - status = usb_composite_probe(&printer_driver, printer_bind); + status = usb_composite_probe(&printer_driver); if (status) { class_destroy(usb_gadget_class); unregister_chrdev_region(g_printer_devno, 1); diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index 7b0b6f40ed07..ea3f8e9344ae 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -243,6 +243,7 @@ static __refdata struct usb_composite_driver gserial_driver = { .dev = &device_desc, .strings = dev_strings, .max_speed = USB_SPEED_SUPER, + .bind = gs_bind, }; static int __init init(void) @@ -271,7 +272,7 @@ static int __init init(void) } strings_dev[STRING_DESCRIPTION_IDX].s = serial_config_driver.label; - return usb_composite_probe(&gserial_driver, gs_bind); + return usb_composite_probe(&gserial_driver); } module_init(init); diff --git a/drivers/usb/gadget/tcm_usb_gadget.c b/drivers/usb/gadget/tcm_usb_gadget.c index 28fef844a06a..35ca09af9b4a 100644 --- a/drivers/usb/gadget/tcm_usb_gadget.c +++ b/drivers/usb/gadget/tcm_usb_gadget.c @@ -2441,12 +2441,13 @@ static __refdata struct usb_composite_driver usbg_driver = { .dev = &usbg_device_desc, .strings = usbg_strings, .max_speed = USB_SPEED_SUPER, + .bind = usb_target_bind, .unbind = guas_unbind, }; static int usbg_attach(struct usbg_tpg *tpg) { - return usb_composite_probe(&usbg_driver, usb_target_bind); + return usb_composite_probe(&usbg_driver); } static void usbg_detach(struct usbg_tpg *tpg) diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/webcam.c index d946f19482e8..2a617c3f5a40 100644 --- a/drivers/usb/gadget/webcam.c +++ b/drivers/usb/gadget/webcam.c @@ -395,13 +395,14 @@ static __refdata struct usb_composite_driver webcam_driver = { .dev = &webcam_device_descriptor, .strings = webcam_device_strings, .max_speed = USB_SPEED_SUPER, + .bind = webcam_bind, .unbind = webcam_unbind, }; static int __init webcam_init(void) { - return usb_composite_probe(&webcam_driver, webcam_bind); + return usb_composite_probe(&webcam_driver); } static void __exit diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index 5db33cbe755b..90df613cccc0 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -339,6 +339,7 @@ static __refdata struct usb_composite_driver zero_driver = { .dev = &device_desc, .strings = dev_strings, .max_speed = USB_SPEED_SUPER, + .bind = zero_bind, .unbind = zero_unbind, .suspend = zero_suspend, .resume = zero_resume, @@ -349,7 +350,7 @@ MODULE_LICENSE("GPL"); static int __init init(void) { - return usb_composite_probe(&zero_driver, zero_bind); + return usb_composite_probe(&zero_driver); } module_init(init); diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 3153f73ae083..19a5adf18bf4 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -305,8 +305,7 @@ struct usb_composite_driver { void (*resume)(struct usb_composite_dev *); }; -extern int usb_composite_probe(struct usb_composite_driver *driver, - int (*bind)(struct usb_composite_dev *cdev)); +extern int usb_composite_probe(struct usb_composite_driver *driver); extern void usb_composite_unregister(struct usb_composite_driver *driver); extern void usb_composite_setup_continue(struct usb_composite_dev *cdev); -- cgit v1.2.3 From 93952956c7078eb41058c5ccc5b34ae6cf59bb64 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 6 Sep 2012 20:11:05 +0200 Subject: usb: gadget: move bind() callback back to struct usb_gadget_driver This partly reverts 07a18bd7 ("usb gadget: don't save bind callback in struct usb_composite_driver") and fixes new drivers. The section missmatch problems was solved by whitelisting bind callback in modpost. Acked-by: Michal Nazarewicz Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/gadget/composite.c | 3 ++- drivers/usb/gadget/dbgp.c | 3 ++- drivers/usb/gadget/file_storage.c | 4 +++- drivers/usb/gadget/inode.c | 7 +++++-- drivers/usb/gadget/udc-core.c | 9 ++++----- include/linux/usb/gadget.h | 6 +++--- 6 files changed, 19 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 071d15c44116..2a345f28b9ae 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1585,6 +1585,7 @@ composite_resume(struct usb_gadget *gadget) /*-------------------------------------------------------------------------*/ static struct usb_gadget_driver composite_driver = { + .bind = composite_bind, .unbind = composite_unbind, .setup = composite_setup, @@ -1631,7 +1632,7 @@ int usb_composite_probe(struct usb_composite_driver *driver) composite_driver.max_speed = driver->max_speed; composite = driver; - return usb_gadget_probe_driver(&composite_driver, composite_bind); + return usb_gadget_probe_driver(&composite_driver); } /** diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/dbgp.c index 2d2cff3e454c..df9a0104bdf2 100644 --- a/drivers/usb/gadget/dbgp.c +++ b/drivers/usb/gadget/dbgp.c @@ -405,6 +405,7 @@ fail: static __refdata struct usb_gadget_driver dbgp_driver = { .function = "dbgp", .max_speed = USB_SPEED_HIGH, + .bind = dbgp_bind, .unbind = dbgp_unbind, .setup = dbgp_setup, .disconnect = dbgp_disconnect, @@ -416,7 +417,7 @@ static __refdata struct usb_gadget_driver dbgp_driver = { static int __init dbgp_init(void) { - return usb_gadget_probe_driver(&dbgp_driver, dbgp_bind); + return usb_gadget_probe_driver(&dbgp_driver); } static void __exit dbgp_exit(void) diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 683234bcdced..ce186fa7e7b2 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -3606,6 +3606,7 @@ static void fsg_resume(struct usb_gadget *gadget) static __refdata struct usb_gadget_driver fsg_driver = { .max_speed = USB_SPEED_SUPER, .function = (char *) fsg_string_product, + .bind = fsg_bind, .unbind = fsg_unbind, .disconnect = fsg_disconnect, .setup = fsg_setup, @@ -3653,7 +3654,8 @@ static int __init fsg_init(void) if ((rc = fsg_alloc()) != 0) return rc; fsg = the_fsg; - if ((rc = usb_gadget_probe_driver(&fsg_driver, fsg_bind)) != 0) + rc = usb_gadget_probe_driver(&fsg_driver); + if (rc != 0) kref_put(&fsg->ref, fsg_release); return rc; } diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index ae13a106fb96..2521804ba4ee 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -1758,6 +1758,7 @@ gadgetfs_suspend (struct usb_gadget *gadget) static struct usb_gadget_driver gadgetfs_driver = { .function = (char *) driver_desc, + .bind = gadgetfs_bind, .unbind = gadgetfs_unbind, .setup = gadgetfs_setup, .disconnect = gadgetfs_disconnect, @@ -1780,6 +1781,7 @@ static int gadgetfs_probe (struct usb_gadget *gadget) static struct usb_gadget_driver probe_driver = { .max_speed = USB_SPEED_HIGH, + .bind = gadgetfs_probe, .unbind = gadgetfs_nop, .setup = (void *)gadgetfs_nop, .disconnect = gadgetfs_nop, @@ -1893,7 +1895,8 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) gadgetfs_driver.max_speed = USB_SPEED_HIGH; else gadgetfs_driver.max_speed = USB_SPEED_FULL; - value = usb_gadget_probe_driver(&gadgetfs_driver, gadgetfs_bind); + + value = usb_gadget_probe_driver(&gadgetfs_driver); if (value != 0) { kfree (dev->buf); dev->buf = NULL; @@ -2032,7 +2035,7 @@ gadgetfs_fill_super (struct super_block *sb, void *opts, int silent) return -ESRCH; /* fake probe to determine $CHIP */ - (void) usb_gadget_probe_driver(&probe_driver, gadgetfs_probe); + usb_gadget_probe_driver(&probe_driver); if (!CHIP) return -ENODEV; diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index bae243c23141..85e1e2fdd403 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -311,13 +311,12 @@ EXPORT_SYMBOL_GPL(usb_del_gadget_udc); /* ------------------------------------------------------------------------- */ -int usb_gadget_probe_driver(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) +int usb_gadget_probe_driver(struct usb_gadget_driver *driver) { struct usb_udc *udc = NULL; int ret; - if (!driver || !bind || !driver->setup) + if (!driver || !driver->bind || !driver->setup) return -EINVAL; mutex_lock(&udc_lock); @@ -339,7 +338,7 @@ found: udc->dev.driver = &driver->driver; if (udc_is_newstyle(udc)) { - ret = bind(udc->gadget); + ret = driver->bind(udc->gadget); if (ret) goto err1; ret = usb_gadget_udc_start(udc->gadget, driver); @@ -350,7 +349,7 @@ found: usb_gadget_connect(udc->gadget); } else { - ret = usb_gadget_start(udc->gadget, driver, bind); + ret = usb_gadget_start(udc->gadget, driver, driver->bind); if (ret) goto err1; diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index d05b220f0fd3..9eb4e712168f 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -766,6 +766,7 @@ static inline int usb_gadget_disconnect(struct usb_gadget *gadget) * when the host is disconnected. May be called in_interrupt; this * may not sleep. Some devices can't detect disconnect, so this might * not be called except as part of controller shutdown. + * @bind: the driver's bind callback * @unbind: Invoked when the driver is unbound from a gadget, * usually from rmmod (after a disconnect is reported). * Called in a context that permits sleeping. @@ -820,6 +821,7 @@ static inline int usb_gadget_disconnect(struct usb_gadget *gadget) struct usb_gadget_driver { char *function; enum usb_device_speed max_speed; + int (*bind)(struct usb_gadget *gadget); void (*unbind)(struct usb_gadget *); int (*setup)(struct usb_gadget *, const struct usb_ctrlrequest *); @@ -845,7 +847,6 @@ struct usb_gadget_driver { /** * usb_gadget_probe_driver - probe a gadget driver * @driver: the driver being registered - * @bind: the driver's bind callback * Context: can sleep * * Call this in your gadget driver's module initialization function, @@ -854,8 +855,7 @@ struct usb_gadget_driver { * registration call returns. It's expected that the @bind() function will * be in init sections. */ -int usb_gadget_probe_driver(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)); +int usb_gadget_probe_driver(struct usb_gadget_driver *driver); /** * usb_gadget_unregister_driver - unregister a gadget driver -- cgit v1.2.3 From ffe0b335062505a98d7296dae2c2a197713f87e0 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 7 Sep 2012 09:53:17 +0200 Subject: usb: gadget: remove global variable composite in composite.c This patch removes the global variable composite in composite.c. The private data which was saved there is now passed via an additional argument to the bind() function in struct usb_gadget_driver. Only the "old-style" UDC drivers have to be touched here, new style are doing it right because this change is made in udc-core. Acked-by: Michal Nazarewicz Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/staging/ccg/composite.c | 3 ++- drivers/usb/gadget/amd5536udc.c | 6 ++--- drivers/usb/gadget/composite.c | 52 ++++++++++++++++++++++----------------- drivers/usb/gadget/dbgp.c | 3 ++- drivers/usb/gadget/file_storage.c | 3 ++- drivers/usb/gadget/fsl_udc_core.c | 6 ++--- drivers/usb/gadget/fusb300_udc.c | 4 +-- drivers/usb/gadget/goku_udc.c | 6 ++--- drivers/usb/gadget/inode.c | 7 +++--- drivers/usb/gadget/m66592-udc.c | 4 +-- drivers/usb/gadget/mv_udc_core.c | 6 ++--- drivers/usb/gadget/omap_udc.c | 6 ++--- drivers/usb/gadget/pch_udc.c | 6 ++--- drivers/usb/gadget/pxa25x_udc.c | 6 ++--- drivers/usb/gadget/pxa27x_udc.c | 6 ++--- drivers/usb/gadget/s3c2410_udc.c | 6 ++--- drivers/usb/gadget/udc-core.c | 4 +-- include/linux/usb/composite.h | 1 + include/linux/usb/gadget.h | 6 +++-- 19 files changed, 78 insertions(+), 63 deletions(-) (limited to 'include') diff --git a/drivers/staging/ccg/composite.c b/drivers/staging/ccg/composite.c index 2a345f28b9ae..228b4574f228 100644 --- a/drivers/staging/ccg/composite.c +++ b/drivers/staging/ccg/composite.c @@ -1422,7 +1422,8 @@ static u8 override_id(struct usb_composite_dev *cdev, u8 *desc) return *desc; } -static int composite_bind(struct usb_gadget *gadget) +static int composite_bind(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { struct usb_composite_dev *cdev; int status = -ENOMEM; diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index 187d21181cd5..fc0ec5e0d58e 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -1401,7 +1401,7 @@ static int udc_wakeup(struct usb_gadget *gadget) } static int amd5536_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)); + int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)); static int amd5536_stop(struct usb_gadget_driver *driver); /* gadget operations */ static const struct usb_gadget_ops udc_ops = { @@ -1914,7 +1914,7 @@ static int setup_ep0(struct udc *dev) /* Called by gadget driver to register itself */ static int amd5536_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) + int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)) { struct udc *dev = udc; int retval; @@ -1932,7 +1932,7 @@ static int amd5536_start(struct usb_gadget_driver *driver, dev->driver = driver; dev->gadget.dev.driver = &driver->driver; - retval = bind(&dev->gadget); + retval = bind(&dev->gadget, driver); /* Some gadget drivers use both ep0 directions. * NOTE: to gadget driver, ep0 is just one endpoint... diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 2a345f28b9ae..0b6ee2012af1 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -31,8 +31,6 @@ /* big enough to hold our biggest descriptor */ #define USB_BUFSIZ 1024 -static struct usb_composite_driver *composite; - /* Some systems will need runtime overrides for the product identifiers * published in the device descriptor, either numbers or strings or both. * String parameters are in UTF-8 (superset of ASCII's 7 bit characters). @@ -889,6 +887,7 @@ static int lookup_string( static int get_string(struct usb_composite_dev *cdev, void *buf, u16 language, int id) { + struct usb_composite_driver *composite = cdev->driver; struct usb_configuration *c; struct usb_function *f; int len; @@ -1359,8 +1358,8 @@ static void composite_disconnect(struct usb_gadget *gadget) spin_lock_irqsave(&cdev->lock, flags); if (cdev->config) reset_config(cdev); - if (composite->disconnect) - composite->disconnect(cdev); + if (cdev->driver->disconnect) + cdev->driver->disconnect(cdev); spin_unlock_irqrestore(&cdev->lock, flags); } @@ -1396,8 +1395,8 @@ composite_unbind(struct usb_gadget *gadget) struct usb_configuration, list); remove_config(cdev, c); } - if (composite->unbind) - composite->unbind(cdev); + if (cdev->driver->unbind) + cdev->driver->unbind(cdev); if (cdev->req) { kfree(cdev->req->buf); @@ -1406,7 +1405,6 @@ composite_unbind(struct usb_gadget *gadget) device_remove_file(&gadget->dev, &dev_attr_suspended); kfree(cdev); set_gadget_data(gadget, NULL); - composite = NULL; } static u8 override_id(struct usb_composite_dev *cdev, u8 *desc) @@ -1422,9 +1420,16 @@ static u8 override_id(struct usb_composite_dev *cdev, u8 *desc) return *desc; } -static int composite_bind(struct usb_gadget *gadget) +static struct usb_composite_driver *to_cdriver(struct usb_gadget_driver *gdrv) +{ + return container_of(gdrv, struct usb_composite_driver, gadget_driver); +} + +static int composite_bind(struct usb_gadget *gadget, + struct usb_gadget_driver *gdriver) { struct usb_composite_dev *cdev; + struct usb_composite_driver *composite = to_cdriver(gdriver); int status = -ENOMEM; cdev = kzalloc(sizeof *cdev, GFP_KERNEL); @@ -1546,8 +1551,8 @@ composite_suspend(struct usb_gadget *gadget) f->suspend(f); } } - if (composite->suspend) - composite->suspend(cdev); + if (cdev->driver->suspend) + cdev->driver->suspend(cdev); cdev->suspended = 1; @@ -1565,8 +1570,8 @@ composite_resume(struct usb_gadget *gadget) * suspend/resume callbacks? */ DBG(cdev, "resume\n"); - if (composite->resume) - composite->resume(cdev); + if (cdev->driver->resume) + cdev->driver->resume(cdev); if (cdev->config) { list_for_each_entry(f, &cdev->config->functions, list) { if (f->resume) @@ -1584,7 +1589,7 @@ composite_resume(struct usb_gadget *gadget) /*-------------------------------------------------------------------------*/ -static struct usb_gadget_driver composite_driver = { +static const struct usb_gadget_driver composite_driver_template = { .bind = composite_bind, .unbind = composite_unbind, @@ -1620,19 +1625,24 @@ static struct usb_gadget_driver composite_driver = { */ int usb_composite_probe(struct usb_composite_driver *driver) { - if (!driver || !driver->dev || composite || !driver->bind) + struct usb_gadget_driver *gadget_driver; + + if (!driver || !driver->dev || !driver->bind) return -EINVAL; if (!driver->name) driver->name = "composite"; if (!driver->iProduct) driver->iProduct = driver->name; - composite_driver.function = (char *) driver->name; - composite_driver.driver.name = driver->name; - composite_driver.max_speed = driver->max_speed; - composite = driver; - return usb_gadget_probe_driver(&composite_driver); + driver->gadget_driver = composite_driver_template; + gadget_driver = &driver->gadget_driver; + + gadget_driver->function = (char *) driver->name; + gadget_driver->driver.name = driver->name; + gadget_driver->max_speed = driver->max_speed; + + return usb_gadget_probe_driver(gadget_driver); } /** @@ -1644,9 +1654,7 @@ int usb_composite_probe(struct usb_composite_driver *driver) */ void usb_composite_unregister(struct usb_composite_driver *driver) { - if (composite != driver) - return; - usb_gadget_unregister_driver(&composite_driver); + usb_gadget_unregister_driver(&driver->gadget_driver); } /** diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/dbgp.c index df9a0104bdf2..cc1746597aab 100644 --- a/drivers/usb/gadget/dbgp.c +++ b/drivers/usb/gadget/dbgp.c @@ -292,7 +292,8 @@ fail_1: return -ENODEV; } -static int __init dbgp_bind(struct usb_gadget *gadget) +static int __init dbgp_bind(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { int err, stp; diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index ce186fa7e7b2..cdacae706b70 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -3331,7 +3331,8 @@ static int __init check_parameters(struct fsg_dev *fsg) } -static int __init fsg_bind(struct usb_gadget *gadget) +static int __init fsg_bind(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { struct fsg_dev *fsg = the_fsg; int rc; diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 3def828f85e7..6ae70cba0c4a 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -1255,7 +1255,7 @@ static int fsl_pullup(struct usb_gadget *gadget, int is_on) } static int fsl_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)); + int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)); static int fsl_stop(struct usb_gadget_driver *driver); /* defined in gadget.h */ static struct usb_gadget_ops fsl_gadget_ops = { @@ -1951,7 +1951,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) * Called by initialization code of gadget drivers *----------------------------------------------------------------*/ static int fsl_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) + int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)) { int retval = -ENODEV; unsigned long flags = 0; @@ -1976,7 +1976,7 @@ static int fsl_start(struct usb_gadget_driver *driver, spin_unlock_irqrestore(&udc_controller->lock, flags); /* bind udc driver to gadget driver */ - retval = bind(&udc_controller->gadget); + retval = bind(&udc_controller->gadget, driver); if (retval) { VDBG("bind to %s --> %d", driver->driver.name, retval); udc_controller->gadget.dev.driver = NULL; diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c index cdd94540e1cd..72cd5e6719db 100644 --- a/drivers/usb/gadget/fusb300_udc.c +++ b/drivers/usb/gadget/fusb300_udc.c @@ -1311,7 +1311,7 @@ static void init_controller(struct fusb300 *fusb300) static struct fusb300 *the_controller; static int fusb300_udc_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) + int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)) { struct fusb300 *fusb300 = the_controller; int retval; @@ -1339,7 +1339,7 @@ static int fusb300_udc_start(struct usb_gadget_driver *driver, goto error; } - retval = bind(&fusb300->gadget); + retval = bind(&fusb300->gadget, driver); if (retval) { pr_err("bind to driver error (%d)\n", retval); device_del(&fusb300->gadget.dev); diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index 9fd7886cfa9a..51037cb78604 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -994,7 +994,7 @@ static int goku_get_frame(struct usb_gadget *_gadget) } static int goku_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)); + int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)); static int goku_stop(struct usb_gadget_driver *driver); static const struct usb_gadget_ops goku_ops = { @@ -1348,7 +1348,7 @@ static struct goku_udc *the_controller; * the driver might get unbound. */ static int goku_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) + int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)) { struct goku_udc *dev = the_controller; int retval; @@ -1368,7 +1368,7 @@ static int goku_start(struct usb_gadget_driver *driver, driver->driver.bus = NULL; dev->driver = driver; dev->gadget.dev.driver = &driver->driver; - retval = bind(&dev->gadget); + retval = bind(&dev->gadget, driver); if (retval) { DBG(dev, "bind to driver %s --> error %d\n", driver->driver.name, retval); diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index 2521804ba4ee..4bb6d53f2de3 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -1679,8 +1679,8 @@ gadgetfs_unbind (struct usb_gadget *gadget) static struct dev_data *the_device; -static int -gadgetfs_bind (struct usb_gadget *gadget) +static int gadgetfs_bind(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { struct dev_data *dev = the_device; @@ -1773,7 +1773,8 @@ static struct usb_gadget_driver gadgetfs_driver = { static void gadgetfs_nop(struct usb_gadget *arg) { } -static int gadgetfs_probe (struct usb_gadget *gadget) +static int gadgetfs_probe(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { CHIP = gadget->name; return -EISNAM; diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index cf6bd626f3fe..b6401f1b56ce 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -1466,7 +1466,7 @@ static struct usb_ep_ops m66592_ep_ops = { static struct m66592 *the_controller; static int m66592_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) + int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)) { struct m66592 *m66592 = the_controller; int retval; @@ -1492,7 +1492,7 @@ static int m66592_start(struct usb_gadget_driver *driver, goto error; } - retval = bind(&m66592->gadget); + retval = bind(&m66592->gadget, driver); if (retval) { pr_err("bind to driver error (%d)\n", retval); device_del(&m66592->gadget.dev); diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index ad91de4d60d8..ea45224f78c8 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -1269,7 +1269,7 @@ static int mv_udc_pullup(struct usb_gadget *gadget, int is_on) } static int mv_udc_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)); + int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)); static int mv_udc_stop(struct usb_gadget_driver *driver); /* device controller usb_gadget_ops structure */ static const struct usb_gadget_ops mv_ops = { @@ -1374,7 +1374,7 @@ static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver) } static int mv_udc_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) + int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)) { struct mv_udc *udc = the_controller; int retval = 0; @@ -1399,7 +1399,7 @@ static int mv_udc_start(struct usb_gadget_driver *driver, spin_unlock_irqrestore(&udc->lock, flags); - retval = bind(&udc->gadget); + retval = bind(&udc->gadget, driver); if (retval) { dev_err(&udc->dev->dev, "bind to driver %s --> %d\n", driver->driver.name, retval); diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index f9132ada53b5..2a4749c3eb3f 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -1308,7 +1308,7 @@ static int omap_pullup(struct usb_gadget *gadget, int is_on) } static int omap_udc_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)); + int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)); static int omap_udc_stop(struct usb_gadget_driver *driver); static struct usb_gadget_ops omap_gadget_ops = { @@ -2040,7 +2040,7 @@ static inline int machine_without_vbus_sense(void) } static int omap_udc_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) + int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)) { int status = -ENODEV; struct omap_ep *ep; @@ -2082,7 +2082,7 @@ static int omap_udc_start(struct usb_gadget_driver *driver, if (udc->dc_clk != NULL) omap_udc_enable_clock(1); - status = bind(&udc->gadget); + status = bind(&udc->gadget, driver); if (status) { DBG("bind to %s --> %d\n", driver->driver.name, status); udc->gadget.dev.driver = NULL; diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index f4fb71c9ae08..6490c0040e3a 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -1236,7 +1236,7 @@ static int pch_udc_pcd_vbus_draw(struct usb_gadget *gadget, unsigned int mA) } static int pch_udc_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)); + int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)); static int pch_udc_stop(struct usb_gadget_driver *driver); static const struct usb_gadget_ops pch_udc_ops = { .get_frame = pch_udc_pcd_get_frame, @@ -2982,7 +2982,7 @@ static int init_dma_pools(struct pch_udc_dev *dev) } static int pch_udc_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) + int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)) { struct pch_udc_dev *dev = pch_udc; int retval; @@ -3006,7 +3006,7 @@ static int pch_udc_start(struct usb_gadget_driver *driver, dev->gadget.dev.driver = &driver->driver; /* Invoke the bind routine of the gadget driver */ - retval = bind(&dev->gadget); + retval = bind(&dev->gadget, driver); if (retval) { dev_err(&dev->pdev->dev, "%s: binding to %s returning %d\n", diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index c246c08a242d..8efbf08c3561 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -999,7 +999,7 @@ static int pxa25x_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA) } static int pxa25x_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)); + int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)); static int pxa25x_stop(struct usb_gadget_driver *driver); static const struct usb_gadget_ops pxa25x_udc_ops = { @@ -1257,7 +1257,7 @@ static void udc_enable (struct pxa25x_udc *dev) * the driver might get unbound. */ static int pxa25x_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) + int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)) { struct pxa25x_udc *dev = the_controller; int retval; @@ -1285,7 +1285,7 @@ fail: dev->gadget.dev.driver = NULL; return retval; } - retval = bind(&dev->gadget); + retval = bind(&dev->gadget, driver); if (retval) { DMSG("bind to driver %s --> error %d\n", driver->driver.name, retval); diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 644b4305cb99..979ddaddb0f8 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -1672,7 +1672,7 @@ static int pxa_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA) } static int pxa27x_udc_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)); + int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)); static int pxa27x_udc_stop(struct usb_gadget_driver *driver); static const struct usb_gadget_ops pxa_udc_ops = { @@ -1803,7 +1803,7 @@ static void udc_enable(struct pxa_udc *udc) * Returns 0 if no error, -EINVAL, -ENODEV, -EBUSY otherwise */ static int pxa27x_udc_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) + int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)) { struct pxa_udc *udc = the_controller; int retval; @@ -1826,7 +1826,7 @@ static int pxa27x_udc_start(struct usb_gadget_driver *driver, dev_err(udc->dev, "device_add error %d\n", retval); goto add_fail; } - retval = bind(&udc->gadget); + retval = bind(&udc->gadget, driver); if (retval) { dev_err(udc->dev, "bind to driver %s --> error %d\n", driver->driver.name, retval); diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index b627c9b51fda..c33e942d119c 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -1539,7 +1539,7 @@ static int s3c2410_vbus_draw(struct usb_gadget *_gadget, unsigned ma) } static int s3c2410_udc_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)); + int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)); static int s3c2410_udc_stop(struct usb_gadget_driver *driver); static const struct usb_gadget_ops s3c2410_ops = { @@ -1665,7 +1665,7 @@ static void s3c2410_udc_enable(struct s3c2410_udc *dev) } static int s3c2410_udc_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) + int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)) { struct s3c2410_udc *udc = the_controller; int retval; @@ -1705,7 +1705,7 @@ static int s3c2410_udc_start(struct usb_gadget_driver *driver, dprintk(DEBUG_NORMAL, "binding gadget driver '%s'\n", driver->driver.name); - retval = bind(&udc->gadget); + retval = bind(&udc->gadget, driver); if (retval) { device_del(&udc->gadget.dev); goto register_error; diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 85e1e2fdd403..f3cd9690b101 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -118,7 +118,7 @@ EXPORT_SYMBOL_GPL(usb_gadget_unmap_request); */ static inline int usb_gadget_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) + int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)) { return gadget->ops->start(driver, bind); } @@ -338,7 +338,7 @@ found: udc->dev.driver = &driver->driver; if (udc_is_newstyle(udc)) { - ret = driver->bind(udc->gadget); + ret = driver->bind(udc->gadget, driver); if (ret) goto err1; ret = usb_gadget_udc_start(udc->gadget, driver); diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 19a5adf18bf4..43d6b9ca51b7 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -303,6 +303,7 @@ struct usb_composite_driver { /* global suspend hooks */ void (*suspend)(struct usb_composite_dev *); void (*resume)(struct usb_composite_dev *); + struct usb_gadget_driver gadget_driver; }; extern int usb_composite_probe(struct usb_composite_driver *driver); diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 9eb4e712168f..822c1b88f95a 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -474,7 +474,8 @@ struct usb_gadget_ops { /* Those two are deprecated */ int (*start)(struct usb_gadget_driver *, - int (*bind)(struct usb_gadget *)); + int (*bind)(struct usb_gadget *, + struct usb_gadget_driver *driver)); int (*stop)(struct usb_gadget_driver *); }; @@ -821,7 +822,8 @@ static inline int usb_gadget_disconnect(struct usb_gadget *gadget) struct usb_gadget_driver { char *function; enum usb_device_speed max_speed; - int (*bind)(struct usb_gadget *gadget); + int (*bind)(struct usb_gadget *gadget, + struct usb_gadget_driver *driver); void (*unbind)(struct usb_gadget *); int (*setup)(struct usb_gadget *, const struct usb_ctrlrequest *); -- cgit v1.2.3 From e87bb7118c4f752de4616a7ab56c51ed3e7f6f53 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 6 Sep 2012 20:11:11 +0200 Subject: usb: gadget: move global vars from epautoconf into struct usb_gadget epautoconf has two global variables which count the endpoint number of last assigned endpoint. This patch removes the global variable and keeps it as per (UDC) gadget. While here, the ifdef is removed and now the in and outpoint are enumerated unconditionally. The dwc3 for instance supports 32 endpoints in total. Acked-by: Michal Nazarewicz Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/gadget/epautoconf.c | 27 ++++++--------------------- include/linux/usb/gadget.h | 4 ++++ 2 files changed, 10 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 51f3d42f5a64..d5a905dbed00 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -22,17 +22,6 @@ #include "gadget_chips.h" - -/* we must assign addresses for configurable endpoints (like net2280) */ -static unsigned epnum; - -// #define MANY_ENDPOINTS -#ifdef MANY_ENDPOINTS -/* more than 15 configurable endpoints */ -static unsigned in_epnum; -#endif - - /* * This should work with endpoints from controller drivers sharing the * same endpoint naming convention. By example: @@ -176,16 +165,14 @@ ep_matches ( if (isdigit (ep->name [2])) { u8 num = simple_strtoul (&ep->name [2], NULL, 10); desc->bEndpointAddress |= num; -#ifdef MANY_ENDPOINTS } else if (desc->bEndpointAddress & USB_DIR_IN) { - if (++in_epnum > 15) + if (++gadget->in_epnum > 15) return 0; - desc->bEndpointAddress = USB_DIR_IN | in_epnum; -#endif + desc->bEndpointAddress = USB_DIR_IN | gadget->in_epnum; } else { - if (++epnum > 15) + if (++gadget->out_epnum > 15) return 0; - desc->bEndpointAddress |= epnum; + desc->bEndpointAddress |= gadget->out_epnum; } /* report (variable) full speed bulk maxpacket */ @@ -385,9 +372,7 @@ void usb_ep_autoconfig_reset (struct usb_gadget *gadget) list_for_each_entry (ep, &gadget->ep_list, ep_list) { ep->driver_data = NULL; } -#ifdef MANY_ENDPOINTS - in_epnum = 0; -#endif - epnum = 0; + gadget->in_epnum = 0; + gadget->out_epnum = 0; } diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 822c1b88f95a..5b6e50888248 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -503,6 +503,8 @@ struct usb_gadget_ops { * @name: Identifies the controller hardware type. Used in diagnostics * and sometimes configuration. * @dev: Driver model state for this abstract device. + * @out_epnum: last used out ep number + * @in_epnum: last used in ep number * * Gadgets have a mostly-portable "gadget driver" implementing device * functions, handling all usb configurations and interfaces. Gadget @@ -537,6 +539,8 @@ struct usb_gadget { unsigned a_alt_hnp_support:1; const char *name; struct device dev; + unsigned out_epnum; + unsigned in_epnum; }; static inline void set_gadget_data(struct usb_gadget *gadget, void *data) -- cgit v1.2.3 From e13f17ff8854e04cfc6b9f981a974f55d8da9b92 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 10 Sep 2012 15:01:51 +0200 Subject: usb: gadget: move USB_BUFSIZ into global composite.h This patch moves USB_BUFSIZ into global header file as USB_COMP_EP0_BUFSIZ. There is currently only one user (f_sourcesink) besides composite which need it. Ideally f_sourcesink would have its own ep0 buffer. Lets keep it that way it was for now. Acked-by: Michal Nazarewicz Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/gadget/composite.c | 10 ++++------ drivers/usb/gadget/f_sourcesink.c | 2 +- include/linux/usb/composite.h | 3 +++ 3 files changed, 8 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 0b6ee2012af1..52689ee9db12 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -28,9 +28,6 @@ * with the relevant device-wide data. */ -/* big enough to hold our biggest descriptor */ -#define USB_BUFSIZ 1024 - /* Some systems will need runtime overrides for the product identifiers * published in the device descriptor, either numbers or strings or both. * String parameters are in UTF-8 (superset of ASCII's 7 bit characters). @@ -355,10 +352,11 @@ static int config_buf(struct usb_configuration *config, { struct usb_config_descriptor *c = buf; void *next = buf + USB_DT_CONFIG_SIZE; - int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE; + int len; struct usb_function *f; int status; + len = USB_COMP_EP0_BUFSIZ - USB_DT_CONFIG_SIZE; /* write the config descriptor */ c = buf; c->bLength = USB_DT_CONFIG_SIZE; @@ -1445,13 +1443,13 @@ static int composite_bind(struct usb_gadget *gadget, cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); if (!cdev->req) goto fail; - cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL); + cdev->req->buf = kmalloc(USB_COMP_EP0_BUFSIZ, GFP_KERNEL); if (!cdev->req->buf) goto fail; cdev->req->complete = composite_setup_complete; gadget->ep0->driver_data = cdev; - cdev->bufsiz = USB_BUFSIZ; + cdev->bufsiz = USB_COMP_EP0_BUFSIZ; cdev->driver = composite; /* diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c index 5c1b68b63c98..3c126fde6e7e 100644 --- a/drivers/usb/gadget/f_sourcesink.c +++ b/drivers/usb/gadget/f_sourcesink.c @@ -795,7 +795,7 @@ static int sourcesink_setup(struct usb_configuration *c, u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); - req->length = USB_BUFSIZ; + req->length = USB_COMP_EP0_BUFSIZ; /* composite driver infrastructure handles everything except * the two control test requests. diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 43d6b9ca51b7..89d91b671eb0 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -46,6 +46,9 @@ */ #define USB_GADGET_DELAYED_STATUS 0x7fff /* Impossibly large value */ +/* big enough to hold our biggest descriptor */ +#define USB_COMP_EP0_BUFSIZ 1024 + struct usb_configuration; /** -- cgit v1.2.3 From 40bfef0535e8a10486c9d81fd2f55e3b9dc71db6 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 10 Sep 2012 15:01:52 +0200 Subject: usb: gadget: remove bufsiz from struct usb_composite_dev there is no read user of bufsiz, its content is available via USB_COMP_EP0_BUFSIZ. Remove it. Acked-by: Michal Nazarewicz Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/gadget/composite.c | 1 - include/linux/usb/composite.h | 2 -- 2 files changed, 3 deletions(-) (limited to 'include') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 52689ee9db12..16b353fa160a 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1449,7 +1449,6 @@ static int composite_bind(struct usb_gadget *gadget, cdev->req->complete = composite_setup_complete; gadget->ep0->driver_data = cdev; - cdev->bufsiz = USB_COMP_EP0_BUFSIZ; cdev->driver = composite; /* diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 89d91b671eb0..e970fba6dbbb 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -318,7 +318,6 @@ extern void usb_composite_setup_continue(struct usb_composite_dev *cdev); * struct usb_composite_device - represents one composite usb gadget * @gadget: read-only, abstracts the gadget's usb peripheral controller * @req: used for control responses; buffer is pre-allocated - * @bufsiz: size of buffer pre-allocated in @req * @config: the currently active configuration * * One of these devices is allocated and initialized before the @@ -349,7 +348,6 @@ extern void usb_composite_setup_continue(struct usb_composite_dev *cdev); struct usb_composite_dev { struct usb_gadget *gadget; struct usb_request *req; - unsigned bufsiz; struct usb_configuration *config; -- cgit v1.2.3 From 7d16e8d3eb704f5f6eb5a271d5758b495634e8e6 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 10 Sep 2012 15:01:53 +0200 Subject: usb: gadget: push VID/PID/USB BCD module option into gadgets This patch moves the module options idVendor, idProduct and bcdDevice from composite.c into each gadgets. This ensures compatibility with current gadgets and removes the global variable which brings me step closer towards composite.c in libcomposite Acked-by: Michal Nazarewicz Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/gadget/acm_ms.c | 2 ++ drivers/usb/gadget/audio.c | 3 ++ drivers/usb/gadget/cdc2.c | 2 ++ drivers/usb/gadget/composite.c | 63 +++++++++++++++++++++++-------------- drivers/usb/gadget/ether.c | 2 ++ drivers/usb/gadget/g_ffs.c | 4 ++- drivers/usb/gadget/gmidi.c | 4 ++- drivers/usb/gadget/hid.c | 2 ++ drivers/usb/gadget/mass_storage.c | 3 +- drivers/usb/gadget/multi.c | 3 +- drivers/usb/gadget/ncm.c | 2 ++ drivers/usb/gadget/nokia.c | 2 ++ drivers/usb/gadget/printer.c | 4 +++ drivers/usb/gadget/serial.c | 2 ++ drivers/usb/gadget/tcm_usb_gadget.c | 5 +++ drivers/usb/gadget/webcam.c | 2 ++ drivers/usb/gadget/zero.c | 2 ++ include/linux/usb/composite.h | 24 ++++++++++++++ 18 files changed, 103 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/acm_ms.c index ea349bab7f15..7bd6b71af5d3 100644 --- a/drivers/usb/gadget/acm_ms.c +++ b/drivers/usb/gadget/acm_ms.c @@ -47,6 +47,7 @@ #include "f_mass_storage.c" /*-------------------------------------------------------------------------*/ +USB_GADGET_COMPOSITE_OPTIONS(); static struct usb_device_descriptor device_desc = { .bLength = sizeof device_desc, @@ -203,6 +204,7 @@ static int __init acm_ms_bind(struct usb_composite_dev *cdev) if (status < 0) goto fail1; + usb_composite_overwrite_options(cdev, &coverwrite); dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n", DRIVER_DESC); fsg_common_put(&fsg_common); diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c index 5702ce3777fb..55b593c5a9c8 100644 --- a/drivers/usb/gadget/audio.c +++ b/drivers/usb/gadget/audio.c @@ -13,6 +13,7 @@ #include #include +#include #include "gadget_chips.h" #define DRIVER_DESC "Linux USB Audio Gadget" @@ -28,6 +29,7 @@ * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ #include "composite.c" +USB_GADGET_COMPOSITE_OPTIONS(); /* string IDs are assigned dynamically */ @@ -174,6 +176,7 @@ static int __init audio_bind(struct usb_composite_dev *cdev) status = usb_add_config(cdev, &audio_config_driver, audio_do_config); if (status < 0) goto fail; + usb_composite_overwrite_options(cdev, &coverwrite); INFO(cdev, "%s, version: %s\n", DRIVER_DESC, DRIVER_VERSION); return 0; diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index 3b89fe2bd0b8..93a75809b4de 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -34,6 +34,7 @@ #define CDC_PRODUCT_NUM 0xa4aa /* CDC Composite: ECM + ACM */ /*-------------------------------------------------------------------------*/ +USB_GADGET_COMPOSITE_OPTIONS(); /* * Kbuild is not very cooperative with respect to linking separately @@ -204,6 +205,7 @@ static int __init cdc_bind(struct usb_composite_dev *cdev) if (status < 0) goto fail1; + usb_composite_overwrite_options(cdev, &coverwrite); dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n", DRIVER_DESC); diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 8d81a5ccfa03..317a5ece3bd2 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -32,19 +32,6 @@ * published in the device descriptor, either numbers or strings or both. * String parameters are in UTF-8 (superset of ASCII's 7 bit characters). */ - -static ushort idVendor; -module_param(idVendor, ushort, S_IRUGO); -MODULE_PARM_DESC(idVendor, "USB Vendor ID"); - -static ushort idProduct; -module_param(idProduct, ushort, S_IRUGO); -MODULE_PARM_DESC(idProduct, "USB Product ID"); - -static ushort bcdDevice; -module_param(bcdDevice, ushort, S_IRUGO); -MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)"); - static char *iManufacturer; module_param(iManufacturer, charp, S_IRUGO); MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); @@ -1418,6 +1405,30 @@ static u8 override_id(struct usb_composite_dev *cdev, u8 *desc) return *desc; } +static void update_unchanged_dev_desc(struct usb_device_descriptor *new, + const struct usb_device_descriptor *old) +{ + __le16 idVendor; + __le16 idProduct; + __le16 bcdDevice; + + /* + * these variables may have been set in + * usb_composite_overwrite_options() + */ + idVendor = new->idVendor; + idProduct = new->idProduct; + bcdDevice = new->bcdDevice; + + *new = *old; + if (idVendor) + new->idVendor = idVendor; + if (idProduct) + new->idProduct = idProduct; + if (bcdDevice) + new->bcdDevice = bcdDevice; +} + static struct usb_composite_driver *to_cdriver(struct usb_gadget_driver *gdrv) { return container_of(gdrv, struct usb_composite_driver, gadget_driver); @@ -1473,17 +1484,7 @@ static int composite_bind(struct usb_gadget *gadget, if (status < 0) goto fail; - cdev->desc = *composite->dev; - - /* standardized runtime overrides for device ID data */ - if (idVendor) - cdev->desc.idVendor = cpu_to_le16(idVendor); - - if (idProduct) - cdev->desc.idProduct = cpu_to_le16(idProduct); - - if (bcdDevice) - cdev->desc.bcdDevice = cpu_to_le16(bcdDevice); + update_unchanged_dev_desc(&cdev->desc, composite->dev); /* string overrides */ if (iManufacturer || !cdev->desc.iManufacturer) { @@ -1686,3 +1687,17 @@ void usb_composite_setup_continue(struct usb_composite_dev *cdev) spin_unlock_irqrestore(&cdev->lock, flags); } +void usb_composite_overwrite_options(struct usb_composite_dev *cdev, + struct usb_composite_overwrite *covr) +{ + struct usb_device_descriptor *desc = &cdev->desc; + + if (covr->idVendor) + desc->idVendor = cpu_to_le16(covr->idVendor); + + if (covr->idProduct) + desc->idProduct = cpu_to_le16(covr->idProduct); + + if (covr->bcdDevice) + desc->bcdDevice = cpu_to_le16(covr->bcdDevice); +} diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 004c6ed79e3b..709ef2265596 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -114,6 +114,7 @@ static inline bool has_rndis(void) #include "u_ether.c" /*-------------------------------------------------------------------------*/ +USB_GADGET_COMPOSITE_OPTIONS(); /* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! * Instead: allocate your own, using normal USB-IF procedures. @@ -363,6 +364,7 @@ static int __init eth_bind(struct usb_composite_dev *cdev) if (status < 0) goto fail; + usb_composite_overwrite_options(cdev, &coverwrite); dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n", DRIVER_DESC); diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index c3a583ea9938..a44ed661c16b 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -73,6 +73,8 @@ struct gfs_ffs_obj { struct ffs_data *ffs_data; }; +USB_GADGET_COMPOSITE_OPTIONS(); + static struct usb_device_descriptor gfs_dev_desc = { .bLength = sizeof gfs_dev_desc, .bDescriptorType = USB_DT_DEVICE, @@ -377,7 +379,7 @@ static int gfs_bind(struct usb_composite_dev *cdev) if (unlikely(ret < 0)) goto error_unbind; } - + usb_composite_overwrite_options(cdev, &coverwrite); return 0; error_unbind: diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index 68f8c032ba6b..01b31e2d03af 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -48,6 +48,8 @@ MODULE_LICENSE("GPL v2"); static const char shortname[] = "g_midi"; static const char longname[] = "MIDI Gadget"; +USB_GADGET_COMPOSITE_OPTIONS(); + static int index = SNDRV_DEFAULT_IDX1; module_param(index, int, S_IRUGO); MODULE_PARM_DESC(index, "Index value for the USB MIDI Gadget adapter."); @@ -163,7 +165,7 @@ static int __init midi_bind(struct usb_composite_dev *cdev) status = usb_add_config(cdev, &midi_config, midi_bind_config); if (status < 0) return status; - + usb_composite_overwrite_options(cdev, &coverwrite); pr_info("%s\n", longname); return 0; } diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/hid.c index 1e3f03be94df..59fe7eef00da 100644 --- a/drivers/usb/gadget/hid.c +++ b/drivers/usb/gadget/hid.c @@ -48,6 +48,7 @@ struct hidg_func_node { static LIST_HEAD(hidg_func_list); /*-------------------------------------------------------------------------*/ +USB_GADGET_COMPOSITE_OPTIONS(); static struct usb_device_descriptor device_desc = { .bLength = sizeof device_desc, @@ -188,6 +189,7 @@ static int __init hid_bind(struct usb_composite_dev *cdev) if (status < 0) return status; + usb_composite_overwrite_options(cdev, &coverwrite); dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); return 0; diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c index 0b0f008427ed..8ffbade383f2 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/mass_storage.c @@ -52,6 +52,7 @@ #include "f_mass_storage.c" /*-------------------------------------------------------------------------*/ +USB_GADGET_COMPOSITE_OPTIONS(); static struct usb_device_descriptor msg_device_desc = { .bLength = sizeof msg_device_desc, @@ -143,7 +144,7 @@ static int __init msg_bind(struct usb_composite_dev *cdev) status = usb_add_config(cdev, &msg_config_driver, msg_do_config); if (status < 0) return status; - + usb_composite_overwrite_options(cdev, &coverwrite); dev_info(&cdev->gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); set_bit(0, &msg_registered); diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 72fb30141ff4..60168877330a 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -58,7 +58,7 @@ MODULE_LICENSE("GPL"); #endif #include "u_ether.c" - +USB_GADGET_COMPOSITE_OPTIONS(); /***************************** Device Descriptor ****************************/ @@ -307,6 +307,7 @@ static int __ref multi_bind(struct usb_composite_dev *cdev) status = cdc_config_register(cdev); if (unlikely(status < 0)) goto fail2; + usb_composite_overwrite_options(cdev, &coverwrite); /* we're done */ dev_info(&gadget->dev, DRIVER_DESC "\n"); diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c index 6c7e15984535..8537cd9c6aff 100644 --- a/drivers/usb/gadget/ncm.c +++ b/drivers/usb/gadget/ncm.c @@ -54,6 +54,7 @@ #define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */ /*-------------------------------------------------------------------------*/ +USB_GADGET_COMPOSITE_OPTIONS(); static struct usb_device_descriptor device_desc = { .bLength = sizeof device_desc, @@ -191,6 +192,7 @@ static int __init gncm_bind(struct usb_composite_dev *cdev) if (status < 0) goto fail; + usb_composite_overwrite_options(cdev, &coverwrite); dev_info(&gadget->dev, "%s\n", DRIVER_DESC); return 0; diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index b0893136a05d..5ed927b16c0e 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -49,6 +49,7 @@ #include "u_ether.c" /*-------------------------------------------------------------------------*/ +USB_GADGET_COMPOSITE_OPTIONS(); #define NOKIA_VENDOR_ID 0x0421 /* Nokia */ #define NOKIA_PRODUCT_ID 0x01c8 /* Nokia Gadget */ @@ -197,6 +198,7 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) if (status < 0) goto err_usb; + usb_composite_overwrite_options(cdev, &coverwrite); dev_info(&gadget->dev, "%s\n", NOKIA_LONG_NAME); return 0; diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index 51b6e7bf413e..3fa4d80629cc 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -54,6 +54,7 @@ #include "composite.c" /*-------------------------------------------------------------------------*/ +USB_GADGET_COMPOSITE_OPTIONS(); #define DRIVER_DESC "Printer Gadget" #define DRIVER_VERSION "2007 OCT 06" @@ -1265,6 +1266,9 @@ static int __init printer_bind(struct usb_composite_dev *cdev) device_desc.iSerialNumber = strings[STRING_SERIALNUM].id; ret = usb_add_config(cdev, &printer_cfg_driver, printer_bind_config); + if (ret) + return ret; + usb_composite_overwrite_options(cdev, &coverwrite); return ret; } diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index 9962504e14f1..27b5ce939bdb 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -45,6 +45,7 @@ #include "u_serial.c" /*-------------------------------------------------------------------------*/ +USB_GADGET_COMPOSITE_OPTIONS(); /* Thanks to NetChip Technologies for donating this product ID. * @@ -212,6 +213,7 @@ static int __init gs_bind(struct usb_composite_dev *cdev) if (status < 0) goto fail; + usb_composite_overwrite_options(cdev, &coverwrite); INFO(cdev, "%s\n", GS_VERSION_NAME); return 0; diff --git a/drivers/usb/gadget/tcm_usb_gadget.c b/drivers/usb/gadget/tcm_usb_gadget.c index 2f4980881a80..fa3137ef228b 100644 --- a/drivers/usb/gadget/tcm_usb_gadget.c +++ b/drivers/usb/gadget/tcm_usb_gadget.c @@ -29,6 +29,8 @@ #include "tcm_usb_gadget.h" +USB_GADGET_COMPOSITE_OPTIONS(); + static struct target_fabric_configfs *usbg_fabric_configfs; static inline struct f_uas *to_f_uas(struct usb_function *f) @@ -2437,6 +2439,9 @@ static int usb_target_bind(struct usb_composite_dev *cdev) ret = usb_add_config(cdev, &usbg_config_driver, usbg_cfg_bind); + if (ret) + return ret; + usb_composite_overwrite_options(cdev, &coverwrite); return 0; } diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/webcam.c index d44a4510a65a..306006419f38 100644 --- a/drivers/usb/gadget/webcam.c +++ b/drivers/usb/gadget/webcam.c @@ -30,6 +30,7 @@ #include "uvc_v4l2.c" #include "f_uvc.c" +USB_GADGET_COMPOSITE_OPTIONS(); /* -------------------------------------------------------------------------- * Device descriptor */ @@ -370,6 +371,7 @@ webcam_bind(struct usb_composite_dev *cdev) webcam_config_bind)) < 0) goto error; + usb_composite_overwrite_options(cdev, &coverwrite); INFO(cdev, "Webcam Video Gadget\n"); return 0; diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index dbc336e3ba98..2b31a4ae26b9 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -64,6 +64,7 @@ #include "f_loopback.c" /*-------------------------------------------------------------------------*/ +USB_GADGET_COMPOSITE_OPTIONS(); #define DRIVER_VERSION "Cinco de Mayo 2008" @@ -305,6 +306,7 @@ static int __init zero_bind(struct usb_composite_dev *cdev) longname, gadget->name); device_desc.bcdDevice = cpu_to_le16(0x9999); } + usb_composite_overwrite_options(cdev, &coverwrite); INFO(cdev, "%s, version: " DRIVER_VERSION "\n", longname); diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index e970fba6dbbb..7651e5bf7487 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -381,6 +381,30 @@ extern int usb_string_ids_tab(struct usb_composite_dev *c, struct usb_string *str); extern int usb_string_ids_n(struct usb_composite_dev *c, unsigned n); +/* + * Some systems will need runtime overrides for the product identifiers + * published in the device descriptor, either numbers or strings or both. + * String parameters are in UTF-8 (superset of ASCII's 7 bit characters). + */ +struct usb_composite_overwrite { + u16 idVendor; + u16 idProduct; + u16 bcdDevice; +}; +#define USB_GADGET_COMPOSITE_OPTIONS() \ + static struct usb_composite_overwrite coverwrite; \ + \ + module_param_named(idVendor, coverwrite.idVendor, ushort, S_IRUGO); \ + MODULE_PARM_DESC(idVendor, "USB Vendor ID"); \ + \ + module_param_named(idProduct, coverwrite.idProduct, ushort, S_IRUGO); \ + MODULE_PARM_DESC(idProduct, "USB Product ID"); \ + \ + module_param_named(bcdDevice, coverwrite.bcdDevice, ushort, S_IRUGO); \ + MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)") + +void usb_composite_overwrite_options(struct usb_composite_dev *cdev, + struct usb_composite_overwrite *covr); /* messaging utils */ #define DBG(d, fmt, args...) \ -- cgit v1.2.3 From 276e2e4f1f3e07a0ad891bf757dbcfd655ff5f91 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 6 Sep 2012 20:11:21 +0200 Subject: usb: gadget: make sure each gadget is using same index for Product, Serial,… MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The index in usb_string array is defined by the gadget. The gadget can choose which index entry it assigns for the serial number and which the product name. The gadget has just to ensure that the descriptor contains the proper string id which is assigned by composite. If the composite layer knows the index of the "default" information which will be overwritten by module parameters, it can be used later to overwrite it. Acked-by: Michal Nazarewicz Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/gadget/acm_ms.c | 12 +++++------- drivers/usb/gadget/audio.c | 12 +++++------- drivers/usb/gadget/cdc2.c | 13 +++++-------- drivers/usb/gadget/ether.c | 14 +++++--------- drivers/usb/gadget/g_ffs.c | 8 ++++++-- drivers/usb/gadget/gmidi.c | 13 ++++++------- drivers/usb/gadget/hid.c | 13 +++++-------- drivers/usb/gadget/multi.c | 13 ++++--------- drivers/usb/gadget/ncm.c | 13 +++++-------- drivers/usb/gadget/nokia.c | 13 ++++++------- drivers/usb/gadget/printer.c | 16 ++++++---------- drivers/usb/gadget/serial.c | 13 ++++++------- drivers/usb/gadget/tcm_usb_gadget.c | 13 +++++++------ drivers/usb/gadget/tcm_usb_gadget.h | 5 +---- drivers/usb/gadget/webcam.c | 13 ++++++------- drivers/usb/gadget/zero.c | 17 ++++++----------- include/linux/usb/composite.h | 12 +++++++++++- 17 files changed, 95 insertions(+), 118 deletions(-) (limited to 'include') diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/acm_ms.c index 7bd6b71af5d3..35db6aa57281 100644 --- a/drivers/usb/gadget/acm_ms.c +++ b/drivers/usb/gadget/acm_ms.c @@ -90,14 +90,12 @@ static const struct usb_descriptor_header *otg_desc[] = { /* string IDs are assigned dynamically */ -#define STRING_MANUFACTURER_IDX 0 -#define STRING_PRODUCT_IDX 1 - static char manufacturer[50]; static struct usb_string strings_dev[] = { - [STRING_MANUFACTURER_IDX].s = manufacturer, - [STRING_PRODUCT_IDX].s = DRIVER_DESC, + [USB_GADGET_MANUFACTURER_IDX].s = manufacturer, + [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, + [USB_GADGET_SERIAL_IDX].s = "", { } /* end of list */ }; @@ -196,8 +194,8 @@ static int __init acm_ms_bind(struct usb_composite_dev *cdev) status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) goto fail1; - device_desc.iManufacturer = strings_dev[STRING_MANUFACTURER_IDX].id; - device_desc.iProduct = strings_dev[STRING_PRODUCT_IDX].id; + device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; + device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; /* register our configuration */ status = usb_add_config(cdev, &acm_ms_config_driver, acm_ms_do_config); diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c index 55b593c5a9c8..8857b6eeb6a2 100644 --- a/drivers/usb/gadget/audio.c +++ b/drivers/usb/gadget/audio.c @@ -33,14 +33,12 @@ USB_GADGET_COMPOSITE_OPTIONS(); /* string IDs are assigned dynamically */ -#define STRING_MANUFACTURER_IDX 0 -#define STRING_PRODUCT_IDX 1 - static char manufacturer[50]; static struct usb_string strings_dev[] = { - [STRING_MANUFACTURER_IDX].s = manufacturer, - [STRING_PRODUCT_IDX].s = DRIVER_DESC, + [USB_GADGET_MANUFACTURER_IDX].s = manufacturer, + [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, + [USB_GADGET_SERIAL_IDX].s = "", { } /* end of list */ }; @@ -170,8 +168,8 @@ static int __init audio_bind(struct usb_composite_dev *cdev) status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) goto fail; - device_desc.iManufacturer = strings_dev[STRING_MANUFACTURER_IDX].id; - device_desc.iProduct = strings_dev[STRING_PRODUCT_IDX].id; + device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; + device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; status = usb_add_config(cdev, &audio_config_driver, audio_do_config); if (status < 0) diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index 93a75809b4de..8966bdec1534 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -90,15 +90,12 @@ static const struct usb_descriptor_header *otg_desc[] = { /* string IDs are assigned dynamically */ - -#define STRING_MANUFACTURER_IDX 0 -#define STRING_PRODUCT_IDX 1 - static char manufacturer[50]; static struct usb_string strings_dev[] = { - [STRING_MANUFACTURER_IDX].s = manufacturer, - [STRING_PRODUCT_IDX].s = DRIVER_DESC, + [USB_GADGET_MANUFACTURER_IDX].s = manufacturer, + [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, + [USB_GADGET_SERIAL_IDX].s = "", { } /* end of list */ }; @@ -197,8 +194,8 @@ static int __init cdc_bind(struct usb_composite_dev *cdev) status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) goto fail1; - device_desc.iManufacturer = strings_dev[STRING_MANUFACTURER_IDX].id; - device_desc.iProduct = strings_dev[STRING_PRODUCT_IDX].id; + device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; + device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; /* register our configuration */ status = usb_add_config(cdev, &cdc_config_driver, cdc_do_config); diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 709ef2265596..dd5e00d207fe 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -193,17 +193,13 @@ static const struct usb_descriptor_header *otg_desc[] = { NULL, }; - /* string IDs are assigned dynamically */ - -#define STRING_MANUFACTURER_IDX 0 -#define STRING_PRODUCT_IDX 1 - static char manufacturer[50]; static struct usb_string strings_dev[] = { - [STRING_MANUFACTURER_IDX].s = manufacturer, - [STRING_PRODUCT_IDX].s = PREFIX DRIVER_DESC, + [USB_GADGET_MANUFACTURER_IDX].s = manufacturer, + [USB_GADGET_PRODUCT_IDX].s = PREFIX DRIVER_DESC, + [USB_GADGET_SERIAL_IDX].s = "", { } /* end of list */ }; @@ -349,8 +345,8 @@ static int __init eth_bind(struct usb_composite_dev *cdev) status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) goto fail; - device_desc.iManufacturer = strings_dev[STRING_MANUFACTURER_IDX].id; - device_desc.iProduct = strings_dev[STRING_PRODUCT_IDX].id; + device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; + device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; /* register our configuration(s); RNDIS first, if it's used */ if (has_rndis()) { diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index a44ed661c16b..16d18873ebeb 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -116,6 +116,9 @@ static const struct usb_descriptor_header *gfs_otg_desc[] = { /* String IDs are assigned dynamically */ static struct usb_string gfs_strings[] = { + [USB_GADGET_MANUFACTURER_IDX].s = "", + [USB_GADGET_PRODUCT_IDX].s = "", + [USB_GADGET_SERIAL_IDX].s = "", #ifdef CONFIG_USB_FUNCTIONFS_RNDIS { .s = "FunctionFS + RNDIS" }, #endif @@ -369,9 +372,10 @@ static int gfs_bind(struct usb_composite_dev *cdev) for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) { struct gfs_configuration *c = gfs_configurations + i; + int sid = USB_GADGET_FIRST_AVAIL_IDX + i; - c->c.label = gfs_strings[i].s; - c->c.iConfiguration = gfs_strings[i].id; + c->c.label = gfs_strings[sid].s; + c->c.iConfiguration = gfs_strings[sid].id; c->c.bConfigurationValue = 1 + i; c->c.bmAttributes = USB_CONFIG_ATT_SELFPOWER; diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index 01b31e2d03af..2ee3a74056c9 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -84,9 +84,7 @@ MODULE_PARM_DESC(out_ports, "Number of MIDI output ports"); /* string IDs are assigned dynamically */ -#define STRING_MANUFACTURER_IDX 0 -#define STRING_PRODUCT_IDX 1 -#define STRING_DESCRIPTION_IDX 2 +#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX static struct usb_device_descriptor device_desc = { .bLength = USB_DT_DEVICE_SIZE, @@ -101,8 +99,9 @@ static struct usb_device_descriptor device_desc = { }; static struct usb_string strings_dev[] = { - [STRING_MANUFACTURER_IDX].s = "Grey Innovation", - [STRING_PRODUCT_IDX].s = "MIDI Gadget", + [USB_GADGET_MANUFACTURER_IDX].s = "Grey Innovation", + [USB_GADGET_PRODUCT_IDX].s = "MIDI Gadget", + [USB_GADGET_SERIAL_IDX].s = "", [STRING_DESCRIPTION_IDX].s = "MIDI", { } /* end of list */ }; @@ -145,8 +144,8 @@ static int __init midi_bind(struct usb_composite_dev *cdev) status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) return status; - device_desc.iManufacturer = strings_dev[STRING_MANUFACTURER_IDX].id; - device_desc.iProduct = strings_dev[STRING_PRODUCT_IDX].id; + device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; + device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; midi_config.iConfiguration = strings_dev[STRING_DESCRIPTION_IDX].id; gcnum = usb_gadget_controller_number(gadget); diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/hid.c index 59fe7eef00da..16caf50e916d 100644 --- a/drivers/usb/gadget/hid.c +++ b/drivers/usb/gadget/hid.c @@ -91,15 +91,12 @@ static const struct usb_descriptor_header *otg_desc[] = { /* string IDs are assigned dynamically */ - -#define STRING_MANUFACTURER_IDX 0 -#define STRING_PRODUCT_IDX 1 - static char manufacturer[50]; static struct usb_string strings_dev[] = { - [STRING_MANUFACTURER_IDX].s = manufacturer, - [STRING_PRODUCT_IDX].s = DRIVER_DESC, + [USB_GADGET_MANUFACTURER_IDX].s = manufacturer, + [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, + [USB_GADGET_SERIAL_IDX].s = "", { } /* end of list */ }; @@ -181,8 +178,8 @@ static int __init hid_bind(struct usb_composite_dev *cdev) status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) return status; - device_desc.iManufacturer = strings_dev[STRING_MANUFACTURER_IDX].id; - device_desc.iProduct = strings_dev[STRING_PRODUCT_IDX].id; + device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; + device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; /* register our configuration */ status = usb_add_config(cdev, &config_driver, do_config); diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 60168877330a..94b35e5539d7 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -109,21 +109,16 @@ static const struct usb_descriptor_header *otg_desc[] = { enum { -#ifdef CONFIG_USB_G_MULTI_RNDIS - MULTI_STRING_RNDIS_CONFIG_IDX, -#endif -#ifdef CONFIG_USB_G_MULTI_CDC + MULTI_STRING_RNDIS_CONFIG_IDX = USB_GADGET_FIRST_AVAIL_IDX, MULTI_STRING_CDC_CONFIG_IDX, -#endif }; static struct usb_string strings_dev[] = { -#ifdef CONFIG_USB_G_MULTI_RNDIS + [USB_GADGET_MANUFACTURER_IDX].s = "", + [USB_GADGET_PRODUCT_IDX].s = "", + [USB_GADGET_SERIAL_IDX].s = "", [MULTI_STRING_RNDIS_CONFIG_IDX].s = "Multifunction with RNDIS", -#endif -#ifdef CONFIG_USB_G_MULTI_CDC [MULTI_STRING_CDC_CONFIG_IDX].s = "Multifunction with CDC ECM", -#endif { } /* end of list */ }; diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c index 8537cd9c6aff..1a26452ce4ca 100644 --- a/drivers/usb/gadget/ncm.c +++ b/drivers/usb/gadget/ncm.c @@ -97,15 +97,12 @@ static const struct usb_descriptor_header *otg_desc[] = { /* string IDs are assigned dynamically */ - -#define STRING_MANUFACTURER_IDX 0 -#define STRING_PRODUCT_IDX 1 - static char manufacturer[50]; static struct usb_string strings_dev[] = { - [STRING_MANUFACTURER_IDX].s = manufacturer, - [STRING_PRODUCT_IDX].s = DRIVER_DESC, + [USB_GADGET_MANUFACTURER_IDX].s = manufacturer, + [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, + [USB_GADGET_SERIAL_IDX].s = "", { } /* end of list */ }; @@ -184,8 +181,8 @@ static int __init gncm_bind(struct usb_composite_dev *cdev) status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) goto fail; - device_desc.iManufacturer = strings_dev[STRING_MANUFACTURER_IDX].id; - device_desc.iProduct = strings_dev[STRING_PRODUCT_IDX].id; + device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; + device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; status = usb_add_config(cdev, &ncm_config_driver, ncm_do_config); diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index 5ed927b16c0e..34b97f12b7da 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -56,17 +56,16 @@ USB_GADGET_COMPOSITE_OPTIONS(); /* string IDs are assigned dynamically */ -#define STRING_MANUFACTURER_IDX 0 -#define STRING_PRODUCT_IDX 1 -#define STRING_DESCRIPTION_IDX 2 +#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX static char manufacturer_nokia[] = "Nokia"; static const char product_nokia[] = NOKIA_LONG_NAME; static const char description_nokia[] = "PC-Suite Configuration"; static struct usb_string strings_dev[] = { - [STRING_MANUFACTURER_IDX].s = manufacturer_nokia, - [STRING_PRODUCT_IDX].s = NOKIA_LONG_NAME, + [USB_GADGET_MANUFACTURER_IDX].s = manufacturer_nokia, + [USB_GADGET_PRODUCT_IDX].s = NOKIA_LONG_NAME, + [USB_GADGET_SERIAL_IDX].s = "", [STRING_DESCRIPTION_IDX].s = description_nokia, { } /* end of list */ }; @@ -168,8 +167,8 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) goto err_usb; - device_desc.iManufacturer = strings_dev[STRING_MANUFACTURER_IDX].id; - device_desc.iProduct = strings_dev[STRING_PRODUCT_IDX].id; + device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; + device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; status = strings_dev[STRING_DESCRIPTION_IDX].id; nokia_config_500ma_driver.iConfiguration = status; nokia_config_100ma_driver.iConfiguration = status; diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index 3fa4d80629cc..f02434a10dec 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -139,10 +139,6 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR); * descriptors are built on demand. */ -#define STRING_MANUFACTURER 0 -#define STRING_PRODUCT 1 -#define STRING_SERIALNUM 2 - /* holds our biggest descriptor */ #define USB_DESC_BUFSIZE 256 #define USB_BUFSIZE 8192 @@ -250,9 +246,9 @@ static char pnp_string [1024] = /* static strings, in UTF-8 */ static struct usb_string strings [] = { - [STRING_MANUFACTURER].s = manufacturer, - [STRING_PRODUCT].s = product_desc, - [STRING_SERIALNUM].s = serial_num, + [USB_GADGET_MANUFACTURER_IDX].s = manufacturer, + [USB_GADGET_PRODUCT_IDX].s = product_desc, + [USB_GADGET_SERIAL_IDX].s = serial_num, { } /* end of list */ }; @@ -1261,9 +1257,9 @@ static int __init printer_bind(struct usb_composite_dev *cdev) ret = usb_string_ids_tab(cdev, strings); if (ret < 0) return ret; - device_desc.iManufacturer = strings[STRING_MANUFACTURER].id; - device_desc.iProduct = strings[STRING_PRODUCT].id; - device_desc.iSerialNumber = strings[STRING_SERIALNUM].id; + device_desc.iManufacturer = strings[USB_GADGET_MANUFACTURER_IDX].id; + device_desc.iProduct = strings[USB_GADGET_PRODUCT_IDX].id; + device_desc.iSerialNumber = strings[USB_GADGET_SERIAL_IDX].id; ret = usb_add_config(cdev, &printer_cfg_driver, printer_bind_config); if (ret) diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index 27b5ce939bdb..768a38e896f7 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -59,15 +59,14 @@ USB_GADGET_COMPOSITE_OPTIONS(); /* string IDs are assigned dynamically */ -#define STRING_MANUFACTURER_IDX 0 -#define STRING_PRODUCT_IDX 1 -#define STRING_DESCRIPTION_IDX 2 +#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX static char manufacturer[50]; static struct usb_string strings_dev[] = { - [STRING_MANUFACTURER_IDX].s = manufacturer, - [STRING_PRODUCT_IDX].s = GS_VERSION_NAME, + [USB_GADGET_MANUFACTURER_IDX].s = manufacturer, + [USB_GADGET_PRODUCT_IDX].s = GS_VERSION_NAME, + [USB_GADGET_SERIAL_IDX].s = "", [STRING_DESCRIPTION_IDX].s = NULL /* updated; f(use_acm) */, { } /* end of list */ }; @@ -179,8 +178,8 @@ static int __init gs_bind(struct usb_composite_dev *cdev) status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) goto fail; - device_desc.iManufacturer = strings_dev[STRING_MANUFACTURER_IDX].id; - device_desc.iProduct = strings_dev[STRING_PRODUCT_IDX].id; + device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; + device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; status = strings_dev[STRING_DESCRIPTION_IDX].id; serial_config_driver.iConfiguration = status; diff --git a/drivers/usb/gadget/tcm_usb_gadget.c b/drivers/usb/gadget/tcm_usb_gadget.c index fa3137ef228b..b56d57d59796 100644 --- a/drivers/usb/gadget/tcm_usb_gadget.c +++ b/drivers/usb/gadget/tcm_usb_gadget.c @@ -2210,9 +2210,9 @@ static struct usb_device_descriptor usbg_device_desc = { }; static struct usb_string usbg_us_strings[] = { - [USB_G_STR_MANUFACTOR].s = "Target Manufactor", - [USB_G_STR_PRODUCT].s = "Target Product", - [USB_G_STR_SERIAL].s = "000000000001", + [USB_GADGET_MANUFACTURER_IDX].s = "Target Manufactor", + [USB_GADGET_PRODUCT_IDX].s = "Target Product", + [USB_GADGET_SERIAL_IDX].s = "000000000001", [USB_G_STR_CONFIG].s = "default config", [USB_G_STR_INT_UAS].s = "USB Attached SCSI", [USB_G_STR_INT_BBB].s = "Bulk Only Transport", @@ -2431,9 +2431,10 @@ static int usb_target_bind(struct usb_composite_dev *cdev) return ret; usbg_device_desc.iManufacturer = - usbg_us_strings[USB_G_STR_MANUFACTOR].id; - usbg_device_desc.iProduct = usbg_us_strings[USB_G_STR_PRODUCT].id; - usbg_device_desc.iSerialNumber = usbg_us_strings[USB_G_STR_SERIAL].id; + usbg_us_strings[USB_GADGET_MANUFACTURER_IDX].id; + usbg_device_desc.iProduct = usbg_us_strings[USB_GADGET_PRODUCT_IDX].id; + usbg_device_desc.iSerialNumber = + usbg_us_strings[USB_GADGET_SERIAL_IDX].id; usbg_config_driver.iConfiguration = usbg_us_strings[USB_G_STR_CONFIG].id; diff --git a/drivers/usb/gadget/tcm_usb_gadget.h b/drivers/usb/gadget/tcm_usb_gadget.h index 9d32ec30f4e4..8289219925b8 100644 --- a/drivers/usb/gadget/tcm_usb_gadget.h +++ b/drivers/usb/gadget/tcm_usb_gadget.h @@ -17,10 +17,7 @@ #define UASP_SS_EP_COMP_NUM_STREAMS (1 << UASP_SS_EP_COMP_LOG_STREAMS) enum { - USB_G_STR_MANUFACTOR, - USB_G_STR_PRODUCT, - USB_G_STR_SERIAL, - USB_G_STR_CONFIG, + USB_G_STR_CONFIG = USB_GADGET_FIRST_AVAIL_IDX, USB_G_STR_INT_UAS, USB_G_STR_INT_BBB, }; diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/webcam.c index 306006419f38..03591194b17a 100644 --- a/drivers/usb/gadget/webcam.c +++ b/drivers/usb/gadget/webcam.c @@ -45,13 +45,12 @@ static char webcam_config_label[] = "Video"; /* string IDs are assigned dynamically */ -#define STRING_MANUFACTURER_IDX 0 -#define STRING_PRODUCT_IDX 1 -#define STRING_DESCRIPTION_IDX 2 +#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX static struct usb_string webcam_strings[] = { - [STRING_MANUFACTURER_IDX].s = webcam_vendor_label, - [STRING_PRODUCT_IDX].s = webcam_product_label, + [USB_GADGET_MANUFACTURER_IDX].s = webcam_vendor_label, + [USB_GADGET_PRODUCT_IDX].s = webcam_product_label, + [USB_GADGET_SERIAL_IDX].s = "", [STRING_DESCRIPTION_IDX].s = webcam_config_label, { } }; @@ -360,9 +359,9 @@ webcam_bind(struct usb_composite_dev *cdev) if (ret < 0) goto error; webcam_device_descriptor.iManufacturer = - webcam_strings[STRING_MANUFACTURER_IDX].id; + webcam_strings[USB_GADGET_MANUFACTURER_IDX].id; webcam_device_descriptor.iProduct = - webcam_strings[STRING_PRODUCT_IDX].id; + webcam_strings[USB_GADGET_PRODUCT_IDX].id; webcam_config_driver.iConfiguration = webcam_strings[STRING_DESCRIPTION_IDX].id; diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index 2b31a4ae26b9..ee769c45498b 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -139,20 +139,15 @@ const struct usb_descriptor_header *otg_desc[] = { #endif /* string IDs are assigned dynamically */ - -#define STRING_MANUFACTURER_IDX 0 -#define STRING_PRODUCT_IDX 1 -#define STRING_SERIAL_IDX 2 - static char manufacturer[50]; /* default serial number takes at least two packets */ static char serial[] = "0123456789.0123456789.0123456789"; static struct usb_string strings_dev[] = { - [STRING_MANUFACTURER_IDX].s = manufacturer, - [STRING_PRODUCT_IDX].s = longname, - [STRING_SERIAL_IDX].s = serial, + [USB_GADGET_MANUFACTURER_IDX].s = manufacturer, + [USB_GADGET_PRODUCT_IDX].s = longname, + [USB_GADGET_SERIAL_IDX].s = serial, { } /* end of list */ }; @@ -274,9 +269,9 @@ static int __init zero_bind(struct usb_composite_dev *cdev) if (status < 0) return status; - device_desc.iManufacturer = strings_dev[STRING_MANUFACTURER_IDX].id; - device_desc.iProduct = strings_dev[STRING_PRODUCT_IDX].id; - device_desc.iSerialNumber = strings_dev[STRING_SERIAL_IDX].id; + device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; + device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; + device_desc.iSerialNumber = strings_dev[USB_GADGET_SERIAL_IDX].id; setup_timer(&autoresume_timer, zero_autoresume, (unsigned long) cdev); diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 7651e5bf7487..f821a3ad475d 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -248,6 +248,14 @@ int usb_add_config(struct usb_composite_dev *, void usb_remove_config(struct usb_composite_dev *, struct usb_configuration *); +/* predefined index for usb_composite_driver */ +enum { + USB_GADGET_MANUFACTURER_IDX = 0, + USB_GADGET_PRODUCT_IDX, + USB_GADGET_SERIAL_IDX, + USB_GADGET_FIRST_AVAIL_IDX, +}; + /** * struct usb_composite_driver - groups configurations into a gadget * @name: For diagnostics, identifies the driver. @@ -261,7 +269,9 @@ void usb_remove_config(struct usb_composite_dev *, * @dev: Template descriptor for the device, including default device * identifiers. * @strings: tables of strings, keyed by identifiers assigned during @bind - * and language IDs provided in control requests + * and language IDs provided in control requests. Note: The first entries + * are predefined. The first entry that may be used is + * USB_GADGET_FIRST_AVAIL_IDX * @max_speed: Highest speed the driver supports. * @needs_serial: set to 1 if the gadget needs userspace to provide * a serial number. If one is not provided, warning will be printed. -- cgit v1.2.3 From 1cf0d264088907038be560ba2dd472d5e432a3dc Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 10 Sep 2012 15:01:54 +0200 Subject: usb: gadget: push iSerialNumber into gadgets This patch pushes the iSerialNumber module argument from composite into each gadget. Once the user uses the module paramter, the string is overwritten with the final value. Acked-by: Michal Nazarewicz Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/gadget/composite.c | 20 +++++++++++++------- drivers/usb/gadget/mass_storage.c | 21 +++++++++++++++++++++ drivers/usb/gadget/printer.c | 6 +----- include/linux/usb/composite.h | 7 ++++++- 4 files changed, 41 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 317a5ece3bd2..5642b2170541 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -40,10 +40,6 @@ static char *iProduct; module_param(iProduct, charp, S_IRUGO); MODULE_PARM_DESC(iProduct, "USB Product string"); -static char *iSerialNumber; -module_param(iSerialNumber, charp, S_IRUGO); -MODULE_PARM_DESC(iSerialNumber, "SerialNumber string"); - static char composite_manufacturer[50]; /*-------------------------------------------------------------------------*/ @@ -925,7 +921,7 @@ static int get_string(struct usb_composite_dev *cdev, else if (cdev->product_override == id) str = iProduct ?: composite->iProduct; else if (cdev->serial_override == id) - str = iSerialNumber ?: composite->iSerialNumber; + str = composite->iSerialNumber; else str = NULL; if (str) { @@ -1411,6 +1407,7 @@ static void update_unchanged_dev_desc(struct usb_device_descriptor *new, __le16 idVendor; __le16 idProduct; __le16 bcdDevice; + u8 iSerialNumber; /* * these variables may have been set in @@ -1419,6 +1416,7 @@ static void update_unchanged_dev_desc(struct usb_device_descriptor *new, idVendor = new->idVendor; idProduct = new->idProduct; bcdDevice = new->bcdDevice; + iSerialNumber = new->iSerialNumber; *new = *old; if (idVendor) @@ -1427,6 +1425,8 @@ static void update_unchanged_dev_desc(struct usb_device_descriptor *new, new->idProduct = idProduct; if (bcdDevice) new->bcdDevice = bcdDevice; + if (iSerialNumber) + new->iSerialNumber = iSerialNumber; } static struct usb_composite_driver *to_cdriver(struct usb_gadget_driver *gdrv) @@ -1505,8 +1505,7 @@ static int composite_bind(struct usb_gadget *gadget, cdev->product_override = override_id(cdev, &cdev->desc.iProduct); - if (iSerialNumber || - (!cdev->desc.iSerialNumber && composite->iSerialNumber)) + if (composite->iSerialNumber) cdev->serial_override = override_id(cdev, &cdev->desc.iSerialNumber); @@ -1691,6 +1690,8 @@ void usb_composite_overwrite_options(struct usb_composite_dev *cdev, struct usb_composite_overwrite *covr) { struct usb_device_descriptor *desc = &cdev->desc; + struct usb_gadget_strings *gstr = cdev->driver->strings[0]; + struct usb_string *dev_str = gstr->strings; if (covr->idVendor) desc->idVendor = cpu_to_le16(covr->idVendor); @@ -1700,4 +1701,9 @@ void usb_composite_overwrite_options(struct usb_composite_dev *cdev, if (covr->bcdDevice) desc->bcdDevice = cpu_to_le16(covr->bcdDevice); + + if (covr->serial_number) { + desc->iSerialNumber = dev_str[USB_GADGET_SERIAL_IDX].id; + dev_str[USB_GADGET_SERIAL_IDX].s = covr->serial_number; + } } diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c index 8ffbade383f2..6f5a3b232d81 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/mass_storage.c @@ -83,6 +83,22 @@ static const struct usb_descriptor_header *otg_desc[] = { NULL, }; +static struct usb_string strings_dev[] = { + [USB_GADGET_MANUFACTURER_IDX].s = "", + [USB_GADGET_PRODUCT_IDX].s = "", + [USB_GADGET_SERIAL_IDX].s = "", + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, +}; + +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, +}; /****************************** Configurations ******************************/ @@ -141,6 +157,10 @@ static int __init msg_bind(struct usb_composite_dev *cdev) { int status; + status = usb_string_ids_tab(cdev, strings_dev); + if (status < 0) + return status; + status = usb_add_config(cdev, &msg_config_driver, msg_do_config); if (status < 0) return status; @@ -160,6 +180,7 @@ static __refdata struct usb_composite_driver msg_driver = { .iProduct = DRIVER_DESC, .max_speed = USB_SPEED_SUPER, .needs_serial = 1, + .strings = dev_strings, .bind = msg_bind, }; diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index f02434a10dec..3321a9df4862 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -118,8 +118,7 @@ static struct printer_dev usb_printer_gadget; * parameters are in UTF-8 (superset of ASCII's 7 bit characters). */ -static char *iSerialNum; -module_param(iSerialNum, charp, S_IRUGO); +module_param_named(iSerialNum, coverwrite.serial_number, charp, S_IRUGO); MODULE_PARM_DESC(iSerialNum, "1"); static char *iPNPstring; @@ -1170,9 +1169,6 @@ static int __init printer_bind_config(struct usb_configuration *c) init_utsname()->sysname, init_utsname()->release, gadget->name); - if (iSerialNum) - strlcpy(serial_num, iSerialNum, sizeof serial_num); - if (iPNPstring) strlcpy(&pnp_string[2], iPNPstring, (sizeof pnp_string)-2); diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index f821a3ad475d..9d068a4be778 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -400,6 +400,7 @@ struct usb_composite_overwrite { u16 idVendor; u16 idProduct; u16 bcdDevice; + char *serial_number; }; #define USB_GADGET_COMPOSITE_OPTIONS() \ static struct usb_composite_overwrite coverwrite; \ @@ -411,7 +412,11 @@ struct usb_composite_overwrite { MODULE_PARM_DESC(idProduct, "USB Product ID"); \ \ module_param_named(bcdDevice, coverwrite.bcdDevice, ushort, S_IRUGO); \ - MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)") + MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)"); \ + \ + module_param_named(iSerialNumber, coverwrite.serial_number, charp, \ + S_IRUGO); \ + MODULE_PARM_DESC(iSerialNumber, "SerialNumber string") void usb_composite_overwrite_options(struct usb_composite_dev *cdev, struct usb_composite_overwrite *covr); -- cgit v1.2.3 From 03de9bf69c589b71c43aa52b838690cb477903c9 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 10 Sep 2012 15:01:55 +0200 Subject: usb: gadget: push iManufacturer into gadgets This patch pushes the iManufacturer module argument from composite into each gadget. Once the user uses the module paramter, the string is overwritten with the final value. Acked-by: Michal Nazarewicz Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/gadget/composite.c | 20 +++++++++++--------- include/linux/usb/composite.h | 7 ++++++- 2 files changed, 17 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 5642b2170541..482cf8cf301d 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -32,10 +32,6 @@ * published in the device descriptor, either numbers or strings or both. * String parameters are in UTF-8 (superset of ASCII's 7 bit characters). */ -static char *iManufacturer; -module_param(iManufacturer, charp, S_IRUGO); -MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); - static char *iProduct; module_param(iProduct, charp, S_IRUGO); MODULE_PARM_DESC(iProduct, "USB Product string"); @@ -916,8 +912,7 @@ static int get_string(struct usb_composite_dev *cdev, * check if the string has not been overridden. */ if (cdev->manufacturer_override == id) - str = iManufacturer ?: composite->iManufacturer ?: - composite_manufacturer; + str = composite->iManufacturer ?: composite_manufacturer; else if (cdev->product_override == id) str = iProduct ?: composite->iProduct; else if (cdev->serial_override == id) @@ -1408,6 +1403,7 @@ static void update_unchanged_dev_desc(struct usb_device_descriptor *new, __le16 idProduct; __le16 bcdDevice; u8 iSerialNumber; + u8 iManufacturer; /* * these variables may have been set in @@ -1417,6 +1413,7 @@ static void update_unchanged_dev_desc(struct usb_device_descriptor *new, idProduct = new->idProduct; bcdDevice = new->bcdDevice; iSerialNumber = new->iSerialNumber; + iManufacturer = new->iManufacturer; *new = *old; if (idVendor) @@ -1427,6 +1424,8 @@ static void update_unchanged_dev_desc(struct usb_device_descriptor *new, new->bcdDevice = bcdDevice; if (iSerialNumber) new->iSerialNumber = iSerialNumber; + if (iManufacturer) + new->iManufacturer = iManufacturer; } static struct usb_composite_driver *to_cdriver(struct usb_gadget_driver *gdrv) @@ -1487,9 +1486,8 @@ static int composite_bind(struct usb_gadget *gadget, update_unchanged_dev_desc(&cdev->desc, composite->dev); /* string overrides */ - if (iManufacturer || !cdev->desc.iManufacturer) { - if (!iManufacturer && !composite->iManufacturer && - !*composite_manufacturer) + if (!cdev->desc.iManufacturer) { + if (!composite->iManufacturer) snprintf(composite_manufacturer, sizeof composite_manufacturer, "%s %s with %s", @@ -1706,4 +1704,8 @@ void usb_composite_overwrite_options(struct usb_composite_dev *cdev, desc->iSerialNumber = dev_str[USB_GADGET_SERIAL_IDX].id; dev_str[USB_GADGET_SERIAL_IDX].s = covr->serial_number; } + if (covr->manufacturer) { + desc->iManufacturer = dev_str[USB_GADGET_MANUFACTURER_IDX].id; + dev_str[USB_GADGET_MANUFACTURER_IDX].s = covr->manufacturer; + } } diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 9d068a4be778..86553c8c3e8b 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -401,6 +401,7 @@ struct usb_composite_overwrite { u16 idProduct; u16 bcdDevice; char *serial_number; + char *manufacturer; }; #define USB_GADGET_COMPOSITE_OPTIONS() \ static struct usb_composite_overwrite coverwrite; \ @@ -416,7 +417,11 @@ struct usb_composite_overwrite { \ module_param_named(iSerialNumber, coverwrite.serial_number, charp, \ S_IRUGO); \ - MODULE_PARM_DESC(iSerialNumber, "SerialNumber string") + MODULE_PARM_DESC(iSerialNumber, "SerialNumber string"); \ + \ + module_param_named(iManufacturer, coverwrite.manufacturer, charp, \ + S_IRUGO); \ + MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string") void usb_composite_overwrite_options(struct usb_composite_dev *cdev, struct usb_composite_overwrite *covr); -- cgit v1.2.3 From 2d35ee47aaafac152bc4bc5020660ffa1753ab02 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 10 Sep 2012 15:01:56 +0200 Subject: usb: gadget: push iProduct into gadgets This patch pushes the iProduct module argument from composite into each gadget. Acked-by: Michal Nazarewicz Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/gadget/composite.c | 21 +++++++++++---------- include/linux/usb/composite.h | 6 +++++- 2 files changed, 16 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 482cf8cf301d..47b9130968bc 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -28,14 +28,6 @@ * with the relevant device-wide data. */ -/* Some systems will need runtime overrides for the product identifiers - * published in the device descriptor, either numbers or strings or both. - * String parameters are in UTF-8 (superset of ASCII's 7 bit characters). - */ -static char *iProduct; -module_param(iProduct, charp, S_IRUGO); -MODULE_PARM_DESC(iProduct, "USB Product string"); - static char composite_manufacturer[50]; /*-------------------------------------------------------------------------*/ @@ -914,7 +906,7 @@ static int get_string(struct usb_composite_dev *cdev, if (cdev->manufacturer_override == id) str = composite->iManufacturer ?: composite_manufacturer; else if (cdev->product_override == id) - str = iProduct ?: composite->iProduct; + str = composite->iProduct; else if (cdev->serial_override == id) str = composite->iSerialNumber; else @@ -1404,6 +1396,7 @@ static void update_unchanged_dev_desc(struct usb_device_descriptor *new, __le16 bcdDevice; u8 iSerialNumber; u8 iManufacturer; + u8 iProduct; /* * these variables may have been set in @@ -1414,6 +1407,7 @@ static void update_unchanged_dev_desc(struct usb_device_descriptor *new, bcdDevice = new->bcdDevice; iSerialNumber = new->iSerialNumber; iManufacturer = new->iManufacturer; + iProduct = new->iProduct; *new = *old; if (idVendor) @@ -1426,6 +1420,8 @@ static void update_unchanged_dev_desc(struct usb_device_descriptor *new, new->iSerialNumber = iSerialNumber; if (iManufacturer) new->iManufacturer = iManufacturer; + if (iProduct) + new->iProduct = iProduct; } static struct usb_composite_driver *to_cdriver(struct usb_gadget_driver *gdrv) @@ -1499,7 +1495,7 @@ static int composite_bind(struct usb_gadget *gadget, override_id(cdev, &cdev->desc.iManufacturer); } - if (iProduct || (!cdev->desc.iProduct && composite->iProduct)) + if (!cdev->desc.iProduct && composite->iProduct) cdev->product_override = override_id(cdev, &cdev->desc.iProduct); @@ -1708,4 +1704,9 @@ void usb_composite_overwrite_options(struct usb_composite_dev *cdev, desc->iManufacturer = dev_str[USB_GADGET_MANUFACTURER_IDX].id; dev_str[USB_GADGET_MANUFACTURER_IDX].s = covr->manufacturer; } + + if (covr->product) { + desc->iProduct = dev_str[USB_GADGET_PRODUCT_IDX].id; + dev_str[USB_GADGET_PRODUCT_IDX].s = covr->product; + } } diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 86553c8c3e8b..5cd110ec0a23 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -402,6 +402,7 @@ struct usb_composite_overwrite { u16 bcdDevice; char *serial_number; char *manufacturer; + char *product; }; #define USB_GADGET_COMPOSITE_OPTIONS() \ static struct usb_composite_overwrite coverwrite; \ @@ -421,7 +422,10 @@ struct usb_composite_overwrite { \ module_param_named(iManufacturer, coverwrite.manufacturer, charp, \ S_IRUGO); \ - MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string") + MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); \ + \ + module_param_named(iProduct, coverwrite.product, charp, S_IRUGO); \ + MODULE_PARM_DESC(iProduct, "USB Product string") void usb_composite_overwrite_options(struct usb_composite_dev *cdev, struct usb_composite_overwrite *covr); -- cgit v1.2.3 From d33f74fce3756d51a0203cec3d0d278e3b48d827 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 10 Sep 2012 15:01:57 +0200 Subject: usb: gadget: remove string override from struct usb_composite_driver The struct usb_composite_driver members iProduct, iSerial and iManufacturer can be entered directly via the string array. There is no need for them to appear here. Acked-by: Michal Nazarewicz Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/gadget/composite.c | 29 +++++++---------------------- drivers/usb/gadget/g_ffs.c | 4 ++-- drivers/usb/gadget/mass_storage.c | 4 ++-- drivers/usb/gadget/multi.c | 4 ++-- include/linux/usb/composite.h | 12 ------------ 5 files changed, 13 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 47b9130968bc..f3689e1bf4b2 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -904,11 +904,7 @@ static int get_string(struct usb_composite_dev *cdev, * check if the string has not been overridden. */ if (cdev->manufacturer_override == id) - str = composite->iManufacturer ?: composite_manufacturer; - else if (cdev->product_override == id) - str = composite->iProduct; - else if (cdev->serial_override == id) - str = composite->iSerialNumber; + str = composite_manufacturer; else str = NULL; if (str) { @@ -1483,26 +1479,17 @@ static int composite_bind(struct usb_gadget *gadget, /* string overrides */ if (!cdev->desc.iManufacturer) { - if (!composite->iManufacturer) - snprintf(composite_manufacturer, - sizeof composite_manufacturer, - "%s %s with %s", - init_utsname()->sysname, - init_utsname()->release, - gadget->name); + snprintf(composite_manufacturer, + sizeof composite_manufacturer, + "%s %s with %s", + init_utsname()->sysname, + init_utsname()->release, + gadget->name); cdev->manufacturer_override = override_id(cdev, &cdev->desc.iManufacturer); } - if (!cdev->desc.iProduct && composite->iProduct) - cdev->product_override = - override_id(cdev, &cdev->desc.iProduct); - - if (composite->iSerialNumber) - cdev->serial_override = - override_id(cdev, &cdev->desc.iSerialNumber); - /* has userspace failed to provide a serial number? */ if (composite->needs_serial && !cdev->desc.iSerialNumber) WARNING(cdev, "userspace failed to provide iSerialNumber\n"); @@ -1619,8 +1606,6 @@ int usb_composite_probe(struct usb_composite_driver *driver) if (!driver->name) driver->name = "composite"; - if (!driver->iProduct) - driver->iProduct = driver->name; driver->gadget_driver = composite_driver_template; gadget_driver = &driver->gadget_driver; diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index 16d18873ebeb..eaaed199e453 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -117,7 +117,7 @@ static const struct usb_descriptor_header *gfs_otg_desc[] = { /* String IDs are assigned dynamically */ static struct usb_string gfs_strings[] = { [USB_GADGET_MANUFACTURER_IDX].s = "", - [USB_GADGET_PRODUCT_IDX].s = "", + [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, [USB_GADGET_SERIAL_IDX].s = "", #ifdef CONFIG_USB_FUNCTIONFS_RNDIS { .s = "FunctionFS + RNDIS" }, @@ -172,7 +172,6 @@ static __refdata struct usb_composite_driver gfs_driver = { .max_speed = USB_SPEED_HIGH, .bind = gfs_bind, .unbind = gfs_unbind, - .iProduct = DRIVER_DESC, }; static DEFINE_MUTEX(gfs_lock); @@ -360,6 +359,7 @@ static int gfs_bind(struct usb_composite_dev *cdev) ret = usb_string_ids_tab(cdev, gfs_strings); if (unlikely(ret < 0)) goto error; + gfs_dev_desc.iProduct = gfs_strings[USB_GADGET_PRODUCT_IDX].id; for (i = func_num; --i; ) { ret = functionfs_bind(ffs_tab[i].ffs_data, cdev); diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c index 6f5a3b232d81..50da3c88cb06 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/mass_storage.c @@ -85,7 +85,7 @@ static const struct usb_descriptor_header *otg_desc[] = { static struct usb_string strings_dev[] = { [USB_GADGET_MANUFACTURER_IDX].s = "", - [USB_GADGET_PRODUCT_IDX].s = "", + [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, [USB_GADGET_SERIAL_IDX].s = "", { } /* end of list */ }; @@ -160,6 +160,7 @@ static int __init msg_bind(struct usb_composite_dev *cdev) status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) return status; + msg_device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; status = usb_add_config(cdev, &msg_config_driver, msg_do_config); if (status < 0) @@ -177,7 +178,6 @@ static int __init msg_bind(struct usb_composite_dev *cdev) static __refdata struct usb_composite_driver msg_driver = { .name = "g_mass_storage", .dev = &msg_device_desc, - .iProduct = DRIVER_DESC, .max_speed = USB_SPEED_SUPER, .needs_serial = 1, .strings = dev_strings, diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 94b35e5539d7..c158706b5ab0 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -115,7 +115,7 @@ enum { static struct usb_string strings_dev[] = { [USB_GADGET_MANUFACTURER_IDX].s = "", - [USB_GADGET_PRODUCT_IDX].s = "", + [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, [USB_GADGET_SERIAL_IDX].s = "", [MULTI_STRING_RNDIS_CONFIG_IDX].s = "Multifunction with RNDIS", [MULTI_STRING_CDC_CONFIG_IDX].s = "Multifunction with CDC ECM", @@ -293,6 +293,7 @@ static int __ref multi_bind(struct usb_composite_dev *cdev) status = usb_string_ids_tab(cdev, strings_dev); if (unlikely(status < 0)) goto fail2; + device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; /* register configurations */ status = rndis_config_register(cdev); @@ -338,7 +339,6 @@ static __refdata struct usb_composite_driver multi_driver = { .max_speed = USB_SPEED_HIGH, .bind = multi_bind, .unbind = __exit_p(multi_unbind), - .iProduct = DRIVER_DESC, .needs_serial = 1, }; diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 5cd110ec0a23..60f8815998bd 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -259,13 +259,6 @@ enum { /** * struct usb_composite_driver - groups configurations into a gadget * @name: For diagnostics, identifies the driver. - * @iProduct: Used as iProduct override if @dev->iProduct is not set. - * If NULL value of @name is taken. - * @iManufacturer: Used as iManufacturer override if @dev->iManufacturer is - * not set. If NULL a default " with " value - * will be used. - * @iSerialNumber: Used as iSerialNumber override if @dev->iSerialNumber is - * not set. * @dev: Template descriptor for the device, including default device * identifiers. * @strings: tables of strings, keyed by identifiers assigned during @bind @@ -300,9 +293,6 @@ enum { */ struct usb_composite_driver { const char *name; - const char *iProduct; - const char *iManufacturer; - const char *iSerialNumber; const struct usb_device_descriptor *dev; struct usb_gadget_strings **strings; enum usb_device_speed max_speed; @@ -369,8 +359,6 @@ struct usb_composite_dev { struct usb_composite_driver *driver; u8 next_string_id; u8 manufacturer_override; - u8 product_override; - u8 serial_override; /* the gadget driver won't enable the data pullup * while the deactivation count is nonzero. -- cgit v1.2.3 From cc2683c318a5bf192b75cd5c343b51009db0cf6c Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 10 Sep 2012 15:01:58 +0200 Subject: usb: gadget: Provide a default implementation of default manufacturer string Some gadgets provide custom entry here. Some may override it with an etntry that is also created by composite if there was no value sumbitted at all. This patch removes all "custom manufacturer" strings which are the same as these which are created by composite. Then it moves the creation of the default manufacturer string to usb_composite_overwrite_options() in case no command line argument has been used and the entry is still an empty string. By doing this we get rid of the global variable "composite_manufacturer" in composite. Acked-by: Michal Nazarewicz Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/gadget/acm_ms.c | 12 +------ drivers/usb/gadget/audio.c | 9 +---- drivers/usb/gadget/cdc2.c | 10 +----- drivers/usb/gadget/composite.c | 67 ++++++++++++------------------------- drivers/usb/gadget/ether.c | 12 +------ drivers/usb/gadget/f_hid.c | 1 - drivers/usb/gadget/f_mass_storage.c | 1 - drivers/usb/gadget/f_midi.c | 1 - drivers/usb/gadget/g_ffs.c | 1 - drivers/usb/gadget/gmidi.c | 1 - drivers/usb/gadget/hid.c | 10 +----- drivers/usb/gadget/mass_storage.c | 2 -- drivers/usb/gadget/multi.c | 2 -- drivers/usb/gadget/ncm.c | 12 +------ drivers/usb/gadget/nokia.c | 1 - drivers/usb/gadget/printer.c | 8 +---- drivers/usb/gadget/serial.c | 9 +---- drivers/usb/gadget/zero.c | 9 +---- include/linux/usb/composite.h | 2 +- 19 files changed, 32 insertions(+), 138 deletions(-) (limited to 'include') diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/acm_ms.c index 35db6aa57281..d280f164887c 100644 --- a/drivers/usb/gadget/acm_ms.c +++ b/drivers/usb/gadget/acm_ms.c @@ -15,7 +15,6 @@ */ #include -#include #include "u_serial.h" @@ -87,13 +86,9 @@ static const struct usb_descriptor_header *otg_desc[] = { NULL, }; - /* string IDs are assigned dynamically */ - -static char manufacturer[50]; - static struct usb_string strings_dev[] = { - [USB_GADGET_MANUFACTURER_IDX].s = manufacturer, + [USB_GADGET_MANUFACTURER_IDX].s = "", [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, [USB_GADGET_SERIAL_IDX].s = "", { } /* end of list */ @@ -186,11 +181,6 @@ static int __init acm_ms_bind(struct usb_composite_dev *cdev) * Allocate string descriptor numbers ... note that string * contents can be overridden by the composite_dev glue. */ - - /* device descriptor strings: manufacturer, product */ - snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", - init_utsname()->sysname, init_utsname()->release, - gadget->name); status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) goto fail1; diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c index 8857b6eeb6a2..1f81e0f4fab9 100644 --- a/drivers/usb/gadget/audio.c +++ b/drivers/usb/gadget/audio.c @@ -12,7 +12,6 @@ /* #define VERBOSE_DEBUG */ #include -#include #include #include "gadget_chips.h" @@ -33,10 +32,8 @@ USB_GADGET_COMPOSITE_OPTIONS(); /* string IDs are assigned dynamically */ -static char manufacturer[50]; - static struct usb_string strings_dev[] = { - [USB_GADGET_MANUFACTURER_IDX].s = manufacturer, + [USB_GADGET_MANUFACTURER_IDX].s = "", [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, [USB_GADGET_SERIAL_IDX].s = "", { } /* end of list */ @@ -161,10 +158,6 @@ static int __init audio_bind(struct usb_composite_dev *cdev) __constant_cpu_to_le16(0x0300 | 0x0099); } - /* device descriptor strings: manufacturer, product */ - snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", - init_utsname()->sysname, init_utsname()->release, - cdev->gadget->name); status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) goto fail; diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index 8966bdec1534..4e2060bf35e3 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -11,7 +11,6 @@ */ #include -#include #include #include "u_ether.h" @@ -90,10 +89,8 @@ static const struct usb_descriptor_header *otg_desc[] = { /* string IDs are assigned dynamically */ -static char manufacturer[50]; - static struct usb_string strings_dev[] = { - [USB_GADGET_MANUFACTURER_IDX].s = manufacturer, + [USB_GADGET_MANUFACTURER_IDX].s = "", [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, [USB_GADGET_SERIAL_IDX].s = "", { } /* end of list */ @@ -182,15 +179,10 @@ static int __init cdc_bind(struct usb_composite_dev *cdev) cpu_to_le16(0x0300 | 0x0099); } - /* Allocate string descriptor numbers ... note that string * contents can be overridden by the composite_dev glue. */ - /* device descriptor strings: manufacturer, product */ - snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", - init_utsname()->sysname, init_utsname()->release, - gadget->name); status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) goto fail1; diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index f3689e1bf4b2..c7066cd4c95a 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -28,9 +28,6 @@ * with the relevant device-wide data. */ -static char composite_manufacturer[50]; - -/*-------------------------------------------------------------------------*/ /** * next_ep_desc() - advance to the next EP descriptor * @t: currect pointer within descriptor array @@ -860,7 +857,6 @@ static int get_string(struct usb_composite_dev *cdev, struct usb_configuration *c; struct usb_function *f; int len; - const char *str; /* Yes, not only is USB's I18N support probably more than most * folk will ever care about ... also, it's all supported here. @@ -900,21 +896,6 @@ static int get_string(struct usb_composite_dev *cdev, return s->bLength; } - /* Otherwise, look up and return a specified string. First - * check if the string has not been overridden. - */ - if (cdev->manufacturer_override == id) - str = composite_manufacturer; - else - str = NULL; - if (str) { - struct usb_gadget_strings strings = { - .language = language, - .strings = &(struct usb_string) { 0xff, str } - }; - return usb_gadget_get_string(&strings, 0xff, buf); - } - /* String IDs are device-scoped, so we look up each string * table we're told about. These lookups are infrequent; * simpler-is-better here. @@ -1367,23 +1348,11 @@ composite_unbind(struct usb_gadget *gadget) usb_ep_free_request(gadget->ep0, cdev->req); } device_remove_file(&gadget->dev, &dev_attr_suspended); + kfree(cdev->def_manufacturer); kfree(cdev); set_gadget_data(gadget, NULL); } -static u8 override_id(struct usb_composite_dev *cdev, u8 *desc) -{ - if (!*desc) { - int ret = usb_string_id(cdev); - if (unlikely(ret < 0)) - WARNING(cdev, "failed to override string ID\n"); - else - *desc = ret; - } - - return *desc; -} - static void update_unchanged_dev_desc(struct usb_device_descriptor *new, const struct usb_device_descriptor *old) { @@ -1477,19 +1446,6 @@ static int composite_bind(struct usb_gadget *gadget, update_unchanged_dev_desc(&cdev->desc, composite->dev); - /* string overrides */ - if (!cdev->desc.iManufacturer) { - snprintf(composite_manufacturer, - sizeof composite_manufacturer, - "%s %s with %s", - init_utsname()->sysname, - init_utsname()->release, - gadget->name); - - cdev->manufacturer_override = - override_id(cdev, &cdev->desc.iManufacturer); - } - /* has userspace failed to provide a serial number? */ if (composite->needs_serial && !cdev->desc.iSerialNumber) WARNING(cdev, "userspace failed to provide iSerialNumber\n"); @@ -1665,6 +1621,22 @@ void usb_composite_setup_continue(struct usb_composite_dev *cdev) spin_unlock_irqrestore(&cdev->lock, flags); } +static char *composite_default_mfr(struct usb_gadget *gadget) +{ + char *mfr; + int len; + + len = snprintf(NULL, 0, "%s %s with %s", init_utsname()->sysname, + init_utsname()->release, gadget->name); + len++; + mfr = kmalloc(len, GFP_KERNEL); + if (!mfr) + return NULL; + snprintf(mfr, len, "%s %s with %s", init_utsname()->sysname, + init_utsname()->release, gadget->name); + return mfr; +} + void usb_composite_overwrite_options(struct usb_composite_dev *cdev, struct usb_composite_overwrite *covr) { @@ -1688,6 +1660,11 @@ void usb_composite_overwrite_options(struct usb_composite_dev *cdev, if (covr->manufacturer) { desc->iManufacturer = dev_str[USB_GADGET_MANUFACTURER_IDX].id; dev_str[USB_GADGET_MANUFACTURER_IDX].s = covr->manufacturer; + + } else if (!strlen(dev_str[USB_GADGET_MANUFACTURER_IDX].s)) { + desc->iManufacturer = dev_str[USB_GADGET_MANUFACTURER_IDX].id; + cdev->def_manufacturer = composite_default_mfr(cdev->gadget); + dev_str[USB_GADGET_MANUFACTURER_IDX].s = cdev->def_manufacturer; } if (covr->product) { diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index dd5e00d207fe..a5c272067625 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -14,8 +14,6 @@ /* #define VERBOSE_DEBUG */ #include -#include - #if defined USB_ETH_RNDIS # undef USB_ETH_RNDIS @@ -193,11 +191,8 @@ static const struct usb_descriptor_header *otg_desc[] = { NULL, }; -/* string IDs are assigned dynamically */ -static char manufacturer[50]; - static struct usb_string strings_dev[] = { - [USB_GADGET_MANUFACTURER_IDX].s = manufacturer, + [USB_GADGET_MANUFACTURER_IDX].s = "", [USB_GADGET_PRODUCT_IDX].s = PREFIX DRIVER_DESC, [USB_GADGET_SERIAL_IDX].s = "", { } /* end of list */ @@ -333,15 +328,10 @@ static int __init eth_bind(struct usb_composite_dev *cdev) cpu_to_le16(0x0300 | 0x0099); } - /* Allocate string descriptor numbers ... note that string * contents can be overridden by the composite_dev glue. */ - /* device descriptor strings: manufacturer, product */ - snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", - init_utsname()->sysname, init_utsname()->release, - gadget->name); status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) goto fail; diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/f_hid.c index 16a8b1c15c62..77dbca099bcb 100644 --- a/drivers/usb/gadget/f_hid.c +++ b/drivers/usb/gadget/f_hid.c @@ -10,7 +10,6 @@ */ #include -#include #include #include #include diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 4f1142efa6d1..11150960d88b 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -213,7 +213,6 @@ #include #include #include -#include #include #include diff --git a/drivers/usb/gadget/f_midi.c b/drivers/usb/gadget/f_midi.c index 2f7e8f2930cc..8ed1259fe80d 100644 --- a/drivers/usb/gadget/f_midi.c +++ b/drivers/usb/gadget/f_midi.c @@ -21,7 +21,6 @@ #include #include -#include #include #include diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index eaaed199e453..9e62c20fb5bc 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -13,7 +13,6 @@ #define pr_fmt(fmt) "g_ffs: " fmt #include -#include /* * kbuild is not very cooperative with respect to linking separately diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index 2ee3a74056c9..59621ef7657d 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -22,7 +22,6 @@ #include #include -#include #include #include diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/hid.c index 16caf50e916d..0e2e357cc59e 100644 --- a/drivers/usb/gadget/hid.c +++ b/drivers/usb/gadget/hid.c @@ -91,10 +91,8 @@ static const struct usb_descriptor_header *otg_desc[] = { /* string IDs are assigned dynamically */ -static char manufacturer[50]; - static struct usb_string strings_dev[] = { - [USB_GADGET_MANUFACTURER_IDX].s = manufacturer, + [USB_GADGET_MANUFACTURER_IDX].s = "", [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, [USB_GADGET_SERIAL_IDX].s = "", { } /* end of list */ @@ -165,16 +163,10 @@ static int __init hid_bind(struct usb_composite_dev *cdev) else device_desc.bcdDevice = cpu_to_le16(0x0300 | 0x0099); - /* Allocate string descriptor numbers ... note that string * contents can be overridden by the composite_dev glue. */ - /* device descriptor strings: manufacturer, product */ - snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", - init_utsname()->sysname, init_utsname()->release, - gadget->name); - status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) return status; diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c index 50da3c88cb06..9eb2be685803 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/mass_storage.c @@ -29,10 +29,8 @@ #include -#include #include - /*-------------------------------------------------------------------------*/ #define DRIVER_DESC "Mass Storage Gadget" diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index c158706b5ab0..5bc5d96beaac 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -14,10 +14,8 @@ #include -#include #include - #if defined USB_ETH_RNDIS # undef USB_ETH_RNDIS #endif diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c index 1a26452ce4ca..343f3d364ef8 100644 --- a/drivers/usb/gadget/ncm.c +++ b/drivers/usb/gadget/ncm.c @@ -20,8 +20,6 @@ /* #define VERBOSE_DEBUG */ #include -#include - #include "u_ether.h" @@ -95,12 +93,9 @@ static const struct usb_descriptor_header *otg_desc[] = { NULL, }; - /* string IDs are assigned dynamically */ -static char manufacturer[50]; - static struct usb_string strings_dev[] = { - [USB_GADGET_MANUFACTURER_IDX].s = manufacturer, + [USB_GADGET_MANUFACTURER_IDX].s = "", [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, [USB_GADGET_SERIAL_IDX].s = "", { } /* end of list */ @@ -169,15 +164,10 @@ static int __init gncm_bind(struct usb_composite_dev *cdev) cpu_to_le16(0x0300 | 0x0099); } - /* Allocate string descriptor numbers ... note that string * contents can be overridden by the composite_dev glue. */ - /* device descriptor strings: manufacturer, product */ - snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", - init_utsname()->sysname, init_utsname()->release, - gadget->name); status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) goto fail; diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index 34b97f12b7da..465766e41442 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -16,7 +16,6 @@ */ #include -#include #include #include "u_serial.h" diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index 3321a9df4862..9bd994203d3d 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -237,7 +236,6 @@ static const struct usb_descriptor_header *otg_desc[] = { /* descriptors that are built on-demand */ -static char manufacturer [50]; static char product_desc [40] = DRIVER_DESC; static char serial_num [40] = "1"; static char pnp_string [1024] = @@ -245,7 +243,7 @@ static char pnp_string [1024] = /* static strings, in UTF-8 */ static struct usb_string strings [] = { - [USB_GADGET_MANUFACTURER_IDX].s = manufacturer, + [USB_GADGET_MANUFACTURER_IDX].s = "", [USB_GADGET_PRODUCT_IDX].s = product_desc, [USB_GADGET_SERIAL_IDX].s = serial_num, { } /* end of list */ @@ -1165,10 +1163,6 @@ static int __init printer_bind_config(struct usb_configuration *c) device_desc.bcdDevice = cpu_to_le16(0xFFFF); } - snprintf(manufacturer, sizeof(manufacturer), "%s %s with %s", - init_utsname()->sysname, init_utsname()->release, - gadget->name); - if (iPNPstring) strlcpy(&pnp_string[2], iPNPstring, (sizeof pnp_string)-2); diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index 768a38e896f7..bf12d55cd07b 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -11,7 +11,6 @@ */ #include -#include #include #include #include @@ -61,10 +60,8 @@ USB_GADGET_COMPOSITE_OPTIONS(); #define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX -static char manufacturer[50]; - static struct usb_string strings_dev[] = { - [USB_GADGET_MANUFACTURER_IDX].s = manufacturer, + [USB_GADGET_MANUFACTURER_IDX].s = "", [USB_GADGET_PRODUCT_IDX].s = GS_VERSION_NAME, [USB_GADGET_SERIAL_IDX].s = "", [STRING_DESCRIPTION_IDX].s = NULL /* updated; f(use_acm) */, @@ -171,10 +168,6 @@ static int __init gs_bind(struct usb_composite_dev *cdev) * contents can be overridden by the composite_dev glue. */ - /* device description: manufacturer, product */ - snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", - init_utsname()->sysname, init_utsname()->release, - gadget->name); status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) goto fail; diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index ee769c45498b..a837f3af2047 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -42,7 +42,6 @@ #include #include -#include #include #include "g_zero.h" @@ -139,13 +138,11 @@ const struct usb_descriptor_header *otg_desc[] = { #endif /* string IDs are assigned dynamically */ -static char manufacturer[50]; - /* default serial number takes at least two packets */ static char serial[] = "0123456789.0123456789.0123456789"; static struct usb_string strings_dev[] = { - [USB_GADGET_MANUFACTURER_IDX].s = manufacturer, + [USB_GADGET_MANUFACTURER_IDX].s = "", [USB_GADGET_PRODUCT_IDX].s = longname, [USB_GADGET_SERIAL_IDX].s = serial, { } /* end of list */ @@ -305,10 +302,6 @@ static int __init zero_bind(struct usb_composite_dev *cdev) INFO(cdev, "%s, version: " DRIVER_VERSION "\n", longname); - snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", - init_utsname()->sysname, init_utsname()->release, - gadget->name); - return 0; } diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 60f8815998bd..65ae0a3feb5b 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -358,7 +358,7 @@ struct usb_composite_dev { struct list_head configs; struct usb_composite_driver *driver; u8 next_string_id; - u8 manufacturer_override; + char *def_manufacturer; /* the gadget driver won't enable the data pullup * while the deactivation count is nonzero. -- cgit v1.2.3 From ed9cbda63d45638b69ce62412e3a3c7b00644835 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 10 Sep 2012 09:16:07 +0200 Subject: usb: gadget: remove usb_gadget_controller_number() The bcdDevice field is defined as |Device release number in binary-coded decimal in the USB 2.0 specification. We use this field to distinguish the UDCs from each other. In theory this could be used on the host side to apply certain quirks if the "special" UDC in combination with this gadget is used. This hasn't been done as far as I am aware. In practice it would be better to fix the UDC driver before shipping since a later release might not need this quirk anymore. There are some driver in tree (on the host side) which use the bcdDevice field to figure out special workarounds for a given firmware revision. This seems to make sense. Therefore this patch converts all gadgets (except a few) to use the kernel version instead a random 2 or 3 plus the UDC number. The few that don't report kernel's version are: - webcam This one reports always a version 0x10 so allow it to do so in future. - nokia This one reports always 0x211. The comment says that this gadget works only if the UDC supports altsettings so I added a check for this. - serial This one reports 0x2400 + UDC number. Since the gadget version is 2.4 this could make sense. Therefore bcdDevice is 0x2400 here. I also remove various gadget_is_ macros which are unused. The remaining few macros should be moved to feature / bug bitfield. Acked-by: Michal Nazarewicz Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Makefile | 2 +- drivers/usb/gadget/acm_ms.c | 13 ----- drivers/usb/gadget/audio.c | 12 ----- drivers/usb/gadget/cdc2.c | 16 ------- drivers/usb/gadget/composite.c | 2 + drivers/usb/gadget/ether.c | 17 ------- drivers/usb/gadget/f_mass_storage.c | 15 +----- drivers/usb/gadget/file_storage.c | 14 ++---- drivers/usb/gadget/gadget_chips.c | 94 ------------------------------------- drivers/usb/gadget/gadget_chips.h | 23 --------- drivers/usb/gadget/gmidi.c | 16 +------ drivers/usb/gadget/hid.c | 8 +--- drivers/usb/gadget/multi.c | 11 +---- drivers/usb/gadget/ncm.c | 17 ------- drivers/usb/gadget/nokia.c | 14 +----- drivers/usb/gadget/printer.c | 11 ----- drivers/usb/gadget/serial.c | 22 +-------- drivers/usb/gadget/zero.c | 17 ------- include/linux/usb/composite.h | 11 +++++ 19 files changed, 24 insertions(+), 311 deletions(-) delete mode 100644 drivers/usb/gadget/gadget_chips.c (limited to 'include') diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 5c4a1330b498..307be5fa824c 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -6,7 +6,7 @@ ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG obj-$(CONFIG_USB_GADGET) += udc-core.o obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o libcomposite-y := usbstring.o config.o epautoconf.o -libcomposite-y += gadget_chips.o composite.o +libcomposite-y += composite.o obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o obj-$(CONFIG_USB_NET2272) += net2272.o obj-$(CONFIG_USB_NET2280) += net2280.o diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/acm_ms.c index b0abc2518b38..5a7f289805ff 100644 --- a/drivers/usb/gadget/acm_ms.c +++ b/drivers/usb/gadget/acm_ms.c @@ -148,7 +148,6 @@ static struct usb_configuration acm_ms_config_driver = { static int __init acm_ms_bind(struct usb_composite_dev *cdev) { - int gcnum; struct usb_gadget *gadget = cdev->gadget; int status; void *retp; @@ -165,18 +164,6 @@ static int __init acm_ms_bind(struct usb_composite_dev *cdev) goto fail0; } - /* set bcdDevice */ - gcnum = usb_gadget_controller_number(gadget); - if (gcnum >= 0) { - device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); - } else { - WARNING(cdev, "controller '%s' not recognized; trying %s\n", - gadget->name, - acm_ms_config_driver.label); - device_desc.bcdDevice = - cpu_to_le16(0x0300 | 0x0099); - } - /* * Allocate string descriptor numbers ... note that string * contents can be overridden by the composite_dev glue. diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c index 1b9dee91d686..231b0efe8fdc 100644 --- a/drivers/usb/gadget/audio.c +++ b/drivers/usb/gadget/audio.c @@ -135,20 +135,8 @@ static struct usb_configuration audio_config_driver = { static int __init audio_bind(struct usb_composite_dev *cdev) { - int gcnum; int status; - gcnum = usb_gadget_controller_number(cdev->gadget); - if (gcnum >= 0) - device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); - else { - ERROR(cdev, "controller '%s' not recognized; trying %s\n", - cdev->gadget->name, - audio_config_driver.label); - device_desc.bcdDevice = - __constant_cpu_to_le16(0x0300 | 0x0099); - } - status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) goto fail; diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index ba38d2a43723..1e4bb77f00bb 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -143,7 +143,6 @@ static struct usb_configuration cdc_config_driver = { static int __init cdc_bind(struct usb_composite_dev *cdev) { - int gcnum; struct usb_gadget *gadget = cdev->gadget; int status; @@ -163,21 +162,6 @@ static int __init cdc_bind(struct usb_composite_dev *cdev) if (status < 0) goto fail0; - gcnum = usb_gadget_controller_number(gadget); - if (gcnum >= 0) - device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); - else { - /* We assume that can_support_ecm() tells the truth; - * but if the controller isn't recognized at all then - * that assumption is a bit more likely to be wrong. - */ - WARNING(cdev, "controller '%s' not recognized; trying %s\n", - gadget->name, - cdc_config_driver.label); - device_desc.bcdDevice = - cpu_to_le16(0x0300 | 0x0099); - } - /* Allocate string descriptor numbers ... note that string * contents can be overridden by the composite_dev glue. */ diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index cacb273ba708..957f973dd96a 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1389,6 +1389,8 @@ static void update_unchanged_dev_desc(struct usb_device_descriptor *new, new->idProduct = idProduct; if (bcdDevice) new->bcdDevice = bcdDevice; + else + new->bcdDevice = cpu_to_le16(get_default_bcdDevice()); if (iSerialNumber) new->iSerialNumber = iSerialNumber; if (iManufacturer) diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index d7ec87e0bfcb..18c3f423706e 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -275,7 +275,6 @@ static struct usb_configuration eth_config_driver = { static int __init eth_bind(struct usb_composite_dev *cdev) { - int gcnum; struct usb_gadget *gadget = cdev->gadget; int status; @@ -310,22 +309,6 @@ static int __init eth_bind(struct usb_composite_dev *cdev) device_desc.bNumConfigurations = 2; } - gcnum = usb_gadget_controller_number(gadget); - if (gcnum >= 0) - device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); - else { - /* We assume that can_support_ecm() tells the truth; - * but if the controller isn't recognized at all then - * that assumption is a bit more likely to be wrong. - */ - dev_warn(&gadget->dev, - "controller '%s' not recognized; trying %s\n", - gadget->name, - eth_config_driver.label); - device_desc.bcdDevice = - cpu_to_le16(0x0300 | 0x0099); - } - /* Allocate string descriptor numbers ... note that string * contents can be overridden by the composite_dev glue. */ diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 11150960d88b..3a7668bde3ef 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -348,7 +348,6 @@ struct fsg_config { const char *vendor_name; /* 8 characters or less */ const char *product_name; /* 16 characters or less */ - u16 release; char can_stall; }; @@ -2772,18 +2771,7 @@ buffhds_first_it: bh->next = common->buffhds; /* Prepare inquiryString */ - if (cfg->release != 0xffff) { - i = cfg->release; - } else { - i = usb_gadget_controller_number(gadget); - if (i >= 0) { - i = 0x0300 + i; - } else { - WARNING(common, "controller '%s' not recognized\n", - gadget->name); - i = 0x0399; - } - } + i = get_default_bcdDevice(); snprintf(common->inquiry_string, sizeof common->inquiry_string, "%-8s%-16s%04x", cfg->vendor_name ?: "Linux", /* Assume product name dependent on the first LUN */ @@ -3109,7 +3097,6 @@ fsg_config_from_params(struct fsg_config *cfg, /* Let MSF use defaults */ cfg->vendor_name = 0; cfg->product_name = 0; - cfg->release = 0xffff; cfg->ops = NULL; cfg->private_data = NULL; diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index ce362f7e39d3..3f7d640b6758 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -251,6 +251,7 @@ #include #include +#include #include #include @@ -3198,7 +3199,6 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) static int __init check_parameters(struct fsg_dev *fsg) { int prot; - int gcnum; /* Store the default values */ mod_data.transport_type = USB_PR_BULK; @@ -3213,16 +3213,8 @@ static int __init check_parameters(struct fsg_dev *fsg) if (gadget_is_at91(fsg->gadget)) mod_data.can_stall = 0; - if (mod_data.release == 0xffff) { // Parameter wasn't set - gcnum = usb_gadget_controller_number(fsg->gadget); - if (gcnum >= 0) - mod_data.release = 0x0300 + gcnum; - else { - WARNING(fsg, "controller '%s' not recognized\n", - fsg->gadget->name); - mod_data.release = 0x0399; - } - } + if (mod_data.release == 0xffff) + mod_data.release = get_default_bcdDevice(); prot = simple_strtol(mod_data.protocol_parm, NULL, 0); diff --git a/drivers/usb/gadget/gadget_chips.c b/drivers/usb/gadget/gadget_chips.c deleted file mode 100644 index 6387d43d3f8c..000000000000 --- a/drivers/usb/gadget/gadget_chips.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * USB device controllers have lots of quirks. Use these macros in - * gadget drivers or other code that needs to deal with them, and which - * autoconfigures instead of using early binding to the hardware. - * - * This SHOULD eventually work like the ARM mach_is_*() stuff, driven by - * some config file that gets updated as new hardware is supported. - * (And avoiding all runtime comparisons in typical one-choice configs!) - * - * NOTE: some of these controller drivers may not be available yet. - * Some are available on 2.4 kernels; several are available, but not - * yet pushed in the 2.6 mainline tree. - */ - -#include -#include - -#include "gadget_chips.h" - -/** - * usb_gadget_controller_number - support bcdDevice id convention - * @gadget: the controller being driven - * - * Return a 2-digit BCD value associated with the peripheral controller, - * suitable for use as part of a bcdDevice value, or a negative error code. - * - * NOTE: this convention is purely optional, and has no meaning in terms of - * any USB specification. If you want to use a different convention in your - * gadget driver firmware -- maybe a more formal revision ID -- feel free. - * - * Hosts see these bcdDevice numbers, and are allowed (but not encouraged!) - * to change their behavior accordingly. For example it might help avoiding - * some chip bug. - */ -int usb_gadget_controller_number(struct usb_gadget *gadget) -{ - if (gadget_is_net2280(gadget)) - return 0x01; - else if (gadget_is_dummy(gadget)) - return 0x02; - else if (gadget_is_pxa(gadget)) - return 0x03; - else if (gadget_is_goku(gadget)) - return 0x06; - else if (gadget_is_omap(gadget)) - return 0x08; - else if (gadget_is_pxa27x(gadget)) - return 0x11; - else if (gadget_is_s3c2410(gadget)) - return 0x12; - else if (gadget_is_at91(gadget)) - return 0x13; - else if (gadget_is_imx(gadget)) - return 0x14; - else if (gadget_is_musbhdrc(gadget)) - return 0x16; - else if (gadget_is_atmel_usba(gadget)) - return 0x18; - else if (gadget_is_fsl_usb2(gadget)) - return 0x19; - else if (gadget_is_amd5536udc(gadget)) - return 0x20; - else if (gadget_is_m66592(gadget)) - return 0x21; - else if (gadget_is_fsl_qe(gadget)) - return 0x22; - else if (gadget_is_ci13xxx_pci(gadget)) - return 0x23; - else if (gadget_is_langwell(gadget)) - return 0x24; - else if (gadget_is_r8a66597(gadget)) - return 0x25; - else if (gadget_is_s3c_hsotg(gadget)) - return 0x26; - else if (gadget_is_pch(gadget)) - return 0x27; - else if (gadget_is_ci13xxx_msm(gadget)) - return 0x28; - else if (gadget_is_renesas_usbhs(gadget)) - return 0x29; - else if (gadget_is_s3c_hsudc(gadget)) - return 0x30; - else if (gadget_is_net2272(gadget)) - return 0x31; - else if (gadget_is_dwc3(gadget)) - return 0x32; - else if (gadget_is_lpc32xx(gadget)) - return 0x33; - else if (gadget_is_bcm63xx(gadget)) - return 0x34; - - return -ENOENT; -} -EXPORT_SYMBOL_GPL(usb_gadget_controller_number); diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index c7055cd1e92e..bcd04bc66b98 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -27,35 +27,12 @@ * If you have forgotten the alphabetical order let VIM/EMACS * do that for you. */ -#define gadget_is_amd5536udc(g) (!strcmp("amd5536udc", (g)->name)) #define gadget_is_at91(g) (!strcmp("at91_udc", (g)->name)) -#define gadget_is_atmel_usba(g) (!strcmp("atmel_usba_udc", (g)->name)) -#define gadget_is_bcm63xx(g) (!strcmp("bcm63xx_udc", (g)->name)) -#define gadget_is_ci13xxx_msm(g) (!strcmp("ci13xxx_msm", (g)->name)) -#define gadget_is_ci13xxx_pci(g) (!strcmp("ci13xxx_pci", (g)->name)) -#define gadget_is_dummy(g) (!strcmp("dummy_udc", (g)->name)) -#define gadget_is_dwc3(g) (!strcmp("dwc3-gadget", (g)->name)) -#define gadget_is_fsl_qe(g) (!strcmp("fsl_qe_udc", (g)->name)) -#define gadget_is_fsl_usb2(g) (!strcmp("fsl-usb2-udc", (g)->name)) #define gadget_is_goku(g) (!strcmp("goku_udc", (g)->name)) -#define gadget_is_imx(g) (!strcmp("imx_udc", (g)->name)) -#define gadget_is_langwell(g) (!strcmp("langwell_udc", (g)->name)) -#define gadget_is_lpc32xx(g) (!strcmp("lpc32xx_udc", (g)->name)) -#define gadget_is_m66592(g) (!strcmp("m66592_udc", (g)->name)) #define gadget_is_musbhdrc(g) (!strcmp("musb-hdrc", (g)->name)) -#define gadget_is_net2272(g) (!strcmp("net2272", (g)->name)) #define gadget_is_net2280(g) (!strcmp("net2280", (g)->name)) -#define gadget_is_omap(g) (!strcmp("omap_udc", (g)->name)) -#define gadget_is_pch(g) (!strcmp("pch_udc", (g)->name)) #define gadget_is_pxa(g) (!strcmp("pxa25x_udc", (g)->name)) #define gadget_is_pxa27x(g) (!strcmp("pxa27x_udc", (g)->name)) -#define gadget_is_r8a66597(g) (!strcmp("r8a66597_udc", (g)->name)) -#define gadget_is_renesas_usbhs(g) (!strcmp("renesas_usbhs_udc", (g)->name)) -#define gadget_is_s3c2410(g) (!strcmp("s3c2410_udc", (g)->name)) -#define gadget_is_s3c_hsotg(g) (!strcmp("s3c-hsotg", (g)->name)) -#define gadget_is_s3c_hsudc(g) (!strcmp("s3c-hsudc", (g)->name)) - -int usb_gadget_controller_number(struct usb_gadget *gadget); /** * gadget_supports_altsettings - return true if altsettings work diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index 4181524d1c4b..881aab86ae99 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -137,8 +137,7 @@ static int __init midi_bind_config(struct usb_configuration *c) static int __init midi_bind(struct usb_composite_dev *cdev) { - struct usb_gadget *gadget = cdev->gadget; - int gcnum, status; + int status; status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) @@ -147,19 +146,6 @@ static int __init midi_bind(struct usb_composite_dev *cdev) device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; midi_config.iConfiguration = strings_dev[STRING_DESCRIPTION_IDX].id; - gcnum = usb_gadget_controller_number(gadget); - if (gcnum < 0) { - /* gmidi is so simple (no altsettings) that - * it SHOULD NOT have problems with bulk-capable hardware. - * so warn about unrecognized controllers, don't panic. - */ - pr_warning("%s: controller '%s' not recognized\n", - __func__, gadget->name); - device_desc.bcdDevice = cpu_to_le16(0x9999); - } else { - device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); - } - status = usb_add_config(cdev, &midi_config, midi_bind_config); if (status < 0) return status; diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/hid.c index 5fa1bfa8c6c6..74130f6c12c0 100644 --- a/drivers/usb/gadget/hid.c +++ b/drivers/usb/gadget/hid.c @@ -143,7 +143,7 @@ static int __init hid_bind(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget; struct list_head *tmp; - int status, gcnum, funcs = 0; + int status, funcs = 0; list_for_each(tmp, &hidg_func_list) funcs++; @@ -156,12 +156,6 @@ static int __init hid_bind(struct usb_composite_dev *cdev) if (status < 0) return status; - gcnum = usb_gadget_controller_number(gadget); - if (gcnum >= 0) - device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); - else - device_desc.bcdDevice = cpu_to_le16(0x0300 | 0x0099); - /* Allocate string descriptor numbers ... note that string * contents can be overridden by the composite_dev glue. */ diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 9d2e75c017dc..88472bf7dbb7 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -247,7 +247,7 @@ static int cdc_config_register(struct usb_composite_dev *cdev) static int __ref multi_bind(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget; - int status, gcnum; + int status; if (!can_support_ecm(cdev->gadget)) { dev_err(&gadget->dev, "controller '%s' not usable\n", @@ -275,15 +275,6 @@ static int __ref multi_bind(struct usb_composite_dev *cdev) } } - /* set bcdDevice */ - gcnum = usb_gadget_controller_number(gadget); - if (gcnum >= 0) { - device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); - } else { - WARNING(cdev, "controller '%s' not recognized\n", gadget->name); - device_desc.bcdDevice = cpu_to_le16(0x0300 | 0x0099); - } - /* allocate string IDs */ status = usb_string_ids_tab(cdev, strings_dev); if (unlikely(status < 0)) diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c index b21ec269b8f5..a22ad9af0565 100644 --- a/drivers/usb/gadget/ncm.c +++ b/drivers/usb/gadget/ncm.c @@ -139,7 +139,6 @@ static struct usb_configuration ncm_config_driver = { static int __init gncm_bind(struct usb_composite_dev *cdev) { - int gcnum; struct usb_gadget *gadget = cdev->gadget; int status; @@ -148,22 +147,6 @@ static int __init gncm_bind(struct usb_composite_dev *cdev) if (status < 0) return status; - gcnum = usb_gadget_controller_number(gadget); - if (gcnum >= 0) - device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); - else { - /* We assume that can_support_ecm() tells the truth; - * but if the controller isn't recognized at all then - * that assumption is a bit more likely to be wrong. - */ - dev_warn(&gadget->dev, - "controller '%s' not recognized; trying %s\n", - gadget->name, - ncm_config_driver.label); - device_desc.bcdDevice = - cpu_to_le16(0x0300 | 0x0099); - } - /* Allocate string descriptor numbers ... note that string * contents can be overridden by the composite_dev glue. */ diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index ad0cc3473073..661600abace8 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -84,6 +84,7 @@ static struct usb_device_descriptor device_desc = { .bDeviceClass = USB_CLASS_COMM, .idVendor = __constant_cpu_to_le16(NOKIA_VENDOR_ID), .idProduct = __constant_cpu_to_le16(NOKIA_PRODUCT_ID), + .bcdDevice = cpu_to_le16(NOKIA_VERSION_NUM), /* .iManufacturer = DYNAMIC */ /* .iProduct = DYNAMIC */ .bNumConfigurations = 1, @@ -145,7 +146,6 @@ static struct usb_configuration nokia_config_100ma_driver = { static int __init nokia_bind(struct usb_composite_dev *cdev) { - int gcnum; struct usb_gadget *gadget = cdev->gadget; int status; @@ -170,18 +170,8 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) nokia_config_500ma_driver.iConfiguration = status; nokia_config_100ma_driver.iConfiguration = status; - /* set up other descriptors */ - gcnum = usb_gadget_controller_number(gadget); - if (gcnum >= 0) - device_desc.bcdDevice = cpu_to_le16(NOKIA_VERSION_NUM); - else { - /* this should only work with hw that supports altsettings - * and several endpoints, anything else, panic. - */ - pr_err("nokia_bind: controller '%s' not recognized\n", - gadget->name); + if (!gadget_supports_altsettings(gadget)) goto err_usb; - } /* finally register the configuration */ status = usb_add_config(cdev, &nokia_config_500ma_driver, diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index e394050afd02..e156e3f26727 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -1101,7 +1101,6 @@ static int __init printer_bind_config(struct usb_configuration *c) struct usb_gadget *gadget = c->cdev->gadget; struct printer_dev *dev; int status = -ENOMEM; - int gcnum; size_t len; u32 i; struct usb_request *req; @@ -1143,16 +1142,6 @@ static int __init printer_bind_config(struct usb_configuration *c) goto fail; } - gcnum = usb_gadget_controller_number(gadget); - if (gcnum >= 0) { - device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); - } else { - dev_warn(&gadget->dev, "controller '%s' not recognized\n", - gadget->name); - /* unrecognized, but safe unless bulk is REALLY quirky */ - device_desc.bcdDevice = - cpu_to_le16(0xFFFF); - } if (iPNPstring) strlcpy(&pnp_string[2], iPNPstring, (sizeof pnp_string)-2); diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index f17d4626af55..942067327d88 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -86,7 +86,7 @@ static struct usb_device_descriptor device_desc = { /* .bMaxPacketSize0 = f(hardware) */ .idVendor = cpu_to_le16(GS_VENDOR_ID), /* .idProduct = f(use_acm) */ - /* .bcdDevice = f(hardware) */ + .bcdDevice = cpu_to_le16(GS_VERSION_NUM << 16), /* .iManufacturer = DYNAMIC */ /* .iProduct = DYNAMIC */ .bNumConfigurations = 1, @@ -154,8 +154,6 @@ static struct usb_configuration serial_config_driver = { static int __init gs_bind(struct usb_composite_dev *cdev) { - int gcnum; - struct usb_gadget *gadget = cdev->gadget; int status; status = gserial_setup(cdev->gadget, n_ports); @@ -174,24 +172,6 @@ static int __init gs_bind(struct usb_composite_dev *cdev) status = strings_dev[STRING_DESCRIPTION_IDX].id; serial_config_driver.iConfiguration = status; - /* set up other descriptors */ - gcnum = usb_gadget_controller_number(gadget); - if (gcnum >= 0) - device_desc.bcdDevice = cpu_to_le16(GS_VERSION_NUM | gcnum); - else { - /* this is so simple (for now, no altsettings) that it - * SHOULD NOT have problems with bulk-capable hardware. - * so warn about unrcognized controllers -- don't panic. - * - * things like configuration and altsetting numbering - * can need hardware-specific attention though. - */ - pr_warning("gs_bind: controller '%s' not recognized\n", - gadget->name); - device_desc.bcdDevice = - cpu_to_le16(GS_VERSION_NUM | 0x0099); - } - if (gadget_is_otg(cdev->gadget)) { serial_config_driver.descriptors = otg_desc; serial_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index 0dabffa5f493..6bf4c0611365 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -253,8 +253,6 @@ static void zero_resume(struct usb_composite_dev *cdev) static int __init zero_bind(struct usb_composite_dev *cdev) { - int gcnum; - struct usb_gadget *gadget = cdev->gadget; int status; /* Allocate string descriptor numbers ... note that string @@ -281,21 +279,6 @@ static int __init zero_bind(struct usb_composite_dev *cdev) loopback_add(cdev, autoresume != 0); } - gcnum = usb_gadget_controller_number(gadget); - if (gcnum >= 0) - device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); - else { - /* gadget zero is so simple (for now, no altsettings) that - * it SHOULD NOT have problems with bulk-capable hardware. - * so just warn about unrcognized controllers -- don't panic. - * - * things like configuration and altsetting numbering - * can need hardware-specific attention though. - */ - pr_warning("%s: controller '%s' not recognized\n", - longname, gadget->name); - device_desc.bcdDevice = cpu_to_le16(0x9999); - } usb_composite_overwrite_options(cdev, &coverwrite); INFO(cdev, "%s, version: " DRIVER_VERSION "\n", longname); diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 65ae0a3feb5b..f8dda0621800 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -34,6 +34,8 @@ * the composite model the host can use both functions at the same time. */ +#include +#include #include #include @@ -418,6 +420,15 @@ struct usb_composite_overwrite { void usb_composite_overwrite_options(struct usb_composite_dev *cdev, struct usb_composite_overwrite *covr); +static inline u16 get_default_bcdDevice(void) +{ + u16 bcdDevice; + + bcdDevice = bin2bcd((LINUX_VERSION_CODE >> 16 & 0xff)) << 8; + bcdDevice |= bin2bcd((LINUX_VERSION_CODE >> 8 & 0xff)); + return bcdDevice; +} + /* messaging utils */ #define DBG(d, fmt, args...) \ dev_dbg(&(d)->gadget->dev , fmt , ## args) -- cgit v1.2.3 From 0837e7e5270bd5547ba5763f11611dc43f677b3d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 8 Sep 2012 20:02:05 +0200 Subject: usbfs: Add a new disconnect-and-claim ioctl (v2) Apps which deal with devices which also have a kernel driver, need to do the following: 1) Check which driver is attached, so as to not detach the wrong driver (ie detaching usbfs while another instance of the app is using the device) 2) Detach the kernel driver 3) Claim the interface Where moving from one step to the next for both 1-2 and 2-3 consists of a (small) race window. So currently such apps are racy and people just live with it. This patch adds a new ioctl which makes it possible for apps to do this in a race free manner. For flexibility apps can choose to: 1) Specify the driver to disconnect 2) Specify to disconnect any driver except for the one named by the app 3) Disconnect any driver Note that if there is no driver attached, the ioctl will just act like the regular claim-interface ioctl, this is by design, as returning an error for this condition would open a new bag of race-conditions. Changes in v2: -Fix indentation of if blocks where the condition spans multiple lines Signed-off-by: Hans de Goede Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 35 +++++++++++++++++++++++++++++++++++ include/linux/usbdevice_fs.h | 14 ++++++++++++++ 2 files changed, 49 insertions(+) (limited to 'include') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index ebb8a9de8b5f..e0356cb859b5 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1928,6 +1928,38 @@ static int proc_get_capabilities(struct dev_state *ps, void __user *arg) return 0; } +static int proc_disconnect_claim(struct dev_state *ps, void __user *arg) +{ + struct usbdevfs_disconnect_claim dc; + struct usb_interface *intf; + + if (copy_from_user(&dc, arg, sizeof(dc))) + return -EFAULT; + + intf = usb_ifnum_to_if(ps->dev, dc.interface); + if (!intf) + return -EINVAL; + + if (intf->dev.driver) { + struct usb_driver *driver = to_usb_driver(intf->dev.driver); + + if ((dc.flags & USBDEVFS_DISCONNECT_CLAIM_IF_DRIVER) && + strncmp(dc.driver, intf->dev.driver->name, + sizeof(dc.driver)) != 0) + return -EBUSY; + + if ((dc.flags & USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER) && + strncmp(dc.driver, intf->dev.driver->name, + sizeof(dc.driver)) == 0) + return -EBUSY; + + dev_dbg(&intf->dev, "disconnect by usbfs\n"); + usb_driver_release_interface(driver, intf); + } + + return claimintf(ps, dc.interface); +} + /* * NOTE: All requests here that have interface numbers as parameters * are assuming that somehow the configuration has been prevented from @@ -2101,6 +2133,9 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, case USBDEVFS_GET_CAPABILITIES: ret = proc_get_capabilities(ps, p); break; + case USBDEVFS_DISCONNECT_CLAIM: + ret = proc_disconnect_claim(ps, p); + break; } usb_unlock_device(dev); if (ret >= 0) diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h index 3b74666be027..4abe28e41cbc 100644 --- a/include/linux/usbdevice_fs.h +++ b/include/linux/usbdevice_fs.h @@ -131,6 +131,19 @@ struct usbdevfs_hub_portinfo { #define USBDEVFS_CAP_NO_PACKET_SIZE_LIM 0x04 #define USBDEVFS_CAP_BULK_SCATTER_GATHER 0x08 +/* USBDEVFS_DISCONNECT_CLAIM flags & struct */ + +/* disconnect-and-claim if the driver matches the driver field */ +#define USBDEVFS_DISCONNECT_CLAIM_IF_DRIVER 0x01 +/* disconnect-and-claim except when the driver matches the driver field */ +#define USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER 0x02 + +struct usbdevfs_disconnect_claim { + unsigned int interface; + unsigned int flags; + char driver[USBDEVFS_MAXDRIVERNAME + 1]; +}; + #ifdef __KERNEL__ #ifdef CONFIG_COMPAT #include @@ -211,5 +224,6 @@ struct usbdevfs_ioctl32 { #define USBDEVFS_CLAIM_PORT _IOR('U', 24, unsigned int) #define USBDEVFS_RELEASE_PORT _IOR('U', 25, unsigned int) #define USBDEVFS_GET_CAPABILITIES _IOR('U', 26, __u32) +#define USBDEVFS_DISCONNECT_CLAIM _IOR('U', 27, struct usbdevfs_disconnect_claim) #endif /* _LINUX_USBDEVICE_FS_H */ -- cgit v1.2.3 From b53d657d84f530e5d83f34ff1b81ceedad3faa31 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 7 Sep 2012 14:31:45 +0200 Subject: usb/core: use bin2bcd() for bcdDevice in RH The kernel's version number is used as decimal in the bcdDevice field of the RH descriptor. For kernel version v3.12 we would see 3.0c in lsusb. I am not sure how important it is to stick with bcd values since this is this way since we started git history and nobody complained (however back then we reported only 2.6). Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 6 +++--- include/linux/bcd.h | 17 +++++++++++++++-- lib/bcd.c | 8 ++++---- 3 files changed, 22 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index bc84106ac057..6e4fd28bc242 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -22,6 +22,7 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include #include #include #include @@ -123,9 +124,8 @@ static inline int is_root_hub(struct usb_device *udev) */ /*-------------------------------------------------------------------------*/ - -#define KERNEL_REL ((LINUX_VERSION_CODE >> 16) & 0x0ff) -#define KERNEL_VER ((LINUX_VERSION_CODE >> 8) & 0x0ff) +#define KERNEL_REL bin2bcd(((LINUX_VERSION_CODE >> 16) & 0x0ff)) +#define KERNEL_VER bin2bcd(((LINUX_VERSION_CODE >> 8) & 0x0ff)) /* usb 3.0 root hub device descriptor */ static const u8 usb3_rh_dev_descriptor[18] = { diff --git a/include/linux/bcd.h b/include/linux/bcd.h index 22ea563ba3eb..18fff11fb3ea 100644 --- a/include/linux/bcd.h +++ b/include/linux/bcd.h @@ -3,7 +3,20 @@ #include -unsigned bcd2bin(unsigned char val) __attribute_const__; -unsigned char bin2bcd(unsigned val) __attribute_const__; +#define bcd2bin(x) \ + (__builtin_constant_p((u8 )(x)) ? \ + const_bcd2bin(x) : \ + _bcd2bin(x)) + +#define bin2bcd(x) \ + (__builtin_constant_p((u8 )(x)) ? \ + const_bin2bcd(x) : \ + _bin2bcd(x)) + +#define const_bcd2bin(x) (((x) & 0x0f) + ((x) >> 4) * 10) +#define const_bin2bcd(x) ((((x) / 10) << 4) + (x) % 10) + +unsigned _bcd2bin(unsigned char val) __attribute_const__; +unsigned char _bin2bcd(unsigned val) __attribute_const__; #endif /* _BCD_H */ diff --git a/lib/bcd.c b/lib/bcd.c index 55efaf742346..40d304efe272 100644 --- a/lib/bcd.c +++ b/lib/bcd.c @@ -1,14 +1,14 @@ #include #include -unsigned bcd2bin(unsigned char val) +unsigned _bcd2bin(unsigned char val) { return (val & 0x0f) + (val >> 4) * 10; } -EXPORT_SYMBOL(bcd2bin); +EXPORT_SYMBOL(_bcd2bin); -unsigned char bin2bcd(unsigned val) +unsigned char _bin2bcd(unsigned val) { return ((val / 10) << 4) + val % 10; } -EXPORT_SYMBOL(bin2bcd); +EXPORT_SYMBOL(_bin2bcd); -- cgit v1.2.3 From 9c2089045b87130e8464ca7e21725959446d7f0c Mon Sep 17 00:00:00 2001 From: Lan Tianyu Date: Mon, 10 Sep 2012 21:24:41 +0800 Subject: usb: redefine DeviceRemovable and wHubDelay as _le16 DeviceRemovalbe and wHubDelay for usb3.0 hub are little-endian and so define them as _le16. Signed-off-by: Lan Tianyu Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/ch11.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/usb/ch11.h b/include/linux/usb/ch11.h index b6c2863b2c94..7692dc69ccf7 100644 --- a/include/linux/usb/ch11.h +++ b/include/linux/usb/ch11.h @@ -236,8 +236,8 @@ struct usb_hub_descriptor { struct { __u8 bHubHdrDecLat; - __u16 wHubDelay; - __u16 DeviceRemovable; + __le16 wHubDelay; + __le16 DeviceRemovable; } __attribute__ ((packed)) ss; } u; } __attribute__ ((packed)); -- cgit v1.2.3 From 15e473046cb6e5d18a4d0057e61d76315230382b Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 7 Sep 2012 20:12:54 +0000 Subject: netlink: Rename pid to portid to avoid confusion It is a frequent mistake to confuse the netlink port identifier with a process identifier. Try to reduce this confusion by renaming fields that hold port identifiers portid instead of pid. I have carefully avoided changing the structures exported to userspace to avoid changing the userspace API. I have successfully built an allyesconfig kernel with this change. Signed-off-by: "Eric W. Biederman" Acked-by: Stephen Hemminger Signed-off-by: David S. Miller --- crypto/crypto_user.c | 4 +- drivers/net/team/team.c | 38 +++--- drivers/net/wireless/mac80211_hwsim.c | 46 ++++---- drivers/scsi/scsi_transport_iscsi.c | 4 +- drivers/staging/gdm72xx/netlink_k.c | 2 +- fs/dlm/netlink.c | 8 +- include/linux/netlink.h | 14 +-- include/net/cfg80211.h | 2 +- include/net/genetlink.h | 34 +++--- include/net/netfilter/nf_conntrack_ecache.h | 32 +++--- include/net/netlink.h | 26 ++--- include/net/nfc/nfc.h | 2 +- include/net/xfrm.h | 6 +- kernel/audit.c | 20 ++-- kernel/taskstats.c | 4 +- net/bridge/br_fdb.c | 6 +- net/bridge/br_netlink.c | 2 +- net/can/gw.c | 2 +- net/core/fib_rules.c | 6 +- net/core/neighbour.c | 8 +- net/core/rtnetlink.c | 12 +- net/dcb/dcbnl.c | 18 +-- net/decnet/dn_dev.c | 6 +- net/decnet/dn_route.c | 10 +- net/decnet/dn_table.c | 12 +- net/ieee802154/nl-mac.c | 6 +- net/ieee802154/nl-phy.c | 6 +- net/ipv4/devinet.c | 28 ++--- net/ipv4/fib_frontend.c | 10 +- net/ipv4/fib_semantics.c | 8 +- net/ipv4/fib_trie.c | 2 +- net/ipv4/inet_diag.c | 32 +++--- net/ipv4/ipmr.c | 10 +- net/ipv4/route.c | 8 +- net/ipv4/tcp_metrics.c | 2 +- net/ipv4/udp_diag.c | 6 +- net/ipv6/addrconf.c | 32 +++--- net/ipv6/addrlabel.c | 10 +- net/ipv6/ip6mr.c | 10 +- net/ipv6/route.c | 20 ++-- net/irda/irnetlink.c | 2 +- net/key/af_key.c | 32 +++--- net/l2tp/l2tp_netlink.c | 24 ++-- net/netfilter/ipset/ip_set_core.c | 24 ++-- net/netfilter/ipvs/ip_vs_ctl.c | 6 +- net/netfilter/nf_conntrack_ecache.c | 2 +- net/netfilter/nf_conntrack_netlink.c | 78 ++++++------- net/netfilter/nfnetlink_acct.c | 12 +- net/netfilter/nfnetlink_cthelper.c | 12 +- net/netfilter/nfnetlink_cttimeout.c | 12 +- net/netfilter/nfnetlink_log.c | 18 +-- net/netfilter/nfnetlink_queue_core.c | 28 ++--- net/netlabel/netlabel_cipso_v4.c | 2 +- net/netlabel/netlabel_mgmt.c | 4 +- net/netlabel/netlabel_unlabeled.c | 2 +- net/netlink/af_netlink.c | 172 ++++++++++++++-------------- net/netlink/genetlink.c | 42 +++---- net/nfc/netlink.c | 26 ++--- net/openvswitch/actions.c | 4 +- net/openvswitch/datapath.c | 84 +++++++------- net/openvswitch/datapath.h | 2 +- net/openvswitch/vport.c | 2 +- net/openvswitch/vport.h | 6 +- net/packet/diag.c | 6 +- net/phonet/pn_netlink.c | 14 +-- net/sched/act_api.c | 52 ++++----- net/sched/cls_api.c | 14 +-- net/sched/sch_api.c | 44 +++---- net/tipc/netlink.c | 2 +- net/unix/diag.c | 14 +-- net/wireless/core.h | 2 +- net/wireless/mlme.c | 16 +-- net/wireless/nl80211.c | 110 +++++++++--------- net/xfrm/xfrm_state.c | 10 +- net/xfrm/xfrm_user.c | 62 +++++----- 75 files changed, 728 insertions(+), 728 deletions(-) (limited to 'include') diff --git a/crypto/crypto_user.c b/crypto/crypto_user.c index 165914e63ef2..6bba414d0c61 100644 --- a/crypto/crypto_user.c +++ b/crypto/crypto_user.c @@ -166,7 +166,7 @@ static int crypto_report_alg(struct crypto_alg *alg, struct crypto_user_alg *ualg; int err = 0; - nlh = nlmsg_put(skb, NETLINK_CB(in_skb).pid, info->nlmsg_seq, + nlh = nlmsg_put(skb, NETLINK_CB(in_skb).portid, info->nlmsg_seq, CRYPTO_MSG_GETALG, sizeof(*ualg), info->nlmsg_flags); if (!nlh) { err = -EMSGSIZE; @@ -216,7 +216,7 @@ static int crypto_report(struct sk_buff *in_skb, struct nlmsghdr *in_nlh, if (err) return err; - return nlmsg_unicast(crypto_nlsk, skb, NETLINK_CB(in_skb).pid); + return nlmsg_unicast(crypto_nlsk, skb, NETLINK_CB(in_skb).portid); } static int crypto_dump_report(struct sk_buff *skb, struct netlink_callback *cb) diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index b4f67b55ef79..266af7b38ebc 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1886,7 +1886,7 @@ static int team_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info) if (!msg) return -ENOMEM; - hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq, + hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, &team_nl_family, 0, TEAM_CMD_NOOP); if (IS_ERR(hdr)) { err = PTR_ERR(hdr); @@ -1895,7 +1895,7 @@ static int team_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info) genlmsg_end(msg, hdr); - return genlmsg_unicast(genl_info_net(info), msg, info->snd_pid); + return genlmsg_unicast(genl_info_net(info), msg, info->snd_portid); err_msg_put: nlmsg_free(msg); @@ -1952,7 +1952,7 @@ static int team_nl_send_generic(struct genl_info *info, struct team *team, if (err < 0) goto err_fill; - err = genlmsg_unicast(genl_info_net(info), skb, info->snd_pid); + err = genlmsg_unicast(genl_info_net(info), skb, info->snd_portid); return err; err_fill: @@ -1961,11 +1961,11 @@ err_fill: } typedef int team_nl_send_func_t(struct sk_buff *skb, - struct team *team, u32 pid); + struct team *team, u32 portid); -static int team_nl_send_unicast(struct sk_buff *skb, struct team *team, u32 pid) +static int team_nl_send_unicast(struct sk_buff *skb, struct team *team, u32 portid) { - return genlmsg_unicast(dev_net(team->dev), skb, pid); + return genlmsg_unicast(dev_net(team->dev), skb, portid); } static int team_nl_fill_one_option_get(struct sk_buff *skb, struct team *team, @@ -2050,13 +2050,13 @@ nest_cancel: } static int __send_and_alloc_skb(struct sk_buff **pskb, - struct team *team, u32 pid, + struct team *team, u32 portid, team_nl_send_func_t *send_func) { int err; if (*pskb) { - err = send_func(*pskb, team, pid); + err = send_func(*pskb, team, portid); if (err) return err; } @@ -2066,7 +2066,7 @@ static int __send_and_alloc_skb(struct sk_buff **pskb, return 0; } -static int team_nl_send_options_get(struct team *team, u32 pid, u32 seq, +static int team_nl_send_options_get(struct team *team, u32 portid, u32 seq, int flags, team_nl_send_func_t *send_func, struct list_head *sel_opt_inst_list) { @@ -2083,11 +2083,11 @@ static int team_nl_send_options_get(struct team *team, u32 pid, u32 seq, struct team_option_inst, tmp_list); start_again: - err = __send_and_alloc_skb(&skb, team, pid, send_func); + err = __send_and_alloc_skb(&skb, team, portid, send_func); if (err) return err; - hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags | NLM_F_MULTI, + hdr = genlmsg_put(skb, portid, seq, &team_nl_family, flags | NLM_F_MULTI, TEAM_CMD_OPTIONS_GET); if (IS_ERR(hdr)) return PTR_ERR(hdr); @@ -2120,15 +2120,15 @@ start_again: goto start_again; send_done: - nlh = nlmsg_put(skb, pid, seq, NLMSG_DONE, 0, flags | NLM_F_MULTI); + nlh = nlmsg_put(skb, portid, seq, NLMSG_DONE, 0, flags | NLM_F_MULTI); if (!nlh) { - err = __send_and_alloc_skb(&skb, team, pid, send_func); + err = __send_and_alloc_skb(&skb, team, portid, send_func); if (err) goto errout; goto send_done; } - return send_func(skb, team, pid); + return send_func(skb, team, portid); nla_put_failure: err = -EMSGSIZE; @@ -2151,7 +2151,7 @@ static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info) list_for_each_entry(opt_inst, &team->option_inst_list, list) list_add_tail(&opt_inst->tmp_list, &sel_opt_inst_list); - err = team_nl_send_options_get(team, info->snd_pid, info->snd_seq, + err = team_nl_send_options_get(team, info->snd_portid, info->snd_seq, NLM_F_ACK, team_nl_send_unicast, &sel_opt_inst_list); @@ -2305,7 +2305,7 @@ team_put: } static int team_nl_fill_port_list_get(struct sk_buff *skb, - u32 pid, u32 seq, int flags, + u32 portid, u32 seq, int flags, struct team *team, bool fillall) { @@ -2313,7 +2313,7 @@ static int team_nl_fill_port_list_get(struct sk_buff *skb, void *hdr; struct team_port *port; - hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags, + hdr = genlmsg_put(skb, portid, seq, &team_nl_family, flags, TEAM_CMD_PORT_LIST_GET); if (IS_ERR(hdr)) return PTR_ERR(hdr); @@ -2362,7 +2362,7 @@ static int team_nl_fill_port_list_get_all(struct sk_buff *skb, struct genl_info *info, int flags, struct team *team) { - return team_nl_fill_port_list_get(skb, info->snd_pid, + return team_nl_fill_port_list_get(skb, info->snd_portid, info->snd_seq, NLM_F_ACK, team, true); } @@ -2415,7 +2415,7 @@ static struct genl_multicast_group team_change_event_mcgrp = { }; static int team_nl_send_multicast(struct sk_buff *skb, - struct team *team, u32 pid) + struct team *team, u32 portid) { return genlmsg_multicast_netns(dev_net(team->dev), skb, 0, team_change_event_mcgrp.id, GFP_KERNEL); diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 72b0456e41bf..9d45b3bb974c 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -38,7 +38,7 @@ MODULE_AUTHOR("Jouni Malinen"); MODULE_DESCRIPTION("Software simulator of 802.11 radio(s) for mac80211"); MODULE_LICENSE("GPL"); -static u32 wmediumd_pid; +static u32 wmediumd_portid; static int radios = 2; module_param(radios, int, 0444); @@ -545,7 +545,7 @@ static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data, static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, struct sk_buff *my_skb, - int dst_pid) + int dst_portid) { struct sk_buff *skb; struct mac80211_hwsim_data *data = hw->priv; @@ -619,7 +619,7 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, goto nla_put_failure; genlmsg_end(skb, msg_head); - genlmsg_unicast(&init_net, skb, dst_pid); + genlmsg_unicast(&init_net, skb, dst_portid); /* Enqueue the packet */ skb_queue_tail(&data->pending, my_skb); @@ -715,7 +715,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, { bool ack; struct ieee80211_tx_info *txi; - u32 _pid; + u32 _portid; mac80211_hwsim_monitor_rx(hw, skb); @@ -726,10 +726,10 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, } /* wmediumd mode check */ - _pid = ACCESS_ONCE(wmediumd_pid); + _portid = ACCESS_ONCE(wmediumd_portid); - if (_pid) - return mac80211_hwsim_tx_frame_nl(hw, skb, _pid); + if (_portid) + return mac80211_hwsim_tx_frame_nl(hw, skb, _portid); /* NO wmediumd detected, perfect medium simulation */ ack = mac80211_hwsim_tx_frame_no_nl(hw, skb); @@ -814,7 +814,7 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac, struct ieee80211_hw *hw = arg; struct sk_buff *skb; struct ieee80211_tx_info *info; - u32 _pid; + u32 _portid; hwsim_check_magic(vif); @@ -831,10 +831,10 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac, mac80211_hwsim_monitor_rx(hw, skb); /* wmediumd mode check */ - _pid = ACCESS_ONCE(wmediumd_pid); + _portid = ACCESS_ONCE(wmediumd_portid); - if (_pid) - return mac80211_hwsim_tx_frame_nl(hw, skb, _pid); + if (_portid) + return mac80211_hwsim_tx_frame_nl(hw, skb, _portid); mac80211_hwsim_tx_frame_no_nl(hw, skb); dev_kfree_skb(skb); @@ -1315,7 +1315,7 @@ static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif) struct hwsim_vif_priv *vp = (void *)vif->drv_priv; struct sk_buff *skb; struct ieee80211_pspoll *pspoll; - u32 _pid; + u32 _portid; if (!vp->assoc) return; @@ -1336,10 +1336,10 @@ static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif) memcpy(pspoll->ta, mac, ETH_ALEN); /* wmediumd mode check */ - _pid = ACCESS_ONCE(wmediumd_pid); + _portid = ACCESS_ONCE(wmediumd_portid); - if (_pid) - return mac80211_hwsim_tx_frame_nl(data->hw, skb, _pid); + if (_portid) + return mac80211_hwsim_tx_frame_nl(data->hw, skb, _portid); if (!mac80211_hwsim_tx_frame_no_nl(data->hw, skb)) printk(KERN_DEBUG "%s: PS-poll frame not ack'ed\n", __func__); @@ -1353,7 +1353,7 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac, struct hwsim_vif_priv *vp = (void *)vif->drv_priv; struct sk_buff *skb; struct ieee80211_hdr *hdr; - u32 _pid; + u32 _portid; if (!vp->assoc) return; @@ -1375,10 +1375,10 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac, memcpy(hdr->addr3, vp->bssid, ETH_ALEN); /* wmediumd mode check */ - _pid = ACCESS_ONCE(wmediumd_pid); + _portid = ACCESS_ONCE(wmediumd_portid); - if (_pid) - return mac80211_hwsim_tx_frame_nl(data->hw, skb, _pid); + if (_portid) + return mac80211_hwsim_tx_frame_nl(data->hw, skb, _portid); if (!mac80211_hwsim_tx_frame_no_nl(data->hw, skb)) printk(KERN_DEBUG "%s: nullfunc frame not ack'ed\n", __func__); @@ -1632,10 +1632,10 @@ static int hwsim_register_received_nl(struct sk_buff *skb_2, if (info == NULL) goto out; - wmediumd_pid = info->snd_pid; + wmediumd_portid = info->snd_portid; printk(KERN_DEBUG "mac80211_hwsim: received a REGISTER, " - "switching to wmediumd mode with pid %d\n", info->snd_pid); + "switching to wmediumd mode with pid %d\n", info->snd_portid); return 0; out: @@ -1672,10 +1672,10 @@ static int mac80211_hwsim_netlink_notify(struct notifier_block *nb, if (state != NETLINK_URELEASE) return NOTIFY_DONE; - if (notify->pid == wmediumd_pid) { + if (notify->portid == wmediumd_portid) { printk(KERN_INFO "mac80211_hwsim: wmediumd released netlink" " socket, switching to perfect channel medium\n"); - wmediumd_pid = 0; + wmediumd_portid = 0; } return NOTIFY_DONE; diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 519bd5303f3f..31969f2e13ce 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -2119,7 +2119,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) switch (nlh->nlmsg_type) { case ISCSI_UEVENT_CREATE_SESSION: err = iscsi_if_create_session(priv, ep, ev, - NETLINK_CB(skb).pid, + NETLINK_CB(skb).portid, ev->u.c_session.initial_cmdsn, ev->u.c_session.cmds_max, ev->u.c_session.queue_depth); @@ -2132,7 +2132,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) } err = iscsi_if_create_session(priv, ep, ev, - NETLINK_CB(skb).pid, + NETLINK_CB(skb).portid, ev->u.c_bound_session.initial_cmdsn, ev->u.c_bound_session.cmds_max, ev->u.c_bound_session.queue_depth); diff --git a/drivers/staging/gdm72xx/netlink_k.c b/drivers/staging/gdm72xx/netlink_k.c index 2109cab0a14c..20d0aec52e72 100644 --- a/drivers/staging/gdm72xx/netlink_k.c +++ b/drivers/staging/gdm72xx/netlink_k.c @@ -135,7 +135,7 @@ int netlink_send(struct sock *sock, int group, u16 type, void *msg, int len) } memcpy(nlmsg_data(nlh), msg, len); - NETLINK_CB(skb).pid = 0; + NETLINK_CB(skb).portid = 0; NETLINK_CB(skb).dst_group = 0; ret = netlink_broadcast(sock, skb, 0, group+1, GFP_ATOMIC); diff --git a/fs/dlm/netlink.c b/fs/dlm/netlink.c index ef17e0169da1..60a327863b11 100644 --- a/fs/dlm/netlink.c +++ b/fs/dlm/netlink.c @@ -14,7 +14,7 @@ #include "dlm_internal.h" static uint32_t dlm_nl_seqnum; -static uint32_t listener_nlpid; +static uint32_t listener_nlportid; static struct genl_family family = { .id = GENL_ID_GENERATE, @@ -64,13 +64,13 @@ static int send_data(struct sk_buff *skb) return rv; } - return genlmsg_unicast(&init_net, skb, listener_nlpid); + return genlmsg_unicast(&init_net, skb, listener_nlportid); } static int user_cmd(struct sk_buff *skb, struct genl_info *info) { - listener_nlpid = info->snd_pid; - printk("user_cmd nlpid %u\n", listener_nlpid); + listener_nlportid = info->snd_portid; + printk("user_cmd nlpid %u\n", listener_nlportid); return 0; } diff --git a/include/linux/netlink.h b/include/linux/netlink.h index cd17dda5a987..73ade5fbc856 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -165,7 +165,7 @@ static inline struct nlmsghdr *nlmsg_hdr(const struct sk_buff *skb) struct netlink_skb_parms { struct scm_creds creds; /* Skb credentials */ - __u32 pid; + __u32 portid; __u32 dst_group; struct sock *ssk; }; @@ -205,14 +205,14 @@ extern void __netlink_clear_multicast_users(struct sock *sk, unsigned int group) extern void netlink_clear_multicast_users(struct sock *sk, unsigned int group); extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err); extern int netlink_has_listeners(struct sock *sk, unsigned int group); -extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int nonblock); -extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 pid, +extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock); +extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 portid, __u32 group, gfp_t allocation); extern int netlink_broadcast_filtered(struct sock *ssk, struct sk_buff *skb, - __u32 pid, __u32 group, gfp_t allocation, + __u32 portid, __u32 group, gfp_t allocation, int (*filter)(struct sock *dsk, struct sk_buff *skb, void *data), void *filter_data); -extern int netlink_set_err(struct sock *ssk, __u32 pid, __u32 group, int code); +extern int netlink_set_err(struct sock *ssk, __u32 portid, __u32 group, int code); extern int netlink_register_notifier(struct notifier_block *nb); extern int netlink_unregister_notifier(struct notifier_block *nb); @@ -253,12 +253,12 @@ struct netlink_callback { struct netlink_notify { struct net *net; - int pid; + int portid; int protocol; }; struct nlmsghdr * -__nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len, int flags); +__nlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, int type, int len, int flags); struct netlink_dump_control { int (*dump)(struct sk_buff *skb, struct netlink_callback *); diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ba2e6160fad1..7b3b0568d289 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2458,7 +2458,7 @@ struct wireless_dev { int beacon_interval; - u32 ap_unexpected_nlpid; + u32 ap_unexpected_nlportid; #ifdef CONFIG_CFG80211_WEXT /* wext data */ diff --git a/include/net/genetlink.h b/include/net/genetlink.h index 48905cd3884c..bdfbe68c1c3b 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -65,7 +65,7 @@ struct genl_family { /** * struct genl_info - receiving information * @snd_seq: sending sequence number - * @snd_pid: netlink pid of sender + * @snd_portid: netlink portid of sender * @nlhdr: netlink message header * @genlhdr: generic netlink message header * @userhdr: user specific header @@ -75,7 +75,7 @@ struct genl_family { */ struct genl_info { u32 snd_seq; - u32 snd_pid; + u32 snd_portid; struct nlmsghdr * nlhdr; struct genlmsghdr * genlhdr; void * userhdr; @@ -130,10 +130,10 @@ extern int genl_register_mc_group(struct genl_family *family, struct genl_multicast_group *grp); extern void genl_unregister_mc_group(struct genl_family *family, struct genl_multicast_group *grp); -extern void genl_notify(struct sk_buff *skb, struct net *net, u32 pid, +extern void genl_notify(struct sk_buff *skb, struct net *net, u32 portid, u32 group, struct nlmsghdr *nlh, gfp_t flags); -void *genlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, +void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, struct genl_family *family, int flags, u8 cmd); /** @@ -183,7 +183,7 @@ static inline void *genlmsg_put_reply(struct sk_buff *skb, struct genl_family *family, int flags, u8 cmd) { - return genlmsg_put(skb, info->snd_pid, info->snd_seq, family, + return genlmsg_put(skb, info->snd_portid, info->snd_seq, family, flags, cmd); } @@ -212,49 +212,49 @@ static inline void genlmsg_cancel(struct sk_buff *skb, void *hdr) * genlmsg_multicast_netns - multicast a netlink message to a specific netns * @net: the net namespace * @skb: netlink message as socket buffer - * @pid: own netlink pid to avoid sending to yourself + * @portid: own netlink portid to avoid sending to yourself * @group: multicast group id * @flags: allocation flags */ static inline int genlmsg_multicast_netns(struct net *net, struct sk_buff *skb, - u32 pid, unsigned int group, gfp_t flags) + u32 portid, unsigned int group, gfp_t flags) { - return nlmsg_multicast(net->genl_sock, skb, pid, group, flags); + return nlmsg_multicast(net->genl_sock, skb, portid, group, flags); } /** * genlmsg_multicast - multicast a netlink message to the default netns * @skb: netlink message as socket buffer - * @pid: own netlink pid to avoid sending to yourself + * @portid: own netlink portid to avoid sending to yourself * @group: multicast group id * @flags: allocation flags */ -static inline int genlmsg_multicast(struct sk_buff *skb, u32 pid, +static inline int genlmsg_multicast(struct sk_buff *skb, u32 portid, unsigned int group, gfp_t flags) { - return genlmsg_multicast_netns(&init_net, skb, pid, group, flags); + return genlmsg_multicast_netns(&init_net, skb, portid, group, flags); } /** * genlmsg_multicast_allns - multicast a netlink message to all net namespaces * @skb: netlink message as socket buffer - * @pid: own netlink pid to avoid sending to yourself + * @portid: own netlink portid to avoid sending to yourself * @group: multicast group id * @flags: allocation flags * * This function must hold the RTNL or rcu_read_lock(). */ -int genlmsg_multicast_allns(struct sk_buff *skb, u32 pid, +int genlmsg_multicast_allns(struct sk_buff *skb, u32 portid, unsigned int group, gfp_t flags); /** * genlmsg_unicast - unicast a netlink message * @skb: netlink message as socket buffer - * @pid: netlink pid of the destination socket + * @portid: netlink portid of the destination socket */ -static inline int genlmsg_unicast(struct net *net, struct sk_buff *skb, u32 pid) +static inline int genlmsg_unicast(struct net *net, struct sk_buff *skb, u32 portid) { - return nlmsg_unicast(net->genl_sock, skb, pid); + return nlmsg_unicast(net->genl_sock, skb, portid); } /** @@ -264,7 +264,7 @@ static inline int genlmsg_unicast(struct net *net, struct sk_buff *skb, u32 pid) */ static inline int genlmsg_reply(struct sk_buff *skb, struct genl_info *info) { - return genlmsg_unicast(genl_info_net(info), skb, info->snd_pid); + return genlmsg_unicast(genl_info_net(info), skb, info->snd_portid); } /** diff --git a/include/net/netfilter/nf_conntrack_ecache.h b/include/net/netfilter/nf_conntrack_ecache.h index 4a045cda9c60..5654d292efd4 100644 --- a/include/net/netfilter/nf_conntrack_ecache.h +++ b/include/net/netfilter/nf_conntrack_ecache.h @@ -17,7 +17,7 @@ struct nf_conntrack_ecache { unsigned long missed; /* missed events */ u16 ctmask; /* bitmask of ct events to be delivered */ u16 expmask; /* bitmask of expect events to be delivered */ - u32 pid; /* netlink pid of destroyer */ + u32 portid; /* netlink portid of destroyer */ struct timer_list timeout; }; @@ -60,7 +60,7 @@ nf_ct_ecache_ext_add(struct nf_conn *ct, u16 ctmask, u16 expmask, gfp_t gfp) /* This structure is passed to event handler */ struct nf_ct_event { struct nf_conn *ct; - u32 pid; + u32 portid; int report; }; @@ -92,7 +92,7 @@ nf_conntrack_event_cache(enum ip_conntrack_events event, struct nf_conn *ct) static inline int nf_conntrack_eventmask_report(unsigned int eventmask, struct nf_conn *ct, - u32 pid, + u32 portid, int report) { int ret = 0; @@ -112,11 +112,11 @@ nf_conntrack_eventmask_report(unsigned int eventmask, if (nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct)) { struct nf_ct_event item = { .ct = ct, - .pid = e->pid ? e->pid : pid, + .portid = e->portid ? e->portid : portid, .report = report }; /* This is a resent of a destroy event? If so, skip missed */ - unsigned long missed = e->pid ? 0 : e->missed; + unsigned long missed = e->portid ? 0 : e->missed; if (!((eventmask | missed) & e->ctmask)) goto out_unlock; @@ -126,11 +126,11 @@ nf_conntrack_eventmask_report(unsigned int eventmask, spin_lock_bh(&ct->lock); if (ret < 0) { /* This is a destroy event that has been - * triggered by a process, we store the PID + * triggered by a process, we store the PORTID * to include it in the retransmission. */ if (eventmask & (1 << IPCT_DESTROY) && - e->pid == 0 && pid != 0) - e->pid = pid; + e->portid == 0 && portid != 0) + e->portid = portid; else e->missed |= eventmask; } else @@ -145,9 +145,9 @@ out_unlock: static inline int nf_conntrack_event_report(enum ip_conntrack_events event, struct nf_conn *ct, - u32 pid, int report) + u32 portid, int report) { - return nf_conntrack_eventmask_report(1 << event, ct, pid, report); + return nf_conntrack_eventmask_report(1 << event, ct, portid, report); } static inline int @@ -158,7 +158,7 @@ nf_conntrack_event(enum ip_conntrack_events event, struct nf_conn *ct) struct nf_exp_event { struct nf_conntrack_expect *exp; - u32 pid; + u32 portid; int report; }; @@ -172,7 +172,7 @@ extern void nf_ct_expect_unregister_notifier(struct net *net, struct nf_exp_even static inline void nf_ct_expect_event_report(enum ip_conntrack_expect_events event, struct nf_conntrack_expect *exp, - u32 pid, + u32 portid, int report) { struct net *net = nf_ct_exp_net(exp); @@ -191,7 +191,7 @@ nf_ct_expect_event_report(enum ip_conntrack_expect_events event, if (e->expmask & (1 << event)) { struct nf_exp_event item = { .exp = exp, - .pid = pid, + .portid = portid, .report = report }; notify->fcn(1 << event, &item); @@ -216,20 +216,20 @@ static inline void nf_conntrack_event_cache(enum ip_conntrack_events event, struct nf_conn *ct) {} static inline int nf_conntrack_eventmask_report(unsigned int eventmask, struct nf_conn *ct, - u32 pid, + u32 portid, int report) { return 0; } static inline int nf_conntrack_event(enum ip_conntrack_events event, struct nf_conn *ct) { return 0; } static inline int nf_conntrack_event_report(enum ip_conntrack_events event, struct nf_conn *ct, - u32 pid, + u32 portid, int report) { return 0; } static inline void nf_ct_deliver_cached_events(const struct nf_conn *ct) {} static inline void nf_ct_expect_event(enum ip_conntrack_expect_events event, struct nf_conntrack_expect *exp) {} static inline void nf_ct_expect_event_report(enum ip_conntrack_expect_events e, struct nf_conntrack_expect *exp, - u32 pid, + u32 portid, int report) {} static inline int nf_conntrack_ecache_init(struct net *net) diff --git a/include/net/netlink.h b/include/net/netlink.h index 09175d5d1fbf..9690b0f6698a 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -217,19 +217,19 @@ struct nla_policy { /** * struct nl_info - netlink source information * @nlh: Netlink message header of original request - * @pid: Netlink PID of requesting application + * @portid: Netlink PORTID of requesting application */ struct nl_info { struct nlmsghdr *nlh; struct net *nl_net; - u32 pid; + u32 portid; }; extern int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *, struct nlmsghdr *)); extern int nlmsg_notify(struct sock *sk, struct sk_buff *skb, - u32 pid, unsigned int group, int report, + u32 portid, unsigned int group, int report, gfp_t flags); extern int nla_validate(const struct nlattr *head, @@ -444,7 +444,7 @@ static inline int nlmsg_report(const struct nlmsghdr *nlh) /** * nlmsg_put - Add a new netlink message to an skb * @skb: socket buffer to store message in - * @pid: netlink process id + * @portid: netlink process id * @seq: sequence number of message * @type: message type * @payload: length of message payload @@ -453,13 +453,13 @@ static inline int nlmsg_report(const struct nlmsghdr *nlh) * Returns NULL if the tailroom of the skb is insufficient to store * the message header and payload. */ -static inline struct nlmsghdr *nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, +static inline struct nlmsghdr *nlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, int type, int payload, int flags) { if (unlikely(skb_tailroom(skb) < nlmsg_total_size(payload))) return NULL; - return __nlmsg_put(skb, pid, seq, type, payload, flags); + return __nlmsg_put(skb, portid, seq, type, payload, flags); } /** @@ -478,7 +478,7 @@ static inline struct nlmsghdr *nlmsg_put_answer(struct sk_buff *skb, int type, int payload, int flags) { - return nlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, + return nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, type, payload, flags); } @@ -563,18 +563,18 @@ static inline void nlmsg_free(struct sk_buff *skb) * nlmsg_multicast - multicast a netlink message * @sk: netlink socket to spread messages to * @skb: netlink message as socket buffer - * @pid: own netlink pid to avoid sending to yourself + * @portid: own netlink portid to avoid sending to yourself * @group: multicast group id * @flags: allocation flags */ static inline int nlmsg_multicast(struct sock *sk, struct sk_buff *skb, - u32 pid, unsigned int group, gfp_t flags) + u32 portid, unsigned int group, gfp_t flags) { int err; NETLINK_CB(skb).dst_group = group; - err = netlink_broadcast(sk, skb, pid, group, flags); + err = netlink_broadcast(sk, skb, portid, group, flags); if (err > 0) err = 0; @@ -585,13 +585,13 @@ static inline int nlmsg_multicast(struct sock *sk, struct sk_buff *skb, * nlmsg_unicast - unicast a netlink message * @sk: netlink socket to spread message to * @skb: netlink message as socket buffer - * @pid: netlink pid of the destination socket + * @portid: netlink portid of the destination socket */ -static inline int nlmsg_unicast(struct sock *sk, struct sk_buff *skb, u32 pid) +static inline int nlmsg_unicast(struct sock *sk, struct sk_buff *skb, u32 portid) { int err; - err = netlink_unicast(sk, skb, pid, MSG_DONTWAIT); + err = netlink_unicast(sk, skb, portid, MSG_DONTWAIT); if (err > 0) err = 0; diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 6431f5e39022..6735909f826d 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -89,7 +89,7 @@ struct nfc_target { }; struct nfc_genl_data { - u32 poll_req_pid; + u32 poll_req_portid; struct mutex genl_data_mutex; }; diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 36ad56ba648b..ee8613f01860 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -263,7 +263,7 @@ struct km_event { } data; u32 seq; - u32 pid; + u32 portid; u32 event; struct net *net; }; @@ -310,7 +310,7 @@ extern void km_state_notify(struct xfrm_state *x, const struct km_event *c); struct xfrm_tmpl; extern int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol); -extern void km_state_expired(struct xfrm_state *x, int hard, u32 pid); +extern void km_state_expired(struct xfrm_state *x, int hard, u32 portid); extern int __xfrm_state_delete(struct xfrm_state *x); struct xfrm_state_afinfo { @@ -1554,7 +1554,7 @@ extern int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, #endif extern int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport); -extern void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid); +extern void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 portid); extern int km_report(struct net *net, u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr); extern void xfrm_input_init(void); diff --git a/kernel/audit.c b/kernel/audit.c index a24aafa850ae..e0cf64a0ae2d 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -87,11 +87,11 @@ static int audit_failure = AUDIT_FAIL_PRINTK; /* * If audit records are to be written to the netlink socket, audit_pid - * contains the pid of the auditd process and audit_nlk_pid contains - * the pid to use to send netlink messages to that process. + * contains the pid of the auditd process and audit_nlk_portid contains + * the portid to use to send netlink messages to that process. */ int audit_pid; -static int audit_nlk_pid; +static int audit_nlk_portid; /* If audit_rate_limit is non-zero, limit the rate of sending audit records * to that number per second. This prevents DoS attacks, but results in @@ -401,7 +401,7 @@ static void kauditd_send_skb(struct sk_buff *skb) int err; /* take a reference in case we can't send it and we want to hold it */ skb_get(skb); - err = netlink_unicast(audit_sock, skb, audit_nlk_pid, 0); + err = netlink_unicast(audit_sock, skb, audit_nlk_portid, 0); if (err < 0) { BUG_ON(err != -ECONNREFUSED); /* Shouldn't happen */ printk(KERN_ERR "audit: *NO* daemon at audit_pid=%d\n", audit_pid); @@ -692,7 +692,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) status_set.backlog_limit = audit_backlog_limit; status_set.lost = atomic_read(&audit_lost); status_set.backlog = skb_queue_len(&audit_skb_queue); - audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_GET, 0, 0, + audit_send_reply(NETLINK_CB(skb).portid, seq, AUDIT_GET, 0, 0, &status_set, sizeof(status_set)); break; case AUDIT_SET: @@ -720,7 +720,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) sessionid, sid, 1); audit_pid = new_pid; - audit_nlk_pid = NETLINK_CB(skb).pid; + audit_nlk_portid = NETLINK_CB(skb).portid; } if (status_get->mask & AUDIT_STATUS_RATE_LIMIT) { err = audit_set_rate_limit(status_get->rate_limit, @@ -782,7 +782,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) } /* fallthrough */ case AUDIT_LIST: - err = audit_receive_filter(msg_type, NETLINK_CB(skb).pid, + err = audit_receive_filter(msg_type, NETLINK_CB(skb).portid, uid, seq, data, nlmsg_len(nlh), loginuid, sessionid, sid); break; @@ -801,7 +801,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) } /* fallthrough */ case AUDIT_LIST_RULES: - err = audit_receive_filter(msg_type, NETLINK_CB(skb).pid, + err = audit_receive_filter(msg_type, NETLINK_CB(skb).portid, uid, seq, data, nlmsg_len(nlh), loginuid, sessionid, sid); break; @@ -872,7 +872,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) memcpy(sig_data->ctx, ctx, len); security_release_secctx(ctx, len); } - audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_SIGNAL_INFO, + audit_send_reply(NETLINK_CB(skb).portid, seq, AUDIT_SIGNAL_INFO, 0, 0, sig_data, sizeof(*sig_data) + len); kfree(sig_data); break; @@ -891,7 +891,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) rcu_read_unlock(); if (!err) - audit_send_reply(NETLINK_CB(skb).pid, seq, + audit_send_reply(NETLINK_CB(skb).portid, seq, AUDIT_TTY_GET, 0, 0, &s, sizeof(s)); break; } diff --git a/kernel/taskstats.c b/kernel/taskstats.c index d0a32796550f..123793cd06f9 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -467,7 +467,7 @@ static int cmd_attr_register_cpumask(struct genl_info *info) rc = parse(info->attrs[TASKSTATS_CMD_ATTR_REGISTER_CPUMASK], mask); if (rc < 0) goto out; - rc = add_del_listener(info->snd_pid, mask, REGISTER); + rc = add_del_listener(info->snd_portid, mask, REGISTER); out: free_cpumask_var(mask); return rc; @@ -483,7 +483,7 @@ static int cmd_attr_deregister_cpumask(struct genl_info *info) rc = parse(info->attrs[TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK], mask); if (rc < 0) goto out; - rc = add_del_listener(info->snd_pid, mask, DEREGISTER); + rc = add_del_listener(info->snd_portid, mask, DEREGISTER); out: free_cpumask_var(mask); return rc; diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 9ce430b4657c..ddf93efc133c 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -467,14 +467,14 @@ static int fdb_to_nud(const struct net_bridge_fdb_entry *fdb) static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br, const struct net_bridge_fdb_entry *fdb, - u32 pid, u32 seq, int type, unsigned int flags) + u32 portid, u32 seq, int type, unsigned int flags) { unsigned long now = jiffies; struct nda_cacheinfo ci; struct nlmsghdr *nlh; struct ndmsg *ndm; - nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), flags); + nlh = nlmsg_put(skb, portid, seq, type, sizeof(*ndm), flags); if (nlh == NULL) return -EMSGSIZE; @@ -555,7 +555,7 @@ int br_fdb_dump(struct sk_buff *skb, goto skip; if (fdb_fill_info(skb, br, f, - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, RTM_NEWNEIGH, NLM_F_MULTI) < 0) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index fe41260fbf38..093f527276a3 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -127,7 +127,7 @@ static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) goto skip; if (br_fill_ifinfo(skb, port, - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, RTM_NEWLINK, NLM_F_MULTI) < 0) break; diff --git a/net/can/gw.c b/net/can/gw.c index b54d5e695b03..127879c55fb6 100644 --- a/net/can/gw.c +++ b/net/can/gw.c @@ -549,7 +549,7 @@ static int cgw_dump_jobs(struct sk_buff *skb, struct netlink_callback *cb) if (idx < s_idx) goto cont; - if (cgw_put_job(skb, gwj, RTM_NEWROUTE, NETLINK_CB(cb->skb).pid, + if (cgw_put_job(skb, gwj, RTM_NEWROUTE, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI) < 0) break; cont: diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index ab7db83236c9..58a4ba27dfe3 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -402,7 +402,7 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) if (unresolved) ops->unresolved_rules++; - notify_rule_change(RTM_NEWRULE, rule, ops, nlh, NETLINK_CB(skb).pid); + notify_rule_change(RTM_NEWRULE, rule, ops, nlh, NETLINK_CB(skb).portid); flush_route_cache(ops); rules_ops_put(ops); return 0; @@ -500,7 +500,7 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) } notify_rule_change(RTM_DELRULE, rule, ops, nlh, - NETLINK_CB(skb).pid); + NETLINK_CB(skb).portid); if (ops->delete) ops->delete(rule); fib_rule_put(rule); @@ -601,7 +601,7 @@ static int dump_rules(struct sk_buff *skb, struct netlink_callback *cb, if (idx < cb->args[1]) goto skip; - if (fib_nl_fill_rule(skb, rule, NETLINK_CB(cb->skb).pid, + if (fib_nl_fill_rule(skb, rule, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, RTM_NEWRULE, NLM_F_MULTI, ops) < 0) break; diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 117afaf51268..c160adb38e5a 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -2102,7 +2102,7 @@ static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb) if (tidx < tbl_skip || (family && tbl->family != family)) continue; - if (neightbl_fill_info(skb, tbl, NETLINK_CB(cb->skb).pid, + if (neightbl_fill_info(skb, tbl, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, RTM_NEWNEIGHTBL, NLM_F_MULTI) <= 0) break; @@ -2115,7 +2115,7 @@ static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb) goto next; if (neightbl_fill_param_info(skb, tbl, p, - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, RTM_NEWNEIGHTBL, NLM_F_MULTI) <= 0) @@ -2244,7 +2244,7 @@ static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, continue; if (idx < s_idx) goto next; - if (neigh_fill_info(skb, n, NETLINK_CB(cb->skb).pid, + if (neigh_fill_info(skb, n, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, RTM_NEWNEIGH, NLM_F_MULTI) <= 0) { @@ -2281,7 +2281,7 @@ static int pneigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, continue; if (idx < s_idx) goto next; - if (pneigh_fill_info(skb, n, NETLINK_CB(cb->skb).pid, + if (pneigh_fill_info(skb, n, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, RTM_NEWNEIGH, NLM_F_MULTI, tbl) <= 0) { diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 508c5df4a09c..92575370d9f0 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1081,7 +1081,7 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) if (idx < s_idx) goto cont; if (rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK, - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 0, NLM_F_MULTI, ext_filter_mask) <= 0) @@ -1899,14 +1899,14 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) if (nskb == NULL) return -ENOBUFS; - err = rtnl_fill_ifinfo(nskb, dev, RTM_NEWLINK, NETLINK_CB(skb).pid, + err = rtnl_fill_ifinfo(nskb, dev, RTM_NEWLINK, NETLINK_CB(skb).portid, nlh->nlmsg_seq, 0, 0, ext_filter_mask); if (err < 0) { /* -EMSGSIZE implies BUG in if_nlmsg_size */ WARN_ON(err == -EMSGSIZE); kfree_skb(nskb); } else - err = rtnl_unicast(nskb, net, NETLINK_CB(skb).pid); + err = rtnl_unicast(nskb, net, NETLINK_CB(skb).portid); return err; } @@ -2180,9 +2180,9 @@ static int nlmsg_populate_fdb(struct sk_buff *skb, { struct netdev_hw_addr *ha; int err; - u32 pid, seq; + u32 portid, seq; - pid = NETLINK_CB(cb->skb).pid; + portid = NETLINK_CB(cb->skb).portid; seq = cb->nlh->nlmsg_seq; list_for_each_entry(ha, &list->list, list) { @@ -2190,7 +2190,7 @@ static int nlmsg_populate_fdb(struct sk_buff *skb, goto skip; err = nlmsg_populate_fdb_fill(skb, dev, ha->addr, - pid, seq, 0, NTF_SELF); + portid, seq, 0, NTF_SELF); if (err < 0) return err; skip: diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index 81f2bb62dea3..70989e672304 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -1319,7 +1319,7 @@ nla_put_failure: } static int dcbnl_notify(struct net_device *dev, int event, int cmd, - u32 seq, u32 pid, int dcbx_ver) + u32 seq, u32 portid, int dcbx_ver) { struct net *net = dev_net(dev); struct sk_buff *skb; @@ -1330,7 +1330,7 @@ static int dcbnl_notify(struct net_device *dev, int event, int cmd, if (!ops) return -EOPNOTSUPP; - skb = dcbnl_newmsg(event, cmd, pid, seq, 0, &nlh); + skb = dcbnl_newmsg(event, cmd, portid, seq, 0, &nlh); if (!skb) return -ENOBUFS; @@ -1353,16 +1353,16 @@ static int dcbnl_notify(struct net_device *dev, int event, int cmd, } int dcbnl_ieee_notify(struct net_device *dev, int event, int cmd, - u32 seq, u32 pid) + u32 seq, u32 portid) { - return dcbnl_notify(dev, event, cmd, seq, pid, DCB_CAP_DCBX_VER_IEEE); + return dcbnl_notify(dev, event, cmd, seq, portid, DCB_CAP_DCBX_VER_IEEE); } EXPORT_SYMBOL(dcbnl_ieee_notify); int dcbnl_cee_notify(struct net_device *dev, int event, int cmd, - u32 seq, u32 pid) + u32 seq, u32 portid) { - return dcbnl_notify(dev, event, cmd, seq, pid, DCB_CAP_DCBX_VER_CEE); + return dcbnl_notify(dev, event, cmd, seq, portid, DCB_CAP_DCBX_VER_CEE); } EXPORT_SYMBOL(dcbnl_cee_notify); @@ -1656,7 +1656,7 @@ static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) struct net_device *netdev; struct dcbmsg *dcb = nlmsg_data(nlh); struct nlattr *tb[DCB_ATTR_MAX + 1]; - u32 pid = skb ? NETLINK_CB(skb).pid : 0; + u32 portid = skb ? NETLINK_CB(skb).portid : 0; int ret = -EINVAL; struct sk_buff *reply_skb; struct nlmsghdr *reply_nlh = NULL; @@ -1690,7 +1690,7 @@ static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) goto out; } - reply_skb = dcbnl_newmsg(fn->type, dcb->cmd, pid, nlh->nlmsg_seq, + reply_skb = dcbnl_newmsg(fn->type, dcb->cmd, portid, nlh->nlmsg_seq, nlh->nlmsg_flags, &reply_nlh); if (!reply_skb) { ret = -ENOBUFS; @@ -1705,7 +1705,7 @@ static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) nlmsg_end(reply_skb, reply_nlh); - ret = rtnl_unicast(reply_skb, &init_net, pid); + ret = rtnl_unicast(reply_skb, &init_net, portid); out: dev_put(netdev); return ret; diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c index f3924ab1e019..7b7e561412d3 100644 --- a/net/decnet/dn_dev.c +++ b/net/decnet/dn_dev.c @@ -667,12 +667,12 @@ static inline size_t dn_ifaddr_nlmsg_size(void) } static int dn_nl_fill_ifaddr(struct sk_buff *skb, struct dn_ifaddr *ifa, - u32 pid, u32 seq, int event, unsigned int flags) + u32 portid, u32 seq, int event, unsigned int flags) { struct ifaddrmsg *ifm; struct nlmsghdr *nlh; - nlh = nlmsg_put(skb, pid, seq, event, sizeof(*ifm), flags); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags); if (nlh == NULL) return -EMSGSIZE; @@ -753,7 +753,7 @@ static int dn_nl_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) if (dn_idx < skip_naddr) continue; - if (dn_nl_fill_ifaddr(skb, ifa, NETLINK_CB(cb->skb).pid, + if (dn_nl_fill_ifaddr(skb, ifa, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, RTM_NEWADDR, NLM_F_MULTI) < 0) goto done; diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index c855e8d0738f..b57419cc41a4 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -1543,7 +1543,7 @@ static int dn_route_input(struct sk_buff *skb) return dn_route_input_slow(skb); } -static int dn_rt_fill_info(struct sk_buff *skb, u32 pid, u32 seq, +static int dn_rt_fill_info(struct sk_buff *skb, u32 portid, u32 seq, int event, int nowait, unsigned int flags) { struct dn_route *rt = (struct dn_route *)skb_dst(skb); @@ -1551,7 +1551,7 @@ static int dn_rt_fill_info(struct sk_buff *skb, u32 pid, u32 seq, struct nlmsghdr *nlh; long expires; - nlh = nlmsg_put(skb, pid, seq, event, sizeof(*r), flags); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*r), flags); if (!nlh) return -EMSGSIZE; @@ -1685,7 +1685,7 @@ static int dn_cache_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void if (rtm->rtm_flags & RTM_F_NOTIFY) rt->rt_flags |= RTCF_NOTIFY; - err = dn_rt_fill_info(skb, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq, RTM_NEWROUTE, 0, 0); + err = dn_rt_fill_info(skb, NETLINK_CB(in_skb).portid, nlh->nlmsg_seq, RTM_NEWROUTE, 0, 0); if (err == 0) goto out_free; @@ -1694,7 +1694,7 @@ static int dn_cache_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void goto out_free; } - return rtnl_unicast(skb, &init_net, NETLINK_CB(in_skb).pid); + return rtnl_unicast(skb, &init_net, NETLINK_CB(in_skb).portid); out_free: kfree_skb(skb); @@ -1737,7 +1737,7 @@ int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb) if (idx < s_idx) continue; skb_dst_set(skb, dst_clone(&rt->dst)); - if (dn_rt_fill_info(skb, NETLINK_CB(cb->skb).pid, + if (dn_rt_fill_info(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, RTM_NEWROUTE, 1, NLM_F_MULTI) <= 0) { skb_dst_drop(skb); diff --git a/net/decnet/dn_table.c b/net/decnet/dn_table.c index 16c986ab1228..f968c1b58f47 100644 --- a/net/decnet/dn_table.c +++ b/net/decnet/dn_table.c @@ -291,14 +291,14 @@ static inline size_t dn_fib_nlmsg_size(struct dn_fib_info *fi) return payload; } -static int dn_fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event, +static int dn_fib_dump_info(struct sk_buff *skb, u32 portid, u32 seq, int event, u32 tb_id, u8 type, u8 scope, void *dst, int dst_len, struct dn_fib_info *fi, unsigned int flags) { struct rtmsg *rtm; struct nlmsghdr *nlh; - nlh = nlmsg_put(skb, pid, seq, event, sizeof(*rtm), flags); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*rtm), flags); if (!nlh) return -EMSGSIZE; @@ -374,14 +374,14 @@ static void dn_rtmsg_fib(int event, struct dn_fib_node *f, int z, u32 tb_id, struct nlmsghdr *nlh, struct netlink_skb_parms *req) { struct sk_buff *skb; - u32 pid = req ? req->pid : 0; + u32 portid = req ? req->portid : 0; int err = -ENOBUFS; skb = nlmsg_new(dn_fib_nlmsg_size(DN_FIB_INFO(f)), GFP_KERNEL); if (skb == NULL) goto errout; - err = dn_fib_dump_info(skb, pid, nlh->nlmsg_seq, event, tb_id, + err = dn_fib_dump_info(skb, portid, nlh->nlmsg_seq, event, tb_id, f->fn_type, f->fn_scope, &f->fn_key, z, DN_FIB_INFO(f), 0); if (err < 0) { @@ -390,7 +390,7 @@ static void dn_rtmsg_fib(int event, struct dn_fib_node *f, int z, u32 tb_id, kfree_skb(skb); goto errout; } - rtnl_notify(skb, &init_net, pid, RTNLGRP_DECnet_ROUTE, nlh, GFP_KERNEL); + rtnl_notify(skb, &init_net, portid, RTNLGRP_DECnet_ROUTE, nlh, GFP_KERNEL); return; errout: if (err < 0) @@ -411,7 +411,7 @@ static __inline__ int dn_hash_dump_bucket(struct sk_buff *skb, continue; if (f->fn_state & DN_S_ZOMBIE) continue; - if (dn_fib_dump_info(skb, NETLINK_CB(cb->skb).pid, + if (dn_fib_dump_info(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, RTM_NEWROUTE, tb->n, diff --git a/net/ieee802154/nl-mac.c b/net/ieee802154/nl-mac.c index 1e9917124e75..96bb08abece2 100644 --- a/net/ieee802154/nl-mac.c +++ b/net/ieee802154/nl-mac.c @@ -246,7 +246,7 @@ nla_put_failure: } EXPORT_SYMBOL(ieee802154_nl_start_confirm); -static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 pid, +static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags, struct net_device *dev) { void *hdr; @@ -534,7 +534,7 @@ static int ieee802154_list_iface(struct sk_buff *skb, if (!msg) goto out_dev; - rc = ieee802154_nl_fill_iface(msg, info->snd_pid, info->snd_seq, + rc = ieee802154_nl_fill_iface(msg, info->snd_portid, info->snd_seq, 0, dev); if (rc < 0) goto out_free; @@ -565,7 +565,7 @@ static int ieee802154_dump_iface(struct sk_buff *skb, if (idx < s_idx || (dev->type != ARPHRD_IEEE802154)) goto cont; - if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).pid, + if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, dev) < 0) break; cont: diff --git a/net/ieee802154/nl-phy.c b/net/ieee802154/nl-phy.c index d54be34cca94..22b1a7058fd3 100644 --- a/net/ieee802154/nl-phy.c +++ b/net/ieee802154/nl-phy.c @@ -35,7 +35,7 @@ #include "ieee802154.h" -static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 pid, +static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid, u32 seq, int flags, struct wpan_phy *phy) { void *hdr; @@ -105,7 +105,7 @@ static int ieee802154_list_phy(struct sk_buff *skb, if (!msg) goto out_dev; - rc = ieee802154_nl_fill_phy(msg, info->snd_pid, info->snd_seq, + rc = ieee802154_nl_fill_phy(msg, info->snd_portid, info->snd_seq, 0, phy); if (rc < 0) goto out_free; @@ -138,7 +138,7 @@ static int ieee802154_dump_phy_iter(struct wpan_phy *phy, void *_data) return 0; rc = ieee802154_nl_fill_phy(data->skb, - NETLINK_CB(data->cb->skb).pid, + NETLINK_CB(data->cb->skb).portid, data->cb->nlh->nlmsg_seq, NLM_F_MULTI, phy); diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index f14eff506743..7b00556e184b 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -311,7 +311,7 @@ int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b) } static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, - int destroy, struct nlmsghdr *nlh, u32 pid) + int destroy, struct nlmsghdr *nlh, u32 portid) { struct in_ifaddr *promote = NULL; struct in_ifaddr *ifa, *ifa1 = *ifap; @@ -345,7 +345,7 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, inet_hash_remove(ifa); *ifap1 = ifa->ifa_next; - rtmsg_ifa(RTM_DELADDR, ifa, nlh, pid); + rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid); blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa); inet_free_ifa(ifa); @@ -382,7 +382,7 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, is valid, it will try to restore deleted routes... Grr. So that, this order is correct. */ - rtmsg_ifa(RTM_DELADDR, ifa1, nlh, pid); + rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid); blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); if (promote) { @@ -395,7 +395,7 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, } promote->ifa_flags &= ~IFA_F_SECONDARY; - rtmsg_ifa(RTM_NEWADDR, promote, nlh, pid); + rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid); blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, promote); for (ifa = next_sec; ifa; ifa = ifa->ifa_next) { @@ -417,7 +417,7 @@ static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, } static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, - u32 pid) + u32 portid) { struct in_device *in_dev = ifa->ifa_dev; struct in_ifaddr *ifa1, **ifap, **last_primary; @@ -464,7 +464,7 @@ static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, /* Send message first, then call notifier. Notifier will trigger FIB update, so that listeners of netlink will know about new ifaddr */ - rtmsg_ifa(RTM_NEWADDR, ifa, nlh, pid); + rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid); blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); return 0; @@ -563,7 +563,7 @@ static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa))) continue; - __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).pid); + __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid); return 0; } @@ -649,7 +649,7 @@ static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg if (IS_ERR(ifa)) return PTR_ERR(ifa); - return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).pid); + return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid); } /* @@ -1246,12 +1246,12 @@ static size_t inet_nlmsg_size(void) } static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, - u32 pid, u32 seq, int event, unsigned int flags) + u32 portid, u32 seq, int event, unsigned int flags) { struct ifaddrmsg *ifm; struct nlmsghdr *nlh; - nlh = nlmsg_put(skb, pid, seq, event, sizeof(*ifm), flags); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags); if (nlh == NULL) return -EMSGSIZE; @@ -1313,7 +1313,7 @@ static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) if (ip_idx < s_ip_idx) continue; if (inet_fill_ifaddr(skb, ifa, - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, RTM_NEWADDR, NLM_F_MULTI) <= 0) { rcu_read_unlock(); @@ -1335,7 +1335,7 @@ done: } static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh, - u32 pid) + u32 portid) { struct sk_buff *skb; u32 seq = nlh ? nlh->nlmsg_seq : 0; @@ -1347,14 +1347,14 @@ static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh, if (skb == NULL) goto errout; - err = inet_fill_ifaddr(skb, ifa, pid, seq, event, 0); + err = inet_fill_ifaddr(skb, ifa, portid, seq, event, 0); if (err < 0) { /* -EMSGSIZE implies BUG in inet_nlmsg_size() */ WARN_ON(err == -EMSGSIZE); kfree_skb(skb); goto errout; } - rtnl_notify(skb, net, pid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL); + rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL); return; errout: if (err < 0) diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index dc1f10ed1872..68c93d1bb03a 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -557,7 +557,7 @@ static int rtm_to_fib_config(struct net *net, struct sk_buff *skb, cfg->fc_flags = rtm->rtm_flags; cfg->fc_nlflags = nlh->nlmsg_flags; - cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid; + cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid; cfg->fc_nlinfo.nlh = nlh; cfg->fc_nlinfo.nl_net = net; @@ -955,7 +955,7 @@ static void nl_fib_input(struct sk_buff *skb) struct fib_result_nl *frn; struct nlmsghdr *nlh; struct fib_table *tb; - u32 pid; + u32 portid; net = sock_net(skb->sk); nlh = nlmsg_hdr(skb); @@ -973,10 +973,10 @@ static void nl_fib_input(struct sk_buff *skb) nl_fib_lookup(frn, tb); - pid = NETLINK_CB(skb).pid; /* pid of sending process */ - NETLINK_CB(skb).pid = 0; /* from kernel */ + portid = NETLINK_CB(skb).portid; /* pid of sending process */ + NETLINK_CB(skb).portid = 0; /* from kernel */ NETLINK_CB(skb).dst_group = 0; /* unicast */ - netlink_unicast(net->ipv4.fibnl, skb, pid, MSG_DONTWAIT); + netlink_unicast(net->ipv4.fibnl, skb, portid, MSG_DONTWAIT); } static int __net_init nl_fib_lookup_init(struct net *net) diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index da80dc14cc76..3509065e409a 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -391,7 +391,7 @@ void rtmsg_fib(int event, __be32 key, struct fib_alias *fa, if (skb == NULL) goto errout; - err = fib_dump_info(skb, info->pid, seq, event, tb_id, + err = fib_dump_info(skb, info->portid, seq, event, tb_id, fa->fa_type, key, dst_len, fa->fa_tos, fa->fa_info, nlm_flags); if (err < 0) { @@ -400,7 +400,7 @@ void rtmsg_fib(int event, __be32 key, struct fib_alias *fa, kfree_skb(skb); goto errout; } - rtnl_notify(skb, info->nl_net, info->pid, RTNLGRP_IPV4_ROUTE, + rtnl_notify(skb, info->nl_net, info->portid, RTNLGRP_IPV4_ROUTE, info->nlh, GFP_KERNEL); return; errout: @@ -989,14 +989,14 @@ failure: return ERR_PTR(err); } -int fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event, +int fib_dump_info(struct sk_buff *skb, u32 portid, u32 seq, int event, u32 tb_id, u8 type, __be32 dst, int dst_len, u8 tos, struct fib_info *fi, unsigned int flags) { struct nlmsghdr *nlh; struct rtmsg *rtm; - nlh = nlmsg_put(skb, pid, seq, event, sizeof(*rtm), flags); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*rtm), flags); if (nlh == NULL) return -EMSGSIZE; diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 8d766106b540..31d771ca9a70 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1873,7 +1873,7 @@ static int fn_trie_dump_fa(t_key key, int plen, struct list_head *fah, continue; } - if (fib_dump_info(skb, NETLINK_CB(cb->skb).pid, + if (fib_dump_info(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, RTM_NEWROUTE, tb->tb_id, diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 8bc005b1435f..535584c00f91 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -70,7 +70,7 @@ static inline void inet_diag_unlock_handler( int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, struct sk_buff *skb, struct inet_diag_req_v2 *req, struct user_namespace *user_ns, - u32 pid, u32 seq, u16 nlmsg_flags, + u32 portid, u32 seq, u16 nlmsg_flags, const struct nlmsghdr *unlh) { const struct inet_sock *inet = inet_sk(sk); @@ -84,7 +84,7 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, handler = inet_diag_table[req->sdiag_protocol]; BUG_ON(handler == NULL); - nlh = nlmsg_put(skb, pid, seq, unlh->nlmsg_type, sizeof(*r), + nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r), nlmsg_flags); if (!nlh) return -EMSGSIZE; @@ -201,23 +201,23 @@ EXPORT_SYMBOL_GPL(inet_sk_diag_fill); static int inet_csk_diag_fill(struct sock *sk, struct sk_buff *skb, struct inet_diag_req_v2 *req, struct user_namespace *user_ns, - u32 pid, u32 seq, u16 nlmsg_flags, + u32 portid, u32 seq, u16 nlmsg_flags, const struct nlmsghdr *unlh) { return inet_sk_diag_fill(sk, inet_csk(sk), - skb, req, user_ns, pid, seq, nlmsg_flags, unlh); + skb, req, user_ns, portid, seq, nlmsg_flags, unlh); } static int inet_twsk_diag_fill(struct inet_timewait_sock *tw, struct sk_buff *skb, struct inet_diag_req_v2 *req, - u32 pid, u32 seq, u16 nlmsg_flags, + u32 portid, u32 seq, u16 nlmsg_flags, const struct nlmsghdr *unlh) { long tmo; struct inet_diag_msg *r; struct nlmsghdr *nlh; - nlh = nlmsg_put(skb, pid, seq, unlh->nlmsg_type, sizeof(*r), + nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r), nlmsg_flags); if (!nlh) return -EMSGSIZE; @@ -260,14 +260,14 @@ static int inet_twsk_diag_fill(struct inet_timewait_sock *tw, static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct inet_diag_req_v2 *r, struct user_namespace *user_ns, - u32 pid, u32 seq, u16 nlmsg_flags, + u32 portid, u32 seq, u16 nlmsg_flags, const struct nlmsghdr *unlh) { if (sk->sk_state == TCP_TIME_WAIT) return inet_twsk_diag_fill((struct inet_timewait_sock *)sk, - skb, r, pid, seq, nlmsg_flags, + skb, r, portid, seq, nlmsg_flags, unlh); - return inet_csk_diag_fill(sk, skb, r, user_ns, pid, seq, nlmsg_flags, unlh); + return inet_csk_diag_fill(sk, skb, r, user_ns, portid, seq, nlmsg_flags, unlh); } int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *in_skb, @@ -316,14 +316,14 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *in_s err = sk_diag_fill(sk, rep, req, sk_user_ns(NETLINK_CB(in_skb).ssk), - NETLINK_CB(in_skb).pid, + NETLINK_CB(in_skb).portid, nlh->nlmsg_seq, 0, nlh); if (err < 0) { WARN_ON(err == -EMSGSIZE); nlmsg_free(rep); goto out; } - err = netlink_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).pid, + err = netlink_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).portid, MSG_DONTWAIT); if (err > 0) err = 0; @@ -557,7 +557,7 @@ static int inet_csk_diag_dump(struct sock *sk, return inet_csk_diag_fill(sk, skb, r, sk_user_ns(NETLINK_CB(cb->skb).ssk), - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh); } @@ -592,14 +592,14 @@ static int inet_twsk_diag_dump(struct inet_timewait_sock *tw, } return inet_twsk_diag_fill(tw, skb, r, - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh); } static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, struct request_sock *req, struct user_namespace *user_ns, - u32 pid, u32 seq, + u32 portid, u32 seq, const struct nlmsghdr *unlh) { const struct inet_request_sock *ireq = inet_rsk(req); @@ -608,7 +608,7 @@ static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, struct nlmsghdr *nlh; long tmo; - nlh = nlmsg_put(skb, pid, seq, unlh->nlmsg_type, sizeof(*r), + nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r), NLM_F_MULTI); if (!nlh) return -EMSGSIZE; @@ -711,7 +711,7 @@ static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk, err = inet_diag_fill_req(skb, sk, req, sk_user_ns(NETLINK_CB(cb->skb).ssk), - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, cb->nlh); if (err < 0) { cb->args[3] = j + 1; diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 8aa7a4cf9139..1daa95c2a0ba 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -626,7 +626,7 @@ static void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c) e->error = -ETIMEDOUT; memset(&e->msg, 0, sizeof(e->msg)); - rtnl_unicast(skb, net, NETLINK_CB(skb).pid); + rtnl_unicast(skb, net, NETLINK_CB(skb).portid); } else { kfree_skb(skb); } @@ -870,7 +870,7 @@ static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt, memset(&e->msg, 0, sizeof(e->msg)); } - rtnl_unicast(skb, net, NETLINK_CB(skb).pid); + rtnl_unicast(skb, net, NETLINK_CB(skb).portid); } else { ip_mr_forward(net, mrt, skb, c, 0); } @@ -2117,12 +2117,12 @@ int ipmr_get_route(struct net *net, struct sk_buff *skb, } static int ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, - u32 pid, u32 seq, struct mfc_cache *c) + u32 portid, u32 seq, struct mfc_cache *c) { struct nlmsghdr *nlh; struct rtmsg *rtm; - nlh = nlmsg_put(skb, pid, seq, RTM_NEWROUTE, sizeof(*rtm), NLM_F_MULTI); + nlh = nlmsg_put(skb, portid, seq, RTM_NEWROUTE, sizeof(*rtm), NLM_F_MULTI); if (nlh == NULL) return -EMSGSIZE; @@ -2176,7 +2176,7 @@ static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb) if (e < s_e) goto next_entry; if (ipmr_fill_mroute(mrt, skb, - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, mfc) < 0) goto done; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index d39edf16d607..940f4f4cb201 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2136,7 +2136,7 @@ struct rtable *ip_route_output_flow(struct net *net, struct flowi4 *flp4, EXPORT_SYMBOL_GPL(ip_route_output_flow); static int rt_fill_info(struct net *net, __be32 dst, __be32 src, - struct flowi4 *fl4, struct sk_buff *skb, u32 pid, + struct flowi4 *fl4, struct sk_buff *skb, u32 portid, u32 seq, int event, int nowait, unsigned int flags) { struct rtable *rt = skb_rtable(skb); @@ -2146,7 +2146,7 @@ static int rt_fill_info(struct net *net, __be32 dst, __be32 src, u32 error; u32 metrics[RTAX_MAX]; - nlh = nlmsg_put(skb, pid, seq, event, sizeof(*r), flags); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*r), flags); if (nlh == NULL) return -EMSGSIZE; @@ -2306,12 +2306,12 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void rt->rt_flags |= RTCF_NOTIFY; err = rt_fill_info(net, dst, src, &fl4, skb, - NETLINK_CB(in_skb).pid, nlh->nlmsg_seq, + NETLINK_CB(in_skb).portid, nlh->nlmsg_seq, RTM_NEWROUTE, 0, 0); if (err <= 0) goto errout_free; - err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).pid); + err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); errout: return err; diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index 988edb63ee73..4c752a6e0bcd 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -803,7 +803,7 @@ static int tcp_metrics_dump_info(struct sk_buff *skb, { void *hdr; - hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, &tcp_metrics_nl_family, NLM_F_MULTI, TCP_METRICS_CMD_GET); if (!hdr) diff --git a/net/ipv4/udp_diag.c b/net/ipv4/udp_diag.c index d2f336ea82ca..505b30ad9182 100644 --- a/net/ipv4/udp_diag.c +++ b/net/ipv4/udp_diag.c @@ -26,7 +26,7 @@ static int sk_diag_dump(struct sock *sk, struct sk_buff *skb, return inet_sk_diag_fill(sk, NULL, skb, req, sk_user_ns(NETLINK_CB(cb->skb).ssk), - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh); } @@ -72,14 +72,14 @@ static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb, err = inet_sk_diag_fill(sk, NULL, rep, req, sk_user_ns(NETLINK_CB(in_skb).ssk), - NETLINK_CB(in_skb).pid, + NETLINK_CB(in_skb).portid, nlh->nlmsg_seq, 0, nlh); if (err < 0) { WARN_ON(err == -EMSGSIZE); kfree_skb(rep); goto out; } - err = netlink_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).pid, + err = netlink_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).portid, MSG_DONTWAIT); if (err > 0) err = 0; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 572cb660837b..1237d5d037d8 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -3534,12 +3534,12 @@ static inline int inet6_ifaddr_msgsize(void) } static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa, - u32 pid, u32 seq, int event, unsigned int flags) + u32 portid, u32 seq, int event, unsigned int flags) { struct nlmsghdr *nlh; u32 preferred, valid; - nlh = nlmsg_put(skb, pid, seq, event, sizeof(struct ifaddrmsg), flags); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct ifaddrmsg), flags); if (nlh == NULL) return -EMSGSIZE; @@ -3577,7 +3577,7 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa, } static int inet6_fill_ifmcaddr(struct sk_buff *skb, struct ifmcaddr6 *ifmca, - u32 pid, u32 seq, int event, u16 flags) + u32 portid, u32 seq, int event, u16 flags) { struct nlmsghdr *nlh; u8 scope = RT_SCOPE_UNIVERSE; @@ -3586,7 +3586,7 @@ static int inet6_fill_ifmcaddr(struct sk_buff *skb, struct ifmcaddr6 *ifmca, if (ipv6_addr_scope(&ifmca->mca_addr) & IFA_SITE) scope = RT_SCOPE_SITE; - nlh = nlmsg_put(skb, pid, seq, event, sizeof(struct ifaddrmsg), flags); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct ifaddrmsg), flags); if (nlh == NULL) return -EMSGSIZE; @@ -3602,7 +3602,7 @@ static int inet6_fill_ifmcaddr(struct sk_buff *skb, struct ifmcaddr6 *ifmca, } static int inet6_fill_ifacaddr(struct sk_buff *skb, struct ifacaddr6 *ifaca, - u32 pid, u32 seq, int event, unsigned int flags) + u32 portid, u32 seq, int event, unsigned int flags) { struct nlmsghdr *nlh; u8 scope = RT_SCOPE_UNIVERSE; @@ -3611,7 +3611,7 @@ static int inet6_fill_ifacaddr(struct sk_buff *skb, struct ifacaddr6 *ifaca, if (ipv6_addr_scope(&ifaca->aca_addr) & IFA_SITE) scope = RT_SCOPE_SITE; - nlh = nlmsg_put(skb, pid, seq, event, sizeof(struct ifaddrmsg), flags); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct ifaddrmsg), flags); if (nlh == NULL) return -EMSGSIZE; @@ -3652,7 +3652,7 @@ static int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb, if (++ip_idx < s_ip_idx) continue; err = inet6_fill_ifaddr(skb, ifa, - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, RTM_NEWADDR, NLM_F_MULTI); @@ -3668,7 +3668,7 @@ static int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb, if (ip_idx < s_ip_idx) continue; err = inet6_fill_ifmcaddr(skb, ifmca, - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, RTM_GETMULTICAST, NLM_F_MULTI); @@ -3683,7 +3683,7 @@ static int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb, if (ip_idx < s_ip_idx) continue; err = inet6_fill_ifacaddr(skb, ifaca, - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, RTM_GETANYCAST, NLM_F_MULTI); @@ -3805,7 +3805,7 @@ static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh, goto errout_ifa; } - err = inet6_fill_ifaddr(skb, ifa, NETLINK_CB(in_skb).pid, + err = inet6_fill_ifaddr(skb, ifa, NETLINK_CB(in_skb).portid, nlh->nlmsg_seq, RTM_NEWADDR, 0); if (err < 0) { /* -EMSGSIZE implies BUG in inet6_ifaddr_msgsize() */ @@ -3813,7 +3813,7 @@ static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh, kfree_skb(skb); goto errout_ifa; } - err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).pid); + err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); errout_ifa: in6_ifa_put(ifa); errout: @@ -4015,14 +4015,14 @@ static int inet6_fill_link_af(struct sk_buff *skb, const struct net_device *dev) } static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev, - u32 pid, u32 seq, int event, unsigned int flags) + u32 portid, u32 seq, int event, unsigned int flags) { struct net_device *dev = idev->dev; struct ifinfomsg *hdr; struct nlmsghdr *nlh; void *protoinfo; - nlh = nlmsg_put(skb, pid, seq, event, sizeof(*hdr), flags); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*hdr), flags); if (nlh == NULL) return -EMSGSIZE; @@ -4080,7 +4080,7 @@ static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) if (!idev) goto cont; if (inet6_fill_ifinfo(skb, idev, - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, RTM_NEWLINK, NLM_F_MULTI) <= 0) goto out; @@ -4128,14 +4128,14 @@ static inline size_t inet6_prefix_nlmsg_size(void) } static int inet6_fill_prefix(struct sk_buff *skb, struct inet6_dev *idev, - struct prefix_info *pinfo, u32 pid, u32 seq, + struct prefix_info *pinfo, u32 portid, u32 seq, int event, unsigned int flags) { struct prefixmsg *pmsg; struct nlmsghdr *nlh; struct prefix_cacheinfo ci; - nlh = nlmsg_put(skb, pid, seq, event, sizeof(*pmsg), flags); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*pmsg), flags); if (nlh == NULL) return -EMSGSIZE; diff --git a/net/ipv6/addrlabel.c b/net/ipv6/addrlabel.c index eb6a63632d3c..0b0171570d17 100644 --- a/net/ipv6/addrlabel.c +++ b/net/ipv6/addrlabel.c @@ -470,10 +470,10 @@ static void ip6addrlbl_putmsg(struct nlmsghdr *nlh, static int ip6addrlbl_fill(struct sk_buff *skb, struct ip6addrlbl_entry *p, u32 lseq, - u32 pid, u32 seq, int event, + u32 portid, u32 seq, int event, unsigned int flags) { - struct nlmsghdr *nlh = nlmsg_put(skb, pid, seq, event, + struct nlmsghdr *nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct ifaddrlblmsg), flags); if (!nlh) return -EMSGSIZE; @@ -503,7 +503,7 @@ static int ip6addrlbl_dump(struct sk_buff *skb, struct netlink_callback *cb) net_eq(ip6addrlbl_net(p), net)) { if ((err = ip6addrlbl_fill(skb, p, ip6addrlbl_table.seq, - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, RTM_NEWADDRLABEL, NLM_F_MULTI)) <= 0) @@ -574,7 +574,7 @@ static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr* nlh, } err = ip6addrlbl_fill(skb, p, lseq, - NETLINK_CB(in_skb).pid, nlh->nlmsg_seq, + NETLINK_CB(in_skb).portid, nlh->nlmsg_seq, RTM_NEWADDRLABEL, 0); ip6addrlbl_put(p); @@ -585,7 +585,7 @@ static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr* nlh, goto out; } - err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).pid); + err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); out: return err; } diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 4532973f0dd4..08ea3f0b6e55 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -838,7 +838,7 @@ static void ip6mr_destroy_unres(struct mr6_table *mrt, struct mfc6_cache *c) nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr)); skb_trim(skb, nlh->nlmsg_len); ((struct nlmsgerr *)NLMSG_DATA(nlh))->error = -ETIMEDOUT; - rtnl_unicast(skb, net, NETLINK_CB(skb).pid); + rtnl_unicast(skb, net, NETLINK_CB(skb).portid); } else kfree_skb(skb); } @@ -1052,7 +1052,7 @@ static void ip6mr_cache_resolve(struct net *net, struct mr6_table *mrt, skb_trim(skb, nlh->nlmsg_len); ((struct nlmsgerr *)NLMSG_DATA(nlh))->error = -EMSGSIZE; } - rtnl_unicast(skb, net, NETLINK_CB(skb).pid); + rtnl_unicast(skb, net, NETLINK_CB(skb).portid); } else ip6_mr_forward(net, mrt, skb, c); } @@ -2202,12 +2202,12 @@ int ip6mr_get_route(struct net *net, } static int ip6mr_fill_mroute(struct mr6_table *mrt, struct sk_buff *skb, - u32 pid, u32 seq, struct mfc6_cache *c) + u32 portid, u32 seq, struct mfc6_cache *c) { struct nlmsghdr *nlh; struct rtmsg *rtm; - nlh = nlmsg_put(skb, pid, seq, RTM_NEWROUTE, sizeof(*rtm), NLM_F_MULTI); + nlh = nlmsg_put(skb, portid, seq, RTM_NEWROUTE, sizeof(*rtm), NLM_F_MULTI); if (nlh == NULL) return -EMSGSIZE; @@ -2260,7 +2260,7 @@ static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb) if (e < s_e) goto next_entry; if (ip6mr_fill_mroute(mrt, skb, - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, mfc) < 0) goto done; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 339d921cf3b6..a81c6790a648 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1874,7 +1874,7 @@ static struct rt6_info *rt6_add_route_info(struct net *net, .fc_dst_len = prefixlen, .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | RTF_UP | RTF_PREF(pref), - .fc_nlinfo.pid = 0, + .fc_nlinfo.portid = 0, .fc_nlinfo.nlh = NULL, .fc_nlinfo.nl_net = net, }; @@ -1924,7 +1924,7 @@ struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, .fc_ifindex = dev->ifindex, .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | RTF_UP | RTF_EXPIRES | RTF_PREF(pref), - .fc_nlinfo.pid = 0, + .fc_nlinfo.portid = 0, .fc_nlinfo.nlh = NULL, .fc_nlinfo.nl_net = dev_net(dev), }; @@ -2285,7 +2285,7 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, if (rtm->rtm_type == RTN_LOCAL) cfg->fc_flags |= RTF_LOCAL; - cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid; + cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid; cfg->fc_nlinfo.nlh = nlh; cfg->fc_nlinfo.nl_net = sock_net(skb->sk); @@ -2376,7 +2376,7 @@ static inline size_t rt6_nlmsg_size(void) static int rt6_fill_node(struct net *net, struct sk_buff *skb, struct rt6_info *rt, struct in6_addr *dst, struct in6_addr *src, - int iif, int type, u32 pid, u32 seq, + int iif, int type, u32 portid, u32 seq, int prefix, int nowait, unsigned int flags) { struct rtmsg *rtm; @@ -2392,7 +2392,7 @@ static int rt6_fill_node(struct net *net, } } - nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtm), flags); + nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags); if (!nlh) return -EMSGSIZE; @@ -2537,7 +2537,7 @@ int rt6_dump_route(struct rt6_info *rt, void *p_arg) return rt6_fill_node(arg->net, arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE, - NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq, + NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq, prefix, 0, NLM_F_MULTI); } @@ -2617,14 +2617,14 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void skb_dst_set(skb, &rt->dst); err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif, - RTM_NEWROUTE, NETLINK_CB(in_skb).pid, + RTM_NEWROUTE, NETLINK_CB(in_skb).portid, nlh->nlmsg_seq, 0, 0, 0); if (err < 0) { kfree_skb(skb); goto errout; } - err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).pid); + err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); errout: return err; } @@ -2644,14 +2644,14 @@ void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info) goto errout; err = rt6_fill_node(net, skb, rt, NULL, NULL, 0, - event, info->pid, seq, 0, 0, 0); + event, info->portid, seq, 0, 0, 0); if (err < 0) { /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ WARN_ON(err == -EMSGSIZE); kfree_skb(skb); goto errout; } - rtnl_notify(skb, net, info->pid, RTNLGRP_IPV6_ROUTE, + rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, info->nlh, gfp_any()); return; errout: diff --git a/net/irda/irnetlink.c b/net/irda/irnetlink.c index 6c7c4b92e4f8..c32971269280 100644 --- a/net/irda/irnetlink.c +++ b/net/irda/irnetlink.c @@ -100,7 +100,7 @@ static int irda_nl_get_mode(struct sk_buff *skb, struct genl_info *info) goto err_out; } - hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq, + hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, &irda_nl_family, 0, IRDA_NL_CMD_GET_MODE); if (hdr == NULL) { ret = -EMSGSIZE; diff --git a/net/key/af_key.c b/net/key/af_key.c index 334f93b8cfcb..2ca7d7f6861c 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -54,7 +54,7 @@ struct pfkey_sock { struct { uint8_t msg_version; - uint32_t msg_pid; + uint32_t msg_portid; int (*dump)(struct pfkey_sock *sk); void (*done)(struct pfkey_sock *sk); union { @@ -1447,7 +1447,7 @@ static int key_notify_sa(struct xfrm_state *x, const struct km_event *c) hdr->sadb_msg_errno = 0; hdr->sadb_msg_reserved = 0; hdr->sadb_msg_seq = c->seq; - hdr->sadb_msg_pid = c->pid; + hdr->sadb_msg_pid = c->portid; pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, xs_net(x)); @@ -1486,7 +1486,7 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, const struct sadb_msg else c.event = XFRM_MSG_UPDSA; c.seq = hdr->sadb_msg_seq; - c.pid = hdr->sadb_msg_pid; + c.portid = hdr->sadb_msg_pid; km_state_notify(x, &c); out: xfrm_state_put(x); @@ -1523,7 +1523,7 @@ static int pfkey_delete(struct sock *sk, struct sk_buff *skb, const struct sadb_ goto out; c.seq = hdr->sadb_msg_seq; - c.pid = hdr->sadb_msg_pid; + c.portid = hdr->sadb_msg_pid; c.event = XFRM_MSG_DELSA; km_state_notify(x, &c); out: @@ -1701,7 +1701,7 @@ static int key_notify_sa_flush(const struct km_event *c) hdr->sadb_msg_satype = pfkey_proto2satype(c->data.proto); hdr->sadb_msg_type = SADB_FLUSH; hdr->sadb_msg_seq = c->seq; - hdr->sadb_msg_pid = c->pid; + hdr->sadb_msg_pid = c->portid; hdr->sadb_msg_version = PF_KEY_V2; hdr->sadb_msg_errno = (uint8_t) 0; hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t)); @@ -1736,7 +1736,7 @@ static int pfkey_flush(struct sock *sk, struct sk_buff *skb, const struct sadb_m c.data.proto = proto; c.seq = hdr->sadb_msg_seq; - c.pid = hdr->sadb_msg_pid; + c.portid = hdr->sadb_msg_pid; c.event = XFRM_MSG_FLUSHSA; c.net = net; km_state_notify(NULL, &c); @@ -1764,7 +1764,7 @@ static int dump_sa(struct xfrm_state *x, int count, void *ptr) out_hdr->sadb_msg_errno = 0; out_hdr->sadb_msg_reserved = 0; out_hdr->sadb_msg_seq = count + 1; - out_hdr->sadb_msg_pid = pfk->dump.msg_pid; + out_hdr->sadb_msg_pid = pfk->dump.msg_portid; if (pfk->dump.skb) pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE, @@ -1798,7 +1798,7 @@ static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_ms return -EINVAL; pfk->dump.msg_version = hdr->sadb_msg_version; - pfk->dump.msg_pid = hdr->sadb_msg_pid; + pfk->dump.msg_portid = hdr->sadb_msg_pid; pfk->dump.dump = pfkey_dump_sa; pfk->dump.done = pfkey_dump_sa_done; xfrm_state_walk_init(&pfk->dump.u.state, proto); @@ -2157,7 +2157,7 @@ static int key_notify_policy(struct xfrm_policy *xp, int dir, const struct km_ev out_hdr->sadb_msg_type = event2poltype(c->event); out_hdr->sadb_msg_errno = 0; out_hdr->sadb_msg_seq = c->seq; - out_hdr->sadb_msg_pid = c->pid; + out_hdr->sadb_msg_pid = c->portid; pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ALL, NULL, xp_net(xp)); return 0; @@ -2272,7 +2272,7 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, const struct sadb_ c.event = XFRM_MSG_NEWPOLICY; c.seq = hdr->sadb_msg_seq; - c.pid = hdr->sadb_msg_pid; + c.portid = hdr->sadb_msg_pid; km_policy_notify(xp, pol->sadb_x_policy_dir-1, &c); xfrm_pol_put(xp); @@ -2351,7 +2351,7 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, const struct sa goto out; c.seq = hdr->sadb_msg_seq; - c.pid = hdr->sadb_msg_pid; + c.portid = hdr->sadb_msg_pid; c.data.byid = 0; c.event = XFRM_MSG_DELPOLICY; km_policy_notify(xp, pol->sadb_x_policy_dir-1, &c); @@ -2597,7 +2597,7 @@ static int pfkey_spdget(struct sock *sk, struct sk_buff *skb, const struct sadb_ if (err) goto out; c.seq = hdr->sadb_msg_seq; - c.pid = hdr->sadb_msg_pid; + c.portid = hdr->sadb_msg_pid; c.data.byid = 1; c.event = XFRM_MSG_DELPOLICY; km_policy_notify(xp, dir, &c); @@ -2634,7 +2634,7 @@ static int dump_sp(struct xfrm_policy *xp, int dir, int count, void *ptr) out_hdr->sadb_msg_satype = SADB_SATYPE_UNSPEC; out_hdr->sadb_msg_errno = 0; out_hdr->sadb_msg_seq = count + 1; - out_hdr->sadb_msg_pid = pfk->dump.msg_pid; + out_hdr->sadb_msg_pid = pfk->dump.msg_portid; if (pfk->dump.skb) pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE, @@ -2663,7 +2663,7 @@ static int pfkey_spddump(struct sock *sk, struct sk_buff *skb, const struct sadb return -EBUSY; pfk->dump.msg_version = hdr->sadb_msg_version; - pfk->dump.msg_pid = hdr->sadb_msg_pid; + pfk->dump.msg_portid = hdr->sadb_msg_pid; pfk->dump.dump = pfkey_dump_sp; pfk->dump.done = pfkey_dump_sp_done; xfrm_policy_walk_init(&pfk->dump.u.policy, XFRM_POLICY_TYPE_MAIN); @@ -2682,7 +2682,7 @@ static int key_notify_policy_flush(const struct km_event *c) hdr = (struct sadb_msg *) skb_put(skb_out, sizeof(struct sadb_msg)); hdr->sadb_msg_type = SADB_X_SPDFLUSH; hdr->sadb_msg_seq = c->seq; - hdr->sadb_msg_pid = c->pid; + hdr->sadb_msg_pid = c->portid; hdr->sadb_msg_version = PF_KEY_V2; hdr->sadb_msg_errno = (uint8_t) 0; hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t)); @@ -2711,7 +2711,7 @@ static int pfkey_spdflush(struct sock *sk, struct sk_buff *skb, const struct sad c.data.type = XFRM_POLICY_TYPE_MAIN; c.event = XFRM_MSG_FLUSHPOLICY; - c.pid = hdr->sadb_msg_pid; + c.portid = hdr->sadb_msg_pid; c.seq = hdr->sadb_msg_seq; c.net = net; km_policy_notify(NULL, 0, &c); diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c index d71cd9229a47..6ec3f67ad3f1 100644 --- a/net/l2tp/l2tp_netlink.c +++ b/net/l2tp/l2tp_netlink.c @@ -78,7 +78,7 @@ static int l2tp_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info) goto out; } - hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq, + hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, &l2tp_nl_family, 0, L2TP_CMD_NOOP); if (IS_ERR(hdr)) { ret = PTR_ERR(hdr); @@ -87,7 +87,7 @@ static int l2tp_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info) genlmsg_end(msg, hdr); - return genlmsg_unicast(genl_info_net(info), msg, info->snd_pid); + return genlmsg_unicast(genl_info_net(info), msg, info->snd_portid); err_out: nlmsg_free(msg); @@ -235,7 +235,7 @@ out: return ret; } -static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 pid, u32 seq, int flags, +static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, int flags, struct l2tp_tunnel *tunnel) { void *hdr; @@ -248,7 +248,7 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 pid, u32 seq, int flags, struct l2tp_stats stats; unsigned int start; - hdr = genlmsg_put(skb, pid, seq, &l2tp_nl_family, flags, + hdr = genlmsg_put(skb, portid, seq, &l2tp_nl_family, flags, L2TP_CMD_TUNNEL_GET); if (IS_ERR(hdr)) return PTR_ERR(hdr); @@ -359,12 +359,12 @@ static int l2tp_nl_cmd_tunnel_get(struct sk_buff *skb, struct genl_info *info) goto out; } - ret = l2tp_nl_tunnel_send(msg, info->snd_pid, info->snd_seq, + ret = l2tp_nl_tunnel_send(msg, info->snd_portid, info->snd_seq, NLM_F_ACK, tunnel); if (ret < 0) goto err_out; - return genlmsg_unicast(net, msg, info->snd_pid); + return genlmsg_unicast(net, msg, info->snd_portid); err_out: nlmsg_free(msg); @@ -384,7 +384,7 @@ static int l2tp_nl_cmd_tunnel_dump(struct sk_buff *skb, struct netlink_callback if (tunnel == NULL) goto out; - if (l2tp_nl_tunnel_send(skb, NETLINK_CB(cb->skb).pid, + if (l2tp_nl_tunnel_send(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, tunnel) <= 0) goto out; @@ -604,7 +604,7 @@ out: return ret; } -static int l2tp_nl_session_send(struct sk_buff *skb, u32 pid, u32 seq, int flags, +static int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq, int flags, struct l2tp_session *session) { void *hdr; @@ -616,7 +616,7 @@ static int l2tp_nl_session_send(struct sk_buff *skb, u32 pid, u32 seq, int flags sk = tunnel->sock; - hdr = genlmsg_put(skb, pid, seq, &l2tp_nl_family, flags, L2TP_CMD_SESSION_GET); + hdr = genlmsg_put(skb, portid, seq, &l2tp_nl_family, flags, L2TP_CMD_SESSION_GET); if (IS_ERR(hdr)) return PTR_ERR(hdr); @@ -705,12 +705,12 @@ static int l2tp_nl_cmd_session_get(struct sk_buff *skb, struct genl_info *info) goto out; } - ret = l2tp_nl_session_send(msg, info->snd_pid, info->snd_seq, + ret = l2tp_nl_session_send(msg, info->snd_portid, info->snd_seq, 0, session); if (ret < 0) goto err_out; - return genlmsg_unicast(genl_info_net(info), msg, info->snd_pid); + return genlmsg_unicast(genl_info_net(info), msg, info->snd_portid); err_out: nlmsg_free(msg); @@ -742,7 +742,7 @@ static int l2tp_nl_cmd_session_dump(struct sk_buff *skb, struct netlink_callback continue; } - if (l2tp_nl_session_send(skb, NETLINK_CB(cb->skb).pid, + if (l2tp_nl_session_send(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, session) <= 0) break; diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 9730882697aa..ad39ef406851 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -563,13 +563,13 @@ flag_exist(const struct nlmsghdr *nlh) } static struct nlmsghdr * -start_msg(struct sk_buff *skb, u32 pid, u32 seq, unsigned int flags, +start_msg(struct sk_buff *skb, u32 portid, u32 seq, unsigned int flags, enum ipset_cmd cmd) { struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; - nlh = nlmsg_put(skb, pid, seq, cmd | (NFNL_SUBSYS_IPSET << 8), + nlh = nlmsg_put(skb, portid, seq, cmd | (NFNL_SUBSYS_IPSET << 8), sizeof(*nfmsg), flags); if (nlh == NULL) return NULL; @@ -1045,7 +1045,7 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb) ip_set_id_t index = IPSET_INVALID_ID, max; struct ip_set *set = NULL; struct nlmsghdr *nlh = NULL; - unsigned int flags = NETLINK_CB(cb->skb).pid ? NLM_F_MULTI : 0; + unsigned int flags = NETLINK_CB(cb->skb).portid ? NLM_F_MULTI : 0; u32 dump_type, dump_flags; int ret = 0; @@ -1093,7 +1093,7 @@ dump_last: pr_debug("reference set\n"); __ip_set_get(index); } - nlh = start_msg(skb, NETLINK_CB(cb->skb).pid, + nlh = start_msg(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, flags, IPSET_CMD_LIST); if (!nlh) { @@ -1226,7 +1226,7 @@ call_ad(struct sock *ctnl, struct sk_buff *skb, struct ip_set *set, skb2 = nlmsg_new(payload, GFP_KERNEL); if (skb2 == NULL) return -ENOMEM; - rep = __nlmsg_put(skb2, NETLINK_CB(skb).pid, + rep = __nlmsg_put(skb2, NETLINK_CB(skb).portid, nlh->nlmsg_seq, NLMSG_ERROR, payload, 0); errmsg = nlmsg_data(rep); errmsg->error = ret; @@ -1241,7 +1241,7 @@ call_ad(struct sock *ctnl, struct sk_buff *skb, struct ip_set *set, *errline = lineno; - netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); + netlink_unicast(ctnl, skb2, NETLINK_CB(skb).portid, MSG_DONTWAIT); /* Signal netlink not to send its ACK/errmsg. */ return -EINTR; } @@ -1416,7 +1416,7 @@ ip_set_header(struct sock *ctnl, struct sk_buff *skb, if (skb2 == NULL) return -ENOMEM; - nlh2 = start_msg(skb2, NETLINK_CB(skb).pid, nlh->nlmsg_seq, 0, + nlh2 = start_msg(skb2, NETLINK_CB(skb).portid, nlh->nlmsg_seq, 0, IPSET_CMD_HEADER); if (!nlh2) goto nlmsg_failure; @@ -1428,7 +1428,7 @@ ip_set_header(struct sock *ctnl, struct sk_buff *skb, goto nla_put_failure; nlmsg_end(skb2, nlh2); - ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); + ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).portid, MSG_DONTWAIT); if (ret < 0) return ret; @@ -1476,7 +1476,7 @@ ip_set_type(struct sock *ctnl, struct sk_buff *skb, if (skb2 == NULL) return -ENOMEM; - nlh2 = start_msg(skb2, NETLINK_CB(skb).pid, nlh->nlmsg_seq, 0, + nlh2 = start_msg(skb2, NETLINK_CB(skb).portid, nlh->nlmsg_seq, 0, IPSET_CMD_TYPE); if (!nlh2) goto nlmsg_failure; @@ -1489,7 +1489,7 @@ ip_set_type(struct sock *ctnl, struct sk_buff *skb, nlmsg_end(skb2, nlh2); pr_debug("Send TYPE, nlmsg_len: %u\n", nlh2->nlmsg_len); - ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); + ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).portid, MSG_DONTWAIT); if (ret < 0) return ret; @@ -1525,7 +1525,7 @@ ip_set_protocol(struct sock *ctnl, struct sk_buff *skb, if (skb2 == NULL) return -ENOMEM; - nlh2 = start_msg(skb2, NETLINK_CB(skb).pid, nlh->nlmsg_seq, 0, + nlh2 = start_msg(skb2, NETLINK_CB(skb).portid, nlh->nlmsg_seq, 0, IPSET_CMD_PROTOCOL); if (!nlh2) goto nlmsg_failure; @@ -1533,7 +1533,7 @@ ip_set_protocol(struct sock *ctnl, struct sk_buff *skb, goto nla_put_failure; nlmsg_end(skb2, nlh2); - ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); + ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).portid, MSG_DONTWAIT); if (ret < 0) return ret; diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 767cc12da0fe..0f924bf19c2b 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -2939,7 +2939,7 @@ static int ip_vs_genl_dump_service(struct sk_buff *skb, { void *hdr; - hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, &ip_vs_genl_family, NLM_F_MULTI, IPVS_CMD_NEW_SERVICE); if (!hdr) @@ -3128,7 +3128,7 @@ static int ip_vs_genl_dump_dest(struct sk_buff *skb, struct ip_vs_dest *dest, { void *hdr; - hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, &ip_vs_genl_family, NLM_F_MULTI, IPVS_CMD_NEW_DEST); if (!hdr) @@ -3257,7 +3257,7 @@ static int ip_vs_genl_dump_daemon(struct sk_buff *skb, __be32 state, struct netlink_callback *cb) { void *hdr; - hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, &ip_vs_genl_family, NLM_F_MULTI, IPVS_CMD_NEW_DAEMON); if (!hdr) diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c index e7be79e640de..de9781b6464f 100644 --- a/net/netfilter/nf_conntrack_ecache.c +++ b/net/netfilter/nf_conntrack_ecache.c @@ -61,7 +61,7 @@ void nf_ct_deliver_cached_events(struct nf_conn *ct) goto out_unlock; item.ct = ct; - item.pid = 0; + item.portid = 0; item.report = 0; ret = notify->fcn(events | missed, &item); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index a205bd6ce294..59770039b825 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -418,16 +418,16 @@ nla_put_failure: } static int -ctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq, u32 type, +ctnetlink_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, struct nf_conn *ct) { struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; struct nlattr *nest_parms; - unsigned int flags = pid ? NLM_F_MULTI : 0, event; + unsigned int flags = portid ? NLM_F_MULTI : 0, event; event = (NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_CT_NEW); - nlh = nlmsg_put(skb, pid, seq, event, sizeof(*nfmsg), flags); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); if (nlh == NULL) goto nlmsg_failure; @@ -604,7 +604,7 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item) goto errout; type |= NFNL_SUBSYS_CTNETLINK << 8; - nlh = nlmsg_put(skb, item->pid, 0, type, sizeof(*nfmsg), flags); + nlh = nlmsg_put(skb, item->portid, 0, type, sizeof(*nfmsg), flags); if (nlh == NULL) goto nlmsg_failure; @@ -680,7 +680,7 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item) rcu_read_unlock(); nlmsg_end(skb, nlh); - err = nfnetlink_send(skb, net, item->pid, group, item->report, + err = nfnetlink_send(skb, net, item->portid, group, item->report, GFP_ATOMIC); if (err == -ENOBUFS || err == -EAGAIN) return -ENOBUFS; @@ -757,7 +757,7 @@ restart: #endif rcu_read_lock(); res = - ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid, + ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NFNL_MSG_TYPE(cb->nlh->nlmsg_type), ct); @@ -961,7 +961,7 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb, else { /* Flush the whole table */ nf_conntrack_flush_report(net, - NETLINK_CB(skb).pid, + NETLINK_CB(skb).portid, nlmsg_report(nlh)); return 0; } @@ -985,7 +985,7 @@ 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).pid, + NETLINK_CB(skb).portid, nlmsg_report(nlh)) < 0) { nf_ct_delete_from_lists(ct); /* we failed to report the event, try later */ @@ -1069,14 +1069,14 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb, } rcu_read_lock(); - err = ctnetlink_fill_info(skb2, NETLINK_CB(skb).pid, nlh->nlmsg_seq, + err = ctnetlink_fill_info(skb2, NETLINK_CB(skb).portid, nlh->nlmsg_seq, NFNL_MSG_TYPE(nlh->nlmsg_type), ct); rcu_read_unlock(); nf_ct_put(ct); if (err <= 0) goto free; - err = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); + err = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).portid, MSG_DONTWAIT); if (err < 0) goto out; @@ -1616,7 +1616,7 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, (1 << IPCT_PROTOINFO) | (1 << IPCT_NATSEQADJ) | (1 << IPCT_MARK) | events, - ct, NETLINK_CB(skb).pid, + ct, NETLINK_CB(skb).portid, nlmsg_report(nlh)); nf_ct_put(ct); } @@ -1638,7 +1638,7 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, (1 << IPCT_PROTOINFO) | (1 << IPCT_NATSEQADJ) | (1 << IPCT_MARK), - ct, NETLINK_CB(skb).pid, + ct, NETLINK_CB(skb).portid, nlmsg_report(nlh)); } } @@ -1648,15 +1648,15 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, } static int -ctnetlink_ct_stat_cpu_fill_info(struct sk_buff *skb, u32 pid, u32 seq, +ctnetlink_ct_stat_cpu_fill_info(struct sk_buff *skb, u32 portid, u32 seq, __u16 cpu, const struct ip_conntrack_stat *st) { struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; - unsigned int flags = pid ? NLM_F_MULTI : 0, event; + unsigned int flags = portid ? NLM_F_MULTI : 0, event; event = (NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_CT_GET_STATS_CPU); - nlh = nlmsg_put(skb, pid, seq, event, sizeof(*nfmsg), flags); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); if (nlh == NULL) goto nlmsg_failure; @@ -1708,7 +1708,7 @@ ctnetlink_ct_stat_cpu_dump(struct sk_buff *skb, struct netlink_callback *cb) st = per_cpu_ptr(net->ct.stat, cpu); if (ctnetlink_ct_stat_cpu_fill_info(skb, - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, cpu, st) < 0) break; @@ -1734,16 +1734,16 @@ ctnetlink_stat_ct_cpu(struct sock *ctnl, struct sk_buff *skb, } static int -ctnetlink_stat_ct_fill_info(struct sk_buff *skb, u32 pid, u32 seq, u32 type, +ctnetlink_stat_ct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, struct net *net) { struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; - unsigned int flags = pid ? NLM_F_MULTI : 0, event; + unsigned int flags = portid ? NLM_F_MULTI : 0, event; unsigned int nr_conntracks = atomic_read(&net->ct.count); event = (NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_CT_GET_STATS); - nlh = nlmsg_put(skb, pid, seq, event, sizeof(*nfmsg), flags); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); if (nlh == NULL) goto nlmsg_failure; @@ -1776,14 +1776,14 @@ ctnetlink_stat_ct(struct sock *ctnl, struct sk_buff *skb, if (skb2 == NULL) return -ENOMEM; - err = ctnetlink_stat_ct_fill_info(skb2, NETLINK_CB(skb).pid, + err = ctnetlink_stat_ct_fill_info(skb2, NETLINK_CB(skb).portid, nlh->nlmsg_seq, NFNL_MSG_TYPE(nlh->nlmsg_type), sock_net(skb->sk)); if (err <= 0) goto free; - err = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); + err = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).portid, MSG_DONTWAIT); if (err < 0) goto out; @@ -2073,15 +2073,15 @@ nla_put_failure: } static int -ctnetlink_exp_fill_info(struct sk_buff *skb, u32 pid, u32 seq, +ctnetlink_exp_fill_info(struct sk_buff *skb, u32 portid, u32 seq, int event, const struct nf_conntrack_expect *exp) { struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; - unsigned int flags = pid ? NLM_F_MULTI : 0; + unsigned int flags = portid ? NLM_F_MULTI : 0; event |= NFNL_SUBSYS_CTNETLINK_EXP << 8; - nlh = nlmsg_put(skb, pid, seq, event, sizeof(*nfmsg), flags); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); if (nlh == NULL) goto nlmsg_failure; @@ -2132,7 +2132,7 @@ ctnetlink_expect_event(unsigned int events, struct nf_exp_event *item) goto errout; type |= NFNL_SUBSYS_CTNETLINK_EXP << 8; - nlh = nlmsg_put(skb, item->pid, 0, type, sizeof(*nfmsg), flags); + nlh = nlmsg_put(skb, item->portid, 0, type, sizeof(*nfmsg), flags); if (nlh == NULL) goto nlmsg_failure; @@ -2147,7 +2147,7 @@ ctnetlink_expect_event(unsigned int events, struct nf_exp_event *item) rcu_read_unlock(); nlmsg_end(skb, nlh); - nfnetlink_send(skb, net, item->pid, group, item->report, GFP_ATOMIC); + nfnetlink_send(skb, net, item->portid, group, item->report, GFP_ATOMIC); return 0; nla_put_failure: @@ -2190,7 +2190,7 @@ restart: cb->args[1] = 0; } if (ctnetlink_exp_fill_info(skb, - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, IPCTNL_MSG_EXP_NEW, exp) < 0) { @@ -2283,14 +2283,14 @@ ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb, } rcu_read_lock(); - err = ctnetlink_exp_fill_info(skb2, NETLINK_CB(skb).pid, + err = ctnetlink_exp_fill_info(skb2, NETLINK_CB(skb).portid, nlh->nlmsg_seq, IPCTNL_MSG_EXP_NEW, exp); rcu_read_unlock(); nf_ct_expect_put(exp); if (err <= 0) goto free; - err = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); + err = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).portid, MSG_DONTWAIT); if (err < 0) goto out; @@ -2344,7 +2344,7 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb, /* after list removal, usage count == 1 */ spin_lock_bh(&nf_conntrack_lock); if (del_timer(&exp->timeout)) { - nf_ct_unlink_expect_report(exp, NETLINK_CB(skb).pid, + nf_ct_unlink_expect_report(exp, NETLINK_CB(skb).portid, nlmsg_report(nlh)); nf_ct_expect_put(exp); } @@ -2366,7 +2366,7 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb, if (!strcmp(m_help->helper->name, name) && del_timer(&exp->timeout)) { nf_ct_unlink_expect_report(exp, - NETLINK_CB(skb).pid, + NETLINK_CB(skb).portid, nlmsg_report(nlh)); nf_ct_expect_put(exp); } @@ -2382,7 +2382,7 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb, hnode) { if (del_timer(&exp->timeout)) { nf_ct_unlink_expect_report(exp, - NETLINK_CB(skb).pid, + NETLINK_CB(skb).portid, nlmsg_report(nlh)); nf_ct_expect_put(exp); } @@ -2447,7 +2447,7 @@ static int ctnetlink_create_expect(struct net *net, u16 zone, const struct nlattr * const cda[], u_int8_t u3, - u32 pid, int report) + u32 portid, int report) { struct nf_conntrack_tuple tuple, mask, master_tuple; struct nf_conntrack_tuple_hash *h = NULL; @@ -2560,7 +2560,7 @@ ctnetlink_create_expect(struct net *net, u16 zone, if (err < 0) goto err_out; } - err = nf_ct_expect_related_report(exp, pid, report); + err = nf_ct_expect_related_report(exp, portid, report); err_out: nf_ct_expect_put(exp); out: @@ -2603,7 +2603,7 @@ ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb, if (nlh->nlmsg_flags & NLM_F_CREATE) { err = ctnetlink_create_expect(net, zone, cda, u3, - NETLINK_CB(skb).pid, + NETLINK_CB(skb).portid, nlmsg_report(nlh)); } return err; @@ -2618,15 +2618,15 @@ ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb, } static int -ctnetlink_exp_stat_fill_info(struct sk_buff *skb, u32 pid, u32 seq, int cpu, +ctnetlink_exp_stat_fill_info(struct sk_buff *skb, u32 portid, u32 seq, int cpu, const struct ip_conntrack_stat *st) { struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; - unsigned int flags = pid ? NLM_F_MULTI : 0, event; + unsigned int flags = portid ? NLM_F_MULTI : 0, event; event = (NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_EXP_GET_STATS_CPU); - nlh = nlmsg_put(skb, pid, seq, event, sizeof(*nfmsg), flags); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); if (nlh == NULL) goto nlmsg_failure; @@ -2665,7 +2665,7 @@ ctnetlink_exp_stat_cpu_dump(struct sk_buff *skb, struct netlink_callback *cb) continue; st = per_cpu_ptr(net->ct.stat, cpu); - if (ctnetlink_exp_stat_fill_info(skb, NETLINK_CB(cb->skb).pid, + if (ctnetlink_exp_stat_fill_info(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, cpu, st) < 0) break; diff --git a/net/netfilter/nfnetlink_acct.c b/net/netfilter/nfnetlink_acct.c index d7ec92879071..589d686f0b4c 100644 --- a/net/netfilter/nfnetlink_acct.c +++ b/net/netfilter/nfnetlink_acct.c @@ -91,16 +91,16 @@ nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb, } static int -nfnl_acct_fill_info(struct sk_buff *skb, u32 pid, u32 seq, u32 type, +nfnl_acct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, int event, struct nf_acct *acct) { struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; - unsigned int flags = pid ? NLM_F_MULTI : 0; + unsigned int flags = portid ? NLM_F_MULTI : 0; u64 pkts, bytes; event |= NFNL_SUBSYS_ACCT << 8; - nlh = nlmsg_put(skb, pid, seq, event, sizeof(*nfmsg), flags); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); if (nlh == NULL) goto nlmsg_failure; @@ -150,7 +150,7 @@ nfnl_acct_dump(struct sk_buff *skb, struct netlink_callback *cb) if (last && cur != last) continue; - if (nfnl_acct_fill_info(skb, NETLINK_CB(cb->skb).pid, + if (nfnl_acct_fill_info(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NFNL_MSG_TYPE(cb->nlh->nlmsg_type), NFNL_MSG_ACCT_NEW, cur) < 0) { @@ -195,7 +195,7 @@ nfnl_acct_get(struct sock *nfnl, struct sk_buff *skb, break; } - ret = nfnl_acct_fill_info(skb2, NETLINK_CB(skb).pid, + ret = nfnl_acct_fill_info(skb2, NETLINK_CB(skb).portid, nlh->nlmsg_seq, NFNL_MSG_TYPE(nlh->nlmsg_type), NFNL_MSG_ACCT_NEW, cur); @@ -203,7 +203,7 @@ nfnl_acct_get(struct sock *nfnl, struct sk_buff *skb, kfree_skb(skb2); break; } - ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).pid, + ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).portid, MSG_DONTWAIT); if (ret > 0) ret = 0; diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index 32a1ba3f3e27..3678073360a3 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -395,16 +395,16 @@ nla_put_failure: } static int -nfnl_cthelper_fill_info(struct sk_buff *skb, u32 pid, u32 seq, u32 type, +nfnl_cthelper_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, int event, struct nf_conntrack_helper *helper) { struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; - unsigned int flags = pid ? NLM_F_MULTI : 0; + unsigned int flags = portid ? NLM_F_MULTI : 0; int status; event |= NFNL_SUBSYS_CTHELPER << 8; - nlh = nlmsg_put(skb, pid, seq, event, sizeof(*nfmsg), flags); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); if (nlh == NULL) goto nlmsg_failure; @@ -468,7 +468,7 @@ restart: cb->args[1] = 0; } if (nfnl_cthelper_fill_info(skb, - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NFNL_MSG_TYPE(cb->nlh->nlmsg_type), NFNL_MSG_CTHELPER_NEW, cur) < 0) { @@ -538,7 +538,7 @@ nfnl_cthelper_get(struct sock *nfnl, struct sk_buff *skb, break; } - ret = nfnl_cthelper_fill_info(skb2, NETLINK_CB(skb).pid, + ret = nfnl_cthelper_fill_info(skb2, NETLINK_CB(skb).portid, nlh->nlmsg_seq, NFNL_MSG_TYPE(nlh->nlmsg_type), NFNL_MSG_CTHELPER_NEW, cur); @@ -547,7 +547,7 @@ nfnl_cthelper_get(struct sock *nfnl, struct sk_buff *skb, break; } - ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).pid, + ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).portid, MSG_DONTWAIT); if (ret > 0) ret = 0; diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c index cdecbc8fe965..8847b4d8be06 100644 --- a/net/netfilter/nfnetlink_cttimeout.c +++ b/net/netfilter/nfnetlink_cttimeout.c @@ -155,16 +155,16 @@ err_proto_put: } static int -ctnl_timeout_fill_info(struct sk_buff *skb, u32 pid, u32 seq, u32 type, +ctnl_timeout_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, int event, struct ctnl_timeout *timeout) { struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; - unsigned int flags = pid ? NLM_F_MULTI : 0; + unsigned int flags = portid ? NLM_F_MULTI : 0; struct nf_conntrack_l4proto *l4proto = timeout->l4proto; event |= NFNL_SUBSYS_CTNETLINK_TIMEOUT << 8; - nlh = nlmsg_put(skb, pid, seq, event, sizeof(*nfmsg), flags); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); if (nlh == NULL) goto nlmsg_failure; @@ -222,7 +222,7 @@ ctnl_timeout_dump(struct sk_buff *skb, struct netlink_callback *cb) if (last && cur != last) continue; - if (ctnl_timeout_fill_info(skb, NETLINK_CB(cb->skb).pid, + if (ctnl_timeout_fill_info(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NFNL_MSG_TYPE(cb->nlh->nlmsg_type), IPCTNL_MSG_TIMEOUT_NEW, cur) < 0) { @@ -268,7 +268,7 @@ cttimeout_get_timeout(struct sock *ctnl, struct sk_buff *skb, break; } - ret = ctnl_timeout_fill_info(skb2, NETLINK_CB(skb).pid, + ret = ctnl_timeout_fill_info(skb2, NETLINK_CB(skb).portid, nlh->nlmsg_seq, NFNL_MSG_TYPE(nlh->nlmsg_type), IPCTNL_MSG_TIMEOUT_NEW, cur); @@ -276,7 +276,7 @@ cttimeout_get_timeout(struct sock *ctnl, struct sk_buff *skb, kfree_skb(skb2); break; } - ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, + ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).portid, MSG_DONTWAIT); if (ret > 0) ret = 0; diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index be194b144297..8cb67c4dbd62 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -56,7 +56,7 @@ struct nfulnl_instance { struct sk_buff *skb; /* pre-allocatd skb */ struct timer_list timer; struct user_namespace *peer_user_ns; /* User namespace of the peer process */ - int peer_pid; /* PID of the peer process */ + int peer_portid; /* PORTID of the peer process */ /* configurable parameters */ unsigned int flushtimeout; /* timeout until queue flush */ @@ -133,7 +133,7 @@ instance_put(struct nfulnl_instance *inst) static void nfulnl_timer(unsigned long data); static struct nfulnl_instance * -instance_create(u_int16_t group_num, int pid, struct user_namespace *user_ns) +instance_create(u_int16_t group_num, int portid, struct user_namespace *user_ns) { struct nfulnl_instance *inst; int err; @@ -164,7 +164,7 @@ instance_create(u_int16_t group_num, int pid, struct user_namespace *user_ns) setup_timer(&inst->timer, nfulnl_timer, (unsigned long)inst); inst->peer_user_ns = user_ns; - inst->peer_pid = pid; + inst->peer_portid = portid; inst->group_num = group_num; inst->qthreshold = NFULNL_QTHRESH_DEFAULT; @@ -336,7 +336,7 @@ __nfulnl_send(struct nfulnl_instance *inst) if (!nlh) goto out; } - status = nfnetlink_unicast(inst->skb, &init_net, inst->peer_pid, + status = nfnetlink_unicast(inst->skb, &init_net, inst->peer_portid, MSG_DONTWAIT); inst->qlen = 0; @@ -703,7 +703,7 @@ nfulnl_rcv_nl_event(struct notifier_block *this, if (event == NETLINK_URELEASE && n->protocol == NETLINK_NETFILTER) { int i; - /* destroy all instances for this pid */ + /* destroy all instances for this portid */ spin_lock_bh(&instances_lock); for (i = 0; i < INSTANCE_BUCKETS; i++) { struct hlist_node *tmp, *t2; @@ -712,7 +712,7 @@ nfulnl_rcv_nl_event(struct notifier_block *this, hlist_for_each_entry_safe(inst, tmp, t2, head, hlist) { if ((net_eq(n->net, &init_net)) && - (n->pid == inst->peer_pid)) + (n->portid == inst->peer_portid)) __instance_destroy(inst); } } @@ -774,7 +774,7 @@ nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb, } inst = instance_lookup_get(group_num); - if (inst && inst->peer_pid != NETLINK_CB(skb).pid) { + if (inst && inst->peer_portid != NETLINK_CB(skb).portid) { ret = -EPERM; goto out_put; } @@ -788,7 +788,7 @@ nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb, } inst = instance_create(group_num, - NETLINK_CB(skb).pid, + NETLINK_CB(skb).portid, sk_user_ns(NETLINK_CB(skb).ssk)); if (IS_ERR(inst)) { ret = PTR_ERR(inst); @@ -947,7 +947,7 @@ static int seq_show(struct seq_file *s, void *v) return seq_printf(s, "%5d %6d %5d %1d %5d %6d %2d\n", inst->group_num, - inst->peer_pid, inst->qlen, + inst->peer_portid, inst->qlen, inst->copy_mode, inst->copy_range, inst->flushtimeout, atomic_read(&inst->use)); } diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c index c0496a55ad0c..b8d9995b76a8 100644 --- a/net/netfilter/nfnetlink_queue_core.c +++ b/net/netfilter/nfnetlink_queue_core.c @@ -44,7 +44,7 @@ struct nfqnl_instance { struct hlist_node hlist; /* global list of queues */ struct rcu_head rcu; - int peer_pid; + int peer_portid; unsigned int queue_maxlen; unsigned int copy_range; unsigned int queue_dropped; @@ -92,7 +92,7 @@ instance_lookup(u_int16_t queue_num) } static struct nfqnl_instance * -instance_create(u_int16_t queue_num, int pid) +instance_create(u_int16_t queue_num, int portid) { struct nfqnl_instance *inst; unsigned int h; @@ -111,7 +111,7 @@ instance_create(u_int16_t queue_num, int pid) } inst->queue_num = queue_num; - inst->peer_pid = pid; + inst->peer_portid = portid; inst->queue_maxlen = NFQNL_QMAX_DEFAULT; inst->copy_range = 0xfffff; inst->copy_mode = NFQNL_COPY_NONE; @@ -440,7 +440,7 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) } spin_lock_bh(&queue->lock); - if (!queue->peer_pid) { + if (!queue->peer_portid) { err = -EINVAL; goto err_out_free_nskb; } @@ -459,7 +459,7 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) *packet_id_ptr = htonl(entry->id); /* nfnetlink_unicast will either free the nskb or add it to a socket */ - err = nfnetlink_unicast(nskb, &init_net, queue->peer_pid, MSG_DONTWAIT); + err = nfnetlink_unicast(nskb, &init_net, queue->peer_portid, MSG_DONTWAIT); if (err < 0) { queue->queue_user_dropped++; goto err_out_unlock; @@ -616,7 +616,7 @@ nfqnl_rcv_nl_event(struct notifier_block *this, if (event == NETLINK_URELEASE && n->protocol == NETLINK_NETFILTER) { int i; - /* destroy all instances for this pid */ + /* destroy all instances for this portid */ spin_lock(&instances_lock); for (i = 0; i < INSTANCE_BUCKETS; i++) { struct hlist_node *tmp, *t2; @@ -625,7 +625,7 @@ nfqnl_rcv_nl_event(struct notifier_block *this, hlist_for_each_entry_safe(inst, tmp, t2, head, hlist) { if ((n->net == &init_net) && - (n->pid == inst->peer_pid)) + (n->portid == inst->peer_portid)) __instance_destroy(inst); } } @@ -650,7 +650,7 @@ static const struct nla_policy nfqa_verdict_batch_policy[NFQA_MAX+1] = { [NFQA_MARK] = { .type = NLA_U32 }, }; -static struct nfqnl_instance *verdict_instance_lookup(u16 queue_num, int nlpid) +static struct nfqnl_instance *verdict_instance_lookup(u16 queue_num, int nlportid) { struct nfqnl_instance *queue; @@ -658,7 +658,7 @@ static struct nfqnl_instance *verdict_instance_lookup(u16 queue_num, int nlpid) if (!queue) return ERR_PTR(-ENODEV); - if (queue->peer_pid != nlpid) + if (queue->peer_portid != nlportid) return ERR_PTR(-EPERM); return queue; @@ -698,7 +698,7 @@ nfqnl_recv_verdict_batch(struct sock *ctnl, struct sk_buff *skb, LIST_HEAD(batch_list); u16 queue_num = ntohs(nfmsg->res_id); - queue = verdict_instance_lookup(queue_num, NETLINK_CB(skb).pid); + queue = verdict_instance_lookup(queue_num, NETLINK_CB(skb).portid); if (IS_ERR(queue)) return PTR_ERR(queue); @@ -749,7 +749,7 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, queue = instance_lookup(queue_num); if (!queue) - queue = verdict_instance_lookup(queue_num, NETLINK_CB(skb).pid); + queue = verdict_instance_lookup(queue_num, NETLINK_CB(skb).portid); if (IS_ERR(queue)) return PTR_ERR(queue); @@ -832,7 +832,7 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, rcu_read_lock(); queue = instance_lookup(queue_num); - if (queue && queue->peer_pid != NETLINK_CB(skb).pid) { + if (queue && queue->peer_portid != NETLINK_CB(skb).portid) { ret = -EPERM; goto err_out_unlock; } @@ -844,7 +844,7 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, ret = -EBUSY; goto err_out_unlock; } - queue = instance_create(queue_num, NETLINK_CB(skb).pid); + queue = instance_create(queue_num, NETLINK_CB(skb).portid); if (IS_ERR(queue)) { ret = PTR_ERR(queue); goto err_out_unlock; @@ -1016,7 +1016,7 @@ static int seq_show(struct seq_file *s, void *v) return seq_printf(s, "%5d %6d %5d %1d %5d %5d %5d %8d %2d\n", inst->queue_num, - inst->peer_pid, inst->queue_total, + inst->peer_portid, inst->queue_total, inst->copy_mode, inst->copy_range, inst->queue_dropped, inst->queue_user_dropped, inst->id_sequence, 1); diff --git a/net/netlabel/netlabel_cipso_v4.c b/net/netlabel/netlabel_cipso_v4.c index 6bf878335d94..c15042f987bd 100644 --- a/net/netlabel/netlabel_cipso_v4.c +++ b/net/netlabel/netlabel_cipso_v4.c @@ -627,7 +627,7 @@ static int netlbl_cipsov4_listall_cb(struct cipso_v4_doi *doi_def, void *arg) struct netlbl_cipsov4_doiwalk_arg *cb_arg = arg; void *data; - data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).pid, + data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).portid, cb_arg->seq, &netlbl_cipsov4_gnl_family, NLM_F_MULTI, NLBL_CIPSOV4_C_LISTALL); if (data == NULL) diff --git a/net/netlabel/netlabel_mgmt.c b/net/netlabel/netlabel_mgmt.c index 4809e2e48b02..c5384ffc6146 100644 --- a/net/netlabel/netlabel_mgmt.c +++ b/net/netlabel/netlabel_mgmt.c @@ -448,7 +448,7 @@ static int netlbl_mgmt_listall_cb(struct netlbl_dom_map *entry, void *arg) struct netlbl_domhsh_walk_arg *cb_arg = arg; void *data; - data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).pid, + data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).portid, cb_arg->seq, &netlbl_mgmt_gnl_family, NLM_F_MULTI, NLBL_MGMT_C_LISTALL); if (data == NULL) @@ -613,7 +613,7 @@ static int netlbl_mgmt_protocols_cb(struct sk_buff *skb, int ret_val = -ENOMEM; void *data; - data = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, + data = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, &netlbl_mgmt_gnl_family, NLM_F_MULTI, NLBL_MGMT_C_PROTOCOLS); if (data == NULL) diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c index e7ff694f1049..b7944413b404 100644 --- a/net/netlabel/netlabel_unlabeled.c +++ b/net/netlabel/netlabel_unlabeled.c @@ -1096,7 +1096,7 @@ static int netlbl_unlabel_staticlist_gen(u32 cmd, char *secctx; u32 secctx_len; - data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).pid, + data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).portid, cb_arg->seq, &netlbl_unlabel_gnl_family, NLM_F_MULTI, cmd); if (data == NULL) diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 4d348e97e131..0f2e3ad69c47 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -67,8 +67,8 @@ struct netlink_sock { /* struct sock has to be the first member of netlink_sock */ struct sock sk; - u32 pid; - u32 dst_pid; + u32 portid; + u32 dst_portid; u32 dst_group; u32 flags; u32 subscriptions; @@ -104,7 +104,7 @@ static inline int netlink_is_kernel(struct sock *sk) return nlk_sk(sk)->flags & NETLINK_KERNEL_SOCKET; } -struct nl_pid_hash { +struct nl_portid_hash { struct hlist_head *table; unsigned long rehash_time; @@ -118,7 +118,7 @@ struct nl_pid_hash { }; struct netlink_table { - struct nl_pid_hash hash; + struct nl_portid_hash hash; struct hlist_head mc_list; struct listeners __rcu *listeners; unsigned int flags; @@ -145,9 +145,9 @@ static inline u32 netlink_group_mask(u32 group) return group ? 1 << (group - 1) : 0; } -static inline struct hlist_head *nl_pid_hashfn(struct nl_pid_hash *hash, u32 pid) +static inline struct hlist_head *nl_portid_hashfn(struct nl_portid_hash *hash, u32 portid) { - return &hash->table[jhash_1word(pid, hash->rnd) & hash->mask]; + return &hash->table[jhash_1word(portid, hash->rnd) & hash->mask]; } static void netlink_destroy_callback(struct netlink_callback *cb) @@ -239,17 +239,17 @@ netlink_unlock_table(void) wake_up(&nl_table_wait); } -static struct sock *netlink_lookup(struct net *net, int protocol, u32 pid) +static struct sock *netlink_lookup(struct net *net, int protocol, u32 portid) { - struct nl_pid_hash *hash = &nl_table[protocol].hash; + struct nl_portid_hash *hash = &nl_table[protocol].hash; struct hlist_head *head; struct sock *sk; struct hlist_node *node; read_lock(&nl_table_lock); - head = nl_pid_hashfn(hash, pid); + head = nl_portid_hashfn(hash, portid); sk_for_each(sk, node, head) { - if (net_eq(sock_net(sk), net) && (nlk_sk(sk)->pid == pid)) { + if (net_eq(sock_net(sk), net) && (nlk_sk(sk)->portid == portid)) { sock_hold(sk); goto found; } @@ -260,7 +260,7 @@ found: return sk; } -static struct hlist_head *nl_pid_hash_zalloc(size_t size) +static struct hlist_head *nl_portid_hash_zalloc(size_t size) { if (size <= PAGE_SIZE) return kzalloc(size, GFP_ATOMIC); @@ -270,7 +270,7 @@ static struct hlist_head *nl_pid_hash_zalloc(size_t size) get_order(size)); } -static void nl_pid_hash_free(struct hlist_head *table, size_t size) +static void nl_portid_hash_free(struct hlist_head *table, size_t size) { if (size <= PAGE_SIZE) kfree(table); @@ -278,7 +278,7 @@ static void nl_pid_hash_free(struct hlist_head *table, size_t size) free_pages((unsigned long)table, get_order(size)); } -static int nl_pid_hash_rehash(struct nl_pid_hash *hash, int grow) +static int nl_portid_hash_rehash(struct nl_portid_hash *hash, int grow) { unsigned int omask, mask, shift; size_t osize, size; @@ -296,7 +296,7 @@ static int nl_pid_hash_rehash(struct nl_pid_hash *hash, int grow) size *= 2; } - table = nl_pid_hash_zalloc(size); + table = nl_portid_hash_zalloc(size); if (!table) return 0; @@ -311,23 +311,23 @@ static int nl_pid_hash_rehash(struct nl_pid_hash *hash, int grow) struct hlist_node *node, *tmp; sk_for_each_safe(sk, node, tmp, &otable[i]) - __sk_add_node(sk, nl_pid_hashfn(hash, nlk_sk(sk)->pid)); + __sk_add_node(sk, nl_portid_hashfn(hash, nlk_sk(sk)->portid)); } - nl_pid_hash_free(otable, osize); + nl_portid_hash_free(otable, osize); hash->rehash_time = jiffies + 10 * 60 * HZ; return 1; } -static inline int nl_pid_hash_dilute(struct nl_pid_hash *hash, int len) +static inline int nl_portid_hash_dilute(struct nl_portid_hash *hash, int len) { int avg = hash->entries >> hash->shift; - if (unlikely(avg > 1) && nl_pid_hash_rehash(hash, 1)) + if (unlikely(avg > 1) && nl_portid_hash_rehash(hash, 1)) return 1; if (unlikely(len > avg) && time_after(jiffies, hash->rehash_time)) { - nl_pid_hash_rehash(hash, 0); + nl_portid_hash_rehash(hash, 0); return 1; } @@ -356,9 +356,9 @@ netlink_update_listeners(struct sock *sk) * makes sure updates are visible before bind or setsockopt return. */ } -static int netlink_insert(struct sock *sk, struct net *net, u32 pid) +static int netlink_insert(struct sock *sk, struct net *net, u32 portid) { - struct nl_pid_hash *hash = &nl_table[sk->sk_protocol].hash; + struct nl_portid_hash *hash = &nl_table[sk->sk_protocol].hash; struct hlist_head *head; int err = -EADDRINUSE; struct sock *osk; @@ -366,10 +366,10 @@ static int netlink_insert(struct sock *sk, struct net *net, u32 pid) int len; netlink_table_grab(); - head = nl_pid_hashfn(hash, pid); + head = nl_portid_hashfn(hash, portid); len = 0; sk_for_each(osk, node, head) { - if (net_eq(sock_net(osk), net) && (nlk_sk(osk)->pid == pid)) + if (net_eq(sock_net(osk), net) && (nlk_sk(osk)->portid == portid)) break; len++; } @@ -377,17 +377,17 @@ static int netlink_insert(struct sock *sk, struct net *net, u32 pid) goto err; err = -EBUSY; - if (nlk_sk(sk)->pid) + if (nlk_sk(sk)->portid) goto err; err = -ENOMEM; if (BITS_PER_LONG > 32 && unlikely(hash->entries >= UINT_MAX)) goto err; - if (len && nl_pid_hash_dilute(hash, len)) - head = nl_pid_hashfn(hash, pid); + if (len && nl_portid_hash_dilute(hash, len)) + head = nl_portid_hashfn(hash, portid); hash->entries++; - nlk_sk(sk)->pid = pid; + nlk_sk(sk)->portid = portid; sk_add_node(sk, head); err = 0; @@ -518,11 +518,11 @@ static int netlink_release(struct socket *sock) skb_queue_purge(&sk->sk_write_queue); - if (nlk->pid) { + if (nlk->portid) { struct netlink_notify n = { .net = sock_net(sk), .protocol = sk->sk_protocol, - .pid = nlk->pid, + .portid = nlk->portid, }; atomic_notifier_call_chain(&netlink_chain, NETLINK_URELEASE, &n); @@ -559,24 +559,24 @@ static int netlink_autobind(struct socket *sock) { struct sock *sk = sock->sk; struct net *net = sock_net(sk); - struct nl_pid_hash *hash = &nl_table[sk->sk_protocol].hash; + struct nl_portid_hash *hash = &nl_table[sk->sk_protocol].hash; struct hlist_head *head; struct sock *osk; struct hlist_node *node; - s32 pid = task_tgid_vnr(current); + s32 portid = task_tgid_vnr(current); int err; static s32 rover = -4097; retry: cond_resched(); netlink_table_grab(); - head = nl_pid_hashfn(hash, pid); + head = nl_portid_hashfn(hash, portid); sk_for_each(osk, node, head) { if (!net_eq(sock_net(osk), net)) continue; - if (nlk_sk(osk)->pid == pid) { - /* Bind collision, search negative pid values. */ - pid = rover--; + if (nlk_sk(osk)->portid == portid) { + /* Bind collision, search negative portid values. */ + portid = rover--; if (rover > -4097) rover = -4097; netlink_table_ungrab(); @@ -585,7 +585,7 @@ retry: } netlink_table_ungrab(); - err = netlink_insert(sk, net, pid); + err = netlink_insert(sk, net, portid); if (err == -EADDRINUSE) goto retry; @@ -668,8 +668,8 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, return err; } - if (nlk->pid) { - if (nladdr->nl_pid != nlk->pid) + if (nlk->portid) { + if (nladdr->nl_pid != nlk->portid) return -EINVAL; } else { err = nladdr->nl_pid ? @@ -715,7 +715,7 @@ static int netlink_connect(struct socket *sock, struct sockaddr *addr, if (addr->sa_family == AF_UNSPEC) { sk->sk_state = NETLINK_UNCONNECTED; - nlk->dst_pid = 0; + nlk->dst_portid = 0; nlk->dst_group = 0; return 0; } @@ -726,12 +726,12 @@ static int netlink_connect(struct socket *sock, struct sockaddr *addr, if (nladdr->nl_groups && !netlink_capable(sock, NL_CFG_F_NONROOT_SEND)) return -EPERM; - if (!nlk->pid) + if (!nlk->portid) err = netlink_autobind(sock); if (err == 0) { sk->sk_state = NETLINK_CONNECTED; - nlk->dst_pid = nladdr->nl_pid; + nlk->dst_portid = nladdr->nl_pid; nlk->dst_group = ffs(nladdr->nl_groups); } @@ -750,10 +750,10 @@ static int netlink_getname(struct socket *sock, struct sockaddr *addr, *addr_len = sizeof(*nladdr); if (peer) { - nladdr->nl_pid = nlk->dst_pid; + nladdr->nl_pid = nlk->dst_portid; nladdr->nl_groups = netlink_group_mask(nlk->dst_group); } else { - nladdr->nl_pid = nlk->pid; + nladdr->nl_pid = nlk->portid; nladdr->nl_groups = nlk->groups ? nlk->groups[0] : 0; } return 0; @@ -772,19 +772,19 @@ static void netlink_overrun(struct sock *sk) atomic_inc(&sk->sk_drops); } -static struct sock *netlink_getsockbypid(struct sock *ssk, u32 pid) +static struct sock *netlink_getsockbyportid(struct sock *ssk, u32 portid) { struct sock *sock; struct netlink_sock *nlk; - sock = netlink_lookup(sock_net(ssk), ssk->sk_protocol, pid); + sock = netlink_lookup(sock_net(ssk), ssk->sk_protocol, portid); if (!sock) return ERR_PTR(-ECONNREFUSED); /* Don't bother queuing skb if kernel socket has no input function */ nlk = nlk_sk(sock); if (sock->sk_state == NETLINK_CONNECTED && - nlk->dst_pid != nlk_sk(ssk)->pid) { + nlk->dst_portid != nlk_sk(ssk)->portid) { sock_put(sock); return ERR_PTR(-ECONNREFUSED); } @@ -935,7 +935,7 @@ static int netlink_unicast_kernel(struct sock *sk, struct sk_buff *skb, } int netlink_unicast(struct sock *ssk, struct sk_buff *skb, - u32 pid, int nonblock) + u32 portid, int nonblock) { struct sock *sk; int err; @@ -945,7 +945,7 @@ int netlink_unicast(struct sock *ssk, struct sk_buff *skb, timeo = sock_sndtimeo(ssk, nonblock); retry: - sk = netlink_getsockbypid(ssk, pid); + sk = netlink_getsockbyportid(ssk, portid); if (IS_ERR(sk)) { kfree_skb(skb); return PTR_ERR(sk); @@ -1005,7 +1005,7 @@ static int netlink_broadcast_deliver(struct sock *sk, struct sk_buff *skb) struct netlink_broadcast_data { struct sock *exclude_sk; struct net *net; - u32 pid; + u32 portid; u32 group; int failure; int delivery_failure; @@ -1026,7 +1026,7 @@ static int do_one_broadcast(struct sock *sk, if (p->exclude_sk == sk) goto out; - if (nlk->pid == p->pid || p->group - 1 >= nlk->ngroups || + if (nlk->portid == p->portid || p->group - 1 >= nlk->ngroups || !test_bit(p->group - 1, nlk->groups)) goto out; @@ -1078,7 +1078,7 @@ out: return 0; } -int netlink_broadcast_filtered(struct sock *ssk, struct sk_buff *skb, u32 pid, +int netlink_broadcast_filtered(struct sock *ssk, struct sk_buff *skb, u32 portid, u32 group, gfp_t allocation, int (*filter)(struct sock *dsk, struct sk_buff *skb, void *data), void *filter_data) @@ -1092,7 +1092,7 @@ int netlink_broadcast_filtered(struct sock *ssk, struct sk_buff *skb, u32 pid, info.exclude_sk = ssk; info.net = net; - info.pid = pid; + info.portid = portid; info.group = group; info.failure = 0; info.delivery_failure = 0; @@ -1130,17 +1130,17 @@ int netlink_broadcast_filtered(struct sock *ssk, struct sk_buff *skb, u32 pid, } EXPORT_SYMBOL(netlink_broadcast_filtered); -int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, +int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 portid, u32 group, gfp_t allocation) { - return netlink_broadcast_filtered(ssk, skb, pid, group, allocation, + return netlink_broadcast_filtered(ssk, skb, portid, group, allocation, NULL, NULL); } EXPORT_SYMBOL(netlink_broadcast); struct netlink_set_err_data { struct sock *exclude_sk; - u32 pid; + u32 portid; u32 group; int code; }; @@ -1156,7 +1156,7 @@ static int do_one_set_err(struct sock *sk, struct netlink_set_err_data *p) if (!net_eq(sock_net(sk), sock_net(p->exclude_sk))) goto out; - if (nlk->pid == p->pid || p->group - 1 >= nlk->ngroups || + if (nlk->portid == p->portid || p->group - 1 >= nlk->ngroups || !test_bit(p->group - 1, nlk->groups)) goto out; @@ -1174,14 +1174,14 @@ out: /** * netlink_set_err - report error to broadcast listeners * @ssk: the kernel netlink socket, as returned by netlink_kernel_create() - * @pid: the PID of a process that we want to skip (if any) + * @portid: the PORTID of a process that we want to skip (if any) * @groups: the broadcast group that will notice the error * @code: error code, must be negative (as usual in kernelspace) * * This function returns the number of broadcast listeners that have set the * NETLINK_RECV_NO_ENOBUFS socket option. */ -int netlink_set_err(struct sock *ssk, u32 pid, u32 group, int code) +int netlink_set_err(struct sock *ssk, u32 portid, u32 group, int code) { struct netlink_set_err_data info; struct hlist_node *node; @@ -1189,7 +1189,7 @@ int netlink_set_err(struct sock *ssk, u32 pid, u32 group, int code) int ret = 0; info.exclude_sk = ssk; - info.pid = pid; + info.portid = portid; info.group = group; /* sk->sk_err wants a positive error value */ info.code = -code; @@ -1354,7 +1354,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, struct sock *sk = sock->sk; struct netlink_sock *nlk = nlk_sk(sk); struct sockaddr_nl *addr = msg->msg_name; - u32 dst_pid; + u32 dst_portid; u32 dst_group; struct sk_buff *skb; int err; @@ -1374,18 +1374,18 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, err = -EINVAL; if (addr->nl_family != AF_NETLINK) goto out; - dst_pid = addr->nl_pid; + dst_portid = addr->nl_pid; dst_group = ffs(addr->nl_groups); err = -EPERM; - if ((dst_group || dst_pid) && + if ((dst_group || dst_portid) && !netlink_capable(sock, NL_CFG_F_NONROOT_SEND)) goto out; } else { - dst_pid = nlk->dst_pid; + dst_portid = nlk->dst_portid; dst_group = nlk->dst_group; } - if (!nlk->pid) { + if (!nlk->portid) { err = netlink_autobind(sock); if (err) goto out; @@ -1399,7 +1399,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, if (skb == NULL) goto out; - NETLINK_CB(skb).pid = nlk->pid; + NETLINK_CB(skb).portid = nlk->portid; NETLINK_CB(skb).dst_group = dst_group; NETLINK_CB(skb).creds = siocb->scm->creds; @@ -1417,9 +1417,9 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, if (dst_group) { atomic_inc(&skb->users); - netlink_broadcast(sk, skb, dst_pid, dst_group, GFP_KERNEL); + netlink_broadcast(sk, skb, dst_portid, dst_group, GFP_KERNEL); } - err = netlink_unicast(sk, skb, dst_pid, msg->msg_flags&MSG_DONTWAIT); + err = netlink_unicast(sk, skb, dst_portid, msg->msg_flags&MSG_DONTWAIT); out: scm_destroy(siocb->scm); @@ -1482,7 +1482,7 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock, struct sockaddr_nl *addr = (struct sockaddr_nl *)msg->msg_name; addr->nl_family = AF_NETLINK; addr->nl_pad = 0; - addr->nl_pid = NETLINK_CB(skb).pid; + addr->nl_pid = NETLINK_CB(skb).portid; addr->nl_groups = netlink_group_mask(NETLINK_CB(skb).dst_group); msg->msg_namelen = sizeof(*addr); } @@ -1683,7 +1683,7 @@ void netlink_clear_multicast_users(struct sock *ksk, unsigned int group) } struct nlmsghdr * -__nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len, int flags) +__nlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, int type, int len, int flags) { struct nlmsghdr *nlh; int size = NLMSG_LENGTH(len); @@ -1692,7 +1692,7 @@ __nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len, int flags) nlh->nlmsg_type = type; nlh->nlmsg_len = size; nlh->nlmsg_flags = flags; - nlh->nlmsg_pid = pid; + nlh->nlmsg_pid = portid; nlh->nlmsg_seq = seq; if (!__builtin_constant_p(size) || NLMSG_ALIGN(size) - size != 0) memset(NLMSG_DATA(nlh) + len, 0, NLMSG_ALIGN(size) - size); @@ -1788,7 +1788,7 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, atomic_inc(&skb->users); cb->skb = skb; - sk = netlink_lookup(sock_net(ssk), ssk->sk_protocol, NETLINK_CB(skb).pid); + sk = netlink_lookup(sock_net(ssk), ssk->sk_protocol, NETLINK_CB(skb).portid); if (sk == NULL) { netlink_destroy_callback(cb); return -ECONNREFUSED; @@ -1836,7 +1836,7 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err) sk = netlink_lookup(sock_net(in_skb->sk), in_skb->sk->sk_protocol, - NETLINK_CB(in_skb).pid); + NETLINK_CB(in_skb).portid); if (sk) { sk->sk_err = ENOBUFS; sk->sk_error_report(sk); @@ -1845,12 +1845,12 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err) return; } - rep = __nlmsg_put(skb, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq, + rep = __nlmsg_put(skb, NETLINK_CB(in_skb).portid, nlh->nlmsg_seq, NLMSG_ERROR, payload, 0); errmsg = nlmsg_data(rep); errmsg->error = err; memcpy(&errmsg->msg, nlh, err ? nlh->nlmsg_len : sizeof(*nlh)); - netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT); + netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).portid, MSG_DONTWAIT); } EXPORT_SYMBOL(netlink_ack); @@ -1900,33 +1900,33 @@ EXPORT_SYMBOL(netlink_rcv_skb); * nlmsg_notify - send a notification netlink message * @sk: netlink socket to use * @skb: notification message - * @pid: destination netlink pid for reports or 0 + * @portid: destination netlink portid for reports or 0 * @group: destination multicast group or 0 * @report: 1 to report back, 0 to disable * @flags: allocation flags */ -int nlmsg_notify(struct sock *sk, struct sk_buff *skb, u32 pid, +int nlmsg_notify(struct sock *sk, struct sk_buff *skb, u32 portid, unsigned int group, int report, gfp_t flags) { int err = 0; if (group) { - int exclude_pid = 0; + int exclude_portid = 0; if (report) { atomic_inc(&skb->users); - exclude_pid = pid; + exclude_portid = portid; } /* errors reported via destination sk->sk_err, but propagate * delivery errors if NETLINK_BROADCAST_ERROR flag is set */ - err = nlmsg_multicast(sk, skb, exclude_pid, group, flags); + err = nlmsg_multicast(sk, skb, exclude_portid, group, flags); } if (report) { int err2; - err2 = nlmsg_unicast(sk, skb, pid); + err2 = nlmsg_unicast(sk, skb, portid); if (!err || err == -ESRCH) err = err2; } @@ -1951,7 +1951,7 @@ static struct sock *netlink_seq_socket_idx(struct seq_file *seq, loff_t pos) loff_t off = 0; for (i = 0; i < MAX_LINKS; i++) { - struct nl_pid_hash *hash = &nl_table[i].hash; + struct nl_portid_hash *hash = &nl_table[i].hash; for (j = 0; j <= hash->mask; j++) { sk_for_each(s, node, &hash->table[j]) { @@ -1999,7 +1999,7 @@ static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos) j = iter->hash_idx + 1; do { - struct nl_pid_hash *hash = &nl_table[i].hash; + struct nl_portid_hash *hash = &nl_table[i].hash; for (; j <= hash->mask; j++) { s = sk_head(&hash->table[j]); @@ -2038,7 +2038,7 @@ static int netlink_seq_show(struct seq_file *seq, void *v) seq_printf(seq, "%pK %-3d %-6d %08x %-8d %-8d %pK %-8d %-8d %-8lu\n", s, s->sk_protocol, - nlk->pid, + nlk->portid, nlk->groups ? (u32)nlk->groups[0] : 0, sk_rmem_alloc_get(s), sk_wmem_alloc_get(s), @@ -2183,12 +2183,12 @@ static int __init netlink_proto_init(void) order = get_bitmask_order(min(limit, (unsigned long)UINT_MAX)) - 1; for (i = 0; i < MAX_LINKS; i++) { - struct nl_pid_hash *hash = &nl_table[i].hash; + struct nl_portid_hash *hash = &nl_table[i].hash; - hash->table = nl_pid_hash_zalloc(1 * sizeof(*hash->table)); + hash->table = nl_portid_hash_zalloc(1 * sizeof(*hash->table)); if (!hash->table) { while (i-- > 0) - nl_pid_hash_free(nl_table[i].hash.table, + nl_portid_hash_free(nl_table[i].hash.table, 1 * sizeof(*hash->table)); kfree(nl_table); goto panic; diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 19288b7d6135..f2aabb6f4105 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -501,7 +501,7 @@ EXPORT_SYMBOL(genl_unregister_family); /** * genlmsg_put - Add generic netlink header to netlink message * @skb: socket buffer holding the message - * @pid: netlink pid the message is addressed to + * @portid: netlink portid the message is addressed to * @seq: sequence number (usually the one of the sender) * @family: generic netlink family * @flags: netlink message flags @@ -509,13 +509,13 @@ EXPORT_SYMBOL(genl_unregister_family); * * Returns pointer to user specific header */ -void *genlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, +void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, struct genl_family *family, int flags, u8 cmd) { struct nlmsghdr *nlh; struct genlmsghdr *hdr; - nlh = nlmsg_put(skb, pid, seq, family->id, GENL_HDRLEN + + nlh = nlmsg_put(skb, portid, seq, family->id, GENL_HDRLEN + family->hdrsize, flags); if (nlh == NULL) return NULL; @@ -585,7 +585,7 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) } info.snd_seq = nlh->nlmsg_seq; - info.snd_pid = NETLINK_CB(skb).pid; + info.snd_portid = NETLINK_CB(skb).portid; info.nlhdr = nlh; info.genlhdr = nlmsg_data(nlh); info.userhdr = nlmsg_data(nlh) + GENL_HDRLEN; @@ -626,12 +626,12 @@ static struct genl_family genl_ctrl = { .netnsok = true, }; -static int ctrl_fill_info(struct genl_family *family, u32 pid, u32 seq, +static int ctrl_fill_info(struct genl_family *family, u32 portid, u32 seq, u32 flags, struct sk_buff *skb, u8 cmd) { void *hdr; - hdr = genlmsg_put(skb, pid, seq, &genl_ctrl, flags, cmd); + hdr = genlmsg_put(skb, portid, seq, &genl_ctrl, flags, cmd); if (hdr == NULL) return -1; @@ -701,7 +701,7 @@ nla_put_failure: return -EMSGSIZE; } -static int ctrl_fill_mcgrp_info(struct genl_multicast_group *grp, u32 pid, +static int ctrl_fill_mcgrp_info(struct genl_multicast_group *grp, u32 portid, u32 seq, u32 flags, struct sk_buff *skb, u8 cmd) { @@ -709,7 +709,7 @@ static int ctrl_fill_mcgrp_info(struct genl_multicast_group *grp, u32 pid, struct nlattr *nla_grps; struct nlattr *nest; - hdr = genlmsg_put(skb, pid, seq, &genl_ctrl, flags, cmd); + hdr = genlmsg_put(skb, portid, seq, &genl_ctrl, flags, cmd); if (hdr == NULL) return -1; @@ -756,7 +756,7 @@ static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb) continue; if (++n < fams_to_skip) continue; - if (ctrl_fill_info(rt, NETLINK_CB(cb->skb).pid, + if (ctrl_fill_info(rt, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, skb, CTRL_CMD_NEWFAMILY) < 0) goto errout; @@ -773,7 +773,7 @@ errout: } static struct sk_buff *ctrl_build_family_msg(struct genl_family *family, - u32 pid, int seq, u8 cmd) + u32 portid, int seq, u8 cmd) { struct sk_buff *skb; int err; @@ -782,7 +782,7 @@ static struct sk_buff *ctrl_build_family_msg(struct genl_family *family, if (skb == NULL) return ERR_PTR(-ENOBUFS); - err = ctrl_fill_info(family, pid, seq, 0, skb, cmd); + err = ctrl_fill_info(family, portid, seq, 0, skb, cmd); if (err < 0) { nlmsg_free(skb); return ERR_PTR(err); @@ -792,7 +792,7 @@ static struct sk_buff *ctrl_build_family_msg(struct genl_family *family, } static struct sk_buff *ctrl_build_mcgrp_msg(struct genl_multicast_group *grp, - u32 pid, int seq, u8 cmd) + u32 portid, int seq, u8 cmd) { struct sk_buff *skb; int err; @@ -801,7 +801,7 @@ static struct sk_buff *ctrl_build_mcgrp_msg(struct genl_multicast_group *grp, if (skb == NULL) return ERR_PTR(-ENOBUFS); - err = ctrl_fill_mcgrp_info(grp, pid, seq, 0, skb, cmd); + err = ctrl_fill_mcgrp_info(grp, portid, seq, 0, skb, cmd); if (err < 0) { nlmsg_free(skb); return ERR_PTR(err); @@ -853,7 +853,7 @@ static int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info) return -ENOENT; } - msg = ctrl_build_family_msg(res, info->snd_pid, info->snd_seq, + msg = ctrl_build_family_msg(res, info->snd_portid, info->snd_seq, CTRL_CMD_NEWFAMILY); if (IS_ERR(msg)) return PTR_ERR(msg); @@ -971,7 +971,7 @@ problem: subsys_initcall(genl_init); -static int genlmsg_mcast(struct sk_buff *skb, u32 pid, unsigned long group, +static int genlmsg_mcast(struct sk_buff *skb, u32 portid, unsigned long group, gfp_t flags) { struct sk_buff *tmp; @@ -986,7 +986,7 @@ static int genlmsg_mcast(struct sk_buff *skb, u32 pid, unsigned long group, goto error; } err = nlmsg_multicast(prev->genl_sock, tmp, - pid, group, flags); + portid, group, flags); if (err) goto error; } @@ -994,20 +994,20 @@ static int genlmsg_mcast(struct sk_buff *skb, u32 pid, unsigned long group, prev = net; } - return nlmsg_multicast(prev->genl_sock, skb, pid, group, flags); + return nlmsg_multicast(prev->genl_sock, skb, portid, group, flags); error: kfree_skb(skb); return err; } -int genlmsg_multicast_allns(struct sk_buff *skb, u32 pid, unsigned int group, +int genlmsg_multicast_allns(struct sk_buff *skb, u32 portid, unsigned int group, gfp_t flags) { - return genlmsg_mcast(skb, pid, group, flags); + return genlmsg_mcast(skb, portid, group, flags); } EXPORT_SYMBOL(genlmsg_multicast_allns); -void genl_notify(struct sk_buff *skb, struct net *net, u32 pid, u32 group, +void genl_notify(struct sk_buff *skb, struct net *net, u32 portid, u32 group, struct nlmsghdr *nlh, gfp_t flags) { struct sock *sk = net->genl_sock; @@ -1016,6 +1016,6 @@ void genl_notify(struct sk_buff *skb, struct net *net, u32 pid, u32 group, if (nlh) report = nlmsg_report(nlh); - nlmsg_notify(sk, skb, pid, group, report, flags); + nlmsg_notify(sk, skb, portid, group, report, flags); } EXPORT_SYMBOL(genl_notify); diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 4c51714ee741..4bbb70e32d1e 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -58,7 +58,7 @@ static int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target, { void *hdr; - hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, + hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, &nfc_genl_family, flags, NFC_CMD_GET_TARGET); if (!hdr) return -EMSGSIZE; @@ -165,7 +165,7 @@ int nfc_genl_targets_found(struct nfc_dev *dev) struct sk_buff *msg; void *hdr; - dev->genl_data.poll_req_pid = 0; + dev->genl_data.poll_req_portid = 0; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); if (!msg) @@ -347,13 +347,13 @@ free_msg: } static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev, - u32 pid, u32 seq, + u32 portid, u32 seq, struct netlink_callback *cb, int flags) { void *hdr; - hdr = genlmsg_put(msg, pid, seq, &nfc_genl_family, flags, + hdr = genlmsg_put(msg, portid, seq, &nfc_genl_family, flags, NFC_CMD_GET_DEVICE); if (!hdr) return -EMSGSIZE; @@ -401,7 +401,7 @@ static int nfc_genl_dump_devices(struct sk_buff *skb, while (dev) { int rc; - rc = nfc_genl_send_device(skb, dev, NETLINK_CB(cb->skb).pid, + rc = nfc_genl_send_device(skb, dev, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, cb, NLM_F_MULTI); if (rc < 0) break; @@ -520,7 +520,7 @@ static int nfc_genl_get_device(struct sk_buff *skb, struct genl_info *info) goto out_putdev; } - rc = nfc_genl_send_device(msg, dev, info->snd_pid, info->snd_seq, + rc = nfc_genl_send_device(msg, dev, info->snd_portid, info->snd_seq, NULL, 0); if (rc < 0) goto out_free; @@ -611,7 +611,7 @@ static int nfc_genl_start_poll(struct sk_buff *skb, struct genl_info *info) rc = nfc_start_poll(dev, im_protocols, tm_protocols); if (!rc) - dev->genl_data.poll_req_pid = info->snd_pid; + dev->genl_data.poll_req_portid = info->snd_portid; mutex_unlock(&dev->genl_data.genl_data_mutex); @@ -645,13 +645,13 @@ static int nfc_genl_stop_poll(struct sk_buff *skb, struct genl_info *info) mutex_lock(&dev->genl_data.genl_data_mutex); - if (dev->genl_data.poll_req_pid != info->snd_pid) { + if (dev->genl_data.poll_req_portid != info->snd_portid) { rc = -EBUSY; goto out; } rc = nfc_stop_poll(dev); - dev->genl_data.poll_req_pid = 0; + dev->genl_data.poll_req_portid = 0; out: mutex_unlock(&dev->genl_data.genl_data_mutex); @@ -771,15 +771,15 @@ static int nfc_genl_rcv_nl_event(struct notifier_block *this, if (event != NETLINK_URELEASE || n->protocol != NETLINK_GENERIC) goto out; - pr_debug("NETLINK_URELEASE event from id %d\n", n->pid); + pr_debug("NETLINK_URELEASE event from id %d\n", n->portid); nfc_device_iter_init(&iter); dev = nfc_device_iter_next(&iter); while (dev) { - if (dev->genl_data.poll_req_pid == n->pid) { + if (dev->genl_data.poll_req_portid == n->portid) { nfc_stop_poll(dev); - dev->genl_data.poll_req_pid = 0; + dev->genl_data.poll_req_portid = 0; } dev = nfc_device_iter_next(&iter); } @@ -792,7 +792,7 @@ out: void nfc_genl_data_init(struct nfc_genl_data *genl_data) { - genl_data->poll_req_pid = 0; + genl_data->poll_req_portid = 0; mutex_init(&genl_data->genl_data_mutex); } diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 0da687769f56..c7425f32e7db 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -286,7 +286,7 @@ static int output_userspace(struct datapath *dp, struct sk_buff *skb, upcall.cmd = OVS_PACKET_CMD_ACTION; upcall.key = &OVS_CB(skb)->flow->key; upcall.userdata = NULL; - upcall.pid = 0; + upcall.portid = 0; for (a = nla_data(attr), rem = nla_len(attr); rem > 0; a = nla_next(a, &rem)) { @@ -296,7 +296,7 @@ static int output_userspace(struct datapath *dp, struct sk_buff *skb, break; case OVS_USERSPACE_ATTR_PID: - upcall.pid = nla_get_u32(a); + upcall.portid = nla_get_u32(a); break; } } diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 105a0b5adc51..56327e877ed9 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -225,7 +225,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb) upcall.cmd = OVS_PACKET_CMD_MISS; upcall.key = &key; upcall.userdata = NULL; - upcall.pid = p->upcall_pid; + upcall.portid = p->upcall_portid; ovs_dp_upcall(dp, skb, &upcall); consume_skb(skb); stats_counter = &stats->n_missed; @@ -261,7 +261,7 @@ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb, int dp_ifindex; int err; - if (upcall_info->pid == 0) { + if (upcall_info->portid == 0) { err = -ENOTCONN; goto err; } @@ -395,7 +395,7 @@ static int queue_userspace_packet(struct net *net, int dp_ifindex, skb_copy_and_csum_dev(skb, nla_data(nla)); - err = genlmsg_unicast(net, user_skb, upcall_info->pid); + err = genlmsg_unicast(net, user_skb, upcall_info->portid); out: kfree_skb(nskb); @@ -780,7 +780,7 @@ static struct genl_multicast_group ovs_dp_flow_multicast_group = { /* Called with genl_lock. */ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp, - struct sk_buff *skb, u32 pid, + struct sk_buff *skb, u32 portid, u32 seq, u32 flags, u8 cmd) { const int skb_orig_len = skb->len; @@ -795,7 +795,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp, sf_acts = rcu_dereference_protected(flow->sf_acts, lockdep_genl_is_held()); - ovs_header = genlmsg_put(skb, pid, seq, &dp_flow_genl_family, flags, cmd); + ovs_header = genlmsg_put(skb, portid, seq, &dp_flow_genl_family, flags, cmd); if (!ovs_header) return -EMSGSIZE; @@ -879,7 +879,7 @@ static struct sk_buff *ovs_flow_cmd_alloc_info(struct sw_flow *flow) static struct sk_buff *ovs_flow_cmd_build_info(struct sw_flow *flow, struct datapath *dp, - u32 pid, u32 seq, u8 cmd) + u32 portid, u32 seq, u8 cmd) { struct sk_buff *skb; int retval; @@ -888,7 +888,7 @@ static struct sk_buff *ovs_flow_cmd_build_info(struct sw_flow *flow, if (!skb) return ERR_PTR(-ENOMEM); - retval = ovs_flow_cmd_fill_info(flow, dp, skb, pid, seq, 0, cmd); + retval = ovs_flow_cmd_fill_info(flow, dp, skb, portid, seq, 0, cmd); BUG_ON(retval < 0); return skb; } @@ -970,7 +970,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) flow->hash = ovs_flow_hash(&key, key_len); ovs_flow_tbl_insert(table, flow); - reply = ovs_flow_cmd_build_info(flow, dp, info->snd_pid, + reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid, info->snd_seq, OVS_FLOW_CMD_NEW); } else { @@ -1008,7 +1008,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) ovs_flow_deferred_free_acts(old_acts); } - reply = ovs_flow_cmd_build_info(flow, dp, info->snd_pid, + reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid, info->snd_seq, OVS_FLOW_CMD_NEW); /* Clear stats. */ @@ -1020,7 +1020,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) } if (!IS_ERR(reply)) - genl_notify(reply, genl_info_net(info), info->snd_pid, + genl_notify(reply, genl_info_net(info), info->snd_portid, ovs_dp_flow_multicast_group.id, info->nlhdr, GFP_KERNEL); else @@ -1061,7 +1061,7 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info) if (!flow) return -ENOENT; - reply = ovs_flow_cmd_build_info(flow, dp, info->snd_pid, + reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid, info->snd_seq, OVS_FLOW_CMD_NEW); if (IS_ERR(reply)) return PTR_ERR(reply); @@ -1103,13 +1103,13 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info) ovs_flow_tbl_remove(table, flow); - err = ovs_flow_cmd_fill_info(flow, dp, reply, info->snd_pid, + 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); - genl_notify(reply, genl_info_net(info), info->snd_pid, + genl_notify(reply, genl_info_net(info), info->snd_portid, ovs_dp_flow_multicast_group.id, info->nlhdr, GFP_KERNEL); return 0; } @@ -1137,7 +1137,7 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) break; if (ovs_flow_cmd_fill_info(flow, dp, skb, - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, OVS_FLOW_CMD_NEW) < 0) break; @@ -1191,13 +1191,13 @@ static struct genl_multicast_group ovs_dp_datapath_multicast_group = { }; static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb, - u32 pid, u32 seq, u32 flags, u8 cmd) + u32 portid, u32 seq, u32 flags, u8 cmd) { struct ovs_header *ovs_header; struct ovs_dp_stats dp_stats; int err; - ovs_header = genlmsg_put(skb, pid, seq, &dp_datapath_genl_family, + ovs_header = genlmsg_put(skb, portid, seq, &dp_datapath_genl_family, flags, cmd); if (!ovs_header) goto error; @@ -1222,7 +1222,7 @@ error: return -EMSGSIZE; } -static struct sk_buff *ovs_dp_cmd_build_info(struct datapath *dp, u32 pid, +static struct sk_buff *ovs_dp_cmd_build_info(struct datapath *dp, u32 portid, u32 seq, u8 cmd) { struct sk_buff *skb; @@ -1232,7 +1232,7 @@ static struct sk_buff *ovs_dp_cmd_build_info(struct datapath *dp, u32 pid, if (!skb) return ERR_PTR(-ENOMEM); - retval = ovs_dp_cmd_fill_info(dp, skb, pid, seq, 0, cmd); + retval = ovs_dp_cmd_fill_info(dp, skb, portid, seq, 0, cmd); if (retval < 0) { kfree_skb(skb); return ERR_PTR(retval); @@ -1311,7 +1311,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) parms.options = NULL; parms.dp = dp; parms.port_no = OVSP_LOCAL; - parms.upcall_pid = nla_get_u32(a[OVS_DP_ATTR_UPCALL_PID]); + parms.upcall_portid = nla_get_u32(a[OVS_DP_ATTR_UPCALL_PID]); vport = new_vport(&parms); if (IS_ERR(vport)) { @@ -1322,7 +1322,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) goto err_destroy_ports_array; } - reply = ovs_dp_cmd_build_info(dp, info->snd_pid, + reply = ovs_dp_cmd_build_info(dp, info->snd_portid, info->snd_seq, OVS_DP_CMD_NEW); err = PTR_ERR(reply); if (IS_ERR(reply)) @@ -1332,7 +1332,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) list_add_tail(&dp->list_node, &ovs_net->dps); rtnl_unlock(); - genl_notify(reply, genl_info_net(info), info->snd_pid, + genl_notify(reply, genl_info_net(info), info->snd_portid, ovs_dp_datapath_multicast_group.id, info->nlhdr, GFP_KERNEL); return 0; @@ -1394,7 +1394,7 @@ static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info) if (IS_ERR(dp)) return err; - reply = ovs_dp_cmd_build_info(dp, info->snd_pid, + reply = ovs_dp_cmd_build_info(dp, info->snd_portid, info->snd_seq, OVS_DP_CMD_DEL); err = PTR_ERR(reply); if (IS_ERR(reply)) @@ -1402,7 +1402,7 @@ static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info) __dp_destroy(dp); - genl_notify(reply, genl_info_net(info), info->snd_pid, + genl_notify(reply, genl_info_net(info), info->snd_portid, ovs_dp_datapath_multicast_group.id, info->nlhdr, GFP_KERNEL); @@ -1419,7 +1419,7 @@ static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info) if (IS_ERR(dp)) return PTR_ERR(dp); - reply = ovs_dp_cmd_build_info(dp, info->snd_pid, + reply = ovs_dp_cmd_build_info(dp, info->snd_portid, info->snd_seq, OVS_DP_CMD_NEW); if (IS_ERR(reply)) { err = PTR_ERR(reply); @@ -1428,7 +1428,7 @@ static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info) return 0; } - genl_notify(reply, genl_info_net(info), info->snd_pid, + genl_notify(reply, genl_info_net(info), info->snd_portid, ovs_dp_datapath_multicast_group.id, info->nlhdr, GFP_KERNEL); @@ -1444,7 +1444,7 @@ static int ovs_dp_cmd_get(struct sk_buff *skb, struct genl_info *info) if (IS_ERR(dp)) return PTR_ERR(dp); - reply = ovs_dp_cmd_build_info(dp, info->snd_pid, + reply = ovs_dp_cmd_build_info(dp, info->snd_portid, info->snd_seq, OVS_DP_CMD_NEW); if (IS_ERR(reply)) return PTR_ERR(reply); @@ -1461,7 +1461,7 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) list_for_each_entry(dp, &ovs_net->dps, list_node) { if (i >= skip && - ovs_dp_cmd_fill_info(dp, skb, NETLINK_CB(cb->skb).pid, + ovs_dp_cmd_fill_info(dp, skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, OVS_DP_CMD_NEW) < 0) break; @@ -1521,13 +1521,13 @@ struct genl_multicast_group ovs_dp_vport_multicast_group = { /* Called with RTNL lock or RCU read lock. */ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb, - u32 pid, u32 seq, u32 flags, u8 cmd) + u32 portid, u32 seq, u32 flags, u8 cmd) { struct ovs_header *ovs_header; struct ovs_vport_stats vport_stats; int err; - ovs_header = genlmsg_put(skb, pid, seq, &dp_vport_genl_family, + ovs_header = genlmsg_put(skb, portid, seq, &dp_vport_genl_family, flags, cmd); if (!ovs_header) return -EMSGSIZE; @@ -1537,7 +1537,7 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb, if (nla_put_u32(skb, OVS_VPORT_ATTR_PORT_NO, vport->port_no) || nla_put_u32(skb, OVS_VPORT_ATTR_TYPE, vport->ops->type) || nla_put_string(skb, OVS_VPORT_ATTR_NAME, vport->ops->get_name(vport)) || - nla_put_u32(skb, OVS_VPORT_ATTR_UPCALL_PID, vport->upcall_pid)) + nla_put_u32(skb, OVS_VPORT_ATTR_UPCALL_PID, vport->upcall_portid)) goto nla_put_failure; ovs_vport_get_stats(vport, &vport_stats); @@ -1559,7 +1559,7 @@ error: } /* Called with RTNL lock or RCU read lock. */ -struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, u32 pid, +struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, u32 portid, u32 seq, u8 cmd) { struct sk_buff *skb; @@ -1569,7 +1569,7 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, u32 pid, if (!skb) return ERR_PTR(-ENOMEM); - retval = ovs_vport_cmd_fill_info(vport, skb, pid, seq, 0, cmd); + retval = ovs_vport_cmd_fill_info(vport, skb, portid, seq, 0, cmd); if (retval < 0) { kfree_skb(skb); return ERR_PTR(retval); @@ -1661,21 +1661,21 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info) parms.options = a[OVS_VPORT_ATTR_OPTIONS]; parms.dp = dp; parms.port_no = port_no; - parms.upcall_pid = nla_get_u32(a[OVS_VPORT_ATTR_UPCALL_PID]); + parms.upcall_portid = nla_get_u32(a[OVS_VPORT_ATTR_UPCALL_PID]); vport = new_vport(&parms); err = PTR_ERR(vport); if (IS_ERR(vport)) goto exit_unlock; - reply = ovs_vport_cmd_build_info(vport, info->snd_pid, info->snd_seq, + reply = ovs_vport_cmd_build_info(vport, info->snd_portid, info->snd_seq, OVS_VPORT_CMD_NEW); if (IS_ERR(reply)) { err = PTR_ERR(reply); ovs_dp_detach_port(vport); goto exit_unlock; } - genl_notify(reply, genl_info_net(info), info->snd_pid, + genl_notify(reply, genl_info_net(info), info->snd_portid, ovs_dp_vport_multicast_group.id, info->nlhdr, GFP_KERNEL); exit_unlock: @@ -1707,9 +1707,9 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info) if (err) goto exit_unlock; if (a[OVS_VPORT_ATTR_UPCALL_PID]) - vport->upcall_pid = nla_get_u32(a[OVS_VPORT_ATTR_UPCALL_PID]); + vport->upcall_portid = nla_get_u32(a[OVS_VPORT_ATTR_UPCALL_PID]); - reply = ovs_vport_cmd_build_info(vport, info->snd_pid, info->snd_seq, + reply = ovs_vport_cmd_build_info(vport, info->snd_portid, info->snd_seq, OVS_VPORT_CMD_NEW); if (IS_ERR(reply)) { netlink_set_err(sock_net(skb->sk)->genl_sock, 0, @@ -1717,7 +1717,7 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info) goto exit_unlock; } - genl_notify(reply, genl_info_net(info), info->snd_pid, + genl_notify(reply, genl_info_net(info), info->snd_portid, ovs_dp_vport_multicast_group.id, info->nlhdr, GFP_KERNEL); exit_unlock: @@ -1743,7 +1743,7 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info) goto exit_unlock; } - reply = ovs_vport_cmd_build_info(vport, info->snd_pid, info->snd_seq, + reply = ovs_vport_cmd_build_info(vport, info->snd_portid, info->snd_seq, OVS_VPORT_CMD_DEL); err = PTR_ERR(reply); if (IS_ERR(reply)) @@ -1751,7 +1751,7 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info) ovs_dp_detach_port(vport); - genl_notify(reply, genl_info_net(info), info->snd_pid, + genl_notify(reply, genl_info_net(info), info->snd_portid, ovs_dp_vport_multicast_group.id, info->nlhdr, GFP_KERNEL); exit_unlock: @@ -1773,7 +1773,7 @@ static int ovs_vport_cmd_get(struct sk_buff *skb, struct genl_info *info) if (IS_ERR(vport)) goto exit_unlock; - reply = ovs_vport_cmd_build_info(vport, info->snd_pid, info->snd_seq, + reply = ovs_vport_cmd_build_info(vport, info->snd_portid, info->snd_seq, OVS_VPORT_CMD_NEW); err = PTR_ERR(reply); if (IS_ERR(reply)) @@ -1808,7 +1808,7 @@ static int ovs_vport_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) hlist_for_each_entry_rcu(vport, n, &dp->ports[i], dp_hash_node) { if (j >= skip && ovs_vport_cmd_fill_info(vport, skb, - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, OVS_VPORT_CMD_NEW) < 0) diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h index 129ec5480758..031dfbf37c93 100644 --- a/net/openvswitch/datapath.h +++ b/net/openvswitch/datapath.h @@ -129,7 +129,7 @@ struct dp_upcall_info { u8 cmd; const struct sw_flow_key *key; const struct nlattr *userdata; - u32 pid; + u32 portid; }; static inline struct net *ovs_dp_get_net(struct datapath *dp) diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index 1abd9609ba78..9055dd698c70 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -125,7 +125,7 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops, vport->dp = parms->dp; vport->port_no = parms->port_no; - vport->upcall_pid = parms->upcall_pid; + vport->upcall_portid = parms->upcall_portid; vport->ops = ops; INIT_HLIST_NODE(&vport->dp_hash_node); diff --git a/net/openvswitch/vport.h b/net/openvswitch/vport.h index c56e4836e93b..3f7961ea3c56 100644 --- a/net/openvswitch/vport.h +++ b/net/openvswitch/vport.h @@ -70,7 +70,7 @@ struct vport_err_stats { * @rcu: RCU callback head for deferred destruction. * @port_no: Index into @dp's @ports array. * @dp: Datapath to which this port belongs. - * @upcall_pid: The Netlink port to use for packets received on this port that + * @upcall_portid: The Netlink port to use for packets received on this port that * miss the flow table. * @hash_node: Element in @dev_table hash table in vport.c. * @dp_hash_node: Element in @datapath->ports hash table in datapath.c. @@ -83,7 +83,7 @@ struct vport { struct rcu_head rcu; u16 port_no; struct datapath *dp; - u32 upcall_pid; + u32 upcall_portid; struct hlist_node hash_node; struct hlist_node dp_hash_node; @@ -113,7 +113,7 @@ struct vport_parms { /* For ovs_vport_alloc(). */ struct datapath *dp; u16 port_no; - u32 upcall_pid; + u32 upcall_portid; }; /** diff --git a/net/packet/diag.c b/net/packet/diag.c index 39bce0d50df9..8db6e21c46bd 100644 --- a/net/packet/diag.c +++ b/net/packet/diag.c @@ -126,13 +126,13 @@ static int pdiag_put_fanout(struct packet_sock *po, struct sk_buff *nlskb) } static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct packet_diag_req *req, - u32 pid, u32 seq, u32 flags, int sk_ino) + u32 portid, u32 seq, u32 flags, int sk_ino) { struct nlmsghdr *nlh; struct packet_diag_msg *rp; struct packet_sock *po = pkt_sk(sk); - nlh = nlmsg_put(skb, pid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rp), flags); + nlh = nlmsg_put(skb, portid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rp), flags); if (!nlh) return -EMSGSIZE; @@ -184,7 +184,7 @@ static int packet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) if (num < s_num) goto next; - if (sk_diag_fill(sk, skb, req, NETLINK_CB(cb->skb).pid, + if (sk_diag_fill(sk, skb, req, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, sock_i_ino(sk)) < 0) goto done; diff --git a/net/phonet/pn_netlink.c b/net/phonet/pn_netlink.c index 7dd762a464e5..83a8389619aa 100644 --- a/net/phonet/pn_netlink.c +++ b/net/phonet/pn_netlink.c @@ -33,7 +33,7 @@ /* Device address handling */ static int fill_addr(struct sk_buff *skb, struct net_device *dev, u8 addr, - u32 pid, u32 seq, int event); + u32 portid, u32 seq, int event); void phonet_address_notify(int event, struct net_device *dev, u8 addr) { @@ -101,12 +101,12 @@ static int addr_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *attr) } static int fill_addr(struct sk_buff *skb, struct net_device *dev, u8 addr, - u32 pid, u32 seq, int event) + u32 portid, u32 seq, int event) { struct ifaddrmsg *ifm; struct nlmsghdr *nlh; - nlh = nlmsg_put(skb, pid, seq, event, sizeof(*ifm), 0); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), 0); if (nlh == NULL) return -EMSGSIZE; @@ -148,7 +148,7 @@ static int getaddr_dumpit(struct sk_buff *skb, struct netlink_callback *cb) continue; if (fill_addr(skb, pnd->netdev, addr << 2, - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, RTM_NEWADDR) < 0) goto out; } @@ -165,12 +165,12 @@ out: /* Routes handling */ static int fill_route(struct sk_buff *skb, struct net_device *dev, u8 dst, - u32 pid, u32 seq, int event) + u32 portid, u32 seq, int event) { struct rtmsg *rtm; struct nlmsghdr *nlh; - nlh = nlmsg_put(skb, pid, seq, event, sizeof(*rtm), 0); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*rtm), 0); if (nlh == NULL) return -EMSGSIZE; @@ -276,7 +276,7 @@ static int route_dumpit(struct sk_buff *skb, struct netlink_callback *cb) if (addr_idx++ < addr_start_idx) continue; - if (fill_route(skb, dev, addr << 2, NETLINK_CB(cb->skb).pid, + if (fill_route(skb, dev, addr << 2, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, RTM_NEWROUTE)) goto out; } diff --git a/net/sched/act_api.c b/net/sched/act_api.c index e3d2c78cb52c..102761d294cb 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -644,7 +644,7 @@ errout: } static int -tca_get_fill(struct sk_buff *skb, struct tc_action *a, u32 pid, u32 seq, +tca_get_fill(struct sk_buff *skb, struct tc_action *a, u32 portid, u32 seq, u16 flags, int event, int bind, int ref) { struct tcamsg *t; @@ -652,7 +652,7 @@ tca_get_fill(struct sk_buff *skb, struct tc_action *a, u32 pid, u32 seq, unsigned char *b = skb_tail_pointer(skb); struct nlattr *nest; - nlh = nlmsg_put(skb, pid, seq, event, sizeof(*t), flags); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*t), flags); if (!nlh) goto out_nlmsg_trim; t = nlmsg_data(nlh); @@ -678,7 +678,7 @@ out_nlmsg_trim: } static int -act_get_notify(struct net *net, u32 pid, struct nlmsghdr *n, +act_get_notify(struct net *net, u32 portid, struct nlmsghdr *n, struct tc_action *a, int event) { struct sk_buff *skb; @@ -686,16 +686,16 @@ act_get_notify(struct net *net, u32 pid, struct nlmsghdr *n, skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); if (!skb) return -ENOBUFS; - if (tca_get_fill(skb, a, pid, n->nlmsg_seq, 0, event, 0, 0) <= 0) { + if (tca_get_fill(skb, a, portid, n->nlmsg_seq, 0, event, 0, 0) <= 0) { kfree_skb(skb); return -EINVAL; } - return rtnl_unicast(skb, net, pid); + return rtnl_unicast(skb, net, portid); } static struct tc_action * -tcf_action_get_1(struct nlattr *nla, struct nlmsghdr *n, u32 pid) +tcf_action_get_1(struct nlattr *nla, struct nlmsghdr *n, u32 portid) { struct nlattr *tb[TCA_ACT_MAX + 1]; struct tc_action *a; @@ -762,7 +762,7 @@ static struct tc_action *create_a(int i) } static int tca_action_flush(struct net *net, struct nlattr *nla, - struct nlmsghdr *n, u32 pid) + struct nlmsghdr *n, u32 portid) { struct sk_buff *skb; unsigned char *b; @@ -799,7 +799,7 @@ static int tca_action_flush(struct net *net, struct nlattr *nla, if (a->ops == NULL) goto err_out; - nlh = nlmsg_put(skb, pid, n->nlmsg_seq, RTM_DELACTION, sizeof(*t), 0); + nlh = nlmsg_put(skb, portid, n->nlmsg_seq, RTM_DELACTION, sizeof(*t), 0); if (!nlh) goto out_module_put; t = nlmsg_data(nlh); @@ -823,7 +823,7 @@ static int tca_action_flush(struct net *net, struct nlattr *nla, nlh->nlmsg_flags |= NLM_F_ROOT; module_put(a->ops->owner); kfree(a); - err = rtnetlink_send(skb, net, pid, RTNLGRP_TC, + err = rtnetlink_send(skb, net, portid, RTNLGRP_TC, n->nlmsg_flags & NLM_F_ECHO); if (err > 0) return 0; @@ -841,7 +841,7 @@ noflush_out: static int tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n, - u32 pid, int event) + u32 portid, int event) { int i, ret; struct nlattr *tb[TCA_ACT_MAX_PRIO + 1]; @@ -853,13 +853,13 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n, if (event == RTM_DELACTION && n->nlmsg_flags & NLM_F_ROOT) { if (tb[1] != NULL) - return tca_action_flush(net, tb[1], n, pid); + return tca_action_flush(net, tb[1], n, portid); else return -EINVAL; } for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) { - act = tcf_action_get_1(tb[i], n, pid); + act = tcf_action_get_1(tb[i], n, portid); if (IS_ERR(act)) { ret = PTR_ERR(act); goto err; @@ -874,7 +874,7 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n, } if (event == RTM_GETACTION) - ret = act_get_notify(net, pid, n, head, event); + ret = act_get_notify(net, portid, n, head, event); else { /* delete */ struct sk_buff *skb; @@ -884,7 +884,7 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n, goto err; } - if (tca_get_fill(skb, head, pid, n->nlmsg_seq, 0, event, + if (tca_get_fill(skb, head, portid, n->nlmsg_seq, 0, event, 0, 1) <= 0) { kfree_skb(skb); ret = -EINVAL; @@ -893,7 +893,7 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n, /* now do the delete */ tcf_action_destroy(head, 0); - ret = rtnetlink_send(skb, net, pid, RTNLGRP_TC, + ret = rtnetlink_send(skb, net, portid, RTNLGRP_TC, n->nlmsg_flags & NLM_F_ECHO); if (ret > 0) return 0; @@ -905,7 +905,7 @@ err: } static int tcf_add_notify(struct net *net, struct tc_action *a, - u32 pid, u32 seq, int event, u16 flags) + u32 portid, u32 seq, int event, u16 flags) { struct tcamsg *t; struct nlmsghdr *nlh; @@ -920,7 +920,7 @@ static int tcf_add_notify(struct net *net, struct tc_action *a, b = skb_tail_pointer(skb); - nlh = nlmsg_put(skb, pid, seq, event, sizeof(*t), flags); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*t), flags); if (!nlh) goto out_kfree_skb; t = nlmsg_data(nlh); @@ -940,7 +940,7 @@ static int tcf_add_notify(struct net *net, struct tc_action *a, nlh->nlmsg_len = skb_tail_pointer(skb) - b; NETLINK_CB(skb).dst_group = RTNLGRP_TC; - err = rtnetlink_send(skb, net, pid, RTNLGRP_TC, flags & NLM_F_ECHO); + err = rtnetlink_send(skb, net, portid, RTNLGRP_TC, flags & NLM_F_ECHO); if (err > 0) err = 0; return err; @@ -953,7 +953,7 @@ out_kfree_skb: static int tcf_action_add(struct net *net, struct nlattr *nla, struct nlmsghdr *n, - u32 pid, int ovr) + u32 portid, int ovr) { int ret = 0; struct tc_action *act; @@ -971,7 +971,7 @@ tcf_action_add(struct net *net, struct nlattr *nla, struct nlmsghdr *n, /* dump then free all the actions after update; inserted policy * stays intact */ - ret = tcf_add_notify(net, act, pid, seq, RTM_NEWACTION, n->nlmsg_flags); + ret = tcf_add_notify(net, act, portid, seq, RTM_NEWACTION, n->nlmsg_flags); for (a = act; a; a = act) { act = a->next; kfree(a); @@ -984,7 +984,7 @@ static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n, void *arg) { struct net *net = sock_net(skb->sk); struct nlattr *tca[TCA_ACT_MAX + 1]; - u32 pid = skb ? NETLINK_CB(skb).pid : 0; + u32 portid = skb ? NETLINK_CB(skb).portid : 0; int ret = 0, ovr = 0; ret = nlmsg_parse(n, sizeof(struct tcamsg), tca, TCA_ACT_MAX, NULL); @@ -1008,17 +1008,17 @@ static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n, void *arg) if (n->nlmsg_flags & NLM_F_REPLACE) ovr = 1; replay: - ret = tcf_action_add(net, tca[TCA_ACT_TAB], n, pid, ovr); + ret = tcf_action_add(net, tca[TCA_ACT_TAB], n, portid, ovr); if (ret == -EAGAIN) goto replay; break; case RTM_DELACTION: ret = tca_action_gd(net, tca[TCA_ACT_TAB], n, - pid, RTM_DELACTION); + portid, RTM_DELACTION); break; case RTM_GETACTION: ret = tca_action_gd(net, tca[TCA_ACT_TAB], n, - pid, RTM_GETACTION); + portid, RTM_GETACTION); break; default: BUG(); @@ -1085,7 +1085,7 @@ tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb) goto out_module_put; } - nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, + nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, cb->nlh->nlmsg_type, sizeof(*t), 0); if (!nlh) goto out_module_put; @@ -1109,7 +1109,7 @@ tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb) nla_nest_cancel(skb, nest); nlh->nlmsg_len = skb_tail_pointer(skb) - b; - if (NETLINK_CB(cb->skb).pid && ret) + if (NETLINK_CB(cb->skb).portid && ret) nlh->nlmsg_flags |= NLM_F_MULTI; module_put(a_o->owner); return skb->len; diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index dc3ef5aef355..7ae02892437c 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -343,13 +343,13 @@ errout: } static int tcf_fill_node(struct sk_buff *skb, struct tcf_proto *tp, - unsigned long fh, u32 pid, u32 seq, u16 flags, int event) + unsigned long fh, u32 portid, u32 seq, u16 flags, int event) { struct tcmsg *tcm; struct nlmsghdr *nlh; unsigned char *b = skb_tail_pointer(skb); - nlh = nlmsg_put(skb, pid, seq, event, sizeof(*tcm), flags); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags); if (!nlh) goto out_nlmsg_trim; tcm = nlmsg_data(nlh); @@ -381,18 +381,18 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb, unsigned long fh, int event) { struct sk_buff *skb; - u32 pid = oskb ? NETLINK_CB(oskb).pid : 0; + u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); if (!skb) return -ENOBUFS; - if (tcf_fill_node(skb, tp, fh, pid, n->nlmsg_seq, 0, event) <= 0) { + if (tcf_fill_node(skb, tp, fh, portid, n->nlmsg_seq, 0, event) <= 0) { kfree_skb(skb); return -EINVAL; } - return rtnetlink_send(skb, net, pid, RTNLGRP_TC, + return rtnetlink_send(skb, net, portid, RTNLGRP_TC, n->nlmsg_flags & NLM_F_ECHO); } @@ -407,7 +407,7 @@ static int tcf_node_dump(struct tcf_proto *tp, unsigned long n, { struct tcf_dump_args *a = (void *)arg; - return tcf_fill_node(a->skb, tp, n, NETLINK_CB(a->cb->skb).pid, + return tcf_fill_node(a->skb, tp, n, NETLINK_CB(a->cb->skb).portid, a->cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWTFILTER); } @@ -465,7 +465,7 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) if (t > s_t) memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0])); if (cb->args[1] == 0) { - if (tcf_fill_node(skb, tp, 0, NETLINK_CB(cb->skb).pid, + if (tcf_fill_node(skb, tp, 0, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWTFILTER) <= 0) break; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index a08b4ab3e421..a18d975db59c 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1185,7 +1185,7 @@ graft: } static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid, - u32 pid, u32 seq, u16 flags, int event) + u32 portid, u32 seq, u16 flags, int event) { struct tcmsg *tcm; struct nlmsghdr *nlh; @@ -1193,7 +1193,7 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid, struct gnet_dump d; struct qdisc_size_table *stab; - nlh = nlmsg_put(skb, pid, seq, event, sizeof(*tcm), flags); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags); if (!nlh) goto out_nlmsg_trim; tcm = nlmsg_data(nlh); @@ -1248,25 +1248,25 @@ static int qdisc_notify(struct net *net, struct sk_buff *oskb, struct Qdisc *old, struct Qdisc *new) { struct sk_buff *skb; - u32 pid = oskb ? NETLINK_CB(oskb).pid : 0; + u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); if (!skb) return -ENOBUFS; if (old && !tc_qdisc_dump_ignore(old)) { - if (tc_fill_qdisc(skb, old, clid, pid, n->nlmsg_seq, + if (tc_fill_qdisc(skb, old, clid, portid, n->nlmsg_seq, 0, RTM_DELQDISC) < 0) goto err_out; } if (new && !tc_qdisc_dump_ignore(new)) { - if (tc_fill_qdisc(skb, new, clid, pid, n->nlmsg_seq, + if (tc_fill_qdisc(skb, new, clid, portid, n->nlmsg_seq, old ? NLM_F_REPLACE : 0, RTM_NEWQDISC) < 0) goto err_out; } if (skb->len) - return rtnetlink_send(skb, net, pid, RTNLGRP_TC, + return rtnetlink_send(skb, net, portid, RTNLGRP_TC, n->nlmsg_flags & NLM_F_ECHO); err_out: @@ -1289,7 +1289,7 @@ static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb, q_idx++; } else { if (!tc_qdisc_dump_ignore(q) && - tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).pid, + tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0) goto done; q_idx++; @@ -1300,7 +1300,7 @@ static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb, continue; } if (!tc_qdisc_dump_ignore(q) && - tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).pid, + tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0) goto done; q_idx++; @@ -1375,7 +1375,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) const struct Qdisc_class_ops *cops; unsigned long cl = 0; unsigned long new_cl; - u32 pid = tcm->tcm_parent; + u32 portid = tcm->tcm_parent; u32 clid = tcm->tcm_handle; u32 qid = TC_H_MAJ(clid); int err; @@ -1403,8 +1403,8 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) /* Step 1. Determine qdisc handle X:0 */ - if (pid != TC_H_ROOT) { - u32 qid1 = TC_H_MAJ(pid); + if (portid != TC_H_ROOT) { + u32 qid1 = TC_H_MAJ(portid); if (qid && qid1) { /* If both majors are known, they must be identical. */ @@ -1418,10 +1418,10 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) /* Now qid is genuine qdisc handle consistent * both with parent and child. * - * TC_H_MAJ(pid) still may be unspecified, complete it now. + * TC_H_MAJ(portid) still may be unspecified, complete it now. */ - if (pid) - pid = TC_H_MAKE(qid, pid); + if (portid) + portid = TC_H_MAKE(qid, portid); } else { if (qid == 0) qid = dev->qdisc->handle; @@ -1439,7 +1439,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) /* Now try to get class */ if (clid == 0) { - if (pid == TC_H_ROOT) + if (portid == TC_H_ROOT) clid = qid; } else clid = TC_H_MAKE(qid, clid); @@ -1478,7 +1478,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) new_cl = cl; err = -EOPNOTSUPP; if (cops->change) - err = cops->change(q, clid, pid, tca, &new_cl); + err = cops->change(q, clid, portid, tca, &new_cl); if (err == 0) tclass_notify(net, skb, n, q, new_cl, RTM_NEWTCLASS); @@ -1492,7 +1492,7 @@ out: static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q, unsigned long cl, - u32 pid, u32 seq, u16 flags, int event) + u32 portid, u32 seq, u16 flags, int event) { struct tcmsg *tcm; struct nlmsghdr *nlh; @@ -1500,7 +1500,7 @@ static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q, struct gnet_dump d; const struct Qdisc_class_ops *cl_ops = q->ops->cl_ops; - nlh = nlmsg_put(skb, pid, seq, event, sizeof(*tcm), flags); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags); if (!nlh) goto out_nlmsg_trim; tcm = nlmsg_data(nlh); @@ -1540,18 +1540,18 @@ static int tclass_notify(struct net *net, struct sk_buff *oskb, unsigned long cl, int event) { struct sk_buff *skb; - u32 pid = oskb ? NETLINK_CB(oskb).pid : 0; + u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); if (!skb) return -ENOBUFS; - if (tc_fill_tclass(skb, q, cl, pid, n->nlmsg_seq, 0, event) < 0) { + if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, event) < 0) { kfree_skb(skb); return -EINVAL; } - return rtnetlink_send(skb, net, pid, RTNLGRP_TC, + return rtnetlink_send(skb, net, portid, RTNLGRP_TC, n->nlmsg_flags & NLM_F_ECHO); } @@ -1565,7 +1565,7 @@ static int qdisc_class_dump(struct Qdisc *q, unsigned long cl, struct qdisc_walk { struct qdisc_dump_args *a = (struct qdisc_dump_args *)arg; - return tc_fill_tclass(a->skb, q, cl, NETLINK_CB(a->cb->skb).pid, + return tc_fill_tclass(a->skb, q, cl, NETLINK_CB(a->cb->skb).portid, a->cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWTCLASS); } diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index 47a839df27dc..6675914dc592 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c @@ -62,7 +62,7 @@ static int handle_cmd(struct sk_buff *skb, struct genl_info *info) rep_nlh = nlmsg_hdr(rep_buf); memcpy(rep_nlh, req_nlh, hdr_space); rep_nlh->nlmsg_len = rep_buf->len; - genlmsg_unicast(&init_net, rep_buf, NETLINK_CB(skb).pid); + genlmsg_unicast(&init_net, rep_buf, NETLINK_CB(skb).portid); } return 0; diff --git a/net/unix/diag.c b/net/unix/diag.c index 750b13408449..06748f108a57 100644 --- a/net/unix/diag.c +++ b/net/unix/diag.c @@ -110,12 +110,12 @@ static int sk_diag_show_rqlen(struct sock *sk, struct sk_buff *nlskb) } static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_req *req, - u32 pid, u32 seq, u32 flags, int sk_ino) + u32 portid, u32 seq, u32 flags, int sk_ino) { struct nlmsghdr *nlh; struct unix_diag_msg *rep; - nlh = nlmsg_put(skb, pid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rep), + nlh = nlmsg_put(skb, portid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rep), flags); if (!nlh) return -EMSGSIZE; @@ -159,7 +159,7 @@ out_nlmsg_trim: } static int sk_diag_dump(struct sock *sk, struct sk_buff *skb, struct unix_diag_req *req, - u32 pid, u32 seq, u32 flags) + u32 portid, u32 seq, u32 flags) { int sk_ino; @@ -170,7 +170,7 @@ static int sk_diag_dump(struct sock *sk, struct sk_buff *skb, struct unix_diag_r if (!sk_ino) return 0; - return sk_diag_fill(sk, skb, req, pid, seq, flags, sk_ino); + return sk_diag_fill(sk, skb, req, portid, seq, flags, sk_ino); } static int unix_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) @@ -200,7 +200,7 @@ static int unix_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) if (!(req->udiag_states & (1 << sk->sk_state))) goto next; if (sk_diag_dump(sk, skb, req, - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI) < 0) goto done; @@ -267,7 +267,7 @@ again: if (!rep) goto out; - err = sk_diag_fill(sk, rep, req, NETLINK_CB(in_skb).pid, + err = sk_diag_fill(sk, rep, req, NETLINK_CB(in_skb).portid, nlh->nlmsg_seq, 0, req->udiag_ino); if (err < 0) { nlmsg_free(rep); @@ -277,7 +277,7 @@ again: goto again; } - err = netlink_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).pid, + err = netlink_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).portid, MSG_DONTWAIT); if (err > 0) err = 0; diff --git a/net/wireless/core.h b/net/wireless/core.h index bc7430b54771..a343be4a52bd 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -55,7 +55,7 @@ struct cfg80211_registered_device { int opencount; /* also protected by devlist_mtx */ wait_queue_head_t dev_wait; - u32 ap_beacons_nlpid; + u32 ap_beacons_nlportid; /* protected by RTNL only */ int num_running_ifaces; diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 8fd0242ee169..ec7fcee5bad6 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -615,7 +615,7 @@ EXPORT_SYMBOL(cfg80211_del_sta); struct cfg80211_mgmt_registration { struct list_head list; - u32 nlpid; + u32 nlportid; int match_len; @@ -624,7 +624,7 @@ struct cfg80211_mgmt_registration { u8 match[]; }; -int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid, +int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid, u16 frame_type, const u8 *match_data, int match_len) { @@ -672,7 +672,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid, memcpy(nreg->match, match_data, match_len); nreg->match_len = match_len; - nreg->nlpid = snd_pid; + nreg->nlportid = snd_portid; nreg->frame_type = cpu_to_le16(frame_type); list_add(&nreg->list, &wdev->mgmt_registrations); @@ -685,7 +685,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid, return err; } -void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid) +void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid) { struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); @@ -694,7 +694,7 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid) spin_lock_bh(&wdev->mgmt_registrations_lock); list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { - if (reg->nlpid != nlpid) + if (reg->nlportid != nlportid) continue; if (rdev->ops->mgmt_frame_register) { @@ -710,8 +710,8 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid) spin_unlock_bh(&wdev->mgmt_registrations_lock); - if (nlpid == wdev->ap_unexpected_nlpid) - wdev->ap_unexpected_nlpid = 0; + if (nlportid == wdev->ap_unexpected_nlportid) + wdev->ap_unexpected_nlportid = 0; } void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev) @@ -872,7 +872,7 @@ bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm, /* found match! */ /* Indicate the received Action frame to user space */ - if (nl80211_send_mgmt(rdev, wdev, reg->nlpid, + if (nl80211_send_mgmt(rdev, wdev, reg->nlportid, freq, sig_mbm, buf, len, gfp)) continue; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 787aeaa902fe..34eb5c07a567 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -496,11 +496,11 @@ static bool is_valid_ie_attr(const struct nlattr *attr) } /* message building helper */ -static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq, +static inline void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq, int flags, u8 cmd) { /* since there is no private header just add the generic one */ - return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd); + return genlmsg_put(skb, portid, seq, &nl80211_fam, flags, cmd); } static int nl80211_msg_put_channel(struct sk_buff *msg, @@ -851,7 +851,7 @@ nla_put_failure: return -ENOBUFS; } -static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, +static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flags, struct cfg80211_registered_device *dev) { void *hdr; @@ -866,7 +866,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, const struct ieee80211_txrx_stypes *mgmt_stypes = dev->wiphy.mgmt_stypes; - hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY); + hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY); if (!hdr) return -1; @@ -1267,7 +1267,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) continue; if (++idx <= start) continue; - if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid, + if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, dev) < 0) { idx--; @@ -1290,7 +1290,7 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info) if (!msg) return -ENOMEM; - if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0) { + if (nl80211_send_wiphy(msg, info->snd_portid, info->snd_seq, 0, dev) < 0) { nlmsg_free(msg); return -ENOBUFS; } @@ -1736,14 +1736,14 @@ static inline u64 wdev_id(struct wireless_dev *wdev) ((u64)wiphy_to_dev(wdev->wiphy)->wiphy_idx << 32); } -static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, +static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { struct net_device *dev = wdev->netdev; void *hdr; - hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE); + hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_INTERFACE); if (!hdr) return -1; @@ -1807,7 +1807,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback * if_idx++; continue; } - if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid, + if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, rdev, wdev) < 0) { mutex_unlock(&rdev->devlist_mtx); @@ -1838,7 +1838,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info) if (!msg) return -ENOMEM; - if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0, + if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0, dev, wdev) < 0) { nlmsg_free(msg); return -ENOBUFS; @@ -2056,7 +2056,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) break; } - if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0, + if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0, rdev, wdev) < 0) { nlmsg_free(msg); return -ENOBUFS; @@ -2191,7 +2191,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) if (!msg) return -ENOMEM; - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, + hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, NL80211_CMD_NEW_KEY); if (IS_ERR(hdr)) return PTR_ERR(hdr); @@ -2769,7 +2769,7 @@ nla_put_failure: return false; } -static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, +static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq, int flags, struct cfg80211_registered_device *rdev, struct net_device *dev, @@ -2778,7 +2778,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, void *hdr; struct nlattr *sinfoattr, *bss_param; - hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); + hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_STATION); if (!hdr) return -1; @@ -2931,7 +2931,7 @@ static int nl80211_dump_station(struct sk_buff *skb, goto out_err; if (nl80211_send_station(skb, - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, dev, netdev, mac_addr, &sinfo) < 0) @@ -2977,7 +2977,7 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) if (!msg) return -ENOMEM; - if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0, + if (nl80211_send_station(msg, info->snd_portid, info->snd_seq, 0, rdev, dev, mac_addr, &sinfo) < 0) { nlmsg_free(msg); return -ENOBUFS; @@ -3303,7 +3303,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) return rdev->ops->del_station(&rdev->wiphy, dev, mac_addr); } -static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq, +static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq, int flags, struct net_device *dev, u8 *dst, u8 *next_hop, struct mpath_info *pinfo) @@ -3311,7 +3311,7 @@ static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq, void *hdr; struct nlattr *pinfoattr; - hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); + hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_STATION); if (!hdr) return -1; @@ -3389,7 +3389,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb, if (err) goto out_err; - if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid, + if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, netdev, dst, next_hop, &pinfo) < 0) @@ -3438,7 +3438,7 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) if (!msg) return -ENOMEM; - if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0, + if (nl80211_send_mpath(msg, info->snd_portid, info->snd_seq, 0, dev, dst, next_hop, &pinfo) < 0) { nlmsg_free(msg); return -ENOBUFS; @@ -3679,7 +3679,7 @@ static int nl80211_get_mesh_config(struct sk_buff *skb, msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, + hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, NL80211_CMD_GET_MESH_CONFIG); if (!hdr) goto out; @@ -3998,7 +3998,7 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) goto out; } - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, + hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, NL80211_CMD_GET_REG); if (!hdr) goto put_failure; @@ -4616,7 +4616,7 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, ASSERT_WDEV_LOCK(wdev); - hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).pid, seq, flags, + hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).portid, seq, flags, NL80211_CMD_NEW_SCAN_RESULTS); if (!hdr) return -1; @@ -4735,14 +4735,14 @@ static int nl80211_dump_scan(struct sk_buff *skb, return skb->len; } -static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq, +static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq, int flags, struct net_device *dev, struct survey_info *survey) { void *hdr; struct nlattr *infoattr; - hdr = nl80211hdr_put(msg, pid, seq, flags, + hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_SURVEY_RESULTS); if (!hdr) return -ENOMEM; @@ -4836,7 +4836,7 @@ static int nl80211_dump_survey(struct sk_buff *skb, } if (nl80211_send_survey(skb, - NETLINK_CB(cb->skb).pid, + NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, netdev, &survey) < 0) @@ -5451,7 +5451,7 @@ static int nl80211_testmode_dump(struct sk_buff *skb, } while (1) { - void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).pid, + void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, NL80211_CMD_TESTMODE); struct nlattr *tmdata; @@ -5491,7 +5491,7 @@ static int nl80211_testmode_dump(struct sk_buff *skb, static struct sk_buff * __cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev, - int approxlen, u32 pid, u32 seq, gfp_t gfp) + int approxlen, u32 portid, u32 seq, gfp_t gfp) { struct sk_buff *skb; void *hdr; @@ -5501,7 +5501,7 @@ __cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev, if (!skb) return NULL; - hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE); + hdr = nl80211hdr_put(skb, portid, seq, 0, NL80211_CMD_TESTMODE); if (!hdr) { kfree_skb(skb); return NULL; @@ -5531,7 +5531,7 @@ struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, return NULL; return __cfg80211_testmode_alloc_skb(rdev, approxlen, - rdev->testmode_info->snd_pid, + rdev->testmode_info->snd_portid, rdev->testmode_info->snd_seq, GFP_KERNEL); } @@ -5867,7 +5867,7 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, if (!msg) return -ENOMEM; - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, + hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, NL80211_CMD_REMAIN_ON_CHANNEL); if (IS_ERR(hdr)) { @@ -6086,7 +6086,7 @@ static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info) if (!rdev->ops->mgmt_tx) return -EOPNOTSUPP; - return cfg80211_mlme_register_mgmt(wdev, info->snd_pid, frame_type, + return cfg80211_mlme_register_mgmt(wdev, info->snd_portid, frame_type, nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]), nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH])); } @@ -6167,7 +6167,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) if (!msg) return -ENOMEM; - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, + hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, NL80211_CMD_FRAME); if (IS_ERR(hdr)) { @@ -6284,7 +6284,7 @@ static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info) if (!msg) return -ENOMEM; - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, + hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, NL80211_CMD_GET_POWER_SAVE); if (!hdr) { err = -ENOBUFS; @@ -6486,7 +6486,7 @@ static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info) if (!msg) return -ENOMEM; - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, + hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, NL80211_CMD_GET_WOWLAN); if (!hdr) goto nla_put_failure; @@ -6760,10 +6760,10 @@ static int nl80211_register_unexpected_frame(struct sk_buff *skb, wdev->iftype != NL80211_IFTYPE_P2P_GO) return -EINVAL; - if (wdev->ap_unexpected_nlpid) + if (wdev->ap_unexpected_nlportid) return -EBUSY; - wdev->ap_unexpected_nlpid = info->snd_pid; + wdev->ap_unexpected_nlportid = info->snd_portid; return 0; } @@ -6793,7 +6793,7 @@ static int nl80211_probe_client(struct sk_buff *skb, if (!msg) return -ENOMEM; - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, + hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, NL80211_CMD_PROBE_CLIENT); if (IS_ERR(hdr)) { @@ -6828,10 +6828,10 @@ static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info) if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS)) return -EOPNOTSUPP; - if (rdev->ap_beacons_nlpid) + if (rdev->ap_beacons_nlportid) return -EBUSY; - rdev->ap_beacons_nlpid = info->snd_pid; + rdev->ap_beacons_nlportid = info->snd_portid; return 0; } @@ -7628,12 +7628,12 @@ static int nl80211_add_scan_req(struct sk_buff *msg, static int nl80211_send_scan_msg(struct sk_buff *msg, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, - u32 pid, u32 seq, int flags, + u32 portid, u32 seq, int flags, u32 cmd) { void *hdr; - hdr = nl80211hdr_put(msg, pid, seq, flags, cmd); + hdr = nl80211hdr_put(msg, portid, seq, flags, cmd); if (!hdr) return -1; @@ -7657,11 +7657,11 @@ static int nl80211_send_sched_scan_msg(struct sk_buff *msg, struct cfg80211_registered_device *rdev, struct net_device *netdev, - u32 pid, u32 seq, int flags, u32 cmd) + u32 portid, u32 seq, int flags, u32 cmd) { void *hdr; - hdr = nl80211hdr_put(msg, pid, seq, flags, cmd); + hdr = nl80211hdr_put(msg, portid, seq, flags, cmd); if (!hdr) return -1; @@ -8370,9 +8370,9 @@ static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd, struct sk_buff *msg; void *hdr; int err; - u32 nlpid = ACCESS_ONCE(wdev->ap_unexpected_nlpid); + u32 nlportid = ACCESS_ONCE(wdev->ap_unexpected_nlportid); - if (!nlpid) + if (!nlportid) return false; msg = nlmsg_new(100, gfp); @@ -8396,7 +8396,7 @@ static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd, return true; } - genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid); + genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid); return true; nla_put_failure: @@ -8420,7 +8420,7 @@ bool nl80211_unexpected_4addr_frame(struct net_device *dev, } int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, u32 nlpid, + struct wireless_dev *wdev, u32 nlportid, int freq, int sig_dbm, const u8 *buf, size_t len, gfp_t gfp) { @@ -8449,7 +8449,7 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, genlmsg_end(msg, hdr); - return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid); + return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid); nla_put_failure: genlmsg_cancel(msg, hdr); @@ -8804,9 +8804,9 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy, struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct sk_buff *msg; void *hdr; - u32 nlpid = ACCESS_ONCE(rdev->ap_beacons_nlpid); + u32 nlportid = ACCESS_ONCE(rdev->ap_beacons_nlportid); - if (!nlpid) + if (!nlportid) return; msg = nlmsg_new(len + 100, gfp); @@ -8829,7 +8829,7 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy, genlmsg_end(msg, hdr); - genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid); + genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid); return; nla_put_failure: @@ -8853,9 +8853,9 @@ static int nl80211_netlink_notify(struct notifier_block * nb, list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) - cfg80211_mlme_unregister_socket(wdev, notify->pid); - if (rdev->ap_beacons_nlpid == notify->pid) - rdev->ap_beacons_nlpid = 0; + cfg80211_mlme_unregister_socket(wdev, notify->portid); + if (rdev->ap_beacons_nlportid == notify->portid) + rdev->ap_beacons_nlportid = 0; } rcu_read_unlock(); diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 7856c33898fa..30edad44e7fc 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -166,7 +166,7 @@ static DEFINE_SPINLOCK(xfrm_state_gc_lock); int __xfrm_state_delete(struct xfrm_state *x); int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol); -void km_state_expired(struct xfrm_state *x, int hard, u32 pid); +void km_state_expired(struct xfrm_state *x, int hard, u32 portid); static struct xfrm_state_afinfo *xfrm_state_lock_afinfo(unsigned int family) { @@ -1674,13 +1674,13 @@ void km_state_notify(struct xfrm_state *x, const struct km_event *c) EXPORT_SYMBOL(km_policy_notify); EXPORT_SYMBOL(km_state_notify); -void km_state_expired(struct xfrm_state *x, int hard, u32 pid) +void km_state_expired(struct xfrm_state *x, int hard, u32 portid) { struct net *net = xs_net(x); struct km_event c; c.data.hard = hard; - c.pid = pid; + c.portid = portid; c.event = XFRM_MSG_EXPIRE; km_state_notify(x, &c); @@ -1726,13 +1726,13 @@ int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport) } EXPORT_SYMBOL(km_new_mapping); -void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid) +void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 portid) { struct net *net = xp_net(pol); struct km_event c; c.data.hard = hard; - c.pid = pid; + c.portid = portid; c.event = XFRM_MSG_POLEXPIRE; km_policy_notify(pol, dir, &c); diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 354070adb5ef..b313d932d678 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -603,7 +603,7 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, } c.seq = nlh->nlmsg_seq; - c.pid = nlh->nlmsg_pid; + c.portid = nlh->nlmsg_pid; c.event = nlh->nlmsg_type; km_state_notify(x, &c); @@ -676,7 +676,7 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, goto out; c.seq = nlh->nlmsg_seq; - c.pid = nlh->nlmsg_pid; + c.portid = nlh->nlmsg_pid; c.event = nlh->nlmsg_type; km_state_notify(x, &c); @@ -826,7 +826,7 @@ static int dump_one_state(struct xfrm_state *x, int count, void *ptr) struct nlmsghdr *nlh; int err; - nlh = nlmsg_put(skb, NETLINK_CB(in_skb).pid, sp->nlmsg_seq, + nlh = nlmsg_put(skb, NETLINK_CB(in_skb).portid, sp->nlmsg_seq, XFRM_MSG_NEWSA, sizeof(*p), sp->nlmsg_flags); if (nlh == NULL) return -EMSGSIZE; @@ -904,7 +904,7 @@ static inline size_t xfrm_spdinfo_msgsize(void) } static int build_spdinfo(struct sk_buff *skb, struct net *net, - u32 pid, u32 seq, u32 flags) + u32 portid, u32 seq, u32 flags) { struct xfrmk_spdinfo si; struct xfrmu_spdinfo spc; @@ -913,7 +913,7 @@ static int build_spdinfo(struct sk_buff *skb, struct net *net, int err; u32 *f; - nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0); + nlh = nlmsg_put(skb, portid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0); if (nlh == NULL) /* shouldn't really happen ... */ return -EMSGSIZE; @@ -946,17 +946,17 @@ static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, struct net *net = sock_net(skb->sk); struct sk_buff *r_skb; u32 *flags = nlmsg_data(nlh); - u32 spid = NETLINK_CB(skb).pid; + u32 sportid = NETLINK_CB(skb).portid; u32 seq = nlh->nlmsg_seq; r_skb = nlmsg_new(xfrm_spdinfo_msgsize(), GFP_ATOMIC); if (r_skb == NULL) return -ENOMEM; - if (build_spdinfo(r_skb, net, spid, seq, *flags) < 0) + if (build_spdinfo(r_skb, net, sportid, seq, *flags) < 0) BUG(); - return nlmsg_unicast(net->xfrm.nlsk, r_skb, spid); + return nlmsg_unicast(net->xfrm.nlsk, r_skb, sportid); } static inline size_t xfrm_sadinfo_msgsize(void) @@ -967,7 +967,7 @@ static inline size_t xfrm_sadinfo_msgsize(void) } static int build_sadinfo(struct sk_buff *skb, struct net *net, - u32 pid, u32 seq, u32 flags) + u32 portid, u32 seq, u32 flags) { struct xfrmk_sadinfo si; struct xfrmu_sadhinfo sh; @@ -975,7 +975,7 @@ static int build_sadinfo(struct sk_buff *skb, struct net *net, int err; u32 *f; - nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSADINFO, sizeof(u32), 0); + nlh = nlmsg_put(skb, portid, seq, XFRM_MSG_NEWSADINFO, sizeof(u32), 0); if (nlh == NULL) /* shouldn't really happen ... */ return -EMSGSIZE; @@ -1003,17 +1003,17 @@ static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh, struct net *net = sock_net(skb->sk); struct sk_buff *r_skb; u32 *flags = nlmsg_data(nlh); - u32 spid = NETLINK_CB(skb).pid; + u32 sportid = NETLINK_CB(skb).portid; u32 seq = nlh->nlmsg_seq; r_skb = nlmsg_new(xfrm_sadinfo_msgsize(), GFP_ATOMIC); if (r_skb == NULL) return -ENOMEM; - if (build_sadinfo(r_skb, net, spid, seq, *flags) < 0) + if (build_sadinfo(r_skb, net, sportid, seq, *flags) < 0) BUG(); - return nlmsg_unicast(net->xfrm.nlsk, r_skb, spid); + return nlmsg_unicast(net->xfrm.nlsk, r_skb, sportid); } static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh, @@ -1033,7 +1033,7 @@ static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh, if (IS_ERR(resp_skb)) { err = PTR_ERR(resp_skb); } else { - err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).pid); + err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).portid); } xfrm_state_put(x); out_noput: @@ -1114,7 +1114,7 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, goto out; } - err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).pid); + err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).portid); out: xfrm_state_put(x); @@ -1401,7 +1401,7 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, c.event = nlh->nlmsg_type; c.seq = nlh->nlmsg_seq; - c.pid = nlh->nlmsg_pid; + c.portid = nlh->nlmsg_pid; km_policy_notify(xp, p->dir, &c); xfrm_pol_put(xp); @@ -1486,7 +1486,7 @@ static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr struct nlmsghdr *nlh; int err; - nlh = nlmsg_put(skb, NETLINK_CB(in_skb).pid, sp->nlmsg_seq, + nlh = nlmsg_put(skb, NETLINK_CB(in_skb).portid, sp->nlmsg_seq, XFRM_MSG_NEWPOLICY, sizeof(*p), sp->nlmsg_flags); if (nlh == NULL) return -EMSGSIZE; @@ -1621,7 +1621,7 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh, err = PTR_ERR(resp_skb); } else { err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, - NETLINK_CB(skb).pid); + NETLINK_CB(skb).portid); } } else { uid_t loginuid = audit_get_loginuid(current); @@ -1638,7 +1638,7 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh, c.data.byid = p->index; c.event = nlh->nlmsg_type; c.seq = nlh->nlmsg_seq; - c.pid = nlh->nlmsg_pid; + c.portid = nlh->nlmsg_pid; km_policy_notify(xp, p->dir, &c); } @@ -1668,7 +1668,7 @@ static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh, c.data.proto = p->proto; c.event = nlh->nlmsg_type; c.seq = nlh->nlmsg_seq; - c.pid = nlh->nlmsg_pid; + c.portid = nlh->nlmsg_pid; c.net = net; km_state_notify(NULL, &c); @@ -1695,7 +1695,7 @@ static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct struct nlmsghdr *nlh; int err; - nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_NEWAE, sizeof(*id), 0); + nlh = nlmsg_put(skb, c->portid, c->seq, XFRM_MSG_NEWAE, sizeof(*id), 0); if (nlh == NULL) return -EMSGSIZE; @@ -1777,11 +1777,11 @@ static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh, spin_lock_bh(&x->lock); c.data.aevent = p->flags; c.seq = nlh->nlmsg_seq; - c.pid = nlh->nlmsg_pid; + c.portid = nlh->nlmsg_pid; if (build_aevent(r_skb, x, &c) < 0) BUG(); - err = nlmsg_unicast(net->xfrm.nlsk, r_skb, NETLINK_CB(skb).pid); + err = nlmsg_unicast(net->xfrm.nlsk, r_skb, NETLINK_CB(skb).portid); spin_unlock_bh(&x->lock); xfrm_state_put(x); return err; @@ -1827,7 +1827,7 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh, c.event = nlh->nlmsg_type; c.seq = nlh->nlmsg_seq; - c.pid = nlh->nlmsg_pid; + c.portid = nlh->nlmsg_pid; c.data.aevent = XFRM_AE_CU; km_state_notify(x, &c); err = 0; @@ -1862,7 +1862,7 @@ static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh, c.data.type = type; c.event = nlh->nlmsg_type; c.seq = nlh->nlmsg_seq; - c.pid = nlh->nlmsg_pid; + c.portid = nlh->nlmsg_pid; c.net = net; km_policy_notify(NULL, 0, &c); return 0; @@ -2370,7 +2370,7 @@ static int build_expire(struct sk_buff *skb, struct xfrm_state *x, const struct struct nlmsghdr *nlh; int err; - nlh = nlmsg_put(skb, c->pid, 0, XFRM_MSG_EXPIRE, sizeof(*ue), 0); + nlh = nlmsg_put(skb, c->portid, 0, XFRM_MSG_EXPIRE, sizeof(*ue), 0); if (nlh == NULL) return -EMSGSIZE; @@ -2429,7 +2429,7 @@ static int xfrm_notify_sa_flush(const struct km_event *c) if (skb == NULL) return -ENOMEM; - nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_FLUSHSA, sizeof(*p), 0); + nlh = nlmsg_put(skb, c->portid, c->seq, XFRM_MSG_FLUSHSA, sizeof(*p), 0); if (nlh == NULL) { kfree_skb(skb); return -EMSGSIZE; @@ -2497,7 +2497,7 @@ static int xfrm_notify_sa(struct xfrm_state *x, const struct km_event *c) if (skb == NULL) return -ENOMEM; - nlh = nlmsg_put(skb, c->pid, c->seq, c->event, headlen, 0); + nlh = nlmsg_put(skb, c->portid, c->seq, c->event, headlen, 0); err = -EMSGSIZE; if (nlh == NULL) goto out_free_skb; @@ -2696,7 +2696,7 @@ static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp, struct nlmsghdr *nlh; int err; - nlh = nlmsg_put(skb, c->pid, 0, XFRM_MSG_POLEXPIRE, sizeof(*upe), 0); + nlh = nlmsg_put(skb, c->portid, 0, XFRM_MSG_POLEXPIRE, sizeof(*upe), 0); if (nlh == NULL) return -EMSGSIZE; @@ -2756,7 +2756,7 @@ static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_e if (skb == NULL) return -ENOMEM; - nlh = nlmsg_put(skb, c->pid, c->seq, c->event, headlen, 0); + nlh = nlmsg_put(skb, c->portid, c->seq, c->event, headlen, 0); err = -EMSGSIZE; if (nlh == NULL) goto out_free_skb; @@ -2810,7 +2810,7 @@ static int xfrm_notify_policy_flush(const struct km_event *c) if (skb == NULL) return -ENOMEM; - nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_FLUSHPOLICY, 0, 0); + nlh = nlmsg_put(skb, c->portid, c->seq, XFRM_MSG_FLUSHPOLICY, 0, 0); err = -EMSGSIZE; if (nlh == NULL) goto out_free_skb; -- cgit v1.2.3 From b6069a95706ca5738be3f5d90fd286cbd13ac695 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 7 Sep 2012 22:03:35 +0000 Subject: filter: add MOD operation Add a new ALU opcode, to compute a modulus. Commit ffe06c17afbbb used an ancillary to implement XOR_X, but here we reserve one of the available ALU opcode to implement both MOD_X and MOD_K Signed-off-by: Eric Dumazet Suggested-by: George Bakos Cc: Jay Schulist Cc: Jiri Pirko Cc: Andi Kleen Signed-off-by: David S. Miller --- include/linux/filter.h | 4 ++++ net/core/filter.c | 15 +++++++++++++++ 2 files changed, 19 insertions(+) (limited to 'include') diff --git a/include/linux/filter.h b/include/linux/filter.h index 82b01357af8b..3cf5fd561d86 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -74,6 +74,8 @@ struct sock_fprog { /* Required for SO_ATTACH_FILTER. */ #define BPF_LSH 0x60 #define BPF_RSH 0x70 #define BPF_NEG 0x80 +#define BPF_MOD 0x90 + #define BPF_JA 0x00 #define BPF_JEQ 0x10 #define BPF_JGT 0x20 @@ -196,6 +198,8 @@ enum { BPF_S_ALU_MUL_K, BPF_S_ALU_MUL_X, BPF_S_ALU_DIV_X, + BPF_S_ALU_MOD_K, + BPF_S_ALU_MOD_X, BPF_S_ALU_AND_K, BPF_S_ALU_AND_X, BPF_S_ALU_OR_K, diff --git a/net/core/filter.c b/net/core/filter.c index 907efd27ec77..fbe3a8d12570 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -167,6 +167,14 @@ unsigned int sk_run_filter(const struct sk_buff *skb, case BPF_S_ALU_DIV_K: A = reciprocal_divide(A, K); continue; + case BPF_S_ALU_MOD_X: + if (X == 0) + return 0; + A %= X; + continue; + case BPF_S_ALU_MOD_K: + A %= K; + continue; case BPF_S_ALU_AND_X: A &= X; continue; @@ -469,6 +477,8 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen) [BPF_ALU|BPF_MUL|BPF_K] = BPF_S_ALU_MUL_K, [BPF_ALU|BPF_MUL|BPF_X] = BPF_S_ALU_MUL_X, [BPF_ALU|BPF_DIV|BPF_X] = BPF_S_ALU_DIV_X, + [BPF_ALU|BPF_MOD|BPF_K] = BPF_S_ALU_MOD_K, + [BPF_ALU|BPF_MOD|BPF_X] = BPF_S_ALU_MOD_X, [BPF_ALU|BPF_AND|BPF_K] = BPF_S_ALU_AND_K, [BPF_ALU|BPF_AND|BPF_X] = BPF_S_ALU_AND_X, [BPF_ALU|BPF_OR|BPF_K] = BPF_S_ALU_OR_K, @@ -531,6 +541,11 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen) return -EINVAL; ftest->k = reciprocal_value(ftest->k); break; + case BPF_S_ALU_MOD_K: + /* check for division by zero */ + if (ftest->k == 0) + return -EINVAL; + break; case BPF_S_LD_MEM: case BPF_S_LDX_MEM: case BPF_S_ST: -- cgit v1.2.3 From 6d57e9078e880a3dd232d579f42ac437a8f1ef7b Mon Sep 17 00:00:00 2001 From: Duan Jiong Date: Sat, 8 Sep 2012 16:32:28 +0000 Subject: etherdevice: introduce help function eth_zero_addr() a lot of code has either the memset or an inefficient copy from a static array that contains the all-zeros Ethernet address. Introduce help function eth_zero_addr() to fill an address with all zeros, making the code clearer and allowing us to get rid of some constant arrays. Signed-off-by: Duan Jiong Signed-off-by: David S. Miller --- include/linux/etherdevice.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include') diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h index d426336d92d9..b006ba0a9f42 100644 --- a/include/linux/etherdevice.h +++ b/include/linux/etherdevice.h @@ -150,6 +150,17 @@ static inline void eth_broadcast_addr(u8 *addr) memset(addr, 0xff, ETH_ALEN); } +/** + * eth_zero_addr - Assign zero address + * @addr: Pointer to a six-byte array containing the Ethernet address + * + * Assign the zero address to the given address array. + */ +static inline void eth_zero_addr(u8 *addr) +{ + memset(addr, 0x00, ETH_ALEN); +} + /** * eth_hw_addr_random - Generate software assigned random Ethernet and * set device flag -- cgit v1.2.3 From ff823c79a5c33194c2e5594f7c4686ea3547910c Mon Sep 17 00:00:00 2001 From: Lan Tianyu Date: Wed, 5 Sep 2012 13:44:32 +0800 Subject: usb: move children to struct usb_port The usb_device structure contains an array of usb_device "children". This array is only valid if the usb_device is a hub, so it makes no sense to store it there. Instead, store the usb_device child in its parent usb_port structure. Since usb_port is an internal USB core structure, add a new function to get the USB device child, usb_hub_find_child(). Add a new macro, usb_hub_get_each_child(), to iterate over all the children attached to a particular USB hub. Remove the printing the USB children array pointer from the usb-ip driver, since it's really not necessary. Acked-by: Alan Stern Signed-off-by: Lan Tianyu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/staging/usbip/usbip_common.c | 3 +- drivers/usb/core/devices.c | 7 ++-- drivers/usb/core/hub.c | 73 ++++++++++++++++++++++++------------ drivers/usb/host/r8a66597-hcd.c | 5 +-- include/linux/usb.h | 15 +++++++- 5 files changed, 68 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/drivers/staging/usbip/usbip_common.c b/drivers/staging/usbip/usbip_common.c index 70f230269329..95beb76497d6 100644 --- a/drivers/staging/usbip/usbip_common.c +++ b/drivers/staging/usbip/usbip_common.c @@ -157,8 +157,7 @@ static void usbip_dump_usb_device(struct usb_device *udev) dev_dbg(dev, "have_langid %d, string_langid %d\n", udev->have_langid, udev->string_langid); - dev_dbg(dev, "maxchild %d, children %p\n", - udev->maxchild, udev->children); + dev_dbg(dev, "maxchild %d\n", udev->maxchild); } static void usbip_dump_request_type(__u8 rt) diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index d95696584762..f4ead1296820 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -496,6 +496,7 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, char *pages_start, *data_end, *speed; unsigned int length; ssize_t total_written = 0; + struct usb_device *childdev = NULL; /* don't bother with anything else if we're not writing any data */ if (*nbytes <= 0) @@ -589,14 +590,12 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, free_pages((unsigned long)pages_start, 1); /* Now look at all of this device's children. */ - for (chix = 0; chix < usbdev->maxchild; chix++) { - struct usb_device *childdev = usbdev->children[chix]; - + usb_hub_for_each_child(usbdev, chix, childdev) { if (childdev) { usb_lock_device(childdev); ret = usb_device_dump(buffer, nbytes, skip_bytes, file_offset, childdev, bus, - level + 1, chix, ++cnt); + level + 1, chix - 1, ++cnt); usb_unlock_device(childdev); if (ret == -EFAULT) return total_written; diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 87df22eef491..cdbade148995 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -40,6 +40,7 @@ #endif struct usb_port { + struct usb_device *child; struct device dev; struct dev_state *port_owner; }; @@ -181,7 +182,7 @@ static inline char *portspeed(struct usb_hub *hub, int portstatus) /* Note that hdev or one of its children must be locked! */ static struct usb_hub *hdev_to_hub(struct usb_device *hdev) { - if (!hdev || !hdev->actconfig) + if (!hdev || !hdev->actconfig || !hdev->maxchild) return NULL; return usb_get_intfdata(hdev->actconfig->interface[0]); } @@ -876,8 +877,8 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) struct usb_device *hdev = hub->hdev; int ret = 0; - if (hdev->children[port1-1] && set_state) - usb_set_device_state(hdev->children[port1-1], + if (hub->ports[port1 - 1]->child && set_state) + usb_set_device_state(hub->ports[port1 - 1]->child, USB_STATE_NOTATTACHED); if (!hub->error && !hub_is_superspeed(hub->hdev)) ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); @@ -1033,7 +1034,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) * which ports need attention. */ for (port1 = 1; port1 <= hdev->maxchild; ++port1) { - struct usb_device *udev = hdev->children[port1-1]; + struct usb_device *udev = hub->ports[port1 - 1]->child; u16 portstatus, portchange; portstatus = portchange = 0; @@ -1198,8 +1199,8 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type) if (type != HUB_SUSPEND) { /* Disconnect all the children */ for (i = 0; i < hdev->maxchild; ++i) { - if (hdev->children[i]) - usb_disconnect(&hdev->children[i]); + if (hub->ports[i]->child) + usb_disconnect(&hub->ports[i]->child); } } @@ -1324,11 +1325,9 @@ static int hub_configure(struct usb_hub *hub, dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild, (hdev->maxchild == 1) ? "" : "s"); - hdev->children = kzalloc(hdev->maxchild * - sizeof(struct usb_device *), GFP_KERNEL); hub->ports = kzalloc(hdev->maxchild * sizeof(struct usb_port *), GFP_KERNEL); - if (!hdev->children || !hub->ports) { + if (!hub->ports) { ret = -ENOMEM; goto fail; } @@ -1591,7 +1590,6 @@ static void hub_disconnect(struct usb_interface *intf) highspeed_hubs--; usb_free_urb(hub->urb); - kfree(hdev->children); kfree(hub->ports); kfree(hub->descriptor); kfree(hub->status); @@ -1679,6 +1677,7 @@ static int hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data) { struct usb_device *hdev = interface_to_usbdev (intf); + struct usb_hub *hub = hdev_to_hub(hdev); /* assert ifno == 0 (part of hub spec) */ switch (code) { @@ -1692,11 +1691,11 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data) else { info->nports = hdev->maxchild; for (i = 0; i < info->nports; i++) { - if (hdev->children[i] == NULL) + if (hub->ports[i]->child == NULL) info->port[i] = 0; else info->port[i] = - hdev->children[i]->devnum; + hub->ports[i]->child->devnum; } } spin_unlock_irq(&device_state_lock); @@ -1784,11 +1783,12 @@ bool usb_device_is_owned(struct usb_device *udev) static void recursively_mark_NOTATTACHED(struct usb_device *udev) { + struct usb_hub *hub = hdev_to_hub(udev); int i; for (i = 0; i < udev->maxchild; ++i) { - if (udev->children[i]) - recursively_mark_NOTATTACHED(udev->children[i]); + if (hub->ports[i]->child) + recursively_mark_NOTATTACHED(hub->ports[i]->child); } if (udev->state == USB_STATE_SUSPENDED) udev->active_duration -= jiffies; @@ -1952,6 +1952,7 @@ static void hub_free_dev(struct usb_device *udev) void usb_disconnect(struct usb_device **pdev) { struct usb_device *udev = *pdev; + struct usb_hub *hub = hdev_to_hub(udev); int i; /* mark the device as inactive, so any further urb submissions for @@ -1966,8 +1967,8 @@ void usb_disconnect(struct usb_device **pdev) /* Free up all the children before we remove this device */ for (i = 0; i < udev->maxchild; i++) { - if (udev->children[i]) - usb_disconnect(&udev->children[i]); + if (hub->ports[i]->child) + usb_disconnect(&hub->ports[i]->child); } /* deallocate hcd/hardware state ... nuking all pending urbs and @@ -3131,7 +3132,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) for (port1 = 1; port1 <= hdev->maxchild; port1++) { struct usb_device *udev; - udev = hdev->children [port1-1]; + udev = hub->ports[port1 - 1]->child; if (udev && udev->can_submit) { dev_warn(&intf->dev, "port %d nyet suspended\n", port1); if (PMSG_IS_AUTO(msg)) @@ -4058,7 +4059,7 @@ hub_power_remaining (struct usb_hub *hub) remaining = hdev->bus_mA - hub->descriptor->bHubContrCurrent; for (port1 = 1; port1 <= hdev->maxchild; ++port1) { - struct usb_device *udev = hdev->children[port1 - 1]; + struct usb_device *udev = hub->ports[port1 - 1]->child; int delta; if (!udev) @@ -4122,7 +4123,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, #endif /* Try to resuscitate an existing device */ - udev = hdev->children[port1-1]; + udev = hub->ports[port1 - 1]->child; if ((portstatus & USB_PORT_STAT_CONNECTION) && udev && udev->state != USB_STATE_NOTATTACHED) { usb_lock_device(udev); @@ -4151,7 +4152,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, /* Disconnect any existing devices under this port */ if (udev) - usb_disconnect(&hdev->children[port1-1]); + usb_disconnect(&hub->ports[port1 - 1]->child); clear_bit(port1, hub->change_bits); /* We can forget about a "removed" device when there's a physical @@ -4287,7 +4288,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, if (hdev->state == USB_STATE_NOTATTACHED) status = -ENOTCONN; else - hdev->children[port1-1] = udev; + hub->ports[port1 - 1]->child = udev; spin_unlock_irq(&device_state_lock); /* Run it through the hoops (find a driver, etc) */ @@ -4295,7 +4296,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, status = usb_new_device(udev); if (status) { spin_lock_irq(&device_state_lock); - hdev->children[port1-1] = NULL; + hub->ports[port1 - 1]->child = NULL; spin_unlock_irq(&device_state_lock); } } @@ -4341,7 +4342,7 @@ static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port, int ret; hdev = hub->hdev; - udev = hdev->children[port-1]; + udev = hub->ports[port - 1]->child; if (!hub_is_superspeed(hdev)) { if (!(portchange & USB_PORT_STAT_C_SUSPEND)) return 0; @@ -4495,7 +4496,7 @@ static void hub_events(void) */ if (!(portstatus & USB_PORT_STAT_ENABLE) && !connect_change - && hdev->children[i-1]) { + && hub->ports[i - 1]->child) { dev_err (hub_dev, "port %i " "disabled by hub (EMI?), " @@ -5052,3 +5053,27 @@ void usb_queue_reset_device(struct usb_interface *iface) schedule_work(&iface->reset_ws); } EXPORT_SYMBOL_GPL(usb_queue_reset_device); + +/** + * usb_hub_find_child - Get the pointer of child device + * attached to the port which is specified by @port1. + * @hdev: USB device belonging to the usb hub + * @port1: port num to indicate which port the child device + * is attached to. + * + * USB drivers call this function to get hub's child device + * pointer. + * + * 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, + int port1) +{ + struct usb_hub *hub = hdev_to_hub(hdev); + + if (port1 < 1 || port1 > hdev->maxchild) + return NULL; + return hub->ports[port1 - 1]->child; +} +EXPORT_SYMBOL_GPL(usb_hub_find_child); diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 4c634eb56358..fcc09e5ec0ad 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -2029,15 +2029,14 @@ static int r8a66597_get_frame(struct usb_hcd *hcd) static void collect_usb_address_map(struct usb_device *udev, unsigned long *map) { int chix; + struct usb_device *childdev; if (udev->state == USB_STATE_CONFIGURED && udev->parent && udev->parent->devnum > 1 && udev->parent->descriptor.bDeviceClass == USB_CLASS_HUB) map[udev->devnum/32] |= (1 << (udev->devnum % 32)); - for (chix = 0; chix < udev->maxchild; chix++) { - struct usb_device *childdev = udev->children[chix]; - + usb_hub_for_each_child(udev, chix, childdev) { if (childdev) collect_usb_address_map(childdev, map); } diff --git a/include/linux/usb.h b/include/linux/usb.h index 30d1ae38eab1..ff8ef2d28589 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -469,7 +469,6 @@ struct usb3_lpm_parameters { * access from userspace * @usbfs_dentry: usbfs dentry entry for the device * @maxchild: number of ports if hub - * @children: child devices - USB devices that are attached to this hub * @quirks: quirks of the whole device * @urbnum: number of URBs submitted for the whole device * @active_duration: total time device is not suspended @@ -543,7 +542,6 @@ struct usb_device { struct list_head filelist; int maxchild; - struct usb_device **children; u32 quirks; atomic_t urbnum; @@ -572,6 +570,19 @@ static inline struct usb_device *interface_to_usbdev(struct usb_interface *intf) extern struct usb_device *usb_get_dev(struct usb_device *dev); extern void usb_put_dev(struct usb_device *dev); +extern struct usb_device *usb_hub_find_child(struct usb_device *hdev, + int port1); + +/** + * usb_hub_for_each_child - iterate over all child devices on the hub + * @hdev: USB device belonging to the usb hub + * @port1: portnum associated with child device + * @child: child device pointer + */ +#define usb_hub_for_each_child(hdev, port1, child) \ + for (port1 = 1, child = usb_hub_find_child(hdev, port1); \ + port1 <= hdev->maxchild; \ + child = usb_hub_find_child(hdev, ++port1)) /* USB device locking */ #define usb_lock_device(udev) device_lock(&(udev)->dev) -- cgit v1.2.3 From 05f916894a692f0cc0973aef21521133623b21c0 Mon Sep 17 00:00:00 2001 From: Lan Tianyu Date: Wed, 5 Sep 2012 13:44:34 +0800 Subject: usb/acpi: Store info on device removability. In the upcoming USB port power off patches, we need to know whether a USB port can ever see a disconnect event. Often USB ports are internal to a system, and users can't disconnect USB devices from that port. Sometimes those ports will remain empty, because the OEM chose not to connect an internal USB device to that port. According to ACPI Spec 9.13, PLD indicates whether USB port is user visible and _UPC indicates whether a USB device can be connected to the USB port (we'll call this "connectible"). Here's a matrix of the possible combinations: Visible Connectible Name Example ------------------------------------------------------------------------- Yes No Unknown (Invalid state.) Yes Yes Hot-plug USB ports on the outside of a laptop. A user could freely connect and disconnect USB devices. No Yes Hard-wired A USB modem hard-wired to a port on the inside of a laptop. No No Not used The port is internal to the system and will remain empty. Represent each of these four states with an enum usb_port_connect_type. The four states are USB_PORT_CONNECT_TYPE_UNKNOWN, USB_PORT_CONNECT_TYPE_HOT_PLUG, USB_PORT_CONNECT_TYPE_HARD_WIRED, and USB_PORT_NOT_USED. When we get the USB port's acpi_handle, store the state in connect_type in struct usb_port. Signed-off-by: Lan Tianyu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 31 ++++++++++++++++++ drivers/usb/core/usb-acpi.c | 80 +++++++++++++++++++++++++-------------------- drivers/usb/core/usb.h | 4 +++ include/linux/usb.h | 7 ++++ 4 files changed, 87 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 3341b55f495c..4ffe8371adac 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -43,6 +43,7 @@ struct usb_port { struct usb_device *child; struct device dev; struct dev_state *port_owner; + enum usb_port_connect_type connect_type; }; struct usb_hub { @@ -5078,6 +5079,36 @@ struct usb_device *usb_hub_find_child(struct usb_device *hdev, } EXPORT_SYMBOL_GPL(usb_hub_find_child); +/** + * usb_set_hub_port_connect_type - set hub port connect type. + * @hdev: USB device belonging to the usb hub + * @port1: port num of the port + * @type: connect type of the port + */ +void usb_set_hub_port_connect_type(struct usb_device *hdev, int port1, + enum usb_port_connect_type type) +{ + struct usb_hub *hub = hdev_to_hub(hdev); + + hub->ports[port1 - 1]->connect_type = type; +} + +/** + * usb_get_hub_port_connect_type - Get the port's connect type + * @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. + */ +enum usb_port_connect_type +usb_get_hub_port_connect_type(struct usb_device *hdev, int port1) +{ + struct usb_hub *hub = hdev_to_hub(hdev); + + return hub->ports[port1 - 1]->connect_type; +} + #ifdef CONFIG_ACPI /** * usb_get_hub_port_acpi_handle - Get the usb port's acpi handle diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c index 47197bf0b28d..404d86afb243 100644 --- a/drivers/usb/core/usb-acpi.c +++ b/drivers/usb/core/usb-acpi.c @@ -19,20 +19,29 @@ #include "usb.h" -static int usb_acpi_check_upc(struct usb_device *udev, acpi_handle handle) +static int usb_acpi_check_port_connect_type(struct usb_device *hdev, + acpi_handle handle, int port1) { acpi_status status; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *upc; + struct acpi_pld pld; int ret = 0; - status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer); - + /* + * Accoding to ACPI Spec 9.13. PLD indicates whether usb port is + * user visible and _UPC indicates whether it is connectable. If + * the port was visible and connectable, it could be freely connected + * and disconnected with USB devices. If no visible and connectable, + * a usb device is directly hard-wired to the port. If no visible and + * no connectable, the port would be not used. + */ + status = acpi_get_physical_device_location(handle, &pld); if (ACPI_FAILURE(status)) return -ENODEV; + status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer); upc = buffer.pointer; - if (!upc || (upc->type != ACPI_TYPE_PACKAGE) || upc->package.count != 4) { ret = -EINVAL; @@ -40,33 +49,20 @@ static int usb_acpi_check_upc(struct usb_device *udev, acpi_handle handle) } if (upc->package.elements[0].integer.value) - udev->removable = USB_DEVICE_REMOVABLE; - else - udev->removable = USB_DEVICE_FIXED; + if (pld.user_visible) + usb_set_hub_port_connect_type(hdev, port1, + USB_PORT_CONNECT_TYPE_HOT_PLUG); + else + usb_set_hub_port_connect_type(hdev, port1, + USB_PORT_CONNECT_TYPE_HARD_WIRED); + else if (!pld.user_visible) + usb_set_hub_port_connect_type(hdev, port1, USB_PORT_NOT_USED); out: kfree(upc); return ret; } -static int usb_acpi_check_pld(struct usb_device *udev, acpi_handle handle) -{ - acpi_status status; - struct acpi_pld pld; - - status = acpi_get_physical_device_location(handle, &pld); - - if (ACPI_FAILURE(status)) - return -ENODEV; - - if (pld.user_visible) - udev->removable = USB_DEVICE_REMOVABLE; - else - udev->removable = USB_DEVICE_FIXED; - - return 0; -} - static int usb_acpi_find_device(struct device *dev, acpi_handle *handle) { struct usb_device *udev; @@ -88,8 +84,30 @@ static int usb_acpi_find_device(struct device *dev, acpi_handle *handle) */ if (is_usb_device(dev)) { udev = to_usb_device(dev); - if (udev->parent) + if (udev->parent) { + enum usb_port_connect_type type; + + /* + * According usb port's connect type to set usb device's + * removability. + */ + type = usb_get_hub_port_connect_type(udev->parent, + udev->portnum); + switch (type) { + case USB_PORT_CONNECT_TYPE_HOT_PLUG: + udev->removable = USB_DEVICE_REMOVABLE; + break; + case USB_PORT_CONNECT_TYPE_HARD_WIRED: + udev->removable = USB_DEVICE_FIXED; + break; + default: + udev->removable = USB_DEVICE_REMOVABLE_UNKNOWN; + break; + } + return -ENODEV; + } + /* root hub's parent is the usb hcd. */ parent_handle = DEVICE_ACPI_HANDLE(dev->parent); *handle = acpi_get_child(parent_handle, udev->portnum); @@ -122,18 +140,10 @@ static int usb_acpi_find_device(struct device *dev, acpi_handle *handle) if (!*handle) return -ENODEV; } + usb_acpi_check_port_connect_type(udev, *handle, port_num); } else return -ENODEV; - /* - * PLD will tell us whether a port is removable to the user or - * not. If we don't get an answer from PLD (it's not present - * or it's malformed) then try to infer it from UPC. If a - * device isn't connectable then it's probably not removable. - */ - if (usb_acpi_check_pld(udev, *handle) != 0) - usb_acpi_check_upc(udev, *handle); - return 0; } diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 1633f6e99e35..1c528c1bf0be 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -169,6 +169,10 @@ extern void usb_notify_add_device(struct usb_device *udev); extern void usb_notify_remove_device(struct usb_device *udev); extern void usb_notify_add_bus(struct usb_bus *ubus); extern void usb_notify_remove_bus(struct usb_bus *ubus); +extern enum usb_port_connect_type + usb_get_hub_port_connect_type(struct usb_device *hdev, int port1); +extern void usb_set_hub_port_connect_type(struct usb_device *hdev, int port1, + enum usb_port_connect_type type); #ifdef CONFIG_ACPI extern int usb_acpi_register(void); diff --git a/include/linux/usb.h b/include/linux/usb.h index ff8ef2d28589..e0084a1fa37f 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -384,6 +384,13 @@ enum usb_device_removable { USB_DEVICE_FIXED, }; +enum usb_port_connect_type { + USB_PORT_CONNECT_TYPE_UNKNOWN = 0, + USB_PORT_CONNECT_TYPE_HOT_PLUG, + USB_PORT_CONNECT_TYPE_HARD_WIRED, + USB_PORT_NOT_USED, +}; + /* * USB 3.0 Link Power Management (LPM) parameters. * -- cgit v1.2.3 From f7ac7787ad361e31a7972e2854ed8dc2eedfac3b Mon Sep 17 00:00:00 2001 From: Lan Tianyu Date: Wed, 5 Sep 2012 13:44:36 +0800 Subject: usb/acpi: Use ACPI methods to power off ports. Upcoming Intel systems will have an ACPI method to control whether a USB port can be completely powered off. The implication of powering off a USB port is that the device and host sees a physical disconnect, and subsequent port connections and remote wakeups will be lost. Add a new function, usb_acpi_power_manageable(), that can be used to find whether the usb port has ACPI power resources that can be used to power on and off the port on these machines. Also add a new function called usb_acpi_set_power_state() that controls the port power via these ACPI methods. When the USB core calls into the xHCI hub driver to power off a port, check whether the port can be completely powered off via this new ACPI mechanism. If so, call into these new ACPI methods. Also use the ACPI methods when the USB core asks to power on a port. Signed-off-by: Lan Tianyu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/usb-acpi.c | 62 +++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/xhci-hub.c | 12 +++++++++ include/linux/usb.h | 10 ++++++++ 3 files changed, 84 insertions(+) (limited to 'include') diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c index 404d86afb243..0ef7d42d8abe 100644 --- a/drivers/usb/core/usb-acpi.c +++ b/drivers/usb/core/usb-acpi.c @@ -19,6 +19,68 @@ #include "usb.h" +/** + * usb_acpi_power_manageable - check whether usb port has + * acpi power resource. + * @hdev: USB device belonging to the usb hub + * @index: port index based zero + * + * Return true if the port has acpi power resource and false if no. + */ +bool usb_acpi_power_manageable(struct usb_device *hdev, int index) +{ + acpi_handle port_handle; + int port1 = index + 1; + + port_handle = usb_get_hub_port_acpi_handle(hdev, + port1); + if (port_handle) + return acpi_bus_power_manageable(port_handle); + else + return false; +} +EXPORT_SYMBOL_GPL(usb_acpi_power_manageable); + +/** + * usb_acpi_set_power_state - control usb port's power via acpi power + * resource + * @hdev: USB device belonging to the usb hub + * @index: port index based zero + * @enable: power state expected to be set + * + * Notice to use usb_acpi_power_manageable() to check whether the usb port + * has acpi power resource before invoking this function. + * + * Returns 0 on success, else negative errno. + */ +int usb_acpi_set_power_state(struct usb_device *hdev, int index, bool enable) +{ + acpi_handle port_handle; + unsigned char state; + int port1 = index + 1; + int error = -EINVAL; + + port_handle = (acpi_handle)usb_get_hub_port_acpi_handle(hdev, + port1); + if (!port_handle) + return error; + + if (enable) + state = ACPI_STATE_D0; + else + state = ACPI_STATE_D3_COLD; + + error = acpi_bus_set_power(port_handle, state); + if (!error) + dev_dbg(&hdev->dev, "The power of hub port %d was set to %d\n", + port1, enable); + else + dev_dbg(&hdev->dev, "The power of hub port failed to be set\n"); + + return error; +} +EXPORT_SYMBOL_GPL(usb_acpi_set_power_state); + static int usb_acpi_check_port_connect_type(struct usb_device *hdev, acpi_handle handle, int port1) { diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 03032b3e6ed2..630e9e6e06b5 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -766,6 +766,12 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp = xhci_readl(xhci, port_array[wIndex]); xhci_dbg(xhci, "set port power, actual port %d status = 0x%x\n", wIndex, temp); + + temp = usb_acpi_power_manageable(hcd->self.root_hub, + wIndex); + if (temp) + usb_acpi_set_power_state(hcd->self.root_hub, + wIndex, true); break; case USB_PORT_FEAT_RESET: temp = (temp | PORT_RESET); @@ -868,6 +874,12 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case USB_PORT_FEAT_POWER: xhci_writel(xhci, temp & ~PORT_POWER, port_array[wIndex]); + + temp = usb_acpi_power_manageable(hcd->self.root_hub, + wIndex); + if (temp) + usb_acpi_set_power_state(hcd->self.root_hub, + wIndex, false); break; default: goto error; diff --git a/include/linux/usb.h b/include/linux/usb.h index e0084a1fa37f..07915a32fb9d 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -602,6 +602,16 @@ extern int usb_lock_device_for_reset(struct usb_device *udev, extern int usb_reset_device(struct usb_device *dev); extern void usb_queue_reset_device(struct usb_interface *dev); +#ifdef CONFIG_ACPI +extern int usb_acpi_set_power_state(struct usb_device *hdev, int index, + bool enable); +extern bool usb_acpi_power_manageable(struct usb_device *hdev, int index); +#else +static inline int usb_acpi_set_power_state(struct usb_device *hdev, int index, + bool enable) { return 0; } +static inline bool usb_acpi_power_manageable(struct usb_device *hdev, int index) + { return true; } +#endif /* USB autosuspend and autoresume */ #ifdef CONFIG_USB_SUSPEND -- cgit v1.2.3 From 43b5f0d69291374f602ad8e1817f329573a59010 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 7 Sep 2012 10:02:48 +0200 Subject: serial: pl011: delete reset callback Since commit 4fd0690bb0c3955983560bb2767ee82e2b197f9b "serial: pl011: implement workaround for CTS clear event issue" the PL011 UART is no longer at risk to hang up, so get rid of the callback altogether. Cc: Rajanikanth H.V Signed-off-by: Linus Walleij Acked-by: Lee Jones Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-ux500/board-mop500.c | 21 --------------------- include/linux/amba/serial.h | 1 - 2 files changed, 22 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index 8674a890fd1c..f216c302beb6 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -524,33 +524,12 @@ static struct stedma40_chan_cfg uart2_dma_cfg_tx = { }; #endif -#define PRCC_K_SOFTRST_SET 0x18 -#define PRCC_K_SOFTRST_CLEAR 0x1C -static void ux500_uart0_reset(void) -{ - void __iomem *prcc_rst_set, *prcc_rst_clr; - - prcc_rst_set = (void __iomem *)IO_ADDRESS(U8500_CLKRST1_BASE + - PRCC_K_SOFTRST_SET); - prcc_rst_clr = (void __iomem *)IO_ADDRESS(U8500_CLKRST1_BASE + - PRCC_K_SOFTRST_CLEAR); - - /* Activate soft reset PRCC_K_SOFTRST_CLEAR */ - writel((readl(prcc_rst_clr) | 0x1), prcc_rst_clr); - udelay(1); - - /* Release soft reset PRCC_K_SOFTRST_SET */ - writel((readl(prcc_rst_set) | 0x1), prcc_rst_set); - udelay(1); -} - static struct amba_pl011_data uart0_plat = { #ifdef CONFIG_STE_DMA40 .dma_filter = stedma40_filter, .dma_rx_param = &uart0_dma_cfg_rx, .dma_tx_param = &uart0_dma_cfg_tx, #endif - .reset = ux500_uart0_reset, }; static struct amba_pl011_data uart1_plat = { diff --git a/include/linux/amba/serial.h b/include/linux/amba/serial.h index d117b29d1062..f612c783170f 100644 --- a/include/linux/amba/serial.h +++ b/include/linux/amba/serial.h @@ -205,7 +205,6 @@ struct amba_pl011_data { void *dma_tx_param; void (*init) (void); void (*exit) (void); - void (*reset) (void); }; #endif -- cgit v1.2.3 From cc01272986862b49024b6663559bb89df00f9f1a Mon Sep 17 00:00:00 2001 From: Igor Grinberg Date: Wed, 29 Aug 2012 02:18:54 +0300 Subject: ARM: OMAP1: move omap1_bl pdata out of arch/arm/* omap1 backlight platform data resides inside plat/board.h while it should be inside include/linux/... Move the omap1 backlight platform data to include/linux/platform_data/. Cc: Richard Purdie Cc: Florian Tobias Schandinat Cc: linux-fbdev@vger.kernel.org Signed-off-by: Igor Grinberg Acked-by: Tomi Valkeinen Signed-off-by: Tony Lindgren --- arch/arm/mach-omap1/board-osk.c | 1 + arch/arm/mach-omap1/board-palmte.c | 1 + arch/arm/mach-omap1/board-palmtt.c | 1 + arch/arm/mach-omap1/board-palmz71.c | 1 + arch/arm/plat-omap/include/plat/board.h | 7 ------- drivers/video/backlight/omap1_bl.c | 2 +- include/linux/platform_data/omap1_bl.h | 11 +++++++++++ 7 files changed, 16 insertions(+), 8 deletions(-) create mode 100644 include/linux/platform_data/omap1_bl.h (limited to 'include') diff --git a/arch/arm/mach-omap1/board-osk.c b/arch/arm/mach-omap1/board-osk.c index 8784705edb60..569b6872a2f6 100644 --- a/arch/arm/mach-omap1/board-osk.c +++ b/arch/arm/mach-omap1/board-osk.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include diff --git a/arch/arm/mach-omap1/board-palmte.c b/arch/arm/mach-omap1/board-palmte.c index 26bcb9defcdc..7bf00ba51bd6 100644 --- a/arch/arm/mach-omap1/board-palmte.c +++ b/arch/arm/mach-omap1/board-palmte.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include diff --git a/arch/arm/mach-omap1/board-palmtt.c b/arch/arm/mach-omap1/board-palmtt.c index 4d099446dfa8..2cce505dd8f6 100644 --- a/arch/arm/mach-omap1/board-palmtt.c +++ b/arch/arm/mach-omap1/board-palmtt.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include diff --git a/arch/arm/mach-omap1/board-palmz71.c b/arch/arm/mach-omap1/board-palmz71.c index 355980321c2d..45ab9f03fb85 100644 --- a/arch/arm/mach-omap1/board-palmz71.c +++ b/arch/arm/mach-omap1/board-palmz71.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include diff --git a/arch/arm/plat-omap/include/plat/board.h b/arch/arm/plat-omap/include/plat/board.h index 5938c729b6c0..d0bc46e235bc 100644 --- a/arch/arm/plat-omap/include/plat/board.h +++ b/arch/arm/plat-omap/include/plat/board.h @@ -19,13 +19,6 @@ struct omap_lcd_config { u8 data_lines; }; -struct device; -struct fb_info; -struct omap_backlight_config { - int default_intensity; - int (*set_power)(struct device *dev, int state); -}; - /* for TI reference platforms sharing the same debug card */ extern int debug_card_init(u32 addr, unsigned gpio); diff --git a/drivers/video/backlight/omap1_bl.c b/drivers/video/backlight/omap1_bl.c index bfdc5fbeaa11..92257ef19403 100644 --- a/drivers/video/backlight/omap1_bl.c +++ b/drivers/video/backlight/omap1_bl.c @@ -27,9 +27,9 @@ #include #include #include +#include #include -#include #include #define OMAPBL_MAX_INTENSITY 0xff diff --git a/include/linux/platform_data/omap1_bl.h b/include/linux/platform_data/omap1_bl.h new file mode 100644 index 000000000000..881a8e92d605 --- /dev/null +++ b/include/linux/platform_data/omap1_bl.h @@ -0,0 +1,11 @@ +#ifndef __OMAP1_BL_H__ +#define __OMAP1_BL_H__ + +#include + +struct omap_backlight_config { + int default_intensity; + int (*set_power)(struct device *dev, int state); +}; + +#endif -- cgit v1.2.3 From ad6c9101c7949fd7c6a567e3faae94aaa044cd17 Mon Sep 17 00:00:00 2001 From: Igor Grinberg Date: Wed, 29 Aug 2012 02:18:55 +0300 Subject: ARM: OMAP1: move lcd pdata out of arch/arm/* omap1 lcd platform data resides inside plat/board.h while it should be inside include/linux/... Move the omap1 lcd platform data to include/linux/omapfb.h. Signed-off-by: Igor Grinberg Acked-by: Tomi Valkeinen Signed-off-by: Tony Lindgren --- arch/arm/mach-omap1/board-ams-delta.c | 1 - arch/arm/mach-omap1/board-fsample.c | 1 - arch/arm/mach-omap1/board-htcherald.c | 1 - arch/arm/mach-omap1/board-nokia770.c | 1 - arch/arm/mach-omap1/board-palmte.c | 1 - arch/arm/mach-omap1/board-palmtt.c | 1 - arch/arm/mach-omap1/board-palmz71.c | 1 - arch/arm/mach-omap1/board-perseus2.c | 1 - arch/arm/mach-omap1/board-sx1.c | 1 - arch/arm/plat-omap/fb.c | 2 -- arch/arm/plat-omap/include/plat/board.h | 7 ------- include/linux/omapfb.h | 7 ++++++- 12 files changed, 6 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c index c53469802c03..c162369e9f77 100644 --- a/arch/arm/mach-omap1/board-ams-delta.c +++ b/arch/arm/mach-omap1/board-ams-delta.c @@ -37,7 +37,6 @@ #include #include #include -#include #include #include diff --git a/arch/arm/mach-omap1/board-fsample.c b/arch/arm/mach-omap1/board-fsample.c index 6872f3fd400f..6d985521a39e 100644 --- a/arch/arm/mach-omap1/board-fsample.c +++ b/arch/arm/mach-omap1/board-fsample.c @@ -32,7 +32,6 @@ #include #include #include -#include #include diff --git a/arch/arm/mach-omap1/board-htcherald.c b/arch/arm/mach-omap1/board-htcherald.c index b3f6e943e661..b9771b5a5f7b 100644 --- a/arch/arm/mach-omap1/board-htcherald.c +++ b/arch/arm/mach-omap1/board-htcherald.c @@ -42,7 +42,6 @@ #include #include -#include #include #include diff --git a/arch/arm/mach-omap1/board-nokia770.c b/arch/arm/mach-omap1/board-nokia770.c index 2c0ca8fc3380..ec01f03d0446 100644 --- a/arch/arm/mach-omap1/board-nokia770.c +++ b/arch/arm/mach-omap1/board-nokia770.c @@ -26,7 +26,6 @@ #include #include -#include #include #include #include diff --git a/arch/arm/mach-omap1/board-palmte.c b/arch/arm/mach-omap1/board-palmte.c index 7bf00ba51bd6..49f8d745ea1f 100644 --- a/arch/arm/mach-omap1/board-palmte.c +++ b/arch/arm/mach-omap1/board-palmte.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include diff --git a/arch/arm/mach-omap1/board-palmtt.c b/arch/arm/mach-omap1/board-palmtt.c index 2cce505dd8f6..01523cd78e58 100644 --- a/arch/arm/mach-omap1/board-palmtt.c +++ b/arch/arm/mach-omap1/board-palmtt.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include diff --git a/arch/arm/mach-omap1/board-palmz71.c b/arch/arm/mach-omap1/board-palmz71.c index 45ab9f03fb85..a7abce69043a 100644 --- a/arch/arm/mach-omap1/board-palmz71.c +++ b/arch/arm/mach-omap1/board-palmz71.c @@ -40,7 +40,6 @@ #include #include #include -#include #include #include diff --git a/arch/arm/mach-omap1/board-perseus2.c b/arch/arm/mach-omap1/board-perseus2.c index 703d55ecffe2..277e0bc60a43 100644 --- a/arch/arm/mach-omap1/board-perseus2.c +++ b/arch/arm/mach-omap1/board-perseus2.c @@ -32,7 +32,6 @@ #include #include #include -#include #include diff --git a/arch/arm/mach-omap1/board-sx1.c b/arch/arm/mach-omap1/board-sx1.c index 8c665bd16ac2..2e1fff26a2f3 100644 --- a/arch/arm/mach-omap1/board-sx1.c +++ b/arch/arm/mach-omap1/board-sx1.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include diff --git a/arch/arm/plat-omap/fb.c b/arch/arm/plat-omap/fb.c index dd6f92c99e56..bcbb9d5dc293 100644 --- a/arch/arm/plat-omap/fb.c +++ b/arch/arm/plat-omap/fb.c @@ -33,8 +33,6 @@ #include #include -#include - #if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE) static bool omapfb_lcd_configured; diff --git a/arch/arm/plat-omap/include/plat/board.h b/arch/arm/plat-omap/include/plat/board.h index d0bc46e235bc..2ddc198b0920 100644 --- a/arch/arm/plat-omap/include/plat/board.h +++ b/arch/arm/plat-omap/include/plat/board.h @@ -12,13 +12,6 @@ #include -struct omap_lcd_config { - char panel_name[16]; - char ctrl_name[16]; - s16 nreset_gpio; - u8 data_lines; -}; - /* for TI reference platforms sharing the same debug card */ extern int debug_card_init(u32 addr, unsigned gpio); diff --git a/include/linux/omapfb.h b/include/linux/omapfb.h index 4ff57e81051d..85af8184691a 100644 --- a/include/linux/omapfb.h +++ b/include/linux/omapfb.h @@ -220,7 +220,12 @@ struct omapfb_display_info { #ifdef __KERNEL__ -#include +struct omap_lcd_config { + char panel_name[16]; + char ctrl_name[16]; + s16 nreset_gpio; + u8 data_lines; +}; struct omapfb_platform_data { struct omap_lcd_config lcd; -- cgit v1.2.3 From 32061b4d3830e61975ede409df389804507fd220 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 5 Sep 2012 13:50:13 -0700 Subject: Tools: hv: Implement the KVP verb - KVP_OP_SET_IP_INFO Implement the KVP verb - KVP_OP_SET_IP_INFO. This operation configures the specified interface based on the given configuration. Since configuring an interface is very distro specific, we invoke an external (Distro specific) script to configure the interface. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- include/linux/hyperv.h | 2 + tools/hv/hv_kvp_daemon.c | 443 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 445 insertions(+) (limited to 'include') diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 7585d5533e43..e73b852156b1 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -191,6 +191,8 @@ enum hv_kvp_exchg_pool { #define HV_ERROR_NOT_SUPPORTED 0x80070032 #define HV_ERROR_MACHINE_LOCKED 0x800704F7 #define HV_ERROR_DEVICE_NOT_CONNECTED 0x8007048F +#define HV_INVALIDARG 0x80070057 +#define HV_GUID_NOTFOUND 0x80041002 #define ADDR_FAMILY_NONE 0x00 #define ADDR_FAMILY_IPV4 0x01 diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 6fb2c1c6c32e..ac144b9da0db 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ #include #include #include +#include /* * KVP protocol: The user mode component first registers with the @@ -68,6 +70,14 @@ enum key_index { ProcessorArchitecture }; + +enum { + IPADDR = 0, + NETMASK, + GATEWAY, + DNS +}; + static char kvp_send_buffer[4096]; static char kvp_recv_buffer[4096 * 2]; static struct sockaddr_nl addr; @@ -81,6 +91,11 @@ static char *os_build; static char *lic_version = "Unknown version"; static struct utsname uts_buf; +/* + * The location of the interface configuration file. + */ + +#define KVP_CONFIG_LOC "/var/opt/" #define MAX_FILE_NAME 100 #define ENTRIES_PER_BLOCK 50 @@ -490,6 +505,104 @@ done: return; } + + +/* + * Retrieve an interface name corresponding to the specified guid. + * If there is a match, the function returns a pointer + * to the interface name and if not, a NULL is returned. + * If a match is found, the caller is responsible for + * freeing the memory. + */ + +static char *kvp_get_if_name(char *guid) +{ + DIR *dir; + struct dirent *entry; + FILE *file; + char *p, *q, *x; + char *if_name = NULL; + char buf[256]; + char *kvp_net_dir = "/sys/class/net/"; + char dev_id[256]; + + dir = opendir(kvp_net_dir); + if (dir == NULL) + return NULL; + + snprintf(dev_id, sizeof(dev_id), "%s", kvp_net_dir); + q = dev_id + strlen(kvp_net_dir); + + while ((entry = readdir(dir)) != NULL) { + /* + * Set the state for the next pass. + */ + *q = '\0'; + strcat(dev_id, entry->d_name); + strcat(dev_id, "/device/device_id"); + + file = fopen(dev_id, "r"); + if (file == NULL) + continue; + + p = fgets(buf, sizeof(buf), file); + if (p) { + x = strchr(p, '\n'); + if (x) + *x = '\0'; + + if (!strcmp(p, guid)) { + /* + * Found the guid match; return the interface + * name. The caller will free the memory. + */ + if_name = strdup(entry->d_name); + fclose(file); + break; + } + } + fclose(file); + } + + closedir(dir); + return if_name; +} + +/* + * Retrieve the MAC address given the interface name. + */ + +static char *kvp_if_name_to_mac(char *if_name) +{ + FILE *file; + char *p, *x; + char buf[256]; + char addr_file[256]; + int i; + char *mac_addr = NULL; + + snprintf(addr_file, sizeof(addr_file), "%s%s%s", "/sys/class/net/", + if_name, "/address"); + + file = fopen(addr_file, "r"); + if (file == NULL) + return NULL; + + p = fgets(buf, sizeof(buf), file); + if (p) { + x = strchr(p, '\n'); + if (x) + *x = '\0'; + for (i = 0; i < strlen(p); i++) + p[i] = toupper(p[i]); + mac_addr = strdup(p); + } + + fclose(file); + return mac_addr; +} + + static void kvp_process_ipconfig_file(char *cmd, char *config_buf, int len, int element_size, int offset) @@ -790,6 +903,315 @@ getaddr_done: } +static int expand_ipv6(char *addr, int type) +{ + int ret; + struct in6_addr v6_addr; + + ret = inet_pton(AF_INET6, addr, &v6_addr); + + if (ret != 1) { + if (type == NETMASK) + return 1; + return 0; + } + + sprintf(addr, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:" + "%02x%02x:%02x%02x:%02x%02x", + (int)v6_addr.s6_addr[0], (int)v6_addr.s6_addr[1], + (int)v6_addr.s6_addr[2], (int)v6_addr.s6_addr[3], + (int)v6_addr.s6_addr[4], (int)v6_addr.s6_addr[5], + (int)v6_addr.s6_addr[6], (int)v6_addr.s6_addr[7], + (int)v6_addr.s6_addr[8], (int)v6_addr.s6_addr[9], + (int)v6_addr.s6_addr[10], (int)v6_addr.s6_addr[11], + (int)v6_addr.s6_addr[12], (int)v6_addr.s6_addr[13], + (int)v6_addr.s6_addr[14], (int)v6_addr.s6_addr[15]); + + return 1; + +} + +static int is_ipv4(char *addr) +{ + int ret; + struct in_addr ipv4_addr; + + ret = inet_pton(AF_INET, addr, &ipv4_addr); + + if (ret == 1) + return 1; + return 0; +} + +static int parse_ip_val_buffer(char *in_buf, int *offset, + char *out_buf, int out_len) +{ + char *x; + char *start; + + /* + * in_buf has sequence of characters that are seperated by + * the character ';'. The last sequence does not have the + * terminating ";" character. + */ + start = in_buf + *offset; + + x = strchr(start, ';'); + if (x) + *x = 0; + else + x = start + strlen(start); + + if (strlen(start) != 0) { + int i = 0; + /* + * Get rid of leading spaces. + */ + while (start[i] == ' ') + i++; + + if ((x - start) <= out_len) { + strcpy(out_buf, (start + i)); + *offset += (x - start) + 1; + return 1; + } + } + return 0; +} + +static int kvp_write_file(FILE *f, char *s1, char *s2, char *s3) +{ + int ret; + + ret = fprintf(f, "%s%s%s%s\n", s1, s2, "=", s3); + + if (ret < 0) + return HV_E_FAIL; + + return 0; +} + + +static int process_ip_string(FILE *f, char *ip_string, int type) +{ + int error = 0; + char addr[INET6_ADDRSTRLEN]; + int i = 0; + int j = 0; + char str[256]; + char sub_str[10]; + int offset = 0; + + memset(addr, 0, sizeof(addr)); + + while (parse_ip_val_buffer(ip_string, &offset, addr, + (MAX_IP_ADDR_SIZE * 2))) { + + sub_str[0] = 0; + if (is_ipv4(addr)) { + switch (type) { + case IPADDR: + snprintf(str, sizeof(str), "%s", "IPADDR"); + break; + case NETMASK: + snprintf(str, sizeof(str), "%s", "NETMASK"); + break; + case GATEWAY: + snprintf(str, sizeof(str), "%s", "GATEWAY"); + break; + case DNS: + snprintf(str, sizeof(str), "%s", "DNS"); + break; + } + if (i != 0) { + if (type != DNS) { + snprintf(sub_str, sizeof(sub_str), + "_%d", i++); + } else { + snprintf(sub_str, sizeof(sub_str), + "%d", ++i); + } + } else if (type == DNS) { + snprintf(sub_str, sizeof(sub_str), "%d", ++i); + } + + + } else if (expand_ipv6(addr, type)) { + switch (type) { + case IPADDR: + snprintf(str, sizeof(str), "%s", "IPV6ADDR"); + break; + case NETMASK: + snprintf(str, sizeof(str), "%s", "IPV6NETMASK"); + break; + case GATEWAY: + snprintf(str, sizeof(str), "%s", + "IPV6_DEFAULTGW"); + break; + case DNS: + snprintf(str, sizeof(str), "%s", "DNS"); + break; + } + if ((j != 0) || (type == DNS)) { + if (type != DNS) { + snprintf(sub_str, sizeof(sub_str), + "_%d", j++); + } else { + snprintf(sub_str, sizeof(sub_str), + "%d", ++i); + } + } else if (type == DNS) { + snprintf(sub_str, sizeof(sub_str), + "%d", ++i); + } + } else { + return HV_INVALIDARG; + } + + error = kvp_write_file(f, str, sub_str, addr); + if (error) + return error; + memset(addr, 0, sizeof(addr)); + } + + return 0; +} + +static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) +{ + int error = 0; + char if_file[128]; + FILE *file; + char cmd[512]; + char *mac_addr; + + /* + * Set the configuration for the specified interface with + * the information provided. Since there is no standard + * way to configure an interface, we will have an external + * script that does the job of configuring the interface and + * flushing the configuration. + * + * The parameters passed to this external script are: + * 1. A configuration file that has the specified configuration. + * + * We will embed the name of the interface in the configuration + * file: ifcfg-ethx (where ethx is the interface name). + * + * The information provided here may be more than what is needed + * in a given distro to configure the interface and so are free + * ignore information that may not be relevant. + * + * Here is the format of the ip configuration file: + * + * HWADDR=macaddr + * IF_NAME=interface name + * DHCP=yes (This is optional; if yes, DHCP is configured) + * + * IPADDR=ipaddr1 + * IPADDR_1=ipaddr2 + * IPADDR_x=ipaddry (where y = x + 1) + * + * NETMASK=netmask1 + * NETMASK_x=netmasky (where y = x + 1) + * + * GATEWAY=ipaddr1 + * GATEWAY_x=ipaddry (where y = x + 1) + * + * DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc) + * + * IPV6 addresses will be tagged as IPV6ADDR, IPV6 gateway will be + * tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as + * IPV6NETMASK. + * + * The host can specify multiple ipv4 and ipv6 addresses to be + * configured for the interface. Furthermore, the configuration + * needs to be persistent. A subsequent GET call on the interface + * is expected to return the configuration that is set via the SET + * call. + */ + + snprintf(if_file, sizeof(if_file), "%s%s%s", KVP_CONFIG_LOC, + "hyperv/ifcfg-", if_name); + + file = fopen(if_file, "w"); + + if (file == NULL) { + syslog(LOG_ERR, "Failed to open config file"); + return HV_E_FAIL; + } + + /* + * First write out the MAC address. + */ + + mac_addr = kvp_if_name_to_mac(if_name); + if (mac_addr == NULL) { + error = HV_E_FAIL; + goto setval_error; + } + + error = kvp_write_file(file, "HWADDR", "", mac_addr); + if (error) + goto setval_error; + + error = kvp_write_file(file, "IF_NAME", "", if_name); + if (error) + goto setval_error; + + if (new_val->dhcp_enabled) { + error = kvp_write_file(file, "DHCP", "", "yes"); + if (error) + goto setval_error; + + /* + * We are done!. + */ + goto setval_done; + } + + /* + * Write the configuration for ipaddress, netmask, gateway and + * name servers. + */ + + error = process_ip_string(file, (char *)new_val->ip_addr, IPADDR); + if (error) + goto setval_error; + + error = process_ip_string(file, (char *)new_val->sub_net, NETMASK); + if (error) + goto setval_error; + + error = process_ip_string(file, (char *)new_val->gate_way, GATEWAY); + if (error) + goto setval_error; + + error = process_ip_string(file, (char *)new_val->dns_addr, DNS); + if (error) + goto setval_error; + +setval_done: + free(mac_addr); + fclose(file); + + /* + * Now that we have populated the configuration file, + * invoke the external script to do its magic. + */ + + snprintf(cmd, sizeof(cmd), "%s %s", "hv_set_ifconfig", if_file); + system(cmd); + return 0; + +setval_error: + syslog(LOG_ERR, "Failed to write config file"); + free(mac_addr); + fclose(file); + return error; +} + + static int kvp_get_domain_name(char *buffer, int length) { @@ -859,6 +1281,8 @@ int main(void) char *key_name; int op; int pool; + char *if_name; + struct hv_kvp_ipaddr_value *kvp_ip_val; daemon(1, 0); openlog("KVP", 0, LOG_USER); @@ -962,6 +1386,25 @@ int main(void) } switch (op) { + case KVP_OP_SET_IP_INFO: + kvp_ip_val = &hv_msg->body.kvp_ip_val; + if_name = kvp_get_if_name( + (char *)kvp_ip_val->adapter_id); + if (if_name == NULL) { + /* + * We could not map the guid to an + * interface name; return error. + */ + hv_msg->error = HV_GUID_NOTFOUND; + break; + } + error = kvp_set_ip_info(if_name, kvp_ip_val); + if (error) + hv_msg->error = error; + + free(if_name); + break; + case KVP_OP_SET: if (kvp_key_add_or_modify(pool, hv_msg->body.kvp_set.data.key, -- cgit v1.2.3 From 769ae543dc8d5745e1ade88cbcf1271a96276fd2 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Sun, 2 Sep 2012 23:37:24 +0200 Subject: PCI: Drop duplicate const in DECLARE_PCI_FIXUP_SECTION It's redundant and makes sparse complain about it. Signed-off-by: Mathias Krause Signed-off-by: Bjorn Helgaas --- include/linux/pci.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/pci.h b/include/linux/pci.h index 5faa8310eec9..aee24a8bc09a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1472,7 +1472,7 @@ enum pci_fixup_pass { /* Anonymous variables would be nice... */ #define DECLARE_PCI_FIXUP_SECTION(section, name, vendor, device, class, \ class_shift, hook) \ - static const struct pci_fixup const __pci_fixup_##name __used \ + static const struct pci_fixup __pci_fixup_##name __used \ __attribute__((__section__(#section), aligned((sizeof(void *))))) \ = { vendor, device, class, class_shift, hook }; -- cgit v1.2.3 From 6f586e663e3b3674cadad0d5329424b006a0a289 Mon Sep 17 00:00:00 2001 From: Hiroshi Doyu Date: Tue, 4 Sep 2012 06:40:40 +0200 Subject: driver-core: Shut up dev_dbg_reatelimited() without DEBUG dev_dbg_reatelimited() without DEBUG printed "217078 callbacks suppressed". This shouldn't print anything without DEBUG. With CONFIG_DYNAMIC_DEBUG, the print should be configured as expected. Signed-off-by: Hiroshi Doyu Reported-by: Hin-Tak Leung Tested-by: Antti Palosaari Tested-by: Hin-Tak Leung Acked-by: Hin-Tak Leung Signed-off-by: Greg Kroah-Hartman --- include/linux/device.h | 62 +++++++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/linux/device.h b/include/linux/device.h index ecd900663726..378a27c1d1af 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -950,6 +950,32 @@ int _dev_info(const struct device *dev, const char *fmt, ...) #endif +/* + * Stupid hackaround for existing uses of non-printk uses dev_info + * + * Note that the definition of dev_info below is actually _dev_info + * and a macro is used to avoid redefining dev_info + */ + +#define dev_info(dev, fmt, arg...) _dev_info(dev, fmt, ##arg) + +#if defined(CONFIG_DYNAMIC_DEBUG) +#define dev_dbg(dev, format, ...) \ +do { \ + dynamic_dev_dbg(dev, format, ##__VA_ARGS__); \ +} while (0) +#elif defined(DEBUG) +#define dev_dbg(dev, format, arg...) \ + dev_printk(KERN_DEBUG, dev, format, ##arg) +#else +#define dev_dbg(dev, format, arg...) \ +({ \ + if (0) \ + dev_printk(KERN_DEBUG, dev, format, ##arg); \ + 0; \ +}) +#endif + #define dev_level_ratelimited(dev_level, dev, fmt, ...) \ do { \ static DEFINE_RATELIMIT_STATE(_rs, \ @@ -973,33 +999,21 @@ 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) #define dev_dbg_ratelimited(dev, fmt, ...) \ - dev_level_ratelimited(dev_dbg, dev, fmt, ##__VA_ARGS__) - -/* - * Stupid hackaround for existing uses of non-printk uses dev_info - * - * Note that the definition of dev_info below is actually _dev_info - * and a macro is used to avoid redefining dev_info - */ - -#define dev_info(dev, fmt, arg...) _dev_info(dev, fmt, ##arg) - -#if defined(CONFIG_DYNAMIC_DEBUG) -#define dev_dbg(dev, format, ...) \ -do { \ - dynamic_dev_dbg(dev, format, ##__VA_ARGS__); \ +do { \ + static DEFINE_RATELIMIT_STATE(_rs, \ + DEFAULT_RATELIMIT_INTERVAL, \ + DEFAULT_RATELIMIT_BURST); \ + DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \ + if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT) && \ + __ratelimit(&_rs)) \ + __dynamic_pr_debug(&descriptor, pr_fmt(fmt), \ + ##__VA_ARGS__); \ } while (0) -#elif defined(DEBUG) -#define dev_dbg(dev, format, arg...) \ - dev_printk(KERN_DEBUG, dev, format, ##arg) #else -#define dev_dbg(dev, format, arg...) \ -({ \ - if (0) \ - dev_printk(KERN_DEBUG, dev, format, ##arg); \ - 0; \ -}) +#define dev_dbg_ratelimited(dev, fmt, ...) \ + no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) #endif #ifdef VERBOSE_DEBUG -- cgit v1.2.3 From c1411bfae63286fbe6e63a1ec558eb4eb812859c Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 21 May 2012 21:57:39 +0200 Subject: misc/atmel_tc: make atmel_tc.tcb_config member point to const data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This prepares *of_device_id.data becoming const. Without this change the following warning would occur: drivers/misc/atmel_tclib.c: In function 'tc_probe': drivers/misc/atmel_tclib.c:170: warning: assignment discards qualifiers from pointer target type Signed-off-by: Uwe Kleine-König Acked-by: Nicolas Ferre --- include/linux/atmel_tc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/atmel_tc.h b/include/linux/atmel_tc.h index 1d14b1dc1aee..89a931babecf 100644 --- a/include/linux/atmel_tc.h +++ b/include/linux/atmel_tc.h @@ -63,7 +63,7 @@ struct atmel_tc { struct platform_device *pdev; struct resource *iomem; void __iomem *regs; - struct atmel_tcb_config *tcb_config; + const struct atmel_tcb_config *tcb_config; int irq[3]; struct clk *clk[3]; struct list_head node; -- cgit v1.2.3 From d7c9a53f13cf4b273b220934167c7630c3362563 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 7 Jun 2012 12:20:14 +0200 Subject: of: add const to struct *of_device_id.data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drivers should never need to modify the data of a device id. So it can be const which in turn allows more consts in the driver. Acked-by: Greg Kroah-Hartman Signed-off-by: Uwe Kleine-König --- include/linux/mod_devicetable.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 6955045199b0..78874b361120 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -232,7 +232,7 @@ struct of_device_id char type[32]; char compatible[128]; #ifdef __KERNEL__ - void *data; + const void *data; #else kernel_ulong_t data; #endif -- cgit v1.2.3 From ac5ad93e92c3ffca4c7ba386aaa34244e27b7759 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Tue, 11 Sep 2012 16:59:45 -0600 Subject: PCI: Add weak pcibios_window_alignment() interface This patch implements a weak function to return the default I/O or memory window alignment for a P2P bridge. By default, I/O windows are aligned to 4KiB or 1KiB and memory windows are aligned to 4MiB. Some platforms, e.g., powernv, have special alignment requirements and can override pcibios_window_alignment(). [bhelgaas: changelog] Signed-off-by: Gavin Shan Signed-off-by: Bjorn Helgaas --- drivers/pci/setup-bus.c | 32 ++++++++++++++++++++++++++++++++ include/linux/pci.h | 2 ++ 2 files changed, 34 insertions(+) (limited to 'include') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index fb506137aaee..896f06e3e793 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -697,6 +697,38 @@ static resource_size_t calculate_memsize(resource_size_t size, return size; } +resource_size_t __weak pcibios_window_alignment(struct pci_bus *bus, + unsigned long type) +{ + return 1; +} + +#define PCI_P2P_DEFAULT_MEM_ALIGN 0x100000 /* 1MiB */ +#define PCI_P2P_DEFAULT_IO_ALIGN 0x1000 /* 4KiB */ +#define PCI_P2P_DEFAULT_IO_ALIGN_1K 0x400 /* 1KiB */ + +static resource_size_t window_alignment(struct pci_bus *bus, + unsigned long type) +{ + resource_size_t align = 1, arch_align; + + if (type & IORESOURCE_MEM) + align = PCI_P2P_DEFAULT_MEM_ALIGN; + else if (type & IORESOURCE_IO) { + /* + * Per spec, I/O windows are 4K-aligned, but some + * bridges have an extension to support 1K alignment. + */ + if (bus->self->io_window_1k) + align = PCI_P2P_DEFAULT_IO_ALIGN_1K; + else + align = PCI_P2P_DEFAULT_IO_ALIGN; + } + + arch_align = pcibios_window_alignment(bus, type); + return max(align, arch_align); +} + /** * pbus_size_io() - size the io window of a given bus * diff --git a/include/linux/pci.h b/include/linux/pci.h index b8667e0548e0..2c755243eef2 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1064,6 +1064,8 @@ int pci_cfg_space_size_ext(struct pci_dev *dev); int pci_cfg_space_size(struct pci_dev *dev); unsigned char pci_bus_max_busnr(struct pci_bus *bus); void pci_setup_bridge(struct pci_bus *bus); +resource_size_t pcibios_window_alignment(struct pci_bus *bus, + unsigned long type); #define PCI_VGA_STATE_CHANGE_BRIDGE (1 << 0) #define PCI_VGA_STATE_CHANGE_DECODES (1 << 1) -- cgit v1.2.3 From 7ece55a4a3a04abe37118b1d4fb0b702eeb1de4c Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 4 Sep 2012 23:23:06 -0700 Subject: trace: Don't declare trace_*_rcuidle functions in modules Tracepoints declare a static inline trace_*_rcuidle variant of the trace function, to support safely generating trace events from the idle loop. Module code never actually uses that variant of trace functions, because modules don't run code that needs tracing with RCU idled. However, the declaration of those otherwise unused functions causes the module to reference rcu_idle_exit and rcu_idle_enter, which RCU does not export to modules. To avoid this, don't generate trace_*_rcuidle functions for tracepoints declared in module code. Link: http://lkml.kernel.org/r/20120905062306.GA14756@leaf Reported-by: Steven Rostedt Acked-by: Mathieu Desnoyers Acked-by: Paul E. McKenney Signed-off-by: Josh Triplett Signed-off-by: Steven Rostedt --- include/linux/tracepoint.h | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index 802de56c41e8..2f322c38bd4d 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -136,6 +136,22 @@ static inline void tracepoint_synchronize_unregister(void) postrcu; \ } while (0) +#ifndef MODULE +#define __DECLARE_TRACE_RCU(name, proto, args, cond, data_proto, data_args) \ + static inline void trace_##name##_rcuidle(proto) \ + { \ + if (static_key_false(&__tracepoint_##name.key)) \ + __DO_TRACE(&__tracepoint_##name, \ + TP_PROTO(data_proto), \ + TP_ARGS(data_args), \ + TP_CONDITION(cond), \ + rcu_idle_exit(), \ + rcu_idle_enter()); \ + } +#else +#define __DECLARE_TRACE_RCU(name, proto, args, cond, data_proto, data_args) +#endif + /* * Make sure the alignment of the structure in the __tracepoints section will * not add unwanted padding between the beginning of the section and the @@ -151,16 +167,8 @@ static inline void tracepoint_synchronize_unregister(void) TP_ARGS(data_args), \ TP_CONDITION(cond),,); \ } \ - static inline void trace_##name##_rcuidle(proto) \ - { \ - if (static_key_false(&__tracepoint_##name.key)) \ - __DO_TRACE(&__tracepoint_##name, \ - TP_PROTO(data_proto), \ - TP_ARGS(data_args), \ - TP_CONDITION(cond), \ - rcu_idle_exit(), \ - rcu_idle_enter()); \ - } \ + __DECLARE_TRACE_RCU(name, PARAMS(proto), PARAMS(args), \ + PARAMS(cond), PARAMS(data_proto), PARAMS(data_args)) \ static inline int \ register_trace_##name(void (*probe)(data_proto), void *data) \ { \ -- cgit v1.2.3 From 2fc136eecd0c647a6b13fcd00d0c41a1a28f35a5 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Wed, 12 Sep 2012 12:44:30 +0100 Subject: xen/m2p: do not reuse kmap_op->dev_bus_addr If the caller passes a valid kmap_op to m2p_add_override, we use kmap_op->dev_bus_addr to store the original mfn, but dev_bus_addr is part of the interface with Xen and if we are batching the hypercalls it might not have been written by the hypervisor yet. That means that later on Xen will write to it and we'll think that the original mfn is actually what Xen has written to it. Rather than "stealing" struct members from kmap_op, keep using page->index to store the original mfn and add another parameter to m2p_remove_override to get the corresponding kmap_op instead. It is now responsibility of the caller to keep track of which kmap_op corresponds to a particular page in the m2p_override (gntdev, the only user of this interface that passes a valid kmap_op, is already doing that). CC: stable@kernel.org Reported-and-Tested-By: Sander Eikelenboom Signed-off-by: Stefano Stabellini Signed-off-by: Konrad Rzeszutek Wilk --- arch/x86/include/asm/xen/page.h | 3 ++- arch/x86/xen/p2m.c | 27 +++++++++++---------------- drivers/block/xen-blkback/blkback.c | 2 +- drivers/xen/gntdev.c | 5 +++-- drivers/xen/grant-table.c | 6 ++++-- include/xen/grant_table.h | 3 ++- 6 files changed, 23 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/xen/page.h b/arch/x86/include/asm/xen/page.h index 93971e841dd5..472b9b783019 100644 --- a/arch/x86/include/asm/xen/page.h +++ b/arch/x86/include/asm/xen/page.h @@ -51,7 +51,8 @@ extern unsigned long set_phys_range_identity(unsigned long pfn_s, extern int m2p_add_override(unsigned long mfn, struct page *page, struct gnttab_map_grant_ref *kmap_op); -extern int m2p_remove_override(struct page *page, bool clear_pte); +extern int m2p_remove_override(struct page *page, + struct gnttab_map_grant_ref *kmap_op); extern struct page *m2p_find_override(unsigned long mfn); extern unsigned long m2p_find_override_pfn(unsigned long mfn, unsigned long pfn); diff --git a/arch/x86/xen/p2m.c b/arch/x86/xen/p2m.c index 76ba0e97e530..72213da605f5 100644 --- a/arch/x86/xen/p2m.c +++ b/arch/x86/xen/p2m.c @@ -828,9 +828,6 @@ int m2p_add_override(unsigned long mfn, struct page *page, xen_mc_issue(PARAVIRT_LAZY_MMU); } - /* let's use dev_bus_addr to record the old mfn instead */ - kmap_op->dev_bus_addr = page->index; - page->index = (unsigned long) kmap_op; } spin_lock_irqsave(&m2p_override_lock, flags); list_add(&page->lru, &m2p_overrides[mfn_hash(mfn)]); @@ -857,7 +854,8 @@ int m2p_add_override(unsigned long mfn, struct page *page, return 0; } EXPORT_SYMBOL_GPL(m2p_add_override); -int m2p_remove_override(struct page *page, bool clear_pte) +int m2p_remove_override(struct page *page, + struct gnttab_map_grant_ref *kmap_op) { unsigned long flags; unsigned long mfn; @@ -887,10 +885,8 @@ int m2p_remove_override(struct page *page, bool clear_pte) WARN_ON(!PagePrivate(page)); ClearPagePrivate(page); - if (clear_pte) { - struct gnttab_map_grant_ref *map_op = - (struct gnttab_map_grant_ref *) page->index; - set_phys_to_machine(pfn, map_op->dev_bus_addr); + set_phys_to_machine(pfn, page->index); + if (kmap_op != NULL) { if (!PageHighMem(page)) { struct multicall_space mcs; struct gnttab_unmap_grant_ref *unmap_op; @@ -902,13 +898,13 @@ int m2p_remove_override(struct page *page, bool clear_pte) * issued. In this case handle is going to -1 because * it hasn't been modified yet. */ - if (map_op->handle == -1) + if (kmap_op->handle == -1) xen_mc_flush(); /* - * Now if map_op->handle is negative it means that the + * Now if kmap_op->handle is negative it means that the * hypercall actually returned an error. */ - if (map_op->handle == GNTST_general_error) { + if (kmap_op->handle == GNTST_general_error) { printk(KERN_WARNING "m2p_remove_override: " "pfn %lx mfn %lx, failed to modify kernel mappings", pfn, mfn); @@ -918,8 +914,8 @@ int m2p_remove_override(struct page *page, bool clear_pte) mcs = xen_mc_entry( sizeof(struct gnttab_unmap_grant_ref)); unmap_op = mcs.args; - unmap_op->host_addr = map_op->host_addr; - unmap_op->handle = map_op->handle; + unmap_op->host_addr = kmap_op->host_addr; + unmap_op->handle = kmap_op->handle; unmap_op->dev_bus_addr = 0; MULTI_grant_table_op(mcs.mc, @@ -930,10 +926,9 @@ int m2p_remove_override(struct page *page, bool clear_pte) set_pte_at(&init_mm, address, ptep, pfn_pte(pfn, PAGE_KERNEL)); __flush_tlb_single(address); - map_op->host_addr = 0; + kmap_op->host_addr = 0; } - } else - set_phys_to_machine(pfn, page->index); + } /* p2m(m2p(mfn)) == FOREIGN_FRAME(mfn): the mfn is already present * somewhere in this domain, even before being added to the diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c index 73f196ca713f..c6decb901e5e 100644 --- a/drivers/block/xen-blkback/blkback.c +++ b/drivers/block/xen-blkback/blkback.c @@ -337,7 +337,7 @@ static void xen_blkbk_unmap(struct pending_req *req) invcount++; } - ret = gnttab_unmap_refs(unmap, pages, invcount, false); + ret = gnttab_unmap_refs(unmap, NULL, pages, invcount); BUG_ON(ret); } diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index 1ffd03bf8e10..7f1241608489 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c @@ -314,8 +314,9 @@ static int __unmap_grant_pages(struct grant_map *map, int offset, int pages) } } - err = gnttab_unmap_refs(map->unmap_ops + offset, map->pages + offset, - pages, true); + err = gnttab_unmap_refs(map->unmap_ops + offset, + use_ptemod ? map->kmap_ops + offset : NULL, map->pages + offset, + pages); if (err) return err; diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index 0bfc1ef11259..006726688baf 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -870,7 +870,8 @@ int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops, EXPORT_SYMBOL_GPL(gnttab_map_refs); int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops, - struct page **pages, unsigned int count, bool clear_pte) + struct gnttab_map_grant_ref *kmap_ops, + struct page **pages, unsigned int count) { int i, ret; bool lazy = false; @@ -888,7 +889,8 @@ int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops, } for (i = 0; i < count; i++) { - ret = m2p_remove_override(pages[i], clear_pte); + ret = m2p_remove_override(pages[i], kmap_ops ? + &kmap_ops[i] : NULL); if (ret) return ret; } diff --git a/include/xen/grant_table.h b/include/xen/grant_table.h index 11e27c3af3cb..f19fff8650e9 100644 --- a/include/xen/grant_table.h +++ b/include/xen/grant_table.h @@ -187,6 +187,7 @@ int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops, struct gnttab_map_grant_ref *kmap_ops, struct page **pages, unsigned int count); int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops, - struct page **pages, unsigned int count, bool clear_pte); + struct gnttab_map_grant_ref *kunmap_ops, + struct page **pages, unsigned int count); #endif /* __ASM_GNTTAB_H__ */ -- cgit v1.2.3 From c076ada4e4aaf45e1a31ad6de7c6cce36081e045 Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Wed, 8 Aug 2012 09:42:32 +0200 Subject: i2c: pnx: Fix read transactions of >= 2 bytes On transactions with n>=2 bytes, the controller actually wrongly clocks in n+1 bytes. This is caused by the (wrong) assumption that RFE in the Status Register is 1 iff there is no byte already ordered (via a dummy TX byte). This lead to the implementation of synchronized byte ordering, e.g.: Dummy-TX - RX - Dummy-TX - RX - ... But since RFE actually stays high after some Dummy-TX, it rather looks like: Dummy-TX - Dummy-TX - RX - Dummy-TX - RX - (RX) The last RX byte is clocked in by the bus controller, but ignored by the kernel when filling the userspace buffer. This patch fixes the issue by asking for RX via Dummy-TX asynchronously. Introducing a separate counter for TX bytes. Signed-off-by: Roland Stigge Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-pnx.c | 48 ++++++++++++++++++++++++++------------------ include/linux/i2c-pnx.h | 1 + 2 files changed, 29 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c index b36e21f9fd53..8488bddfe465 100644 --- a/drivers/i2c/busses/i2c-pnx.c +++ b/drivers/i2c/busses/i2c-pnx.c @@ -291,31 +291,37 @@ static int i2c_pnx_master_rcv(struct i2c_pnx_algo_data *alg_data) * or we didn't 'ask' for it yet. */ if (ioread32(I2C_REG_STS(alg_data)) & mstatus_rfe) { - dev_dbg(&alg_data->adapter.dev, - "%s(): Write dummy data to fill Rx-fifo...\n", - __func__); + /* 'Asking' is done asynchronously, e.g. dummy TX of several + * bytes is done before the first actual RX arrives in FIFO. + * Therefore, ordered bytes (via TX) are counted separately. + */ + if (alg_data->mif.order) { + dev_dbg(&alg_data->adapter.dev, + "%s(): Write dummy data to fill Rx-fifo...\n", + __func__); - if (alg_data->mif.len == 1) { - /* Last byte, do not acknowledge next rcv. */ - val |= stop_bit; + if (alg_data->mif.order == 1) { + /* Last byte, do not acknowledge next rcv. */ + val |= stop_bit; + + /* + * Enable interrupt RFDAIE (data in Rx fifo), + * and disable DRMIE (need data for Tx) + */ + ctl = ioread32(I2C_REG_CTL(alg_data)); + ctl |= mcntrl_rffie | mcntrl_daie; + ctl &= ~mcntrl_drmie; + iowrite32(ctl, I2C_REG_CTL(alg_data)); + } /* - * Enable interrupt RFDAIE (data in Rx fifo), - * and disable DRMIE (need data for Tx) + * Now we'll 'ask' for data: + * For each byte we want to receive, we must + * write a (dummy) byte to the Tx-FIFO. */ - ctl = ioread32(I2C_REG_CTL(alg_data)); - ctl |= mcntrl_rffie | mcntrl_daie; - ctl &= ~mcntrl_drmie; - iowrite32(ctl, I2C_REG_CTL(alg_data)); + iowrite32(val, I2C_REG_TX(alg_data)); + alg_data->mif.order--; } - - /* - * Now we'll 'ask' for data: - * For each byte we want to receive, we must - * write a (dummy) byte to the Tx-FIFO. - */ - iowrite32(val, I2C_REG_TX(alg_data)); - return 0; } @@ -515,6 +521,7 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) alg_data->mif.buf = pmsg->buf; alg_data->mif.len = pmsg->len; + alg_data->mif.order = pmsg->len; alg_data->mif.mode = (pmsg->flags & I2C_M_RD) ? I2C_SMBUS_READ : I2C_SMBUS_WRITE; alg_data->mif.ret = 0; @@ -567,6 +574,7 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) /* Cleanup to be sure... */ alg_data->mif.buf = NULL; alg_data->mif.len = 0; + alg_data->mif.order = 0; dev_dbg(&alg_data->adapter.dev, "%s(): exiting, stat = %x\n", __func__, ioread32(I2C_REG_STS(alg_data))); diff --git a/include/linux/i2c-pnx.h b/include/linux/i2c-pnx.h index 1bc74afe7a35..49ed17fdf055 100644 --- a/include/linux/i2c-pnx.h +++ b/include/linux/i2c-pnx.h @@ -22,6 +22,7 @@ struct i2c_pnx_mif { struct timer_list timer; /* Timeout */ u8 * buf; /* Data buffer */ int len; /* Length of data buffer */ + int order; /* RX Bytes to order via TX */ }; struct i2c_pnx_algo_data { -- cgit v1.2.3 From a940d9a1cb2ea0833421fd57e47f8ce2a6d9953b Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Tue, 4 Sep 2012 17:43:30 -0700 Subject: ARM: OMAP2+: Remove hardcoded twl4030 gpio_base, irq_base and irq_end We can't use hardcoded interrupts for SPARSE_IRQ, and can replace the hardcoded gpio_base with twl_gpiochip.base after it's been allocated. Cc: Grant Likely Cc: Samuel Ortiz Cc: Peter Ujfalusi Acked-by: Linus Walleij Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/board-2430sdp.c | 3 --- arch/arm/mach-omap2/board-3430sdp.c | 3 --- arch/arm/mach-omap2/board-4430sdp.c | 1 - arch/arm/mach-omap2/board-cm-t35.c | 3 --- arch/arm/mach-omap2/board-devkit8000.c | 3 --- arch/arm/mach-omap2/board-igep0020.c | 3 --- arch/arm/mach-omap2/board-ldp.c | 3 --- arch/arm/mach-omap2/board-omap3beagle.c | 3 --- arch/arm/mach-omap2/board-omap3evm.c | 3 --- arch/arm/mach-omap2/board-omap3logic.c | 3 --- arch/arm/mach-omap2/board-omap3pandora.c | 3 --- arch/arm/mach-omap2/board-omap3stalker.c | 3 --- arch/arm/mach-omap2/board-omap3touchbook.c | 3 --- arch/arm/mach-omap2/board-omap4panda.c | 1 - arch/arm/mach-omap2/board-overo.c | 3 --- arch/arm/mach-omap2/board-rm680.c | 3 --- arch/arm/mach-omap2/board-rx51-peripherals.c | 3 --- arch/arm/mach-omap2/board-zoom-peripherals.c | 3 --- drivers/gpio/gpio-twl4030.c | 15 ++++++++++----- include/linux/i2c/twl.h | 3 --- include/linux/mfd/twl6040.h | 1 - 21 files changed, 10 insertions(+), 59 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-omap2/board-2430sdp.c b/arch/arm/mach-omap2/board-2430sdp.c index 36eee4b512f6..cacc49889129 100644 --- a/arch/arm/mach-omap2/board-2430sdp.c +++ b/arch/arm/mach-omap2/board-2430sdp.c @@ -211,9 +211,6 @@ static struct regulator_init_data sdp2430_vmmc1 = { }; static struct twl4030_gpio_platform_data sdp2430_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, }; static struct twl4030_platform_data sdp2430_twldata = { diff --git a/arch/arm/mach-omap2/board-3430sdp.c b/arch/arm/mach-omap2/board-3430sdp.c index 0f78cdbec5c1..c843d01aedda 100644 --- a/arch/arm/mach-omap2/board-3430sdp.c +++ b/arch/arm/mach-omap2/board-3430sdp.c @@ -229,9 +229,6 @@ static int sdp3430_twl_gpio_setup(struct device *dev, } static struct twl4030_gpio_platform_data sdp3430_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .pulldowns = BIT(2) | BIT(6) | BIT(8) | BIT(13) | BIT(16) | BIT(17), .setup = sdp3430_twl_gpio_setup, diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c index 04e857419bb6..ee8260481273 100644 --- a/arch/arm/mach-omap2/board-4430sdp.c +++ b/arch/arm/mach-omap2/board-4430sdp.c @@ -543,7 +543,6 @@ static struct twl6040_platform_data twl6040_data = { .codec = &twl6040_codec, .vibra = &twl6040_vibra, .audpwron_gpio = 127, - .irq_base = TWL6040_CODEC_IRQ_BASE, }; static struct twl4030_platform_data sdp4430_twldata = { diff --git a/arch/arm/mach-omap2/board-cm-t35.c b/arch/arm/mach-omap2/board-cm-t35.c index d94a640fe41c..ea3410953d2c 100644 --- a/arch/arm/mach-omap2/board-cm-t35.c +++ b/arch/arm/mach-omap2/board-cm-t35.c @@ -469,9 +469,6 @@ static int cm_t35_twl_gpio_setup(struct device *dev, unsigned gpio, } static struct twl4030_gpio_platform_data cm_t35_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .setup = cm_t35_twl_gpio_setup, }; diff --git a/arch/arm/mach-omap2/board-devkit8000.c b/arch/arm/mach-omap2/board-devkit8000.c index 870a2a55a032..9032807702b5 100644 --- a/arch/arm/mach-omap2/board-devkit8000.c +++ b/arch/arm/mach-omap2/board-devkit8000.c @@ -235,9 +235,6 @@ static int devkit8000_twl_gpio_setup(struct device *dev, } static struct twl4030_gpio_platform_data devkit8000_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .use_leds = true, .pulldowns = BIT(1) | BIT(2) | BIT(6) | BIT(8) | BIT(13) | BIT(15) | BIT(16) | BIT(17), diff --git a/arch/arm/mach-omap2/board-igep0020.c b/arch/arm/mach-omap2/board-igep0020.c index 01103b38f77f..577554933862 100644 --- a/arch/arm/mach-omap2/board-igep0020.c +++ b/arch/arm/mach-omap2/board-igep0020.c @@ -424,9 +424,6 @@ static int igep_twl_gpio_setup(struct device *dev, }; static struct twl4030_gpio_platform_data igep_twl4030_gpio_pdata = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .use_leds = true, .setup = igep_twl_gpio_setup, }; diff --git a/arch/arm/mach-omap2/board-ldp.c b/arch/arm/mach-omap2/board-ldp.c index c37c2a17c410..bea2e3a4c00f 100644 --- a/arch/arm/mach-omap2/board-ldp.c +++ b/arch/arm/mach-omap2/board-ldp.c @@ -274,9 +274,6 @@ static int ldp_twl_gpio_setup(struct device *dev, unsigned gpio, unsigned ngpio) } static struct twl4030_gpio_platform_data ldp_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .setup = ldp_twl_gpio_setup, }; diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c index 293e3b26c471..9d9b2abaf7d9 100644 --- a/arch/arm/mach-omap2/board-omap3beagle.c +++ b/arch/arm/mach-omap2/board-omap3beagle.c @@ -296,9 +296,6 @@ static int beagle_twl_gpio_setup(struct device *dev, } static struct twl4030_gpio_platform_data beagle_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .use_leds = true, .pullups = BIT(1), .pulldowns = BIT(2) | BIT(6) | BIT(7) | BIT(8) | BIT(13) diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c index 851aec6ecb8a..493bd96746b9 100644 --- a/arch/arm/mach-omap2/board-omap3evm.c +++ b/arch/arm/mach-omap2/board-omap3evm.c @@ -388,9 +388,6 @@ static int omap3evm_twl_gpio_setup(struct device *dev, } static struct twl4030_gpio_platform_data omap3evm_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .use_leds = true, .setup = omap3evm_twl_gpio_setup, }; diff --git a/arch/arm/mach-omap2/board-omap3logic.c b/arch/arm/mach-omap2/board-omap3logic.c index a63a49d51fc8..8fe7f0cd9b0f 100644 --- a/arch/arm/mach-omap2/board-omap3logic.c +++ b/arch/arm/mach-omap2/board-omap3logic.c @@ -77,9 +77,6 @@ static struct regulator_init_data omap3logic_vmmc1 = { }; static struct twl4030_gpio_platform_data omap3logic_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .use_leds = true, .pullups = BIT(1), .pulldowns = BIT(2) | BIT(6) | BIT(7) | BIT(8) diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c index 633c445c7cc4..38521d43b6dd 100644 --- a/arch/arm/mach-omap2/board-omap3pandora.c +++ b/arch/arm/mach-omap2/board-omap3pandora.c @@ -320,9 +320,6 @@ static int omap3pandora_twl_gpio_setup(struct device *dev, } static struct twl4030_gpio_platform_data omap3pandora_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .setup = omap3pandora_twl_gpio_setup, }; diff --git a/arch/arm/mach-omap2/board-omap3stalker.c b/arch/arm/mach-omap2/board-omap3stalker.c index 421fb8e76f27..87aa5b1d4c88 100644 --- a/arch/arm/mach-omap2/board-omap3stalker.c +++ b/arch/arm/mach-omap2/board-omap3stalker.c @@ -278,9 +278,6 @@ omap3stalker_twl_gpio_setup(struct device *dev, } static struct twl4030_gpio_platform_data omap3stalker_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .use_leds = true, .setup = omap3stalker_twl_gpio_setup, }; diff --git a/arch/arm/mach-omap2/board-omap3touchbook.c b/arch/arm/mach-omap2/board-omap3touchbook.c index d3556c938900..88dc913b6325 100644 --- a/arch/arm/mach-omap2/board-omap3touchbook.c +++ b/arch/arm/mach-omap2/board-omap3touchbook.c @@ -138,9 +138,6 @@ static int touchbook_twl_gpio_setup(struct device *dev, } static struct twl4030_gpio_platform_data touchbook_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .use_leds = true, .pullups = BIT(1), .pulldowns = BIT(2) | BIT(6) | BIT(7) | BIT(8) | BIT(13) diff --git a/arch/arm/mach-omap2/board-omap4panda.c b/arch/arm/mach-omap2/board-omap4panda.c index 3911c13a342a..e37ee6749bed 100644 --- a/arch/arm/mach-omap2/board-omap4panda.c +++ b/arch/arm/mach-omap2/board-omap4panda.c @@ -262,7 +262,6 @@ static struct twl6040_codec_data twl6040_codec = { static struct twl6040_platform_data twl6040_data = { .codec = &twl6040_codec, .audpwron_gpio = 127, - .irq_base = TWL6040_CODEC_IRQ_BASE, }; /* Panda board uses the common PMIC configuration */ diff --git a/arch/arm/mach-omap2/board-overo.c b/arch/arm/mach-omap2/board-overo.c index 193d1608b39a..4754f051b91a 100644 --- a/arch/arm/mach-omap2/board-overo.c +++ b/arch/arm/mach-omap2/board-overo.c @@ -398,9 +398,6 @@ static int overo_twl_gpio_setup(struct device *dev, } static struct twl4030_gpio_platform_data overo_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .use_leds = true, .setup = overo_twl_gpio_setup, }; diff --git a/arch/arm/mach-omap2/board-rm680.c b/arch/arm/mach-omap2/board-rm680.c index 0ad1bb3bdb98..12411b9fa88c 100644 --- a/arch/arm/mach-omap2/board-rm680.c +++ b/arch/arm/mach-omap2/board-rm680.c @@ -72,9 +72,6 @@ static struct platform_device *rm680_peripherals_devices[] __initdata = { /* TWL */ static struct twl4030_gpio_platform_data rm680_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .pullups = BIT(0), .pulldowns = BIT(1) | BIT(2) | BIT(8) | BIT(15), }; diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c index 3b9fc6105329..e8b6dda85945 100644 --- a/arch/arm/mach-omap2/board-rx51-peripherals.c +++ b/arch/arm/mach-omap2/board-rx51-peripherals.c @@ -773,9 +773,6 @@ static int rx51_twlgpio_setup(struct device *dev, unsigned gpio, unsigned n) } static struct twl4030_gpio_platform_data rx51_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .pulldowns = BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(8) | BIT(9) | BIT(10) | BIT(11) diff --git a/arch/arm/mach-omap2/board-zoom-peripherals.c b/arch/arm/mach-omap2/board-zoom-peripherals.c index b797cb279618..00f73b7792e0 100644 --- a/arch/arm/mach-omap2/board-zoom-peripherals.c +++ b/arch/arm/mach-omap2/board-zoom-peripherals.c @@ -251,9 +251,6 @@ static void zoom2_set_hs_extmute(int mute) } static struct twl4030_gpio_platform_data zoom_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .setup = zoom_twl_gpio_setup, }; diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index 94256fe7bf36..f030880bc9bb 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -51,6 +51,7 @@ static struct gpio_chip twl_gpiochip; +static int twl4030_gpio_base; static int twl4030_gpio_irq_base; /* genirq interfaces are not available to modules */ @@ -428,8 +429,6 @@ no_irqs: twl_gpiochip.dev = &pdev->dev; if (pdata) { - twl_gpiochip.base = pdata->gpio_base; - /* * NOTE: boards may waste power if they don't set pullups * and pulldowns correctly ... default for non-ULPI pins is @@ -461,15 +460,21 @@ no_irqs: dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret); twl_gpiochip.ngpio = 0; gpio_twl4030_remove(pdev); - } else if (pdata && pdata->setup) { + goto out; + } + + twl4030_gpio_base = twl_gpiochip.base; + + if (pdata && pdata->setup) { int status; status = pdata->setup(&pdev->dev, - pdata->gpio_base, TWL4030_GPIO_MAX); + twl4030_gpio_base, TWL4030_GPIO_MAX); if (status) dev_dbg(&pdev->dev, "setup --> %d\n", status); } +out: return ret; } @@ -481,7 +486,7 @@ static int gpio_twl4030_remove(struct platform_device *pdev) if (pdata && pdata->teardown) { status = pdata->teardown(&pdev->dev, - pdata->gpio_base, TWL4030_GPIO_MAX); + twl4030_gpio_base, TWL4030_GPIO_MAX); if (status) { dev_dbg(&pdev->dev, "teardown --> %d\n", status); return status; diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 7ea898c55a60..a12a38107c1a 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -561,9 +561,6 @@ struct twl4030_bci_platform_data { /* TWL4030_GPIO_MAX (18) GPIOs, with interrupts */ struct twl4030_gpio_platform_data { - int gpio_base; - unsigned irq_base, irq_end; - /* package the two LED signals as output-only GPIOs? */ bool use_leds; diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h index eaad49f7c130..ba43d4806b83 100644 --- a/include/linux/mfd/twl6040.h +++ b/include/linux/mfd/twl6040.h @@ -194,7 +194,6 @@ struct twl6040_vibra_data { struct twl6040_platform_data { int audpwron_gpio; /* audio power-on gpio */ - unsigned int irq_base; struct twl6040_codec_data *codec; struct twl6040_vibra_data *vibra; -- cgit v1.2.3 From 4b25408f1f61c35b70a19a41053b5e5e3224e97f Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 30 Aug 2012 15:37:24 -0700 Subject: ARM: OMAP: Move gpio.h to include/linux/platform_data This way we can remove includes of plat/gpio.h which won't work with the single zImage support. Note that we also remove the cpu_class_is_omap2() check in gpio-omap.c as the drivers should not call it as we need to make it local to arch/arm/mach-omap2 for single zImage support. While at it, arrange the related includes in the standard way. Cc: Grant Likely Cc: linux-mtd@lists.infradead.org Cc: alsa-devel@alsa-project.org Acked-by: Linus Walleij Signed-off-by: Tony Lindgren --- arch/arm/mach-omap1/board-ams-delta.c | 1 + arch/arm/mach-omap1/board-osk.c | 1 + arch/arm/mach-omap1/gpio15xx.c | 1 + arch/arm/mach-omap1/gpio16xx.c | 1 + arch/arm/mach-omap1/gpio7xx.c | 1 + arch/arm/mach-omap1/include/mach/gpio.h | 2 - arch/arm/mach-omap1/leds-h2p2-debug.c | 1 + arch/arm/mach-omap1/leds.c | 1 + arch/arm/mach-omap2/board-am3517evm.c | 1 + arch/arm/mach-omap2/board-cm-t35.c | 1 + arch/arm/mach-omap2/board-zoom-display.c | 2 + arch/arm/mach-omap2/board-zoom-peripherals.c | 1 + arch/arm/mach-omap2/gpio.c | 1 + arch/arm/mach-omap2/hsmmc.c | 2 + arch/arm/mach-omap2/include/mach/gpio.h | 2 - arch/arm/mach-omap2/msdi.c | 1 + arch/arm/mach-omap2/omap_hwmod_2420_data.c | 1 - arch/arm/mach-omap2/omap_hwmod_2430_data.c | 1 - arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c | 2 +- arch/arm/mach-omap2/omap_hwmod_3xxx_data.c | 2 +- arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 2 +- arch/arm/mach-omap2/pm24xx.c | 1 + arch/arm/mach-omap2/pm34xx.c | 2 + arch/arm/plat-omap/debug-leds.c | 1 + arch/arm/plat-omap/include/plat/gpio.h | 228 --------------------- drivers/gpio/gpio-omap.c | 15 +- drivers/mtd/nand/ams-delta.c | 8 +- include/linux/platform_data/gpio-omap.h | 217 ++++++++++++++++++++ sound/soc/omap/sdp3430.c | 1 + 29 files changed, 255 insertions(+), 246 deletions(-) delete mode 100644 arch/arm/plat-omap/include/plat/gpio.h create mode 100644 include/linux/platform_data/gpio-omap.h (limited to 'include') diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c index c162369e9f77..6f192c4900b1 100644 --- a/arch/arm/mach-omap1/board-ams-delta.c +++ b/arch/arm/mach-omap1/board-ams-delta.c @@ -26,6 +26,7 @@ #include #include #include +#include #include diff --git a/arch/arm/mach-omap1/board-osk.c b/arch/arm/mach-omap1/board-osk.c index 569b6872a2f6..3b2d9071022a 100644 --- a/arch/arm/mach-omap1/board-osk.c +++ b/arch/arm/mach-omap1/board-osk.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include diff --git a/arch/arm/mach-omap1/gpio15xx.c b/arch/arm/mach-omap1/gpio15xx.c index ebef15e5e7b7..98e6f39224a4 100644 --- a/arch/arm/mach-omap1/gpio15xx.c +++ b/arch/arm/mach-omap1/gpio15xx.c @@ -17,6 +17,7 @@ */ #include +#include #define OMAP1_MPUIO_VBASE OMAP1_MPUIO_BASE #define OMAP1510_GPIO_BASE 0xFFFCE000 diff --git a/arch/arm/mach-omap1/gpio16xx.c b/arch/arm/mach-omap1/gpio16xx.c index 2a48cd2e1754..33f419236b17 100644 --- a/arch/arm/mach-omap1/gpio16xx.c +++ b/arch/arm/mach-omap1/gpio16xx.c @@ -17,6 +17,7 @@ */ #include +#include #define OMAP1610_GPIO1_BASE 0xfffbe400 #define OMAP1610_GPIO2_BASE 0xfffbec00 diff --git a/arch/arm/mach-omap1/gpio7xx.c b/arch/arm/mach-omap1/gpio7xx.c index acf12b73eace..958ce9acee95 100644 --- a/arch/arm/mach-omap1/gpio7xx.c +++ b/arch/arm/mach-omap1/gpio7xx.c @@ -17,6 +17,7 @@ */ #include +#include #define OMAP7XX_GPIO1_BASE 0xfffbc000 #define OMAP7XX_GPIO2_BASE 0xfffbc800 diff --git a/arch/arm/mach-omap1/include/mach/gpio.h b/arch/arm/mach-omap1/include/mach/gpio.h index e737706a8fe1..ebf86c0f4f46 100644 --- a/arch/arm/mach-omap1/include/mach/gpio.h +++ b/arch/arm/mach-omap1/include/mach/gpio.h @@ -1,5 +1,3 @@ /* * arch/arm/mach-omap1/include/mach/gpio.h */ - -#include diff --git a/arch/arm/mach-omap1/leds-h2p2-debug.c b/arch/arm/mach-omap1/leds-h2p2-debug.c index f6b14a14a957..7f4bba9fa02e 100644 --- a/arch/arm/mach-omap1/leds-h2p2-debug.c +++ b/arch/arm/mach-omap1/leds-h2p2-debug.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include diff --git a/arch/arm/mach-omap1/leds.c b/arch/arm/mach-omap1/leds.c index ae6dd93b8ddc..7b1a3833165d 100644 --- a/arch/arm/mach-omap1/leds.c +++ b/arch/arm/mach-omap1/leds.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include diff --git a/arch/arm/mach-omap2/board-am3517evm.c b/arch/arm/mach-omap2/board-am3517evm.c index 00abda138022..029ebdf70c43 100644 --- a/arch/arm/mach-omap2/board-am3517evm.c +++ b/arch/arm/mach-omap2/board-am3517evm.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include diff --git a/arch/arm/mach-omap2/board-cm-t35.c b/arch/arm/mach-omap2/board-cm-t35.c index ea3410953d2c..34cb90471d96 100644 --- a/arch/arm/mach-omap2/board-cm-t35.c +++ b/arch/arm/mach-omap2/board-cm-t35.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include diff --git a/arch/arm/mach-omap2/board-zoom-display.c b/arch/arm/mach-omap2/board-zoom-display.c index 28187f134fff..d64f35fd75f5 100644 --- a/arch/arm/mach-omap2/board-zoom-display.c +++ b/arch/arm/mach-omap2/board-zoom-display.c @@ -18,6 +18,8 @@ #include