The 3.2.65-1+deb7u2 kernel is no longer used.

This commit is contained in:
Jeffrey Townsend
2017-02-22 02:25:55 +00:00
parent 6b727f4dd1
commit 48f4db30d9
279 changed files with 1 additions and 271675 deletions

View File

@@ -19,25 +19,13 @@ default:
# this is mostly to *reject* invalid disk labels,
# since we will never create our own
kernel-3.2: &kernel-3-2
=: kernel-3.2-deb7-x86_64-all
package: onl-kernel-3.2-deb7-x86-64-all:amd64
kernel-3.9.6: &kernel-3-9-6
=: kernel-3.9.6-x86-64-all
package: onl-kernel-3.9.6-x86-64-all:amd64
kernel-3.18: &kernel-3-18
=: kernel-3.18-x86_64-all
package: onl-kernel-3.18-x86-64-all:amd64
kernel-3.16: &kernel-3-16
=: kernel-3.16-lts-x86_64-all
package: onl-kernel-3.16-lts-x86-64-all:amd64
# pick one of the above kernels
kernel:
<<: *kernel-3-2
<<: *kernel-3-16
# GRUB command line arguments for 'serial' declaration
# this is equivalent to, but not in the same format as,

View File

@@ -1,4 +0,0 @@
linux-3.2.65-1+deb7u2
linux-3.2.65-1+deb7u2-mbuild
linux-3.2.65-1+deb7u2-dtbs

View File

@@ -1 +0,0 @@
kernel-3.2-deb7-powerpc-e500-all*

View File

@@ -1,44 +0,0 @@
############################################################
# <bsn.cl fy=2015 v=onl>
#
# Copyright 2015 Big Switch Networks, Inc.
#
# Licensed under the Eclipse Public License, Version 1.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.eclipse.org/legal/epl-v10.html
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
# either express or implied. See the License for the specific
# language governing permissions and limitations under the
# License.
#
# </bsn.cl>
############################################################
#
# Default 3.2.65-1+deb7u2 configuration for PowerPC e500v platforms.
#
############################################################
THIS_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
include $(ONL)/make/config.mk
ifndef K_TARGET_DIR
K_TARGET_DIR := $(THIS_DIR)
endif
include ../../kconfig.mk
K_CONFIG := powerpc-e500v-all.config
K_BUILD_TARGET := uImage
K_COPY_SRC := vmlinux.bin.gz
ifndef K_COPY_DST
K_COPY_DST := kernel-3.2-deb7-powerpc-e500-all.bin.gz
endif
export ARCH=powerpc
DTS_LIST := powerpc-quanta-lb9-r0 powerpc-quanta-ly2-r0 powerpc-accton-as4600-54t-r0 powerpc-accton-as5610-52x-r0 powerpc-dni-7448-r0 powerpc-dell-s4810-p2020-r0
include $(ONL)/make/kbuild.mk

View File

@@ -1 +0,0 @@
kernel-3.2-deb7-x86_64-all

View File

@@ -1,42 +0,0 @@
############################################################
# <bsn.cl fy=2015 v=onl>
#
# Copyright 2015 Big Switch Networks, Inc.
#
# Licensed under the Eclipse Public License, Version 1.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.eclipse.org/legal/epl-v10.html
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
# either express or implied. See the License for the specific
# language governing permissions and limitations under the
# License.
#
# </bsn.cl>
############################################################
#
# Default 3.2.65-1+deb7u2 configuration for x86_64 platforms.
#
############################################################
THIS_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
include $(ONL)/make/config.mk
export ARCH := x86_64
ifndef K_TARGET_DIR
K_TARGET_DIR := $(THIS_DIR)
endif
include ../../kconfig.mk
K_CONFIG := x86_64-all.config
K_BUILD_TARGET := bzImage
K_COPY_SRC := arch/x86/boot/bzImage
ifndef K_COPY_DST
K_COPY_DST := kernel-3.2-deb7-x86_64-all
endif
include $(ONL)/make/kbuild.mk

View File

@@ -1,31 +0,0 @@
############################################################
# <bsn.cl fy=2015 v=onl>
#
# Copyright 2015 Big Switch Networks, Inc.
#
# Licensed under the Eclipse Public License, Version 1.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.eclipse.org/legal/epl-v10.html
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
# either express or implied. See the License for the specific
# language governing permissions and limitations under the
# License.
#
# </bsn.cl>
############################################################
#
# 3.2.65-1+deb7u2 Kernel Builds
#
############################################################
THIS_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
K_MAJOR_VERSION := 3
K_PATCH_LEVEL := 2
K_SUB_LEVEL := 65
K_SUFFIX := -1+deb7u2
K_PATCH_DIR := $(THIS_DIR)/patches
K_ARCHIVE_URL := http://opennetlinux.org/tarballs/linux-3.2.65-1+deb7u2.tar.xz

View File

@@ -1,75 +0,0 @@
diff -urpN a/include/linux/mm.h b/include/linux/mm.h
--- a/include/linux/mm.h 2016-11-02 14:46:33.278862661 -0700
+++ b/include/linux/mm.h 2016-11-02 14:47:01.338863270 -0700
@@ -1526,6 +1526,7 @@ struct page *follow_page(struct vm_area_
#define FOLL_MLOCK 0x40 /* mark page as mlocked */
#define FOLL_SPLIT 0x80 /* don't return transhuge pages, split them */
#define FOLL_HWPOISON 0x100 /* check page is hwpoisoned */
+#define FOLL_COW 0x4000 /* internal GUP flag */
typedef int (*pte_fn_t)(pte_t *pte, pgtable_t token, unsigned long addr,
void *data);
diff -urpN a/mm/memory.c b/mm/memory.c
--- a/mm/memory.c 2016-11-02 14:46:33.938862676 -0700
+++ b/mm/memory.c 2016-11-02 14:50:52.086868277 -0700
@@ -1427,6 +1427,23 @@ int zap_vma_ptes(struct vm_area_struct *
}
EXPORT_SYMBOL_GPL(zap_vma_ptes);
+static inline bool can_follow_write_pte(pte_t pte, struct page *page,
+ unsigned int flags)
+{
+ if (pte_write(pte))
+ return true;
+
+ /*
+ * Make sure that we are really following CoWed page. We do not really
+ * have to care about exclusiveness of the page because we only want
+ * to ensure that once COWed page hasn't disappeared in the meantime
+ * or it hasn't been merged to a KSM page.
+ */
+ if ((flags & FOLL_FORCE) && (flags & FOLL_COW))
+ return page && PageAnon(page) && !PageKsm(page);
+
+ return false;
+}
/**
* follow_page - look up a page descriptor from a user-virtual address
* @vma: vm_area_struct mapping @address
@@ -1509,10 +1526,12 @@ split_fallthrough:
pte = *ptep;
if (!pte_present(pte))
goto no_page;
- if ((flags & FOLL_WRITE) && !pte_write(pte))
- goto unlock;
page = vm_normal_page(vma, address, pte);
+ if ((flags & FOLL_WRITE) && !can_follow_write_pte(pte, page, flags)) {
+ pte_unmap_unlock(ptep, ptl);
+ return NULL;
+ }
if (unlikely(!page)) {
if ((flags & FOLL_DUMP) ||
!is_zero_pfn(pte_pfn(pte)))
@@ -1789,17 +1808,13 @@ int __get_user_pages(struct task_struct
* The VM_FAULT_WRITE bit tells us that
* do_wp_page has broken COW when necessary,
* even if maybe_mkwrite decided not to set
- * pte_write. We can thus safely do subsequent
- * page lookups as if they were reads. But only
- * do so when looping for pte_write is futile:
- * in some cases userspace may also be wanting
- * to write to the gotten user page, which a
- * read fault here might prevent (a readonly
- * page might get reCOWed by userspace write).
+ * pte_write. We cannot simply drop FOLL_WRITE
+ * here because the COWed page might be gone by
+ * the time we do the subsequent page lookups.
*/
if ((ret & VM_FAULT_WRITE) &&
!(vma->vm_flags & VM_WRITE))
- foll_flags &= ~FOLL_WRITE;
+ foll_flags |= FOLL_COW;
cond_resched();
}

View File

@@ -1,17 +0,0 @@
############################################################
#
# Debian 3.2 Kernel Patches
#
############################################################
#
# The majority of these patches were imported from
# the Cumulus OSS Repository, version 2.5.1, for the Debian 3.2 kernel
# distributed with Wheezy.
#
# http://oss.cumulusnetworks.com/CumulusLinux-2.5.1
#
# See the kernel patch directory:
#
# http://oss.cumulusnetworks.com/CumulusLinux-2.5.1/patches/kernel
#
############################################################

View File

@@ -1,36 +0,0 @@
Kernel stgit notes
==================
See also: https://wiki.cumulusnetworks.com/display/DE/Linux+Kernel+stgit+patches
The Linux kernel stg patches are broken up into categories. The patch
name is prefixed with the category.
When creating a new patch please put it into an existing category. If
you really need a new category open a discussion.
Categories:
Prefix Meaning
=============+===========
git- Specific to git, e.g. the standard gitignore.patch.
arch-powerpc- PowerPC CPU patches. Not tied to a specific platform.
arch-intel- Intel CPU patches. Not tied to a specific platform.
kernel- Kernel features, like file systems, back ported items.
driver- Device drivers. Not tied to a specific platform.
platform- Switching hardware platforms, like DNI-7448.
network- Networking, routing, bridging, tun, ipv4, ipv6, etc.
platform- Category Specific Notes
=================================
Within this category the platforms are stored alphabetically.
One patch, platform-powerpc-85xx-Makefile.patch, is used by all the
powerpc platforms. This patch contains changes to
arch/powerpc/platforms/85xx/Makefile and
arch/powerpc/platforms/85xx/Kconfig.
By keeping these changes in a separate patch allows the platform
specific patchsets to sink/float without conflicts in these two files.

View File

@@ -1,20 +0,0 @@
arch-intel-centerton-pci-id.patch
Add the PCI device ID for the Intel Centerton Integrated Legacy Block
(ILB).
This device ID is used by a number of drivers, including GPIO and
Multifunction Devices.
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index d93f417..d35b1f4 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2484,6 +2484,7 @@
#define PCI_DEVICE_ID_INTEL_MRST_SD2 0x084F
#define PCI_DEVICE_ID_INTEL_I960 0x0960
#define PCI_DEVICE_ID_INTEL_I960RM 0x0962
+#define PCI_DEVICE_ID_INTEL_CENTERTON_ILB 0x0c60
#define PCI_DEVICE_ID_INTEL_8257X_SOL 0x1062
#define PCI_DEVICE_ID_INTEL_82573E_SOL 0x1085
#define PCI_DEVICE_ID_INTEL_82573L_SOL 0x108F

View File

@@ -1,91 +0,0 @@
Patch to reboot x86_64 machines using boot code CF9
diff --git a/arch/x86/include/asm/emergency-restart.h b/arch/x86/include/asm/emergency-restart.h
index cc70c1c..441a910 100644
--- a/arch/x86/include/asm/emergency-restart.h
+++ b/arch/x86/include/asm/emergency-restart.h
@@ -11,6 +11,7 @@ enum reboot_type {
BOOT_EFI = 'e',
BOOT_CF9 = 'p',
BOOT_CF9_COND = 'q',
+ BOOT_CF9_COLD = 'd',
};
extern enum reboot_type reboot_type;
diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c
index f411aca..a10547c 100644
--- a/arch/x86/kernel/reboot.c
+++ b/arch/x86/kernel/reboot.c
@@ -93,6 +93,7 @@ static int __init reboot_setup(char *str)
case 'b':
#endif
case 'a':
+ case 'd':
case 'k':
case 't':
case 'e':
@@ -395,6 +396,16 @@ static int __init set_pci_reboot(const struct dmi_system_id *d)
return 0;
}
+static int __init set_cold_cf9_reboot(const struct dmi_system_id *d)
+{
+ if (reboot_type != BOOT_CF9_COLD) {
+ reboot_type = BOOT_CF9_COLD;
+ printk(KERN_INFO "%s series board detected. "
+ "Selecting CF9 Cold Reset method for reboots.\n", d->ident);
+ }
+ return 0;
+}
+
static struct dmi_system_id __initdata pci_reboot_dmi_table[] = {
{ /* Handle problems with rebooting on Apple MacBook5 */
.callback = set_pci_reboot,
@@ -468,6 +479,23 @@ static struct dmi_system_id __initdata pci_reboot_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Precision M6600"),
},
},
+ { /* Perform cold reset on Dell S6000 */
+ .callback = set_cold_cf9_reboot,
+ .ident = "Dell S6000",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "S6000"),
+ },
+ },
+ { /* Dell S6000 could have either manufacturer names: Dell Inc or
+ Dell Force10 Networks. Accomodating for both */
+ .callback = set_cold_cf9_reboot,
+ .ident = "Dell S6000",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Force10 Networks"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "S6000"),
+ },
+ },
{ /* Handle problems with rebooting on the Dell PowerEdge C6100. */
.callback = set_pci_reboot,
.ident = "Dell PowerEdge C6100",
@@ -569,6 +597,7 @@ static void native_machine_emergency_restart(void)
int i;
int attempt = 0;
int orig_reboot_type = reboot_type;
+ u8 cf9_cold = 0;
if (reboot_emergency)
emergency_vmx_disable_all();
@@ -641,6 +670,15 @@ static void native_machine_emergency_restart(void)
}
reboot_type = BOOT_KBD;
break;
+
+ case BOOT_CF9_COLD:
+ cf9_cold = inb(0xcf9) & ~6;
+ outb(cf9_cold|8, 0xcf9); /* Request cold reset */
+ udelay(50);
+ outb(cf9_cold|12, 0xcf9); /* Actually do the reset */
+ udelay(50);
+ reboot_type = BOOT_KBD;
+ break;
}
}
}

View File

@@ -1,121 +0,0 @@
#Copyright 2014 Cumulus Networks, Inc. All rights reserved.
Have the OS set up all PCI device maxpayload and maxreadrequest on the PCIe bus
Notes:
1. This is similar to the payload size fixup done for powerpc.
Ref: arch-powerpc-os-driven-pci-maxpayload-readreq-setup.patch
2. pci=pcie_bus_safe is supposed to fix up the entire tree below a root complex
with the smallest common denominator MPS. However it is only setting the
devices associated with the children (child buses) of the root. And that leaves
some of the devices on the root with a different (and in some cases lower MPS
based on the bios setting).
2. This patch works by walking the bus underneath each PCIe controller,
querying PCI_EXP_DEVCAP_PAYLOAD, and finding the minimum value underneath each
controller. Then it walks though the same set of devices setting
PCI_EXP_DEVCTL_PAYLOAD and PCI_EXP_DEVCTL_READRQ to this value. The payloads
are powers of 2 starting at 128...
0 -> 128
1 -> 256
...
7 -> 16,384:1
diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c
index 0ed97d8..f1a970f 100644
--- a/arch/x86/pci/acpi.c
+++ b/arch/x86/pci/acpi.c
@@ -329,6 +329,66 @@ res_alloc_fail:
return;
}
+/*
+ * scan and set the PCIe bus payload and read request sizes.
+ */
+static int __devinit __fixup_pcie_scan_payload(struct pci_dev *pdev,
+ void *data)
+{
+ int *payload_size = data;
+ int rval, cap, payload_cap;
+ uint32_t devcap;
+
+ if (!pci_is_pcie(pdev))
+ return 0;
+
+ cap = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+ if (cap == 0)
+ return -ENODEV;
+
+ rval = pci_read_config_dword(pdev, cap + PCI_EXP_DEVCAP, &devcap);
+ if (rval)
+ return rval;
+
+ payload_cap = devcap & PCI_EXP_DEVCAP_PAYLOAD;
+ if (payload_cap < *payload_size)
+ *payload_size = payload_cap;
+
+ return 0;
+}
+
+static int __devinit __fixup_pcie_set_payload(struct pci_dev *pdev,
+ void *data)
+{
+ int *payload_size = data;
+ int rval, cap;
+ uint16_t devctl;
+
+ if (!pci_is_pcie(pdev))
+ return 0;
+
+ cap = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+ if (cap == 0)
+ return -ENODEV;
+
+ rval = pci_read_config_word(pdev, cap + PCI_EXP_DEVCTL, &devctl);
+ if (rval)
+ return rval;
+
+ devctl &= ~PCI_EXP_DEVCTL_PAYLOAD;
+ devctl |= *payload_size << 5;
+
+ devctl &= ~PCI_EXP_DEVCTL_READRQ;
+ devctl |= *payload_size << 12;
+
+ rval = pci_write_config_word(pdev, cap + PCI_EXP_DEVCTL, devctl);
+ if (rval)
+ return rval;
+
+ return 0;
+}
+
+
struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root)
{
struct acpi_device *device = root->device;
@@ -395,6 +455,28 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root)
}
}
+/* Set the payload size for all devices on the bus */
+ if (bus) {
+ int payload_size;
+ payload_size = PCI_EXP_DEVCAP_PAYLOAD;
+ pci_walk_bus(bus, __fixup_pcie_scan_payload, &payload_size);
+ if (payload_size < PCI_EXP_DEVCAP_PAYLOAD) {
+ pci_walk_bus(bus, __fixup_pcie_set_payload,
+ &payload_size);
+ dev_info(&bus->dev,
+ "Set PCIe payload and read request to %d\n",
+ (payload_size == 0) ? 128 :
+ (payload_size == 1) ? 256 :
+ (payload_size == 2) ? 512 :
+ (payload_size == 3) ? 1024 :
+ (payload_size == 4) ? 2048 :
+ (payload_size == 5) ? 4096 :
+ (payload_size == 6) ? 8192 :
+ 16384
+ );
+ }
+ }
+
/* After the PCI-E bus has been walked and all devices discovered,
* configure any settings of the fabric that might be necessary.
*/

View File

@@ -1,23 +0,0 @@
--- a/arch/x86/kernel/reboot.c 2016-06-01 13:19:53.102025910 -0700
+++ b/arch/x86/kernel/reboot.c 2016-06-01 13:21:04.250027454 -0700
@@ -672,13 +672,13 @@ static void native_machine_emergency_res
break;
case BOOT_CF9_COLD:
- cf9_cold = inb(0xcf9) & ~6;
- outb(cf9_cold|8, 0xcf9); /* Request cold reset */
- udelay(50);
- outb(cf9_cold|12, 0xcf9); /* Actually do the reset */
- udelay(50);
- reboot_type = BOOT_KBD;
- break;
+ cf9_cold = inb(0xcf9) & ~0xE;
+ outb(cf9_cold|2, 0xcf9); /* Request cold reset */
+ udelay(50);
+ outb(cf9_cold|0xE, 0xcf9); /* Actually do the reset */
+ udelay(50);
+ reboot_type = BOOT_KBD;
+ break;
}
}
}

View File

@@ -1,15 +0,0 @@
diff --git a/arch/x86/include/asm/gpio.h b/arch/x86/include/asm/gpio.h
index 91d915a..31ce3f7 100644
--- a/arch/x86/include/asm/gpio.h
+++ b/arch/x86/include/asm/gpio.h
@@ -16,6 +16,8 @@
#ifndef _ASM_X86_GPIO_H
#define _ASM_X86_GPIO_H
+#define ARCH_NR_GPIOS 512
+
#include <asm-generic/gpio.h>
#ifdef CONFIG_GPIOLIB

View File

@@ -1,89 +0,0 @@
Fix missing L2 cache in /sys/devices/system/cpu/cpu0/cache/index2/size
This appears to have been introduced in 2.6.29 by
93197a36a9c16a85fb24cf5a8639f7bf9af838a3.
This caused lscpu to error out on e500v2 devices, and probably others
error: cannot open /sys/devices/system/cpu/cpu0/cache/index2/size: No such file or directory
Some embedded powerpc sysystems use cache-size in DTS for the unified L2 cache
size, not d-cache-size, so we need to allow for both DTS names. Added a
second CACHE_TYPE_UNIFIED_D cache_type_info structure to handle this.
This is a redo after trying to push the previous version upstream, and finding out
that the previous patch broke OpenFirmware PowerPC systems like the Mac. This also
now includes all descriptive entries in the index2 directory; the previous still had
some missing.
diff --git a/arch/powerpc/kernel/cacheinfo.c b/arch/powerpc/kernel/cacheinfo.c
index b4437e8..592b096 100644
--- a/arch/powerpc/kernel/cacheinfo.c
+++ b/arch/powerpc/kernel/cacheinfo.c
@@ -62,12 +62,22 @@ struct cache_type_info {
};
/* These are used to index the cache_type_info array. */
-#define CACHE_TYPE_UNIFIED 0
-#define CACHE_TYPE_INSTRUCTION 1
-#define CACHE_TYPE_DATA 2
+#define CACHE_TYPE_UNIFIED 0 /* cache-size, cache-block-size, etc. */
+#define CACHE_TYPE_UNIFIED_D 1 /* d-cache-size, d-cache-block-size, etc */
+#define CACHE_TYPE_INSTRUCTION 2
+#define CACHE_TYPE_DATA 3
static const struct cache_type_info cache_type_info[] = {
{
+ /* Embedded systems that use cache-size, cache-block-size,
+ * etc. for the Unified (typically L2) cache. */
+ .name = "Unified",
+ .size_prop = "cache-size",
+ .line_size_props = { "cache-line-size",
+ "cache-block-size", },
+ .nr_sets_prop = "cache-sets",
+ },
+ {
/* PowerPC Processor binding says the [di]-cache-*
* must be equal on unified caches, so just use
* d-cache properties. */
@@ -293,7 +303,8 @@ static struct cache *cache_find_first_sibling(struct cache *cache)
{
struct cache *iter;
- if (cache->type == CACHE_TYPE_UNIFIED)
+ if (cache->type == CACHE_TYPE_UNIFIED ||
+ cache->type == CACHE_TYPE_UNIFIED_D)
return cache;
list_for_each_entry(iter, &cache_list, list)
@@ -324,13 +335,31 @@ static bool cache_node_is_unified(const struct device_node *np)
return of_get_property(np, "cache-unified", NULL);
}
+/*
+ * Handle unified caches that have two different types of tags. Most embedded
+ * use cache-size, etc. for the unified cache size, but open firmware systems
+ * use d-cache-size, etc. Since they all appear to be consistent, check on
+ * initialization for which type we are, and use the appropriate structure.
+ */
static struct cache *__cpuinit cache_do_one_devnode_unified(struct device_node *node, int level)
{
struct cache *cache;
+ int ucache;
pr_debug("creating L%d ucache for %s\n", level, node->full_name);
- cache = new_cache(CACHE_TYPE_UNIFIED, level, node);
+ if (of_get_property(node,
+ cache_type_info[CACHE_TYPE_UNIFIED_D].size_prop, NULL)) {
+ ucache = CACHE_TYPE_UNIFIED_D;
+ } else {
+ ucache = CACHE_TYPE_UNIFIED; /* assume embedded */
+ if (of_get_property(node,
+ cache_type_info[CACHE_TYPE_UNIFIED].size_prop, NULL) ==
+ NULL)
+ printk(KERN_WARNING "Unified cache property missing\n");
+ }
+
+ cache = new_cache(ucache, level, node);
return cache;
}

View File

@@ -1,45 +0,0 @@
#Copyright 2012 Cumulus Networks, Inc. All rights reserved.
Emulate the 603 lwsync instruction
Issue a memory barrier (mbar) instead of lwsync on e500 processors. This, along with SW emuluation of the FPU allow us to run PPC603 code from the Debian repositories directly on the e500 cores.
diff --git a/arch/powerpc/include/asm/emulated_ops.h b/arch/powerpc/include/asm/emulated_ops.h
index 63f2a22..093359a 100644
--- a/arch/powerpc/include/asm/emulated_ops.h
+++ b/arch/powerpc/include/asm/emulated_ops.h
@@ -44,6 +44,7 @@ extern struct ppc_emulated {
struct ppc_emulated_entry spe;
struct ppc_emulated_entry string;
struct ppc_emulated_entry unaligned;
+ struct ppc_emulated_entry lwsync;
#ifdef CONFIG_MATH_EMULATION
struct ppc_emulated_entry math;
#elif defined(CONFIG_8XX_MINIMAL_FPEMU)
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index 9844662..a689b66 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -928,6 +928,14 @@ static int emulate_instruction(struct pt_regs *regs)
return emulate_isel(regs, instword);
}
+ /* Emulate lwsync (Lightweight Sync) instruction */
+ if (instword == PPC_INST_LWSYNC) {
+ PPC_WARN_EMULATED(lwsync, regs);
+ /* This is probably more pessimistic than required */
+ mb();
+ return 0;
+ }
+
#ifdef CONFIG_PPC64
/* Emulate the mfspr rD, DSCR. */
if (((instword & PPC_INST_MFSPR_DSCR_MASK) == PPC_INST_MFSPR_DSCR) &&
@@ -1542,6 +1550,7 @@ struct ppc_emulated ppc_emulated = {
WARN_EMULATED_SETUP(spe),
WARN_EMULATED_SETUP(string),
WARN_EMULATED_SETUP(unaligned),
+ WARN_EMULATED_SETUP(lwsync),
#ifdef CONFIG_MATH_EMULATION
WARN_EMULATED_SETUP(math),
#elif defined(CONFIG_8XX_MINIMAL_FPEMU)

View File

@@ -1,59 +0,0 @@
#Copyright 2012 Cumulus Networks, Inc. All rights reserved.
Tweak the kernel to support a JTAG hardware debugger
- disable the software watchdog
- enable Debug Interrupt in the PPC Machine State Register
diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug
index 1b8a9c9..aba8704 100644
--- a/arch/powerpc/Kconfig.debug
+++ b/arch/powerpc/Kconfig.debug
@@ -132,6 +132,15 @@ config BDI_SWITCH
Unless you are intending to debug the kernel with one of these
machines, say N here.
+config JTAG_DEBUGGER
+ bool "Kernel modifications for JTAG debuggers"
+ depends on DEBUG_KERNEL && PPC32
+ help
+ Modify kernel to allow JTAG debuggers to work. So far, this includes
+ setting the "DE" bit in booke ppc MSR_KERNEL and set the kernel's
+ softlockup threshold to be negative, effectively disabling software based
+ watchdogs.
+
config BOOTX_TEXT
bool "Support for early boot text console (BootX or OpenFirmware only)"
depends on PPC_OF && PPC_BOOK3S
diff --git a/arch/powerpc/include/asm/reg_booke.h b/arch/powerpc/include/asm/reg_booke.h
index 03c48e8..cd7b5af 100644
--- a/arch/powerpc/include/asm/reg_booke.h
+++ b/arch/powerpc/include/asm/reg_booke.h
@@ -37,7 +37,11 @@
#define MSR_KERNEL (MSR_ME|MSR_RI|MSR_IR|MSR_DR|MSR_CE)
#define MSR_USER (MSR_KERNEL|MSR_PR|MSR_EE)
#else
+#if defined(CONFIG_JTAG_DEBUGGER)
+#define MSR_KERNEL (MSR_ME|MSR_RI|MSR_CE|MSR_DE)
+#else
#define MSR_KERNEL (MSR_ME|MSR_RI|MSR_CE)
+#endif
#define MSR_USER (MSR_KERNEL|MSR_PR|MSR_EE)
#endif
diff --git a/kernel/watchdog.c b/kernel/watchdog.c
index a8bc4d9..08beea7 100644
--- a/kernel/watchdog.c
+++ b/kernel/watchdog.c
@@ -28,7 +28,11 @@
#include <linux/perf_event.h>
int watchdog_enabled = 1;
+#if defined(CONFIG_JTAG_DEBUGGER)
+int __read_mostly watchdog_thresh = -10;
+#else
int __read_mostly watchdog_thresh = 10;
+#endif
static DEFINE_PER_CPU(unsigned long, watchdog_touch_ts);
static DEFINE_PER_CPU(struct task_struct *, softlockup_watchdog);

View File

@@ -1,126 +0,0 @@
#Copyright 2012 Cumulus Networks, Inc. All rights reserved.
Have the OS set up all PCI device maxpayload and maxreadrequest on the PCIe bus
This version of the linux kernel doesn't do anything regarding the PCIe max
payload size or read request size, but rather rely on BIOS to take care of
things. Upcoming versions of the kernel patch core PCI code in cool and useful
ways, but there is a lot of flux, every version I've looked at has a different
take on this.
Rather than patching PCI core code, I've chosen to patch this in an arch
dependent manner (powerpc only) that will not conflict with any work at the PCI
core (redundant, but no functional or merge conflicts). We can remove this
patch once the PCI core code settles down.
The patch works by walking the bus underneath each PCIe controller, querying
PCI_EXP_DEVCAP_PAYLOAD, and finding the minimum value underneath each
controller. Then we walk though the same set of devices setting
PCI_EXP_DEVCTL_PAYLOAD and PCI_EXP_DEVCTL_READRQ to this value. The payloads
are powers of 2 starting at 128...
0 -> 128
1 -> 256
...
7 -> 16,384
diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c
index 9508bec..abb9e95 100644
--- a/arch/powerpc/kernel/pci-common.c
+++ b/arch/powerpc/kernel/pci-common.c
@@ -1697,6 +1697,62 @@ struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus)
return of_node_get(hose->dn);
}
+/*
+ * scan and set the PCIe bus payload and read request sizes.
+ */
+static int __devinit __fixup_pcie_scan_payload(struct pci_dev * pdev,
+ void * data)
+{
+ int * payload_size = data;
+ int rval, cap, payload_cap;
+ uint32_t devcap;
+
+ cap = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+ if (cap == 0) {
+ return -ENODEV;
+ }
+
+ rval = pci_read_config_dword(pdev, cap + PCI_EXP_DEVCAP, &devcap);
+ if (rval) {
+ return rval;
+ }
+ payload_cap = devcap & PCI_EXP_DEVCAP_PAYLOAD;
+ if (payload_cap < *payload_size) {
+ *payload_size = payload_cap;
+ }
+ return 0;
+}
+
+static int __devinit __fixup_pcie_set_payload(struct pci_dev * pdev,
+ void * data)
+{
+ int * payload_size = data;
+ int rval, cap;
+ uint16_t devctl;
+
+ cap = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+ if (cap == 0) {
+ return -ENODEV;
+ }
+
+ rval = pci_read_config_word(pdev, cap + PCI_EXP_DEVCTL, &devctl);
+ if (rval) {
+ return rval;
+ }
+
+ devctl &= ~PCI_EXP_DEVCTL_PAYLOAD;
+ devctl |= *payload_size << 5;
+
+ devctl &= ~PCI_EXP_DEVCTL_READRQ;
+ devctl |= *payload_size << 12;
+
+ rval = pci_write_config_word(pdev, cap + PCI_EXP_DEVCTL, devctl);
+ if (rval) {
+ return rval;
+ }
+ return 0;
+}
+
/**
* pci_scan_phb - Given a pci_controller, setup and scan the PCI bus
* @hose: Pointer to the PCI host controller instance structure
@@ -1706,6 +1762,7 @@ void __devinit pcibios_scan_phb(struct pci_controller *hose)
struct pci_bus *bus;
struct device_node *node = hose->dn;
int mode;
+ int payload_size;
pr_debug("PCI: Scanning PHB %s\n",
node ? node->full_name : "<NO NAME>");
@@ -1739,6 +1796,24 @@ void __devinit pcibios_scan_phb(struct pci_controller *hose)
if (mode == PCI_PROBE_NORMAL)
hose->last_busno = bus->subordinate = pci_scan_child_bus(bus);
+ /* Set the payload size for all devices on the bus */
+ payload_size = PCI_EXP_DEVCAP_PAYLOAD;
+ pci_walk_bus(hose->bus, __fixup_pcie_scan_payload, &payload_size);
+ if (payload_size < PCI_EXP_DEVCAP_PAYLOAD) {
+ pci_walk_bus(hose->bus, __fixup_pcie_set_payload, &payload_size);
+ dev_info(&hose->bus->dev,
+ "Set PCIe payload and read request to %d\n",
+ (payload_size == 0) ? 128 :
+ (payload_size == 1) ? 256 :
+ (payload_size == 2) ? 512 :
+ (payload_size == 3) ? 1024 :
+ (payload_size == 4) ? 2048 :
+ (payload_size == 5) ? 4096 :
+ (payload_size == 6) ? 8192 :
+ 16384
+ );
+ }
+
/* Configure PCI Express settings */
if (bus && !pci_has_flag(PCI_PROBE_ONLY)) {
struct pci_bus *child;

View File

@@ -1,33 +0,0 @@
From e5ae2bb5c7cf0389f0333e5d36be193e7738f4e9 Mon Sep 17 00:00:00 2001
Subject: [PATCH 2/2] powerpc: remove arch specific overrides of panic_timeout
Signed-off-by: Jonathan Toppins <jtoppins@cumulusnetworks.com>
diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c
index ac76108..287d120 100644
--- a/arch/powerpc/kernel/setup_32.c
+++ b/arch/powerpc/kernel/setup_32.c
@@ -317,9 +317,6 @@ void __init setup_arch(char **cmdline_p)
if (cpu_has_feature(CPU_FTR_UNIFIED_ID_CACHE))
ucache_bsize = icache_bsize = dcache_bsize;
- /* reboot on panic */
- panic_timeout = 180;
-
if (ppc_md.panic)
setup_panic();
diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c
index 2c8890a..38533f2 100644
--- a/arch/powerpc/kernel/setup_64.c
+++ b/arch/powerpc/kernel/setup_64.c
@@ -554,9 +554,6 @@ void __init setup_arch(char **cmdline_p)
dcache_bsize = ppc64_caches.dline_size;
icache_bsize = ppc64_caches.iline_size;
- /* reboot on panic */
- panic_timeout = 180;
-
if (ppc_md.panic)
setup_panic();

View File

@@ -1,63 +0,0 @@
Fix PPC toology macros
diff --git a/arch/powerpc/include/asm/topology.h b/arch/powerpc/include/asm/topology.h
index c971858..eefc7df 100644
--- a/arch/powerpc/include/asm/topology.h
+++ b/arch/powerpc/include/asm/topology.h
@@ -124,13 +124,15 @@ static inline int stop_topology_update(void)
#include <asm/cputable.h>
#define smt_capable() (cpu_has_feature(CPU_FTR_SMT))
-#ifdef CONFIG_PPC64
#include <asm/smp.h>
-#define topology_thread_cpumask(cpu) (per_cpu(cpu_sibling_map, cpu))
-#define topology_core_cpumask(cpu) (per_cpu(cpu_core_map, cpu))
-#define topology_core_id(cpu) (cpu_to_core_id(cpu))
+#ifdef CONFIG_PPC64
+#define topology_thread_cpumask(cpu) (per_cpu(cpu_sibling_map, cpu))
+#define topology_physical_package_id(cpu) (get_hard_smp_processor_id(cpu))
#endif
+#define topology_core_cpumask(cpu) (per_cpu(cpu_core_map, cpu))
+#define topology_core_id(cpu) (cpu_to_core_id(cpu))
+#define topology_physical_package_id(cpu) (0)
#endif
#endif /* __KERNEL__ */
diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c
index 77bb77d..efd43dd 100644
--- a/arch/powerpc/kernel/setup-common.c
+++ b/arch/powerpc/kernel/setup-common.c
@@ -199,6 +199,23 @@ static void show_cpuinfo_summary(struct seq_file *m)
#endif
}
+/*
+ * Get CPU information for use by the procfs.
+ */
+static void show_cpuinfo_core(struct seq_file *m, unsigned int cpu)
+{
+
+
+#ifdef CONFIG_SMP
+#ifdef CONFIG_PPC64
+ seq_printf(m, "physical id\t: %d\n", get_hard_smp_processor_id(cpu));
+#else /* Assume 32 bit archs are single package */
+ seq_printf(m, "physical id\t: %d\n", (0));
+#endif /* CONFIG_PPC64 */
+ seq_printf(m, "core id\t\t: %d\n", (cpu_to_core_id(cpu)));
+#endif
+}
+
static int show_cpuinfo(struct seq_file *m, void *v)
{
unsigned long cpu_id = (unsigned long)v - 1;
@@ -302,6 +319,8 @@ static int show_cpuinfo(struct seq_file *m, void *v)
seq_printf(m, "revision\t: %hd.%hd (pvr %04x %04x)\n",
maj, min, PVR_VER(pvr), PVR_REV(pvr));
+ show_cpuinfo_core(m, cpu_id);
+
#ifdef CONFIG_PPC32
seq_printf(m, "bogomips\t: %lu.%02lu\n",
loops_per_jiffy / (500000/HZ),

View File

@@ -1,20 +0,0 @@
#Copyright 2012 Cumulus Networks, Inc. All rights reserved.
diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c
index 458ed3b..9508bec 100644
--- a/arch/powerpc/kernel/pci-common.c
+++ b/arch/powerpc/kernel/pci-common.c
@@ -1112,6 +1112,13 @@ void __devinit pcibios_setup_bus_devices(struct pci_bus *bus)
ppc_md.pci_dma_dev_setup(dev);
/* Read default IRQs and fixup if necessary */
+ if (pci_read_irq_line(dev) && (dev->devfn != 0)) {
+ printk(KERN_WARNING
+ "WARNING: Unable to map IRQ in device tree for pci device "
+ "%04x:%02x:%02x.%d\n",
+ pci_domain_nr(dev->bus), dev->bus->number, PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn));
+ }
pci_read_irq_line(dev);
if (ppc_md.pci_irq_fixup)
ppc_md.pci_irq_fixup(dev);

View File

@@ -1,13 +0,0 @@
Add control and postinstall
diff --git a/debiancl/control.bcm-modules b/debiancl/control.bcm-modules
new file mode 100644
index 0000000..98dd20b
--- /dev/null
+++ b/debiancl/control.bcm-modules
@@ -0,0 +1,5 @@
+Package: bcm-modules
+Version: =V
+Architecture: =A
+Maintainer: support@cumulusnetworks.com
+Description: Modules for BCM SDK

View File

@@ -1,163 +0,0 @@
Patch for debian post install script
diff --git a/cumulus/control.linux-headers b/cumulus/control.linux-headers
new file mode 100644
index 0000000..6d999dd
--- /dev/null
+++ b/cumulus/control.linux-headers
@@ -0,0 +1,5 @@
+Package: linux-headers
+Version: =V
+Maintainer: support@cumulusnetworks.com
+Architecture: =A
+Description: Cumulus Linux kernel headers
diff --git a/cumulus/control.linux-image b/cumulus/control.linux-image
new file mode 100644
index 0000000..03fccc4
--- /dev/null
+++ b/cumulus/control.linux-image
@@ -0,0 +1,5 @@
+Package: linux-image
+Version: =V
+Maintainer: support@cumulusnetworks.com
+Architecture: =A
+Description: Cumulus Linux kernel
diff --git a/cumulus/control.linux-libc-dev b/cumulus/control.linux-libc-dev
new file mode 100644
index 0000000..4ae15ce
--- /dev/null
+++ b/cumulus/control.linux-libc-dev
@@ -0,0 +1,12 @@
+Package: linux-libc-dev
+Source: linux-upstream
+Version: =V
+Architecture: =A
+Maintainer: cumulus <support@cumulusnetworks.com>
+Provides: linux-kernel-headers
+Section: devel
+Priority: optional
+Homepage: http://www.kernel.org/
+Description: Linux support headers for userspace development
+ This package provides userspaces headers from the Linux kernel. These headers
+ are used by the installed headers for GNU glibc and other system libraries.
diff --git a/cumulus/etc/kernel/postinst.d/update-cumulus b/cumulus/etc/kernel/postinst.d/update-cumulus
new file mode 100755
index 0000000..0333980
--- /dev/null
+++ b/cumulus/etc/kernel/postinst.d/update-cumulus
@@ -0,0 +1,52 @@
+#!/bin/sh -e
+
+version="$1"
+
+# exit if we dont need to update the kernel and initrd
+if [ "$UPDATE_CUMULUS" = 'No' ]; then
+ exit 0
+fi
+
+. /usr/share/cumulus/img/functions
+
+# passing the kernel version is required
+if [ -z "${version}" ]; then
+ echo >&2 "W: cumulus-kernel: ${DPKG_MAINTSCRIPT_PACKAGE:-kernel package} did not pass a version number"
+ exit 2
+fi
+
+bootdir="/boot"
+kernel_newimg_src="${bootdir}/vmlinuz-${version}"
+initrd_newimg_src="${bootdir}/initrd.img-${version}"
+
+if [ ! -e "$kernel_newimg_src" ]; then
+ echo >&2 "W: cumulus-kernel: ${DPKG_MAINTSCRIPT_PACKAGE:-kernel package} cannot find kernel image"
+ exit 2
+fi
+
+if [ ! -e "$initrd_newimg_src" ]; then
+ echo >&2 "W: cumulus-kernel: ${DPKG_MAINTSCRIPT_PACKAGE:-kernel package} cannot find initrd image"
+ exit 2
+fi
+
+cl_check_env root-priv
+
+currslot=${slot_prefix}${active}
+kernel_oldimg=${bootdir}/${kernel_prefix}vmlinuz-*-${currslot}
+initrd_oldimg=${bootdir}/${kernel_prefix}initrd.img-*-${currslot}
+
+kernel_newimg_dst=${bootdir}/${kernel_prefix}vmlinuz-${version}-${currslot}
+initrd_newimg_dst=${bootdir}/${kernel_prefix}initrd.img-${version}-${currslot}
+
+#remove existing currslot images
+rm -f ${kernel_oldimg}
+rm -f ${initrd_oldimg}
+
+# Move kernel/initrd files to the kernel initrd dst files
+cmd="mv -f $kernel_newimg_src $kernel_newimg_dst"
+echo "$cmd"
+$cmd
+
+cmd="mv -f $initrd_newimg_src $initrd_newimg_dst"
+echo "$cmd"
+$cmd
diff --git a/cumulus/postinst.linux-image.powerpc b/cumulus/postinst.linux-image.powerpc
new file mode 100755
index 0000000..2aabfea
--- /dev/null
+++ b/cumulus/postinst.linux-image.powerpc
@@ -0,0 +1,57 @@
+#!/bin/sh
+# postinst script for switchd
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postinst> `configure' <most-recently-configured-version>
+# * <old-postinst> `abort-upgrade' <new version>
+# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+# <new-version>
+# * <postinst> `abort-remove'
+# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+# <failed-install-package> <version> `removing'
+# <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ configure)
+ ;;
+
+ abort-upgrade|abort-remove|abort-deconfigue)
+ exit 1
+ ;;
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+. /usr/share/cumulus/img/functions
+
+install_file=/usr/share/cumulus/.installer/kernel_install.sh
+kimage_linkname="uImage.itb"
+kimage_name="uImage-=V-=A.itb"
+kimage_path="/boot/"$kimage_name
+
+if [ -e "/.buildroot" ]; then
+ cd /boot && ln -sf $kimage_name $kimage_linkname
+ exit 0
+fi
+
+cl_check_env root-priv
+slot=$active
+
+sh $install_file $kimage_path $slot || {
+ log_failure_msg "Kernel installtion failed."
+ log_failure_msg "Problems installing image file."
+ exit 1
+}
+
+cd /boot && ln -sf $kimage_name $kimage_linkname
+
+exit 0

View File

@@ -1,60 +0,0 @@
Patch for debian post install script
diff --git a/cumulus/postinst b/cumulus/postinst
new file mode 100755
index 0000000..6b9ca39
--- /dev/null
+++ b/cumulus/postinst
@@ -0,0 +1,52 @@
+#!/bin/sh
+# postinst script for switchd
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postinst> `configure' <most-recently-configured-version>
+# * <old-postinst> `abort-upgrade' <new version>
+# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+# <new-version>
+# * <postinst> `abort-remove'
+# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+# <failed-install-package> <version> `removing'
+# <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ configure)
+ ;;
+
+ abort-upgrade|abort-remove|abort-deconfigue)
+ exit 1
+ ;;
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+. /usr/share/cumulus/img/functions
+cl_check_env root-priv
+
+install_file=/usr/share/cumulus/.installer/kernel_install.sh
+slot=$active
+kimage="/boot/uImage-${arch}.itb"
+
+sh $install_file $kimage $slot || {
+ log_failure_msg "Kernel installtion failed."
+ log_failure_msg "Problems installing image file."
+ clean_up 1
+}
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0

View File

@@ -1,32 +0,0 @@
Post install hooks need to call depmod to properly load kernel modules after
reboot.
diff --git a/cumulus/etc/kernel/postinst.d/update-cumulus b/cumulus/etc/kernel/postinst.d/update-cumulus
index 0333980..cd8d311 100755
--- a/cumulus/etc/kernel/postinst.d/update-cumulus
+++ b/cumulus/etc/kernel/postinst.d/update-cumulus
@@ -50,3 +50,5 @@ $cmd
cmd="mv -f $initrd_newimg_src $initrd_newimg_dst"
echo "$cmd"
$cmd
+
+depmod -a $version
diff --git a/cumulus/postinst.linux-image.powerpc b/cumulus/postinst.linux-image.powerpc
index 2aabfea..fb775d4 100755
--- a/cumulus/postinst.linux-image.powerpc
+++ b/cumulus/postinst.linux-image.powerpc
@@ -1,5 +1,5 @@
#!/bin/sh
-# postinst script for switchd
+# postinst script for kernel
#
# see: dh_installdeb(1)
@@ -54,4 +54,7 @@ sh $install_file $kimage_path $slot || {
cd /boot && ln -sf $kimage_name $kimage_linkname
+echo "depmod -a =V"
+depmod -a =V
+
exit 0

View File

@@ -1,57 +0,0 @@
driver adt7470 add knob to disable smbus timeout feature
The ADT7470 has a configurable feature called "disable SMBUS timeout".
Quoting their data sheet:
The ADT7470 includes an SMBus timeout feature. If there is no SMBus
activity for more than 31 ms, the ADT7470 assumes that the bus is
locked and releases the bus. This prevents the device from locking
or holding the SMBus expecting data. Some SMBus controllers cannot
handle the SMBus timeout feature, so it can be disabled.
It appears the FSL I2C controller cannot handle this feature. With
the timeout enabled we see intermittent failures waiting for the bus
to become ready or for transaction to complete.
This patch adds a configurable option to the device tree. If the
property "disable-smbus-timeout" exists in the node then the ADT7470
driver will disable the SMBUS timeout feature.
This way platforms that need to can disable this feature.
diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
index a9726c1..6ca8aae 100644
--- a/drivers/hwmon/adt7470.c
+++ b/drivers/hwmon/adt7470.c
@@ -32,6 +32,7 @@
#include <linux/log2.h>
#include <linux/kthread.h>
#include <linux/slab.h>
+#include <linux/of.h>
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
@@ -48,6 +49,7 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
#define ADT7470_REG_PWM_MAX_MAX_ADDR 0x3B
#define ADT7470_REG_CFG 0x40
#define ADT7470_FSPD_MASK 0x04
+#define ADT7470_TODIS_MASK 0x08
#define ADT7470_REG_ALARM1 0x41
#define ADT7470_R1T_ALARM 0x01
#define ADT7470_R2T_ALARM 0x02
@@ -225,6 +227,14 @@ static void adt7470_init_client(struct i2c_client *client)
if (reg < 0) {
dev_err(&client->dev, "cannot read configuration register\n");
} else {
+ struct property *pp;
+ pp = of_find_property(client->dev.of_node,
+ "disable-smbus-timeout", NULL);
+ if (pp)
+ reg |= ADT7470_TODIS_MASK;
+ else
+ reg &= ~ADT7470_TODIS_MASK;
+
/* start monitoring (and do a self-test) */
i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, reg | 3);
}

View File

@@ -1,41 +0,0 @@
Adds a flag that lets you disable FEATURE_I2C_BLOCK_READ on a device basis.
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c
index 841631c..67f4e6f 100644
--- a/drivers/misc/eeprom/at24.c
+++ b/drivers/misc/eeprom/at24.c
@@ -582,8 +582,9 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
if (chip.flags & AT24_FLAG_ADDR16) {
use_smbus = I2C_SMBUS_BYTE_DATA;
- } else if (i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
+ } else if (!(chip.flags & AT24_FLAG_DISABLE_I2CBLOCK) &&
+ (i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_I2C_BLOCK))) {
use_smbus = I2C_SMBUS_I2C_BLOCK_DATA;
} else if (i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_WORD_DATA)) {
@@ -630,8 +631,9 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
writable = !(chip.flags & AT24_FLAG_READONLY);
if (writable) {
if (!use_smbus ||
+ (!(chip.flags & AT24_FLAG_DISABLE_I2CBLOCK) &&
i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_WRITE_I2C_BLOCK) ||
+ I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) ||
i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WRITE_WORD_DATA) ||
i2c_check_functionality(client->adapter,
diff --git a/include/linux/i2c/at24.h b/include/linux/i2c/at24.h
index a881e5e..3bdb466 100644
--- a/include/linux/i2c/at24.h
+++ b/include/linux/i2c/at24.h
@@ -25,6 +25,7 @@ struct at24_platform_data {
#define AT24_FLAG_READONLY 0x40 /* sysfs-entry will be read-only */
#define AT24_FLAG_IRUGO 0x20 /* sysfs-entry will be world-readable */
#define AT24_FLAG_TAKE8ADDR 0x10 /* take always 8 addresses (24c00) */
+#define AT24_FLAG_DISABLE_I2CBLOCK 0x08 /*disable smbus i2c block access */
void (*setup)(struct memory_accessor *, void *context);
void *context;

View File

@@ -1,105 +0,0 @@
Adds byte and word write access to the at24 driver
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c
index c98c736..2ec05ce 100644
--- a/drivers/misc/eeprom/at24.c
+++ b/drivers/misc/eeprom/at24.c
@@ -332,6 +332,7 @@ static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
ssize_t status;
unsigned long timeout, write_time;
unsigned next_page;
+ int i = 0;
/* Get corresponding I2C address and adjust offset */
client = at24_translate_offset(at24, &offset);
@@ -345,10 +346,22 @@ static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
if (offset + count > next_page)
count = next_page - offset;
- /* If we'll use I2C calls for I/O, set up the message */
- if (!at24->use_smbus) {
- int i = 0;
+ switch (at24->use_smbus) {
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+ /* Smaller eeproms can work given some SMBus extension calls */
+ if (count > I2C_SMBUS_BLOCK_MAX)
+ count = I2C_SMBUS_BLOCK_MAX;
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ /* Check for odd length transaction */
+ count = (count == 1) ? 1 : 2;
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ count = 1;
+ break;
+ default:
+ /* If we'll use I2C calls for I/O, set up the message */
msg.addr = client->addr;
msg.flags = 0;
@@ -360,6 +373,7 @@ static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
msg.buf[i++] = offset;
memcpy(&msg.buf[i], buf, count);
msg.len = i + count;
+ break;
}
/*
@@ -370,15 +384,37 @@ static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
timeout = jiffies + msecs_to_jiffies(write_timeout);
do {
write_time = jiffies;
- if (at24->use_smbus) {
+ switch (at24->use_smbus) {
+ case I2C_SMBUS_I2C_BLOCK_DATA:
status = i2c_smbus_write_i2c_block_data(client,
offset, count, buf);
if (status == 0)
status = count;
- } else {
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ if (count == 2) {
+ status = i2c_smbus_write_word_data(
+ client,offset,(u16)((buf[0]) |
+ (buf[1] << 8)));
+ } else {
+ /* count = 1 */
+ status = i2c_smbus_write_byte_data(
+ client, offset, buf[0]);
+ }
+ if (status == 0)
+ status = count;
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ status = i2c_smbus_write_byte_data(client, offset,
+ buf[0]);
+ if (status == 0)
+ status = count;
+ break;
+ default:
status = i2c_transfer(client->adapter, &msg, 1);
if (status == 1)
status = count;
+ break;
}
dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n",
count, offset, status, jiffies);
@@ -585,9 +621,13 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
writable = !(chip.flags & AT24_FLAG_READONLY);
if (writable) {
- if (!use_smbus || i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
-
+ if (!use_smbus ||
+ i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WRITE_I2C_BLOCK) ||
+ i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WRITE_WORD_DATA) ||
+ i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) {
unsigned write_max = chip.page_size;
at24->macc.write = at24_macc_write;

View File

@@ -1,78 +0,0 @@
Add at24 based EEPROMs to the eeprom_dev hardware class
During device instantiation have the at24 driver add the new device to
the eeprom_dev hardware class. The functionality is enabled by
CONFIG_EEPROM_CLASS.
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c
index 47bcd10..c98c736 100644
--- a/drivers/misc/eeprom/at24.c
+++ b/drivers/misc/eeprom/at24.c
@@ -23,6 +23,7 @@
#include <linux/of.h>
#include <linux/i2c.h>
#include <linux/i2c/at24.h>
+#include <linux/eeprom_class.h>
/*
* I2C EEPROMs from most vendors are inexpensive and mostly interchangeable.
@@ -68,6 +69,8 @@ struct at24_data {
unsigned write_max;
unsigned num_addresses;
+ struct eeprom_device *eeprom_dev;
+
/*
* Some chips tie up multiple I2C addresses; dummy devices reserve
* them for us, and we'll use them with SMBus calls.
@@ -514,6 +517,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
chip.setup = NULL;
chip.context = NULL;
+ chip.eeprom_data = NULL;
}
if (!is_power_of_2(chip.byte_len))
@@ -627,6 +631,13 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (err)
goto err_clients;
+ at24->eeprom_dev = eeprom_device_register(&client->dev, chip.eeprom_data);
+ if (IS_ERR(at24->eeprom_dev)) {
+ dev_err(&client->dev, "error registering eeprom device.\n");
+ err = PTR_ERR(at24->eeprom_dev);
+ goto err_clients;
+ }
+
i2c_set_clientdata(client, at24);
dev_info(&client->dev, "%zu byte %s EEPROM, %s, %u bytes/write\n",
@@ -669,6 +680,8 @@ static int __devexit at24_remove(struct i2c_client *client)
for (i = 1; i < at24->num_addresses; i++)
i2c_unregister_device(at24->client[i]);
+ eeprom_device_unregister(at24->eeprom_dev);
+
kfree(at24->writebuf);
kfree(at24);
return 0;
diff --git a/include/linux/i2c/at24.h b/include/linux/i2c/at24.h
index 8ace930..a881e5e 100644
--- a/include/linux/i2c/at24.h
+++ b/include/linux/i2c/at24.h
@@ -3,6 +3,7 @@
#include <linux/types.h>
#include <linux/memory.h>
+#include <linux/eeprom_class.h>
/*
* As seen through Linux I2C, differences between the most common types of I2C
@@ -27,6 +28,7 @@ struct at24_platform_data {
void (*setup)(struct memory_accessor *, void *context);
void *context;
+ struct eeprom_platform_data *eeprom_data; /* extra data for the eeprom_class */
};
#endif /* _LINUX_AT24_H */

View File

@@ -1,40 +0,0 @@
driver at24 fix odd length two byte access
For I2C_SMBUS_WORD_DATA read accesses check if the access length is
one or two bytes. For transactions that have an odd length eventualy
we read 1 byte at the end to complete the request.
The previous code always used a count of 2, which works fine if the
requested total length is even. If the requested length was odd,
however, the code would cause a kernel OOPS.
The while (count) loop would go forever as count went from 1 to -1,
never becoming zero. Also the return buffer would overrun.
This patch allows for reading an odd number of bytes in
I2C_SMBUS_WORD_DATA mode.
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c
index ab1ad41..47bcd10 100644
--- a/drivers/misc/eeprom/at24.c
+++ b/drivers/misc/eeprom/at24.c
@@ -192,7 +192,8 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
count = I2C_SMBUS_BLOCK_MAX;
break;
case I2C_SMBUS_WORD_DATA:
- count = 2;
+ /* Check for odd length transaction */
+ count = (count == 1) ? 1 : 2;
break;
case I2C_SMBUS_BYTE_DATA:
count = 1;
@@ -237,7 +238,8 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
status = i2c_smbus_read_word_data(client, offset);
if (status >= 0) {
buf[0] = status & 0xff;
- buf[1] = status >> 8;
+ if (count == 2)
+ buf[1] = status >> 8;
status = count;
}
break;

View File

@@ -1,49 +0,0 @@
patch for celestica Redstone XP
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c
index 2ec05ce..841631c 100644
--- a/drivers/misc/eeprom/at24.c
+++ b/drivers/misc/eeprom/at24.c
@@ -247,7 +247,14 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
}
break;
case I2C_SMBUS_BYTE_DATA:
- status = i2c_smbus_read_byte_data(client, offset);
+ if (at24->chip.flags & AT24_FLAG_ADDR16) {
+ status = i2c_smbus_write_byte_data(client, (offset >> 8) & 0xff, offset & 0xff);
+ if (status >= 0) {
+ status = i2c_smbus_read_byte(client);
+ }
+ } else {
+ status = i2c_smbus_read_byte_data(client, offset);
+ }
if (status >= 0) {
buf[0] = status;
status = count;
@@ -405,8 +412,11 @@ static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
status = count;
break;
case I2C_SMBUS_BYTE_DATA:
- status = i2c_smbus_write_byte_data(client, offset,
- buf[0]);
+ if (at24->chip.flags & AT24_FLAG_ADDR16) {
+ status = i2c_smbus_write_word_data(client, (offset >> 8) & 0xff, buf[0] << 8 | (offset & 0xff));
+ } else {
+ status = i2c_smbus_write_byte_data(client, offset, buf[0]);
+ }
if (status == 0)
status = count;
break;
@@ -571,10 +581,8 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
/* Use I2C operations unless we're stuck with SMBus extensions. */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
if (chip.flags & AT24_FLAG_ADDR16) {
- err = -EPFNOSUPPORT;
- goto err_out;
- }
- if (i2c_check_functionality(client->adapter,
+ use_smbus = I2C_SMBUS_BYTE_DATA;
+ } else if (i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
use_smbus = I2C_SMBUS_I2C_BLOCK_DATA;
} else if (i2c_check_functionality(client->adapter,

View File

@@ -1,546 +0,0 @@
Driver for DS100DF410 Low Power 10GbE Quad Channel Retimer/Equalizer
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 846aab1..41989b0 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -527,6 +527,27 @@ config EDA_DEF_ALIGN
help
Default alignment of the memory region. Default is 1MB.
+config RETIMER_CLASS
+ tristate "Retimer Class support"
+ depends on SYSFS
+ default y
+ help
+ Creates a hardware class in sysfs called "retimer_dev",
+ providing a common place to register RETIMER devices.
+
+ This support can also be built as a module. If so, the module
+ will be called retimer_class.
+
+config DS100DF410
+ tristate "DS100DF410 Low Power 10GbE Quad Channel Retimer"
+ depends on I2C && SYSFS
+ help
+ If you say yes here you get support for the DS100DF410
+ Low Power 10GbE Quad Channel Retimer.
+
+ This driver can also be built as a module. If so, the module
+ will be called ds100df410.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index cf09aa8..ad70876 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -49,3 +49,5 @@ obj-y += carma/
obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/
obj-$(CONFIG_EARLY_DMA_ALLOC) += early_dma_alloc.o
+obj-$(CONFIG_RETIMER_CLASS) += retimer_class.o
+obj-$(CONFIG_DS100DF410) += ds100df410.o
diff --git a/drivers/misc/ds100df410.c b/drivers/misc/ds100df410.c
new file mode 100644
index 0000000..b626111
--- /dev/null
+++ b/drivers/misc/ds100df410.c
@@ -0,0 +1,290 @@
+/*
+ * ds100df410.c - I2c client driver to manage DS100DF410
+ * DS100DF410 Low Power 10GbE Quad Channel Retimer
+ *
+ * Copyright (C) 2014 Cumulus Networks, Inc.
+ * Author: Puneet Shenoy <puneet@cumulusnetworks.com>
+ *
+ * Ideas and structure regarding introducing the class device graciously borrowed
+ * from the eeprom sysfs/class support by:
+ * Copyright (C) 2013 CumulusNetworks, Inc.
+ * Author: Curt Brune <curt@cumulusnetworks.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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kdev_t.h>
+#include <linux/idr.h>
+#include <linux/gfp.h>
+#include <linux/spinlock.h>
+
+#ifdef CONFIG_RETIMER_CLASS
+#include <linux/retimer_class.h>
+#endif
+
+#define DS100DF410_DRV_NAME "ds100df410"
+#define DRIVER_VERSION "1.0"
+
+#define DS100DF410_CDR_RST_REG 0x0a
+#define DS100DF410_TAP_DEM_REG 0x15
+#define DS100DF410_PFD_PRBS_DFE_REG 0x1e
+#define DS100DF410_DRV_SEL_VOD_REG 0x2d
+#define DS100DF410_ADAPT_EQ_SM_REG 0x31
+#define DS100DF410_VEO_CLK_CDR_CAP_REG 0x36
+#define DS100DF410_CHANNELS_REG 0xff
+
+struct ds100df410_data {
+ struct i2c_client *client;
+
+#ifdef CONFIG_RETIMER_CLASS
+ struct device *retimer_dev;
+#endif
+ struct mutex lock;
+};
+
+static u32 ds100df410_read(struct device *dev, u8 reg, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ u32 ret = i2c_smbus_read_byte_data(client, reg);
+
+ return sprintf(buf, "%d\n", ret);
+}
+
+static u32 ds100df410_write(struct device *dev, u8 reg, const char *buf,
+ size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned long val;
+ int ret;
+
+ if (strict_strtoul(buf, 0, &val) < 0)
+ return -EINVAL;
+
+ ret = i2c_smbus_write_byte_data(client, reg, (u8)val);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static ssize_t ds100df410_show_cdr_rst(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return ds100df410_read(dev, DS100DF410_CDR_RST_REG, buf);
+}
+
+static ssize_t ds100df410_store_cdr_rst(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return ds100df410_write(dev, DS100DF410_CDR_RST_REG, buf, count);
+}
+
+static ssize_t ds100df410_show_tap_dem(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return ds100df410_read(dev, DS100DF410_TAP_DEM_REG, buf);
+}
+
+static ssize_t ds100df410_store_tap_dem(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return ds100df410_write(dev, DS100DF410_TAP_DEM_REG, buf, count);
+}
+
+static ssize_t ds100df410_show_pfd_prbs_dfe(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return ds100df410_read(dev, DS100DF410_PFD_PRBS_DFE_REG, buf);
+}
+
+static ssize_t ds100df410_store_pfd_prbs_dfe(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return ds100df410_write(dev, DS100DF410_PFD_PRBS_DFE_REG, buf, count);
+}
+
+static ssize_t ds100df410_show_drv_sel_vod(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return ds100df410_read(dev, DS100DF410_DRV_SEL_VOD_REG, buf);
+}
+
+static ssize_t ds100df410_store_drv_sel_vod(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return ds100df410_write(dev, DS100DF410_DRV_SEL_VOD_REG, buf, count);
+}
+
+static ssize_t ds100df410_show_adapt_eq_sm(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return ds100df410_read(dev, DS100DF410_ADAPT_EQ_SM_REG, buf);
+}
+
+static ssize_t ds100df410_store_adapt_eq_sm(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return ds100df410_write(dev, DS100DF410_ADAPT_EQ_SM_REG, buf, count);
+}
+
+static ssize_t ds100df410_show_veo_clk_cdr_cap(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return ds100df410_read(dev, DS100DF410_VEO_CLK_CDR_CAP_REG, buf);
+}
+
+static ssize_t ds100df410_store_veo_clk_cdr_cap(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return ds100df410_write(dev, DS100DF410_VEO_CLK_CDR_CAP_REG, buf, count);
+}
+
+static ssize_t ds100df410_show_channels(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return ds100df410_read(dev, DS100DF410_CHANNELS_REG, buf);
+}
+
+static ssize_t ds100df410_store_channels(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return ds100df410_write(dev, DS100DF410_CHANNELS_REG, buf, count);
+}
+
+static DEVICE_ATTR(cdr_rst, S_IWUSR | S_IRUGO,
+ ds100df410_show_cdr_rst, ds100df410_store_cdr_rst);
+static DEVICE_ATTR(tap_dem, S_IWUSR | S_IRUGO,
+ ds100df410_show_tap_dem, ds100df410_store_tap_dem);
+static DEVICE_ATTR(pfd_prbs_dfe, S_IWUSR | S_IRUGO,
+ ds100df410_show_pfd_prbs_dfe, ds100df410_store_pfd_prbs_dfe);
+static DEVICE_ATTR(drv_sel_vod, S_IWUSR | S_IRUGO,
+ ds100df410_show_drv_sel_vod, ds100df410_store_drv_sel_vod);
+static DEVICE_ATTR(adapt_eq_sm, S_IWUSR | S_IRUGO,
+ ds100df410_show_adapt_eq_sm, ds100df410_store_adapt_eq_sm);
+static DEVICE_ATTR(veo_clk_cdr_cap, S_IWUSR | S_IRUGO,
+ ds100df410_show_veo_clk_cdr_cap,
+ ds100df410_store_veo_clk_cdr_cap);
+static DEVICE_ATTR(channels, S_IWUSR | S_IRUGO,
+ ds100df410_show_channels, ds100df410_store_channels);
+
+static struct attribute *ds100df410_attributes[] = {
+ &dev_attr_cdr_rst.attr,
+ &dev_attr_tap_dem.attr,
+ &dev_attr_pfd_prbs_dfe.attr,
+ &dev_attr_drv_sel_vod.attr,
+ &dev_attr_adapt_eq_sm.attr,
+ &dev_attr_veo_clk_cdr_cap.attr,
+ &dev_attr_channels.attr,
+ NULL
+};
+
+static const struct attribute_group ds100df410_attr_group = {
+ .attrs = ds100df410_attributes,
+};
+
+static int __devinit ds100df410_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct ds100df410_data *data;
+ int err = 0;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) {
+ return -EIO;
+ }
+
+ data = kzalloc(sizeof(struct ds100df410_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ data->client = client;
+ mutex_init(&data->lock);
+
+ /* register sysfs hooks */
+ err = sysfs_create_group(&client->dev.kobj, &ds100df410_attr_group);
+ if (err)
+ goto exit_kfree;
+
+
+#ifdef CONFIG_RETIMER_CLASS
+ data->retimer_dev = retimer_device_register(&client->dev);
+ if (IS_ERR(data->retimer_dev)) {
+ dev_err(&client->dev, "error registering retimer device.\n");
+ err = PTR_ERR(data->retimer_dev);
+ goto exit_kfree;
+ }
+#endif
+
+ i2c_set_clientdata(client, data);
+ return 0;
+exit_kfree:
+ kfree(data);
+ return err;
+}
+
+static int __devexit ds100df410_remove(struct i2c_client *client)
+{
+ struct ds100df410_data *data;
+
+ data = i2c_get_clientdata(client);
+ sysfs_remove_group(&client->dev.kobj, &ds100df410_attr_group);
+
+#ifdef CONFIG_RETIMER_CLASS
+ retimer_device_unregister(data->retimer_dev);
+#endif
+
+ kfree(data);
+ return 0;
+}
+
+static const struct i2c_device_id ds100df410_id[] = {
+ { "ds100df410", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ds100df410_id);
+
+static struct i2c_driver ds100df410_driver = {
+ .driver = {
+ .name = DS100DF410_DRV_NAME,
+ },
+ .probe = ds100df410_probe,
+ .remove = __devexit_p(ds100df410_remove),
+ .id_table = ds100df410_id,
+};
+
+module_i2c_driver(ds100df410_driver);
+MODULE_AUTHOR("Puneet Shenoy <puneet@cumulusnetworks.com>");
+MODULE_DESCRIPTION("I2C client for DS100DF410 10GE Quad Core Retimer");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/drivers/misc/retimer_class.c b/drivers/misc/retimer_class.c
new file mode 100644
index 0000000..176df29
--- /dev/null
+++ b/drivers/misc/retimer_class.c
@@ -0,0 +1,159 @@
+/*
+ * retimer_class.c
+ *
+ * This file defines the sysfs class "retimer", for use by RETIMER
+ * drivers.
+ *
+ * Copyright (C) 2014 Cumulus Networks, Inc.
+ * Author: Puneet Shenoy <puneet@cumulusnetworks.com>
+ *
+ * Ideas and structure graciously borrowed from the eeprom_class class:
+ * Copyright (C) 2013 Curt Brune <curt@cumulusnetworks.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 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kdev_t.h>
+#include <linux/idr.h>
+#include <linux/retimer_class.h>
+#include <linux/gfp.h>
+#include <linux/spinlock.h>
+#include <linux/pci.h>
+
+/* Root retimer "class" object (corresponds to '/<sysfs>/class/retimer_dev/') */
+static struct class *retimer_class;
+
+#define RETIMER_CLASS_NAME "retimer_dev"
+#define RETIMER_ID_PREFIX "retimer"
+#define RETIMER_ID_FORMAT RETIMER_ID_PREFIX "%d"
+
+static DEFINE_IDA(retimer_ida);
+
+/**
+ * retimer_device_register - register w/ retimer class
+ * @dev: the device to register
+ *
+ * retimer_device_unregister() must be called when the device is no
+ * longer needed.
+ *
+ * Creates a new retimer class device that is a child of @dev. Also
+ * creates a symlink in /<sysfs>/class/retimer_dev/retimer[N] pointing
+ * to the new device.
+ *
+ * Returns the pointer to the new device.
+ */
+struct device *retimer_device_register(struct device *dev)
+{
+ struct device *retimer_dev;
+ int id;
+
+ id = ida_simple_get(&retimer_ida, 0, 0, GFP_KERNEL);
+ if (id < 0)
+ return ERR_PTR(id);
+
+ retimer_dev = device_create(retimer_class, dev, MKDEV(0, 0), NULL,
+ RETIMER_ID_FORMAT, id);
+
+ if (IS_ERR(retimer_dev))
+ ida_simple_remove(&retimer_ida, id);
+
+ return retimer_dev;
+}
+
+/**
+ * retimer_device_unregister - removes the previously registered class device
+ *
+ * @dev: the class device to destroy
+ */
+void retimer_device_unregister(struct device *dev)
+{
+ int id;
+
+ if (likely(sscanf(dev_name(dev), RETIMER_ID_FORMAT, &id) == 1)) {
+ device_unregister(dev);
+ ida_simple_remove(&retimer_ida, id);
+ } else
+ dev_dbg(dev->parent,
+ "retimer_device_unregister() failed: bad class ID!\n");
+}
+
+/**
+ * Each member of the retimer class exports a sysfs file called
+ * "label", containing the label property from the corresponding
+ * device tree node.
+ *
+ * Userspace can use the label to identify what the RETIMER is for.
+ */
+static ssize_t label_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ const char* cp = NULL;
+ int len = 0;
+
+ /*
+ * The class device is a child of the original device,
+ * i.e. dev->parent points to the original device.
+ */
+ if (dev->parent && dev->parent->of_node)
+ cp = of_get_property(dev->parent->of_node, "label", &len);
+
+ if ((cp == NULL) || (len == 0)) {
+ cp = "unknown";
+ len = strlen(cp) + 1;
+ }
+
+ strncpy(buf, cp, len - 1);
+ buf[len - 1] = '\n';
+ buf[len] = '\0';
+
+ return len;
+}
+
+struct device_attribute retimer_class_dev_attrs[] = {
+ __ATTR_RO(label),
+ __ATTR_NULL,
+};
+
+static int __init retimer_init(void)
+{
+ retimer_class = class_create(THIS_MODULE, RETIMER_CLASS_NAME);
+ if (IS_ERR(retimer_class)) {
+ pr_err("couldn't create sysfs class\n");
+ return PTR_ERR(retimer_class);
+ }
+
+ retimer_class->dev_attrs = retimer_class_dev_attrs;
+
+ return 0;
+}
+
+static void __exit retimer_exit(void)
+{
+ class_destroy(retimer_class);
+}
+
+subsys_initcall(retimer_init);
+module_exit(retimer_exit);
+
+EXPORT_SYMBOL_GPL(retimer_device_register);
+EXPORT_SYMBOL_GPL(retimer_device_unregister);
+
+MODULE_AUTHOR("Puneet Shenoy <puneet@cumulusnetworks.com>");
+MODULE_DESCRIPTION("retimer sysfs/class support");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/retimer_class.h b/include/linux/retimer_class.h
new file mode 100644
index 0000000..6f37318
--- /dev/null
+++ b/include/linux/retimer_class.h
@@ -0,0 +1,35 @@
+/*
+ * retimer_class.c
+ *
+ * This file exports interface functions for the sysfs class "retimer",
+ * for use by RETIMER drivers.
+ *
+ * Copyright (C) 2014 Cumulus Networks, Inc.
+ * Author: Puneet Shenoy <puneet@cumulusnetworks.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 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef RETIMER_CLASS_H__
+#define RETIMER_CLASS_H__
+
+#include <linux/device.h>
+#include <linux/err.h>
+
+struct device *retimer_device_register(struct device *dev);
+
+void retimer_device_unregister(struct device *dev);
+
+#endif /* RETIMER_CLASS_H__ */

View File

@@ -1,357 +0,0 @@
early dma allocator patch
Add a tiny driver called "Early DMA Allocator (EDA)" that allocates
memory using the early boot alloc_bootmem() interface. The platform
dependent kernel code (dni_7448.c for example) would call this
driver's init at boot time.
The size and alignment of the DMA memory region are specified in any
one of a kernel command line option, device tree node (compatible with
"early-dma-alloc"), or a Kbuild configurable compiled in default.
The driver also publishes an interface for other kernel drivers
to call that returns the physical offset and size of the allocated
region.
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index b506f41..cfaf677 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -50,6 +50,9 @@
#include <asm/pci-direct.h>
#include <linux/init_ohci1394_dma.h>
#include <linux/kvm_para.h>
+#ifdef CONFIG_EARLY_DMA_ALLOC
+#include <linux/early_dma_alloc.h>
+#endif
#include <linux/errno.h>
#include <linux/kernel.h>
@@ -1144,6 +1147,9 @@ void __init setup_arch(char **cmdline_p)
mcheck_init();
arch_init_ideal_nops();
+#ifdef CONFIG_EARLY_DMA_ALLOC
+ eda_init();
+#endif
}
#ifdef CONFIG_X86_32
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 5664696..846aab1 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -500,6 +500,33 @@ config USB_SWITCH_FSA9480
stereo and mono audio, video, microphone and UART data to use
a common connector port.
+config EARLY_DMA_ALLOC
+ bool "Early DMA Memory Allocator"
+ depends on HAS_DMA
+
+ ---help---
+ This driver locks down a region of DMA accessible memory
+ early in the boot process. This memory can be used by other
+ drivers that might rmmod/insmod, insuring the memory region
+ does not become fragmented.
+
+config EDA_DEF_SIZE
+ hex "EDA Default Region Size"
+ depends on EARLY_DMA_ALLOC
+ default 0x04000000
+ help
+ Default size of the reserved memory pool, if not altered by the
+ open firmware interface or kernel boot parameter. This memory
+ will not be accessable to the rest of the system. Default is
+ 64MB.
+
+config EDA_DEF_ALIGN
+ hex "EDA Default Alignment"
+ depends on EARLY_DMA_ALLOC
+ default 0x00100000
+ help
+ Default alignment of the memory region. Default is 1MB.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index b26495a..cf09aa8 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -48,3 +48,4 @@ obj-y += lis3lv02d/
obj-y += carma/
obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/
+obj-$(CONFIG_EARLY_DMA_ALLOC) += early_dma_alloc.o
diff --git a/drivers/misc/early_dma_alloc.c b/drivers/misc/early_dma_alloc.c
new file mode 100644
index 0000000..ab3ac32
--- /dev/null
+++ b/drivers/misc/early_dma_alloc.c
@@ -0,0 +1,223 @@
+/*
+ * Early DMA Memory Allocator
+ *
+ * Copyright © 2013,2014 Cumulus Networks, Inc.
+ *
+ * Author: Curt Brune <curt@cumulusnetworks.com>
+ * Modified: Jonathan Toppins <jtoppins@cumulusnetworks.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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+/*
+ * This driver allocates a region of DMA accessible memory, making it
+ * available to one other device driver.
+ *
+ * The client device driver may be unloaded and reloaded over time.
+ * This driver keeps the DMA region from becoming fragmented across
+ * module reloads.
+ *
+ * Memory Region Restrictions
+ * --------------------------
+ * The memory region allocated by EDA MUST exist below a 4GB limit. This
+ * is because EDA's primary (only at time of writing) user is the
+ * Broadcom BDE driver wich assumes a 32-bit physical address space and
+ * assumes paddr is no more than 32-bits wide. Furthermore, before porting
+ * the BDE driver to use EDA the BDE driver specifically checked if the
+ * memory region provided by highmem was less than 4GB. We assume Broadcom
+ * knew what they were doing and there is a specific reason why this 4GB
+ * limit is needed, so we enforce this limit by checking the physical address
+ * after allocation.
+ *
+ * Memory Region Size and Alignment
+ * --------------------------------
+ * This driver allows three ways for the user to define the DMA memory
+ * that will be created, listed in order of preference.
+ * 1. The user may specify on the kernel command line in the boot loader
+ * the "eda_mem" option, this option has the format "size@alignment",
+ * example: eda_mem=0x04000000@0x00100000
+ * 2. This driver looks for a device tree node compatible with
+ * "early-dma-alloc". The "region_size" property of the node contains
+ * the size, in bytes, of the desired DMA memory region. The
+ * "alignment" property contains the desired memory alignment of the
+ * region.
+ * 3. Finally if neither of the above are provided the Kbuild changable,
+ * compiled in default size and alignment will be used.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/of_platform.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/dma-mapping.h>
+#include <linux/bootmem.h>
+#include <linux/early_dma_alloc.h>
+
+#if (!defined CONFIG_EDA_DEF_SIZE) || \
+ (!defined CONFIG_EDA_DEF_ALIGN)
+#error incorrect kernel config - fix it
+#endif
+
+// #define DEBUG
+#if (defined DEBUG)
+#define eda_debug(fmt, ... ) \
+ printk(KERN_ERR "eda-debug:%s(): " fmt "\n", __func__ , \
+ ##__VA_ARGS__)
+#else
+#define eda_debug(fmt, ... )
+#endif
+
+#define eda_info(fmt, ... ) \
+ printk(KERN_INFO "eda: " fmt "\n", ##__VA_ARGS__)
+
+static uint32_t dma_size;
+static void *dma_vaddr;
+static u32 dma_align __initdata;
+static bool eda_cmdline __initdata;
+
+static int __init setup_eda_mem(char *str)
+{
+ char *endp;
+
+ dma_size = memparse(str, &endp) & PAGE_MASK;
+ if (*endp == '@')
+ dma_align = memparse(endp + 1, NULL) & PAGE_MASK;
+ eda_cmdline = true;
+ return 0;
+}
+early_param("eda_mem", setup_eda_mem);
+
+static int __init of_eda_init(uint32_t *size, u32 *align)
+#ifdef CONFIG_OF_FLATTREE
+{
+ int rc = -ENODEV;
+ struct device_node *np = NULL;
+ const u32 *region_sz_p = NULL;
+ const u32 *align_p = NULL;
+ u32 prop_sz = 0;
+
+ eda_debug("entry");
+
+ /* is a programming error make it really painful so it gets fixed */
+ BUG_ON(NULL == size || NULL == align);
+
+ np = of_find_compatible_node(NULL, NULL, "early-dma-alloc");
+ if (!np) {
+ printk(KERN_WARNING "WARN: Can not find `early-dma-alloc'"
+ " device tree node.\n");
+ goto cleanup;
+ }
+
+ region_sz_p = of_get_property(np, "region_size", &prop_sz);
+ if (!region_sz_p || (prop_sz != sizeof(*region_sz_p))) {
+ printk(KERN_ERR "ERROR: Can not find `region_size' property"
+ " in early-dma-alloc device tree node.\n");
+ goto cleanup;
+ }
+ *size = *region_sz_p;
+
+ align_p = of_get_property(np, "alignment", &prop_sz);
+ if (!align_p || (prop_sz != sizeof(*align_p))) {
+ printk(KERN_ERR "ERROR: Can not find `alignment' property in"
+ "early-dma-alloc device tree node.\n");
+ goto cleanup;
+ }
+ *align = *align_p;
+ rc = 0;
+
+ eda_debug("cleanup");
+
+cleanup:
+ of_node_put(np);
+ return rc;
+
+}
+#else
+{
+ return -ENODEV;
+}
+#endif
+
+int eda_dma_info_get(void **vaddr, uint32_t *paddr, uint32_t *size)
+{
+ eda_debug("entry");
+
+ if (!dma_vaddr)
+ return -ENOMEM;
+
+ if (!vaddr || !paddr || !size)
+ return -EINVAL;
+
+ *vaddr = dma_vaddr;
+ *paddr = (uint32_t) virt_to_phys(dma_vaddr);
+ *size = dma_size;
+
+ eda_debug("returning -- dma_vaddr: 0x%pK, dma_paddr: 0x%08x,"
+ " size: 0x%08x", *vaddr, *paddr, *size);
+
+ return 0;
+}
+EXPORT_SYMBOL(eda_dma_info_get);
+
+int __init eda_init(void)
+{
+ int rc = 0;
+
+ if (eda_cmdline) {
+ if (!dma_align)
+ dma_align = CONFIG_EDA_DEF_ALIGN;
+ if (!dma_size)
+ dma_size = CONFIG_EDA_DEF_SIZE;
+ eda_debug("size & alignment came from: kernel cmdline");
+ } else if (!of_eda_init(&dma_size, &dma_align)) {
+ eda_debug("size & alignment came from: open firmware entry");
+ } else {
+ dma_align = CONFIG_EDA_DEF_ALIGN;
+ dma_size = CONFIG_EDA_DEF_SIZE;
+ eda_debug("size & alignment came from: compiled in defaults");
+ }
+
+ dma_vaddr = __alloc_bootmem_low(dma_size, dma_align, 0);
+ /*
+ * enforce EDA's requirement to allocate the memory region below a
+ * 32-bit limit.
+ */
+ if (virt_to_phys(dma_vaddr) > 0xFFFFFFFFULL) {
+ rc = -ENOMEM;
+ printk(KERN_ERR "ERROR: DMA memory beyond 32-bit address"
+ " space not supported.\n");
+ goto cleanup;
+ }
+
+ eda_info("dma_vaddr: 0x%pK, dma_paddr: 0x%016llx, size: 0x%08x,"
+ " alignment: 0x%08x",
+ dma_vaddr, (unsigned long long) virt_to_phys(dma_vaddr),
+ dma_size, dma_align);
+cleanup:
+ if (rc && dma_vaddr) {
+ free_bootmem(dma_vaddr, dma_size);
+ }
+ if (rc) {
+ dma_vaddr = NULL;
+ dma_size = 0;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(eda_init);
diff --git a/include/linux/early_dma_alloc.h b/include/linux/early_dma_alloc.h
new file mode 100644
index 0000000..a6d87ec
--- /dev/null
+++ b/include/linux/early_dma_alloc.h
@@ -0,0 +1,36 @@
+/*
+ * Early DMA Memory Allocator
+ *
+ * Copyright © 2013 Cumulus Networks, Inc.
+ *
+ * Author: Curt Brune <curt@cumulusnetworks.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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef EARLY_DMA_ALLOC_H__
+#define EARLY_DMA_ALLOC_H__
+
+#ifdef __KERNEL__
+
+#include <linux/types.h>
+
+extern int eda_init(void);
+extern int eda_dma_info_get(void** vaddr, uint32_t* paddr, uint32_t* size);
+
+#endif /* __KERNEL__ */
+
+#endif /* EARLY_DMA_ALLOC_H__ */

View File

@@ -1,345 +0,0 @@
Create eeprom_dev hardware class for EEPROM devices
Create a new hardware class under /sys/class/eeprom_dev
EEPROM drivers can register their devices with the eeprom_dev class
during instantiation.
The registered devices show up as:
/sys/class/eeprom_dev/eeprom0
/sys/class/eeprom_dev/eeprom1
...
/sys/class/eeprom_dev/eeprom[N]
Each member of the eeprom class exports a sysfs file called "label",
containing the label property from the corresponding device tree node.
Example:
/sys/class/eeprom_dev/eeprom0/label
If the device tree node property "label" does not exist the value
"unknown" is used.
Userspace can use the label to identify what the EEPROM is for.
The real device is available from the class device via the "device"
link:
/sys/class/eeprom_dev/eeprom0/device
diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig
index 701edf6..08c7a23 100644
--- a/drivers/misc/eeprom/Kconfig
+++ b/drivers/misc/eeprom/Kconfig
@@ -1,5 +1,16 @@
menu "EEPROM support"
+config EEPROM_CLASS
+ tristate "EEPROM Hardware Class support"
+ depends on SYSFS
+ default y
+ help
+ Creates a hardware class in sysfs called "eeprom_dev",
+ providing a common place to register EEPROM devices.
+
+ This support can also be built as a module. If so, the module
+ will be called eeprom_class.
+
config EEPROM_AT24
tristate "I2C EEPROMs from most vendors"
depends on I2C && SYSFS
diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile
index fc1e81d..eabb373 100644
--- a/drivers/misc/eeprom/Makefile
+++ b/drivers/misc/eeprom/Makefile
@@ -1,3 +1,4 @@
+obj-$(CONFIG_EEPROM_CLASS) += eeprom_class.o
obj-$(CONFIG_EEPROM_AT24) += at24.o
obj-$(CONFIG_EEPROM_AT25) += at25.o
obj-$(CONFIG_EEPROM_LEGACY) += eeprom.o
diff --git a/drivers/misc/eeprom/eeprom_class.c b/drivers/misc/eeprom/eeprom_class.c
new file mode 100644
index 0000000..aecb778
--- /dev/null
+++ b/drivers/misc/eeprom/eeprom_class.c
@@ -0,0 +1,193 @@
+/*
+ * eeprom_class.c
+ *
+ * This file defines the sysfs class "eeprom", for use by EEPROM
+ * drivers.
+ *
+ * Copyright (C) 2013 Cumulus Networks, Inc.
+ * Author: Curt Brune <curt@cumulusnetworks.com>
+ *
+ * Ideas and structure graciously borrowed from the hwmon class:
+ * Copyright (C) 2005 Mark M. Hoffman <mhoffman@lightlink.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 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kdev_t.h>
+#include <linux/idr.h>
+#include <linux/eeprom_class.h>
+#include <linux/gfp.h>
+#include <linux/spinlock.h>
+#include <linux/pci.h>
+#include <linux/of.h>
+
+/* Root eeprom "class" object (corresponds to '/<sysfs>/class/eeprom_dev/') */
+static struct class *eeprom_class;
+
+#define EEPROM_CLASS_NAME "eeprom_dev"
+#define EEPROM_ID_PREFIX "eeprom"
+#define EEPROM_ID_FORMAT EEPROM_ID_PREFIX "%d"
+
+static DEFINE_IDA(eeprom_ida);
+
+/**
+ * eeprom_device_register - register w/ eeprom class
+ * @dev: the device to register
+ * @data: platform data to use for the device
+ *
+ * eeprom_device_unregister() must be called when the device is no
+ * longer needed.
+ *
+ * Creates a new eeprom class device that is a child of @dev. Also
+ * creates a symlink in /<sysfs>/class/eeprom_dev/eeprom[N] pointing
+ * to the new device.
+ *
+ * Returns the pointer to the new device.
+ */
+struct eeprom_device *eeprom_device_register(struct device *dev, struct eeprom_platform_data *data)
+{
+ struct eeprom_device *eeprom_dev;
+ int id;
+ int ret;
+
+ id = ida_simple_get(&eeprom_ida, 0, 0, GFP_KERNEL);
+ if (id < 0)
+ return ERR_PTR(id);
+
+ eeprom_dev = kzalloc(sizeof(struct eeprom_device), GFP_KERNEL);
+ if (!eeprom_dev) {
+ ret = -ENOMEM;
+ goto err_ida;
+ }
+
+ eeprom_dev->dev = device_create(eeprom_class, dev, MKDEV(0, 0),
+ eeprom_dev, EEPROM_ID_FORMAT, id);
+ if (IS_ERR(eeprom_dev->dev)) {
+ ret = PTR_ERR(eeprom_dev->dev);
+ goto err_eeprom_dev_free;
+ }
+
+ eeprom_dev->data = data;
+
+ return eeprom_dev;
+
+err_eeprom_dev_free:
+ kfree(eeprom_dev);
+
+err_ida:
+ ida_simple_remove(&eeprom_ida, id);
+ return ERR_PTR(ret);
+}
+
+/**
+ * eeprom_device_unregister - removes the previously registered class device
+ *
+ * @eeprom: the eeprom class device to destroy
+ */
+void eeprom_device_unregister(struct eeprom_device *eeprom_dev)
+{
+ int id;
+
+ if (likely(sscanf(dev_name(eeprom_dev->dev), EEPROM_ID_FORMAT, &id) == 1)) {
+ device_unregister(eeprom_dev->dev);
+ kfree(eeprom_dev);
+ ida_simple_remove(&eeprom_ida, id);
+ } else
+ dev_dbg(eeprom_dev->dev->parent,
+ "eeprom_device_unregister() failed: bad class ID!\n");
+}
+
+/**
+ * Each member of the eeprom class exports a sysfs file called
+ * "label", containing the label property from the corresponding
+ * device tree node.
+ *
+ * Userspace can use the label to identify what the EEPROM is for.
+ */
+static ssize_t label_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct eeprom_device *eeprom_dev = (struct eeprom_device *)dev_get_drvdata(dev);
+ const char* cp = NULL;
+ int len = 0;
+
+ /* Check if the eeprom device has an explicit label:
+ * - explicitly passed in to eeprom_device_register()
+ * - explicitly passed via the device tree node
+ *
+ * Otherwise use "unknown".
+ */
+ if (eeprom_dev->data && eeprom_dev->data->label) {
+ cp = eeprom_dev->data->label;
+ len = strlen(cp) + 1;
+ } else {
+ /*
+ * Check for a device tree property.
+ *
+ * The class device is a child of the original device,
+ * i.e. dev->parent points to the original device.
+ */
+ if (dev->parent && dev->parent->of_node)
+ cp = of_get_property(dev->parent->of_node, "label", &len);
+ }
+
+ if ((cp == NULL) || (len == 0)) {
+ cp = "unknown";
+ len = strlen(cp) + 1;
+ }
+
+ strncpy(buf, cp, len - 1);
+ buf[len - 1] = '\n';
+ buf[len] = '\0';
+
+ return len;
+}
+
+struct device_attribute eeprom_class_dev_attrs[] = {
+ __ATTR_RO(label),
+ __ATTR_NULL,
+};
+
+static int __init eeprom_init(void)
+{
+ eeprom_class = class_create(THIS_MODULE, EEPROM_CLASS_NAME);
+ if (IS_ERR(eeprom_class)) {
+ pr_err("couldn't create sysfs class\n");
+ return PTR_ERR(eeprom_class);
+ }
+
+ eeprom_class->dev_attrs = eeprom_class_dev_attrs;
+
+ return 0;
+}
+
+static void __exit eeprom_exit(void)
+{
+ class_destroy(eeprom_class);
+}
+
+subsys_initcall(eeprom_init);
+module_exit(eeprom_exit);
+
+EXPORT_SYMBOL_GPL(eeprom_device_register);
+EXPORT_SYMBOL_GPL(eeprom_device_unregister);
+
+MODULE_AUTHOR("Curt Brune <curt@cumulusnetworks.com>");
+MODULE_DESCRIPTION("eeprom sysfs/class support");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/eeprom_class.h b/include/linux/eeprom_class.h
new file mode 100644
index 0000000..d21d350
--- /dev/null
+++ b/include/linux/eeprom_class.h
@@ -0,0 +1,79 @@
+/*
+ * eeprom_class.h
+ *
+ * This file exports interface functions for the sysfs class "eeprom",
+ * for use by EEPROM drivers.
+ *
+ * Copyright (C) 2013 Cumulus Networks, Inc.
+ * Author: Curt Brune <curt@cumulusnetworks.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 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef EEPROM_CLASS_H__
+#define EEPROM_CLASS_H__
+
+#include <linux/device.h>
+#include <linux/err.h>
+
+/*
+ * Extra platform data used by the eeprom class
+ *
+ * An eeprom device can include this structure in its own platform
+ * data structure.
+ *
+ * A specific platform can set the values in this structure to values
+ * suitable for that platform.
+ *
+ */
+struct eeprom_platform_data {
+ char *label; /* device label to use with the eeprom class */
+};
+
+/*
+ * EEPROM device structure
+ *
+ * This structure is used by the eeprom_class driver to manage the
+ * state of the class device.
+ *
+ */
+struct eeprom_device {
+ struct device *dev;
+ struct eeprom_platform_data *data;
+};
+
+#if defined(CONFIG_EEPROM_CLASS) || defined (CONFIG_EEPROM_CLASS_MODULE)
+
+extern struct eeprom_device *
+eeprom_device_register(struct device *dev, struct eeprom_platform_data *data);
+extern void
+eeprom_device_unregister(struct eeprom_device *eeprom_dev);
+
+#else
+
+static inline struct eeprom_device *
+eeprom_device_register(struct device *dev, char *label)
+{
+ return NULL;
+}
+
+static inline void
+eeprom_device_unregister(struct eeprom_device *eeprom_dev)
+{
+}
+
+#endif
+
+#endif /* EEPROM_CLASS_H__ */

View File

@@ -1,16 +0,0 @@
The P2020 platforms have a broken timeout for the ESDHC device.
This patch forces the driver to use the maximum value.
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index 01e5f62..92c86bf 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -98,7 +98,7 @@ static struct sdhci_ops sdhci_esdhc_ops = {
static struct sdhci_pltfm_data sdhci_esdhc_pdata = {
/* card detection could be handled via GPIO */
.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION
- | SDHCI_QUIRK_NO_CARD_NO_RESET,
+ | SDHCI_QUIRK_NO_CARD_NO_RESET | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
.ops = &sdhci_esdhc_ops,
};

View File

@@ -1,233 +0,0 @@
Back port gpio-sch.c driver for the Intel Centerton (Atom)
Back port the current gpio-sch.c driver from linux-stable, adding
support for the Intel Centerton (Atom) GPIO controller.
Also pull in the related PCI device ID.
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 2870b99..410e264 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -154,13 +154,13 @@ config GPIO_VR41XX
Say yes here to support the NEC VR4100 series General-purpose I/O Uint
config GPIO_SCH
- tristate "Intel SCH/TunnelCreek GPIO"
+ tristate "Intel SCH/TunnelCreek/Centerton GPIO"
depends on PCI && X86
select MFD_CORE
select LPC_SCH
help
- Say yes here to support GPIO interface on Intel Poulsbo SCH
- or Intel Tunnel Creek processor.
+ Say yes here to support GPIO interface on Intel Poulsbo SCH,
+ Intel Tunnel Creek processor or Intel Centerton processor.
The Intel SCH contains a total of 14 GPIO pins. Ten GPIOs are
powered by the core power rail and are turned off during sleep
modes (S3 and higher). The remaining four GPIOs are powered by
@@ -169,6 +169,9 @@ config GPIO_SCH
system from the Suspend-to-RAM state.
The Intel Tunnel Creek processor has 5 GPIOs powered by the
core power rail and 9 from suspend power supply.
+ The Intel Centerton processor has a total of 30 GPIO pins.
+ Twenty-one are powered by the core power rail and 9 from the
+ suspend power supply.
config GPIO_U300
bool "ST-Ericsson U300 COH 901 335/571 GPIO"
diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c
index 1635158..9610ae5 100644
--- a/drivers/gpio/gpio-sch.c
+++ b/drivers/gpio/gpio-sch.c
@@ -125,13 +125,17 @@ static int sch_gpio_resume_direction_in(struct gpio_chip *gc,
unsigned gpio_num)
{
u8 curr_dirs;
+ unsigned short offset, bit;
spin_lock(&gpio_lock);
- curr_dirs = inb(gpio_ba + RGIO);
+ offset = RGIO + gpio_num / 8;
+ bit = gpio_num % 8;
+
+ curr_dirs = inb(gpio_ba + offset);
- if (!(curr_dirs & (1 << gpio_num)))
- outb(curr_dirs | (1 << gpio_num) , gpio_ba + RGIO);
+ if (!(curr_dirs & (1 << bit)))
+ outb(curr_dirs | (1 << bit), gpio_ba + offset);
spin_unlock(&gpio_lock);
return 0;
@@ -139,22 +143,31 @@ static int sch_gpio_resume_direction_in(struct gpio_chip *gc,
static int sch_gpio_resume_get(struct gpio_chip *gc, unsigned gpio_num)
{
- return !!(inb(gpio_ba + RGLV) & (1 << gpio_num));
+ unsigned short offset, bit;
+
+ offset = RGLV + gpio_num / 8;
+ bit = gpio_num % 8;
+
+ return !!(inb(gpio_ba + offset) & (1 << bit));
}
static void sch_gpio_resume_set(struct gpio_chip *gc,
unsigned gpio_num, int val)
{
u8 curr_vals;
+ unsigned short offset, bit;
spin_lock(&gpio_lock);
- curr_vals = inb(gpio_ba + RGLV);
+ offset = RGLV + gpio_num / 8;
+ bit = gpio_num % 8;
+
+ curr_vals = inb(gpio_ba + offset);
if (val)
- outb(curr_vals | (1 << gpio_num), gpio_ba + RGLV);
+ outb(curr_vals | (1 << bit), gpio_ba + offset);
else
- outb((curr_vals & ~(1 << gpio_num)), gpio_ba + RGLV);
+ outb((curr_vals & ~(1 << bit)), gpio_ba + offset);
spin_unlock(&gpio_lock);
}
@@ -163,14 +176,18 @@ static int sch_gpio_resume_direction_out(struct gpio_chip *gc,
unsigned gpio_num, int val)
{
u8 curr_dirs;
+ unsigned short offset, bit;
sch_gpio_resume_set(gc, gpio_num, val);
+ offset = RGIO + gpio_num / 8;
+ bit = gpio_num % 8;
+
spin_lock(&gpio_lock);
- curr_dirs = inb(gpio_ba + RGIO);
- if (curr_dirs & (1 << gpio_num))
- outb(curr_dirs & ~(1 << gpio_num), gpio_ba + RGIO);
+ curr_dirs = inb(gpio_ba + offset);
+ if (curr_dirs & (1 << bit))
+ outb(curr_dirs & ~(1 << bit), gpio_ba + offset);
spin_unlock(&gpio_lock);
return 0;
@@ -185,7 +202,7 @@ static struct gpio_chip sch_gpio_resume = {
.set = sch_gpio_resume_set,
};
-static int __devinit sch_gpio_probe(struct platform_device *pdev)
+static int sch_gpio_probe(struct platform_device *pdev)
{
struct resource *res;
int err, id;
@@ -204,36 +221,41 @@ static int __devinit sch_gpio_probe(struct platform_device *pdev)
gpio_ba = res->start;
switch (id) {
- case PCI_DEVICE_ID_INTEL_SCH_LPC:
- sch_gpio_core.base = 0;
- sch_gpio_core.ngpio = 10;
-
- sch_gpio_resume.base = 10;
- sch_gpio_resume.ngpio = 4;
-
- /*
- * GPIO[6:0] enabled by default
- * GPIO7 is configured by the CMC as SLPIOVR
- * Enable GPIO[9:8] core powered gpios explicitly
- */
- outb(0x3, gpio_ba + CGEN + 1);
- /*
- * SUS_GPIO[2:0] enabled by default
- * Enable SUS_GPIO3 resume powered gpio explicitly
- */
- outb(0x8, gpio_ba + RGEN);
- break;
-
- case PCI_DEVICE_ID_INTEL_ITC_LPC:
- sch_gpio_core.base = 0;
- sch_gpio_core.ngpio = 5;
-
- sch_gpio_resume.base = 5;
- sch_gpio_resume.ngpio = 9;
- break;
-
- default:
- return -ENODEV;
+ case PCI_DEVICE_ID_INTEL_SCH_LPC:
+ sch_gpio_core.base = 0;
+ sch_gpio_core.ngpio = 10;
+ sch_gpio_resume.base = 10;
+ sch_gpio_resume.ngpio = 4;
+ /*
+ * GPIO[6:0] enabled by default
+ * GPIO7 is configured by the CMC as SLPIOVR
+ * Enable GPIO[9:8] core powered gpios explicitly
+ */
+ outb(0x3, gpio_ba + CGEN + 1);
+ /*
+ * SUS_GPIO[2:0] enabled by default
+ * Enable SUS_GPIO3 resume powered gpio explicitly
+ */
+ outb(0x8, gpio_ba + RGEN);
+ break;
+
+ case PCI_DEVICE_ID_INTEL_ITC_LPC:
+ sch_gpio_core.base = 0;
+ sch_gpio_core.ngpio = 5;
+ sch_gpio_resume.base = 5;
+ sch_gpio_resume.ngpio = 9;
+ break;
+
+ case PCI_DEVICE_ID_INTEL_CENTERTON_ILB:
+ sch_gpio_core.base = 0;
+ sch_gpio_core.ngpio = 21;
+ sch_gpio_resume.base = 21;
+ sch_gpio_resume.ngpio = 9;
+ break;
+
+ default:
+ err = -ENODEV;
+ goto err_sch_gpio_core;
}
sch_gpio_core.dev = &pdev->dev;
@@ -250,10 +272,8 @@ static int __devinit sch_gpio_probe(struct platform_device *pdev)
return 0;
err_sch_gpio_resume:
- err = gpiochip_remove(&sch_gpio_core);
- if (err)
- dev_err(&pdev->dev, "%s failed, %d\n",
- "gpiochip_remove()", err);
+ if (gpiochip_remove(&sch_gpio_core))
+ dev_err(&pdev->dev, "%s gpiochip_remove failed\n", __func__);
err_sch_gpio_core:
release_region(res->start, resource_size(res));
@@ -262,7 +282,7 @@ err_sch_gpio_core:
return err;
}
-static int __devexit sch_gpio_remove(struct platform_device *pdev)
+static int sch_gpio_remove(struct platform_device *pdev)
{
struct resource *res;
if (gpio_ba) {
@@ -294,7 +314,7 @@ static struct platform_driver sch_gpio_driver = {
.owner = THIS_MODULE,
},
.probe = sch_gpio_probe,
- .remove = __devexit_p(sch_gpio_remove),
+ .remove = sch_gpio_remove,
};
static int __init sch_gpio_init(void)

View File

@@ -1,22 +0,0 @@
driver-gpio-mpc8xxx-do-not-set-IRQ_TYPE_NONE
Apply upstream patch to avoid harmless, but annoying error messages
when setting GPIO via sysfs.
See commit de0ccf788147440eee2383c74408080f3ff0a43b in the stable
kernel tree. Or this thread:
https://lkml.org/lkml/2013/2/4/77
diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c
index 34be13b..60e259f 100644
--- a/drivers/gpio/gpio-mpc8xxx.c
+++ b/drivers/gpio/gpio-mpc8xxx.c
@@ -296,7 +296,6 @@ static int mpc8xxx_gpio_irq_map(struct irq_host *h, unsigned int virq,
irq_set_chip_data(virq, h->host_data);
irq_set_chip_and_handler(virq, &mpc8xxx_irq_chip, handle_level_irq);
- irq_set_irq_type(virq, IRQ_TYPE_NONE);
return 0;
}

View File

@@ -1,47 +0,0 @@
driver hwmon adt7475 clear pwm invert bit
The Accton 4654, the only system using this driver, has a problem
where sometimes the power on value of the PWM control registers
(registers 0x5C,0x5D,0x5E) differs from the default. Sometimes the
"PWM invert" bit is set incorrectly, it should be clear for normal
operations.
With the improper setting the fans will not spin.
This patch clears the "PWM invert" bit every time the PWM value is
updated via sysfs.
Clear PWM invert bit, i.e. force normal sense of duty cycle. See
ADT7473 data sheet for description of register 0x5C, bit 4:
This bit inverts the PWM output. The default is 0, which corresponds
to a logic high output for 100% duty cycle. Setting this bit to 1
inverts the PWM output, so 100% duty cycle corresponds to a logic
low output.
diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c
index b5fcd87..d460f4e 100644
--- a/drivers/hwmon/adt7475.c
+++ b/drivers/hwmon/adt7475.c
@@ -671,6 +671,21 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
return count;
}
+ /* Clear PWM invert bit, i.e. force normal sense of
+ * duty cycle. See ADT7473 data sheet for description
+ * of register 0x5C, bit 4:
+ *
+ * This bit inverts the PWM output. The default is
+ * 0, which corresponds to a logic high output for
+ * 100% duty cycle. Setting this bit to 1 inverts
+ * the PWM output, so 100% duty cycle corresponds to
+ * a logic low output.
+ *
+ */
+ data->pwm[CONTROL][sattr->index] &= ~(1 << 4);
+ i2c_smbus_write_byte_data(client, PWM_CONFIG_REG(sattr->index),
+ data->pwm[CONTROL][sattr->index]);
+
reg = PWM_REG(sattr->index);
break;

View File

@@ -1,190 +0,0 @@
The driver only fills the most significant 8 bits of the fan tach
count (11 bit value). Fixing the driver to use all of 11 bits for
more accuracy.
diff --git a/drivers/hwmon/max6620.c b/drivers/hwmon/max6620.c
index 3c337c7..76c1f7f 100644
--- a/drivers/hwmon/max6620.c
+++ b/drivers/hwmon/max6620.c
@@ -46,6 +46,8 @@
/* clock: The clock frequency of the chip the driver should assume */
static int clock = 8192;
+static u32 sr = 2;
+static u32 np = 2;
module_param(clock, int, S_IRUGO);
@@ -213,22 +215,22 @@ static ssize_t get_fan(struct device *dev, struct device_attribute *devattr, cha
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct max6620_data *data = max6620_update_device(dev);
- int rpm;
-
- /*
- * Calculation details:
- *
- * Each tachometer counts over an interval given by the "count"
- * register (0.25, 0.5, 1 or 2 seconds). This module assumes
- * that the fans produce two pulses per revolution (this seems
- * to be the most common).
- */
- if(data->tach[attr->index] == 0 || data->tach[attr->index] == 255) {
+ struct i2c_client *client = to_i2c_client(dev);
+ u32 rpm = 0;
+ u32 tach = 0;
+ u32 tach1 = 0;
+ u32 tach2 = 0;
+
+ tach1 = i2c_smbus_read_byte_data(client, tach_reg[attr->index]);
+ tach1 = (tach1 << 3) & 0x7f8;
+ tach2 = i2c_smbus_read_byte_data(client, tach_reg[attr->index] + 1);
+ tach2 = (tach2 >> 5) & 0x7;
+ tach = tach1 | tach2;
+ if (tach == 0) {
rpm = 0;
} else {
- rpm = ((clock / (data->tach[attr->index] << 3)) * 30 * DIV_FROM_REG(data->fandyn[attr->index]));
+ rpm = (60 * sr * clock)/(tach * np);
}
-
return sprintf(buf, "%d\n", rpm);
}
@@ -236,22 +238,21 @@ static ssize_t get_target(struct device *dev, struct device_attribute *devattr,
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct max6620_data *data = max6620_update_device(dev);
- int kscale, ktach, rpm;
-
- /*
- * Use the datasheet equation:
- *
- * FanSpeed = KSCALE x fCLK / [256 x (KTACH + 1)]
- *
- * then multiply by 60 to give rpm.
- */
-
- kscale = DIV_FROM_REG(data->fandyn[attr->index]);
- ktach = data->target[attr->index];
- if(ktach == 0) {
+ struct i2c_client *client = to_i2c_client(dev);
+ u32 rpm;
+ u32 target;
+ u32 target1;
+ u32 target2;
+
+ target1 = i2c_smbus_read_byte_data(client, target_reg[attr->index]);
+ target1 = (target1 << 3) & 0x7f8;
+ target2 = i2c_smbus_read_byte_data(client, target_reg[attr->index] + 1);
+ target2 = (target2 >> 5) & 0x7;
+ target = target1 | target2;
+ if (target == 0) {
rpm = 0;
} else {
- rpm = ((60 * kscale * clock) / (ktach << 3));
+ rpm = (60 * sr * clock)/(target * np);
}
return sprintf(buf, "%d\n", rpm);
}
@@ -261,9 +262,11 @@ static ssize_t set_target(struct device *dev, struct device_attribute *devattr,
struct i2c_client *client = to_i2c_client(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct max6620_data *data = i2c_get_clientdata(client);
- int kscale, ktach;
- unsigned long rpm;
+ u32 rpm;
int err;
+ u32 target;
+ u32 target1;
+ u32 target2;
err = kstrtoul(buf, 10, &rpm);
if (err)
@@ -271,25 +274,13 @@ static ssize_t set_target(struct device *dev, struct device_attribute *devattr,
rpm = SENSORS_LIMIT(rpm, FAN_RPM_MIN, FAN_RPM_MAX);
- /*
- * Divide the required speed by 60 to get from rpm to rps, then
- * use the datasheet equation:
- *
- * KTACH = [(fCLK x KSCALE) / (256 x FanSpeed)] - 1
- */
-
mutex_lock(&data->update_lock);
- kscale = DIV_FROM_REG(data->fandyn[attr->index]);
- ktach = ((60 * kscale * clock) / rpm);
- if (ktach < 0)
- ktach = 0;
- if (ktach > 255)
- ktach = 255;
- data->target[attr->index] = ktach;
-
- i2c_smbus_write_byte_data(client, target_reg[attr->index], data->target[attr->index]);
- i2c_smbus_write_byte_data(client, target_reg[attr->index]+0x01, 0x00);
+ target = (60 * sr * 8192)/(rpm * np);
+ target1 = (target >> 3) & 0xff;
+ target2 = (target << 5) & 0xe0;
+ i2c_smbus_write_byte_data(client, target_reg[attr->index], target1);
+ i2c_smbus_write_byte_data(client, target_reg[attr->index] + 1, target2);
mutex_unlock(&data->update_lock);
@@ -609,8 +600,11 @@ static int max6620_init_client(struct i2c_client *client) {
}
-
- if (i2c_smbus_write_byte_data(client, MAX6620_REG_CONFIG, config)) {
+ /*
+ * Set bit 4, disable other fans from going full speed on a fail
+ * failure.
+ */
+ if (i2c_smbus_write_byte_data(client, MAX6620_REG_CONFIG, config | 0x10)) {
dev_err(&client->dev, "Config write error, aborting.\n");
return err;
}
@@ -618,28 +612,21 @@ static int max6620_init_client(struct i2c_client *client) {
data->config = config;
for (i = 0; i < 4; i++) {
data->fancfg[i] = i2c_smbus_read_byte_data(client, config_reg[i]);
- data->fancfg[i] |= 0x80; // enable TACH monitoring
+ data->fancfg[i] |= 0xa8; // enable TACH monitoring
i2c_smbus_write_byte_data(client, config_reg[i], data->fancfg[i]);
data->fandyn[i] = i2c_smbus_read_byte_data(client, dyn_reg[i]);
- data-> fandyn[i] |= 0x1C;
+ /* 2 counts (001) and Rate change 100 (0.125 secs) */
+ data-> fandyn[i] = 0x30;
i2c_smbus_write_byte_data(client, dyn_reg[i], data->fandyn[i]);
data->tach[i] = i2c_smbus_read_byte_data(client, tach_reg[i]);
data->volt[i] = i2c_smbus_read_byte_data(client, volt_reg[i]);
data->target[i] = i2c_smbus_read_byte_data(client, target_reg[i]);
data->dac[i] = i2c_smbus_read_byte_data(client, dac_reg[i]);
-
-
}
-
-
-
return 0;
}
-
-
-
static struct max6620_data *max6620_update_device(struct device *dev)
{
int i;
@@ -678,7 +665,7 @@ static struct max6620_data *max6620_update_device(struct device *dev)
return data;
}
-module_i2c_driver(max6620_driver);
+// module_i2c_driver(max6620_driver);
static int __init max6620_init(void)
{

View File

@@ -1,743 +0,0 @@
Driver for MAX6620 Fan sensor
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 33b8c3a..5c984a6 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -832,6 +832,16 @@ config SENSORS_MAX6650
This driver can also be built as a module. If so, the module
will be called max6650.
+config SENSORS_MAX6620
+ tristate "Maxim MAX6620 sensor chip"
+ depends on I2C && EXPERIMENTAL
+ help
+ If you say yes here you get support for the MAX6620
+ sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called max6620.
+
config SENSORS_MAX6697
tristate "Maxim MAX6697 and compatibles"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 64c587d..ff3a18e 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -97,6 +97,7 @@ obj-$(CONFIG_SENSORS_MAX1668) += max1668.o
obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
+obj-$(CONFIG_SENSORS_MAX6620) += max6620.o
obj-$(CONFIG_SENSORS_MAX6697) += max6697.o
obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o
diff --git a/drivers/hwmon/max6620.c b/drivers/hwmon/max6620.c
new file mode 100644
index 0000000..3c337c7
--- /dev/null
+++ b/drivers/hwmon/max6620.c
@@ -0,0 +1,702 @@
+/*
+ * max6620.c - Linux Kernel module for hardware monitoring.
+ *
+ * (C) 2012 by L. Grunenberg <contact@lgrunenberg.de>
+ *
+ * based on code written by :
+ * 2007 by Hans J. Koch <hjk@hansjkoch.de>
+ * John Morris <john.morris@spirentcom.com>
+ * Copyright (c) 2003 Spirent Communications
+ * and Claus Gindhart <claus.gindhart@kontron.com>
+ *
+ * This module has only been tested with the MAX6620 chip.
+ *
+ * The datasheet was last seen at:
+ *
+ * http://pdfserv.maxim-ic.com/en/ds/MAX6620.pdf
+ *
+ * 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+
+/*
+ * Insmod parameters
+ */
+
+
+/* clock: The clock frequency of the chip the driver should assume */
+static int clock = 8192;
+
+module_param(clock, int, S_IRUGO);
+
+static const unsigned short normal_i2c[] = {0x0a, 0x1a, 0x2a, I2C_CLIENT_END};
+
+/*
+ * MAX 6620 registers
+ */
+
+#define MAX6620_REG_CONFIG 0x00
+#define MAX6620_REG_FAULT 0x01
+#define MAX6620_REG_CONF_FAN0 0x02
+#define MAX6620_REG_CONF_FAN1 0x03
+#define MAX6620_REG_CONF_FAN2 0x04
+#define MAX6620_REG_CONF_FAN3 0x05
+#define MAX6620_REG_DYN_FAN0 0x06
+#define MAX6620_REG_DYN_FAN1 0x07
+#define MAX6620_REG_DYN_FAN2 0x08
+#define MAX6620_REG_DYN_FAN3 0x09
+#define MAX6620_REG_TACH0 0x10
+#define MAX6620_REG_TACH1 0x12
+#define MAX6620_REG_TACH2 0x14
+#define MAX6620_REG_TACH3 0x16
+#define MAX6620_REG_VOLT0 0x18
+#define MAX6620_REG_VOLT1 0x1A
+#define MAX6620_REG_VOLT2 0x1C
+#define MAX6620_REG_VOLT3 0x1E
+#define MAX6620_REG_TAR0 0x20
+#define MAX6620_REG_TAR1 0x22
+#define MAX6620_REG_TAR2 0x24
+#define MAX6620_REG_TAR3 0x26
+#define MAX6620_REG_DAC0 0x28
+#define MAX6620_REG_DAC1 0x2A
+#define MAX6620_REG_DAC2 0x2C
+#define MAX6620_REG_DAC3 0x2E
+
+/*
+ * Config register bits
+ */
+
+#define MAX6620_CFG_RUN 0x80
+#define MAX6620_CFG_POR 0x40
+#define MAX6620_CFG_TIMEOUT 0x20
+#define MAX6620_CFG_FULLFAN 0x10
+#define MAX6620_CFG_OSC 0x08
+#define MAX6620_CFG_WD_MASK 0x06
+#define MAX6620_CFG_WD_2 0x02
+#define MAX6620_CFG_WD_6 0x04
+#define MAX6620_CFG_WD10 0x06
+#define MAX6620_CFG_WD 0x01
+
+
+/*
+ * Failure status register bits
+ */
+
+#define MAX6620_FAIL_TACH0 0x10
+#define MAX6620_FAIL_TACH1 0x20
+#define MAX6620_FAIL_TACH2 0x40
+#define MAX6620_FAIL_TACH3 0x80
+#define MAX6620_FAIL_MASK0 0x01
+#define MAX6620_FAIL_MASK1 0x02
+#define MAX6620_FAIL_MASK2 0x04
+#define MAX6620_FAIL_MASK3 0x08
+
+
+/* Minimum and maximum values of the FAN-RPM */
+#define FAN_RPM_MIN 240
+#define FAN_RPM_MAX 30000
+
+#define DIV_FROM_REG(reg) (1 << ((reg & 0xE0) >> 5))
+
+static int max6620_probe(struct i2c_client *client, const struct i2c_device_id *id);
+static int max6620_init_client(struct i2c_client *client);
+static int max6620_remove(struct i2c_client *client);
+static struct max6620_data *max6620_update_device(struct device *dev);
+
+static const u8 config_reg[] = {
+ MAX6620_REG_CONF_FAN0,
+ MAX6620_REG_CONF_FAN1,
+ MAX6620_REG_CONF_FAN2,
+ MAX6620_REG_CONF_FAN3,
+};
+
+static const u8 dyn_reg[] = {
+ MAX6620_REG_DYN_FAN0,
+ MAX6620_REG_DYN_FAN1,
+ MAX6620_REG_DYN_FAN2,
+ MAX6620_REG_DYN_FAN3,
+};
+
+static const u8 tach_reg[] = {
+ MAX6620_REG_TACH0,
+ MAX6620_REG_TACH1,
+ MAX6620_REG_TACH2,
+ MAX6620_REG_TACH3,
+};
+
+static const u8 volt_reg[] = {
+ MAX6620_REG_VOLT0,
+ MAX6620_REG_VOLT1,
+ MAX6620_REG_VOLT2,
+ MAX6620_REG_VOLT3,
+};
+
+static const u8 target_reg[] = {
+ MAX6620_REG_TAR0,
+ MAX6620_REG_TAR1,
+ MAX6620_REG_TAR2,
+ MAX6620_REG_TAR3,
+};
+
+static const u8 dac_reg[] = {
+ MAX6620_REG_DAC0,
+ MAX6620_REG_DAC1,
+ MAX6620_REG_DAC2,
+ MAX6620_REG_DAC3,
+};
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static const struct i2c_device_id max6620_id[] = {
+ { "max6620", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max6620_id);
+
+static struct i2c_driver max6620_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "max6620",
+ },
+ .probe = max6620_probe,
+ .remove = __devexit_p(max6620_remove),
+ .id_table = max6620_id,
+ .address_list = normal_i2c,
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct max6620_data {
+ struct device *hwmon_dev;
+ struct mutex update_lock;
+ int nr_fans;
+ char valid; /* zero until following fields are valid */
+ unsigned long last_updated; /* in jiffies */
+
+ /* register values */
+ u8 speed[4];
+ u8 config;
+ u8 fancfg[4];
+ u8 fandyn[4];
+ u8 tach[4];
+ u8 volt[4];
+ u8 target[4];
+ u8 dac[4];
+ u8 fault;
+};
+
+static ssize_t get_fan(struct device *dev, struct device_attribute *devattr, char *buf) {
+
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct max6620_data *data = max6620_update_device(dev);
+ int rpm;
+
+ /*
+ * Calculation details:
+ *
+ * Each tachometer counts over an interval given by the "count"
+ * register (0.25, 0.5, 1 or 2 seconds). This module assumes
+ * that the fans produce two pulses per revolution (this seems
+ * to be the most common).
+ */
+ if(data->tach[attr->index] == 0 || data->tach[attr->index] == 255) {
+ rpm = 0;
+ } else {
+ rpm = ((clock / (data->tach[attr->index] << 3)) * 30 * DIV_FROM_REG(data->fandyn[attr->index]));
+ }
+
+ return sprintf(buf, "%d\n", rpm);
+}
+
+static ssize_t get_target(struct device *dev, struct device_attribute *devattr, char *buf) {
+
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct max6620_data *data = max6620_update_device(dev);
+ int kscale, ktach, rpm;
+
+ /*
+ * Use the datasheet equation:
+ *
+ * FanSpeed = KSCALE x fCLK / [256 x (KTACH + 1)]
+ *
+ * then multiply by 60 to give rpm.
+ */
+
+ kscale = DIV_FROM_REG(data->fandyn[attr->index]);
+ ktach = data->target[attr->index];
+ if(ktach == 0) {
+ rpm = 0;
+ } else {
+ rpm = ((60 * kscale * clock) / (ktach << 3));
+ }
+ return sprintf(buf, "%d\n", rpm);
+}
+
+static ssize_t set_target(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) {
+
+ struct i2c_client *client = to_i2c_client(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct max6620_data *data = i2c_get_clientdata(client);
+ int kscale, ktach;
+ unsigned long rpm;
+ int err;
+
+ err = kstrtoul(buf, 10, &rpm);
+ if (err)
+ return err;
+
+ rpm = SENSORS_LIMIT(rpm, FAN_RPM_MIN, FAN_RPM_MAX);
+
+ /*
+ * Divide the required speed by 60 to get from rpm to rps, then
+ * use the datasheet equation:
+ *
+ * KTACH = [(fCLK x KSCALE) / (256 x FanSpeed)] - 1
+ */
+
+ mutex_lock(&data->update_lock);
+
+ kscale = DIV_FROM_REG(data->fandyn[attr->index]);
+ ktach = ((60 * kscale * clock) / rpm);
+ if (ktach < 0)
+ ktach = 0;
+ if (ktach > 255)
+ ktach = 255;
+ data->target[attr->index] = ktach;
+
+ i2c_smbus_write_byte_data(client, target_reg[attr->index], data->target[attr->index]);
+ i2c_smbus_write_byte_data(client, target_reg[attr->index]+0x01, 0x00);
+
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/*
+ * Get/set the fan speed in open loop mode using pwm1 sysfs file.
+ * Speed is given as a relative value from 0 to 255, where 255 is maximum
+ * speed. Note that this is done by writing directly to the chip's DAC,
+ * it won't change the closed loop speed set by fan1_target.
+ * Also note that due to rounding errors it is possible that you don't read
+ * back exactly the value you have set.
+ */
+
+static ssize_t get_pwm(struct device *dev, struct device_attribute *devattr, char *buf) {
+
+ int pwm;
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct max6620_data *data = max6620_update_device(dev);
+
+ /*
+ * Useful range for dac is 0-180 for 12V fans and 0-76 for 5V fans.
+ * Lower DAC values mean higher speeds.
+ */
+ pwm = ((int)data->volt[attr->index]);
+
+ if (pwm < 0)
+ pwm = 0;
+
+ return sprintf(buf, "%d\n", pwm);
+}
+
+static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) {
+
+ struct i2c_client *client = to_i2c_client(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct max6620_data *data = i2c_get_clientdata(client);
+ unsigned long pwm;
+ int err;
+
+ err = kstrtoul(buf, 10, &pwm);
+ if (err)
+ return err;
+
+ pwm = SENSORS_LIMIT(pwm, 0, 255);
+
+ mutex_lock(&data->update_lock);
+
+ data->dac[attr->index] = pwm;
+
+
+ i2c_smbus_write_byte_data(client, dac_reg[attr->index], data->dac[attr->index]);
+ i2c_smbus_write_byte_data(client, dac_reg[attr->index]+1, 0x00);
+
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/*
+ * Get/Set controller mode:
+ * Possible values:
+ * 0 = Fan always on
+ * 1 = Open loop, Voltage is set according to speed, not regulated.
+ * 2 = Closed loop, RPM for all fans regulated by fan1 tachometer
+ */
+
+static ssize_t get_enable(struct device *dev, struct device_attribute *devattr, char *buf) {
+
+ struct max6620_data *data = max6620_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int mode = (data->fancfg[attr->index] & 0x80 ) >> 7;
+ int sysfs_modes[2] = {1, 2};
+
+ return sprintf(buf, "%d\n", sysfs_modes[mode]);
+}
+
+static ssize_t set_enable(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) {
+
+ struct i2c_client *client = to_i2c_client(dev);
+ struct max6620_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int max6620_modes[3] = {0, 1, 0};
+ unsigned long mode;
+ int err;
+
+ err = kstrtoul(buf, 10, &mode);
+ if (err)
+ return err;
+
+ if (mode > 2)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+
+ data->fancfg[attr->index] = i2c_smbus_read_byte_data(client, config_reg[attr->index]);
+ data->fancfg[attr->index] = (data->fancfg[attr->index] & ~0x80)
+ | (max6620_modes[mode] << 7);
+
+ i2c_smbus_write_byte_data(client, config_reg[attr->index], data->fancfg[attr->index]);
+
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/*
+ * Read/write functions for fan1_div sysfs file. The MAX6620 has no such
+ * divider. We handle this by converting between divider and counttime:
+ *
+ * (counttime == k) <==> (divider == 2^k), k = 0, 1, 2, 3, 4 or 5
+ *
+ * Lower values of k allow to connect a faster fan without the risk of
+ * counter overflow. The price is lower resolution. You can also set counttime
+ * using the module parameter. Note that the module parameter "prescaler" also
+ * influences the behaviour. Unfortunately, there's no sysfs attribute
+ * defined for that. See the data sheet for details.
+ */
+
+static ssize_t get_div(struct device *dev, struct device_attribute *devattr, char *buf) {
+
+ struct max6620_data *data = max6620_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+ return sprintf(buf, "%d\n", DIV_FROM_REG(data->fandyn[attr->index]));
+}
+
+static ssize_t set_div(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) {
+
+ struct i2c_client *client = to_i2c_client(dev);
+ struct max6620_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ unsigned long div;
+ int err;
+ u8 div_bin;
+
+ err = kstrtoul(buf, 10, &div);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ switch (div) {
+ case 1:
+ div_bin = 0;
+ break;
+ case 2:
+ div_bin = 1;
+ break;
+ case 4:
+ div_bin = 2;
+ break;
+ case 8:
+ div_bin = 3;
+ break;
+ case 16:
+ div_bin = 4;
+ break;
+ case 32:
+ div_bin = 5;
+ break;
+ default:
+ mutex_unlock(&data->update_lock);
+ return -EINVAL;
+ }
+ data->fandyn[attr->index] &= 0x1F;
+ data->fandyn[attr->index] |= div_bin << 5;
+ i2c_smbus_write_byte_data(client, dyn_reg[attr->index], data->fandyn[attr->index]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/*
+ * Get alarm stati:
+ * Possible values:
+ * 0 = no alarm
+ * 1 = alarm
+ */
+
+static ssize_t get_alarm(struct device *dev, struct device_attribute *devattr, char *buf) {
+
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct max6620_data *data = max6620_update_device(dev);
+ struct i2c_client *client = to_i2c_client(dev);
+ int alarm = 0;
+
+ if (data->fault & (1 << attr->index)) {
+ mutex_lock(&data->update_lock);
+ alarm = 1;
+ data->fault &= ~(1 << attr->index);
+ data->fault |= i2c_smbus_read_byte_data(client,
+ MAX6620_REG_FAULT);
+ mutex_unlock(&data->update_lock);
+ }
+
+ return sprintf(buf, "%d\n", alarm);
+}
+
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, get_fan, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, get_fan, NULL, 2);
+static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, get_fan, NULL, 3);
+static SENSOR_DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, get_target, set_target, 0);
+static SENSOR_DEVICE_ATTR(fan1_div, S_IWUSR | S_IRUGO, get_div, set_div, 0);
+// static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, get_enable, set_enable, 0);
+static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 0);
+static SENSOR_DEVICE_ATTR(fan2_target, S_IWUSR | S_IRUGO, get_target, set_target, 1);
+static SENSOR_DEVICE_ATTR(fan2_div, S_IWUSR | S_IRUGO, get_div, set_div, 1);
+// static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, get_enable, set_enable, 1);
+static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 1);
+static SENSOR_DEVICE_ATTR(fan3_target, S_IWUSR | S_IRUGO, get_target, set_target, 2);
+static SENSOR_DEVICE_ATTR(fan3_div, S_IWUSR | S_IRUGO, get_div, set_div, 2);
+// static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, get_enable, set_enable, 2);
+static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 2);
+static SENSOR_DEVICE_ATTR(fan4_target, S_IWUSR | S_IRUGO, get_target, set_target, 3);
+static SENSOR_DEVICE_ATTR(fan4_div, S_IWUSR | S_IRUGO, get_div, set_div, 3);
+// static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, get_enable, set_enable, 3);
+static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 3);
+
+static struct attribute *max6620_attrs[] = {
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan4_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_target.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+// &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_fan2_target.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+// &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_fan3_target.dev_attr.attr,
+ &sensor_dev_attr_fan3_div.dev_attr.attr,
+// &sensor_dev_attr_pwm3_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm3.dev_attr.attr,
+ &sensor_dev_attr_fan4_target.dev_attr.attr,
+ &sensor_dev_attr_fan4_div.dev_attr.attr,
+// &sensor_dev_attr_pwm4_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm4.dev_attr.attr,
+ NULL
+};
+
+static struct attribute_group max6620_attr_grp = {
+ .attrs = max6620_attrs,
+};
+
+
+/*
+ * Real code
+ */
+
+static int __devinit max6620_probe(struct i2c_client *client, const struct i2c_device_id *id) {
+
+ struct max6620_data *data;
+ int err;
+
+ data = devm_kzalloc(&client->dev, sizeof(struct max6620_data), GFP_KERNEL);
+ if (!data) {
+ dev_err(&client->dev, "out of memory.\n");
+ return -ENOMEM;
+ }
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+ data->nr_fans = id->driver_data;
+
+ /*
+ * Initialize the max6620 chip
+ */
+ dev_info(&client->dev, "About to initialize module\n");
+
+ err = max6620_init_client(client);
+ if (err)
+ return err;
+ dev_info(&client->dev, "Module initialized\n");
+
+ err = sysfs_create_group(&client->dev.kobj, &max6620_attr_grp);
+ if (err)
+ return err;
+dev_info(&client->dev, "Sysfs entries created\n");
+
+ data->hwmon_dev = hwmon_device_register(&client->dev);
+ if (!IS_ERR(data->hwmon_dev))
+ return 0;
+
+ err = PTR_ERR(data->hwmon_dev);
+ dev_err(&client->dev, "error registering hwmon device.\n");
+
+ sysfs_remove_group(&client->dev.kobj, &max6620_attr_grp);
+ return err;
+}
+
+static int __devexit max6620_remove(struct i2c_client *client) {
+
+ struct max6620_data *data = i2c_get_clientdata(client);
+
+ hwmon_device_unregister(data->hwmon_dev);
+
+ sysfs_remove_group(&client->dev.kobj, &max6620_attr_grp);
+ return 0;
+}
+
+static int max6620_init_client(struct i2c_client *client) {
+
+ struct max6620_data *data = i2c_get_clientdata(client);
+ int config;
+ int err = -EIO;
+ int i;
+
+ config = i2c_smbus_read_byte_data(client, MAX6620_REG_CONFIG);
+
+ if (config < 0) {
+ dev_err(&client->dev, "Error reading config, aborting.\n");
+ return err;
+ }
+
+
+
+ if (i2c_smbus_write_byte_data(client, MAX6620_REG_CONFIG, config)) {
+ dev_err(&client->dev, "Config write error, aborting.\n");
+ return err;
+ }
+
+ data->config = config;
+ for (i = 0; i < 4; i++) {
+ data->fancfg[i] = i2c_smbus_read_byte_data(client, config_reg[i]);
+ data->fancfg[i] |= 0x80; // enable TACH monitoring
+ i2c_smbus_write_byte_data(client, config_reg[i], data->fancfg[i]);
+ data->fandyn[i] = i2c_smbus_read_byte_data(client, dyn_reg[i]);
+ data-> fandyn[i] |= 0x1C;
+ i2c_smbus_write_byte_data(client, dyn_reg[i], data->fandyn[i]);
+ data->tach[i] = i2c_smbus_read_byte_data(client, tach_reg[i]);
+ data->volt[i] = i2c_smbus_read_byte_data(client, volt_reg[i]);
+ data->target[i] = i2c_smbus_read_byte_data(client, target_reg[i]);
+ data->dac[i] = i2c_smbus_read_byte_data(client, dac_reg[i]);
+
+
+
+ }
+
+
+
+ return 0;
+}
+
+
+
+
+static struct max6620_data *max6620_update_device(struct device *dev)
+{
+ int i;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct max6620_data *data = i2c_get_clientdata(client);
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+
+ for (i = 0; i < 4; i++) {
+ data->fancfg[i] = i2c_smbus_read_byte_data(client, config_reg[i]);
+ data->fandyn[i] = i2c_smbus_read_byte_data(client, dyn_reg[i]);
+ data->tach[i] = i2c_smbus_read_byte_data(client, tach_reg[i]);
+ data->volt[i] = i2c_smbus_read_byte_data(client, volt_reg[i]);
+ data->target[i] = i2c_smbus_read_byte_data(client, target_reg[i]);
+ data->dac[i] = i2c_smbus_read_byte_data(client, dac_reg[i]);
+ }
+
+
+ /*
+ * Alarms are cleared on read in case the condition that
+ * caused the alarm is removed. Keep the value latched here
+ * for providing the register through different alarm files.
+ */
+ u8 fault_reg;
+ fault_reg = i2c_smbus_read_byte_data(client, MAX6620_REG_FAULT);
+ data->fault |= (fault_reg >> 4) & (fault_reg & 0x0F);
+
+ data->last_updated = jiffies;
+ data->valid = 1;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+module_i2c_driver(max6620_driver);
+
+static int __init max6620_init(void)
+{
+ return i2c_add_driver(&max6620_driver);
+}
+module_init(max6620_init);
+
+/**
+ * sht21_init() - clean up driver
+ *
+ * Called when module is removed.
+ */
+static void __exit max6620_exit(void)
+{
+ i2c_del_driver(&max6620_driver);
+}
+module_exit(max6620_exit);
+
+MODULE_AUTHOR("Lucas Grunenberg");
+MODULE_DESCRIPTION("MAX6620 sensor driver");
+MODULE_LICENSE("GPL");

View File

@@ -1,856 +0,0 @@
Backport max6697.c from mainline Linux kernel
diff --git a/Documentation/devicetree/bindings/i2c/max6697.txt b/Documentation/devicetree/bindings/i2c/max6697.txt
new file mode 100644
index 0000000..5f79399
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/max6697.txt
@@ -0,0 +1,64 @@
+max6697 properties
+
+Required properties:
+- compatible:
+ Should be one of
+ maxim,max6581
+ maxim,max6602
+ maxim,max6622
+ maxim,max6636
+ maxim,max6689
+ maxim,max6693
+ maxim,max6694
+ maxim,max6697
+ maxim,max6698
+ maxim,max6699
+- reg: I2C address
+
+Optional properties:
+
+- smbus-timeout-disable
+ Set to disable SMBus timeout. If not specified, SMBus timeout will be
+ enabled.
+- extended-range-enable
+ Only valid for MAX6581. Set to enable extended temperature range.
+ Extended temperature will be disabled if not specified.
+- beta-compensation-enable
+ Only valid for MAX6693 and MX6694. Set to enable beta compensation on
+ remote temperature channel 1.
+ Beta compensation will be disabled if not specified.
+- alert-mask
+ Alert bit mask. Alert disabled for bits set.
+ Select bit 0 for local temperature, bit 1..7 for remote temperatures.
+ If not specified, alert will be enabled for all channels.
+- over-temperature-mask
+ Over-temperature bit mask. Over-temperature reporting disabled for
+ bits set.
+ Select bit 0 for local temperature, bit 1..7 for remote temperatures.
+ If not specified, over-temperature reporting will be enabled for all
+ channels.
+- resistance-cancellation
+ Boolean for all chips other than MAX6581. Set to enable resistance
+ cancellation on remote temperature channel 1.
+ For MAX6581, resistance cancellation enabled for all channels if
+ specified as boolean, otherwise as per bit mask specified.
+ Only supported for remote temperatures (bit 1..7).
+ If not specified, resistance cancellation will be disabled for all
+ channels.
+- transistor-ideality
+ For MAX6581 only. Two values; first is bit mask, second is ideality
+ select value as per MAX6581 data sheet. Select bit 1..7 for remote
+ channels.
+ Transistor ideality will be initialized to default (1.008) if not
+ specified.
+
+Example:
+
+temp-sensor@1a {
+ compatible = "maxim,max6697";
+ reg = <0x1a>;
+ smbus-timeout-disable;
+ resistance-cancellation;
+ alert-mask = <0x72>;
+ over-temperature-mask = <0x7f>;
+};
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 6b366b4..33b8c3a 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -832,6 +832,17 @@ config SENSORS_MAX6650
This driver can also be built as a module. If so, the module
will be called max6650.
+config SENSORS_MAX6697
+ tristate "Maxim MAX6697 and compatibles"
+ depends on I2C
+ help
+ If you say yes here you get support for MAX6581, MAX6602, MAX6622,
+ MAX6636, MAX6689, MAX6693, MAX6694, MAX6697, MAX6698, and MAX6699
+ temperature sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called max6697.
+
config SENSORS_NTC_THERMISTOR
tristate "NTC thermistor support"
depends on EXPERIMENTAL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index dba7a20..64c587d 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -97,6 +97,7 @@ obj-$(CONFIG_SENSORS_MAX1668) += max1668.o
obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
+obj-$(CONFIG_SENSORS_MAX6697) += max6697.o
obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c
new file mode 100644
index 0000000..b567828
--- /dev/null
+++ b/drivers/hwmon/max6697.c
@@ -0,0 +1,702 @@
+/*
+ * Copyright (c) 2012 Guenter Roeck <linux@roeck-us.net>
+ *
+ * based on max1668.c
+ * Copyright (c) 2011 David George <david.george@ska.ac.za>
+ *
+ * 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+
+#include <linux/platform_data/max6697.h>
+
+enum chips { max6581, max6602, max6622, max6636, max6689, max6693, max6694,
+ max6697, max6698, max6699 };
+
+/* Report local sensor as temp1 */
+
+static const u8 MAX6697_REG_TEMP[] = {
+ 0x07, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08 };
+static const u8 MAX6697_REG_TEMP_EXT[] = {
+ 0x57, 0x09, 0x52, 0x53, 0x54, 0x55, 0x56, 0 };
+static const u8 MAX6697_REG_MAX[] = {
+ 0x17, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x18 };
+static const u8 MAX6697_REG_CRIT[] = {
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27 };
+
+/*
+ * Map device tree / platform data register bit map to chip bit map.
+ * Applies to alert register and over-temperature register.
+ */
+#define MAX6697_MAP_BITS(reg) ((((reg) & 0x7e) >> 1) | \
+ (((reg) & 0x01) << 6) | ((reg) & 0x80))
+
+#define MAX6697_REG_STAT(n) (0x44 + (n))
+
+#define MAX6697_REG_CONFIG 0x41
+#define MAX6581_CONF_EXTENDED (1 << 1)
+#define MAX6693_CONF_BETA (1 << 2)
+#define MAX6697_CONF_RESISTANCE (1 << 3)
+#define MAX6697_CONF_TIMEOUT (1 << 5)
+#define MAX6697_REG_ALERT_MASK 0x42
+#define MAX6697_REG_OVERT_MASK 0x43
+
+#define MAX6581_REG_RESISTANCE 0x4a
+#define MAX6581_REG_IDEALITY 0x4b
+#define MAX6581_REG_IDEALITY_SELECT 0x4c
+#define MAX6581_REG_OFFSET 0x4d
+#define MAX6581_REG_OFFSET_SELECT 0x4e
+
+#define MAX6697_CONV_TIME 156 /* ms per channel, worst case */
+
+struct max6697_chip_data {
+ int channels;
+ u32 have_ext;
+ u32 have_crit;
+ u32 have_fault;
+ u8 valid_conf;
+ const u8 *alarm_map;
+};
+
+struct max6697_data {
+ struct device *hwmon_dev;
+
+ enum chips type;
+ const struct max6697_chip_data *chip;
+
+ int update_interval; /* in milli-seconds */
+ int temp_offset; /* in degrees C */
+
+ struct mutex update_lock;
+ unsigned long last_updated; /* In jiffies */
+ bool valid; /* true if following fields are valid */
+
+ /* 1x local and up to 7x remote */
+ u8 temp[8][4]; /* [nr][0]=temp [1]=ext [2]=max [3]=crit */
+#define MAX6697_TEMP_INPUT 0
+#define MAX6697_TEMP_EXT 1
+#define MAX6697_TEMP_MAX 2
+#define MAX6697_TEMP_CRIT 3
+ u32 alarms;
+};
+
+/* Diode fault status bits on MAX6581 are right shifted by one bit */
+static const u8 max6581_alarm_map[] = {
+ 0, 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23 };
+
+static const struct max6697_chip_data max6697_chip_data[] = {
+ [max6581] = {
+ .channels = 8,
+ .have_crit = 0xff,
+ .have_ext = 0x7f,
+ .have_fault = 0xfe,
+ .valid_conf = MAX6581_CONF_EXTENDED | MAX6697_CONF_TIMEOUT,
+ .alarm_map = max6581_alarm_map,
+ },
+ [max6602] = {
+ .channels = 5,
+ .have_crit = 0x12,
+ .have_ext = 0x02,
+ .have_fault = 0x1e,
+ .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT,
+ },
+ [max6622] = {
+ .channels = 5,
+ .have_crit = 0x12,
+ .have_ext = 0x02,
+ .have_fault = 0x1e,
+ .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT,
+ },
+ [max6636] = {
+ .channels = 7,
+ .have_crit = 0x72,
+ .have_ext = 0x02,
+ .have_fault = 0x7e,
+ .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT,
+ },
+ [max6689] = {
+ .channels = 7,
+ .have_crit = 0x72,
+ .have_ext = 0x02,
+ .have_fault = 0x7e,
+ .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT,
+ },
+ [max6693] = {
+ .channels = 7,
+ .have_crit = 0x72,
+ .have_ext = 0x02,
+ .have_fault = 0x7e,
+ .valid_conf = MAX6697_CONF_RESISTANCE | MAX6693_CONF_BETA |
+ MAX6697_CONF_TIMEOUT,
+ },
+ [max6694] = {
+ .channels = 5,
+ .have_crit = 0x12,
+ .have_ext = 0x02,
+ .have_fault = 0x1e,
+ .valid_conf = MAX6697_CONF_RESISTANCE | MAX6693_CONF_BETA |
+ MAX6697_CONF_TIMEOUT,
+ },
+ [max6697] = {
+ .channels = 7,
+ .have_crit = 0x72,
+ .have_ext = 0x02,
+ .have_fault = 0x7e,
+ .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT,
+ },
+ [max6698] = {
+ .channels = 7,
+ .have_crit = 0x72,
+ .have_ext = 0x02,
+ .have_fault = 0x0e,
+ .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT,
+ },
+ [max6699] = {
+ .channels = 5,
+ .have_crit = 0x12,
+ .have_ext = 0x02,
+ .have_fault = 0x1e,
+ .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT,
+ },
+};
+
+static struct max6697_data *max6697_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct max6697_data *data = i2c_get_clientdata(client);
+ struct max6697_data *ret = data;
+ int val;
+ int i;
+ u32 alarms;
+
+ mutex_lock(&data->update_lock);
+
+ if (data->valid &&
+ !time_after(jiffies, data->last_updated
+ + msecs_to_jiffies(data->update_interval)))
+ goto abort;
+
+ for (i = 0; i < data->chip->channels; i++) {
+ if (data->chip->have_ext & (1 << i)) {
+ val = i2c_smbus_read_byte_data(client,
+ MAX6697_REG_TEMP_EXT[i]);
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->temp[i][MAX6697_TEMP_EXT] = val;
+ }
+
+ val = i2c_smbus_read_byte_data(client, MAX6697_REG_TEMP[i]);
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->temp[i][MAX6697_TEMP_INPUT] = val;
+
+ val = i2c_smbus_read_byte_data(client, MAX6697_REG_MAX[i]);
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->temp[i][MAX6697_TEMP_MAX] = val;
+
+ if (data->chip->have_crit & (1 << i)) {
+ val = i2c_smbus_read_byte_data(client,
+ MAX6697_REG_CRIT[i]);
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->temp[i][MAX6697_TEMP_CRIT] = val;
+ }
+ }
+
+ alarms = 0;
+ for (i = 0; i < 3; i++) {
+ val = i2c_smbus_read_byte_data(client, MAX6697_REG_STAT(i));
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ alarms = (alarms << 8) | val;
+ }
+ data->alarms = alarms;
+ data->last_updated = jiffies;
+ data->valid = true;
+abort:
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+static ssize_t show_temp_input(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct max6697_data *data = max6697_update_device(dev);
+ int temp;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ temp = (data->temp[index][MAX6697_TEMP_INPUT] - data->temp_offset) << 3;
+ temp |= data->temp[index][MAX6697_TEMP_EXT] >> 5;
+
+ return sprintf(buf, "%d\n", temp * 125);
+}
+
+static ssize_t show_temp(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int nr = to_sensor_dev_attr_2(devattr)->nr;
+ int index = to_sensor_dev_attr_2(devattr)->index;
+ struct max6697_data *data = max6697_update_device(dev);
+ int temp;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ temp = data->temp[nr][index];
+ temp -= data->temp_offset;
+
+ return sprintf(buf, "%d\n", temp * 1000);
+}
+
+static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int index = to_sensor_dev_attr(attr)->index;
+ struct max6697_data *data = max6697_update_device(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ if (data->chip->alarm_map)
+ index = data->chip->alarm_map[index];
+
+ return sprintf(buf, "%u\n", (data->alarms >> index) & 0x1);
+}
+
+static ssize_t set_temp(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr_2(devattr)->nr;
+ int index = to_sensor_dev_attr_2(devattr)->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct max6697_data *data = i2c_get_clientdata(client);
+ long temp;
+ int ret;
+
+ ret = kstrtol(buf, 10, &temp);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&data->update_lock);
+ temp = DIV_ROUND_CLOSEST(temp, 1000) + data->temp_offset;
+ temp = clamp_val(temp, 0, data->type == max6581 ? 255 : 127);
+ data->temp[nr][index] = temp;
+ ret = i2c_smbus_write_byte_data(client,
+ index == 2 ? MAX6697_REG_MAX[nr]
+ : MAX6697_REG_CRIT[nr],
+ temp);
+ mutex_unlock(&data->update_lock);
+
+ return ret < 0 ? ret : count;
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL, 0);
+static SENSOR_DEVICE_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
+ 0, MAX6697_TEMP_MAX);
+static SENSOR_DEVICE_ATTR_2(temp1_crit, S_IRUGO | S_IWUSR, show_temp, set_temp,
+ 0, MAX6697_TEMP_CRIT);
+
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_input, NULL, 1);
+static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
+ 1, MAX6697_TEMP_MAX);
+static SENSOR_DEVICE_ATTR_2(temp2_crit, S_IRUGO | S_IWUSR, show_temp, set_temp,
+ 1, MAX6697_TEMP_CRIT);
+
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp_input, NULL, 2);
+static SENSOR_DEVICE_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
+ 2, MAX6697_TEMP_MAX);
+static SENSOR_DEVICE_ATTR_2(temp3_crit, S_IRUGO | S_IWUSR, show_temp, set_temp,
+ 2, MAX6697_TEMP_CRIT);
+
+static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp_input, NULL, 3);
+static SENSOR_DEVICE_ATTR_2(temp4_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
+ 3, MAX6697_TEMP_MAX);
+static SENSOR_DEVICE_ATTR_2(temp4_crit, S_IRUGO | S_IWUSR, show_temp, set_temp,
+ 3, MAX6697_TEMP_CRIT);
+
+static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp_input, NULL, 4);
+static SENSOR_DEVICE_ATTR_2(temp5_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
+ 4, MAX6697_TEMP_MAX);
+static SENSOR_DEVICE_ATTR_2(temp5_crit, S_IRUGO | S_IWUSR, show_temp, set_temp,
+ 4, MAX6697_TEMP_CRIT);
+
+static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, show_temp_input, NULL, 5);
+static SENSOR_DEVICE_ATTR_2(temp6_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
+ 5, MAX6697_TEMP_MAX);
+static SENSOR_DEVICE_ATTR_2(temp6_crit, S_IRUGO | S_IWUSR, show_temp, set_temp,
+ 5, MAX6697_TEMP_CRIT);
+
+static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO, show_temp_input, NULL, 6);
+static SENSOR_DEVICE_ATTR_2(temp7_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
+ 6, MAX6697_TEMP_MAX);
+static SENSOR_DEVICE_ATTR_2(temp7_crit, S_IRUGO | S_IWUSR, show_temp, set_temp,
+ 6, MAX6697_TEMP_CRIT);
+
+static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, show_temp_input, NULL, 7);
+static SENSOR_DEVICE_ATTR_2(temp8_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
+ 7, MAX6697_TEMP_MAX);
+static SENSOR_DEVICE_ATTR_2(temp8_crit, S_IRUGO | S_IWUSR, show_temp, set_temp,
+ 7, MAX6697_TEMP_CRIT);
+
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 22);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 16);
+static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 17);
+static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_alarm, NULL, 18);
+static SENSOR_DEVICE_ATTR(temp5_max_alarm, S_IRUGO, show_alarm, NULL, 19);
+static SENSOR_DEVICE_ATTR(temp6_max_alarm, S_IRUGO, show_alarm, NULL, 20);
+static SENSOR_DEVICE_ATTR(temp7_max_alarm, S_IRUGO, show_alarm, NULL, 21);
+static SENSOR_DEVICE_ATTR(temp8_max_alarm, S_IRUGO, show_alarm, NULL, 23);
+
+static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 14);
+static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 8);
+static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, 9);
+static SENSOR_DEVICE_ATTR(temp4_crit_alarm, S_IRUGO, show_alarm, NULL, 10);
+static SENSOR_DEVICE_ATTR(temp5_crit_alarm, S_IRUGO, show_alarm, NULL, 11);
+static SENSOR_DEVICE_ATTR(temp6_crit_alarm, S_IRUGO, show_alarm, NULL, 12);
+static SENSOR_DEVICE_ATTR(temp7_crit_alarm, S_IRUGO, show_alarm, NULL, 13);
+static SENSOR_DEVICE_ATTR(temp8_crit_alarm, S_IRUGO, show_alarm, NULL, 15);
+
+static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp6_fault, S_IRUGO, show_alarm, NULL, 5);
+static SENSOR_DEVICE_ATTR(temp7_fault, S_IRUGO, show_alarm, NULL, 6);
+static SENSOR_DEVICE_ATTR(temp8_fault, S_IRUGO, show_alarm, NULL, 7);
+
+static DEVICE_ATTR(dummy, 0, NULL, NULL);
+
+static mode_t max6697_is_visible(struct kobject *kobj, struct attribute *attr,
+ int index)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct i2c_client *client = to_i2c_client(dev);
+ struct max6697_data *data = i2c_get_clientdata(client);
+ const struct max6697_chip_data *chip = data->chip;
+ int channel = index / 6; /* channel number */
+ int nr = index % 6; /* attribute index within channel */
+
+ if (channel >= chip->channels)
+ return 0;
+
+ if ((nr == 3 || nr == 4) && !(chip->have_crit & (1 << channel)))
+ return 0;
+ if (nr == 5 && !(chip->have_fault & (1 << channel)))
+ return 0;
+
+ return attr->mode;
+}
+
+/*
+ * max6697_is_visible uses the index into the following array to determine
+ * if attributes should be created or not. Any change in order or content
+ * must be matched in max6697_is_visible.
+ */
+static struct attribute *max6697_attributes[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+ &dev_attr_dummy.attr,
+
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_fault.dev_attr.attr,
+
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_crit.dev_attr.attr,
+ &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_fault.dev_attr.attr,
+
+ &sensor_dev_attr_temp5_input.dev_attr.attr,
+ &sensor_dev_attr_temp5_max.dev_attr.attr,
+ &sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_crit.dev_attr.attr,
+ &sensor_dev_attr_temp5_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_fault.dev_attr.attr,
+
+ &sensor_dev_attr_temp6_input.dev_attr.attr,
+ &sensor_dev_attr_temp6_max.dev_attr.attr,
+ &sensor_dev_attr_temp6_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp6_crit.dev_attr.attr,
+ &sensor_dev_attr_temp6_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp6_fault.dev_attr.attr,
+
+ &sensor_dev_attr_temp7_input.dev_attr.attr,
+ &sensor_dev_attr_temp7_max.dev_attr.attr,
+ &sensor_dev_attr_temp7_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp7_crit.dev_attr.attr,
+ &sensor_dev_attr_temp7_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp7_fault.dev_attr.attr,
+
+ &sensor_dev_attr_temp8_input.dev_attr.attr,
+ &sensor_dev_attr_temp8_max.dev_attr.attr,
+ &sensor_dev_attr_temp8_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp8_crit.dev_attr.attr,
+ &sensor_dev_attr_temp8_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp8_fault.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group max6697_group = {
+ .attrs = max6697_attributes, .is_visible = max6697_is_visible,
+};
+
+static void max6697_get_config_of(struct device_node *node,
+ struct max6697_platform_data *pdata)
+{
+ int len;
+ const __be32 *prop;
+
+ prop = of_get_property(node, "smbus-timeout-disable", &len);
+ if (prop)
+ pdata->smbus_timeout_disable = true;
+ prop = of_get_property(node, "extended-range-enable", &len);
+ if (prop)
+ pdata->extended_range_enable = true;
+ prop = of_get_property(node, "beta-compensation-enable", &len);
+ if (prop)
+ pdata->beta_compensation = true;
+ prop = of_get_property(node, "alert-mask", &len);
+ if (prop && len == sizeof(u32))
+ pdata->alert_mask = be32_to_cpu(prop[0]);
+ prop = of_get_property(node, "over-temperature-mask", &len);
+ if (prop && len == sizeof(u32))
+ pdata->over_temperature_mask = be32_to_cpu(prop[0]);
+ prop = of_get_property(node, "resistance-cancellation", &len);
+ if (prop) {
+ if (len == sizeof(u32))
+ pdata->resistance_cancellation = be32_to_cpu(prop[0]);
+ else
+ pdata->resistance_cancellation = 0xfe;
+ }
+ prop = of_get_property(node, "transistor-ideality", &len);
+ if (prop && len == 2 * sizeof(u32)) {
+ pdata->ideality_mask = be32_to_cpu(prop[0]);
+ pdata->ideality_value = be32_to_cpu(prop[1]);
+ }
+}
+
+static int max6697_init_chip(struct i2c_client *client)
+{
+ struct max6697_data *data = i2c_get_clientdata(client);
+ struct max6697_platform_data *pdata = dev_get_platdata(&client->dev);
+ struct max6697_platform_data p;
+ const struct max6697_chip_data *chip = data->chip;
+ int factor = chip->channels;
+ int ret, reg;
+
+ /*
+ * Don't touch configuration if neither platform data nor OF
+ * configuration was specified. If that is the case, use the
+ * current chip configuration.
+ */
+ if (!pdata && !client->dev.of_node) {
+ reg = i2c_smbus_read_byte_data(client, MAX6697_REG_CONFIG);
+ if (reg < 0)
+ return reg;
+ if (data->type == max6581) {
+ if (reg & MAX6581_CONF_EXTENDED)
+ data->temp_offset = 64;
+ reg = i2c_smbus_read_byte_data(client,
+ MAX6581_REG_RESISTANCE);
+ if (reg < 0)
+ return reg;
+ factor += hweight8(reg);
+ } else {
+ if (reg & MAX6697_CONF_RESISTANCE)
+ factor++;
+ }
+ goto done;
+ }
+
+ if (client->dev.of_node) {
+ memset(&p, 0, sizeof(p));
+ max6697_get_config_of(client->dev.of_node, &p);
+ pdata = &p;
+ }
+
+ reg = 0;
+ if (pdata->smbus_timeout_disable &&
+ (chip->valid_conf & MAX6697_CONF_TIMEOUT)) {
+ reg |= MAX6697_CONF_TIMEOUT;
+ }
+ if (pdata->extended_range_enable &&
+ (chip->valid_conf & MAX6581_CONF_EXTENDED)) {
+ reg |= MAX6581_CONF_EXTENDED;
+ data->temp_offset = 64;
+ }
+ if (pdata->resistance_cancellation &&
+ (chip->valid_conf & MAX6697_CONF_RESISTANCE)) {
+ reg |= MAX6697_CONF_RESISTANCE;
+ factor++;
+ }
+ if (pdata->beta_compensation &&
+ (chip->valid_conf & MAX6693_CONF_BETA)) {
+ reg |= MAX6693_CONF_BETA;
+ }
+
+ ret = i2c_smbus_write_byte_data(client, MAX6697_REG_CONFIG, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, MAX6697_REG_ALERT_MASK,
+ MAX6697_MAP_BITS(pdata->alert_mask));
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, MAX6697_REG_OVERT_MASK,
+ MAX6697_MAP_BITS(pdata->over_temperature_mask));
+ if (ret < 0)
+ return ret;
+
+ if (data->type == max6581) {
+ factor += hweight8(pdata->resistance_cancellation >> 1);
+ ret = i2c_smbus_write_byte_data(client, MAX6581_REG_RESISTANCE,
+ pdata->resistance_cancellation >> 1);
+ if (ret < 0)
+ return ret;
+ ret = i2c_smbus_write_byte_data(client, MAX6581_REG_IDEALITY,
+ pdata->ideality_value);
+ if (ret < 0)
+ return ret;
+ ret = i2c_smbus_write_byte_data(client,
+ MAX6581_REG_IDEALITY_SELECT,
+ pdata->ideality_mask >> 1);
+ if (ret < 0)
+ return ret;
+ }
+done:
+ data->update_interval = factor * MAX6697_CONV_TIME;
+ return 0;
+}
+
+static int max6697_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct device *dev = &client->dev;
+ struct max6697_data *data;
+ int err;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ data = devm_kzalloc(dev, sizeof(struct max6697_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->type = id->driver_data;
+ data->chip = &max6697_chip_data[data->type];
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+
+ err = max6697_init_chip(client);
+ if (err)
+ return err;
+
+ err = sysfs_create_group(&client->dev.kobj, &max6697_group);
+ if (err)
+ return err;
+
+ data->hwmon_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ sysfs_remove_group(&client->dev.kobj, &max6697_group);
+ return err;
+}
+
+static int max6697_remove(struct i2c_client *client)
+{
+ struct max6697_data *data = i2c_get_clientdata(client);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&client->dev.kobj, &max6697_group);
+
+ return 0;
+}
+
+static const struct i2c_device_id max6697_id[] = {
+ { "max6581", max6581 },
+ { "max6602", max6602 },
+ { "max6622", max6622 },
+ { "max6636", max6636 },
+ { "max6689", max6689 },
+ { "max6693", max6693 },
+ { "max6694", max6694 },
+ { "max6697", max6697 },
+ { "max6698", max6698 },
+ { "max6699", max6699 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max6697_id);
+
+static struct i2c_driver max6697_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "max6697",
+ },
+ .probe = max6697_probe,
+ .remove = max6697_remove,
+ .id_table = max6697_id,
+};
+
+module_i2c_driver(max6697_driver);
+
+MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
+MODULE_DESCRIPTION("MAX6697 temperature sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/platform_data/max6697.h b/include/linux/platform_data/max6697.h
new file mode 100644
index 0000000..ed9d3b3
--- /dev/null
+++ b/include/linux/platform_data/max6697.h
@@ -0,0 +1,36 @@
+/*
+ * max6697.h
+ * Copyright (c) 2012 Guenter Roeck <linux@roeck-us.net>
+ *
+ * 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 MAX6697_H
+#define MAX6697_H
+
+#include <linux/types.h>
+
+/*
+ * For all bit masks:
+ * bit 0: local temperature
+ * bit 1..7: remote temperatures
+ */
+struct max6697_platform_data {
+ bool smbus_timeout_disable; /* set to disable SMBus timeouts */
+ bool extended_range_enable; /* set to enable extended temp range */
+ bool beta_compensation; /* set to enable beta compensation */
+ u8 alert_mask; /* set bit to 1 to disable alert */
+ u8 over_temperature_mask; /* set bit to 1 to disable */
+ u8 resistance_cancellation; /* set bit to 0 to disable
+ * bit mask for MAX6581,
+ * boolean for other chips
+ */
+ u8 ideality_mask; /* set bit to 0 to disable */
+ u8 ideality_value; /* transistor ideality as per
+ * MAX6581 datasheet
+ */
+};
+
+#endif /* MAX6697_H */

View File

@@ -1,294 +0,0 @@
Add PMBUS driver for DNI DPS460 Power Supply
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index 4b26f51..de91280 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -76,6 +76,16 @@ config SENSORS_MAX34440
This driver can also be built as a module. If so, the module will
be called max34440.
+config SENSORS_DNI_DPS460
+ tristate "Delta DPS460"
+ default n
+ help
+ If you say yes here you get hardware monitoring support for Delta
+ DPS460.
+
+ This driver can also be built as a module. If so, the module will
+ be called dni_dps460.
+
config SENSORS_MAX8688
tristate "Maxim MAX8688"
default n
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index 789376c..be70828 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_SENSORS_LM25066) += lm25066.o
obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o
obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
+obj-$(CONFIG_SENSORS_DNI_DPS460) += dni_dps460.o
obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o
obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o
diff --git a/drivers/hwmon/pmbus/dni_dps460.c b/drivers/hwmon/pmbus/dni_dps460.c
new file mode 100644
index 0000000..c687217
--- /dev/null
+++ b/drivers/hwmon/pmbus/dni_dps460.c
@@ -0,0 +1,253 @@
+/*
+ * Hardware monitoring driver for Delta DPS460
+ *
+ * Copyright (C) 2014 Cumulus Networks, LLC
+ * Author: Puneet Shenoy <puneet@cumulusnetworks.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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c/pmbus.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include "pmbus.h"
+
+enum chips { dni_dps460 };
+
+/* Data provided by DELL Inc */
+#define FAN_RPM_MIN 7200
+#define FAN_RPM_MAX 18000
+#define FAN_VALUE_MIN 0x28
+#define FAN_VALUE_MAX 0x64
+
+/* Needed to access the mutex. Copied from pmbus_core.c */
+#define PB_NUM_STATUS_REG (PMBUS_PAGES * 6 + 1)
+struct pmbus_data {
+ struct device *hwmon_dev;
+
+ u32 flags; /* from platform data */
+
+ int exponent; /* linear mode: exponent for output voltages */
+
+ const struct pmbus_driver_info *info;
+
+ int max_attributes;
+ int num_attributes;
+ struct attribute **attributes;
+ struct attribute_group group;
+
+ /*
+ * Sensors cover both sensor and limit registers.
+ */
+ int max_sensors;
+ int num_sensors;
+ struct pmbus_sensor *sensors;
+ /*
+ * Booleans are used for alarms.
+ * Values are determined from status registers.
+ */
+ int max_booleans;
+ int num_booleans;
+ struct pmbus_boolean *booleans;
+ /*
+ * Labels are used to map generic names (e.g., "in1")
+ * to PMBus specific names (e.g., "vin" or "vout1").
+ */
+ int max_labels;
+ int num_labels;
+ struct pmbus_label *labels;
+
+ struct mutex update_lock;
+ bool valid;
+ unsigned long last_updated; /* in jiffies */
+
+ /*
+ * A single status register covers multiple attributes,
+ * so we keep them all together.
+ */
+ u8 status[PB_NUM_STATUS_REG];
+
+ u8 currpage;
+};
+
+/*
+ * We are only concerned with the first fan. The get_target and set_target are
+ * are written accordingly.
+ */
+static ssize_t get_target(struct device *dev, struct device_attribute *devattr,
+ char *buf) {
+
+ struct i2c_client *client = to_i2c_client(dev);
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ int val;
+ u32 rpm;
+
+ /*
+ * The FAN_COMMAND_n takes a value which is not the RPM.
+ * The value and RPM have a liner relation.
+ * rpm = (FAN_RPM_MIN/FAN_VALUE_MIN) * val
+ * The slope is (FAN_RPM_MIN/FAN_VALUE_MIN) = 180
+ */
+ mutex_lock(&data->update_lock);
+ val = pmbus_read_word_data(client, 0, PMBUS_FAN_COMMAND_1);
+ pmbus_clear_faults(client);
+ mutex_unlock(&data->update_lock);
+ if (val < 0) {
+ return val;
+ }
+ rpm = val * (FAN_RPM_MIN/FAN_VALUE_MIN);
+ return sprintf(buf, "%d\n", rpm);
+}
+
+static ssize_t set_target(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count) {
+
+ struct i2c_client *client = to_i2c_client(dev);
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ int err;
+ unsigned int val;
+ unsigned int rpm;
+
+ err = kstrtol(buf, 10, &rpm);
+ if (err)
+ return err;
+
+ rpm = SENSORS_LIMIT(rpm, FAN_RPM_MIN, FAN_RPM_MAX);
+
+ mutex_lock(&data->update_lock);
+
+ val = FAN_VALUE_MIN * rpm;
+ val /= FAN_RPM_MIN;
+ pmbus_write_word_data(client, 0, PMBUS_FAN_COMMAND_1, (u16)val);
+ pmbus_clear_faults(client);
+
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t show_pec(struct device *dev, struct device_attribute *dummy,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ return sprintf(buf, "%d\n", !!(client->flags & I2C_CLIENT_PEC));
+}
+
+static ssize_t set_pec(struct device *dev, struct device_attribute *dummy,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ long val;
+ int err;
+
+ err = strict_strtol(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ if (val != 0)
+ client->flags |= I2C_CLIENT_PEC;
+ else
+ client->flags &= ~I2C_CLIENT_PEC;
+
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR(pec, S_IWUSR | S_IRUGO, show_pec, set_pec, 0);
+static SENSOR_DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, get_target,
+ set_target, 0);
+
+static struct attribute *dni_dps460_attrs[] = {
+ &sensor_dev_attr_fan1_target.dev_attr.attr,
+ &sensor_dev_attr_pec.dev_attr.attr,
+ NULL
+};
+static struct attribute_group dni_dps460_attr_grp = {
+ .attrs = dni_dps460_attrs,
+};
+
+static int dni_dps460_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pmbus_driver_info *info;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_PEC))
+ return -ENODEV;
+
+ /* Needs PEC(PACKET ERROR CODE). Writes wont work without this. */
+ client->flags = I2C_CLIENT_PEC;
+
+ info = kzalloc(sizeof(struct pmbus_driver_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ /* Use only 1 page with 1 Fan, 2 Temps. */
+ info->pages = 1;
+ info->func[0] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12 |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP;
+
+ ret = pmbus_do_probe(client, id, info);
+ if (ret < 0)
+ goto out;
+
+ ret = sysfs_create_group(&client->dev.kobj, &dni_dps460_attr_grp);
+ if (ret)
+ goto out;
+ return 0;
+out:
+ kfree(info);
+ return ret;
+}
+
+static int dni_dps460_remove(struct i2c_client *client)
+{
+ struct pmbus_data *data = i2c_get_clientdata(client);
+
+ sysfs_remove_group(&client->dev.kobj, &dni_dps460_attr_grp);
+ if (data->info)
+ kfree(data->info);
+ pmbus_do_remove(client);
+ return 0;
+}
+
+static const struct i2c_device_id dni_dps460_id[] = {
+ {"dni_dps460", dni_dps460},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, dni_dps460_id);
+
+static struct i2c_driver dni_dps460_driver = {
+ .driver = {
+ .name = "dni_dps460",
+ },
+ .probe = dni_dps460_probe,
+ .remove = dni_dps460_remove,
+ .id_table = dni_dps460_id,
+};
+
+module_i2c_driver(dni_dps460_driver);
+
+MODULE_AUTHOR("Puneet Shenoy");
+MODULE_DESCRIPTION("PMBus driver for Delta DPS460");
+MODULE_LICENSE("GPL");

View File

@@ -1,22 +0,0 @@
Patch to support Intel Avoton I2C smbus
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 817d025..dfe074f 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -149,6 +149,7 @@
#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS 0x9c22
+#define PCI_DEVICE_ID_INTEL_AVOTON_SMBUS 0x1f3c
struct i801_priv {
struct i2c_adapter adapter;
@@ -639,6 +640,7 @@ static const struct pci_device_id i801_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_AVOTON_SMBUS) },
{ 0, }
};

View File

@@ -1,974 +0,0 @@
Update the i801 driver to the latest kernel source (3.15.7). Needed for
accton as5712. CM-3232.
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index dfe074f..6777cd6 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -2,7 +2,7 @@
Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>,
Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker
<mdsxyz123@yahoo.com>
- Copyright (C) 2007, 2008 Jean Delvare <khali@linux-fr.org>
+ Copyright (C) 2007 - 2012 Jean Delvare <jdelvare@suse.de>
Copyright (C) 2010 Intel Corporation,
David Woodhouse <dwmw2@infradead.org>
@@ -53,6 +53,14 @@
Panther Point (PCH) 0x1e22 32 hard yes yes yes
Lynx Point (PCH) 0x8c22 32 hard yes yes yes
Lynx Point-LP (PCH) 0x9c22 32 hard yes yes yes
+ Avoton (SOC) 0x1f3c 32 hard yes yes yes
+ Wellsburg (PCH) 0x8d22 32 hard yes yes yes
+ Wellsburg (PCH) MS 0x8d7d 32 hard yes yes yes
+ Wellsburg (PCH) MS 0x8d7e 32 hard yes yes yes
+ Wellsburg (PCH) MS 0x8d7f 32 hard yes yes yes
+ Coleto Creek (PCH) 0x23b0 32 hard yes yes yes
+ Wildcat Point-LP (PCH) 0x9ca2 32 hard yes yes yes
+ BayTrail (SOC) 0x0f12 32 hard yes yes yes
Features supported by this driver:
Software PEC no
@@ -61,10 +69,12 @@
Block process call transaction no
I2C block read transaction yes (doesn't use the block buffer)
Slave mode no
+ Interrupt processing yes
See the file Documentation/i2c/busses/i2c-i801 for details.
*/
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
@@ -77,6 +87,15 @@
#include <linux/io.h>
#include <linux/dmi.h>
#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/err.h>
+
+#if (defined CONFIG_I2C_MUX_GPIO || defined CONFIG_I2C_MUX_GPIO_MODULE) && \
+ defined CONFIG_DMI
+#include <linux/gpio.h>
+#include <linux/i2c-mux-gpio.h>
+#include <linux/platform_device.h>
+#endif
/* I801 SMBus address offsets */
#define SMBHSTSTS(p) (0 + (p)->smba)
@@ -92,8 +111,12 @@
/* PCI Address Constants */
#define SMBBAR 4
+#define SMBPCISTS 0x006
#define SMBHSTCFG 0x040
+/* Host status bits for SMBPCISTS */
+#define SMBPCISTS_INTS 0x08
+
/* Host configuration bits for SMBHSTCFG */
#define SMBHSTCFG_HST_EN 1
#define SMBHSTCFG_SMB_SMI_EN 2
@@ -103,12 +126,8 @@
#define SMBAUXCTL_CRC 1
#define SMBAUXCTL_E32B 2
-/* kill bit for SMBHSTCNT */
-#define SMBHSTCNT_KILL 2
-
/* Other settings */
-#define MAX_TIMEOUT 100
-#define ENABLE_INT9 0 /* set to 0x01 to enable - untested */
+#define MAX_RETRIES 400
/* I801 command constants */
#define I801_QUICK 0x00
@@ -118,10 +137,13 @@
#define I801_PROC_CALL 0x10 /* unimplemented */
#define I801_BLOCK_DATA 0x14
#define I801_I2C_BLOCK_DATA 0x18 /* ICH5 and later */
-#define I801_BLOCK_LAST 0x34
-#define I801_I2C_BLOCK_LAST 0x38 /* ICH5 and later */
-#define I801_START 0x40
-#define I801_PEC_EN 0x80 /* ICH3 and later */
+
+/* I801 Host Control register bits */
+#define SMBHSTCNT_INTREN 0x01
+#define SMBHSTCNT_KILL 0x02
+#define SMBHSTCNT_LAST_BYTE 0x20
+#define SMBHSTCNT_START 0x40
+#define SMBHSTCNT_PEC_EN 0x80 /* ICH3 and later */
/* I801 Hosts Status register bits */
#define SMBHSTSTS_BYTE_DONE 0x80
@@ -133,11 +155,14 @@
#define SMBHSTSTS_INTR 0x02
#define SMBHSTSTS_HOST_BUSY 0x01
-#define STATUS_FLAGS (SMBHSTSTS_BYTE_DONE | SMBHSTSTS_FAILED | \
- SMBHSTSTS_BUS_ERR | SMBHSTSTS_DEV_ERR | \
- SMBHSTSTS_INTR)
+#define STATUS_ERROR_FLAGS (SMBHSTSTS_FAILED | SMBHSTSTS_BUS_ERR | \
+ SMBHSTSTS_DEV_ERR)
+
+#define STATUS_FLAGS (SMBHSTSTS_BYTE_DONE | SMBHSTSTS_INTR | \
+ STATUS_ERROR_FLAGS)
/* Older devices have their ID defined in <linux/pci_ids.h> */
+#define PCI_DEVICE_ID_INTEL_BAYTRAIL_SMBUS 0x0f12
#define PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS 0x1c22
#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS 0x1d22
/* Patsburg also has three 'Integrated Device Function' SMBus controllers */
@@ -145,11 +170,26 @@
#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1 0x1d71
#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2 0x1d72
#define PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS 0x1e22
+#define PCI_DEVICE_ID_INTEL_AVOTON_SMBUS 0x1f3c
#define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330
+#define PCI_DEVICE_ID_INTEL_COLETOCREEK_SMBUS 0x23b0
#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22
+#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS 0x8d22
+#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS0 0x8d7d
+#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS1 0x8d7e
+#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2 0x8d7f
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS 0x9c22
-#define PCI_DEVICE_ID_INTEL_AVOTON_SMBUS 0x1f3c
+#define PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_SMBUS 0x9ca2
+
+struct i801_mux_config {
+ char *gpio_chip;
+ unsigned values[3];
+ int n_values;
+ unsigned classes[3];
+ unsigned gpios[2]; /* Relative to gpio_chip->base */
+ int n_gpios;
+};
struct i801_priv {
struct i2c_adapter adapter;
@@ -157,6 +197,23 @@ struct i801_priv {
unsigned char original_hstcfg;
struct pci_dev *pci_dev;
unsigned int features;
+
+ /* isr processing */
+ wait_queue_head_t waitq;
+ u8 status;
+
+ /* Command state used by isr for byte-by-byte block transactions */
+ u8 cmd;
+ bool is_read;
+ int count;
+ int len;
+ u8 *data;
+
+#if (defined CONFIG_I2C_MUX_GPIO || defined CONFIG_I2C_MUX_GPIO_MODULE) && \
+ defined CONFIG_DMI
+ const struct i801_mux_config *mux_drvdata;
+ struct platform_device *mux_pdev;
+#endif
};
static struct pci_driver i801_driver;
@@ -165,6 +222,7 @@ static struct pci_driver i801_driver;
#define FEATURE_BLOCK_BUFFER (1 << 1)
#define FEATURE_BLOCK_PROC (1 << 2)
#define FEATURE_I2C_BLOCK_READ (1 << 3)
+#define FEATURE_IRQ (1 << 4)
/* Not really a feature, but it's convenient to handle it as such */
#define FEATURE_IDF (1 << 15)
@@ -173,11 +231,16 @@ static const char *i801_feature_names[] = {
"Block buffer",
"Block process call",
"I2C block read",
+ "Interrupt",
};
static unsigned int disable_features;
module_param(disable_features, uint, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(disable_features, "Disable selected driver features");
+MODULE_PARM_DESC(disable_features, "Disable selected driver features:\n"
+ "\t\t 0x01 disable SMBus PEC\n"
+ "\t\t 0x02 disable the block buffer\n"
+ "\t\t 0x08 disable the I2C block read functionality\n"
+ "\t\t 0x10 don't use interrupts ");
/* Make sure the SMBus host is ready to start transmitting.
Return 0 if it is, -EBUSY if it is not. */
@@ -208,19 +271,28 @@ static int i801_check_pre(struct i801_priv *priv)
return 0;
}
-/* Convert the status register to an error code, and clear it. */
-static int i801_check_post(struct i801_priv *priv, int status, int timeout)
+/*
+ * Convert the status register to an error code, and clear it.
+ * Note that status only contains the bits we want to clear, not the
+ * actual register value.
+ */
+static int i801_check_post(struct i801_priv *priv, int status)
{
int result = 0;
- /* If the SMBus is still busy, we give up */
- if (timeout) {
+ /*
+ * If the SMBus is still busy, we give up
+ * Note: This timeout condition only happens when using polling
+ * transactions. For interrupt operation, NAK/timeout is indicated by
+ * DEV_ERR.
+ */
+ if (unlikely(status < 0)) {
dev_err(&priv->pci_dev->dev, "Transaction timeout\n");
/* try to stop the current command */
dev_dbg(&priv->pci_dev->dev, "Terminating the current operation\n");
outb_p(inb_p(SMBHSTCNT(priv)) | SMBHSTCNT_KILL,
SMBHSTCNT(priv));
- msleep(1);
+ usleep_range(1000, 2000);
outb_p(inb_p(SMBHSTCNT(priv)) & (~SMBHSTCNT_KILL),
SMBHSTCNT(priv));
@@ -247,64 +319,76 @@ static int i801_check_post(struct i801_priv *priv, int status, int timeout)
dev_dbg(&priv->pci_dev->dev, "Lost arbitration\n");
}
- if (result) {
- /* Clear error flags */
- outb_p(status & STATUS_FLAGS, SMBHSTSTS(priv));
- status = inb_p(SMBHSTSTS(priv)) & STATUS_FLAGS;
- if (status) {
- dev_warn(&priv->pci_dev->dev, "Failed clearing status "
- "flags at end of transaction (%02x)\n",
- status);
- }
- }
+ /* Clear status flags except BYTE_DONE, to be cleared by caller */
+ outb_p(status, SMBHSTSTS(priv));
return result;
}
-static int i801_transaction(struct i801_priv *priv, int xact)
+/* Wait for BUSY being cleared and either INTR or an error flag being set */
+static int i801_wait_intr(struct i801_priv *priv)
{
- int status;
- int result;
int timeout = 0;
-
- result = i801_check_pre(priv);
- if (result < 0)
- return result;
-
- /* the current contents of SMBHSTCNT can be overwritten, since PEC,
- * INTREN, SMBSCMD are passed in xact */
- outb_p(xact | I801_START, SMBHSTCNT(priv));
+ int status;
/* We will always wait for a fraction of a second! */
do {
- msleep(1);
+ usleep_range(250, 500);
status = inb_p(SMBHSTSTS(priv));
- } while ((status & SMBHSTSTS_HOST_BUSY) && (timeout++ < MAX_TIMEOUT));
-
- result = i801_check_post(priv, status, timeout > MAX_TIMEOUT);
- if (result < 0)
- return result;
+ } while (((status & SMBHSTSTS_HOST_BUSY) ||
+ !(status & (STATUS_ERROR_FLAGS | SMBHSTSTS_INTR))) &&
+ (timeout++ < MAX_RETRIES));
- outb_p(SMBHSTSTS_INTR, SMBHSTSTS(priv));
- return 0;
+ if (timeout > MAX_RETRIES) {
+ dev_dbg(&priv->pci_dev->dev, "INTR Timeout!\n");
+ return -ETIMEDOUT;
+ }
+ return status & (STATUS_ERROR_FLAGS | SMBHSTSTS_INTR);
}
-/* wait for INTR bit as advised by Intel */
-static void i801_wait_hwpec(struct i801_priv *priv)
+/* Wait for either BYTE_DONE or an error flag being set */
+static int i801_wait_byte_done(struct i801_priv *priv)
{
int timeout = 0;
int status;
+ /* We will always wait for a fraction of a second! */
do {
- msleep(1);
+ usleep_range(250, 500);
status = inb_p(SMBHSTSTS(priv));
- } while ((!(status & SMBHSTSTS_INTR))
- && (timeout++ < MAX_TIMEOUT));
+ } while (!(status & (STATUS_ERROR_FLAGS | SMBHSTSTS_BYTE_DONE)) &&
+ (timeout++ < MAX_RETRIES));
- if (timeout > MAX_TIMEOUT)
- dev_dbg(&priv->pci_dev->dev, "PEC Timeout!\n");
+ if (timeout > MAX_RETRIES) {
+ dev_dbg(&priv->pci_dev->dev, "BYTE_DONE Timeout!\n");
+ return -ETIMEDOUT;
+ }
+ return status & STATUS_ERROR_FLAGS;
+}
- outb_p(status, SMBHSTSTS(priv));
+static int i801_transaction(struct i801_priv *priv, int xact)
+{
+ int status;
+ int result;
+
+ result = i801_check_pre(priv);
+ if (result < 0)
+ return result;
+
+ if (priv->features & FEATURE_IRQ) {
+ outb_p(xact | SMBHSTCNT_INTREN | SMBHSTCNT_START,
+ SMBHSTCNT(priv));
+ wait_event(priv->waitq, (status = priv->status));
+ priv->status = 0;
+ return i801_check_post(priv, status);
+ }
+
+ /* the current contents of SMBHSTCNT can be overwritten, since PEC,
+ * SMBSCMD are passed in xact */
+ outb_p(xact | SMBHSTCNT_START, SMBHSTCNT(priv));
+
+ status = i801_wait_intr(priv);
+ return i801_check_post(priv, status);
}
static int i801_block_transaction_by_block(struct i801_priv *priv,
@@ -324,8 +408,8 @@ static int i801_block_transaction_by_block(struct i801_priv *priv,
outb_p(data->block[i+1], SMBBLKDAT(priv));
}
- status = i801_transaction(priv, I801_BLOCK_DATA | ENABLE_INT9 |
- I801_PEC_EN * hwpec);
+ status = i801_transaction(priv, I801_BLOCK_DATA |
+ (hwpec ? SMBHSTCNT_PEC_EN : 0));
if (status)
return status;
@@ -341,6 +425,98 @@ static int i801_block_transaction_by_block(struct i801_priv *priv,
return 0;
}
+static void i801_isr_byte_done(struct i801_priv *priv)
+{
+ if (priv->is_read) {
+ /* For SMBus block reads, length is received with first byte */
+ if (((priv->cmd & 0x1c) == I801_BLOCK_DATA) &&
+ (priv->count == 0)) {
+ priv->len = inb_p(SMBHSTDAT0(priv));
+ if (priv->len < 1 || priv->len > I2C_SMBUS_BLOCK_MAX) {
+ dev_err(&priv->pci_dev->dev,
+ "Illegal SMBus block read size %d\n",
+ priv->len);
+ /* FIXME: Recover */
+ priv->len = I2C_SMBUS_BLOCK_MAX;
+ } else {
+ dev_dbg(&priv->pci_dev->dev,
+ "SMBus block read size is %d\n",
+ priv->len);
+ }
+ priv->data[-1] = priv->len;
+ }
+
+ /* Read next byte */
+ if (priv->count < priv->len)
+ priv->data[priv->count++] = inb(SMBBLKDAT(priv));
+ else
+ dev_dbg(&priv->pci_dev->dev,
+ "Discarding extra byte on block read\n");
+
+ /* Set LAST_BYTE for last byte of read transaction */
+ if (priv->count == priv->len - 1)
+ outb_p(priv->cmd | SMBHSTCNT_LAST_BYTE,
+ SMBHSTCNT(priv));
+ } else if (priv->count < priv->len - 1) {
+ /* Write next byte, except for IRQ after last byte */
+ outb_p(priv->data[++priv->count], SMBBLKDAT(priv));
+ }
+
+ /* Clear BYTE_DONE to continue with next byte */
+ outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv));
+}
+
+/*
+ * There are two kinds of interrupts:
+ *
+ * 1) i801 signals transaction completion with one of these interrupts:
+ * INTR - Success
+ * DEV_ERR - Invalid command, NAK or communication timeout
+ * BUS_ERR - SMI# transaction collision
+ * FAILED - transaction was canceled due to a KILL request
+ * When any of these occur, update ->status and wake up the waitq.
+ * ->status must be cleared before kicking off the next transaction.
+ *
+ * 2) For byte-by-byte (I2C read/write) transactions, one BYTE_DONE interrupt
+ * occurs for each byte of a byte-by-byte to prepare the next byte.
+ */
+static irqreturn_t i801_isr(int irq, void *dev_id)
+{
+ struct i801_priv *priv = dev_id;
+ u16 pcists;
+ u8 status;
+
+ /* Confirm this is our interrupt */
+ pci_read_config_word(priv->pci_dev, SMBPCISTS, &pcists);
+ if (!(pcists & SMBPCISTS_INTS))
+ return IRQ_NONE;
+
+ status = inb_p(SMBHSTSTS(priv));
+ if (status != 0x42)
+ dev_dbg(&priv->pci_dev->dev, "irq: status = %02x\n", status);
+
+ if (status & SMBHSTSTS_BYTE_DONE)
+ i801_isr_byte_done(priv);
+
+ /*
+ * Clear irq sources and report transaction result.
+ * ->status must be cleared before the next transaction is started.
+ */
+ status &= SMBHSTSTS_INTR | STATUS_ERROR_FLAGS;
+ if (status) {
+ outb_p(status, SMBHSTSTS(priv));
+ priv->status |= status;
+ wake_up(&priv->waitq);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * For "byte-by-byte" block transactions:
+ * I2C write uses cmd=I801_BLOCK_DATA, I2C_EN=1
+ * I2C read uses cmd=I801_I2C_BLOCK_DATA
+ */
static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
union i2c_smbus_data *data,
char read_write, int command,
@@ -350,7 +526,6 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
int smbcmd;
int status;
int result;
- int timeout;
result = i801_check_pre(priv);
if (result < 0)
@@ -363,36 +538,39 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
outb_p(data->block[1], SMBBLKDAT(priv));
}
+ if (command == I2C_SMBUS_I2C_BLOCK_DATA &&
+ read_write == I2C_SMBUS_READ)
+ smbcmd = I801_I2C_BLOCK_DATA;
+ else
+ smbcmd = I801_BLOCK_DATA;
+
+ if (priv->features & FEATURE_IRQ) {
+ priv->is_read = (read_write == I2C_SMBUS_READ);
+ if (len == 1 && priv->is_read)
+ smbcmd |= SMBHSTCNT_LAST_BYTE;
+ priv->cmd = smbcmd | SMBHSTCNT_INTREN;
+ priv->len = len;
+ priv->count = 0;
+ priv->data = &data->block[1];
+
+ outb_p(priv->cmd | SMBHSTCNT_START, SMBHSTCNT(priv));
+ wait_event(priv->waitq, (status = priv->status));
+ priv->status = 0;
+ return i801_check_post(priv, status);
+ }
+
for (i = 1; i <= len; i++) {
- if (i == len && read_write == I2C_SMBUS_READ) {
- if (command == I2C_SMBUS_I2C_BLOCK_DATA)
- smbcmd = I801_I2C_BLOCK_LAST;
- else
- smbcmd = I801_BLOCK_LAST;
- } else {
- if (command == I2C_SMBUS_I2C_BLOCK_DATA
- && read_write == I2C_SMBUS_READ)
- smbcmd = I801_I2C_BLOCK_DATA;
- else
- smbcmd = I801_BLOCK_DATA;
- }
- outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT(priv));
+ if (i == len && read_write == I2C_SMBUS_READ)
+ smbcmd |= SMBHSTCNT_LAST_BYTE;
+ outb_p(smbcmd, SMBHSTCNT(priv));
if (i == 1)
- outb_p(inb(SMBHSTCNT(priv)) | I801_START,
+ outb_p(inb(SMBHSTCNT(priv)) | SMBHSTCNT_START,
SMBHSTCNT(priv));
- /* We will always wait for a fraction of a second! */
- timeout = 0;
- do {
- msleep(1);
- status = inb_p(SMBHSTSTS(priv));
- } while ((!(status & SMBHSTSTS_BYTE_DONE))
- && (timeout++ < MAX_TIMEOUT));
-
- result = i801_check_post(priv, status, timeout > MAX_TIMEOUT);
- if (result < 0)
- return result;
+ status = i801_wait_byte_done(priv);
+ if (status)
+ goto exit;
if (i == 1 && read_write == I2C_SMBUS_READ
&& command != I2C_SMBUS_I2C_BLOCK_DATA) {
@@ -419,10 +597,12 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
outb_p(data->block[i+1], SMBBLKDAT(priv));
/* signals SMBBLKDAT ready */
- outb_p(SMBHSTSTS_BYTE_DONE | SMBHSTSTS_INTR, SMBHSTSTS(priv));
+ outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv));
}
- return 0;
+ status = i801_wait_intr(priv);
+exit:
+ return i801_check_post(priv, status);
}
static int i801_set_block_buffer_mode(struct i801_priv *priv)
@@ -477,9 +657,6 @@ static int i801_block_transaction(struct i801_priv *priv,
read_write,
command, hwpec);
- if (result == 0 && hwpec)
- i801_wait_hwpec(priv);
-
if (command == I2C_SMBUS_I2C_BLOCK_DATA
&& read_write == I2C_SMBUS_WRITE) {
/* restore saved configuration register value */
@@ -567,7 +744,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
ret = i801_block_transaction(priv, data, read_write, size,
hwpec);
else
- ret = i801_transaction(priv, xact | ENABLE_INT9);
+ ret = i801_transaction(priv, xact);
/* Some BIOSes don't like it when PEC is enabled at reboot or resume
time, so we forcibly disable it after every transaction. Turn off
@@ -641,6 +818,13 @@ static const struct pci_device_id i801_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_AVOTON_SMBUS) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS0) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS1) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COLETOCREEK_SMBUS) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_SMBUS) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BAYTRAIL_SMBUS) },
{ 0, }
};
@@ -684,14 +868,14 @@ struct dmi_onboard_device_info {
const char *i2c_type;
};
-static struct dmi_onboard_device_info __devinitdata dmi_devices[] = {
+static const struct dmi_onboard_device_info dmi_devices[] = {
{ "Syleus", DMI_DEV_TYPE_OTHER, 0x73, "fscsyl" },
{ "Hermes", DMI_DEV_TYPE_OTHER, 0x73, "fscher" },
{ "Hades", DMI_DEV_TYPE_OTHER, 0x73, "fschds" },
};
-static void __devinit dmi_check_onboard_device(u8 type, const char *name,
- struct i2c_adapter *adap)
+static void dmi_check_onboard_device(u8 type, const char *name,
+ struct i2c_adapter *adap)
{
int i;
struct i2c_board_info info;
@@ -714,8 +898,7 @@ static void __devinit dmi_check_onboard_device(u8 type, const char *name,
/* We use our own function to check for onboard devices instead of
dmi_find_device() as some buggy BIOS's have the devices we are interested
in marked as disabled */
-static void __devinit dmi_check_onboard_devices(const struct dmi_header *dm,
- void *adap)
+static void dmi_check_onboard_devices(const struct dmi_header *dm, void *adap)
{
int i, count;
@@ -744,7 +927,7 @@ static void __devinit dmi_check_onboard_devices(const struct dmi_header *dm,
}
/* Register optional slaves */
-static void __devinit i801_probe_optional_slaves(struct i801_priv *priv)
+static void i801_probe_optional_slaves(struct i801_priv *priv)
{
/* Only register slaves on main SMBus channel */
if (priv->features & FEATURE_IDF)
@@ -764,11 +947,170 @@ static void __devinit i801_probe_optional_slaves(struct i801_priv *priv)
}
#else
static void __init input_apanel_init(void) {}
-static void __devinit i801_probe_optional_slaves(struct i801_priv *priv) {}
+static void i801_probe_optional_slaves(struct i801_priv *priv) {}
#endif /* CONFIG_X86 && CONFIG_DMI */
-static int __devinit i801_probe(struct pci_dev *dev,
- const struct pci_device_id *id)
+#if (defined CONFIG_I2C_MUX_GPIO || defined CONFIG_I2C_MUX_GPIO_MODULE) && \
+ defined CONFIG_DMI
+static struct i801_mux_config i801_mux_config_asus_z8_d12 = {
+ .gpio_chip = "gpio_ich",
+ .values = { 0x02, 0x03 },
+ .n_values = 2,
+ .classes = { I2C_CLASS_SPD, I2C_CLASS_SPD },
+ .gpios = { 52, 53 },
+ .n_gpios = 2,
+};
+
+static struct i801_mux_config i801_mux_config_asus_z8_d18 = {
+ .gpio_chip = "gpio_ich",
+ .values = { 0x02, 0x03, 0x01 },
+ .n_values = 3,
+ .classes = { I2C_CLASS_SPD, I2C_CLASS_SPD, I2C_CLASS_SPD },
+ .gpios = { 52, 53 },
+ .n_gpios = 2,
+};
+
+static const struct dmi_system_id mux_dmi_table[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "Z8NA-D6(C)"),
+ },
+ .driver_data = &i801_mux_config_asus_z8_d12,
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "Z8P(N)E-D12(X)"),
+ },
+ .driver_data = &i801_mux_config_asus_z8_d12,
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "Z8NH-D12"),
+ },
+ .driver_data = &i801_mux_config_asus_z8_d12,
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "Z8PH-D12/IFB"),
+ },
+ .driver_data = &i801_mux_config_asus_z8_d12,
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "Z8NR-D12"),
+ },
+ .driver_data = &i801_mux_config_asus_z8_d12,
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "Z8P(N)H-D12"),
+ },
+ .driver_data = &i801_mux_config_asus_z8_d12,
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "Z8PG-D18"),
+ },
+ .driver_data = &i801_mux_config_asus_z8_d18,
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "Z8PE-D18"),
+ },
+ .driver_data = &i801_mux_config_asus_z8_d18,
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "Z8PS-D12"),
+ },
+ .driver_data = &i801_mux_config_asus_z8_d12,
+ },
+ { }
+};
+
+/* Setup multiplexing if needed */
+static int i801_add_mux(struct i801_priv *priv)
+{
+ struct device *dev = &priv->adapter.dev;
+ const struct i801_mux_config *mux_config;
+ struct i2c_mux_gpio_platform_data gpio_data;
+ int err;
+
+ if (!priv->mux_drvdata)
+ return 0;
+ mux_config = priv->mux_drvdata;
+
+ /* Prepare the platform data */
+ memset(&gpio_data, 0, sizeof(struct i2c_mux_gpio_platform_data));
+ gpio_data.parent = priv->adapter.nr;
+ gpio_data.values = mux_config->values;
+ gpio_data.n_values = mux_config->n_values;
+ gpio_data.classes = mux_config->classes;
+ gpio_data.gpio_chip = mux_config->gpio_chip;
+ gpio_data.gpios = mux_config->gpios;
+ gpio_data.n_gpios = mux_config->n_gpios;
+ gpio_data.idle = I2C_MUX_GPIO_NO_IDLE;
+
+ /* Register the mux device */
+ priv->mux_pdev = platform_device_register_data(dev, "i2c-mux-gpio",
+ PLATFORM_DEVID_AUTO, &gpio_data,
+ sizeof(struct i2c_mux_gpio_platform_data));
+ if (IS_ERR(priv->mux_pdev)) {
+ err = PTR_ERR(priv->mux_pdev);
+ priv->mux_pdev = NULL;
+ dev_err(dev, "Failed to register i2c-mux-gpio device\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static void i801_del_mux(struct i801_priv *priv)
+{
+ if (priv->mux_pdev)
+ platform_device_unregister(priv->mux_pdev);
+}
+
+static unsigned int i801_get_adapter_class(struct i801_priv *priv)
+{
+ const struct dmi_system_id *id;
+ const struct i801_mux_config *mux_config;
+ unsigned int class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ int i;
+
+ id = dmi_first_match(mux_dmi_table);
+ if (id) {
+ /* Remove branch classes from trunk */
+ mux_config = id->driver_data;
+ for (i = 0; i < mux_config->n_values; i++)
+ class &= ~mux_config->classes[i];
+
+ /* Remember for later */
+ priv->mux_drvdata = mux_config;
+ }
+
+ return class;
+}
+#else
+static inline int i801_add_mux(struct i801_priv *priv) { return 0; }
+static inline void i801_del_mux(struct i801_priv *priv) { }
+
+static inline unsigned int i801_get_adapter_class(struct i801_priv *priv)
+{
+ return I2C_CLASS_HWMON | I2C_CLASS_SPD;
+}
+#endif
+
+static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
unsigned char temp;
int err, i;
@@ -780,7 +1122,7 @@ static int __devinit i801_probe(struct pci_dev *dev,
i2c_set_adapdata(&priv->adapter, priv);
priv->adapter.owner = THIS_MODULE;
- priv->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ priv->adapter.class = i801_get_adapter_class(priv);
priv->adapter.algo = &smbus_algorithm;
priv->pci_dev = dev;
@@ -788,10 +1130,14 @@ static int __devinit i801_probe(struct pci_dev *dev,
case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0:
case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1:
case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2:
+ case PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS0:
+ case PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS1:
+ case PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2:
priv->features |= FEATURE_IDF;
/* fall through */
default:
priv->features |= FEATURE_I2C_BLOCK_READ;
+ priv->features |= FEATURE_IRQ;
/* fall through */
case PCI_DEVICE_ID_INTEL_82801DB_3:
priv->features |= FEATURE_SMBUS_PEC;
@@ -851,16 +1197,30 @@ static int __devinit i801_probe(struct pci_dev *dev,
}
pci_write_config_byte(priv->pci_dev, SMBHSTCFG, temp);
- if (temp & SMBHSTCFG_SMB_SMI_EN)
+ if (temp & SMBHSTCFG_SMB_SMI_EN) {
dev_dbg(&dev->dev, "SMBus using interrupt SMI#\n");
- else
- dev_dbg(&dev->dev, "SMBus using PCI Interrupt\n");
+ /* Disable SMBus interrupt feature if SMBus using SMI# */
+ priv->features &= ~FEATURE_IRQ;
+ }
/* Clear special mode bits */
if (priv->features & (FEATURE_SMBUS_PEC | FEATURE_BLOCK_BUFFER))
outb_p(inb_p(SMBAUXCTL(priv)) &
~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv));
+ if (priv->features & FEATURE_IRQ) {
+ init_waitqueue_head(&priv->waitq);
+
+ err = request_irq(dev->irq, i801_isr, IRQF_SHARED,
+ i801_driver.name, priv);
+ if (err) {
+ dev_err(&dev->dev, "Failed to allocate irq %d: %d\n",
+ dev->irq, err);
+ goto exit_release;
+ }
+ dev_info(&dev->dev, "SMBus using PCI Interrupt\n");
+ }
+
/* set up the sysfs linkage to our parent device */
priv->adapter.dev.parent = &dev->dev;
@@ -872,14 +1232,20 @@ static int __devinit i801_probe(struct pci_dev *dev,
err = i2c_add_adapter(&priv->adapter);
if (err) {
dev_err(&dev->dev, "Failed to add SMBus adapter\n");
- goto exit_release;
+ goto exit_free_irq;
}
i801_probe_optional_slaves(priv);
+ /* We ignore errors - multiplexing is optional */
+ i801_add_mux(priv);
pci_set_drvdata(dev, priv);
+
return 0;
+exit_free_irq:
+ if (priv->features & FEATURE_IRQ)
+ free_irq(dev->irq, priv);
exit_release:
pci_release_region(dev, SMBBAR);
exit:
@@ -887,14 +1253,18 @@ exit:
return err;
}
-static void __devexit i801_remove(struct pci_dev *dev)
+static void i801_remove(struct pci_dev *dev)
{
struct i801_priv *priv = pci_get_drvdata(dev);
+ i801_del_mux(priv);
i2c_del_adapter(&priv->adapter);
pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg);
+
+ if (priv->features & FEATURE_IRQ)
+ free_irq(dev->irq, priv);
pci_release_region(dev, SMBBAR);
- pci_set_drvdata(dev, NULL);
+
kfree(priv);
/*
* do not call pci_disable_device(dev) since it can cause hard hangs on
@@ -928,7 +1298,7 @@ static struct pci_driver i801_driver = {
.name = "i801_smbus",
.id_table = i801_ids,
.probe = i801_probe,
- .remove = __devexit_p(i801_remove),
+ .remove = i801_remove,
.suspend = i801_suspend,
.resume = i801_resume,
};
@@ -945,8 +1315,7 @@ static void __exit i2c_i801_exit(void)
pci_unregister_driver(&i801_driver);
}
-MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>, "
- "Jean Delvare <khali@linux-fr.org>");
+MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>, Jean Delvare <jdelvare@suse.de>");
MODULE_DESCRIPTION("I801 SMBus driver");
MODULE_LICENSE("GPL");
diff --git a/include/linux/i2c-mux-gpio.h b/include/linux/i2c-mux-gpio.h
new file mode 100644
index 0000000..4406108
--- /dev/null
+++ b/include/linux/i2c-mux-gpio.h
@@ -0,0 +1,43 @@
+/*
+ * i2c-mux-gpio interface to platform code
+ *
+ * Peter Korsgaard <peter.korsgaard@barco.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _LINUX_I2C_MUX_GPIO_H
+#define _LINUX_I2C_MUX_GPIO_H
+
+/* MUX has no specific idle mode */
+#define I2C_MUX_GPIO_NO_IDLE ((unsigned)-1)
+
+/**
+ * struct i2c_mux_gpio_platform_data - Platform-dependent data for i2c-mux-gpio
+ * @parent: Parent I2C bus adapter number
+ * @base_nr: Base I2C bus number to number adapters from or zero for dynamic
+ * @values: Array of bitmasks of GPIO settings (low/high) for each
+ * position
+ * @n_values: Number of multiplexer positions (busses to instantiate)
+ * @classes: Optional I2C auto-detection classes
+ * @gpio_chip: Optional GPIO chip name; if set, GPIO pin numbers are given
+ * relative to the base GPIO number of that chip
+ * @gpios: Array of GPIO numbers used to control MUX
+ * @n_gpios: Number of GPIOs used to control MUX
+ * @idle: Bitmask to write to MUX when idle or GPIO_I2CMUX_NO_IDLE if not used
+ */
+struct i2c_mux_gpio_platform_data {
+ int parent;
+ int base_nr;
+ const unsigned *values;
+ int n_values;
+ const unsigned *classes;
+ char *gpio_chip;
+ const unsigned *gpios;
+ int n_gpios;
+ unsigned idle;
+};
+
+#endif /* _LINUX_I2C_MUX_GPIO_H */
diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h
index 165a8d1..4cf54aa 100644
--- a/include/linux/platform_device.h
+++ b/include/linux/platform_device.h
@@ -14,6 +14,9 @@
#include <linux/device.h>
#include <linux/mod_devicetable.h>
+#define PLATFORM_DEVID_NONE (-1)
+#define PLATFORM_DEVID_AUTO (-2)
+
struct mfd_cell;
struct platform_device {

View File

@@ -1,50 +0,0 @@
Add 'delay' module param to the driver. This is needed on S6000 for safe PMBUS
access. Without setting the 'delay', the ismt driver throws 'completion wait
timed out' error message.
diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c
index 09a3e03..44f54ff 100644
--- a/drivers/i2c/busses/i2c-ismt.c
+++ b/drivers/i2c/busses/i2c-ismt.c
@@ -70,6 +70,7 @@
#include <linux/i2c.h>
#include <linux/acpi.h>
#include <linux/interrupt.h>
+#include <linux/delay.h>
#include <asm-generic/io-64-nonatomic-lo-hi.h>
@@ -192,9 +193,12 @@ static DEFINE_PCI_DEVICE_TABLE(ismt_ids) = {
MODULE_DEVICE_TABLE(pci, ismt_ids);
/* Bus speed control bits for slow debuggers - refer to the docs for usage */
-static unsigned int bus_speed;
+static unsigned int bus_speed = 100;
+static unsigned int delay = 1000;
module_param(bus_speed, uint, S_IRUGO);
-MODULE_PARM_DESC(bus_speed, "Bus Speed in kHz (0 = BIOS default)");
+MODULE_PARM_DESC(bus_speed, "Bus Speed in kHz (1000 by default)");
+module_param(delay, uint, S_IRUGO);
+MODULE_PARM_DESC(delay, "Delay in microsecs before access (1000 by default)");
/**
* __ismt_desc_dump() - dump the contents of a specific descriptor
@@ -391,6 +395,9 @@ static int ismt_access(struct i2c_adapter *adap, u16 addr,
struct ismt_priv *priv = i2c_get_adapdata(adap);
struct device *dev = &priv->pci_dev->dev;
+ if (delay > 0)
+ udelay(delay);
+
desc = &priv->hw[priv->head];
/* Initialize the DMA buffer */
@@ -756,7 +763,7 @@ static void ismt_hw_init(struct ismt_priv *priv)
bus_speed = 1000;
break;
}
- dev_dbg(dev, "SMBus clock is running at %d kHz\n", bus_speed);
+ dev_info(dev, "SMBus clock is running at %d kHz with delay %d us\n", bus_speed, delay);
}
/**

View File

@@ -1,159 +0,0 @@
Seperating the ismt_priv structure into a i2c-ismt.h that can they be used in
platform modules like dell_s6000_platform
diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c
index 44f54ff..9058fc6 100644
--- a/drivers/i2c/busses/i2c-ismt.c
+++ b/drivers/i2c/busses/i2c-ismt.c
@@ -62,15 +62,12 @@
*/
#include <linux/module.h>
-#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
-#include <linux/completion.h>
-#include <linux/dma-mapping.h>
-#include <linux/i2c.h>
#include <linux/acpi.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
+#include <linux/i2c-ismt.h>
#include <asm-generic/io-64-nonatomic-lo-hi.h>
@@ -150,36 +147,9 @@
#define ISMT_SPGT_SPD_400K (0x2 << 30) /* 400 kHz */
#define ISMT_SPGT_SPD_1M (0x3 << 30) /* 1 MHz */
-
/* MSI Control Register (MSICTL) bit definitions */
#define ISMT_MSICTL_MSIE 0x01 /* MSI Enable */
-/* iSMT Hardware Descriptor */
-struct ismt_desc {
- u8 tgtaddr_rw; /* target address & r/w bit */
- u8 wr_len_cmd; /* write length in bytes or a command */
- u8 rd_len; /* read length */
- u8 control; /* control bits */
- u8 status; /* status bits */
- u8 retry; /* collision retry and retry count */
- u8 rxbytes; /* received bytes */
- u8 txbytes; /* transmitted bytes */
- u32 dptr_low; /* lower 32 bit of the data pointer */
- u32 dptr_high; /* upper 32 bit of the data pointer */
-} __packed;
-
-struct ismt_priv {
- struct i2c_adapter adapter;
- void *smba; /* PCI BAR */
- struct pci_dev *pci_dev;
- struct ismt_desc *hw; /* descriptor virt base addr */
- dma_addr_t io_rng_dma; /* descriptor HW base addr */
- u8 head; /* ring buffer head pointer */
- struct completion cmp; /* interrupt completion */
- u8 dma_buffer[I2C_SMBUS_BLOCK_MAX + 1]; /* temp R/W data buffer */
- bool using_msi; /* type of interrupt flag */
-};
-
/**
* ismt_ids - PCI device IDs supported by this driver
*/
diff --git a/include/linux/i2c-ismt.h b/include/linux/i2c-ismt.h
new file mode 100644
index 0000000..98be6d7
--- /dev/null
+++ b/include/linux/i2c-ismt.h
@@ -0,0 +1,92 @@
+/*
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * Copyright(c) 2012 Intel Corporation. All rights reserved.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * BSD LICENSE
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Supports the SMBus Message Transport (SMT) in the Intel Atom Processor
+ * S12xx Product Family.
+ *
+ * Features supported by this driver:
+ * Hardware PEC yes
+ * Block buffer yes
+ * Block process call transaction no
+ * Slave mode no
+ */
+#include <linux/i2c.h>
+#include <linux/pci.h>
+#include <linux/completion.h>
+#include <linux/dma-mapping.h>
+
+/* iSMT Hardware Descriptor */
+struct ismt_desc {
+ u8 tgtaddr_rw; /* target address & r/w bit */
+ u8 wr_len_cmd; /* write length in bytes or a command */
+ u8 rd_len; /* read length */
+ u8 control; /* control bits */
+ u8 status; /* status bits */
+ u8 retry; /* collision retry and retry count */
+ u8 rxbytes; /* received bytes */
+ u8 txbytes; /* transmitted bytes */
+ u32 dptr_low; /* lower 32 bit of the data pointer */
+ u32 dptr_high; /* upper 32 bit of the data pointer */
+} __packed;
+
+struct ismt_priv {
+ struct i2c_adapter adapter;
+ void *smba; /* PCI BAR */
+ struct pci_dev *pci_dev;
+ struct ismt_desc *hw; /* descriptor virt base addr */
+ dma_addr_t io_rng_dma; /* descriptor HW base addr */
+ u8 head; /* ring buffer head pointer */
+ struct completion cmp; /* interrupt completion */
+ u8 dma_buffer[I2C_SMBUS_BLOCK_MAX + 1]; /* temp R/W data buffer */
+ bool using_msi; /* type of interrupt flag */
+};

View File

@@ -1,247 +0,0 @@
This is back porting the i2c mux device tree bindings patch from
David Deney: https://lkml.org/lkml/2012/4/12/422
Quoting that email:
We need to populate our I2C devices from the device tree, but some
of our systems have multiplexers which currently break this process.
The basic idea is to have the generic i2c-mux framework propagate
the device_node for the child bus into the corresponding
i2c_adapter, and then call of_i2c_register_devices(). This means
that the device tree bindings for *all* i2c muxes must have some
common properties. I try to document these in mux.txt.
diff --git a/Documentation/devicetree/bindings/i2c/mux.txt b/Documentation/devicetree/bindings/i2c/mux.txt
new file mode 100644
index 0000000..af84cce
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/mux.txt
@@ -0,0 +1,60 @@
+Common i2c bus multiplexer/switch properties.
+
+An i2c bus multiplexer/switch will have several child busses that are
+numbered uniquely in a device dependent manner. The nodes for an i2c bus
+multiplexer/switch will have one child node for each child
+bus.
+
+Required properties:
+- #address-cells = <1>;
+- #size-cells = <0>;
+
+Required properties for child nodes:
+- #address-cells = <1>;
+- #size-cells = <0>;
+- reg : The sub-bus number.
+
+Optional properties for child nodes:
+- Other properties specific to the multiplexer/switch hardware.
+- Child nodes conforming to i2c bus binding
+
+
+Example :
+
+ /*
+ An NXP pca9548 8 channel I2C multiplexer at address 0x70
+ with two NXP pca8574 GPIO expanders attached, one each to
+ ports 3 and 4.
+ */
+
+ mux@70 {
+ compatible = "nxp,pca9548";
+ reg = <0x70>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ i2c@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+
+ gpio1: gpio@38 {
+ compatible = "nxp,pca8574";
+ reg = <0x38>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ };
+ };
+ i2c@4 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <4>;
+
+ gpio2: gpio@38 {
+ compatible = "nxp,pca8574";
+ reg = <0x38>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ };
+ };
+ };
diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c
index d7a4833..609794b 100644
--- a/drivers/i2c/i2c-mux.c
+++ b/drivers/i2c/i2c-mux.c
@@ -24,6 +24,8 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
+#include <linux/of.h>
+#include <linux/of_i2c.h>
/* multiplexer per channel data */
struct i2c_mux_priv {
@@ -31,11 +33,11 @@ struct i2c_mux_priv {
struct i2c_algorithm algo;
struct i2c_adapter *parent;
- void *mux_dev; /* the mux chip/device */
+ void *mux_priv; /* the mux chip/device */
u32 chan_id; /* the channel id */
- int (*select)(struct i2c_adapter *, void *mux_dev, u32 chan_id);
- int (*deselect)(struct i2c_adapter *, void *mux_dev, u32 chan_id);
+ int (*select)(struct i2c_adapter *, void *mux_priv, u32 chan_id);
+ int (*deselect)(struct i2c_adapter *, void *mux_priv, u32 chan_id);
};
static int i2c_mux_master_xfer(struct i2c_adapter *adap,
@@ -47,11 +49,11 @@ static int i2c_mux_master_xfer(struct i2c_adapter *adap,
/* Switch to the right mux port and perform the transfer. */
- ret = priv->select(parent, priv->mux_dev, priv->chan_id);
+ ret = priv->select(parent, priv->mux_priv, priv->chan_id);
if (ret >= 0)
ret = parent->algo->master_xfer(parent, msgs, num);
if (priv->deselect)
- priv->deselect(parent, priv->mux_dev, priv->chan_id);
+ priv->deselect(parent, priv->mux_priv, priv->chan_id);
return ret;
}
@@ -67,12 +69,12 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap,
/* Select the right mux port and perform the transfer. */
- ret = priv->select(parent, priv->mux_dev, priv->chan_id);
+ ret = priv->select(parent, priv->mux_priv, priv->chan_id);
if (ret >= 0)
ret = parent->algo->smbus_xfer(parent, addr, flags,
read_write, command, size, data);
if (priv->deselect)
- priv->deselect(parent, priv->mux_dev, priv->chan_id);
+ priv->deselect(parent, priv->mux_priv, priv->chan_id);
return ret;
}
@@ -87,7 +89,8 @@ static u32 i2c_mux_functionality(struct i2c_adapter *adap)
}
struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
- void *mux_dev, u32 force_nr, u32 chan_id,
+ struct device *mux_dev,
+ void *mux_priv, u32 force_nr, u32 chan_id,
int (*select) (struct i2c_adapter *,
void *, u32),
int (*deselect) (struct i2c_adapter *,
@@ -102,7 +105,7 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
/* Set up private adapter data */
priv->parent = parent;
- priv->mux_dev = mux_dev;
+ priv->mux_priv = mux_priv;
priv->chan_id = chan_id;
priv->select = select;
priv->deselect = deselect;
@@ -124,6 +127,26 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
priv->adap.algo_data = priv;
priv->adap.dev.parent = &parent->dev;
+ /*
+ * Try to get populate the mux adapter's of_node, expands to
+ * nothing if !CONFIG_OF.
+ */
+ if (mux_dev->of_node) {
+ struct device_node *child;
+ u32 reg;
+ int ret;
+
+ for_each_child_of_node(mux_dev->of_node, child) {
+ ret = of_property_read_u32(child, "reg", &reg);
+ if (ret)
+ continue;
+ if (chan_id == reg) {
+ priv->adap.dev.of_node = child;
+ break;
+ }
+ }
+ }
+
if (force_nr) {
priv->adap.nr = force_nr;
ret = i2c_add_numbered_adapter(&priv->adap);
@@ -141,6 +164,8 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
dev_info(&parent->dev, "Added multiplexed i2c bus %d\n",
i2c_adapter_id(&priv->adap));
+ of_i2c_register_devices(&priv->adap);
+
return &priv->adap;
}
EXPORT_SYMBOL_GPL(i2c_add_mux_adapter);
diff --git a/drivers/i2c/muxes/gpio-i2cmux.c b/drivers/i2c/muxes/gpio-i2cmux.c
index 7b6ce62..cd238c7 100644
--- a/drivers/i2c/muxes/gpio-i2cmux.c
+++ b/drivers/i2c/muxes/gpio-i2cmux.c
@@ -105,7 +105,8 @@ static int __devinit gpiomux_probe(struct platform_device *pdev)
for (i = 0; i < pdata->n_values; i++) {
u32 nr = pdata->base_nr ? (pdata->base_nr + i) : 0;
- mux->adap[i] = i2c_add_mux_adapter(parent, mux, nr, i,
+ mux->adap[i] = i2c_add_mux_adapter(parent, &pdev->dev, mux,
+ nr, i,
gpiomux_select, deselect);
if (!mux->adap[i]) {
ret = -ENODEV;
diff --git a/drivers/i2c/muxes/pca9541.c b/drivers/i2c/muxes/pca9541.c
index ed699c5..e9e07ba 100644
--- a/drivers/i2c/muxes/pca9541.c
+++ b/drivers/i2c/muxes/pca9541.c
@@ -353,7 +353,8 @@ static int pca9541_probe(struct i2c_client *client,
force = 0;
if (pdata)
force = pdata->modes[0].adap_id;
- data->mux_adap = i2c_add_mux_adapter(adap, client, force, 0,
+ data->mux_adap = i2c_add_mux_adapter(adap, &client->dev, client,
+ force, 0,
pca9541_select_chan,
pca9541_release_chan);
diff --git a/drivers/i2c/muxes/pca954x.c b/drivers/i2c/muxes/pca954x.c
index 6f89536..5c6ecc7 100644
--- a/drivers/i2c/muxes/pca954x.c
+++ b/drivers/i2c/muxes/pca954x.c
@@ -226,7 +226,7 @@ static int pca954x_probe(struct i2c_client *client,
}
data->virt_adaps[num] =
- i2c_add_mux_adapter(adap, client,
+ i2c_add_mux_adapter(adap, &client->dev, client,
force, num, pca954x_select_chan,
(pdata && pdata->modes[num].deselect_on_exit)
? pca954x_deselect_mux : NULL);
diff --git a/include/linux/i2c-mux.h b/include/linux/i2c-mux.h
index 34536ef..260ca6a 100644
--- a/include/linux/i2c-mux.h
+++ b/include/linux/i2c-mux.h
@@ -33,7 +33,8 @@
* mux control.
*/
struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
- void *mux_dev, u32 force_nr, u32 chan_id,
+ struct device *mux_dev,
+ void *mux_priv, u32 force_nr, u32 chan_id,
int (*select) (struct i2c_adapter *,
void *mux_dev, u32 chan_id),
int (*deselect) (struct i2c_adapter *,

View File

@@ -1,210 +0,0 @@
Back port Intel's Multifunction Device (MFD) driver.
Adds Intel Centerton Multifunction Device support.
This patch adds the Intel Centerton processor DeviceID for the
Integrated Legacy Block (ILB). The ILB provides GPIO, SMBus, and
Watchdog functionality.
Summary:
diff --git a/drivers/mfd/lpc_sch.c b/drivers/mfd/lpc_sch.c
index ea1169b..753d6ef 100644
--- a/drivers/mfd/lpc_sch.c
+++ b/drivers/mfd/lpc_sch.c
@@ -36,6 +36,7 @@
#define GPIOBASE 0x44
#define GPIO_IO_SIZE 64
+#define GPIO_IO_SIZE_CENTERTON 128
#define WDTBASE 0x84
#define WDT_IO_SIZE 64
@@ -44,39 +45,41 @@ static struct resource smbus_sch_resource = {
.flags = IORESOURCE_IO,
};
-
static struct resource gpio_sch_resource = {
.flags = IORESOURCE_IO,
};
-static struct mfd_cell lpc_sch_cells[] = {
- {
- .name = "isch_smbus",
- .num_resources = 1,
- .resources = &smbus_sch_resource,
- },
- {
- .name = "sch_gpio",
- .num_resources = 1,
- .resources = &gpio_sch_resource,
- },
-};
-
static struct resource wdt_sch_resource = {
.flags = IORESOURCE_IO,
};
-static struct mfd_cell tunnelcreek_cells[] = {
- {
- .name = "tunnelcreek_wdt",
- .num_resources = 1,
- .resources = &wdt_sch_resource,
- },
+static struct mfd_cell lpc_sch_cells[3];
+
+static struct mfd_cell isch_smbus_cell = {
+ .name = "isch_smbus",
+ .num_resources = 1,
+ .resources = &smbus_sch_resource,
+ .ignore_resource_conflicts = true,
};
-static struct pci_device_id lpc_sch_ids[] = {
+static struct mfd_cell sch_gpio_cell = {
+ .name = "sch_gpio",
+ .num_resources = 1,
+ .resources = &gpio_sch_resource,
+ .ignore_resource_conflicts = true,
+};
+
+static struct mfd_cell wdt_sch_cell = {
+ .name = "ie6xx_wdt",
+ .num_resources = 1,
+ .resources = &wdt_sch_resource,
+ .ignore_resource_conflicts = true,
+};
+
+static const struct pci_device_id lpc_sch_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CENTERTON_ILB) },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, lpc_sch_ids);
@@ -86,72 +89,76 @@ static int __devinit lpc_sch_probe(struct pci_dev *dev,
{
unsigned int base_addr_cfg;
unsigned short base_addr;
- int i;
+ int i, cells = 0;
int ret;
pci_read_config_dword(dev, SMBASE, &base_addr_cfg);
- if (!(base_addr_cfg & (1 << 31))) {
- dev_err(&dev->dev, "Decode of the SMBus I/O range disabled\n");
- return -ENODEV;
+ base_addr = 0;
+ if (!(base_addr_cfg & (1 << 31)))
+ dev_warn(&dev->dev, "Decode of the SMBus I/O range disabled\n");
+ else
+ base_addr = (unsigned short)base_addr_cfg;
+
+ if (base_addr == 0) {
+ dev_warn(&dev->dev, "I/O space for SMBus uninitialized\n");
+ } else {
+ lpc_sch_cells[cells++] = isch_smbus_cell;
+ smbus_sch_resource.start = base_addr;
+ smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1;
}
- base_addr = (unsigned short)base_addr_cfg;
+
+ pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
+ base_addr = 0;
+ if (!(base_addr_cfg & (1 << 31)))
+ dev_warn(&dev->dev, "Decode of the GPIO I/O range disabled\n");
+ else
+ base_addr = (unsigned short)base_addr_cfg;
+
if (base_addr == 0) {
- dev_err(&dev->dev, "I/O space for SMBus uninitialized\n");
- return -ENODEV;
+ dev_warn(&dev->dev, "I/O space for GPIO uninitialized\n");
+ } else {
+ lpc_sch_cells[cells++] = sch_gpio_cell;
+ gpio_sch_resource.start = base_addr;
+ if (id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB)
+ gpio_sch_resource.end = base_addr + GPIO_IO_SIZE_CENTERTON - 1;
+ else
+ gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1;
}
- smbus_sch_resource.start = base_addr;
- smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1;
+ if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC
+ || id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) {
+ pci_read_config_dword(dev, WDTBASE, &base_addr_cfg);
+ base_addr = 0;
+ if (!(base_addr_cfg & (1 << 31)))
+ dev_warn(&dev->dev, "Decode of the WDT I/O range disabled\n");
+ else
+ base_addr = (unsigned short)base_addr_cfg;
+ if (base_addr == 0)
+ dev_warn(&dev->dev, "I/O space for WDT uninitialized\n");
+ else {
+ lpc_sch_cells[cells++] = wdt_sch_cell;
+ wdt_sch_resource.start = base_addr;
+ wdt_sch_resource.end = base_addr + WDT_IO_SIZE - 1;
+ }
+ }
- pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
- if (!(base_addr_cfg & (1 << 31))) {
- dev_err(&dev->dev, "Decode of the GPIO I/O range disabled\n");
+ if (WARN_ON(cells > ARRAY_SIZE(lpc_sch_cells))) {
+ dev_err(&dev->dev, "Cell count exceeds array size");
return -ENODEV;
}
- base_addr = (unsigned short)base_addr_cfg;
- if (base_addr == 0) {
- dev_err(&dev->dev, "I/O space for GPIO uninitialized\n");
+
+ if (cells == 0) {
+ dev_err(&dev->dev, "All decode registers disabled.\n");
return -ENODEV;
}
- gpio_sch_resource.start = base_addr;
- gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1;
-
- for (i=0; i < ARRAY_SIZE(lpc_sch_cells); i++)
+ for (i = 0; i < cells; i++)
lpc_sch_cells[i].id = id->device;
- ret = mfd_add_devices(&dev->dev, 0,
- lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL, 0);
+ ret = mfd_add_devices(&dev->dev, 0, lpc_sch_cells, cells, NULL, 0);
if (ret)
- goto out_dev;
+ mfd_remove_devices(&dev->dev);
- if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC) {
- pci_read_config_dword(dev, WDTBASE, &base_addr_cfg);
- if (!(base_addr_cfg & (1 << 31))) {
- dev_err(&dev->dev, "Decode of the WDT I/O range disabled\n");
- ret = -ENODEV;
- goto out_dev;
- }
- base_addr = (unsigned short)base_addr_cfg;
- if (base_addr == 0) {
- dev_err(&dev->dev, "I/O space for WDT uninitialized\n");
- ret = -ENODEV;
- goto out_dev;
- }
-
- wdt_sch_resource.start = base_addr;
- wdt_sch_resource.end = base_addr + WDT_IO_SIZE - 1;
-
- for (i = 0; i < ARRAY_SIZE(tunnelcreek_cells); i++)
- tunnelcreek_cells[i].id = id->device;
-
- ret = mfd_add_devices(&dev->dev, 0, tunnelcreek_cells,
- ARRAY_SIZE(tunnelcreek_cells), NULL, 0);
- }
-
- return ret;
-out_dev:
- mfd_remove_devices(&dev->dev);
return ret;
}

View File

@@ -1,134 +0,0 @@
Add Micron N25Q64 ID to SPI rom driver
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index bc02754..9145658 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -47,6 +47,7 @@
#define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */
#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */
#define OPCODE_RDID 0x9f /* Read JEDEC ID */
+#define OPCODE_RDFSR 0x70 /* Read Flag Status Register */
/* Used for SST flashes only. */
#define OPCODE_BP 0x02 /* Byte program */
@@ -69,6 +70,9 @@
#define SR_BP2 0x10 /* Block protect 2 */
#define SR_SRWD 0x80 /* SR write protect */
+/* Flag Status Register bits. */
+#define FSR_RDY 0x80 /* Ready/Busy program erase controller */
+
/* Define max times to check status register before we give up. */
#define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */
#define MAX_CMD_SIZE 6
@@ -91,6 +95,7 @@ struct m25p {
struct mtd_info mtd;
u16 page_size;
u16 addr_width;
+ bool check_fsr;
u8 erase_opcode;
u8 *command;
};
@@ -107,6 +112,28 @@ static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd)
*/
/*
+ * Read the Flag Status Register (required for some Micron chips).
+ * Return the Flag Status Register value.
+ * Returns negative if error occurred.
+ */
+static int read_fsr(struct m25p *flash)
+{
+ ssize_t retval;
+ u8 code = OPCODE_RDFSR;
+ u8 val;
+
+ retval = spi_write_then_read(flash->spi, &code, 1, &val, 1);
+
+ if (retval < 0) {
+ dev_err(&flash->spi->dev, "error %d reading FSR\n",
+ (int) retval);
+ return retval;
+ }
+
+ return val;
+}
+
+/*
* Read the status register, returning its value in the location
* Return the status register value.
* Returns negative if error occurred.
@@ -166,10 +193,18 @@ static inline int write_disable(struct m25p *flash)
*/
static inline int set_4byte(struct m25p *flash, u32 jedec_id, int enable)
{
+ int status;
+
switch (JEDEC_MFR(jedec_id)) {
case CFI_MFR_MACRONIX:
flash->command[0] = enable ? OPCODE_EN4B : OPCODE_EX4B;
return spi_write(flash->spi, flash->command, 1);
+ case CFI_MFR_ST:
+ flash->command[0] = enable ? OPCODE_EN4B : OPCODE_EX4B;
+ write_enable(flash);
+ status = spi_write(flash->spi, flash->command, 1);
+ write_disable(flash);
+ return status;
default:
/* Spansion style */
flash->command[0] = OPCODE_BRWR;
@@ -185,15 +220,21 @@ static inline int set_4byte(struct m25p *flash, u32 jedec_id, int enable)
static int wait_till_ready(struct m25p *flash)
{
unsigned long deadline;
- int sr;
+ int sr, fsr;
deadline = jiffies + MAX_READY_WAIT_JIFFIES;
do {
if ((sr = read_sr(flash)) < 0)
break;
- else if (!(sr & SR_WIP))
+ else if (!(sr & SR_WIP)) {
+ if (flash->check_fsr) {
+ fsr = read_fsr(flash);
+ if (!(fsr & FSR_RDY))
+ return 1;
+ }
return 0;
+ }
cond_resched();
@@ -625,6 +666,7 @@ struct flash_info {
u16 flags;
#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */
#define M25P_NO_ERASE 0x02 /* No erase command needed */
+#define E_FSR 0x08 /* Flag SR exists for flash */
};
#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
@@ -731,6 +773,11 @@ static const struct spi_device_id m25p_ids[] = {
{ "m25p64", INFO(0x202017, 0, 64 * 1024, 128, 0) },
{ "m25p128", INFO(0x202018, 0, 256 * 1024, 64, 0) },
+ { "n25q64", INFO(0x20ba17, 0, 64 * 1024, 128, 0) },
+ { "n25q128", INFO(0x20ba18, 0, 64 * 1024, 256, E_FSR) },
+ { "n25q256", INFO(0x20ba19, 0, 64 * 1024, 512, E_FSR | SECT_4K) },
+ { "n25q512", INFO(0x20ba20, 0, 64 * 1024, 1024, E_FSR | SECT_4K) },
+
{ "m25p05-nonjedec", INFO(0, 0, 32 * 1024, 2, 0) },
{ "m25p10-nonjedec", INFO(0, 0, 32 * 1024, 4, 0) },
{ "m25p20-nonjedec", INFO(0, 0, 64 * 1024, 4, 0) },
@@ -932,6 +979,9 @@ static int __devinit m25p_probe(struct spi_device *spi)
if (info->flags & M25P_NO_ERASE)
flash->mtd.flags |= MTD_NO_ERASE;
+ if (info->flags & E_FSR)
+ flash->check_fsr = 1;
+
ppdata.of_node = spi->dev.of_node;
flash->mtd.dev.parent = &spi->dev;
flash->page_size = info->page_size;

View File

@@ -1,270 +0,0 @@
Support port width and no data swap for OF ata devices
diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c
index 4cadfa2..50a570c 100644
--- a/drivers/ata/libata-sff.c
+++ b/drivers/ata/libata-sff.c
@@ -561,12 +561,27 @@ unsigned int ata_sff_data_xfer(struct ata_device *dev, unsigned char *buf,
struct ata_port *ap = dev->link->ap;
void __iomem *data_addr = ap->ioaddr.data_addr;
unsigned int words = buflen >> 1;
+ unsigned int word;
/* Transfer multiple of 2 bytes */
- if (rw == READ)
+ if (rw == READ) {
ioread16_rep(data_addr, buf, words);
- else
+ } else {
+ for (word = 0; word < words; word++) {
+ unsigned char tmp;
+ tmp = buf[word * 2];
+ buf[word * 2] = buf[word * 2 + 1];
+ buf[word * 2 + 1] = tmp;
+ }
iowrite16_rep(data_addr, buf, words);
+ }
+
+ for (word = 0; word < words; word++) {
+ unsigned char tmp;
+ tmp = buf[word * 2];
+ buf[word * 2] = buf[word * 2 + 1];
+ buf[word * 2 + 1] = tmp;
+ }
/* Transfer trailing byte, if any. */
if (unlikely(buflen & 0x01)) {
@@ -581,9 +596,9 @@ unsigned int ata_sff_data_xfer(struct ata_device *dev, unsigned char *buf,
*/
if (rw == READ) {
ioread16_rep(data_addr, pad, 1);
- *buf = pad[0];
+ *buf = pad[1];
} else {
- pad[0] = *buf;
+ pad[1] = *buf;
iowrite16_rep(data_addr, pad, 1);
}
words++;
diff --git a/drivers/ata/pata_of_platform.c b/drivers/ata/pata_of_platform.c
index 2a472c5..7128350 100644
--- a/drivers/ata/pata_of_platform.c
+++ b/drivers/ata/pata_of_platform.c
@@ -23,9 +23,11 @@ static int __devinit pata_of_platform_probe(struct platform_device *ofdev)
struct resource io_res;
struct resource ctl_res;
struct resource irq_res;
- unsigned int reg_shift = 0;
+ unsigned int port_width = 1;
+ bool port_bswap = 0;
int pio_mode = 0;
int pio_mask;
+ const u32 *reg_shift_prop, *port_width_prop;
const u32 *prop;
ret = of_address_to_resource(dn, 0, &io_res);
@@ -57,26 +59,31 @@ static int __devinit pata_of_platform_probe(struct platform_device *ofdev)
else
irq_res.flags = 0;
- prop = of_get_property(dn, "reg-shift", NULL);
- if (prop)
- reg_shift = be32_to_cpup(prop);
+ reg_shift_prop = of_get_property(dn, "reg-shift", NULL);
+ port_width_prop = of_get_property(dn, "port-width", NULL);
+ if (reg_shift_prop && port_width_prop) {
+ dev_err(&ofdev->dev, "invalid configuration, reg-shift and port-width "
+ "are mutually exclusive\n");
+ return -EINVAL;
+ }
+
+ if (reg_shift_prop) {
+ unsigned int reg_shift = be32_to_cpup(reg_shift_prop);
+ port_width = (reg_shift ? (reg_shift << 1) : 1);
+ } else if (port_width_prop) {
+ port_width = be32_to_cpup(port_width_prop);
+ }
- prop = of_get_property(dn, "pio-mode", NULL);
+ prop = of_get_property(dn, "port-bswap", NULL);
if (prop) {
- pio_mode = be32_to_cpup(prop);
- if (pio_mode > 6) {
- dev_err(&ofdev->dev, "invalid pio-mode\n");
- return -EINVAL;
- }
- } else {
- dev_info(&ofdev->dev, "pio-mode unspecified, assuming PIO0\n");
+ port_bswap = (be32_to_cpup(prop) != 0);
}
pio_mask = 1 << pio_mode;
pio_mask |= (1 << pio_mode) - 1;
return __pata_platform_probe(&ofdev->dev, &io_res, &ctl_res, &irq_res,
- reg_shift, pio_mask);
+ port_width, port_bswap, pio_mask);
}
static int __devexit pata_of_platform_remove(struct platform_device *ofdev)
diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c
index 2067308..e493501 100644
--- a/drivers/ata/pata_platform.c
+++ b/drivers/ata/pata_platform.c
@@ -55,20 +55,83 @@ static struct ata_port_operations pata_platform_port_ops = {
.set_mode = pata_platform_set_mode,
};
+static void bswap_buf16(unsigned char *buf, unsigned int words)
+{
+ u16 *wbuf = (u16 *)buf;
+ unsigned int word;
+
+ for (word = 0; word < words; word++) {
+ *wbuf = (*wbuf << 8) | ((*wbuf >> 8) & 0xff);
+ wbuf++;
+ }
+}
+
+static unsigned int pata_platform_data_xfer_bswap(struct ata_device *dev, unsigned char *buf,
+ unsigned int buflen, int rw)
+{
+ struct ata_port *ap = dev->link->ap;
+ void __iomem *data_addr = ap->ioaddr.data_addr;
+ unsigned int words = buflen >> 1;
+
+ if (ap->pflags & ATA_PFLAG_PIO32) {
+ dev_err(&dev->tdev, "32-bit PIO with port-bswap not impelemented\n");
+ return 0;
+ }
+
+ /* Transfer multiple of 2 bytes */
+ if (rw == READ) {
+ ioread16_rep(data_addr, buf, words);
+ } else {
+ bswap_buf16(buf, words);
+ iowrite16_rep(data_addr, buf, words);
+ }
+
+ bswap_buf16(buf, words);
+
+ /* Transfer trailing byte, if any. */
+ if (unlikely(buflen & 0x01)) {
+ unsigned char pad[2] = { };
+
+ /* Point buf to the tail of buffer */
+ buf += buflen - 1;
+
+ if (rw == READ) {
+ ioread16_rep(data_addr, pad, 1);
+ *buf = pad[1];
+ } else {
+ pad[1] = *buf;
+ iowrite16_rep(data_addr, pad, 1);
+ }
+ words++;
+ }
+
+ return words << 1;
+}
+
static void pata_platform_setup_port(struct ata_ioports *ioaddr,
- unsigned int shift)
+ unsigned int port_width,
+ bool port_bswap)
{
+ unsigned int port_shift = port_width >> 1;
+ unsigned int data_offset = 0;
+ unsigned int ctl_offset = 0;
+
+ if (port_bswap) {
+ data_offset = (port_width - 2);
+ ctl_offset = (port_width - 1);
+ }
+
/* Fixup the port shift for platforms that need it */
- ioaddr->data_addr = ioaddr->cmd_addr + (ATA_REG_DATA << shift);
- ioaddr->error_addr = ioaddr->cmd_addr + (ATA_REG_ERR << shift);
- ioaddr->feature_addr = ioaddr->cmd_addr + (ATA_REG_FEATURE << shift);
- ioaddr->nsect_addr = ioaddr->cmd_addr + (ATA_REG_NSECT << shift);
- ioaddr->lbal_addr = ioaddr->cmd_addr + (ATA_REG_LBAL << shift);
- ioaddr->lbam_addr = ioaddr->cmd_addr + (ATA_REG_LBAM << shift);
- ioaddr->lbah_addr = ioaddr->cmd_addr + (ATA_REG_LBAH << shift);
- ioaddr->device_addr = ioaddr->cmd_addr + (ATA_REG_DEVICE << shift);
- ioaddr->status_addr = ioaddr->cmd_addr + (ATA_REG_STATUS << shift);
- ioaddr->command_addr = ioaddr->cmd_addr + (ATA_REG_CMD << shift);
+ ioaddr->data_addr = ioaddr->cmd_addr + (ATA_REG_DATA << port_shift) + data_offset;
+ ioaddr->error_addr = ioaddr->cmd_addr + (ATA_REG_ERR << port_shift) + ctl_offset;
+ ioaddr->feature_addr = ioaddr->cmd_addr + (ATA_REG_FEATURE << port_shift) + ctl_offset;
+ ioaddr->nsect_addr = ioaddr->cmd_addr + (ATA_REG_NSECT << port_shift) + ctl_offset;
+ ioaddr->lbal_addr = ioaddr->cmd_addr + (ATA_REG_LBAL << port_shift) + ctl_offset;
+ ioaddr->lbam_addr = ioaddr->cmd_addr + (ATA_REG_LBAM << port_shift) + ctl_offset;
+ ioaddr->lbah_addr = ioaddr->cmd_addr + (ATA_REG_LBAH << port_shift) + ctl_offset;
+ ioaddr->device_addr = ioaddr->cmd_addr + (ATA_REG_DEVICE << port_shift) + ctl_offset;
+ ioaddr->status_addr = ioaddr->cmd_addr + (ATA_REG_STATUS << port_shift) + ctl_offset;
+ ioaddr->command_addr = ioaddr->cmd_addr + (ATA_REG_CMD << port_shift) + ctl_offset;
}
/**
@@ -102,7 +165,8 @@ int __devinit __pata_platform_probe(struct device *dev,
struct resource *io_res,
struct resource *ctl_res,
struct resource *irq_res,
- unsigned int ioport_shift,
+ unsigned int port_width,
+ bool port_bswap,
int __pio_mask)
{
struct ata_host *host;
@@ -166,7 +230,12 @@ int __devinit __pata_platform_probe(struct device *dev,
ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr;
- pata_platform_setup_port(&ap->ioaddr, ioport_shift);
+ pata_platform_setup_port(&ap->ioaddr, port_width, port_bswap);
+ if (port_bswap) {
+ pata_platform_port_ops.sff_data_xfer = pata_platform_data_xfer_bswap;
+ }
+ dev_info(dev, "port width: %u byte swap %s\n",
+ port_width, port_bswap ? "enabled" : "disabled");
ata_port_desc(ap, "%s cmd 0x%llx ctl 0x%llx", mmio ? "mmio" : "ioport",
(unsigned long long)io_res->start,
@@ -238,7 +307,8 @@ static int __devinit pata_platform_probe(struct platform_device *pdev)
irq_res->flags = pp_info ? pp_info->irq_flags : 0;
return __pata_platform_probe(&pdev->dev, io_res, ctl_res, irq_res,
- pp_info ? pp_info->ioport_shift : 0,
+ pp_info ? pp_info->port_width : 1,
+ pp_info ? pp_info->port_bswap : false,
pio_mask);
}
diff --git a/include/linux/ata_platform.h b/include/linux/ata_platform.h
index 9a26c83..b38ab9c 100644
--- a/include/linux/ata_platform.h
+++ b/include/linux/ata_platform.h
@@ -7,7 +7,12 @@ struct pata_platform_info {
* constantly spaced and need larger than the 1-byte
* spacing used by ata_std_ports().
*/
- unsigned int ioport_shift;
+ unsigned int port_width;
+ /*
+ * Handle data byte swapping for platforms with byte-swapped
+ * ports.
+ */
+ bool port_bswap;
/*
* Indicate platform specific irq types and initial
* IRQ flags when call request_irq()
@@ -19,7 +24,8 @@ extern int __devinit __pata_platform_probe(struct device *dev,
struct resource *io_res,
struct resource *ctl_res,
struct resource *irq_res,
- unsigned int ioport_shift,
+ unsigned int port_width,
+ bool port_bswap,
int __pio_mask);
extern int __devexit __pata_platform_remove(struct device *dev);

View File

@@ -1,874 +0,0 @@
Adds pca9505 and pca9506 40-bit gpio support to the pca953x driver
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 4e04157..2870b99 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -227,7 +227,7 @@ config GPIO_MAX732X_IRQ
controller. It requires the driver to be built in the kernel.
config GPIO_PCA953X
- tristate "PCA953x, PCA955x, TCA64xx, and MAX7310 I/O ports"
+ tristate "PCA953x, PCA955x, PCA9698, TCA64xx, and MAX7310 I/O ports"
depends on I2C
help
Say yes here to provide access to several register-oriented
@@ -241,6 +241,8 @@ config GPIO_PCA953X
16 bits: pca9535, pca9539, pca9555, tca6416
+ 40 bits: pca9698
+
config GPIO_PCA953X_IRQ
bool "Interrupt controller support for PCA953x"
depends on GPIO_PCA953X=y
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index d3f3e8f..5a215fb 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -16,6 +16,7 @@
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/i2c.h>
#include <linux/i2c/pca953x.h>
#include <linux/slab.h>
@@ -28,6 +29,8 @@
#define PCA953X_INVERT 2
#define PCA953X_DIRECTION 3
+#define REG_ADDR_AI 0x80
+
#define PCA957X_IN 0
#define PCA957X_INVRT 1
#define PCA957X_BKEN 2
@@ -43,6 +46,8 @@
#define PCA957X_TYPE 0x2000
static const struct i2c_device_id pca953x_id[] = {
+ { "pca9505", 40 | PCA953X_TYPE | PCA_INT, },
+ { "pca9506", 40 | PCA953X_TYPE | PCA_INT, },
{ "pca9534", 8 | PCA953X_TYPE | PCA_INT, },
{ "pca9535", 16 | PCA953X_TYPE | PCA_INT, },
{ "pca9536", 4 | PCA953X_TYPE, },
@@ -55,6 +60,7 @@ static const struct i2c_device_id pca953x_id[] = {
{ "pca9557", 8 | PCA953X_TYPE, },
{ "pca9574", 8 | PCA957X_TYPE | PCA_INT, },
{ "pca9575", 16 | PCA957X_TYPE | PCA_INT, },
+ { "pca9698", 40 | PCA953X_TYPE, },
{ "max7310", 8 | PCA953X_TYPE, },
{ "max7312", 16 | PCA953X_TYPE | PCA_INT, },
@@ -63,24 +69,29 @@ static const struct i2c_device_id pca953x_id[] = {
{ "pca6107", 8 | PCA953X_TYPE | PCA_INT, },
{ "tca6408", 8 | PCA953X_TYPE | PCA_INT, },
{ "tca6416", 16 | PCA953X_TYPE | PCA_INT, },
- /* NYET: { "tca6424", 24, }, */
+ { "tca6424", 24 | PCA953X_TYPE | PCA_INT, },
{ }
};
MODULE_DEVICE_TABLE(i2c, pca953x_id);
+#define MAX_BANK 5
+#define BANK_SZ 8
+
+#define NBANK(chip) (chip->gpio_chip.ngpio / BANK_SZ)
+
struct pca953x_chip {
unsigned gpio_start;
- uint16_t reg_output;
- uint16_t reg_direction;
+ u8 reg_output[MAX_BANK];
+ u8 reg_direction[MAX_BANK];
struct mutex i2c_lock;
#ifdef CONFIG_GPIO_PCA953X_IRQ
struct mutex irq_lock;
- uint16_t irq_mask;
- uint16_t irq_stat;
- uint16_t irq_trig_raise;
- uint16_t irq_trig_fall;
- int irq_base;
+ u8 irq_mask[MAX_BANK];
+ u8 irq_stat[MAX_BANK];
+ u8 irq_trig_raise[MAX_BANK];
+ u8 irq_trig_fall[MAX_BANK];
+ struct irq_domain *domain;
#endif
struct i2c_client *client;
@@ -89,26 +100,68 @@ struct pca953x_chip {
int chip_type;
};
-static int pca953x_write_reg(struct pca953x_chip *chip, int reg, uint16_t val)
+static int pca953x_read_single(struct pca953x_chip *chip, int reg, u32 *val,
+ int off)
+{
+ int ret;
+ int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
+ int offset = off / BANK_SZ;
+
+ ret = i2c_smbus_read_byte_data(chip->client,
+ (reg << bank_shift) + offset);
+ *val = ret;
+
+ if (ret < 0) {
+ dev_err(&chip->client->dev, "failed reading register\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int pca953x_write_single(struct pca953x_chip *chip, int reg, u32 val,
+ int off)
+{
+ int ret = 0;
+ int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
+ int offset = off / BANK_SZ;
+
+ ret = i2c_smbus_write_byte_data(chip->client,
+ (reg << bank_shift) + offset, val);
+
+ if (ret < 0) {
+ dev_err(&chip->client->dev, "failed writing register\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int pca953x_write_regs(struct pca953x_chip *chip, int reg, u8 *val)
{
int ret = 0;
if (chip->gpio_chip.ngpio <= 8)
- ret = i2c_smbus_write_byte_data(chip->client, reg, val);
- else {
+ ret = i2c_smbus_write_byte_data(chip->client, reg, *val);
+ else if (chip->gpio_chip.ngpio >= 24) {
+ int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
+ ret = i2c_smbus_write_i2c_block_data(chip->client,
+ (reg << bank_shift) | REG_ADDR_AI,
+ NBANK(chip), val);
+ } else {
switch (chip->chip_type) {
case PCA953X_TYPE:
ret = i2c_smbus_write_word_data(chip->client,
- reg << 1, val);
+ reg << 1, (u16) *val);
break;
case PCA957X_TYPE:
ret = i2c_smbus_write_byte_data(chip->client, reg << 1,
- val & 0xff);
+ val[0]);
if (ret < 0)
break;
ret = i2c_smbus_write_byte_data(chip->client,
(reg << 1) + 1,
- (val & 0xff00) >> 8);
+ val[1]);
break;
}
}
@@ -121,34 +174,42 @@ static int pca953x_write_reg(struct pca953x_chip *chip, int reg, uint16_t val)
return 0;
}
-static int pca953x_read_reg(struct pca953x_chip *chip, int reg, uint16_t *val)
+static int pca953x_read_regs(struct pca953x_chip *chip, int reg, u8 *val)
{
int ret;
- if (chip->gpio_chip.ngpio <= 8)
+ if (chip->gpio_chip.ngpio <= 8) {
ret = i2c_smbus_read_byte_data(chip->client, reg);
- else
- ret = i2c_smbus_read_word_data(chip->client, reg << 1);
+ *val = ret;
+ } else if (chip->gpio_chip.ngpio >= 24) {
+ int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
+ ret = i2c_smbus_read_i2c_block_data(chip->client,
+ (reg << bank_shift) | REG_ADDR_AI,
+ NBANK(chip), val);
+ } else {
+ ret = i2c_smbus_read_word_data(chip->client, reg << 1);
+ val[0] = (u16)ret & 0xFF;
+ val[1] = (u16)ret >> 8;
+ }
if (ret < 0) {
dev_err(&chip->client->dev, "failed reading register\n");
return ret;
}
- *val = (uint16_t)ret;
return 0;
}
static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
{
struct pca953x_chip *chip;
- uint16_t reg_val;
+ u8 reg_val;
int ret, offset = 0;
chip = container_of(gc, struct pca953x_chip, gpio_chip);
mutex_lock(&chip->i2c_lock);
- reg_val = chip->reg_direction | (1u << off);
+ reg_val = chip->reg_direction[off / BANK_SZ] | (1u << (off % BANK_SZ));
switch (chip->chip_type) {
case PCA953X_TYPE:
@@ -158,11 +219,11 @@ static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
offset = PCA957X_CFG;
break;
}
- ret = pca953x_write_reg(chip, offset, reg_val);
+ ret = pca953x_write_single(chip, offset, reg_val, off);
if (ret)
goto exit;
- chip->reg_direction = reg_val;
+ chip->reg_direction[off / BANK_SZ] = reg_val;
ret = 0;
exit:
mutex_unlock(&chip->i2c_lock);
@@ -173,7 +234,7 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,
unsigned off, int val)
{
struct pca953x_chip *chip;
- uint16_t reg_val;
+ u8 reg_val;
int ret, offset = 0;
chip = container_of(gc, struct pca953x_chip, gpio_chip);
@@ -181,9 +242,11 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,
mutex_lock(&chip->i2c_lock);
/* set output level */
if (val)
- reg_val = chip->reg_output | (1u << off);
+ reg_val = chip->reg_output[off / BANK_SZ]
+ | (1u << (off % BANK_SZ));
else
- reg_val = chip->reg_output & ~(1u << off);
+ reg_val = chip->reg_output[off / BANK_SZ]
+ & ~(1u << (off % BANK_SZ));
switch (chip->chip_type) {
case PCA953X_TYPE:
@@ -193,14 +256,14 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,
offset = PCA957X_OUT;
break;
}
- ret = pca953x_write_reg(chip, offset, reg_val);
+ ret = pca953x_write_single(chip, offset, reg_val, off);
if (ret)
goto exit;
- chip->reg_output = reg_val;
+ chip->reg_output[off / BANK_SZ] = reg_val;
/* then direction */
- reg_val = chip->reg_direction & ~(1u << off);
+ reg_val = chip->reg_direction[off / BANK_SZ] & ~(1u << (off % BANK_SZ));
switch (chip->chip_type) {
case PCA953X_TYPE:
offset = PCA953X_DIRECTION;
@@ -209,11 +272,11 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,
offset = PCA957X_CFG;
break;
}
- ret = pca953x_write_reg(chip, offset, reg_val);
+ ret = pca953x_write_single(chip, offset, reg_val, off);
if (ret)
goto exit;
- chip->reg_direction = reg_val;
+ chip->reg_direction[off / BANK_SZ] = reg_val;
ret = 0;
exit:
mutex_unlock(&chip->i2c_lock);
@@ -223,7 +286,7 @@ exit:
static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
{
struct pca953x_chip *chip;
- uint16_t reg_val;
+ u32 reg_val;
int ret, offset = 0;
chip = container_of(gc, struct pca953x_chip, gpio_chip);
@@ -237,7 +300,7 @@ static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
offset = PCA957X_IN;
break;
}
- ret = pca953x_read_reg(chip, offset, &reg_val);
+ ret = pca953x_read_single(chip, offset, &reg_val, off);
mutex_unlock(&chip->i2c_lock);
if (ret < 0) {
/* NOTE: diagnostic already emitted; that's all we should
@@ -247,22 +310,24 @@ static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
return 0;
}
- return (reg_val & (1u << off)) ? 1 : 0;
+ return (reg_val & (1u << (off % BANK_SZ))) ? 1 : 0;
}
static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
{
struct pca953x_chip *chip;
- uint16_t reg_val;
+ u8 reg_val;
int ret, offset = 0;
chip = container_of(gc, struct pca953x_chip, gpio_chip);
mutex_lock(&chip->i2c_lock);
if (val)
- reg_val = chip->reg_output | (1u << off);
+ reg_val = chip->reg_output[off / BANK_SZ]
+ | (1u << (off % BANK_SZ));
else
- reg_val = chip->reg_output & ~(1u << off);
+ reg_val = chip->reg_output[off / BANK_SZ]
+ & ~(1u << (off % BANK_SZ));
switch (chip->chip_type) {
case PCA953X_TYPE:
@@ -272,11 +337,11 @@ static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
offset = PCA957X_OUT;
break;
}
- ret = pca953x_write_reg(chip, offset, reg_val);
+ ret = pca953x_write_single(chip, offset, reg_val, off);
if (ret)
goto exit;
- chip->reg_output = reg_val;
+ chip->reg_output[off / BANK_SZ] = reg_val;
exit:
mutex_unlock(&chip->i2c_lock);
}
@@ -307,21 +372,21 @@ static int pca953x_gpio_to_irq(struct gpio_chip *gc, unsigned off)
struct pca953x_chip *chip;
chip = container_of(gc, struct pca953x_chip, gpio_chip);
- return chip->irq_base + off;
+ return irq_create_mapping(chip->domain, off);
}
static void pca953x_irq_mask(struct irq_data *d)
{
struct pca953x_chip *chip = irq_data_get_irq_chip_data(d);
- chip->irq_mask &= ~(1 << (d->irq - chip->irq_base));
+ chip->irq_mask[d->hwirq / BANK_SZ] &= ~(1 << (d->hwirq % BANK_SZ));
}
static void pca953x_irq_unmask(struct irq_data *d)
{
struct pca953x_chip *chip = irq_data_get_irq_chip_data(d);
- chip->irq_mask |= 1 << (d->irq - chip->irq_base);
+ chip->irq_mask[d->hwirq / BANK_SZ] |= 1 << (d->hwirq % BANK_SZ);
}
static void pca953x_irq_bus_lock(struct irq_data *d)
@@ -334,17 +399,20 @@ static void pca953x_irq_bus_lock(struct irq_data *d)
static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
{
struct pca953x_chip *chip = irq_data_get_irq_chip_data(d);
- uint16_t new_irqs;
- uint16_t level;
+ u8 new_irqs;
+ int level, i;
/* Look for any newly setup interrupt */
- new_irqs = chip->irq_trig_fall | chip->irq_trig_raise;
- new_irqs &= ~chip->reg_direction;
-
- while (new_irqs) {
- level = __ffs(new_irqs);
- pca953x_gpio_direction_input(&chip->gpio_chip, level);
- new_irqs &= ~(1 << level);
+ for (i = 0; i < NBANK(chip); i++) {
+ new_irqs = chip->irq_trig_fall[i] | chip->irq_trig_raise[i];
+ new_irqs &= ~chip->reg_direction[i];
+
+ while (new_irqs) {
+ level = __ffs(new_irqs);
+ pca953x_gpio_direction_input(&chip->gpio_chip,
+ level + (BANK_SZ * i));
+ new_irqs &= ~(1 << level);
+ }
}
mutex_unlock(&chip->irq_lock);
@@ -353,8 +421,8 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
static int pca953x_irq_set_type(struct irq_data *d, unsigned int type)
{
struct pca953x_chip *chip = irq_data_get_irq_chip_data(d);
- uint16_t level = d->irq - chip->irq_base;
- uint16_t mask = 1 << level;
+ int bank_nb = d->hwirq / BANK_SZ;
+ u8 mask = 1 << (d->hwirq % BANK_SZ);
if (!(type & IRQ_TYPE_EDGE_BOTH)) {
dev_err(&chip->client->dev, "irq %d: unsupported type %d\n",
@@ -363,14 +431,14 @@ static int pca953x_irq_set_type(struct irq_data *d, unsigned int type)
}
if (type & IRQ_TYPE_EDGE_FALLING)
- chip->irq_trig_fall |= mask;
+ chip->irq_trig_fall[bank_nb] |= mask;
else
- chip->irq_trig_fall &= ~mask;
+ chip->irq_trig_fall[bank_nb] &= ~mask;
if (type & IRQ_TYPE_EDGE_RISING)
- chip->irq_trig_raise |= mask;
+ chip->irq_trig_raise[bank_nb] |= mask;
else
- chip->irq_trig_raise &= ~mask;
+ chip->irq_trig_raise[bank_nb] &= ~mask;
return 0;
}
@@ -384,13 +452,13 @@ static struct irq_chip pca953x_irq_chip = {
.irq_set_type = pca953x_irq_set_type,
};
-static uint16_t pca953x_irq_pending(struct pca953x_chip *chip)
+static u8 pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending)
{
- uint16_t cur_stat;
- uint16_t old_stat;
- uint16_t pending;
- uint16_t trigger;
- int ret, offset = 0;
+ u8 cur_stat[MAX_BANK];
+ u8 old_stat[MAX_BANK];
+ u8 pendings = 0;
+ u8 trigger[MAX_BANK], triggers = 0;
+ int ret, i, offset = 0;
switch (chip->chip_type) {
case PCA953X_TYPE:
@@ -400,59 +468,88 @@ static uint16_t pca953x_irq_pending(struct pca953x_chip *chip)
offset = PCA957X_IN;
break;
}
- ret = pca953x_read_reg(chip, offset, &cur_stat);
+ ret = pca953x_read_regs(chip, offset, cur_stat);
if (ret)
return 0;
/* Remove output pins from the equation */
- cur_stat &= chip->reg_direction;
+ for (i = 0; i < NBANK(chip); i++)
+ cur_stat[i] &= chip->reg_direction[i];
+
+ memcpy(old_stat, chip->irq_stat, NBANK(chip));
- old_stat = chip->irq_stat;
- trigger = (cur_stat ^ old_stat) & chip->irq_mask;
+ for (i = 0; i < NBANK(chip); i++) {
+ trigger[i] = (cur_stat[i] ^ old_stat[i]) & chip->irq_mask[i];
+ triggers += trigger[i];
+ }
- if (!trigger)
+ if (!triggers)
return 0;
- chip->irq_stat = cur_stat;
+ memcpy(chip->irq_stat, cur_stat, NBANK(chip));
- pending = (old_stat & chip->irq_trig_fall) |
- (cur_stat & chip->irq_trig_raise);
- pending &= trigger;
+ for (i = 0; i < NBANK(chip); i++) {
+ pending[i] = (old_stat[i] & chip->irq_trig_fall[i]) |
+ (cur_stat[i] & chip->irq_trig_raise[i]);
+ pending[i] &= trigger[i];
+ pendings += pending[i];
+ }
- return pending;
+ return pendings;
}
static irqreturn_t pca953x_irq_handler(int irq, void *devid)
{
struct pca953x_chip *chip = devid;
- uint16_t pending;
- uint16_t level;
-
- pending = pca953x_irq_pending(chip);
+ u8 pending[MAX_BANK];
+ u8 level;
+ int i;
- if (!pending)
+ if (!pca953x_irq_pending(chip, pending))
return IRQ_HANDLED;
- do {
- level = __ffs(pending);
- handle_nested_irq(level + chip->irq_base);
-
- pending &= ~(1 << level);
- } while (pending);
+ for (i = 0; i < NBANK(chip); i++) {
+ while (pending[i]) {
+ level = __ffs(pending[i]);
+ handle_nested_irq(irq_find_mapping(chip->domain,
+ level + (BANK_SZ * i)));
+ pending[i] &= ~(1 << level);
+ }
+ }
return IRQ_HANDLED;
}
+static int pca953x_gpio_irq_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ irq_clear_status_flags(irq, IRQ_NOREQUEST);
+ irq_set_chip_data(irq, d->host_data);
+ irq_set_chip(irq, &pca953x_irq_chip);
+ irq_set_nested_thread(irq, true);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ irq_set_noprobe(irq);
+#endif
+
+ return 0;
+}
+
+static const struct irq_domain_ops pca953x_irq_simple_ops = {
+ .map = pca953x_gpio_irq_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
static int pca953x_irq_setup(struct pca953x_chip *chip,
const struct i2c_device_id *id,
int irq_base)
{
struct i2c_client *client = chip->client;
- int ret, offset = 0;
+ int ret, i, offset = 0;
if (irq_base != -1
&& (id->driver_data & PCA_INT)) {
- int lvl;
switch (chip->chip_type) {
case PCA953X_TYPE:
@@ -462,37 +559,29 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
offset = PCA957X_IN;
break;
}
- ret = pca953x_read_reg(chip, offset, &chip->irq_stat);
+ ret = pca953x_read_regs(chip, offset, chip->irq_stat);
if (ret)
- goto out_failed;
+ return ret;
/*
* There is no way to know which GPIO line generated the
* interrupt. We have to rely on the previous read for
* this purpose.
*/
- chip->irq_stat &= chip->reg_direction;
+ for (i = 0; i < NBANK(chip); i++)
+ chip->irq_stat[i] &= chip->reg_direction[i];
mutex_init(&chip->irq_lock);
- chip->irq_base = irq_alloc_descs(-1, irq_base, chip->gpio_chip.ngpio, -1);
- if (chip->irq_base < 0)
- goto out_failed;
-
- for (lvl = 0; lvl < chip->gpio_chip.ngpio; lvl++) {
- int irq = lvl + chip->irq_base;
+ chip->domain = irq_domain_add_simple(client->dev.of_node,
+ chip->gpio_chip.ngpio,
+ irq_base,
+ &pca953x_irq_simple_ops,
+ chip);
+ if (!chip->domain)
+ return -ENODEV;
- irq_clear_status_flags(irq, IRQ_NOREQUEST);
- irq_set_chip_data(irq, chip);
- irq_set_chip(irq, &pca953x_irq_chip);
- irq_set_nested_thread(irq, true);
-#ifdef CONFIG_ARM
- set_irq_flags(irq, IRQF_VALID);
-#else
- irq_set_noprobe(irq);
-#endif
- }
-
- ret = request_threaded_irq(client->irq,
+ ret = devm_request_threaded_irq(&client->dev,
+ client->irq,
NULL,
pca953x_irq_handler,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
@@ -500,26 +589,15 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
if (ret) {
dev_err(&client->dev, "failed to request irq %d\n",
client->irq);
- goto out_failed;
+ return ret;
}
chip->gpio_chip.to_irq = pca953x_gpio_to_irq;
}
return 0;
-
-out_failed:
- chip->irq_base = -1;
- return ret;
}
-static void pca953x_irq_teardown(struct pca953x_chip *chip)
-{
- if (chip->irq_base != -1) {
- irq_free_descs(chip->irq_base, chip->gpio_chip.ngpio);
- free_irq(chip->client->irq, chip);
- }
-}
#else /* CONFIG_GPIO_PCA953X_IRQ */
static int pca953x_irq_setup(struct pca953x_chip *chip,
const struct i2c_device_id *id,
@@ -532,10 +610,6 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
return 0;
}
-
-static void pca953x_irq_teardown(struct pca953x_chip *chip)
-{
-}
#endif
/*
@@ -547,7 +621,7 @@ static void pca953x_irq_teardown(struct pca953x_chip *chip)
* WARNING: This is DEPRECATED and will be removed eventually!
*/
static void
-pca953x_get_alt_pdata(struct i2c_client *client, int *gpio_base, int *invert)
+pca953x_get_alt_pdata(struct i2c_client *client, int *gpio_base, u32 *invert)
{
struct device_node *node;
const __be32 *val;
@@ -575,75 +649,80 @@ pca953x_get_alt_pdata(struct i2c_client *client, int *gpio_base, int *invert)
}
#else
static void
-pca953x_get_alt_pdata(struct i2c_client *client, int *gpio_base, int *invert)
+pca953x_get_alt_pdata(struct i2c_client *client, int *gpio_base, u32 *invert)
{
*gpio_base = -1;
}
#endif
-static int __devinit device_pca953x_init(struct pca953x_chip *chip, int invert)
+static int device_pca953x_init(struct pca953x_chip *chip, u32 invert)
{
int ret;
+ u8 val[MAX_BANK];
- ret = pca953x_read_reg(chip, PCA953X_OUTPUT, &chip->reg_output);
+ ret = pca953x_read_regs(chip, PCA953X_OUTPUT, chip->reg_output);
if (ret)
goto out;
- ret = pca953x_read_reg(chip, PCA953X_DIRECTION,
- &chip->reg_direction);
+ ret = pca953x_read_regs(chip, PCA953X_DIRECTION,
+ chip->reg_direction);
if (ret)
goto out;
/* set platform specific polarity inversion */
- ret = pca953x_write_reg(chip, PCA953X_INVERT, invert);
+ if (invert)
+ memset(val, 0xFF, NBANK(chip));
+ else
+ memset(val, 0, NBANK(chip));
+
+ ret = pca953x_write_regs(chip, PCA953X_INVERT, val);
out:
return ret;
}
-static int __devinit device_pca957x_init(struct pca953x_chip *chip, int invert)
+static int device_pca957x_init(struct pca953x_chip *chip, u32 invert)
{
int ret;
- uint16_t val = 0;
-
- /* Let every port in proper state, that could save power */
- pca953x_write_reg(chip, PCA957X_PUPD, 0x0);
- pca953x_write_reg(chip, PCA957X_CFG, 0xffff);
- pca953x_write_reg(chip, PCA957X_OUT, 0x0);
+ u8 val[MAX_BANK];
- ret = pca953x_read_reg(chip, PCA957X_IN, &val);
- if (ret)
- goto out;
- ret = pca953x_read_reg(chip, PCA957X_OUT, &chip->reg_output);
+ ret = pca953x_read_regs(chip, PCA957X_OUT, chip->reg_output);
if (ret)
goto out;
- ret = pca953x_read_reg(chip, PCA957X_CFG, &chip->reg_direction);
+ ret = pca953x_read_regs(chip, PCA957X_CFG, chip->reg_direction);
if (ret)
goto out;
/* set platform specific polarity inversion */
- pca953x_write_reg(chip, PCA957X_INVRT, invert);
+ if (invert)
+ memset(val, 0xFF, NBANK(chip));
+ else
+ memset(val, 0, NBANK(chip));
+ pca953x_write_regs(chip, PCA957X_INVRT, val);
/* To enable register 6, 7 to controll pull up and pull down */
- pca953x_write_reg(chip, PCA957X_BKEN, 0x202);
+ memset(val, 0x02, NBANK(chip));
+ pca953x_write_regs(chip, PCA957X_BKEN, val);
return 0;
out:
return ret;
}
-static int __devinit pca953x_probe(struct i2c_client *client,
+static int pca953x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pca953x_platform_data *pdata;
struct pca953x_chip *chip;
- int irq_base=0, invert=0;
+ int irq_base = 0;
int ret;
+ u32 invert = 0;
- chip = kzalloc(sizeof(struct pca953x_chip), GFP_KERNEL);
+ chip = devm_kzalloc(&client->dev,
+ sizeof(struct pca953x_chip), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
- pdata = client->dev.platform_data;
+ pdata = dev_get_platdata(&client->dev);
if (pdata) {
irq_base = pdata->irq_base;
chip->gpio_start = pdata->gpio_base;
@@ -674,15 +753,15 @@ static int __devinit pca953x_probe(struct i2c_client *client,
else
ret = device_pca957x_init(chip, invert);
if (ret)
- goto out_failed;
+ return ret;
ret = pca953x_irq_setup(chip, id, irq_base);
if (ret)
- goto out_failed;
+ return ret;
ret = gpiochip_add(&chip->gpio_chip);
if (ret)
- goto out_failed_irq;
+ return ret;
if (pdata && pdata->setup) {
ret = pdata->setup(client, chip->gpio_chip.base,
@@ -693,17 +772,11 @@ static int __devinit pca953x_probe(struct i2c_client *client,
i2c_set_clientdata(client, chip);
return 0;
-
-out_failed_irq:
- pca953x_irq_teardown(chip);
-out_failed:
- kfree(chip);
- return ret;
}
static int pca953x_remove(struct i2c_client *client)
{
- struct pca953x_platform_data *pdata = client->dev.platform_data;
+ struct pca953x_platform_data *pdata = dev_get_platdata(&client->dev);
struct pca953x_chip *chip = i2c_get_clientdata(client);
int ret = 0;
@@ -724,14 +797,44 @@ static int pca953x_remove(struct i2c_client *client)
return ret;
}
- pca953x_irq_teardown(chip);
- kfree(chip);
return 0;
}
+static const struct of_device_id pca953x_dt_ids[] = {
+ { .compatible = "nxp,pca9505", },
+ { .compatible = "nxp,pca9506", },
+ { .compatible = "nxp,pca9534", },
+ { .compatible = "nxp,pca9535", },
+ { .compatible = "nxp,pca9536", },
+ { .compatible = "nxp,pca9537", },
+ { .compatible = "nxp,pca9538", },
+ { .compatible = "nxp,pca9539", },
+ { .compatible = "nxp,pca9554", },
+ { .compatible = "nxp,pca9555", },
+ { .compatible = "nxp,pca9556", },
+ { .compatible = "nxp,pca9557", },
+ { .compatible = "nxp,pca9574", },
+ { .compatible = "nxp,pca9575", },
+ { .compatible = "nxp,pca9698", },
+
+ { .compatible = "maxim,max7310", },
+ { .compatible = "maxim,max7312", },
+ { .compatible = "maxim,max7313", },
+ { .compatible = "maxim,max7315", },
+
+ { .compatible = "ti,pca6107", },
+ { .compatible = "ti,tca6408", },
+ { .compatible = "ti,tca6416", },
+ { .compatible = "ti,tca6424", },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, pca953x_dt_ids);
+
static struct i2c_driver pca953x_driver = {
.driver = {
.name = "pca953x",
+ .of_match_table = pca953x_dt_ids,
},
.probe = pca953x_probe,
.remove = pca953x_remove,
diff --git a/include/linux/i2c/pca953x.h b/include/linux/i2c/pca953x.h
index 139ba52..2a69244 100644
--- a/include/linux/i2c/pca953x.h
+++ b/include/linux/i2c/pca953x.h
@@ -11,7 +11,7 @@ struct pca953x_platform_data {
unsigned gpio_base;
/* initial polarity inversion setting */
- uint16_t invert;
+ uint32_t invert;
/* interrupt base */
int irq_base;

View File

@@ -1,30 +0,0 @@
--- a/drivers/i2c/muxes/Kconfig 2015-10-27 16:54:02.741944172 +0000
+++ b/drivers/i2c/muxes/Kconfig 2015-10-27 16:55:02.645945471 +0000
@@ -56,6 +56,14 @@ config I2C_MUX_PCA954x
This driver can also be built as a module. If so, the module
will be called pca954x.
+config I2C_MUX_PCA954X_DESELECT_ON_EXIT
+ bool "Enable deselect-on-exit feature for PCA954X devices."
+ depends on I2C_MUX_PCA954x
+ help
+ If you say yes here you enable the deselect-on-exit feature in
+ the pca954x i2c driver.
+
+
config I2C_MUX_DNI_6448
tristate "Delta Networks 6448 I2C Mux"
depends on EXPERIMENTAL
--- linux-3.2.65-1+deb7u2/drivers/i2c/muxes/pca954x.c 2015-10-27 17:13:03.937968935 +0000
+++ b/drivers/i2c/muxes/pca954x.c 2015-10-27 17:12:41.265968443 +0000
@@ -222,6 +222,10 @@ static int pca954x_probe(struct i2c_clie
deselect_on_exit = 1;
}
+#ifdef CONFIG_I2C_MUX_PCA954X_DESELECT_ON_EXIT
+ deselect_on_exit = 1;
+#endif
+
data->type = id->driver_data;
data->last_chan = 0; /* force the first selection */

View File

@@ -1,66 +0,0 @@
Modify the pca954x driver to check for the "deselect_on_exit" property in the dtb.
After the current transaction is complete the MUX deselects all sub-interfaces.
diff --git a/Documentation/devicetree/bindings/i2c/mux.txt b/Documentation/devicetree/bindings/i2c/mux.txt
index af84cce..fc9b542 100644
--- a/Documentation/devicetree/bindings/i2c/mux.txt
+++ b/Documentation/devicetree/bindings/i2c/mux.txt
@@ -17,7 +17,8 @@ Required properties for child nodes:
Optional properties for child nodes:
- Other properties specific to the multiplexer/switch hardware.
- Child nodes conforming to i2c bus binding
-
+- deselect-on-exit -- if set deselect the mux after each transaction,
+ supported by the pca954x.c driver.
Example :
@@ -32,6 +33,7 @@ Example :
reg = <0x70>;
#address-cells = <1>;
#size-cells = <0>;
+ deselect-on-exit;
i2c@3 {
#address-cells = <1>;
diff --git a/drivers/i2c/muxes/pca954x.c b/drivers/i2c/muxes/pca954x.c
index 5c6ecc7..c9db544 100644
--- a/drivers/i2c/muxes/pca954x.c
+++ b/drivers/i2c/muxes/pca954x.c
@@ -188,6 +188,7 @@ static int pca954x_probe(struct i2c_client *client,
struct pca954x_platform_data *pdata = client->dev.platform_data;
int num, force;
struct pca954x *data;
+ int deselect_on_exit = 0;
int ret = -ENODEV;
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE))
@@ -210,6 +211,17 @@ static int pca954x_probe(struct i2c_client *client,
goto exit_free;
}
+ /*
+ * Check whether we want to deselect the mux after the
+ * transaction. This can be specified in one of two ways:
+ *
+ * 1. using platform data: pdata->modes[num].deselect_on_exit
+ * 2. using the device tree property deselect_on_exit
+ */
+ if (of_find_property(client->dev.of_node, "deselect-on-exit", NULL)) {
+ deselect_on_exit = 1;
+ }
+
data->type = id->driver_data;
data->last_chan = 0; /* force the first selection */
@@ -228,8 +240,8 @@ static int pca954x_probe(struct i2c_client *client,
data->virt_adaps[num] =
i2c_add_mux_adapter(adap, &client->dev, client,
force, num, pca954x_select_chan,
- (pdata && pdata->modes[num].deselect_on_exit)
- ? pca954x_deselect_mux : NULL);
+ (pdata && pdata->modes[num].deselect_on_exit) || deselect_on_exit
+ ? pca954x_deselect_mux : NULL);
if (data->virt_adaps[num] == NULL) {
ret = -ENODEV;

View File

@@ -1,92 +0,0 @@
platform rtc hw clock fix up
In some cases the RTC contains invalid date/time values. At boot time
when the invalid values are detected, attempt to write valid time/date
to the hardware.
diff --git a/drivers/rtc/hctosys.c b/drivers/rtc/hctosys.c
index bc90b09..b7bb5ca 100644
--- a/drivers/rtc/hctosys.c
+++ b/drivers/rtc/hctosys.c
@@ -24,6 +24,57 @@
int rtc_hctosys_ret = -ENODEV;
+// Approximately the number of seconds in a 42 year span
+#define SECONDS_IN_42_YEARS (42 * 365 * 24 * 3600)
+
+static int __init rtc_hc_fixup(struct rtc_device *rtc, struct rtc_time *p_tm)
+{
+ int err = 0;
+
+ /*
+ * rtc_time_to_tm() converts seconds since 1970-01-01 to a
+ * struct rtc_time.
+ *
+ * Some RTC drivers only store (year % 100) and assume 2000 on
+ * read back. For example when storing 1970, only 70 is
+ * stored in the hardware, but on read back it would be 2070.
+ *
+ * To work around this, pick a default date/time with the
+ * year > 2000.
+ *
+ * Adding 42 years worth of seconds puts the default date/time
+ * near 2012.
+ *
+ * As this code is being written in 2013, a default time
+ * around 2012 is safe to assume.
+ *
+ */
+ rtc_time_to_tm(SECONDS_IN_42_YEARS, p_tm);
+ err = rtc_set_time(rtc, p_tm);
+
+ if (err) {
+ dev_err(rtc->dev.parent,
+ "hc_fixup: unable to set hardware clock date/time\n");
+ return err;
+ }
+
+ err = rtc_read_time(rtc, p_tm);
+ if (err) {
+ dev_err(rtc->dev.parent,
+ "hc_fixup: unable to read the hardware clock\n");
+ return err;
+ }
+
+ err = rtc_valid_tm(p_tm);
+ if (err) {
+ dev_err(rtc->dev.parent,
+ "hc_fixup: unable to fix-up invalid date/time\n");
+ return err;
+ }
+
+ return err;
+}
+
static int __init rtc_hctosys(void)
{
int err = -ENODEV;
@@ -43,15 +94,20 @@ static int __init rtc_hctosys(void)
if (err) {
dev_err(rtc->dev.parent,
"hctosys: unable to read the hardware clock\n");
- goto err_read;
-
+ err = rtc_hc_fixup(rtc, &tm);
+ if (err) {
+ goto err_read;
+ }
}
err = rtc_valid_tm(&tm);
if (err) {
dev_err(rtc->dev.parent,
"hctosys: invalid date/time\n");
- goto err_invalid;
+ err = rtc_hc_fixup(rtc, &tm);
+ if (err) {
+ goto err_invalid;
+ }
}
rtc_tm_to_time(&tm, &tv.tv_sec);

View File

@@ -1,236 +0,0 @@
#Copyright 2012 Cumulus Networks, Inc. All rights reserved.
Adjust multi-byte word CFI endianness based on device tree specification.
Currently, CFI byte swapping is controlled by set of configuration parameters
CONFIG_MTD_CFI_NOSWAP, CONFIG_MTD_CFI_LE_BYTE_SWAP, and
CONFIG_MTD_CFI_BE_BYTE_SWAP. This clearly prohibits using the same kernel on
systems whose local address busses are not connected the same way.
This patch adds a new configuration variable CONFIG_MTD_CFI_OF_SWAP which
evaluates the device tree specification for a "byteswap" property that is used
to control byte swapping where desired.
diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig
index b1e3c26..c7ae3f5 100644
--- a/drivers/mtd/chips/Kconfig
+++ b/drivers/mtd/chips/Kconfig
@@ -52,8 +52,8 @@ config MTD_CFI_NOSWAP
'NO', which is the default when CONFIG_MTD_CFI_ADV_OPTIONS isn't
enabled, means that the CPU will not do any swapping; the chips
are expected to be wired to the CPU in 'host-endian' form.
- Specific arrangements are possible with the BIG_ENDIAN_BYTE and
- LITTLE_ENDIAN_BYTE, if the bytes are reversed.
+ Specific arrangements are possible with the BIG_ENDIAN_BYTE,
+ LITTLE_ENDIAN_BYTE, and OF_BYTE_SWAP if the bytes are reversed.
If you have a LART, on which the data (and address) lines were
connected in a fashion which ensured that the nets were as short
@@ -68,6 +68,9 @@ config MTD_CFI_BE_BYTE_SWAP
config MTD_CFI_LE_BYTE_SWAP
bool "LITTLE_ENDIAN_BYTE"
+config MTD_CFI_OF_BYTE_SWAP
+ bool "OF_BYTESWAP_PROPERTY"
+
endchoice
config MTD_CFI_GEOMETRY
diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c
index 179814a..aa272a3 100644
--- a/drivers/mtd/chips/cfi_cmdset_0020.c
+++ b/drivers/mtd/chips/cfi_cmdset_0020.c
@@ -139,8 +139,8 @@ struct mtd_info *cfi_cmdset_0020(struct map_info *map, int primary)
}
/* Do some byteswapping if necessary */
- extp->FeatureSupport = cfi32_to_cpu(extp->FeatureSupport);
- extp->BlkStatusRegMask = cfi32_to_cpu(extp->BlkStatusRegMask);
+ extp->FeatureSupport = cfi32_to_cpu(map, extp->FeatureSupport);
+ extp->BlkStatusRegMask = cfi32_to_cpu(map, extp->BlkStatusRegMask);
#ifdef DEBUG_CFI_FEATURES
/* Tell the user about it in lots of lovely detail */
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c
index 7d65f9d..7f32f30 100644
--- a/drivers/mtd/maps/physmap_of.c
+++ b/drivers/mtd/maps/physmap_of.c
@@ -161,6 +161,9 @@ static int __devinit of_flash_probe(struct platform_device *dev)
struct of_flash *info;
const char *probe_type;
const __be32 *width;
+#ifdef CONFIG_MTD_CFI_OF_BYTE_SWAP
+ struct property * byteswap_prop;
+#endif
int err;
int i;
int count;
@@ -236,6 +239,16 @@ static int __devinit of_flash_probe(struct platform_device *dev)
info->list[i].map.size = res_size;
info->list[i].map.bankwidth = be32_to_cpup(width);
+#ifdef CONFIG_MTD_CFI_OF_BYTE_SWAP
+ byteswap_prop = of_find_property(dp, "byteswap", NULL);
+ if (byteswap_prop == NULL) {
+ info->list[i].map.byteswap = 0;
+ } else {
+ info->list[i].map.byteswap = 1;
+ dev_info(&dev->dev, "byteswapping configured in OF\n");
+ }
+#endif
+
err = -ENOMEM;
info->list[i].map.virt = ioremap(info->list[i].map.phys,
info->list[i].map.size);
diff --git a/include/linux/mtd/cfi.h b/include/linux/mtd/cfi.h
index d249254..d5d2ec6 100644
--- a/include/linux/mtd/cfi.h
+++ b/include/linux/mtd/cfi.h
@@ -354,10 +354,10 @@ static inline map_word cfi_build_cmd(u_long cmd, struct map_info *map, struct cf
onecmd = cmd;
break;
case 2:
- onecmd = cpu_to_cfi16(cmd);
+ onecmd = cpu_to_cfi16(map, cmd);
break;
case 4:
- onecmd = cpu_to_cfi32(cmd);
+ onecmd = cpu_to_cfi32(map, cmd);
break;
}
@@ -437,10 +437,10 @@ static inline unsigned long cfi_merge_status(map_word val, struct map_info *map,
case 1:
break;
case 2:
- res = cfi16_to_cpu(res);
+ res = cfi16_to_cpu(map, res);
break;
case 4:
- res = cfi32_to_cpu(res);
+ res = cfi32_to_cpu(map, res);
break;
default: BUG();
}
@@ -480,12 +480,12 @@ static inline uint8_t cfi_read_query(struct map_info *map, uint32_t addr)
if (map_bankwidth_is_1(map)) {
return val.x[0];
} else if (map_bankwidth_is_2(map)) {
- return cfi16_to_cpu(val.x[0]);
+ return cfi16_to_cpu(map, val.x[0]);
} else {
/* No point in a 64-bit byteswap since that would just be
swapping the responses from different chips, and we are
only interested in one chip (a representative sample) */
- return cfi32_to_cpu(val.x[0]);
+ return cfi32_to_cpu(map, val.x[0]);
}
}
@@ -496,12 +496,12 @@ static inline uint16_t cfi_read_query16(struct map_info *map, uint32_t addr)
if (map_bankwidth_is_1(map)) {
return val.x[0] & 0xff;
} else if (map_bankwidth_is_2(map)) {
- return cfi16_to_cpu(val.x[0]);
+ return cfi16_to_cpu(map, val.x[0]);
} else {
/* No point in a 64-bit byteswap since that would just be
swapping the responses from different chips, and we are
only interested in one chip (a representative sample) */
- return cfi32_to_cpu(val.x[0]);
+ return cfi32_to_cpu(map, val.x[0]);
}
}
diff --git a/include/linux/mtd/cfi_endian.h b/include/linux/mtd/cfi_endian.h
index 51cc3f5..c785284 100644
--- a/include/linux/mtd/cfi_endian.h
+++ b/include/linux/mtd/cfi_endian.h
@@ -37,35 +37,48 @@
#define CFI_BIG_ENDIAN
#endif
+#ifdef CONFIG_MTD_CFI_OF_BYTE_SWAP
+#define CFI_OF_BYTE_SWAP
+#endif
+
#endif
#if defined(CFI_LITTLE_ENDIAN)
-#define cpu_to_cfi8(x) (x)
-#define cfi8_to_cpu(x) (x)
-#define cpu_to_cfi16(x) cpu_to_le16(x)
-#define cpu_to_cfi32(x) cpu_to_le32(x)
-#define cpu_to_cfi64(x) cpu_to_le64(x)
-#define cfi16_to_cpu(x) le16_to_cpu(x)
-#define cfi32_to_cpu(x) le32_to_cpu(x)
-#define cfi64_to_cpu(x) le64_to_cpu(x)
+#define cpu_to_cfi8(m,x) (x)
+#define cfi8_to_cpu(m,x) (x)
+#define cpu_to_cfi16(m,x) cpu_to_le16(x)
+#define cpu_to_cfi32(m,x) cpu_to_le32(x)
+#define cpu_to_cfi64(m,x) cpu_to_le64(x)
+#define cfi16_to_cpu(m,x) le16_to_cpu(x)
+#define cfi32_to_cpu(m,x) le32_to_cpu(x)
+#define cfi64_to_cpu(m,x) le64_to_cpu(x)
#elif defined (CFI_BIG_ENDIAN)
-#define cpu_to_cfi8(x) (x)
-#define cfi8_to_cpu(x) (x)
-#define cpu_to_cfi16(x) cpu_to_be16(x)
-#define cpu_to_cfi32(x) cpu_to_be32(x)
-#define cpu_to_cfi64(x) cpu_to_be64(x)
-#define cfi16_to_cpu(x) be16_to_cpu(x)
-#define cfi32_to_cpu(x) be32_to_cpu(x)
-#define cfi64_to_cpu(x) be64_to_cpu(x)
+#define cpu_to_cfi8(m,x) (x)
+#define cfi8_to_cpu(m,x) (x)
+#define cpu_to_cfi16(m,x) cpu_to_be16(x)
+#define cpu_to_cfi32(m,x) cpu_to_be32(x)
+#define cpu_to_cfi64(m,x) cpu_to_be64(x)
+#define cfi16_to_cpu(m,x) be16_to_cpu(x)
+#define cfi32_to_cpu(m,x) be32_to_cpu(x)
+#define cfi64_to_cpu(m,x) be64_to_cpu(x)
#elif defined (CFI_HOST_ENDIAN)
-#define cpu_to_cfi8(x) (x)
-#define cfi8_to_cpu(x) (x)
-#define cpu_to_cfi16(x) (x)
-#define cpu_to_cfi32(x) (x)
-#define cpu_to_cfi64(x) (x)
-#define cfi16_to_cpu(x) (x)
-#define cfi32_to_cpu(x) (x)
-#define cfi64_to_cpu(x) (x)
+#define cpu_to_cfi8(m,x) (x)
+#define cfi8_to_cpu(m,x) (x)
+#define cpu_to_cfi16(m,x) (x)
+#define cpu_to_cfi32(m,x) (x)
+#define cpu_to_cfi64(m,x) (x)
+#define cfi16_to_cpu(m,x) (x)
+#define cfi32_to_cpu(m,x) (x)
+#define cfi64_to_cpu(m,x) (x)
+#elif defined (CFI_OF_BYTE_SWAP)
+#define cpu_to_cfi8(m,x) (x)
+#define cfi8_to_cpu(m,x) (x)
+#define cpu_to_cfi16(m,x) ((m)->byteswap ? swab16(x) : (x))
+#define cpu_to_cfi32(m,x) ((m)->byteswap ? swab32(x) : (x))
+#define cpu_to_cfi64(m,x) ((m)->byteswap ? swab64(x) : (x))
+#define cfi16_to_cpu(m,x) ((m)->byteswap ? swab16(x) : (x))
+#define cfi32_to_cpu(m,x) ((m)->byteswap ? swab32(x) : (x))
+#define cfi64_to_cpu(m,x) ((m)->byteswap ? swab64(x) : (x))
#else
#error No CFI endianness defined
#endif
diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h
index 3887901..b8ca7de 100644
--- a/include/linux/mtd/map.h
+++ b/include/linux/mtd/map.h
@@ -219,6 +219,10 @@ struct map_info {
in bytes, before you are talking to the first chip again.
*/
+#ifdef CONFIG_MTD_CFI_OF_BYTE_SWAP
+ int byteswap; /* get byte swap configuration from device tree */
+#endif
+
#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
map_word (*read)(struct map_info *, unsigned long);
void (*copy_from)(struct map_info *, void *, unsigned long, ssize_t);

View File

@@ -1,157 +0,0 @@
diff -rupN a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
--- a/drivers/platform/x86/Kconfig 2015-02-19 18:57:03.000000000 -0800
+++ b/drivers/platform/x86/Kconfig 2015-06-13 22:57:05.465077296 -0700
@@ -786,4 +786,9 @@ config SAMSUNG_Q10
This driver provides support for backlight control on Samsung Q10
and related laptops, including Dell Latitude X200.
+config X86_64_DELL_S6000_S1220_R0
+ tristate "Platform Driver for the DELL S6000"
+ ---help---
+ Support the Dell S6000.
+
endif # X86_PLATFORM_DEVICES
diff -rupN a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
--- a/drivers/platform/x86/Makefile 2015-02-19 18:57:03.000000000 -0800
+++ b/drivers/platform/x86/Makefile 2015-06-13 22:56:47.129074056 -0700
@@ -46,3 +46,4 @@ obj-$(CONFIG_MXM_WMI) += mxm-wmi.o
obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o
obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o
obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o
+obj-$(CONFIG_X86_64_DELL_S6000_S1220_R0) += x86_64_dell_s6000_s1220_r0.o
diff -rupN a/drivers/platform/x86/x86_64_dell_s6000_s1220_r0.c b/drivers/platform/x86/x86_64_dell_s6000_s1220_r0.c
--- a/drivers/platform/x86/x86_64_dell_s6000_s1220_r0.c 1969-12-31 16:00:00.000000000 -0800
+++ b/drivers/platform/x86/x86_64_dell_s6000_s1220_r0.c 2015-06-13 22:56:25.865078612 -0700
@@ -0,0 +1,132 @@
+/**
+ * Dell S6000 Platform Support.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+
+#include <linux/gpio-i2cmux.h>
+#include <linux/platform_device.h>
+#include <linux/dmi.h>
+
+
+/*************************************************************
+ *
+ * I2C Bus 0 on the S6000 is muxed via gpio1 and gpio2.
+ *
+ ************************************************************/
+static const unsigned s6000_gpiomux_gpios[] = {
+ 1, 2
+};
+
+static const unsigned s6000_gpiomux_values[] = {
+ 0, 1, 2, 3
+};
+
+static struct gpio_i2cmux_platform_data s6000_i2cmux_data = {
+ /*
+ * i2c Bus 0
+ */
+ .parent = 0,
+
+ /*
+ * Start the bus numbers at 10. The first digit
+ * will represent the different bus numbers based
+ * the gpio selector (00, 01, 10, 11):
+ *
+ * i2c-10 --> i2c-0, gpios = 00
+ * i2c-11 --> i2c-0, gpios = 01
+ * i2c-12 --> i2c-0, gpios = 10
+ * i2c-13 --> i2c-0, gpios = 11
+ */
+ .base_nr = 10,
+
+ .values = s6000_gpiomux_values,
+ .n_values = ARRAY_SIZE(s6000_gpiomux_values),
+ .gpios = s6000_gpiomux_gpios,
+ .n_gpios = ARRAY_SIZE(s6000_gpiomux_gpios),
+ .idle = 0,
+};
+
+static struct platform_device s6000_i2cmux = {
+ .name = "gpio-i2cmux",
+ .id = 12,
+ .dev = {
+ .platform_data = &s6000_i2cmux_data,
+ },
+};
+
+/*************************************************************
+ *
+ * Sensors on i2c-11 (See mux data above).
+ *
+ ************************************************************/
+static struct i2c_board_info s6000_i2c_11_board_info[] = {
+ { I2C_BOARD_INFO("lm75", 0x4c) },
+ { I2C_BOARD_INFO("lm75", 0x4d) },
+ { I2C_BOARD_INFO("lm75", 0x4e) },
+ { I2C_BOARD_INFO("ltc4215", 0x42) },
+ { I2C_BOARD_INFO("ltc4215", 0x40) },
+ { I2C_BOARD_INFO("max6620", 0x29) },
+ { I2C_BOARD_INFO("max6620", 0x2A) },
+ { I2C_BOARD_INFO("24c02", 0x51) },
+ { I2C_BOARD_INFO("24c02", 0x52) },
+ { I2C_BOARD_INFO("24c02", 0x53) },
+};
+
+static int __init x86_64_dell_s6000_s1220_r0_init(void)
+{
+ int i;
+ int rv = 0;
+ char const *vendor, *product;
+ struct i2c_adapter * i2ca;
+
+ vendor = dmi_get_system_info(DMI_SYS_VENDOR);
+ product = dmi_get_system_info(DMI_PRODUCT_NAME);
+
+ if(strcmp(vendor, "Dell Inc") ||
+ (strcmp(product, "S6000 (SI)") && strcmp(product, "S6000-ON") &&
+ strcmp(product, "S6000-ON (SI)"))) {
+ /* Not the S6000 */
+ return -ENODEV;
+ }
+
+ /**
+ * Register the GPIO mux for bus 0.
+ */
+ rv = platform_device_register(&s6000_i2cmux);
+ if(rv < 0) {
+ pr_err("%s: platform_device_register() failed: %d", __FUNCTION__, rv);
+ return rv;
+ }
+
+ /**
+ * Register I2C devices on new buses
+ */
+ i2ca = i2c_get_adapter(11);
+ for(i = 0; i < ARRAY_SIZE(s6000_i2c_11_board_info); i++) {
+ if(i2c_new_device(i2ca, s6000_i2c_11_board_info+i) == NULL) {
+ pr_err("%s: i2c_new_device for bus 11:0x%x failed.",
+ __FUNCTION__, s6000_i2c_11_board_info[i].addr);
+ }
+ }
+
+ return 0;
+
+}
+
+static void __exit x86_64_dell_s6000_s1220_r0_cleanup(void)
+{
+ platform_device_unregister(&s6000_i2cmux);
+}
+
+module_init(x86_64_dell_s6000_s1220_r0_init);
+module_exit(x86_64_dell_s6000_s1220_r0_cleanup);
+
+MODULE_AUTHOR("Big Switch Networks (support@bigswitch.com)");
+MODULE_VERSION("1.0");
+MODULE_DESCRIPTION("Dell S6000");
+MODULE_LICENSE("GPL");
+

View File

@@ -1,77 +0,0 @@
Support Broadcom 54616 phy in Intel IGB Ethernet Driver
diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c
index ffdd020..a775254 100644
--- a/drivers/net/ethernet/intel/igb/e1000_82575.c
+++ b/drivers/net/ethernet/intel/igb/e1000_82575.c
@@ -217,6 +217,9 @@ static s32 igb_init_phy_params_82575(struct e1000_hw *hw)
phy->ops.set_d3_lplu_state = igb_set_d3_lplu_state_82580;
phy->ops.force_speed_duplex = igb_phy_force_speed_duplex_m88;
break;
+ case BCM54616_E_PHY_ID:
+ phy->type = e1000_phy_bcm54616;
+ break;
default:
ret_val = -E1000_ERR_PHY;
goto out;
@@ -1464,6 +1467,7 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw)
case e1000_i350:
case e1000_i210:
case e1000_i211:
+ case e1000_i354:
phpm_reg = rd32(E1000_82580_PHY_POWER_MGMT);
phpm_reg &= ~E1000_82580_PM_GO_LINKD;
wr32(E1000_82580_PHY_POWER_MGMT, phpm_reg);
@@ -1507,6 +1511,8 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw)
case e1000_phy_82580:
ret_val = igb_copper_link_setup_82580(hw);
break;
+ case e1000_phy_bcm54616:
+ break;
default:
ret_val = -E1000_ERR_PHY;
break;
diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h
index 7459648..7a0cc00 100644
--- a/drivers/net/ethernet/intel/igb/e1000_defines.h
+++ b/drivers/net/ethernet/intel/igb/e1000_defines.h
@@ -786,6 +786,7 @@
#define M88_VENDOR 0x0141
#define I210_I_PHY_ID 0x01410C00
#define M88E1543_E_PHY_ID 0x01410EA0
+#define BCM54616_E_PHY_ID 0x3625D10
/* M88E1000 Specific Registers */
#define M88E1000_PHY_SPEC_CTRL 0x10 /* PHY Specific Control Register */
diff --git a/drivers/net/ethernet/intel/igb/e1000_hw.h b/drivers/net/ethernet/intel/igb/e1000_hw.h
index 2e166b2..32b7c2f 100644
--- a/drivers/net/ethernet/intel/igb/e1000_hw.h
+++ b/drivers/net/ethernet/intel/igb/e1000_hw.h
@@ -133,6 +133,7 @@ enum e1000_phy_type {
e1000_phy_ife,
e1000_phy_82580,
e1000_phy_i210,
+ e1000_phy_bcm54616,
};
enum e1000_bus_type {
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index 1675d7d..ad0f728 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -112,6 +112,7 @@ static DEFINE_PCI_DEVICE_TABLE(igb_pci_tbl) = {
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82575EB_COPPER), board_82575 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82575EB_FIBER_SERDES), board_82575 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82575GB_QUAD_COPPER), board_82575 },
+ { PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_SGMII), board_82575 },
/* required last entry */
{0, }
};
@@ -1585,6 +1586,7 @@ void igb_power_up_link(struct igb_adapter *adapter)
igb_power_up_phy_copper(&adapter->hw);
else
igb_power_up_serdes_link_82575(&adapter->hw);
+ igb_setup_link(&adapter->hw);
}
/**

View File

@@ -1,229 +0,0 @@
patches the Intel IGB driver to add bcm5461s phy support
diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c
index a775254..d05abb2 100644
--- a/drivers/net/ethernet/intel/igb/e1000_82575.c
+++ b/drivers/net/ethernet/intel/igb/e1000_82575.c
@@ -133,7 +133,10 @@ static s32 igb_init_phy_params_82575(struct e1000_hw *hw)
ctrl_ext = rd32(E1000_CTRL_EXT);
if (igb_sgmii_active_82575(hw)) {
- phy->ops.reset = igb_phy_hw_reset_sgmii_82575;
+ if(phy->type == e1000_phy_bcm5461s)
+ phy->ops.reset = igb_phy_hw_reset;
+ else
+ phy->ops.reset = igb_phy_hw_reset_sgmii_82575;
ctrl_ext |= E1000_CTRL_I2C_ENA;
} else {
phy->ops.reset = igb_phy_hw_reset;
@@ -220,6 +223,13 @@ static s32 igb_init_phy_params_82575(struct e1000_hw *hw)
case BCM54616_E_PHY_ID:
phy->type = e1000_phy_bcm54616;
break;
+ case BCM5461S_PHY_ID:
+ phy->type = e1000_phy_bcm5461s;
+ phy->ops.check_polarity = NULL;
+ phy->ops.get_phy_info = igb_get_phy_info_5461s;
+ phy->ops.get_cable_length = NULL;
+ phy->ops.force_speed_duplex = igb_phy_force_speed_duplex_82580;
+ break;
default:
ret_val = -E1000_ERR_PHY;
goto out;
@@ -758,6 +768,16 @@ static s32 igb_get_phy_id_82575(struct e1000_hw *hw)
break;
}
ret_val = igb_get_phy_id(hw);
+ if (ret_val && hw->mac.type == e1000_i354) {
+ /* we do a special check for bcm5461s phy by setting
+ * the phy->addr to 5 and doing the phy check again. This
+ * call will succeed and retrieve a valid phy id if we have
+ * the bcm5461s phy
+ */
+ phy->addr = 5;
+ phy->type = e1000_phy_bcm5461s;
+ ret_val = igb_get_phy_id(hw);
+ }
goto out;
}
@@ -1141,6 +1161,9 @@ static s32 igb_get_cfg_done_82575(struct e1000_hw *hw)
(hw->phy.type == e1000_phy_igp_3))
igb_phy_init_script_igp3(hw);
+ if (hw->phy.type == e1000_phy_bcm5461s)
+ igb_phy_init_script_5461s(hw);
+
return ret_val;
}
@@ -1512,6 +1535,7 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw)
ret_val = igb_copper_link_setup_82580(hw);
break;
case e1000_phy_bcm54616:
+ case e1000_phy_bcm5461s:
break;
default:
ret_val = -E1000_ERR_PHY;
diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h
index 7a0cc00..bee365f 100644
--- a/drivers/net/ethernet/intel/igb/e1000_defines.h
+++ b/drivers/net/ethernet/intel/igb/e1000_defines.h
@@ -787,6 +787,7 @@
#define I210_I_PHY_ID 0x01410C00
#define M88E1543_E_PHY_ID 0x01410EA0
#define BCM54616_E_PHY_ID 0x3625D10
+#define BCM5461S_PHY_ID 0x002060C0
/* M88E1000 Specific Registers */
#define M88E1000_PHY_SPEC_CTRL 0x10 /* PHY Specific Control Register */
diff --git a/drivers/net/ethernet/intel/igb/e1000_hw.h b/drivers/net/ethernet/intel/igb/e1000_hw.h
index 32b7c2f..d7d3504 100644
--- a/drivers/net/ethernet/intel/igb/e1000_hw.h
+++ b/drivers/net/ethernet/intel/igb/e1000_hw.h
@@ -134,6 +134,7 @@ enum e1000_phy_type {
e1000_phy_82580,
e1000_phy_i210,
e1000_phy_bcm54616,
+ e1000_phy_bcm5461s,
};
enum e1000_bus_type {
diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c
index ad2b74d..0df0346 100644
--- a/drivers/net/ethernet/intel/igb/e1000_phy.c
+++ b/drivers/net/ethernet/intel/igb/e1000_phy.c
@@ -152,6 +152,13 @@ s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data)
* Control register. The MAC will take care of interfacing with the
* PHY to retrieve the desired data.
*/
+ if (phy->type == e1000_phy_bcm5461s) {
+ mdic = rd32(E1000_MDICNFG);
+ mdic &= ~E1000_MDICNFG_PHY_MASK;
+ mdic |= (phy->addr << E1000_MDICNFG_PHY_SHIFT);
+ wr32(E1000_MDICNFG, mdic);
+ }
+
mdic = ((offset << E1000_MDIC_REG_SHIFT) |
(phy->addr << E1000_MDIC_PHY_SHIFT) |
(E1000_MDIC_OP_READ));
@@ -208,6 +215,13 @@ s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data)
* Control register. The MAC will take care of interfacing with the
* PHY to retrieve the desired data.
*/
+ if (phy->type == e1000_phy_bcm5461s) {
+ mdic = rd32(E1000_MDICNFG);
+ mdic &= ~E1000_MDICNFG_PHY_MASK;
+ mdic |= (phy->addr << E1000_MDICNFG_PHY_SHIFT);
+ wr32(E1000_MDICNFG, mdic);
+ }
+
mdic = (((u32)data) |
(offset << E1000_MDIC_REG_SHIFT) |
(phy->addr << E1000_MDIC_PHY_SHIFT) |
@@ -2599,3 +2613,68 @@ static s32 igb_set_master_slave_mode(struct e1000_hw *hw)
return hw->phy.ops.write_reg(hw, PHY_1000T_CTRL, phy_data);
}
+
+
+/**
+ * igb_phy_init_script_5461s - Inits the BCM5461S PHY
+ * @hw: pointer to the HW structure
+ *
+ * Initializes a Broadcom Gigabit PHY.
+ **/
+s32 igb_phy_init_script_5461s(struct e1000_hw *hw)
+{
+ u16 mii_reg_led = 0;
+
+ /* 1. Speed LED (Set the Link LED mode), Shadow 00010, 0x1C.bit2=1 */
+ hw->phy.ops.write_reg(hw, 0x1C, 0x0800);
+ hw->phy.ops.read_reg(hw, 0x1C, &mii_reg_led);
+ mii_reg_led |= 0x0004;
+ hw->phy.ops.write_reg(hw, 0x1C, mii_reg_led | 0x8000);
+
+ /* 2. Active LED (Set the Link LED mode), Shadow 01001, 0x1C.bit4=1, 0x10.bit5=0 */
+ hw->phy.ops.write_reg(hw, 0x1C, 0x2400);
+ hw->phy.ops.read_reg(hw, 0x1C, &mii_reg_led);
+ mii_reg_led |= 0x0010;
+ hw->phy.ops.write_reg(hw, 0x1C, mii_reg_led | 0x8000);
+ hw->phy.ops.read_reg(hw, 0x10, &mii_reg_led);
+ mii_reg_led &= 0xffdf;
+ hw->phy.ops.write_reg(hw, 0x10, mii_reg_led);
+
+ return 0;
+}
+
+
+/**
+ * igb_get_phy_info_5461s - Retrieve 5461s PHY information
+ * @hw: pointer to the HW structure
+ *
+ * Read PHY status to determine if link is up. If link is up, then
+ * set/determine 10base-T extended distance and polarity correction. Read
+ * PHY port status to determine MDI/MDIx and speed. Based on the speed,
+ * determine on the cable length, local and remote receiver.
+ **/
+s32 igb_get_phy_info_5461s(struct e1000_hw *hw)
+{
+ struct e1000_phy_info *phy = &hw->phy;
+ s32 ret_val;
+ bool link;
+
+ ret_val = igb_phy_has_link(hw, 1, 0, &link);
+ if (ret_val)
+ goto out;
+
+ if (!link) {
+ ret_val = -E1000_ERR_CONFIG;
+ goto out;
+ }
+
+ phy->polarity_correction = true;
+
+ phy->is_mdix = true;
+ phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED;
+ phy->local_rx = e1000_1000t_rx_status_ok;
+ phy->remote_rx = e1000_1000t_rx_status_ok;
+
+out:
+ return ret_val;
+}
diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.h b/drivers/net/ethernet/intel/igb/e1000_phy.h
index 6a0873f..ca60225 100644
--- a/drivers/net/ethernet/intel/igb/e1000_phy.h
+++ b/drivers/net/ethernet/intel/igb/e1000_phy.h
@@ -65,6 +65,8 @@ s32 igb_phy_has_link(struct e1000_hw *hw, u32 iterations,
void igb_power_up_phy_copper(struct e1000_hw *hw);
void igb_power_down_phy_copper(struct e1000_hw *hw);
s32 igb_phy_init_script_igp3(struct e1000_hw *hw);
+s32 igb_phy_init_script_5461s(struct e1000_hw *hw);
+s32 igb_get_phy_info_5461s(struct e1000_hw *hw);
s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data);
s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data);
s32 igb_read_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 *data);
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index ad0f728..0b8cb81 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -6841,11 +6841,19 @@ static int igb_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
data->phy_id = adapter->hw.phy.addr;
break;
case SIOCGMIIREG:
+ adapter->hw.phy.addr = data->phy_id;
if (igb_read_phy_reg(&adapter->hw, data->reg_num & 0x1F,
&data->val_out))
return -EIO;
break;
case SIOCSMIIREG:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ adapter->hw.phy.addr = data->phy_id;
+ if (igb_write_phy_reg(&adapter->hw, data->reg_num & 0x1F,
+ data->val_in))
+ return -EIO;
+ break;
default:
return -EOPNOTSUPP;
}

View File

@@ -1,29 +0,0 @@
--- a/drivers/hwmon/adm1021.c 2014-12-14 08:24:02.000000000 -0800
+++ b/drivers/hwmon/adm1021.c 2016-10-13 10:48:10.045055678 -0700
@@ -105,6 +105,7 @@ static struct adm1021_data *adm1021_upda
/* (amalysh) read only mode, otherwise any limit's writing confuse BIOS */
static int read_only;
+static int detect = 1;
static const struct i2c_device_id adm1021_id[] = {
{ "adm1021", adm1021 },
@@ -295,6 +296,9 @@ static int adm1021_detect(struct i2c_cli
"smbus byte data not supported!\n");
return -ENODEV;
}
+ if(detect == 0) {
+ return -ENODEV;
+ }
status = i2c_smbus_read_byte_data(client, ADM1021_REG_STATUS);
conv_rate = i2c_smbus_read_byte_data(client,
@@ -510,6 +514,8 @@ MODULE_LICENSE("GPL");
module_param(read_only, bool, 0);
MODULE_PARM_DESC(read_only, "Don't set any values, read only mode");
+module_param(detect, bool, 1);
+MODULE_PARM_DESC(detect, "Enable or disable device detection.");
module_init(sensors_adm1021_init)
module_exit(sensors_adm1021_exit)

View File

@@ -1,36 +0,0 @@
--- a/drivers/i2c/busses/i2c-isch.c 2014-12-14 08:24:02.000000000 -0800
+++ b/drivers/i2c/busses/i2c-isch.c 2016-10-13 08:02:44.564840300 -0700
@@ -47,7 +47,7 @@
#define SMBBLKDAT (0x20 + sch_smba)
/* Other settings */
-#define MAX_TIMEOUT 500
+#define MAX_RETRIES 5000
/* I2C constants */
#define SCH_QUICK 0x00
@@ -68,7 +68,7 @@ static int sch_transaction(void)
{
int temp;
int result = 0;
- int timeout = 0;
+ int retries = 0;
dev_dbg(&sch_adapter.dev, "Transaction (pre): CNT=%02x, CMD=%02x, "
"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb(SMBHSTCNT),
@@ -100,12 +100,12 @@ static int sch_transaction(void)
outb(inb(SMBHSTCNT) | 0x10, SMBHSTCNT);
do {
- msleep(1);
+ usleep_range(100, 200);
temp = inb(SMBHSTSTS) & 0x0f;
- } while ((temp & 0x08) && (timeout++ < MAX_TIMEOUT));
+ } while ((temp & 0x08) && (retries++ < MAX_RETRIES));
/* If the SMBus is still busy, we give up */
- if (timeout > MAX_TIMEOUT) {
+ if (retries > MAX_RETRIES) {
dev_err(&sch_adapter.dev, "SMBus Timeout!\n");
result = -ETIMEDOUT;
}

View File

@@ -1,44 +0,0 @@
diff -urpN a/drivers/net/ethernet/broadcom/tg3/tg3.c b/drivers/net/ethernet/broadcom/tg3/tg3.c
--- a/drivers/net/ethernet/broadcom/tg3/tg3.c 2017-01-03 10:58:35.130883468 -0800
+++ b/drivers/net/ethernet/broadcom/tg3/tg3.c 2017-01-03 11:22:46.910914971 -0800
@@ -324,6 +324,14 @@ module_param(tg3_disable_eee, int, 0);
MODULE_PARM_DESC(tg3_disable_eee, "Disable Energy Efficient Ethernet (EEE) support");
#endif
+static int short_preamble = 0;
+module_param(short_preamble, int, 0);
+MODULE_PARM_DESC(short_preamble, "Enable short preamble.");
+
+static int bcm5718s_reset = 0;
+module_param(bcm5718s_reset, int, 0);
+MODULE_PARM_DESC(bcm5718s_reset, "Enable BCM5718S reset support.");
+
#define TG3_DRV_DATA_FLAG_10_100_ONLY 0x0001
#define TG3_DRV_DATA_FLAG_5705_10_100 0x0002
@@ -1628,6 +1635,12 @@ static void tg3_mdio_config_5785(struct
static void tg3_mdio_start(struct tg3 *tp)
{
tp->mi_mode &= ~MAC_MI_MODE_AUTO_POLL;
+
+ if(short_preamble) {
+ netdev_info(tp->dev, "Setting short preamble...");
+ tp->mi_mode |= MAC_MI_MODE_SHORT_PREAMBLE;
+ }
+
tw32_f(MAC_MI_MODE, tp->mi_mode);
udelay(80);
@@ -2899,6 +2911,12 @@ static int tg3_phy_reset(struct tg3 *tp)
}
}
+ if (bcm5718s_reset && tp->phy_id == TG3_PHY_ID_BCM5718S) {
+ netdev_info(tp->dev, "BCM5718S reset...");
+ __tg3_writephy(tp, 0x8, 0x10, 0x1d0); /* set internal phy 0x8 to make linkup */
+ __tg3_writephy(tp, 0x1f, 0x4, 0x5e1); /* enable 10/100 cability of external phy */
+ }
+
if (tg3_flag(tp, 5717_PLUS) &&
(tp->phy_flags & TG3_PHYFLG_MII_SERDES))
return 0;

View File

@@ -1,24 +0,0 @@
Use GFP_ATOMIC instead of GFP_KERNEL since interrupts are disabled.
diff --git a/net/core/port.c b/net/core/port.c
index 0fa33bd..6fea48f 100644
--- a/net/core/port.c
+++ b/net/core/port.c
@@ -69,7 +69,7 @@ static void __port_cache_init(int ifindex)
return;
}
- port = kzalloc(sizeof(*port), GFP_KERNEL);
+ port = kzalloc(sizeof(*port), GFP_ATOMIC);
if (!port) {
spin_unlock_irqrestore(&port_cache_lock, flags);
return;
@@ -177,7 +177,7 @@ static void port_cache_set_stat_strings(int ifindex, int count, u8 *strings)
return;
}
- new_strings = kmalloc(count * ETH_GSTRING_LEN, GFP_KERNEL);
+ new_strings = kmalloc(count * ETH_GSTRING_LEN, GFP_ATOMIC);
if (!new_strings) {
spin_unlock_irqrestore(&port_cache_lock, flags);
return;

View File

@@ -1,37 +0,0 @@
#Copyright 2012 Cumulus Networks, Inc. All rights reserved.
Supplement the kernel's .gitignore
- Ignore platform device tree blob files
- vmlinux.strip
diff --git a/.gitignore b/.gitignore
index 57af07c..3eaa29a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,6 +40,7 @@ modules.builtin
/TAGS
/linux
/vmlinux
+/vmlinux.strip
/vmlinuz
/System.map
/Module.markers
diff --git a/arch/powerpc/boot/.gitignore b/arch/powerpc/boot/.gitignore
index 12da77e..652e35d 100644
--- a/arch/powerpc/boot/.gitignore
+++ b/arch/powerpc/boot/.gitignore
@@ -21,6 +21,7 @@ uImage
cuImage.*
dtbImage.*
treeImage.*
+vmlinux.strip
zImage
zImage.initrd
zImage.bin.*
@@ -44,4 +45,4 @@ fdt_sw.c
fdt_wip.c
libfdt.h
libfdt_internal.h
-
+*.dtb

View File

@@ -1,130 +0,0 @@
From 54b501992dd2a839e94e76aa392c392b55080ce8 Mon Sep 17 00:00:00 2001
Subject: [PATCH] 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 <keescook@chromium.org>
Suggested-by: Andrew Morton <akpm@linux-foundation.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Alan Cox <alan@linux.intel.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Doug Ledford <dledford@redhat.com>
Cc: Serge Hallyn <serge.hallyn@canonical.com>
Cc: James Morris <james.l.morris@oracle.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
diff --git a/fs/exec.c b/fs/exec.c
index 9c5fb55..a9dbf33 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -2006,17 +2006,17 @@ static void coredump_finish(struct mm_struct *mm)
void set_dumpable(struct mm_struct *mm, int value)
{
switch (value) {
- case 0:
+ case SUID_DUMP_DISABLE:
clear_bit(MMF_DUMPABLE, &mm->flags);
smp_wmb();
clear_bit(MMF_DUMP_SECURELY, &mm->flags);
break;
- case 1:
+ case SUID_DUMP_USER:
set_bit(MMF_DUMPABLE, &mm->flags);
smp_wmb();
clear_bit(MMF_DUMP_SECURELY, &mm->flags);
break;
- case 2:
+ case SUID_DUMP_ROOT:
set_bit(MMF_DUMP_SECURELY, &mm->flags);
smp_wmb();
set_bit(MMF_DUMPABLE, &mm->flags);
@@ -2029,7 +2029,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_DUMP_USER) ? SUID_DUMP_ROOT : ret;
}
/*
@@ -2152,7 +2152,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_DUMP_ROOT) {
/* Setuid core dump mode */
flag = O_EXCL; /* Stop rewrite attacks */
cred->fsuid = 0; /* Dump root private */
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index d6ff129..093f6c5 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -170,6 +170,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;
@@ -420,7 +425,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",
@@ -1517,7 +1522,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,
},
@@ -2506,6 +2511,34 @@ int proc_dointvec_minmax(struct ctl_table *table, int write,
do_proc_dointvec_minmax_conv, &param);
}
+static void validate_coredump_safety(void)
+{
+ if (suid_dumpable == SUID_DUMP_ROOT &&
+ 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,

View File

@@ -1,301 +0,0 @@
Enable building firmware into kernel binary
The Debian kernel removes the ability of compiling device driver
firmware into the kernel image.
This patch adds that capability back.
See configuration options:
CONFIG_FIRMWARE_IN_KERNEL=y
CONFIG_EXTRA_FIRMWARE
CONFIG_EXTRA_FIRMWARE_DIR
diff --git a/Makefile b/Makefile
index 2800ffe..8d45e46 100644
--- a/Makefile
+++ b/Makefile
@@ -505,7 +505,7 @@ scripts: scripts_basic include/config/auto.conf include/config/tristate.conf
# Objects we will link into vmlinux / subdirs we need to visit
init-y := init/
-drivers-y := drivers/ sound/
+drivers-y := drivers/ sound/ firmware/
net-y := net/
libs-y := lib/
core-y := usr/
diff --git a/firmware/.gitignore b/firmware/.gitignore
new file mode 100644
index 0000000..d9c6901
--- /dev/null
+++ b/firmware/.gitignore
@@ -0,0 +1,6 @@
+*.gen.S
+*.fw
+*.bin
+*.csp
+*.dsp
+ihex2fw
diff --git a/firmware/Makefile b/firmware/Makefile
new file mode 100644
index 0000000..93ce280
--- /dev/null
+++ b/firmware/Makefile
@@ -0,0 +1,257 @@
+#
+# kbuild file for firmware/
+#
+
+# Create $(fwabs) from $(CONFIG_EXTRA_FIRMWARE_DIR) -- if it doesn't have a
+# leading /, it's relative to $(srctree).
+fwdir := $(subst ",,$(CONFIG_EXTRA_FIRMWARE_DIR))
+fwabs := $(addprefix $(srctree)/,$(filter-out /%,$(fwdir)))$(filter /%,$(fwdir))
+
+fw-external-y := $(subst ",,$(CONFIG_EXTRA_FIRMWARE))
+
+# There are three cases to care about:
+# 1. Building kernel with CONFIG_FIRMWARE_IN_KERNEL=y -- $(fw-shipped-y) should
+# include the firmware files to include, according to .config
+# 2. 'make modules_install', which will install firmware for modules, and
+# _also_ for the in-kernel drivers when CONFIG_FIRMWARE_IN_KERNEL=n
+# 3. 'make firmware_install', which installs all firmware, unconditionally.
+
+# For the former two cases we want $(fw-shipped-y) and $(fw-shipped-m) to be
+# accurate. In the latter case it doesn't matter -- it'll use $(fw-shipped-all).
+# But be aware that the config file might not be included at all.
+
+ifdef CONFIG_ACENIC_OMIT_TIGON_I
+acenic-objs := acenic/tg2.bin
+fw-shipped- += acenic/tg1.bin
+else
+acenic-objs := acenic/tg1.bin acenic/tg2.bin
+endif
+fw-shipped-$(CONFIG_3C359) += 3com/3C359.bin
+fw-shipped-$(CONFIG_ACENIC) += $(acenic-objs)
+fw-shipped-$(CONFIG_ADAPTEC_STARFIRE) += adaptec/starfire_rx.bin \
+ adaptec/starfire_tx.bin
+fw-shipped-$(CONFIG_ATARI_DSP56K) += dsp56k/bootstrap.bin
+fw-shipped-$(CONFIG_ATM_AMBASSADOR) += atmsar11.fw
+fw-shipped-$(CONFIG_BNX2X) += bnx2x/bnx2x-e1-6.2.9.0.fw \
+ bnx2x/bnx2x-e1h-6.2.9.0.fw \
+ bnx2x/bnx2x-e2-6.2.9.0.fw
+fw-shipped-$(CONFIG_BNX2) += bnx2/bnx2-mips-09-6.2.1a.fw \
+ bnx2/bnx2-rv2p-09-6.0.17.fw \
+ bnx2/bnx2-rv2p-09ax-6.0.17.fw \
+ bnx2/bnx2-mips-06-6.2.1.fw \
+ bnx2/bnx2-rv2p-06-6.0.15.fw
+fw-shipped-$(CONFIG_CASSINI) += sun/cassini.bin
+fw-shipped-$(CONFIG_COMPUTONE) += intelliport2.bin
+fw-shipped-$(CONFIG_CHELSIO_T3) += cxgb3/t3b_psram-1.1.0.bin \
+ cxgb3/t3c_psram-1.1.0.bin \
+ cxgb3/t3fw-7.10.0.bin \
+ cxgb3/ael2005_opt_edc.bin \
+ cxgb3/ael2005_twx_edc.bin \
+ cxgb3/ael2020_twx_edc.bin
+fw-shipped-$(CONFIG_DRM_MGA) += matrox/g200_warp.fw matrox/g400_warp.fw
+fw-shipped-$(CONFIG_DRM_R128) += r128/r128_cce.bin
+fw-shipped-$(CONFIG_DRM_RADEON) += radeon/R100_cp.bin radeon/R200_cp.bin \
+ radeon/R300_cp.bin radeon/R420_cp.bin \
+ radeon/RS690_cp.bin radeon/RS600_cp.bin \
+ radeon/R520_cp.bin \
+ radeon/R600_pfp.bin radeon/R600_me.bin \
+ radeon/RV610_pfp.bin radeon/RV610_me.bin \
+ radeon/RV630_pfp.bin radeon/RV630_me.bin \
+ radeon/RV620_pfp.bin radeon/RV620_me.bin \
+ radeon/RV635_pfp.bin radeon/RV635_me.bin \
+ radeon/RV670_pfp.bin radeon/RV670_me.bin \
+ radeon/RS780_pfp.bin radeon/RS780_me.bin \
+ radeon/RV770_pfp.bin radeon/RV770_me.bin \
+ radeon/RV730_pfp.bin radeon/RV730_me.bin \
+ radeon/RV710_pfp.bin radeon/RV710_me.bin
+fw-shipped-$(CONFIG_DVB_AV7110) += av7110/bootcode.bin
+fw-shipped-$(CONFIG_DVB_TTUSB_BUDGET) += ttusb-budget/dspbootcode.bin
+fw-shipped-$(CONFIG_E100) += e100/d101m_ucode.bin e100/d101s_ucode.bin \
+ e100/d102e_ucode.bin
+fw-shipped-$(CONFIG_MYRI_SBUS) += myricom/lanai.bin
+fw-shipped-$(CONFIG_PCMCIA_PCNET) += cis/LA-PCM.cis cis/PCMLM28.cis \
+ cis/DP83903.cis cis/NE2K.cis \
+ cis/tamarack.cis cis/PE-200.cis \
+ cis/PE520.cis
+fw-shipped-$(CONFIG_PCMCIA_3C589) += cis/3CXEM556.cis
+fw-shipped-$(CONFIG_PCMCIA_3C574) += cis/3CCFEM556.cis
+fw-shipped-$(CONFIG_SERIAL_8250_CS) += cis/MT5634ZLX.cis cis/RS-COM-2P.cis \
+ cis/COMpad2.cis cis/COMpad4.cis \
+ cis/SW_555_SER.cis cis/SW_7xx_SER.cis \
+ cis/SW_8xx_SER.cis
+fw-shipped-$(CONFIG_PCMCIA_SMC91C92) += ositech/Xilinx7OD.bin
+fw-shipped-$(CONFIG_SCSI_ADVANSYS) += advansys/mcode.bin advansys/38C1600.bin \
+ advansys/3550.bin advansys/38C0800.bin
+fw-shipped-$(CONFIG_SCSI_ISCI) += isci/isci_firmware.bin
+fw-shipped-$(CONFIG_SCSI_QLOGIC_1280) += qlogic/1040.bin qlogic/1280.bin \
+ qlogic/12160.bin
+fw-shipped-$(CONFIG_SCSI_QLOGICPTI) += qlogic/isp1000.bin
+fw-shipped-$(CONFIG_INFINIBAND_QIB) += qlogic/sd7220.fw
+fw-shipped-$(CONFIG_SMCTR) += tr_smctr.bin
+fw-shipped-$(CONFIG_SND_KORG1212) += korg/k1212.dsp
+fw-shipped-$(CONFIG_SND_MAESTRO3) += ess/maestro3_assp_kernel.fw \
+ ess/maestro3_assp_minisrc.fw
+fw-shipped-$(CONFIG_SND_SB16_CSP) += sb16/mulaw_main.csp sb16/alaw_main.csp \
+ sb16/ima_adpcm_init.csp \
+ sb16/ima_adpcm_playback.csp \
+ sb16/ima_adpcm_capture.csp
+fw-shipped-$(CONFIG_SND_YMFPCI) += yamaha/ds1_ctrl.fw yamaha/ds1_dsp.fw \
+ yamaha/ds1e_ctrl.fw
+fw-shipped-$(CONFIG_SND_WAVEFRONT) += yamaha/yss225_registers.bin
+fw-shipped-$(CONFIG_TEHUTI) += tehuti/bdx.bin
+fw-shipped-$(CONFIG_TIGON3) += tigon/tg3.bin tigon/tg3_tso.bin \
+ tigon/tg3_tso5.bin
+fw-shipped-$(CONFIG_TYPHOON) += 3com/typhoon.bin
+fw-shipped-$(CONFIG_USB_DABUSB) += dabusb/firmware.fw dabusb/bitstream.bin
+fw-shipped-$(CONFIG_USB_EMI26) += emi26/loader.fw emi26/firmware.fw \
+ emi26/bitstream.fw
+fw-shipped-$(CONFIG_USB_EMI62) += emi62/loader.fw emi62/bitstream.fw \
+ emi62/spdif.fw emi62/midi.fw
+fw-shipped-$(CONFIG_USB_KAWETH) += kaweth/new_code.bin kaweth/trigger_code.bin \
+ kaweth/new_code_fix.bin \
+ kaweth/trigger_code_fix.bin
+ifdef CONFIG_FIRMWARE_IN_KERNEL
+fw-shipped-$(CONFIG_USB_SERIAL_KEYSPAN_MPR) += keyspan/mpr.fw
+fw-shipped-$(CONFIG_USB_SERIAL_KEYSPAN_USA18X) += keyspan/usa18x.fw
+fw-shipped-$(CONFIG_USB_SERIAL_KEYSPAN_USA19) += keyspan/usa19.fw
+fw-shipped-$(CONFIG_USB_SERIAL_KEYSPAN_USA19QI) += keyspan/usa19qi.fw
+fw-shipped-$(CONFIG_USB_SERIAL_KEYSPAN_USA19QW) += keyspan/usa19qw.fw
+fw-shipped-$(CONFIG_USB_SERIAL_KEYSPAN_USA19W) += keyspan/usa19w.fw
+fw-shipped-$(CONFIG_USB_SERIAL_KEYSPAN_USA28) += keyspan/usa28.fw
+fw-shipped-$(CONFIG_USB_SERIAL_KEYSPAN_USA28XA) += keyspan/usa28xa.fw
+fw-shipped-$(CONFIG_USB_SERIAL_KEYSPAN_USA28XB) += keyspan/usa28xb.fw
+fw-shipped-$(CONFIG_USB_SERIAL_KEYSPAN_USA28X) += keyspan/usa28x.fw
+fw-shipped-$(CONFIG_USB_SERIAL_KEYSPAN_USA49W) += keyspan/usa49w.fw
+fw-shipped-$(CONFIG_USB_SERIAL_KEYSPAN_USA49WLC) += keyspan/usa49wlc.fw
+else
+fw-shipped- += keyspan/mpr.fw keyspan/usa18x.fw keyspan/usa19.fw \
+ keyspan/usa19qi.fw keyspan/usa19qw.fw keyspan/usa19w.fw \
+ keyspan/usa28.fw keyspan/usa28xa.fw keyspan/usa28xb.fw \
+ keyspan/usa28x.fw keyspan/usa49w.fw keyspan/usa49wlc.fw
+endif
+fw-shipped-$(CONFIG_USB_SERIAL_TI) += ti_3410.fw ti_5052.fw \
+ mts_cdma.fw mts_gsm.fw mts_edge.fw
+fw-shipped-$(CONFIG_USB_SERIAL_EDGEPORT) += edgeport/boot.fw edgeport/boot2.fw \
+ edgeport/down.fw edgeport/down2.fw
+fw-shipped-$(CONFIG_USB_SERIAL_EDGEPORT_TI) += edgeport/down3.bin
+fw-shipped-$(CONFIG_USB_SERIAL_WHITEHEAT) += whiteheat_loader.fw whiteheat.fw \
+ # whiteheat_loader_debug.fw
+fw-shipped-$(CONFIG_USB_SERIAL_KEYSPAN_PDA) += keyspan_pda/keyspan_pda.fw
+fw-shipped-$(CONFIG_USB_SERIAL_XIRCOM) += keyspan_pda/xircom_pgs.fw
+fw-shipped-$(CONFIG_USB_VICAM) += vicam/firmware.fw
+fw-shipped-$(CONFIG_VIDEO_CPIA2) += cpia2/stv0672_vp4.bin
+fw-shipped-$(CONFIG_YAM) += yam/1200.bin yam/9600.bin
+
+fw-shipped-all := $(fw-shipped-y) $(fw-shipped-m) $(fw-shipped-)
+
+# Directories which we _might_ need to create, so we have a rule for them.
+firmware-dirs := $(sort $(addprefix $(objtree)/$(obj)/,$(dir $(fw-external-y) $(fw-shipped-all))))
+
+quiet_cmd_mkdir = MKDIR $(patsubst $(objtree)/%,%,$@)
+ cmd_mkdir = mkdir -p $@
+
+quiet_cmd_ihex = IHEX $@
+ cmd_ihex = $(OBJCOPY) -Iihex -Obinary $< $@
+
+quiet_cmd_ihex2fw = IHEX2FW $@
+ cmd_ihex2fw = $(objtree)/$(obj)/ihex2fw $< $@
+
+quiet_cmd_h16tofw = H16TOFW $@
+ cmd_h16tofw = $(objtree)/$(obj)/ihex2fw -w $< $@
+
+quiet_cmd_fwbin = MK_FW $@
+ cmd_fwbin = FWNAME="$(patsubst firmware/%.gen.S,%,$@)"; \
+ FWSTR="$(subst /,_,$(subst .,_,$(subst -,_,$(patsubst \
+ firmware/%.gen.S,%,$@))))"; \
+ ASM_WORD=$(if $(CONFIG_64BIT),.quad,.long); \
+ ASM_ALIGN=$(if $(CONFIG_64BIT),3,2); \
+ PROGBITS=$(if $(CONFIG_ARM),%,@)progbits; \
+ echo "/* Generated by firmware/Makefile */" > $@;\
+ echo " .section .rodata" >>$@;\
+ echo " .p2align $${ASM_ALIGN}" >>$@;\
+ echo "_fw_$${FWSTR}_bin:" >>$@;\
+ echo " .incbin \"$(2)\"" >>$@;\
+ echo "_fw_end:" >>$@;\
+ echo " .section .rodata.str,\"aMS\",$${PROGBITS},1" >>$@;\
+ echo " .p2align $${ASM_ALIGN}" >>$@;\
+ echo "_fw_$${FWSTR}_name:" >>$@;\
+ echo " .string \"$$FWNAME\"" >>$@;\
+ echo " .section .builtin_fw,\"a\",$${PROGBITS}" >>$@;\
+ echo " .p2align $${ASM_ALIGN}" >>$@;\
+ echo " $${ASM_WORD} _fw_$${FWSTR}_name" >>$@;\
+ echo " $${ASM_WORD} _fw_$${FWSTR}_bin" >>$@;\
+ echo " $${ASM_WORD} _fw_end - _fw_$${FWSTR}_bin" >>$@;
+
+# One of these files will change, or come into existence, whenever
+# the configuration changes between 32-bit and 64-bit. The .S files
+# need to change when that happens.
+wordsize_deps := $(wildcard include/config/64bit.h include/config/32bit.h \
+ include/config/ppc32.h include/config/ppc64.h \
+ include/config/superh32.h include/config/superh64.h \
+ include/config/x86_32.h include/config/x86_64.h)
+
+# Workaround for make < 3.81, where .SECONDEXPANSION doesn't work.
+# It'll end up depending on these targets, so make them a PHONY rule which
+# depends on _all_ the directories in $(firmware-dirs), and it'll work out OK.
+PHONY += $(objtree)/$$(%) $(objtree)/$(obj)/$$(%)
+$(objtree)/$$(%) $(objtree)/$(obj)/$$(%): $(firmware-dirs)
+ @true
+
+# For the $$(dir %) trick, where we need % to be expanded first.
+.SECONDEXPANSION:
+
+$(patsubst %,$(obj)/%.gen.S, $(fw-shipped-y)): %: $(wordsize_deps) \
+ | $(objtree)/$$(dir %)
+ $(call cmd,fwbin,$(patsubst %.gen.S,%,$@))
+$(patsubst %,$(obj)/%.gen.S, $(fw-external-y)): %: $(wordsize_deps) \
+ include/config/extra/firmware/dir.h | $(objtree)/$$(dir %)
+ $(call cmd,fwbin,$(fwabs)/$(patsubst $(obj)/%.gen.S,%,$@))
+
+# The .o files depend on the binaries directly; the .S files don't.
+$(patsubst %,$(obj)/%.gen.o, $(fw-shipped-y)): %.gen.o: %
+$(patsubst %,$(obj)/%.gen.o, $(fw-external-y)): $(obj)/%.gen.o: $(fwdir)/%
+
+# .ihex is used just as a simple way to hold binary files in a source tree
+# where binaries are frowned upon. They are directly converted with objcopy.
+$(obj)/%: $(obj)/%.ihex | $(objtree)/$(obj)/$$(dir %)
+ $(call cmd,ihex)
+
+# Don't depend on ihex2fw if we're installing and it already exists.
+# Putting it after | in the dependencies doesn't seem sufficient when
+# we're installing after a cross-compile, because ihex2fw has dependencies
+# on stuff like /usr/lib/gcc/ppc64-redhat-linux/4.3.0/include/stddef.h and
+# thus wants to be rebuilt. Which it can't be, if the prebuilt kernel tree
+# is exported read-only for someone to run 'make install'.
+ifeq ($(INSTALL):$(wildcard $(obj)/ihex2fw),install:$(obj)/ihex2fw)
+ihex2fw_dep :=
+else
+ihex2fw_dep := $(obj)/ihex2fw
+endif
+
+# .HEX is also Intel HEX, but where the offset and length in each record
+# is actually meaningful, because the firmware has to be loaded in a certain
+# order rather than as a single binary blob. Thus, we convert them into our
+# more compact binary representation of ihex records (<linux/ihex.h>)
+$(obj)/%.fw: $(obj)/%.HEX $(ihex2fw_dep) | $(objtree)/$(obj)/$$(dir %)
+ $(call cmd,ihex2fw)
+
+# .H16 is our own modified form of Intel HEX, with 16-bit length for records.
+$(obj)/%.fw: $(obj)/%.H16 $(ihex2fw_dep) | $(objtree)/$(obj)/$$(dir %)
+ $(call cmd,h16tofw)
+
+$(firmware-dirs):
+ $(call cmd,mkdir)
+
+obj-y += $(patsubst %,%.gen.o, $(fw-external-y))
+obj-$(CONFIG_FIRMWARE_IN_KERNEL) += $(patsubst %,%.gen.o, $(fw-shipped-y))
+
+# Remove .S files and binaries created from ihex
+# (during 'make clean' .config isn't included so they're all in $(fw-shipped-))
+targets := $(fw-shipped-) $(patsubst $(obj)/%,%, \
+ $(shell find $(obj) -name \*.gen.S 2>/dev/null))
+
+# Without this, built-in.o won't be created when it's empty, and the
+# final vmlinux link will fail.
+obj-n := dummy
+
+hostprogs-y := ihex2fw

View File

@@ -1,134 +0,0 @@
From 9520628e8ceb69fa9a4aee6b57f22675d9e1b709 Mon Sep 17 00:00:00 2001
Subject: [PATCH] fs: make dumpable=2 require fully qualified path
When the suid_dumpable sysctl is set to "2", and there is no core dump
pipe defined in the core_pattern sysctl, a local user can cause core files
to be written to root-writable directories, potentially with
user-controlled content.
This means an admin can unknowningly reintroduce a variation of
CVE-2006-2451, allowing local users to gain root privileges.
$ cat /proc/sys/fs/suid_dumpable
2
$ cat /proc/sys/kernel/core_pattern
core
$ ulimit -c unlimited
$ cd /
$ ls -l core
ls: cannot access core: No such file or directory
$ touch core
touch: cannot touch `core': Permission denied
$ OHAI="evil-string-here" ping localhost >/dev/null 2>&1 &
$ pid=$!
$ sleep 1
$ kill -SEGV $pid
$ ls -l core
-rw------- 1 root kees 458752 Jun 21 11:35 core
$ sudo strings core | grep evil
OHAI=evil-string-here
While cron has been fixed to abort reading a file when there is any
parse error, there are still other sensitive directories that will read
any file present and skip unparsable lines.
Instead of introducing a suid_dumpable=3 mode and breaking all users of
mode 2, this only disables the unsafe portion of mode 2 (writing to disk
via relative path). Most users of mode 2 (e.g. Chrome OS) already use
a core dump pipe handler, so this change will not break them. For the
situations where a pipe handler is not defined but mode 2 is still
active, crash dumps will only be written to fully qualified paths. If a
relative path is defined (e.g. the default "core" pattern), dump
attempts will trigger a printk yelling about the lack of a fully
qualified path.
Signed-off-by: Kees Cook <keescook@chromium.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Alan Cox <alan@linux.intel.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Doug Ledford <dledford@redhat.com>
Cc: Serge Hallyn <serge.hallyn@canonical.com>
Cc: James Morris <james.l.morris@oracle.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
diff --git a/Documentation/sysctl/fs.txt b/Documentation/sysctl/fs.txt
index 9d29414..171c65a 100644
--- a/Documentation/sysctl/fs.txt
+++ b/Documentation/sysctl/fs.txt
@@ -205,16 +205,22 @@ This value can be used to query and set the core dump mode for setuid
or otherwise protected/tainted binaries. The modes are
0 - (default) - traditional behaviour. Any process which has changed
- privilege levels or is execute only will not be dumped
+ privilege levels or is execute only will not be dumped.
1 - (debug) - all processes dump core when possible. The core dump is
owned by the current user and no security is applied. This is
intended for system debugging situations only. Ptrace is unchecked.
+ This is insecure as it allows regular users to examine the memory
+ contents of privileged processes.
2 - (suidsafe) - any binary which normally would not be dumped is dumped
- readable by root only. This allows the end user to remove
- such a dump but not access it directly. For security reasons
- core dumps in this mode will not overwrite one another or
- other files. This mode is appropriate when administrators are
- attempting to debug problems in a normal environment.
+ anyway, but only if the "core_pattern" kernel sysctl is set to
+ either a pipe handler or a fully qualified path. (For more details
+ on this limitation, see CVE-2006-2451.) This mode is appropriate
+ when administrators are attempting to debug problems in a normal
+ environment, and either have a core dump pipe handler that knows
+ to treat privileged core dumps with care, or specific directory
+ defined for catching core dumps. If a core dump happens without
+ a pipe handler or fully qualifid path, a message will be emitted
+ to syslog warning about the lack of a correct setting.
==============================================================
diff --git a/fs/exec.c b/fs/exec.c
index 78199eb..9c5fb55 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -2121,6 +2121,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
int retval = 0;
int flag = 0;
int ispipe;
+ bool need_nonrelative = false;
static atomic_t core_dump_count = ATOMIC_INIT(0);
struct coredump_params cprm = {
.signr = signr,
@@ -2146,14 +2147,16 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
if (!cred)
goto fail;
/*
- * We cannot trust fsuid as being the "true" uid of the
- * process nor do we know its entire history. We only know it
- * was tainted so we dump it as root in mode 2.
+ * We cannot trust fsuid as being the "true" uid of the process
+ * nor do we know its entire history. We only know it was tainted
+ * 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) {
/* Setuid core dump mode */
flag = O_EXCL; /* Stop rewrite attacks */
cred->fsuid = 0; /* Dump root private */
+ need_nonrelative = true;
}
retval = coredump_wait(exit_code, &core_state);
@@ -2233,6 +2236,14 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
if (cprm.limit < binfmt->min_coredump)
goto fail_unlock;
+ if (need_nonrelative && cn.corename[0] != '/') {
+ printk(KERN_WARNING "Pid %d(%s) can only dump core "\
+ "to fully qualified path!\n",
+ task_tgid_vnr(current), current->comm);
+ printk(KERN_WARNING "Skipping core dump\n");
+ goto fail_unlock;
+ }
+
cprm.file = filp_open(cn.corename,
O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag,
0600);

View File

@@ -1,12 +0,0 @@
diff -uNpr linux-3.2.65-deb7.a/fs/overlayfs/inode.c linux-3.2.65-deb7.b/fs/overlayfs/inode.c
--- linux-3.2.65-deb7.a/fs/overlayfs/inode.c 2015-04-07 13:55:17.487864270 -0700
+++ linux-3.2.65-deb7.b/fs/overlayfs/inode.c 2015-04-07 13:56:24.948259512 -0700
@@ -68,7 +68,7 @@ int ovl_permission(struct inode *inode,
spin_unlock(&inode->i_lock);
return -ENOENT;
}
- alias = list_entry(inode->i_dentry.next, struct dentry, d_alias);
+ alias = list_entry(inode->i_dentry.next, struct dentry, d_u.d_alias);
dget(alias);
spin_unlock(&inode->i_lock);
oe = alias->d_fsdata;

View File

@@ -1,22 +0,0 @@
This patch adds null addr check for ioremap_prot return addr.
This fixes kernel crash seen in CM-934.
Failure was seen when ioremap_prot fails with the below
error in arch/powerpc/mm/pgtable_32.c
Jul 23 15:02:04 act-5652-24 kernel: __ioremap(): phys addr 0x7f46000 is RAM lr c00af1f4
diff --git a/mm/memory.c b/mm/memory.c
index 483e665..7f26814 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -3823,6 +3823,9 @@ int generic_access_phys(struct vm_area_struct *vma, unsigned long addr,
return -EINVAL;
maddr = ioremap_prot(phys_addr, PAGE_SIZE, prot);
+ if (!maddr)
+ return -EFAULT;
+
if (write)
memcpy_toio(maddr + offset, buf, len);
else

View File

@@ -1,28 +0,0 @@
Change the warning for scan_unevictable_pages to DEBUG, since the warning is just annoying and serves no purpose.
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 313381c..de7701d 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -3493,7 +3493,7 @@ void check_move_unevictable_pages(struct page **pages, int nr_pages)
static void warn_scan_unevictable_pages(void)
{
- printk_once(KERN_WARNING
+ printk_once(KERN_DEBUG
"The scan_unevictable_pages sysctl/node-interface has been "
"disabled for lack of a legitimate use case. If you have "
"one, please send an email to linux-mm@kvack.org.\n");
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 319cd57..5c6399c 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -1761,7 +1761,7 @@ static void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl,
static int warned;
if (strcmp(warncomm, current->comm) && warned < 5) {
strcpy(warncomm, current->comm);
- printk(KERN_WARNING
+ printk(KERN_DEBUG
"process `%s' is using deprecated sysctl (%s) "
"net.ipv6.neigh.%s.%s; "
"Use net.ipv6.neigh.%s.%s_ms "

View File

@@ -1,21 +0,0 @@
#Copyright 2012 Cumulus Networks, Inc. All rights reserved.
Reduce the log level of the oom_score_adj vs oom_adj usage warning
Both files exist in the kernel, but they are trying to move people to
oom_score_adj. With that said, it's annoying for a warning to pop up
on normal boots.
diff --git a/fs/proc/base.c b/fs/proc/base.c
index d851316..cbcff3e 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -1076,7 +1076,7 @@ static ssize_t oom_adjust_write(struct file *file, const char __user *buf,
* Warn that /proc/pid/oom_adj is deprecated, see
* Documentation/feature-removal-schedule.txt.
*/
- printk_once(KERN_WARNING "%s (%d): /proc/%d/oom_adj is deprecated, please use /proc/%d/oom_score_adj instead.\n",
+ printk_once(KERN_DEBUG "%s (%d): /proc/%d/oom_adj is deprecated, please use /proc/%d/oom_score_adj instead.\n",
current->comm, task_pid_nr(current), task_pid_nr(task),
task_pid_nr(task));
task->signal->oom_adj = oom_adjust;

View File

@@ -1,68 +0,0 @@
From efb2109f545b8881bc0b48aeaed787d6c91714a2 Mon Sep 17 00:00:00 2001
Subject: [PATCH 1/2] panic: Make panic_timeout configurable
The panic_timeout value can be set via the command line option
'panic=x', or via /proc/sys/kernel/panic, however that is not
sufficient when the panic occurs before we are able to set up
these values. Thus, add a CONFIG_PANIC_TIMEOUT so that we can
set the desired value from the .config.
The default panic_timeout value continues to be 0 - wait
forever. Also adds set_arch_panic_timeout(new_timeout,
arch_default_timeout), which is intended to be used by arches in
arch_setup(). The idea being that the new_timeout is only set if
the user hasn't changed from the arch_default_timeout.
Signed-off-by: Jason Baron <jbaron@akamai.com>
Cc: benh@kernel.crashing.org
Cc: paulus@samba.org
Cc: ralf@linux-mips.org
Cc: mpe@ellerman.id.au
Cc: felipe.contreras@gmail.com
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/1a1674daec27c534df409697025ac568ebcee91e.1385418410.git.jbaron@akamai.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
(cherry picked from commit 5800dc3cff87c3a1548382298bb16e1fb4ec7e32)
Conflicts:
include/linux/kernel.h
lib/Kconfig.debug
Signed-off-by: Jonathan Toppins <jtoppins@cumulusnetworks.com>
diff --git a/kernel/panic.c b/kernel/panic.c
index 3458469..8713b71 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -33,7 +33,7 @@ static int pause_on_oops;
static int pause_on_oops_flag;
static DEFINE_SPINLOCK(pause_on_oops_lock);
-int panic_timeout;
+int panic_timeout = CONFIG_PANIC_TIMEOUT;
EXPORT_SYMBOL_GPL(panic_timeout);
ATOMIC_NOTIFIER_HEAD(panic_notifier_list);
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 6cd177f..005f0b9 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -286,6 +286,15 @@ config BOOTPARAM_HUNG_TASK_PANIC_VALUE
default 0 if !BOOTPARAM_HUNG_TASK_PANIC
default 1 if BOOTPARAM_HUNG_TASK_PANIC
+config PANIC_TIMEOUT
+ int "panic timeout"
+ default 0
+ help
+ Set the timeout value (in seconds) until a reboot occurs when the
+ the kernel panics. If n = 0, then we wait forever. A timeout
+ value n > 0 will wait n seconds before rebooting, while a timeout
+ value n < 0 will reboot immediately.
+
config SCHED_DEBUG
bool "Collect scheduler debugging info"
depends on DEBUG_KERNEL && PROC_FS

View File

@@ -1,106 +0,0 @@
ubifs - xattr - bugs
An amalgam of patches posted to linux-mtd adding xattr support for symlinks and
related bugs in ubifs.
http://lists.infradead.org/pipermail/linux-mtd/2013-February/045794.html
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index f9c234b..6b79020 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -1575,6 +1575,12 @@ const struct inode_operations ubifs_symlink_inode_operations = {
.follow_link = ubifs_follow_link,
.setattr = ubifs_setattr,
.getattr = ubifs_getattr,
+#ifdef CONFIG_UBIFS_FS_XATTR
+ .setxattr = ubifs_setxattr,
+ .getxattr = ubifs_getxattr,
+ .listxattr = ubifs_listxattr,
+ .removexattr = ubifs_removexattr,
+#endif
};
const struct file_operations ubifs_file_operations = {
diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c
index cef0460..84dd0f4 100644
--- a/fs/ubifs/journal.c
+++ b/fs/ubifs/journal.c
@@ -553,7 +553,8 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
dbg_jnl("ino %lu, dent '%.*s', data len %d in dir ino %lu",
inode->i_ino, nm->len, nm->name, ui->data_len, dir->i_ino);
- ubifs_assert(dir_ui->data_len == 0);
+ if (!xent)
+ ubifs_assert(dir_ui->data_len == 0);
ubifs_assert(mutex_is_locked(&dir_ui->ui_mutex));
dlen = UBIFS_DENT_NODE_SZ + nm->len + 1;
@@ -572,7 +573,12 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
aligned_dlen = ALIGN(dlen, 8);
aligned_ilen = ALIGN(ilen, 8);
- len = aligned_dlen + aligned_ilen + UBIFS_INO_NODE_SZ;
+ /*
+ * Make sure to account for dir_ui+data_len in length
+ * calculation in case there is extended attribute.
+ */
+ len = aligned_dlen + aligned_ilen +
+ UBIFS_INO_NODE_SZ + dir_ui->data_len;
dent = kmalloc(len, GFP_NOFS);
if (!dent)
return -ENOMEM;
@@ -649,7 +655,8 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
ino_key_init(c, &ino_key, dir->i_ino);
ino_offs += aligned_ilen;
- err = ubifs_tnc_add(c, &ino_key, lnum, ino_offs, UBIFS_INO_NODE_SZ);
+ err = ubifs_tnc_add(c, &ino_key, lnum, ino_offs,
+ UBIFS_INO_NODE_SZ + dir_ui->data_len);
if (err)
goto out_ro;
diff --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c
index bf18f7a..2e1db01 100644
--- a/fs/ubifs/xattr.c
+++ b/fs/ubifs/xattr.c
@@ -138,16 +138,15 @@ static int create_xattr(struct ubifs_info *c, struct inode *host,
ui = ubifs_inode(inode);
ui->xattr = 1;
ui->flags |= UBIFS_XATTR_FL;
- ui->data = kmalloc(size, GFP_NOFS);
+ ui->data = kmemdup(value, size, GFP_NOFS);
if (!ui->data) {
err = -ENOMEM;
goto out_free;
}
- memcpy(ui->data, value, size);
- inode->i_size = ui->ui_size = size;
- ui->data_len = size;
mutex_lock(&host_ui->ui_mutex);
+ inode->i_size = ui->ui_size = size;
+ ui->data_len = size;
host->i_ctime = ubifs_current_time(host);
host_ui->xattr_cnt += 1;
host_ui->xattr_size += CALC_DENT_SIZE(nm->len);
@@ -204,16 +203,15 @@ static int change_xattr(struct ubifs_info *c, struct inode *host,
return err;
kfree(ui->data);
- ui->data = kmalloc(size, GFP_NOFS);
+ ui->data = kmemdup(value, size, GFP_NOFS);
if (!ui->data) {
err = -ENOMEM;
goto out_free;
}
- memcpy(ui->data, value, size);
- inode->i_size = ui->ui_size = size;
- ui->data_len = size;
mutex_lock(&host_ui->ui_mutex);
+ inode->i_size = ui->ui_size = size;
+ ui->data_len = size;
host->i_ctime = ubifs_current_time(host);
host_ui->xattr_size -= CALC_XATTR_BYTES(ui->data_len);
host_ui->xattr_size += CALC_XATTR_BYTES(size);

View File

@@ -1,43 +0,0 @@
From 124d37e9f088a8f56494b0264d63d22555f53fef Mon Sep 17 00:00:00 2001
Subject: [PATCH] arp: allow arp processing to honor per interface arp_accept
sysctl
I found recently that the arp_process function which handles all of our received
arp frames, is using IPV4_DEVCONF_ALL macro to check the state of the arp_process
flag. This seems wrong, as it implies that either none or all of the network
interfaces accept gratuitous arps. This patch corrects that, allowing
per-interface arp_accept configuration to deviate from the all setting. Note
this also brings us into line with the way the arp_filter setting is handled
during arp_process execution.
Tested this myself on my home network, and confirmed it works as expected.
Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
CC: "David S. Miller" <davem@davemloft.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h
index 85bb88e..0ced5ee 100644
--- a/include/linux/inetdevice.h
+++ b/include/linux/inetdevice.h
@@ -139,6 +139,7 @@ static inline void ipv4_devconf_setall(struct in_device *in_dev)
IN_DEV_ORCONF((in_dev), ACCEPT_REDIRECTS)))
#define IN_DEV_ARPFILTER(in_dev) IN_DEV_ORCONF((in_dev), ARPFILTER)
+#define IN_DEV_ARP_ACCEPT(in_dev) IN_DEV_ORCONF((in_dev), ARP_ACCEPT)
#define IN_DEV_ARP_ANNOUNCE(in_dev) IN_DEV_MAXCONF((in_dev), ARP_ANNOUNCE)
#define IN_DEV_ARP_IGNORE(in_dev) IN_DEV_MAXCONF((in_dev), ARP_IGNORE)
#define IN_DEV_ARP_NOTIFY(in_dev) IN_DEV_MAXCONF((in_dev), ARP_NOTIFY)
diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
index 59a7041..8042b80 100644
--- a/net/ipv4/arp.c
+++ b/net/ipv4/arp.c
@@ -893,7 +893,7 @@ static int arp_process(struct sk_buff *skb)
n = __neigh_lookup(&arp_tbl, &sip, dev, 0);
- if (IPV4_DEVCONF_ALL(dev_net(dev), ARP_ACCEPT)) {
+ if (IN_DEV_ARP_ACCEPT(in_dev)) {
/* Unsolicited ARP is not accepted by default.
It is possible, that this option should be enabled for some
devices (strip is candidate)

View File

@@ -1,317 +0,0 @@
Add slave membership notification via generic netlink.
Add /sys/class/net/bondX/bonding/ad_active_slaves node.
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 5af2a8f..173cdbc 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -78,6 +78,8 @@
#include <net/net_namespace.h>
#include <net/netns/generic.h>
#include <net/pkt_sched.h>
+#include <net/netlink.h>
+#include <net/genetlink.h>
#include "bonding.h"
#include "bond_3ad.h"
#include "bond_alb.h"
@@ -271,6 +273,160 @@ const char *bond_mode_name(int mode)
return names[mode];
}
+/*---------------------------- genl/bond -----------------------------*/
+
+enum {
+ BOND_GENL_ATTR_UNSPEC,
+ BOND_GENL_ATTR_BOND,
+ BOND_GENL_ATTR_SLAVE,
+ __BOND_GENL_ATTR_MAX,
+};
+#define BOND_GENL_ATTR_MAX (__BOND_GENL_ATTR_MAX - 1)
+
+enum {
+ BOND_GENL_CMD_UNSPEC,
+ BOND_GENL_CMD_GET,
+ BOND_GENL_CMD_ACTIVATE_EVENT,
+ BOND_GENL_CMD_DEACTIVATE_EVENT,
+ __BOND_GENL_CMD_MAX,
+};
+
+static struct genl_family bond_event_genl_family = {
+ .id = GENL_ID_GENERATE,
+ .name = "bond_event",
+ .version = 1,
+ .maxattr = BOND_GENL_ATTR_MAX,
+};
+
+static int fill_genlmsg(struct sk_buff *skb, u32 pid, u32 seq,
+ int flags, u8 cmd, struct bonding *bond, struct slave *slave)
+{
+ void *hdr;
+
+ hdr = genlmsg_put(skb, pid, seq, &bond_event_genl_family,
+ flags, cmd);
+ if (hdr == NULL)
+ return -EMSGSIZE;
+
+ if (bond)
+ NLA_PUT_U32(skb, BOND_GENL_ATTR_BOND, bond->dev->ifindex);
+ else
+ NLA_PUT_U32(skb, BOND_GENL_ATTR_BOND, 0);
+ NLA_PUT_U32(skb, BOND_GENL_ATTR_SLAVE, slave->dev->ifindex);
+
+ return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(skb, hdr);
+ return -EMSGSIZE;
+}
+
+static int bond_event_genl_dump(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct bond_net *bn = net_generic(&init_net, bond_net_id);
+ struct slave *slave;
+ struct bonding *bond;
+ u32 pid = NETLINK_CB(cb->skb).pid;
+ u32 seq = cb->nlh->nlmsg_seq;
+ int bond_idx = 0, bond_start_idx = cb->args[0];
+ int slave_idx = 0, slave_start_idx = cb->args[1];
+ int i;
+
+ rtnl_lock();
+
+ list_for_each_entry(bond, &bn->dev_list, bond_list) {
+
+ if (bond_idx > bond_start_idx)
+ slave_start_idx = 0;
+ if (bond_idx++ < bond_start_idx)
+ continue;
+
+ slave_idx = 0;
+
+ read_lock(&bond->lock);
+ bond_for_each_slave(bond, slave, i) {
+ u8 cmd = bond_is_active_slave(slave) ?
+ BOND_GENL_CMD_ACTIVATE_EVENT :
+ BOND_GENL_CMD_DEACTIVATE_EVENT;
+ if (slave_idx++ < slave_start_idx)
+ continue;
+ if (fill_genlmsg(skb, pid, seq, NLM_F_MULTI,
+ cmd, bond, slave) < 0) {
+ read_unlock(&bond->lock);
+ goto out;
+ }
+ }
+ read_unlock(&bond->lock);
+ }
+
+out:
+ rtnl_unlock();
+
+ cb->args[0] = bond_idx;
+ cb->args[1] = slave_idx;
+
+ return skb->len;
+}
+
+static struct genl_ops bond_event_get_ops = {
+ .cmd = BOND_GENL_CMD_GET,
+ .dumpit = bond_event_genl_dump,
+};
+
+static struct genl_multicast_group bond_event_mcgrp = {
+ .name = "bond_event_mc",
+};
+
+static int bond_netlink_event(struct bonding *bond,
+ struct slave *slave,
+ u8 cmd)
+{
+ static atomic_t seq;
+ struct sk_buff *skb;
+ int msg_size = 2 * nla_total_size(sizeof(u32));
+
+ skb = genlmsg_new(msg_size, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ if (fill_genlmsg(skb, 0, atomic_add_return(1, &seq),
+ 0, cmd, bond, slave) < 0)
+ goto err_out;
+
+ genlmsg_multicast(skb, 0, bond_event_mcgrp.id, GFP_ATOMIC);
+
+ return 0;
+
+err_out:
+ nlmsg_free(skb);
+ return -ENOMEM;
+}
+
+static int bond_genl_init(void)
+{
+ int res;
+
+ res = genl_register_family(&bond_event_genl_family);
+ if (res)
+ return res;
+
+ res = genl_register_ops(&bond_event_genl_family,
+ &bond_event_get_ops);
+ if (res)
+ goto err;
+
+ res = genl_register_mc_group(&bond_event_genl_family,
+ &bond_event_mcgrp);
+ if (res)
+ goto err;
+
+ return res;
+err:
+ genl_unregister_family(&bond_event_genl_family);
+ return res;
+}
+
/*---------------------------------- VLAN -----------------------------------*/
/**
@@ -2080,6 +2236,11 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
slave_dev->priv_flags &= ~IFF_BONDING;
+ /* deactivate slave in bond cache. bond ifindex for cache
+ * entry will be 0 (slave no longer in bond).
+ */
+ bond_netlink_event(NULL, slave, BOND_GENL_CMD_DEACTIVATE_EVENT);
+
kfree(slave);
return 0; /* deletion OK */
@@ -4882,6 +5043,18 @@ static struct pernet_operations bond_net_ops = {
.size = sizeof(struct bond_net),
};
+void bond_set_active_slave(struct slave *slave)
+{
+ slave->backup = 0;
+ bond_netlink_event(slave->bond, slave, BOND_GENL_CMD_ACTIVATE_EVENT);
+}
+
+void bond_set_backup_slave(struct slave *slave)
+{
+ slave->backup = 1;
+ bond_netlink_event(slave->bond, slave, BOND_GENL_CMD_DEACTIVATE_EVENT);
+}
+
static int __init bonding_init(void)
{
int i;
@@ -4901,6 +5074,10 @@ static int __init bonding_init(void)
if (res)
goto err_link;
+ res = bond_genl_init();
+ if (res)
+ goto err_genl;
+
bond_create_debugfs();
for (i = 0; i < max_bonds; i++) {
@@ -4914,6 +5091,8 @@ static int __init bonding_init(void)
out:
return res;
err:
+ genl_unregister_family(&bond_event_genl_family);
+err_genl:
bond_destroy_debugfs();
rtnl_link_unregister(&bond_link_ops);
err_link:
@@ -4929,6 +5108,7 @@ static void __exit bonding_exit(void)
bond_destroy_debugfs();
+ genl_unregister_family(&bond_event_genl_family);
rtnl_link_unregister(&bond_link_ops);
unregister_pernet_subsys(&bond_net_ops);
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c
index cf95bd8..e894f2d 100644
--- a/drivers/net/bonding/bond_sysfs.c
+++ b/drivers/net/bonding/bond_sysfs.c
@@ -1428,6 +1428,46 @@ static ssize_t bonding_show_ad_partner_mac(struct device *d,
static DEVICE_ATTR(ad_partner_mac, S_IRUGO, bonding_show_ad_partner_mac, NULL);
/*
+ * Show the current 802.3ad active slaves.
+ */
+static ssize_t bonding_show_ad_active_slaves(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct slave *slave;
+ int i, count = 0;
+ struct bonding *bond = to_bond(d);
+
+ if (bond->params.mode != BOND_MODE_8023AD)
+ return count;
+
+ if (!rtnl_trylock())
+ return restart_syscall();
+
+ read_lock(&bond->lock);
+ bond_for_each_slave(bond, slave, i) {
+ int active;
+ if (count > (PAGE_SIZE - IFNAMSIZ - 6)) {
+ /* not enough space for another interface_name:status pair */
+ if ((PAGE_SIZE - count) > 10)
+ count = PAGE_SIZE - 10;
+ count += sprintf(buf + count, "++more++ ");
+ break;
+ }
+ active = bond_is_active_slave(slave) ? 1 : 0;
+ count += sprintf(buf + count, "%s:%d ",
+ slave->dev->name, active);
+ }
+ read_unlock(&bond->lock);
+ if (count)
+ buf[count-1] = '\n'; /* eat the leftover space */
+ rtnl_unlock();
+
+ return count;
+}
+static DEVICE_ATTR(ad_active_slaves, S_IRUGO, bonding_show_ad_active_slaves, NULL);
+
+/*
* Show the queue_ids of the slaves in the current bond.
*/
static ssize_t bonding_show_queue_id(struct device *d,
@@ -1665,6 +1705,7 @@ static struct attribute *per_bond_attrs[] = {
&dev_attr_ad_actor_key.attr,
&dev_attr_ad_partner_key.attr,
&dev_attr_ad_partner_mac.attr,
+ &dev_attr_ad_active_slaves.attr,
&dev_attr_queue_id.attr,
&dev_attr_all_slaves_active.attr,
&dev_attr_resend_igmp.attr,
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index 1aecc37..387f9f6 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -293,15 +293,8 @@ static inline bool bond_is_lb(const struct bonding *bond)
bond->params.mode == BOND_MODE_ALB);
}
-static inline void bond_set_active_slave(struct slave *slave)
-{
- slave->backup = 0;
-}
-
-static inline void bond_set_backup_slave(struct slave *slave)
-{
- slave->backup = 1;
-}
+void bond_set_active_slave(struct slave *slave);
+void bond_set_backup_slave(struct slave *slave);
static inline int bond_slave_state(struct slave *slave)
{

View File

@@ -1,143 +0,0 @@
Advertise speed/duplex for 802.3ad mode. Basically add up all the active slave speeds,
and report duplex as FULL.
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index aaa7999..c7e3e53 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -2354,15 +2354,7 @@ int bond_3ad_set_carrier(struct bonding *bond)
return 0;
}
-/**
- * bond_3ad_get_active_agg_info - get information of the active aggregator
- * @bond: bonding struct to work on
- * @ad_info: ad_info struct to fill with the bond's info
- *
- * Returns: 0 on success
- * < 0 on error
- */
-int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info)
+struct aggregator * bond_3ad_get_active_agg(struct bonding *bond)
{
struct aggregator *aggregator = NULL;
struct port *port;
@@ -2374,6 +2366,21 @@ int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info)
}
}
+ return aggregator;
+}
+
+/**
+ * bond_3ad_get_active_agg_info - get information of the active aggregator
+ * @bond: bonding struct to work on
+ * @ad_info: ad_info struct to fill with the bond's info
+ *
+ * Returns: 0 on success
+ * < 0 on error
+ */
+int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info)
+{
+ struct aggregator *aggregator = bond_3ad_get_active_agg(bond);
+
if (aggregator) {
ad_info->aggregator_id = aggregator->aggregator_identifier;
ad_info->ports = aggregator->num_of_ports;
diff --git a/drivers/net/bonding/bond_3ad.h b/drivers/net/bonding/bond_3ad.h
index 20d9c78..046bb1f 100644
--- a/drivers/net/bonding/bond_3ad.h
+++ b/drivers/net/bonding/bond_3ad.h
@@ -273,6 +273,7 @@ void bond_3ad_initiate_agg_selection(struct bonding *bond, int timeout);
void bond_3ad_adapter_speed_changed(struct slave *slave);
void bond_3ad_adapter_duplex_changed(struct slave *slave);
void bond_3ad_handle_link_change(struct slave *slave, char link);
+struct aggregator *bond_3ad_get_active_agg(struct bonding *bond);
int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info);
int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev);
void bond_3ad_lacpdu_recv(struct sk_buff *skb, struct bonding *bond,
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 173cdbc..a986b86 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -2583,6 +2583,7 @@ static int bond_miimon_inspect(struct bonding *bond)
static void bond_miimon_commit(struct bonding *bond)
{
struct slave *slave;
+ struct ethtool_cmd ecmd;
int i;
bond_for_each_slave(bond, slave, i) {
@@ -2605,9 +2606,11 @@ static void bond_miimon_commit(struct bonding *bond)
bond_set_backup_slave(slave);
}
+ __ethtool_get_settings(slave->dev, &ecmd);
pr_info("%s: link status definitely up for interface %s, %u Mbps %s duplex.\n",
bond->dev->name, slave->dev->name,
- slave->speed, slave->duplex ? "full" : "half");
+ ethtool_cmd_speed(&ecmd),
+ ecmd.duplex == DUPLEX_FULL ? "full" : "half");
/* notify ad that the link status has changed */
if (bond->params.mode == BOND_MODE_8023AD)
@@ -4414,6 +4417,36 @@ void bond_set_mode_ops(struct bonding *bond, int mode)
}
}
+static int bond_get_settings(struct net_device *dev,
+ struct ethtool_cmd *ecmd)
+{
+ struct bonding *bond = netdev_priv(dev);
+ u32 speed_mbps = SPEED_UNKNOWN;
+
+ if (bond->params.mode != BOND_MODE_8023AD)
+ return -EOPNOTSUPP;
+
+ if (bond->first_slave)
+ speed_mbps = bond->first_slave->speed;
+
+ switch(speed_mbps) {
+ case SPEED_10:
+ case SPEED_100:
+ case SPEED_1000:
+ case SPEED_10000:
+ speed_mbps = speed_mbps * bond->slave_cnt;
+ break;
+ default:
+ speed_mbps = SPEED_UNKNOWN;
+ break;
+ }
+
+ ethtool_cmd_speed_set(ecmd, speed_mbps);
+ ecmd->duplex = DUPLEX_FULL;
+
+ return 0;
+}
+
static void bond_ethtool_get_drvinfo(struct net_device *bond_dev,
struct ethtool_drvinfo *drvinfo)
{
@@ -4423,6 +4456,7 @@ static void bond_ethtool_get_drvinfo(struct net_device *bond_dev,
}
static const struct ethtool_ops bond_ethtool_ops = {
+ .get_settings = bond_get_settings,
.get_drvinfo = bond_ethtool_get_drvinfo,
.get_link = ethtool_op_get_link,
};
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c
index e894f2d..67f9073 100644
--- a/drivers/net/bonding/bond_sysfs.c
+++ b/drivers/net/bonding/bond_sysfs.c
@@ -1431,8 +1431,8 @@ static DEVICE_ATTR(ad_partner_mac, S_IRUGO, bonding_show_ad_partner_mac, NULL);
* Show the current 802.3ad active slaves.
*/
static ssize_t bonding_show_ad_active_slaves(struct device *d,
- struct device_attribute *attr,
- char *buf)
+ struct device_attribute *attr,
+ char *buf)
{
struct slave *slave;
int i, count = 0;

View File

@@ -1,137 +0,0 @@
Hold down the slaves for multi-chassis LAG enabled bonds. For such bonds the
slave state (dev_open/dev_close) is controlled by a MC-LAG application. This
change involves -
1. dev_close on all the cuurent slaves when clag_enable is set to 1.
2. dev_open on all the current slaves when clag_enable is set to 0. To resume
regular operation.
3. skip slave dev_open when a new slave is added to an MC-LAG bond (clag_enable
set to 1).
Without this change in the following cases the slaves will start forwarding
traffic independent of the MC-LAG control protocol which could result in
duplicate packets, station moves, incorrect path changes (traffic black holes)
and loops (causing a network meltdown) -
1. On switch reboot prior to the MC-LAG app taking control of the MC-LAG bonds.
2. When a new slave is added to a bond (bond_enslave)
CL Note: Is dependent on networking-bonding-clag.patch
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index d08ed85..0356165 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1810,7 +1810,14 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
goto err_restore_mac;
}
- /* open the slave since the application closed it */
+ /* If the bond is multi-chassis LAG enabled the slave is placed in a
+ * proto_down state. This has to be done before device is opened and
+ * is needed to prevent the slaves from becoming active independent of
+ * the mc-lag control protocol run by the app. */
+ if (bond->params.clag_enable)
+ dev_set_proto_down(slave_dev, IF_LINK_PROTO_DOWN_MLAG,
+ IF_LINK_PROTO_DOWN_MLAG);
+
res = dev_open(slave_dev);
if (res) {
pr_debug("Opening slave %s failed\n", slave_dev->name);
@@ -2043,6 +2050,8 @@ err_close:
dev_close(slave_dev);
err_unset_master:
+ if (bond->params.clag_enable)
+ dev_set_proto_down(slave_dev, 0, IF_LINK_PROTO_DOWN_MLAG);
netdev_set_bond_master(slave_dev, NULL);
err_restore_mac:
@@ -2248,6 +2257,10 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
/* close slave before restoring its mac address */
dev_close(slave_dev);
+ /* Clear MLAG proto_down state */
+ if (bond->params.clag_enable)
+ dev_set_proto_down(slave_dev, 0, IF_LINK_PROTO_DOWN_MLAG);
+
if (bond->params.fail_over_mac != BOND_FOM_ACTIVE) {
/* restore original ("permanent") mac address */
memcpy(addr.sa_data, slave->perm_hwaddr, ETH_ALEN);
@@ -5404,6 +5417,53 @@ void bond_set_backup_slave(struct slave *slave)
bond_netlink_event(slave->bond, slave, BOND_GENL_CMD_DEACTIVATE_EVENT);
}
+static void bond_set_clag_proto_down_state(struct bonding *bond, u8 proto_down)
+{
+ struct slave *slave;
+ struct net_device *slave_dev;
+ struct net_device *bond_dev = bond->dev;
+ int i;
+
+ read_lock(&bond->lock);
+ bond_for_each_slave(bond, slave, i) {
+ slave_dev = slave->dev;
+ if (proto_down)
+ dev_set_proto_down(slave_dev, IF_LINK_PROTO_DOWN_MLAG,
+ IF_LINK_PROTO_DOWN_MLAG);
+ else
+ dev_set_proto_down(slave_dev, 0,
+ IF_LINK_PROTO_DOWN_MLAG);
+ }
+ read_unlock(&bond->lock);
+
+ if (proto_down)
+ dev_set_proto_down(bond_dev, IF_LINK_PROTO_DOWN_MLAG,
+ IF_LINK_PROTO_DOWN_MLAG);
+ else
+ dev_set_proto_down(bond_dev, 0,
+ IF_LINK_PROTO_DOWN_MLAG);
+}
+
+/* Enable/disable multi-chassis LAG on a bond. Once mc-lag is enabled on a bond
+ * the slaves are place in a proto_down state as the slaves' state will now be
+ * controlled by a mc-lag application. On mc-lag disable the bonding driver
+ * takes back slave state control and clears proto_down on slaves that may have
+ * been shutdown by the application */
+void bond_set_clag_enable(struct bonding *bond, u8 clag_enable)
+{
+ if (bond->params.clag_enable == clag_enable)
+ return;
+
+ bond->params.clag_enable = clag_enable;
+
+ if (clag_enable)
+ /* proto_down the bond and its slaves - they are now in
+ * MC-LAG app's control */
+ bond_set_clag_proto_down_state(bond, 1);
+ else
+ bond_set_clag_proto_down_state(bond, 0);
+}
+
static int __init bonding_init(void)
{
int i;
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c
index 93a4db8..dc68bbc 100644
--- a/drivers/net/bonding/bond_sysfs.c
+++ b/drivers/net/bonding/bond_sysfs.c
@@ -1012,7 +1012,7 @@ static ssize_t bonding_store_clag_enable(struct device *d,
return restart_syscall();
pr_debug("%s: Setting clag enable to %u\n",
bond->dev->name, new_value);
- bond->params.clag_enable = new_value;
+ bond_set_clag_enable(bond, new_value);
call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
rtnl_unlock();
return count;
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index eefaa18..6f8b59c 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -425,6 +425,7 @@ void bond_debug_register(struct bonding *bond);
void bond_debug_unregister(struct bonding *bond);
void bond_debug_reregister(struct bonding *bond);
const char *bond_mode_name(int mode);
+void bond_set_clag_enable(struct bonding *bond, u8 clag_enable);
struct bond_net {
struct net * net; /* Associated network namespace */

View File

@@ -1,108 +0,0 @@
Introduces CLAG (multi-chassis lag) enable bond config. On its own this patch
doesn't provide any functionality. It was done just to add the "clag_enable"
param in 2.5.0.
Note: Associated functional changes were introduced in 2.5.1 via
networking-bonding-clag-proto-down.patch
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 70c865b..6e7f377 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -5071,6 +5071,7 @@ static size_t bond_get_size(const struct net_device *bond_dev)
nla_total_size(sizeof(u8)) + /* IFLA_BOND_CL_LACP_BYPASS_ALLOW */
nla_total_size(sizeof(u8)) + /* IFLA_BOND_CL_LACP_BYPASS_ACTIVE */
nla_total_size(sizeof(u32)) + /* IFLA_BOND_CL_LACP_BYPASS_PERIOD */
+ nla_total_size(sizeof(u8)) + /* IFLA_BOND_CL_CLAG_ENABLE */
0;
}
@@ -5229,6 +5230,10 @@ static int bond_fill_info(struct sk_buff *skb,
if (nla_put_u32(skb, IFLA_BOND_CL_LACP_BYPASS_PERIOD,
bond->params.lacp_bypass_period))
goto nla_put_failure;
+
+ if (nla_put_u8(skb, IFLA_BOND_CL_CLAG_ENABLE,
+ bond->params.clag_enable))
+ goto nla_put_failure;
}
return 0;
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c
index 4e377e0..93a4db8 100644
--- a/drivers/net/bonding/bond_sysfs.c
+++ b/drivers/net/bonding/bond_sysfs.c
@@ -984,6 +984,42 @@ static ssize_t bonding_store_min_links(struct device *d,
static DEVICE_ATTR(min_links, S_IRUGO | S_IWUSR,
bonding_show_min_links, bonding_store_min_links);
+static ssize_t bonding_show_clag_enable(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct bonding *bond = to_bond(d);
+
+ return sprintf(buf, "%d\n", bond->params.clag_enable);
+}
+
+static ssize_t bonding_store_clag_enable(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bonding *bond = to_bond(d);
+ int ret;
+ u8 new_value;
+
+ ret = kstrtou8(buf, 10, &new_value);
+ if (ret < 0) {
+ pr_err("%s: Ignoring invalid clag enable value %s.\n",
+ bond->dev->name, buf);
+ return ret;
+ }
+
+ if (!rtnl_trylock())
+ return restart_syscall();
+ pr_debug("%s: Setting clag enable to %u\n",
+ bond->dev->name, new_value);
+ bond->params.clag_enable = new_value;
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
+ rtnl_unlock();
+ return count;
+}
+static DEVICE_ATTR(clag_enable, S_IRUGO | S_IWUSR,
+ bonding_show_clag_enable, bonding_store_clag_enable);
+
static ssize_t bonding_show_lacp_bypass_allow(struct device *d,
struct device_attribute *attr,
char *buf)
@@ -2095,6 +2131,7 @@ static struct attribute *per_bond_attrs[] = {
&dev_attr_lacp_bypass_allow.attr,
&dev_attr_lacp_bypass_active.attr,
&dev_attr_lacp_bypass_period.attr,
+ &dev_attr_clag_enable.attr,
NULL,
};
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index b3594f7..eefaa18 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -163,6 +163,7 @@ struct bond_params {
int lacp_bypass_allow;
int lacp_bypass_active;
u32 lacp_bypass_period;
+ u8 clag_enable;
};
struct bond_parm_tbl {
diff --git a/include/linux/if_link.h b/include/linux/if_link.h
index 086e2ad..b262ddd 100644
--- a/include/linux/if_link.h
+++ b/include/linux/if_link.h
@@ -357,6 +357,7 @@ enum {
IFLA_BOND_CL_LACP_BYPASS_ALLOW = IFLA_BOND_CL_START,
IFLA_BOND_CL_LACP_BYPASS_ACTIVE,
IFLA_BOND_CL_LACP_BYPASS_PERIOD,
+ IFLA_BOND_CL_CLAG_ENABLE,
__IFLA_BOND_MAX,
};

View File

@@ -1,372 +0,0 @@
From 7faa104a7f66e35a8c9538ae8cd3a7a0ce5e5291 Mon Sep 17 00:00:00 2001
Subject: [PATCH 3/4] bonding: create netlink event when bonding option is
changed
Userspace needs to be notified if one changes some option.
Signed-off-by: Jiri Pirko <jiri@resnulli.us>
Acked-by: Veaceslav Falico <vfalico@gmail.com>
Acked-by: Andy Gospodarek <gospo@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
(cherry picked from commit d4261e5650004d6d51137553ea5433d5828562dc)
Conflicts:
drivers/net/bonding/bond_options.c
include/linux/netdevice.h
To prevent a deadlock (due to a nested lock) made sure all called to
call_netdevice_notifiers was outside a rw_lock critical section, this
because bond_fill_info also has a rw_lock critical section.
Signed-off-by: Jonathan Toppins <jtoppins@cumulusnetworks.com>
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c
index 6d7ea50..6103587 100644
--- a/drivers/net/bonding/bond_sysfs.c
+++ b/drivers/net/bonding/bond_sysfs.c
@@ -108,6 +108,7 @@ static ssize_t slave_store(struct kobject *kobj,
ret = slave_attr->store(slave, val);
if (ret == 0)
ret = count;
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, slave->bond->dev);
rtnl_unlock();
}
@@ -419,11 +420,15 @@ static ssize_t bonding_store_mode(struct device *d,
goto out;
}
+ if (!rtnl_trylock())
+ return restart_syscall();
bond->params.mode = new_value;
bond_set_mode_ops(bond, bond->params.mode);
pr_info("%s: setting mode to %s (%d).\n",
bond->dev->name, bond_mode_tbl[new_value].modename,
new_value);
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
+ rtnl_unlock();
out:
return ret;
}
@@ -466,13 +471,17 @@ static ssize_t bonding_store_xmit_hash(struct device *d,
(int)strlen(buf) - 1, buf);
ret = -EINVAL;
goto out;
- } else {
- bond->params.xmit_policy = new_value;
- bond_set_mode_ops(bond, bond->params.mode);
- pr_info("%s: setting xmit hash policy to %s (%d).\n",
+ }
+
+ if (!rtnl_trylock())
+ return restart_syscall();
+ bond->params.xmit_policy = new_value;
+ bond_set_mode_ops(bond, bond->params.mode);
+ pr_info("%s: setting xmit hash policy to %s (%d).\n",
bond->dev->name,
xmit_hashtype_tbl[new_value].modename, new_value);
- }
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
+ rtnl_unlock();
out:
return ret;
}
@@ -515,7 +524,11 @@ static ssize_t bonding_store_arp_validate(struct device *d,
bond->dev->name, arp_validate_tbl[new_value].modename,
new_value);
+ if (!rtnl_trylock())
+ return restart_syscall();
bond->params.arp_validate = new_value;
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
+ rtnl_unlock();
return count;
}
@@ -558,10 +571,14 @@ static ssize_t bonding_store_fail_over_mac(struct device *d,
return -EINVAL;
}
+ if (!rtnl_trylock())
+ return restart_syscall();
bond->params.fail_over_mac = new_value;
pr_info("%s: Setting fail_over_mac to %s (%d).\n",
bond->dev->name, fail_over_mac_tbl[new_value].modename,
new_value);
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
+ rtnl_unlock();
return count;
}
@@ -639,6 +656,7 @@ static ssize_t bonding_store_arp_interval(struct device *d,
queue_delayed_work(bond->wq, &bond->arp_work, 0);
}
}
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
out:
rtnl_unlock();
return ret;
@@ -675,6 +693,8 @@ static ssize_t bonding_store_arp_targets(struct device *d,
struct bonding *bond = to_bond(d);
__be32 *targets;
+ if (!rtnl_trylock())
+ return restart_syscall();
targets = bond->params.arp_targets;
newtarget = in_aton(buf + 1);
/* look for adds */
@@ -739,8 +759,10 @@ static ssize_t bonding_store_arp_targets(struct device *d,
ret = -EPERM;
goto out;
}
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
out:
+ rtnl_unlock();
return ret;
}
static DEVICE_ATTR(arp_ip_target, S_IRUGO | S_IWUSR , bonding_show_arp_targets, bonding_store_arp_targets);
@@ -799,6 +821,7 @@ static ssize_t bonding_store_downdelay(struct device *d,
bond->params.downdelay * bond->params.miimon);
}
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
out:
rtnl_unlock();
@@ -857,6 +880,7 @@ static ssize_t bonding_store_updelay(struct device *d,
bond->dev->name,
bond->params.updelay * bond->params.miimon);
}
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
out:
rtnl_unlock();
@@ -904,11 +928,15 @@ static ssize_t bonding_store_lacp(struct device *d,
new_value = bond_parse_parm(buf, bond_lacp_tbl);
if ((new_value == 1) || (new_value == 0)) {
+ if (!rtnl_trylock())
+ return restart_syscall();
bond->params.lacp_fast = new_value;
bond_3ad_update_lacp_rate(bond);
pr_info("%s: Setting LACP rate to %s (%d).\n",
bond->dev->name, bond_lacp_tbl[new_value].modename,
new_value);
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
+ rtnl_unlock();
} else {
pr_err("%s: Ignoring invalid LACP rate value %.*s.\n",
bond->dev->name, (int)strlen(buf) - 1, buf);
@@ -944,9 +972,13 @@ static ssize_t bonding_store_min_links(struct device *d,
return ret;
}
+ if (!rtnl_trylock())
+ return restart_syscall();
pr_info("%s: Setting min links value to %u\n",
bond->dev->name, new_value);
bond->params.min_links = new_value;
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
+ rtnl_unlock();
return count;
}
static DEVICE_ATTR(min_links, S_IRUGO | S_IWUSR,
@@ -976,9 +1008,13 @@ static ssize_t bonding_store_lacp_fallback_allow(struct device *d,
return ret;
}
+ if (!rtnl_trylock())
+ return restart_syscall();
pr_debug("%s: Setting lacp_fallback_allow to %u\n",
bond->dev->name, new_value);
bond->params.lacp_fallback_allow = new_value;
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
+ rtnl_unlock();
return count;
}
static DEVICE_ATTR(lacp_fallback_allow, S_IRUGO | S_IWUSR,
@@ -1008,9 +1044,13 @@ static ssize_t bonding_store_lacp_fallback_period(struct device *d,
return ret;
}
+ if (!rtnl_trylock())
+ return restart_syscall();
pr_debug("%s: Setting lacp_fallback period to %u\n",
bond->dev->name, new_value);
bond->params.lacp_fallback_period = new_value;
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
+ rtnl_unlock();
return count;
}
static DEVICE_ATTR(lacp_fallback_period, S_IRUGO | S_IWUSR,
@@ -1041,9 +1081,13 @@ static ssize_t bonding_store_lacp_fallback_active(struct device *d,
return ret;
}
+ if (!rtnl_trylock())
+ return restart_syscall();
pr_debug("%s: Setting lacp_fallback active to %u\n",
bond->dev->name, new_value);
bond->params.lacp_fallback_active = new_value;
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
+ rtnl_unlock();
return count;
}
static DEVICE_ATTR(lacp_fallback_active, S_IRUGO | S_IWUSR,
@@ -1079,10 +1123,14 @@ static ssize_t bonding_store_ad_select(struct device *d,
new_value = bond_parse_parm(buf, ad_select_tbl);
if (new_value != -1) {
+ if (!rtnl_trylock())
+ return restart_syscall();
bond->params.ad_select = new_value;
pr_info("%s: Setting ad_select to %s (%d).\n",
bond->dev->name, ad_select_tbl[new_value].modename,
new_value);
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
+ rtnl_unlock();
} else {
pr_err("%s: Ignoring invalid ad_select value %.*s.\n",
bond->dev->name, (int)strlen(buf) - 1, buf);
@@ -1110,8 +1158,19 @@ static ssize_t bonding_store_num_peer_notif(struct device *d,
const char *buf, size_t count)
{
struct bonding *bond = to_bond(d);
- int err = kstrtou8(buf, 10, &bond->params.num_peer_notif);
- return err ? err : count;
+ int err;
+
+ if (!rtnl_trylock())
+ return restart_syscall();
+
+ err = kstrtou8(buf, 10, &bond->params.num_peer_notif);
+ if (err)
+ goto out;
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
+ err = count;
+out:
+ rtnl_unlock();
+ return err;
}
static DEVICE_ATTR(num_grat_arp, S_IRUGO | S_IWUSR,
bonding_show_num_peer_notif, bonding_store_num_peer_notif);
@@ -1185,6 +1244,7 @@ static ssize_t bonding_store_miimon(struct device *d,
queue_delayed_work(bond->wq, &bond->mii_work, 0);
}
}
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
out:
rtnl_unlock();
return ret;
@@ -1261,6 +1321,7 @@ out:
write_unlock_bh(&bond->curr_slave_lock);
read_unlock(&bond->lock);
unblock_netpoll_tx();
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
rtnl_unlock();
return count;
@@ -1313,6 +1374,7 @@ static ssize_t bonding_store_primary_reselect(struct device *d,
write_unlock_bh(&bond->curr_slave_lock);
read_unlock(&bond->lock);
unblock_netpoll_tx();
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
out:
rtnl_unlock();
return ret;
@@ -1348,9 +1410,13 @@ static ssize_t bonding_store_carrier(struct device *d,
goto out;
}
if ((new_value == 0) || (new_value == 1)) {
+ if (!rtnl_trylock())
+ return restart_syscall();
bond->params.use_carrier = new_value;
pr_info("%s: Setting use_carrier to %d.\n",
bond->dev->name, new_value);
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
+ rtnl_unlock();
} else {
pr_info("%s: Ignoring invalid use_carrier value %d.\n",
bond->dev->name, new_value);
@@ -1460,7 +1526,7 @@ static ssize_t bonding_store_active_slave(struct device *d,
write_unlock_bh(&bond->curr_slave_lock);
read_unlock(&bond->lock);
unblock_netpoll_tx();
-
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
rtnl_unlock();
return count;
@@ -1684,6 +1750,7 @@ static ssize_t bonding_store_ad_sys_priority(struct device *d,
bond->dev->name, new_value);
bond->params.sys_priority = new_value;
bond_3ad_update_sys_priority(bond);
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
out_unlock:
rtnl_unlock();
@@ -1739,6 +1806,7 @@ static ssize_t bonding_store_ad_sys_mac_addr(struct device *d,
bond->dev->name, new_mac);
memcpy(&(bond->params.sys_mac_addr), new_mac, ETH_ALEN);
bond_3ad_update_sys_mac_addr(bond);
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
out_unlock:
rtnl_unlock();
@@ -1845,8 +1913,8 @@ static ssize_t bonding_store_queue_id(struct device *d,
/* Actually set the qids for the slave */
update_slave->queue_id = qid;
-
read_unlock(&bond->lock);
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
out:
rtnl_unlock();
return ret;
@@ -1884,6 +1952,9 @@ static ssize_t bonding_store_slaves_active(struct device *d,
struct bonding *bond = to_bond(d);
struct slave *slave;
+ if (!rtnl_trylock())
+ return restart_syscall();
+
if (sscanf(buf, "%d", &new_value) != 1) {
pr_err("%s: no all_slaves_active value specified.\n",
bond->dev->name);
@@ -1913,7 +1984,9 @@ static ssize_t bonding_store_slaves_active(struct device *d,
}
}
read_unlock(&bond->lock);
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
out:
+ rtnl_unlock();
return ret;
}
static DEVICE_ATTR(all_slaves_active, S_IRUGO | S_IWUSR,
@@ -1952,9 +2025,13 @@ static ssize_t bonding_store_resend_igmp(struct device *d,
goto out;
}
+ if (!rtnl_trylock())
+ return restart_syscall();
pr_info("%s: Setting resend_igmp to %d.\n",
bond->dev->name, new_value);
bond->params.resend_igmp = new_value;
+ call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
+ rtnl_unlock();
out:
return ret;
}
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index ec21a37..7e9cae6 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1641,6 +1641,7 @@ struct pcpu_sw_netstats {
#define NETDEV_CHANGEUPPER 0x0015
#define NETDEV_RESEND_IGMP 0x0016
#define NETDEV_PRECHANGEMTU 0x0017 /* notify before mtu change happened */
+#define NETDEV_CHANGEINFODATA 0x0018
extern int register_netdevice_notifier(struct notifier_block *nb);
extern int unregister_netdevice_notifier(struct notifier_block *nb);

View File

@@ -1,412 +0,0 @@
From acd711787be585716c57b2659a4ebcd8b5abab65 Mon Sep 17 00:00:00 2001
Subject: [PATCH 2/4] cumulus: bonding: add support for RTM_GETLINK
A "spirit" backport of a subset of the upstream (net-next tree) rtnetlink
support in bonding. All upstream commit hashes are from the net-next tree.
The actual code was copied from upstream commit
4daaab4f0c2b55adccab08da06e17acc270cb84a with the relavent upstream
commits being:
3bad540 bonding: convert netlink to use slave data info api
1d3ee88 bonding: add netlink attributes to slave link dev
288db0a bonding: fix netlink msg size
4ee7ac7 bonding: add ad_info attribute netlink support
ec029fa bonding: add ad_select attribute netlink support
998e40b bonding: add lacp_rate attribute netlink support
7d10100 bonding: add min_links attribute netlink support
1cc0b1e bonding: add all_slaves_active attribute netlink support
2c9839c bonding: add num_grat_arp attribute netlink support
d8838de bonding: add resend_igmp attribute netlink support
f70161c bonding: add xmit_hash_policy attribute netlink support
8990197 bonding: add fail_over_mac attribute netlink support
8a41ae4 bonding: add primary_select attribute netlink support
0a98a0d bonding: add primary attribute netlink support
90af231 bonding: add Netlink support mode option
Removed call to rtnl_dereference in bond_fill_info because
primary_slave has not been converted to using RCU.
Signed-off-by: Jonathan Toppins <jtoppins@cumulusnetworks.com>
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 768b0de..14b8bc1 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -2015,7 +2015,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
res = bond_sysfs_slave_add(new_slave);
if (res) {
pr_debug("Error %d calling bond_sysfs_slave_add\n", res);
- goto err_detach;
+ goto err_dest_symlinks;
}
pr_info("%s: enslaving %s as a%s interface with a%s link.\n",
@@ -5036,12 +5036,259 @@ static int bond_get_tx_queues(struct net *net, struct nlattr *tb[],
return 0;
}
+static size_t bond_get_size(const struct net_device *bond_dev)
+{
+ return nla_total_size(sizeof(u8)) + /* IFLA_BOND_MODE */
+ nla_total_size(sizeof(u32)) + /* IFLA_BOND_ACTIVE_SLAVE */
+ nla_total_size(sizeof(u32)) + /* IFLA_BOND_MIIMON */
+ nla_total_size(sizeof(u32)) + /* IFLA_BOND_UPDELAY */
+ nla_total_size(sizeof(u32)) + /* IFLA_BOND_DOWNDELAY */
+ nla_total_size(sizeof(u8)) + /* IFLA_BOND_USE_CARRIER */
+ nla_total_size(sizeof(u32)) + /* IFLA_BOND_ARP_INTERVAL */
+ /* IFLA_BOND_ARP_IP_TARGET */
+ nla_total_size(sizeof(struct nlattr)) +
+ nla_total_size(sizeof(u32)) * BOND_MAX_ARP_TARGETS +
+ nla_total_size(sizeof(u32)) + /* IFLA_BOND_ARP_VALIDATE */
+ nla_total_size(sizeof(u32)) + /* IFLA_BOND_ARP_ALL_TARGETS */
+ nla_total_size(sizeof(u32)) + /* IFLA_BOND_PRIMARY */
+ nla_total_size(sizeof(u8)) + /* IFLA_BOND_PRIMARY_RESELECT */
+ nla_total_size(sizeof(u8)) + /* IFLA_BOND_FAIL_OVER_MAC */
+ nla_total_size(sizeof(u8)) + /* IFLA_BOND_XMIT_HASH_POLICY */
+ nla_total_size(sizeof(u32)) + /* IFLA_BOND_RESEND_IGMP */
+ nla_total_size(sizeof(u8)) + /* IFLA_BOND_NUM_PEER_NOTIF */
+ nla_total_size(sizeof(u8)) + /* IFLA_BOND_ALL_SLAVES_ACTIVE */
+ nla_total_size(sizeof(u32)) + /* IFLA_BOND_MIN_LINKS */
+ nla_total_size(sizeof(u32)) + /* IFLA_BOND_LP_INTERVAL */
+ nla_total_size(sizeof(u32)) + /* IFLA_BOND_PACKETS_PER_SLAVE */
+ nla_total_size(sizeof(u8)) + /* IFLA_BOND_AD_LACP_RATE */
+ nla_total_size(sizeof(u8)) + /* IFLA_BOND_AD_SELECT */
+ nla_total_size(sizeof(struct nlattr)) + /* IFLA_BOND_AD_INFO */
+ nla_total_size(sizeof(u16)) + /* IFLA_BOND_AD_INFO_AGGREGATOR */
+ nla_total_size(sizeof(u16)) + /* IFLA_BOND_AD_INFO_NUM_PORTS */
+ nla_total_size(sizeof(u16)) + /* IFLA_BOND_AD_INFO_ACTOR_KEY */
+ nla_total_size(sizeof(u16)) + /* IFLA_BOND_AD_INFO_PARTNER_KEY*/
+ nla_total_size(ETH_ALEN) + /* IFLA_BOND_AD_INFO_PARTNER_MAC*/
+ 0;
+}
+
+static struct net_device *__bond_option_active_slave_get(struct bonding *bond,
+ struct slave *slave)
+{
+ return bond_uses_primary(bond) && slave ? slave->dev : NULL;
+}
+
+static int bond_option_active_slave_get_ifindex(struct bonding *bond)
+{
+ const struct net_device *slave;
+ int ifindex;
+
+ read_lock(&bond->lock);
+ read_lock(&bond->curr_slave_lock);
+ slave = __bond_option_active_slave_get(bond, bond->curr_active_slave);
+ ifindex = slave ? slave->ifindex : 0;
+ read_unlock(&bond->curr_slave_lock);
+ read_unlock(&bond->lock);
+ return ifindex;
+}
+
+
+static int bond_fill_info(struct sk_buff *skb,
+ const struct net_device *bond_dev)
+{
+ struct bonding *bond = netdev_priv(bond_dev);
+ int ifindex, i, targets_added;
+ struct nlattr *targets;
+ struct slave *primary;
+
+ if (nla_put_u8(skb, IFLA_BOND_MODE, BOND_MODE(bond)))
+ goto nla_put_failure;
+
+ ifindex = bond_option_active_slave_get_ifindex(bond);
+ if (ifindex && nla_put_u32(skb, IFLA_BOND_ACTIVE_SLAVE, ifindex))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb, IFLA_BOND_MIIMON, bond->params.miimon))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb, IFLA_BOND_UPDELAY,
+ bond->params.updelay * bond->params.miimon))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb, IFLA_BOND_DOWNDELAY,
+ bond->params.downdelay * bond->params.miimon))
+ goto nla_put_failure;
+
+ if (nla_put_u8(skb, IFLA_BOND_USE_CARRIER, bond->params.use_carrier))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb, IFLA_BOND_ARP_INTERVAL, bond->params.arp_interval))
+ goto nla_put_failure;
+
+ targets = nla_nest_start(skb, IFLA_BOND_ARP_IP_TARGET);
+ if (!targets)
+ goto nla_put_failure;
+
+ targets_added = 0;
+ for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) {
+ if (bond->params.arp_targets[i]) {
+ nla_put_be32(skb, i, bond->params.arp_targets[i]);
+ targets_added = 1;
+ }
+ }
+
+ if (targets_added)
+ nla_nest_end(skb, targets);
+ else
+ nla_nest_cancel(skb, targets);
+
+ if (nla_put_u32(skb, IFLA_BOND_ARP_VALIDATE, bond->params.arp_validate))
+ goto nla_put_failure;
+
+ primary = bond->primary_slave;
+ if (primary &&
+ nla_put_u32(skb, IFLA_BOND_PRIMARY, primary->dev->ifindex))
+ goto nla_put_failure;
+
+ if (nla_put_u8(skb, IFLA_BOND_PRIMARY_RESELECT,
+ bond->params.primary_reselect))
+ goto nla_put_failure;
+
+ if (nla_put_u8(skb, IFLA_BOND_FAIL_OVER_MAC,
+ bond->params.fail_over_mac))
+ goto nla_put_failure;
+
+ if (nla_put_u8(skb, IFLA_BOND_XMIT_HASH_POLICY,
+ bond->params.xmit_policy))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb, IFLA_BOND_RESEND_IGMP,
+ bond->params.resend_igmp))
+ goto nla_put_failure;
+
+ if (nla_put_u8(skb, IFLA_BOND_NUM_PEER_NOTIF,
+ bond->params.num_peer_notif))
+ goto nla_put_failure;
+
+ if (nla_put_u8(skb, IFLA_BOND_ALL_SLAVES_ACTIVE,
+ bond->params.all_slaves_active))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb, IFLA_BOND_MIN_LINKS,
+ bond->params.min_links))
+ goto nla_put_failure;
+
+ if (nla_put_u8(skb, IFLA_BOND_AD_LACP_RATE,
+ bond->params.lacp_fast))
+ goto nla_put_failure;
+
+ if (nla_put_u8(skb, IFLA_BOND_AD_SELECT,
+ bond->params.ad_select))
+ goto nla_put_failure;
+
+ if (BOND_MODE(bond) == BOND_MODE_8023AD) {
+ struct ad_info info;
+
+ if (!bond_3ad_get_active_agg_info(bond, &info)) {
+ struct nlattr *nest;
+
+ nest = nla_nest_start(skb, IFLA_BOND_AD_INFO);
+ if (!nest)
+ goto nla_put_failure;
+
+ if (nla_put_u16(skb, IFLA_BOND_AD_INFO_AGGREGATOR,
+ info.aggregator_id))
+ goto nla_put_failure;
+ if (nla_put_u16(skb, IFLA_BOND_AD_INFO_NUM_PORTS,
+ info.ports))
+ goto nla_put_failure;
+ if (nla_put_u16(skb, IFLA_BOND_AD_INFO_ACTOR_KEY,
+ info.actor_key))
+ goto nla_put_failure;
+ if (nla_put_u16(skb, IFLA_BOND_AD_INFO_PARTNER_KEY,
+ info.partner_key))
+ goto nla_put_failure;
+ if (nla_put(skb, IFLA_BOND_AD_INFO_PARTNER_MAC,
+ sizeof(info.partner_system),
+ &info.partner_system))
+ goto nla_put_failure;
+
+ nla_nest_end(skb, nest);
+ }
+ }
+
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+
+static size_t bond_get_slave_size(const struct net_device *bond_dev,
+ const struct net_device *slave_dev)
+{
+ return nla_total_size(sizeof(u8)) + /* IFLA_BOND_SLAVE_STATE */
+ nla_total_size(sizeof(u8)) + /* IFLA_BOND_SLAVE_MII_STATUS */
+ nla_total_size(sizeof(u32)) + /* IFLA_BOND_SLAVE_LINK_FAILURE_COUNT */
+ nla_total_size(MAX_ADDR_LEN) + /* IFLA_BOND_SLAVE_PERM_HWADDR */
+ nla_total_size(sizeof(u16)) + /* IFLA_BOND_SLAVE_QUEUE_ID */
+ nla_total_size(sizeof(u16)) + /* IFLA_BOND_SLAVE_AD_AGGREGATOR_ID */
+ 0;
+}
+
+static int bond_fill_slave_info(struct sk_buff *skb,
+ const struct net_device *bond_dev,
+ const struct net_device *slave_dev)
+{
+ struct slave *slave = bond_slave_get_rtnl(slave_dev);
+
+ if (!slave)
+ return 0;
+
+ if (nla_put_u8(skb, IFLA_BOND_SLAVE_STATE, bond_slave_state(slave)))
+ goto nla_put_failure;
+
+ if (nla_put_u8(skb, IFLA_BOND_SLAVE_MII_STATUS, slave->link))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb, IFLA_BOND_SLAVE_LINK_FAILURE_COUNT,
+ slave->link_failure_count))
+ goto nla_put_failure;
+
+ if (nla_put(skb, IFLA_BOND_SLAVE_PERM_HWADDR,
+ slave_dev->addr_len, slave->perm_hwaddr))
+ goto nla_put_failure;
+
+ if (nla_put_u16(skb, IFLA_BOND_SLAVE_QUEUE_ID, slave->queue_id))
+ goto nla_put_failure;
+
+ if (BOND_MODE(slave->bond) == BOND_MODE_8023AD) {
+ const struct aggregator *agg;
+
+ agg = SLAVE_AD_INFO(slave).port.aggregator;
+ if (agg)
+ if (nla_put_u16(skb, IFLA_BOND_SLAVE_AD_AGGREGATOR_ID,
+ agg->aggregator_identifier))
+ goto nla_put_failure;
+ }
+
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+
+
static struct rtnl_link_ops bond_link_ops __read_mostly = {
.kind = "bond",
.priv_size = sizeof(struct bonding),
.setup = bond_setup,
+ .maxtype = IFLA_BOND_MAX,
.validate = bond_validate,
+ .get_size = bond_get_size,
+ .fill_info = bond_fill_info,
.get_tx_queues = bond_get_tx_queues,
+ .slave_maxtype = IFLA_BOND_SLAVE_MAX,
+ .get_slave_size = bond_get_slave_size,
+ .fill_slave_info= bond_fill_slave_info,
};
/* Create a new bond based on the specified name and bonding parameters.
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index e70ce3e..af93ed4 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -70,6 +70,8 @@
set_fs(fs); \
res; })
+#define BOND_MODE(bond) ((bond)->params.mode)
+
/**
* bond_for_each_slave_from - iterate the slaves list from a starting point
* @bond: the bond holding this list.
@@ -268,6 +270,9 @@ static inline bool bond_vlan_used(struct bonding *bond)
#define bond_slave_get_rcu(dev) \
((struct slave *) rcu_dereference(dev->rx_handler_data))
+#define bond_slave_get_rtnl(dev) \
+ ((struct slave *) rtnl_dereference(dev->rx_handler_data))
+
/**
* Returns NULL if the net_device does not belong to any of the bond's slaves
*
@@ -303,6 +308,17 @@ static inline bool bond_is_lb(const struct bonding *bond)
bond->params.mode == BOND_MODE_ALB);
}
+static inline bool bond_mode_uses_primary(int mode)
+{
+ return mode == BOND_MODE_ACTIVEBACKUP || mode == BOND_MODE_TLB ||
+ mode == BOND_MODE_ALB;
+}
+
+static inline bool bond_uses_primary(struct bonding *bond)
+{
+ return bond_mode_uses_primary(BOND_MODE(bond));
+}
+
void bond_set_active_slave(struct slave *slave);
void bond_set_backup_slave(struct slave *slave);
diff --git a/include/linux/if_link.h b/include/linux/if_link.h
index bb5739c..158b989 100644
--- a/include/linux/if_link.h
+++ b/include/linux/if_link.h
@@ -325,6 +325,63 @@ enum {
VXLAN_REPLICATION_SELF, /* self or head-end replication */
};
+/* Bonding section */
+
+enum {
+ IFLA_BOND_UNSPEC,
+ IFLA_BOND_MODE,
+ IFLA_BOND_ACTIVE_SLAVE,
+ IFLA_BOND_MIIMON,
+ IFLA_BOND_UPDELAY,
+ IFLA_BOND_DOWNDELAY,
+ IFLA_BOND_USE_CARRIER,
+ IFLA_BOND_ARP_INTERVAL,
+ IFLA_BOND_ARP_IP_TARGET,
+ IFLA_BOND_ARP_VALIDATE,
+ IFLA_BOND_ARP_ALL_TARGETS,
+ IFLA_BOND_PRIMARY,
+ IFLA_BOND_PRIMARY_RESELECT,
+ IFLA_BOND_FAIL_OVER_MAC,
+ IFLA_BOND_XMIT_HASH_POLICY,
+ IFLA_BOND_RESEND_IGMP,
+ IFLA_BOND_NUM_PEER_NOTIF,
+ IFLA_BOND_ALL_SLAVES_ACTIVE,
+ IFLA_BOND_MIN_LINKS,
+ IFLA_BOND_LP_INTERVAL,
+ IFLA_BOND_PACKETS_PER_SLAVE,
+ IFLA_BOND_AD_LACP_RATE,
+ IFLA_BOND_AD_SELECT,
+ IFLA_BOND_AD_INFO,
+ __IFLA_BOND_MAX,
+};
+
+#define IFLA_BOND_MAX (__IFLA_BOND_MAX - 1)
+
+enum {
+ IFLA_BOND_AD_INFO_UNSPEC,
+ IFLA_BOND_AD_INFO_AGGREGATOR,
+ IFLA_BOND_AD_INFO_NUM_PORTS,
+ IFLA_BOND_AD_INFO_ACTOR_KEY,
+ IFLA_BOND_AD_INFO_PARTNER_KEY,
+ IFLA_BOND_AD_INFO_PARTNER_MAC,
+ __IFLA_BOND_AD_INFO_MAX,
+};
+
+#define IFLA_BOND_AD_INFO_MAX (__IFLA_BOND_AD_INFO_MAX - 1)
+
+enum {
+ IFLA_BOND_SLAVE_UNSPEC,
+ IFLA_BOND_SLAVE_STATE,
+ IFLA_BOND_SLAVE_MII_STATUS,
+ IFLA_BOND_SLAVE_LINK_FAILURE_COUNT,
+ IFLA_BOND_SLAVE_PERM_HWADDR,
+ IFLA_BOND_SLAVE_QUEUE_ID,
+ IFLA_BOND_SLAVE_AD_AGGREGATOR_ID,
+ __IFLA_BOND_SLAVE_MAX,
+};
+
+#define IFLA_BOND_SLAVE_MAX (__IFLA_BOND_SLAVE_MAX - 1)
+
/* SR-IOV virtual function management section */
enum {

View File

@@ -1,17 +0,0 @@
During bond_open(), the bonding driver always set the slave active flag to
true if the bond is not in active/backup mode. Bonding driver should let
the aggregator selection logic set the active flag.
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 3891c08..536d298 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -3648,7 +3648,7 @@ static int bond_open(struct net_device *bond_dev)
if ((bond->params.mode == BOND_MODE_ACTIVEBACKUP)
&& (slave != bond->curr_active_slave)) {
bond_set_slave_inactive_flags(slave);
- } else {
+ } else if (bond->params.mode != BOND_MODE_8023AD) {
bond_set_slave_active_flags(slave);
}
}

View File

@@ -1,177 +0,0 @@
backport of the following upstream commit with changes to deal with
locking differences in 3.2:
commit 5f0c5f73e5efaee2928c4cabcf48b03f6ba99fc8
Author: Andy Gospodarek <gospo@cumulusnetworks.com>
Date: Sun Sep 28 22:34:37 2014 -0400
bonding: make global bonding stats more reliable
As the code stands today, bonding stats are based simply on the stats
from the member interfaces. If a member was to be removed from a bond,
the stats would instantly drop. This would be confusing to an admin
would would suddonly see interface stats drop while traffic is still
flowing.
In addition to preventing the stats drops mentioned above, new members
will now be added to the bond and only traffic received after the member
was added to the bond will be counted as part of bonding stats. Bonding
counters will also be updated when any slaves are dropped to make sure
the reported stats are reliable.
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index c75b3ef..768b0de 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -254,6 +254,10 @@ struct bond_parm_tbl ad_select_tbl[] = {
static int bond_init(struct net_device *bond_dev);
static void bond_uninit(struct net_device *bond_dev);
+static struct rtnl_link_stats64 *bond_get_stats(struct net_device *bond_dev,
+ struct rtnl_link_stats64 *stats);
+static struct rtnl_link_stats64 *__bond_get_stats(struct net_device *bond_dev,
+ struct rtnl_link_stats64 *stats);
/*---------------------------- General routines -----------------------------*/
@@ -1816,6 +1820,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
new_slave->bond = bond;
new_slave->dev = slave_dev;
slave_dev->priv_flags |= IFF_BONDING;
+ /* initialize slave stats */
+ dev_get_stats(new_slave->dev, &new_slave->slave_stats);
if (bond_is_lb(bond)) {
/* bond_alb_init_slave() must be called before all other stages since
@@ -2135,6 +2141,9 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
/* release the slave from its bond */
bond_sysfs_slave_del(slave);
+ /* recompute stats just before removing the slave */
+ __bond_get_stats(bond->dev, &bond->bond_stats);
+
bond_detach_slave(bond, slave);
if (bond->primary_slave == slave)
@@ -3701,46 +3710,61 @@ static struct rtnl_link_stats64 *bond_get_stats(struct net_device *bond_dev,
struct rtnl_link_stats64 *stats)
{
struct bonding *bond = netdev_priv(bond_dev);
- struct rtnl_link_stats64 temp;
- struct slave *slave;
- int i;
-
- memset(stats, 0, sizeof(*stats));
read_lock_bh(&bond->lock);
- bond_for_each_slave(bond, slave, i) {
- const struct rtnl_link_stats64 *sstats =
- dev_get_stats(slave->dev, &temp);
+ stats = __bond_get_stats(bond_dev, stats);
- stats->rx_packets += sstats->rx_packets;
- stats->rx_bytes += sstats->rx_bytes;
- stats->rx_errors += sstats->rx_errors;
- stats->rx_dropped += sstats->rx_dropped;
-
- stats->tx_packets += sstats->tx_packets;
- stats->tx_bytes += sstats->tx_bytes;
- stats->tx_errors += sstats->tx_errors;
- stats->tx_dropped += sstats->tx_dropped;
+ read_unlock_bh(&bond->lock);
- stats->multicast += sstats->multicast;
- stats->collisions += sstats->collisions;
+ return stats;
+}
- stats->rx_length_errors += sstats->rx_length_errors;
- stats->rx_over_errors += sstats->rx_over_errors;
- stats->rx_crc_errors += sstats->rx_crc_errors;
- stats->rx_frame_errors += sstats->rx_frame_errors;
- stats->rx_fifo_errors += sstats->rx_fifo_errors;
- stats->rx_missed_errors += sstats->rx_missed_errors;
+static struct rtnl_link_stats64 *__bond_get_stats(struct net_device *bond_dev,
+ struct rtnl_link_stats64 *stats)
+{
+ struct bonding *bond = netdev_priv(bond_dev);
+ struct rtnl_link_stats64 temp;
+ struct slave *slave;
+ int i;
- stats->tx_aborted_errors += sstats->tx_aborted_errors;
- stats->tx_carrier_errors += sstats->tx_carrier_errors;
- stats->tx_fifo_errors += sstats->tx_fifo_errors;
- stats->tx_heartbeat_errors += sstats->tx_heartbeat_errors;
- stats->tx_window_errors += sstats->tx_window_errors;
- }
+ memcpy(stats, &bond->bond_stats, sizeof(*stats));
- read_unlock_bh(&bond->lock);
+ bond_for_each_slave(bond, slave, i) {
+ const struct rtnl_link_stats64 *sstats =
+ dev_get_stats(slave->dev, &temp);
+ struct rtnl_link_stats64 *pstats = &slave->slave_stats;
+
+ stats->rx_packets += sstats->rx_packets - pstats->rx_packets;
+ stats->rx_bytes += sstats->rx_bytes - pstats->rx_bytes;
+ stats->rx_errors += sstats->rx_errors - pstats->rx_errors;
+ stats->rx_dropped += sstats->rx_dropped - pstats->rx_dropped;
+
+ stats->tx_packets += sstats->tx_packets - pstats->tx_packets;
+ stats->tx_bytes += sstats->tx_bytes - pstats->tx_bytes;
+ stats->tx_errors += sstats->tx_errors - pstats->tx_errors;
+ stats->tx_dropped += sstats->tx_dropped - pstats->tx_dropped;
+
+ stats->multicast += sstats->multicast - pstats->multicast;
+ stats->collisions += sstats->collisions - pstats->collisions;
+
+ stats->rx_length_errors += sstats->rx_length_errors - pstats->rx_length_errors;
+ stats->rx_over_errors += sstats->rx_over_errors - pstats->rx_over_errors;
+ stats->rx_crc_errors += sstats->rx_crc_errors - pstats->rx_crc_errors;
+ stats->rx_frame_errors += sstats->rx_frame_errors - pstats->rx_frame_errors;
+ stats->rx_fifo_errors += sstats->rx_fifo_errors - pstats->rx_fifo_errors;
+ stats->rx_missed_errors += sstats->rx_missed_errors - pstats->rx_missed_errors;
+
+ stats->tx_aborted_errors += sstats->tx_aborted_errors - pstats->tx_aborted_errors;
+ stats->tx_carrier_errors += sstats->tx_carrier_errors - pstats->tx_carrier_errors;
+ stats->tx_fifo_errors += sstats->tx_fifo_errors - pstats->tx_fifo_errors;
+ stats->tx_heartbeat_errors += sstats->tx_heartbeat_errors - pstats->tx_heartbeat_errors;
+ stats->tx_window_errors += sstats->tx_window_errors - pstats->tx_window_errors;
+
+ /* save off the slave stats for the next run */
+ memcpy(pstats, sstats, sizeof(*sstats));
+ }
+ memcpy(&bond->bond_stats, stats, sizeof(*stats));
return stats;
}
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index 382947a..e70ce3e 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -21,6 +21,7 @@
#include <linux/cpumask.h>
#include <linux/in6.h>
#include <linux/netpoll.h>
+#include <linux/if_link.h>
#include "bond_3ad.h"
#include "bond_alb.h"
@@ -201,6 +202,7 @@ struct slave {
struct netpoll *np;
#endif
struct kobject kobj;
+ struct rtnl_link_stats64 slave_stats;
};
/*
@@ -255,6 +257,7 @@ struct bonding {
/* debugging suport via debugfs */
struct dentry *debug_dir;
#endif /* CONFIG_DEBUG_FS */
+ struct rtnl_link_stats64 bond_stats;
};
static inline bool bond_vlan_used(struct bonding *bond)

View File

@@ -1,56 +0,0 @@
CM-1191: keep bond interface carrier off until bond has atleast one active member.
LACP will still do it's job while bond is carrier off, and if bond members become
active down the road, bond carrier will be turned back on, signaling higher-
level protocols that bond is viable.
This patch is rushed for CL launch release and probably needs to be revisited
for better integration into the LACP state machines implemented in the bonding
driver. For now, the patch uses number of active slaves to compare against
min_links to know when to on/off bond carrier. Suggested setting of min_links
is 1, rather than the default of zero. Using min_links=1 says that atleast 1
slave must be active within bond for bond to be carrier on.
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index c7e3e53..082a399 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -224,6 +224,7 @@ static inline int __agg_has_partner(struct aggregator *agg)
static inline void __disable_port(struct port *port)
{
bond_set_slave_inactive_flags(port->slave);
+ bond_3ad_set_carrier(port->slave->bond);
}
/**
@@ -235,8 +236,10 @@ static inline void __enable_port(struct port *port)
{
struct slave *slave = port->slave;
- if ((slave->link == BOND_LINK_UP) && IS_UP(slave->dev))
+ if ((slave->link == BOND_LINK_UP) && IS_UP(slave->dev)) {
bond_set_slave_active_flags(slave);
+ bond_3ad_set_carrier(slave->bond);
+ }
}
/**
@@ -2331,11 +2334,17 @@ void bond_3ad_handle_link_change(struct slave *slave, char link)
int bond_3ad_set_carrier(struct bonding *bond)
{
struct aggregator *active;
+ struct slave *slave;
+ int active_slaves = 0, i;
+
+ bond_for_each_slave(bond, slave, i)
+ if (bond_is_active_slave(slave))
+ active_slaves++;
active = __get_active_agg(&(SLAVE_AD_INFO(bond->first_slave).aggregator));
- if (active) {
+ if (active && __agg_has_partner(active)) {
/* are enough slaves available to consider link up? */
- if (active->num_of_ports < bond->params.min_links) {
+ if (active_slaves < bond->params.min_links) {
if (netif_carrier_ok(bond->dev)) {
netif_carrier_off(bond->dev);
return 1;

View File

@@ -1,39 +0,0 @@
Backport of upstream patch:
commit b59340c2c0508d280f10658ad662fa56a39c74c2
Author: nikolay@redhat.com <nikolay@redhat.com>
Date: Mon Feb 18 07:59:02 2013 +0000
bonding: Fix race condition between bond_enslave() and bond_3ad_update_lacp_rate()
for bond_3ad_update_lacp_rate and for similar functions we have added
that are not upstream.
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index c5ab398..25edb23 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -2730,11 +2730,13 @@ void bond_3ad_update_lacp_rate(struct bonding *bond)
struct port *port = NULL;
int lacp_fast;
- read_lock(&bond->lock);
+ write_lock_bh(&bond->lock);
lacp_fast = bond->params.lacp_fast;
bond_for_each_slave(bond, slave, i) {
port = &(SLAVE_AD_INFO(slave).port);
+ if (port->slave == NULL)
+ continue;
__get_state_machine_lock(port);
if (lacp_fast)
port->actor_oper_port_state |= AD_STATE_LACP_TIMEOUT;
@@ -2743,7 +2745,7 @@ void bond_3ad_update_lacp_rate(struct bonding *bond)
__release_state_machine_lock(port);
}
- read_unlock(&bond->lock);
+ write_unlock_bh(&bond->lock);
}
/*

View File

@@ -1,57 +0,0 @@
The main change here is to move the call to bond_sysfs_slave_del() in
bond_release() outside an area that is under a bh-lock since
bond_sysfs_slave_del() may sleep trying to take a mutex. The code in
bond_release() changed pretty dramatically between 3.2 and when the
upstream commit that added the sysfs slave info was added, so it was
easy to miss the fact that this call needed to be outside a bh-lock. I
also added back an error cleanup in bond_enslave() that was missed in
the first patch. We have probably never hit this, but it should be
there.
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index d84b0b7..bc7b125 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -2020,7 +2020,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
res = bond_sysfs_slave_add(new_slave);
if (res) {
pr_debug("Error %d calling bond_sysfs_slave_add\n", res);
- goto err_dest_symlinks;
+ goto err_unregister;
}
pr_info("%s: enslaving %s as a%s interface with a%s link.\n",
@@ -2032,6 +2032,9 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
return 0;
/* Undo stages on error */
+err_unregister:
+ netdev_rx_handler_unregister(slave_dev);
+
err_dest_symlinks:
bond_destroy_slave_symlinks(bond_dev, slave_dev);
@@ -2112,10 +2115,14 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
}
write_unlock_bh(&bond->lock);
+
+ bond_sysfs_slave_del(slave);
+
/* unregister rx_handler early so bond_handle_frame wouldn't be called
* for this slave anymore.
*/
netdev_rx_handler_unregister(slave_dev);
+
write_lock_bh(&bond->lock);
if (!bond->params.fail_over_mac) {
@@ -2144,8 +2151,6 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
bond->current_arp_slave = NULL;
- /* release the slave from its bond */
- bond_sysfs_slave_del(slave);
/* recompute stats just before removing the slave */
__bond_get_stats(bond->dev, &bond->bond_stats);

Some files were not shown because too many files have changed in this diff Show More