diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-11-16 09:10:59 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-11-16 09:10:59 -0800 |
commit | 2bf16b7a73caf3435f782e4170cfe563675e10f9 (patch) | |
tree | 7f4c5b28a02f08c4d6fd69dd43db5872b07c20c4 | |
parent | b9743042b3d31fed271ae19aee79dd86817904f0 (diff) | |
parent | f13d1a8a801dae552ef495c84a223280586a9f67 (diff) |
Merge tag 'char-misc-4.15-rc1' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char/misc updates from Greg KH:
"Here is the big set of char/misc and other driver subsystem patches
for 4.15-rc1.
There are small changes all over here, hyperv driver updates, pcmcia
driver updates, w1 driver updats, vme driver updates, nvmem driver
updates, and lots of other little one-off driver updates as well. The
shortlog has the full details.
All of these have been in linux-next for quite a while with no
reported issues"
* tag 'char-misc-4.15-rc1' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (90 commits)
VME: Return -EBUSY when DMA list in use
w1: keep balance of mutex locks and refcnts
MAINTAINERS: Update VME subsystem tree.
nvmem: sunxi-sid: add support for A64/H5's SID controller
nvmem: imx-ocotp: Update module description
nvmem: imx-ocotp: Enable i.MX7D OTP write support
nvmem: imx-ocotp: Add i.MX7D timing write clock setup support
nvmem: imx-ocotp: Move i.MX6 write clock setup to dedicated function
nvmem: imx-ocotp: Add support for banked OTP addressing
nvmem: imx-ocotp: Pass parameters via a struct
nvmem: imx-ocotp: Restrict OTP write to IMX6 processors
nvmem: uniphier: add UniPhier eFuse driver
dt-bindings: nvmem: add description for UniPhier eFuse
nvmem: set nvmem->owner to nvmem->dev->driver->owner if unset
nvmem: qfprom: fix different address space warnings of sparse
nvmem: mtk-efuse: fix different address space warnings of sparse
nvmem: mtk-efuse: use stack for nvmem_config instead of malloc'ing it
nvmem: imx-iim: use stack for nvmem_config instead of malloc'ing it
thunderbolt: tb: fix use after free in tb_activate_pcie_devices
MAINTAINERS: Add git tree for Thunderbolt development
...
79 files changed, 2770 insertions, 517 deletions
diff --git a/Documentation/ABI/stable/sysfs-bus-vmbus b/Documentation/ABI/stable/sysfs-bus-vmbus index 5d0125f7bcaf..d4077cc60d55 100644 --- a/Documentation/ABI/stable/sysfs-bus-vmbus +++ b/Documentation/ABI/stable/sysfs-bus-vmbus @@ -41,3 +41,73 @@ KernelVersion: 4.5 Contact: K. Y. Srinivasan <kys@microsoft.com> Description: The 16 bit vendor ID of the device Users: tools/hv/lsvmbus and user level RDMA libraries + +What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/cpu +Date: September. 2017 +KernelVersion: 4.14 +Contact: Stephen Hemminger <sthemmin@microsoft.com> +Description: VCPU (sub)channel is affinitized to +Users: tools/hv/lsvmbus and other debuggig tools + +What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/cpu +Date: September. 2017 +KernelVersion: 4.14 +Contact: Stephen Hemminger <sthemmin@microsoft.com> +Description: VCPU (sub)channel is affinitized to +Users: tools/hv/lsvmbus and other debuggig tools + +What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/in_mask +Date: September. 2017 +KernelVersion: 4.14 +Contact: Stephen Hemminger <sthemmin@microsoft.com> +Description: Inbound channel signaling state +Users: Debugging tools + +What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/latency +Date: September. 2017 +KernelVersion: 4.14 +Contact: Stephen Hemminger <sthemmin@microsoft.com> +Description: Channel signaling latency +Users: Debugging tools + +What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/out_mask +Date: September. 2017 +KernelVersion: 4.14 +Contact: Stephen Hemminger <sthemmin@microsoft.com> +Description: Outbound channel signaling state +Users: Debugging tools + +What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/pending +Date: September. 2017 +KernelVersion: 4.14 +Contact: Stephen Hemminger <sthemmin@microsoft.com> +Description: Channel interrupt pending state +Users: Debugging tools + +What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/read_avail +Date: September. 2017 +KernelVersion: 4.14 +Contact: Stephen Hemminger <sthemmin@microsoft.com> +Description: Bytes availabble to read +Users: Debugging tools + +What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/write_avail +Date: September. 2017 +KernelVersion: 4.14 +Contact: Stephen Hemminger <sthemmin@microsoft.com> +Description: Bytes availabble to write +Users: Debugging tools + +What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/events +Date: September. 2017 +KernelVersion: 4.14 +Contact: Stephen Hemminger <sthemmin@microsoft.com> +Description: Number of times we have signaled the host +Users: Debugging tools + +What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/interrupts +Date: September. 2017 +KernelVersion: 4.14 +Contact: Stephen Hemminger <sthemmin@microsoft.com> +Description: Number of times we have taken an interrupt (incoming) +Users: Debugging tools diff --git a/Documentation/ABI/testing/sysfs-driver-w1_ds28e17 b/Documentation/ABI/testing/sysfs-driver-w1_ds28e17 new file mode 100644 index 000000000000..d301e7017afe --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-w1_ds28e17 @@ -0,0 +1,21 @@ +What: /sys/bus/w1/devices/19-<id>/speed +Date: Sep 2017 +KernelVersion: 4.14 +Contact: Jan Kandziora <jjj@gmx.de> +Description: When written, this file sets the I2C speed on the connected + DS28E17 chip. When read, it reads the current setting from + the DS28E17 chip. + Valid values: 100, 400, 900 [kBaud]. + Default 100, can be set by w1_ds28e17.speed= module parameter. +Users: w1_ds28e17 driver + +What: /sys/bus/w1/devices/19-<id>/stretch +Date: Sep 2017 +KernelVersion: 4.14 +Contact: Jan Kandziora <jjj@gmx.de> +Description: When written, this file sets the multiplier used to calculate + the busy timeout for I2C operations on the connected DS28E17 + chip. When read, returns the current setting. + Valid values: 1 to 9. + Default 1, can be set by w1_ds28e17.stretch= module parameter. +Users: w1_ds28e17 driver diff --git a/Documentation/devicetree/bindings/nvmem/allwinner,sunxi-sid.txt b/Documentation/devicetree/bindings/nvmem/allwinner,sunxi-sid.txt index 081c49c0dac0..d69543701d5d 100644 --- a/Documentation/devicetree/bindings/nvmem/allwinner,sunxi-sid.txt +++ b/Documentation/devicetree/bindings/nvmem/allwinner,sunxi-sid.txt @@ -5,6 +5,7 @@ Required properties: "allwinner,sun4i-a10-sid" "allwinner,sun7i-a20-sid" "allwinner,sun8i-h3-sid" + "allwinner,sun50i-a64-sid" - reg: Should contain registers location and length diff --git a/Documentation/devicetree/bindings/nvmem/amlogic-efuse.txt b/Documentation/devicetree/bindings/nvmem/amlogic-efuse.txt index fafd85bd67a6..e3298e18de26 100644 --- a/Documentation/devicetree/bindings/nvmem/amlogic-efuse.txt +++ b/Documentation/devicetree/bindings/nvmem/amlogic-efuse.txt @@ -1,4 +1,4 @@ -= Amlogic eFuse device tree bindings = += Amlogic Meson GX eFuse device tree bindings = Required properties: - compatible: should be "amlogic,meson-gxbb-efuse" diff --git a/Documentation/devicetree/bindings/nvmem/amlogic-meson-mx-efuse.txt b/Documentation/devicetree/bindings/nvmem/amlogic-meson-mx-efuse.txt new file mode 100644 index 000000000000..a3c63954a1a4 --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/amlogic-meson-mx-efuse.txt @@ -0,0 +1,22 @@ +Amlogic Meson6/Meson8/Meson8b efuse + +Required Properties: +- compatible: depending on the SoC this should be one of: + - "amlogic,meson6-efuse" + - "amlogic,meson8-efuse" + - "amlogic,meson8b-efuse" +- reg: base address and size of the efuse registers +- clocks: a reference to the efuse core gate clock +- clock-names: must be "core" + +All properties and sub-nodes as well as the consumer bindings +defined in nvmem.txt in this directory are also supported. + + +Example: + efuse: nvmem@0 { + compatible = "amlogic,meson8-efuse"; + reg = <0x0 0x2000>; + clocks = <&clkc CLKID_EFUSE>; + clock-names = "core"; + }; diff --git a/Documentation/devicetree/bindings/nvmem/rockchip-efuse.txt b/Documentation/devicetree/bindings/nvmem/rockchip-efuse.txt index 1ff02afdc55a..60bec4782806 100644 --- a/Documentation/devicetree/bindings/nvmem/rockchip-efuse.txt +++ b/Documentation/devicetree/bindings/nvmem/rockchip-efuse.txt @@ -6,6 +6,7 @@ Required properties: - "rockchip,rk3188-efuse" - for RK3188 SoCs. - "rockchip,rk3228-efuse" - for RK3228 SoCs. - "rockchip,rk3288-efuse" - for RK3288 SoCs. + - "rockchip,rk3368-efuse" - for RK3368 SoCs. - "rockchip,rk3399-efuse" - for RK3399 SoCs. - reg: Should contain the registers location and exact eFuse size - clocks: Should be the clock id of eFuse diff --git a/Documentation/devicetree/bindings/nvmem/snvs-lpgpr.txt b/Documentation/devicetree/bindings/nvmem/snvs-lpgpr.txt new file mode 100644 index 000000000000..20bc49b49799 --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/snvs-lpgpr.txt @@ -0,0 +1,20 @@ +Device tree bindings for Low Power General Purpose Register found in i.MX6Q/D +Secure Non-Volatile Storage. + +This DT node should be represented as a sub-node of a "syscon", +"simple-mfd" node. + +Required properties: +- compatible: should be one of the fallowing variants: + "fsl,imx6q-snvs-lpgpr" for Freescale i.MX6Q/D/DL/S + "fsl,imx6ul-snvs-lpgpr" for Freescale i.MX6UL + +Example: +snvs: snvs@020cc000 { + compatible = "fsl,sec-v4.0-mon", "syscon", "simple-mfd"; + reg = <0x020cc000 0x4000>; + + snvs_lpgpr: snvs-lpgpr { + compatible = "fsl,imx6q-snvs-lpgpr"; + }; +}; diff --git a/Documentation/devicetree/bindings/nvmem/uniphier-efuse.txt b/Documentation/devicetree/bindings/nvmem/uniphier-efuse.txt new file mode 100644 index 000000000000..eccf490d5a6d --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/uniphier-efuse.txt @@ -0,0 +1,49 @@ += UniPhier eFuse device tree bindings = + +This UniPhier eFuse must be under soc-glue. + +Required properties: +- compatible: should be "socionext,uniphier-efuse" +- reg: should contain the register location and length + += Data cells = +Are child nodes of efuse, bindings of which as described in +bindings/nvmem/nvmem.txt + +Example: + + soc-glue@5f900000 { + compatible = "socionext,uniphier-ld20-soc-glue-debug", + "simple-mfd"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x5f900000 0x2000>; + + efuse@100 { + compatible = "socionext,uniphier-efuse"; + reg = <0x100 0x28>; + }; + + efuse@200 { + compatible = "socionext,uniphier-efuse"; + reg = <0x200 0x68>; + #address-cells = <1>; + #size-cells = <1>; + + /* Data cells */ + usb_mon: usb-mon@54 { + reg = <0x54 0xc>; + }; + }; + }; + += Data consumers = +Are device nodes which consume nvmem data cells. + +Example: + + usb { + ... + nvmem-cells = <&usb_mon>; + nvmem-cell-names = "usb_mon"; + } diff --git a/Documentation/trace/coresight-cpu-debug.txt b/Documentation/trace/coresight-cpu-debug.txt index b3da1f90b861..2b9b51cd501e 100644 --- a/Documentation/trace/coresight-cpu-debug.txt +++ b/Documentation/trace/coresight-cpu-debug.txt @@ -149,11 +149,23 @@ If you want to limit idle states at boot time, you can use "nohlt" or At the runtime you can disable idle states with below methods: -Set latency request to /dev/cpu_dma_latency to disable all CPUs specific idle -states (if latency = 0uS then disable all idle states): -# echo "what_ever_latency_you_need_in_uS" > /dev/cpu_dma_latency - -Disable specific CPU's specific idle state: +It is possible to disable CPU idle states by way of the PM QoS +subsystem, more specifically by using the "/dev/cpu_dma_latency" +interface (see Documentation/power/pm_qos_interface.txt for more +details). As specified in the PM QoS documentation the requested +parameter will stay in effect until the file descriptor is released. +For example: + +# exec 3<> /dev/cpu_dma_latency; echo 0 >&3 +... +Do some work... +... +# exec 3<>- + +The same can also be done from an application program. + +Disable specific CPU's specific idle state from cpuidle sysfs (see +Documentation/cpuidle/sysfs.txt): # echo 1 > /sys/devices/system/cpu/cpu$cpu/cpuidle/state$state/disable diff --git a/Documentation/w1/slaves/00-INDEX b/Documentation/w1/slaves/00-INDEX index 8d76718e1ea2..68946f83e579 100644 --- a/Documentation/w1/slaves/00-INDEX +++ b/Documentation/w1/slaves/00-INDEX @@ -10,3 +10,5 @@ w1_ds2438 - The Maxim/Dallas Semiconductor ds2438 smart battery monitor. w1_ds28e04 - The Maxim/Dallas Semiconductor ds28e04 eeprom. +w1_ds28e17 + - The Maxim/Dallas Semiconductor ds28e17 1-Wire-to-I2C Master Bridge. diff --git a/Documentation/w1/slaves/w1_ds28e17 b/Documentation/w1/slaves/w1_ds28e17 new file mode 100644 index 000000000000..7fcfad5b4a37 --- /dev/null +++ b/Documentation/w1/slaves/w1_ds28e17 @@ -0,0 +1,68 @@ +Kernel driver w1_ds28e17 +======================== + +Supported chips: + * Maxim DS28E17 1-Wire-to-I2C Master Bridge + +supported family codes: + W1_FAMILY_DS28E17 0x19 + +Author: Jan Kandziora <jjj@gmx.de> + + +Description +----------- +The DS28E17 is a Onewire slave device which acts as an I2C bus master. + +This driver creates a new I2C bus for any DS28E17 device detected. I2C buses +come and go as the DS28E17 devices come and go. I2C slave devices connected to +a DS28E17 can be accessed by the kernel or userspace tools as if they were +connected to a "native" I2C bus master. + + +An udev rule like the following +------------------------------------------------------------------------------- +SUBSYSTEM=="i2c-dev", KERNEL=="i2c-[0-9]*", ATTRS{name}=="w1-19-*", \ + SYMLINK+="i2c-$attr{name}" +------------------------------------------------------------------------------- +may be used to create stable /dev/i2c- entries based on the unique id of the +DS28E17 chip. + + +Driver parameters are: + +speed: + This sets up the default I2C speed a DS28E17 get configured for as soon + it is connected. The power-on default of the DS28E17 is 400kBaud, but + chips may come and go on the Onewire bus without being de-powered and + as soon the "w1_ds28e17" driver notices a freshly connected, or + reconnected DS28E17 device on the Onewire bus, it will re-apply this + setting. + + Valid values are 100, 400, 900 [kBaud]. Any other value means to leave + alone the current DS28E17 setting on detect. The default value is 100. + +stretch: + This sets up the default stretch value used for freshly connected + DS28E17 devices. It is a multiplier used on the calculation of the busy + wait time for an I2C transfer. This is to account for I2C slave devices + which make heavy use of the I2C clock stretching feature and thus, the + needed timeout cannot be pre-calculated correctly. As the w1_ds28e17 + driver checks the DS28E17's busy flag in a loop after the precalculated + wait time, it should be hardly needed to tweak this setting. + + Leave it at 1 unless you get ETIMEDOUT errors and a "w1_slave_driver + 19-00000002dbd8: busy timeout" in the kernel log. + + Valid values are 1 to 9. The default is 1. + + +The driver creates sysfs files /sys/bus/w1/devices/19-<id>/speed and +/sys/bus/w1/devices/19-<id>/stretch for each device, preloaded with the default +settings from the driver parameters. They may be changed anytime. In addition a +directory /sys/bus/w1/devices/19-<id>/i2c-<nnn> for the I2C bus master sysfs +structure is created. + + +See https://github.com/ianka/w1_ds28e17 for even more information. + diff --git a/MAINTAINERS b/MAINTAINERS index d32726746a88..7f9c4f3fc941 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5474,7 +5474,7 @@ K: fmc_d.*register FPGA MANAGER FRAMEWORK M: Alan Tull <atull@kernel.org> -R: Moritz Fischer <mdf@kernel.org> +M: Moritz Fischer <mdf@kernel.org> L: linux-fpga@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/atull/linux-fpga.git @@ -13390,6 +13390,7 @@ M: Andreas Noever <andreas.noever@gmail.com> M: Michael Jamet <michael.jamet@intel.com> M: Mika Westerberg <mika.westerberg@linux.intel.com> M: Yehezkel Bernat <yehezkel.bernat@intel.com> +T: git git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt.git S: Maintained F: drivers/thunderbolt/ F: include/linux/thunderbolt.h @@ -14485,7 +14486,7 @@ M: Manohar Vanga <manohar.vanga@gmail.com> M: Greg Kroah-Hartman <gregkh@linuxfoundation.org> L: devel@driverdev.osuosl.org S: Maintained -T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git F: Documentation/driver-api/vme.rst F: drivers/staging/vme/ F: drivers/vme/ diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index a0b86cf486e0..189a398290db 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c @@ -210,9 +210,10 @@ void hyperv_cleanup(void) } EXPORT_SYMBOL_GPL(hyperv_cleanup); -void hyperv_report_panic(struct pt_regs *regs) +void hyperv_report_panic(struct pt_regs *regs, long err) { static bool panic_reported; + u64 guest_id; /* * We prefer to report panic on 'die' chain as we have proper @@ -223,11 +224,13 @@ void hyperv_report_panic(struct pt_regs *regs) return; panic_reported = true; - wrmsrl(HV_X64_MSR_CRASH_P0, regs->ip); - wrmsrl(HV_X64_MSR_CRASH_P1, regs->ax); - wrmsrl(HV_X64_MSR_CRASH_P2, regs->bx); - wrmsrl(HV_X64_MSR_CRASH_P3, regs->cx); - wrmsrl(HV_X64_MSR_CRASH_P4, regs->dx); + rdmsrl(HV_X64_MSR_GUEST_OS_ID, guest_id); + + wrmsrl(HV_X64_MSR_CRASH_P0, err); + wrmsrl(HV_X64_MSR_CRASH_P1, guest_id); + wrmsrl(HV_X64_MSR_CRASH_P2, regs->ip); + wrmsrl(HV_X64_MSR_CRASH_P3, regs->ax); + wrmsrl(HV_X64_MSR_CRASH_P4, regs->sp); /* * Let Hyper-V know there is crash data available diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index 581bb54dd464..5400add2885b 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -311,7 +311,7 @@ static inline int hv_cpu_number_to_vp_number(int cpu_number) void hyperv_init(void); void hyperv_setup_mmu_ops(void); void hyper_alloc_mmu(void); -void hyperv_report_panic(struct pt_regs *regs); +void hyperv_report_panic(struct pt_regs *regs, long err); bool hv_is_hypercall_page_setup(void); void hyperv_cleanup(void); #else /* CONFIG_HYPERV */ diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 7255f94ded30..a73596a4f804 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -2192,7 +2192,7 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, off_start, offp - off_start); if (!parent) { - pr_err("transaction release %d bad parent offset", + pr_err("transaction release %d bad parent offset\n", debug_id); continue; } diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index c2819a3d58a6..6f6f745605af 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -186,12 +186,12 @@ struct binder_buffer *binder_alloc_prepare_to_free(struct binder_alloc *alloc, } static int binder_update_page_range(struct binder_alloc *alloc, int allocate, - void *start, void *end, - struct vm_area_struct *vma) + void *start, void *end) { void *page_addr; unsigned long user_page_addr; struct binder_lru_page *page; + struct vm_area_struct *vma = NULL; struct mm_struct *mm = NULL; bool need_mm = false; @@ -215,7 +215,7 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate, } } - if (!vma && need_mm && mmget_not_zero(alloc->vma_vm_mm)) + if (need_mm && mmget_not_zero(alloc->vma_vm_mm)) mm = alloc->vma_vm_mm; if (mm) { @@ -437,7 +437,7 @@ struct binder_buffer *binder_alloc_new_buf_locked(struct binder_alloc *alloc, if (end_page_addr > has_page_addr) end_page_addr = has_page_addr; ret = binder_update_page_range(alloc, 1, - (void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr, NULL); + (void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr); if (ret) return ERR_PTR(ret); @@ -478,7 +478,7 @@ struct binder_buffer *binder_alloc_new_buf_locked(struct binder_alloc *alloc, err_alloc_buf_struct_failed: binder_update_page_range(alloc, 0, (void *)PAGE_ALIGN((uintptr_t)buffer->data), - end_page_addr, NULL); + end_page_addr); return ERR_PTR(-ENOMEM); } @@ -562,8 +562,7 @@ static void binder_delete_free_buffer(struct binder_alloc *alloc, alloc->pid, buffer->data, prev->data, next ? next->data : NULL); binder_update_page_range(alloc, 0, buffer_start_page(buffer), - buffer_start_page(buffer) + PAGE_SIZE, - NULL); + buffer_start_page(buffer) + PAGE_SIZE); } list_del(&buffer->entry); kfree(buffer); @@ -600,8 +599,7 @@ static void binder_free_buf_locked(struct binder_alloc *alloc, binder_update_page_range(alloc, 0, (void *)PAGE_ALIGN((uintptr_t)buffer->data), - (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK), - NULL); + (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK)); rb_erase(&buffer->rb_node, &alloc->allocated_buffers); buffer->free = 1; @@ -984,7 +982,7 @@ binder_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) return ret; } -struct shrinker binder_shrinker = { +static struct shrinker binder_shrinker = { .count_objects = binder_shrink_count, .scan_objects = binder_shrink_scan, .seeks = DEFAULT_SEEKS, diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index cd53771b9ae7..370e0a64ead1 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -659,9 +659,9 @@ static void terminate_monitor(struct cm4000_dev *dev) * is already doing that for you. */ -static void monitor_card(unsigned long p) +static void monitor_card(struct timer_list *t) { - struct cm4000_dev *dev = (struct cm4000_dev *) p; + struct cm4000_dev *dev = from_timer(dev, t, timer); unsigned int iobase = dev->p_dev->resource[0]->start; unsigned short s; struct ptsreq ptsreq; @@ -1374,7 +1374,7 @@ static void start_monitor(struct cm4000_dev *dev) DEBUGP(3, dev, "-> start_monitor\n"); if (!dev->monitor_running) { DEBUGP(5, dev, "create, init and add timer\n"); - setup_timer(&dev->timer, monitor_card, (unsigned long)dev); + timer_setup(&dev->timer, monitor_card, 0); dev->monitor_running = 1; mod_timer(&dev->timer, jiffies); } else diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c index 382c864814d9..9a1aaf538758 100644 --- a/drivers/char/pcmcia/cm4040_cs.c +++ b/drivers/char/pcmcia/cm4040_cs.c @@ -104,9 +104,9 @@ static inline unsigned char xinb(unsigned short port) /* poll the device fifo status register. not to be confused with * the poll syscall. */ -static void cm4040_do_poll(unsigned long dummy) +static void cm4040_do_poll(struct timer_list *t) { - struct reader_dev *dev = (struct reader_dev *) dummy; + struct reader_dev *dev = from_timer(dev, t, poll_timer); unsigned int obs = xinb(dev->p_dev->resource[0]->start + REG_OFFSET_BUFFER_STATUS); @@ -465,7 +465,6 @@ static int cm4040_open(struct inode *inode, struct file *filp) link->open = 1; - dev->poll_timer.data = (unsigned long) dev; mod_timer(&dev->poll_timer, jiffies + POLL_PERIOD); DEBUGP(2, dev, "<- cm4040_open (successfully)\n"); @@ -585,7 +584,7 @@ static int reader_probe(struct pcmcia_device *link) init_waitqueue_head(&dev->poll_wait); init_waitqueue_head(&dev->read_wait); init_waitqueue_head(&dev->write_wait); - setup_timer(&dev->poll_timer, cm4040_do_poll, 0); + timer_setup(&dev->poll_timer, cm4040_do_poll, 0); ret = reader_config(link, i); if (ret) { diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 62be953e5fb0..aa502e9fb7fa 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -375,7 +375,7 @@ static void reset_device(MGSLPC_INFO *info); static void hdlc_mode(MGSLPC_INFO *info); static void async_mode(MGSLPC_INFO *info); -static void tx_timeout(unsigned long context); +static void tx_timeout(struct timer_list *t); static int carrier_raised(struct tty_port *port); static void dtr_rts(struct tty_port *port, int onoff); @@ -1289,7 +1289,7 @@ static int startup(MGSLPC_INFO * info, struct tty_struct *tty) memset(&info->icount, 0, sizeof(info->icount)); - setup_timer(&info->tx_timer, tx_timeout, (unsigned long)info); + timer_setup(&info->tx_timer, tx_timeout, 0); /* Allocate and claim adapter resources */ retval = claim_resources(info); @@ -3846,9 +3846,9 @@ static void trace_block(MGSLPC_INFO *info,const char* data, int count, int xmit) /* HDLC frame time out * update stats and do tx completion processing */ -static void tx_timeout(unsigned long context) +static void tx_timeout(struct timer_list *t) { - MGSLPC_INFO *info = (MGSLPC_INFO*)context; + MGSLPC_INFO *info = from_timer(info, t, tx_timer); unsigned long flags; if (debug_level >= DEBUG_LEVEL_INFO) diff --git a/drivers/fpga/xilinx-pr-decoupler.c b/drivers/fpga/xilinx-pr-decoupler.c index e359930bebc8..0d7743089414 100644 --- a/drivers/fpga/xilinx-pr-decoupler.c +++ b/drivers/fpga/xilinx-pr-decoupler.c @@ -79,7 +79,7 @@ static int xlnx_pr_decoupler_enable_show(struct fpga_bridge *bridge) return !status; } -static struct fpga_bridge_ops xlnx_pr_decoupler_br_ops = { +static const struct fpga_bridge_ops xlnx_pr_decoupler_br_ops = { .enable_set = xlnx_pr_decoupler_enable_set, .enable_show = xlnx_pr_decoupler_enable_show, }; diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c index 4ea63d9bd131..e318bf8c623c 100644 --- a/drivers/fsi/fsi-core.c +++ b/drivers/fsi/fsi-core.c @@ -185,7 +185,7 @@ static int fsi_slave_calc_addr(struct fsi_slave *slave, uint32_t *addrp, return 0; } -int fsi_slave_report_and_clear_errors(struct fsi_slave *slave) +static int fsi_slave_report_and_clear_errors(struct fsi_slave *slave) { struct fsi_master *master = slave->master; uint32_t irq, stat; @@ -215,8 +215,8 @@ int fsi_slave_report_and_clear_errors(struct fsi_slave *slave) static int fsi_slave_set_smode(struct fsi_master *master, int link, int id); -int fsi_slave_handle_error(struct fsi_slave *slave, bool write, uint32_t addr, - size_t size) +static int fsi_slave_handle_error(struct fsi_slave *slave, bool write, + uint32_t addr, size_t size) { struct fsi_master *master = slave->master; int rc, link; diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile index e7b1d796ba2e..14c22786b519 100644 --- a/drivers/hv/Makefile +++ b/drivers/hv/Makefile @@ -3,7 +3,9 @@ obj-$(CONFIG_HYPERV) += hv_vmbus.o obj-$(CONFIG_HYPERV_UTILS) += hv_utils.o obj-$(CONFIG_HYPERV_BALLOON) += hv_balloon.o +CFLAGS_hv_trace.o = -I$(src) + hv_vmbus-y := vmbus_drv.o \ hv.o connection.o channel.o \ - channel_mgmt.o ring_buffer.o + channel_mgmt.o ring_buffer.o hv_trace.o hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_fcopy.o hv_utils_transport.o diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 894b67ac2cae..19f0cf37e0ed 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -43,6 +43,8 @@ void vmbus_setevent(struct vmbus_channel *channel) { struct hv_monitor_page *monitorpage; + trace_vmbus_setevent(channel); + /* * For channels marked as in "low latency" mode * bypass the monitor page mechanism. @@ -185,6 +187,8 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, ret = vmbus_post_msg(open_msg, sizeof(struct vmbus_channel_open_channel), true); + trace_vmbus_open(open_msg, ret); + if (ret != 0) { err = ret; goto error_clean_msglist; @@ -234,13 +238,18 @@ int vmbus_send_tl_connect_request(const uuid_le *shv_guest_servie_id, const uuid_le *shv_host_servie_id) { struct vmbus_channel_tl_connect_request conn_msg; + int ret; memset(&conn_msg, 0, sizeof(conn_msg)); conn_msg.header.msgtype = CHANNELMSG_TL_CONNECT_REQUEST; conn_msg.guest_endpoint_id = *shv_guest_servie_id; conn_msg.host_service_id = *shv_host_servie_id; - return vmbus_post_msg(&conn_msg, sizeof(conn_msg), true); + ret = vmbus_post_msg(&conn_msg, sizeof(conn_msg), true); + + trace_vmbus_send_tl_connect_request(&conn_msg, ret); + + return ret; } EXPORT_SYMBOL_GPL(vmbus_send_tl_connect_request); @@ -433,6 +442,9 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, ret = vmbus_post_msg(gpadlmsg, msginfo->msgsize - sizeof(*msginfo), true); + + trace_vmbus_establish_gpadl_header(gpadlmsg, ret); + if (ret != 0) goto cleanup; @@ -448,6 +460,9 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, ret = vmbus_post_msg(gpadl_body, submsginfo->msgsize - sizeof(*submsginfo), true); + + trace_vmbus_establish_gpadl_body(gpadl_body, ret); + if (ret != 0) goto cleanup; @@ -511,6 +526,8 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle) ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_gpadl_teardown), true); + trace_vmbus_teardown_gpadl(msg, ret); + if (ret) goto post_msg_err; @@ -589,6 +606,8 @@ static int vmbus_close_internal(struct vmbus_channel *channel) ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_close_channel), true); + trace_vmbus_close_internal(msg, ret); + if (ret) { pr_err("Close failed: close post msg return is %d\n", ret); /* @@ -745,6 +764,7 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, desc.dataoffset8 = descsize >> 3; /* in 8-bytes granularity */ desc.length8 = (u16)(packetlen_aligned >> 3); desc.transactionid = requestid; + desc.reserved = 0; desc.rangecount = pagecount; for (i = 0; i < pagecount; i++) { @@ -788,6 +808,7 @@ int vmbus_sendpacket_mpb_desc(struct vmbus_channel *channel, desc->dataoffset8 = desc_size >> 3; /* in 8-bytes granularity */ desc->length8 = (u16)(packetlen_aligned >> 3); desc->transactionid = requestid; + desc->reserved = 0; desc->rangecount = 1; bufferlist[0].iov_base = desc; diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 379b0df123be..ec5454f3f4a6 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -350,7 +350,7 @@ static void free_channel(struct vmbus_channel *channel) { tasklet_kill(&channel->callback_event); - kfree_rcu(channel, rcu); + kobject_put(&channel->kobj); } static void percpu_channel_enq(void *arg) @@ -373,12 +373,15 @@ static void percpu_channel_deq(void *arg) static void vmbus_release_relid(u32 relid) { struct vmbus_channel_relid_released msg; + int ret; memset(&msg, 0, sizeof(struct vmbus_channel_relid_released)); msg.child_relid = relid; msg.header.msgtype = CHANNELMSG_RELID_RELEASED; - vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released), - true); + ret = vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released), + true); + + trace_vmbus_release_relid(&msg, ret); } void hv_process_channel_removal(u32 relid) @@ -520,6 +523,14 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) newchannel->state = CHANNEL_OPEN_STATE; if (!fnew) { + struct hv_device *dev + = newchannel->primary_channel->device_obj; + + if (vmbus_add_channel_kobj(dev, newchannel)) { + atomic_dec(&vmbus_connection.offer_in_progress); + goto err_free_chan; + } + if (channel->sc_creation_callback != NULL) channel->sc_creation_callback(newchannel); newchannel->probe_done = true; @@ -805,6 +816,8 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr) offer = (struct vmbus_channel_offer_channel *)hdr; + trace_vmbus_onoffer(offer); + /* Allocate the channel object and save this offer. */ newchannel = alloc_channel(); if (!newchannel) { @@ -846,6 +859,8 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) rescind = (struct vmbus_channel_rescind_offer *)hdr; + trace_vmbus_onoffer_rescind(rescind); + /* * The offer msg and the corresponding rescind msg * from the host are guranteed to be ordered - @@ -974,6 +989,8 @@ static void vmbus_onopen_result(struct vmbus_channel_message_header *hdr) result = (struct vmbus_channel_open_result *)hdr; + trace_vmbus_onopen_result(result); + /* * Find the open msg, copy the result and signal/unblock the wait event */ @@ -1018,6 +1035,8 @@ static void vmbus_ongpadl_created(struct vmbus_channel_message_header *hdr) gpadlcreated = (struct vmbus_channel_gpadl_created *)hdr; + trace_vmbus_ongpadl_created(gpadlcreated); + /* * Find the establish msg, copy the result and signal/unblock the wait * event @@ -1066,6 +1085,8 @@ static void vmbus_ongpadl_torndown( gpadl_torndown = (struct vmbus_channel_gpadl_torndown *)hdr; + trace_vmbus_ongpadl_torndown(gpadl_torndown); + /* * Find the open msg, copy the result and signal/unblock the wait event */ @@ -1109,6 +1130,9 @@ static void vmbus_onversion_response( unsigned long flags; version_response = (struct vmbus_channel_version_response *)hdr; + + trace_vmbus_onversion_response(version_response); + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, @@ -1168,6 +1192,8 @@ void vmbus_onmessage(void *context) hdr = (struct vmbus_channel_message_header *)msg->u.payload; size = msg->header.payload_size; + trace_vmbus_on_message(hdr); + if (hdr->msgtype >= CHANNELMSG_COUNT) { pr_err("Received invalid channel message type %d size %d\n", hdr->msgtype, size); @@ -1201,9 +1227,11 @@ int vmbus_request_offers(void) msg->msgtype = CHANNELMSG_REQUESTOFFERS; - ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_message_header), true); + + trace_vmbus_request_offers(ret); + if (ret != 0) { pr_err("Unable to request offers - %d\n", ret); diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index f41901f80b64..447371f4de56 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -117,6 +117,9 @@ static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_initiate_contact), true); + + trace_vmbus_negotiate_version(msg, ret); + if (ret != 0) { spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); list_del(&msginfo->msglistentry); @@ -319,6 +322,8 @@ void vmbus_on_event(unsigned long data) struct vmbus_channel *channel = (void *) data; unsigned long time_limit = jiffies + 2; + trace_vmbus_on_event(channel); + do { void (*callback_fn)(void *); @@ -409,6 +414,8 @@ void vmbus_set_event(struct vmbus_channel *channel) if (!channel->is_dedicated_interrupt) vmbus_send_interrupt(child_relid); + ++channel->sig_events; + hv_do_fast_hypercall8(HVCALL_SIGNAL_EVENT, channel->sig_event); } EXPORT_SYMBOL_GPL(vmbus_set_event); diff --git a/drivers/hv/hv_trace.c b/drivers/hv/hv_trace.c new file mode 100644 index 000000000000..df47acd01a81 --- /dev/null +++ b/drivers/hv/hv_trace.c @@ -0,0 +1,4 @@ +#include "hyperv_vmbus.h" + +#define CREATE_TRACE_POINTS +#include "hv_trace.h" diff --git a/drivers/hv/hv_trace.h b/drivers/hv/hv_trace.h new file mode 100644 index 000000000000..d635ee95b20d --- /dev/null +++ b/drivers/hv/hv_trace.h @@ -0,0 +1,327 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM hyperv + +#if !defined(_HV_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _HV_TRACE_H + +#include <linux/tracepoint.h> + +DECLARE_EVENT_CLASS(vmbus_hdr_msg, + TP_PROTO(const struct vmbus_channel_message_header *hdr), + TP_ARGS(hdr), + TP_STRUCT__entry(__field(unsigned int, msgtype)), + TP_fast_assign(__entry->msgtype = hdr->msgtype;), + TP_printk("msgtype=%u", __entry->msgtype) +); + +DEFINE_EVENT(vmbus_hdr_msg, vmbus_on_msg_dpc, + TP_PROTO(const struct vmbus_channel_message_header *hdr), + TP_ARGS(hdr) +); + +DEFINE_EVENT(vmbus_hdr_msg, vmbus_on_message, + TP_PROTO(const struct vmbus_channel_message_header *hdr), + TP_ARGS(hdr) +); + +TRACE_EVENT(vmbus_onoffer, + TP_PROTO(const struct vmbus_channel_offer_channel *offer), + TP_ARGS(offer), + TP_STRUCT__entry( + __field(u32, child_relid) + __field(u8, monitorid) + __field(u16, is_ddc_int) + __field(u32, connection_id) + __array(char, if_type, 16) + __array(char, if_instance, 16) + __field(u16, chn_flags) + __field(u16, mmio_mb) + __field(u16, sub_idx) + ), + TP_fast_assign(__entry->child_relid = offer->child_relid; + __entry->monitorid = offer->monitorid; + __entry->is_ddc_int = offer->is_dedicated_interrupt; + __entry->connection_id = offer->connection_id; + memcpy(__entry->if_type, + &offer->offer.if_type.b, 16); + memcpy(__entry->if_instance, + &offer->offer.if_instance.b, 16); + __entry->chn_flags = offer->offer.chn_flags; + __entry->mmio_mb = offer->offer.mmio_megabytes; + __entry->sub_idx = offer->offer.sub_channel_index; + ), + TP_printk("child_relid 0x%x, monitorid 0x%x, is_dedicated %d, " + "connection_id 0x%x, if_type %pUl, if_instance %pUl, " + "chn_flags 0x%x, mmio_megabytes %d, sub_channel_index %d", + __entry->child_relid, __entry->monitorid, + __entry->is_ddc_int, __entry->connection_id, + __entry->if_type, __entry->if_instance, + __entry->chn_flags, __entry->mmio_mb, + __entry->sub_idx + ) + ); + +TRACE_EVENT(vmbus_onoffer_rescind, + TP_PROTO(const struct vmbus_channel_rescind_offer *offer), + TP_ARGS(offer), + TP_STRUCT__entry(__field(u32, child_relid)), + TP_fast_assign(__entry->child_relid = offer->child_relid), + TP_printk("child_relid 0x%x", __entry->child_relid) + ); + +TRACE_EVENT(vmbus_onopen_result, + TP_PROTO(const struct vmbus_channel_open_result *result), + TP_ARGS(result), + TP_STRUCT__entry( + __field(u32, child_relid) + __field(u32, openid) + __field(u32, status) + ), + TP_fast_assign(__entry->child_relid = result->child_relid; + __entry->openid = result->openid; + __entry->status = result->status; + ), + TP_printk("child_relid 0x%x, openid %d, status %d", + __entry->child_relid, __entry->openid, __entry->status + ) + ); + +TRACE_EVENT(vmbus_ongpadl_created, + TP_PROTO(const struct vmbus_channel_gpadl_created *gpadlcreated), + TP_ARGS(gpadlcreated), + TP_STRUCT__entry( + __field(u32, child_relid) + __field(u32, gpadl) + __field(u32, status) + ), + TP_fast_assign(__entry->child_relid = gpadlcreated->child_relid; + __entry->gpadl = gpadlcreated->gpadl; + __entry->status = gpadlcreated->creation_status; + ), + TP_printk("child_relid 0x%x, gpadl 0x%x, creation_status %d", + __entry->child_relid, __entry->gpadl, __entry->status + ) + ); + +TRACE_EVENT(vmbus_ongpadl_torndown, + TP_PROTO(const struct vmbus_channel_gpadl_torndown *gpadltorndown), + TP_ARGS(gpadltorndown), + TP_STRUCT__entry(__field(u32, gpadl)), + TP_fast_assign(__entry->gpadl = gpadltorndown->gpadl), + TP_printk("gpadl 0x%x", __entry->gpadl) + ); + +TRACE_EVENT(vmbus_onversion_response, + TP_PROTO(const struct vmbus_channel_version_response *response), + TP_ARGS(response), + TP_STRUCT__entry( + __field(u8, ver) + ), + TP_fast_assign(__entry->ver = response->version_supported; + ), + TP_printk("version_supported %d", __entry->ver) + ); + +TRACE_EVENT(vmbus_request_offers, + TP_PROTO(int ret), + TP_ARGS(ret), + TP_STRUCT__entry(__field(int, ret)), + TP_fast_assign(__entry->ret = ret), + TP_printk("sending ret %d", __entry->ret) + ); + +TRACE_EVENT(vmbus_open, + TP_PROTO(const struct vmbus_channel_open_channel *msg, int ret), + TP_ARGS(msg, ret), + TP_STRUCT__entry( + __field(u32, child_relid) + __field(u32, openid) + __field(u32, gpadlhandle) + __field(u32, target_vp) + __field(u32, offset) + __field(int, ret) + ), + TP_fast_assign( + __entry->child_relid = msg->child_relid; + __entry->openid = msg->openid; + __entry->gpadlhandle = msg->ringbuffer_gpadlhandle; + __entry->target_vp = msg->target_vp; + __entry->offset = msg->downstream_ringbuffer_pageoffset; + __entry->ret = ret; + ), + TP_printk("sending child_relid 0x%x, openid %d, " + "gpadlhandle 0x%x, target_vp 0x%x, offset 0x%x, ret %d", + __entry->child_relid, __entry->openid, + __entry->gpadlhandle, __entry->target_vp, + __entry->offset, __entry->ret + ) + ); + +TRACE_EVENT(vmbus_close_internal, + TP_PROTO(const struct vmbus_channel_close_channel *msg, int ret), + TP_ARGS(msg, ret), + TP_STRUCT__entry( + __field(u32, child_relid) + __field(int, ret) + ), + TP_fast_assign( + __entry->child_relid = msg->child_relid; + __entry->ret = ret; + ), + TP_printk("sending child_relid 0x%x, ret %d", __entry->child_relid, + __entry->ret) + ); + +TRACE_EVENT(vmbus_establish_gpadl_header, + TP_PROTO(const struct vmbus_channel_gpadl_header *msg, int ret), + TP_ARGS(msg, ret), + TP_STRUCT__entry( + __field(u32, child_relid) + __field(u32, gpadl) + __field(u16, range_buflen) + __field(u16, rangecount) + __field(int, ret) + ), + TP_fast_assign( + __entry->child_relid = msg->child_relid; + __entry->gpadl = msg->gpadl; + __entry->range_buflen = msg->range_buflen; + __entry->rangecount = msg->rangecount; + __entry->ret = ret; + ), + TP_printk("sending child_relid 0x%x, gpadl 0x%x, range_buflen %d " + "rangecount %d, ret %d", + __entry->child_relid, __entry->gpadl, + __entry->range_buflen, __entry->rangecount, __entry->ret + ) + ); + +TRACE_EVENT(vmbus_establish_gpadl_body, + TP_PROTO(const struct vmbus_channel_gpadl_body *msg, int ret), + TP_ARGS(msg, ret), + TP_STRUCT__entry( + __field(u32, msgnumber) + __field(u32, gpadl) + __field(int, ret) + ), + TP_fast_assign( + __entry->msgnumber = msg->msgnumber; + __entry->gpadl = msg->gpadl; + __entry->ret = ret; + ), + TP_printk("sending msgnumber %d, gpadl 0x%x, ret %d", + __entry->msgnumber, __entry->gpadl, __entry->ret + ) + ); + +TRACE_EVENT(vmbus_teardown_gpadl, + TP_PROTO(const struct vmbus_channel_gpadl_teardown *msg, int ret), + TP_ARGS(msg, ret), + TP_STRUCT__entry( + __field(u32, child_relid) + __field(u32, gpadl) + __field(int, ret) + ), + TP_fast_assign( + __entry->child_relid = msg->child_relid; + __entry->gpadl = msg->gpadl; + __entry->ret = ret; + ), + TP_printk("sending child_relid 0x%x, gpadl 0x%x, ret %d", + __entry->child_relid, __entry->gpadl, __entry->ret + ) + ); + +TRACE_EVENT(vmbus_negotiate_version, + TP_PROTO(const struct vmbus_channel_initiate_contact *msg, int ret), + TP_ARGS(msg, ret), + TP_STRUCT__entry( + __field(u32, ver) + __field(u32, target_vcpu) + __field(int, ret) + __field(u64, int_page) + __field(u64, mon_page1) + __field(u64, mon_page2) + ), + TP_fast_assign( + __entry->ver = msg->vmbus_version_requested; + __entry->target_vcpu = msg->target_vcpu; + __entry->int_page = msg->interrupt_page; + __entry->mon_page1 = msg->monitor_page1; + __entry->mon_page2 = msg->monitor_page2; + __entry->ret = ret; + ), + TP_printk("sending vmbus_version_requested %d, target_vcpu 0x%x, " + "pages %llx:%llx:%llx, ret %d", + __entry->ver, __entry->target_vcpu, __entry->int_page, + __entry->mon_page1, __entry->mon_page2, __entry->ret + ) + ); + +TRACE_EVENT(vmbus_release_relid, + TP_PROTO(const struct vmbus_channel_relid_released *msg, int ret), + TP_ARGS(msg, ret), + TP_STRUCT__entry( + __field(u32, child_relid) + __field(int, ret) + ), + TP_fast_assign( + __entry->child_relid = msg->child_relid; + __entry->ret = ret; + ), + TP_printk("sending child_relid 0x%x, ret %d", + __entry->child_relid, __entry->ret + ) + ); + +TRACE_EVENT(vmbus_send_tl_connect_request, + TP_PROTO(const struct vmbus_channel_tl_connect_request *msg, + int ret), + TP_ARGS(msg, ret), + TP_STRUCT__entry( + __array(char, guest_id, 16) + __array(char, host_id, 16) + __field(int, ret) + ), + TP_fast_assign( + memcpy(__entry->guest_id, &msg->guest_endpoint_id.b, 16); + memcpy(__entry->host_id, &msg->host_service_id.b, 16); + __entry->ret = ret; + ), + TP_printk("sending guest_endpoint_id %pUl, host_service_id %pUl, " + "ret %d", + __entry->guest_id, __entry->host_id, __entry->ret + ) + ); + +DECLARE_EVENT_CLASS(vmbus_channel, + TP_PROTO(const struct vmbus_channel *channel), + TP_ARGS(channel), + TP_STRUCT__entry(__field(u32, relid)), + TP_fast_assign(__entry->relid = channel->offermsg.child_relid), + TP_printk("relid 0x%x", __entry->relid) +); + +DEFINE_EVENT(vmbus_channel, vmbus_chan_sched, + TP_PROTO(const struct vmbus_channel *channel), + TP_ARGS(channel) +); + +DEFINE_EVENT(vmbus_channel, vmbus_setevent, + TP_PROTO(const struct vmbus_channel *channel), + TP_ARGS(channel) +); + +DEFINE_EVENT(vmbus_channel, vmbus_on_event, + TP_PROTO(const struct vmbus_channel *channel), + TP_ARGS(channel) +); + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE hv_trace +#endif /* _HV_TRACE_H */ + +/* This part must be outside protection */ +#include <trace/define_trace.h> diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 49569f8fe038..22300ec7b556 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -31,6 +31,8 @@ #include <linux/hyperv.h> #include <linux/interrupt.h> +#include "hv_trace.h" + /* * Timeout for services such as KVP and fcopy. */ @@ -373,6 +375,8 @@ struct hv_device *vmbus_device_create(const uuid_le *type, int vmbus_device_register(struct hv_device *child_device_obj); void vmbus_device_unregister(struct hv_device *device_obj); +int vmbus_add_channel_kobj(struct hv_device *device_obj, + struct vmbus_channel *channel); struct vmbus_channel *relid2channel(u32 relid); diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 2cd134dd94d2..76ed9a216f10 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -65,7 +65,7 @@ static int hyperv_panic_event(struct notifier_block *nb, unsigned long val, regs = current_pt_regs(); - hyperv_report_panic(regs); + hyperv_report_panic(regs, val); return NOTIFY_DONE; } @@ -75,7 +75,7 @@ static int hyperv_die_event(struct notifier_block *nb, unsigned long val, struct die_args *die = (struct die_args *)args; struct pt_regs *regs = die->regs; - hyperv_report_panic(regs); + hyperv_report_panic(regs, val); return NOTIFY_DONE; } @@ -107,28 +107,30 @@ static void print_alias_name(struct hv_device *hv_dev, char *alias_name) sprintf(&alias_name[i], "%02x", hv_dev->dev_type.b[i/2]); } -static u8 channel_monitor_group(struct vmbus_channel *channel) +static u8 channel_monitor_group(const struct vmbus_channel *channel) { return (u8)channel->offermsg.monitorid / 32; } -static u8 channel_monitor_offset(struct vmbus_channel *channel) +static u8 channel_monitor_offset(const struct vmbus_channel *channel) { return (u8)channel->offermsg.monitorid % 32; } -static u32 channel_pending(struct vmbus_channel *channel, - struct hv_monitor_page *monitor_page) +static u32 channel_pending(const struct vmbus_channel *channel, + const struct hv_monitor_page *monitor_page) { u8 monitor_group = channel_monitor_group(channel); + return monitor_page->trigger_group[monitor_group].pending; } -static u32 channel_latency(struct vmbus_channel *channel, - struct hv_monitor_page *monitor_page) +static u32 channel_latency(const struct vmbus_channel *channel, + const struct hv_monitor_page *monitor_page) { u8 monitor_group = channel_monitor_group(channel); u8 monitor_offset = channel_monitor_offset(channel); + return monitor_page->latency[monitor_group][monitor_offset]; } @@ -833,6 +835,8 @@ void vmbus_on_msg_dpc(unsigned long data) hdr = (struct vmbus_channel_message_header *)msg->u.payload; + trace_vmbus_on_msg_dpc(hdr); + if (hdr->msgtype >= CHANNELMSG_COUNT) { WARN_ONCE(1, "unknown msgtype=%d\n", hdr->msgtype); goto msg_handled; @@ -942,6 +946,10 @@ static void vmbus_chan_sched(struct hv_per_cpu_context *hv_cpu) if (channel->rescind) continue; + trace_vmbus_chan_sched(channel); + + ++channel->interrupts; + switch (channel->callback_mode) { case HV_CALL_ISR: vmbus_channel_isr(channel); @@ -1133,6 +1141,159 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver) } EXPORT_SYMBOL_GPL(vmbus_driver_unregister); + +/* + * Called when last reference to channel is gone. + */ +static void vmbus_chan_release(struct kobject *kobj) +{ + struct vmbus_channel *channel + = container_of(kobj, struct vmbus_channel, kobj); + + kfree_rcu(channel, rcu); +} + +struct vmbus_chan_attribute { + struct attribute attr; + ssize_t (*show)(const struct vmbus_channel *chan, char *buf); + ssize_t (*store)(struct vmbus_channel *chan, + const char *buf, size_t count); +}; +#define VMBUS_CHAN_ATTR(_name, _mode, _show, _store) \ + struct vmbus_chan_attribute chan_attr_##_name \ + = __ATTR(_name, _mode, _show, _store) +#define VMBUS_CHAN_ATTR_RW(_name) \ + struct vmbus_chan_attribute chan_attr_##_name = __ATTR_RW(_name) +#define VMBUS_CHAN_ATTR_RO(_name) \ + struct vmbus_chan_attribute chan_attr_##_name = __ATTR_RO(_name) +#define VMBUS_CHAN_ATTR_WO(_name) \ + struct vmbus_chan_attribute chan_attr_##_name = __ATTR_WO(_name) + +static ssize_t vmbus_chan_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + const struct vmbus_chan_attribute *attribute + = container_of(attr, struct vmbus_chan_attribute, attr); + const struct vmbus_channel *chan + = container_of(kobj, struct vmbus_channel, kobj); + + if (!attribute->show) + return -EIO; + + return attribute->show(chan, buf); +} + +static const struct sysfs_ops vmbus_chan_sysfs_ops = { + .show = vmbus_chan_attr_show, +}; + +static ssize_t out_mask_show(const struct vmbus_channel *channel, char *buf) +{ + const struct hv_ring_buffer_info *rbi = &channel->outbound; + + return sprintf(buf, "%u\n", rbi->ring_buffer->interrupt_mask); +} +VMBUS_CHAN_ATTR_RO(out_mask); + +static ssize_t in_mask_show(const struct vmbus_channel *channel, char *buf) +{ + const struct hv_ring_buffer_info *rbi = &channel->inbound; + + return sprintf(buf, "%u\n", rbi->ring_buffer->interrupt_mask); +} +VMBUS_CHAN_ATTR_RO(in_mask); + +static ssize_t read_avail_show(const struct vmbus_channel *channel, char *buf) +{ + const struct hv_ring_buffer_info *rbi = &channel->inbound; + + return sprintf(buf, "%u\n", hv_get_bytes_to_read(rbi)); +} +VMBUS_CHAN_ATTR_RO(read_avail); + +static ssize_t write_avail_show(const struct vmbus_channel *channel, char *buf) +{ + const struct hv_ring_buffer_info *rbi = &channel->outbound; + + return sprintf(buf, "%u\n", hv_get_bytes_to_write(rbi)); +} +VMBUS_CHAN_ATTR_RO(write_avail); + +static ssize_t show_target_cpu(const struct vmbus_channel *channel, char *buf) +{ + return sprintf(buf, "%u\n", channel->target_cpu); +} +VMBUS_CHAN_ATTR(cpu, S_IRUGO, show_target_cpu, NULL); + +static ssize_t channel_pending_show(const struct vmbus_channel *channel, + char *buf) +{ + return sprintf(buf, "%d\n", + channel_pending(channel, + vmbus_connection.monitor_pages[1])); +} +VMBUS_CHAN_ATTR(pending, S_IRUGO, channel_pending_show, NULL); + +static ssize_t channel_latency_show(const struct vmbus_channel *channel, + char *buf) +{ + return sprintf(buf, "%d\n", + channel_latency(channel, + vmbus_connection.monitor_pages[1])); +} +VMBUS_CHAN_ATTR(latency, S_IRUGO, channel_latency_show, NULL); + +static ssize_t channel_interrupts_show(const struct vmbus_channel *channel, char *buf) +{ + return sprintf(buf, "%llu\n", channel->interrupts); +} +VMBUS_CHAN_ATTR(interrupts, S_IRUGO, channel_interrupts_show, NULL); + +static ssize_t channel_events_show(const struct vmbus_channel *channel, char *buf) +{ + return sprintf(buf, "%llu\n", channel->sig_events); +} +VMBUS_CHAN_ATTR(events, S_IRUGO, channel_events_show, NULL); + +static struct attribute *vmbus_chan_attrs[] = { + &chan_attr_out_mask.attr, + &chan_attr_in_mask.attr, + &chan_attr_read_avail.attr, + &chan_attr_write_avail.attr, + &chan_attr_cpu.attr, + &chan_attr_pending.attr, + &chan_attr_latency.attr, + &chan_attr_interrupts.attr, + &chan_attr_events.attr, + NULL +}; + +static struct kobj_type vmbus_chan_ktype = { + .sysfs_ops = &vmbus_chan_sysfs_ops, + .release = vmbus_chan_release, + .default_attrs = vmbus_chan_attrs, +}; + +/* + * vmbus_add_channel_kobj - setup a sub-directory under device/channels + */ +int vmbus_add_channel_kobj(struct hv_device *dev, struct vmbus_channel *channel) +{ + struct kobject *kobj = &channel->kobj; + u32 relid = channel->offermsg.child_relid; + int ret; + + kobj->kset = dev->channels_kset; + ret = kobject_init_and_add(kobj, &vmbus_chan_ktype, NULL, + "%u", relid); + if (ret) + return ret; + + kobject_uevent(kobj, KOBJ_ADD); + + return 0; +} + /* * vmbus_device_create - Creates and registers a new child device * on the vmbus. @@ -1164,7 +1325,8 @@ struct hv_device *vmbus_device_create(const uuid_le *type, */ int vmbus_device_register(struct hv_device *child_device_obj) { - int ret = 0; + struct kobject *kobj = &child_device_obj->device.kobj; + int ret; dev_set_name(&child_device_obj->device, "%pUl", child_device_obj->channel->offermsg.offer.if_instance.b); @@ -1178,13 +1340,32 @@ int vmbus_device_register(struct hv_device *child_device_obj) * binding...which will eventually call vmbus_match() and vmbus_probe() */ ret = device_register(&child_device_obj->device); - - if (ret) + if (ret) { pr_err("Unable to register child device\n"); - else - pr_debug("child device %s registered\n", - dev_name(&child_device_obj->device)); + return ret; + } + + child_device_obj->channels_kset = kset_create_and_add("channels", + NULL, kobj); + if (!child_device_obj->channels_kset) { + ret = -ENOMEM; + goto err_dev_unregister; + } + + ret = vmbus_add_channel_kobj(child_device_obj, + child_device_obj->channel); + if (ret) { + pr_err("Unable to register primary channeln"); + goto err_kset_unregister; + } + + return 0; + +err_kset_unregister: + kset_unregister(child_device_obj->channels_kset); +err_dev_unregister: + device_unregister(&child_device_obj->device); return ret; } diff --git a/drivers/hwtracing/coresight/coresight-dynamic-replicator.c b/drivers/hwtracing/coresight/coresight-dynamic-replicator.c index accc2056f7c6..8f4357e2626c 100644 --- a/drivers/hwtracing/coresight/coresight-dynamic-replicator.c +++ b/drivers/hwtracing/coresight/coresight-dynamic-replicator.c @@ -199,8 +199,8 @@ static const struct dev_pm_ops replicator_dev_pm_ops = { static const struct amba_id replicator_ids[] = { { - .id = 0x0003b909, - .mask = 0x0003ffff, + .id = 0x000bb909, + .mask = 0x000fffff, }, { /* Coresight SoC-600 */ diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 56ecd7aff5eb..e03e58933141 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -748,8 +748,8 @@ static const struct dev_pm_ops etb_dev_pm_ops = { static const struct amba_id etb_ids[] = { { - .id = 0x0003b907, - .mask = 0x0003ffff, + .id = 0x000bb907, + .mask = 0x000fffff, }, { 0, 0}, }; diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index e5b1ec57dbde..39f42fdd503d 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -901,33 +901,33 @@ static const struct dev_pm_ops etm_dev_pm_ops = { static const struct amba_id etm_ids[] = { { /* ETM 3.3 */ - .id = 0x0003b921, - .mask = 0x0003ffff, + .id = 0x000bb921, + .mask = 0x000fffff, .data = "ETM 3.3", }, { /* ETM 3.5 - Cortex-A5 */ - .id = 0x0003b955, - .mask = 0x0003ffff, + .id = 0x000bb955, + .mask = 0x000fffff, .data = "ETM 3.5", }, { /* ETM 3.5 */ - .id = 0x0003b956, - .mask = 0x0003ffff, + .id = 0x000bb956, + .mask = 0x000fffff, .data = "ETM 3.5", }, { /* PTM 1.0 */ - .id = 0x0003b950, - .mask = 0x0003ffff, + .id = 0x000bb950, + .mask = 0x000fffff, .data = "PTM 1.0", }, { /* PTM 1.1 */ - .id = 0x0003b95f, - .mask = 0x0003ffff, + .id = 0x000bb95f, + .mask = 0x000fffff, .data = "PTM 1.1", }, { /* PTM 1.1 Qualcomm */ - .id = 0x0003006f, - .mask = 0x0003ffff, + .id = 0x000b006f, + .mask = 0x000fffff, .data = "PTM 1.1", }, { 0, 0}, diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 77642e0e955b..fd3c396717f6 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -248,8 +248,8 @@ static const struct dev_pm_ops funnel_dev_pm_ops = { static const struct amba_id funnel_ids[] = { { - .id = 0x0003b908, - .mask = 0x0003ffff, + .id = 0x000bb908, + .mask = 0x000fffff, }, { /* Coresight SoC-600 */ diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index 92a780a6df1d..15e7ef3891f5 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -917,13 +917,13 @@ static const struct dev_pm_ops stm_dev_pm_ops = { static const struct amba_id stm_ids[] = { { - .id = 0x0003b962, - .mask = 0x0003ffff, + .id = 0x000bb962, + .mask = 0x000fffff, .data = "STM32", }, { - .id = 0x0003b963, - .mask = 0x0003ffff, + .id = 0x000bb963, + .mask = 0x000fffff, .data = "STM500", }, { 0, 0}, diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 2ff4a66a3caa..0ea04f588de0 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -439,8 +439,8 @@ out: static const struct amba_id tmc_ids[] = { { - .id = 0x0003b961, - .mask = 0x0003ffff, + .id = 0x000bb961, + .mask = 0x000fffff, }, { /* Coresight SoC 600 TMC-ETR/ETS */ diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index d7a3e453016d..bef49a3a5ca7 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -194,8 +194,8 @@ static const struct dev_pm_ops tpiu_dev_pm_ops = { static const struct amba_id tpiu_ids[] = { { - .id = 0x0003b912, - .mask = 0x0003ffff, + .id = 0x000bb912, + .mask = 0x000fffff, }, { .id = 0x0004b912, diff --git a/drivers/misc/altera-stapl/Kconfig b/drivers/misc/altera-stapl/Kconfig index 7f01d8e93992..8a828fe41fad 100644 --- a/drivers/misc/altera-stapl/Kconfig +++ b/drivers/misc/altera-stapl/Kconfig @@ -1,4 +1,5 @@ -comment "Altera FPGA firmware download module" +comment "Altera FPGA firmware download module (requires I2C)" + depends on !I2C config ALTERA_STAPL tristate "Altera FPGA firmware download module" diff --git a/drivers/misc/genwqe/card_base.h b/drivers/misc/genwqe/card_base.h index 5813b5f25006..3743c87f8ab9 100644 --- a/drivers/misc/genwqe/card_base.h +++ b/drivers/misc/genwqe/card_base.h @@ -182,6 +182,7 @@ struct dma_mapping { struct list_head card_list; /* list of usr_maps for card */ struct list_head pin_list; /* list of pinned memory for dev */ + int write; /* writable map? useful in unmapping */ }; static inline void genwqe_mapping_init(struct dma_mapping *m, @@ -189,6 +190,7 @@ static inline void genwqe_mapping_init(struct dma_mapping *m, { memset(m, 0, sizeof(*m)); m->type = type; + m->write = 1; /* Assume the maps we create are R/W */ } /** @@ -347,6 +349,7 @@ enum genwqe_requ_state { * @user_size: size of user-space memory area * @page: buffer for partial pages if needed * @page_dma_addr: dma address partial pages + * @write: should we write it back to userspace? */ struct genwqe_sgl { dma_addr_t sgl_dma_addr; @@ -356,6 +359,8 @@ struct genwqe_sgl { void __user *user_addr; /* user-space base-address */ size_t user_size; /* size of memory area */ + int write; + unsigned long nr_pages; unsigned long fpage_offs; size_t fpage_size; @@ -369,7 +374,7 @@ struct genwqe_sgl { }; int genwqe_alloc_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, - void __user *user_addr, size_t user_size); + void __user *user_addr, size_t user_size, int write); int genwqe_setup_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, dma_addr_t *dma_list); diff --git a/drivers/misc/genwqe/card_dev.c b/drivers/misc/genwqe/card_dev.c index dd4617764f14..3ecfa35457e0 100644 --- a/drivers/misc/genwqe/card_dev.c +++ b/drivers/misc/genwqe/card_dev.c @@ -942,6 +942,10 @@ static int ddcb_cmd_fixups(struct genwqe_file *cfile, struct ddcb_requ *req) genwqe_mapping_init(m, GENWQE_MAPPING_SGL_TEMP); + + if (ats_flags == ATS_TYPE_SGL_RD) + m->write = 0; + rc = genwqe_user_vmap(cd, m, (void *)u_addr, u_size, req); if (rc != 0) @@ -954,7 +958,7 @@ static int ddcb_cmd_fixups(struct genwqe_file *cfile, struct ddcb_requ *req) /* create genwqe style scatter gather list */ rc = genwqe_alloc_sync_sgl(cd, &req->sgls[i], (void __user *)u_addr, - u_size); + u_size, m->write); if (rc != 0) goto err_out; diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c index 147b83011b58..5c0d917636f7 100644 --- a/drivers/misc/genwqe/card_utils.c +++ b/drivers/misc/genwqe/card_utils.c @@ -296,7 +296,7 @@ static int genwqe_sgl_size(int num_pages) * from user-space into the cached pages. */ int genwqe_alloc_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, - void __user *user_addr, size_t user_size) + void __user *user_addr, size_t user_size, int write) { int rc; struct pci_dev *pci_dev = cd->pci_dev; @@ -312,6 +312,7 @@ int genwqe_alloc_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, sgl->user_addr = user_addr; sgl->user_size = user_size; + sgl->write = write; sgl->sgl_size = genwqe_sgl_size(sgl->nr_pages); if (get_order(sgl->sgl_size) > MAX_ORDER) { @@ -476,14 +477,20 @@ int genwqe_setup_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, int genwqe_free_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl) { int rc = 0; + size_t offset; + unsigned long res; struct pci_dev *pci_dev = cd->pci_dev; if (sgl->fpage) { - if (copy_to_user(sgl->user_addr, sgl->fpage + sgl->fpage_offs, - sgl->fpage_size)) { - dev_err(&pci_dev->dev, "[%s] err: copying fpage!\n", - __func__); - rc = -EFAULT; + if (sgl->write) { + res = copy_to_user(sgl->user_addr, + sgl->fpage + sgl->fpage_offs, sgl->fpage_size); + if (res) { + dev_err(&pci_dev->dev, + "[%s] err: copying fpage! (res=%lu)\n", + __func__, res); + rc = -EFAULT; + } } __genwqe_free_consistent(cd, PAGE_SIZE, sgl->fpage, sgl->fpage_dma_addr); @@ -491,12 +498,16 @@ int genwqe_free_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl) sgl->fpage_dma_addr = 0; } if (sgl->lpage) { - if (copy_to_user(sgl->user_addr + sgl->user_size - - sgl->lpage_size, sgl->lpage, - sgl->lpage_size)) { - dev_err(&pci_dev->dev, "[%s] err: copying lpage!\n", - __func__); - rc = -EFAULT; + if (sgl->write) { + offset = sgl->user_size - sgl->lpage_size; + res = copy_to_user(sgl->user_addr + offset, sgl->lpage, + sgl->lpage_size); + if (res) { + dev_err(&pci_dev->dev, + "[%s] err: copying lpage! (res=%lu)\n", + __func__, res); + rc = -EFAULT; + } } __genwqe_free_consistent(cd, PAGE_SIZE, sgl->lpage, sgl->lpage_dma_addr); @@ -599,14 +610,14 @@ int genwqe_user_vmap(struct genwqe_dev *cd, struct dma_mapping *m, void *uaddr, /* pin user pages in memory */ rc = get_user_pages_fast(data & PAGE_MASK, /* page aligned addr */ m->nr_pages, - 1, /* write by caller */ + m->write, /* readable/writable */ m->page_list); /* ptrs to pages */ if (rc < 0) goto fail_get_user_pages; /* assumption: get_user_pages can be killed by signals. */ if (rc < m->nr_pages) { - free_user_pages(m->page_list, rc, 0); + free_user_pages(m->page_list, rc, m->write); rc = -EFAULT; goto fail_get_user_pages; } @@ -618,7 +629,7 @@ int genwqe_user_vmap(struct genwqe_dev *cd, struct dma_mapping *m, void *uaddr, return 0; fail_free_user_pages: - free_user_pages(m->page_list, m->nr_pages, 0); + free_user_pages(m->page_list, m->nr_pages, m->write); fail_get_user_pages: kfree(m->page_list); @@ -651,7 +662,7 @@ int genwqe_user_vunmap(struct genwqe_dev *cd, struct dma_mapping *m, genwqe_unmap_pages(cd, m->dma_list, m->nr_pages); if (m->page_list) { - free_user_pages(m->page_list, m->nr_pages, 1); + free_user_pages(m->page_list, m->nr_pages, m->write); kfree(m->page_list); m->page_list = NULL; diff --git a/drivers/misc/lkdtm_core.c b/drivers/misc/lkdtm_core.c index ed7f0c61c59a..ba92291508dc 100644 --- a/drivers/misc/lkdtm_core.c +++ b/drivers/misc/lkdtm_core.c @@ -122,7 +122,7 @@ struct crashtype { } /* Define the possible types of crashes that can be triggered. */ -struct crashtype crashtypes[] = { +static const struct crashtype crashtypes[] = { CRASHTYPE(PANIC), CRASHTYPE(BUG), CRASHTYPE(WARNING), @@ -188,8 +188,8 @@ struct crashtype crashtypes[] = { /* Global kprobe entry and crashtype. */ static struct kprobe *lkdtm_kprobe; -struct crashpoint *lkdtm_crashpoint; -struct crashtype *lkdtm_crashtype; +static struct crashpoint *lkdtm_crashpoint; +static const struct crashtype *lkdtm_crashtype; /* Module parameters */ static int recur_count = -1; @@ -212,7 +212,7 @@ MODULE_PARM_DESC(cpoint_count, " Crash Point Count, number of times the "\ /* Return the crashtype number or NULL if the name is invalid */ -static struct crashtype *find_crashtype(const char *name) +static const struct crashtype *find_crashtype(const char *name) { int i; @@ -228,7 +228,7 @@ static struct crashtype *find_crashtype(const char *name) * This is forced noinline just so it distinctly shows up in the stackdump * which makes validation of expected lkdtm crashes easier. */ -static noinline void lkdtm_do_action(struct crashtype *crashtype) +static noinline void lkdtm_do_action(const struct crashtype *crashtype) { if (WARN_ON(!crashtype || !crashtype->func)) return; @@ -236,7 +236,7 @@ static noinline void lkdtm_do_action(struct crashtype *crashtype) } static int lkdtm_register_cpoint(struct crashpoint *crashpoint, - struct crashtype *crashtype) + const struct crashtype *crashtype) { int ret; @@ -300,7 +300,7 @@ static ssize_t lkdtm_debugfs_entry(struct file *f, size_t count, loff_t *off) { struct crashpoint *crashpoint = file_inode(f)->i_private; - struct crashtype *crashtype = NULL; + const struct crashtype *crashtype = NULL; char *buf; int err; @@ -368,7 +368,7 @@ static int lkdtm_debugfs_open(struct inode *inode, struct file *file) static ssize_t direct_entry(struct file *f, const char __user *user_buf, size_t count, loff_t *off) { - struct crashtype *crashtype; + const struct crashtype *crashtype; char *buf; if (count >= PAGE_SIZE) @@ -404,7 +404,7 @@ static struct dentry *lkdtm_debugfs_root; static int __init lkdtm_module_init(void) { struct crashpoint *crashpoint = NULL; - struct crashtype *crashtype = NULL; + const struct crashtype *crashtype = NULL; int ret = -EINVAL; int i; diff --git a/drivers/misc/mei/mei-trace.c b/drivers/misc/mei/mei-trace.c index e19e6acb191b..374edde72a14 100644 --- a/drivers/misc/mei/mei-trace.c +++ b/drivers/misc/mei/mei-trace.c @@ -23,5 +23,4 @@ EXPORT_TRACEPOINT_SYMBOL(mei_reg_read); EXPORT_TRACEPOINT_SYMBOL(mei_reg_write); EXPORT_TRACEPOINT_SYMBOL(mei_pci_cfg_read); -EXPORT_TRACEPOINT_SYMBOL(mei_pci_cfg_write); #endif /* __CHECKER__ */ diff --git a/drivers/misc/mei/mei-trace.h b/drivers/misc/mei/mei-trace.h index 7d2d5d4a1624..b52e9b97a7c0 100644 --- a/drivers/misc/mei/mei-trace.h +++ b/drivers/misc/mei/mei-trace.h @@ -83,25 +83,6 @@ TRACE_EVENT(mei_pci_cfg_read, __get_str(dev), __entry->reg, __entry->offs, __entry->val) ); -TRACE_EVENT(mei_pci_cfg_write, - TP_PROTO(const struct device *dev, const char *reg, u32 offs, u32 val), - TP_ARGS(dev, reg, offs, val), - TP_STRUCT__entry( - __string(dev, dev_name(dev)) - __field(const char *, reg) - __field(u32, offs) - __field(u32, val) - ), - TP_fast_assign( - __assign_str(dev, dev_name(dev)) - __entry->reg = reg; - __entry->offs = offs; - __entry->val = val; - ), - TP_printk("[%s] pci cfg write %s[%#x] = %#x", - __get_str(dev), __entry->reg, __entry->offs, __entry->val) -); - #endif /* _MEI_TRACE_H_ */ /* This part must be outside protection */ diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig index 6fd9d367dea7..227cc7443671 100644 --- a/drivers/misc/mic/Kconfig +++ b/drivers/misc/mic/Kconfig @@ -1,3 +1,5 @@ +menu "Intel MIC & related support" + comment "Intel MIC Bus Driver" config INTEL_MIC_BUS @@ -150,3 +152,5 @@ config VOP if VOP source "drivers/vhost/Kconfig.vringh" endif + +endmenu diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 101ced4c84be..ff505af064ba 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -123,6 +123,17 @@ config NVMEM_SUNXI_SID This driver can also be built as a module. If so, the module will be called nvmem_sunxi_sid. +config UNIPHIER_EFUSE + tristate "UniPhier SoCs eFuse support" + depends on ARCH_UNIPHIER || COMPILE_TEST + depends on HAS_IOMEM + help + This is a simple driver to dump specified values of UniPhier SoC + from eFuse. + + This driver can also be built as a module. If so, the module + will be called nvmem-uniphier-efuse. + config NVMEM_VF610_OCOTP tristate "VF610 SoC OCOTP support" depends on SOC_VF610 || COMPILE_TEST @@ -135,13 +146,33 @@ config NVMEM_VF610_OCOTP be called nvmem-vf610-ocotp. config MESON_EFUSE - tristate "Amlogic eFuse Support" + tristate "Amlogic Meson GX eFuse Support" depends on (ARCH_MESON || COMPILE_TEST) && MESON_SM help This is a driver to retrieve specific values from the eFuse found on - the Amlogic Meson SoCs. + the Amlogic Meson GX SoCs. This driver can also be built as a module. If so, the module will be called nvmem_meson_efuse. +config MESON_MX_EFUSE + tristate "Amlogic Meson6/Meson8/Meson8b eFuse Support" + depends on ARCH_MESON || COMPILE_TEST + help + This is a driver to retrieve specific values from the eFuse found on + the Amlogic Meson6, Meson8 and Meson8b SoCs. + + This driver can also be built as a module. If so, the module + will be called nvmem_meson_mx_efuse. + +config NVMEM_SNVS_LPGPR + tristate "Support for Low Power General Purpose Register" + depends on SOC_IMX6 || COMPILE_TEST + help + This is a driver for Low Power General Purpose Register (LPGPR) available on + i.MX6 SoCs in Secure Non-Volatile Storage (SNVS) of this chip. + + This driver can also be built as a module. If so, the module + will be called nvmem-snvs-lpgpr. + endif diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index 6f7a77fb3ee7..e54dcfa6565a 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -27,7 +27,13 @@ obj-$(CONFIG_ROCKCHIP_EFUSE) += nvmem_rockchip_efuse.o nvmem_rockchip_efuse-y := rockchip-efuse.o obj-$(CONFIG_NVMEM_SUNXI_SID) += nvmem_sunxi_sid.o nvmem_sunxi_sid-y := sunxi_sid.o +obj-$(CONFIG_UNIPHIER_EFUSE) += nvmem-uniphier-efuse.o +nvmem-uniphier-efuse-y := uniphier-efuse.o obj-$(CONFIG_NVMEM_VF610_OCOTP) += nvmem-vf610-ocotp.o nvmem-vf610-ocotp-y := vf610-ocotp.o obj-$(CONFIG_MESON_EFUSE) += nvmem_meson_efuse.o nvmem_meson_efuse-y := meson-efuse.o +obj-$(CONFIG_MESON_MX_EFUSE) += nvmem_meson_mx_efuse.o +nvmem_meson_mx_efuse-y := meson-mx-efuse.o +obj-$(CONFIG_NVMEM_SNVS_LPGPR) += nvmem_snvs_lpgpr.o +nvmem_snvs_lpgpr-y := snvs_lpgpr.o diff --git a/drivers/nvmem/bcm-ocotp.c b/drivers/nvmem/bcm-ocotp.c index 3c56e3b2bd65..5e9e324427f9 100644 --- a/drivers/nvmem/bcm-ocotp.c +++ b/drivers/nvmem/bcm-ocotp.c @@ -232,7 +232,6 @@ static struct nvmem_config bcm_otpc_nvmem_config = { .read_only = false, .word_size = 4, .stride = 4, - .owner = THIS_MODULE, .reg_read = bcm_otpc_read, .reg_write = bcm_otpc_write, }; diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index d12e5de78e70..5a5cefd12153 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -462,6 +462,8 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) nvmem->id = rval; nvmem->owner = config->owner; + if (!nvmem->owner && config->dev->driver) + nvmem->owner = config->dev->driver->owner; nvmem->stride = config->stride; nvmem->word_size = config->word_size; nvmem->size = config->size; @@ -615,7 +617,7 @@ static struct nvmem_device *nvmem_find(const char *name) return to_nvmem_device(d); } -#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF) +#if IS_ENABLED(CONFIG_OF) /** * of_nvmem_device_get() - Get nvmem device from a given id * @@ -753,7 +755,7 @@ static struct nvmem_cell *nvmem_cell_get_from_list(const char *cell_id) return cell; } -#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF) +#if IS_ENABLED(CONFIG_OF) /** * of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id * @@ -946,8 +948,7 @@ void nvmem_cell_put(struct nvmem_cell *cell) } EXPORT_SYMBOL_GPL(nvmem_cell_put); -static inline void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, - void *buf) +static void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, void *buf) { u8 *p, *b; int i, bit_offset = cell->bit_offset; @@ -1028,8 +1029,8 @@ void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len) } EXPORT_SYMBOL_GPL(nvmem_cell_read); -static inline void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell, - u8 *_buf, int len) +static void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell, + u8 *_buf, int len) { struct nvmem_device *nvmem = cell->nvmem; int i, rc, nbits, bit_offset = cell->bit_offset; diff --git a/drivers/nvmem/imx-iim.c b/drivers/nvmem/imx-iim.c index 52ff65e0673f..52cfe91d9762 100644 --- a/drivers/nvmem/imx-iim.c +++ b/drivers/nvmem/imx-iim.c @@ -34,7 +34,6 @@ struct imx_iim_drvdata { struct iim_priv { void __iomem *base; struct clk *clk; - struct nvmem_config nvmem; }; static int imx_iim_read(void *context, unsigned int offset, @@ -108,7 +107,7 @@ static int imx_iim_probe(struct platform_device *pdev) struct resource *res; struct iim_priv *iim; struct nvmem_device *nvmem; - struct nvmem_config *cfg; + struct nvmem_config cfg = {}; const struct imx_iim_drvdata *drvdata = NULL; iim = devm_kzalloc(dev, sizeof(*iim), GFP_KERNEL); @@ -130,19 +129,16 @@ static int imx_iim_probe(struct platform_device *pdev) if (IS_ERR(iim->clk)) return PTR_ERR(iim->clk); - cfg = &iim->nvmem; + cfg.name = "imx-iim", + cfg.read_only = true, + cfg.word_size = 1, + cfg.stride = 1, + cfg.reg_read = imx_iim_read, + cfg.dev = dev; + cfg.size = drvdata->nregs; + cfg.priv = iim; - cfg->name = "imx-iim", - cfg->read_only = true, - cfg->word_size = 1, - cfg->stride = 1, - cfg->owner = THIS_MODULE, - cfg->reg_read = imx_iim_read, - cfg->dev = dev; - cfg->size = drvdata->nregs; - cfg->priv = iim; - - nvmem = nvmem_register(cfg); + nvmem = nvmem_register(&cfg); if (IS_ERR(nvmem)) return PTR_ERR(nvmem); diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c index 193ca8fd350a..d7ba351a70c9 100644 --- a/drivers/nvmem/imx-ocotp.c +++ b/drivers/nvmem/imx-ocotp.c @@ -40,14 +40,19 @@ #define IMX_OCOTP_ADDR_CTRL_SET 0x0004 #define IMX_OCOTP_ADDR_CTRL_CLR 0x0008 #define IMX_OCOTP_ADDR_TIMING 0x0010 -#define IMX_OCOTP_ADDR_DATA 0x0020 +#define IMX_OCOTP_ADDR_DATA0 0x0020 +#define IMX_OCOTP_ADDR_DATA1 0x0030 +#define IMX_OCOTP_ADDR_DATA2 0x0040 +#define IMX_OCOTP_ADDR_DATA3 0x0050 #define IMX_OCOTP_BM_CTRL_ADDR 0x0000007F #define IMX_OCOTP_BM_CTRL_BUSY 0x00000100 #define IMX_OCOTP_BM_CTRL_ERROR 0x00000200 #define IMX_OCOTP_BM_CTRL_REL_SHADOWS 0x00000400 -#define DEF_RELAX 20 /* > 16.5ns */ +#define DEF_RELAX 20 /* > 16.5ns */ +#define DEF_FSOURCE 1001 /* > 1000 ns */ +#define DEF_STROBE_PROG 10000 /* IPG clocks */ #define IMX_OCOTP_WR_UNLOCK 0x3E770000 #define IMX_OCOTP_READ_LOCKED_VAL 0xBADABADA @@ -57,10 +62,16 @@ struct ocotp_priv { struct device *dev; struct clk *clk; void __iomem *base; - unsigned int nregs; + const struct ocotp_params *params; struct nvmem_config *config; }; +struct ocotp_params { + unsigned int nregs; + unsigned int bank_address_words; + void (*set_timing)(struct ocotp_priv *priv); +}; + static int imx_ocotp_wait_for_busy(void __iomem *base, u32 flags) { int count; @@ -121,8 +132,8 @@ static int imx_ocotp_read(void *context, unsigned int offset, index = offset >> 2; count = bytes >> 2; - if (count > (priv->nregs - index)) - count = priv->nregs - index; + if (count > (priv->params->nregs - index)) + count = priv->params->nregs - index; mutex_lock(&ocotp_mutex); @@ -160,6 +171,52 @@ read_end: return ret; } +static void imx_ocotp_set_imx6_timing(struct ocotp_priv *priv) +{ + unsigned long clk_rate = 0; + unsigned long strobe_read, relax, strobe_prog; + u32 timing = 0; + + /* 47.3.1.3.1 + * Program HW_OCOTP_TIMING[STROBE_PROG] and HW_OCOTP_TIMING[RELAX] + * fields with timing values to match the current frequency of the + * ipg_clk. OTP writes will work at maximum bus frequencies as long + * as the HW_OCOTP_TIMING parameters are set correctly. + */ + clk_rate = clk_get_rate(priv->clk); + + relax = clk_rate / (1000000000 / DEF_RELAX) - 1; + strobe_prog = clk_rate / (1000000000 / 10000) + 2 * (DEF_RELAX + 1) - 1; + strobe_read = clk_rate / (1000000000 / 40) + 2 * (DEF_RELAX + 1) - 1; + + timing = strobe_prog & 0x00000FFF; + timing |= (relax << 12) & 0x0000F000; + timing |= (strobe_read << 16) & 0x003F0000; + + writel(timing, priv->base + IMX_OCOTP_ADDR_TIMING); +} + +static void imx_ocotp_set_imx7_timing(struct ocotp_priv *priv) +{ + unsigned long clk_rate = 0; + u64 fsource, strobe_prog; + u32 timing = 0; + + /* i.MX 7Solo Applications Processor Reference Manual, Rev. 0.1 + * 6.4.3.3 + */ + clk_rate = clk_get_rate(priv->clk); + fsource = DIV_ROUND_UP_ULL((u64)clk_rate * DEF_FSOURCE, + NSEC_PER_SEC) + 1; + strobe_prog = DIV_ROUND_CLOSEST_ULL((u64)clk_rate * DEF_STROBE_PROG, + NSEC_PER_SEC) + 1; + + timing = strobe_prog & 0x00000FFF; + timing |= (fsource << 12) & 0x000FF000; + + writel(timing, priv->base + IMX_OCOTP_ADDR_TIMING); +} + static int imx_ocotp_write(void *context, unsigned int offset, void *val, size_t bytes) { @@ -167,11 +224,9 @@ static int imx_ocotp_write(void *context, unsigned int offset, void *val, u32 *buf = val; int ret; - unsigned long clk_rate = 0; - unsigned long strobe_read, relax, strobe_prog; - u32 timing = 0; u32 ctrl; u8 waddr; + u8 word = 0; /* allow only writing one complete OTP word at a time */ if ((bytes != priv->config->word_size) || @@ -187,23 +242,8 @@ static int imx_ocotp_write(void *context, unsigned int offset, void *val, return ret; } - /* 47.3.1.3.1 - * Program HW_OCOTP_TIMING[STROBE_PROG] and HW_OCOTP_TIMING[RELAX] - * fields with timing values to match the current frequency of the - * ipg_clk. OTP writes will work at maximum bus frequencies as long - * as the HW_OCOTP_TIMING parameters are set correctly. - */ - clk_rate = clk_get_rate(priv->clk); - - relax = clk_rate / (1000000000 / DEF_RELAX) - 1; - strobe_prog = clk_rate / (1000000000 / 10000) + 2 * (DEF_RELAX + 1) - 1; - strobe_read = clk_rate / (1000000000 / 40) + 2 * (DEF_RELAX + 1) - 1; - - timing = strobe_prog & 0x00000FFF; - timing |= (relax << 12) & 0x0000F000; - timing |= (strobe_read << 16) & 0x003F0000; - - writel(timing, priv->base + IMX_OCOTP_ADDR_TIMING); + /* Setup the write timing values */ + priv->params->set_timing(priv); /* 47.3.1.3.2 * Check that HW_OCOTP_CTRL[BUSY] and HW_OCOTP_CTRL[ERROR] are clear. @@ -224,8 +264,23 @@ static int imx_ocotp_write(void *context, unsigned int offset, void *val, * description. Both the unlock code and address can be written in the * same operation. */ - /* OTP write/read address specifies one of 128 word address locations */ - waddr = offset / 4; + if (priv->params->bank_address_words != 0) { + /* + * In banked/i.MX7 mode the OTP register bank goes into waddr + * see i.MX 7Solo Applications Processor Reference Manual, Rev. + * 0.1 section 6.4.3.1 + */ + offset = offset / priv->config->word_size; + waddr = offset / priv->params->bank_address_words; + word = offset & (priv->params->bank_address_words - 1); + } else { + /* + * Non-banked i.MX6 mode. + * OTP write/read address specifies one of 128 word address + * locations + */ + waddr = offset / 4; + } ctrl = readl(priv->base + IMX_OCOTP_ADDR_CTRL); ctrl &= ~IMX_OCOTP_BM_CTRL_ADDR; @@ -251,8 +306,43 @@ static int imx_ocotp_write(void *context, unsigned int offset, void *val, * shift right (with zero fill). This shifting is required to program * the OTP serially. During the write operation, HW_OCOTP_DATA cannot be * modified. + * Note: on i.MX7 there are four data fields to write for banked write + * with the fuse blowing operation only taking place after data0 + * has been written. This is why data0 must always be the last + * register written. */ - writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA); + if (priv->params->bank_address_words != 0) { + /* Banked/i.MX7 mode */ + switch (word) { + case 0: + writel(0, priv->base + IMX_OCOTP_ADDR_DATA1); + writel(0, priv->base + IMX_OCOTP_ADDR_DATA2); + writel(0, priv->base + IMX_OCOTP_ADDR_DATA3); + writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA0); + break; + case 1: + writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA1); + writel(0, priv->base + IMX_OCOTP_ADDR_DATA2); + writel(0, priv->base + IMX_OCOTP_ADDR_DATA3); + writel(0, priv->base + IMX_OCOTP_ADDR_DATA0); + break; + case 2: + writel(0, priv->base + IMX_OCOTP_ADDR_DATA1); + writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA2); + writel(0, priv->base + IMX_OCOTP_ADDR_DATA3); + writel(0, priv->base + IMX_OCOTP_ADDR_DATA0); + break; + case 3: + writel(0, priv->base + IMX_OCOTP_ADDR_DATA1); + writel(0, priv->base + IMX_OCOTP_ADDR_DATA2); + writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA3); + writel(0, priv->base + IMX_OCOTP_ADDR_DATA0); + break; + } + } else { + /* Non-banked i.MX6 mode */ + writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA0); + } /* 47.4.1.4.5 * Once complete, the controller will clear BUSY. A write request to a @@ -303,17 +393,46 @@ static struct nvmem_config imx_ocotp_nvmem_config = { .read_only = false, .word_size = 4, .stride = 4, - .owner = THIS_MODULE, .reg_read = imx_ocotp_read, .reg_write = imx_ocotp_write, }; +static const struct ocotp_params imx6q_params = { + .nregs = 128, + .bank_address_words = 0, + .set_timing = imx_ocotp_set_imx6_timing, +}; + +static const struct ocotp_params imx6sl_params = { + .nregs = 64, + .bank_address_words = 0, + .set_timing = imx_ocotp_set_imx6_timing, +}; + +static const struct ocotp_params imx6sx_params = { + .nregs = 128, + .bank_address_words = 0, + .set_timing = imx_ocotp_set_imx6_timing, +}; + +static const struct ocotp_params imx6ul_params = { + .nregs = 128, + .bank_address_words = 0, + .set_timing = imx_ocotp_set_imx6_timing, +}; + +static const struct ocotp_params imx7d_params = { + .nregs = 64, + .bank_address_words = 4, + .set_timing = imx_ocotp_set_imx7_timing, +}; + static const struct of_device_id imx_ocotp_dt_ids[] = { - { .compatible = "fsl,imx6q-ocotp", (void *)128 }, - { .compatible = "fsl,imx6sl-ocotp", (void *)64 }, - { .compatible = "fsl,imx6sx-ocotp", (void *)128 }, - { .compatible = "fsl,imx6ul-ocotp", (void *)128 }, - { .compatible = "fsl,imx7d-ocotp", (void *)64 }, + { .compatible = "fsl,imx6q-ocotp", .data = &imx6q_params }, + { .compatible = "fsl,imx6sl-ocotp", .data = &imx6sl_params }, + { .compatible = "fsl,imx6sx-ocotp", .data = &imx6sx_params }, + { .compatible = "fsl,imx6ul-ocotp", .data = &imx6ul_params }, + { .compatible = "fsl,imx7d-ocotp", .data = &imx7d_params }, { }, }; MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids); @@ -342,8 +461,8 @@ static int imx_ocotp_probe(struct platform_device *pdev) return PTR_ERR(priv->clk); of_id = of_match_device(imx_ocotp_dt_ids, dev); - priv->nregs = (unsigned long)of_id->data; - imx_ocotp_nvmem_config.size = 4 * priv->nregs; + priv->params = of_device_get_match_data(&pdev->dev); + imx_ocotp_nvmem_config.size = 4 * priv->params->nregs; imx_ocotp_nvmem_config.dev = dev; imx_ocotp_nvmem_config.priv = priv; priv->config = &imx_ocotp_nvmem_config; @@ -375,5 +494,5 @@ static struct platform_driver imx_ocotp_driver = { module_platform_driver(imx_ocotp_driver); MODULE_AUTHOR("Philipp Zabel <p.zabel@pengutronix.de>"); -MODULE_DESCRIPTION("i.MX6 OCOTP fuse box driver"); +MODULE_DESCRIPTION("i.MX6/i.MX7 OCOTP fuse box driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/nvmem/lpc18xx_eeprom.c b/drivers/nvmem/lpc18xx_eeprom.c index 6c7e2c424a4e..b1af966206a6 100644 --- a/drivers/nvmem/lpc18xx_eeprom.c +++ b/drivers/nvmem/lpc18xx_eeprom.c @@ -159,7 +159,6 @@ static struct nvmem_config lpc18xx_nvmem_config = { .word_size = 4, .reg_read = lpc18xx_eeprom_read, .reg_write = lpc18xx_eeprom_gather_write, - .owner = THIS_MODULE, }; static int lpc18xx_eeprom_probe(struct platform_device *pdev) diff --git a/drivers/nvmem/lpc18xx_otp.c b/drivers/nvmem/lpc18xx_otp.c index be8d07403ffc..95268db155e9 100644 --- a/drivers/nvmem/lpc18xx_otp.c +++ b/drivers/nvmem/lpc18xx_otp.c @@ -64,7 +64,6 @@ static struct nvmem_config lpc18xx_otp_nvmem_config = { .read_only = true, .word_size = LPC18XX_OTP_WORD_SIZE, .stride = LPC18XX_OTP_WORD_SIZE, - .owner = THIS_MODULE, .reg_read = lpc18xx_otp_read, }; diff --git a/drivers/nvmem/meson-efuse.c b/drivers/nvmem/meson-efuse.c index 70bfc9839bb2..a43c68f90937 100644 --- a/drivers/nvmem/meson-efuse.c +++ b/drivers/nvmem/meson-efuse.c @@ -1,5 +1,5 @@ /* - * Amlogic eFuse Driver + * Amlogic Meson GX eFuse Driver * * Copyright (c) 2016 Endless Computers, Inc. * Author: Carlo Caione <carlo@endlessm.com> @@ -37,7 +37,6 @@ static int meson_efuse_read(void *context, unsigned int offset, static struct nvmem_config econfig = { .name = "meson-efuse", - .owner = THIS_MODULE, .stride = 1, .word_size = 1, .read_only = true, @@ -89,5 +88,5 @@ static struct platform_driver meson_efuse_driver = { module_platform_driver(meson_efuse_driver); MODULE_AUTHOR("Carlo Caione <carlo@endlessm.com>"); -MODULE_DESCRIPTION("Amlogic Meson NVMEM driver"); +MODULE_DESCRIPTION("Amlogic Meson GX NVMEM driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/nvmem/meson-mx-efuse.c b/drivers/nvmem/meson-mx-efuse.c new file mode 100644 index 000000000000..a346b4923550 --- /dev/null +++ b/drivers/nvmem/meson-mx-efuse.c @@ -0,0 +1,265 @@ +/* + * Amlogic Meson6, Meson8 and Meson8b eFuse Driver + * + * Copyright (c) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com> + * + * 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. + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/nvmem-provider.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/sizes.h> +#include <linux/slab.h> + +#define MESON_MX_EFUSE_CNTL1 0x04 +#define MESON_MX_EFUSE_CNTL1_PD_ENABLE BIT(27) +#define MESON_MX_EFUSE_CNTL1_AUTO_RD_BUSY BIT(26) +#define MESON_MX_EFUSE_CNTL1_AUTO_RD_START BIT(25) +#define MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE BIT(24) +#define MESON_MX_EFUSE_CNTL1_BYTE_WR_DATA GENMASK(23, 16) +#define MESON_MX_EFUSE_CNTL1_AUTO_WR_BUSY BIT(14) +#define MESON_MX_EFUSE_CNTL1_AUTO_WR_START BIT(13) +#define MESON_MX_EFUSE_CNTL1_AUTO_WR_ENABLE BIT(12) +#define MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET BIT(11) +#define MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK GENMASK(10, 0) + +#define MESON_MX_EFUSE_CNTL2 0x08 + +#define MESON_MX_EFUSE_CNTL4 0x10 +#define MESON_MX_EFUSE_CNTL4_ENCRYPT_ENABLE BIT(10) + +struct meson_mx_efuse_platform_data { + const char *name; + unsigned int word_size; +}; + +struct meson_mx_efuse { + void __iomem *base; + struct clk *core_clk; + struct nvmem_device *nvmem; + struct nvmem_config config; +}; + +static void meson_mx_efuse_mask_bits(struct meson_mx_efuse *efuse, u32 reg, + u32 mask, u32 set) +{ + u32 data; + + data = readl(efuse->base + reg); + data &= ~mask; + data |= (set & mask); + + writel(data, efuse->base + reg); +} + +static int meson_mx_efuse_hw_enable(struct meson_mx_efuse *efuse) +{ + int err; + + err = clk_prepare_enable(efuse->core_clk); + if (err) + return err; + + /* power up the efuse */ + meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, + MESON_MX_EFUSE_CNTL1_PD_ENABLE, 0); + + meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL4, + MESON_MX_EFUSE_CNTL4_ENCRYPT_ENABLE, 0); + + return 0; +} + +static void meson_mx_efuse_hw_disable(struct meson_mx_efuse *efuse) +{ + meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, + MESON_MX_EFUSE_CNTL1_PD_ENABLE, + MESON_MX_EFUSE_CNTL1_PD_ENABLE); + + clk_disable_unprepare(efuse->core_clk); +} + +static int meson_mx_efuse_read_addr(struct meson_mx_efuse *efuse, + unsigned int addr, u32 *value) +{ + int err; + u32 regval; + + /* write the address to read */ + regval = FIELD_PREP(MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK, addr); + meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, + MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK, regval); + + /* inform the hardware that we changed the address */ + meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, + MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET, + MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET); + meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, + MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET, 0); + + /* start the read process */ + meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, + MESON_MX_EFUSE_CNTL1_AUTO_RD_START, + MESON_MX_EFUSE_CNTL1_AUTO_RD_START); + meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, + MESON_MX_EFUSE_CNTL1_AUTO_RD_START, 0); + + /* + * perform a dummy read to ensure that the HW has the RD_BUSY bit set + * when polling for the status below. + */ + readl(efuse->base + MESON_MX_EFUSE_CNTL1); + + err = readl_poll_timeout_atomic(efuse->base + MESON_MX_EFUSE_CNTL1, + regval, + (!(regval & MESON_MX_EFUSE_CNTL1_AUTO_RD_BUSY)), + 1, 1000); + if (err) { + dev_err(efuse->config.dev, + "Timeout while reading efuse address %u\n", addr); + return err; + } + + *value = readl(efuse->base + MESON_MX_EFUSE_CNTL2); + + return 0; +} + +static int meson_mx_efuse_read(void *context, unsigned int offset, + void *buf, size_t bytes) +{ + struct meson_mx_efuse *efuse = context; + u32 tmp; + int err, i, addr; + + err = meson_mx_efuse_hw_enable(efuse); + if (err) + return err; + + meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, + MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE, + MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE); + + for (i = offset; i < offset + bytes; i += efuse->config.word_size) { + addr = i / efuse->config.word_size; + + err = meson_mx_efuse_read_addr(efuse, addr, &tmp); + if (err) + break; + + memcpy(buf + i, &tmp, efuse->config.word_size); + } + + meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, + MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE, 0); + + meson_mx_efuse_hw_disable(efuse); + + return err; +} + +static const struct meson_mx_efuse_platform_data meson6_efuse_data = { + .name = "meson6-efuse", + .word_size = 1, +}; + +static const struct meson_mx_efuse_platform_data meson8_efuse_data = { + .name = "meson8-efuse", + .word_size = 4, +}; + +static const struct meson_mx_efuse_platform_data meson8b_efuse_data = { + .name = "meson8b-efuse", + .word_size = 4, +}; + +static const struct of_device_id meson_mx_efuse_match[] = { + { .compatible = "amlogic,meson6-efuse", .data = &meson6_efuse_data }, + { .compatible = "amlogic,meson8-efuse", .data = &meson8_efuse_data }, + { .compatible = "amlogic,meson8b-efuse", .data = &meson8b_efuse_data }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, meson_mx_efuse_match); + +static int meson_mx_efuse_probe(struct platform_device *pdev) +{ + const struct meson_mx_efuse_platform_data *drvdata; + struct meson_mx_efuse *efuse; + struct resource *res; + + drvdata = of_device_get_match_data(&pdev->dev); + if (!drvdata) + return -EINVAL; + + efuse = devm_kzalloc(&pdev->dev, sizeof(*efuse), GFP_KERNEL); + if (!efuse) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + efuse->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(efuse->base)) + return PTR_ERR(efuse->base); + + efuse->config.name = devm_kstrdup(&pdev->dev, drvdata->name, + GFP_KERNEL); + efuse->config.owner = THIS_MODULE; + efuse->config.dev = &pdev->dev; + efuse->config.priv = efuse; + efuse->config.stride = drvdata->word_size; + efuse->config.word_size = drvdata->word_size; + efuse->config.size = SZ_512; + efuse->config.read_only = true; + efuse->config.reg_read = meson_mx_efuse_read; + + efuse->core_clk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(efuse->core_clk)) { + dev_err(&pdev->dev, "Failed to get core clock\n"); + return PTR_ERR(efuse->core_clk); + } + + efuse->nvmem = nvmem_register(&efuse->config); + if (IS_ERR(efuse->nvmem)) + return PTR_ERR(efuse->nvmem); + + platform_set_drvdata(pdev, efuse); + + return 0; +} + +static int meson_mx_efuse_remove(struct platform_device *pdev) +{ + struct meson_mx_efuse *efuse = platform_get_drvdata(pdev); + + return nvmem_unregister(efuse->nvmem); +} + +static struct platform_driver meson_mx_efuse_driver = { + .probe = meson_mx_efuse_probe, + .remove = meson_mx_efuse_remove, + .driver = { + .name = "meson-mx-efuse", + .of_match_table = meson_mx_efuse_match, + }, +}; + +module_platform_driver(meson_mx_efuse_driver); + +MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>"); +MODULE_DESCRIPTION("Amlogic Meson MX eFuse NVMEM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/nvmem/mtk-efuse.c b/drivers/nvmem/mtk-efuse.c index 32fd572e18c5..9ee3479cfc7b 100644 --- a/drivers/nvmem/mtk-efuse.c +++ b/drivers/nvmem/mtk-efuse.c @@ -18,15 +18,19 @@ #include <linux/nvmem-provider.h> #include <linux/platform_device.h> +struct mtk_efuse_priv { + void __iomem *base; +}; + static int mtk_reg_read(void *context, unsigned int reg, void *_val, size_t bytes) { - void __iomem *base = context; + struct mtk_efuse_priv *priv = context; u32 *val = _val; int i = 0, words = bytes / 4; while (words--) - *val++ = readl(base + reg + (i++ * 4)); + *val++ = readl(priv->base + reg + (i++ * 4)); return 0; } @@ -34,12 +38,12 @@ static int mtk_reg_read(void *context, static int mtk_reg_write(void *context, unsigned int reg, void *_val, size_t bytes) { - void __iomem *base = context; + struct mtk_efuse_priv *priv = context; u32 *val = _val; int i = 0, words = bytes / 4; while (words--) - writel(*val++, base + reg + (i++ * 4)); + writel(*val++, priv->base + reg + (i++ * 4)); return 0; } @@ -49,27 +53,26 @@ static int mtk_efuse_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct resource *res; struct nvmem_device *nvmem; - struct nvmem_config *econfig; - void __iomem *base; + struct nvmem_config econfig = {}; + struct mtk_efuse_priv *priv; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - econfig = devm_kzalloc(dev, sizeof(*econfig), GFP_KERNEL); - if (!econfig) + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) return -ENOMEM; - econfig->stride = 4; - econfig->word_size = 4; - econfig->reg_read = mtk_reg_read; - econfig->reg_write = mtk_reg_write; - econfig->size = resource_size(res); - econfig->priv = base; - econfig->dev = dev; - econfig->owner = THIS_MODULE; - nvmem = nvmem_register(econfig); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + econfig.stride = 4; + econfig.word_size = 4; + econfig.reg_read = mtk_reg_read; + econfig.reg_write = mtk_reg_write; + econfig.size = resource_size(res); + econfig.priv = priv; + econfig.dev = dev; + nvmem = nvmem_register(&econfig); if (IS_ERR(nvmem)) return PTR_ERR(nvmem); diff --git a/drivers/nvmem/mxs-ocotp.c b/drivers/nvmem/mxs-ocotp.c index d26dd03cec80..7018e2ef5714 100644 --- a/drivers/nvmem/mxs-ocotp.c +++ b/drivers/nvmem/mxs-ocotp.c @@ -118,7 +118,6 @@ static struct nvmem_config ocotp_config = { .name = "mxs-ocotp", .stride = 16, .word_size = 4, - .owner = THIS_MODULE, .reg_read = mxs_ocotp_read, }; diff --git a/drivers/nvmem/qfprom.c b/drivers/nvmem/qfprom.c index 2bdb6c389328..cb3b48b47d64 100644 --- a/drivers/nvmem/qfprom.c +++ b/drivers/nvmem/qfprom.c @@ -17,15 +17,19 @@ #include <linux/nvmem-provider.h> #include <linux/platform_device.h> +struct qfprom_priv { + void __iomem *base; +}; + static int qfprom_reg_read(void *context, unsigned int reg, void *_val, size_t bytes) { - void __iomem *base = context; + struct qfprom_priv *priv = context; u8 *val = _val; int i = 0, words = bytes; while (words--) - *val++ = readb(base + reg + i++); + *val++ = readb(priv->base + reg + i++); return 0; } @@ -33,12 +37,12 @@ static int qfprom_reg_read(void *context, static int qfprom_reg_write(void *context, unsigned int reg, void *_val, size_t bytes) { - void __iomem *base = context; + struct qfprom_priv *priv = context; u8 *val = _val; int i = 0, words = bytes; while (words--) - writeb(*val++, base + reg + i++); + writeb(*val++, priv->base + reg + i++); return 0; } @@ -52,7 +56,6 @@ static int qfprom_remove(struct platform_device *pdev) static struct nvmem_config econfig = { .name = "qfprom", - .owner = THIS_MODULE, .stride = 1, .word_size = 1, .reg_read = qfprom_reg_read, @@ -64,16 +67,20 @@ static int qfprom_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct resource *res; struct nvmem_device *nvmem; - void __iomem *base; + struct qfprom_priv *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); + priv->base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); econfig.size = resource_size(res); econfig.dev = dev; - econfig.priv = base; + econfig.priv = priv; nvmem = nvmem_register(&econfig); if (IS_ERR(nvmem)) diff --git a/drivers/nvmem/rockchip-efuse.c b/drivers/nvmem/rockchip-efuse.c index 63e3eb55f3ac..123de77ca5d6 100644 --- a/drivers/nvmem/rockchip-efuse.c +++ b/drivers/nvmem/rockchip-efuse.c @@ -149,7 +149,6 @@ static int rockchip_rk3399_efuse_read(void *context, unsigned int offset, static struct nvmem_config econfig = { .name = "rockchip-efuse", - .owner = THIS_MODULE, .stride = 1, .word_size = 1, .read_only = true, @@ -178,6 +177,10 @@ static const struct of_device_id rockchip_efuse_match[] = { .data = (void *)&rockchip_rk3288_efuse_read, }, { + .compatible = "rockchip,rk3368-efuse", + .data = (void *)&rockchip_rk3288_efuse_read, + }, + { .compatible = "rockchip,rk3399-efuse", .data = (void *)&rockchip_rk3399_efuse_read, }, diff --git a/drivers/nvmem/snvs_lpgpr.c b/drivers/nvmem/snvs_lpgpr.c new file mode 100644 index 000000000000..e5c2a4a17f03 --- /dev/null +++ b/drivers/nvmem/snvs_lpgpr.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2015 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de> + * Copyright (c) 2017 Pengutronix, Oleksij Rempel <kernel@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + */ + +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/nvmem-provider.h> +#include <linux/of_device.h> +#include <linux/regmap.h> + +#define IMX6Q_SNVS_HPLR 0x00 +#define IMX6Q_GPR_SL BIT(5) +#define IMX6Q_SNVS_LPLR 0x34 +#define IMX6Q_GPR_HL BIT(5) +#define IMX6Q_SNVS_LPGPR 0x68 + +struct snvs_lpgpr_cfg { + int offset; + int offset_hplr; + int offset_lplr; +}; + +struct snvs_lpgpr_priv { + struct device_d *dev; + struct regmap *regmap; + struct nvmem_config cfg; + const struct snvs_lpgpr_cfg *dcfg; +}; + +static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx6q = { + .offset = IMX6Q_SNVS_LPGPR, + .offset_hplr = IMX6Q_SNVS_HPLR, + .offset_lplr = IMX6Q_SNVS_LPLR, +}; + +static int snvs_lpgpr_write(void *context, unsigned int offset, void *val, + size_t bytes) +{ + struct snvs_lpgpr_priv *priv = context; + const struct snvs_lpgpr_cfg *dcfg = priv->dcfg; + unsigned int lock_reg; + int ret; + + ret = regmap_read(priv->regmap, dcfg->offset_hplr, &lock_reg); + if (ret < 0) + return ret; + + if (lock_reg & IMX6Q_GPR_SL) + return -EPERM; + + ret = regmap_read(priv->regmap, dcfg->offset_lplr, &lock_reg); + if (ret < 0) + return ret; + + if (lock_reg & IMX6Q_GPR_HL) + return -EPERM; + + return regmap_bulk_write(priv->regmap, dcfg->offset + offset, val, + bytes / 4); +} + +static int snvs_lpgpr_read(void *context, unsigned int offset, void *val, + size_t bytes) +{ + struct snvs_lpgpr_priv *priv = context; + const struct snvs_lpgpr_cfg *dcfg = priv->dcfg; + + return regmap_bulk_read(priv->regmap, dcfg->offset + offset, + val, bytes / 4); +} + +static int snvs_lpgpr_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct device_node *syscon_node; + struct snvs_lpgpr_priv *priv; + struct nvmem_config *cfg; + struct nvmem_device *nvmem; + const struct snvs_lpgpr_cfg *dcfg; + + if (!node) + return -ENOENT; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dcfg = of_device_get_match_data(dev); + if (!dcfg) + return -EINVAL; + + syscon_node = of_get_parent(node); + if (!syscon_node) + return -ENODEV; + + priv->regmap = syscon_node_to_regmap(syscon_node); + of_node_put(syscon_node); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->dcfg = dcfg; + + cfg = &priv->cfg; + cfg->priv = priv; + cfg->name = dev_name(dev); + cfg->dev = dev; + cfg->stride = 4, + cfg->word_size = 4, + cfg->size = 4, + cfg->owner = THIS_MODULE, + cfg->reg_read = snvs_lpgpr_read, + cfg->reg_write = snvs_lpgpr_write, + + nvmem = nvmem_register(cfg); + if (IS_ERR(nvmem)) + return PTR_ERR(nvmem); + + platform_set_drvdata(pdev, nvmem); + + return 0; +} + +static int snvs_lpgpr_remove(struct platform_device *pdev) +{ + struct nvmem_device *nvmem = platform_get_drvdata(pdev); + + return nvmem_unregister(nvmem); +} + +static const struct of_device_id snvs_lpgpr_dt_ids[] = { + { .compatible = "fsl,imx6q-snvs-lpgpr", .data = &snvs_lpgpr_cfg_imx6q }, + { .compatible = "fsl,imx6ul-snvs-lpgpr", + .data = &snvs_lpgpr_cfg_imx6q }, + { }, +}; +MODULE_DEVICE_TABLE(of, snvs_lpgpr_dt_ids); + +static struct platform_driver snvs_lpgpr_driver = { + .probe = snvs_lpgpr_probe, + .remove = snvs_lpgpr_remove, + .driver = { + .name = "snvs_lpgpr", + .of_match_table = snvs_lpgpr_dt_ids, + }, +}; +module_platform_driver(snvs_lpgpr_driver); + +MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>"); +MODULE_DESCRIPTION("Low Power General Purpose Register in i.MX6 Secure Non-Volatile Storage"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/nvmem/sunxi_sid.c b/drivers/nvmem/sunxi_sid.c index 0d6648be93b8..99bd54d85fcb 100644 --- a/drivers/nvmem/sunxi_sid.c +++ b/drivers/nvmem/sunxi_sid.c @@ -40,7 +40,6 @@ static struct nvmem_config econfig = { .read_only = true, .stride = 4, .word_size = 1, - .owner = THIS_MODULE, }; struct sunxi_sid_cfg { @@ -199,10 +198,16 @@ static const struct sunxi_sid_cfg sun8i_h3_cfg = { .need_register_readout = true, }; +static const struct sunxi_sid_cfg sun50i_a64_cfg = { + .value_offset = 0x200, + .size = 0x100, +}; + static const struct of_device_id sunxi_sid_of_match[] = { { .compatible = "allwinner,sun4i-a10-sid", .data = &sun4i_a10_cfg }, { .compatible = "allwinner,sun7i-a20-sid", .data = &sun7i_a20_cfg }, { .compatible = "allwinner,sun8i-h3-sid", .data = &sun8i_h3_cfg }, + { .compatible = "allwinner,sun50i-a64-sid", .data = &sun50i_a64_cfg }, {/* sentinel */}, }; MODULE_DEVICE_TABLE(of, sunxi_sid_of_match); diff --git a/drivers/nvmem/uniphier-efuse.c b/drivers/nvmem/uniphier-efuse.c new file mode 100644 index 000000000000..9d278b4e1dc7 --- /dev/null +++ b/drivers/nvmem/uniphier-efuse.c @@ -0,0 +1,97 @@ +/* + * UniPhier eFuse driver + * + * Copyright (C) 2017 Socionext Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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/device.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/nvmem-provider.h> +#include <linux/platform_device.h> + +struct uniphier_efuse_priv { + void __iomem *base; +}; + +static int uniphier_reg_read(void *context, + unsigned int reg, void *_val, size_t bytes) +{ + struct uniphier_efuse_priv *priv = context; + u32 *val = _val; + int offs; + + for (offs = 0; offs < bytes; offs += sizeof(u32)) + *val++ = readl(priv->base + reg + offs); + + return 0; +} + +static int uniphier_efuse_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct nvmem_device *nvmem; + struct nvmem_config econfig = {}; + struct uniphier_efuse_priv *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + econfig.stride = 4; + econfig.word_size = 4; + econfig.read_only = true; + econfig.reg_read = uniphier_reg_read; + econfig.size = resource_size(res); + econfig.priv = priv; + econfig.dev = dev; + nvmem = nvmem_register(&econfig); + if (IS_ERR(nvmem)) + return PTR_ERR(nvmem); + + platform_set_drvdata(pdev, nvmem); + + return 0; +} + +static int uniphier_efuse_remove(struct platform_device *pdev) +{ + struct nvmem_device *nvmem = platform_get_drvdata(pdev); + + return nvmem_unregister(nvmem); +} + +static const struct of_device_id uniphier_efuse_of_match[] = { + { .compatible = "socionext,uniphier-efuse",}, + {/* sentinel */}, +}; +MODULE_DEVICE_TABLE(of, uniphier_efuse_of_match); + +static struct platform_driver uniphier_efuse_driver = { + .probe = uniphier_efuse_probe, + .remove = uniphier_efuse_remove, + .driver = { + .name = "uniphier-efuse", + .of_match_table = uniphier_efuse_of_match, + }, +}; +module_platform_driver(uniphier_efuse_driver); + +MODULE_AUTHOR("Keiji Hayashibara <hayashibara.keiji@socionext.com>"); +MODULE_DESCRIPTION("UniPhier eFuse driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/nvmem/vf610-ocotp.c b/drivers/nvmem/vf610-ocotp.c index 72e4faabce29..5ae9e002f195 100644 --- a/drivers/nvmem/vf610-ocotp.c +++ b/drivers/nvmem/vf610-ocotp.c @@ -206,7 +206,6 @@ static int vf610_ocotp_read(void *context, unsigned int offset, static struct nvmem_config ocotp_config = { .name = "ocotp", - .owner = THIS_MODULE, .stride = 4, .word_size = 4, .reg_read = vf610_ocotp_read, diff --git a/drivers/parport/parport_ip32.c b/drivers/parport/parport_ip32.c index 0186db7680d4..62873070f988 100644 --- a/drivers/parport/parport_ip32.c +++ b/drivers/parport/parport_ip32.c @@ -1769,7 +1769,7 @@ stop: /*--- Default parport operations ---------------------------------------*/ -static __initdata struct parport_operations parport_ip32_ops = { +static const struct parport_operations parport_ip32_ops __initconst = { .write_data = parport_ip32_write_data, .read_data = parport_ip32_read_data, diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 55ef7d1fd8da..102646fedb56 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -1599,7 +1599,7 @@ static ssize_t pccard_store_cis(struct file *filp, struct kobject *kobj, } -struct bin_attribute pccard_cis_attr = { +const struct bin_attribute pccard_cis_attr = { .attr = { .name = "cis", .mode = S_IRUGO | S_IWUSR }, .size = 0x200, .read = pccard_show_cis, diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index e86cd6b31773..6765beadea95 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -152,7 +152,7 @@ void pcmcia_cleanup_irq(struct pcmcia_socket *s); int pcmcia_setup_irq(struct pcmcia_device *p_dev); /* cistpl.c */ -extern struct bin_attribute pccard_cis_attr; +extern const struct bin_attribute pccard_cis_attr; int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr); diff --git a/drivers/pcmcia/m32r_cfc.c b/drivers/pcmcia/m32r_cfc.c index 70b089430fcc..9a4940e56e2f 100644 --- a/drivers/pcmcia/m32r_cfc.c +++ b/drivers/pcmcia/m32r_cfc.c @@ -380,11 +380,10 @@ static irqreturn_t pcc_interrupt(int irq, void *dev) return IRQ_RETVAL(handled); } /* pcc_interrupt */ -static void pcc_interrupt_wrapper(u_long data) +static void pcc_interrupt_wrapper(struct timer_list *unused) { pr_debug("m32r_cfc: pcc_interrupt_wrapper:\n"); pcc_interrupt(0, NULL); - init_timer(&poll_timer); poll_timer.expires = jiffies + poll_interval; add_timer(&poll_timer); } @@ -758,9 +757,7 @@ static int __init init_m32r_pcc(void) /* Finally, schedule a polling interrupt */ if (poll_interval != 0) { - poll_timer.function = pcc_interrupt_wrapper; - poll_timer.data = 0; - init_timer(&poll_timer); + timer_setup(&poll_timer, pcc_interrupt_wrapper, 0); poll_timer.expires = jiffies + poll_interval; add_timer(&poll_timer); } diff --git a/drivers/pcmcia/m32r_pcc.c b/drivers/pcmcia/m32r_pcc.c index e50bbf826188..c2239a7e383a 100644 --- a/drivers/pcmcia/m32r_pcc.c +++ b/drivers/pcmcia/m32r_pcc.c @@ -386,10 +386,9 @@ static irqreturn_t pcc_interrupt(int irq, void *dev) return IRQ_RETVAL(handled); } /* pcc_interrupt */ -static void pcc_interrupt_wrapper(u_long data) +static void pcc_interrupt_wrapper(struct timer_list *unused) { pcc_interrupt(0, NULL); - init_timer(&poll_timer); poll_timer.expires = jiffies + poll_interval; add_timer(&poll_timer); } @@ -729,9 +728,7 @@ static int __init init_m32r_pcc(void) /* Finally, schedule a polling interrupt */ if (poll_interval != 0) { - poll_timer.function = pcc_interrupt_wrapper; - poll_timer.data = 0; - init_timer(&poll_timer); + timer_setup(&poll_timer, pcc_interrupt_wrapper, 0); poll_timer.expires = jiffies + poll_interval; add_timer(&poll_timer); } diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index d674e06767a5..1424581fd9af 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -225,6 +225,7 @@ static void tb_activate_pcie_devices(struct tb *tb) tb_port_info(up_port, "PCIe tunnel activation failed, aborting\n"); tb_pci_free(tunnel); + continue; } list_add(&tunnel->list, &tcm->tunnel_list); diff --git a/drivers/vme/bridges/vme_ca91cx42.c b/drivers/vme/bridges/vme_ca91cx42.c index 7cc51223db1c..5dd284008630 100644 --- a/drivers/vme/bridges/vme_ca91cx42.c +++ b/drivers/vme/bridges/vme_ca91cx42.c @@ -511,7 +511,7 @@ static int ca91cx42_alloc_resource(struct vme_master_resource *image, ca91cx42_bridge = image->parent; /* Find pci_dev container of dev */ - if (ca91cx42_bridge->parent == NULL) { + if (!ca91cx42_bridge->parent) { dev_err(ca91cx42_bridge->parent, "Dev entry NULL\n"); return -EINVAL; } @@ -529,14 +529,12 @@ static int ca91cx42_alloc_resource(struct vme_master_resource *image, image->kern_base = NULL; kfree(image->bus_resource.name); release_resource(&image->bus_resource); - memset(&image->bus_resource, 0, sizeof(struct resource)); + memset(&image->bus_resource, 0, sizeof(image->bus_resource)); } - if (image->bus_resource.name == NULL) { + if (!image->bus_resource.name) { image->bus_resource.name = kmalloc(VMENAMSIZ+3, GFP_ATOMIC); - if (image->bus_resource.name == NULL) { - dev_err(ca91cx42_bridge->parent, "Unable to allocate " - "memory for resource name\n"); + if (!image->bus_resource.name) { retval = -ENOMEM; goto err_name; } @@ -562,7 +560,7 @@ static int ca91cx42_alloc_resource(struct vme_master_resource *image, image->kern_base = ioremap_nocache( image->bus_resource.start, size); - if (image->kern_base == NULL) { + if (!image->kern_base) { dev_err(ca91cx42_bridge->parent, "Failed to remap resource\n"); retval = -ENOMEM; goto err_remap; @@ -574,7 +572,7 @@ err_remap: release_resource(&image->bus_resource); err_resource: kfree(image->bus_resource.name); - memset(&image->bus_resource, 0, sizeof(struct resource)); + memset(&image->bus_resource, 0, sizeof(image->bus_resource)); err_name: return retval; } @@ -588,7 +586,7 @@ static void ca91cx42_free_resource(struct vme_master_resource *image) image->kern_base = NULL; release_resource(&image->bus_resource); kfree(image->bus_resource.name); - memset(&image->bus_resource, 0, sizeof(struct resource)); + memset(&image->bus_resource, 0, sizeof(image->bus_resource)); } @@ -1036,10 +1034,8 @@ static int ca91cx42_dma_list_add(struct vme_dma_list *list, dev = list->parent->parent->parent; /* XXX descriptor must be aligned on 64-bit boundaries */ - entry = kmalloc(sizeof(struct ca91cx42_dma_entry), GFP_KERNEL); - if (entry == NULL) { - dev_err(dev, "Failed to allocate memory for dma resource " - "structure\n"); + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { retval = -ENOMEM; goto err_mem; } @@ -1052,7 +1048,7 @@ static int ca91cx42_dma_list_add(struct vme_dma_list *list, goto err_align; } - memset(&entry->descriptor, 0, sizeof(struct ca91cx42_dma_descriptor)); + memset(&entry->descriptor, 0, sizeof(entry->descriptor)); if (dest->type == VME_DMA_VME) { entry->descriptor.dctl |= CA91CX42_DCTL_L2V; @@ -1323,7 +1319,7 @@ static int ca91cx42_lm_set(struct vme_lm_resource *lm, /* If we already have a callback attached, we can't move it! */ for (i = 0; i < lm->monitors; i++) { - if (bridge->lm_callback[i] != NULL) { + if (bridge->lm_callback[i]) { mutex_unlock(&lm->mtx); dev_err(dev, "Location monitor callback attached, " "can't reset\n"); @@ -1432,7 +1428,7 @@ static int ca91cx42_lm_attach(struct vme_lm_resource *lm, int monitor, } /* Check that a callback isn't already attached */ - if (bridge->lm_callback[monitor] != NULL) { + if (bridge->lm_callback[monitor]) { mutex_unlock(&lm->mtx); dev_err(dev, "Existing callback attached\n"); return -EBUSY; @@ -1567,7 +1563,7 @@ static int ca91cx42_crcsr_init(struct vme_bridge *ca91cx42_bridge, /* Allocate mem for CR/CSR image */ bridge->crcsr_kernel = pci_zalloc_consistent(pdev, VME_CRCSR_BUF_SIZE, &bridge->crcsr_bus); - if (bridge->crcsr_kernel == NULL) { + if (!bridge->crcsr_kernel) { dev_err(&pdev->dev, "Failed to allocate memory for CR/CSR " "image\n"); return -ENOMEM; @@ -1618,21 +1614,15 @@ static int ca91cx42_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* We want to support more than one of each bridge so we need to * dynamically allocate the bridge structure */ - ca91cx42_bridge = kzalloc(sizeof(struct vme_bridge), GFP_KERNEL); - - if (ca91cx42_bridge == NULL) { - dev_err(&pdev->dev, "Failed to allocate memory for device " - "structure\n"); + ca91cx42_bridge = kzalloc(sizeof(*ca91cx42_bridge), GFP_KERNEL); + if (!ca91cx42_bridge) { retval = -ENOMEM; goto err_struct; } vme_init_bridge(ca91cx42_bridge); - ca91cx42_device = kzalloc(sizeof(struct ca91cx42_driver), GFP_KERNEL); - - if (ca91cx42_device == NULL) { - dev_err(&pdev->dev, "Failed to allocate memory for device " - "structure\n"); + ca91cx42_device = kzalloc(sizeof(*ca91cx42_device), GFP_KERNEL); + if (!ca91cx42_device) { retval = -ENOMEM; goto err_driver; } @@ -1688,11 +1678,8 @@ static int ca91cx42_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* Add master windows to list */ for (i = 0; i < CA91C142_MAX_MASTER; i++) { - master_image = kmalloc(sizeof(struct vme_master_resource), - GFP_KERNEL); - if (master_image == NULL) { - dev_err(&pdev->dev, "Failed to allocate memory for " - "master resource structure\n"); + master_image = kmalloc(sizeof(*master_image), GFP_KERNEL); + if (!master_image) { retval = -ENOMEM; goto err_master; } @@ -1706,7 +1693,7 @@ static int ca91cx42_probe(struct pci_dev *pdev, const struct pci_device_id *id) VME_SUPER | VME_USER | VME_PROG | VME_DATA; master_image->width_attr = VME_D8 | VME_D16 | VME_D32 | VME_D64; memset(&master_image->bus_resource, 0, - sizeof(struct resource)); + sizeof(master_image->bus_resource)); master_image->kern_base = NULL; list_add_tail(&master_image->list, &ca91cx42_bridge->master_resources); @@ -1714,11 +1701,8 @@ static int ca91cx42_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* Add slave windows to list */ for (i = 0; i < CA91C142_MAX_SLAVE; i++) { - slave_image = kmalloc(sizeof(struct vme_slave_resource), - GFP_KERNEL); - if (slave_image == NULL) { - dev_err(&pdev->dev, "Failed to allocate memory for " - "slave resource structure\n"); + slave_image = kmalloc(sizeof(*slave_image), GFP_KERNEL); + if (!slave_image) { retval = -ENOMEM; goto err_slave; } @@ -1741,11 +1725,8 @@ static int ca91cx42_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* Add dma engines to list */ for (i = 0; i < CA91C142_MAX_DMA; i++) { - dma_ctrlr = kmalloc(sizeof(struct vme_dma_resource), - GFP_KERNEL); - if (dma_ctrlr == NULL) { - dev_err(&pdev->dev, "Failed to allocate memory for " - "dma resource structure\n"); + dma_ctrlr = kmalloc(sizeof(*dma_ctrlr), GFP_KERNEL); + if (!dma_ctrlr) { retval = -ENOMEM; goto err_dma; } @@ -1762,10 +1743,8 @@ static int ca91cx42_probe(struct pci_dev *pdev, const struct pci_device_id *id) } /* Add location monitor to list */ - lm = kmalloc(sizeof(struct vme_lm_resource), GFP_KERNEL); - if (lm == NULL) { - dev_err(&pdev->dev, "Failed to allocate memory for " - "location monitor resource structure\n"); + lm = kmalloc(sizeof(*lm), GFP_KERNEL); + if (!lm) { retval = -ENOMEM; goto err_lm; } diff --git a/drivers/vme/bridges/vme_fake.c b/drivers/vme/bridges/vme_fake.c index 30b3acc93833..7d83691047f4 100644 --- a/drivers/vme/bridges/vme_fake.c +++ b/drivers/vme/bridges/vme_fake.c @@ -409,7 +409,7 @@ static void fake_lm_check(struct fake_driver *bridge, unsigned long long addr, /* Each location monitor covers 8 bytes */ if (((lm_base + (8 * i)) <= addr) && ((lm_base + (8 * i) + 8) > addr)) { - if (bridge->lm_callback[i] != NULL) + if (bridge->lm_callback[i]) bridge->lm_callback[i]( bridge->lm_data[i]); } @@ -866,7 +866,7 @@ static int fake_lm_set(struct vme_lm_resource *lm, unsigned long long lm_base, /* If we already have a callback attached, we can't move it! */ for (i = 0; i < lm->monitors; i++) { - if (bridge->lm_callback[i] != NULL) { + if (bridge->lm_callback[i]) { mutex_unlock(&lm->mtx); pr_err("Location monitor callback attached, can't reset\n"); return -EBUSY; @@ -940,7 +940,7 @@ static int fake_lm_attach(struct vme_lm_resource *lm, int monitor, } /* Check that a callback isn't already attached */ - if (bridge->lm_callback[monitor] != NULL) { + if (bridge->lm_callback[monitor]) { mutex_unlock(&lm->mtx); pr_err("Existing callback attached\n"); return -EBUSY; @@ -978,7 +978,7 @@ static int fake_lm_detach(struct vme_lm_resource *lm, int monitor) /* If all location monitors disabled, disable global Location Monitor */ tmp = 0; for (i = 0; i < lm->monitors; i++) { - if (bridge->lm_callback[i] != NULL) + if (bridge->lm_callback[i]) tmp = 1; } @@ -1003,7 +1003,7 @@ static void *fake_alloc_consistent(struct device *parent, size_t size, { void *alloc = kmalloc(size, GFP_KERNEL); - if (alloc != NULL) + if (alloc) *dma = fake_ptr_to_pci(alloc); return alloc; @@ -1039,7 +1039,7 @@ static int fake_crcsr_init(struct vme_bridge *fake_bridge) /* Allocate mem for CR/CSR image */ bridge->crcsr_kernel = kzalloc(VME_CRCSR_BUF_SIZE, GFP_KERNEL); bridge->crcsr_bus = fake_ptr_to_pci(bridge->crcsr_kernel); - if (bridge->crcsr_kernel == NULL) + if (!bridge->crcsr_kernel) return -ENOMEM; vstat = fake_slot_get(fake_bridge); @@ -1075,14 +1075,14 @@ static int __init fake_init(void) /* If we want to support more than one bridge at some point, we need to * dynamically allocate this so we get one per device. */ - fake_bridge = kzalloc(sizeof(struct vme_bridge), GFP_KERNEL); - if (fake_bridge == NULL) { + fake_bridge = kzalloc(sizeof(*fake_bridge), GFP_KERNEL); + if (!fake_bridge) { retval = -ENOMEM; goto err_struct; } - fake_device = kzalloc(sizeof(struct fake_driver), GFP_KERNEL); - if (fake_device == NULL) { + fake_device = kzalloc(sizeof(*fake_device), GFP_KERNEL); + if (!fake_device) { retval = -ENOMEM; goto err_driver; } @@ -1104,9 +1104,8 @@ static int __init fake_init(void) /* Add master windows to list */ INIT_LIST_HEAD(&fake_bridge->master_resources); for (i = 0; i < FAKE_MAX_MASTER; i++) { - master_image = kmalloc(sizeof(struct vme_master_resource), - GFP_KERNEL); - if (master_image == NULL) { + master_image = kmalloc(sizeof(*master_image), GFP_KERNEL); + if (!master_image) { retval = -ENOMEM; goto err_master; } @@ -1131,9 +1130,8 @@ static int __init fake_init(void) /* Add slave windows to list */ INIT_LIST_HEAD(&fake_bridge->slave_resources); for (i = 0; i < FAKE_MAX_SLAVE; i++) { - slave_image = kmalloc(sizeof(struct vme_slave_resource), - GFP_KERNEL); - if (slave_image == NULL) { + slave_image = kmalloc(sizeof(*slave_image), GFP_KERNEL); + if (!slave_image) { retval = -ENOMEM; goto err_slave; } @@ -1154,9 +1152,8 @@ static int __init fake_init(void) /* Add location monitor to list */ INIT_LIST_HEAD(&fake_bridge->lm_resources); - lm = kmalloc(sizeof(struct vme_lm_resource), GFP_KERNEL); - if (lm == NULL) { - pr_err("Failed to allocate memory for location monitor resource structure\n"); + lm = kmalloc(sizeof(*lm), GFP_KERNEL); + if (!lm) { retval = -ENOMEM; goto err_lm; } diff --git a/drivers/vme/bridges/vme_tsi148.c b/drivers/vme/bridges/vme_tsi148.c index fc1b634b969a..647d231d4422 100644 --- a/drivers/vme/bridges/vme_tsi148.c +++ b/drivers/vme/bridges/vme_tsi148.c @@ -741,18 +741,16 @@ static int tsi148_alloc_resource(struct vme_master_resource *image, image->kern_base = NULL; kfree(image->bus_resource.name); release_resource(&image->bus_resource); - memset(&image->bus_resource, 0, sizeof(struct resource)); + memset(&image->bus_resource, 0, sizeof(image->bus_resource)); } /* Exit here if size is zero */ if (size == 0) return 0; - if (image->bus_resource.name == NULL) { + if (!image->bus_resource.name) { image->bus_resource.name = kmalloc(VMENAMSIZ+3, GFP_ATOMIC); - if (image->bus_resource.name == NULL) { - dev_err(tsi148_bridge->parent, "Unable to allocate " - "memory for resource name\n"); + if (!image->bus_resource.name) { retval = -ENOMEM; goto err_name; } @@ -778,7 +776,7 @@ static int tsi148_alloc_resource(struct vme_master_resource *image, image->kern_base = ioremap_nocache( image->bus_resource.start, size); - if (image->kern_base == NULL) { + if (!image->kern_base) { dev_err(tsi148_bridge->parent, "Failed to remap resource\n"); retval = -ENOMEM; goto err_remap; @@ -790,7 +788,7 @@ err_remap: release_resource(&image->bus_resource); err_resource: kfree(image->bus_resource.name); - memset(&image->bus_resource, 0, sizeof(struct resource)); + memset(&image->bus_resource, 0, sizeof(image->bus_resource)); err_name: return retval; } @@ -804,7 +802,7 @@ static void tsi148_free_resource(struct vme_master_resource *image) image->kern_base = NULL; release_resource(&image->bus_resource); kfree(image->bus_resource.name); - memset(&image->bus_resource, 0, sizeof(struct resource)); + memset(&image->bus_resource, 0, sizeof(image->bus_resource)); } /* @@ -1641,10 +1639,8 @@ static int tsi148_dma_list_add(struct vme_dma_list *list, tsi148_bridge = list->parent->parent; /* Descriptor must be aligned on 64-bit boundaries */ - entry = kmalloc(sizeof(struct tsi148_dma_entry), GFP_KERNEL); - if (entry == NULL) { - dev_err(tsi148_bridge->parent, "Failed to allocate memory for " - "dma resource structure\n"); + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { retval = -ENOMEM; goto err_mem; } @@ -1661,7 +1657,7 @@ static int tsi148_dma_list_add(struct vme_dma_list *list, /* Given we are going to fill out the structure, we probably don't * need to zero it, but better safe than sorry for now. */ - memset(&entry->descriptor, 0, sizeof(struct tsi148_dma_descriptor)); + memset(&entry->descriptor, 0, sizeof(entry->descriptor)); /* Fill out source part */ switch (src->type) { @@ -1756,8 +1752,9 @@ static int tsi148_dma_list_add(struct vme_dma_list *list, list_add_tail(&entry->list, &list->entries); entry->dma_handle = dma_map_single(tsi148_bridge->parent, - &entry->descriptor, - sizeof(struct tsi148_dma_descriptor), DMA_TO_DEVICE); + &entry->descriptor, + sizeof(entry->descriptor), + DMA_TO_DEVICE); if (dma_mapping_error(tsi148_bridge->parent, entry->dma_handle)) { dev_err(tsi148_bridge->parent, "DMA mapping error\n"); retval = -EINVAL; @@ -1946,7 +1943,7 @@ static int tsi148_lm_set(struct vme_lm_resource *lm, unsigned long long lm_base, /* If we already have a callback attached, we can't move it! */ for (i = 0; i < lm->monitors; i++) { - if (bridge->lm_callback[i] != NULL) { + if (bridge->lm_callback[i]) { mutex_unlock(&lm->mtx); dev_err(tsi148_bridge->parent, "Location monitor " "callback attached, can't reset\n"); @@ -2071,7 +2068,7 @@ static int tsi148_lm_attach(struct vme_lm_resource *lm, int monitor, } /* Check that a callback isn't already attached */ - if (bridge->lm_callback[monitor] != NULL) { + if (bridge->lm_callback[monitor]) { mutex_unlock(&lm->mtx); dev_err(tsi148_bridge->parent, "Existing callback attached\n"); return -EBUSY; @@ -2208,7 +2205,7 @@ static int tsi148_crcsr_init(struct vme_bridge *tsi148_bridge, /* Allocate mem for CR/CSR image */ bridge->crcsr_kernel = pci_zalloc_consistent(pdev, VME_CRCSR_BUF_SIZE, &bridge->crcsr_bus); - if (bridge->crcsr_kernel == NULL) { + if (!bridge->crcsr_kernel) { dev_err(tsi148_bridge->parent, "Failed to allocate memory for " "CR/CSR image\n"); return -ENOMEM; @@ -2294,19 +2291,15 @@ static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* If we want to support more than one of each bridge, we need to * dynamically generate this so we get one per device */ - tsi148_bridge = kzalloc(sizeof(struct vme_bridge), GFP_KERNEL); - if (tsi148_bridge == NULL) { - dev_err(&pdev->dev, "Failed to allocate memory for device " - "structure\n"); + tsi148_bridge = kzalloc(sizeof(*tsi148_bridge), GFP_KERNEL); + if (!tsi148_bridge) { retval = -ENOMEM; goto err_struct; } vme_init_bridge(tsi148_bridge); - tsi148_device = kzalloc(sizeof(struct tsi148_driver), GFP_KERNEL); - if (tsi148_device == NULL) { - dev_err(&pdev->dev, "Failed to allocate memory for device " - "structure\n"); + tsi148_device = kzalloc(sizeof(*tsi148_device), GFP_KERNEL); + if (!tsi148_device) { retval = -ENOMEM; goto err_driver; } @@ -2371,10 +2364,9 @@ static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id) master_num--; tsi148_device->flush_image = - kmalloc(sizeof(struct vme_master_resource), GFP_KERNEL); - if (tsi148_device->flush_image == NULL) { - dev_err(&pdev->dev, "Failed to allocate memory for " - "flush resource structure\n"); + kmalloc(sizeof(*tsi148_device->flush_image), + GFP_KERNEL); + if (!tsi148_device->flush_image) { retval = -ENOMEM; goto err_master; } @@ -2383,17 +2375,14 @@ static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id) tsi148_device->flush_image->locked = 1; tsi148_device->flush_image->number = master_num; memset(&tsi148_device->flush_image->bus_resource, 0, - sizeof(struct resource)); + sizeof(tsi148_device->flush_image->bus_resource)); tsi148_device->flush_image->kern_base = NULL; } /* Add master windows to list */ for (i = 0; i < master_num; i++) { - master_image = kmalloc(sizeof(struct vme_master_resource), - GFP_KERNEL); - if (master_image == NULL) { - dev_err(&pdev->dev, "Failed to allocate memory for " - "master resource structure\n"); + master_image = kmalloc(sizeof(*master_image), GFP_KERNEL); + if (!master_image) { retval = -ENOMEM; goto err_master; } @@ -2410,7 +2399,7 @@ static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id) VME_PROG | VME_DATA; master_image->width_attr = VME_D16 | VME_D32; memset(&master_image->bus_resource, 0, - sizeof(struct resource)); + sizeof(master_image->bus_resource)); master_image->kern_base = NULL; list_add_tail(&master_image->list, &tsi148_bridge->master_resources); @@ -2418,11 +2407,8 @@ static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* Add slave windows to list */ for (i = 0; i < TSI148_MAX_SLAVE; i++) { - slave_image = kmalloc(sizeof(struct vme_slave_resource), - GFP_KERNEL); - if (slave_image == NULL) { - dev_err(&pdev->dev, "Failed to allocate memory for " - "slave resource structure\n"); + slave_image = kmalloc(sizeof(*slave_image), GFP_KERNEL); + if (!slave_image) { retval = -ENOMEM; goto err_slave; } @@ -2442,11 +2428,8 @@ static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* Add dma engines to list */ for (i = 0; i < TSI148_MAX_DMA; i++) { - dma_ctrlr = kmalloc(sizeof(struct vme_dma_resource), - GFP_KERNEL); - if (dma_ctrlr == NULL) { - dev_err(&pdev->dev, "Failed to allocate memory for " - "dma resource structure\n"); + dma_ctrlr = kmalloc(sizeof(*dma_ctrlr), GFP_KERNEL); + if (!dma_ctrlr) { retval = -ENOMEM; goto err_dma; } @@ -2465,10 +2448,8 @@ static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id) } /* Add location monitor to list */ - lm = kmalloc(sizeof(struct vme_lm_resource), GFP_KERNEL); - if (lm == NULL) { - dev_err(&pdev->dev, "Failed to allocate memory for " - "location monitor resource structure\n"); + lm = kmalloc(sizeof(*lm), GFP_KERNEL); + if (!lm) { retval = -ENOMEM; goto err_lm; } diff --git a/drivers/vme/vme.c b/drivers/vme/vme.c index 6a3ead42aba8..81246221a13b 100644 --- a/drivers/vme/vme.c +++ b/drivers/vme/vme.c @@ -92,23 +92,23 @@ void *vme_alloc_consistent(struct vme_resource *resource, size_t size, { struct vme_bridge *bridge; - if (resource == NULL) { + if (!resource) { printk(KERN_ERR "No resource\n"); return NULL; } bridge = find_bridge(resource); - if (bridge == NULL) { + if (!bridge) { printk(KERN_ERR "Can't find bridge\n"); return NULL; } - if (bridge->parent == NULL) { + if (!bridge->parent) { printk(KERN_ERR "Dev entry NULL for bridge %s\n", bridge->name); return NULL; } - if (bridge->alloc_consistent == NULL) { + if (!bridge->alloc_consistent) { printk(KERN_ERR "alloc_consistent not supported by bridge %s\n", bridge->name); return NULL; @@ -132,23 +132,23 @@ void vme_free_consistent(struct vme_resource *resource, size_t size, { struct vme_bridge *bridge; - if (resource == NULL) { + if (!resource) { printk(KERN_ERR "No resource\n"); return; } bridge = find_bridge(resource); - if (bridge == NULL) { + if (!bridge) { printk(KERN_ERR "Can't find bridge\n"); return; } - if (bridge->parent == NULL) { + if (!bridge->parent) { printk(KERN_ERR "Dev entry NULL for bridge %s\n", bridge->name); return; } - if (bridge->free_consistent == NULL) { + if (!bridge->free_consistent) { printk(KERN_ERR "free_consistent not supported by bridge %s\n", bridge->name); return; @@ -208,29 +208,27 @@ int vme_check_window(u32 aspace, unsigned long long vme_base, { int retval = 0; + if (vme_base + size < size) + return -EINVAL; + switch (aspace) { case VME_A16: - if (((vme_base + size) > VME_A16_MAX) || - (vme_base > VME_A16_MAX)) + if (vme_base + size > VME_A16_MAX) retval = -EFAULT; break; case VME_A24: - if (((vme_base + size) > VME_A24_MAX) || - (vme_base > VME_A24_MAX)) + if (vme_base + size > VME_A24_MAX) retval = -EFAULT; break; case VME_A32: - if (((vme_base + size) > VME_A32_MAX) || - (vme_base > VME_A32_MAX)) + if (vme_base + size > VME_A32_MAX) retval = -EFAULT; break; case VME_A64: - if ((size != 0) && (vme_base > U64_MAX + 1 - size)) - retval = -EFAULT; + /* The VME_A64_MAX limit is actually U64_MAX + 1 */ break; case VME_CRCSR: - if (((vme_base + size) > VME_CRCSR_MAX) || - (vme_base > VME_CRCSR_MAX)) + if (vme_base + size > VME_CRCSR_MAX) retval = -EFAULT; break; case VME_USER1: @@ -303,7 +301,7 @@ struct vme_resource *vme_slave_request(struct vme_dev *vdev, u32 address, struct vme_resource *resource = NULL; bridge = vdev->bridge; - if (bridge == NULL) { + if (!bridge) { printk(KERN_ERR "Can't find VME bus\n"); goto err_bus; } @@ -313,7 +311,7 @@ struct vme_resource *vme_slave_request(struct vme_dev *vdev, u32 address, slave_image = list_entry(slave_pos, struct vme_slave_resource, list); - if (slave_image == NULL) { + if (!slave_image) { printk(KERN_ERR "Registered NULL Slave resource\n"); continue; } @@ -333,14 +331,13 @@ struct vme_resource *vme_slave_request(struct vme_dev *vdev, u32 address, } /* No free image */ - if (allocated_image == NULL) + if (!allocated_image) goto err_image; - resource = kmalloc(sizeof(struct vme_resource), GFP_KERNEL); - if (resource == NULL) { - printk(KERN_WARNING "Unable to allocate resource structure\n"); + resource = kmalloc(sizeof(*resource), GFP_KERNEL); + if (!resource) goto err_alloc; - } + resource->type = VME_SLAVE; resource->entry = &allocated_image->list; @@ -389,7 +386,7 @@ int vme_slave_set(struct vme_resource *resource, int enabled, image = list_entry(resource->entry, struct vme_slave_resource, list); - if (bridge->slave_set == NULL) { + if (!bridge->slave_set) { printk(KERN_ERR "Function not supported\n"); return -ENOSYS; } @@ -438,7 +435,7 @@ int vme_slave_get(struct vme_resource *resource, int *enabled, image = list_entry(resource->entry, struct vme_slave_resource, list); - if (bridge->slave_get == NULL) { + if (!bridge->slave_get) { printk(KERN_ERR "vme_slave_get not supported\n"); return -EINVAL; } @@ -465,7 +462,7 @@ void vme_slave_free(struct vme_resource *resource) slave_image = list_entry(resource->entry, struct vme_slave_resource, list); - if (slave_image == NULL) { + if (!slave_image) { printk(KERN_ERR "Can't find slave resource\n"); return; } @@ -505,7 +502,7 @@ struct vme_resource *vme_master_request(struct vme_dev *vdev, u32 address, struct vme_resource *resource = NULL; bridge = vdev->bridge; - if (bridge == NULL) { + if (!bridge) { printk(KERN_ERR "Can't find VME bus\n"); goto err_bus; } @@ -515,7 +512,7 @@ struct vme_resource *vme_master_request(struct vme_dev *vdev, u32 address, master_image = list_entry(master_pos, struct vme_master_resource, list); - if (master_image == NULL) { + if (!master_image) { printk(KERN_WARNING "Registered NULL master resource\n"); continue; } @@ -536,16 +533,15 @@ struct vme_resource *vme_master_request(struct vme_dev *vdev, u32 address, } /* Check to see if we found a resource */ - if (allocated_image == NULL) { + if (!allocated_image) { printk(KERN_ERR "Can't find a suitable resource\n"); goto err_image; } - resource = kmalloc(sizeof(struct vme_resource), GFP_KERNEL); - if (resource == NULL) { - printk(KERN_ERR "Unable to allocate resource structure\n"); + resource = kmalloc(sizeof(*resource), GFP_KERNEL); + if (!resource) goto err_alloc; - } + resource->type = VME_MASTER; resource->entry = &allocated_image->list; @@ -594,7 +590,7 @@ int vme_master_set(struct vme_resource *resource, int enabled, image = list_entry(resource->entry, struct vme_master_resource, list); - if (bridge->master_set == NULL) { + if (!bridge->master_set) { printk(KERN_WARNING "vme_master_set not supported\n"); return -EINVAL; } @@ -644,7 +640,7 @@ int vme_master_get(struct vme_resource *resource, int *enabled, image = list_entry(resource->entry, struct vme_master_resource, list); - if (bridge->master_get == NULL) { + if (!bridge->master_get) { printk(KERN_WARNING "%s not supported\n", __func__); return -EINVAL; } @@ -676,7 +672,7 @@ ssize_t vme_master_read(struct vme_resource *resource, void *buf, size_t count, struct vme_master_resource *image; size_t length; - if (bridge->master_read == NULL) { + if (!bridge->master_read) { printk(KERN_WARNING "Reading from resource not supported\n"); return -EINVAL; } @@ -725,7 +721,7 @@ ssize_t vme_master_write(struct vme_resource *resource, void *buf, struct vme_master_resource *image; size_t length; - if (bridge->master_write == NULL) { + if (!bridge->master_write) { printk(KERN_WARNING "Writing to resource not supported\n"); return -EINVAL; } @@ -776,7 +772,7 @@ unsigned int vme_master_rmw(struct vme_resource *resource, unsigned int mask, struct vme_bridge *bridge = find_bridge(resource); struct vme_master_resource *image; - if (bridge->master_rmw == NULL) { + if (!bridge->master_rmw) { printk(KERN_WARNING "Writing to resource not supported\n"); return -EINVAL; } @@ -846,7 +842,7 @@ void vme_master_free(struct vme_resource *resource) master_image = list_entry(resource->entry, struct vme_master_resource, list); - if (master_image == NULL) { + if (!master_image) { printk(KERN_ERR "Can't find master resource\n"); return; } @@ -886,7 +882,7 @@ struct vme_resource *vme_dma_request(struct vme_dev *vdev, u32 route) printk(KERN_ERR "No VME resource Attribute tests done\n"); bridge = vdev->bridge; - if (bridge == NULL) { + if (!bridge) { printk(KERN_ERR "Can't find VME bus\n"); goto err_bus; } @@ -895,8 +891,7 @@ struct vme_resource *vme_dma_request(struct vme_dev *vdev, u32 route) list_for_each(dma_pos, &bridge->dma_resources) { dma_ctrlr = list_entry(dma_pos, struct vme_dma_resource, list); - - if (dma_ctrlr == NULL) { + if (!dma_ctrlr) { printk(KERN_ERR "Registered NULL DMA resource\n"); continue; } @@ -915,14 +910,13 @@ struct vme_resource *vme_dma_request(struct vme_dev *vdev, u32 route) } /* Check to see if we found a resource */ - if (allocated_ctrlr == NULL) + if (!allocated_ctrlr) goto err_ctrlr; - resource = kmalloc(sizeof(struct vme_resource), GFP_KERNEL); - if (resource == NULL) { - printk(KERN_WARNING "Unable to allocate resource structure\n"); + resource = kmalloc(sizeof(*resource), GFP_KERNEL); + if (!resource) goto err_alloc; - } + resource->type = VME_DMA; resource->entry = &allocated_ctrlr->list; @@ -951,7 +945,6 @@ EXPORT_SYMBOL(vme_dma_request); */ struct vme_dma_list *vme_new_dma_list(struct vme_resource *resource) { - struct vme_dma_resource *ctrlr; struct vme_dma_list *dma_list; if (resource->type != VME_DMA) { @@ -959,15 +952,14 @@ struct vme_dma_list *vme_new_dma_list(struct vme_resource *resource) return NULL; } - ctrlr = list_entry(resource->entry, struct vme_dma_resource, list); - - dma_list = kmalloc(sizeof(struct vme_dma_list), GFP_KERNEL); - if (dma_list == NULL) { - printk(KERN_ERR "Unable to allocate memory for new DMA list\n"); + dma_list = kmalloc(sizeof(*dma_list), GFP_KERNEL); + if (!dma_list) return NULL; - } + INIT_LIST_HEAD(&dma_list->entries); - dma_list->parent = ctrlr; + dma_list->parent = list_entry(resource->entry, + struct vme_dma_resource, + list); mutex_init(&dma_list->mtx); return dma_list; @@ -990,17 +982,13 @@ struct vme_dma_attr *vme_dma_pattern_attribute(u32 pattern, u32 type) struct vme_dma_attr *attributes; struct vme_dma_pattern *pattern_attr; - attributes = kmalloc(sizeof(struct vme_dma_attr), GFP_KERNEL); - if (attributes == NULL) { - printk(KERN_ERR "Unable to allocate memory for attributes structure\n"); + attributes = kmalloc(sizeof(*attributes), GFP_KERNEL); + if (!attributes) goto err_attr; - } - pattern_attr = kmalloc(sizeof(struct vme_dma_pattern), GFP_KERNEL); - if (pattern_attr == NULL) { - printk(KERN_ERR "Unable to allocate memory for pattern attributes\n"); + pattern_attr = kmalloc(sizeof(*pattern_attr), GFP_KERNEL); + if (!pattern_attr) goto err_pat; - } attributes->type = VME_DMA_PATTERN; attributes->private = (void *)pattern_attr; @@ -1034,19 +1022,13 @@ struct vme_dma_attr *vme_dma_pci_attribute(dma_addr_t address) /* XXX Run some sanity checks here */ - attributes = kmalloc(sizeof(struct vme_dma_attr), GFP_KERNEL); - if (attributes == NULL) { - printk(KERN_ERR "Unable to allocate memory for attributes structure\n"); + attributes = kmalloc(sizeof(*attributes), GFP_KERNEL); + if (!attributes) goto err_attr; - } - pci_attr = kmalloc(sizeof(struct vme_dma_pci), GFP_KERNEL); - if (pci_attr == NULL) { - printk(KERN_ERR "Unable to allocate memory for PCI attributes\n"); + pci_attr = kmalloc(sizeof(*pci_attr), GFP_KERNEL); + if (!pci_attr) goto err_pci; - } - - attributes->type = VME_DMA_PCI; attributes->private = (void *)pci_attr; @@ -1081,18 +1063,13 @@ struct vme_dma_attr *vme_dma_vme_attribute(unsigned long long address, struct vme_dma_attr *attributes; struct vme_dma_vme *vme_attr; - attributes = kmalloc( - sizeof(struct vme_dma_attr), GFP_KERNEL); - if (attributes == NULL) { - printk(KERN_ERR "Unable to allocate memory for attributes structure\n"); + attributes = kmalloc(sizeof(*attributes), GFP_KERNEL); + if (!attributes) goto err_attr; - } - vme_attr = kmalloc(sizeof(struct vme_dma_vme), GFP_KERNEL); - if (vme_attr == NULL) { - printk(KERN_ERR "Unable to allocate memory for VME attributes\n"); + vme_attr = kmalloc(sizeof(*vme_attr), GFP_KERNEL); + if (!vme_attr) goto err_vme; - } attributes->type = VME_DMA_VME; attributes->private = (void *)vme_attr; @@ -1148,7 +1125,7 @@ int vme_dma_list_add(struct vme_dma_list *list, struct vme_dma_attr *src, struct vme_bridge *bridge = list->parent->parent; int retval; - if (bridge->dma_list_add == NULL) { + if (!bridge->dma_list_add) { printk(KERN_WARNING "Link List DMA generation not supported\n"); return -EINVAL; } @@ -1181,7 +1158,7 @@ int vme_dma_list_exec(struct vme_dma_list *list) struct vme_bridge *bridge = list->parent->parent; int retval; - if (bridge->dma_list_exec == NULL) { + if (!bridge->dma_list_exec) { printk(KERN_ERR "Link List DMA execution not supported\n"); return -EINVAL; } @@ -1210,14 +1187,14 @@ int vme_dma_list_free(struct vme_dma_list *list) struct vme_bridge *bridge = list->parent->parent; int retval; - if (bridge->dma_list_empty == NULL) { + if (!bridge->dma_list_empty) { printk(KERN_WARNING "Emptying of Link Lists not supported\n"); return -EINVAL; } if (!mutex_trylock(&list->mtx)) { printk(KERN_ERR "Link List in use\n"); - return -EINVAL; + return -EBUSY; } /* @@ -1342,8 +1319,7 @@ void vme_irq_handler(struct vme_bridge *bridge, int level, int statid) call = bridge->irq[level - 1].callback[statid].func; priv_data = bridge->irq[level - 1].callback[statid].priv_data; - - if (call != NULL) + if (call) call(level, statid, priv_data); else printk(KERN_WARNING "Spurious VME interrupt, level:%x, vector:%x\n", @@ -1374,7 +1350,7 @@ int vme_irq_request(struct vme_dev *vdev, int level, int statid, struct vme_bridge *bridge; bridge = vdev->bridge; - if (bridge == NULL) { + if (!bridge) { printk(KERN_ERR "Can't find VME bus\n"); return -EINVAL; } @@ -1384,7 +1360,7 @@ int vme_irq_request(struct vme_dev *vdev, int level, int statid, return -EINVAL; } - if (bridge->irq_set == NULL) { + if (!bridge->irq_set) { printk(KERN_ERR "Configuring interrupts not supported\n"); return -EINVAL; } @@ -1423,7 +1399,7 @@ void vme_irq_free(struct vme_dev *vdev, int level, int statid) struct vme_bridge *bridge; bridge = vdev->bridge; - if (bridge == NULL) { + if (!bridge) { printk(KERN_ERR "Can't find VME bus\n"); return; } @@ -1433,7 +1409,7 @@ void vme_irq_free(struct vme_dev *vdev, int level, int statid) return; } - if (bridge->irq_set == NULL) { + if (!bridge->irq_set) { printk(KERN_ERR "Configuring interrupts not supported\n"); return; } @@ -1470,7 +1446,7 @@ int vme_irq_generate(struct vme_dev *vdev, int level, int statid) struct vme_bridge *bridge; bridge = vdev->bridge; - if (bridge == NULL) { + if (!bridge) { printk(KERN_ERR "Can't find VME bus\n"); return -EINVAL; } @@ -1480,7 +1456,7 @@ int vme_irq_generate(struct vme_dev *vdev, int level, int statid) return -EINVAL; } - if (bridge->irq_generate == NULL) { + if (!bridge->irq_generate) { printk(KERN_WARNING "Interrupt generation not supported\n"); return -EINVAL; } @@ -1508,7 +1484,7 @@ struct vme_resource *vme_lm_request(struct vme_dev *vdev) struct vme_resource *resource = NULL; bridge = vdev->bridge; - if (bridge == NULL) { + if (!bridge) { printk(KERN_ERR "Can't find VME bus\n"); goto err_bus; } @@ -1517,8 +1493,7 @@ struct vme_resource *vme_lm_request(struct vme_dev *vdev) list_for_each(lm_pos, &bridge->lm_resources) { lm = list_entry(lm_pos, struct vme_lm_resource, list); - - if (lm == NULL) { + if (!lm) { printk(KERN_ERR "Registered NULL Location Monitor resource\n"); continue; } @@ -1535,14 +1510,13 @@ struct vme_resource *vme_lm_request(struct vme_dev *vdev) } /* Check to see if we found a resource */ - if (allocated_lm == NULL) + if (!allocated_lm) goto err_lm; - resource = kmalloc(sizeof(struct vme_resource), GFP_KERNEL); - if (resource == NULL) { - printk(KERN_ERR "Unable to allocate resource structure\n"); + resource = kmalloc(sizeof(*resource), GFP_KERNEL); + if (!resource) goto err_alloc; - } + resource->type = VME_LM; resource->entry = &allocated_lm->list; @@ -1612,7 +1586,7 @@ int vme_lm_set(struct vme_resource *resource, unsigned long long lm_base, lm = list_entry(resource->entry, struct vme_lm_resource, list); - if (bridge->lm_set == NULL) { + if (!bridge->lm_set) { printk(KERN_ERR "vme_lm_set not supported\n"); return -EINVAL; } @@ -1648,7 +1622,7 @@ int vme_lm_get(struct vme_resource *resource, unsigned long long *lm_base, lm = list_entry(resource->entry, struct vme_lm_resource, list); - if (bridge->lm_get == NULL) { + if (!bridge->lm_get) { printk(KERN_ERR "vme_lm_get not supported\n"); return -EINVAL; } @@ -1685,7 +1659,7 @@ int vme_lm_attach(struct vme_resource *resource, int monitor, lm = list_entry(resource->entry, struct vme_lm_resource, list); - if (bridge->lm_attach == NULL) { + if (!bridge->lm_attach) { printk(KERN_ERR "vme_lm_attach not supported\n"); return -EINVAL; } @@ -1718,7 +1692,7 @@ int vme_lm_detach(struct vme_resource *resource, int monitor) lm = list_entry(resource->entry, struct vme_lm_resource, list); - if (bridge->lm_detach == NULL) { + if (!bridge->lm_detach) { printk(KERN_ERR "vme_lm_detach not supported\n"); return -EINVAL; } @@ -1780,12 +1754,12 @@ int vme_slot_num(struct vme_dev *vdev) struct vme_bridge *bridge; bridge = vdev->bridge; - if (bridge == NULL) { + if (!bridge) { printk(KERN_ERR "Can't find VME bus\n"); return -EINVAL; } - if (bridge->slot_get == NULL) { + if (!bridge->slot_get) { printk(KERN_WARNING "vme_slot_num not supported\n"); return -EINVAL; } @@ -1808,7 +1782,7 @@ int vme_bus_num(struct vme_dev *vdev) struct vme_bridge *bridge; bridge = vdev->bridge; - if (bridge == NULL) { + if (!bridge) { pr_err("Can't find VME bus\n"); return -EINVAL; } @@ -1888,7 +1862,7 @@ static int __vme_register_driver_bus(struct vme_driver *drv, struct vme_dev *tmp; for (i = 0; i < ndevs; i++) { - vdev = kzalloc(sizeof(struct vme_dev), GFP_KERNEL); + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); if (!vdev) { err = -ENOMEM; goto err_devalloc; @@ -2020,30 +1994,26 @@ static int vme_bus_match(struct device *dev, struct device_driver *drv) static int vme_bus_probe(struct device *dev) { - int retval = -ENODEV; struct vme_driver *driver; struct vme_dev *vdev = dev_to_vme_dev(dev); driver = dev->platform_data; + if (driver->probe) + return driver->probe(vdev); - if (driver->probe != NULL) - retval = driver->probe(vdev); - - return retval; + return -ENODEV; } static int vme_bus_remove(struct device *dev) { - int retval = -ENODEV; struct vme_driver *driver; struct vme_dev *vdev = dev_to_vme_dev(dev); driver = dev->platform_data; + if (driver->remove) + return driver->remove(vdev); - if (driver->remove != NULL) - retval = driver->remove(vdev); - - return retval; + return -ENODEV; } struct bus_type vme_bus_type = { diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index 3c945f9f5f0f..7931231d8e80 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -148,4 +148,19 @@ config W1_SLAVE_DS28E04 If you are unsure, say N. +config W1_SLAVE_DS28E17 + tristate "1-wire-to-I2C master bridge (DS28E17)" + select CRC16 + depends on I2C + help + Say Y here if you want to use the DS28E17 1-wire-to-I2C master bridge. + For each DS28E17 detected, a new I2C adapter is created within the + kernel. I2C devices on that bus can be configured to be used by the + kernel and userspace tools as on any other "native" I2C bus. + + This driver is also available as a module. If so, the module + will be called w1_ds28e17. + + If you are unsure, say N. + endmenu diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index 79c611ce5f18..d5f4f4d5b9e5 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile @@ -18,3 +18,4 @@ obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o obj-$(CONFIG_W1_SLAVE_DS2780) += w1_ds2780.o obj-$(CONFIG_W1_SLAVE_DS2781) += w1_ds2781.o obj-$(CONFIG_W1_SLAVE_DS28E04) += w1_ds28e04.o +obj-$(CONFIG_W1_SLAVE_DS28E17) += w1_ds28e17.o diff --git a/drivers/w1/slaves/w1_ds28e17.c b/drivers/w1/slaves/w1_ds28e17.c new file mode 100644 index 000000000000..e78b63ea4daf --- /dev/null +++ b/drivers/w1/slaves/w1_ds28e17.c @@ -0,0 +1,771 @@ +/* + * w1_ds28e17.c - w1 family 19 (DS28E17) driver + * + * Copyright (c) 2016 Jan Kandziora <jjj@gmx.de> + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <linux/crc16.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/uaccess.h> + +#define CRC16_INIT 0 + +#include <linux/w1.h> + +#define W1_FAMILY_DS28E17 0x19 + +/* Module setup. */ +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Jan Kandziora <jjj@gmx.de>"); +MODULE_DESCRIPTION("w1 family 19 driver for DS28E17, 1-wire to I2C master bridge"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS28E17)); + + +/* Default I2C speed to be set when a DS28E17 is detected. */ +static int i2c_speed = 100; +module_param_named(speed, i2c_speed, int, (S_IRUSR | S_IWUSR)); +MODULE_PARM_DESC(speed, "Default I2C speed to be set when a DS28E17 is detected"); + +/* Default I2C stretch value to be set when a DS28E17 is detected. */ +static char i2c_stretch = 1; +module_param_named(stretch, i2c_stretch, byte, (S_IRUSR | S_IWUSR)); +MODULE_PARM_DESC(stretch, "Default I2C stretch value to be set when a DS28E17 is detected"); + +/* DS28E17 device command codes. */ +#define W1_F19_WRITE_DATA_WITH_STOP 0x4B +#define W1_F19_WRITE_DATA_NO_STOP 0x5A +#define W1_F19_WRITE_DATA_ONLY 0x69 +#define W1_F19_WRITE_DATA_ONLY_WITH_STOP 0x78 +#define W1_F19_READ_DATA_WITH_STOP 0x87 +#define W1_F19_WRITE_READ_DATA_WITH_STOP 0x2D +#define W1_F19_WRITE_CONFIGURATION 0xD2 +#define W1_F19_READ_CONFIGURATION 0xE1 +#define W1_F19_ENABLE_SLEEP_MODE 0x1E +#define W1_F19_READ_DEVICE_REVISION 0xC4 + +/* DS28E17 status bits */ +#define W1_F19_STATUS_CRC 0x01 +#define W1_F19_STATUS_ADDRESS 0x02 +#define W1_F19_STATUS_START 0x08 + +/* + * Maximum number of I2C bytes to transfer within one CRC16 protected onewire + * command. + * */ +#define W1_F19_WRITE_DATA_LIMIT 255 + +/* Maximum number of I2C bytes to read with one onewire command. */ +#define W1_F19_READ_DATA_LIMIT 255 + +/* Constants for calculating the busy sleep. */ +#define W1_F19_BUSY_TIMEBASES { 90, 23, 10 } +#define W1_F19_BUSY_GRATUITY 1000 + +/* Number of checks for the busy flag before timeout. */ +#define W1_F19_BUSY_CHECKS 1000 + + +/* Slave specific data. */ +struct w1_f19_data { + u8 speed; + u8 stretch; + struct i2c_adapter adapter; +}; + + +/* Wait a while until the busy flag clears. */ +static int w1_f19_i2c_busy_wait(struct w1_slave *sl, size_t count) +{ + const unsigned long timebases[3] = W1_F19_BUSY_TIMEBASES; + struct w1_f19_data *data = sl->family_data; + unsigned int checks; + + /* Check the busy flag first in any case.*/ + if (w1_touch_bit(sl->master, 1) == 0) + return 0; + + /* + * Do a generously long sleep in the beginning, + * as we have to wait at least this time for all + * the I2C bytes at the given speed to be transferred. + */ + usleep_range(timebases[data->speed] * (data->stretch) * count, + timebases[data->speed] * (data->stretch) * count + + W1_F19_BUSY_GRATUITY); + + /* Now continusly check the busy flag sent by the DS28E17. */ + checks = W1_F19_BUSY_CHECKS; + while ((checks--) > 0) { + /* Return success if the busy flag is cleared. */ + if (w1_touch_bit(sl->master, 1) == 0) + return 0; + + /* Wait one non-streched byte timeslot. */ + udelay(timebases[data->speed]); + } + + /* Timeout. */ + dev_warn(&sl->dev, "busy timeout\n"); + return -ETIMEDOUT; +} + + +/* Utility function: result. */ +static size_t w1_f19_error(struct w1_slave *sl, u8 w1_buf[]) +{ + /* Warnings. */ + if (w1_buf[0] & W1_F19_STATUS_CRC) + dev_warn(&sl->dev, "crc16 mismatch\n"); + if (w1_buf[0] & W1_F19_STATUS_ADDRESS) + dev_warn(&sl->dev, "i2c device not responding\n"); + if ((w1_buf[0] & (W1_F19_STATUS_CRC | W1_F19_STATUS_ADDRESS)) == 0 + && w1_buf[1] != 0) { + dev_warn(&sl->dev, "i2c short write, %d bytes not acknowledged\n", + w1_buf[1]); + } + + /* Check error conditions. */ + if (w1_buf[0] & W1_F19_STATUS_ADDRESS) + return -ENXIO; + if (w1_buf[0] & W1_F19_STATUS_START) + return -EAGAIN; + if (w1_buf[0] != 0 || w1_buf[1] != 0) + return -EIO; + + /* All ok. */ + return 0; +} + + +/* Utility function: write data to I2C slave, single chunk. */ +static int __w1_f19_i2c_write(struct w1_slave *sl, + const u8 *command, size_t command_count, + const u8 *buffer, size_t count) +{ + u16 crc; + int error; + u8 w1_buf[2]; + + /* Send command and I2C data to DS28E17. */ + crc = crc16(CRC16_INIT, command, command_count); + w1_write_block(sl->master, command, command_count); + + w1_buf[0] = count; + crc = crc16(crc, w1_buf, 1); + w1_write_8(sl->master, w1_buf[0]); + + crc = crc16(crc, buffer, count); + w1_write_block(sl->master, buffer, count); + + w1_buf[0] = ~(crc & 0xFF); + w1_buf[1] = ~((crc >> 8) & 0xFF); + w1_write_block(sl->master, w1_buf, 2); + + /* Wait until busy flag clears (or timeout). */ + if (w1_f19_i2c_busy_wait(sl, count + 1) < 0) + return -ETIMEDOUT; + + /* Read status from DS28E17. */ + w1_read_block(sl->master, w1_buf, 2); + + /* Check error conditions. */ + error = w1_f19_error(sl, w1_buf); + if (error < 0) + return error; + + /* Return number of bytes written. */ + return count; +} + + +/* Write data to I2C slave. */ +static int w1_f19_i2c_write(struct w1_slave *sl, u16 i2c_address, + const u8 *buffer, size_t count, bool stop) +{ + int result; + int remaining = count; + const u8 *p; + u8 command[2]; + + /* Check input. */ + if (count == 0) + return -EOPNOTSUPP; + + /* Check whether we need multiple commands. */ + if (count <= W1_F19_WRITE_DATA_LIMIT) { + /* + * Small data amount. Data can be sent with + * a single onewire command. + */ + + /* Send all data to DS28E17. */ + command[0] = (stop ? W1_F19_WRITE_DATA_WITH_STOP + : W1_F19_WRITE_DATA_NO_STOP); + command[1] = i2c_address << 1; + result = __w1_f19_i2c_write(sl, command, 2, buffer, count); + } else { + /* Large data amount. Data has to be sent in multiple chunks. */ + + /* Send first chunk to DS28E17. */ + p = buffer; + command[0] = W1_F19_WRITE_DATA_NO_STOP; + command[1] = i2c_address << 1; + result = __w1_f19_i2c_write(sl, command, 2, p, + W1_F19_WRITE_DATA_LIMIT); + if (result < 0) + return result; + + /* Resume to same DS28E17. */ + if (w1_reset_resume_command(sl->master)) + return -EIO; + + /* Next data chunk. */ + p += W1_F19_WRITE_DATA_LIMIT; + remaining -= W1_F19_WRITE_DATA_LIMIT; + + while (remaining > W1_F19_WRITE_DATA_LIMIT) { + /* Send intermediate chunk to DS28E17. */ + command[0] = W1_F19_WRITE_DATA_ONLY; + result = __w1_f19_i2c_write(sl, command, 1, p, + W1_F19_WRITE_DATA_LIMIT); + if (result < 0) + return result; + + /* Resume to same DS28E17. */ + if (w1_reset_resume_command(sl->master)) + return -EIO; + + /* Next data chunk. */ + p += W1_F19_WRITE_DATA_LIMIT; + remaining -= W1_F19_WRITE_DATA_LIMIT; + } + + /* Send final chunk to DS28E17. */ + command[0] = (stop ? W1_F19_WRITE_DATA_ONLY_WITH_STOP + : W1_F19_WRITE_DATA_ONLY); + result = __w1_f19_i2c_write(sl, command, 1, p, remaining); + } + + return result; +} + + +/* Read data from I2C slave. */ +static int w1_f19_i2c_read(struct w1_slave *sl, u16 i2c_address, + u8 *buffer, size_t count) +{ + u16 crc; + int error; + u8 w1_buf[5]; + + /* Check input. */ + if (count == 0) + return -EOPNOTSUPP; + + /* Send command to DS28E17. */ + w1_buf[0] = W1_F19_READ_DATA_WITH_STOP; + w1_buf[1] = i2c_address << 1 | 0x01; + w1_buf[2] = count; + crc = crc16(CRC16_INIT, w1_buf, 3); + w1_buf[3] = ~(crc & 0xFF); + w1_buf[4] = ~((crc >> 8) & 0xFF); + w1_write_block(sl->master, w1_buf, 5); + + /* Wait until busy flag clears (or timeout). */ + if (w1_f19_i2c_busy_wait(sl, count + 1) < 0) + return -ETIMEDOUT; + + /* Read status from DS28E17. */ + w1_buf[0] = w1_read_8(sl->master); + w1_buf[1] = 0; + + /* Check error conditions. */ + error = w1_f19_error(sl, w1_buf); + if (error < 0) + return error; + + /* Read received I2C data from DS28E17. */ + return w1_read_block(sl->master, buffer, count); +} + + +/* Write to, then read data from I2C slave. */ +static int w1_f19_i2c_write_read(struct w1_slave *sl, u16 i2c_address, + const u8 *wbuffer, size_t wcount, u8 *rbuffer, size_t rcount) +{ + u16 crc; + int error; + u8 w1_buf[3]; + + /* Check input. */ + if (wcount == 0 || rcount == 0) + return -EOPNOTSUPP; + + /* Send command and I2C data to DS28E17. */ + w1_buf[0] = W1_F19_WRITE_READ_DATA_WITH_STOP; + w1_buf[1] = i2c_address << 1; + w1_buf[2] = wcount; + crc = crc16(CRC16_INIT, w1_buf, 3); + w1_write_block(sl->master, w1_buf, 3); + + crc = crc16(crc, wbuffer, wcount); + w1_write_block(sl->master, wbuffer, wcount); + + w1_buf[0] = rcount; + crc = crc16(crc, w1_buf, 1); + w1_buf[1] = ~(crc & 0xFF); + w1_buf[2] = ~((crc >> 8) & 0xFF); + w1_write_block(sl->master, w1_buf, 3); + + /* Wait until busy flag clears (or timeout). */ + if (w1_f19_i2c_busy_wait(sl, wcount + rcount + 2) < 0) + return -ETIMEDOUT; + + /* Read status from DS28E17. */ + w1_read_block(sl->master, w1_buf, 2); + + /* Check error conditions. */ + error = w1_f19_error(sl, w1_buf); + if (error < 0) + return error; + + /* Read received I2C data from DS28E17. */ + return w1_read_block(sl->master, rbuffer, rcount); +} + + +/* Do an I2C master transfer. */ +static int w1_f19_i2c_master_transfer(struct i2c_adapter *adapter, + struct i2c_msg *msgs, int num) +{ + struct w1_slave *sl = (struct w1_slave *) adapter->algo_data; + int i = 0; + int result = 0; + + /* Start onewire transaction. */ + mutex_lock(&sl->master->bus_mutex); + + /* Select DS28E17. */ + if (w1_reset_select_slave(sl)) { + i = -EIO; + goto error; + } + + /* Loop while there are still messages to transfer. */ + while (i < num) { + /* + * Check for special case: Small write followed + * by read to same I2C device. + */ + if (i < (num-1) + && msgs[i].addr == msgs[i+1].addr + && !(msgs[i].flags & I2C_M_RD) + && (msgs[i+1].flags & I2C_M_RD) + && (msgs[i].len <= W1_F19_WRITE_DATA_LIMIT)) { + /* + * The DS28E17 has a combined transfer + * for small write+read. + */ + result = w1_f19_i2c_write_read(sl, msgs[i].addr, + msgs[i].buf, msgs[i].len, + msgs[i+1].buf, msgs[i+1].len); + if (result < 0) { + i = result; + goto error; + } + + /* + * Check if we should interpret the read data + * as a length byte. The DS28E17 unfortunately + * has no read without stop, so we can just do + * another simple read in that case. + */ + if (msgs[i+1].flags & I2C_M_RECV_LEN) { + result = w1_f19_i2c_read(sl, msgs[i+1].addr, + &(msgs[i+1].buf[1]), msgs[i+1].buf[0]); + if (result < 0) { + i = result; + goto error; + } + } + + /* Eat up read message, too. */ + i++; + } else if (msgs[i].flags & I2C_M_RD) { + /* Read transfer. */ + result = w1_f19_i2c_read(sl, msgs[i].addr, + msgs[i].buf, msgs[i].len); + if (result < 0) { + i = result; + goto error; + } + + /* + * Check if we should interpret the read data + * as a length byte. The DS28E17 unfortunately + * has no read without stop, so we can just do + * another simple read in that case. + */ + if (msgs[i].flags & I2C_M_RECV_LEN) { + result = w1_f19_i2c_read(sl, + msgs[i].addr, + &(msgs[i].buf[1]), + msgs[i].buf[0]); + if (result < 0) { + i = result; + goto error; + } + } + } else { + /* + * Write transfer. + * Stop condition only for last + * transfer. + */ + result = w1_f19_i2c_write(sl, + msgs[i].addr, + msgs[i].buf, + msgs[i].len, + i == (num-1)); + if (result < 0) { + i = result; + goto error; + } + } + + /* Next message. */ + i++; + + /* Are there still messages to send/receive? */ + if (i < num) { + /* Yes. Resume to same DS28E17. */ + if (w1_reset_resume_command(sl->master)) { + i = -EIO; + goto error; + } + } + } + +error: + /* End onewire transaction. */ + mutex_unlock(&sl->master->bus_mutex); + + /* Return number of messages processed or error. */ + return i; +} + + +/* Get I2C adapter functionality. */ +static u32 w1_f19_i2c_functionality(struct i2c_adapter *adapter) +{ + /* + * Plain I2C functions only. + * SMBus is emulated by the kernel's I2C layer. + * No "I2C_FUNC_SMBUS_QUICK" + * No "I2C_FUNC_SMBUS_READ_BLOCK_DATA" + * No "I2C_FUNC_SMBUS_BLOCK_PROC_CALL" + */ + return I2C_FUNC_I2C | + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_PROC_CALL | + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK | + I2C_FUNC_SMBUS_PEC; +} + + +/* I2C adapter quirks. */ +static const struct i2c_adapter_quirks w1_f19_i2c_adapter_quirks = { + .max_read_len = W1_F19_READ_DATA_LIMIT, +}; + +/* I2C algorithm. */ +static const struct i2c_algorithm w1_f19_i2c_algorithm = { + .master_xfer = w1_f19_i2c_master_transfer, + .functionality = w1_f19_i2c_functionality, +}; + + +/* Read I2C speed from DS28E17. */ +static int w1_f19_get_i2c_speed(struct w1_slave *sl) +{ + struct w1_f19_data *data = sl->family_data; + int result = -EIO; + + /* Start onewire transaction. */ + mutex_lock(&sl->master->bus_mutex); + + /* Select slave. */ + if (w1_reset_select_slave(sl)) + goto error; + + /* Read slave configuration byte. */ + w1_write_8(sl->master, W1_F19_READ_CONFIGURATION); + result = w1_read_8(sl->master); + if (result < 0 || result > 2) { + result = -EIO; + goto error; + } + + /* Update speed in slave specific data. */ + data->speed = result; + +error: + /* End onewire transaction. */ + mutex_unlock(&sl->master->bus_mutex); + + return result; +} + + +/* Set I2C speed on DS28E17. */ +static int __w1_f19_set_i2c_speed(struct w1_slave *sl, u8 speed) +{ + struct w1_f19_data *data = sl->family_data; + const int i2c_speeds[3] = { 100, 400, 900 }; + u8 w1_buf[2]; + + /* Select slave. */ + if (w1_reset_select_slave(sl)) + return -EIO; + + w1_buf[0] = W1_F19_WRITE_CONFIGURATION; + w1_buf[1] = speed; + w1_write_block(sl->master, w1_buf, 2); + + /* Update speed in slave specific data. */ + data->speed = speed; + + dev_info(&sl->dev, "i2c speed set to %d kBaud\n", i2c_speeds[speed]); + + return 0; +} + +static int w1_f19_set_i2c_speed(struct w1_slave *sl, u8 speed) +{ + int result; + + /* Start onewire transaction. */ + mutex_lock(&sl->master->bus_mutex); + + /* Set I2C speed on DS28E17. */ + result = __w1_f19_set_i2c_speed(sl, speed); + + /* End onewire transaction. */ + mutex_unlock(&sl->master->bus_mutex); + + return result; +} + + +/* Sysfs attributes. */ + +/* I2C speed attribute for a single chip. */ +static ssize_t speed_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct w1_slave *sl = dev_to_w1_slave(dev); + int result; + + /* Read current speed from slave. Updates data->speed. */ + result = w1_f19_get_i2c_speed(sl); + if (result < 0) + return result; + + /* Return current speed value. */ + return sprintf(buf, "%d\n", result); +} + +static ssize_t speed_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct w1_slave *sl = dev_to_w1_slave(dev); + int error; + + /* Valid values are: "100", "400", "900" */ + if (count < 3 || count > 4 || !buf) + return -EINVAL; + if (count == 4 && buf[3] != '\n') + return -EINVAL; + if (buf[1] != '0' || buf[2] != '0') + return -EINVAL; + + /* Set speed on slave. */ + switch (buf[0]) { + case '1': + error = w1_f19_set_i2c_speed(sl, 0); + break; + case '4': + error = w1_f19_set_i2c_speed(sl, 1); + break; + case '9': + error = w1_f19_set_i2c_speed(sl, 2); + break; + default: + return -EINVAL; + } + + if (error < 0) + return error; + + /* Return bytes written. */ + return count; +} + +static DEVICE_ATTR_RW(speed); + + +/* Busy stretch attribute for a single chip. */ +static ssize_t stretch_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct w1_slave *sl = dev_to_w1_slave(dev); + struct w1_f19_data *data = sl->family_data; + + /* Return current stretch value. */ + return sprintf(buf, "%d\n", data->stretch); +} + +static ssize_t stretch_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct w1_slave *sl = dev_to_w1_slave(dev); + struct w1_f19_data *data = sl->family_data; + + /* Valid values are '1' to '9' */ + if (count < 1 || count > 2 || !buf) + return -EINVAL; + if (count == 2 && buf[1] != '\n') + return -EINVAL; + if (buf[0] < '1' || buf[0] > '9') + return -EINVAL; + + /* Set busy stretch value. */ + data->stretch = buf[0] & 0x0F; + + /* Return bytes written. */ + return count; +} + +static DEVICE_ATTR_RW(stretch); + + +/* All attributes. */ +static struct attribute *w1_f19_attrs[] = { + &dev_attr_speed.attr, + &dev_attr_stretch.attr, + NULL, +}; + +static const struct attribute_group w1_f19_group = { + .attrs = w1_f19_attrs, +}; + +static const struct attribute_group *w1_f19_groups[] = { + &w1_f19_group, + NULL, +}; + + +/* Slave add and remove functions. */ +static int w1_f19_add_slave(struct w1_slave *sl) +{ + struct w1_f19_data *data = NULL; + + /* Allocate memory for slave specific data. */ + data = devm_kzalloc(&sl->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + sl->family_data = data; + + /* Setup default I2C speed on slave. */ + switch (i2c_speed) { + case 100: + __w1_f19_set_i2c_speed(sl, 0); + break; + case 400: + __w1_f19_set_i2c_speed(sl, 1); + break; + case 900: + __w1_f19_set_i2c_speed(sl, 2); + break; + default: + /* + * A i2c_speed module parameter of anything else + * than 100, 400, 900 means not to touch the + * speed of the DS28E17. + * We assume 400kBaud, the power-on value. + */ + data->speed = 1; + } + + /* + * Setup default busy stretch + * configuration for the DS28E17. + */ + data->stretch = i2c_stretch; + + /* Setup I2C adapter. */ + data->adapter.owner = THIS_MODULE; + data->adapter.algo = &w1_f19_i2c_algorithm; + data->adapter.algo_data = sl; + strcpy(data->adapter.name, "w1-"); + strcat(data->adapter.name, sl->name); + data->adapter.dev.parent = &sl->dev; + data->adapter.quirks = &w1_f19_i2c_adapter_quirks; + + return i2c_add_adapter(&data->adapter); +} + +static void w1_f19_remove_slave(struct w1_slave *sl) +{ + struct w1_f19_data *family_data = sl->family_data; + + /* Delete I2C adapter. */ + i2c_del_adapter(&family_data->adapter); + + /* Free slave specific data. */ + devm_kfree(&sl->dev, family_data); + sl->family_data = NULL; +} + + +/* Declarations within the w1 subsystem. */ +static struct w1_family_ops w1_f19_fops = { + .add_slave = w1_f19_add_slave, + .remove_slave = w1_f19_remove_slave, + .groups = w1_f19_groups, +}; + +static struct w1_family w1_family_19 = { + .fid = W1_FAMILY_DS28E17, + .fops = &w1_f19_fops, +}; + + +/* Module init and remove functions. */ +static int __init w1_f19_init(void) +{ + return w1_register_family(&w1_family_19); +} + +static void __exit w1_f19_fini(void) +{ + w1_unregister_family(&w1_family_19); +} + +module_init(w1_f19_init); +module_exit(w1_f19_fini); + diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index 259525c3382a..3c350dfbcd0b 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -268,17 +268,18 @@ static inline int w1_therm_eeprom(struct device *device) int ret, max_trying = 10; u8 *family_data = sl->family_data; - ret = mutex_lock_interruptible(&dev->bus_mutex); - if (ret != 0) - goto post_unlock; - if (!sl->family_data) { ret = -ENODEV; - goto pre_unlock; + goto error; } /* prevent the slave from going away in sleep */ atomic_inc(THERM_REFCNT(family_data)); + + ret = mutex_lock_interruptible(&dev->bus_mutex); + if (ret != 0) + goto dec_refcnt; + memset(rom, 0, sizeof(rom)); while (max_trying--) { @@ -306,17 +307,17 @@ static inline int w1_therm_eeprom(struct device *device) sleep_rem = msleep_interruptible(tm); if (sleep_rem != 0) { ret = -EINTR; - goto post_unlock; + goto dec_refcnt; } ret = mutex_lock_interruptible(&dev->bus_mutex); if (ret != 0) - goto post_unlock; + goto dec_refcnt; } else if (!w1_strong_pullup) { sleep_rem = msleep_interruptible(tm); if (sleep_rem != 0) { ret = -EINTR; - goto pre_unlock; + goto mt_unlock; } } @@ -324,11 +325,11 @@ static inline int w1_therm_eeprom(struct device *device) } } -pre_unlock: +mt_unlock: mutex_unlock(&dev->bus_mutex); - -post_unlock: +dec_refcnt: atomic_dec(THERM_REFCNT(family_data)); +error: return ret; } @@ -350,20 +351,22 @@ static inline int w1_DS18B20_precision(struct device *device, int val) if (val > 12 || val < 9) { pr_warn("Unsupported precision\n"); - return -1; + ret = -EINVAL; + goto error; } - ret = mutex_lock_interruptible(&dev->bus_mutex); - if (ret != 0) - goto post_unlock; - if (!sl->family_data) { ret = -ENODEV; - goto pre_unlock; + goto error; } /* prevent the slave from going away in sleep */ atomic_inc(THERM_REFCNT(family_data)); + + ret = mutex_lock_interruptible(&dev->bus_mutex); + if (ret != 0) + goto dec_refcnt; + memset(rom, 0, sizeof(rom)); /* translate precision to bitmask (see datasheet page 9) */ @@ -411,11 +414,10 @@ static inline int w1_DS18B20_precision(struct device *device, int val) } } -pre_unlock: mutex_unlock(&dev->bus_mutex); - -post_unlock: +dec_refcnt: atomic_dec(THERM_REFCNT(family_data)); +error: return ret; } @@ -490,17 +492,18 @@ static ssize_t read_therm(struct device *device, int ret, max_trying = 10; u8 *family_data = sl->family_data; - ret = mutex_lock_interruptible(&dev->bus_mutex); - if (ret != 0) - goto error; - if (!family_data) { ret = -ENODEV; - goto mt_unlock; + goto error; } /* prevent the slave from going away in sleep */ atomic_inc(THERM_REFCNT(family_data)); + + ret = mutex_lock_interruptible(&dev->bus_mutex); + if (ret != 0) + goto dec_refcnt; + memset(info->rom, 0, sizeof(info->rom)); while (max_trying--) { @@ -542,7 +545,7 @@ static ssize_t read_therm(struct device *device, sleep_rem = msleep_interruptible(tm); if (sleep_rem != 0) { ret = -EINTR; - goto dec_refcnt; + goto mt_unlock; } } @@ -567,10 +570,10 @@ static ssize_t read_therm(struct device *device, break; } -dec_refcnt: - atomic_dec(THERM_REFCNT(family_data)); mt_unlock: mutex_unlock(&dev->bus_mutex); +dec_refcnt: + atomic_dec(THERM_REFCNT(family_data)); error: return ret; } diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c index d191e1f80579..075d120e7b88 100644 --- a/drivers/w1/w1_io.c +++ b/drivers/w1/w1_io.c @@ -58,7 +58,7 @@ static u8 w1_read_bit(struct w1_master *dev); * @dev: the master device * @bit: 0 - write a 0, 1 - write a 0 read the level */ -static u8 w1_touch_bit(struct w1_master *dev, int bit) +u8 w1_touch_bit(struct w1_master *dev, int bit) { if (dev->bus_master->touch_bit) return dev->bus_master->touch_bit(dev->bus_master->data, bit); @@ -69,6 +69,7 @@ static u8 w1_touch_bit(struct w1_master *dev, int bit) return 0; } } +EXPORT_SYMBOL_GPL(w1_touch_bit); /** * w1_write_bit() - Generates a write-0 or write-1 cycle. diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 6431087816ba..f3e97c5f94c9 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -719,6 +719,10 @@ struct vmbus_channel { struct vmbus_close_msg close_msg; + /* Statistics */ + u64 interrupts; /* Host to Guest interrupts */ + u64 sig_events; /* Guest to Host events */ + /* Channel callback's invoked in softirq context */ struct tasklet_struct callback_event; void (*onchannel_callback)(void *context); @@ -829,6 +833,11 @@ struct vmbus_channel { struct rcu_head rcu; /* + * For sysfs per-channel properties. + */ + struct kobject kobj; + + /* * For performance critical channels (storage, networking * etc,), Hyper-V has a mechanism to enhance the throughput * at the expense of latency: @@ -1089,6 +1098,7 @@ struct hv_device { struct device device; struct vmbus_channel *channel; + struct kset *channels_kset; }; diff --git a/include/linux/w1.h b/include/linux/w1.h index 5b2972946dda..694101f744c7 100644 --- a/include/linux/w1.h +++ b/include/linux/w1.h @@ -293,6 +293,7 @@ void w1_unregister_family(struct w1_family *family); w1_unregister_family) u8 w1_triplet(struct w1_master *dev, int bdir); +u8 w1_touch_bit(struct w1_master *dev, int bit); void w1_write_8(struct w1_master *, u8); u8 w1_read_8(struct w1_master *); int w1_reset_bus(struct w1_master *); |