mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-23 20:24:12 +08:00
Char/Misc driver patches for 4.2-rc1
Here's the big char/misc driver pull request for 4.2-rc1. Lots of mei, extcon, coresight, uio, mic, and other driver updates in here. Full details in the shortlog. All of these have been in linux-next for some time with no reported problems. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlWNn0gACgkQMUfUDdst+ykCCQCgvdF4F2+Hy9+RATdk22ak1uq1 JDMAoJTf4oyaIEdaiOKfEIWg9MasS42B =H5wD -----END PGP SIGNATURE----- Merge tag 'char-misc-4.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc Pull char/misc driver updates from Greg KH: "Here's the big char/misc driver pull request for 4.2-rc1. Lots of mei, extcon, coresight, uio, mic, and other driver updates in here. Full details in the shortlog. All of these have been in linux-next for some time with no reported problems" * tag 'char-misc-4.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (176 commits) mei: me: wait for power gating exit confirmation mei: reset flow control on the last client disconnection MAINTAINERS: mei: add mei_cl_bus.h to maintained file list misc: sram: sort and clean up included headers misc: sram: move reserved block logic out of probe function misc: sram: add private struct device and virt_base members misc: sram: report correct SRAM pool size misc: sram: bump error message level on unclean driver unbinding misc: sram: fix device node reference leak on error misc: sram: fix enabled clock leak on error path misc: mic: Fix reported static checker warning misc: mic: Fix randconfig build error by including errno.h uio: pruss: Drop depends on ARCH_DAVINCI_DA850 from config uio: pruss: Add CONFIG_HAS_IOMEM dependence uio: pruss: Include <linux/sizes.h> extcon: Redefine the unique id of supported external connectors without 'enum extcon' type char:xilinx_hwicap:buffer_icap - change 1/0 to true/false for bool type variable in function buffer_icap_set_configuration(). Drivers: hv: vmbus: Allocate ring buffer memory in NUMA aware fashion parport: check exclusive access before register w1: use correct lock on error in w1_seq_show() ...
This commit is contained in:
commit
d87823813f
11
Documentation/ABI/stable/sysfs-bus-w1
Normal file
11
Documentation/ABI/stable/sysfs-bus-w1
Normal file
@ -0,0 +1,11 @@
|
||||
What: /sys/bus/w1/devices/.../w1_master_timeout_us
|
||||
Date: April 2015
|
||||
Contact: Dmitry Khromov <dk@icelogic.net>
|
||||
Description: Bus scanning interval, microseconds component.
|
||||
Some of 1-Wire devices commonly associated with physical access
|
||||
control systems are attached/generate presence for as short as
|
||||
100 ms - hence the tens-to-hundreds milliseconds scan intervals
|
||||
are required.
|
||||
see Documentation/w1/w1.generic for detailed information.
|
||||
Users: any user space application which wants to know bus scanning
|
||||
interval
|
6
Documentation/ABI/stable/sysfs-driver-w1_ds28ea00
Normal file
6
Documentation/ABI/stable/sysfs-driver-w1_ds28ea00
Normal file
@ -0,0 +1,6 @@
|
||||
What: /sys/bus/w1/devices/.../w1_seq
|
||||
Date: Apr 2015
|
||||
Contact: Matt Campbell <mattrcampbell@gmail.com>
|
||||
Description: Support for the DS28EA00 chain sequence function
|
||||
see Documentation/w1/slaves/w1_therm for detailed information
|
||||
Users: any user space application which wants to communicate with DS28EA00
|
450
Documentation/ABI/testing/sysfs-bus-coresight-devices-etm4x
Normal file
450
Documentation/ABI/testing/sysfs-bus-coresight-devices-etm4x
Normal file
@ -0,0 +1,450 @@
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/enable_source
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Enable/disable tracing on this specific trace entiry.
|
||||
Enabling a source implies the source has been configured
|
||||
properly and a sink has been identidifed for it. The path
|
||||
of coresight components linking the source to the sink is
|
||||
configured and managed automatically by the coresight framework.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/cpu
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) The CPU this tracing entity is associated with.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/nr_pe_cmp
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Indicates the number of PE comparator inputs that are
|
||||
available for tracing.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/nr_addr_cmp
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Indicates the number of address comparator pairs that are
|
||||
available for tracing.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/nr_cntr
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Indicates the number of counters that are available for
|
||||
tracing.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/nr_ext_inp
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Indicates how many external inputs are implemented.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/numcidc
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Indicates the number of Context ID comparators that are
|
||||
available for tracing.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/numvmidc
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Indicates the number of VMID comparators that are available
|
||||
for tracing.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/nrseqstate
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Indicates the number of sequencer states that are
|
||||
implemented.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/nr_resource
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Indicates the number of resource selection pairs that are
|
||||
available for tracing.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/nr_ss_cmp
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Indicates the number of single-shot comparator controls that
|
||||
are available for tracing.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/reset
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (W) Cancels all configuration on a trace unit and set it back
|
||||
to its boot configuration.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/mode
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Controls various modes supported by this ETM, for example
|
||||
P0 instruction tracing, branch broadcast, cycle counting and
|
||||
context ID tracing.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/pe
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Controls which PE to trace.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/event
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Controls the tracing of arbitrary events from bank 0 to 3.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/event_instren
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Controls the behavior of the events in bank 0 to 3.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/event_ts
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Controls the insertion of global timestamps in the trace
|
||||
streams.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/syncfreq
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Controls how often trace synchronization requests occur.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/cyc_threshold
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Sets the threshold value for cycle counting.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/bb_ctrl
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Controls which regions in the memory map are enabled to
|
||||
use branch broadcasting.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/event_vinst
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Controls instruction trace filtering.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/s_exlevel_vinst
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) In Secure state, each bit controls whether instruction
|
||||
tracing is enabled for the corresponding exception level.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/ns_exlevel_vinst
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) In non-secure state, each bit controls whether instruction
|
||||
tracing is enabled for the corresponding exception level.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/addr_idx
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Select which address comparator or pair (of comparators) to
|
||||
work with.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/addr_instdatatype
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Controls what type of comparison the trace unit performs.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/addr_single
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Used to setup single address comparator values.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/addr_range
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Used to setup address range comparator values.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/seq_idx
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Select which sequensor.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/seq_state
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Use this to set, or read, the sequencer state.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/seq_event
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Moves the sequencer state to a specific state.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/seq_reset_event
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Moves the sequencer to state 0 when a programmed event
|
||||
occurs.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/cntr_idx
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Select which counter unit to work with.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/cntrldvr
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) This sets or returns the reload count value of the
|
||||
specific counter.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/cntr_val
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) This sets or returns the current count value of the
|
||||
specific counter.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/cntr_ctrl
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Controls the operation of the selected counter.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/res_idx
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Select which resource selection unit to work with.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/res_ctrl
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Controls the selection of the resources in the trace unit.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/ctxid_idx
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Select which context ID comparator to work with.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/ctxid_val
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Get/Set the context ID comparator value to trigger on.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/ctxid_masks
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Mask for all 8 context ID comparator value
|
||||
registers (if implemented).
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/vmid_idx
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Select which virtual machine ID comparator to work with.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/vmid_val
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Get/Set the virtual machine ID comparator value to
|
||||
trigger on.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/vmid_masks
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Mask for all 8 virtual machine ID comparator value
|
||||
registers (if implemented).
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/mgmt/trcoslsr
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Print the content of the OS Lock Status Register (0x304).
|
||||
The value it taken directly from the HW.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/mgmt/trcpdcr
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Print the content of the Power Down Control Register
|
||||
(0x310). The value is taken directly from the HW.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/mgmt/trcpdsr
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Print the content of the Power Down Status Register
|
||||
(0x314). The value is taken directly from the HW.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/mgmt/trclsr
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Print the content of the SW Lock Status Register
|
||||
(0xFB4). The value is taken directly from the HW.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/mgmt/trcauthstatus
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Print the content of the Authentication Status Register
|
||||
(0xFB8). The value is taken directly from the HW.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/mgmt/trcdevid
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Print the content of the Device ID Register
|
||||
(0xFC8). The value is taken directly from the HW.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/mgmt/trcdevtype
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Print the content of the Device Type Register
|
||||
(0xFCC). The value is taken directly from the HW.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/mgmt/trcpidr0
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Print the content of the Peripheral ID0 Register
|
||||
(0xFE0). The value is taken directly from the HW.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/mgmt/trcpidr1
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Print the content of the Peripheral ID1 Register
|
||||
(0xFE4). The value is taken directly from the HW.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/mgmt/trcpidr2
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Print the content of the Peripheral ID2 Register
|
||||
(0xFE8). The value is taken directly from the HW.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/mgmt/trcpidr3
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Print the content of the Peripheral ID3 Register
|
||||
(0xFEC). The value is taken directly from the HW.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/trcidr/trcidr0
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Returns the tracing capabilities of the trace unit (0x1E0).
|
||||
The value is taken directly from the HW.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/trcidr/trcidr1
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Returns the tracing capabilities of the trace unit (0x1E4).
|
||||
The value is taken directly from the HW.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/trcidr/trcidr2
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Returns the maximum size of the data value, data address,
|
||||
VMID, context ID and instuction address in the trace unit
|
||||
(0x1E8). The value is taken directly from the HW.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/trcidr/trcidr3
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Returns the value associated with various resources
|
||||
available to the trace unit. See the Trace Macrocell
|
||||
architecture specification for more details (0x1E8).
|
||||
The value is taken directly from the HW.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/trcidr/trcidr4
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Returns how many resources the trace unit supports (0x1F0).
|
||||
The value is taken directly from the HW.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/trcidr/trcidr5
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Returns how many resources the trace unit supports (0x1F4).
|
||||
The value is taken directly from the HW.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/trcidr/trcidr8
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Returns the maximum speculation depth of the instruction
|
||||
trace stream. (0x180). The value is taken directly from the HW.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/trcidr/trcidr9
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Returns the number of P0 right-hand keys that the trace unit
|
||||
can use (0x184). The value is taken directly from the HW.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/trcidr/trcidr10
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Returns the number of P1 right-hand keys that the trace unit
|
||||
can use (0x188). The value is taken directly from the HW.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/trcidr/trcidr11
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Returns the number of special P1 right-hand keys that the
|
||||
trace unit can use (0x18C). The value is taken directly from
|
||||
the HW.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/trcidr/trcidr12
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Returns the number of conditional P1 right-hand keys that
|
||||
the trace unit can use (0x190). The value is taken directly
|
||||
from the HW.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.etm/trcidr/trcidr13
|
||||
Date: April 2015
|
||||
KernelVersion: 4.01
|
||||
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (R) Returns the number of special conditional P1 right-hand keys
|
||||
that the trace unit can use (0x194). The value is taken
|
||||
directly from the HW.
|
@ -4,4 +4,18 @@ KernelVersion: 3.10
|
||||
Contact: Samuel Ortiz <sameo@linux.intel.com>
|
||||
linux-mei@linux.intel.com
|
||||
Description: Stores the same MODALIAS value emitted by uevent
|
||||
Format: mei:<mei device name>
|
||||
Format: mei:<mei device name>:<device uuid>:
|
||||
|
||||
What: /sys/bus/mei/devices/.../name
|
||||
Date: May 2015
|
||||
KernelVersion: 4.2
|
||||
Contact: Tomas Winkler <tomas.winkler@intel.com>
|
||||
Description: Stores mei client device name
|
||||
Format: string
|
||||
|
||||
What: /sys/bus/mei/devices/.../uuid
|
||||
Date: May 2015
|
||||
KernelVersion: 4.2
|
||||
Contact: Tomas Winkler <tomas.winkler@intel.com>
|
||||
Description: Stores mei client device uuid
|
||||
Format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
|
@ -17,15 +17,19 @@ its hardware characteristcs.
|
||||
- "arm,coresight-tmc", "arm,primecell";
|
||||
- "arm,coresight-funnel", "arm,primecell";
|
||||
- "arm,coresight-etm3x", "arm,primecell";
|
||||
- "qcom,coresight-replicator1x", "arm,primecell";
|
||||
|
||||
* reg: physical base address and length of the register
|
||||
set(s) of the component.
|
||||
|
||||
* clocks: the clock associated to this component.
|
||||
* clocks: the clocks associated to this component.
|
||||
|
||||
* clock-names: the name of the clock as referenced by the code.
|
||||
Since we are using the AMBA framework, the name should be
|
||||
"apb_pclk".
|
||||
* clock-names: the name of the clocks referenced by the code.
|
||||
Since we are using the AMBA framework, the name of the clock
|
||||
providing the interconnect should be "apb_pclk", and some
|
||||
coresight blocks also have an additional clock "atclk", which
|
||||
clocks the core of that coresight component. The latter clock
|
||||
is optional.
|
||||
|
||||
* port or ports: The representation of the component's port
|
||||
layout using the generic DT graph presentation found in
|
||||
|
@ -67,6 +67,12 @@ Optional properties:
|
||||
present, the number of values should be less than or equal to the
|
||||
number of inputs, unspecified inputs will use the chip default.
|
||||
|
||||
- wlf,hpdet-channel : Headphone detection channel.
|
||||
ARIZONA_ACCDET_MODE_HPL or 1 - Headphone detect mode is set to HPDETL
|
||||
ARIZONA_ACCDET_MODE_HPR or 2 - Headphone detect mode is set to HPDETR
|
||||
If this node is not mentioned or if the value is unknown, then
|
||||
headphone detection mode is set to HPDETL.
|
||||
|
||||
- DCVDD-supply, MICVDD-supply : Power supplies, only need to be specified if
|
||||
they are being externally supplied. As covered in
|
||||
Documentation/devicetree/bindings/regulator/regulator.txt
|
||||
|
@ -24,6 +24,10 @@ a virtual bus called mic bus is created and virtual dma devices are
|
||||
created on it by the host/card drivers. On host the channels are private
|
||||
and used only by the host driver to transfer data for the virtio devices.
|
||||
|
||||
The Symmetric Communication Interface (SCIF (pronounced as skiff)) is a
|
||||
low level communications API across PCIe currently implemented for MIC.
|
||||
More details are available at scif_overview.txt.
|
||||
|
||||
Here is a block diagram of the various components described above. The
|
||||
virtio backends are situated on the host rather than the card given better
|
||||
single threaded performance for the host compared to MIC, the ability of
|
||||
@ -47,18 +51,18 @@ the fact that the virtio block storage backend can only be on the host.
|
||||
| | | Virtio over PCIe IOCTLs |
|
||||
| | +--------------------------+
|
||||
+-----------+ | | | +-----------+
|
||||
| MIC DMA | | | | | MIC DMA |
|
||||
| Driver | | | | | Driver |
|
||||
+-----------+ | | | +-----------+
|
||||
| | | | |
|
||||
+---------------+ | | | +----------------+
|
||||
|MIC virtual Bus| | | | |MIC virtual Bus |
|
||||
+---------------+ | | | +----------------+
|
||||
| | | | |
|
||||
| +--------------+ | +---------------+ |
|
||||
| |Intel MIC | | |Intel MIC | |
|
||||
+---|Card Driver | | |Host Driver | |
|
||||
+--------------+ | +---------------+-----+
|
||||
| MIC DMA | | +----------+ | +-----------+ | | MIC DMA |
|
||||
| Driver | | | SCIF | | | SCIF | | | Driver |
|
||||
+-----------+ | +----------+ | +-----------+ | +-----------+
|
||||
| | | | | | |
|
||||
+---------------+ | +-----+-----+ | +-----+-----+ | +---------------+
|
||||
|MIC virtual Bus| | |SCIF HW Bus| | |SCIF HW BUS| | |MIC virtual Bus|
|
||||
+---------------+ | +-----------+ | +-----+-----+ | +---------------+
|
||||
| | | | | | |
|
||||
| +--------------+ | | | +---------------+ |
|
||||
| |Intel MIC | | | | |Intel MIC | |
|
||||
+---|Card Driver +----+ | | |Host Driver | |
|
||||
+--------------+ | +----+---------------+-----+
|
||||
| | |
|
||||
+-------------------------------------------------------------+
|
||||
| |
|
||||
|
@ -35,6 +35,7 @@
|
||||
|
||||
exec=/usr/sbin/mpssd
|
||||
sysfs="/sys/class/mic"
|
||||
mic_modules="mic_host mic_x100_dma scif"
|
||||
|
||||
start()
|
||||
{
|
||||
@ -48,18 +49,15 @@ start()
|
||||
fi
|
||||
|
||||
echo -e $"Starting MPSS Stack"
|
||||
echo -e $"Loading MIC_X100_DMA & MIC_HOST Modules"
|
||||
echo -e $"Loading MIC drivers:" $mic_modules
|
||||
|
||||
for f in "mic_host" "mic_x100_dma"
|
||||
do
|
||||
modprobe $f
|
||||
RETVAL=$?
|
||||
if [ $RETVAL -ne 0 ]; then
|
||||
failure
|
||||
echo
|
||||
return $RETVAL
|
||||
fi
|
||||
done
|
||||
modprobe -a $mic_modules
|
||||
RETVAL=$?
|
||||
if [ $RETVAL -ne 0 ]; then
|
||||
failure
|
||||
echo
|
||||
return $RETVAL
|
||||
fi
|
||||
|
||||
# Start the daemon
|
||||
echo -n $"Starting MPSSD "
|
||||
@ -170,8 +168,8 @@ unload()
|
||||
stop
|
||||
|
||||
sleep 5
|
||||
echo -n $"Removing MIC_HOST & MIC_X100_DMA Modules: "
|
||||
modprobe -r mic_host mic_x100_dma
|
||||
echo -n $"Removing MIC drivers:" $mic_modules
|
||||
modprobe -r $mic_modules
|
||||
RETVAL=$?
|
||||
[ $RETVAL -ne 0 ] && failure || success
|
||||
echo
|
||||
|
98
Documentation/mic/scif_overview.txt
Normal file
98
Documentation/mic/scif_overview.txt
Normal file
@ -0,0 +1,98 @@
|
||||
The Symmetric Communication Interface (SCIF (pronounced as skiff)) is a low
|
||||
level communications API across PCIe currently implemented for MIC. Currently
|
||||
SCIF provides inter-node communication within a single host platform, where a
|
||||
node is a MIC Coprocessor or Xeon based host. SCIF abstracts the details of
|
||||
communicating over the PCIe bus while providing an API that is symmetric
|
||||
across all the nodes in the PCIe network. An important design objective for SCIF
|
||||
is to deliver the maximum possible performance given the communication
|
||||
abilities of the hardware. SCIF has been used to implement an offload compiler
|
||||
runtime and OFED support for MPI implementations for MIC coprocessors.
|
||||
|
||||
==== SCIF API Components ====
|
||||
The SCIF API has the following parts:
|
||||
1. Connection establishment using a client server model
|
||||
2. Byte stream messaging intended for short messages
|
||||
3. Node enumeration to determine online nodes
|
||||
4. Poll semantics for detection of incoming connections and messages
|
||||
5. Memory registration to pin down pages
|
||||
6. Remote memory mapping for low latency CPU accesses via mmap
|
||||
7. Remote DMA (RDMA) for high bandwidth DMA transfers
|
||||
8. Fence APIs for RDMA synchronization
|
||||
|
||||
SCIF exposes the notion of a connection which can be used by peer processes on
|
||||
nodes in a SCIF PCIe "network" to share memory "windows" and to communicate. A
|
||||
process in a SCIF node initiates a SCIF connection to a peer process on a
|
||||
different node via a SCIF "endpoint". SCIF endpoints support messaging APIs
|
||||
which are similar to connection oriented socket APIs. Connected SCIF endpoints
|
||||
can also register local memory which is followed by data transfer using either
|
||||
DMA, CPU copies or remote memory mapping via mmap. SCIF supports both user and
|
||||
kernel mode clients which are functionally equivalent.
|
||||
|
||||
==== SCIF Performance for MIC ====
|
||||
DMA bandwidth comparison between the TCP (over ethernet over PCIe) stack versus
|
||||
SCIF shows the performance advantages of SCIF for HPC applications and runtimes.
|
||||
|
||||
Comparison of TCP and SCIF based BW
|
||||
|
||||
Throughput (GB/sec)
|
||||
8 + PCIe Bandwidth ******
|
||||
+ TCP ######
|
||||
7 + ************************************** SCIF %%%%%%
|
||||
| %%%%%%%%%%%%%%%%%%%
|
||||
6 + %%%%
|
||||
| %%
|
||||
| %%%
|
||||
5 + %%
|
||||
| %%
|
||||
4 + %%
|
||||
| %%
|
||||
3 + %%
|
||||
| %
|
||||
2 + %%
|
||||
| %%
|
||||
| %
|
||||
1 +
|
||||
+ ######################################
|
||||
0 +++---+++--+--+-+--+--+-++-+--+-++-+--+-++-+-
|
||||
1 10 100 1000 10000 100000
|
||||
Transfer Size (KBytes)
|
||||
|
||||
SCIF allows memory sharing via mmap(..) between processes on different PCIe
|
||||
nodes and thus provides bare-metal PCIe latency. The round trip SCIF mmap
|
||||
latency from the host to an x100 MIC for an 8 byte message is 0.44 usecs.
|
||||
|
||||
SCIF has a user space library which is a thin IOCTL wrapper providing a user
|
||||
space API similar to the kernel API in scif.h. The SCIF user space library
|
||||
is distributed @ https://software.intel.com/en-us/mic-developer
|
||||
|
||||
Here is some pseudo code for an example of how two applications on two PCIe
|
||||
nodes would typically use the SCIF API:
|
||||
|
||||
Process A (on node A) Process B (on node B)
|
||||
|
||||
/* get online node information */
|
||||
scif_get_node_ids(..) scif_get_node_ids(..)
|
||||
scif_open(..) scif_open(..)
|
||||
scif_bind(..) scif_bind(..)
|
||||
scif_listen(..)
|
||||
scif_accept(..) scif_connect(..)
|
||||
/* SCIF connection established */
|
||||
|
||||
/* Send and receive short messages */
|
||||
scif_send(..)/scif_recv(..) scif_send(..)/scif_recv(..)
|
||||
|
||||
/* Register memory */
|
||||
scif_register(..) scif_register(..)
|
||||
|
||||
/* RDMA */
|
||||
scif_readfrom(..)/scif_writeto(..) scif_readfrom(..)/scif_writeto(..)
|
||||
|
||||
/* Fence DMAs */
|
||||
scif_fence_signal(..) scif_fence_signal(..)
|
||||
|
||||
mmap(..) mmap(..)
|
||||
|
||||
/* Access remote registered memory */
|
||||
|
||||
/* Close the endpoints */
|
||||
scif_close(..) scif_close(..)
|
@ -11,12 +11,14 @@ Author: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
|
||||
Description
|
||||
-----------
|
||||
|
||||
w1_therm provides basic temperature conversion for ds18*20 devices.
|
||||
w1_therm provides basic temperature conversion for ds18*20 devices, and the
|
||||
ds28ea00 device.
|
||||
supported family codes:
|
||||
W1_THERM_DS18S20 0x10
|
||||
W1_THERM_DS1822 0x22
|
||||
W1_THERM_DS18B20 0x28
|
||||
W1_THERM_DS1825 0x3B
|
||||
W1_THERM_DS28EA00 0x42
|
||||
|
||||
Support is provided through the sysfs w1_slave file. Each open and
|
||||
read sequence will initiate a temperature conversion then provide two
|
||||
@ -48,3 +50,10 @@ resistor). The DS18b20 temperature sensor specification lists a
|
||||
maximum current draw of 1.5mA and that a 5k pullup resistor is not
|
||||
sufficient. The strong pullup is designed to provide the additional
|
||||
current required.
|
||||
|
||||
The DS28EA00 provides an additional two pins for implementing a sequence
|
||||
detection algorithm. This feature allows you to determine the physical
|
||||
location of the chip in the 1-wire bus without needing pre-existing
|
||||
knowledge of the bus ordering. Support is provided through the sysfs
|
||||
w1_seq file. The file will contain a single line with an integer value
|
||||
representing the device index in the bus starting at 0.
|
||||
|
@ -76,21 +76,24 @@ See struct w1_bus_master definition in w1.h for details.
|
||||
|
||||
w1 master sysfs interface
|
||||
------------------------------------------------------------------
|
||||
<xx-xxxxxxxxxxxxx> - a directory for a found device. The format is family-serial
|
||||
<xx-xxxxxxxxxxxxx> - A directory for a found device. The format is family-serial
|
||||
bus - (standard) symlink to the w1 bus
|
||||
driver - (standard) symlink to the w1 driver
|
||||
w1_master_add - Manually register a slave device
|
||||
w1_master_attempts - the number of times a search was attempted
|
||||
w1_master_add - (rw) manually register a slave device
|
||||
w1_master_attempts - (ro) the number of times a search was attempted
|
||||
w1_master_max_slave_count
|
||||
- maximum number of slaves to search for at a time
|
||||
w1_master_name - the name of the device (w1_bus_masterX)
|
||||
w1_master_pullup - 5V strong pullup 0 enabled, 1 disabled
|
||||
w1_master_remove - Manually remove a slave device
|
||||
w1_master_search - the number of searches left to do, -1=continual (default)
|
||||
- (rw) maximum number of slaves to search for at a time
|
||||
w1_master_name - (ro) the name of the device (w1_bus_masterX)
|
||||
w1_master_pullup - (rw) 5V strong pullup 0 enabled, 1 disabled
|
||||
w1_master_remove - (rw) manually remove a slave device
|
||||
w1_master_search - (rw) the number of searches left to do,
|
||||
-1=continual (default)
|
||||
w1_master_slave_count
|
||||
- the number of slaves found
|
||||
w1_master_slaves - the names of the slaves, one per line
|
||||
w1_master_timeout - the delay in seconds between searches
|
||||
- (ro) the number of slaves found
|
||||
w1_master_slaves - (ro) the names of the slaves, one per line
|
||||
w1_master_timeout - (ro) the delay in seconds between searches
|
||||
w1_master_timeout_us
|
||||
- (ro) the delay in microseconds beetwen searches
|
||||
|
||||
If you have a w1 bus that never changes (you don't add or remove devices),
|
||||
you can set the module parameter search_count to a small positive number
|
||||
@ -101,6 +104,11 @@ generally only make sense when searching is disabled, as a search will
|
||||
redetect manually removed devices that are present and timeout manually
|
||||
added devices that aren't on the bus.
|
||||
|
||||
Bus searches occur at an interval, specified as a summ of timeout and
|
||||
timeout_us module parameters (either of which may be 0) for as long as
|
||||
w1_master_search remains greater than 0 or is -1. Each search attempt
|
||||
decrements w1_master_search by 1 (down to 0) and increments
|
||||
w1_master_attempts by 1.
|
||||
|
||||
w1 slave sysfs interface
|
||||
------------------------------------------------------------------
|
||||
|
14
MAINTAINERS
14
MAINTAINERS
@ -732,7 +732,7 @@ ANDROID DRIVERS
|
||||
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
M: Arve Hjønnevåg <arve@android.com>
|
||||
M: Riley Andrews <riandrews@android.com>
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/gregkh/staging.git
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
|
||||
L: devel@driverdev.osuosl.org
|
||||
S: Supported
|
||||
F: drivers/android/
|
||||
@ -3207,9 +3207,9 @@ S: Maintained
|
||||
F: drivers/platform/x86/dell-smo8800.c
|
||||
|
||||
DELL LAPTOP SMM DRIVER
|
||||
M: Guenter Roeck <linux@roeck-us.net>
|
||||
M: Pali Rohár <pali.rohar@gmail.com>
|
||||
S: Maintained
|
||||
F: drivers/char/i8k.c
|
||||
F: drivers/hwmon/dell-smm-hwmon.c
|
||||
F: include/uapi/linux/i8k.h
|
||||
|
||||
DELL SYSTEMS MANAGEMENT BASE DRIVER (dcdbas)
|
||||
@ -5444,6 +5444,7 @@ M: Tomas Winkler <tomas.winkler@intel.com>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Supported
|
||||
F: include/uapi/linux/mei.h
|
||||
F: include/linux/mei_cl_bus.h
|
||||
F: drivers/misc/mei/*
|
||||
F: Documentation/misc-devices/mei/*
|
||||
|
||||
@ -7572,13 +7573,16 @@ S: Maintained
|
||||
F: Documentation/mn10300/
|
||||
F: arch/mn10300/
|
||||
|
||||
PARALLEL PORT SUPPORT
|
||||
PARALLEL PORT SUBSYSTEM
|
||||
M: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
|
||||
M: Sudip Mukherjee <sudip@vectorindia.org>
|
||||
L: linux-parport@lists.infradead.org (subscribers-only)
|
||||
S: Orphan
|
||||
S: Maintained
|
||||
F: drivers/parport/
|
||||
F: include/linux/parport*.h
|
||||
F: drivers/char/ppdev.c
|
||||
F: include/uapi/linux/ppdev.h
|
||||
F: Documentation/parport*.txt
|
||||
|
||||
PARAVIRT_OPS INTERFACE
|
||||
M: Jeremy Fitzhardinge <jeremy@goop.org>
|
||||
|
@ -105,7 +105,7 @@ static int etap_tramp(char *dev, char *gate, int control_me,
|
||||
sprintf(data_fd_buf, "%d", data_remote);
|
||||
sprintf(version_buf, "%d", UML_NET_VERSION);
|
||||
if (gate != NULL) {
|
||||
strcpy(gate_buf, gate);
|
||||
strncpy(gate_buf, gate, 15);
|
||||
args = setup_args;
|
||||
}
|
||||
else args = nosetup_args;
|
||||
|
@ -1055,24 +1055,19 @@ config TOSHIBA
|
||||
Say N otherwise.
|
||||
|
||||
config I8K
|
||||
tristate "Dell laptop support"
|
||||
tristate "Dell i8k legacy laptop support"
|
||||
select HWMON
|
||||
select SENSORS_DELL_SMM
|
||||
---help---
|
||||
This adds a driver to safely access the System Management Mode
|
||||
of the CPU on the Dell Inspiron 8000. The System Management Mode
|
||||
is used to read cpu temperature and cooling fan status and to
|
||||
control the fans on the I8K portables.
|
||||
This option enables legacy /proc/i8k userspace interface in hwmon
|
||||
dell-smm-hwmon driver. Character file /proc/i8k reports bios version,
|
||||
temperature and allows controlling fan speeds of Dell laptops via
|
||||
System Management Mode. For old Dell laptops (like Dell Inspiron 8000)
|
||||
it reports also power and hotkey status. For fan speed control is
|
||||
needed userspace package i8kutils.
|
||||
|
||||
This driver has been tested only on the Inspiron 8000 but it may
|
||||
also work with other Dell laptops. You can force loading on other
|
||||
models by passing the parameter `force=1' to the module. Use at
|
||||
your own risk.
|
||||
|
||||
For information on utilities to make use of this driver see the
|
||||
I8K Linux utilities web site at:
|
||||
<http://people.debian.org/~dz/i8k/>
|
||||
|
||||
Say Y if you intend to run this kernel on a Dell Inspiron 8000.
|
||||
Say Y if you intend to run this kernel on old Dell laptops or want to
|
||||
use userspace package i8kutils.
|
||||
Say N otherwise.
|
||||
|
||||
config X86_REBOOTFIXUPS
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h> /* TASK_* */
|
||||
#include <linux/parport.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "paride.h"
|
||||
|
||||
@ -244,17 +245,19 @@ void paride_unregister(PIP * pr)
|
||||
|
||||
EXPORT_SYMBOL(paride_unregister);
|
||||
|
||||
static int pi_register_parport(PIA * pi, int verbose)
|
||||
static int pi_register_parport(PIA *pi, int verbose, int unit)
|
||||
{
|
||||
struct parport *port;
|
||||
struct pardev_cb par_cb;
|
||||
|
||||
port = parport_find_base(pi->port);
|
||||
if (!port)
|
||||
return 0;
|
||||
|
||||
pi->pardev = parport_register_device(port,
|
||||
pi->device, NULL,
|
||||
pi_wake_up, NULL, 0, (void *) pi);
|
||||
memset(&par_cb, 0, sizeof(par_cb));
|
||||
par_cb.wakeup = pi_wake_up;
|
||||
par_cb.private = (void *)pi;
|
||||
pi->pardev = parport_register_dev_model(port, pi->device, &par_cb,
|
||||
unit);
|
||||
parport_put_port(port);
|
||||
if (!pi->pardev)
|
||||
return 0;
|
||||
@ -311,7 +314,7 @@ static int pi_probe_unit(PIA * pi, int unit, char *scratch, int verbose)
|
||||
e = pi->proto->max_units;
|
||||
}
|
||||
|
||||
if (!pi_register_parport(pi, verbose))
|
||||
if (!pi_register_parport(pi, verbose, s))
|
||||
return 0;
|
||||
|
||||
if (pi->proto->test_port) {
|
||||
@ -432,3 +435,45 @@ int pi_init(PIA * pi, int autoprobe, int port, int mode,
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(pi_init);
|
||||
|
||||
static int pi_probe(struct pardevice *par_dev)
|
||||
{
|
||||
struct device_driver *drv = par_dev->dev.driver;
|
||||
int len = strlen(drv->name);
|
||||
|
||||
if (strncmp(par_dev->name, drv->name, len))
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *pi_register_driver(char *name)
|
||||
{
|
||||
struct parport_driver *parp_drv;
|
||||
int ret;
|
||||
|
||||
parp_drv = kzalloc(sizeof(*parp_drv), GFP_KERNEL);
|
||||
if (!parp_drv)
|
||||
return NULL;
|
||||
|
||||
parp_drv->name = name;
|
||||
parp_drv->probe = pi_probe;
|
||||
parp_drv->devmodel = true;
|
||||
|
||||
ret = parport_register_driver(parp_drv);
|
||||
if (ret) {
|
||||
kfree(parp_drv);
|
||||
return NULL;
|
||||
}
|
||||
return (void *)parp_drv;
|
||||
}
|
||||
EXPORT_SYMBOL(pi_register_driver);
|
||||
|
||||
void pi_unregister_driver(void *_drv)
|
||||
{
|
||||
struct parport_driver *drv = _drv;
|
||||
|
||||
parport_unregister_driver(drv);
|
||||
kfree(drv);
|
||||
}
|
||||
EXPORT_SYMBOL(pi_unregister_driver);
|
||||
|
@ -165,6 +165,8 @@ typedef struct pi_protocol PIP;
|
||||
|
||||
extern int paride_register( PIP * );
|
||||
extern void paride_unregister ( PIP * );
|
||||
void *pi_register_driver(char *);
|
||||
void pi_unregister_driver(void *);
|
||||
|
||||
#endif /* __DRIVERS_PARIDE_H__ */
|
||||
/* end of paride.h */
|
||||
|
@ -221,6 +221,7 @@ static int pcd_busy; /* request being processed ? */
|
||||
static int pcd_sector; /* address of next requested sector */
|
||||
static int pcd_count; /* number of blocks still to do */
|
||||
static char *pcd_buf; /* buffer for request in progress */
|
||||
static void *par_drv; /* reference of parport driver */
|
||||
|
||||
/* kernel glue structures */
|
||||
|
||||
@ -690,6 +691,12 @@ static int pcd_detect(void)
|
||||
printk("%s: %s version %s, major %d, nice %d\n",
|
||||
name, name, PCD_VERSION, major, nice);
|
||||
|
||||
par_drv = pi_register_driver(name);
|
||||
if (!par_drv) {
|
||||
pr_err("failed to register %s driver\n", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
k = 0;
|
||||
if (pcd_drive_count == 0) { /* nothing spec'd - so autoprobe for 1 */
|
||||
cd = pcd;
|
||||
@ -723,6 +730,7 @@ static int pcd_detect(void)
|
||||
printk("%s: No CD-ROM drive found\n", name);
|
||||
for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++)
|
||||
put_disk(cd->disk);
|
||||
pi_unregister_driver(par_drv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -984,6 +992,7 @@ static void __exit pcd_exit(void)
|
||||
}
|
||||
blk_cleanup_queue(pcd_queue);
|
||||
unregister_blkdev(major, name);
|
||||
pi_unregister_driver(par_drv);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -247,6 +247,8 @@ static char *pd_errs[17] = { "ERR", "INDEX", "ECC", "DRQ", "SEEK", "WRERR",
|
||||
"IDNF", "MC", "UNC", "???", "TMO"
|
||||
};
|
||||
|
||||
static void *par_drv; /* reference of parport driver */
|
||||
|
||||
static inline int status_reg(struct pd_unit *disk)
|
||||
{
|
||||
return pi_read_regr(disk->pi, 1, 6);
|
||||
@ -872,6 +874,12 @@ static int pd_detect(void)
|
||||
pd_drive_count++;
|
||||
}
|
||||
|
||||
par_drv = pi_register_driver(name);
|
||||
if (!par_drv) {
|
||||
pr_err("failed to register %s driver\n", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pd_drive_count == 0) { /* nothing spec'd - so autoprobe for 1 */
|
||||
disk = pd;
|
||||
if (pi_init(disk->pi, 1, -1, -1, -1, -1, -1, pd_scratch,
|
||||
@ -902,8 +910,10 @@ static int pd_detect(void)
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
if (!found) {
|
||||
printk("%s: no valid drive found\n", name);
|
||||
pi_unregister_driver(par_drv);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
|
@ -264,6 +264,7 @@ static int pf_cmd; /* current command READ/WRITE */
|
||||
static struct pf_unit *pf_current;/* unit of current request */
|
||||
static int pf_mask; /* stopper for pseudo-int */
|
||||
static char *pf_buf; /* buffer for request in progress */
|
||||
static void *par_drv; /* reference of parport driver */
|
||||
|
||||
/* kernel glue structures */
|
||||
|
||||
@ -703,6 +704,11 @@ static int pf_detect(void)
|
||||
printk("%s: %s version %s, major %d, cluster %d, nice %d\n",
|
||||
name, name, PF_VERSION, major, cluster, nice);
|
||||
|
||||
par_drv = pi_register_driver(name);
|
||||
if (!par_drv) {
|
||||
pr_err("failed to register %s driver\n", name);
|
||||
return -1;
|
||||
}
|
||||
k = 0;
|
||||
if (pf_drive_count == 0) {
|
||||
if (pi_init(pf->pi, 1, -1, -1, -1, -1, -1, pf_scratch, PI_PF,
|
||||
@ -735,6 +741,7 @@ static int pf_detect(void)
|
||||
printk("%s: No ATAPI disk detected\n", name);
|
||||
for (pf = units, unit = 0; unit < PF_UNITS; pf++, unit++)
|
||||
put_disk(pf->disk);
|
||||
pi_unregister_driver(par_drv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -227,6 +227,7 @@ static int pg_identify(struct pg *dev, int log);
|
||||
static char pg_scratch[512]; /* scratch block buffer */
|
||||
|
||||
static struct class *pg_class;
|
||||
static void *par_drv; /* reference of parport driver */
|
||||
|
||||
/* kernel glue structures */
|
||||
|
||||
@ -481,6 +482,12 @@ static int pg_detect(void)
|
||||
|
||||
printk("%s: %s version %s, major %d\n", name, name, PG_VERSION, major);
|
||||
|
||||
par_drv = pi_register_driver(name);
|
||||
if (!par_drv) {
|
||||
pr_err("failed to register %s driver\n", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
k = 0;
|
||||
if (pg_drive_count == 0) {
|
||||
if (pi_init(dev->pi, 1, -1, -1, -1, -1, -1, pg_scratch,
|
||||
@ -511,6 +518,7 @@ static int pg_detect(void)
|
||||
if (k)
|
||||
return 0;
|
||||
|
||||
pi_unregister_driver(par_drv);
|
||||
printk("%s: No ATAPI device detected\n", name);
|
||||
return -1;
|
||||
}
|
||||
|
@ -232,6 +232,7 @@ static int pt_identify(struct pt_unit *tape);
|
||||
static struct pt_unit pt[PT_UNITS];
|
||||
|
||||
static char pt_scratch[512]; /* scratch block buffer */
|
||||
static void *par_drv; /* reference of parport driver */
|
||||
|
||||
/* kernel glue structures */
|
||||
|
||||
@ -605,6 +606,12 @@ static int pt_detect(void)
|
||||
|
||||
printk("%s: %s version %s, major %d\n", name, name, PT_VERSION, major);
|
||||
|
||||
par_drv = pi_register_driver(name);
|
||||
if (!par_drv) {
|
||||
pr_err("failed to register %s driver\n", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
specified = 0;
|
||||
for (unit = 0; unit < PT_UNITS; unit++) {
|
||||
struct pt_unit *tape = &pt[unit];
|
||||
@ -644,6 +651,7 @@ static int pt_detect(void)
|
||||
if (found)
|
||||
return 0;
|
||||
|
||||
pi_unregister_driver(par_drv);
|
||||
printk("%s: No ATAPI tape drive detected\n", name);
|
||||
return -1;
|
||||
}
|
||||
|
@ -590,14 +590,6 @@ config DEVPORT
|
||||
|
||||
source "drivers/s390/char/Kconfig"
|
||||
|
||||
config MSM_SMD_PKT
|
||||
bool "Enable device interface for some SMD packet ports"
|
||||
default n
|
||||
depends on MSM_SMD
|
||||
help
|
||||
Enables userspace clients to read and write to some packet SMD
|
||||
ports via device interface for MSM chipset.
|
||||
|
||||
config TILE_SROM
|
||||
bool "Character-device access via hypervisor to the Tilera SPI ROM"
|
||||
depends on TILE
|
||||
|
@ -9,7 +9,6 @@ obj-$(CONFIG_ATARI_DSP56K) += dsp56k.o
|
||||
obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o
|
||||
obj-$(CONFIG_RAW_DRIVER) += raw.o
|
||||
obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o
|
||||
obj-$(CONFIG_MSM_SMD_PKT) += msm_smd_pkt.o
|
||||
obj-$(CONFIG_MSPEC) += mspec.o
|
||||
obj-$(CONFIG_MMTIMER) += mmtimer.o
|
||||
obj-$(CONFIG_UV_MMTIMER) += uv_mmtimer.o
|
||||
@ -36,7 +35,6 @@ else
|
||||
obj-$(CONFIG_NVRAM) += nvram.o
|
||||
endif
|
||||
obj-$(CONFIG_TOSHIBA) += toshiba.o
|
||||
obj-$(CONFIG_I8K) += i8k.o
|
||||
obj-$(CONFIG_DS1620) += ds1620.o
|
||||
obj-$(CONFIG_HW_RANDOM) += hw_random/
|
||||
obj-$(CONFIG_PPDEV) += ppdev.o
|
||||
|
@ -117,14 +117,14 @@ static int misc_open(struct inode * inode, struct file * file)
|
||||
const struct file_operations *new_fops = NULL;
|
||||
|
||||
mutex_lock(&misc_mtx);
|
||||
|
||||
|
||||
list_for_each_entry(c, &misc_list, list) {
|
||||
if (c->minor == minor) {
|
||||
new_fops = fops_get(c->fops);
|
||||
new_fops = fops_get(c->fops);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!new_fops) {
|
||||
mutex_unlock(&misc_mtx);
|
||||
request_module("char-major-%d-%d", MISC_MAJOR, minor);
|
||||
@ -167,7 +167,7 @@ static const struct file_operations misc_fops = {
|
||||
/**
|
||||
* misc_register - register a miscellaneous device
|
||||
* @misc: device structure
|
||||
*
|
||||
*
|
||||
* Register a miscellaneous device with the kernel. If the minor
|
||||
* number is set to %MISC_DYNAMIC_MINOR a minor number is assigned
|
||||
* and placed in the minor field of the structure. For other cases
|
||||
@ -181,17 +181,18 @@ static const struct file_operations misc_fops = {
|
||||
* A zero is returned on success and a negative errno code for
|
||||
* failure.
|
||||
*/
|
||||
|
||||
|
||||
int misc_register(struct miscdevice * misc)
|
||||
{
|
||||
dev_t dev;
|
||||
int err = 0;
|
||||
bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR);
|
||||
|
||||
INIT_LIST_HEAD(&misc->list);
|
||||
|
||||
mutex_lock(&misc_mtx);
|
||||
|
||||
if (misc->minor == MISC_DYNAMIC_MINOR) {
|
||||
if (is_dynamic) {
|
||||
int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
|
||||
if (i >= DYNAMIC_MINORS) {
|
||||
err = -EBUSY;
|
||||
@ -216,9 +217,13 @@ int misc_register(struct miscdevice * misc)
|
||||
device_create_with_groups(misc_class, misc->parent, dev,
|
||||
misc, misc->groups, "%s", misc->name);
|
||||
if (IS_ERR(misc->this_device)) {
|
||||
int i = DYNAMIC_MINORS - misc->minor - 1;
|
||||
if (i < DYNAMIC_MINORS && i >= 0)
|
||||
clear_bit(i, misc_minors);
|
||||
if (is_dynamic) {
|
||||
int i = DYNAMIC_MINORS - misc->minor - 1;
|
||||
|
||||
if (i < DYNAMIC_MINORS && i >= 0)
|
||||
clear_bit(i, misc_minors);
|
||||
misc->minor = MISC_DYNAMIC_MINOR;
|
||||
}
|
||||
err = PTR_ERR(misc->this_device);
|
||||
goto out;
|
||||
}
|
||||
|
@ -1,465 +0,0 @@
|
||||
/* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
/*
|
||||
* SMD Packet Driver -- Provides userspace interface to SMD packet ports.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/poll.h>
|
||||
|
||||
#include <mach/msm_smd.h>
|
||||
|
||||
#define NUM_SMD_PKT_PORTS 9
|
||||
#define DEVICE_NAME "smdpkt"
|
||||
#define MAX_BUF_SIZE 2048
|
||||
|
||||
struct smd_pkt_dev {
|
||||
struct cdev cdev;
|
||||
struct device *devicep;
|
||||
|
||||
struct smd_channel *ch;
|
||||
int open_count;
|
||||
struct mutex ch_lock;
|
||||
struct mutex rx_lock;
|
||||
struct mutex tx_lock;
|
||||
wait_queue_head_t ch_read_wait_queue;
|
||||
wait_queue_head_t ch_opened_wait_queue;
|
||||
|
||||
int i;
|
||||
|
||||
unsigned char tx_buf[MAX_BUF_SIZE];
|
||||
unsigned char rx_buf[MAX_BUF_SIZE];
|
||||
int remote_open;
|
||||
|
||||
} *smd_pkt_devp[NUM_SMD_PKT_PORTS];
|
||||
|
||||
struct class *smd_pkt_classp;
|
||||
static dev_t smd_pkt_number;
|
||||
|
||||
static int msm_smd_pkt_debug_enable;
|
||||
module_param_named(debug_enable, msm_smd_pkt_debug_enable,
|
||||
int, S_IRUGO | S_IWUSR | S_IWGRP);
|
||||
|
||||
#ifdef DEBUG
|
||||
#define D_DUMP_BUFFER(prestr, cnt, buf) do { \
|
||||
int i; \
|
||||
if (msm_smd_pkt_debug_enable) { \
|
||||
pr_debug("%s", prestr); \
|
||||
for (i = 0; i < cnt; i++) \
|
||||
pr_debug("%.2x", buf[i]); \
|
||||
pr_debug("\n"); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define D_DUMP_BUFFER(prestr, cnt, buf) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(x...) do { \
|
||||
if (msm_smd_pkt_debug_enable) \
|
||||
pr_debug(x); \
|
||||
} while (0)
|
||||
#else
|
||||
#define DBG(x...) do {} while (0)
|
||||
#endif
|
||||
|
||||
static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp)
|
||||
{
|
||||
int sz;
|
||||
|
||||
if (!smd_pkt_devp || !smd_pkt_devp->ch)
|
||||
return;
|
||||
|
||||
sz = smd_cur_packet_size(smd_pkt_devp->ch);
|
||||
if (sz == 0) {
|
||||
DBG("no packet\n");
|
||||
return;
|
||||
}
|
||||
if (sz > smd_read_avail(smd_pkt_devp->ch)) {
|
||||
DBG("incomplete packet\n");
|
||||
return;
|
||||
}
|
||||
|
||||
DBG("waking up reader\n");
|
||||
wake_up_interruptible(&smd_pkt_devp->ch_read_wait_queue);
|
||||
}
|
||||
|
||||
static int smd_pkt_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int r, bytes_read;
|
||||
struct smd_pkt_dev *smd_pkt_devp;
|
||||
struct smd_channel *chl;
|
||||
|
||||
DBG("read %d bytes\n", count);
|
||||
if (count > MAX_BUF_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
smd_pkt_devp = file->private_data;
|
||||
if (!smd_pkt_devp || !smd_pkt_devp->ch)
|
||||
return -EINVAL;
|
||||
|
||||
chl = smd_pkt_devp->ch;
|
||||
wait_for_packet:
|
||||
r = wait_event_interruptible(smd_pkt_devp->ch_read_wait_queue,
|
||||
(smd_cur_packet_size(chl) > 0 &&
|
||||
smd_read_avail(chl) >=
|
||||
smd_cur_packet_size(chl)));
|
||||
|
||||
if (r < 0) {
|
||||
if (r != -ERESTARTSYS)
|
||||
pr_err("wait returned %d\n", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
mutex_lock(&smd_pkt_devp->rx_lock);
|
||||
|
||||
bytes_read = smd_cur_packet_size(smd_pkt_devp->ch);
|
||||
if (bytes_read == 0 ||
|
||||
bytes_read < smd_read_avail(smd_pkt_devp->ch)) {
|
||||
mutex_unlock(&smd_pkt_devp->rx_lock);
|
||||
DBG("Nothing to read\n");
|
||||
goto wait_for_packet;
|
||||
}
|
||||
|
||||
if (bytes_read > count) {
|
||||
mutex_unlock(&smd_pkt_devp->rx_lock);
|
||||
pr_info("packet size %d > buffer size %d", bytes_read, count);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = smd_read(smd_pkt_devp->ch, smd_pkt_devp->rx_buf, bytes_read);
|
||||
if (r != bytes_read) {
|
||||
mutex_unlock(&smd_pkt_devp->rx_lock);
|
||||
pr_err("smd_read failed to read %d bytes: %d\n", bytes_read, r);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
D_DUMP_BUFFER("read: ", bytes_read, smd_pkt_devp->rx_buf);
|
||||
r = copy_to_user(buf, smd_pkt_devp->rx_buf, bytes_read);
|
||||
mutex_unlock(&smd_pkt_devp->rx_lock);
|
||||
if (r) {
|
||||
pr_err("copy_to_user failed %d\n", r);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
DBG("read complete %d bytes\n", bytes_read);
|
||||
check_and_wakeup_reader(smd_pkt_devp);
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
static int smd_pkt_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int r;
|
||||
struct smd_pkt_dev *smd_pkt_devp;
|
||||
|
||||
if (count > MAX_BUF_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
DBG("writing %d bytes\n", count);
|
||||
|
||||
smd_pkt_devp = file->private_data;
|
||||
if (!smd_pkt_devp || !smd_pkt_devp->ch)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&smd_pkt_devp->tx_lock);
|
||||
if (smd_write_avail(smd_pkt_devp->ch) < count) {
|
||||
mutex_unlock(&smd_pkt_devp->tx_lock);
|
||||
DBG("Not enough space to write\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
D_DUMP_BUFFER("write: ", count, buf);
|
||||
r = copy_from_user(smd_pkt_devp->tx_buf, buf, count);
|
||||
if (r) {
|
||||
mutex_unlock(&smd_pkt_devp->tx_lock);
|
||||
pr_err("copy_from_user failed %d\n", r);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
r = smd_write(smd_pkt_devp->ch, smd_pkt_devp->tx_buf, count);
|
||||
if (r != count) {
|
||||
mutex_unlock(&smd_pkt_devp->tx_lock);
|
||||
pr_err("smd_write failed to write %d bytes: %d.\n", count, r);
|
||||
return -EIO;
|
||||
}
|
||||
mutex_unlock(&smd_pkt_devp->tx_lock);
|
||||
|
||||
DBG("wrote %d bytes\n", count);
|
||||
return count;
|
||||
}
|
||||
|
||||
static unsigned int smd_pkt_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct smd_pkt_dev *smd_pkt_devp;
|
||||
unsigned int mask = 0;
|
||||
|
||||
smd_pkt_devp = file->private_data;
|
||||
if (!smd_pkt_devp)
|
||||
return POLLERR;
|
||||
|
||||
DBG("poll waiting\n");
|
||||
poll_wait(file, &smd_pkt_devp->ch_read_wait_queue, wait);
|
||||
if (smd_read_avail(smd_pkt_devp->ch))
|
||||
mask |= POLLIN | POLLRDNORM;
|
||||
|
||||
DBG("poll return\n");
|
||||
return mask;
|
||||
}
|
||||
|
||||
static void smd_pkt_ch_notify(void *priv, unsigned event)
|
||||
{
|
||||
struct smd_pkt_dev *smd_pkt_devp = priv;
|
||||
|
||||
if (smd_pkt_devp->ch == 0)
|
||||
return;
|
||||
|
||||
switch (event) {
|
||||
case SMD_EVENT_DATA:
|
||||
DBG("data\n");
|
||||
check_and_wakeup_reader(smd_pkt_devp);
|
||||
break;
|
||||
|
||||
case SMD_EVENT_OPEN:
|
||||
DBG("remote open\n");
|
||||
smd_pkt_devp->remote_open = 1;
|
||||
wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue);
|
||||
break;
|
||||
|
||||
case SMD_EVENT_CLOSE:
|
||||
smd_pkt_devp->remote_open = 0;
|
||||
pr_info("remote closed\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_err("unknown event %d\n", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static char *smd_pkt_dev_name[] = {
|
||||
"smdcntl0",
|
||||
"smdcntl1",
|
||||
"smdcntl2",
|
||||
"smdcntl3",
|
||||
"smdcntl4",
|
||||
"smdcntl5",
|
||||
"smdcntl6",
|
||||
"smdcntl7",
|
||||
"smd22",
|
||||
};
|
||||
|
||||
static char *smd_ch_name[] = {
|
||||
"DATA5_CNTL",
|
||||
"DATA6_CNTL",
|
||||
"DATA7_CNTL",
|
||||
"DATA8_CNTL",
|
||||
"DATA9_CNTL",
|
||||
"DATA12_CNTL",
|
||||
"DATA13_CNTL",
|
||||
"DATA14_CNTL",
|
||||
"DATA22",
|
||||
};
|
||||
|
||||
static int smd_pkt_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int r = 0;
|
||||
struct smd_pkt_dev *smd_pkt_devp;
|
||||
|
||||
smd_pkt_devp = container_of(inode->i_cdev, struct smd_pkt_dev, cdev);
|
||||
if (!smd_pkt_devp)
|
||||
return -EINVAL;
|
||||
|
||||
file->private_data = smd_pkt_devp;
|
||||
|
||||
mutex_lock(&smd_pkt_devp->ch_lock);
|
||||
if (smd_pkt_devp->open_count == 0) {
|
||||
r = smd_open(smd_ch_name[smd_pkt_devp->i],
|
||||
&smd_pkt_devp->ch, smd_pkt_devp,
|
||||
smd_pkt_ch_notify);
|
||||
if (r < 0) {
|
||||
pr_err("smd_open failed for %s, %d\n",
|
||||
smd_ch_name[smd_pkt_devp->i], r);
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = wait_event_interruptible_timeout(
|
||||
smd_pkt_devp->ch_opened_wait_queue,
|
||||
smd_pkt_devp->remote_open,
|
||||
msecs_to_jiffies(2 * HZ));
|
||||
if (r == 0)
|
||||
r = -ETIMEDOUT;
|
||||
|
||||
if (r < 0) {
|
||||
pr_err("wait returned %d\n", r);
|
||||
smd_close(smd_pkt_devp->ch);
|
||||
smd_pkt_devp->ch = 0;
|
||||
} else {
|
||||
smd_pkt_devp->open_count++;
|
||||
r = 0;
|
||||
}
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&smd_pkt_devp->ch_lock);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int smd_pkt_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
int r = 0;
|
||||
struct smd_pkt_dev *smd_pkt_devp = file->private_data;
|
||||
|
||||
if (!smd_pkt_devp)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&smd_pkt_devp->ch_lock);
|
||||
if (--smd_pkt_devp->open_count == 0) {
|
||||
r = smd_close(smd_pkt_devp->ch);
|
||||
smd_pkt_devp->ch = 0;
|
||||
}
|
||||
mutex_unlock(&smd_pkt_devp->ch_lock);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static const struct file_operations smd_pkt_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = smd_pkt_open,
|
||||
.release = smd_pkt_release,
|
||||
.read = smd_pkt_read,
|
||||
.write = smd_pkt_write,
|
||||
.poll = smd_pkt_poll,
|
||||
};
|
||||
|
||||
static int __init smd_pkt_init(void)
|
||||
{
|
||||
int i;
|
||||
int r;
|
||||
|
||||
r = alloc_chrdev_region(&smd_pkt_number, 0,
|
||||
NUM_SMD_PKT_PORTS, DEVICE_NAME);
|
||||
if (r) {
|
||||
pr_err("alloc_chrdev_region() failed %d\n", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
smd_pkt_classp = class_create(THIS_MODULE, DEVICE_NAME);
|
||||
if (IS_ERR(smd_pkt_classp)) {
|
||||
r = PTR_ERR(smd_pkt_classp);
|
||||
pr_err("class_create() failed %d\n", r);
|
||||
goto unreg_chardev;
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
|
||||
smd_pkt_devp[i] = kzalloc(sizeof(struct smd_pkt_dev),
|
||||
GFP_KERNEL);
|
||||
if (!smd_pkt_devp[i]) {
|
||||
pr_err("kmalloc() failed\n");
|
||||
goto clean_cdevs;
|
||||
}
|
||||
|
||||
smd_pkt_devp[i]->i = i;
|
||||
|
||||
init_waitqueue_head(&smd_pkt_devp[i]->ch_read_wait_queue);
|
||||
smd_pkt_devp[i]->remote_open = 0;
|
||||
init_waitqueue_head(&smd_pkt_devp[i]->ch_opened_wait_queue);
|
||||
|
||||
mutex_init(&smd_pkt_devp[i]->ch_lock);
|
||||
mutex_init(&smd_pkt_devp[i]->rx_lock);
|
||||
mutex_init(&smd_pkt_devp[i]->tx_lock);
|
||||
|
||||
cdev_init(&smd_pkt_devp[i]->cdev, &smd_pkt_fops);
|
||||
smd_pkt_devp[i]->cdev.owner = THIS_MODULE;
|
||||
|
||||
r = cdev_add(&smd_pkt_devp[i]->cdev,
|
||||
(smd_pkt_number + i), 1);
|
||||
if (r) {
|
||||
pr_err("cdev_add() failed %d\n", r);
|
||||
kfree(smd_pkt_devp[i]);
|
||||
goto clean_cdevs;
|
||||
}
|
||||
|
||||
smd_pkt_devp[i]->devicep =
|
||||
device_create(smd_pkt_classp, NULL,
|
||||
(smd_pkt_number + i), NULL,
|
||||
smd_pkt_dev_name[i]);
|
||||
if (IS_ERR(smd_pkt_devp[i]->devicep)) {
|
||||
r = PTR_ERR(smd_pkt_devp[i]->devicep);
|
||||
pr_err("device_create() failed %d\n", r);
|
||||
cdev_del(&smd_pkt_devp[i]->cdev);
|
||||
kfree(smd_pkt_devp[i]);
|
||||
goto clean_cdevs;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pr_info("SMD Packet Port Driver Initialized.\n");
|
||||
return 0;
|
||||
|
||||
clean_cdevs:
|
||||
if (i > 0) {
|
||||
while (--i >= 0) {
|
||||
mutex_destroy(&smd_pkt_devp[i]->ch_lock);
|
||||
mutex_destroy(&smd_pkt_devp[i]->rx_lock);
|
||||
mutex_destroy(&smd_pkt_devp[i]->tx_lock);
|
||||
cdev_del(&smd_pkt_devp[i]->cdev);
|
||||
kfree(smd_pkt_devp[i]);
|
||||
device_destroy(smd_pkt_classp,
|
||||
MKDEV(MAJOR(smd_pkt_number), i));
|
||||
}
|
||||
}
|
||||
|
||||
class_destroy(smd_pkt_classp);
|
||||
unreg_chardev:
|
||||
unregister_chrdev_region(MAJOR(smd_pkt_number), NUM_SMD_PKT_PORTS);
|
||||
return r;
|
||||
}
|
||||
module_init(smd_pkt_init);
|
||||
|
||||
static void __exit smd_pkt_cleanup(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
|
||||
mutex_destroy(&smd_pkt_devp[i]->ch_lock);
|
||||
mutex_destroy(&smd_pkt_devp[i]->rx_lock);
|
||||
mutex_destroy(&smd_pkt_devp[i]->tx_lock);
|
||||
cdev_del(&smd_pkt_devp[i]->cdev);
|
||||
kfree(smd_pkt_devp[i]);
|
||||
device_destroy(smd_pkt_classp,
|
||||
MKDEV(MAJOR(smd_pkt_number), i));
|
||||
}
|
||||
|
||||
class_destroy(smd_pkt_classp);
|
||||
unregister_chrdev_region(MAJOR(smd_pkt_number), NUM_SMD_PKT_PORTS);
|
||||
}
|
||||
module_exit(smd_pkt_cleanup);
|
||||
|
||||
MODULE_DESCRIPTION("MSM Shared Memory Packet Port");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -437,7 +437,7 @@ static int mgslpc_device_count = 0;
|
||||
* .text section address and breakpoint on module load.
|
||||
* This is useful for use with gdb and add-symbol-file command.
|
||||
*/
|
||||
static bool break_on_load=0;
|
||||
static bool break_on_load;
|
||||
|
||||
/*
|
||||
* Driver major number, defaults to zero to get auto
|
||||
|
@ -198,7 +198,7 @@ scdrv_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos)
|
||||
add_wait_queue(&sd->sd_rq, &wait);
|
||||
spin_unlock_irqrestore(&sd->sd_rlock, flags);
|
||||
|
||||
schedule_timeout(SCDRV_TIMEOUT);
|
||||
schedule_timeout(msecs_to_jiffies(SCDRV_TIMEOUT));
|
||||
|
||||
remove_wait_queue(&sd->sd_rq, &wait);
|
||||
if (signal_pending(current)) {
|
||||
@ -294,7 +294,7 @@ scdrv_write(struct file *file, const char __user *buf,
|
||||
add_wait_queue(&sd->sd_wq, &wait);
|
||||
spin_unlock_irqrestore(&sd->sd_wlock, flags);
|
||||
|
||||
schedule_timeout(SCDRV_TIMEOUT);
|
||||
schedule_timeout(msecs_to_jiffies(SCDRV_TIMEOUT));
|
||||
|
||||
remove_wait_queue(&sd->sd_wq, &wait);
|
||||
if (signal_pending(current)) {
|
||||
|
@ -1492,8 +1492,8 @@ static int add_port(struct ports_device *portdev, u32 id)
|
||||
* Finally, create the debugfs file that we can use to
|
||||
* inspect a port's state at any time
|
||||
*/
|
||||
sprintf(debugfs_name, "vport%up%u",
|
||||
port->portdev->vdev->index, id);
|
||||
snprintf(debugfs_name, sizeof(debugfs_name), "vport%up%u",
|
||||
port->portdev->vdev->index, id);
|
||||
port->debugfs_file = debugfs_create_file(debugfs_name, 0444,
|
||||
pdrvdata.debugfs_dir,
|
||||
port,
|
||||
|
@ -270,7 +270,7 @@ int buffer_icap_set_configuration(struct hwicap_drvdata *drvdata, u32 *data,
|
||||
int status;
|
||||
s32 buffer_count = 0;
|
||||
s32 num_writes = 0;
|
||||
bool dirty = 0;
|
||||
bool dirty = false;
|
||||
u32 i;
|
||||
void __iomem *base_address = drvdata->base_address;
|
||||
|
||||
@ -279,7 +279,7 @@ int buffer_icap_set_configuration(struct hwicap_drvdata *drvdata, u32 *data,
|
||||
|
||||
/* Copy data to bram */
|
||||
buffer_icap_set_bram(base_address, buffer_count, data[i]);
|
||||
dirty = 1;
|
||||
dirty = true;
|
||||
|
||||
if (buffer_count < XHI_MAX_BUFFER_INTS - 1) {
|
||||
buffer_count++;
|
||||
@ -299,7 +299,7 @@ int buffer_icap_set_configuration(struct hwicap_drvdata *drvdata, u32 *data,
|
||||
|
||||
buffer_count = 0;
|
||||
num_writes++;
|
||||
dirty = 0;
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
/* Write unwritten data to ICAP */
|
||||
|
@ -24,7 +24,7 @@ config XILLYBUS_PCIE
|
||||
|
||||
config XILLYBUS_OF
|
||||
tristate "Xillybus over Device Tree"
|
||||
depends on OF_ADDRESS && OF_IRQ
|
||||
depends on OF_ADDRESS && OF_IRQ && HAS_DMA
|
||||
help
|
||||
Set to M if you want Xillybus to find its resources from the
|
||||
Open Firmware Flattened Device Tree. If the target is an embedded
|
||||
|
@ -28,15 +28,22 @@ config EXTCON_ARIZONA
|
||||
with Wolfson Arizona devices. These are audio CODECs with
|
||||
advanced audio accessory detection support.
|
||||
|
||||
config EXTCON_AXP288
|
||||
tristate "X-Power AXP288 EXTCON support"
|
||||
depends on MFD_AXP20X && USB_PHY
|
||||
help
|
||||
Say Y here to enable support for USB peripheral detection
|
||||
and USB MUX switching by X-Power AXP288 PMIC.
|
||||
|
||||
config EXTCON_GPIO
|
||||
tristate "GPIO extcon support"
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
help
|
||||
Say Y here to enable GPIO based extcon support. Note that GPIO
|
||||
extcon supports single state per extcon instance.
|
||||
|
||||
config EXTCON_MAX14577
|
||||
tristate "MAX14577/77836 EXTCON Support"
|
||||
tristate "Maxim MAX14577/77836 EXTCON Support"
|
||||
depends on MFD_MAX14577
|
||||
select IRQ_DOMAIN
|
||||
select REGMAP_I2C
|
||||
@ -46,7 +53,7 @@ config EXTCON_MAX14577
|
||||
detector and switch.
|
||||
|
||||
config EXTCON_MAX77693
|
||||
tristate "MAX77693 EXTCON Support"
|
||||
tristate "Maxim MAX77693 EXTCON Support"
|
||||
depends on MFD_MAX77693 && INPUT
|
||||
select IRQ_DOMAIN
|
||||
select REGMAP_I2C
|
||||
@ -56,7 +63,7 @@ config EXTCON_MAX77693
|
||||
detector and switch.
|
||||
|
||||
config EXTCON_MAX77843
|
||||
tristate "MAX77843 EXTCON Support"
|
||||
tristate "Maxim MAX77843 EXTCON Support"
|
||||
depends on MFD_MAX77843
|
||||
select IRQ_DOMAIN
|
||||
select REGMAP_I2C
|
||||
@ -66,7 +73,7 @@ config EXTCON_MAX77843
|
||||
detector add switch.
|
||||
|
||||
config EXTCON_MAX8997
|
||||
tristate "MAX8997 EXTCON Support"
|
||||
tristate "Maxim MAX8997 EXTCON Support"
|
||||
depends on MFD_MAX8997 && IRQ_DOMAIN
|
||||
help
|
||||
If you say yes here you get support for the MUIC device of
|
||||
@ -81,7 +88,7 @@ config EXTCON_PALMAS
|
||||
detection by palmas usb.
|
||||
|
||||
config EXTCON_RT8973A
|
||||
tristate "RT8973A EXTCON support"
|
||||
tristate "Richtek RT8973A EXTCON support"
|
||||
depends on I2C
|
||||
select IRQ_DOMAIN
|
||||
select REGMAP_I2C
|
||||
@ -93,7 +100,7 @@ config EXTCON_RT8973A
|
||||
from abnormal high input voltage (up to 28V).
|
||||
|
||||
config EXTCON_SM5502
|
||||
tristate "SM5502 EXTCON support"
|
||||
tristate "Silicon Mitus SM5502 EXTCON support"
|
||||
depends on I2C
|
||||
select IRQ_DOMAIN
|
||||
select REGMAP_I2C
|
||||
@ -105,9 +112,9 @@ config EXTCON_SM5502
|
||||
|
||||
config EXTCON_USB_GPIO
|
||||
tristate "USB GPIO extcon support"
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
help
|
||||
Say Y here to enable GPIO based USB cable detection extcon support.
|
||||
Used typically if GPIO is used for USB ID pin detection.
|
||||
|
||||
endif # MULTISTATE_SWITCH
|
||||
endif
|
||||
|
@ -5,6 +5,7 @@
|
||||
obj-$(CONFIG_EXTCON) += extcon.o
|
||||
obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o
|
||||
obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o
|
||||
obj-$(CONFIG_EXTCON_AXP288) += extcon-axp288.o
|
||||
obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o
|
||||
obj-$(CONFIG_EXTCON_MAX14577) += extcon-max14577.o
|
||||
obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o
|
||||
|
@ -29,7 +29,6 @@
|
||||
* struct adc_jack_data - internal data for adc_jack device driver
|
||||
* @edev: extcon device.
|
||||
* @cable_names: list of supported cables.
|
||||
* @num_cables: size of cable_names.
|
||||
* @adc_conditions: list of adc value conditions.
|
||||
* @num_conditions: size of adc_conditions.
|
||||
* @irq: irq number of attach/detach event (0 if not exist).
|
||||
@ -41,8 +40,7 @@
|
||||
struct adc_jack_data {
|
||||
struct extcon_dev *edev;
|
||||
|
||||
const char **cable_names;
|
||||
int num_cables;
|
||||
const unsigned int **cable_names;
|
||||
struct adc_jack_cond *adc_conditions;
|
||||
int num_conditions;
|
||||
|
||||
@ -112,17 +110,6 @@ static int adc_jack_probe(struct platform_device *pdev)
|
||||
dev_err(&pdev->dev, "failed to allocate extcon device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
data->edev->name = pdata->name;
|
||||
|
||||
/* Check the length of array and set num_cables */
|
||||
for (i = 0; data->edev->supported_cable[i]; i++)
|
||||
;
|
||||
if (i == 0 || i > SUPPORTED_CABLE_MAX) {
|
||||
dev_err(&pdev->dev, "error: pdata->cable_names size = %d\n",
|
||||
i - 1);
|
||||
return -EINVAL;
|
||||
}
|
||||
data->num_cables = i;
|
||||
|
||||
if (!pdata->adc_conditions ||
|
||||
!pdata->adc_conditions[0].state) {
|
||||
|
@ -32,13 +32,10 @@
|
||||
#include <linux/mfd/arizona/core.h>
|
||||
#include <linux/mfd/arizona/pdata.h>
|
||||
#include <linux/mfd/arizona/registers.h>
|
||||
#include <dt-bindings/mfd/arizona.h>
|
||||
|
||||
#define ARIZONA_MAX_MICD_RANGE 8
|
||||
|
||||
#define ARIZONA_ACCDET_MODE_MIC 0
|
||||
#define ARIZONA_ACCDET_MODE_HPL 1
|
||||
#define ARIZONA_ACCDET_MODE_HPR 2
|
||||
|
||||
#define ARIZONA_MICD_CLAMP_MODE_JDL 0x4
|
||||
#define ARIZONA_MICD_CLAMP_MODE_JDH 0x5
|
||||
#define ARIZONA_MICD_CLAMP_MODE_JDL_GP5H 0x9
|
||||
@ -94,7 +91,7 @@ struct arizona_extcon_info {
|
||||
bool detecting;
|
||||
int jack_flips;
|
||||
|
||||
int hpdet_ip;
|
||||
int hpdet_ip_version;
|
||||
|
||||
struct extcon_dev *edev;
|
||||
};
|
||||
@ -121,17 +118,12 @@ static const int arizona_micd_levels[] = {
|
||||
1257,
|
||||
};
|
||||
|
||||
#define ARIZONA_CABLE_MECHANICAL 0
|
||||
#define ARIZONA_CABLE_MICROPHONE 1
|
||||
#define ARIZONA_CABLE_HEADPHONE 2
|
||||
#define ARIZONA_CABLE_LINEOUT 3
|
||||
|
||||
static const char *arizona_cable[] = {
|
||||
"Mechanical",
|
||||
"Microphone",
|
||||
"Headphone",
|
||||
"Line-out",
|
||||
NULL,
|
||||
static const unsigned int arizona_cable[] = {
|
||||
EXTCON_MECHANICAL,
|
||||
EXTCON_MICROPHONE,
|
||||
EXTCON_HEADPHONE,
|
||||
EXTCON_LINE_OUT,
|
||||
EXTCON_NONE,
|
||||
};
|
||||
|
||||
static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info);
|
||||
@ -145,6 +137,7 @@ static void arizona_extcon_hp_clamp(struct arizona_extcon_info *info,
|
||||
|
||||
switch (arizona->type) {
|
||||
case WM5110:
|
||||
case WM8280:
|
||||
mask = ARIZONA_HP1L_SHRTO | ARIZONA_HP1L_FLWR |
|
||||
ARIZONA_HP1L_SHRTI;
|
||||
if (clamp)
|
||||
@ -380,7 +373,7 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (info->hpdet_ip) {
|
||||
switch (info->hpdet_ip_version) {
|
||||
case 0:
|
||||
if (!(val & ARIZONA_HP_DONE)) {
|
||||
dev_err(arizona->dev, "HPDET did not complete: %x\n",
|
||||
@ -441,7 +434,7 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
|
||||
|
||||
default:
|
||||
dev_warn(arizona->dev, "Unknown HPDET IP revision %d\n",
|
||||
info->hpdet_ip);
|
||||
info->hpdet_ip_version);
|
||||
case 2:
|
||||
if (!(val & ARIZONA_HP_DONE_B)) {
|
||||
dev_err(arizona->dev, "HPDET did not complete: %x\n",
|
||||
@ -559,7 +552,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
|
||||
struct arizona_extcon_info *info = data;
|
||||
struct arizona *arizona = info->arizona;
|
||||
int id_gpio = arizona->pdata.hpdet_id_gpio;
|
||||
int report = ARIZONA_CABLE_HEADPHONE;
|
||||
unsigned int report = EXTCON_HEADPHONE;
|
||||
int ret, reading;
|
||||
bool mic = false;
|
||||
|
||||
@ -573,7 +566,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
|
||||
}
|
||||
|
||||
/* If the cable was removed while measuring ignore the result */
|
||||
ret = extcon_get_cable_state_(info->edev, ARIZONA_CABLE_MECHANICAL);
|
||||
ret = extcon_get_cable_state_(info->edev, EXTCON_MECHANICAL);
|
||||
if (ret < 0) {
|
||||
dev_err(arizona->dev, "Failed to check cable state: %d\n",
|
||||
ret);
|
||||
@ -604,9 +597,9 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
|
||||
|
||||
/* Report high impedence cables as line outputs */
|
||||
if (reading >= 5000)
|
||||
report = ARIZONA_CABLE_LINEOUT;
|
||||
report = EXTCON_LINE_OUT;
|
||||
else
|
||||
report = ARIZONA_CABLE_HEADPHONE;
|
||||
report = EXTCON_HEADPHONE;
|
||||
|
||||
ret = extcon_set_cable_state_(info->edev, report, true);
|
||||
if (ret != 0)
|
||||
@ -670,9 +663,9 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info)
|
||||
ret = regmap_update_bits(arizona->regmap,
|
||||
ARIZONA_ACCESSORY_DETECT_MODE_1,
|
||||
ARIZONA_ACCDET_MODE_MASK,
|
||||
ARIZONA_ACCDET_MODE_HPL);
|
||||
arizona->pdata.hpdet_channel);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Failed to set HPDETL mode: %d\n", ret);
|
||||
dev_err(arizona->dev, "Failed to set HPDET mode: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -691,8 +684,7 @@ err:
|
||||
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
|
||||
|
||||
/* Just report headphone */
|
||||
ret = extcon_set_cable_state_(info->edev,
|
||||
ARIZONA_CABLE_HEADPHONE, true);
|
||||
ret = extcon_set_cable_state_(info->edev, EXTCON_HEADPHONE, true);
|
||||
if (ret != 0)
|
||||
dev_err(arizona->dev, "Failed to report headphone: %d\n", ret);
|
||||
|
||||
@ -722,9 +714,9 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
|
||||
ARIZONA_ACCESSORY_DETECT_MODE_1,
|
||||
ARIZONA_ACCDET_SRC | ARIZONA_ACCDET_MODE_MASK,
|
||||
info->micd_modes[0].src |
|
||||
ARIZONA_ACCDET_MODE_HPL);
|
||||
arizona->pdata.hpdet_channel);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Failed to set HPDETL mode: %d\n", ret);
|
||||
dev_err(arizona->dev, "Failed to set HPDET mode: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -749,8 +741,7 @@ err:
|
||||
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
|
||||
|
||||
/* Just report headphone */
|
||||
ret = extcon_set_cable_state_(info->edev,
|
||||
ARIZONA_CABLE_HEADPHONE, true);
|
||||
ret = extcon_set_cable_state_(info->edev, EXTCON_HEADPHONE, true);
|
||||
if (ret != 0)
|
||||
dev_err(arizona->dev, "Failed to report headphone: %d\n", ret);
|
||||
|
||||
@ -789,7 +780,7 @@ static void arizona_micd_detect(struct work_struct *work)
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
/* If the cable was removed while measuring ignore the result */
|
||||
ret = extcon_get_cable_state_(info->edev, ARIZONA_CABLE_MECHANICAL);
|
||||
ret = extcon_get_cable_state_(info->edev, EXTCON_MECHANICAL);
|
||||
if (ret < 0) {
|
||||
dev_err(arizona->dev, "Failed to check cable state: %d\n",
|
||||
ret);
|
||||
@ -838,8 +829,7 @@ static void arizona_micd_detect(struct work_struct *work)
|
||||
arizona_identify_headphone(info);
|
||||
|
||||
ret = extcon_set_cable_state_(info->edev,
|
||||
ARIZONA_CABLE_MICROPHONE, true);
|
||||
|
||||
EXTCON_MICROPHONE, true);
|
||||
if (ret != 0)
|
||||
dev_err(arizona->dev, "Headset report failed: %d\n",
|
||||
ret);
|
||||
@ -1030,7 +1020,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
|
||||
if (info->last_jackdet == present) {
|
||||
dev_dbg(arizona->dev, "Detected jack\n");
|
||||
ret = extcon_set_cable_state_(info->edev,
|
||||
ARIZONA_CABLE_MECHANICAL, true);
|
||||
EXTCON_MECHANICAL, true);
|
||||
|
||||
if (ret != 0)
|
||||
dev_err(arizona->dev, "Mechanical report failed: %d\n",
|
||||
@ -1120,6 +1110,26 @@ static void arizona_micd_set_level(struct arizona *arizona, int index,
|
||||
regmap_update_bits(arizona->regmap, reg, mask, level);
|
||||
}
|
||||
|
||||
static int arizona_extcon_of_get_pdata(struct arizona *arizona)
|
||||
{
|
||||
struct arizona_pdata *pdata = &arizona->pdata;
|
||||
unsigned int val = ARIZONA_ACCDET_MODE_HPL;
|
||||
|
||||
of_property_read_u32(arizona->dev->of_node, "wlf,hpdet-channel", &val);
|
||||
switch (val) {
|
||||
case ARIZONA_ACCDET_MODE_HPL:
|
||||
case ARIZONA_ACCDET_MODE_HPR:
|
||||
pdata->hpdet_channel = val;
|
||||
break;
|
||||
default:
|
||||
dev_err(arizona->dev,
|
||||
"Wrong wlf,hpdet-channel DT value %d\n", val);
|
||||
pdata->hpdet_channel = ARIZONA_ACCDET_MODE_HPL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int arizona_extcon_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
|
||||
@ -1137,6 +1147,11 @@ static int arizona_extcon_probe(struct platform_device *pdev)
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
if (IS_ENABLED(CONFIG_OF)) {
|
||||
if (!dev_get_platdata(arizona->dev))
|
||||
arizona_extcon_of_get_pdata(arizona);
|
||||
}
|
||||
|
||||
info->micvdd = devm_regulator_get(&pdev->dev, "MICVDD");
|
||||
if (IS_ERR(info->micvdd)) {
|
||||
ret = PTR_ERR(info->micvdd);
|
||||
@ -1161,7 +1176,7 @@ static int arizona_extcon_probe(struct platform_device *pdev)
|
||||
break;
|
||||
default:
|
||||
info->micd_clamp = true;
|
||||
info->hpdet_ip = 1;
|
||||
info->hpdet_ip_version = 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@ -1172,7 +1187,7 @@ static int arizona_extcon_probe(struct platform_device *pdev)
|
||||
break;
|
||||
default:
|
||||
info->micd_clamp = true;
|
||||
info->hpdet_ip = 2;
|
||||
info->hpdet_ip_version = 2;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@ -1185,7 +1200,6 @@ static int arizona_extcon_probe(struct platform_device *pdev)
|
||||
dev_err(&pdev->dev, "failed to allocate extcon device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
info->edev->name = "Headset Jack";
|
||||
|
||||
ret = devm_extcon_dev_register(&pdev->dev, info->edev);
|
||||
if (ret < 0) {
|
||||
|
381
drivers/extcon/extcon-axp288.c
Normal file
381
drivers/extcon/extcon-axp288.c
Normal file
@ -0,0 +1,381 @@
|
||||
/*
|
||||
* extcon-axp288.c - X-Power AXP288 PMIC extcon cable detection driver
|
||||
*
|
||||
* Copyright (C) 2015 Intel Corporation
|
||||
* Author: Ramakrishna Pallala <ramakrishna.pallala@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/usb/phy.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/mfd/axp20x.h>
|
||||
|
||||
/* Power source status register */
|
||||
#define PS_STAT_VBUS_TRIGGER BIT(0)
|
||||
#define PS_STAT_BAT_CHRG_DIR BIT(2)
|
||||
#define PS_STAT_VBUS_ABOVE_VHOLD BIT(3)
|
||||
#define PS_STAT_VBUS_VALID BIT(4)
|
||||
#define PS_STAT_VBUS_PRESENT BIT(5)
|
||||
|
||||
/* BC module global register */
|
||||
#define BC_GLOBAL_RUN BIT(0)
|
||||
#define BC_GLOBAL_DET_STAT BIT(2)
|
||||
#define BC_GLOBAL_DBP_TOUT BIT(3)
|
||||
#define BC_GLOBAL_VLGC_COM_SEL BIT(4)
|
||||
#define BC_GLOBAL_DCD_TOUT_MASK (BIT(6)|BIT(5))
|
||||
#define BC_GLOBAL_DCD_TOUT_300MS 0
|
||||
#define BC_GLOBAL_DCD_TOUT_100MS 1
|
||||
#define BC_GLOBAL_DCD_TOUT_500MS 2
|
||||
#define BC_GLOBAL_DCD_TOUT_900MS 3
|
||||
#define BC_GLOBAL_DCD_DET_SEL BIT(7)
|
||||
|
||||
/* BC module vbus control and status register */
|
||||
#define VBUS_CNTL_DPDM_PD_EN BIT(4)
|
||||
#define VBUS_CNTL_DPDM_FD_EN BIT(5)
|
||||
#define VBUS_CNTL_FIRST_PO_STAT BIT(6)
|
||||
|
||||
/* BC USB status register */
|
||||
#define USB_STAT_BUS_STAT_MASK (BIT(3)|BIT(2)|BIT(1)|BIT(0))
|
||||
#define USB_STAT_BUS_STAT_SHIFT 0
|
||||
#define USB_STAT_BUS_STAT_ATHD 0
|
||||
#define USB_STAT_BUS_STAT_CONN 1
|
||||
#define USB_STAT_BUS_STAT_SUSP 2
|
||||
#define USB_STAT_BUS_STAT_CONF 3
|
||||
#define USB_STAT_USB_SS_MODE BIT(4)
|
||||
#define USB_STAT_DEAD_BAT_DET BIT(6)
|
||||
#define USB_STAT_DBP_UNCFG BIT(7)
|
||||
|
||||
/* BC detect status register */
|
||||
#define DET_STAT_MASK (BIT(7)|BIT(6)|BIT(5))
|
||||
#define DET_STAT_SHIFT 5
|
||||
#define DET_STAT_SDP 1
|
||||
#define DET_STAT_CDP 2
|
||||
#define DET_STAT_DCP 3
|
||||
|
||||
/* IRQ enable-1 register */
|
||||
#define PWRSRC_IRQ_CFG_MASK (BIT(4)|BIT(3)|BIT(2))
|
||||
|
||||
/* IRQ enable-6 register */
|
||||
#define BC12_IRQ_CFG_MASK BIT(1)
|
||||
|
||||
enum axp288_extcon_reg {
|
||||
AXP288_PS_STAT_REG = 0x00,
|
||||
AXP288_PS_BOOT_REASON_REG = 0x02,
|
||||
AXP288_BC_GLOBAL_REG = 0x2c,
|
||||
AXP288_BC_VBUS_CNTL_REG = 0x2d,
|
||||
AXP288_BC_USB_STAT_REG = 0x2e,
|
||||
AXP288_BC_DET_STAT_REG = 0x2f,
|
||||
AXP288_PWRSRC_IRQ_CFG_REG = 0x40,
|
||||
AXP288_BC12_IRQ_CFG_REG = 0x45,
|
||||
};
|
||||
|
||||
enum axp288_mux_select {
|
||||
EXTCON_GPIO_MUX_SEL_PMIC = 0,
|
||||
EXTCON_GPIO_MUX_SEL_SOC,
|
||||
};
|
||||
|
||||
enum axp288_extcon_irq {
|
||||
VBUS_FALLING_IRQ = 0,
|
||||
VBUS_RISING_IRQ,
|
||||
MV_CHNG_IRQ,
|
||||
BC_USB_CHNG_IRQ,
|
||||
EXTCON_IRQ_END,
|
||||
};
|
||||
|
||||
static const unsigned int axp288_extcon_cables[] = {
|
||||
EXTCON_SLOW_CHARGER,
|
||||
EXTCON_CHARGE_DOWNSTREAM,
|
||||
EXTCON_FAST_CHARGER,
|
||||
EXTCON_NONE,
|
||||
};
|
||||
|
||||
struct axp288_extcon_info {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct regmap_irq_chip_data *regmap_irqc;
|
||||
struct axp288_extcon_pdata *pdata;
|
||||
int irq[EXTCON_IRQ_END];
|
||||
struct extcon_dev *edev;
|
||||
struct notifier_block extcon_nb;
|
||||
struct usb_phy *otg;
|
||||
};
|
||||
|
||||
/* Power up/down reason string array */
|
||||
static char *axp288_pwr_up_down_info[] = {
|
||||
"Last wake caused by user pressing the power button",
|
||||
"Last wake caused by a charger insertion",
|
||||
"Last wake caused by a battery insertion",
|
||||
"Last wake caused by SOC initiated global reset",
|
||||
"Last wake caused by cold reset",
|
||||
"Last shutdown caused by PMIC UVLO threshold",
|
||||
"Last shutdown caused by SOC initiated cold off",
|
||||
"Last shutdown caused by user pressing the power button",
|
||||
NULL,
|
||||
};
|
||||
|
||||
/*
|
||||
* Decode and log the given "reset source indicator" (rsi)
|
||||
* register and then clear it.
|
||||
*/
|
||||
static void axp288_extcon_log_rsi(struct axp288_extcon_info *info)
|
||||
{
|
||||
char **rsi;
|
||||
unsigned int val, i, clear_mask = 0;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(info->regmap, AXP288_PS_BOOT_REASON_REG, &val);
|
||||
for (i = 0, rsi = axp288_pwr_up_down_info; *rsi; rsi++, i++) {
|
||||
if (val & BIT(i)) {
|
||||
dev_dbg(info->dev, "%s\n", *rsi);
|
||||
clear_mask |= BIT(i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear the register value for next reboot (write 1 to clear bit) */
|
||||
regmap_write(info->regmap, AXP288_PS_BOOT_REASON_REG, clear_mask);
|
||||
}
|
||||
|
||||
static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info)
|
||||
{
|
||||
static bool notify_otg, notify_charger;
|
||||
static unsigned int cable;
|
||||
int ret, stat, cfg, pwr_stat;
|
||||
u8 chrg_type;
|
||||
bool vbus_attach = false;
|
||||
|
||||
ret = regmap_read(info->regmap, AXP288_PS_STAT_REG, &pwr_stat);
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "failed to read vbus status\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
vbus_attach = (pwr_stat & PS_STAT_VBUS_PRESENT);
|
||||
if (!vbus_attach)
|
||||
goto notify_otg;
|
||||
|
||||
/* Check charger detection completion status */
|
||||
ret = regmap_read(info->regmap, AXP288_BC_GLOBAL_REG, &cfg);
|
||||
if (ret < 0)
|
||||
goto dev_det_ret;
|
||||
if (cfg & BC_GLOBAL_DET_STAT) {
|
||||
dev_dbg(info->dev, "can't complete the charger detection\n");
|
||||
goto dev_det_ret;
|
||||
}
|
||||
|
||||
ret = regmap_read(info->regmap, AXP288_BC_DET_STAT_REG, &stat);
|
||||
if (ret < 0)
|
||||
goto dev_det_ret;
|
||||
|
||||
chrg_type = (stat & DET_STAT_MASK) >> DET_STAT_SHIFT;
|
||||
|
||||
switch (chrg_type) {
|
||||
case DET_STAT_SDP:
|
||||
dev_dbg(info->dev, "sdp cable is connecetd\n");
|
||||
notify_otg = true;
|
||||
notify_charger = true;
|
||||
cable = EXTCON_SLOW_CHARGER;
|
||||
break;
|
||||
case DET_STAT_CDP:
|
||||
dev_dbg(info->dev, "cdp cable is connecetd\n");
|
||||
notify_otg = true;
|
||||
notify_charger = true;
|
||||
cable = EXTCON_CHARGE_DOWNSTREAM;
|
||||
break;
|
||||
case DET_STAT_DCP:
|
||||
dev_dbg(info->dev, "dcp cable is connecetd\n");
|
||||
notify_charger = true;
|
||||
cable = EXTCON_FAST_CHARGER;
|
||||
break;
|
||||
default:
|
||||
dev_warn(info->dev,
|
||||
"disconnect or unknown or ID event\n");
|
||||
}
|
||||
|
||||
notify_otg:
|
||||
if (notify_otg) {
|
||||
/*
|
||||
* If VBUS is absent Connect D+/D- lines to PMIC for BC
|
||||
* detection. Else connect them to SOC for USB communication.
|
||||
*/
|
||||
if (info->pdata->gpio_mux_cntl)
|
||||
gpiod_set_value(info->pdata->gpio_mux_cntl,
|
||||
vbus_attach ? EXTCON_GPIO_MUX_SEL_SOC
|
||||
: EXTCON_GPIO_MUX_SEL_PMIC);
|
||||
|
||||
atomic_notifier_call_chain(&info->otg->notifier,
|
||||
vbus_attach ? USB_EVENT_VBUS : USB_EVENT_NONE, NULL);
|
||||
}
|
||||
|
||||
if (notify_charger)
|
||||
extcon_set_cable_state_(info->edev, cable, vbus_attach);
|
||||
|
||||
/* Clear the flags on disconnect event */
|
||||
if (!vbus_attach)
|
||||
notify_otg = notify_charger = false;
|
||||
|
||||
return 0;
|
||||
|
||||
dev_det_ret:
|
||||
if (ret < 0)
|
||||
dev_err(info->dev, "failed to detect BC Mod\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t axp288_extcon_isr(int irq, void *data)
|
||||
{
|
||||
struct axp288_extcon_info *info = data;
|
||||
int ret;
|
||||
|
||||
ret = axp288_handle_chrg_det_event(info);
|
||||
if (ret < 0)
|
||||
dev_err(info->dev, "failed to handle the interrupt\n");
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void axp288_extcon_enable_irq(struct axp288_extcon_info *info)
|
||||
{
|
||||
/* Unmask VBUS interrupt */
|
||||
regmap_write(info->regmap, AXP288_PWRSRC_IRQ_CFG_REG,
|
||||
PWRSRC_IRQ_CFG_MASK);
|
||||
regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG,
|
||||
BC_GLOBAL_RUN, 0);
|
||||
/* Unmask the BC1.2 complete interrupts */
|
||||
regmap_write(info->regmap, AXP288_BC12_IRQ_CFG_REG, BC12_IRQ_CFG_MASK);
|
||||
/* Enable the charger detection logic */
|
||||
regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG,
|
||||
BC_GLOBAL_RUN, BC_GLOBAL_RUN);
|
||||
}
|
||||
|
||||
static int axp288_extcon_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct axp288_extcon_info *info;
|
||||
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
|
||||
int ret, i, pirq, gpio;
|
||||
|
||||
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
info->dev = &pdev->dev;
|
||||
info->regmap = axp20x->regmap;
|
||||
info->regmap_irqc = axp20x->regmap_irqc;
|
||||
info->pdata = pdev->dev.platform_data;
|
||||
|
||||
if (!info->pdata) {
|
||||
/* Try ACPI provided pdata via device properties */
|
||||
if (!device_property_present(&pdev->dev,
|
||||
"axp288_extcon_data\n"))
|
||||
dev_err(&pdev->dev, "failed to get platform data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
axp288_extcon_log_rsi(info);
|
||||
|
||||
/* Initialize extcon device */
|
||||
info->edev = devm_extcon_dev_allocate(&pdev->dev,
|
||||
axp288_extcon_cables);
|
||||
if (IS_ERR(info->edev)) {
|
||||
dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
|
||||
return PTR_ERR(info->edev);
|
||||
}
|
||||
|
||||
/* Register extcon device */
|
||||
ret = devm_extcon_dev_register(&pdev->dev, info->edev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register extcon device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get otg transceiver phy */
|
||||
info->otg = usb_get_phy(USB_PHY_TYPE_USB2);
|
||||
if (IS_ERR(info->otg)) {
|
||||
dev_err(&pdev->dev, "failed to get otg transceiver\n");
|
||||
return PTR_ERR(info->otg);
|
||||
}
|
||||
|
||||
/* Set up gpio control for USB Mux */
|
||||
if (info->pdata->gpio_mux_cntl) {
|
||||
gpio = desc_to_gpio(info->pdata->gpio_mux_cntl);
|
||||
ret = gpio_request(gpio, "USB_MUX");
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to request the gpio=%d\n", gpio);
|
||||
goto gpio_req_failed;
|
||||
}
|
||||
gpiod_direction_output(info->pdata->gpio_mux_cntl,
|
||||
EXTCON_GPIO_MUX_SEL_PMIC);
|
||||
}
|
||||
|
||||
for (i = 0; i < EXTCON_IRQ_END; i++) {
|
||||
pirq = platform_get_irq(pdev, i);
|
||||
info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq);
|
||||
if (info->irq[i] < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to get virtual interrupt=%d\n", pirq);
|
||||
ret = info->irq[i];
|
||||
goto gpio_req_failed;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, info->irq[i],
|
||||
NULL, axp288_extcon_isr,
|
||||
IRQF_ONESHOT | IRQF_NO_SUSPEND,
|
||||
pdev->name, info);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to request interrupt=%d\n",
|
||||
info->irq[i]);
|
||||
goto gpio_req_failed;
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable interrupts */
|
||||
axp288_extcon_enable_irq(info);
|
||||
|
||||
return 0;
|
||||
|
||||
gpio_req_failed:
|
||||
usb_put_phy(info->otg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int axp288_extcon_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct axp288_extcon_info *info = platform_get_drvdata(pdev);
|
||||
|
||||
usb_put_phy(info->otg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver axp288_extcon_driver = {
|
||||
.probe = axp288_extcon_probe,
|
||||
.remove = axp288_extcon_remove,
|
||||
.driver = {
|
||||
.name = "axp288_extcon",
|
||||
},
|
||||
};
|
||||
module_platform_driver(axp288_extcon_driver);
|
||||
|
||||
MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");
|
||||
MODULE_DESCRIPTION("X-Powers AXP288 extcon driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -104,7 +104,6 @@ static int gpio_extcon_probe(struct platform_device *pdev)
|
||||
dev_err(&pdev->dev, "failed to allocate extcon device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
extcon_data->edev->name = pdata->name;
|
||||
|
||||
extcon_data->gpio = pdata->gpio;
|
||||
extcon_data->gpio_active_low = pdata->gpio_active_low;
|
||||
|
@ -148,33 +148,14 @@ enum max14577_muic_acc_type {
|
||||
MAX14577_MUIC_ADC_OPEN,
|
||||
};
|
||||
|
||||
/* max14577 MUIC device support below list of accessories(external connector) */
|
||||
enum {
|
||||
EXTCON_CABLE_USB = 0,
|
||||
EXTCON_CABLE_TA,
|
||||
EXTCON_CABLE_FAST_CHARGER,
|
||||
EXTCON_CABLE_SLOW_CHARGER,
|
||||
EXTCON_CABLE_CHARGE_DOWNSTREAM,
|
||||
EXTCON_CABLE_JIG_USB_ON,
|
||||
EXTCON_CABLE_JIG_USB_OFF,
|
||||
EXTCON_CABLE_JIG_UART_OFF,
|
||||
EXTCON_CABLE_JIG_UART_ON,
|
||||
|
||||
_EXTCON_CABLE_NUM,
|
||||
};
|
||||
|
||||
static const char *max14577_extcon_cable[] = {
|
||||
[EXTCON_CABLE_USB] = "USB",
|
||||
[EXTCON_CABLE_TA] = "TA",
|
||||
[EXTCON_CABLE_FAST_CHARGER] = "Fast-charger",
|
||||
[EXTCON_CABLE_SLOW_CHARGER] = "Slow-charger",
|
||||
[EXTCON_CABLE_CHARGE_DOWNSTREAM] = "Charge-downstream",
|
||||
[EXTCON_CABLE_JIG_USB_ON] = "JIG-USB-ON",
|
||||
[EXTCON_CABLE_JIG_USB_OFF] = "JIG-USB-OFF",
|
||||
[EXTCON_CABLE_JIG_UART_OFF] = "JIG-UART-OFF",
|
||||
[EXTCON_CABLE_JIG_UART_ON] = "JIG-UART-ON",
|
||||
|
||||
NULL,
|
||||
static const unsigned int max14577_extcon_cable[] = {
|
||||
EXTCON_USB,
|
||||
EXTCON_TA,
|
||||
EXTCON_FAST_CHARGER,
|
||||
EXTCON_SLOW_CHARGER,
|
||||
EXTCON_CHARGE_DOWNSTREAM,
|
||||
EXTCON_JIG,
|
||||
EXTCON_NONE,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -348,7 +329,6 @@ static int max14577_muic_get_cable_type(struct max14577_muic_info *info,
|
||||
static int max14577_muic_jig_handler(struct max14577_muic_info *info,
|
||||
int cable_type, bool attached)
|
||||
{
|
||||
char cable_name[32];
|
||||
int ret = 0;
|
||||
u8 path = CTRL1_SW_OPEN;
|
||||
|
||||
@ -358,18 +338,12 @@ static int max14577_muic_jig_handler(struct max14577_muic_info *info,
|
||||
|
||||
switch (cable_type) {
|
||||
case MAX14577_MUIC_ADC_FACTORY_MODE_USB_OFF: /* ADC_JIG_USB_OFF */
|
||||
/* PATH:AP_USB */
|
||||
strcpy(cable_name, "JIG-USB-OFF");
|
||||
path = CTRL1_SW_USB;
|
||||
break;
|
||||
case MAX14577_MUIC_ADC_FACTORY_MODE_USB_ON: /* ADC_JIG_USB_ON */
|
||||
/* PATH:AP_USB */
|
||||
strcpy(cable_name, "JIG-USB-ON");
|
||||
path = CTRL1_SW_USB;
|
||||
break;
|
||||
case MAX14577_MUIC_ADC_FACTORY_MODE_UART_OFF: /* ADC_JIG_UART_OFF */
|
||||
/* PATH:AP_UART */
|
||||
strcpy(cable_name, "JIG-UART-OFF");
|
||||
path = CTRL1_SW_UART;
|
||||
break;
|
||||
default:
|
||||
@ -382,7 +356,7 @@ static int max14577_muic_jig_handler(struct max14577_muic_info *info,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
extcon_set_cable_state(info->edev, cable_name, attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_JIG, attached);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -479,20 +453,22 @@ static int max14577_muic_chg_handler(struct max14577_muic_info *info)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
extcon_set_cable_state(info->edev, "USB", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_USB, attached);
|
||||
break;
|
||||
case MAX14577_CHARGER_TYPE_DEDICATED_CHG:
|
||||
extcon_set_cable_state(info->edev, "TA", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_TA, attached);
|
||||
break;
|
||||
case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT:
|
||||
extcon_set_cable_state(info->edev,
|
||||
"Charge-downstream", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_CHARGE_DOWNSTREAM,
|
||||
attached);
|
||||
break;
|
||||
case MAX14577_CHARGER_TYPE_SPECIAL_500MA:
|
||||
extcon_set_cable_state(info->edev, "Slow-charger", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_SLOW_CHARGER,
|
||||
attached);
|
||||
break;
|
||||
case MAX14577_CHARGER_TYPE_SPECIAL_1A:
|
||||
extcon_set_cable_state(info->edev, "Fast-charger", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_FAST_CHARGER,
|
||||
attached);
|
||||
break;
|
||||
case MAX14577_CHARGER_TYPE_NONE:
|
||||
case MAX14577_CHARGER_TYPE_DEAD_BATTERY:
|
||||
@ -742,8 +718,6 @@ static int max14577_muic_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
info->edev->name = dev_name(&pdev->dev);
|
||||
|
||||
ret = devm_extcon_dev_register(&pdev->dev, info->edev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register extcon device\n");
|
||||
|
@ -200,44 +200,17 @@ enum max77693_muic_acc_type {
|
||||
/*
|
||||
* MAX77693 MUIC device support below list of accessories(external connector)
|
||||
*/
|
||||
enum {
|
||||
EXTCON_CABLE_USB = 0,
|
||||
EXTCON_CABLE_USB_HOST,
|
||||
EXTCON_CABLE_TA,
|
||||
EXTCON_CABLE_FAST_CHARGER,
|
||||
EXTCON_CABLE_SLOW_CHARGER,
|
||||
EXTCON_CABLE_CHARGE_DOWNSTREAM,
|
||||
EXTCON_CABLE_MHL,
|
||||
EXTCON_CABLE_MHL_TA,
|
||||
EXTCON_CABLE_JIG_USB_ON,
|
||||
EXTCON_CABLE_JIG_USB_OFF,
|
||||
EXTCON_CABLE_JIG_UART_OFF,
|
||||
EXTCON_CABLE_JIG_UART_ON,
|
||||
EXTCON_CABLE_DOCK_SMART,
|
||||
EXTCON_CABLE_DOCK_DESK,
|
||||
EXTCON_CABLE_DOCK_AUDIO,
|
||||
|
||||
_EXTCON_CABLE_NUM,
|
||||
};
|
||||
|
||||
static const char *max77693_extcon_cable[] = {
|
||||
[EXTCON_CABLE_USB] = "USB",
|
||||
[EXTCON_CABLE_USB_HOST] = "USB-Host",
|
||||
[EXTCON_CABLE_TA] = "TA",
|
||||
[EXTCON_CABLE_FAST_CHARGER] = "Fast-charger",
|
||||
[EXTCON_CABLE_SLOW_CHARGER] = "Slow-charger",
|
||||
[EXTCON_CABLE_CHARGE_DOWNSTREAM] = "Charge-downstream",
|
||||
[EXTCON_CABLE_MHL] = "MHL",
|
||||
[EXTCON_CABLE_MHL_TA] = "MHL-TA",
|
||||
[EXTCON_CABLE_JIG_USB_ON] = "JIG-USB-ON",
|
||||
[EXTCON_CABLE_JIG_USB_OFF] = "JIG-USB-OFF",
|
||||
[EXTCON_CABLE_JIG_UART_OFF] = "JIG-UART-OFF",
|
||||
[EXTCON_CABLE_JIG_UART_ON] = "JIG-UART-ON",
|
||||
[EXTCON_CABLE_DOCK_SMART] = "Dock-Smart",
|
||||
[EXTCON_CABLE_DOCK_DESK] = "Dock-Desk",
|
||||
[EXTCON_CABLE_DOCK_AUDIO] = "Dock-Audio",
|
||||
|
||||
NULL,
|
||||
static const unsigned int max77693_extcon_cable[] = {
|
||||
EXTCON_USB,
|
||||
EXTCON_USB_HOST,
|
||||
EXTCON_TA,
|
||||
EXTCON_FAST_CHARGER,
|
||||
EXTCON_SLOW_CHARGER,
|
||||
EXTCON_CHARGE_DOWNSTREAM,
|
||||
EXTCON_MHL,
|
||||
EXTCON_JIG,
|
||||
EXTCON_DOCK,
|
||||
EXTCON_NONE,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -484,7 +457,7 @@ static int max77693_muic_dock_handler(struct max77693_muic_info *info,
|
||||
int ret = 0;
|
||||
int vbvolt;
|
||||
bool cable_attached;
|
||||
char dock_name[CABLE_NAME_MAX];
|
||||
unsigned int dock_id;
|
||||
|
||||
dev_info(info->dev,
|
||||
"external connector is %s (adc:0x%02x)\n",
|
||||
@ -507,15 +480,15 @@ static int max77693_muic_dock_handler(struct max77693_muic_info *info,
|
||||
}
|
||||
|
||||
/*
|
||||
* Notify Dock-Smart/MHL state.
|
||||
* - Dock-Smart device include three type of cable which
|
||||
* Notify Dock/MHL state.
|
||||
* - Dock device include three type of cable which
|
||||
* are HDMI, USB for mouse/keyboard and micro-usb port
|
||||
* for USB/TA cable. Dock-Smart device need always exteranl
|
||||
* power supply(USB/TA cable through micro-usb cable). Dock-
|
||||
* Smart device support screen output of target to separate
|
||||
* for USB/TA cable. Dock device need always exteranl
|
||||
* power supply(USB/TA cable through micro-usb cable). Dock
|
||||
* device support screen output of target to separate
|
||||
* monitor and mouse/keyboard for desktop mode.
|
||||
*
|
||||
* Features of 'USB/TA cable with Dock-Smart device'
|
||||
* Features of 'USB/TA cable with Dock device'
|
||||
* - Support MHL
|
||||
* - Support external output feature of audio
|
||||
* - Support charging through micro-usb port without data
|
||||
@ -529,16 +502,16 @@ static int max77693_muic_dock_handler(struct max77693_muic_info *info,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
extcon_set_cable_state(info->edev, "Dock-Smart", attached);
|
||||
extcon_set_cable_state(info->edev, "MHL", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_DOCK, attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_MHL, attached);
|
||||
goto out;
|
||||
case MAX77693_MUIC_ADC_AUDIO_MODE_REMOTE: /* Dock-Desk */
|
||||
strcpy(dock_name, "Dock-Desk");
|
||||
dock_id = EXTCON_DOCK;
|
||||
break;
|
||||
case MAX77693_MUIC_ADC_AV_CABLE_NOLOAD: /* Dock-Audio */
|
||||
strcpy(dock_name, "Dock-Audio");
|
||||
dock_id = EXTCON_DOCK;
|
||||
if (!attached)
|
||||
extcon_set_cable_state(info->edev, "USB", false);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_USB, false);
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev, "failed to detect %s dock device\n",
|
||||
@ -550,7 +523,7 @@ static int max77693_muic_dock_handler(struct max77693_muic_info *info,
|
||||
ret = max77693_muic_set_path(info, CONTROL1_SW_AUDIO, attached);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
extcon_set_cable_state(info->edev, dock_name, attached);
|
||||
extcon_set_cable_state_(info->edev, dock_id, attached);
|
||||
|
||||
out:
|
||||
return 0;
|
||||
@ -615,20 +588,19 @@ static int max77693_muic_adc_ground_handler(struct max77693_muic_info *info)
|
||||
ret = max77693_muic_set_path(info, CONTROL1_SW_USB, attached);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
extcon_set_cable_state(info->edev, "USB-Host", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, attached);
|
||||
break;
|
||||
case MAX77693_MUIC_GND_AV_CABLE_LOAD:
|
||||
/* Audio Video Cable with load, PATH:AUDIO */
|
||||
ret = max77693_muic_set_path(info, CONTROL1_SW_AUDIO, attached);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
extcon_set_cable_state(info->edev,
|
||||
"Audio-video-load", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_USB, attached);
|
||||
break;
|
||||
case MAX77693_MUIC_GND_MHL:
|
||||
case MAX77693_MUIC_GND_MHL_VB:
|
||||
/* MHL or MHL with USB/TA cable */
|
||||
extcon_set_cable_state(info->edev, "MHL", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_MHL, attached);
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev, "failed to detect %s cable of gnd type\n",
|
||||
@ -642,7 +614,6 @@ static int max77693_muic_adc_ground_handler(struct max77693_muic_info *info)
|
||||
static int max77693_muic_jig_handler(struct max77693_muic_info *info,
|
||||
int cable_type, bool attached)
|
||||
{
|
||||
char cable_name[32];
|
||||
int ret = 0;
|
||||
u8 path = CONTROL1_SW_OPEN;
|
||||
|
||||
@ -652,23 +623,13 @@ static int max77693_muic_jig_handler(struct max77693_muic_info *info,
|
||||
|
||||
switch (cable_type) {
|
||||
case MAX77693_MUIC_ADC_FACTORY_MODE_USB_OFF: /* ADC_JIG_USB_OFF */
|
||||
/* PATH:AP_USB */
|
||||
strcpy(cable_name, "JIG-USB-OFF");
|
||||
path = CONTROL1_SW_USB;
|
||||
break;
|
||||
case MAX77693_MUIC_ADC_FACTORY_MODE_USB_ON: /* ADC_JIG_USB_ON */
|
||||
/* PATH:AP_USB */
|
||||
strcpy(cable_name, "JIG-USB-ON");
|
||||
path = CONTROL1_SW_USB;
|
||||
break;
|
||||
case MAX77693_MUIC_ADC_FACTORY_MODE_UART_OFF: /* ADC_JIG_UART_OFF */
|
||||
/* PATH:AP_UART */
|
||||
strcpy(cable_name, "JIG-UART-OFF");
|
||||
path = CONTROL1_SW_UART;
|
||||
break;
|
||||
case MAX77693_MUIC_ADC_FACTORY_MODE_UART_ON: /* ADC_JIG_UART_ON */
|
||||
/* PATH:AP_UART */
|
||||
strcpy(cable_name, "JIG-UART-ON");
|
||||
path = CONTROL1_SW_UART;
|
||||
break;
|
||||
default:
|
||||
@ -681,7 +642,7 @@ static int max77693_muic_jig_handler(struct max77693_muic_info *info,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
extcon_set_cable_state(info->edev, cable_name, attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_JIG, attached);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -823,22 +784,22 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
|
||||
case MAX77693_MUIC_GND_MHL:
|
||||
case MAX77693_MUIC_GND_MHL_VB:
|
||||
/*
|
||||
* MHL cable with MHL-TA(USB/TA) cable
|
||||
* MHL cable with USB/TA cable
|
||||
* - MHL cable include two port(HDMI line and separate
|
||||
* micro-usb port. When the target connect MHL cable,
|
||||
* extcon driver check whether MHL-TA(USB/TA) cable is
|
||||
* connected. If MHL-TA cable is connected, extcon
|
||||
* extcon driver check whether USB/TA cable is
|
||||
* connected. If USB/TA cable is connected, extcon
|
||||
* driver notify state to notifiee for charging battery.
|
||||
*
|
||||
* Features of 'MHL-TA(USB/TA) with MHL cable'
|
||||
* Features of 'USB/TA with MHL cable'
|
||||
* - Support MHL
|
||||
* - Support charging through micro-usb port without
|
||||
* data connection
|
||||
*/
|
||||
extcon_set_cable_state(info->edev, "MHL-TA", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_TA, attached);
|
||||
if (!cable_attached)
|
||||
extcon_set_cable_state(info->edev,
|
||||
"MHL", cable_attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_MHL,
|
||||
cable_attached);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -861,11 +822,12 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
|
||||
* - Support charging through micro-usb port without
|
||||
* data connection.
|
||||
*/
|
||||
extcon_set_cable_state(info->edev, "USB", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_USB,
|
||||
attached);
|
||||
|
||||
if (!cable_attached)
|
||||
extcon_set_cable_state(info->edev, "Dock-Audio",
|
||||
cable_attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_DOCK,
|
||||
cable_attached);
|
||||
break;
|
||||
case MAX77693_MUIC_ADC_RESERVED_ACC_3: /* Dock-Smart */
|
||||
/*
|
||||
@ -893,10 +855,10 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
extcon_set_cable_state(info->edev, "Dock-Smart",
|
||||
attached);
|
||||
extcon_set_cable_state(info->edev, "MHL", attached);
|
||||
|
||||
extcon_set_cable_state_(info->edev, EXTCON_DOCK,
|
||||
attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_MHL,
|
||||
attached);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -929,23 +891,26 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
extcon_set_cable_state(info->edev, "USB", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_USB,
|
||||
attached);
|
||||
break;
|
||||
case MAX77693_CHARGER_TYPE_DEDICATED_CHG:
|
||||
/* Only TA cable */
|
||||
extcon_set_cable_state(info->edev, "TA", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_TA, attached);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case MAX77693_CHARGER_TYPE_DOWNSTREAM_PORT:
|
||||
extcon_set_cable_state(info->edev,
|
||||
"Charge-downstream", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_CHARGE_DOWNSTREAM,
|
||||
attached);
|
||||
break;
|
||||
case MAX77693_CHARGER_TYPE_APPLE_500MA:
|
||||
extcon_set_cable_state(info->edev, "Slow-charger", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_SLOW_CHARGER,
|
||||
attached);
|
||||
break;
|
||||
case MAX77693_CHARGER_TYPE_APPLE_1A_2A:
|
||||
extcon_set_cable_state(info->edev, "Fast-charger", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_FAST_CHARGER,
|
||||
attached);
|
||||
break;
|
||||
case MAX77693_CHARGER_TYPE_DEAD_BATTERY:
|
||||
break;
|
||||
@ -1182,7 +1147,6 @@ static int max77693_muic_probe(struct platform_device *pdev)
|
||||
dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
info->edev->name = DEV_NAME;
|
||||
|
||||
ret = devm_extcon_dev_register(&pdev->dev, info->edev);
|
||||
if (ret) {
|
||||
|
@ -118,36 +118,16 @@ enum max77843_muic_charger_type {
|
||||
MAX77843_MUIC_CHG_GND,
|
||||
};
|
||||
|
||||
enum {
|
||||
MAX77843_CABLE_USB = 0,
|
||||
MAX77843_CABLE_USB_HOST,
|
||||
MAX77843_CABLE_TA,
|
||||
MAX77843_CABLE_CHARGE_DOWNSTREAM,
|
||||
MAX77843_CABLE_FAST_CHARGER,
|
||||
MAX77843_CABLE_SLOW_CHARGER,
|
||||
MAX77843_CABLE_MHL,
|
||||
MAX77843_CABLE_MHL_TA,
|
||||
MAX77843_CABLE_JIG_USB_ON,
|
||||
MAX77843_CABLE_JIG_USB_OFF,
|
||||
MAX77843_CABLE_JIG_UART_ON,
|
||||
MAX77843_CABLE_JIG_UART_OFF,
|
||||
|
||||
MAX77843_CABLE_NUM,
|
||||
};
|
||||
|
||||
static const char *max77843_extcon_cable[] = {
|
||||
[MAX77843_CABLE_USB] = "USB",
|
||||
[MAX77843_CABLE_USB_HOST] = "USB-HOST",
|
||||
[MAX77843_CABLE_TA] = "TA",
|
||||
[MAX77843_CABLE_CHARGE_DOWNSTREAM] = "CHARGER-DOWNSTREAM",
|
||||
[MAX77843_CABLE_FAST_CHARGER] = "FAST-CHARGER",
|
||||
[MAX77843_CABLE_SLOW_CHARGER] = "SLOW-CHARGER",
|
||||
[MAX77843_CABLE_MHL] = "MHL",
|
||||
[MAX77843_CABLE_MHL_TA] = "MHL-TA",
|
||||
[MAX77843_CABLE_JIG_USB_ON] = "JIG-USB-ON",
|
||||
[MAX77843_CABLE_JIG_USB_OFF] = "JIG-USB-OFF",
|
||||
[MAX77843_CABLE_JIG_UART_ON] = "JIG-UART-ON",
|
||||
[MAX77843_CABLE_JIG_UART_OFF] = "JIG-UART-OFF",
|
||||
static const unsigned int max77843_extcon_cable[] = {
|
||||
EXTCON_USB,
|
||||
EXTCON_USB_HOST,
|
||||
EXTCON_TA,
|
||||
EXTCON_CHARGE_DOWNSTREAM,
|
||||
EXTCON_FAST_CHARGER,
|
||||
EXTCON_SLOW_CHARGER,
|
||||
EXTCON_MHL,
|
||||
EXTCON_JIG,
|
||||
EXTCON_NONE,
|
||||
};
|
||||
|
||||
struct max77843_muic_irq {
|
||||
@ -362,7 +342,7 @@ static int max77843_muic_adc_gnd_handler(struct max77843_muic_info *info)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
extcon_set_cable_state(info->edev, "USB-HOST", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, attached);
|
||||
break;
|
||||
case MAX77843_MUIC_GND_MHL_VB:
|
||||
case MAX77843_MUIC_GND_MHL:
|
||||
@ -370,7 +350,7 @@ static int max77843_muic_adc_gnd_handler(struct max77843_muic_info *info)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
extcon_set_cable_state(info->edev, "MHL", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_MHL, attached);
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev, "failed to detect %s accessory(gnd:0x%x)\n",
|
||||
@ -385,36 +365,29 @@ static int max77843_muic_jig_handler(struct max77843_muic_info *info,
|
||||
int cable_type, bool attached)
|
||||
{
|
||||
int ret;
|
||||
u8 path = CONTROL1_SW_OPEN;
|
||||
|
||||
dev_dbg(info->dev, "external connector is %s (adc:0x%02x)\n",
|
||||
attached ? "attached" : "detached", cable_type);
|
||||
|
||||
switch (cable_type) {
|
||||
case MAX77843_MUIC_ADC_FACTORY_MODE_USB_OFF:
|
||||
ret = max77843_muic_set_path(info, CONTROL1_SW_USB, attached);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
extcon_set_cable_state(info->edev, "JIG-USB-OFF", attached);
|
||||
break;
|
||||
case MAX77843_MUIC_ADC_FACTORY_MODE_USB_ON:
|
||||
ret = max77843_muic_set_path(info, CONTROL1_SW_USB, attached);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
extcon_set_cable_state(info->edev, "JIG-USB-ON", attached);
|
||||
path = CONTROL1_SW_USB;
|
||||
break;
|
||||
case MAX77843_MUIC_ADC_FACTORY_MODE_UART_OFF:
|
||||
ret = max77843_muic_set_path(info, CONTROL1_SW_UART, attached);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
extcon_set_cable_state(info->edev, "JIG-UART-OFF", attached);
|
||||
path = CONTROL1_SW_UART;
|
||||
break;
|
||||
default:
|
||||
ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = max77843_muic_set_path(info, path, attached);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
extcon_set_cable_state_(info->edev, EXTCON_JIG, attached);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -505,36 +478,38 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
extcon_set_cable_state(info->edev, "USB", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_USB, attached);
|
||||
break;
|
||||
case MAX77843_MUIC_CHG_DOWNSTREAM:
|
||||
ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
extcon_set_cable_state(info->edev,
|
||||
"CHARGER-DOWNSTREAM", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_CHARGE_DOWNSTREAM,
|
||||
attached);
|
||||
break;
|
||||
case MAX77843_MUIC_CHG_DEDICATED:
|
||||
ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
extcon_set_cable_state(info->edev, "TA", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_TA, attached);
|
||||
break;
|
||||
case MAX77843_MUIC_CHG_SPECIAL_500MA:
|
||||
ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
extcon_set_cable_state(info->edev, "SLOW-CHAREGER", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_SLOW_CHARGER,
|
||||
attached);
|
||||
break;
|
||||
case MAX77843_MUIC_CHG_SPECIAL_1A:
|
||||
ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
extcon_set_cable_state(info->edev, "FAST-CHARGER", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_FAST_CHARGER,
|
||||
attached);
|
||||
break;
|
||||
case MAX77843_MUIC_CHG_GND:
|
||||
gnd_type = max77843_muic_get_cable_type(info,
|
||||
@ -542,9 +517,9 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info)
|
||||
|
||||
/* Charger cable on MHL accessory is attach or detach */
|
||||
if (gnd_type == MAX77843_MUIC_GND_MHL_VB)
|
||||
extcon_set_cable_state(info->edev, "MHL-TA", true);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_TA, true);
|
||||
else if (gnd_type == MAX77843_MUIC_GND_MHL)
|
||||
extcon_set_cable_state(info->edev, "MHL-TA", false);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_TA, false);
|
||||
break;
|
||||
case MAX77843_MUIC_CHG_NONE:
|
||||
break;
|
||||
|
@ -145,34 +145,17 @@ struct max8997_muic_info {
|
||||
int path_uart;
|
||||
};
|
||||
|
||||
enum {
|
||||
EXTCON_CABLE_USB = 0,
|
||||
EXTCON_CABLE_USB_HOST,
|
||||
EXTCON_CABLE_TA,
|
||||
EXTCON_CABLE_FAST_CHARGER,
|
||||
EXTCON_CABLE_SLOW_CHARGER,
|
||||
EXTCON_CABLE_CHARGE_DOWNSTREAM,
|
||||
EXTCON_CABLE_MHL,
|
||||
EXTCON_CABLE_DOCK_DESK,
|
||||
EXTCON_CABLE_DOCK_CARD,
|
||||
EXTCON_CABLE_JIG,
|
||||
|
||||
_EXTCON_CABLE_NUM,
|
||||
};
|
||||
|
||||
static const char *max8997_extcon_cable[] = {
|
||||
[EXTCON_CABLE_USB] = "USB",
|
||||
[EXTCON_CABLE_USB_HOST] = "USB-Host",
|
||||
[EXTCON_CABLE_TA] = "TA",
|
||||
[EXTCON_CABLE_FAST_CHARGER] = "Fast-charger",
|
||||
[EXTCON_CABLE_SLOW_CHARGER] = "Slow-charger",
|
||||
[EXTCON_CABLE_CHARGE_DOWNSTREAM] = "Charge-downstream",
|
||||
[EXTCON_CABLE_MHL] = "MHL",
|
||||
[EXTCON_CABLE_DOCK_DESK] = "Dock-Desk",
|
||||
[EXTCON_CABLE_DOCK_CARD] = "Dock-Card",
|
||||
[EXTCON_CABLE_JIG] = "JIG",
|
||||
|
||||
NULL,
|
||||
static const unsigned int max8997_extcon_cable[] = {
|
||||
EXTCON_USB,
|
||||
EXTCON_USB_HOST,
|
||||
EXTCON_TA,
|
||||
EXTCON_FAST_CHARGER,
|
||||
EXTCON_SLOW_CHARGER,
|
||||
EXTCON_CHARGE_DOWNSTREAM,
|
||||
EXTCON_MHL,
|
||||
EXTCON_DOCK,
|
||||
EXTCON_JIG,
|
||||
EXTCON_NONE,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -347,10 +330,10 @@ static int max8997_muic_handle_usb(struct max8997_muic_info *info,
|
||||
|
||||
switch (usb_type) {
|
||||
case MAX8997_USB_HOST:
|
||||
extcon_set_cable_state(info->edev, "USB-Host", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, attached);
|
||||
break;
|
||||
case MAX8997_USB_DEVICE:
|
||||
extcon_set_cable_state(info->edev, "USB", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_USB, attached);
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev, "failed to detect %s usb cable\n",
|
||||
@ -374,10 +357,8 @@ static int max8997_muic_handle_dock(struct max8997_muic_info *info,
|
||||
|
||||
switch (cable_type) {
|
||||
case MAX8997_MUIC_ADC_AV_CABLE_NOLOAD:
|
||||
extcon_set_cable_state(info->edev, "Dock-desk", attached);
|
||||
break;
|
||||
case MAX8997_MUIC_ADC_FACTORY_MODE_UART_ON:
|
||||
extcon_set_cable_state(info->edev, "Dock-card", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_DOCK, attached);
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev, "failed to detect %s dock device\n",
|
||||
@ -400,7 +381,7 @@ static int max8997_muic_handle_jig_uart(struct max8997_muic_info *info,
|
||||
return ret;
|
||||
}
|
||||
|
||||
extcon_set_cable_state(info->edev, "JIG", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_JIG, attached);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -422,7 +403,7 @@ static int max8997_muic_adc_handler(struct max8997_muic_info *info)
|
||||
return ret;
|
||||
break;
|
||||
case MAX8997_MUIC_ADC_MHL:
|
||||
extcon_set_cable_state(info->edev, "MHL", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_MHL, attached);
|
||||
break;
|
||||
case MAX8997_MUIC_ADC_FACTORY_MODE_USB_OFF:
|
||||
case MAX8997_MUIC_ADC_FACTORY_MODE_USB_ON:
|
||||
@ -505,17 +486,19 @@ static int max8997_muic_chg_handler(struct max8997_muic_info *info)
|
||||
}
|
||||
break;
|
||||
case MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT:
|
||||
extcon_set_cable_state(info->edev,
|
||||
"Charge-downstream", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_CHARGE_DOWNSTREAM,
|
||||
attached);
|
||||
break;
|
||||
case MAX8997_CHARGER_TYPE_DEDICATED_CHG:
|
||||
extcon_set_cable_state(info->edev, "TA", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_TA, attached);
|
||||
break;
|
||||
case MAX8997_CHARGER_TYPE_500MA:
|
||||
extcon_set_cable_state(info->edev, "Slow-charger", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_SLOW_CHARGER,
|
||||
attached);
|
||||
break;
|
||||
case MAX8997_CHARGER_TYPE_1A:
|
||||
extcon_set_cable_state(info->edev, "Fast-charger", attached);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_FAST_CHARGER,
|
||||
attached);
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev,
|
||||
@ -700,7 +683,6 @@ static int max8997_muic_probe(struct platform_device *pdev)
|
||||
ret = -ENOMEM;
|
||||
goto err_irq;
|
||||
}
|
||||
info->edev->name = DEV_NAME;
|
||||
|
||||
ret = devm_extcon_dev_register(&pdev->dev, info->edev);
|
||||
if (ret) {
|
||||
|
@ -29,10 +29,10 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
static const char *palmas_extcon_cable[] = {
|
||||
[0] = "USB",
|
||||
[1] = "USB-HOST",
|
||||
NULL,
|
||||
static const unsigned int palmas_extcon_cable[] = {
|
||||
EXTCON_USB,
|
||||
EXTCON_USB_HOST,
|
||||
EXTCON_NONE,
|
||||
};
|
||||
|
||||
static const int mutually_exclusive[] = {0x3, 0x0};
|
||||
@ -49,6 +49,7 @@ static void palmas_usb_wakeup(struct palmas *palmas, int enable)
|
||||
static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
|
||||
{
|
||||
struct palmas_usb *palmas_usb = _palmas_usb;
|
||||
struct extcon_dev *edev = palmas_usb->edev;
|
||||
unsigned int vbus_line_state;
|
||||
|
||||
palmas_read(palmas_usb->palmas, PALMAS_INTERRUPT_BASE,
|
||||
@ -57,7 +58,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
|
||||
if (vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS) {
|
||||
if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) {
|
||||
palmas_usb->linkstat = PALMAS_USB_STATE_VBUS;
|
||||
extcon_set_cable_state(palmas_usb->edev, "USB", true);
|
||||
extcon_set_cable_state_(edev, EXTCON_USB, true);
|
||||
dev_info(palmas_usb->dev, "USB cable is attached\n");
|
||||
} else {
|
||||
dev_dbg(palmas_usb->dev,
|
||||
@ -66,7 +67,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
|
||||
} else if (!(vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS)) {
|
||||
if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) {
|
||||
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
|
||||
extcon_set_cable_state(palmas_usb->edev, "USB", false);
|
||||
extcon_set_cable_state_(edev, EXTCON_USB, false);
|
||||
dev_info(palmas_usb->dev, "USB cable is detached\n");
|
||||
} else {
|
||||
dev_dbg(palmas_usb->dev,
|
||||
@ -81,6 +82,7 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
|
||||
{
|
||||
unsigned int set, id_src;
|
||||
struct palmas_usb *palmas_usb = _palmas_usb;
|
||||
struct extcon_dev *edev = palmas_usb->edev;
|
||||
|
||||
palmas_read(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
|
||||
PALMAS_USB_ID_INT_LATCH_SET, &set);
|
||||
@ -93,7 +95,7 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
|
||||
PALMAS_USB_ID_INT_LATCH_CLR,
|
||||
PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND);
|
||||
palmas_usb->linkstat = PALMAS_USB_STATE_ID;
|
||||
extcon_set_cable_state(palmas_usb->edev, "USB-HOST", true);
|
||||
extcon_set_cable_state_(edev, EXTCON_USB_HOST, true);
|
||||
dev_info(palmas_usb->dev, "USB-HOST cable is attached\n");
|
||||
} else if ((set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) &&
|
||||
(id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT)) {
|
||||
@ -101,17 +103,17 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
|
||||
PALMAS_USB_ID_INT_LATCH_CLR,
|
||||
PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT);
|
||||
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
|
||||
extcon_set_cable_state(palmas_usb->edev, "USB-HOST", false);
|
||||
extcon_set_cable_state_(edev, EXTCON_USB_HOST, false);
|
||||
dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
|
||||
} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) &&
|
||||
(!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) {
|
||||
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
|
||||
extcon_set_cable_state(palmas_usb->edev, "USB-HOST", false);
|
||||
extcon_set_cable_state_(edev, EXTCON_USB_HOST, false);
|
||||
dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
|
||||
} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_DISCONNECT) &&
|
||||
(id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) {
|
||||
palmas_usb->linkstat = PALMAS_USB_STATE_ID;
|
||||
extcon_set_cable_state(palmas_usb->edev, "USB-HOST", true);
|
||||
extcon_set_cable_state_(edev, EXTCON_USB_HOST, true);
|
||||
dev_info(palmas_usb->dev, " USB-HOST cable is attached\n");
|
||||
}
|
||||
|
||||
@ -193,7 +195,6 @@ static int palmas_usb_probe(struct platform_device *pdev)
|
||||
dev_err(&pdev->dev, "failed to allocate extcon device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
palmas_usb->edev->name = kstrdup(node->name, GFP_KERNEL);
|
||||
palmas_usb->edev->mutually_exclusive = mutually_exclusive;
|
||||
|
||||
status = devm_extcon_dev_register(&pdev->dev, palmas_usb->edev);
|
||||
|
@ -90,27 +90,12 @@ static struct reg_data rt8973a_reg_data[] = {
|
||||
};
|
||||
|
||||
/* List of detectable cables */
|
||||
enum {
|
||||
EXTCON_CABLE_USB = 0,
|
||||
EXTCON_CABLE_USB_HOST,
|
||||
EXTCON_CABLE_TA,
|
||||
EXTCON_CABLE_JIG_OFF_USB,
|
||||
EXTCON_CABLE_JIG_ON_USB,
|
||||
EXTCON_CABLE_JIG_OFF_UART,
|
||||
EXTCON_CABLE_JIG_ON_UART,
|
||||
|
||||
EXTCON_CABLE_END,
|
||||
};
|
||||
|
||||
static const char *rt8973a_extcon_cable[] = {
|
||||
[EXTCON_CABLE_USB] = "USB",
|
||||
[EXTCON_CABLE_USB_HOST] = "USB-Host",
|
||||
[EXTCON_CABLE_TA] = "TA",
|
||||
[EXTCON_CABLE_JIG_OFF_USB] = "JIG-USB-OFF",
|
||||
[EXTCON_CABLE_JIG_ON_USB] = "JIG-USB-ON",
|
||||
[EXTCON_CABLE_JIG_OFF_UART] = "JIG-UART-OFF",
|
||||
[EXTCON_CABLE_JIG_ON_UART] = "JIG-UART-ON",
|
||||
NULL,
|
||||
static const unsigned int rt8973a_extcon_cable[] = {
|
||||
EXTCON_USB,
|
||||
EXTCON_USB_HOST,
|
||||
EXTCON_TA,
|
||||
EXTCON_JIG,
|
||||
EXTCON_NONE,
|
||||
};
|
||||
|
||||
/* Define OVP (Over Voltage Protection), OTP (Over Temperature Protection) */
|
||||
@ -313,14 +298,11 @@ static int rt8973a_muic_cable_handler(struct rt8973a_muic_info *info,
|
||||
enum rt8973a_event_type event)
|
||||
{
|
||||
static unsigned int prev_cable_type;
|
||||
const char **cable_names = info->edev->supported_cable;
|
||||
unsigned int con_sw = DM_DP_SWITCH_UART;
|
||||
int ret, idx = 0, cable_type;
|
||||
int ret, cable_type;
|
||||
unsigned int id;
|
||||
bool attached = false;
|
||||
|
||||
if (!cable_names)
|
||||
return 0;
|
||||
|
||||
switch (event) {
|
||||
case RT8973A_EVENT_ATTACH:
|
||||
cable_type = rt8973a_muic_get_cable_type(info);
|
||||
@ -347,31 +329,25 @@ static int rt8973a_muic_cable_handler(struct rt8973a_muic_info *info,
|
||||
|
||||
switch (cable_type) {
|
||||
case RT8973A_MUIC_ADC_OTG:
|
||||
idx = EXTCON_CABLE_USB_HOST;
|
||||
id = EXTCON_USB_HOST;
|
||||
con_sw = DM_DP_SWITCH_USB;
|
||||
break;
|
||||
case RT8973A_MUIC_ADC_TA:
|
||||
idx = EXTCON_CABLE_TA;
|
||||
id = EXTCON_TA;
|
||||
con_sw = DM_DP_SWITCH_OPEN;
|
||||
break;
|
||||
case RT8973A_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB:
|
||||
idx = EXTCON_CABLE_JIG_OFF_USB;
|
||||
con_sw = DM_DP_SWITCH_UART;
|
||||
break;
|
||||
case RT8973A_MUIC_ADC_FACTORY_MODE_BOOT_ON_USB:
|
||||
idx = EXTCON_CABLE_JIG_ON_USB;
|
||||
con_sw = DM_DP_SWITCH_UART;
|
||||
id = EXTCON_JIG;
|
||||
con_sw = DM_DP_SWITCH_USB;
|
||||
break;
|
||||
case RT8973A_MUIC_ADC_FACTORY_MODE_BOOT_OFF_UART:
|
||||
idx = EXTCON_CABLE_JIG_OFF_UART;
|
||||
con_sw = DM_DP_SWITCH_UART;
|
||||
break;
|
||||
case RT8973A_MUIC_ADC_FACTORY_MODE_BOOT_ON_UART:
|
||||
idx = EXTCON_CABLE_JIG_ON_UART;
|
||||
id = EXTCON_JIG;
|
||||
con_sw = DM_DP_SWITCH_UART;
|
||||
break;
|
||||
case RT8973A_MUIC_ADC_USB:
|
||||
idx = EXTCON_CABLE_USB;
|
||||
id = EXTCON_USB;
|
||||
con_sw = DM_DP_SWITCH_USB;
|
||||
break;
|
||||
case RT8973A_MUIC_ADC_OPEN:
|
||||
@ -421,7 +397,7 @@ static int rt8973a_muic_cable_handler(struct rt8973a_muic_info *info,
|
||||
return ret;
|
||||
|
||||
/* Change the state of external accessory */
|
||||
extcon_set_cable_state(info->edev, cable_names[idx], attached);
|
||||
extcon_set_cable_state_(info->edev, id, attached);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -643,7 +619,6 @@ static int rt8973a_muic_i2c_probe(struct i2c_client *i2c,
|
||||
dev_err(info->dev, "failed to allocate memory for extcon\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
info->edev->name = np->name;
|
||||
|
||||
/* Register extcon device */
|
||||
ret = devm_extcon_dev_register(info->dev, info->edev);
|
||||
|
@ -92,19 +92,11 @@ static struct reg_data sm5502_reg_data[] = {
|
||||
};
|
||||
|
||||
/* List of detectable cables */
|
||||
enum {
|
||||
EXTCON_CABLE_USB = 0,
|
||||
EXTCON_CABLE_USB_HOST,
|
||||
EXTCON_CABLE_TA,
|
||||
|
||||
EXTCON_CABLE_END,
|
||||
};
|
||||
|
||||
static const char *sm5502_extcon_cable[] = {
|
||||
[EXTCON_CABLE_USB] = "USB",
|
||||
[EXTCON_CABLE_USB_HOST] = "USB-Host",
|
||||
[EXTCON_CABLE_TA] = "TA",
|
||||
NULL,
|
||||
static const unsigned int sm5502_extcon_cable[] = {
|
||||
EXTCON_USB,
|
||||
EXTCON_USB_HOST,
|
||||
EXTCON_TA,
|
||||
EXTCON_NONE,
|
||||
};
|
||||
|
||||
/* Define supported accessory type */
|
||||
@ -377,16 +369,12 @@ static int sm5502_muic_cable_handler(struct sm5502_muic_info *info,
|
||||
bool attached)
|
||||
{
|
||||
static unsigned int prev_cable_type = SM5502_MUIC_ADC_GROUND;
|
||||
const char **cable_names = info->edev->supported_cable;
|
||||
unsigned int cable_type = SM5502_MUIC_ADC_GROUND;
|
||||
unsigned int con_sw = DM_DP_SWITCH_OPEN;
|
||||
unsigned int vbus_sw = VBUSIN_SWITCH_OPEN;
|
||||
unsigned int idx = 0;
|
||||
unsigned int id;
|
||||
int ret;
|
||||
|
||||
if (!cable_names)
|
||||
return 0;
|
||||
|
||||
/* Get the type of attached or detached cable */
|
||||
if (attached)
|
||||
cable_type = sm5502_muic_get_cable_type(info);
|
||||
@ -396,17 +384,17 @@ static int sm5502_muic_cable_handler(struct sm5502_muic_info *info,
|
||||
|
||||
switch (cable_type) {
|
||||
case SM5502_MUIC_ADC_OPEN_USB:
|
||||
idx = EXTCON_CABLE_USB;
|
||||
id = EXTCON_USB;
|
||||
con_sw = DM_DP_SWITCH_USB;
|
||||
vbus_sw = VBUSIN_SWITCH_VBUSOUT_WITH_USB;
|
||||
break;
|
||||
case SM5502_MUIC_ADC_OPEN_TA:
|
||||
idx = EXTCON_CABLE_TA;
|
||||
id = EXTCON_TA;
|
||||
con_sw = DM_DP_SWITCH_OPEN;
|
||||
vbus_sw = VBUSIN_SWITCH_VBUSOUT;
|
||||
break;
|
||||
case SM5502_MUIC_ADC_OPEN_USB_OTG:
|
||||
idx = EXTCON_CABLE_USB_HOST;
|
||||
id = EXTCON_USB_HOST;
|
||||
con_sw = DM_DP_SWITCH_USB;
|
||||
vbus_sw = VBUSIN_SWITCH_OPEN;
|
||||
break;
|
||||
@ -422,7 +410,7 @@ static int sm5502_muic_cable_handler(struct sm5502_muic_info *info,
|
||||
return ret;
|
||||
|
||||
/* Change the state of external accessory */
|
||||
extcon_set_cable_state(info->edev, cable_names[idx], attached);
|
||||
extcon_set_cable_state_(info->edev, id, attached);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -623,7 +611,6 @@ static int sm5022_muic_i2c_probe(struct i2c_client *i2c,
|
||||
dev_err(info->dev, "failed to allocate memory for extcon\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
info->edev->name = np->name;
|
||||
|
||||
/* Register extcon device */
|
||||
ret = devm_extcon_dev_register(info->dev, info->edev);
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
@ -38,18 +39,10 @@ struct usb_extcon_info {
|
||||
struct delayed_work wq_detcable;
|
||||
};
|
||||
|
||||
/* List of detectable cables */
|
||||
enum {
|
||||
EXTCON_CABLE_USB = 0,
|
||||
EXTCON_CABLE_USB_HOST,
|
||||
|
||||
EXTCON_CABLE_END,
|
||||
};
|
||||
|
||||
static const char *usb_extcon_cable[] = {
|
||||
[EXTCON_CABLE_USB] = "USB",
|
||||
[EXTCON_CABLE_USB_HOST] = "USB-HOST",
|
||||
NULL,
|
||||
static const unsigned int usb_extcon_cable[] = {
|
||||
EXTCON_USB,
|
||||
EXTCON_USB_HOST,
|
||||
EXTCON_NONE,
|
||||
};
|
||||
|
||||
static void usb_extcon_detect_cable(struct work_struct *work)
|
||||
@ -67,24 +60,16 @@ static void usb_extcon_detect_cable(struct work_struct *work)
|
||||
* As we don't have event for USB peripheral cable attached,
|
||||
* we simulate USB peripheral attach here.
|
||||
*/
|
||||
extcon_set_cable_state(info->edev,
|
||||
usb_extcon_cable[EXTCON_CABLE_USB_HOST],
|
||||
false);
|
||||
extcon_set_cable_state(info->edev,
|
||||
usb_extcon_cable[EXTCON_CABLE_USB],
|
||||
true);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, false);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_USB, true);
|
||||
} else {
|
||||
/*
|
||||
* ID = 0 means USB HOST cable attached.
|
||||
* As we don't have event for USB peripheral cable detached,
|
||||
* we simulate USB peripheral detach here.
|
||||
*/
|
||||
extcon_set_cable_state(info->edev,
|
||||
usb_extcon_cable[EXTCON_CABLE_USB],
|
||||
false);
|
||||
extcon_set_cable_state(info->edev,
|
||||
usb_extcon_cable[EXTCON_CABLE_USB_HOST],
|
||||
true);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_USB, false);
|
||||
extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,7 +98,7 @@ static int usb_extcon_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
info->dev = dev;
|
||||
info->id_gpiod = devm_gpiod_get(&pdev->dev, "id");
|
||||
info->id_gpiod = devm_gpiod_get(&pdev->dev, "id", GPIOD_IN);
|
||||
if (IS_ERR(info->id_gpiod)) {
|
||||
dev_err(dev, "failed to get ID GPIO\n");
|
||||
return PTR_ERR(info->id_gpiod);
|
||||
|
@ -1,8 +1,11 @@
|
||||
/*
|
||||
* drivers/extcon/extcon_class.c
|
||||
* drivers/extcon/extcon.c - External Connector (extcon) framework.
|
||||
*
|
||||
* External connector (extcon) class driver
|
||||
*
|
||||
* Copyright (C) 2015 Samsung Electronics
|
||||
* Author: Chanwoo Choi <cw00.choi@samsung.com>
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
* Author: Donggeun Kim <dg77.kim@samsung.com>
|
||||
* Author: MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
@ -19,8 +22,7 @@
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
@ -33,36 +35,43 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
/*
|
||||
* extcon_cable_name suggests the standard cable names for commonly used
|
||||
* cable types.
|
||||
*
|
||||
* However, please do not use extcon_cable_name directly for extcon_dev
|
||||
* struct's supported_cable pointer unless your device really supports
|
||||
* every single port-type of the following cable names. Please choose cable
|
||||
* names that are actually used in your extcon device.
|
||||
*/
|
||||
const char extcon_cable_name[][CABLE_NAME_MAX + 1] = {
|
||||
#define SUPPORTED_CABLE_MAX 32
|
||||
#define CABLE_NAME_MAX 30
|
||||
|
||||
static const char *extcon_name[] = {
|
||||
[EXTCON_NONE] = "NONE",
|
||||
|
||||
/* USB external connector */
|
||||
[EXTCON_USB] = "USB",
|
||||
[EXTCON_USB_HOST] = "USB-Host",
|
||||
[EXTCON_USB_HOST] = "USB-HOST",
|
||||
|
||||
/* Charger external connector */
|
||||
[EXTCON_TA] = "TA",
|
||||
[EXTCON_FAST_CHARGER] = "Fast-charger",
|
||||
[EXTCON_SLOW_CHARGER] = "Slow-charger",
|
||||
[EXTCON_CHARGE_DOWNSTREAM] = "Charge-downstream",
|
||||
[EXTCON_FAST_CHARGER] = "FAST-CHARGER",
|
||||
[EXTCON_SLOW_CHARGER] = "SLOW-CHARGER",
|
||||
[EXTCON_CHARGE_DOWNSTREAM] = "CHARGE-DOWNSTREAM",
|
||||
|
||||
/* Audio/Video external connector */
|
||||
[EXTCON_LINE_IN] = "LINE-IN",
|
||||
[EXTCON_LINE_OUT] = "LINE-OUT",
|
||||
[EXTCON_MICROPHONE] = "MICROPHONE",
|
||||
[EXTCON_HEADPHONE] = "HEADPHONE",
|
||||
|
||||
[EXTCON_HDMI] = "HDMI",
|
||||
[EXTCON_MHL] = "MHL",
|
||||
[EXTCON_DVI] = "DVI",
|
||||
[EXTCON_VGA] = "VGA",
|
||||
[EXTCON_DOCK] = "Dock",
|
||||
[EXTCON_LINE_IN] = "Line-in",
|
||||
[EXTCON_LINE_OUT] = "Line-out",
|
||||
[EXTCON_MIC_IN] = "Microphone",
|
||||
[EXTCON_HEADPHONE_OUT] = "Headphone",
|
||||
[EXTCON_SPDIF_IN] = "SPDIF-in",
|
||||
[EXTCON_SPDIF_OUT] = "SPDIF-out",
|
||||
[EXTCON_VIDEO_IN] = "Video-in",
|
||||
[EXTCON_VIDEO_OUT] = "Video-out",
|
||||
[EXTCON_MECHANICAL] = "Mechanical",
|
||||
[EXTCON_SPDIF_IN] = "SPDIF-IN",
|
||||
[EXTCON_SPDIF_OUT] = "SPDIF-OUT",
|
||||
[EXTCON_VIDEO_IN] = "VIDEO-IN",
|
||||
[EXTCON_VIDEO_OUT] = "VIDEO-OUT",
|
||||
|
||||
/* Etc external connector */
|
||||
[EXTCON_DOCK] = "DOCK",
|
||||
[EXTCON_JIG] = "JIG",
|
||||
[EXTCON_MECHANICAL] = "MECHANICAL",
|
||||
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct class *extcon_class;
|
||||
@ -102,6 +111,51 @@ static int check_mutually_exclusive(struct extcon_dev *edev, u32 new_state)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_cable_index_by_id(struct extcon_dev *edev, const unsigned int id)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Find the the index of extcon cable in edev->supported_cable */
|
||||
for (i = 0; i < edev->max_supported; i++) {
|
||||
if (edev->supported_cable[i] == id)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int find_cable_index_by_name(struct extcon_dev *edev, const char *name)
|
||||
{
|
||||
unsigned int id = EXTCON_NONE;
|
||||
int i = 0;
|
||||
|
||||
if (edev->max_supported == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Find the the number of extcon cable */
|
||||
while (extcon_name[i]) {
|
||||
if (!strncmp(extcon_name[i], name, CABLE_NAME_MAX)) {
|
||||
id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (id == EXTCON_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
return find_cable_index_by_id(edev, id);
|
||||
}
|
||||
|
||||
static bool is_extcon_changed(u32 prev, u32 new, int idx, bool *attached)
|
||||
{
|
||||
if (((prev >> idx) & 0x1) != ((new >> idx) & 0x1)) {
|
||||
*attached = new ? true : false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static ssize_t state_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
@ -119,11 +173,9 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
|
||||
if (edev->max_supported == 0)
|
||||
return sprintf(buf, "%u\n", edev->state);
|
||||
|
||||
for (i = 0; i < SUPPORTED_CABLE_MAX; i++) {
|
||||
if (!edev->supported_cable[i])
|
||||
break;
|
||||
for (i = 0; i < edev->max_supported; i++) {
|
||||
count += sprintf(buf + count, "%s=%d\n",
|
||||
edev->supported_cable[i],
|
||||
extcon_name[edev->supported_cable[i]],
|
||||
!!(edev->state & (1 << i)));
|
||||
}
|
||||
|
||||
@ -155,15 +207,7 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct extcon_dev *edev = dev_get_drvdata(dev);
|
||||
|
||||
/* Optional callback given by the user */
|
||||
if (edev->print_name) {
|
||||
int ret = edev->print_name(edev, buf);
|
||||
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return sprintf(buf, "%s\n", dev_name(&edev->dev));
|
||||
return sprintf(buf, "%s\n", edev->name);
|
||||
}
|
||||
static DEVICE_ATTR_RO(name);
|
||||
|
||||
@ -172,9 +216,10 @@ static ssize_t cable_name_show(struct device *dev,
|
||||
{
|
||||
struct extcon_cable *cable = container_of(attr, struct extcon_cable,
|
||||
attr_name);
|
||||
int i = cable->cable_index;
|
||||
|
||||
return sprintf(buf, "%s\n",
|
||||
cable->edev->supported_cable[cable->cable_index]);
|
||||
extcon_name[cable->edev->supported_cable[i]]);
|
||||
}
|
||||
|
||||
static ssize_t cable_state_show(struct device *dev,
|
||||
@ -211,23 +256,27 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
|
||||
char *envp[3];
|
||||
int env_offset = 0;
|
||||
int length;
|
||||
int index;
|
||||
unsigned long flags;
|
||||
bool attached;
|
||||
|
||||
spin_lock_irqsave(&edev->lock, flags);
|
||||
|
||||
if (edev->state != ((edev->state & ~mask) | (state & mask))) {
|
||||
u32 old_state = edev->state;
|
||||
|
||||
if (check_mutually_exclusive(edev, (edev->state & ~mask) |
|
||||
(state & mask))) {
|
||||
spin_unlock_irqrestore(&edev->lock, flags);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
for (index = 0; index < edev->max_supported; index++) {
|
||||
if (is_extcon_changed(edev->state, state, index, &attached))
|
||||
raw_notifier_call_chain(&edev->nh[index], attached, edev);
|
||||
}
|
||||
|
||||
edev->state &= ~mask;
|
||||
edev->state |= state & mask;
|
||||
|
||||
raw_notifier_call_chain(&edev->nh, old_state, edev);
|
||||
/* This could be in interrupt handler */
|
||||
prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
|
||||
if (prop_buf) {
|
||||
@ -283,40 +332,20 @@ int extcon_set_state(struct extcon_dev *edev, u32 state)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(extcon_set_state);
|
||||
|
||||
/**
|
||||
* extcon_find_cable_index() - Get the cable index based on the cable name.
|
||||
* @edev: the extcon device that has the cable.
|
||||
* @cable_name: cable name to be searched.
|
||||
*
|
||||
* Note that accessing a cable state based on cable_index is faster than
|
||||
* cable_name because using cable_name induces a loop with strncmp().
|
||||
* Thus, when get/set_cable_state is repeatedly used, using cable_index
|
||||
* is recommended.
|
||||
*/
|
||||
int extcon_find_cable_index(struct extcon_dev *edev, const char *cable_name)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (edev->supported_cable) {
|
||||
for (i = 0; edev->supported_cable[i]; i++) {
|
||||
if (!strncmp(edev->supported_cable[i],
|
||||
cable_name, CABLE_NAME_MAX))
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(extcon_find_cable_index);
|
||||
|
||||
/**
|
||||
* extcon_get_cable_state_() - Get the status of a specific cable.
|
||||
* @edev: the extcon device that has the cable.
|
||||
* @index: cable index that can be retrieved by extcon_find_cable_index().
|
||||
* @id: the unique id of each external connector in extcon enumeration.
|
||||
*/
|
||||
int extcon_get_cable_state_(struct extcon_dev *edev, int index)
|
||||
int extcon_get_cable_state_(struct extcon_dev *edev, const unsigned int id)
|
||||
{
|
||||
if (index < 0 || (edev->max_supported && edev->max_supported <= index))
|
||||
int index;
|
||||
|
||||
index = find_cable_index_by_id(edev, id);
|
||||
if (index < 0)
|
||||
return index;
|
||||
|
||||
if (edev->max_supported && edev->max_supported <= index)
|
||||
return -EINVAL;
|
||||
|
||||
return !!(edev->state & (1 << index));
|
||||
@ -332,7 +361,7 @@ EXPORT_SYMBOL_GPL(extcon_get_cable_state_);
|
||||
*/
|
||||
int extcon_get_cable_state(struct extcon_dev *edev, const char *cable_name)
|
||||
{
|
||||
return extcon_get_cable_state_(edev, extcon_find_cable_index
|
||||
return extcon_get_cable_state_(edev, find_cable_index_by_name
|
||||
(edev, cable_name));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(extcon_get_cable_state);
|
||||
@ -340,17 +369,22 @@ EXPORT_SYMBOL_GPL(extcon_get_cable_state);
|
||||
/**
|
||||
* extcon_set_cable_state_() - Set the status of a specific cable.
|
||||
* @edev: the extcon device that has the cable.
|
||||
* @index: cable index that can be retrieved by
|
||||
* extcon_find_cable_index().
|
||||
* @cable_state: the new cable status. The default semantics is
|
||||
* @id: the unique id of each external connector
|
||||
* in extcon enumeration.
|
||||
* @state: the new cable status. The default semantics is
|
||||
* true: attached / false: detached.
|
||||
*/
|
||||
int extcon_set_cable_state_(struct extcon_dev *edev,
|
||||
int index, bool cable_state)
|
||||
int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
|
||||
bool cable_state)
|
||||
{
|
||||
u32 state;
|
||||
int index;
|
||||
|
||||
if (index < 0 || (edev->max_supported && edev->max_supported <= index))
|
||||
index = find_cable_index_by_id(edev, id);
|
||||
if (index < 0)
|
||||
return index;
|
||||
|
||||
if (edev->max_supported && edev->max_supported <= index)
|
||||
return -EINVAL;
|
||||
|
||||
state = cable_state ? (1 << index) : 0;
|
||||
@ -370,7 +404,7 @@ EXPORT_SYMBOL_GPL(extcon_set_cable_state_);
|
||||
int extcon_set_cable_state(struct extcon_dev *edev,
|
||||
const char *cable_name, bool cable_state)
|
||||
{
|
||||
return extcon_set_cable_state_(edev, extcon_find_cable_index
|
||||
return extcon_set_cable_state_(edev, find_cable_index_by_name
|
||||
(edev, cable_name), cable_state);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(extcon_set_cable_state);
|
||||
@ -395,29 +429,6 @@ out:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(extcon_get_extcon_dev);
|
||||
|
||||
static int _call_per_cable(struct notifier_block *nb, unsigned long val,
|
||||
void *ptr)
|
||||
{
|
||||
struct extcon_specific_cable_nb *obj = container_of(nb,
|
||||
struct extcon_specific_cable_nb, internal_nb);
|
||||
struct extcon_dev *edev = ptr;
|
||||
|
||||
if ((val & (1 << obj->cable_index)) !=
|
||||
(edev->state & (1 << obj->cable_index))) {
|
||||
bool cable_state = true;
|
||||
|
||||
obj->previous_value = val;
|
||||
|
||||
if (val & (1 << obj->cable_index))
|
||||
cable_state = false;
|
||||
|
||||
return obj->user_nb->notifier_call(obj->user_nb,
|
||||
cable_state, ptr);
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* extcon_register_interest() - Register a notifier for a state change of a
|
||||
* specific cable, not an entier set of cables of a
|
||||
@ -456,20 +467,18 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj,
|
||||
if (!obj->edev)
|
||||
return -ENODEV;
|
||||
|
||||
obj->cable_index = extcon_find_cable_index(obj->edev,
|
||||
cable_name);
|
||||
obj->cable_index = find_cable_index_by_name(obj->edev,
|
||||
cable_name);
|
||||
if (obj->cable_index < 0)
|
||||
return obj->cable_index;
|
||||
|
||||
obj->user_nb = nb;
|
||||
|
||||
obj->internal_nb.notifier_call = _call_per_cable;
|
||||
|
||||
spin_lock_irqsave(&obj->edev->lock, flags);
|
||||
ret = raw_notifier_chain_register(&obj->edev->nh,
|
||||
&obj->internal_nb);
|
||||
ret = raw_notifier_chain_register(
|
||||
&obj->edev->nh[obj->cable_index],
|
||||
obj->user_nb);
|
||||
spin_unlock_irqrestore(&obj->edev->lock, flags);
|
||||
return ret;
|
||||
} else {
|
||||
struct class_dev_iter iter;
|
||||
struct extcon_dev *extd;
|
||||
@ -481,7 +490,7 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj,
|
||||
while ((dev = class_dev_iter_next(&iter))) {
|
||||
extd = dev_get_drvdata(dev);
|
||||
|
||||
if (extcon_find_cable_index(extd, cable_name) < 0)
|
||||
if (find_cable_index_by_name(extd, cable_name) < 0)
|
||||
continue;
|
||||
|
||||
class_dev_iter_exit(&iter);
|
||||
@ -489,8 +498,10 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj,
|
||||
cable_name, nb);
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
ret = -ENODEV;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(extcon_register_interest);
|
||||
|
||||
@ -509,7 +520,8 @@ int extcon_unregister_interest(struct extcon_specific_cable_nb *obj)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&obj->edev->lock, flags);
|
||||
ret = raw_notifier_chain_unregister(&obj->edev->nh, &obj->internal_nb);
|
||||
ret = raw_notifier_chain_unregister(
|
||||
&obj->edev->nh[obj->cable_index], obj->user_nb);
|
||||
spin_unlock_irqrestore(&obj->edev->lock, flags);
|
||||
|
||||
return ret;
|
||||
@ -519,21 +531,24 @@ EXPORT_SYMBOL_GPL(extcon_unregister_interest);
|
||||
/**
|
||||
* extcon_register_notifier() - Register a notifiee to get notified by
|
||||
* any attach status changes from the extcon.
|
||||
* @edev: the extcon device.
|
||||
* @edev: the extcon device that has the external connecotr.
|
||||
* @id: the unique id of each external connector in extcon enumeration.
|
||||
* @nb: a notifier block to be registered.
|
||||
*
|
||||
* Note that the second parameter given to the callback of nb (val) is
|
||||
* "old_state", not the current state. The current state can be retrieved
|
||||
* by looking at the third pameter (edev pointer)'s state value.
|
||||
*/
|
||||
int extcon_register_notifier(struct extcon_dev *edev,
|
||||
struct notifier_block *nb)
|
||||
int extcon_register_notifier(struct extcon_dev *edev, unsigned int id,
|
||||
struct notifier_block *nb)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
int ret, idx;
|
||||
|
||||
idx = find_cable_index_by_id(edev, id);
|
||||
|
||||
spin_lock_irqsave(&edev->lock, flags);
|
||||
ret = raw_notifier_chain_register(&edev->nh, nb);
|
||||
ret = raw_notifier_chain_register(&edev->nh[idx], nb);
|
||||
spin_unlock_irqrestore(&edev->lock, flags);
|
||||
|
||||
return ret;
|
||||
@ -542,17 +557,20 @@ EXPORT_SYMBOL_GPL(extcon_register_notifier);
|
||||
|
||||
/**
|
||||
* extcon_unregister_notifier() - Unregister a notifiee from the extcon device.
|
||||
* @edev: the extcon device.
|
||||
* @nb: a registered notifier block to be unregistered.
|
||||
* @edev: the extcon device that has the external connecotr.
|
||||
* @id: the unique id of each external connector in extcon enumeration.
|
||||
* @nb: a notifier block to be registered.
|
||||
*/
|
||||
int extcon_unregister_notifier(struct extcon_dev *edev,
|
||||
struct notifier_block *nb)
|
||||
int extcon_unregister_notifier(struct extcon_dev *edev, unsigned int id,
|
||||
struct notifier_block *nb)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
int ret, idx;
|
||||
|
||||
idx = find_cable_index_by_id(edev, id);
|
||||
|
||||
spin_lock_irqsave(&edev->lock, flags);
|
||||
ret = raw_notifier_chain_unregister(&edev->nh, nb);
|
||||
ret = raw_notifier_chain_unregister(&edev->nh[idx], nb);
|
||||
spin_unlock_irqrestore(&edev->lock, flags);
|
||||
|
||||
return ret;
|
||||
@ -595,7 +613,7 @@ static void dummy_sysfs_dev_release(struct device *dev)
|
||||
|
||||
/*
|
||||
* extcon_dev_allocate() - Allocate the memory of extcon device.
|
||||
* @supported_cable: Array of supported cable names ending with NULL.
|
||||
* @supported_cable: Array of supported extcon ending with EXTCON_NONE.
|
||||
* If supported_cable is NULL, cable name related APIs
|
||||
* are disabled.
|
||||
*
|
||||
@ -605,7 +623,7 @@ static void dummy_sysfs_dev_release(struct device *dev)
|
||||
*
|
||||
* Return the pointer of extcon device if success or ERR_PTR(err) if fail
|
||||
*/
|
||||
struct extcon_dev *extcon_dev_allocate(const char **supported_cable)
|
||||
struct extcon_dev *extcon_dev_allocate(const unsigned int *supported_cable)
|
||||
{
|
||||
struct extcon_dev *edev;
|
||||
|
||||
@ -647,7 +665,7 @@ static void devm_extcon_dev_release(struct device *dev, void *res)
|
||||
/**
|
||||
* devm_extcon_dev_allocate - Allocate managed extcon device
|
||||
* @dev: device owning the extcon device being created
|
||||
* @supported_cable: Array of supported cable names ending with NULL.
|
||||
* @supported_cable: Array of supported extcon ending with EXTCON_NONE.
|
||||
* If supported_cable is NULL, cable name related APIs
|
||||
* are disabled.
|
||||
*
|
||||
@ -659,7 +677,7 @@ static void devm_extcon_dev_release(struct device *dev, void *res)
|
||||
* or ERR_PTR(err) if fail
|
||||
*/
|
||||
struct extcon_dev *devm_extcon_dev_allocate(struct device *dev,
|
||||
const char **supported_cable)
|
||||
const unsigned int *supported_cable)
|
||||
{
|
||||
struct extcon_dev **ptr, *edev;
|
||||
|
||||
@ -701,6 +719,7 @@ EXPORT_SYMBOL_GPL(devm_extcon_dev_free);
|
||||
int extcon_dev_register(struct extcon_dev *edev)
|
||||
{
|
||||
int ret, index = 0;
|
||||
static atomic_t edev_no = ATOMIC_INIT(-1);
|
||||
|
||||
if (!extcon_class) {
|
||||
ret = create_extcon_class();
|
||||
@ -708,30 +727,29 @@ int extcon_dev_register(struct extcon_dev *edev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (edev->supported_cable) {
|
||||
/* Get size of array */
|
||||
for (index = 0; edev->supported_cable[index]; index++)
|
||||
;
|
||||
edev->max_supported = index;
|
||||
} else {
|
||||
edev->max_supported = 0;
|
||||
}
|
||||
if (!edev->supported_cable)
|
||||
return -EINVAL;
|
||||
|
||||
for (; edev->supported_cable[index] != EXTCON_NONE; index++);
|
||||
|
||||
edev->max_supported = index;
|
||||
if (index > SUPPORTED_CABLE_MAX) {
|
||||
dev_err(&edev->dev, "extcon: maximum number of supported cables exceeded.\n");
|
||||
dev_err(&edev->dev,
|
||||
"exceed the maximum number of supported cables\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
edev->dev.class = extcon_class;
|
||||
edev->dev.release = extcon_dev_release;
|
||||
|
||||
edev->name = edev->name ? edev->name : dev_name(edev->dev.parent);
|
||||
edev->name = dev_name(edev->dev.parent);
|
||||
if (IS_ERR_OR_NULL(edev->name)) {
|
||||
dev_err(&edev->dev,
|
||||
"extcon device name is null\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
dev_set_name(&edev->dev, "%s", edev->name);
|
||||
dev_set_name(&edev->dev, "extcon%lu",
|
||||
(unsigned long)atomic_inc_return(&edev_no));
|
||||
|
||||
if (edev->max_supported) {
|
||||
char buf[10];
|
||||
@ -864,7 +882,15 @@ int extcon_dev_register(struct extcon_dev *edev)
|
||||
|
||||
spin_lock_init(&edev->lock);
|
||||
|
||||
RAW_INIT_NOTIFIER_HEAD(&edev->nh);
|
||||
edev->nh = devm_kzalloc(&edev->dev,
|
||||
sizeof(*edev->nh) * edev->max_supported, GFP_KERNEL);
|
||||
if (!edev->nh) {
|
||||
ret = -ENOMEM;
|
||||
goto err_dev;
|
||||
}
|
||||
|
||||
for (index = 0; index < edev->max_supported; index++)
|
||||
RAW_INIT_NOTIFIER_HEAD(&edev->nh[index]);
|
||||
|
||||
dev_set_drvdata(&edev->dev, edev);
|
||||
edev->state = 0;
|
||||
@ -1044,6 +1070,15 @@ struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
|
||||
#endif /* CONFIG_OF */
|
||||
EXPORT_SYMBOL_GPL(extcon_get_edev_by_phandle);
|
||||
|
||||
/**
|
||||
* extcon_get_edev_name() - Get the name of the extcon device.
|
||||
* @edev: the extcon device
|
||||
*/
|
||||
const char *extcon_get_edev_name(struct extcon_dev *edev)
|
||||
{
|
||||
return !edev ? NULL : edev->name;
|
||||
}
|
||||
|
||||
static int __init extcon_class_init(void)
|
||||
{
|
||||
return create_extcon_class();
|
||||
@ -1059,6 +1094,7 @@ static void __exit extcon_class_exit(void)
|
||||
}
|
||||
module_exit(extcon_class_exit);
|
||||
|
||||
MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
|
||||
MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
|
||||
MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
|
||||
MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
|
||||
|
@ -5,4 +5,4 @@ obj-$(CONFIG_HYPERV_BALLOON) += hv_balloon.o
|
||||
hv_vmbus-y := vmbus_drv.o \
|
||||
hv.o connection.o channel.o \
|
||||
channel_mgmt.o ring_buffer.o
|
||||
hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_fcopy.o
|
||||
hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_fcopy.o hv_utils_transport.o
|
||||
|
@ -73,6 +73,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
|
||||
unsigned long flags;
|
||||
int ret, err = 0;
|
||||
unsigned long t;
|
||||
struct page *page;
|
||||
|
||||
spin_lock_irqsave(&newchannel->lock, flags);
|
||||
if (newchannel->state == CHANNEL_OPEN_STATE) {
|
||||
@ -87,8 +88,17 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
|
||||
newchannel->channel_callback_context = context;
|
||||
|
||||
/* Allocate the ring buffer */
|
||||
out = (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO,
|
||||
get_order(send_ringbuffer_size + recv_ringbuffer_size));
|
||||
page = alloc_pages_node(cpu_to_node(newchannel->target_cpu),
|
||||
GFP_KERNEL|__GFP_ZERO,
|
||||
get_order(send_ringbuffer_size +
|
||||
recv_ringbuffer_size));
|
||||
|
||||
if (!page)
|
||||
out = (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO,
|
||||
get_order(send_ringbuffer_size +
|
||||
recv_ringbuffer_size));
|
||||
else
|
||||
out = (void *)page_address(page);
|
||||
|
||||
if (!out) {
|
||||
err = -ENOMEM;
|
||||
@ -178,19 +188,18 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
|
||||
goto error1;
|
||||
}
|
||||
|
||||
|
||||
if (open_info->response.open_result.status)
|
||||
err = open_info->response.open_result.status;
|
||||
|
||||
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
|
||||
list_del(&open_info->msglistentry);
|
||||
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
|
||||
|
||||
if (err == 0)
|
||||
newchannel->state = CHANNEL_OPENED_STATE;
|
||||
if (open_info->response.open_result.status) {
|
||||
err = -EAGAIN;
|
||||
goto error_gpadl;
|
||||
}
|
||||
|
||||
newchannel->state = CHANNEL_OPENED_STATE;
|
||||
kfree(open_info);
|
||||
return err;
|
||||
return 0;
|
||||
|
||||
error1:
|
||||
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
|
||||
|
@ -32,6 +32,9 @@
|
||||
|
||||
#include "hyperv_vmbus.h"
|
||||
|
||||
static void init_vp_index(struct vmbus_channel *channel,
|
||||
const uuid_le *type_guid);
|
||||
|
||||
/**
|
||||
* vmbus_prep_negotiate_resp() - Create default response for Hyper-V Negotiate message
|
||||
* @icmsghdrp: Pointer to msg header structure
|
||||
@ -205,6 +208,7 @@ void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid)
|
||||
primary_channel = channel->primary_channel;
|
||||
spin_lock_irqsave(&primary_channel->lock, flags);
|
||||
list_del(&channel->sc_list);
|
||||
primary_channel->num_sc--;
|
||||
spin_unlock_irqrestore(&primary_channel->lock, flags);
|
||||
}
|
||||
free_channel(channel);
|
||||
@ -212,11 +216,16 @@ void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid)
|
||||
|
||||
void vmbus_free_channels(void)
|
||||
{
|
||||
struct vmbus_channel *channel;
|
||||
struct vmbus_channel *channel, *tmp;
|
||||
|
||||
list_for_each_entry_safe(channel, tmp, &vmbus_connection.chn_list,
|
||||
listentry) {
|
||||
/* if we don't set rescind to true, vmbus_close_internal()
|
||||
* won't invoke hv_process_channel_removal().
|
||||
*/
|
||||
channel->rescind = true;
|
||||
|
||||
list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
|
||||
vmbus_device_unregister(channel->device_obj);
|
||||
free_channel(channel);
|
||||
}
|
||||
}
|
||||
|
||||
@ -228,7 +237,6 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
|
||||
{
|
||||
struct vmbus_channel *channel;
|
||||
bool fnew = true;
|
||||
bool enq = false;
|
||||
unsigned long flags;
|
||||
|
||||
/* Make sure this is a new offer */
|
||||
@ -244,25 +252,12 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
|
||||
}
|
||||
}
|
||||
|
||||
if (fnew) {
|
||||
if (fnew)
|
||||
list_add_tail(&newchannel->listentry,
|
||||
&vmbus_connection.chn_list);
|
||||
enq = true;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
|
||||
|
||||
if (enq) {
|
||||
if (newchannel->target_cpu != get_cpu()) {
|
||||
put_cpu();
|
||||
smp_call_function_single(newchannel->target_cpu,
|
||||
percpu_channel_enq,
|
||||
newchannel, true);
|
||||
} else {
|
||||
percpu_channel_enq(newchannel);
|
||||
put_cpu();
|
||||
}
|
||||
}
|
||||
if (!fnew) {
|
||||
/*
|
||||
* Check to see if this is a sub-channel.
|
||||
@ -274,27 +269,22 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
|
||||
newchannel->primary_channel = channel;
|
||||
spin_lock_irqsave(&channel->lock, flags);
|
||||
list_add_tail(&newchannel->sc_list, &channel->sc_list);
|
||||
spin_unlock_irqrestore(&channel->lock, flags);
|
||||
|
||||
if (newchannel->target_cpu != get_cpu()) {
|
||||
put_cpu();
|
||||
smp_call_function_single(newchannel->target_cpu,
|
||||
percpu_channel_enq,
|
||||
newchannel, true);
|
||||
} else {
|
||||
percpu_channel_enq(newchannel);
|
||||
put_cpu();
|
||||
}
|
||||
|
||||
newchannel->state = CHANNEL_OPEN_STATE;
|
||||
channel->num_sc++;
|
||||
if (channel->sc_creation_callback != NULL)
|
||||
channel->sc_creation_callback(newchannel);
|
||||
spin_unlock_irqrestore(&channel->lock, flags);
|
||||
} else
|
||||
goto err_free_chan;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
init_vp_index(newchannel, &newchannel->offermsg.offer.if_type);
|
||||
|
||||
goto err_free_chan;
|
||||
if (newchannel->target_cpu != get_cpu()) {
|
||||
put_cpu();
|
||||
smp_call_function_single(newchannel->target_cpu,
|
||||
percpu_channel_enq,
|
||||
newchannel, true);
|
||||
} else {
|
||||
percpu_channel_enq(newchannel);
|
||||
put_cpu();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -304,6 +294,12 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
|
||||
*/
|
||||
newchannel->state = CHANNEL_OPEN_STATE;
|
||||
|
||||
if (!fnew) {
|
||||
if (channel->sc_creation_callback != NULL)
|
||||
channel->sc_creation_callback(newchannel);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start the process of binding this offer to the driver
|
||||
* We need to set the DeviceObject field before calling
|
||||
@ -374,23 +370,27 @@ static const struct hv_vmbus_device_id hp_devs[] = {
|
||||
/*
|
||||
* We use this state to statically distribute the channel interrupt load.
|
||||
*/
|
||||
static u32 next_vp;
|
||||
static int next_numa_node_id;
|
||||
|
||||
/*
|
||||
* Starting with Win8, we can statically distribute the incoming
|
||||
* channel interrupt load by binding a channel to VCPU. We
|
||||
* implement here a simple round robin scheme for distributing
|
||||
* the interrupt load.
|
||||
* We will bind channels that are not performance critical to cpu 0 and
|
||||
* performance critical channels (IDE, SCSI and Network) will be uniformly
|
||||
* distributed across all available CPUs.
|
||||
* channel interrupt load by binding a channel to VCPU.
|
||||
* We do this in a hierarchical fashion:
|
||||
* First distribute the primary channels across available NUMA nodes
|
||||
* and then distribute the subchannels amongst the CPUs in the NUMA
|
||||
* node assigned to the primary channel.
|
||||
*
|
||||
* For pre-win8 hosts or non-performance critical channels we assign the
|
||||
* first CPU in the first NUMA node.
|
||||
*/
|
||||
static void init_vp_index(struct vmbus_channel *channel, const uuid_le *type_guid)
|
||||
{
|
||||
u32 cur_cpu;
|
||||
int i;
|
||||
bool perf_chn = false;
|
||||
u32 max_cpus = num_online_cpus();
|
||||
struct vmbus_channel *primary = channel->primary_channel;
|
||||
int next_node;
|
||||
struct cpumask available_mask;
|
||||
|
||||
for (i = IDE; i < MAX_PERF_CHN; i++) {
|
||||
if (!memcmp(type_guid->b, hp_devs[i].guid,
|
||||
@ -407,15 +407,76 @@ static void init_vp_index(struct vmbus_channel *channel, const uuid_le *type_gui
|
||||
* Also if the channel is not a performance critical
|
||||
* channel, bind it to cpu 0.
|
||||
*/
|
||||
channel->numa_node = 0;
|
||||
cpumask_set_cpu(0, &channel->alloced_cpus_in_node);
|
||||
channel->target_cpu = 0;
|
||||
channel->target_vp = 0;
|
||||
channel->target_vp = hv_context.vp_index[0];
|
||||
return;
|
||||
}
|
||||
cur_cpu = (++next_vp % max_cpus);
|
||||
|
||||
/*
|
||||
* We distribute primary channels evenly across all the available
|
||||
* NUMA nodes and within the assigned NUMA node we will assign the
|
||||
* first available CPU to the primary channel.
|
||||
* The sub-channels will be assigned to the CPUs available in the
|
||||
* NUMA node evenly.
|
||||
*/
|
||||
if (!primary) {
|
||||
while (true) {
|
||||
next_node = next_numa_node_id++;
|
||||
if (next_node == nr_node_ids)
|
||||
next_node = next_numa_node_id = 0;
|
||||
if (cpumask_empty(cpumask_of_node(next_node)))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
channel->numa_node = next_node;
|
||||
primary = channel;
|
||||
}
|
||||
|
||||
if (cpumask_weight(&primary->alloced_cpus_in_node) ==
|
||||
cpumask_weight(cpumask_of_node(primary->numa_node))) {
|
||||
/*
|
||||
* We have cycled through all the CPUs in the node;
|
||||
* reset the alloced map.
|
||||
*/
|
||||
cpumask_clear(&primary->alloced_cpus_in_node);
|
||||
}
|
||||
|
||||
cpumask_xor(&available_mask, &primary->alloced_cpus_in_node,
|
||||
cpumask_of_node(primary->numa_node));
|
||||
|
||||
cur_cpu = cpumask_next(-1, &available_mask);
|
||||
cpumask_set_cpu(cur_cpu, &primary->alloced_cpus_in_node);
|
||||
|
||||
channel->target_cpu = cur_cpu;
|
||||
channel->target_vp = hv_context.vp_index[cur_cpu];
|
||||
}
|
||||
|
||||
/*
|
||||
* vmbus_unload_response - Handler for the unload response.
|
||||
*/
|
||||
static void vmbus_unload_response(struct vmbus_channel_message_header *hdr)
|
||||
{
|
||||
/*
|
||||
* This is a global event; just wakeup the waiting thread.
|
||||
* Once we successfully unload, we can cleanup the monitor state.
|
||||
*/
|
||||
complete(&vmbus_connection.unload_event);
|
||||
}
|
||||
|
||||
void vmbus_initiate_unload(void)
|
||||
{
|
||||
struct vmbus_channel_message_header hdr;
|
||||
|
||||
init_completion(&vmbus_connection.unload_event);
|
||||
memset(&hdr, 0, sizeof(struct vmbus_channel_message_header));
|
||||
hdr.msgtype = CHANNELMSG_UNLOAD;
|
||||
vmbus_post_msg(&hdr, sizeof(struct vmbus_channel_message_header));
|
||||
|
||||
wait_for_completion(&vmbus_connection.unload_event);
|
||||
}
|
||||
|
||||
/*
|
||||
* vmbus_onoffer - Handler for channel offers from vmbus in parent partition.
|
||||
*
|
||||
@ -461,8 +522,6 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
|
||||
offer->connection_id;
|
||||
}
|
||||
|
||||
init_vp_index(newchannel, &offer->offer.if_type);
|
||||
|
||||
memcpy(&newchannel->offermsg, offer,
|
||||
sizeof(struct vmbus_channel_offer_channel));
|
||||
newchannel->monitor_grp = (u8)offer->monitorid / 32;
|
||||
@ -712,6 +771,7 @@ struct vmbus_channel_message_table_entry
|
||||
{CHANNELMSG_INITIATE_CONTACT, 0, NULL},
|
||||
{CHANNELMSG_VERSION_RESPONSE, 1, vmbus_onversion_response},
|
||||
{CHANNELMSG_UNLOAD, 0, NULL},
|
||||
{CHANNELMSG_UNLOAD_RESPONSE, 1, vmbus_unload_response},
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -58,6 +58,9 @@ static __u32 vmbus_get_next_version(__u32 current_version)
|
||||
case (VERSION_WIN8_1):
|
||||
return VERSION_WIN8;
|
||||
|
||||
case (VERSION_WIN10):
|
||||
return VERSION_WIN8_1;
|
||||
|
||||
case (VERSION_WS2008):
|
||||
default:
|
||||
return VERSION_INVAL;
|
||||
@ -80,7 +83,7 @@ static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo,
|
||||
msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
|
||||
msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages[0]);
|
||||
msg->monitor_page2 = virt_to_phys(vmbus_connection.monitor_pages[1]);
|
||||
if (version == VERSION_WIN8_1) {
|
||||
if (version >= VERSION_WIN8_1) {
|
||||
msg->target_vcpu = hv_context.vp_index[get_cpu()];
|
||||
put_cpu();
|
||||
}
|
||||
@ -227,6 +230,11 @@ cleanup:
|
||||
|
||||
void vmbus_disconnect(void)
|
||||
{
|
||||
/*
|
||||
* First send the unload request to the host.
|
||||
*/
|
||||
vmbus_initiate_unload();
|
||||
|
||||
if (vmbus_connection.work_queue) {
|
||||
drain_workqueue(vmbus_connection.work_queue);
|
||||
destroy_workqueue(vmbus_connection.work_queue);
|
||||
@ -371,8 +379,7 @@ void vmbus_on_event(unsigned long data)
|
||||
int cpu = smp_processor_id();
|
||||
union hv_synic_event_flags *event;
|
||||
|
||||
if ((vmbus_proto_version == VERSION_WS2008) ||
|
||||
(vmbus_proto_version == VERSION_WIN7)) {
|
||||
if (vmbus_proto_version < VERSION_WIN8) {
|
||||
maxdword = MAX_NUM_CHANNELS_SUPPORTED >> 5;
|
||||
recv_int_page = vmbus_connection.recv_int_page;
|
||||
} else {
|
||||
|
@ -567,7 +567,9 @@ static int hv_memory_notifier(struct notifier_block *nb, unsigned long val,
|
||||
case MEM_ONLINE:
|
||||
dm_device.num_pages_onlined += mem->nr_pages;
|
||||
case MEM_CANCEL_ONLINE:
|
||||
mutex_unlock(&dm_device.ha_region_mutex);
|
||||
if (val == MEM_ONLINE ||
|
||||
mutex_is_locked(&dm_device.ha_region_mutex))
|
||||
mutex_unlock(&dm_device.ha_region_mutex);
|
||||
if (dm_device.ha_waiting) {
|
||||
dm_device.ha_waiting = false;
|
||||
complete(&dm_device.ol_waitevent);
|
||||
|
@ -19,17 +19,13 @@
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/semaphore.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/nls.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/hyperv.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/miscdevice.h>
|
||||
|
||||
#include "hyperv_vmbus.h"
|
||||
#include "hv_utils_transport.h"
|
||||
|
||||
#define WIN8_SRV_MAJOR 1
|
||||
#define WIN8_SRV_MINOR 1
|
||||
@ -47,39 +43,31 @@
|
||||
* ensure this by serializing packet processing in this driver - we do not
|
||||
* read additional packets from the VMBUs until the current packet is fully
|
||||
* handled.
|
||||
*
|
||||
* The transaction "active" state is set when we receive a request from the
|
||||
* host and we cleanup this state when the transaction is completed - when we
|
||||
* respond to the host with our response. When the transaction active state is
|
||||
* set, we defer handling incoming packets.
|
||||
*/
|
||||
|
||||
static struct {
|
||||
bool active; /* transaction status - active or not */
|
||||
int state; /* hvutil_device_state */
|
||||
int recv_len; /* number of bytes received. */
|
||||
struct hv_fcopy_hdr *fcopy_msg; /* current message */
|
||||
struct hv_start_fcopy message; /* sent to daemon */
|
||||
struct vmbus_channel *recv_channel; /* chn we got the request */
|
||||
u64 recv_req_id; /* request ID. */
|
||||
void *fcopy_context; /* for the channel callback */
|
||||
struct semaphore read_sema;
|
||||
} fcopy_transaction;
|
||||
|
||||
static bool opened; /* currently device opened */
|
||||
|
||||
/*
|
||||
* Before we can accept copy messages from the host, we need
|
||||
* to handshake with the user level daemon. This state tracks
|
||||
* if we are in the handshake phase.
|
||||
*/
|
||||
static bool in_hand_shake = true;
|
||||
static void fcopy_send_data(void);
|
||||
static void fcopy_respond_to_host(int error);
|
||||
static void fcopy_work_func(struct work_struct *dummy);
|
||||
static DECLARE_DELAYED_WORK(fcopy_work, fcopy_work_func);
|
||||
static void fcopy_send_data(struct work_struct *dummy);
|
||||
static void fcopy_timeout_func(struct work_struct *dummy);
|
||||
static DECLARE_DELAYED_WORK(fcopy_timeout_work, fcopy_timeout_func);
|
||||
static DECLARE_WORK(fcopy_send_work, fcopy_send_data);
|
||||
static const char fcopy_devname[] = "vmbus/hv_fcopy";
|
||||
static u8 *recv_buffer;
|
||||
static struct hvutil_transport *hvt;
|
||||
/*
|
||||
* This state maintains the version number registered by the daemon.
|
||||
*/
|
||||
static int dm_reg_value;
|
||||
|
||||
static void fcopy_work_func(struct work_struct *dummy)
|
||||
static void fcopy_timeout_func(struct work_struct *dummy)
|
||||
{
|
||||
/*
|
||||
* If the timer fires, the user-mode component has not responded;
|
||||
@ -87,23 +75,28 @@ static void fcopy_work_func(struct work_struct *dummy)
|
||||
*/
|
||||
fcopy_respond_to_host(HV_E_FAIL);
|
||||
|
||||
/* In the case the user-space daemon crashes, hangs or is killed, we
|
||||
* need to down the semaphore, otherwise, after the daemon starts next
|
||||
* time, the obsolete data in fcopy_transaction.message or
|
||||
* fcopy_transaction.fcopy_msg will be used immediately.
|
||||
*
|
||||
* NOTE: fcopy_read() happens to get the semaphore (very rare)? We're
|
||||
* still OK, because we've reported the failure to the host.
|
||||
*/
|
||||
if (down_trylock(&fcopy_transaction.read_sema))
|
||||
;
|
||||
/* Transaction is finished, reset the state. */
|
||||
if (fcopy_transaction.state > HVUTIL_READY)
|
||||
fcopy_transaction.state = HVUTIL_READY;
|
||||
|
||||
hv_poll_channel(fcopy_transaction.fcopy_context,
|
||||
hv_fcopy_onchannelcallback);
|
||||
}
|
||||
|
||||
static int fcopy_handle_handshake(u32 version)
|
||||
{
|
||||
u32 our_ver = FCOPY_CURRENT_VERSION;
|
||||
|
||||
switch (version) {
|
||||
case FCOPY_CURRENT_VERSION:
|
||||
case FCOPY_VERSION_0:
|
||||
/* Daemon doesn't expect us to reply */
|
||||
dm_reg_value = version;
|
||||
break;
|
||||
case FCOPY_VERSION_1:
|
||||
/* Daemon expects us to reply with our own version */
|
||||
if (hvutil_transport_send(hvt, &our_ver, sizeof(our_ver)))
|
||||
return -EFAULT;
|
||||
dm_reg_value = version;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
@ -114,20 +107,20 @@ static int fcopy_handle_handshake(u32 version)
|
||||
*/
|
||||
return -EINVAL;
|
||||
}
|
||||
pr_info("FCP: user-mode registering done. Daemon version: %d\n",
|
||||
version);
|
||||
fcopy_transaction.active = false;
|
||||
if (fcopy_transaction.fcopy_context)
|
||||
hv_fcopy_onchannelcallback(fcopy_transaction.fcopy_context);
|
||||
in_hand_shake = false;
|
||||
pr_debug("FCP: userspace daemon ver. %d registered\n", version);
|
||||
fcopy_transaction.state = HVUTIL_READY;
|
||||
hv_poll_channel(fcopy_transaction.fcopy_context,
|
||||
hv_fcopy_onchannelcallback);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fcopy_send_data(void)
|
||||
static void fcopy_send_data(struct work_struct *dummy)
|
||||
{
|
||||
struct hv_start_fcopy *smsg_out = &fcopy_transaction.message;
|
||||
struct hv_start_fcopy smsg_out;
|
||||
int operation = fcopy_transaction.fcopy_msg->operation;
|
||||
struct hv_start_fcopy *smsg_in;
|
||||
void *out_src;
|
||||
int rc, out_len;
|
||||
|
||||
/*
|
||||
* The strings sent from the host are encoded in
|
||||
@ -142,26 +135,39 @@ static void fcopy_send_data(void)
|
||||
|
||||
switch (operation) {
|
||||
case START_FILE_COPY:
|
||||
memset(smsg_out, 0, sizeof(struct hv_start_fcopy));
|
||||
smsg_out->hdr.operation = operation;
|
||||
out_len = sizeof(struct hv_start_fcopy);
|
||||
memset(&smsg_out, 0, out_len);
|
||||
smsg_out.hdr.operation = operation;
|
||||
smsg_in = (struct hv_start_fcopy *)fcopy_transaction.fcopy_msg;
|
||||
|
||||
utf16s_to_utf8s((wchar_t *)smsg_in->file_name, W_MAX_PATH,
|
||||
UTF16_LITTLE_ENDIAN,
|
||||
(__u8 *)smsg_out->file_name, W_MAX_PATH - 1);
|
||||
(__u8 *)&smsg_out.file_name, W_MAX_PATH - 1);
|
||||
|
||||
utf16s_to_utf8s((wchar_t *)smsg_in->path_name, W_MAX_PATH,
|
||||
UTF16_LITTLE_ENDIAN,
|
||||
(__u8 *)smsg_out->path_name, W_MAX_PATH - 1);
|
||||
(__u8 *)&smsg_out.path_name, W_MAX_PATH - 1);
|
||||
|
||||
smsg_out->copy_flags = smsg_in->copy_flags;
|
||||
smsg_out->file_size = smsg_in->file_size;
|
||||
smsg_out.copy_flags = smsg_in->copy_flags;
|
||||
smsg_out.file_size = smsg_in->file_size;
|
||||
out_src = &smsg_out;
|
||||
break;
|
||||
|
||||
default:
|
||||
out_src = fcopy_transaction.fcopy_msg;
|
||||
out_len = fcopy_transaction.recv_len;
|
||||
break;
|
||||
}
|
||||
up(&fcopy_transaction.read_sema);
|
||||
|
||||
fcopy_transaction.state = HVUTIL_USERSPACE_REQ;
|
||||
rc = hvutil_transport_send(hvt, out_src, out_len);
|
||||
if (rc) {
|
||||
pr_debug("FCP: failed to communicate to the daemon: %d\n", rc);
|
||||
if (cancel_delayed_work_sync(&fcopy_timeout_work)) {
|
||||
fcopy_respond_to_host(HV_E_FAIL);
|
||||
fcopy_transaction.state = HVUTIL_READY;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -189,8 +195,6 @@ fcopy_respond_to_host(int error)
|
||||
channel = fcopy_transaction.recv_channel;
|
||||
req_id = fcopy_transaction.recv_req_id;
|
||||
|
||||
fcopy_transaction.active = false;
|
||||
|
||||
icmsghdr = (struct icmsg_hdr *)
|
||||
&recv_buffer[sizeof(struct vmbuspipe_hdr)];
|
||||
|
||||
@ -218,7 +222,7 @@ void hv_fcopy_onchannelcallback(void *context)
|
||||
int util_fw_version;
|
||||
int fcopy_srv_version;
|
||||
|
||||
if (fcopy_transaction.active) {
|
||||
if (fcopy_transaction.state > HVUTIL_READY) {
|
||||
/*
|
||||
* We will defer processing this callback once
|
||||
* the current transaction is complete.
|
||||
@ -226,6 +230,7 @@ void hv_fcopy_onchannelcallback(void *context)
|
||||
fcopy_transaction.fcopy_context = context;
|
||||
return;
|
||||
}
|
||||
fcopy_transaction.fcopy_context = NULL;
|
||||
|
||||
vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen,
|
||||
&requestid);
|
||||
@ -249,17 +254,23 @@ void hv_fcopy_onchannelcallback(void *context)
|
||||
* transaction; note transactions are serialized.
|
||||
*/
|
||||
|
||||
fcopy_transaction.active = true;
|
||||
fcopy_transaction.recv_len = recvlen;
|
||||
fcopy_transaction.recv_channel = channel;
|
||||
fcopy_transaction.recv_req_id = requestid;
|
||||
fcopy_transaction.fcopy_msg = fcopy_msg;
|
||||
|
||||
if (fcopy_transaction.state < HVUTIL_READY) {
|
||||
/* Userspace is not registered yet */
|
||||
fcopy_respond_to_host(HV_E_FAIL);
|
||||
return;
|
||||
}
|
||||
fcopy_transaction.state = HVUTIL_HOSTMSG_RECEIVED;
|
||||
|
||||
/*
|
||||
* Send the information to the user-level daemon.
|
||||
*/
|
||||
schedule_delayed_work(&fcopy_work, 5*HZ);
|
||||
fcopy_send_data();
|
||||
schedule_work(&fcopy_send_work);
|
||||
schedule_delayed_work(&fcopy_timeout_work, 5*HZ);
|
||||
return;
|
||||
}
|
||||
icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
|
||||
@ -267,155 +278,44 @@ void hv_fcopy_onchannelcallback(void *context)
|
||||
VM_PKT_DATA_INBAND, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a char device that can support read/write for passing
|
||||
* the payload.
|
||||
*/
|
||||
|
||||
static ssize_t fcopy_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
/* Callback when data is received from userspace */
|
||||
static int fcopy_on_msg(void *msg, int len)
|
||||
{
|
||||
void *src;
|
||||
size_t copy_size;
|
||||
int operation;
|
||||
int *val = (int *)msg;
|
||||
|
||||
/*
|
||||
* Wait until there is something to be read.
|
||||
*/
|
||||
if (down_interruptible(&fcopy_transaction.read_sema))
|
||||
return -EINTR;
|
||||
|
||||
/*
|
||||
* The channel may be rescinded and in this case, we will wakeup the
|
||||
* the thread blocked on the semaphore and we will use the opened
|
||||
* state to correctly handle this case.
|
||||
*/
|
||||
if (!opened)
|
||||
return -ENODEV;
|
||||
|
||||
operation = fcopy_transaction.fcopy_msg->operation;
|
||||
|
||||
if (operation == START_FILE_COPY) {
|
||||
src = &fcopy_transaction.message;
|
||||
copy_size = sizeof(struct hv_start_fcopy);
|
||||
if (count < copy_size)
|
||||
return 0;
|
||||
} else {
|
||||
src = fcopy_transaction.fcopy_msg;
|
||||
copy_size = sizeof(struct hv_do_fcopy);
|
||||
if (count < copy_size)
|
||||
return 0;
|
||||
}
|
||||
if (copy_to_user(buf, src, copy_size))
|
||||
return -EFAULT;
|
||||
|
||||
return copy_size;
|
||||
}
|
||||
|
||||
static ssize_t fcopy_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int response = 0;
|
||||
|
||||
if (count != sizeof(int))
|
||||
if (len != sizeof(int))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&response, buf, sizeof(int)))
|
||||
return -EFAULT;
|
||||
if (fcopy_transaction.state == HVUTIL_DEVICE_INIT)
|
||||
return fcopy_handle_handshake(*val);
|
||||
|
||||
if (in_hand_shake) {
|
||||
if (fcopy_handle_handshake(response))
|
||||
return -EINVAL;
|
||||
return sizeof(int);
|
||||
}
|
||||
if (fcopy_transaction.state != HVUTIL_USERSPACE_REQ)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Complete the transaction by forwarding the result
|
||||
* to the host. But first, cancel the timeout.
|
||||
*/
|
||||
if (cancel_delayed_work_sync(&fcopy_work))
|
||||
fcopy_respond_to_host(response);
|
||||
if (cancel_delayed_work_sync(&fcopy_timeout_work)) {
|
||||
fcopy_transaction.state = HVUTIL_USERSPACE_RECV;
|
||||
fcopy_respond_to_host(*val);
|
||||
fcopy_transaction.state = HVUTIL_READY;
|
||||
hv_poll_channel(fcopy_transaction.fcopy_context,
|
||||
hv_fcopy_onchannelcallback);
|
||||
}
|
||||
|
||||
return sizeof(int);
|
||||
}
|
||||
|
||||
static int fcopy_open(struct inode *inode, struct file *f)
|
||||
{
|
||||
/*
|
||||
* The user level daemon that will open this device is
|
||||
* really an extension of this driver. We can have only
|
||||
* active open at a time.
|
||||
*/
|
||||
if (opened)
|
||||
return -EBUSY;
|
||||
|
||||
/*
|
||||
* The daemon is alive; setup the state.
|
||||
*/
|
||||
opened = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXX: there are still some tricky corner cases, e.g.,
|
||||
* 1) In a SMP guest, when fcopy_release() runs between
|
||||
* schedule_delayed_work() and fcopy_send_data(), there is
|
||||
* still a chance an obsolete message will be queued.
|
||||
*
|
||||
* 2) When the fcopy daemon is running, if we unload the driver,
|
||||
* we'll notice a kernel oops when we kill the daemon later.
|
||||
*/
|
||||
static int fcopy_release(struct inode *inode, struct file *f)
|
||||
static void fcopy_on_reset(void)
|
||||
{
|
||||
/*
|
||||
* The daemon has exited; reset the state.
|
||||
*/
|
||||
in_hand_shake = true;
|
||||
opened = false;
|
||||
fcopy_transaction.state = HVUTIL_DEVICE_INIT;
|
||||
|
||||
if (cancel_delayed_work_sync(&fcopy_work)) {
|
||||
/* We haven't up()-ed the semaphore(very rare)? */
|
||||
if (down_trylock(&fcopy_transaction.read_sema))
|
||||
;
|
||||
if (cancel_delayed_work_sync(&fcopy_timeout_work))
|
||||
fcopy_respond_to_host(HV_E_FAIL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct file_operations fcopy_fops = {
|
||||
.read = fcopy_read,
|
||||
.write = fcopy_write,
|
||||
.release = fcopy_release,
|
||||
.open = fcopy_open,
|
||||
};
|
||||
|
||||
static struct miscdevice fcopy_misc = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = "vmbus/hv_fcopy",
|
||||
.fops = &fcopy_fops,
|
||||
};
|
||||
|
||||
static int fcopy_dev_init(void)
|
||||
{
|
||||
return misc_register(&fcopy_misc);
|
||||
}
|
||||
|
||||
static void fcopy_dev_deinit(void)
|
||||
{
|
||||
|
||||
/*
|
||||
* The device is going away - perhaps because the
|
||||
* host has rescinded the channel. Setup state so that
|
||||
* user level daemon can gracefully exit if it is blocked
|
||||
* on the read semaphore.
|
||||
*/
|
||||
opened = false;
|
||||
/*
|
||||
* Signal the semaphore as the device is
|
||||
* going away.
|
||||
*/
|
||||
up(&fcopy_transaction.read_sema);
|
||||
misc_deregister(&fcopy_misc);
|
||||
}
|
||||
|
||||
int hv_fcopy_init(struct hv_util_service *srv)
|
||||
@ -428,14 +328,19 @@ int hv_fcopy_init(struct hv_util_service *srv)
|
||||
* Defer processing channel callbacks until the daemon
|
||||
* has registered.
|
||||
*/
|
||||
fcopy_transaction.active = true;
|
||||
sema_init(&fcopy_transaction.read_sema, 0);
|
||||
fcopy_transaction.state = HVUTIL_DEVICE_INIT;
|
||||
|
||||
return fcopy_dev_init();
|
||||
hvt = hvutil_transport_init(fcopy_devname, 0, 0,
|
||||
fcopy_on_msg, fcopy_on_reset);
|
||||
if (!hvt)
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hv_fcopy_deinit(void)
|
||||
{
|
||||
cancel_delayed_work_sync(&fcopy_work);
|
||||
fcopy_dev_deinit();
|
||||
fcopy_transaction.state = HVUTIL_DEVICE_DYING;
|
||||
cancel_delayed_work_sync(&fcopy_timeout_work);
|
||||
hvutil_transport_destroy(hvt);
|
||||
}
|
||||
|
@ -28,6 +28,8 @@
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/hyperv.h>
|
||||
|
||||
#include "hyperv_vmbus.h"
|
||||
#include "hv_utils_transport.h"
|
||||
|
||||
/*
|
||||
* Pre win8 version numbers used in ws2008 and ws 2008 r2 (win7)
|
||||
@ -45,16 +47,21 @@
|
||||
#define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
|
||||
|
||||
/*
|
||||
* Global state maintained for transaction that is being processed.
|
||||
* Note that only one transaction can be active at any point in time.
|
||||
* Global state maintained for transaction that is being processed. For a class
|
||||
* of integration services, including the "KVP service", the specified protocol
|
||||
* is a "request/response" protocol which means that there can only be single
|
||||
* outstanding transaction from the host at any given point in time. We use
|
||||
* this to simplify memory management in this driver - we cache and process
|
||||
* only one message at a time.
|
||||
*
|
||||
* This state is set when we receive a request from the host; we
|
||||
* cleanup this state when the transaction is completed - when we respond
|
||||
* to the host with the key value.
|
||||
* While the request/response protocol is guaranteed by the host, we further
|
||||
* ensure this by serializing packet processing in this driver - we do not
|
||||
* read additional packets from the VMBUs until the current packet is fully
|
||||
* handled.
|
||||
*/
|
||||
|
||||
static struct {
|
||||
bool active; /* transaction status - active or not */
|
||||
int state; /* hvutil_device_state */
|
||||
int recv_len; /* number of bytes received. */
|
||||
struct hv_kvp_msg *kvp_msg; /* current message */
|
||||
struct vmbus_channel *recv_channel; /* chn we got the request */
|
||||
@ -62,13 +69,6 @@ static struct {
|
||||
void *kvp_context; /* for the channel callback */
|
||||
} kvp_transaction;
|
||||
|
||||
/*
|
||||
* Before we can accept KVP messages from the host, we need
|
||||
* to handshake with the user level daemon. This state tracks
|
||||
* if we are in the handshake phase.
|
||||
*/
|
||||
static bool in_hand_shake = true;
|
||||
|
||||
/*
|
||||
* This state maintains the version number registered by the daemon.
|
||||
*/
|
||||
@ -78,15 +78,15 @@ static void kvp_send_key(struct work_struct *dummy);
|
||||
|
||||
|
||||
static void kvp_respond_to_host(struct hv_kvp_msg *msg, int error);
|
||||
static void kvp_work_func(struct work_struct *dummy);
|
||||
static void kvp_timeout_func(struct work_struct *dummy);
|
||||
static void kvp_register(int);
|
||||
|
||||
static DECLARE_DELAYED_WORK(kvp_work, kvp_work_func);
|
||||
static DECLARE_DELAYED_WORK(kvp_timeout_work, kvp_timeout_func);
|
||||
static DECLARE_WORK(kvp_sendkey_work, kvp_send_key);
|
||||
|
||||
static struct cb_id kvp_id = { CN_KVP_IDX, CN_KVP_VAL };
|
||||
static const char kvp_name[] = "kvp_kernel_module";
|
||||
static const char kvp_devname[] = "vmbus/hv_kvp";
|
||||
static u8 *recv_buffer;
|
||||
static struct hvutil_transport *hvt;
|
||||
/*
|
||||
* Register the kernel component with the user-level daemon.
|
||||
* As part of this registration, pass the LIC version number.
|
||||
@ -98,50 +98,39 @@ static void
|
||||
kvp_register(int reg_value)
|
||||
{
|
||||
|
||||
struct cn_msg *msg;
|
||||
struct hv_kvp_msg *kvp_msg;
|
||||
char *version;
|
||||
|
||||
msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg), GFP_ATOMIC);
|
||||
kvp_msg = kzalloc(sizeof(*kvp_msg), GFP_KERNEL);
|
||||
|
||||
if (msg) {
|
||||
kvp_msg = (struct hv_kvp_msg *)msg->data;
|
||||
if (kvp_msg) {
|
||||
version = kvp_msg->body.kvp_register.version;
|
||||
msg->id.idx = CN_KVP_IDX;
|
||||
msg->id.val = CN_KVP_VAL;
|
||||
|
||||
kvp_msg->kvp_hdr.operation = reg_value;
|
||||
strcpy(version, HV_DRV_VERSION);
|
||||
msg->len = sizeof(struct hv_kvp_msg);
|
||||
cn_netlink_send(msg, 0, 0, GFP_ATOMIC);
|
||||
kfree(msg);
|
||||
|
||||
hvutil_transport_send(hvt, kvp_msg, sizeof(*kvp_msg));
|
||||
kfree(kvp_msg);
|
||||
}
|
||||
}
|
||||
static void
|
||||
kvp_work_func(struct work_struct *dummy)
|
||||
|
||||
static void kvp_timeout_func(struct work_struct *dummy)
|
||||
{
|
||||
/*
|
||||
* If the timer fires, the user-mode component has not responded;
|
||||
* process the pending transaction.
|
||||
*/
|
||||
kvp_respond_to_host(NULL, HV_E_FAIL);
|
||||
}
|
||||
|
||||
static void poll_channel(struct vmbus_channel *channel)
|
||||
{
|
||||
if (channel->target_cpu != smp_processor_id())
|
||||
smp_call_function_single(channel->target_cpu,
|
||||
hv_kvp_onchannelcallback,
|
||||
channel, true);
|
||||
else
|
||||
hv_kvp_onchannelcallback(channel);
|
||||
}
|
||||
/* Transaction is finished, reset the state. */
|
||||
if (kvp_transaction.state > HVUTIL_READY)
|
||||
kvp_transaction.state = HVUTIL_READY;
|
||||
|
||||
hv_poll_channel(kvp_transaction.kvp_context,
|
||||
hv_kvp_onchannelcallback);
|
||||
}
|
||||
|
||||
static int kvp_handle_handshake(struct hv_kvp_msg *msg)
|
||||
{
|
||||
int ret = 1;
|
||||
|
||||
switch (msg->kvp_hdr.operation) {
|
||||
case KVP_OP_REGISTER:
|
||||
dm_reg_value = KVP_OP_REGISTER;
|
||||
@ -155,20 +144,18 @@ static int kvp_handle_handshake(struct hv_kvp_msg *msg)
|
||||
pr_info("KVP: incompatible daemon\n");
|
||||
pr_info("KVP: KVP version: %d, Daemon version: %d\n",
|
||||
KVP_OP_REGISTER1, msg->kvp_hdr.operation);
|
||||
ret = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
/*
|
||||
* We have a compatible daemon; complete the handshake.
|
||||
*/
|
||||
pr_info("KVP: user-mode registering done.\n");
|
||||
kvp_register(dm_reg_value);
|
||||
kvp_transaction.active = false;
|
||||
if (kvp_transaction.kvp_context)
|
||||
poll_channel(kvp_transaction.kvp_context);
|
||||
}
|
||||
return ret;
|
||||
/*
|
||||
* We have a compatible daemon; complete the handshake.
|
||||
*/
|
||||
pr_debug("KVP: userspace daemon ver. %d registered\n",
|
||||
KVP_OP_REGISTER);
|
||||
kvp_register(dm_reg_value);
|
||||
kvp_transaction.state = HVUTIL_READY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -176,26 +163,30 @@ static int kvp_handle_handshake(struct hv_kvp_msg *msg)
|
||||
* Callback when data is received from user mode.
|
||||
*/
|
||||
|
||||
static void
|
||||
kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
|
||||
static int kvp_on_msg(void *msg, int len)
|
||||
{
|
||||
struct hv_kvp_msg *message;
|
||||
struct hv_kvp_msg *message = (struct hv_kvp_msg *)msg;
|
||||
struct hv_kvp_msg_enumerate *data;
|
||||
int error = 0;
|
||||
|
||||
message = (struct hv_kvp_msg *)msg->data;
|
||||
if (len < sizeof(*message))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* If we are negotiating the version information
|
||||
* with the daemon; handle that first.
|
||||
*/
|
||||
|
||||
if (in_hand_shake) {
|
||||
if (kvp_handle_handshake(message))
|
||||
in_hand_shake = false;
|
||||
return;
|
||||
if (kvp_transaction.state < HVUTIL_READY) {
|
||||
return kvp_handle_handshake(message);
|
||||
}
|
||||
|
||||
/* We didn't send anything to userspace so the reply is spurious */
|
||||
if (kvp_transaction.state < HVUTIL_USERSPACE_REQ)
|
||||
return -EINVAL;
|
||||
|
||||
kvp_transaction.state = HVUTIL_USERSPACE_RECV;
|
||||
|
||||
/*
|
||||
* Based on the version of the daemon, we propagate errors from the
|
||||
* daemon differently.
|
||||
@ -225,8 +216,14 @@ kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
|
||||
* Complete the transaction by forwarding the key value
|
||||
* to the host. But first, cancel the timeout.
|
||||
*/
|
||||
if (cancel_delayed_work_sync(&kvp_work))
|
||||
if (cancel_delayed_work_sync(&kvp_timeout_work)) {
|
||||
kvp_respond_to_host(message, error);
|
||||
kvp_transaction.state = HVUTIL_READY;
|
||||
hv_poll_channel(kvp_transaction.kvp_context,
|
||||
hv_kvp_onchannelcallback);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -343,7 +340,6 @@ static void process_ib_ipinfo(void *in_msg, void *out_msg, int op)
|
||||
static void
|
||||
kvp_send_key(struct work_struct *dummy)
|
||||
{
|
||||
struct cn_msg *msg;
|
||||
struct hv_kvp_msg *message;
|
||||
struct hv_kvp_msg *in_msg;
|
||||
__u8 operation = kvp_transaction.kvp_msg->kvp_hdr.operation;
|
||||
@ -352,14 +348,11 @@ kvp_send_key(struct work_struct *dummy)
|
||||
__u64 val64;
|
||||
int rc;
|
||||
|
||||
msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg) , GFP_ATOMIC);
|
||||
if (!msg)
|
||||
/* The transaction state is wrong. */
|
||||
if (kvp_transaction.state != HVUTIL_HOSTMSG_RECEIVED)
|
||||
return;
|
||||
|
||||
msg->id.idx = CN_KVP_IDX;
|
||||
msg->id.val = CN_KVP_VAL;
|
||||
|
||||
message = (struct hv_kvp_msg *)msg->data;
|
||||
message = kzalloc(sizeof(*message), GFP_KERNEL);
|
||||
message->kvp_hdr.operation = operation;
|
||||
message->kvp_hdr.pool = pool;
|
||||
in_msg = kvp_transaction.kvp_msg;
|
||||
@ -446,15 +439,17 @@ kvp_send_key(struct work_struct *dummy)
|
||||
break;
|
||||
}
|
||||
|
||||
msg->len = sizeof(struct hv_kvp_msg);
|
||||
rc = cn_netlink_send(msg, 0, 0, GFP_ATOMIC);
|
||||
kvp_transaction.state = HVUTIL_USERSPACE_REQ;
|
||||
rc = hvutil_transport_send(hvt, message, sizeof(*message));
|
||||
if (rc) {
|
||||
pr_debug("KVP: failed to communicate to the daemon: %d\n", rc);
|
||||
if (cancel_delayed_work_sync(&kvp_work))
|
||||
if (cancel_delayed_work_sync(&kvp_timeout_work)) {
|
||||
kvp_respond_to_host(message, HV_E_FAIL);
|
||||
kvp_transaction.state = HVUTIL_READY;
|
||||
}
|
||||
}
|
||||
|
||||
kfree(msg);
|
||||
kfree(message);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -478,17 +473,6 @@ kvp_respond_to_host(struct hv_kvp_msg *msg_to_host, int error)
|
||||
u64 req_id;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* If a transaction is not active; log and return.
|
||||
*/
|
||||
|
||||
if (!kvp_transaction.active) {
|
||||
/*
|
||||
* This is a spurious call!
|
||||
*/
|
||||
pr_warn("KVP: Transaction not active\n");
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Copy the global state for completing the transaction. Note that
|
||||
* only one transaction can be active at a time.
|
||||
@ -498,8 +482,6 @@ kvp_respond_to_host(struct hv_kvp_msg *msg_to_host, int error)
|
||||
channel = kvp_transaction.recv_channel;
|
||||
req_id = kvp_transaction.recv_req_id;
|
||||
|
||||
kvp_transaction.active = false;
|
||||
|
||||
icmsghdrp = (struct icmsg_hdr *)
|
||||
&recv_buffer[sizeof(struct vmbuspipe_hdr)];
|
||||
|
||||
@ -586,7 +568,6 @@ response_done:
|
||||
|
||||
vmbus_sendpacket(channel, recv_buffer, buf_len, req_id,
|
||||
VM_PKT_DATA_INBAND, 0);
|
||||
poll_channel(channel);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -612,7 +593,7 @@ void hv_kvp_onchannelcallback(void *context)
|
||||
int util_fw_version;
|
||||
int kvp_srv_version;
|
||||
|
||||
if (kvp_transaction.active) {
|
||||
if (kvp_transaction.state > HVUTIL_READY) {
|
||||
/*
|
||||
* We will defer processing this callback once
|
||||
* the current transaction is complete.
|
||||
@ -620,6 +601,7 @@ void hv_kvp_onchannelcallback(void *context)
|
||||
kvp_transaction.kvp_context = context;
|
||||
return;
|
||||
}
|
||||
kvp_transaction.kvp_context = NULL;
|
||||
|
||||
vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 4, &recvlen,
|
||||
&requestid);
|
||||
@ -664,9 +646,15 @@ void hv_kvp_onchannelcallback(void *context)
|
||||
kvp_transaction.recv_len = recvlen;
|
||||
kvp_transaction.recv_channel = channel;
|
||||
kvp_transaction.recv_req_id = requestid;
|
||||
kvp_transaction.active = true;
|
||||
kvp_transaction.kvp_msg = kvp_msg;
|
||||
|
||||
if (kvp_transaction.state < HVUTIL_READY) {
|
||||
/* Userspace is not registered yet */
|
||||
kvp_respond_to_host(NULL, HV_E_FAIL);
|
||||
return;
|
||||
}
|
||||
kvp_transaction.state = HVUTIL_HOSTMSG_RECEIVED;
|
||||
|
||||
/*
|
||||
* Get the information from the
|
||||
* user-mode component.
|
||||
@ -677,7 +665,7 @@ void hv_kvp_onchannelcallback(void *context)
|
||||
* user-mode not responding.
|
||||
*/
|
||||
schedule_work(&kvp_sendkey_work);
|
||||
schedule_delayed_work(&kvp_work, 5*HZ);
|
||||
schedule_delayed_work(&kvp_timeout_work, 5*HZ);
|
||||
|
||||
return;
|
||||
|
||||
@ -693,14 +681,16 @@ void hv_kvp_onchannelcallback(void *context)
|
||||
|
||||
}
|
||||
|
||||
static void kvp_on_reset(void)
|
||||
{
|
||||
if (cancel_delayed_work_sync(&kvp_timeout_work))
|
||||
kvp_respond_to_host(NULL, HV_E_FAIL);
|
||||
kvp_transaction.state = HVUTIL_DEVICE_INIT;
|
||||
}
|
||||
|
||||
int
|
||||
hv_kvp_init(struct hv_util_service *srv)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = cn_add_callback(&kvp_id, kvp_name, kvp_cn_callback);
|
||||
if (err)
|
||||
return err;
|
||||
recv_buffer = srv->recv_buffer;
|
||||
|
||||
/*
|
||||
@ -709,14 +699,20 @@ hv_kvp_init(struct hv_util_service *srv)
|
||||
* Defer processing channel callbacks until the daemon
|
||||
* has registered.
|
||||
*/
|
||||
kvp_transaction.active = true;
|
||||
kvp_transaction.state = HVUTIL_DEVICE_INIT;
|
||||
|
||||
hvt = hvutil_transport_init(kvp_devname, CN_KVP_IDX, CN_KVP_VAL,
|
||||
kvp_on_msg, kvp_on_reset);
|
||||
if (!hvt)
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hv_kvp_deinit(void)
|
||||
{
|
||||
cn_del_callback(&kvp_id);
|
||||
cancel_delayed_work_sync(&kvp_work);
|
||||
kvp_transaction.state = HVUTIL_DEVICE_DYING;
|
||||
cancel_delayed_work_sync(&kvp_timeout_work);
|
||||
cancel_work_sync(&kvp_sendkey_work);
|
||||
hvutil_transport_destroy(hvt);
|
||||
}
|
||||
|
@ -24,6 +24,9 @@
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/hyperv.h>
|
||||
|
||||
#include "hyperv_vmbus.h"
|
||||
#include "hv_utils_transport.h"
|
||||
|
||||
#define VSS_MAJOR 5
|
||||
#define VSS_MINOR 0
|
||||
#define VSS_VERSION (VSS_MAJOR << 16 | VSS_MINOR)
|
||||
@ -31,28 +34,39 @@
|
||||
#define VSS_USERSPACE_TIMEOUT (msecs_to_jiffies(10 * 1000))
|
||||
|
||||
/*
|
||||
* Global state maintained for transaction that is being processed.
|
||||
* Note that only one transaction can be active at any point in time.
|
||||
* Global state maintained for transaction that is being processed. For a class
|
||||
* of integration services, including the "VSS service", the specified protocol
|
||||
* is a "request/response" protocol which means that there can only be single
|
||||
* outstanding transaction from the host at any given point in time. We use
|
||||
* this to simplify memory management in this driver - we cache and process
|
||||
* only one message at a time.
|
||||
*
|
||||
* This state is set when we receive a request from the host; we
|
||||
* cleanup this state when the transaction is completed - when we respond
|
||||
* to the host with the key value.
|
||||
* While the request/response protocol is guaranteed by the host, we further
|
||||
* ensure this by serializing packet processing in this driver - we do not
|
||||
* read additional packets from the VMBUs until the current packet is fully
|
||||
* handled.
|
||||
*/
|
||||
|
||||
static struct {
|
||||
bool active; /* transaction status - active or not */
|
||||
int state; /* hvutil_device_state */
|
||||
int recv_len; /* number of bytes received. */
|
||||
struct vmbus_channel *recv_channel; /* chn we got the request */
|
||||
u64 recv_req_id; /* request ID. */
|
||||
struct hv_vss_msg *msg; /* current message */
|
||||
void *vss_context; /* for the channel callback */
|
||||
} vss_transaction;
|
||||
|
||||
|
||||
static void vss_respond_to_host(int error);
|
||||
|
||||
static struct cb_id vss_id = { CN_VSS_IDX, CN_VSS_VAL };
|
||||
static const char vss_name[] = "vss_kernel_module";
|
||||
/*
|
||||
* This state maintains the version number registered by the daemon.
|
||||
*/
|
||||
static int dm_reg_value;
|
||||
|
||||
static const char vss_devname[] = "vmbus/hv_vss";
|
||||
static __u8 *recv_buffer;
|
||||
static struct hvutil_transport *hvt;
|
||||
|
||||
static void vss_send_op(struct work_struct *dummy);
|
||||
static void vss_timeout_func(struct work_struct *dummy);
|
||||
@ -71,25 +85,69 @@ static void vss_timeout_func(struct work_struct *dummy)
|
||||
*/
|
||||
pr_warn("VSS: timeout waiting for daemon to reply\n");
|
||||
vss_respond_to_host(HV_E_FAIL);
|
||||
|
||||
/* Transaction is finished, reset the state. */
|
||||
if (vss_transaction.state > HVUTIL_READY)
|
||||
vss_transaction.state = HVUTIL_READY;
|
||||
|
||||
hv_poll_channel(vss_transaction.vss_context,
|
||||
hv_vss_onchannelcallback);
|
||||
}
|
||||
|
||||
static void
|
||||
vss_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
|
||||
static int vss_handle_handshake(struct hv_vss_msg *vss_msg)
|
||||
{
|
||||
struct hv_vss_msg *vss_msg;
|
||||
|
||||
vss_msg = (struct hv_vss_msg *)msg->data;
|
||||
|
||||
if (vss_msg->vss_hdr.operation == VSS_OP_REGISTER) {
|
||||
pr_info("VSS daemon registered\n");
|
||||
vss_transaction.active = false;
|
||||
if (vss_transaction.recv_channel != NULL)
|
||||
hv_vss_onchannelcallback(vss_transaction.recv_channel);
|
||||
return;
|
||||
u32 our_ver = VSS_OP_REGISTER1;
|
||||
|
||||
switch (vss_msg->vss_hdr.operation) {
|
||||
case VSS_OP_REGISTER:
|
||||
/* Daemon doesn't expect us to reply */
|
||||
dm_reg_value = VSS_OP_REGISTER;
|
||||
break;
|
||||
case VSS_OP_REGISTER1:
|
||||
/* Daemon expects us to reply with our own version*/
|
||||
if (hvutil_transport_send(hvt, &our_ver, sizeof(our_ver)))
|
||||
return -EFAULT;
|
||||
dm_reg_value = VSS_OP_REGISTER1;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (cancel_delayed_work_sync(&vss_timeout_work))
|
||||
vss_respond_to_host(vss_msg->error);
|
||||
vss_transaction.state = HVUTIL_READY;
|
||||
pr_debug("VSS: userspace daemon ver. %d registered\n", dm_reg_value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vss_on_msg(void *msg, int len)
|
||||
{
|
||||
struct hv_vss_msg *vss_msg = (struct hv_vss_msg *)msg;
|
||||
|
||||
if (len != sizeof(*vss_msg))
|
||||
return -EINVAL;
|
||||
|
||||
if (vss_msg->vss_hdr.operation == VSS_OP_REGISTER ||
|
||||
vss_msg->vss_hdr.operation == VSS_OP_REGISTER1) {
|
||||
/*
|
||||
* Don't process registration messages if we're in the middle
|
||||
* of a transaction processing.
|
||||
*/
|
||||
if (vss_transaction.state > HVUTIL_READY)
|
||||
return -EINVAL;
|
||||
return vss_handle_handshake(vss_msg);
|
||||
} else if (vss_transaction.state == HVUTIL_USERSPACE_REQ) {
|
||||
vss_transaction.state = HVUTIL_USERSPACE_RECV;
|
||||
if (cancel_delayed_work_sync(&vss_timeout_work)) {
|
||||
vss_respond_to_host(vss_msg->error);
|
||||
/* Transaction is finished, reset the state. */
|
||||
vss_transaction.state = HVUTIL_READY;
|
||||
hv_poll_channel(vss_transaction.vss_context,
|
||||
hv_vss_onchannelcallback);
|
||||
}
|
||||
} else {
|
||||
/* This is a spurious call! */
|
||||
pr_warn("VSS: Transaction not active\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -97,28 +155,29 @@ static void vss_send_op(struct work_struct *dummy)
|
||||
{
|
||||
int op = vss_transaction.msg->vss_hdr.operation;
|
||||
int rc;
|
||||
struct cn_msg *msg;
|
||||
struct hv_vss_msg *vss_msg;
|
||||
|
||||
msg = kzalloc(sizeof(*msg) + sizeof(*vss_msg), GFP_ATOMIC);
|
||||
if (!msg)
|
||||
/* The transaction state is wrong. */
|
||||
if (vss_transaction.state != HVUTIL_HOSTMSG_RECEIVED)
|
||||
return;
|
||||
|
||||
vss_msg = (struct hv_vss_msg *)msg->data;
|
||||
|
||||
msg->id.idx = CN_VSS_IDX;
|
||||
msg->id.val = CN_VSS_VAL;
|
||||
vss_msg = kzalloc(sizeof(*vss_msg), GFP_KERNEL);
|
||||
if (!vss_msg)
|
||||
return;
|
||||
|
||||
vss_msg->vss_hdr.operation = op;
|
||||
msg->len = sizeof(struct hv_vss_msg);
|
||||
|
||||
rc = cn_netlink_send(msg, 0, 0, GFP_ATOMIC);
|
||||
vss_transaction.state = HVUTIL_USERSPACE_REQ;
|
||||
rc = hvutil_transport_send(hvt, vss_msg, sizeof(*vss_msg));
|
||||
if (rc) {
|
||||
pr_warn("VSS: failed to communicate to the daemon: %d\n", rc);
|
||||
if (cancel_delayed_work_sync(&vss_timeout_work))
|
||||
if (cancel_delayed_work_sync(&vss_timeout_work)) {
|
||||
vss_respond_to_host(HV_E_FAIL);
|
||||
vss_transaction.state = HVUTIL_READY;
|
||||
}
|
||||
}
|
||||
kfree(msg);
|
||||
|
||||
kfree(vss_msg);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -135,17 +194,6 @@ vss_respond_to_host(int error)
|
||||
struct vmbus_channel *channel;
|
||||
u64 req_id;
|
||||
|
||||
/*
|
||||
* If a transaction is not active; log and return.
|
||||
*/
|
||||
|
||||
if (!vss_transaction.active) {
|
||||
/*
|
||||
* This is a spurious call!
|
||||
*/
|
||||
pr_warn("VSS: Transaction not active\n");
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Copy the global state for completing the transaction. Note that
|
||||
* only one transaction can be active at a time.
|
||||
@ -154,7 +202,6 @@ vss_respond_to_host(int error)
|
||||
buf_len = vss_transaction.recv_len;
|
||||
channel = vss_transaction.recv_channel;
|
||||
req_id = vss_transaction.recv_req_id;
|
||||
vss_transaction.active = false;
|
||||
|
||||
icmsghdrp = (struct icmsg_hdr *)
|
||||
&recv_buffer[sizeof(struct vmbuspipe_hdr)];
|
||||
@ -191,14 +238,15 @@ void hv_vss_onchannelcallback(void *context)
|
||||
struct icmsg_hdr *icmsghdrp;
|
||||
struct icmsg_negotiate *negop = NULL;
|
||||
|
||||
if (vss_transaction.active) {
|
||||
if (vss_transaction.state > HVUTIL_READY) {
|
||||
/*
|
||||
* We will defer processing this callback once
|
||||
* the current transaction is complete.
|
||||
*/
|
||||
vss_transaction.recv_channel = channel;
|
||||
vss_transaction.vss_context = context;
|
||||
return;
|
||||
}
|
||||
vss_transaction.vss_context = NULL;
|
||||
|
||||
vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen,
|
||||
&requestid);
|
||||
@ -224,7 +272,6 @@ void hv_vss_onchannelcallback(void *context)
|
||||
vss_transaction.recv_len = recvlen;
|
||||
vss_transaction.recv_channel = channel;
|
||||
vss_transaction.recv_req_id = requestid;
|
||||
vss_transaction.active = true;
|
||||
vss_transaction.msg = (struct hv_vss_msg *)vss_msg;
|
||||
|
||||
switch (vss_msg->vss_hdr.operation) {
|
||||
@ -241,6 +288,12 @@ void hv_vss_onchannelcallback(void *context)
|
||||
*/
|
||||
case VSS_OP_FREEZE:
|
||||
case VSS_OP_THAW:
|
||||
if (vss_transaction.state < HVUTIL_READY) {
|
||||
/* Userspace is not registered yet */
|
||||
vss_respond_to_host(HV_E_FAIL);
|
||||
return;
|
||||
}
|
||||
vss_transaction.state = HVUTIL_HOSTMSG_RECEIVED;
|
||||
schedule_work(&vss_send_op_work);
|
||||
schedule_delayed_work(&vss_timeout_work,
|
||||
VSS_USERSPACE_TIMEOUT);
|
||||
@ -275,14 +328,16 @@ void hv_vss_onchannelcallback(void *context)
|
||||
|
||||
}
|
||||
|
||||
static void vss_on_reset(void)
|
||||
{
|
||||
if (cancel_delayed_work_sync(&vss_timeout_work))
|
||||
vss_respond_to_host(HV_E_FAIL);
|
||||
vss_transaction.state = HVUTIL_DEVICE_INIT;
|
||||
}
|
||||
|
||||
int
|
||||
hv_vss_init(struct hv_util_service *srv)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = cn_add_callback(&vss_id, vss_name, vss_cn_callback);
|
||||
if (err)
|
||||
return err;
|
||||
recv_buffer = srv->recv_buffer;
|
||||
|
||||
/*
|
||||
@ -291,13 +346,20 @@ hv_vss_init(struct hv_util_service *srv)
|
||||
* Defer processing channel callbacks until the daemon
|
||||
* has registered.
|
||||
*/
|
||||
vss_transaction.active = true;
|
||||
vss_transaction.state = HVUTIL_DEVICE_INIT;
|
||||
|
||||
hvt = hvutil_transport_init(vss_devname, CN_VSS_IDX, CN_VSS_VAL,
|
||||
vss_on_msg, vss_on_reset);
|
||||
if (!hvt)
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hv_vss_deinit(void)
|
||||
{
|
||||
cn_del_callback(&vss_id);
|
||||
vss_transaction.state = HVUTIL_DEVICE_DYING;
|
||||
cancel_delayed_work_sync(&vss_timeout_work);
|
||||
cancel_work_sync(&vss_send_op_work);
|
||||
hvutil_transport_destroy(hvt);
|
||||
}
|
||||
|
276
drivers/hv/hv_utils_transport.c
Normal file
276
drivers/hv/hv_utils_transport.c
Normal file
@ -0,0 +1,276 @@
|
||||
/*
|
||||
* Kernel/userspace transport abstraction for Hyper-V util driver.
|
||||
*
|
||||
* Copyright (C) 2015, Vitaly Kuznetsov <vkuznets@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
* 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, GOOD TITLE or
|
||||
* NON INFRINGEMENT. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/poll.h>
|
||||
|
||||
#include "hyperv_vmbus.h"
|
||||
#include "hv_utils_transport.h"
|
||||
|
||||
static DEFINE_SPINLOCK(hvt_list_lock);
|
||||
static struct list_head hvt_list = LIST_HEAD_INIT(hvt_list);
|
||||
|
||||
static void hvt_reset(struct hvutil_transport *hvt)
|
||||
{
|
||||
mutex_lock(&hvt->outmsg_lock);
|
||||
kfree(hvt->outmsg);
|
||||
hvt->outmsg = NULL;
|
||||
hvt->outmsg_len = 0;
|
||||
mutex_unlock(&hvt->outmsg_lock);
|
||||
if (hvt->on_reset)
|
||||
hvt->on_reset();
|
||||
}
|
||||
|
||||
static ssize_t hvt_op_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hvutil_transport *hvt;
|
||||
int ret;
|
||||
|
||||
hvt = container_of(file->f_op, struct hvutil_transport, fops);
|
||||
|
||||
if (wait_event_interruptible(hvt->outmsg_q, hvt->outmsg_len > 0))
|
||||
return -EINTR;
|
||||
|
||||
mutex_lock(&hvt->outmsg_lock);
|
||||
if (!hvt->outmsg) {
|
||||
ret = -EAGAIN;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (count < hvt->outmsg_len) {
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (!copy_to_user(buf, hvt->outmsg, hvt->outmsg_len))
|
||||
ret = hvt->outmsg_len;
|
||||
else
|
||||
ret = -EFAULT;
|
||||
|
||||
kfree(hvt->outmsg);
|
||||
hvt->outmsg = NULL;
|
||||
hvt->outmsg_len = 0;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&hvt->outmsg_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t hvt_op_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hvutil_transport *hvt;
|
||||
u8 *inmsg;
|
||||
|
||||
hvt = container_of(file->f_op, struct hvutil_transport, fops);
|
||||
|
||||
inmsg = kzalloc(count, GFP_KERNEL);
|
||||
if (copy_from_user(inmsg, buf, count)) {
|
||||
kfree(inmsg);
|
||||
return -EFAULT;
|
||||
}
|
||||
if (hvt->on_msg(inmsg, count))
|
||||
return -EFAULT;
|
||||
kfree(inmsg);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static unsigned int hvt_op_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct hvutil_transport *hvt;
|
||||
|
||||
hvt = container_of(file->f_op, struct hvutil_transport, fops);
|
||||
|
||||
poll_wait(file, &hvt->outmsg_q, wait);
|
||||
if (hvt->outmsg_len > 0)
|
||||
return POLLIN | POLLRDNORM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hvt_op_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct hvutil_transport *hvt;
|
||||
|
||||
hvt = container_of(file->f_op, struct hvutil_transport, fops);
|
||||
|
||||
/*
|
||||
* Switching to CHARDEV mode. We switch bach to INIT when device
|
||||
* gets released.
|
||||
*/
|
||||
if (hvt->mode == HVUTIL_TRANSPORT_INIT)
|
||||
hvt->mode = HVUTIL_TRANSPORT_CHARDEV;
|
||||
else if (hvt->mode == HVUTIL_TRANSPORT_NETLINK) {
|
||||
/*
|
||||
* We're switching from netlink communication to using char
|
||||
* device. Issue the reset first.
|
||||
*/
|
||||
hvt_reset(hvt);
|
||||
hvt->mode = HVUTIL_TRANSPORT_CHARDEV;
|
||||
} else
|
||||
return -EBUSY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hvt_op_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct hvutil_transport *hvt;
|
||||
|
||||
hvt = container_of(file->f_op, struct hvutil_transport, fops);
|
||||
|
||||
hvt->mode = HVUTIL_TRANSPORT_INIT;
|
||||
/*
|
||||
* Cleanup message buffers to avoid spurious messages when the daemon
|
||||
* connects back.
|
||||
*/
|
||||
hvt_reset(hvt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hvt_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
|
||||
{
|
||||
struct hvutil_transport *hvt, *hvt_found = NULL;
|
||||
|
||||
spin_lock(&hvt_list_lock);
|
||||
list_for_each_entry(hvt, &hvt_list, list) {
|
||||
if (hvt->cn_id.idx == msg->id.idx &&
|
||||
hvt->cn_id.val == msg->id.val) {
|
||||
hvt_found = hvt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock(&hvt_list_lock);
|
||||
if (!hvt_found) {
|
||||
pr_warn("hvt_cn_callback: spurious message received!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Switching to NETLINK mode. Switching to CHARDEV happens when someone
|
||||
* opens the device.
|
||||
*/
|
||||
if (hvt->mode == HVUTIL_TRANSPORT_INIT)
|
||||
hvt->mode = HVUTIL_TRANSPORT_NETLINK;
|
||||
|
||||
if (hvt->mode == HVUTIL_TRANSPORT_NETLINK)
|
||||
hvt_found->on_msg(msg->data, msg->len);
|
||||
else
|
||||
pr_warn("hvt_cn_callback: unexpected netlink message!\n");
|
||||
}
|
||||
|
||||
int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len)
|
||||
{
|
||||
struct cn_msg *cn_msg;
|
||||
int ret = 0;
|
||||
|
||||
if (hvt->mode == HVUTIL_TRANSPORT_INIT) {
|
||||
return -EINVAL;
|
||||
} else if (hvt->mode == HVUTIL_TRANSPORT_NETLINK) {
|
||||
cn_msg = kzalloc(sizeof(*cn_msg) + len, GFP_ATOMIC);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
cn_msg->id.idx = hvt->cn_id.idx;
|
||||
cn_msg->id.val = hvt->cn_id.val;
|
||||
cn_msg->len = len;
|
||||
memcpy(cn_msg->data, msg, len);
|
||||
ret = cn_netlink_send(cn_msg, 0, 0, GFP_ATOMIC);
|
||||
kfree(cn_msg);
|
||||
return ret;
|
||||
}
|
||||
/* HVUTIL_TRANSPORT_CHARDEV */
|
||||
mutex_lock(&hvt->outmsg_lock);
|
||||
if (hvt->outmsg) {
|
||||
/* Previous message wasn't received */
|
||||
ret = -EFAULT;
|
||||
goto out_unlock;
|
||||
}
|
||||
hvt->outmsg = kzalloc(len, GFP_KERNEL);
|
||||
memcpy(hvt->outmsg, msg, len);
|
||||
hvt->outmsg_len = len;
|
||||
wake_up_interruptible(&hvt->outmsg_q);
|
||||
out_unlock:
|
||||
mutex_unlock(&hvt->outmsg_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct hvutil_transport *hvutil_transport_init(const char *name,
|
||||
u32 cn_idx, u32 cn_val,
|
||||
int (*on_msg)(void *, int),
|
||||
void (*on_reset)(void))
|
||||
{
|
||||
struct hvutil_transport *hvt;
|
||||
|
||||
hvt = kzalloc(sizeof(*hvt), GFP_KERNEL);
|
||||
if (!hvt)
|
||||
return NULL;
|
||||
|
||||
hvt->cn_id.idx = cn_idx;
|
||||
hvt->cn_id.val = cn_val;
|
||||
|
||||
hvt->mdev.minor = MISC_DYNAMIC_MINOR;
|
||||
hvt->mdev.name = name;
|
||||
|
||||
hvt->fops.owner = THIS_MODULE;
|
||||
hvt->fops.read = hvt_op_read;
|
||||
hvt->fops.write = hvt_op_write;
|
||||
hvt->fops.poll = hvt_op_poll;
|
||||
hvt->fops.open = hvt_op_open;
|
||||
hvt->fops.release = hvt_op_release;
|
||||
|
||||
hvt->mdev.fops = &hvt->fops;
|
||||
|
||||
init_waitqueue_head(&hvt->outmsg_q);
|
||||
mutex_init(&hvt->outmsg_lock);
|
||||
|
||||
spin_lock(&hvt_list_lock);
|
||||
list_add(&hvt->list, &hvt_list);
|
||||
spin_unlock(&hvt_list_lock);
|
||||
|
||||
hvt->on_msg = on_msg;
|
||||
hvt->on_reset = on_reset;
|
||||
|
||||
if (misc_register(&hvt->mdev))
|
||||
goto err_free_hvt;
|
||||
|
||||
/* Use cn_id.idx/cn_id.val to determine if we need to setup netlink */
|
||||
if (hvt->cn_id.idx > 0 && hvt->cn_id.val > 0 &&
|
||||
cn_add_callback(&hvt->cn_id, name, hvt_cn_callback))
|
||||
goto err_free_hvt;
|
||||
|
||||
return hvt;
|
||||
|
||||
err_free_hvt:
|
||||
kfree(hvt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void hvutil_transport_destroy(struct hvutil_transport *hvt)
|
||||
{
|
||||
spin_lock(&hvt_list_lock);
|
||||
list_del(&hvt->list);
|
||||
spin_unlock(&hvt_list_lock);
|
||||
if (hvt->cn_id.idx > 0 && hvt->cn_id.val > 0)
|
||||
cn_del_callback(&hvt->cn_id);
|
||||
misc_deregister(&hvt->mdev);
|
||||
kfree(hvt->outmsg);
|
||||
kfree(hvt);
|
||||
}
|
51
drivers/hv/hv_utils_transport.h
Normal file
51
drivers/hv/hv_utils_transport.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Kernel/userspace transport abstraction for Hyper-V util driver.
|
||||
*
|
||||
* Copyright (C) 2015, Vitaly Kuznetsov <vkuznets@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
* 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, GOOD TITLE or
|
||||
* NON INFRINGEMENT. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _HV_UTILS_TRANSPORT_H
|
||||
#define _HV_UTILS_TRANSPORT_H
|
||||
|
||||
#include <linux/connector.h>
|
||||
#include <linux/miscdevice.h>
|
||||
|
||||
enum hvutil_transport_mode {
|
||||
HVUTIL_TRANSPORT_INIT = 0,
|
||||
HVUTIL_TRANSPORT_NETLINK,
|
||||
HVUTIL_TRANSPORT_CHARDEV,
|
||||
};
|
||||
|
||||
struct hvutil_transport {
|
||||
int mode; /* hvutil_transport_mode */
|
||||
struct file_operations fops; /* file operations */
|
||||
struct miscdevice mdev; /* misc device */
|
||||
struct cb_id cn_id; /* CN_*_IDX/CN_*_VAL */
|
||||
struct list_head list; /* hvt_list */
|
||||
int (*on_msg)(void *, int); /* callback on new user message */
|
||||
void (*on_reset)(void); /* callback when userspace drops */
|
||||
u8 *outmsg; /* message to the userspace */
|
||||
int outmsg_len; /* its length */
|
||||
wait_queue_head_t outmsg_q; /* poll/read wait queue */
|
||||
struct mutex outmsg_lock; /* protects outmsg */
|
||||
};
|
||||
|
||||
struct hvutil_transport *hvutil_transport_init(const char *name,
|
||||
u32 cn_idx, u32 cn_val,
|
||||
int (*on_msg)(void *, int),
|
||||
void (*on_reset)(void));
|
||||
int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len);
|
||||
void hvutil_transport_destroy(struct hvutil_transport *hvt);
|
||||
|
||||
#endif /* _HV_UTILS_TRANSPORT_H */
|
@ -647,6 +647,7 @@ struct vmbus_connection {
|
||||
|
||||
atomic_t next_gpadl_handle;
|
||||
|
||||
struct completion unload_event;
|
||||
/*
|
||||
* Represents channel interrupts. Each bit position represents a
|
||||
* channel. When a channel sends an interrupt via VMBUS, it finds its
|
||||
@ -730,9 +731,39 @@ int vmbus_set_event(struct vmbus_channel *channel);
|
||||
|
||||
void vmbus_on_event(unsigned long data);
|
||||
|
||||
int hv_kvp_init(struct hv_util_service *);
|
||||
void hv_kvp_deinit(void);
|
||||
void hv_kvp_onchannelcallback(void *);
|
||||
|
||||
int hv_vss_init(struct hv_util_service *);
|
||||
void hv_vss_deinit(void);
|
||||
void hv_vss_onchannelcallback(void *);
|
||||
|
||||
int hv_fcopy_init(struct hv_util_service *);
|
||||
void hv_fcopy_deinit(void);
|
||||
void hv_fcopy_onchannelcallback(void *);
|
||||
void vmbus_initiate_unload(void);
|
||||
|
||||
static inline void hv_poll_channel(struct vmbus_channel *channel,
|
||||
void (*cb)(void *))
|
||||
{
|
||||
if (!channel)
|
||||
return;
|
||||
|
||||
if (channel->target_cpu != smp_processor_id())
|
||||
smp_call_function_single(channel->target_cpu,
|
||||
cb, channel, true);
|
||||
else
|
||||
cb(channel);
|
||||
}
|
||||
|
||||
enum hvutil_device_state {
|
||||
HVUTIL_DEVICE_INIT = 0, /* driver is loaded, waiting for userspace */
|
||||
HVUTIL_READY, /* userspace is registered */
|
||||
HVUTIL_HOSTMSG_RECEIVED, /* message from the host was received */
|
||||
HVUTIL_USERSPACE_REQ, /* request to userspace was sent */
|
||||
HVUTIL_USERSPACE_RECV, /* reply from userspace was received */
|
||||
HVUTIL_DEVICE_DYING, /* driver unload is in progress */
|
||||
};
|
||||
|
||||
#endif /* _HYPERV_VMBUS_H */
|
||||
|
@ -1035,6 +1035,15 @@ acpi_walk_err:
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static int vmbus_acpi_remove(struct acpi_device *device)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (hyperv_mmio.start && hyperv_mmio.end)
|
||||
ret = release_resource(&hyperv_mmio);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id vmbus_acpi_device_ids[] = {
|
||||
{"VMBUS", 0},
|
||||
{"VMBus", 0},
|
||||
@ -1047,6 +1056,7 @@ static struct acpi_driver vmbus_acpi_driver = {
|
||||
.ids = vmbus_acpi_device_ids,
|
||||
.ops = {
|
||||
.add = vmbus_acpi_add,
|
||||
.remove = vmbus_acpi_remove,
|
||||
},
|
||||
};
|
||||
|
||||
@ -1096,15 +1106,22 @@ static void __exit vmbus_exit(void)
|
||||
|
||||
vmbus_connection.conn_state = DISCONNECTED;
|
||||
hv_synic_clockevents_cleanup();
|
||||
vmbus_disconnect();
|
||||
hv_remove_vmbus_irq();
|
||||
tasklet_kill(&msg_dpc);
|
||||
vmbus_free_channels();
|
||||
if (ms_hyperv.features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) {
|
||||
atomic_notifier_chain_unregister(&panic_notifier_list,
|
||||
&hyperv_panic_block);
|
||||
}
|
||||
bus_unregister(&hv_bus);
|
||||
hv_cleanup();
|
||||
for_each_online_cpu(cpu)
|
||||
for_each_online_cpu(cpu) {
|
||||
tasklet_kill(hv_context.event_dpc[cpu]);
|
||||
smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1);
|
||||
}
|
||||
acpi_bus_unregister_driver(&vmbus_acpi_driver);
|
||||
hv_cpu_hotplug_quirk(false);
|
||||
vmbus_disconnect();
|
||||
}
|
||||
|
||||
|
||||
|
@ -371,6 +371,17 @@ config SENSORS_DS1621
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called ds1621.
|
||||
|
||||
config SENSORS_DELL_SMM
|
||||
tristate "Dell laptop SMM BIOS hwmon driver"
|
||||
depends on X86
|
||||
help
|
||||
This hwmon driver adds support for reporting temperature of different
|
||||
sensors and controls the fans on Dell laptops via System Management
|
||||
Mode provided by Dell BIOS.
|
||||
|
||||
When option I8K is also enabled this driver provides legacy /proc/i8k
|
||||
userspace interface for i8kutils package.
|
||||
|
||||
config SENSORS_DA9052_ADC
|
||||
tristate "Dialog DA9052/DA9053 ADC"
|
||||
depends on PMIC_DA9052
|
||||
|
@ -49,6 +49,7 @@ obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o
|
||||
obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o
|
||||
obj-$(CONFIG_SENSORS_DA9052_ADC)+= da9052-hwmon.o
|
||||
obj-$(CONFIG_SENSORS_DA9055)+= da9055-hwmon.o
|
||||
obj-$(CONFIG_SENSORS_DELL_SMM) += dell-smm-hwmon.o
|
||||
obj-$(CONFIG_SENSORS_DME1737) += dme1737.o
|
||||
obj-$(CONFIG_SENSORS_DS620) += ds620.o
|
||||
obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
|
||||
|
@ -1,12 +1,12 @@
|
||||
/*
|
||||
* i8k.c -- Linux driver for accessing the SMM BIOS on Dell laptops.
|
||||
* dell-smm-hwmon.c -- Linux driver for accessing the SMM BIOS on Dell laptops.
|
||||
*
|
||||
* Copyright (C) 2001 Massimo Dal Zotto <dz@debian.org>
|
||||
*
|
||||
* Hwmon integration:
|
||||
* Copyright (C) 2011 Jean Delvare <jdelvare@suse.de>
|
||||
* Copyright (C) 2013, 2014 Guenter Roeck <linux@roeck-us.net>
|
||||
* Copyright (C) 2014 Pali Rohár <pali.rohar@gmail.com>
|
||||
* Copyright (C) 2014, 2015 Pali Rohár <pali.rohar@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
@ -80,8 +80,10 @@ static uint i8k_fan_max = I8K_FAN_HIGH;
|
||||
#define I8K_HWMON_HAVE_FAN2 (1 << 5)
|
||||
|
||||
MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)");
|
||||
MODULE_DESCRIPTION("Driver for accessing SMM BIOS on Dell laptops");
|
||||
MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
|
||||
MODULE_DESCRIPTION("Dell laptop SMM BIOS hwmon driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("i8k");
|
||||
|
||||
static bool force;
|
||||
module_param(force, bool, 0);
|
||||
@ -91,6 +93,7 @@ static bool ignore_dmi;
|
||||
module_param(ignore_dmi, bool, 0);
|
||||
MODULE_PARM_DESC(ignore_dmi, "Continue probing hardware even if DMI data does not match");
|
||||
|
||||
#if IS_ENABLED(CONFIG_I8K)
|
||||
static bool restricted;
|
||||
module_param(restricted, bool, 0);
|
||||
MODULE_PARM_DESC(restricted, "Allow fan control if SYS_ADMIN capability set");
|
||||
@ -98,6 +101,7 @@ MODULE_PARM_DESC(restricted, "Allow fan control if SYS_ADMIN capability set");
|
||||
static bool power_status;
|
||||
module_param(power_status, bool, 0600);
|
||||
MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k");
|
||||
#endif
|
||||
|
||||
static uint fan_mult;
|
||||
module_param(fan_mult, uint, 0);
|
||||
@ -107,18 +111,6 @@ static uint fan_max;
|
||||
module_param(fan_max, uint, 0);
|
||||
MODULE_PARM_DESC(fan_max, "Maximum configurable fan speed (default: autodetect)");
|
||||
|
||||
static int i8k_open_fs(struct inode *inode, struct file *file);
|
||||
static long i8k_ioctl(struct file *, unsigned int, unsigned long);
|
||||
|
||||
static const struct file_operations i8k_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = i8k_open_fs,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.unlocked_ioctl = i8k_ioctl,
|
||||
};
|
||||
|
||||
struct smm_regs {
|
||||
unsigned int eax;
|
||||
unsigned int ebx __packed;
|
||||
@ -218,45 +210,6 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the Fn key status.
|
||||
*/
|
||||
static int i8k_get_fn_status(void)
|
||||
{
|
||||
struct smm_regs regs = { .eax = I8K_SMM_FN_STATUS, };
|
||||
int rc;
|
||||
|
||||
rc = i8k_smm(®s);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
switch ((regs.eax >> I8K_FN_SHIFT) & I8K_FN_MASK) {
|
||||
case I8K_FN_UP:
|
||||
return I8K_VOL_UP;
|
||||
case I8K_FN_DOWN:
|
||||
return I8K_VOL_DOWN;
|
||||
case I8K_FN_MUTE:
|
||||
return I8K_VOL_MUTE;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the power status.
|
||||
*/
|
||||
static int i8k_get_power_status(void)
|
||||
{
|
||||
struct smm_regs regs = { .eax = I8K_SMM_POWER_STATUS, };
|
||||
int rc;
|
||||
|
||||
rc = i8k_smm(®s);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return (regs.eax & 0xff) == I8K_POWER_AC ? I8K_AC : I8K_BATTERY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the fan status.
|
||||
*/
|
||||
@ -376,6 +329,51 @@ static int i8k_get_dell_signature(int req_fn)
|
||||
return regs.eax == 1145651527 && regs.edx == 1145392204 ? 0 : -1;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_I8K)
|
||||
|
||||
/*
|
||||
* Read the Fn key status.
|
||||
*/
|
||||
static int i8k_get_fn_status(void)
|
||||
{
|
||||
struct smm_regs regs = { .eax = I8K_SMM_FN_STATUS, };
|
||||
int rc;
|
||||
|
||||
rc = i8k_smm(®s);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
switch ((regs.eax >> I8K_FN_SHIFT) & I8K_FN_MASK) {
|
||||
case I8K_FN_UP:
|
||||
return I8K_VOL_UP;
|
||||
case I8K_FN_DOWN:
|
||||
return I8K_VOL_DOWN;
|
||||
case I8K_FN_MUTE:
|
||||
return I8K_VOL_MUTE;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the power status.
|
||||
*/
|
||||
static int i8k_get_power_status(void)
|
||||
{
|
||||
struct smm_regs regs = { .eax = I8K_SMM_POWER_STATUS, };
|
||||
int rc;
|
||||
|
||||
rc = i8k_smm(®s);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return (regs.eax & 0xff) == I8K_POWER_AC ? I8K_AC : I8K_BATTERY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Procfs interface
|
||||
*/
|
||||
|
||||
static int
|
||||
i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
@ -526,6 +524,37 @@ static int i8k_open_fs(struct inode *inode, struct file *file)
|
||||
return single_open(file, i8k_proc_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations i8k_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = i8k_open_fs,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.unlocked_ioctl = i8k_ioctl,
|
||||
};
|
||||
|
||||
static void __init i8k_init_procfs(void)
|
||||
{
|
||||
/* Register the proc entry */
|
||||
proc_create("i8k", 0, NULL, &i8k_fops);
|
||||
}
|
||||
|
||||
static void __exit i8k_exit_procfs(void)
|
||||
{
|
||||
remove_proc_entry("i8k", NULL);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline void __init i8k_init_procfs(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void __exit i8k_exit_procfs(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Hwmon interface
|
||||
@ -748,8 +777,8 @@ static int __init i8k_init_hwmon(void)
|
||||
if (err >= 0)
|
||||
i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN2;
|
||||
|
||||
i8k_hwmon_dev = hwmon_device_register_with_groups(NULL, "i8k", NULL,
|
||||
i8k_groups);
|
||||
i8k_hwmon_dev = hwmon_device_register_with_groups(NULL, "dell-smm",
|
||||
NULL, i8k_groups);
|
||||
if (IS_ERR(i8k_hwmon_dev)) {
|
||||
err = PTR_ERR(i8k_hwmon_dev);
|
||||
i8k_hwmon_dev = NULL;
|
||||
@ -974,33 +1003,24 @@ static int __init i8k_probe(void)
|
||||
|
||||
static int __init i8k_init(void)
|
||||
{
|
||||
struct proc_dir_entry *proc_i8k;
|
||||
int err;
|
||||
|
||||
/* Are we running on an supported laptop? */
|
||||
if (i8k_probe())
|
||||
return -ENODEV;
|
||||
|
||||
/* Register the proc entry */
|
||||
proc_i8k = proc_create("i8k", 0, NULL, &i8k_fops);
|
||||
if (!proc_i8k)
|
||||
return -ENOENT;
|
||||
|
||||
err = i8k_init_hwmon();
|
||||
if (err)
|
||||
goto exit_remove_proc;
|
||||
return err;
|
||||
|
||||
i8k_init_procfs();
|
||||
return 0;
|
||||
|
||||
exit_remove_proc:
|
||||
remove_proc_entry("i8k", NULL);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit i8k_exit(void)
|
||||
{
|
||||
hwmon_device_unregister(i8k_hwmon_dev);
|
||||
remove_proc_entry("i8k", NULL);
|
||||
i8k_exit_procfs();
|
||||
}
|
||||
|
||||
module_init(i8k_init);
|
@ -58,4 +58,23 @@ config CORESIGHT_SOURCE_ETM3X
|
||||
which allows tracing the instructions that a processor is executing
|
||||
This is primarily useful for instruction level tracing. Depending
|
||||
the ETM version data tracing may also be available.
|
||||
|
||||
config CORESIGHT_SOURCE_ETM4X
|
||||
bool "CoreSight Embedded Trace Macrocell 4.x driver"
|
||||
depends on ARM64
|
||||
select CORESIGHT_LINKS_AND_SINKS
|
||||
help
|
||||
This driver provides support for the ETM4.x tracer module, tracing the
|
||||
instructions that a processor is executing. This is primarily useful
|
||||
for instruction level tracing. Depending on the implemented version
|
||||
data tracing may also be available.
|
||||
|
||||
config CORESIGHT_QCOM_REPLICATOR
|
||||
bool "Qualcomm CoreSight Replicator driver"
|
||||
depends on CORESIGHT_LINKS_AND_SINKS
|
||||
help
|
||||
This enables support for Qualcomm CoreSight link driver. The
|
||||
programmable ATB replicator sends the ATB trace stream from the
|
||||
ETB/ETF to the TPIUi and ETR.
|
||||
|
||||
endif
|
||||
|
@ -9,3 +9,5 @@ obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o
|
||||
obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \
|
||||
coresight-replicator.o
|
||||
obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o
|
||||
obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o
|
||||
obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o
|
||||
|
@ -22,10 +22,11 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/coresight.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include "coresight-priv.h"
|
||||
|
||||
@ -66,9 +67,9 @@
|
||||
* struct etb_drvdata - specifics associated to an ETB component
|
||||
* @base: memory mapped base address for this component.
|
||||
* @dev: the device entity associated to this component.
|
||||
* @atclk: optional clock for the core parts of the ETB.
|
||||
* @csdev: component vitals needed by the framework.
|
||||
* @miscdev: specifics to handle "/dev/xyz.etb" entry.
|
||||
* @clk: the clock this component is associated to.
|
||||
* @spinlock: only one at a time pls.
|
||||
* @in_use: synchronise user space access to etb buffer.
|
||||
* @buf: area of memory where ETB buffer content gets sent.
|
||||
@ -79,9 +80,9 @@
|
||||
struct etb_drvdata {
|
||||
void __iomem *base;
|
||||
struct device *dev;
|
||||
struct clk *atclk;
|
||||
struct coresight_device *csdev;
|
||||
struct miscdevice miscdev;
|
||||
struct clk *clk;
|
||||
spinlock_t spinlock;
|
||||
atomic_t in_use;
|
||||
u8 *buf;
|
||||
@ -92,17 +93,14 @@ struct etb_drvdata {
|
||||
|
||||
static unsigned int etb_get_buffer_depth(struct etb_drvdata *drvdata)
|
||||
{
|
||||
int ret;
|
||||
u32 depth = 0;
|
||||
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
pm_runtime_get_sync(drvdata->dev);
|
||||
|
||||
/* RO registers don't need locking */
|
||||
depth = readl_relaxed(drvdata->base + ETB_RAM_DEPTH_REG);
|
||||
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
pm_runtime_put(drvdata->dev);
|
||||
return depth;
|
||||
}
|
||||
|
||||
@ -137,12 +135,9 @@ static void etb_enable_hw(struct etb_drvdata *drvdata)
|
||||
static int etb_enable(struct coresight_device *csdev)
|
||||
{
|
||||
struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
pm_runtime_get_sync(drvdata->dev);
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
etb_enable_hw(drvdata);
|
||||
@ -252,7 +247,7 @@ static void etb_disable(struct coresight_device *csdev)
|
||||
drvdata->enable = false;
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
pm_runtime_put(drvdata->dev);
|
||||
|
||||
dev_info(drvdata->dev, "ETB disabled\n");
|
||||
}
|
||||
@ -339,16 +334,12 @@ static const struct file_operations etb_fops = {
|
||||
static ssize_t status_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
u32 etb_rdr, etb_sr, etb_rrp, etb_rwp;
|
||||
u32 etb_trg, etb_cr, etb_ffsr, etb_ffcr;
|
||||
struct etb_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
pm_runtime_get_sync(drvdata->dev);
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
@ -364,7 +355,7 @@ static ssize_t status_show(struct device *dev,
|
||||
CS_LOCK(drvdata->base);
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
pm_runtime_put(drvdata->dev);
|
||||
|
||||
return sprintf(buf,
|
||||
"Depth:\t\t0x%x\n"
|
||||
@ -377,7 +368,7 @@ static ssize_t status_show(struct device *dev,
|
||||
"Flush ctrl:\t0x%x\n",
|
||||
etb_rdr, etb_sr, etb_rrp, etb_rwp,
|
||||
etb_trg, etb_cr, etb_ffsr, etb_ffcr);
|
||||
out:
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
static DEVICE_ATTR_RO(status);
|
||||
@ -438,6 +429,12 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
return -ENOMEM;
|
||||
|
||||
drvdata->dev = &adev->dev;
|
||||
drvdata->atclk = devm_clk_get(&adev->dev, "atclk"); /* optional */
|
||||
if (!IS_ERR(drvdata->atclk)) {
|
||||
ret = clk_prepare_enable(drvdata->atclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
dev_set_drvdata(dev, drvdata);
|
||||
|
||||
/* validity for the resource is already checked by the AMBA core */
|
||||
@ -449,21 +446,19 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
|
||||
spin_lock_init(&drvdata->spinlock);
|
||||
|
||||
drvdata->clk = adev->pclk;
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drvdata->buffer_depth = etb_get_buffer_depth(drvdata);
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
pm_runtime_put(&adev->dev);
|
||||
|
||||
if (drvdata->buffer_depth < 0)
|
||||
if (drvdata->buffer_depth & 0x80000000)
|
||||
return -EINVAL;
|
||||
|
||||
drvdata->buf = devm_kzalloc(dev,
|
||||
drvdata->buffer_depth * 4, GFP_KERNEL);
|
||||
if (!drvdata->buf)
|
||||
if (!drvdata->buf) {
|
||||
dev_err(dev, "Failed to allocate %u bytes for buffer data\n",
|
||||
drvdata->buffer_depth * 4);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
|
||||
if (!desc)
|
||||
@ -503,6 +498,32 @@ static int etb_remove(struct amba_device *adev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int etb_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct etb_drvdata *drvdata = dev_get_drvdata(dev);
|
||||
|
||||
if (drvdata && !IS_ERR(drvdata->atclk))
|
||||
clk_disable_unprepare(drvdata->atclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int etb_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct etb_drvdata *drvdata = dev_get_drvdata(dev);
|
||||
|
||||
if (drvdata && !IS_ERR(drvdata->atclk))
|
||||
clk_prepare_enable(drvdata->atclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops etb_dev_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(etb_runtime_suspend, etb_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct amba_id etb_ids[] = {
|
||||
{
|
||||
.id = 0x0003b907,
|
||||
@ -515,6 +536,8 @@ static struct amba_driver etb_driver = {
|
||||
.drv = {
|
||||
.name = "coresight-etb10",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &etb_dev_pm_ops,
|
||||
|
||||
},
|
||||
.probe = etb_probe,
|
||||
.remove = etb_remove,
|
||||
|
@ -140,8 +140,8 @@
|
||||
* struct etm_drvdata - specifics associated to an ETM component
|
||||
* @base: memory mapped base address for this component.
|
||||
* @dev: the device entity associated to this component.
|
||||
* @atclk: optional clock for the core parts of the ETM.
|
||||
* @csdev: component vitals needed by the framework.
|
||||
* @clk: the clock this component is associated to.
|
||||
* @spinlock: only one at a time pls.
|
||||
* @cpu: the cpu this component is affined to.
|
||||
* @port_size: port size as reported by ETMCR bit 4-6 and 21.
|
||||
@ -192,8 +192,8 @@
|
||||
struct etm_drvdata {
|
||||
void __iomem *base;
|
||||
struct device *dev;
|
||||
struct clk *atclk;
|
||||
struct coresight_device *csdev;
|
||||
struct clk *clk;
|
||||
spinlock_t spinlock;
|
||||
int cpu;
|
||||
int port_size;
|
||||
|
@ -23,13 +23,14 @@
|
||||
#include <linux/smp.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/coresight.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/clk.h>
|
||||
#include <asm/sections.h>
|
||||
|
||||
#include "coresight-etm.h"
|
||||
@ -325,9 +326,7 @@ static int etm_trace_id(struct coresight_device *csdev)
|
||||
|
||||
if (!drvdata->enable)
|
||||
return drvdata->traceid;
|
||||
|
||||
if (clk_prepare_enable(drvdata->clk))
|
||||
goto out;
|
||||
pm_runtime_get_sync(csdev->dev.parent);
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
|
||||
@ -336,8 +335,8 @@ static int etm_trace_id(struct coresight_device *csdev)
|
||||
CS_LOCK(drvdata->base);
|
||||
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
out:
|
||||
pm_runtime_put(csdev->dev.parent);
|
||||
|
||||
return trace_id;
|
||||
}
|
||||
|
||||
@ -346,10 +345,7 @@ static int etm_enable(struct coresight_device *csdev)
|
||||
struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
goto err_clk;
|
||||
|
||||
pm_runtime_get_sync(csdev->dev.parent);
|
||||
spin_lock(&drvdata->spinlock);
|
||||
|
||||
/*
|
||||
@ -373,8 +369,7 @@ static int etm_enable(struct coresight_device *csdev)
|
||||
return 0;
|
||||
err:
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
err_clk:
|
||||
pm_runtime_put(csdev->dev.parent);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -423,8 +418,7 @@ static void etm_disable(struct coresight_device *csdev)
|
||||
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
put_online_cpus();
|
||||
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
pm_runtime_put(csdev->dev.parent);
|
||||
|
||||
dev_info(drvdata->dev, "ETM tracing disabled\n");
|
||||
}
|
||||
@ -474,14 +468,10 @@ static DEVICE_ATTR_RO(nr_ctxid_cmp);
|
||||
static ssize_t etmsr_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags, val;
|
||||
struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_runtime_get_sync(drvdata->dev);
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
@ -489,7 +479,7 @@ static ssize_t etmsr_show(struct device *dev,
|
||||
|
||||
CS_LOCK(drvdata->base);
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
pm_runtime_put(drvdata->dev);
|
||||
|
||||
return sprintf(buf, "%#lx\n", val);
|
||||
}
|
||||
@ -1317,7 +1307,6 @@ static DEVICE_ATTR_RW(seq_13_event);
|
||||
static ssize_t seq_curr_state_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int ret;
|
||||
unsigned long val, flags;
|
||||
struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
|
||||
@ -1326,10 +1315,7 @@ static ssize_t seq_curr_state_show(struct device *dev,
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_runtime_get_sync(drvdata->dev);
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
|
||||
CS_UNLOCK(drvdata->base);
|
||||
@ -1337,7 +1323,7 @@ static ssize_t seq_curr_state_show(struct device *dev,
|
||||
CS_LOCK(drvdata->base);
|
||||
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
pm_runtime_put(drvdata->dev);
|
||||
out:
|
||||
return sprintf(buf, "%#lx\n", val);
|
||||
}
|
||||
@ -1521,10 +1507,7 @@ static ssize_t status_show(struct device *dev,
|
||||
unsigned long flags;
|
||||
struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_runtime_get_sync(drvdata->dev);
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
|
||||
CS_UNLOCK(drvdata->base);
|
||||
@ -1550,7 +1533,7 @@ static ssize_t status_show(struct device *dev,
|
||||
CS_LOCK(drvdata->base);
|
||||
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
pm_runtime_put(drvdata->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -1559,7 +1542,6 @@ static DEVICE_ATTR_RO(status);
|
||||
static ssize_t traceid_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int ret;
|
||||
unsigned long val, flags;
|
||||
struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
|
||||
@ -1568,10 +1550,7 @@ static ssize_t traceid_show(struct device *dev,
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_runtime_get_sync(drvdata->dev);
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
@ -1579,7 +1558,7 @@ static ssize_t traceid_show(struct device *dev,
|
||||
|
||||
CS_LOCK(drvdata->base);
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
pm_runtime_put(drvdata->dev);
|
||||
out:
|
||||
return sprintf(buf, "%#lx\n", val);
|
||||
}
|
||||
@ -1817,10 +1796,12 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
|
||||
spin_lock_init(&drvdata->spinlock);
|
||||
|
||||
drvdata->clk = adev->pclk;
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
drvdata->atclk = devm_clk_get(&adev->dev, "atclk"); /* optional */
|
||||
if (!IS_ERR(drvdata->atclk)) {
|
||||
ret = clk_prepare_enable(drvdata->atclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
drvdata->cpu = pdata ? pdata->cpu : 0;
|
||||
|
||||
@ -1845,8 +1826,6 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
}
|
||||
etm_init_default_data(drvdata);
|
||||
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
|
||||
desc->type = CORESIGHT_DEV_TYPE_SOURCE;
|
||||
desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
|
||||
desc->ops = &etm_cs_ops;
|
||||
@ -1859,7 +1838,8 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
goto err_arch_supported;
|
||||
}
|
||||
|
||||
dev_info(dev, "ETM initialized\n");
|
||||
pm_runtime_put(&adev->dev);
|
||||
dev_info(dev, "%s initialized\n", (char *)id->data);
|
||||
|
||||
if (boot_enable) {
|
||||
coresight_enable(drvdata->csdev);
|
||||
@ -1869,7 +1849,6 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
return 0;
|
||||
|
||||
err_arch_supported:
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
if (--etm_count == 0)
|
||||
unregister_hotcpu_notifier(&etm_cpu_notifier);
|
||||
return ret;
|
||||
@ -1886,22 +1865,52 @@ static int etm_remove(struct amba_device *adev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int etm_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct etm_drvdata *drvdata = dev_get_drvdata(dev);
|
||||
|
||||
if (drvdata && !IS_ERR(drvdata->atclk))
|
||||
clk_disable_unprepare(drvdata->atclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int etm_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct etm_drvdata *drvdata = dev_get_drvdata(dev);
|
||||
|
||||
if (drvdata && !IS_ERR(drvdata->atclk))
|
||||
clk_prepare_enable(drvdata->atclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops etm_dev_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(etm_runtime_suspend, etm_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct amba_id etm_ids[] = {
|
||||
{ /* ETM 3.3 */
|
||||
.id = 0x0003b921,
|
||||
.mask = 0x0003ffff,
|
||||
.data = "ETM 3.3",
|
||||
},
|
||||
{ /* ETM 3.5 */
|
||||
.id = 0x0003b956,
|
||||
.mask = 0x0003ffff,
|
||||
.data = "ETM 3.5",
|
||||
},
|
||||
{ /* PTM 1.0 */
|
||||
.id = 0x0003b950,
|
||||
.mask = 0x0003ffff,
|
||||
.data = "PTM 1.0",
|
||||
},
|
||||
{ /* PTM 1.1 */
|
||||
.id = 0x0003b95f,
|
||||
.mask = 0x0003ffff,
|
||||
.data = "PTM 1.1",
|
||||
},
|
||||
{ 0, 0},
|
||||
};
|
||||
@ -1910,23 +1919,14 @@ static struct amba_driver etm_driver = {
|
||||
.drv = {
|
||||
.name = "coresight-etm3x",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &etm_dev_pm_ops,
|
||||
},
|
||||
.probe = etm_probe,
|
||||
.remove = etm_remove,
|
||||
.id_table = etm_ids,
|
||||
};
|
||||
|
||||
int __init etm_init(void)
|
||||
{
|
||||
return amba_driver_register(&etm_driver);
|
||||
}
|
||||
module_init(etm_init);
|
||||
|
||||
void __exit etm_exit(void)
|
||||
{
|
||||
amba_driver_unregister(&etm_driver);
|
||||
}
|
||||
module_exit(etm_exit);
|
||||
module_amba_driver(etm_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("CoreSight Program Flow Trace driver");
|
||||
|
2702
drivers/hwtracing/coresight/coresight-etm4x.c
Normal file
2702
drivers/hwtracing/coresight/coresight-etm4x.c
Normal file
File diff suppressed because it is too large
Load Diff
391
drivers/hwtracing/coresight/coresight-etm4x.h
Normal file
391
drivers/hwtracing/coresight/coresight-etm4x.h
Normal file
@ -0,0 +1,391 @@
|
||||
/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only 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.
|
||||
*/
|
||||
|
||||
#ifndef _CORESIGHT_CORESIGHT_ETM_H
|
||||
#define _CORESIGHT_CORESIGHT_ETM_H
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include "coresight-priv.h"
|
||||
|
||||
/*
|
||||
* Device registers:
|
||||
* 0x000 - 0x2FC: Trace registers
|
||||
* 0x300 - 0x314: Management registers
|
||||
* 0x318 - 0xEFC: Trace registers
|
||||
* 0xF00: Management registers
|
||||
* 0xFA0 - 0xFA4: Trace registers
|
||||
* 0xFA8 - 0xFFC: Management registers
|
||||
*/
|
||||
/* Trace registers (0x000-0x2FC) */
|
||||
/* Main control and configuration registers */
|
||||
#define TRCPRGCTLR 0x004
|
||||
#define TRCPROCSELR 0x008
|
||||
#define TRCSTATR 0x00C
|
||||
#define TRCCONFIGR 0x010
|
||||
#define TRCAUXCTLR 0x018
|
||||
#define TRCEVENTCTL0R 0x020
|
||||
#define TRCEVENTCTL1R 0x024
|
||||
#define TRCSTALLCTLR 0x02C
|
||||
#define TRCTSCTLR 0x030
|
||||
#define TRCSYNCPR 0x034
|
||||
#define TRCCCCTLR 0x038
|
||||
#define TRCBBCTLR 0x03C
|
||||
#define TRCTRACEIDR 0x040
|
||||
#define TRCQCTLR 0x044
|
||||
/* Filtering control registers */
|
||||
#define TRCVICTLR 0x080
|
||||
#define TRCVIIECTLR 0x084
|
||||
#define TRCVISSCTLR 0x088
|
||||
#define TRCVIPCSSCTLR 0x08C
|
||||
#define TRCVDCTLR 0x0A0
|
||||
#define TRCVDSACCTLR 0x0A4
|
||||
#define TRCVDARCCTLR 0x0A8
|
||||
/* Derived resources registers */
|
||||
#define TRCSEQEVRn(n) (0x100 + (n * 4))
|
||||
#define TRCSEQRSTEVR 0x118
|
||||
#define TRCSEQSTR 0x11C
|
||||
#define TRCEXTINSELR 0x120
|
||||
#define TRCCNTRLDVRn(n) (0x140 + (n * 4))
|
||||
#define TRCCNTCTLRn(n) (0x150 + (n * 4))
|
||||
#define TRCCNTVRn(n) (0x160 + (n * 4))
|
||||
/* ID registers */
|
||||
#define TRCIDR8 0x180
|
||||
#define TRCIDR9 0x184
|
||||
#define TRCIDR10 0x188
|
||||
#define TRCIDR11 0x18C
|
||||
#define TRCIDR12 0x190
|
||||
#define TRCIDR13 0x194
|
||||
#define TRCIMSPEC0 0x1C0
|
||||
#define TRCIMSPECn(n) (0x1C0 + (n * 4))
|
||||
#define TRCIDR0 0x1E0
|
||||
#define TRCIDR1 0x1E4
|
||||
#define TRCIDR2 0x1E8
|
||||
#define TRCIDR3 0x1EC
|
||||
#define TRCIDR4 0x1F0
|
||||
#define TRCIDR5 0x1F4
|
||||
#define TRCIDR6 0x1F8
|
||||
#define TRCIDR7 0x1FC
|
||||
/* Resource selection registers */
|
||||
#define TRCRSCTLRn(n) (0x200 + (n * 4))
|
||||
/* Single-shot comparator registers */
|
||||
#define TRCSSCCRn(n) (0x280 + (n * 4))
|
||||
#define TRCSSCSRn(n) (0x2A0 + (n * 4))
|
||||
#define TRCSSPCICRn(n) (0x2C0 + (n * 4))
|
||||
/* Management registers (0x300-0x314) */
|
||||
#define TRCOSLAR 0x300
|
||||
#define TRCOSLSR 0x304
|
||||
#define TRCPDCR 0x310
|
||||
#define TRCPDSR 0x314
|
||||
/* Trace registers (0x318-0xEFC) */
|
||||
/* Comparator registers */
|
||||
#define TRCACVRn(n) (0x400 + (n * 8))
|
||||
#define TRCACATRn(n) (0x480 + (n * 8))
|
||||
#define TRCDVCVRn(n) (0x500 + (n * 16))
|
||||
#define TRCDVCMRn(n) (0x580 + (n * 16))
|
||||
#define TRCCIDCVRn(n) (0x600 + (n * 8))
|
||||
#define TRCVMIDCVRn(n) (0x640 + (n * 8))
|
||||
#define TRCCIDCCTLR0 0x680
|
||||
#define TRCCIDCCTLR1 0x684
|
||||
#define TRCVMIDCCTLR0 0x688
|
||||
#define TRCVMIDCCTLR1 0x68C
|
||||
/* Management register (0xF00) */
|
||||
/* Integration control registers */
|
||||
#define TRCITCTRL 0xF00
|
||||
/* Trace registers (0xFA0-0xFA4) */
|
||||
/* Claim tag registers */
|
||||
#define TRCCLAIMSET 0xFA0
|
||||
#define TRCCLAIMCLR 0xFA4
|
||||
/* Management registers (0xFA8-0xFFC) */
|
||||
#define TRCDEVAFF0 0xFA8
|
||||
#define TRCDEVAFF1 0xFAC
|
||||
#define TRCLAR 0xFB0
|
||||
#define TRCLSR 0xFB4
|
||||
#define TRCAUTHSTATUS 0xFB8
|
||||
#define TRCDEVARCH 0xFBC
|
||||
#define TRCDEVID 0xFC8
|
||||
#define TRCDEVTYPE 0xFCC
|
||||
#define TRCPIDR4 0xFD0
|
||||
#define TRCPIDR5 0xFD4
|
||||
#define TRCPIDR6 0xFD8
|
||||
#define TRCPIDR7 0xFDC
|
||||
#define TRCPIDR0 0xFE0
|
||||
#define TRCPIDR1 0xFE4
|
||||
#define TRCPIDR2 0xFE8
|
||||
#define TRCPIDR3 0xFEC
|
||||
#define TRCCIDR0 0xFF0
|
||||
#define TRCCIDR1 0xFF4
|
||||
#define TRCCIDR2 0xFF8
|
||||
#define TRCCIDR3 0xFFC
|
||||
|
||||
/* ETMv4 resources */
|
||||
#define ETM_MAX_NR_PE 8
|
||||
#define ETMv4_MAX_CNTR 4
|
||||
#define ETM_MAX_SEQ_STATES 4
|
||||
#define ETM_MAX_EXT_INP_SEL 4
|
||||
#define ETM_MAX_EXT_INP 256
|
||||
#define ETM_MAX_EXT_OUT 4
|
||||
#define ETM_MAX_SINGLE_ADDR_CMP 16
|
||||
#define ETM_MAX_ADDR_RANGE_CMP (ETM_MAX_SINGLE_ADDR_CMP / 2)
|
||||
#define ETM_MAX_DATA_VAL_CMP 8
|
||||
#define ETMv4_MAX_CTXID_CMP 8
|
||||
#define ETM_MAX_VMID_CMP 8
|
||||
#define ETM_MAX_PE_CMP 8
|
||||
#define ETM_MAX_RES_SEL 16
|
||||
#define ETM_MAX_SS_CMP 8
|
||||
|
||||
#define ETM_ARCH_V4 0x40
|
||||
#define ETMv4_SYNC_MASK 0x1F
|
||||
#define ETM_CYC_THRESHOLD_MASK 0xFFF
|
||||
#define ETMv4_EVENT_MASK 0xFF
|
||||
#define ETM_CNTR_MAX_VAL 0xFFFF
|
||||
#define ETM_TRACEID_MASK 0x3f
|
||||
|
||||
/* ETMv4 programming modes */
|
||||
#define ETM_MODE_EXCLUDE BIT(0)
|
||||
#define ETM_MODE_LOAD BIT(1)
|
||||
#define ETM_MODE_STORE BIT(2)
|
||||
#define ETM_MODE_LOAD_STORE BIT(3)
|
||||
#define ETM_MODE_BB BIT(4)
|
||||
#define ETMv4_MODE_CYCACC BIT(5)
|
||||
#define ETMv4_MODE_CTXID BIT(6)
|
||||
#define ETM_MODE_VMID BIT(7)
|
||||
#define ETM_MODE_COND(val) BMVAL(val, 8, 10)
|
||||
#define ETMv4_MODE_TIMESTAMP BIT(11)
|
||||
#define ETM_MODE_RETURNSTACK BIT(12)
|
||||
#define ETM_MODE_QELEM(val) BMVAL(val, 13, 14)
|
||||
#define ETM_MODE_DATA_TRACE_ADDR BIT(15)
|
||||
#define ETM_MODE_DATA_TRACE_VAL BIT(16)
|
||||
#define ETM_MODE_ISTALL BIT(17)
|
||||
#define ETM_MODE_DSTALL BIT(18)
|
||||
#define ETM_MODE_ATB_TRIGGER BIT(19)
|
||||
#define ETM_MODE_LPOVERRIDE BIT(20)
|
||||
#define ETM_MODE_ISTALL_EN BIT(21)
|
||||
#define ETM_MODE_DSTALL_EN BIT(22)
|
||||
#define ETM_MODE_INSTPRIO BIT(23)
|
||||
#define ETM_MODE_NOOVERFLOW BIT(24)
|
||||
#define ETM_MODE_TRACE_RESET BIT(25)
|
||||
#define ETM_MODE_TRACE_ERR BIT(26)
|
||||
#define ETM_MODE_VIEWINST_STARTSTOP BIT(27)
|
||||
#define ETMv4_MODE_ALL 0xFFFFFFF
|
||||
|
||||
#define TRCSTATR_IDLE_BIT 0
|
||||
|
||||
/**
|
||||
* struct etm4_drvdata - specifics associated to an ETM component
|
||||
* @base: Memory mapped base address for this component.
|
||||
* @dev: The device entity associated to this component.
|
||||
* @csdev: Component vitals needed by the framework.
|
||||
* @spinlock: Only one at a time pls.
|
||||
* @cpu: The cpu this component is affined to.
|
||||
* @arch: ETM version number.
|
||||
* @enable: Is this ETM currently tracing.
|
||||
* @sticky_enable: true if ETM base configuration has been done.
|
||||
* @boot_enable:True if we should start tracing at boot time.
|
||||
* @os_unlock: True if access to management registers is allowed.
|
||||
* @nr_pe: The number of processing entity available for tracing.
|
||||
* @nr_pe_cmp: The number of processing entity comparator inputs that are
|
||||
* available for tracing.
|
||||
* @nr_addr_cmp:Number of pairs of address comparators available
|
||||
* as found in ETMIDR4 0-3.
|
||||
* @nr_cntr: Number of counters as found in ETMIDR5 bit 28-30.
|
||||
* @nr_ext_inp: Number of external input.
|
||||
* @numcidc: Number of contextID comparators.
|
||||
* @numvmidc: Number of VMID comparators.
|
||||
* @nrseqstate: The number of sequencer states that are implemented.
|
||||
* @nr_event: Indicates how many events the trace unit support.
|
||||
* @nr_resource:The number of resource selection pairs available for tracing.
|
||||
* @nr_ss_cmp: Number of single-shot comparator controls that are available.
|
||||
* @mode: Controls various modes supported by this ETM.
|
||||
* @trcid: value of the current ID for this component.
|
||||
* @trcid_size: Indicates the trace ID width.
|
||||
* @instrp0: Tracing of load and store instructions
|
||||
* as P0 elements is supported.
|
||||
* @trccond: If the trace unit supports conditional
|
||||
* instruction tracing.
|
||||
* @retstack: Indicates if the implementation supports a return stack.
|
||||
* @trc_error: Whether a trace unit can trace a system
|
||||
* error exception.
|
||||
* @atbtrig: If the implementation can support ATB triggers
|
||||
* @lpoverride: If the implementation can support low-power state over.
|
||||
* @pe_sel: Controls which PE to trace.
|
||||
* @cfg: Controls the tracing options.
|
||||
* @eventctrl0: Controls the tracing of arbitrary events.
|
||||
* @eventctrl1: Controls the behavior of the events that @event_ctrl0 selects.
|
||||
* @stallctl: If functionality that prevents trace unit buffer overflows
|
||||
* is available.
|
||||
* @sysstall: Does the system support stall control of the PE?
|
||||
* @nooverflow: Indicate if overflow prevention is supported.
|
||||
* @stall_ctrl: Enables trace unit functionality that prevents trace
|
||||
* unit buffer overflows.
|
||||
* @ts_size: Global timestamp size field.
|
||||
* @ts_ctrl: Controls the insertion of global timestamps in the
|
||||
* trace streams.
|
||||
* @syncpr: Indicates if an implementation has a fixed
|
||||
* synchronization period.
|
||||
* @syncfreq: Controls how often trace synchronization requests occur.
|
||||
* @trccci: Indicates if the trace unit supports cycle counting
|
||||
* for instruction.
|
||||
* @ccsize: Indicates the size of the cycle counter in bits.
|
||||
* @ccitmin: minimum value that can be programmed in
|
||||
* the TRCCCCTLR register.
|
||||
* @ccctlr: Sets the threshold value for cycle counting.
|
||||
* @trcbb: Indicates if the trace unit supports branch broadcast tracing.
|
||||
* @q_support: Q element support characteristics.
|
||||
* @vinst_ctrl: Controls instruction trace filtering.
|
||||
* @viiectlr: Set or read, the address range comparators.
|
||||
* @vissctlr: Set, or read, the single address comparators that control the
|
||||
* ViewInst start-stop logic.
|
||||
* @vipcssctlr: Set, or read, which PE comparator inputs can control the
|
||||
* ViewInst start-stop logic.
|
||||
* @seq_idx: Sequencor index selector.
|
||||
* @seq_ctrl: Control for the sequencer state transition control register.
|
||||
* @seq_rst: Moves the sequencer to state 0 when a programmed event occurs.
|
||||
* @seq_state: Set, or read the sequencer state.
|
||||
* @cntr_idx: Counter index seletor.
|
||||
* @cntrldvr: Sets or returns the reload count value for a counter.
|
||||
* @cntr_ctrl: Controls the operation of a counter.
|
||||
* @cntr_val: Sets or returns the value for a counter.
|
||||
* @res_idx: Resource index selector.
|
||||
* @res_ctrl: Controls the selection of the resources in the trace unit.
|
||||
* @ss_ctrl: Controls the corresponding single-shot comparator resource.
|
||||
* @ss_status: The status of the corresponding single-shot comparator.
|
||||
* @ss_pe_cmp: Selects the PE comparator inputs for Single-shot control.
|
||||
* @addr_idx: Address comparator index selector.
|
||||
* @addr_val: Value for address comparator.
|
||||
* @addr_acc: Address comparator access type.
|
||||
* @addr_type: Current status of the comparator register.
|
||||
* @ctxid_idx: Context ID index selector.
|
||||
* @ctxid_size: Size of the context ID field to consider.
|
||||
* @ctxid_val: Value of the context ID comparator.
|
||||
* @ctxid_mask0:Context ID comparator mask for comparator 0-3.
|
||||
* @ctxid_mask1:Context ID comparator mask for comparator 4-7.
|
||||
* @vmid_idx: VM ID index selector.
|
||||
* @vmid_size: Size of the VM ID comparator to consider.
|
||||
* @vmid_val: Value of the VM ID comparator.
|
||||
* @vmid_mask0: VM ID comparator mask for comparator 0-3.
|
||||
* @vmid_mask1: VM ID comparator mask for comparator 4-7.
|
||||
* @s_ex_level: In secure state, indicates whether instruction tracing is
|
||||
* supported for the corresponding Exception level.
|
||||
* @ns_ex_level:In non-secure state, indicates whether instruction tracing is
|
||||
* supported for the corresponding Exception level.
|
||||
* @ext_inp: External input selection.
|
||||
*/
|
||||
struct etmv4_drvdata {
|
||||
void __iomem *base;
|
||||
struct device *dev;
|
||||
struct coresight_device *csdev;
|
||||
spinlock_t spinlock;
|
||||
int cpu;
|
||||
u8 arch;
|
||||
bool enable;
|
||||
bool sticky_enable;
|
||||
bool boot_enable;
|
||||
bool os_unlock;
|
||||
u8 nr_pe;
|
||||
u8 nr_pe_cmp;
|
||||
u8 nr_addr_cmp;
|
||||
u8 nr_cntr;
|
||||
u8 nr_ext_inp;
|
||||
u8 numcidc;
|
||||
u8 numvmidc;
|
||||
u8 nrseqstate;
|
||||
u8 nr_event;
|
||||
u8 nr_resource;
|
||||
u8 nr_ss_cmp;
|
||||
u32 mode;
|
||||
u8 trcid;
|
||||
u8 trcid_size;
|
||||
bool instrp0;
|
||||
bool trccond;
|
||||
bool retstack;
|
||||
bool trc_error;
|
||||
bool atbtrig;
|
||||
bool lpoverride;
|
||||
u32 pe_sel;
|
||||
u32 cfg;
|
||||
u32 eventctrl0;
|
||||
u32 eventctrl1;
|
||||
bool stallctl;
|
||||
bool sysstall;
|
||||
bool nooverflow;
|
||||
u32 stall_ctrl;
|
||||
u8 ts_size;
|
||||
u32 ts_ctrl;
|
||||
bool syncpr;
|
||||
u32 syncfreq;
|
||||
bool trccci;
|
||||
u8 ccsize;
|
||||
u8 ccitmin;
|
||||
u32 ccctlr;
|
||||
bool trcbb;
|
||||
u32 bb_ctrl;
|
||||
bool q_support;
|
||||
u32 vinst_ctrl;
|
||||
u32 viiectlr;
|
||||
u32 vissctlr;
|
||||
u32 vipcssctlr;
|
||||
u8 seq_idx;
|
||||
u32 seq_ctrl[ETM_MAX_SEQ_STATES];
|
||||
u32 seq_rst;
|
||||
u32 seq_state;
|
||||
u8 cntr_idx;
|
||||
u32 cntrldvr[ETMv4_MAX_CNTR];
|
||||
u32 cntr_ctrl[ETMv4_MAX_CNTR];
|
||||
u32 cntr_val[ETMv4_MAX_CNTR];
|
||||
u8 res_idx;
|
||||
u32 res_ctrl[ETM_MAX_RES_SEL];
|
||||
u32 ss_ctrl[ETM_MAX_SS_CMP];
|
||||
u32 ss_status[ETM_MAX_SS_CMP];
|
||||
u32 ss_pe_cmp[ETM_MAX_SS_CMP];
|
||||
u8 addr_idx;
|
||||
u64 addr_val[ETM_MAX_SINGLE_ADDR_CMP];
|
||||
u64 addr_acc[ETM_MAX_SINGLE_ADDR_CMP];
|
||||
u8 addr_type[ETM_MAX_SINGLE_ADDR_CMP];
|
||||
u8 ctxid_idx;
|
||||
u8 ctxid_size;
|
||||
u64 ctxid_val[ETMv4_MAX_CTXID_CMP];
|
||||
u32 ctxid_mask0;
|
||||
u32 ctxid_mask1;
|
||||
u8 vmid_idx;
|
||||
u8 vmid_size;
|
||||
u64 vmid_val[ETM_MAX_VMID_CMP];
|
||||
u32 vmid_mask0;
|
||||
u32 vmid_mask1;
|
||||
u8 s_ex_level;
|
||||
u8 ns_ex_level;
|
||||
u32 ext_inp;
|
||||
};
|
||||
|
||||
/* Address comparator access types */
|
||||
enum etm_addr_acctype {
|
||||
ETM_INSTR_ADDR,
|
||||
ETM_DATA_LOAD_ADDR,
|
||||
ETM_DATA_STORE_ADDR,
|
||||
ETM_DATA_LOAD_STORE_ADDR,
|
||||
};
|
||||
|
||||
/* Address comparator context types */
|
||||
enum etm_addr_ctxtype {
|
||||
ETM_CTX_NONE,
|
||||
ETM_CTX_CTXID,
|
||||
ETM_CTX_VMID,
|
||||
ETM_CTX_CTXID_VMID,
|
||||
};
|
||||
|
||||
enum etm_addr_type {
|
||||
ETM_ADDR_TYPE_NONE,
|
||||
ETM_ADDR_TYPE_SINGLE,
|
||||
ETM_ADDR_TYPE_RANGE,
|
||||
ETM_ADDR_TYPE_START,
|
||||
ETM_ADDR_TYPE_STOP,
|
||||
};
|
||||
#endif
|
@ -18,9 +18,10 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/coresight.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include "coresight-priv.h"
|
||||
|
||||
@ -35,15 +36,15 @@
|
||||
* struct funnel_drvdata - specifics associated to a funnel component
|
||||
* @base: memory mapped base address for this component.
|
||||
* @dev: the device entity associated to this component.
|
||||
* @atclk: optional clock for the core parts of the funnel.
|
||||
* @csdev: component vitals needed by the framework.
|
||||
* @clk: the clock this component is associated to.
|
||||
* @priority: port selection order.
|
||||
*/
|
||||
struct funnel_drvdata {
|
||||
void __iomem *base;
|
||||
struct device *dev;
|
||||
struct clk *atclk;
|
||||
struct coresight_device *csdev;
|
||||
struct clk *clk;
|
||||
unsigned long priority;
|
||||
};
|
||||
|
||||
@ -67,12 +68,8 @@ static int funnel_enable(struct coresight_device *csdev, int inport,
|
||||
int outport)
|
||||
{
|
||||
struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_runtime_get_sync(drvdata->dev);
|
||||
funnel_enable_hw(drvdata, inport);
|
||||
|
||||
dev_info(drvdata->dev, "FUNNEL inport %d enabled\n", inport);
|
||||
@ -98,8 +95,7 @@ static void funnel_disable(struct coresight_device *csdev, int inport,
|
||||
struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
funnel_disable_hw(drvdata, inport);
|
||||
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
pm_runtime_put(drvdata->dev);
|
||||
|
||||
dev_info(drvdata->dev, "FUNNEL inport %d disabled\n", inport);
|
||||
}
|
||||
@ -153,16 +149,14 @@ static u32 get_funnel_ctrl_hw(struct funnel_drvdata *drvdata)
|
||||
static ssize_t funnel_ctrl_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
pm_runtime_get_sync(drvdata->dev);
|
||||
|
||||
val = get_funnel_ctrl_hw(drvdata);
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
|
||||
pm_runtime_put(drvdata->dev);
|
||||
|
||||
return sprintf(buf, "%#x\n", val);
|
||||
}
|
||||
@ -177,6 +171,7 @@ ATTRIBUTE_GROUPS(coresight_funnel);
|
||||
|
||||
static int funnel_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
{
|
||||
int ret;
|
||||
void __iomem *base;
|
||||
struct device *dev = &adev->dev;
|
||||
struct coresight_platform_data *pdata = NULL;
|
||||
@ -197,6 +192,12 @@ static int funnel_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
return -ENOMEM;
|
||||
|
||||
drvdata->dev = &adev->dev;
|
||||
drvdata->atclk = devm_clk_get(&adev->dev, "atclk"); /* optional */
|
||||
if (!IS_ERR(drvdata->atclk)) {
|
||||
ret = clk_prepare_enable(drvdata->atclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
dev_set_drvdata(dev, drvdata);
|
||||
|
||||
/* Validity for the resource is already checked by the AMBA core */
|
||||
@ -205,8 +206,7 @@ static int funnel_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
return PTR_ERR(base);
|
||||
|
||||
drvdata->base = base;
|
||||
|
||||
drvdata->clk = adev->pclk;
|
||||
pm_runtime_put(&adev->dev);
|
||||
|
||||
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
|
||||
if (!desc)
|
||||
@ -234,6 +234,32 @@ static int funnel_remove(struct amba_device *adev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int funnel_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct funnel_drvdata *drvdata = dev_get_drvdata(dev);
|
||||
|
||||
if (drvdata && !IS_ERR(drvdata->atclk))
|
||||
clk_disable_unprepare(drvdata->atclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int funnel_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct funnel_drvdata *drvdata = dev_get_drvdata(dev);
|
||||
|
||||
if (drvdata && !IS_ERR(drvdata->atclk))
|
||||
clk_prepare_enable(drvdata->atclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops funnel_dev_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(funnel_runtime_suspend, funnel_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct amba_id funnel_ids[] = {
|
||||
{
|
||||
.id = 0x0003b908,
|
||||
@ -246,6 +272,7 @@ static struct amba_driver funnel_driver = {
|
||||
.drv = {
|
||||
.name = "coresight-funnel",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &funnel_dev_pm_ops,
|
||||
},
|
||||
.probe = funnel_probe,
|
||||
.remove = funnel_remove,
|
||||
|
215
drivers/hwtracing/coresight/coresight-replicator-qcom.c
Normal file
215
drivers/hwtracing/coresight/coresight-replicator-qcom.c
Normal file
@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only 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/amba/bus.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/coresight.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "coresight-priv.h"
|
||||
|
||||
#define REPLICATOR_IDFILTER0 0x000
|
||||
#define REPLICATOR_IDFILTER1 0x004
|
||||
|
||||
/**
|
||||
* struct replicator_state - specifics associated to a replicator component
|
||||
* @base: memory mapped base address for this component.
|
||||
* @dev: the device entity associated with this component
|
||||
* @atclk: optional clock for the core parts of the replicator.
|
||||
* @csdev: component vitals needed by the framework
|
||||
*/
|
||||
struct replicator_state {
|
||||
void __iomem *base;
|
||||
struct device *dev;
|
||||
struct clk *atclk;
|
||||
struct coresight_device *csdev;
|
||||
};
|
||||
|
||||
static int replicator_enable(struct coresight_device *csdev, int inport,
|
||||
int outport)
|
||||
{
|
||||
struct replicator_state *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
pm_runtime_get_sync(drvdata->dev);
|
||||
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
/*
|
||||
* Ensure that the other port is disabled
|
||||
* 0x00 - passing through the replicator unimpeded
|
||||
* 0xff - disable (or impede) the flow of ATB data
|
||||
*/
|
||||
if (outport == 0) {
|
||||
writel_relaxed(0x00, drvdata->base + REPLICATOR_IDFILTER0);
|
||||
writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER1);
|
||||
} else {
|
||||
writel_relaxed(0x00, drvdata->base + REPLICATOR_IDFILTER1);
|
||||
writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER0);
|
||||
}
|
||||
|
||||
CS_LOCK(drvdata->base);
|
||||
|
||||
dev_info(drvdata->dev, "REPLICATOR enabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void replicator_disable(struct coresight_device *csdev, int inport,
|
||||
int outport)
|
||||
{
|
||||
struct replicator_state *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
/* disable the flow of ATB data through port */
|
||||
if (outport == 0)
|
||||
writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER0);
|
||||
else
|
||||
writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER1);
|
||||
|
||||
CS_LOCK(drvdata->base);
|
||||
|
||||
pm_runtime_put(drvdata->dev);
|
||||
|
||||
dev_info(drvdata->dev, "REPLICATOR disabled\n");
|
||||
}
|
||||
|
||||
static const struct coresight_ops_link replicator_link_ops = {
|
||||
.enable = replicator_enable,
|
||||
.disable = replicator_disable,
|
||||
};
|
||||
|
||||
static const struct coresight_ops replicator_cs_ops = {
|
||||
.link_ops = &replicator_link_ops,
|
||||
};
|
||||
|
||||
static int replicator_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct device *dev = &adev->dev;
|
||||
struct resource *res = &adev->res;
|
||||
struct coresight_platform_data *pdata = NULL;
|
||||
struct replicator_state *drvdata;
|
||||
struct coresight_desc *desc;
|
||||
struct device_node *np = adev->dev.of_node;
|
||||
void __iomem *base;
|
||||
|
||||
if (np) {
|
||||
pdata = of_get_coresight_platform_data(dev, np);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
adev->dev.platform_data = pdata;
|
||||
}
|
||||
|
||||
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
|
||||
drvdata->dev = &adev->dev;
|
||||
drvdata->atclk = devm_clk_get(&adev->dev, "atclk"); /* optional */
|
||||
if (!IS_ERR(drvdata->atclk)) {
|
||||
ret = clk_prepare_enable(drvdata->atclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Validity for the resource is already checked by the AMBA core */
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
drvdata->base = base;
|
||||
dev_set_drvdata(dev, drvdata);
|
||||
pm_runtime_put(&adev->dev);
|
||||
|
||||
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
|
||||
if (!desc)
|
||||
return -ENOMEM;
|
||||
|
||||
desc->type = CORESIGHT_DEV_TYPE_LINK;
|
||||
desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT;
|
||||
desc->ops = &replicator_cs_ops;
|
||||
desc->pdata = adev->dev.platform_data;
|
||||
desc->dev = &adev->dev;
|
||||
drvdata->csdev = coresight_register(desc);
|
||||
if (IS_ERR(drvdata->csdev))
|
||||
return PTR_ERR(drvdata->csdev);
|
||||
|
||||
dev_info(dev, "%s initialized\n", (char *)id->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int replicator_remove(struct amba_device *adev)
|
||||
{
|
||||
struct replicator_state *drvdata = amba_get_drvdata(adev);
|
||||
|
||||
pm_runtime_disable(&adev->dev);
|
||||
coresight_unregister(drvdata->csdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int replicator_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct replicator_state *drvdata = dev_get_drvdata(dev);
|
||||
|
||||
if (drvdata && !IS_ERR(drvdata->atclk))
|
||||
clk_disable_unprepare(drvdata->atclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int replicator_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct replicator_state *drvdata = dev_get_drvdata(dev);
|
||||
|
||||
if (drvdata && !IS_ERR(drvdata->atclk))
|
||||
clk_prepare_enable(drvdata->atclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops replicator_dev_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(replicator_runtime_suspend,
|
||||
replicator_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static struct amba_id replicator_ids[] = {
|
||||
{
|
||||
.id = 0x0003b909,
|
||||
.mask = 0x0003ffff,
|
||||
.data = "REPLICATOR 1.0",
|
||||
},
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
static struct amba_driver replicator_driver = {
|
||||
.drv = {
|
||||
.name = "coresight-replicator-qcom",
|
||||
.pm = &replicator_dev_pm_ops,
|
||||
},
|
||||
.probe = replicator_probe,
|
||||
.remove = replicator_remove,
|
||||
.id_table = replicator_ids,
|
||||
};
|
||||
|
||||
module_amba_driver(replicator_driver);
|
@ -18,6 +18,7 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/coresight.h>
|
||||
@ -27,10 +28,12 @@
|
||||
/**
|
||||
* struct replicator_drvdata - specifics associated to a replicator component
|
||||
* @dev: the device entity associated with this component
|
||||
* @atclk: optional clock for the core parts of the replicator.
|
||||
* @csdev: component vitals needed by the framework
|
||||
*/
|
||||
struct replicator_drvdata {
|
||||
struct device *dev;
|
||||
struct clk *atclk;
|
||||
struct coresight_device *csdev;
|
||||
};
|
||||
|
||||
@ -39,6 +42,7 @@ static int replicator_enable(struct coresight_device *csdev, int inport,
|
||||
{
|
||||
struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
pm_runtime_get_sync(drvdata->dev);
|
||||
dev_info(drvdata->dev, "REPLICATOR enabled\n");
|
||||
return 0;
|
||||
}
|
||||
@ -48,6 +52,7 @@ static void replicator_disable(struct coresight_device *csdev, int inport,
|
||||
{
|
||||
struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
pm_runtime_put(drvdata->dev);
|
||||
dev_info(drvdata->dev, "REPLICATOR disabled\n");
|
||||
}
|
||||
|
||||
@ -62,6 +67,7 @@ static const struct coresight_ops replicator_cs_ops = {
|
||||
|
||||
static int replicator_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct coresight_platform_data *pdata = NULL;
|
||||
struct replicator_drvdata *drvdata;
|
||||
@ -80,11 +86,22 @@ static int replicator_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
drvdata->dev = &pdev->dev;
|
||||
drvdata->atclk = devm_clk_get(&pdev->dev, "atclk"); /* optional */
|
||||
if (!IS_ERR(drvdata->atclk)) {
|
||||
ret = clk_prepare_enable(drvdata->atclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
platform_set_drvdata(pdev, drvdata);
|
||||
|
||||
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
|
||||
if (!desc)
|
||||
return -ENOMEM;
|
||||
if (!desc) {
|
||||
ret = -ENOMEM;
|
||||
goto out_disable_pm;
|
||||
}
|
||||
|
||||
desc->type = CORESIGHT_DEV_TYPE_LINK;
|
||||
desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT;
|
||||
@ -92,11 +109,23 @@ static int replicator_probe(struct platform_device *pdev)
|
||||
desc->pdata = pdev->dev.platform_data;
|
||||
desc->dev = &pdev->dev;
|
||||
drvdata->csdev = coresight_register(desc);
|
||||
if (IS_ERR(drvdata->csdev))
|
||||
return PTR_ERR(drvdata->csdev);
|
||||
if (IS_ERR(drvdata->csdev)) {
|
||||
ret = PTR_ERR(drvdata->csdev);
|
||||
goto out_disable_pm;
|
||||
}
|
||||
|
||||
pm_runtime_put(&pdev->dev);
|
||||
|
||||
dev_info(dev, "REPLICATOR initialized\n");
|
||||
return 0;
|
||||
|
||||
out_disable_pm:
|
||||
if (!IS_ERR(drvdata->atclk))
|
||||
clk_disable_unprepare(drvdata->atclk);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int replicator_remove(struct platform_device *pdev)
|
||||
@ -104,9 +133,42 @@ static int replicator_remove(struct platform_device *pdev)
|
||||
struct replicator_drvdata *drvdata = platform_get_drvdata(pdev);
|
||||
|
||||
coresight_unregister(drvdata->csdev);
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
if (!IS_ERR(drvdata->atclk))
|
||||
clk_disable_unprepare(drvdata->atclk);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int replicator_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct replicator_drvdata *drvdata = dev_get_drvdata(dev);
|
||||
|
||||
if (drvdata && !IS_ERR(drvdata->atclk))
|
||||
clk_disable_unprepare(drvdata->atclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int replicator_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct replicator_drvdata *drvdata = dev_get_drvdata(dev);
|
||||
|
||||
if (drvdata && !IS_ERR(drvdata->atclk))
|
||||
clk_prepare_enable(drvdata->atclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops replicator_dev_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(replicator_runtime_suspend,
|
||||
replicator_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id replicator_match[] = {
|
||||
{.compatible = "arm,coresight-replicator"},
|
||||
{}
|
||||
@ -118,6 +180,7 @@ static struct platform_driver replicator_driver = {
|
||||
.driver = {
|
||||
.name = "coresight-replicator",
|
||||
.of_match_table = replicator_match,
|
||||
.pm = &replicator_dev_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/coresight.h>
|
||||
#include <linux/amba/bus.h>
|
||||
@ -104,7 +104,6 @@ enum tmc_mem_intf_width {
|
||||
* @dev: the device entity associated to this component.
|
||||
* @csdev: component vitals needed by the framework.
|
||||
* @miscdev: specifics to handle "/dev/xyz.tmc" entry.
|
||||
* @clk: the clock this component is associated to.
|
||||
* @spinlock: only one at a time pls.
|
||||
* @read_count: manages preparation of buffer for reading.
|
||||
* @buf: area of memory where trace data get sent.
|
||||
@ -120,7 +119,6 @@ struct tmc_drvdata {
|
||||
struct device *dev;
|
||||
struct coresight_device *csdev;
|
||||
struct miscdevice miscdev;
|
||||
struct clk *clk;
|
||||
spinlock_t spinlock;
|
||||
int read_count;
|
||||
bool reading;
|
||||
@ -242,17 +240,14 @@ static void tmc_etf_enable_hw(struct tmc_drvdata *drvdata)
|
||||
|
||||
static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
pm_runtime_get_sync(drvdata->dev);
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
if (drvdata->reading) {
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
pm_runtime_put(drvdata->dev);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
@ -386,7 +381,7 @@ out:
|
||||
drvdata->enable = false;
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
pm_runtime_put(drvdata->dev);
|
||||
|
||||
dev_info(drvdata->dev, "TMC disabled\n");
|
||||
}
|
||||
@ -568,17 +563,13 @@ static const struct file_operations tmc_fops = {
|
||||
static ssize_t status_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
u32 tmc_rsz, tmc_sts, tmc_rrp, tmc_rwp, tmc_trg;
|
||||
u32 tmc_ctl, tmc_ffsr, tmc_ffcr, tmc_mode, tmc_pscr;
|
||||
u32 devid;
|
||||
struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
pm_runtime_get_sync(drvdata->dev);
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
@ -596,8 +587,7 @@ static ssize_t status_show(struct device *dev,
|
||||
|
||||
CS_LOCK(drvdata->base);
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
pm_runtime_put(drvdata->dev);
|
||||
|
||||
return sprintf(buf,
|
||||
"Depth:\t\t0x%x\n"
|
||||
@ -613,7 +603,7 @@ static ssize_t status_show(struct device *dev,
|
||||
"DEVID:\t\t0x%x\n",
|
||||
tmc_rsz, tmc_sts, tmc_rrp, tmc_rwp, tmc_trg,
|
||||
tmc_ctl, tmc_ffsr, tmc_ffcr, tmc_mode, tmc_pscr, devid);
|
||||
out:
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
static DEVICE_ATTR_RO(status);
|
||||
@ -700,11 +690,6 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
|
||||
spin_lock_init(&drvdata->spinlock);
|
||||
|
||||
drvdata->clk = adev->pclk;
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
|
||||
drvdata->config_type = BMVAL(devid, 6, 7);
|
||||
|
||||
@ -719,7 +704,7 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
drvdata->size = readl_relaxed(drvdata->base + TMC_RSZ) * 4;
|
||||
}
|
||||
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
pm_runtime_put(&adev->dev);
|
||||
|
||||
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
|
||||
drvdata->vaddr = dma_alloc_coherent(dev, drvdata->size,
|
||||
|
@ -17,9 +17,10 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/coresight.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include "coresight-priv.h"
|
||||
|
||||
@ -50,14 +51,14 @@
|
||||
/**
|
||||
* @base: memory mapped base address for this component.
|
||||
* @dev: the device entity associated to this component.
|
||||
* @atclk: optional clock for the core parts of the TPIU.
|
||||
* @csdev: component vitals needed by the framework.
|
||||
* @clk: the clock this component is associated to.
|
||||
*/
|
||||
struct tpiu_drvdata {
|
||||
void __iomem *base;
|
||||
struct device *dev;
|
||||
struct clk *atclk;
|
||||
struct coresight_device *csdev;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static void tpiu_enable_hw(struct tpiu_drvdata *drvdata)
|
||||
@ -72,12 +73,8 @@ static void tpiu_enable_hw(struct tpiu_drvdata *drvdata)
|
||||
static int tpiu_enable(struct coresight_device *csdev)
|
||||
{
|
||||
struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_runtime_get_sync(csdev->dev.parent);
|
||||
tpiu_enable_hw(drvdata);
|
||||
|
||||
dev_info(drvdata->dev, "TPIU enabled\n");
|
||||
@ -101,8 +98,7 @@ static void tpiu_disable(struct coresight_device *csdev)
|
||||
struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
tpiu_disable_hw(drvdata);
|
||||
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
pm_runtime_put(csdev->dev.parent);
|
||||
|
||||
dev_info(drvdata->dev, "TPIU disabled\n");
|
||||
}
|
||||
@ -139,6 +135,12 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
return -ENOMEM;
|
||||
|
||||
drvdata->dev = &adev->dev;
|
||||
drvdata->atclk = devm_clk_get(&adev->dev, "atclk"); /* optional */
|
||||
if (!IS_ERR(drvdata->atclk)) {
|
||||
ret = clk_prepare_enable(drvdata->atclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
dev_set_drvdata(dev, drvdata);
|
||||
|
||||
/* Validity for the resource is already checked by the AMBA core */
|
||||
@ -148,15 +150,10 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
|
||||
drvdata->base = base;
|
||||
|
||||
drvdata->clk = adev->pclk;
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Disable tpiu to support older devices */
|
||||
tpiu_disable_hw(drvdata);
|
||||
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
pm_runtime_put(&adev->dev);
|
||||
|
||||
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
|
||||
if (!desc)
|
||||
@ -183,11 +180,41 @@ static int tpiu_remove(struct amba_device *adev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int tpiu_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct tpiu_drvdata *drvdata = dev_get_drvdata(dev);
|
||||
|
||||
if (drvdata && !IS_ERR(drvdata->atclk))
|
||||
clk_disable_unprepare(drvdata->atclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpiu_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct tpiu_drvdata *drvdata = dev_get_drvdata(dev);
|
||||
|
||||
if (drvdata && !IS_ERR(drvdata->atclk))
|
||||
clk_prepare_enable(drvdata->atclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops tpiu_dev_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(tpiu_runtime_suspend, tpiu_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct amba_id tpiu_ids[] = {
|
||||
{
|
||||
.id = 0x0003b912,
|
||||
.mask = 0x0003ffff,
|
||||
},
|
||||
{
|
||||
.id = 0x0004b912,
|
||||
.mask = 0x0007ffff,
|
||||
},
|
||||
{ 0, 0},
|
||||
};
|
||||
|
||||
@ -195,6 +222,7 @@ static struct amba_driver tpiu_driver = {
|
||||
.drv = {
|
||||
.name = "coresight-tpiu",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &tpiu_dev_pm_ops,
|
||||
},
|
||||
.probe = tpiu_probe,
|
||||
.remove = tpiu_remove,
|
||||
|
@ -37,7 +37,7 @@ of_coresight_get_endpoint_device(struct device_node *endpoint)
|
||||
struct device *dev = NULL;
|
||||
|
||||
/*
|
||||
* If we have a non-configuable replicator, it will be found on the
|
||||
* If we have a non-configurable replicator, it will be found on the
|
||||
* platform bus.
|
||||
*/
|
||||
dev = bus_find_device(&platform_bus_type, NULL,
|
||||
|
@ -46,6 +46,9 @@ struct i2c_par {
|
||||
|
||||
static LIST_HEAD(adapter_list);
|
||||
static DEFINE_MUTEX(adapter_list_lock);
|
||||
#define MAX_DEVICE 4
|
||||
static int parport[MAX_DEVICE] = {0, -1, -1, -1};
|
||||
|
||||
|
||||
/* ----- Low-level parallel port access ----------------------------------- */
|
||||
|
||||
@ -163,17 +166,34 @@ static void i2c_parport_irq(void *data)
|
||||
static void i2c_parport_attach(struct parport *port)
|
||||
{
|
||||
struct i2c_par *adapter;
|
||||
int i;
|
||||
struct pardev_cb i2c_parport_cb;
|
||||
|
||||
for (i = 0; i < MAX_DEVICE; i++) {
|
||||
if (parport[i] == -1)
|
||||
continue;
|
||||
if (port->number == parport[i])
|
||||
break;
|
||||
}
|
||||
if (i == MAX_DEVICE) {
|
||||
pr_debug("i2c-parport: Not using parport%d.\n", port->number);
|
||||
return;
|
||||
}
|
||||
|
||||
adapter = kzalloc(sizeof(struct i2c_par), GFP_KERNEL);
|
||||
if (adapter == NULL) {
|
||||
printk(KERN_ERR "i2c-parport: Failed to kzalloc\n");
|
||||
return;
|
||||
}
|
||||
memset(&i2c_parport_cb, 0, sizeof(i2c_parport_cb));
|
||||
i2c_parport_cb.flags = PARPORT_FLAG_EXCL;
|
||||
i2c_parport_cb.irq_func = i2c_parport_irq;
|
||||
i2c_parport_cb.private = adapter;
|
||||
|
||||
pr_debug("i2c-parport: attaching to %s\n", port->name);
|
||||
parport_disable_irq(port);
|
||||
adapter->pdev = parport_register_device(port, "i2c-parport",
|
||||
NULL, NULL, i2c_parport_irq, PARPORT_FLAG_EXCL, adapter);
|
||||
adapter->pdev = parport_register_dev_model(port, "i2c-parport",
|
||||
&i2c_parport_cb, i);
|
||||
if (!adapter->pdev) {
|
||||
printk(KERN_ERR "i2c-parport: Unable to register with parport\n");
|
||||
goto err_free;
|
||||
@ -267,9 +287,10 @@ static void i2c_parport_detach(struct parport *port)
|
||||
}
|
||||
|
||||
static struct parport_driver i2c_parport_driver = {
|
||||
.name = "i2c-parport",
|
||||
.attach = i2c_parport_attach,
|
||||
.detach = i2c_parport_detach,
|
||||
.name = "i2c-parport",
|
||||
.match_port = i2c_parport_attach,
|
||||
.detach = i2c_parport_detach,
|
||||
.devmodel = true,
|
||||
};
|
||||
|
||||
/* ----- Module loading, unloading and information ------------------------ */
|
||||
@ -298,5 +319,12 @@ MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
|
||||
MODULE_DESCRIPTION("I2C bus over parallel port");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_param_array(parport, int, NULL, 0);
|
||||
MODULE_PARM_DESC(parport,
|
||||
"List of parallel ports to bind to, by index.\n"
|
||||
" Atmost " __stringify(MAX_DEVICE) " devices are supported.\n"
|
||||
" Default is one device connected to parport0.\n"
|
||||
);
|
||||
|
||||
module_init(i2c_parport_init);
|
||||
module_exit(i2c_parport_exit);
|
||||
|
@ -520,7 +520,6 @@ source "drivers/misc/eeprom/Kconfig"
|
||||
source "drivers/misc/cb710/Kconfig"
|
||||
source "drivers/misc/ti-st/Kconfig"
|
||||
source "drivers/misc/lis3lv02d/Kconfig"
|
||||
source "drivers/misc/carma/Kconfig"
|
||||
source "drivers/misc/altera-stapl/Kconfig"
|
||||
source "drivers/misc/mei/Kconfig"
|
||||
source "drivers/misc/vmw_vmci/Kconfig"
|
||||
|
@ -44,7 +44,6 @@ obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o
|
||||
obj-$(CONFIG_PCH_PHUB) += pch_phub.o
|
||||
obj-y += ti-st/
|
||||
obj-y += lis3lv02d/
|
||||
obj-y += carma/
|
||||
obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
|
||||
obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/
|
||||
obj-$(CONFIG_INTEL_MEI) += mei/
|
||||
|
@ -1,15 +0,0 @@
|
||||
config CARMA_FPGA
|
||||
tristate "CARMA DATA-FPGA Access Driver"
|
||||
depends on FSL_SOC && PPC_83xx && HAS_DMA && FSL_DMA
|
||||
default n
|
||||
help
|
||||
Say Y here to include support for communicating with the data
|
||||
processing FPGAs on the OVRO CARMA board.
|
||||
|
||||
config CARMA_FPGA_PROGRAM
|
||||
tristate "CARMA DATA-FPGA Programmer"
|
||||
depends on FSL_SOC && PPC_83xx && HAS_DMA && FSL_DMA
|
||||
default n
|
||||
help
|
||||
Say Y here to include support for programming the data processing
|
||||
FPGAs on the OVRO CARMA board.
|
@ -1,2 +0,0 @@
|
||||
obj-$(CONFIG_CARMA_FPGA) += carma-fpga.o
|
||||
obj-$(CONFIG_CARMA_FPGA_PROGRAM) += carma-fpga-program.o
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -59,46 +59,29 @@ void mei_amthif_reset_params(struct mei_device *dev)
|
||||
* mei_amthif_host_init - mei initialization amthif client.
|
||||
*
|
||||
* @dev: the device structure
|
||||
* @me_cl: me client
|
||||
*
|
||||
* Return: 0 on success, <0 on failure.
|
||||
*/
|
||||
int mei_amthif_host_init(struct mei_device *dev)
|
||||
int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl)
|
||||
{
|
||||
struct mei_cl *cl = &dev->iamthif_cl;
|
||||
struct mei_me_client *me_cl;
|
||||
int ret;
|
||||
|
||||
dev->iamthif_state = MEI_IAMTHIF_IDLE;
|
||||
|
||||
mei_cl_init(cl, dev);
|
||||
|
||||
me_cl = mei_me_cl_by_uuid(dev, &mei_amthif_guid);
|
||||
if (!me_cl) {
|
||||
dev_info(dev->dev, "amthif: failed to find the client");
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
cl->me_client_id = me_cl->client_id;
|
||||
cl->cl_uuid = me_cl->props.protocol_name;
|
||||
|
||||
/* Assign iamthif_mtu to the value received from ME */
|
||||
|
||||
dev->iamthif_mtu = me_cl->props.max_msg_length;
|
||||
dev_dbg(dev->dev, "IAMTHIF_MTU = %d\n", dev->iamthif_mtu);
|
||||
|
||||
|
||||
ret = mei_cl_link(cl, MEI_IAMTHIF_HOST_CLIENT_ID);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "amthif: failed cl_link %d\n", ret);
|
||||
goto out;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mei_cl_connect(cl, NULL);
|
||||
ret = mei_cl_connect(cl, me_cl, NULL);
|
||||
|
||||
dev->iamthif_state = MEI_IAMTHIF_IDLE;
|
||||
|
||||
out:
|
||||
mei_me_cl_put(me_cl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -250,7 +233,6 @@ static int mei_amthif_read_start(struct mei_cl *cl, struct file *file)
|
||||
{
|
||||
struct mei_device *dev = cl->dev;
|
||||
struct mei_cl_cb *cb;
|
||||
size_t length = dev->iamthif_mtu;
|
||||
int rets;
|
||||
|
||||
cb = mei_io_cb_init(cl, MEI_FOP_READ, file);
|
||||
@ -259,7 +241,7 @@ static int mei_amthif_read_start(struct mei_cl *cl, struct file *file)
|
||||
goto err;
|
||||
}
|
||||
|
||||
rets = mei_io_cb_alloc_buf(cb, length);
|
||||
rets = mei_io_cb_alloc_buf(cb, mei_cl_mtu(cl));
|
||||
if (rets)
|
||||
goto err;
|
||||
|
||||
|
@ -35,18 +35,30 @@ static int mei_cl_device_match(struct device *dev, struct device_driver *drv)
|
||||
struct mei_cl_device *device = to_mei_cl_device(dev);
|
||||
struct mei_cl_driver *driver = to_mei_cl_driver(drv);
|
||||
const struct mei_cl_device_id *id;
|
||||
const uuid_le *uuid;
|
||||
const char *name;
|
||||
|
||||
if (!device)
|
||||
return 0;
|
||||
|
||||
uuid = mei_me_cl_uuid(device->me_cl);
|
||||
name = device->name;
|
||||
|
||||
if (!driver || !driver->id_table)
|
||||
return 0;
|
||||
|
||||
id = driver->id_table;
|
||||
|
||||
while (id->name[0]) {
|
||||
if (!strncmp(dev_name(dev), id->name, sizeof(id->name)))
|
||||
return 1;
|
||||
while (uuid_le_cmp(NULL_UUID_LE, id->uuid)) {
|
||||
|
||||
if (!uuid_le_cmp(*uuid, id->uuid)) {
|
||||
if (id->name[0]) {
|
||||
if (!strncmp(name, id->name, sizeof(id->name)))
|
||||
return 1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
id++;
|
||||
}
|
||||
@ -69,7 +81,7 @@ static int mei_cl_device_probe(struct device *dev)
|
||||
|
||||
dev_dbg(dev, "Device probe\n");
|
||||
|
||||
strlcpy(id.name, dev_name(dev), sizeof(id.name));
|
||||
strlcpy(id.name, device->name, sizeof(id.name));
|
||||
|
||||
return driver->probe(device, &id);
|
||||
}
|
||||
@ -97,18 +109,48 @@ static int mei_cl_device_remove(struct device *dev)
|
||||
return driver->remove(device);
|
||||
}
|
||||
|
||||
static ssize_t name_show(struct device *dev, struct device_attribute *a,
|
||||
char *buf)
|
||||
{
|
||||
struct mei_cl_device *device = to_mei_cl_device(dev);
|
||||
size_t len;
|
||||
|
||||
len = snprintf(buf, PAGE_SIZE, "%s", device->name);
|
||||
|
||||
return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
|
||||
}
|
||||
static DEVICE_ATTR_RO(name);
|
||||
|
||||
static ssize_t uuid_show(struct device *dev, struct device_attribute *a,
|
||||
char *buf)
|
||||
{
|
||||
struct mei_cl_device *device = to_mei_cl_device(dev);
|
||||
const uuid_le *uuid = mei_me_cl_uuid(device->me_cl);
|
||||
size_t len;
|
||||
|
||||
len = snprintf(buf, PAGE_SIZE, "%pUl", uuid);
|
||||
|
||||
return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
|
||||
}
|
||||
static DEVICE_ATTR_RO(uuid);
|
||||
|
||||
static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
|
||||
char *buf)
|
||||
{
|
||||
int len;
|
||||
struct mei_cl_device *device = to_mei_cl_device(dev);
|
||||
const uuid_le *uuid = mei_me_cl_uuid(device->me_cl);
|
||||
size_t len;
|
||||
|
||||
len = snprintf(buf, PAGE_SIZE, "mei:%s\n", dev_name(dev));
|
||||
len = snprintf(buf, PAGE_SIZE, "mei:%s:" MEI_CL_UUID_FMT ":",
|
||||
device->name, MEI_CL_UUID_ARGS(uuid->b));
|
||||
|
||||
return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
|
||||
}
|
||||
static DEVICE_ATTR_RO(modalias);
|
||||
|
||||
static struct attribute *mei_cl_dev_attrs[] = {
|
||||
&dev_attr_name.attr,
|
||||
&dev_attr_uuid.attr,
|
||||
&dev_attr_modalias.attr,
|
||||
NULL,
|
||||
};
|
||||
@ -116,7 +158,17 @@ ATTRIBUTE_GROUPS(mei_cl_dev);
|
||||
|
||||
static int mei_cl_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
{
|
||||
if (add_uevent_var(env, "MODALIAS=mei:%s", dev_name(dev)))
|
||||
struct mei_cl_device *device = to_mei_cl_device(dev);
|
||||
const uuid_le *uuid = mei_me_cl_uuid(device->me_cl);
|
||||
|
||||
if (add_uevent_var(env, "MEI_CL_UUID=%pUl", uuid))
|
||||
return -ENOMEM;
|
||||
|
||||
if (add_uevent_var(env, "MEI_CL_NAME=%s", device->name))
|
||||
return -ENOMEM;
|
||||
|
||||
if (add_uevent_var(env, "MODALIAS=mei:%s:" MEI_CL_UUID_FMT ":",
|
||||
device->name, MEI_CL_UUID_ARGS(uuid->b)))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
@ -133,7 +185,13 @@ static struct bus_type mei_cl_bus_type = {
|
||||
|
||||
static void mei_cl_dev_release(struct device *dev)
|
||||
{
|
||||
kfree(to_mei_cl_device(dev));
|
||||
struct mei_cl_device *device = to_mei_cl_device(dev);
|
||||
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
mei_me_cl_put(device->me_cl);
|
||||
kfree(device);
|
||||
}
|
||||
|
||||
static struct device_type mei_cl_device_type = {
|
||||
@ -141,45 +199,50 @@ static struct device_type mei_cl_device_type = {
|
||||
};
|
||||
|
||||
struct mei_cl *mei_cl_bus_find_cl_by_uuid(struct mei_device *dev,
|
||||
uuid_le uuid)
|
||||
uuid_le uuid)
|
||||
{
|
||||
struct mei_cl *cl;
|
||||
|
||||
list_for_each_entry(cl, &dev->device_list, device_link) {
|
||||
if (!uuid_le_cmp(uuid, cl->cl_uuid))
|
||||
if (cl->device && cl->device->me_cl &&
|
||||
!uuid_le_cmp(uuid, *mei_me_cl_uuid(cl->device->me_cl)))
|
||||
return cl;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct mei_cl_device *mei_cl_add_device(struct mei_device *dev,
|
||||
uuid_le uuid, char *name,
|
||||
struct mei_cl_ops *ops)
|
||||
struct mei_me_client *me_cl,
|
||||
struct mei_cl *cl,
|
||||
char *name)
|
||||
{
|
||||
struct mei_cl_device *device;
|
||||
struct mei_cl *cl;
|
||||
int status;
|
||||
|
||||
cl = mei_cl_bus_find_cl_by_uuid(dev, uuid);
|
||||
if (cl == NULL)
|
||||
return NULL;
|
||||
|
||||
device = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL);
|
||||
if (!device)
|
||||
return NULL;
|
||||
|
||||
device->cl = cl;
|
||||
device->ops = ops;
|
||||
device->me_cl = mei_me_cl_get(me_cl);
|
||||
if (!device->me_cl) {
|
||||
kfree(device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
device->cl = cl;
|
||||
device->dev.parent = dev->dev;
|
||||
device->dev.bus = &mei_cl_bus_type;
|
||||
device->dev.type = &mei_cl_device_type;
|
||||
|
||||
dev_set_name(&device->dev, "%s", name);
|
||||
strlcpy(device->name, name, sizeof(device->name));
|
||||
|
||||
dev_set_name(&device->dev, "mei:%s:%pUl", name, mei_me_cl_uuid(me_cl));
|
||||
|
||||
status = device_register(&device->dev);
|
||||
if (status) {
|
||||
dev_err(dev->dev, "Failed to register MEI device\n");
|
||||
mei_me_cl_put(device->me_cl);
|
||||
kfree(device);
|
||||
return NULL;
|
||||
}
|
||||
@ -224,11 +287,10 @@ void mei_cl_driver_unregister(struct mei_cl_driver *driver)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_cl_driver_unregister);
|
||||
|
||||
static ssize_t ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
|
||||
ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
|
||||
bool blocking)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_me_client *me_cl = NULL;
|
||||
struct mei_cl_cb *cb = NULL;
|
||||
ssize_t rets;
|
||||
|
||||
@ -244,13 +306,12 @@ static ssize_t ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
|
||||
}
|
||||
|
||||
/* Check if we have an ME client device */
|
||||
me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id);
|
||||
if (!me_cl) {
|
||||
if (!mei_me_cl_is_active(cl->me_cl)) {
|
||||
rets = -ENOTTY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (length > me_cl->props.max_msg_length) {
|
||||
if (length > mei_cl_mtu(cl)) {
|
||||
rets = -EFBIG;
|
||||
goto out;
|
||||
}
|
||||
@ -266,7 +327,6 @@ static ssize_t ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
|
||||
rets = mei_cl_write(cl, cb, blocking);
|
||||
|
||||
out:
|
||||
mei_me_cl_put(me_cl);
|
||||
mutex_unlock(&dev->device_lock);
|
||||
if (rets < 0)
|
||||
mei_io_cb_free(cb);
|
||||
@ -341,16 +401,6 @@ out:
|
||||
return rets;
|
||||
}
|
||||
|
||||
inline ssize_t __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length)
|
||||
{
|
||||
return ___mei_cl_send(cl, buf, length, 0);
|
||||
}
|
||||
|
||||
inline ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length)
|
||||
{
|
||||
return ___mei_cl_send(cl, buf, length, 1);
|
||||
}
|
||||
|
||||
ssize_t mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length)
|
||||
{
|
||||
struct mei_cl *cl = device->cl;
|
||||
@ -358,23 +408,17 @@ ssize_t mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length)
|
||||
if (cl == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
if (device->ops && device->ops->send)
|
||||
return device->ops->send(device, buf, length);
|
||||
|
||||
return __mei_cl_send(cl, buf, length);
|
||||
return __mei_cl_send(cl, buf, length, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_cl_send);
|
||||
|
||||
ssize_t mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length)
|
||||
{
|
||||
struct mei_cl *cl = device->cl;
|
||||
struct mei_cl *cl = device->cl;
|
||||
|
||||
if (cl == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
if (device->ops && device->ops->recv)
|
||||
return device->ops->recv(device, buf, length);
|
||||
|
||||
return __mei_cl_recv(cl, buf, length);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_cl_recv);
|
||||
@ -436,7 +480,13 @@ int mei_cl_enable_device(struct mei_cl_device *device)
|
||||
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
err = mei_cl_connect(cl, NULL);
|
||||
if (mei_cl_is_connected(cl)) {
|
||||
mutex_unlock(&dev->device_lock);
|
||||
dev_warn(dev->dev, "Already connected");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
err = mei_cl_connect(cl, device->me_cl, NULL);
|
||||
if (err < 0) {
|
||||
mutex_unlock(&dev->device_lock);
|
||||
dev_err(dev->dev, "Could not connect to the ME client");
|
||||
@ -449,10 +499,7 @@ int mei_cl_enable_device(struct mei_cl_device *device)
|
||||
if (device->event_cb)
|
||||
mei_cl_read_start(device->cl, 0, NULL);
|
||||
|
||||
if (!device->ops || !device->ops->enable)
|
||||
return 0;
|
||||
|
||||
return device->ops->enable(device);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_cl_enable_device);
|
||||
|
||||
@ -467,9 +514,6 @@ int mei_cl_disable_device(struct mei_cl_device *device)
|
||||
|
||||
dev = cl->dev;
|
||||
|
||||
if (device->ops && device->ops->disable)
|
||||
device->ops->disable(device);
|
||||
|
||||
device->event_cb = NULL;
|
||||
|
||||
mutex_lock(&dev->device_lock);
|
||||
@ -480,8 +524,6 @@ int mei_cl_disable_device(struct mei_cl_device *device)
|
||||
goto out;
|
||||
}
|
||||
|
||||
cl->state = MEI_FILE_DISCONNECTING;
|
||||
|
||||
err = mei_cl_disconnect(cl);
|
||||
if (err < 0) {
|
||||
dev_err(dev->dev, "Could not disconnect from the ME client");
|
||||
|
@ -83,7 +83,7 @@ void mei_me_cl_put(struct mei_me_client *me_cl)
|
||||
}
|
||||
|
||||
/**
|
||||
* __mei_me_cl_del - delete me client form the list and decrease
|
||||
* __mei_me_cl_del - delete me client from the list and decrease
|
||||
* reference counter
|
||||
*
|
||||
* @dev: mei device
|
||||
@ -96,10 +96,24 @@ static void __mei_me_cl_del(struct mei_device *dev, struct mei_me_client *me_cl)
|
||||
if (!me_cl)
|
||||
return;
|
||||
|
||||
list_del(&me_cl->list);
|
||||
list_del_init(&me_cl->list);
|
||||
mei_me_cl_put(me_cl);
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_me_cl_del - delete me client from the list and decrease
|
||||
* reference counter
|
||||
*
|
||||
* @dev: mei device
|
||||
* @me_cl: me client
|
||||
*/
|
||||
void mei_me_cl_del(struct mei_device *dev, struct mei_me_client *me_cl)
|
||||
{
|
||||
down_write(&dev->me_clients_rwsem);
|
||||
__mei_me_cl_del(dev, me_cl);
|
||||
up_write(&dev->me_clients_rwsem);
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_me_cl_add - add me client to the list
|
||||
*
|
||||
@ -317,7 +331,7 @@ static inline bool mei_cl_cmp_id(const struct mei_cl *cl1,
|
||||
{
|
||||
return cl1 && cl2 &&
|
||||
(cl1->host_client_id == cl2->host_client_id) &&
|
||||
(cl1->me_client_id == cl2->me_client_id);
|
||||
(mei_cl_me_id(cl1) == mei_cl_me_id(cl2));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -546,6 +560,7 @@ void mei_cl_init(struct mei_cl *cl, struct mei_device *dev)
|
||||
INIT_LIST_HEAD(&cl->link);
|
||||
INIT_LIST_HEAD(&cl->device_link);
|
||||
cl->writing_state = MEI_IDLE;
|
||||
cl->state = MEI_FILE_INITIALIZING;
|
||||
cl->dev = dev;
|
||||
}
|
||||
|
||||
@ -619,7 +634,7 @@ int mei_cl_link(struct mei_cl *cl, int id)
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_cl_unlink - remove me_cl from the list
|
||||
* mei_cl_unlink - remove host client from the list
|
||||
*
|
||||
* @cl: host client
|
||||
*
|
||||
@ -667,17 +682,17 @@ void mei_host_client_init(struct work_struct *work)
|
||||
|
||||
me_cl = mei_me_cl_by_uuid(dev, &mei_amthif_guid);
|
||||
if (me_cl)
|
||||
mei_amthif_host_init(dev);
|
||||
mei_amthif_host_init(dev, me_cl);
|
||||
mei_me_cl_put(me_cl);
|
||||
|
||||
me_cl = mei_me_cl_by_uuid(dev, &mei_wd_guid);
|
||||
if (me_cl)
|
||||
mei_wd_host_init(dev);
|
||||
mei_wd_host_init(dev, me_cl);
|
||||
mei_me_cl_put(me_cl);
|
||||
|
||||
me_cl = mei_me_cl_by_uuid(dev, &mei_nfc_guid);
|
||||
if (me_cl)
|
||||
mei_nfc_host_init(dev);
|
||||
mei_nfc_host_init(dev, me_cl);
|
||||
mei_me_cl_put(me_cl);
|
||||
|
||||
|
||||
@ -699,7 +714,7 @@ void mei_host_client_init(struct work_struct *work)
|
||||
bool mei_hbuf_acquire(struct mei_device *dev)
|
||||
{
|
||||
if (mei_pg_state(dev) == MEI_PG_ON ||
|
||||
dev->pg_event == MEI_PG_EVENT_WAIT) {
|
||||
mei_pg_in_transition(dev)) {
|
||||
dev_dbg(dev->dev, "device is in pg\n");
|
||||
return false;
|
||||
}
|
||||
@ -714,6 +729,120 @@ bool mei_hbuf_acquire(struct mei_device *dev)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_cl_set_disconnected - set disconnected state and clear
|
||||
* associated states and resources
|
||||
*
|
||||
* @cl: host client
|
||||
*/
|
||||
void mei_cl_set_disconnected(struct mei_cl *cl)
|
||||
{
|
||||
struct mei_device *dev = cl->dev;
|
||||
|
||||
if (cl->state == MEI_FILE_DISCONNECTED ||
|
||||
cl->state == MEI_FILE_INITIALIZING)
|
||||
return;
|
||||
|
||||
cl->state = MEI_FILE_DISCONNECTED;
|
||||
mei_io_list_flush(&dev->ctrl_rd_list, cl);
|
||||
mei_io_list_flush(&dev->ctrl_wr_list, cl);
|
||||
cl->mei_flow_ctrl_creds = 0;
|
||||
cl->timer_count = 0;
|
||||
|
||||
if (!cl->me_cl)
|
||||
return;
|
||||
|
||||
if (!WARN_ON(cl->me_cl->connect_count == 0))
|
||||
cl->me_cl->connect_count--;
|
||||
|
||||
if (cl->me_cl->connect_count == 0)
|
||||
cl->me_cl->mei_flow_ctrl_creds = 0;
|
||||
|
||||
mei_me_cl_put(cl->me_cl);
|
||||
cl->me_cl = NULL;
|
||||
}
|
||||
|
||||
static int mei_cl_set_connecting(struct mei_cl *cl, struct mei_me_client *me_cl)
|
||||
{
|
||||
if (!mei_me_cl_get(me_cl))
|
||||
return -ENOENT;
|
||||
|
||||
/* only one connection is allowed for fixed address clients */
|
||||
if (me_cl->props.fixed_address) {
|
||||
if (me_cl->connect_count) {
|
||||
mei_me_cl_put(me_cl);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
|
||||
cl->me_cl = me_cl;
|
||||
cl->state = MEI_FILE_CONNECTING;
|
||||
cl->me_cl->connect_count++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* mei_cl_send_disconnect - send disconnect request
|
||||
*
|
||||
* @cl: host client
|
||||
* @cb: callback block
|
||||
*
|
||||
* Return: 0, OK; otherwise, error.
|
||||
*/
|
||||
static int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
int ret;
|
||||
|
||||
dev = cl->dev;
|
||||
|
||||
ret = mei_hbm_cl_disconnect_req(dev, cl);
|
||||
cl->status = ret;
|
||||
if (ret) {
|
||||
cl->state = MEI_FILE_DISCONNECT_REPLY;
|
||||
return ret;
|
||||
}
|
||||
|
||||
list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
|
||||
cl->timer_count = MEI_CONNECT_TIMEOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_cl_irq_disconnect - processes close related operation from
|
||||
* interrupt thread context - send disconnect request
|
||||
*
|
||||
* @cl: client
|
||||
* @cb: callback block.
|
||||
* @cmpl_list: complete list.
|
||||
*
|
||||
* Return: 0, OK; otherwise, error.
|
||||
*/
|
||||
int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
struct mei_cl_cb *cmpl_list)
|
||||
{
|
||||
struct mei_device *dev = cl->dev;
|
||||
u32 msg_slots;
|
||||
int slots;
|
||||
int ret;
|
||||
|
||||
msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
|
||||
slots = mei_hbuf_empty_slots(dev);
|
||||
|
||||
if (slots < msg_slots)
|
||||
return -EMSGSIZE;
|
||||
|
||||
ret = mei_cl_send_disconnect(cl, cb);
|
||||
if (ret)
|
||||
list_move_tail(&cb->list, &cmpl_list->list);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* mei_cl_disconnect - disconnect host client from the me one
|
||||
*
|
||||
@ -736,9 +865,14 @@ int mei_cl_disconnect(struct mei_cl *cl)
|
||||
|
||||
cl_dbg(dev, cl, "disconnecting");
|
||||
|
||||
if (cl->state != MEI_FILE_DISCONNECTING)
|
||||
if (!mei_cl_is_connected(cl))
|
||||
return 0;
|
||||
|
||||
if (mei_cl_is_fixed_address(cl)) {
|
||||
mei_cl_set_disconnected(cl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rets = pm_runtime_get(dev->dev);
|
||||
if (rets < 0 && rets != -EINPROGRESS) {
|
||||
pm_runtime_put_noidle(dev->dev);
|
||||
@ -746,44 +880,41 @@ int mei_cl_disconnect(struct mei_cl *cl)
|
||||
return rets;
|
||||
}
|
||||
|
||||
cl->state = MEI_FILE_DISCONNECTING;
|
||||
|
||||
cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT, NULL);
|
||||
rets = cb ? 0 : -ENOMEM;
|
||||
if (rets)
|
||||
goto free;
|
||||
goto out;
|
||||
|
||||
cl_dbg(dev, cl, "add disconnect cb to control write list\n");
|
||||
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
|
||||
|
||||
if (mei_hbuf_acquire(dev)) {
|
||||
if (mei_hbm_cl_disconnect_req(dev, cl)) {
|
||||
rets = -ENODEV;
|
||||
rets = mei_cl_send_disconnect(cl, cb);
|
||||
if (rets) {
|
||||
cl_err(dev, cl, "failed to disconnect.\n");
|
||||
goto free;
|
||||
goto out;
|
||||
}
|
||||
cl->timer_count = MEI_CONNECT_TIMEOUT;
|
||||
mdelay(10); /* Wait for hardware disconnection ready */
|
||||
list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
|
||||
} else {
|
||||
cl_dbg(dev, cl, "add disconnect cb to control write list\n");
|
||||
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
|
||||
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
|
||||
wait_event_timeout(cl->wait,
|
||||
MEI_FILE_DISCONNECTED == cl->state,
|
||||
mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
|
||||
|
||||
wait_event_timeout(cl->wait, cl->state == MEI_FILE_DISCONNECT_REPLY,
|
||||
mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
if (MEI_FILE_DISCONNECTED == cl->state) {
|
||||
rets = 0;
|
||||
cl_dbg(dev, cl, "successfully disconnected from FW client.\n");
|
||||
} else {
|
||||
rets = cl->status;
|
||||
if (cl->state != MEI_FILE_DISCONNECT_REPLY) {
|
||||
cl_dbg(dev, cl, "timeout on disconnect from FW client.\n");
|
||||
rets = -ETIME;
|
||||
}
|
||||
|
||||
mei_io_list_flush(&dev->ctrl_rd_list, cl);
|
||||
mei_io_list_flush(&dev->ctrl_wr_list, cl);
|
||||
free:
|
||||
out:
|
||||
/* we disconnect also on error */
|
||||
mei_cl_set_disconnected(cl);
|
||||
if (!rets)
|
||||
cl_dbg(dev, cl, "successfully disconnected from FW client.\n");
|
||||
|
||||
cl_dbg(dev, cl, "rpm: autosuspend\n");
|
||||
pm_runtime_mark_last_busy(dev->dev);
|
||||
pm_runtime_put_autosuspend(dev->dev);
|
||||
@ -801,53 +932,119 @@ free:
|
||||
*
|
||||
* Return: true if other client is connected, false - otherwise.
|
||||
*/
|
||||
bool mei_cl_is_other_connecting(struct mei_cl *cl)
|
||||
static bool mei_cl_is_other_connecting(struct mei_cl *cl)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_cl *ocl; /* the other client */
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
return false;
|
||||
struct mei_cl_cb *cb;
|
||||
|
||||
dev = cl->dev;
|
||||
|
||||
list_for_each_entry(ocl, &dev->file_list, link) {
|
||||
if (ocl->state == MEI_FILE_CONNECTING &&
|
||||
ocl != cl &&
|
||||
cl->me_client_id == ocl->me_client_id)
|
||||
list_for_each_entry(cb, &dev->ctrl_rd_list.list, list) {
|
||||
if (cb->fop_type == MEI_FOP_CONNECT &&
|
||||
mei_cl_me_id(cl) == mei_cl_me_id(cb->cl))
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_cl_send_connect - send connect request
|
||||
*
|
||||
* @cl: host client
|
||||
* @cb: callback block
|
||||
*
|
||||
* Return: 0, OK; otherwise, error.
|
||||
*/
|
||||
static int mei_cl_send_connect(struct mei_cl *cl, struct mei_cl_cb *cb)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
int ret;
|
||||
|
||||
dev = cl->dev;
|
||||
|
||||
ret = mei_hbm_cl_connect_req(dev, cl);
|
||||
cl->status = ret;
|
||||
if (ret) {
|
||||
cl->state = MEI_FILE_DISCONNECT_REPLY;
|
||||
return ret;
|
||||
}
|
||||
|
||||
list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
|
||||
cl->timer_count = MEI_CONNECT_TIMEOUT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_cl_irq_connect - send connect request in irq_thread context
|
||||
*
|
||||
* @cl: host client
|
||||
* @cb: callback block
|
||||
* @cmpl_list: complete list
|
||||
*
|
||||
* Return: 0, OK; otherwise, error.
|
||||
*/
|
||||
int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
struct mei_cl_cb *cmpl_list)
|
||||
{
|
||||
struct mei_device *dev = cl->dev;
|
||||
u32 msg_slots;
|
||||
int slots;
|
||||
int rets;
|
||||
|
||||
msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
|
||||
slots = mei_hbuf_empty_slots(dev);
|
||||
|
||||
if (mei_cl_is_other_connecting(cl))
|
||||
return 0;
|
||||
|
||||
if (slots < msg_slots)
|
||||
return -EMSGSIZE;
|
||||
|
||||
rets = mei_cl_send_connect(cl, cb);
|
||||
if (rets)
|
||||
list_move_tail(&cb->list, &cmpl_list->list);
|
||||
|
||||
return rets;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_cl_connect - connect host client to the me one
|
||||
*
|
||||
* @cl: host client
|
||||
* @me_cl: me client
|
||||
* @file: pointer to file structure
|
||||
*
|
||||
* Locking: called under "dev->device_lock" lock
|
||||
*
|
||||
* Return: 0 on success, <0 on failure.
|
||||
*/
|
||||
int mei_cl_connect(struct mei_cl *cl, struct file *file)
|
||||
int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl,
|
||||
struct file *file)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_cl_cb *cb;
|
||||
int rets;
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
if (WARN_ON(!cl || !cl->dev || !me_cl))
|
||||
return -ENODEV;
|
||||
|
||||
dev = cl->dev;
|
||||
|
||||
rets = mei_cl_set_connecting(cl, me_cl);
|
||||
if (rets)
|
||||
return rets;
|
||||
|
||||
if (mei_cl_is_fixed_address(cl)) {
|
||||
cl->state = MEI_FILE_CONNECTED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
rets = pm_runtime_get(dev->dev);
|
||||
if (rets < 0 && rets != -EINPROGRESS) {
|
||||
pm_runtime_put_noidle(dev->dev);
|
||||
cl_err(dev, cl, "rpm: get failed %d\n", rets);
|
||||
return rets;
|
||||
goto nortpm;
|
||||
}
|
||||
|
||||
cb = mei_io_cb_init(cl, MEI_FOP_CONNECT, file);
|
||||
@ -855,45 +1052,40 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
|
||||
if (rets)
|
||||
goto out;
|
||||
|
||||
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
|
||||
|
||||
/* run hbuf acquire last so we don't have to undo */
|
||||
if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) {
|
||||
cl->state = MEI_FILE_CONNECTING;
|
||||
if (mei_hbm_cl_connect_req(dev, cl)) {
|
||||
rets = -ENODEV;
|
||||
rets = mei_cl_send_connect(cl, cb);
|
||||
if (rets)
|
||||
goto out;
|
||||
}
|
||||
cl->timer_count = MEI_CONNECT_TIMEOUT;
|
||||
list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
|
||||
} else {
|
||||
cl->state = MEI_FILE_INITIALIZING;
|
||||
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
wait_event_timeout(cl->wait,
|
||||
(cl->state == MEI_FILE_CONNECTED ||
|
||||
cl->state == MEI_FILE_DISCONNECTED),
|
||||
cl->state == MEI_FILE_DISCONNECT_REPLY),
|
||||
mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
if (!mei_cl_is_connected(cl)) {
|
||||
cl->state = MEI_FILE_DISCONNECTED;
|
||||
/* something went really wrong */
|
||||
/* timeout or something went really wrong */
|
||||
if (!cl->status)
|
||||
cl->status = -EFAULT;
|
||||
|
||||
mei_io_list_flush(&dev->ctrl_rd_list, cl);
|
||||
mei_io_list_flush(&dev->ctrl_wr_list, cl);
|
||||
}
|
||||
|
||||
rets = cl->status;
|
||||
|
||||
out:
|
||||
cl_dbg(dev, cl, "rpm: autosuspend\n");
|
||||
pm_runtime_mark_last_busy(dev->dev);
|
||||
pm_runtime_put_autosuspend(dev->dev);
|
||||
|
||||
mei_io_cb_free(cb);
|
||||
|
||||
nortpm:
|
||||
if (!mei_cl_is_connected(cl))
|
||||
mei_cl_set_disconnected(cl);
|
||||
|
||||
return rets;
|
||||
}
|
||||
|
||||
@ -934,36 +1126,29 @@ err:
|
||||
* @cl: private data of the file object
|
||||
*
|
||||
* Return: 1 if mei_flow_ctrl_creds >0, 0 - otherwise.
|
||||
* -ENOENT if mei_cl is not present
|
||||
* -EINVAL if single_recv_buf == 0
|
||||
*/
|
||||
int mei_cl_flow_ctrl_creds(struct mei_cl *cl)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_me_client *me_cl;
|
||||
int rets = 0;
|
||||
int rets;
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
if (WARN_ON(!cl || !cl->me_cl))
|
||||
return -EINVAL;
|
||||
|
||||
dev = cl->dev;
|
||||
|
||||
if (cl->mei_flow_ctrl_creds > 0)
|
||||
return 1;
|
||||
|
||||
me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id);
|
||||
if (!me_cl) {
|
||||
cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
|
||||
return -ENOENT;
|
||||
if (mei_cl_is_fixed_address(cl)) {
|
||||
rets = mei_cl_read_start(cl, mei_cl_mtu(cl), NULL);
|
||||
if (rets && rets != -EBUSY)
|
||||
return rets;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (me_cl->mei_flow_ctrl_creds > 0) {
|
||||
rets = 1;
|
||||
if (WARN_ON(me_cl->props.single_recv_buf == 0))
|
||||
rets = -EINVAL;
|
||||
if (mei_cl_is_single_recv_buf(cl)) {
|
||||
if (cl->me_cl->mei_flow_ctrl_creds > 0)
|
||||
return 1;
|
||||
}
|
||||
mei_me_cl_put(me_cl);
|
||||
return rets;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -973,43 +1158,26 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl)
|
||||
*
|
||||
* Return:
|
||||
* 0 on success
|
||||
* -ENOENT when me client is not found
|
||||
* -EINVAL when ctrl credits are <= 0
|
||||
*/
|
||||
int mei_cl_flow_ctrl_reduce(struct mei_cl *cl)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_me_client *me_cl;
|
||||
int rets;
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
if (WARN_ON(!cl || !cl->me_cl))
|
||||
return -EINVAL;
|
||||
|
||||
dev = cl->dev;
|
||||
if (mei_cl_is_fixed_address(cl))
|
||||
return 0;
|
||||
|
||||
me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id);
|
||||
if (!me_cl) {
|
||||
cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (me_cl->props.single_recv_buf) {
|
||||
if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0)) {
|
||||
rets = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
me_cl->mei_flow_ctrl_creds--;
|
||||
if (mei_cl_is_single_recv_buf(cl)) {
|
||||
if (WARN_ON(cl->me_cl->mei_flow_ctrl_creds <= 0))
|
||||
return -EINVAL;
|
||||
cl->me_cl->mei_flow_ctrl_creds--;
|
||||
} else {
|
||||
if (WARN_ON(cl->mei_flow_ctrl_creds <= 0)) {
|
||||
rets = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (WARN_ON(cl->mei_flow_ctrl_creds <= 0))
|
||||
return -EINVAL;
|
||||
cl->mei_flow_ctrl_creds--;
|
||||
}
|
||||
rets = 0;
|
||||
out:
|
||||
mei_me_cl_put(me_cl);
|
||||
return rets;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1025,7 +1193,6 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_cl_cb *cb;
|
||||
struct mei_me_client *me_cl;
|
||||
int rets;
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
@ -1040,27 +1207,29 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp)
|
||||
if (!list_empty(&cl->rd_pending))
|
||||
return -EBUSY;
|
||||
|
||||
me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id);
|
||||
if (!me_cl) {
|
||||
cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
|
||||
if (!mei_me_cl_is_active(cl->me_cl)) {
|
||||
cl_err(dev, cl, "no such me client\n");
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
/* always allocate at least client max message */
|
||||
length = max_t(size_t, length, me_cl->props.max_msg_length);
|
||||
mei_me_cl_put(me_cl);
|
||||
length = max_t(size_t, length, mei_cl_mtu(cl));
|
||||
cb = mei_cl_alloc_cb(cl, length, MEI_FOP_READ, fp);
|
||||
if (!cb)
|
||||
return -ENOMEM;
|
||||
|
||||
if (mei_cl_is_fixed_address(cl)) {
|
||||
list_add_tail(&cb->list, &cl->rd_pending);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rets = pm_runtime_get(dev->dev);
|
||||
if (rets < 0 && rets != -EINPROGRESS) {
|
||||
pm_runtime_put_noidle(dev->dev);
|
||||
cl_err(dev, cl, "rpm: get failed %d\n", rets);
|
||||
return rets;
|
||||
goto nortpm;
|
||||
}
|
||||
|
||||
cb = mei_cl_alloc_cb(cl, length, MEI_FOP_READ, fp);
|
||||
rets = cb ? 0 : -ENOMEM;
|
||||
if (rets)
|
||||
goto out;
|
||||
|
||||
if (mei_hbuf_acquire(dev)) {
|
||||
rets = mei_hbm_cl_flow_control_req(dev, cl);
|
||||
if (rets < 0)
|
||||
@ -1068,6 +1237,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp)
|
||||
|
||||
list_add_tail(&cb->list, &cl->rd_pending);
|
||||
} else {
|
||||
rets = 0;
|
||||
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
|
||||
}
|
||||
|
||||
@ -1075,7 +1245,7 @@ out:
|
||||
cl_dbg(dev, cl, "rpm: autosuspend\n");
|
||||
pm_runtime_mark_last_busy(dev->dev);
|
||||
pm_runtime_put_autosuspend(dev->dev);
|
||||
|
||||
nortpm:
|
||||
if (rets)
|
||||
mei_io_cb_free(cb);
|
||||
|
||||
@ -1102,6 +1272,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
u32 msg_slots;
|
||||
int slots;
|
||||
int rets;
|
||||
bool first_chunk;
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
return -ENODEV;
|
||||
@ -1110,7 +1281,9 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
|
||||
buf = &cb->buf;
|
||||
|
||||
rets = mei_cl_flow_ctrl_creds(cl);
|
||||
first_chunk = cb->buf_idx == 0;
|
||||
|
||||
rets = first_chunk ? mei_cl_flow_ctrl_creds(cl) : 1;
|
||||
if (rets < 0)
|
||||
return rets;
|
||||
|
||||
@ -1123,8 +1296,8 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
len = buf->size - cb->buf_idx;
|
||||
msg_slots = mei_data2slots(len);
|
||||
|
||||
mei_hdr.host_addr = cl->host_client_id;
|
||||
mei_hdr.me_addr = cl->me_client_id;
|
||||
mei_hdr.host_addr = mei_cl_host_addr(cl);
|
||||
mei_hdr.me_addr = mei_cl_me_id(cl);
|
||||
mei_hdr.reserved = 0;
|
||||
mei_hdr.internal = cb->internal;
|
||||
|
||||
@ -1157,12 +1330,14 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
cb->buf_idx += mei_hdr.length;
|
||||
cb->completed = mei_hdr.msg_complete == 1;
|
||||
|
||||
if (mei_hdr.msg_complete) {
|
||||
if (first_chunk) {
|
||||
if (mei_cl_flow_ctrl_reduce(cl))
|
||||
return -EIO;
|
||||
list_move_tail(&cb->list, &dev->write_waiting_list.list);
|
||||
}
|
||||
|
||||
if (mei_hdr.msg_complete)
|
||||
list_move_tail(&cb->list, &dev->write_waiting_list.list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1207,8 +1382,8 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
|
||||
cb->buf_idx = 0;
|
||||
cl->writing_state = MEI_IDLE;
|
||||
|
||||
mei_hdr.host_addr = cl->host_client_id;
|
||||
mei_hdr.me_addr = cl->me_client_id;
|
||||
mei_hdr.host_addr = mei_cl_host_addr(cl);
|
||||
mei_hdr.me_addr = mei_cl_me_id(cl);
|
||||
mei_hdr.reserved = 0;
|
||||
mei_hdr.msg_complete = 0;
|
||||
mei_hdr.internal = cb->internal;
|
||||
@ -1241,21 +1416,19 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
|
||||
if (rets)
|
||||
goto err;
|
||||
|
||||
rets = mei_cl_flow_ctrl_reduce(cl);
|
||||
if (rets)
|
||||
goto err;
|
||||
|
||||
cl->writing_state = MEI_WRITING;
|
||||
cb->buf_idx = mei_hdr.length;
|
||||
cb->completed = mei_hdr.msg_complete == 1;
|
||||
|
||||
out:
|
||||
if (mei_hdr.msg_complete) {
|
||||
rets = mei_cl_flow_ctrl_reduce(cl);
|
||||
if (rets < 0)
|
||||
goto err;
|
||||
|
||||
if (mei_hdr.msg_complete)
|
||||
list_add_tail(&cb->list, &dev->write_waiting_list.list);
|
||||
} else {
|
||||
else
|
||||
list_add_tail(&cb->list, &dev->write_list.list);
|
||||
}
|
||||
|
||||
|
||||
if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) {
|
||||
|
||||
@ -1289,20 +1462,36 @@ err:
|
||||
*/
|
||||
void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
|
||||
{
|
||||
if (cb->fop_type == MEI_FOP_WRITE) {
|
||||
mei_io_cb_free(cb);
|
||||
cb = NULL;
|
||||
cl->writing_state = MEI_WRITE_COMPLETE;
|
||||
if (waitqueue_active(&cl->tx_wait))
|
||||
wake_up_interruptible(&cl->tx_wait);
|
||||
struct mei_device *dev = cl->dev;
|
||||
|
||||
} else if (cb->fop_type == MEI_FOP_READ) {
|
||||
switch (cb->fop_type) {
|
||||
case MEI_FOP_WRITE:
|
||||
mei_io_cb_free(cb);
|
||||
cl->writing_state = MEI_WRITE_COMPLETE;
|
||||
if (waitqueue_active(&cl->tx_wait)) {
|
||||
wake_up_interruptible(&cl->tx_wait);
|
||||
} else {
|
||||
pm_runtime_mark_last_busy(dev->dev);
|
||||
pm_request_autosuspend(dev->dev);
|
||||
}
|
||||
break;
|
||||
|
||||
case MEI_FOP_READ:
|
||||
list_add_tail(&cb->list, &cl->rd_completed);
|
||||
if (waitqueue_active(&cl->rx_wait))
|
||||
wake_up_interruptible_all(&cl->rx_wait);
|
||||
else
|
||||
mei_cl_bus_rx_event(cl);
|
||||
break;
|
||||
|
||||
case MEI_FOP_CONNECT:
|
||||
case MEI_FOP_DISCONNECT:
|
||||
if (waitqueue_active(&cl->wait))
|
||||
wake_up(&cl->wait);
|
||||
|
||||
break;
|
||||
default:
|
||||
BUG_ON(0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1312,16 +1501,12 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
|
||||
*
|
||||
* @dev: mei device
|
||||
*/
|
||||
|
||||
void mei_cl_all_disconnect(struct mei_device *dev)
|
||||
{
|
||||
struct mei_cl *cl;
|
||||
|
||||
list_for_each_entry(cl, &dev->file_list, link) {
|
||||
cl->state = MEI_FILE_DISCONNECTED;
|
||||
cl->mei_flow_ctrl_creds = 0;
|
||||
cl->timer_count = 0;
|
||||
}
|
||||
list_for_each_entry(cl, &dev->file_list, link)
|
||||
mei_cl_set_disconnected(cl);
|
||||
}
|
||||
|
||||
|
||||
|
@ -44,6 +44,30 @@ void mei_me_cl_rm_by_uuid_id(struct mei_device *dev,
|
||||
const uuid_le *uuid, u8 id);
|
||||
void mei_me_cl_rm_all(struct mei_device *dev);
|
||||
|
||||
/**
|
||||
* mei_me_cl_is_active - check whether me client is active in the fw
|
||||
*
|
||||
* @me_cl: me client
|
||||
*
|
||||
* Return: true if the me client is active in the firmware
|
||||
*/
|
||||
static inline bool mei_me_cl_is_active(const struct mei_me_client *me_cl)
|
||||
{
|
||||
return !list_empty_careful(&me_cl->list);
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_me_cl_uuid - return me client protocol name (uuid)
|
||||
*
|
||||
* @me_cl: me client
|
||||
*
|
||||
* Return: me client protocol name
|
||||
*/
|
||||
static inline const uuid_le *mei_me_cl_uuid(const struct mei_me_client *me_cl)
|
||||
{
|
||||
return &me_cl->props.protocol_name;
|
||||
}
|
||||
|
||||
/*
|
||||
* MEI IO Functions
|
||||
*/
|
||||
@ -94,18 +118,96 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl);
|
||||
/**
|
||||
* mei_cl_is_connected - host client is connected
|
||||
*
|
||||
* @cl: host clinet
|
||||
* @cl: host client
|
||||
*
|
||||
* Return: true if the host clinet is connected
|
||||
* Return: true if the host client is connected
|
||||
*/
|
||||
static inline bool mei_cl_is_connected(struct mei_cl *cl)
|
||||
{
|
||||
return cl->state == MEI_FILE_CONNECTED;
|
||||
}
|
||||
|
||||
bool mei_cl_is_other_connecting(struct mei_cl *cl);
|
||||
/**
|
||||
* mei_cl_me_id - me client id
|
||||
*
|
||||
* @cl: host client
|
||||
*
|
||||
* Return: me client id or 0 if client is not connected
|
||||
*/
|
||||
static inline u8 mei_cl_me_id(const struct mei_cl *cl)
|
||||
{
|
||||
return cl->me_cl ? cl->me_cl->client_id : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_cl_mtu - maximal message that client can send and receive
|
||||
*
|
||||
* @cl: host client
|
||||
*
|
||||
* Return: mtu
|
||||
*/
|
||||
static inline size_t mei_cl_mtu(const struct mei_cl *cl)
|
||||
{
|
||||
return cl->me_cl->props.max_msg_length;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_cl_is_fixed_address - check whether the me client uses fixed address
|
||||
*
|
||||
* @cl: host client
|
||||
*
|
||||
* Return: true if the client is connected and it has fixed me address
|
||||
*/
|
||||
static inline bool mei_cl_is_fixed_address(const struct mei_cl *cl)
|
||||
{
|
||||
return cl->me_cl && cl->me_cl->props.fixed_address;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_cl_is_single_recv_buf- check whether the me client
|
||||
* uses single receiving buffer
|
||||
*
|
||||
* @cl: host client
|
||||
*
|
||||
* Return: true if single_recv_buf == 1; 0 otherwise
|
||||
*/
|
||||
static inline bool mei_cl_is_single_recv_buf(const struct mei_cl *cl)
|
||||
{
|
||||
return cl->me_cl->props.single_recv_buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_cl_uuid - client's uuid
|
||||
*
|
||||
* @cl: host client
|
||||
*
|
||||
* Return: return uuid of connected me client
|
||||
*/
|
||||
static inline const uuid_le *mei_cl_uuid(const struct mei_cl *cl)
|
||||
{
|
||||
return mei_me_cl_uuid(cl->me_cl);
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_cl_host_addr - client's host address
|
||||
*
|
||||
* @cl: host client
|
||||
*
|
||||
* Return: 0 for fixed address client, host address for dynamic client
|
||||
*/
|
||||
static inline u8 mei_cl_host_addr(const struct mei_cl *cl)
|
||||
{
|
||||
return mei_cl_is_fixed_address(cl) ? 0 : cl->host_client_id;
|
||||
}
|
||||
|
||||
int mei_cl_disconnect(struct mei_cl *cl);
|
||||
int mei_cl_connect(struct mei_cl *cl, struct file *file);
|
||||
void mei_cl_set_disconnected(struct mei_cl *cl);
|
||||
int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
struct mei_cl_cb *cmpl_list);
|
||||
int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl,
|
||||
struct file *file);
|
||||
int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
struct mei_cl_cb *cmpl_list);
|
||||
int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp);
|
||||
int mei_cl_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *hdr,
|
||||
struct mei_cl_cb *cmpl_list);
|
||||
@ -117,14 +219,12 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb);
|
||||
|
||||
void mei_host_client_init(struct work_struct *work);
|
||||
|
||||
|
||||
|
||||
void mei_cl_all_disconnect(struct mei_device *dev);
|
||||
void mei_cl_all_wakeup(struct mei_device *dev);
|
||||
void mei_cl_all_write_clear(struct mei_device *dev);
|
||||
|
||||
#define MEI_CL_FMT "cl:host=%02d me=%02d "
|
||||
#define MEI_CL_PRM(cl) (cl)->host_client_id, (cl)->me_client_id
|
||||
#define MEI_CL_PRM(cl) (cl)->host_client_id, mei_cl_me_id(cl)
|
||||
|
||||
#define cl_dbg(dev, cl, format, arg...) \
|
||||
dev_dbg((dev)->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg)
|
||||
|
@ -116,7 +116,7 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf,
|
||||
|
||||
pos += scnprintf(buf + pos, bufsz - pos,
|
||||
"%2d|%2d|%4d|%5d|%2d|%2d|\n",
|
||||
i, cl->me_client_id, cl->host_client_id, cl->state,
|
||||
i, mei_cl_me_id(cl), cl->host_client_id, cl->state,
|
||||
!list_empty(&cl->rd_completed), cl->writing_state);
|
||||
i++;
|
||||
}
|
||||
@ -149,6 +149,13 @@ static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf,
|
||||
mei_dev_state_str(dev->dev_state));
|
||||
pos += scnprintf(buf + pos, bufsz - pos, "hbm: %s\n",
|
||||
mei_hbm_state_str(dev->hbm_state));
|
||||
|
||||
if (dev->hbm_state == MEI_HBM_STARTED) {
|
||||
pos += scnprintf(buf + pos, bufsz - pos, "hbm features:\n");
|
||||
pos += scnprintf(buf + pos, bufsz - pos, "\tPG: %01d\n",
|
||||
dev->hbm_f_pg_supported);
|
||||
}
|
||||
|
||||
pos += scnprintf(buf + pos, bufsz - pos, "pg: %s, %s\n",
|
||||
mei_pg_is_enabled(dev) ? "ENABLED" : "DISABLED",
|
||||
mei_pg_state_str(mei_pg_state(dev)));
|
||||
@ -209,6 +216,12 @@ int mei_dbgfs_register(struct mei_device *dev, const char *name)
|
||||
dev_err(dev->dev, "devstate: registration failed\n");
|
||||
goto err;
|
||||
}
|
||||
f = debugfs_create_bool("allow_fixed_address", S_IRUSR | S_IWUSR, dir,
|
||||
&dev->allow_fixed_address);
|
||||
if (!f) {
|
||||
dev_err(dev->dev, "allow_fixed_address: registration failed\n");
|
||||
goto err;
|
||||
}
|
||||
dev->dbgfs_dir = dir;
|
||||
return 0;
|
||||
err:
|
||||
|
@ -150,8 +150,8 @@ void mei_hbm_cl_hdr(struct mei_cl *cl, u8 hbm_cmd, void *buf, size_t len)
|
||||
memset(cmd, 0, len);
|
||||
|
||||
cmd->hbm_cmd = hbm_cmd;
|
||||
cmd->host_addr = cl->host_client_id;
|
||||
cmd->me_addr = cl->me_client_id;
|
||||
cmd->host_addr = mei_cl_host_addr(cl);
|
||||
cmd->me_addr = mei_cl_me_id(cl);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -188,8 +188,8 @@ int mei_hbm_cl_write(struct mei_device *dev,
|
||||
static inline
|
||||
bool mei_hbm_cl_addr_equal(struct mei_cl *cl, struct mei_hbm_cl_cmd *cmd)
|
||||
{
|
||||
return cl->host_client_id == cmd->host_addr &&
|
||||
cl->me_client_id == cmd->me_addr;
|
||||
return mei_cl_host_addr(cl) == cmd->host_addr &&
|
||||
mei_cl_me_id(cl) == cmd->me_addr;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -572,7 +572,7 @@ static void mei_hbm_cl_disconnect_res(struct mei_device *dev, struct mei_cl *cl,
|
||||
cl_dbg(dev, cl, "hbm: disconnect response status=%d\n", rs->status);
|
||||
|
||||
if (rs->status == MEI_CL_DISCONN_SUCCESS)
|
||||
cl->state = MEI_FILE_DISCONNECTED;
|
||||
cl->state = MEI_FILE_DISCONNECT_REPLY;
|
||||
cl->status = 0;
|
||||
}
|
||||
|
||||
@ -611,7 +611,7 @@ static void mei_hbm_cl_connect_res(struct mei_device *dev, struct mei_cl *cl,
|
||||
if (rs->status == MEI_CL_CONN_SUCCESS)
|
||||
cl->state = MEI_FILE_CONNECTED;
|
||||
else
|
||||
cl->state = MEI_FILE_DISCONNECTED;
|
||||
cl->state = MEI_FILE_DISCONNECT_REPLY;
|
||||
cl->status = mei_cl_conn_status_to_errno(rs->status);
|
||||
}
|
||||
|
||||
@ -680,8 +680,8 @@ static int mei_hbm_fw_disconnect_req(struct mei_device *dev,
|
||||
|
||||
cl = mei_hbm_cl_find_by_cmd(dev, disconnect_req);
|
||||
if (cl) {
|
||||
cl_dbg(dev, cl, "disconnect request received\n");
|
||||
cl->state = MEI_FILE_DISCONNECTED;
|
||||
cl_dbg(dev, cl, "fw disconnect request received\n");
|
||||
cl->state = MEI_FILE_DISCONNECTING;
|
||||
cl->timer_count = 0;
|
||||
|
||||
cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT_RSP, NULL);
|
||||
|
@ -663,17 +663,46 @@ int mei_me_pg_exit_sync(struct mei_device *dev)
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
reply:
|
||||
if (dev->pg_event == MEI_PG_EVENT_RECEIVED)
|
||||
ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_EXIT_RES_CMD);
|
||||
if (dev->pg_event != MEI_PG_EVENT_RECEIVED) {
|
||||
ret = -ETIME;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev->pg_event = MEI_PG_EVENT_INTR_WAIT;
|
||||
ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_EXIT_RES_CMD);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
wait_event_timeout(dev->wait_pg,
|
||||
dev->pg_event == MEI_PG_EVENT_INTR_RECEIVED, timeout);
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
if (dev->pg_event == MEI_PG_EVENT_INTR_RECEIVED)
|
||||
ret = 0;
|
||||
else
|
||||
ret = -ETIME;
|
||||
|
||||
out:
|
||||
dev->pg_event = MEI_PG_EVENT_IDLE;
|
||||
hw->pg_state = MEI_PG_OFF;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_me_pg_in_transition - is device now in pg transition
|
||||
*
|
||||
* @dev: the device structure
|
||||
*
|
||||
* Return: true if in pg transition, false otherwise
|
||||
*/
|
||||
static bool mei_me_pg_in_transition(struct mei_device *dev)
|
||||
{
|
||||
return dev->pg_event >= MEI_PG_EVENT_WAIT &&
|
||||
dev->pg_event <= MEI_PG_EVENT_INTR_WAIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_me_pg_is_enabled - detect if PG is supported by HW
|
||||
*
|
||||
@ -704,6 +733,24 @@ notsupported:
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_me_pg_intr - perform pg processing in interrupt thread handler
|
||||
*
|
||||
* @dev: the device structure
|
||||
*/
|
||||
static void mei_me_pg_intr(struct mei_device *dev)
|
||||
{
|
||||
struct mei_me_hw *hw = to_me_hw(dev);
|
||||
|
||||
if (dev->pg_event != MEI_PG_EVENT_INTR_WAIT)
|
||||
return;
|
||||
|
||||
dev->pg_event = MEI_PG_EVENT_INTR_RECEIVED;
|
||||
hw->pg_state = MEI_PG_OFF;
|
||||
if (waitqueue_active(&dev->wait_pg))
|
||||
wake_up(&dev->wait_pg);
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_me_irq_quick_handler - The ISR of the MEI device
|
||||
*
|
||||
@ -761,6 +808,8 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
|
||||
goto end;
|
||||
}
|
||||
|
||||
mei_me_pg_intr(dev);
|
||||
|
||||
/* check if we need to start the dev */
|
||||
if (!mei_host_is_ready(dev)) {
|
||||
if (mei_hw_is_ready(dev)) {
|
||||
@ -797,9 +846,10 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
|
||||
/*
|
||||
* During PG handshake only allowed write is the replay to the
|
||||
* PG exit message, so block calling write function
|
||||
* if the pg state is not idle
|
||||
* if the pg event is in PG handshake
|
||||
*/
|
||||
if (dev->pg_event == MEI_PG_EVENT_IDLE) {
|
||||
if (dev->pg_event != MEI_PG_EVENT_WAIT &&
|
||||
dev->pg_event != MEI_PG_EVENT_RECEIVED) {
|
||||
rets = mei_irq_write_handler(dev, &complete_list);
|
||||
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
|
||||
}
|
||||
@ -824,6 +874,7 @@ static const struct mei_hw_ops mei_me_hw_ops = {
|
||||
.hw_config = mei_me_hw_config,
|
||||
.hw_start = mei_me_hw_start,
|
||||
|
||||
.pg_in_transition = mei_me_pg_in_transition,
|
||||
.pg_is_enabled = mei_me_pg_is_enabled,
|
||||
|
||||
.intr_clear = mei_me_intr_clear,
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/irqreturn.h>
|
||||
@ -218,26 +219,25 @@ static u32 mei_txe_aliveness_get(struct mei_device *dev)
|
||||
*
|
||||
* Polls for HICR_HOST_ALIVENESS_RESP.ALIVENESS_RESP to be set
|
||||
*
|
||||
* Return: > 0 if the expected value was received, -ETIME otherwise
|
||||
* Return: 0 if the expected value was received, -ETIME otherwise
|
||||
*/
|
||||
static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected)
|
||||
{
|
||||
struct mei_txe_hw *hw = to_txe_hw(dev);
|
||||
int t = 0;
|
||||
ktime_t stop, start;
|
||||
|
||||
start = ktime_get();
|
||||
stop = ktime_add(start, ms_to_ktime(SEC_ALIVENESS_WAIT_TIMEOUT));
|
||||
do {
|
||||
hw->aliveness = mei_txe_aliveness_get(dev);
|
||||
if (hw->aliveness == expected) {
|
||||
dev->pg_event = MEI_PG_EVENT_IDLE;
|
||||
dev_dbg(dev->dev,
|
||||
"aliveness settled after %d msecs\n", t);
|
||||
return t;
|
||||
dev_dbg(dev->dev, "aliveness settled after %lld usecs\n",
|
||||
ktime_to_us(ktime_sub(ktime_get(), start)));
|
||||
return 0;
|
||||
}
|
||||
mutex_unlock(&dev->device_lock);
|
||||
msleep(MSEC_PER_SEC / 5);
|
||||
mutex_lock(&dev->device_lock);
|
||||
t += MSEC_PER_SEC / 5;
|
||||
} while (t < SEC_ALIVENESS_WAIT_TIMEOUT);
|
||||
usleep_range(20, 50);
|
||||
} while (ktime_before(ktime_get(), stop));
|
||||
|
||||
dev->pg_event = MEI_PG_EVENT_IDLE;
|
||||
dev_err(dev->dev, "aliveness timed out\n");
|
||||
@ -301,6 +301,18 @@ int mei_txe_aliveness_set_sync(struct mei_device *dev, u32 req)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_txe_pg_in_transition - is device now in pg transition
|
||||
*
|
||||
* @dev: the device structure
|
||||
*
|
||||
* Return: true if in pg transition, false otherwise
|
||||
*/
|
||||
static bool mei_txe_pg_in_transition(struct mei_device *dev)
|
||||
{
|
||||
return dev->pg_event == MEI_PG_EVENT_WAIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_txe_pg_is_enabled - detect if PG is supported by HW
|
||||
*
|
||||
@ -1138,6 +1150,7 @@ static const struct mei_hw_ops mei_txe_hw_ops = {
|
||||
.hw_config = mei_txe_hw_config,
|
||||
.hw_start = mei_txe_hw_start,
|
||||
|
||||
.pg_in_transition = mei_txe_pg_in_transition,
|
||||
.pg_is_enabled = mei_txe_pg_is_enabled,
|
||||
|
||||
.intr_clear = mei_txe_intr_clear,
|
||||
|
@ -361,13 +361,15 @@ bool mei_write_is_idle(struct mei_device *dev)
|
||||
{
|
||||
bool idle = (dev->dev_state == MEI_DEV_ENABLED &&
|
||||
list_empty(&dev->ctrl_wr_list.list) &&
|
||||
list_empty(&dev->write_list.list));
|
||||
list_empty(&dev->write_list.list) &&
|
||||
list_empty(&dev->write_waiting_list.list));
|
||||
|
||||
dev_dbg(dev->dev, "write pg: is idle[%d] state=%s ctrl=%d write=%d\n",
|
||||
dev_dbg(dev->dev, "write pg: is idle[%d] state=%s ctrl=%01d write=%01d wwait=%01d\n",
|
||||
idle,
|
||||
mei_dev_state_str(dev->dev_state),
|
||||
list_empty(&dev->ctrl_wr_list.list),
|
||||
list_empty(&dev->write_list.list));
|
||||
list_empty(&dev->write_list.list),
|
||||
list_empty(&dev->write_waiting_list.list));
|
||||
|
||||
return idle;
|
||||
}
|
||||
|
@ -65,8 +65,8 @@ EXPORT_SYMBOL_GPL(mei_irq_compl_handler);
|
||||
static inline int mei_cl_hbm_equal(struct mei_cl *cl,
|
||||
struct mei_msg_hdr *mei_hdr)
|
||||
{
|
||||
return cl->host_client_id == mei_hdr->host_addr &&
|
||||
cl->me_client_id == mei_hdr->me_addr;
|
||||
return mei_cl_host_addr(cl) == mei_hdr->host_addr &&
|
||||
mei_cl_me_id(cl) == mei_hdr->me_addr;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -180,56 +180,14 @@ static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
return -EMSGSIZE;
|
||||
|
||||
ret = mei_hbm_cl_disconnect_rsp(dev, cl);
|
||||
|
||||
cl->state = MEI_FILE_DISCONNECTED;
|
||||
cl->status = 0;
|
||||
mei_cl_set_disconnected(cl);
|
||||
mei_io_cb_free(cb);
|
||||
mei_me_cl_put(cl->me_cl);
|
||||
cl->me_cl = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* mei_cl_irq_disconnect - processes close related operation from
|
||||
* interrupt thread context - send disconnect request
|
||||
*
|
||||
* @cl: client
|
||||
* @cb: callback block.
|
||||
* @cmpl_list: complete list.
|
||||
*
|
||||
* Return: 0, OK; otherwise, error.
|
||||
*/
|
||||
static int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
struct mei_cl_cb *cmpl_list)
|
||||
{
|
||||
struct mei_device *dev = cl->dev;
|
||||
u32 msg_slots;
|
||||
int slots;
|
||||
|
||||
msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
|
||||
slots = mei_hbuf_empty_slots(dev);
|
||||
|
||||
if (slots < msg_slots)
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (mei_hbm_cl_disconnect_req(dev, cl)) {
|
||||
cl->status = 0;
|
||||
cb->buf_idx = 0;
|
||||
list_move_tail(&cb->list, &cmpl_list->list);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
cl->state = MEI_FILE_DISCONNECTING;
|
||||
cl->status = 0;
|
||||
cb->buf_idx = 0;
|
||||
list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
|
||||
cl->timer_count = MEI_CONNECT_TIMEOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mei_cl_irq_read - processes client read related operation from the
|
||||
* interrupt thread context - request for flow control credits
|
||||
@ -267,49 +225,6 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mei_cl_irq_connect - send connect request in irq_thread context
|
||||
*
|
||||
* @cl: client
|
||||
* @cb: callback block.
|
||||
* @cmpl_list: complete list.
|
||||
*
|
||||
* Return: 0, OK; otherwise, error.
|
||||
*/
|
||||
static int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
struct mei_cl_cb *cmpl_list)
|
||||
{
|
||||
struct mei_device *dev = cl->dev;
|
||||
u32 msg_slots;
|
||||
int slots;
|
||||
int ret;
|
||||
|
||||
msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
|
||||
slots = mei_hbuf_empty_slots(dev);
|
||||
|
||||
if (mei_cl_is_other_connecting(cl))
|
||||
return 0;
|
||||
|
||||
if (slots < msg_slots)
|
||||
return -EMSGSIZE;
|
||||
|
||||
cl->state = MEI_FILE_CONNECTING;
|
||||
|
||||
ret = mei_hbm_cl_connect_req(dev, cl);
|
||||
if (ret) {
|
||||
cl->status = ret;
|
||||
cb->buf_idx = 0;
|
||||
list_del_init(&cb->list);
|
||||
return ret;
|
||||
}
|
||||
|
||||
list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
|
||||
cl->timer_count = MEI_CONNECT_TIMEOUT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mei_irq_read_handler - bottom half read routine after ISR to
|
||||
* handle the read processing.
|
||||
|
@ -94,7 +94,7 @@ static int mei_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct mei_cl *cl = file->private_data;
|
||||
struct mei_device *dev;
|
||||
int rets = 0;
|
||||
int rets;
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
return -ENODEV;
|
||||
@ -106,11 +106,8 @@ static int mei_release(struct inode *inode, struct file *file)
|
||||
rets = mei_amthif_release(dev, file);
|
||||
goto out;
|
||||
}
|
||||
if (mei_cl_is_connected(cl)) {
|
||||
cl->state = MEI_FILE_DISCONNECTING;
|
||||
cl_dbg(dev, cl, "disconnecting\n");
|
||||
rets = mei_cl_disconnect(cl);
|
||||
}
|
||||
rets = mei_cl_disconnect(cl);
|
||||
|
||||
mei_cl_flush_queues(cl, file);
|
||||
cl_dbg(dev, cl, "removing\n");
|
||||
|
||||
@ -186,8 +183,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
|
||||
|
||||
err = mei_cl_read_start(cl, length, file);
|
||||
if (err && err != -EBUSY) {
|
||||
dev_dbg(dev->dev,
|
||||
"mei start read failure with status = %d\n", err);
|
||||
cl_dbg(dev, cl, "mei start read failure status = %d\n", err);
|
||||
rets = err;
|
||||
goto out;
|
||||
}
|
||||
@ -218,6 +214,11 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
|
||||
|
||||
cb = mei_cl_read_cb(cl, file);
|
||||
if (!cb) {
|
||||
if (mei_cl_is_fixed_address(cl) && dev->allow_fixed_address) {
|
||||
cb = mei_cl_read_cb(cl, NULL);
|
||||
if (cb)
|
||||
goto copy_buffer;
|
||||
}
|
||||
rets = 0;
|
||||
goto out;
|
||||
}
|
||||
@ -226,11 +227,11 @@ copy_buffer:
|
||||
/* now copy the data to user space */
|
||||
if (cb->status) {
|
||||
rets = cb->status;
|
||||
dev_dbg(dev->dev, "read operation failed %d\n", rets);
|
||||
cl_dbg(dev, cl, "read operation failed %d\n", rets);
|
||||
goto free;
|
||||
}
|
||||
|
||||
dev_dbg(dev->dev, "buf.size = %d buf.idx= %ld\n",
|
||||
cl_dbg(dev, cl, "buf.size = %d buf.idx = %ld\n",
|
||||
cb->buf.size, cb->buf_idx);
|
||||
if (length == 0 || ubuf == NULL || *offset > cb->buf_idx) {
|
||||
rets = -EMSGSIZE;
|
||||
@ -256,7 +257,7 @@ free:
|
||||
mei_io_cb_free(cb);
|
||||
|
||||
out:
|
||||
dev_dbg(dev->dev, "end mei read rets= %d\n", rets);
|
||||
cl_dbg(dev, cl, "end mei read rets = %d\n", rets);
|
||||
mutex_unlock(&dev->device_lock);
|
||||
return rets;
|
||||
}
|
||||
@ -274,7 +275,6 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
|
||||
size_t length, loff_t *offset)
|
||||
{
|
||||
struct mei_cl *cl = file->private_data;
|
||||
struct mei_me_client *me_cl = NULL;
|
||||
struct mei_cl_cb *write_cb = NULL;
|
||||
struct mei_device *dev;
|
||||
unsigned long timeout = 0;
|
||||
@ -292,27 +292,27 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
|
||||
goto out;
|
||||
}
|
||||
|
||||
me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id);
|
||||
if (!me_cl) {
|
||||
if (!mei_cl_is_connected(cl)) {
|
||||
cl_err(dev, cl, "is not connected");
|
||||
rets = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!mei_me_cl_is_active(cl->me_cl)) {
|
||||
rets = -ENOTTY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (length > mei_cl_mtu(cl)) {
|
||||
rets = -EFBIG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
rets = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (length > me_cl->props.max_msg_length) {
|
||||
rets = -EFBIG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!mei_cl_is_connected(cl)) {
|
||||
cl_err(dev, cl, "is not connected");
|
||||
rets = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
if (cl == &dev->iamthif_cl) {
|
||||
write_cb = mei_amthif_find_read_list_entry(dev, file);
|
||||
|
||||
@ -350,14 +350,12 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
|
||||
"amthif write failed with status = %d\n", rets);
|
||||
goto out;
|
||||
}
|
||||
mei_me_cl_put(me_cl);
|
||||
mutex_unlock(&dev->device_lock);
|
||||
return length;
|
||||
}
|
||||
|
||||
rets = mei_cl_write(cl, write_cb, false);
|
||||
out:
|
||||
mei_me_cl_put(me_cl);
|
||||
mutex_unlock(&dev->device_lock);
|
||||
if (rets < 0)
|
||||
mei_io_cb_free(write_cb);
|
||||
@ -395,17 +393,16 @@ static int mei_ioctl_connect_client(struct file *file,
|
||||
|
||||
/* find ME client we're trying to connect to */
|
||||
me_cl = mei_me_cl_by_uuid(dev, &data->in_client_uuid);
|
||||
if (!me_cl || me_cl->props.fixed_address) {
|
||||
if (!me_cl ||
|
||||
(me_cl->props.fixed_address && !dev->allow_fixed_address)) {
|
||||
dev_dbg(dev->dev, "Cannot connect to FW Client UUID = %pUl\n",
|
||||
&data->in_client_uuid);
|
||||
&data->in_client_uuid);
|
||||
mei_me_cl_put(me_cl);
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
cl->me_client_id = me_cl->client_id;
|
||||
cl->cl_uuid = me_cl->props.protocol_name;
|
||||
|
||||
dev_dbg(dev->dev, "Connect to FW Client ID = %d\n",
|
||||
cl->me_client_id);
|
||||
me_cl->client_id);
|
||||
dev_dbg(dev->dev, "FW Client - Protocol Version = %d\n",
|
||||
me_cl->props.protocol_version);
|
||||
dev_dbg(dev->dev, "FW Client - Max Msg Len = %d\n",
|
||||
@ -441,7 +438,7 @@ static int mei_ioctl_connect_client(struct file *file,
|
||||
client->protocol_version = me_cl->props.protocol_version;
|
||||
dev_dbg(dev->dev, "Can connect?\n");
|
||||
|
||||
rets = mei_cl_connect(cl, file);
|
||||
rets = mei_cl_connect(cl, me_cl, file);
|
||||
|
||||
end:
|
||||
mei_me_cl_put(me_cl);
|
||||
|
@ -88,7 +88,8 @@ enum file_state {
|
||||
MEI_FILE_CONNECTING,
|
||||
MEI_FILE_CONNECTED,
|
||||
MEI_FILE_DISCONNECTING,
|
||||
MEI_FILE_DISCONNECTED
|
||||
MEI_FILE_DISCONNECT_REPLY,
|
||||
MEI_FILE_DISCONNECTED,
|
||||
};
|
||||
|
||||
/* MEI device states */
|
||||
@ -176,6 +177,8 @@ struct mei_fw_status {
|
||||
* @props: client properties
|
||||
* @client_id: me client id
|
||||
* @mei_flow_ctrl_creds: flow control credits
|
||||
* @connect_count: number connections to this client
|
||||
* @reserved: reserved
|
||||
*/
|
||||
struct mei_me_client {
|
||||
struct list_head list;
|
||||
@ -183,6 +186,8 @@ struct mei_me_client {
|
||||
struct mei_client_properties props;
|
||||
u8 client_id;
|
||||
u8 mei_flow_ctrl_creds;
|
||||
u8 connect_count;
|
||||
u8 reserved;
|
||||
};
|
||||
|
||||
|
||||
@ -226,11 +231,11 @@ struct mei_cl_cb {
|
||||
* @rx_wait: wait queue for rx completion
|
||||
* @wait: wait queue for management operation
|
||||
* @status: connection status
|
||||
* @cl_uuid: client uuid name
|
||||
* @me_cl: fw client connected
|
||||
* @host_client_id: host id
|
||||
* @me_client_id: me/fw id
|
||||
* @mei_flow_ctrl_creds: transmit flow credentials
|
||||
* @timer_count: watchdog timer for operation completion
|
||||
* @reserved: reserved for alignment
|
||||
* @writing_state: state of the tx
|
||||
* @rd_pending: pending read credits
|
||||
* @rd_completed: completed read
|
||||
@ -246,11 +251,11 @@ struct mei_cl {
|
||||
wait_queue_head_t rx_wait;
|
||||
wait_queue_head_t wait;
|
||||
int status;
|
||||
uuid_le cl_uuid;
|
||||
struct mei_me_client *me_cl;
|
||||
u8 host_client_id;
|
||||
u8 me_client_id;
|
||||
u8 mei_flow_ctrl_creds;
|
||||
u8 timer_count;
|
||||
u8 reserved;
|
||||
enum mei_file_transaction_states writing_state;
|
||||
struct list_head rd_pending;
|
||||
struct list_head rd_completed;
|
||||
@ -271,6 +276,7 @@ struct mei_cl {
|
||||
|
||||
* @fw_status : get fw status registers
|
||||
* @pg_state : power gating state of the device
|
||||
* @pg_in_transition : is device now in pg transition
|
||||
* @pg_is_enabled : is power gating enabled
|
||||
|
||||
* @intr_clear : clear pending interrupts
|
||||
@ -300,6 +306,7 @@ struct mei_hw_ops {
|
||||
|
||||
int (*fw_status)(struct mei_device *dev, struct mei_fw_status *fw_sts);
|
||||
enum mei_pg_state (*pg_state)(struct mei_device *dev);
|
||||
bool (*pg_in_transition)(struct mei_device *dev);
|
||||
bool (*pg_is_enabled)(struct mei_device *dev);
|
||||
|
||||
void (*intr_clear)(struct mei_device *dev);
|
||||
@ -323,34 +330,14 @@ struct mei_hw_ops {
|
||||
|
||||
/* MEI bus API*/
|
||||
|
||||
/**
|
||||
* struct mei_cl_ops - MEI CL device ops
|
||||
* This structure allows ME host clients to implement technology
|
||||
* specific operations.
|
||||
*
|
||||
* @enable: Enable an MEI CL device. Some devices require specific
|
||||
* HECI commands to initialize completely.
|
||||
* @disable: Disable an MEI CL device.
|
||||
* @send: Tx hook for the device. This allows ME host clients to trap
|
||||
* the device driver buffers before actually physically
|
||||
* pushing it to the ME.
|
||||
* @recv: Rx hook for the device. This allows ME host clients to trap the
|
||||
* ME buffers before forwarding them to the device driver.
|
||||
*/
|
||||
struct mei_cl_ops {
|
||||
int (*enable)(struct mei_cl_device *device);
|
||||
int (*disable)(struct mei_cl_device *device);
|
||||
int (*send)(struct mei_cl_device *device, u8 *buf, size_t length);
|
||||
int (*recv)(struct mei_cl_device *device, u8 *buf, size_t length);
|
||||
};
|
||||
|
||||
struct mei_cl_device *mei_cl_add_device(struct mei_device *dev,
|
||||
uuid_le uuid, char *name,
|
||||
struct mei_cl_ops *ops);
|
||||
struct mei_me_client *me_cl,
|
||||
struct mei_cl *cl,
|
||||
char *name);
|
||||
void mei_cl_remove_device(struct mei_cl_device *device);
|
||||
|
||||
ssize_t __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length);
|
||||
ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length);
|
||||
ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
|
||||
bool blocking);
|
||||
ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length);
|
||||
void mei_cl_bus_rx_event(struct mei_cl *cl);
|
||||
void mei_cl_bus_remove_devices(struct mei_device *dev);
|
||||
@ -358,51 +345,21 @@ int mei_cl_bus_init(void);
|
||||
void mei_cl_bus_exit(void);
|
||||
struct mei_cl *mei_cl_bus_find_cl_by_uuid(struct mei_device *dev, uuid_le uuid);
|
||||
|
||||
|
||||
/**
|
||||
* struct mei_cl_device - MEI device handle
|
||||
* An mei_cl_device pointer is returned from mei_add_device()
|
||||
* and links MEI bus clients to their actual ME host client pointer.
|
||||
* Drivers for MEI devices will get an mei_cl_device pointer
|
||||
* when being probed and shall use it for doing ME bus I/O.
|
||||
*
|
||||
* @dev: linux driver model device pointer
|
||||
* @cl: mei client
|
||||
* @ops: ME transport ops
|
||||
* @event_work: async work to execute event callback
|
||||
* @event_cb: Drivers register this callback to get asynchronous ME
|
||||
* events (e.g. Rx buffer pending) notifications.
|
||||
* @event_context: event callback run context
|
||||
* @events: Events bitmask sent to the driver.
|
||||
* @priv_data: client private data
|
||||
*/
|
||||
struct mei_cl_device {
|
||||
struct device dev;
|
||||
|
||||
struct mei_cl *cl;
|
||||
|
||||
const struct mei_cl_ops *ops;
|
||||
|
||||
struct work_struct event_work;
|
||||
mei_cl_event_cb_t event_cb;
|
||||
void *event_context;
|
||||
unsigned long events;
|
||||
|
||||
void *priv_data;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* enum mei_pg_event - power gating transition events
|
||||
*
|
||||
* @MEI_PG_EVENT_IDLE: the driver is not in power gating transition
|
||||
* @MEI_PG_EVENT_WAIT: the driver is waiting for a pg event to complete
|
||||
* @MEI_PG_EVENT_RECEIVED: the driver received pg event
|
||||
* @MEI_PG_EVENT_INTR_WAIT: the driver is waiting for a pg event interrupt
|
||||
* @MEI_PG_EVENT_INTR_RECEIVED: the driver received pg event interrupt
|
||||
*/
|
||||
enum mei_pg_event {
|
||||
MEI_PG_EVENT_IDLE,
|
||||
MEI_PG_EVENT_WAIT,
|
||||
MEI_PG_EVENT_RECEIVED,
|
||||
MEI_PG_EVENT_INTR_WAIT,
|
||||
MEI_PG_EVENT_INTR_RECEIVED,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -467,6 +424,8 @@ const char *mei_pg_state_str(enum mei_pg_state state);
|
||||
* @host_clients_map : host clients id pool
|
||||
* @me_client_index : last FW client index in enumeration
|
||||
*
|
||||
* @allow_fixed_address: allow user space to connect a fixed client
|
||||
*
|
||||
* @wd_cl : watchdog client
|
||||
* @wd_state : watchdog client state
|
||||
* @wd_pending : watchdog command is pending
|
||||
@ -479,7 +438,6 @@ const char *mei_pg_state_str(enum mei_pg_state state);
|
||||
* @iamthif_cl : amthif host client
|
||||
* @iamthif_current_cb : amthif current operation callback
|
||||
* @iamthif_open_count : number of opened amthif connections
|
||||
* @iamthif_mtu : amthif client max message length
|
||||
* @iamthif_timer : time stamp of current amthif command completion
|
||||
* @iamthif_stall_timer : timer to detect amthif hang
|
||||
* @iamthif_state : amthif processor state
|
||||
@ -558,6 +516,8 @@ struct mei_device {
|
||||
DECLARE_BITMAP(host_clients_map, MEI_CLIENTS_MAX);
|
||||
unsigned long me_client_index;
|
||||
|
||||
u32 allow_fixed_address;
|
||||
|
||||
struct mei_cl wd_cl;
|
||||
enum mei_wd_states wd_state;
|
||||
bool wd_pending;
|
||||
@ -573,7 +533,6 @@ struct mei_device {
|
||||
struct mei_cl iamthif_cl;
|
||||
struct mei_cl_cb *iamthif_current_cb;
|
||||
long iamthif_open_count;
|
||||
int iamthif_mtu;
|
||||
unsigned long iamthif_timer;
|
||||
u32 iamthif_stall_timer;
|
||||
enum iamthif_states iamthif_state;
|
||||
@ -652,7 +611,7 @@ void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list);
|
||||
*/
|
||||
void mei_amthif_reset_params(struct mei_device *dev);
|
||||
|
||||
int mei_amthif_host_init(struct mei_device *dev);
|
||||
int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl);
|
||||
|
||||
int mei_amthif_read(struct mei_device *dev, struct file *file,
|
||||
char __user *ubuf, size_t length, loff_t *offset);
|
||||
@ -679,7 +638,7 @@ int mei_amthif_irq_read(struct mei_device *dev, s32 *slots);
|
||||
/*
|
||||
* NFC functions
|
||||
*/
|
||||
int mei_nfc_host_init(struct mei_device *dev);
|
||||
int mei_nfc_host_init(struct mei_device *dev, struct mei_me_client *me_cl);
|
||||
void mei_nfc_host_exit(struct mei_device *dev);
|
||||
|
||||
/*
|
||||
@ -689,7 +648,7 @@ extern const uuid_le mei_nfc_guid;
|
||||
|
||||
int mei_wd_send(struct mei_device *dev);
|
||||
int mei_wd_stop(struct mei_device *dev);
|
||||
int mei_wd_host_init(struct mei_device *dev);
|
||||
int mei_wd_host_init(struct mei_device *dev, struct mei_me_client *me_cl);
|
||||
/*
|
||||
* mei_watchdog_register - Registering watchdog interface
|
||||
* once we got connection to the WD Client
|
||||
@ -717,6 +676,11 @@ static inline enum mei_pg_state mei_pg_state(struct mei_device *dev)
|
||||
return dev->ops->pg_state(dev);
|
||||
}
|
||||
|
||||
static inline bool mei_pg_in_transition(struct mei_device *dev)
|
||||
{
|
||||
return dev->ops->pg_in_transition(dev);
|
||||
}
|
||||
|
||||
static inline bool mei_pg_is_enabled(struct mei_device *dev)
|
||||
{
|
||||
return dev->ops->pg_is_enabled(dev);
|
||||
|
@ -91,30 +91,25 @@ struct mei_nfc_hci_hdr {
|
||||
/**
|
||||
* struct mei_nfc_dev - NFC mei device
|
||||
*
|
||||
* @me_cl: NFC me client
|
||||
* @cl: NFC host client
|
||||
* @cl_info: NFC info host client
|
||||
* @init_work: perform connection to the info client
|
||||
* @send_wq: send completion wait queue
|
||||
* @fw_ivn: NFC Interface Version Number
|
||||
* @vendor_id: NFC manufacturer ID
|
||||
* @radio_type: NFC radio type
|
||||
* @bus_name: bus name
|
||||
*
|
||||
* @req_id: message counter
|
||||
* @recv_req_id: reception message counter
|
||||
*/
|
||||
struct mei_nfc_dev {
|
||||
struct mei_me_client *me_cl;
|
||||
struct mei_cl *cl;
|
||||
struct mei_cl *cl_info;
|
||||
struct work_struct init_work;
|
||||
wait_queue_head_t send_wq;
|
||||
u8 fw_ivn;
|
||||
u8 vendor_id;
|
||||
u8 radio_type;
|
||||
char *bus_name;
|
||||
|
||||
u16 req_id;
|
||||
u16 recv_req_id;
|
||||
};
|
||||
|
||||
/* UUIDs for NFC F/W clients */
|
||||
@ -151,6 +146,7 @@ static void mei_nfc_free(struct mei_nfc_dev *ndev)
|
||||
kfree(ndev->cl_info);
|
||||
}
|
||||
|
||||
mei_me_cl_put(ndev->me_cl);
|
||||
kfree(ndev);
|
||||
}
|
||||
|
||||
@ -199,73 +195,6 @@ static int mei_nfc_build_bus_name(struct mei_nfc_dev *ndev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mei_nfc_connect(struct mei_nfc_dev *ndev)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_cl *cl;
|
||||
struct mei_nfc_cmd *cmd, *reply;
|
||||
struct mei_nfc_connect *connect;
|
||||
struct mei_nfc_connect_resp *connect_resp;
|
||||
size_t connect_length, connect_resp_length;
|
||||
int bytes_recv, ret;
|
||||
|
||||
cl = ndev->cl;
|
||||
dev = cl->dev;
|
||||
|
||||
connect_length = sizeof(struct mei_nfc_cmd) +
|
||||
sizeof(struct mei_nfc_connect);
|
||||
|
||||
connect_resp_length = sizeof(struct mei_nfc_cmd) +
|
||||
sizeof(struct mei_nfc_connect_resp);
|
||||
|
||||
cmd = kzalloc(connect_length, GFP_KERNEL);
|
||||
if (!cmd)
|
||||
return -ENOMEM;
|
||||
connect = (struct mei_nfc_connect *)cmd->data;
|
||||
|
||||
reply = kzalloc(connect_resp_length, GFP_KERNEL);
|
||||
if (!reply) {
|
||||
kfree(cmd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
connect_resp = (struct mei_nfc_connect_resp *)reply->data;
|
||||
|
||||
cmd->command = MEI_NFC_CMD_MAINTENANCE;
|
||||
cmd->data_size = 3;
|
||||
cmd->sub_command = MEI_NFC_SUBCMD_CONNECT;
|
||||
connect->fw_ivn = ndev->fw_ivn;
|
||||
connect->vendor_id = ndev->vendor_id;
|
||||
|
||||
ret = __mei_cl_send(cl, (u8 *)cmd, connect_length);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "Could not send connect cmd\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
bytes_recv = __mei_cl_recv(cl, (u8 *)reply, connect_resp_length);
|
||||
if (bytes_recv < 0) {
|
||||
dev_err(dev->dev, "Could not read connect response\n");
|
||||
ret = bytes_recv;
|
||||
goto err;
|
||||
}
|
||||
|
||||
dev_info(dev->dev, "IVN 0x%x Vendor ID 0x%x\n",
|
||||
connect_resp->fw_ivn, connect_resp->vendor_id);
|
||||
|
||||
dev_info(dev->dev, "ME FW %d.%d.%d.%d\n",
|
||||
connect_resp->me_major, connect_resp->me_minor,
|
||||
connect_resp->me_hotfix, connect_resp->me_build);
|
||||
|
||||
ret = 0;
|
||||
|
||||
err:
|
||||
kfree(reply);
|
||||
kfree(cmd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mei_nfc_if_version(struct mei_nfc_dev *ndev)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
@ -285,7 +214,7 @@ static int mei_nfc_if_version(struct mei_nfc_dev *ndev)
|
||||
cmd.data_size = 1;
|
||||
cmd.sub_command = MEI_NFC_SUBCMD_IF_VERSION;
|
||||
|
||||
ret = __mei_cl_send(cl, (u8 *)&cmd, sizeof(struct mei_nfc_cmd));
|
||||
ret = __mei_cl_send(cl, (u8 *)&cmd, sizeof(struct mei_nfc_cmd), 1);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "Could not send IF version cmd\n");
|
||||
return ret;
|
||||
@ -317,106 +246,13 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mei_nfc_enable(struct mei_cl_device *cldev)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_nfc_dev *ndev;
|
||||
int ret;
|
||||
|
||||
ndev = (struct mei_nfc_dev *)cldev->priv_data;
|
||||
dev = ndev->cl->dev;
|
||||
|
||||
ret = mei_nfc_connect(ndev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "Could not connect to NFC");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mei_nfc_disable(struct mei_cl_device *cldev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mei_nfc_send(struct mei_cl_device *cldev, u8 *buf, size_t length)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_nfc_dev *ndev;
|
||||
struct mei_nfc_hci_hdr *hdr;
|
||||
u8 *mei_buf;
|
||||
int err;
|
||||
|
||||
ndev = (struct mei_nfc_dev *) cldev->priv_data;
|
||||
dev = ndev->cl->dev;
|
||||
|
||||
err = -ENOMEM;
|
||||
mei_buf = kzalloc(length + MEI_NFC_HEADER_SIZE, GFP_KERNEL);
|
||||
if (!mei_buf)
|
||||
goto out;
|
||||
|
||||
hdr = (struct mei_nfc_hci_hdr *) mei_buf;
|
||||
hdr->cmd = MEI_NFC_CMD_HCI_SEND;
|
||||
hdr->status = 0;
|
||||
hdr->req_id = ndev->req_id;
|
||||
hdr->reserved = 0;
|
||||
hdr->data_size = length;
|
||||
|
||||
memcpy(mei_buf + MEI_NFC_HEADER_SIZE, buf, length);
|
||||
err = __mei_cl_send(ndev->cl, mei_buf, length + MEI_NFC_HEADER_SIZE);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
if (!wait_event_interruptible_timeout(ndev->send_wq,
|
||||
ndev->recv_req_id == ndev->req_id, HZ)) {
|
||||
dev_err(dev->dev, "NFC MEI command timeout\n");
|
||||
err = -ETIME;
|
||||
} else {
|
||||
ndev->req_id++;
|
||||
}
|
||||
out:
|
||||
kfree(mei_buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mei_nfc_recv(struct mei_cl_device *cldev, u8 *buf, size_t length)
|
||||
{
|
||||
struct mei_nfc_dev *ndev;
|
||||
struct mei_nfc_hci_hdr *hci_hdr;
|
||||
int received_length;
|
||||
|
||||
ndev = (struct mei_nfc_dev *)cldev->priv_data;
|
||||
|
||||
received_length = __mei_cl_recv(ndev->cl, buf, length);
|
||||
if (received_length < 0)
|
||||
return received_length;
|
||||
|
||||
hci_hdr = (struct mei_nfc_hci_hdr *) buf;
|
||||
|
||||
if (hci_hdr->cmd == MEI_NFC_CMD_HCI_SEND) {
|
||||
ndev->recv_req_id = hci_hdr->req_id;
|
||||
wake_up(&ndev->send_wq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return received_length;
|
||||
}
|
||||
|
||||
static struct mei_cl_ops nfc_ops = {
|
||||
.enable = mei_nfc_enable,
|
||||
.disable = mei_nfc_disable,
|
||||
.send = mei_nfc_send,
|
||||
.recv = mei_nfc_recv,
|
||||
};
|
||||
|
||||
static void mei_nfc_init(struct work_struct *work)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_cl_device *cldev;
|
||||
struct mei_nfc_dev *ndev;
|
||||
struct mei_cl *cl_info;
|
||||
struct mei_me_client *me_cl_info;
|
||||
|
||||
ndev = container_of(work, struct mei_nfc_dev, init_work);
|
||||
|
||||
@ -425,13 +261,22 @@ static void mei_nfc_init(struct work_struct *work)
|
||||
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
if (mei_cl_connect(cl_info, NULL) < 0) {
|
||||
/* check for valid client id */
|
||||
me_cl_info = mei_me_cl_by_uuid(dev, &mei_nfc_info_guid);
|
||||
if (!me_cl_info) {
|
||||
mutex_unlock(&dev->device_lock);
|
||||
dev_info(dev->dev, "nfc: failed to find the info client\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (mei_cl_connect(cl_info, me_cl_info, NULL) < 0) {
|
||||
mei_me_cl_put(me_cl_info);
|
||||
mutex_unlock(&dev->device_lock);
|
||||
dev_err(dev->dev, "Could not connect to the NFC INFO ME client");
|
||||
|
||||
goto err;
|
||||
}
|
||||
|
||||
mei_me_cl_put(me_cl_info);
|
||||
mutex_unlock(&dev->device_lock);
|
||||
|
||||
if (mei_nfc_if_version(ndev) < 0) {
|
||||
@ -459,7 +304,8 @@ static void mei_nfc_init(struct work_struct *work)
|
||||
return;
|
||||
}
|
||||
|
||||
cldev = mei_cl_add_device(dev, mei_nfc_guid, ndev->bus_name, &nfc_ops);
|
||||
cldev = mei_cl_add_device(dev, ndev->me_cl, ndev->cl,
|
||||
ndev->bus_name);
|
||||
if (!cldev) {
|
||||
dev_err(dev->dev, "Could not add the NFC device to the MEI bus\n");
|
||||
|
||||
@ -479,11 +325,10 @@ err:
|
||||
}
|
||||
|
||||
|
||||
int mei_nfc_host_init(struct mei_device *dev)
|
||||
int mei_nfc_host_init(struct mei_device *dev, struct mei_me_client *me_cl)
|
||||
{
|
||||
struct mei_nfc_dev *ndev;
|
||||
struct mei_cl *cl_info, *cl;
|
||||
struct mei_me_client *me_cl = NULL;
|
||||
int ret;
|
||||
|
||||
|
||||
@ -500,11 +345,9 @@ int mei_nfc_host_init(struct mei_device *dev)
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* check for valid client id */
|
||||
me_cl = mei_me_cl_by_uuid(dev, &mei_nfc_info_guid);
|
||||
if (!me_cl) {
|
||||
dev_info(dev->dev, "nfc: failed to find the client\n");
|
||||
ret = -ENOTTY;
|
||||
ndev->me_cl = mei_me_cl_get(me_cl);
|
||||
if (!ndev->me_cl) {
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -514,48 +357,26 @@ int mei_nfc_host_init(struct mei_device *dev)
|
||||
goto err;
|
||||
}
|
||||
|
||||
cl_info->me_client_id = me_cl->client_id;
|
||||
cl_info->cl_uuid = me_cl->props.protocol_name;
|
||||
mei_me_cl_put(me_cl);
|
||||
me_cl = NULL;
|
||||
|
||||
list_add_tail(&cl_info->device_link, &dev->device_list);
|
||||
|
||||
ndev->cl_info = cl_info;
|
||||
|
||||
/* check for valid client id */
|
||||
me_cl = mei_me_cl_by_uuid(dev, &mei_nfc_guid);
|
||||
if (!me_cl) {
|
||||
dev_info(dev->dev, "nfc: failed to find the client\n");
|
||||
ret = -ENOTTY;
|
||||
goto err;
|
||||
}
|
||||
|
||||
cl = mei_cl_alloc_linked(dev, MEI_HOST_CLIENT_ID_ANY);
|
||||
if (IS_ERR(cl)) {
|
||||
ret = PTR_ERR(cl);
|
||||
goto err;
|
||||
}
|
||||
|
||||
cl->me_client_id = me_cl->client_id;
|
||||
cl->cl_uuid = me_cl->props.protocol_name;
|
||||
mei_me_cl_put(me_cl);
|
||||
me_cl = NULL;
|
||||
|
||||
list_add_tail(&cl->device_link, &dev->device_list);
|
||||
|
||||
ndev->cl = cl;
|
||||
|
||||
ndev->req_id = 1;
|
||||
|
||||
INIT_WORK(&ndev->init_work, mei_nfc_init);
|
||||
init_waitqueue_head(&ndev->send_wq);
|
||||
schedule_work(&ndev->init_work);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
mei_me_cl_put(me_cl);
|
||||
mei_nfc_free(ndev);
|
||||
|
||||
return ret;
|
||||
|
@ -338,7 +338,7 @@ static int mei_txe_pm_runtime_suspend(struct device *device)
|
||||
* However if device is not wakeable we do not enter
|
||||
* D-low state and we need to keep the interrupt kicking
|
||||
*/
|
||||
if (!ret && pci_dev_run_wake(pdev))
|
||||
if (!ret && pci_dev_run_wake(pdev))
|
||||
mei_disable_interrupts(dev);
|
||||
|
||||
dev_dbg(&pdev->dev, "rpm: txe: runtime suspend ret=%d\n", ret);
|
||||
|
@ -50,15 +50,15 @@ static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout)
|
||||
* mei_wd_host_init - connect to the watchdog client
|
||||
*
|
||||
* @dev: the device structure
|
||||
* @me_cl: me client
|
||||
*
|
||||
* Return: -ENOTTY if wd client cannot be found
|
||||
* -EIO if write has failed
|
||||
* 0 on success
|
||||
*/
|
||||
int mei_wd_host_init(struct mei_device *dev)
|
||||
int mei_wd_host_init(struct mei_device *dev, struct mei_me_client *me_cl)
|
||||
{
|
||||
struct mei_cl *cl = &dev->wd_cl;
|
||||
struct mei_me_client *me_cl;
|
||||
int ret;
|
||||
|
||||
mei_cl_init(cl, dev);
|
||||
@ -66,27 +66,13 @@ int mei_wd_host_init(struct mei_device *dev)
|
||||
dev->wd_timeout = MEI_WD_DEFAULT_TIMEOUT;
|
||||
dev->wd_state = MEI_WD_IDLE;
|
||||
|
||||
|
||||
/* check for valid client id */
|
||||
me_cl = mei_me_cl_by_uuid(dev, &mei_wd_guid);
|
||||
if (!me_cl) {
|
||||
dev_info(dev->dev, "wd: failed to find the client\n");
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
cl->me_client_id = me_cl->client_id;
|
||||
cl->cl_uuid = me_cl->props.protocol_name;
|
||||
mei_me_cl_put(me_cl);
|
||||
|
||||
ret = mei_cl_link(cl, MEI_WD_HOST_CLIENT_ID);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_info(dev->dev, "wd: failed link client\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mei_cl_connect(cl, NULL);
|
||||
|
||||
ret = mei_cl_connect(cl, me_cl, NULL);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "wd: failed to connect = %d\n", ret);
|
||||
mei_cl_unlink(cl);
|
||||
@ -118,7 +104,7 @@ int mei_wd_send(struct mei_device *dev)
|
||||
int ret;
|
||||
|
||||
hdr.host_addr = cl->host_client_id;
|
||||
hdr.me_addr = cl->me_client_id;
|
||||
hdr.me_addr = mei_cl_me_id(cl);
|
||||
hdr.msg_complete = 1;
|
||||
hdr.reserved = 0;
|
||||
hdr.internal = 0;
|
||||
|
@ -15,11 +15,28 @@ config INTEL_MIC_BUS
|
||||
OS and tools for MIC to use with this driver are available from
|
||||
<http://software.intel.com/en-us/mic-developer>.
|
||||
|
||||
comment "SCIF Bus Driver"
|
||||
|
||||
config SCIF_BUS
|
||||
tristate "SCIF Bus Driver"
|
||||
depends on 64BIT && PCI && X86 && X86_DEV_DMA_OPS
|
||||
help
|
||||
This option is selected by any driver which registers a
|
||||
device or driver on the SCIF Bus, such as CONFIG_INTEL_MIC_HOST
|
||||
and CONFIG_INTEL_MIC_CARD.
|
||||
|
||||
If you are building a host/card kernel with an Intel MIC device
|
||||
then say M (recommended) or Y, else say N. If unsure say N.
|
||||
|
||||
More information about the Intel MIC family as well as the Linux
|
||||
OS and tools for MIC to use with this driver are available from
|
||||
<http://software.intel.com/en-us/mic-developer>.
|
||||
|
||||
comment "Intel MIC Host Driver"
|
||||
|
||||
config INTEL_MIC_HOST
|
||||
tristate "Intel MIC Host Driver"
|
||||
depends on 64BIT && PCI && X86 && INTEL_MIC_BUS
|
||||
depends on 64BIT && PCI && X86 && INTEL_MIC_BUS && SCIF_BUS
|
||||
select VHOST_RING
|
||||
help
|
||||
This enables Host Driver support for the Intel Many Integrated
|
||||
@ -39,7 +56,7 @@ comment "Intel MIC Card Driver"
|
||||
|
||||
config INTEL_MIC_CARD
|
||||
tristate "Intel MIC Card Driver"
|
||||
depends on 64BIT && X86 && INTEL_MIC_BUS
|
||||
depends on 64BIT && X86 && INTEL_MIC_BUS && SCIF_BUS
|
||||
select VIRTIO
|
||||
help
|
||||
This enables card driver support for the Intel Many Integrated
|
||||
@ -52,3 +69,22 @@ config INTEL_MIC_CARD
|
||||
|
||||
For more information see
|
||||
<http://software.intel.com/en-us/mic-developer>.
|
||||
|
||||
comment "SCIF Driver"
|
||||
|
||||
config SCIF
|
||||
tristate "SCIF Driver"
|
||||
depends on 64BIT && PCI && X86 && SCIF_BUS
|
||||
help
|
||||
This enables SCIF Driver support for the Intel Many Integrated
|
||||
Core (MIC) family of PCIe form factor coprocessor devices that
|
||||
run a 64 bit Linux OS. The Symmetric Communication Interface
|
||||
(SCIF (pronounced as skiff)) is a low level communications API
|
||||
across PCIe currently implemented for MIC.
|
||||
|
||||
If you are building a host kernel with an Intel MIC device then
|
||||
say M (recommended) or Y, else say N. If unsure say N.
|
||||
|
||||
More information about the Intel MIC family as well as the Linux
|
||||
OS and tools for MIC to use with this driver are available from
|
||||
<http://software.intel.com/en-us/mic-developer>.
|
||||
|
@ -4,4 +4,5 @@
|
||||
#
|
||||
obj-$(CONFIG_INTEL_MIC_HOST) += host/
|
||||
obj-$(CONFIG_INTEL_MIC_CARD) += card/
|
||||
obj-$(CONFIG_INTEL_MIC_BUS) += bus/
|
||||
obj-y += bus/
|
||||
obj-$(CONFIG_SCIF) += scif/
|
||||
|
@ -3,3 +3,4 @@
|
||||
# Copyright(c) 2014, Intel Corporation.
|
||||
#
|
||||
obj-$(CONFIG_INTEL_MIC_BUS) += mic_bus.o
|
||||
obj-$(CONFIG_SCIF_BUS) += scif_bus.o
|
||||
|
210
drivers/misc/mic/bus/scif_bus.c
Normal file
210
drivers/misc/mic/bus/scif_bus.c
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Intel MIC Platform Software Stack (MPSS)
|
||||
*
|
||||
* Copyright(c) 2014 Intel Corporation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Intel Symmetric Communications Interface Bus driver.
|
||||
*/
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include "scif_bus.h"
|
||||
|
||||
static ssize_t device_show(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct scif_hw_dev *dev = dev_to_scif(d);
|
||||
|
||||
return sprintf(buf, "0x%04x\n", dev->id.device);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(device);
|
||||
|
||||
static ssize_t vendor_show(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct scif_hw_dev *dev = dev_to_scif(d);
|
||||
|
||||
return sprintf(buf, "0x%04x\n", dev->id.vendor);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(vendor);
|
||||
|
||||
static ssize_t modalias_show(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct scif_hw_dev *dev = dev_to_scif(d);
|
||||
|
||||
return sprintf(buf, "scif:d%08Xv%08X\n",
|
||||
dev->id.device, dev->id.vendor);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(modalias);
|
||||
|
||||
static struct attribute *scif_dev_attrs[] = {
|
||||
&dev_attr_device.attr,
|
||||
&dev_attr_vendor.attr,
|
||||
&dev_attr_modalias.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(scif_dev);
|
||||
|
||||
static inline int scif_id_match(const struct scif_hw_dev *dev,
|
||||
const struct scif_hw_dev_id *id)
|
||||
{
|
||||
if (id->device != dev->id.device && id->device != SCIF_DEV_ANY_ID)
|
||||
return 0;
|
||||
|
||||
return id->vendor == SCIF_DEV_ANY_ID || id->vendor == dev->id.vendor;
|
||||
}
|
||||
|
||||
/*
|
||||
* This looks through all the IDs a driver claims to support. If any of them
|
||||
* match, we return 1 and the kernel will call scif_dev_probe().
|
||||
*/
|
||||
static int scif_dev_match(struct device *dv, struct device_driver *dr)
|
||||
{
|
||||
unsigned int i;
|
||||
struct scif_hw_dev *dev = dev_to_scif(dv);
|
||||
const struct scif_hw_dev_id *ids;
|
||||
|
||||
ids = drv_to_scif(dr)->id_table;
|
||||
for (i = 0; ids[i].device; i++)
|
||||
if (scif_id_match(dev, &ids[i]))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scif_uevent(struct device *dv, struct kobj_uevent_env *env)
|
||||
{
|
||||
struct scif_hw_dev *dev = dev_to_scif(dv);
|
||||
|
||||
return add_uevent_var(env, "MODALIAS=scif:d%08Xv%08X",
|
||||
dev->id.device, dev->id.vendor);
|
||||
}
|
||||
|
||||
static int scif_dev_probe(struct device *d)
|
||||
{
|
||||
struct scif_hw_dev *dev = dev_to_scif(d);
|
||||
struct scif_driver *drv = drv_to_scif(dev->dev.driver);
|
||||
|
||||
return drv->probe(dev);
|
||||
}
|
||||
|
||||
static int scif_dev_remove(struct device *d)
|
||||
{
|
||||
struct scif_hw_dev *dev = dev_to_scif(d);
|
||||
struct scif_driver *drv = drv_to_scif(dev->dev.driver);
|
||||
|
||||
drv->remove(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct bus_type scif_bus = {
|
||||
.name = "scif_bus",
|
||||
.match = scif_dev_match,
|
||||
.dev_groups = scif_dev_groups,
|
||||
.uevent = scif_uevent,
|
||||
.probe = scif_dev_probe,
|
||||
.remove = scif_dev_remove,
|
||||
};
|
||||
|
||||
int scif_register_driver(struct scif_driver *driver)
|
||||
{
|
||||
driver->driver.bus = &scif_bus;
|
||||
return driver_register(&driver->driver);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scif_register_driver);
|
||||
|
||||
void scif_unregister_driver(struct scif_driver *driver)
|
||||
{
|
||||
driver_unregister(&driver->driver);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scif_unregister_driver);
|
||||
|
||||
static void scif_release_dev(struct device *d)
|
||||
{
|
||||
struct scif_hw_dev *sdev = dev_to_scif(d);
|
||||
|
||||
kfree(sdev);
|
||||
}
|
||||
|
||||
struct scif_hw_dev *
|
||||
scif_register_device(struct device *pdev, int id, struct dma_map_ops *dma_ops,
|
||||
struct scif_hw_ops *hw_ops, u8 dnode, u8 snode,
|
||||
struct mic_mw *mmio, struct mic_mw *aper, void *dp,
|
||||
void __iomem *rdp, struct dma_chan **chan, int num_chan)
|
||||
{
|
||||
int ret;
|
||||
struct scif_hw_dev *sdev;
|
||||
|
||||
sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
|
||||
if (!sdev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
sdev->dev.parent = pdev;
|
||||
sdev->id.device = id;
|
||||
sdev->id.vendor = SCIF_DEV_ANY_ID;
|
||||
sdev->dev.archdata.dma_ops = dma_ops;
|
||||
sdev->dev.release = scif_release_dev;
|
||||
sdev->hw_ops = hw_ops;
|
||||
sdev->dnode = dnode;
|
||||
sdev->snode = snode;
|
||||
dev_set_drvdata(&sdev->dev, sdev);
|
||||
sdev->dev.bus = &scif_bus;
|
||||
sdev->mmio = mmio;
|
||||
sdev->aper = aper;
|
||||
sdev->dp = dp;
|
||||
sdev->rdp = rdp;
|
||||
sdev->dev.dma_mask = &sdev->dev.coherent_dma_mask;
|
||||
dma_set_mask(&sdev->dev, DMA_BIT_MASK(64));
|
||||
sdev->dma_ch = chan;
|
||||
sdev->num_dma_ch = num_chan;
|
||||
dev_set_name(&sdev->dev, "scif-dev%u", sdev->dnode);
|
||||
/*
|
||||
* device_register() causes the bus infrastructure to look for a
|
||||
* matching driver.
|
||||
*/
|
||||
ret = device_register(&sdev->dev);
|
||||
if (ret)
|
||||
goto free_sdev;
|
||||
return sdev;
|
||||
free_sdev:
|
||||
kfree(sdev);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scif_register_device);
|
||||
|
||||
void scif_unregister_device(struct scif_hw_dev *sdev)
|
||||
{
|
||||
device_unregister(&sdev->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scif_unregister_device);
|
||||
|
||||
static int __init scif_init(void)
|
||||
{
|
||||
return bus_register(&scif_bus);
|
||||
}
|
||||
|
||||
static void __exit scif_exit(void)
|
||||
{
|
||||
bus_unregister(&scif_bus);
|
||||
}
|
||||
|
||||
core_initcall(scif_init);
|
||||
module_exit(scif_exit);
|
||||
|
||||
MODULE_AUTHOR("Intel Corporation");
|
||||
MODULE_DESCRIPTION("Intel(R) SCIF Bus driver");
|
||||
MODULE_LICENSE("GPL v2");
|
129
drivers/misc/mic/bus/scif_bus.h
Normal file
129
drivers/misc/mic/bus/scif_bus.h
Normal file
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Intel MIC Platform Software Stack (MPSS)
|
||||
*
|
||||
* Copyright(c) 2014 Intel Corporation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Intel Symmetric Communications Interface Bus driver.
|
||||
*/
|
||||
#ifndef _SCIF_BUS_H_
|
||||
#define _SCIF_BUS_H_
|
||||
/*
|
||||
* Everything a scif driver needs to work with any particular scif
|
||||
* hardware abstraction layer.
|
||||
*/
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include <linux/mic_common.h>
|
||||
#include "../common/mic_dev.h"
|
||||
|
||||
struct scif_hw_dev_id {
|
||||
u32 device;
|
||||
u32 vendor;
|
||||
};
|
||||
|
||||
#define MIC_SCIF_DEV 1
|
||||
#define SCIF_DEV_ANY_ID 0xffffffff
|
||||
|
||||
/**
|
||||
* scif_hw_dev - representation of a hardware device abstracted for scif
|
||||
* @hw_ops: the hardware ops supported by this device
|
||||
* @id: the device type identification (used to match it with a driver)
|
||||
* @mmio: MMIO memory window
|
||||
* @aper: Aperture memory window
|
||||
* @dev: underlying device
|
||||
* @dnode - The destination node which this device will communicate with.
|
||||
* @snode - The source node for this device.
|
||||
* @dp - Self device page
|
||||
* @rdp - Remote device page
|
||||
* @dma_ch - Array of DMA channels
|
||||
* @num_dma_ch - Number of DMA channels available
|
||||
*/
|
||||
struct scif_hw_dev {
|
||||
struct scif_hw_ops *hw_ops;
|
||||
struct scif_hw_dev_id id;
|
||||
struct mic_mw *mmio;
|
||||
struct mic_mw *aper;
|
||||
struct device dev;
|
||||
u8 dnode;
|
||||
u8 snode;
|
||||
void *dp;
|
||||
void __iomem *rdp;
|
||||
struct dma_chan **dma_ch;
|
||||
int num_dma_ch;
|
||||
};
|
||||
|
||||
/**
|
||||
* scif_driver - operations for a scif I/O driver
|
||||
* @driver: underlying device driver (populate name and owner).
|
||||
* @id_table: the ids serviced by this driver.
|
||||
* @probe: the function to call when a device is found. Returns 0 or -errno.
|
||||
* @remove: the function to call when a device is removed.
|
||||
*/
|
||||
struct scif_driver {
|
||||
struct device_driver driver;
|
||||
const struct scif_hw_dev_id *id_table;
|
||||
int (*probe)(struct scif_hw_dev *dev);
|
||||
void (*remove)(struct scif_hw_dev *dev);
|
||||
};
|
||||
|
||||
/**
|
||||
* scif_hw_ops - Hardware operations for accessing a SCIF device on the SCIF bus.
|
||||
*
|
||||
* @next_db: Obtain the next available doorbell.
|
||||
* @request_irq: Request an interrupt on a particular doorbell.
|
||||
* @free_irq: Free an interrupt requested previously.
|
||||
* @ack_interrupt: acknowledge an interrupt in the ISR.
|
||||
* @send_intr: Send an interrupt to the remote node on a specified doorbell.
|
||||
* @send_p2p_intr: Send an interrupt to the peer node on a specified doorbell
|
||||
* which is specifically targeted for a peer to peer node.
|
||||
* @ioremap: Map a buffer with the specified physical address and length.
|
||||
* @iounmap: Unmap a buffer previously mapped.
|
||||
*/
|
||||
struct scif_hw_ops {
|
||||
int (*next_db)(struct scif_hw_dev *sdev);
|
||||
struct mic_irq * (*request_irq)(struct scif_hw_dev *sdev,
|
||||
irqreturn_t (*func)(int irq,
|
||||
void *data),
|
||||
const char *name, void *data,
|
||||
int db);
|
||||
void (*free_irq)(struct scif_hw_dev *sdev,
|
||||
struct mic_irq *cookie, void *data);
|
||||
void (*ack_interrupt)(struct scif_hw_dev *sdev, int num);
|
||||
void (*send_intr)(struct scif_hw_dev *sdev, int db);
|
||||
void (*send_p2p_intr)(struct scif_hw_dev *sdev, int db,
|
||||
struct mic_mw *mw);
|
||||
void __iomem * (*ioremap)(struct scif_hw_dev *sdev,
|
||||
phys_addr_t pa, size_t len);
|
||||
void (*iounmap)(struct scif_hw_dev *sdev, void __iomem *va);
|
||||
};
|
||||
|
||||
int scif_register_driver(struct scif_driver *driver);
|
||||
void scif_unregister_driver(struct scif_driver *driver);
|
||||
struct scif_hw_dev *
|
||||
scif_register_device(struct device *pdev, int id,
|
||||
struct dma_map_ops *dma_ops,
|
||||
struct scif_hw_ops *hw_ops, u8 dnode, u8 snode,
|
||||
struct mic_mw *mmio, struct mic_mw *aper,
|
||||
void *dp, void __iomem *rdp,
|
||||
struct dma_chan **chan, int num_chan);
|
||||
void scif_unregister_device(struct scif_hw_dev *sdev);
|
||||
|
||||
static inline struct scif_hw_dev *dev_to_scif(struct device *dev)
|
||||
{
|
||||
return container_of(dev, struct scif_hw_dev, dev);
|
||||
}
|
||||
|
||||
static inline struct scif_driver *drv_to_scif(struct device_driver *drv)
|
||||
{
|
||||
return container_of(drv, struct scif_driver, driver);
|
||||
}
|
||||
#endif /* _SCIF_BUS_H */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user