Char/Misc driver patches for 3.15-rc1
Here's the big char/misc driver updates for 3.15-rc1. Lots of various things here, including the new mcb driver subsystem. All of these have been in linux-next for a while. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iEYEABECAAYFAlM7ArIACgkQMUfUDdst+ylS+gCfcJr0Zo2v5aWnqD7rFtFETmFI LhcAoNTQ4cvlVdxnI0driWCWFYxLj6at =aj+L -----END PGP SIGNATURE----- Merge tag 'char-misc-3.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc Pull char/misc driver patches from Greg KH: "Here's the big char/misc driver updates for 3.15-rc1. Lots of various things here, including the new mcb driver subsystem. All of these have been in linux-next for a while" * tag 'char-misc-3.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (118 commits) extcon: Move OF helper function to extcon core and change function name extcon: of: Remove unnecessary function call by using the name of device_node extcon: gpio: Use SIMPLE_DEV_PM_OPS macro extcon: palmas: Use SIMPLE_DEV_PM_OPS macro mei: don't use deprecated DEFINE_PCI_DEVICE_TABLE macro mei: amthif: fix checkpatch error mei: client.h fix checkpatch errors mei: use cl_dbg where appropriate mei: fix Unnecessary space after function pointer name mei: report consistently copy_from/to_user failures mei: drop pr_fmt macros mei: make me hw headers private to me hw. mei: fix memory leak of pending write cb objects mei: me: do not reset when less than expected data is received drivers: mcb: Fix build error discovered by 0-day bot cs5535-mfgpt: Simplify dependencies spmi: pm: drop bus-level PM suspend/resume routines spmi: pmic_arb: make selectable on ARCH_QCOM Drivers: hv: vmbus: Increase the limit on the number of pfns we can handle pch_phub: Report error writing MAC back to user ...
This commit is contained in:
Коммит
675c354a95
|
@ -14,7 +14,7 @@ DOCBOOKS := z8530book.xml device-drivers.xml \
|
|||
genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \
|
||||
80211.xml debugobjects.xml sh.xml regulator.xml \
|
||||
alsa-driver-api.xml writing-an-alsa-driver.xml \
|
||||
tracepoint.xml drm.xml media_api.xml
|
||||
tracepoint.xml drm.xml media_api.xml w1.xml
|
||||
|
||||
include $(srctree)/Documentation/DocBook/media/Makefile
|
||||
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
|
||||
|
||||
<book id="w1id">
|
||||
<bookinfo>
|
||||
<title>W1: Dallas' 1-wire bus</title>
|
||||
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>David</firstname>
|
||||
<surname>Fries</surname>
|
||||
<affiliation>
|
||||
<address>
|
||||
<email>David@Fries.net</email>
|
||||
</address>
|
||||
</affiliation>
|
||||
</author>
|
||||
|
||||
</authorgroup>
|
||||
|
||||
<copyright>
|
||||
<year>2013</year>
|
||||
<!--
|
||||
<holder></holder>
|
||||
-->
|
||||
</copyright>
|
||||
|
||||
<legalnotice>
|
||||
<para>
|
||||
This documentation is free software; you can redistribute
|
||||
it and/or modify it under the terms of the GNU General Public
|
||||
License version 2.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
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.
|
||||
For more details see the file COPYING in the source
|
||||
distribution of Linux.
|
||||
</para>
|
||||
</legalnotice>
|
||||
</bookinfo>
|
||||
|
||||
<toc></toc>
|
||||
|
||||
<chapter id="w1_internal">
|
||||
<title>W1 API internal to the kernel</title>
|
||||
|
||||
<sect1 id="w1_internal_api">
|
||||
<title>W1 API internal to the kernel</title>
|
||||
<sect2 id="w1.h">
|
||||
<title>drivers/w1/w1.h</title>
|
||||
<para>W1 core functions.</para>
|
||||
!Idrivers/w1/w1.h
|
||||
</sect2>
|
||||
|
||||
<sect2 id="w1.c">
|
||||
<title>drivers/w1/w1.c</title>
|
||||
<para>W1 core functions.</para>
|
||||
!Idrivers/w1/w1.c
|
||||
</sect2>
|
||||
|
||||
<sect2 id="w1_family.h">
|
||||
<title>drivers/w1/w1_family.h</title>
|
||||
<para>Allows registering device family operations.</para>
|
||||
!Idrivers/w1/w1_family.h
|
||||
</sect2>
|
||||
|
||||
<sect2 id="w1_family.c">
|
||||
<title>drivers/w1/w1_family.c</title>
|
||||
<para>Allows registering device family operations.</para>
|
||||
!Edrivers/w1/w1_family.c
|
||||
</sect2>
|
||||
|
||||
<sect2 id="w1_int.c">
|
||||
<title>drivers/w1/w1_int.c</title>
|
||||
<para>W1 internal initialization for master devices.</para>
|
||||
!Edrivers/w1/w1_int.c
|
||||
</sect2>
|
||||
|
||||
<sect2 id="w1_netlink.h">
|
||||
<title>drivers/w1/w1_netlink.h</title>
|
||||
<para>W1 external netlink API structures and commands.</para>
|
||||
!Idrivers/w1/w1_netlink.h
|
||||
</sect2>
|
||||
|
||||
<sect2 id="w1_io.c">
|
||||
<title>drivers/w1/w1_io.c</title>
|
||||
<para>W1 input/output.</para>
|
||||
!Edrivers/w1/w1_io.c
|
||||
!Idrivers/w1/w1_io.c
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
|
||||
</chapter>
|
||||
|
||||
</book>
|
|
@ -145,7 +145,7 @@ static void cn_test_timer_func(unsigned long __data)
|
|||
|
||||
memcpy(m + 1, data, m->len);
|
||||
|
||||
cn_netlink_send(m, 0, GFP_ATOMIC);
|
||||
cn_netlink_send(m, 0, 0, GFP_ATOMIC);
|
||||
kfree(m);
|
||||
}
|
||||
|
||||
|
|
|
@ -410,6 +410,7 @@ Your cooperation is appreciated.
|
|||
194 = /dev/zkshim Zero-Knowledge network shim control
|
||||
195 = /dev/elographics/e2201 Elographics touchscreen E271-2201
|
||||
196 = /dev/vfio/vfio VFIO userspace driver interface
|
||||
197 = /dev/pxa3xx-gcu PXA3xx graphics controller unit driver
|
||||
198 = /dev/sexec Signed executable interface
|
||||
199 = /dev/scanners/cuecat :CueCat barcode scanner
|
||||
200 = /dev/net/tun TAP/TUN network device
|
||||
|
@ -451,6 +452,7 @@ Your cooperation is appreciated.
|
|||
236 = /dev/mapper/control Device-Mapper control device
|
||||
237 = /dev/loop-control Loopback control device
|
||||
238 = /dev/vhost-net Host kernel accelerator for virtio net
|
||||
239 = /dev/uhid User-space I/O driver support for HID subsystem
|
||||
|
||||
240-254 Reserved for local use
|
||||
255 Reserved for MISC_DYNAMIC_MINOR
|
||||
|
|
|
@ -0,0 +1,210 @@
|
|||
* Device tree bindings for Texas instruments AEMIF controller
|
||||
|
||||
The Async External Memory Interface (EMIF16/AEMIF) controller is intended to
|
||||
provide a glue-less interface to a variety of asynchronous memory devices like
|
||||
ASRA M, NOR and NAND memory. A total of 256M bytes of any of these memories
|
||||
can be accessed at any given time via four chip selects with 64M byte access
|
||||
per chip select. Synchronous memories such as DDR1 SD RAM, SDR SDRAM
|
||||
and Mobile SDR are not supported.
|
||||
|
||||
Documentation:
|
||||
Davinci DM646x - http://www.ti.com/lit/ug/sprueq7c/sprueq7c.pdf
|
||||
OMAP-L138 (DA850) - http://www.ti.com/lit/ug/spruh77a/spruh77a.pdf
|
||||
Kestone - http://www.ti.com/lit/ug/sprugz3a/sprugz3a.pdf
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: "ti,davinci-aemif"
|
||||
"ti,keystone-aemif"
|
||||
"ti,da850-aemif"
|
||||
|
||||
- reg: contains offset/length value for AEMIF control registers
|
||||
space.
|
||||
|
||||
- #address-cells: Must be 2. The partition number has to be encoded in the
|
||||
first address cell and it may accept values 0..N-1
|
||||
(N - total number of partitions). It's recommended to
|
||||
assign N-1 number for the control partition. The second
|
||||
cell is the offset into the partition.
|
||||
|
||||
- #size-cells: Must be set to 1.
|
||||
|
||||
- ranges: Contains memory regions. There are two types of
|
||||
ranges/partitions:
|
||||
- CS-specific partition/range. If continuous, must be
|
||||
set up to reflect the memory layout for 4 chipselects,
|
||||
if not then additional range/partition can be added and
|
||||
child device can select the proper one.
|
||||
- control partition which is common for all CS
|
||||
interfaces.
|
||||
|
||||
- clocks: the clock feeding the controller clock. Required only
|
||||
if clock tree data present in device tree.
|
||||
See clock-bindings.txt
|
||||
|
||||
- clock-names: clock name. It has to be "aemif". Required only if clock
|
||||
tree data present in device tree, in another case don't
|
||||
use it.
|
||||
See clock-bindings.txt
|
||||
|
||||
- clock-ranges: Empty property indicating that child nodes can inherit
|
||||
named clocks. Required only if clock tree data present
|
||||
in device tree.
|
||||
See clock-bindings.txt
|
||||
|
||||
|
||||
Child chip-select (cs) nodes contain the memory devices nodes connected to
|
||||
such as NOR (e.g. cfi-flash) and NAND (ti,davinci-nand, see davinci-nand.txt).
|
||||
There might be board specific devices like FPGAs.
|
||||
|
||||
Required child cs node properties:
|
||||
|
||||
- #address-cells: Must be 2.
|
||||
|
||||
- #size-cells: Must be 1.
|
||||
|
||||
- ranges: Empty property indicating that child nodes can inherit
|
||||
memory layout.
|
||||
|
||||
- clock-ranges: Empty property indicating that child nodes can inherit
|
||||
named clocks. Required only if clock tree data present
|
||||
in device tree.
|
||||
|
||||
- ti,cs-chipselect: number of chipselect. Indicates on the aemif driver
|
||||
which chipselect is used for accessing the memory. For
|
||||
compatibles "ti,davinci-aemif" and "ti,keystone-aemif"
|
||||
it can be in range [0-3]. For compatible
|
||||
"ti,da850-aemif" range is [2-5].
|
||||
|
||||
Optional child cs node properties:
|
||||
|
||||
- ti,cs-bus-width: width of the asynchronous device's data bus
|
||||
8 or 16 if not preset 8
|
||||
|
||||
- ti,cs-select-strobe-mode: enable/disable select strobe mode
|
||||
In select strobe mode chip select behaves as
|
||||
the strobe and is active only during the strobe
|
||||
period. If present then enable.
|
||||
|
||||
- ti,cs-extended-wait-mode: enable/disable extended wait mode
|
||||
if set, the controller monitors the EMIFWAIT pin
|
||||
mapped to that chip select to determine if the
|
||||
device wants to extend the strobe period. If
|
||||
present then enable.
|
||||
|
||||
- ti,cs-min-turnaround-ns: minimum turn around time, ns
|
||||
Time between the end of one asynchronous memory
|
||||
access and the start of another asynchronous
|
||||
memory access. This delay is not incurred
|
||||
between a read followed by read or a write
|
||||
followed by a write to same chip select.
|
||||
|
||||
- ti,cs-read-setup-ns: read setup width, ns
|
||||
Time between the beginning of a memory cycle
|
||||
and the activation of read strobe.
|
||||
Minimum value is 1 (0 treated as 1).
|
||||
|
||||
- ti,cs-read-strobe-ns: read strobe width, ns
|
||||
Time between the activation and deactivation of
|
||||
the read strobe.
|
||||
Minimum value is 1 (0 treated as 1).
|
||||
|
||||
- ti,cs-read-hold-ns: read hold width, ns
|
||||
Time between the deactivation of the read
|
||||
strobe and the end of the cycle (which may be
|
||||
either an address change or the deactivation of
|
||||
the chip select signal.
|
||||
Minimum value is 1 (0 treated as 1).
|
||||
|
||||
- ti,cs-write-setup-ns: write setup width, ns
|
||||
Time between the beginning of a memory cycle
|
||||
and the activation of write strobe.
|
||||
Minimum value is 1 (0 treated as 1).
|
||||
|
||||
- ti,cs-write-strobe-ns: write strobe width, ns
|
||||
Time between the activation and deactivation of
|
||||
the write strobe.
|
||||
Minimum value is 1 (0 treated as 1).
|
||||
|
||||
- ti,cs-write-hold-ns: write hold width, ns
|
||||
Time between the deactivation of the write
|
||||
strobe and the end of the cycle (which may be
|
||||
either an address change or the deactivation of
|
||||
the chip select signal.
|
||||
Minimum value is 1 (0 treated as 1).
|
||||
|
||||
If any of the above parameters are absent, current parameter value will be taken
|
||||
from the corresponding HW reg.
|
||||
|
||||
Example for aemif, davinci nand and nor flash chip select shown below.
|
||||
|
||||
memory-controller@21000A00 {
|
||||
compatible = "ti,davinci-aemif";
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
clocks = <&clkaemif 0>;
|
||||
clock-names = "aemif";
|
||||
clock-ranges;
|
||||
reg = <0x21000A00 0x00000100>;
|
||||
ranges = <0 0 0x70000000 0x10000000
|
||||
1 0 0x21000A00 0x00000100>;
|
||||
/*
|
||||
* Partition0: CS-specific memory range which is
|
||||
* implemented as continuous physical memory region
|
||||
* Partition1: control memory range
|
||||
*/
|
||||
|
||||
nand:cs2 {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
clock-ranges;
|
||||
ranges;
|
||||
|
||||
ti,cs-chipselect = <2>;
|
||||
/* all timings in nanoseconds */
|
||||
ti,cs-min-turnaround-ns = <0>;
|
||||
ti,cs-read-hold-ns = <7>;
|
||||
ti,cs-read-strobe-ns = <42>;
|
||||
ti,cs-read-setup-ns = <14>;
|
||||
ti,cs-write-hold-ns = <7>;
|
||||
ti,cs-write-strobe-ns = <42>;
|
||||
ti,cs-write-setup-ns = <14>;
|
||||
|
||||
nand@0,0x8000000 {
|
||||
compatible = "ti,davinci-nand";
|
||||
reg = <0 0x8000000 0x4000000
|
||||
1 0x0000000 0x0000100>;
|
||||
/*
|
||||
* Partition0, offset 0x8000000, size 0x4000000
|
||||
* Partition1, offset 0x0000000, size 0x0000100
|
||||
*/
|
||||
|
||||
.. see davinci-nand.txt
|
||||
};
|
||||
};
|
||||
|
||||
nor:cs0 {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
clock-ranges;
|
||||
ranges;
|
||||
|
||||
ti,cs-chipselect = <0>;
|
||||
/* all timings in nanoseconds */
|
||||
ti,cs-min-turnaround-ns = <0>;
|
||||
ti,cs-read-hold-ns = <8>;
|
||||
ti,cs-read-strobe-ns = <40>;
|
||||
ti,cs-read-setup-ns = <14>;
|
||||
ti,cs-write-hold-ns = <7>;
|
||||
ti,cs-write-strobe-ns = <40>;
|
||||
ti,cs-write-setup-ns = <14>;
|
||||
ti,cs-bus-width = <16>;
|
||||
|
||||
flash@0,0x0000000 {
|
||||
compatible = "cfi-flash";
|
||||
reg = <0 0x0000000 0x4000000>;
|
||||
|
||||
...
|
||||
};
|
||||
};
|
||||
};
|
|
@ -1,12 +1,12 @@
|
|||
Allwinner sunxi-sid
|
||||
|
||||
Required properties:
|
||||
- compatible: "allwinner,sun4i-sid" or "allwinner,sun7i-a20-sid".
|
||||
- compatible: "allwinner,sun4i-a10-sid" or "allwinner,sun7i-a20-sid"
|
||||
- reg: Should contain registers location and length
|
||||
|
||||
Example for sun4i:
|
||||
sid@01c23800 {
|
||||
compatible = "allwinner,sun4i-sid";
|
||||
compatible = "allwinner,sun4i-a10-sid";
|
||||
reg = <0x01c23800 0x10>
|
||||
};
|
||||
|
||||
|
|
|
@ -8,9 +8,44 @@ Required properties:
|
|||
|
||||
- reg : SRAM iomem address range
|
||||
|
||||
Reserving sram areas:
|
||||
---------------------
|
||||
|
||||
Each child of the sram node specifies a region of reserved memory. Each
|
||||
child node should use a 'reg' property to specify a specific range of
|
||||
reserved memory.
|
||||
|
||||
Following the generic-names recommended practice, node names should
|
||||
reflect the purpose of the node. Unit address (@<address>) should be
|
||||
appended to the name.
|
||||
|
||||
Required properties in the sram node:
|
||||
|
||||
- #address-cells, #size-cells : should use the same values as the root node
|
||||
- ranges : standard definition, should translate from local addresses
|
||||
within the sram to bus addresses
|
||||
|
||||
Required properties in the area nodes:
|
||||
|
||||
- reg : iomem address range, relative to the SRAM range
|
||||
|
||||
Optional properties in the area nodes:
|
||||
|
||||
- compatible : standard definition, should contain a vendor specific string
|
||||
in the form <vendor>,[<device>-]<usage>
|
||||
|
||||
Example:
|
||||
|
||||
sram: sram@5c000000 {
|
||||
compatible = "mmio-sram";
|
||||
reg = <0x5c000000 0x40000>; /* 256 KiB SRAM at address 0x5c000000 */
|
||||
|
||||
#adress-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0 0x5c000000 0x40000>;
|
||||
|
||||
smp-sram@100 {
|
||||
compatible = "socvendor,smp-sram";
|
||||
reg = <0x100 0x50>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
Qualcomm SPMI Controller (PMIC Arbiter)
|
||||
|
||||
The SPMI PMIC Arbiter is found on the Snapdragon 800 Series. It is an SPMI
|
||||
controller with wrapping arbitration logic to allow for multiple on-chip
|
||||
devices to control a single SPMI master.
|
||||
|
||||
The PMIC Arbiter can also act as an interrupt controller, providing interrupts
|
||||
to slave devices.
|
||||
|
||||
See spmi.txt for the generic SPMI controller binding requirements for child
|
||||
nodes.
|
||||
|
||||
See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt for
|
||||
generic interrupt controller binding documentation.
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "qcom,spmi-pmic-arb".
|
||||
- reg-names : must contain:
|
||||
"core" - core registers
|
||||
"intr" - interrupt controller registers
|
||||
"cnfg" - configuration registers
|
||||
- reg : address + size pairs describing the PMIC arb register sets; order must
|
||||
correspond with the order of entries in reg-names
|
||||
- #address-cells : must be set to 2
|
||||
- #size-cells : must be set to 0
|
||||
- qcom,ee : indicates the active Execution Environment identifier (0-5)
|
||||
- qcom,channel : which of the PMIC Arb provided channels to use for accesses (0-5)
|
||||
- interrupts : interrupt list for the PMIC Arb controller, must contain a
|
||||
single interrupt entry for the peripheral interrupt
|
||||
- interrupt-names : corresponding interrupt names for the interrupts
|
||||
listed in the 'interrupts' property, must contain:
|
||||
"periph_irq" - summary interrupt for PMIC peripherals
|
||||
- interrupt-controller : boolean indicator that the PMIC arbiter is an interrupt controller
|
||||
- #interrupt-cells : must be set to 4. Interrupts are specified as a 4-tuple:
|
||||
cell 1: slave ID for the requested interrupt (0-15)
|
||||
cell 2: peripheral ID for requested interrupt (0-255)
|
||||
cell 3: the requested peripheral interrupt (0-7)
|
||||
cell 4: interrupt flags indicating level-sense information, as defined in
|
||||
dt-bindings/interrupt-controller/irq.h
|
||||
|
||||
Example:
|
||||
|
||||
spmi {
|
||||
compatible = "qcom,spmi-pmic-arb";
|
||||
reg-names = "core", "intr", "cnfg";
|
||||
reg = <0xfc4cf000 0x1000>,
|
||||
<0xfc4cb000 0x1000>,
|
||||
<0xfc4ca000 0x1000>;
|
||||
|
||||
interrupt-names = "periph_irq";
|
||||
interrupts = <0 190 0>;
|
||||
|
||||
qcom,ee = <0>;
|
||||
qcom,channel = <0>;
|
||||
|
||||
#address-cells = <2>;
|
||||
#size-cells = <0>;
|
||||
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <4>;
|
||||
};
|
|
@ -0,0 +1,41 @@
|
|||
System Power Management Interface (SPMI) Controller
|
||||
|
||||
This document defines a generic set of bindings for use by SPMI controllers. A
|
||||
controller is modelled in device tree as a node with zero or more child nodes,
|
||||
each representing a unique slave on the bus.
|
||||
|
||||
Required properties:
|
||||
- #address-cells : must be set to 2
|
||||
- #size-cells : must be set to 0
|
||||
|
||||
Child nodes:
|
||||
|
||||
An SPMI controller node can contain zero or more child nodes representing slave
|
||||
devices on the bus. Child 'reg' properties are specified as an address, type
|
||||
pair. The address must be in the range 0-15 (4 bits). The type must be one of
|
||||
SPMI_USID (0) or SPMI_GSID (1) for Unique Slave ID or Group Slave ID respectively.
|
||||
These are the identifiers "statically assigned by the system integrator", as
|
||||
per the SPMI spec.
|
||||
|
||||
Each child node must have one and only one 'reg' entry of type SPMI_USID.
|
||||
|
||||
#include <dt-bindings/spmi/spmi.h>
|
||||
|
||||
spmi@.. {
|
||||
compatible = "...";
|
||||
reg = <...>;
|
||||
|
||||
#address-cells = <2>;
|
||||
#size-cells <0>;
|
||||
|
||||
child@0 {
|
||||
compatible = "...";
|
||||
reg = <0 SPMI_USID>;
|
||||
};
|
||||
|
||||
child@7 {
|
||||
compatible = "...";
|
||||
reg = <7 SPMI_USID
|
||||
3 SPMI_GSID>;
|
||||
};
|
||||
};
|
|
@ -9,7 +9,12 @@ Overwriting the EEPROM is not something you should do daily, and it is
|
|||
expected to only happen during manufacturing. For this reason, the
|
||||
module makes it unlikely for the random user to change a working EEPROM.
|
||||
|
||||
The module takes the following measures:
|
||||
However, since the EEPROM may include application-specific information
|
||||
other than the identification, later versions of this packages added
|
||||
write-support through sysfs. See *note Accessing the EEPROM::.
|
||||
|
||||
To avoid damaging the EEPROM content, the module takes the following
|
||||
measures:
|
||||
|
||||
* It accepts a `file=' argument (within /lib/firmware) and if no
|
||||
such argument is received, it doesn't write anything to EEPROM
|
||||
|
@ -70,56 +75,24 @@ first time.
|
|||
[ 132.899872] fake-fmc: Product name: FmcDelay1ns4cha
|
||||
|
||||
|
||||
Writing to the EEPROM
|
||||
Accessing the EEPROM
|
||||
=====================
|
||||
|
||||
Once you have created a binary file for your EEPROM, you can write it
|
||||
to the storage medium using the fmc-write-eeprom (See *note
|
||||
fmc-write-eeprom::, while relying on a carrier driver. The procedure
|
||||
here shown here uses the SPEC driver
|
||||
(`http://www.ohwr.org/projects/spec-sw').
|
||||
The bus creates a sysfs binary file called eeprom for each mezzanine it
|
||||
knows about:
|
||||
|
||||
The example assumes no driver is already loaded (actually, I unloaded
|
||||
them by hand as everything loads automatically at boot time after you
|
||||
installed the modules), and shows kernel messages together with
|
||||
commands. Here the prompt is spusa.root# and two SPEC cards are plugged
|
||||
in the system.
|
||||
spusa.root# cd /sys/bus/fmc/devices; ls -l */eeprom
|
||||
-r--r--r-- 1 root root 8192 Feb 21 12:30 FmcAdc100m14b4cha-0800/eeprom
|
||||
-r--r--r-- 1 root root 8192 Feb 21 12:30 FmcDelay1ns4cha-0200/eeprom
|
||||
-r--r--r-- 1 root root 8192 Feb 21 12:30 FmcDio5cha-0400/eeprom
|
||||
|
||||
spusa.root# insmod fmc.ko
|
||||
spusa.root# insmod spec.ko
|
||||
[13972.382818] spec 0000:02:00.0: probe for device 0002:0000
|
||||
[13972.392773] spec 0000:02:00.0: got file "fmc/spec-init.bin", 1484404 (0x16a674) bytes
|
||||
[13972.591388] spec 0000:02:00.0: FPGA programming successful
|
||||
[13972.883011] spec 0000:02:00.0: EEPROM has no FRU information
|
||||
[13972.888719] spec 0000:02:00.0: No device_id filled, using index
|
||||
[13972.894676] spec 0000:02:00.0: No mezzanine_name found
|
||||
[13972.899863] /home/rubini/wip/spec-sw/kernel/spec-gpio.c - spec_gpio_init
|
||||
[13972.906578] spec 0000:04:00.0: probe for device 0004:0000
|
||||
[13972.916509] spec 0000:04:00.0: got file "fmc/spec-init.bin", 1484404 (0x16a674) bytes
|
||||
[13973.115096] spec 0000:04:00.0: FPGA programming successful
|
||||
[13973.401798] spec 0000:04:00.0: EEPROM has no FRU information
|
||||
[13973.407474] spec 0000:04:00.0: No device_id filled, using index
|
||||
[13973.413417] spec 0000:04:00.0: No mezzanine_name found
|
||||
[13973.418600] /home/rubini/wip/spec-sw/kernel/spec-gpio.c - spec_gpio_init
|
||||
spusa.root# ls /sys/bus/fmc/devices
|
||||
fmc-0000 fmc-0001
|
||||
spusa.root# insmod fmc-write-eeprom.ko busid=0x0200 file=fdelay-eeprom.bin
|
||||
[14103.966259] spec 0000:02:00.0: Matching an generic driver (no ID)
|
||||
[14103.975519] spec 0000:02:00.0: programming 6155 bytes
|
||||
[14126.373762] spec 0000:02:00.0: write_eeprom: success
|
||||
[14126.378770] spec 0000:04:00.0: Matching an generic driver (no ID)
|
||||
[14126.384903] spec 0000:04:00.0: fmc_write_eeprom: no filename given: not programming
|
||||
[14126.392600] fmc_write_eeprom: probe of fmc-0001 failed with error -2
|
||||
Everybody can read the files and the superuser can also modify it, but
|
||||
the operation may on the carrier driver, if the carrier is unable to
|
||||
access the I2C bus. For example, the spec driver can access the bus
|
||||
only with its golden gateware: after a mezzanine driver reprogrammed
|
||||
the FPGA with a custom circuit, the carrier is unable to access the
|
||||
EEPROM and returns ENOTSUPP.
|
||||
|
||||
Reading back the EEPROM
|
||||
=======================
|
||||
|
||||
In order to read back the binary content of the EEPROM of your
|
||||
mezzanine device, the bus creates a read-only sysfs file called eeprom
|
||||
for each mezzanine it knows about:
|
||||
|
||||
spusa.root# cd /sys/bus/fmc/devices; ls -l */eeprom
|
||||
-r--r--r-- 1 root root 8192 Apr 9 16:53 FmcDelay1ns4cha-f001/eeprom
|
||||
-r--r--r-- 1 root root 8192 Apr 9 17:19 fake-design-for-testing-f002/eeprom
|
||||
-r--r--r-- 1 root root 8192 Apr 9 17:19 fake-design-for-testing-f003/eeprom
|
||||
-r--r--r-- 1 root root 8192 Apr 9 17:19 fmc-f004/eeprom
|
||||
An alternative way to write the EEPROM is the mezzanine driver
|
||||
fmc-write-eeprom (See *note fmc-write-eeprom::), but the procedure is
|
||||
more complex.
|
||||
|
|
|
@ -21,8 +21,6 @@ Notes and limitations.
|
|||
- The weak pullup current is a minimum of 0.9mA and maximum of 6.0mA.
|
||||
- The 5V strong pullup is supported with a minimum of 5.9mA and a
|
||||
maximum of 30.4 mA. (From DS2490.pdf)
|
||||
- While the ds2490 supports a hardware search the code doesn't take
|
||||
advantage of it (in tested case it only returned first device).
|
||||
- The hardware will detect when devices are attached to the bus on the
|
||||
next bus (reset?) operation, however only a message is printed as
|
||||
the core w1 code doesn't make use of the information. Connecting
|
||||
|
|
|
@ -5,8 +5,8 @@ Message types.
|
|||
=============
|
||||
|
||||
There are three types of messages between w1 core and userspace:
|
||||
1. Events. They are generated each time new master or slave device
|
||||
found either due to automatic or requested search.
|
||||
1. Events. They are generated each time a new master or slave device
|
||||
is found either due to automatic or requested search.
|
||||
2. Userspace commands.
|
||||
3. Replies to userspace commands.
|
||||
|
||||
|
@ -131,7 +131,7 @@ of the w1_netlink_cmd structure and cn_msg.len will be equal to the sum
|
|||
of the sizeof(struct w1_netlink_msg) and sizeof(struct w1_netlink_cmd).
|
||||
If reply is generated for master or root command (which do not have
|
||||
w1_netlink_cmd attached), reply will contain only cn_msg and w1_netlink_msg
|
||||
structires.
|
||||
structures.
|
||||
|
||||
w1_netlink_msg.status field will carry positive error value
|
||||
(EINVAL for example) or zero in case of success.
|
||||
|
@ -160,7 +160,7 @@ procedure is started to select given device.
|
|||
Then all requested in w1_netlink_msg operations are performed one by one.
|
||||
If command requires reply (like read command) it is sent on command completion.
|
||||
|
||||
When all commands (w1_netlink_cmd) are processed muster device is unlocked
|
||||
When all commands (w1_netlink_cmd) are processed master device is unlocked
|
||||
and next w1_netlink_msg header processing started.
|
||||
|
||||
|
||||
|
|
|
@ -5696,6 +5696,12 @@ L: linux-watchdog@vger.kernel.org
|
|||
S: Supported
|
||||
F: drivers/watchdog/mena21_wdt.c
|
||||
|
||||
MEN CHAMELEON BUS (mcb)
|
||||
M: Johannes Thumshirn <johannes.thumshirn@men.de>
|
||||
S: Supported
|
||||
F: drivers/mcb/
|
||||
F: include/linux/mcb.h
|
||||
|
||||
METAG ARCHITECTURE
|
||||
M: James Hogan <james.hogan@imgtec.com>
|
||||
L: linux-metag@vger.kernel.org
|
||||
|
|
|
@ -421,7 +421,7 @@
|
|||
};
|
||||
|
||||
sid: eeprom@01c23800 {
|
||||
compatible = "allwinner,sun4i-sid";
|
||||
compatible = "allwinner,sun4i-a10-sid";
|
||||
reg = <0x01c23800 0x10>;
|
||||
};
|
||||
|
||||
|
|
|
@ -378,7 +378,7 @@
|
|||
};
|
||||
|
||||
sid: eeprom@01c23800 {
|
||||
compatible = "allwinner,sun4i-sid";
|
||||
compatible = "allwinner,sun4i-a10-sid";
|
||||
reg = <0x01c23800 0x10>;
|
||||
};
|
||||
|
||||
|
|
|
@ -341,7 +341,7 @@
|
|||
};
|
||||
|
||||
sid: eeprom@01c23800 {
|
||||
compatible = "allwinner,sun4i-sid";
|
||||
compatible = "allwinner,sun4i-a10-sid";
|
||||
reg = <0x01c23800 0x10>;
|
||||
};
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
obj-y += setup.o flash.o fram.o
|
||||
obj-y += setup.o flash.o
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
/*
|
||||
* FRAM driver for MIMC200 board
|
||||
*
|
||||
* Copyright 2008 Mark Jackson <mpfj@mimc.co.uk>
|
||||
*
|
||||
* This module adds *very* simply support for the system's FRAM device.
|
||||
* At the moment, this is hard-coded to the MIMC200 platform, and only
|
||||
* supports mmap().
|
||||
*/
|
||||
|
||||
#define FRAM_VERSION "1.0"
|
||||
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#define FRAM_BASE 0xac000000
|
||||
#define FRAM_SIZE 0x20000
|
||||
|
||||
/*
|
||||
* The are the file operation function for user access to /dev/fram
|
||||
*/
|
||||
|
||||
static int fram_mmap(struct file *filp, struct vm_area_struct *vma)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = remap_pfn_range(vma,
|
||||
vma->vm_start,
|
||||
virt_to_phys((void *)((unsigned long)FRAM_BASE)) >> PAGE_SHIFT,
|
||||
vma->vm_end-vma->vm_start,
|
||||
PAGE_SHARED);
|
||||
|
||||
if (ret != 0)
|
||||
return -EAGAIN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations fram_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.mmap = fram_mmap,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
#define FRAM_MINOR 0
|
||||
|
||||
static struct miscdevice fram_dev = {
|
||||
FRAM_MINOR,
|
||||
"fram",
|
||||
&fram_fops
|
||||
};
|
||||
|
||||
static int __init
|
||||
fram_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = misc_register(&fram_dev);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "fram: can't misc_register on minor=%d\n",
|
||||
FRAM_MINOR);
|
||||
return ret;
|
||||
}
|
||||
printk(KERN_INFO "FRAM memory driver v" FRAM_VERSION "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit
|
||||
fram_cleanup_module(void)
|
||||
{
|
||||
misc_deregister(&fram_dev);
|
||||
}
|
||||
|
||||
module_init(fram_init);
|
||||
module_exit(fram_cleanup_module);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
MODULE_ALIAS_MISCDEV(FRAM_MINOR);
|
|
@ -736,10 +736,6 @@ config FSL_LBC
|
|||
controller. Also contains some common code used by
|
||||
drivers for specific local bus peripherals.
|
||||
|
||||
config FSL_IFC
|
||||
bool
|
||||
depends on FSL_SOC
|
||||
|
||||
config FSL_GTM
|
||||
bool
|
||||
depends on PPC_83xx || QUICC_ENGINE || CPM2
|
||||
|
|
|
@ -21,7 +21,6 @@ obj-$(CONFIG_FSL_SOC) += fsl_soc.o fsl_mpic_err.o
|
|||
obj-$(CONFIG_FSL_PCI) += fsl_pci.o $(fsl-msi-obj-y)
|
||||
obj-$(CONFIG_FSL_PMC) += fsl_pmc.o
|
||||
obj-$(CONFIG_FSL_LBC) += fsl_lbc.o
|
||||
obj-$(CONFIG_FSL_IFC) += fsl_ifc.o
|
||||
obj-$(CONFIG_FSL_GTM) += fsl_gtm.o
|
||||
obj-$(CONFIG_FSL_85XX_CACHE_SRAM) += fsl_85xx_l2ctlr.o fsl_85xx_cache_sram.o
|
||||
obj-$(CONFIG_SIMPLE_GPIO) += simple_gpio.o
|
||||
|
|
|
@ -52,6 +52,8 @@ source "drivers/i2c/Kconfig"
|
|||
|
||||
source "drivers/spi/Kconfig"
|
||||
|
||||
source "drivers/spmi/Kconfig"
|
||||
|
||||
source "drivers/hsi/Kconfig"
|
||||
|
||||
source "drivers/pps/Kconfig"
|
||||
|
@ -170,4 +172,6 @@ source "drivers/phy/Kconfig"
|
|||
|
||||
source "drivers/powercap/Kconfig"
|
||||
|
||||
source "drivers/mcb/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -66,6 +66,7 @@ obj-$(CONFIG_ATA) += ata/
|
|||
obj-$(CONFIG_TARGET_CORE) += target/
|
||||
obj-$(CONFIG_MTD) += mtd/
|
||||
obj-$(CONFIG_SPI) += spi/
|
||||
obj-$(CONFIG_SPMI) += spmi/
|
||||
obj-y += hsi/
|
||||
obj-y += net/
|
||||
obj-$(CONFIG_ATM) += atm/
|
||||
|
@ -155,3 +156,4 @@ obj-$(CONFIG_IPACK_BUS) += ipack/
|
|||
obj-$(CONFIG_NTB) += ntb/
|
||||
obj-$(CONFIG_FMC) += fmc/
|
||||
obj-$(CONFIG_POWERCAP) += powercap/
|
||||
obj-$(CONFIG_MCB) += mcb/
|
||||
|
|
|
@ -22,69 +22,235 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
static int regmap_spmi_read(void *context,
|
||||
const void *reg, size_t reg_size,
|
||||
void *val, size_t val_size)
|
||||
static int regmap_spmi_base_read(void *context,
|
||||
const void *reg, size_t reg_size,
|
||||
void *val, size_t val_size)
|
||||
{
|
||||
BUG_ON(reg_size != 2);
|
||||
return spmi_ext_register_readl(context, *(u16 *)reg,
|
||||
val, val_size);
|
||||
u8 addr = *(u8 *)reg;
|
||||
int err = 0;
|
||||
|
||||
BUG_ON(reg_size != 1);
|
||||
|
||||
while (val_size-- && !err)
|
||||
err = spmi_register_read(context, addr++, val++);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int regmap_spmi_gather_write(void *context,
|
||||
const void *reg, size_t reg_size,
|
||||
const void *val, size_t val_size)
|
||||
static int regmap_spmi_base_gather_write(void *context,
|
||||
const void *reg, size_t reg_size,
|
||||
const void *val, size_t val_size)
|
||||
{
|
||||
BUG_ON(reg_size != 2);
|
||||
return spmi_ext_register_writel(context, *(u16 *)reg, val, val_size);
|
||||
const u8 *data = val;
|
||||
u8 addr = *(u8 *)reg;
|
||||
int err = 0;
|
||||
|
||||
BUG_ON(reg_size != 1);
|
||||
|
||||
/*
|
||||
* SPMI defines a more bandwidth-efficient 'Register 0 Write' sequence,
|
||||
* use it when possible.
|
||||
*/
|
||||
if (addr == 0 && val_size) {
|
||||
err = spmi_register_zero_write(context, *data);
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
data++;
|
||||
addr++;
|
||||
val_size--;
|
||||
}
|
||||
|
||||
while (val_size) {
|
||||
err = spmi_register_write(context, addr, *data);
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
data++;
|
||||
addr++;
|
||||
val_size--;
|
||||
}
|
||||
|
||||
err_out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int regmap_spmi_write(void *context, const void *data,
|
||||
size_t count)
|
||||
static int regmap_spmi_base_write(void *context, const void *data,
|
||||
size_t count)
|
||||
{
|
||||
BUG_ON(count < 2);
|
||||
return regmap_spmi_gather_write(context, data, 2, data + 2, count - 2);
|
||||
BUG_ON(count < 1);
|
||||
return regmap_spmi_base_gather_write(context, data, 1, data + 1,
|
||||
count - 1);
|
||||
}
|
||||
|
||||
static struct regmap_bus regmap_spmi = {
|
||||
.read = regmap_spmi_read,
|
||||
.write = regmap_spmi_write,
|
||||
.gather_write = regmap_spmi_gather_write,
|
||||
static struct regmap_bus regmap_spmi_base = {
|
||||
.read = regmap_spmi_base_read,
|
||||
.write = regmap_spmi_base_write,
|
||||
.gather_write = regmap_spmi_base_gather_write,
|
||||
.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
|
||||
.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
|
||||
};
|
||||
|
||||
/**
|
||||
* regmap_init_spmi(): Initialize register map
|
||||
*
|
||||
* @sdev: Device that will be interacted with
|
||||
* @config: Configuration for register map
|
||||
* regmap_init_spmi_base(): Create regmap for the Base register space
|
||||
* @sdev: SPMI device that will be interacted with
|
||||
* @config: Configuration for register map
|
||||
*
|
||||
* The return value will be an ERR_PTR() on error or a valid pointer to
|
||||
* a struct regmap.
|
||||
*/
|
||||
struct regmap *regmap_init_spmi(struct spmi_device *sdev,
|
||||
const struct regmap_config *config)
|
||||
struct regmap *regmap_init_spmi_base(struct spmi_device *sdev,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
return regmap_init(&sdev->dev, ®map_spmi, sdev, config);
|
||||
return regmap_init(&sdev->dev, ®map_spmi_base, sdev, config);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_init_spmi);
|
||||
EXPORT_SYMBOL_GPL(regmap_init_spmi_base);
|
||||
|
||||
/**
|
||||
* devm_regmap_init_spmi(): Initialise managed register map
|
||||
*
|
||||
* @sdev: Device that will be interacted with
|
||||
* @config: Configuration for register map
|
||||
* devm_regmap_init_spmi_base(): Create managed regmap for Base register space
|
||||
* @sdev: SPMI device that will be interacted with
|
||||
* @config: Configuration for register map
|
||||
*
|
||||
* The return value will be an ERR_PTR() on error or a valid pointer
|
||||
* to a struct regmap. The regmap will be automatically freed by the
|
||||
* device management code.
|
||||
*/
|
||||
struct regmap *devm_regmap_init_spmi(struct spmi_device *sdev,
|
||||
struct regmap *devm_regmap_init_spmi_base(struct spmi_device *sdev,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
return devm_regmap_init(&sdev->dev, ®map_spmi_base, sdev, config);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_regmap_init_spmi_base);
|
||||
|
||||
static int regmap_spmi_ext_read(void *context,
|
||||
const void *reg, size_t reg_size,
|
||||
void *val, size_t val_size)
|
||||
{
|
||||
int err = 0;
|
||||
size_t len;
|
||||
u16 addr;
|
||||
|
||||
BUG_ON(reg_size != 2);
|
||||
|
||||
addr = *(u16 *)reg;
|
||||
|
||||
/*
|
||||
* Split accesses into two to take advantage of the more
|
||||
* bandwidth-efficient 'Extended Register Read' command when possible
|
||||
*/
|
||||
while (addr <= 0xFF && val_size) {
|
||||
len = min_t(size_t, val_size, 16);
|
||||
|
||||
err = spmi_ext_register_read(context, addr, val, len);
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
addr += len;
|
||||
val += len;
|
||||
val_size -= len;
|
||||
}
|
||||
|
||||
while (val_size) {
|
||||
len = min_t(size_t, val_size, 8);
|
||||
|
||||
err = spmi_ext_register_readl(context, addr, val, val_size);
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
addr += len;
|
||||
val += len;
|
||||
val_size -= len;
|
||||
}
|
||||
|
||||
err_out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int regmap_spmi_ext_gather_write(void *context,
|
||||
const void *reg, size_t reg_size,
|
||||
const void *val, size_t val_size)
|
||||
{
|
||||
int err = 0;
|
||||
size_t len;
|
||||
u16 addr;
|
||||
|
||||
BUG_ON(reg_size != 2);
|
||||
|
||||
addr = *(u16 *)reg;
|
||||
|
||||
while (addr <= 0xFF && val_size) {
|
||||
len = min_t(size_t, val_size, 16);
|
||||
|
||||
err = spmi_ext_register_write(context, addr, val, len);
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
addr += len;
|
||||
val += len;
|
||||
val_size -= len;
|
||||
}
|
||||
|
||||
while (val_size) {
|
||||
len = min_t(size_t, val_size, 8);
|
||||
|
||||
err = spmi_ext_register_writel(context, addr, val, len);
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
addr += len;
|
||||
val += len;
|
||||
val_size -= len;
|
||||
}
|
||||
|
||||
err_out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int regmap_spmi_ext_write(void *context, const void *data,
|
||||
size_t count)
|
||||
{
|
||||
BUG_ON(count < 2);
|
||||
return regmap_spmi_ext_gather_write(context, data, 2, data + 2,
|
||||
count - 2);
|
||||
}
|
||||
|
||||
static struct regmap_bus regmap_spmi_ext = {
|
||||
.read = regmap_spmi_ext_read,
|
||||
.write = regmap_spmi_ext_write,
|
||||
.gather_write = regmap_spmi_ext_gather_write,
|
||||
.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
|
||||
.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
|
||||
};
|
||||
|
||||
/**
|
||||
* regmap_init_spmi_ext(): Create regmap for Ext register space
|
||||
* @sdev: Device that will be interacted with
|
||||
* @config: Configuration for register map
|
||||
*
|
||||
* The return value will be an ERR_PTR() on error or a valid pointer to
|
||||
* a struct regmap.
|
||||
*/
|
||||
struct regmap *regmap_init_spmi_ext(struct spmi_device *sdev,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
return regmap_init(&sdev->dev, ®map_spmi_ext, sdev, config);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_init_spmi_ext);
|
||||
|
||||
/**
|
||||
* devm_regmap_init_spmi_ext(): Create managed regmap for Ext register space
|
||||
* @sdev: SPMI device that will be interacted with
|
||||
* @config: Configuration for register map
|
||||
*
|
||||
* The return value will be an ERR_PTR() on error or a valid pointer
|
||||
* to a struct regmap. The regmap will be automatically freed by the
|
||||
* device management code.
|
||||
*/
|
||||
struct regmap *devm_regmap_init_spmi_ext(struct spmi_device *sdev,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
return devm_regmap_init(&sdev->dev, ®map_spmi, sdev, config);
|
||||
return devm_regmap_init(&sdev->dev, ®map_spmi_ext, sdev, config);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_regmap_init_spmi);
|
||||
EXPORT_SYMBOL_GPL(devm_regmap_init_spmi_ext);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/agp_backend.h>
|
||||
#include <linux/agpgart.h>
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/pm.h>
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/agp_backend.h>
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/agp_backend.h>
|
||||
#include <asm/sn/addrs.h>
|
||||
#include <asm/sn/io.h>
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/hw_random.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
|
|
@ -37,7 +37,6 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include <linux/hw_random.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/preempt.h>
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/hw_random.h>
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/hw_random.h>
|
||||
|
|
|
@ -61,7 +61,6 @@
|
|||
#include <linux/ipmi_smi.h>
|
||||
#include <asm/io.h>
|
||||
#include "ipmi_si_sm.h"
|
||||
#include <linux/init.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/ctype.h>
|
||||
|
|
|
@ -99,6 +99,9 @@ static ssize_t read_mem(struct file *file, char __user *buf,
|
|||
ssize_t read, sz;
|
||||
char *ptr;
|
||||
|
||||
if (p != *ppos)
|
||||
return 0;
|
||||
|
||||
if (!valid_phys_addr_range(p, count))
|
||||
return -EFAULT;
|
||||
read = 0;
|
||||
|
@ -157,6 +160,9 @@ static ssize_t write_mem(struct file *file, const char __user *buf,
|
|||
unsigned long copied;
|
||||
void *ptr;
|
||||
|
||||
if (p != *ppos)
|
||||
return -EFBIG;
|
||||
|
||||
if (!valid_phys_addr_range(p, count))
|
||||
return -EFAULT;
|
||||
|
||||
|
|
|
@ -50,7 +50,6 @@
|
|||
#include <linux/unistd.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/sched.h> /* cond_resched() */
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h> /* printk() */
|
||||
#include <linux/slab.h> /* kmalloc() */
|
||||
#include <linux/fs.h> /* everything... */
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
*
|
||||
*
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/wait.h>
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
#include <linux/miscdevice.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
|
|
@ -95,7 +95,7 @@ void proc_fork_connector(struct task_struct *task)
|
|||
msg->len = sizeof(*ev);
|
||||
msg->flags = 0; /* not used */
|
||||
/* If cn_netlink_send() failed, the data is not sent */
|
||||
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
|
||||
cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
|
||||
}
|
||||
|
||||
void proc_exec_connector(struct task_struct *task)
|
||||
|
@ -122,7 +122,7 @@ void proc_exec_connector(struct task_struct *task)
|
|||
msg->ack = 0; /* not used */
|
||||
msg->len = sizeof(*ev);
|
||||
msg->flags = 0; /* not used */
|
||||
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
|
||||
cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
|
||||
}
|
||||
|
||||
void proc_id_connector(struct task_struct *task, int which_id)
|
||||
|
@ -163,7 +163,7 @@ void proc_id_connector(struct task_struct *task, int which_id)
|
|||
msg->ack = 0; /* not used */
|
||||
msg->len = sizeof(*ev);
|
||||
msg->flags = 0; /* not used */
|
||||
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
|
||||
cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
|
||||
}
|
||||
|
||||
void proc_sid_connector(struct task_struct *task)
|
||||
|
@ -190,7 +190,7 @@ void proc_sid_connector(struct task_struct *task)
|
|||
msg->ack = 0; /* not used */
|
||||
msg->len = sizeof(*ev);
|
||||
msg->flags = 0; /* not used */
|
||||
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
|
||||
cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
|
||||
}
|
||||
|
||||
void proc_ptrace_connector(struct task_struct *task, int ptrace_id)
|
||||
|
@ -225,7 +225,7 @@ void proc_ptrace_connector(struct task_struct *task, int ptrace_id)
|
|||
msg->ack = 0; /* not used */
|
||||
msg->len = sizeof(*ev);
|
||||
msg->flags = 0; /* not used */
|
||||
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
|
||||
cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
|
||||
}
|
||||
|
||||
void proc_comm_connector(struct task_struct *task)
|
||||
|
@ -253,7 +253,7 @@ void proc_comm_connector(struct task_struct *task)
|
|||
msg->ack = 0; /* not used */
|
||||
msg->len = sizeof(*ev);
|
||||
msg->flags = 0; /* not used */
|
||||
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
|
||||
cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
|
||||
}
|
||||
|
||||
void proc_coredump_connector(struct task_struct *task)
|
||||
|
@ -280,7 +280,7 @@ void proc_coredump_connector(struct task_struct *task)
|
|||
msg->ack = 0; /* not used */
|
||||
msg->len = sizeof(*ev);
|
||||
msg->flags = 0; /* not used */
|
||||
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
|
||||
cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
|
||||
}
|
||||
|
||||
void proc_exit_connector(struct task_struct *task)
|
||||
|
@ -309,7 +309,7 @@ void proc_exit_connector(struct task_struct *task)
|
|||
msg->ack = 0; /* not used */
|
||||
msg->len = sizeof(*ev);
|
||||
msg->flags = 0; /* not used */
|
||||
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
|
||||
cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -343,7 +343,7 @@ static void cn_proc_ack(int err, int rcvd_seq, int rcvd_ack)
|
|||
msg->ack = rcvd_ack + 1;
|
||||
msg->len = sizeof(*ev);
|
||||
msg->flags = 0; /* not used */
|
||||
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
|
||||
cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -50,7 +50,7 @@ static int cn_already_initialized;
|
|||
*
|
||||
* Sequence number is incremented with each message to be sent.
|
||||
*
|
||||
* If we expect reply to our message then the sequence number in
|
||||
* If we expect a reply to our message then the sequence number in
|
||||
* received message MUST be the same as in original message, and
|
||||
* acknowledge number MUST be the same + 1.
|
||||
*
|
||||
|
@ -62,8 +62,11 @@ static int cn_already_initialized;
|
|||
* the acknowledgement number in the original message + 1, then it is
|
||||
* a new message.
|
||||
*
|
||||
* The message is sent to, the portid if given, the group if given, both if
|
||||
* both, or if both are zero then the group is looked up and sent there.
|
||||
*/
|
||||
int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask)
|
||||
int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group,
|
||||
gfp_t gfp_mask)
|
||||
{
|
||||
struct cn_callback_entry *__cbq;
|
||||
unsigned int size;
|
||||
|
@ -74,7 +77,9 @@ int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask)
|
|||
u32 group = 0;
|
||||
int found = 0;
|
||||
|
||||
if (!__group) {
|
||||
if (portid || __group) {
|
||||
group = __group;
|
||||
} else {
|
||||
spin_lock_bh(&dev->cbdev->queue_lock);
|
||||
list_for_each_entry(__cbq, &dev->cbdev->queue_list,
|
||||
callback_entry) {
|
||||
|
@ -88,11 +93,9 @@ int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask)
|
|||
|
||||
if (!found)
|
||||
return -ENODEV;
|
||||
} else {
|
||||
group = __group;
|
||||
}
|
||||
|
||||
if (!netlink_has_listeners(dev->nls, group))
|
||||
if (!portid && !netlink_has_listeners(dev->nls, group))
|
||||
return -ESRCH;
|
||||
|
||||
size = sizeof(*msg) + msg->len;
|
||||
|
@ -113,7 +116,10 @@ int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask)
|
|||
|
||||
NETLINK_CB(skb).dst_group = group;
|
||||
|
||||
return netlink_broadcast(dev->nls, skb, 0, group, gfp_mask);
|
||||
if (group)
|
||||
return netlink_broadcast(dev->nls, skb, portid, group,
|
||||
gfp_mask);
|
||||
return netlink_unicast(dev->nls, skb, portid, !(gfp_mask&__GFP_WAIT));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cn_netlink_send);
|
||||
|
||||
|
|
|
@ -14,10 +14,6 @@ if EXTCON
|
|||
|
||||
comment "Extcon Device Drivers"
|
||||
|
||||
config OF_EXTCON
|
||||
def_tristate y
|
||||
depends on OF
|
||||
|
||||
config EXTCON_GPIO
|
||||
tristate "GPIO extcon support"
|
||||
depends on GPIOLIB
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
# Makefile for external connector class (extcon) devices
|
||||
#
|
||||
|
||||
obj-$(CONFIG_OF_EXTCON) += of_extcon.o
|
||||
|
||||
obj-$(CONFIG_EXTCON) += extcon-class.o
|
||||
obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o
|
||||
obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <linux/extcon.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
/*
|
||||
* extcon_cable_name suggests the standard cable names for commonly used
|
||||
|
@ -818,6 +819,47 @@ void extcon_dev_unregister(struct extcon_dev *edev)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(extcon_dev_unregister);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/*
|
||||
* extcon_get_edev_by_phandle - Get the extcon device from devicetree
|
||||
* @dev - instance to the given device
|
||||
* @index - index into list of extcon_dev
|
||||
*
|
||||
* return the instance of extcon device
|
||||
*/
|
||||
struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
|
||||
{
|
||||
struct device_node *node;
|
||||
struct extcon_dev *edev;
|
||||
|
||||
if (!dev->of_node) {
|
||||
dev_err(dev, "device does not have a device node entry\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
node = of_parse_phandle(dev->of_node, "extcon", index);
|
||||
if (!node) {
|
||||
dev_err(dev, "failed to get phandle in %s node\n",
|
||||
dev->of_node->full_name);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
edev = extcon_get_extcon_dev(node->name);
|
||||
if (!edev) {
|
||||
dev_err(dev, "unable to get extcon device : %s\n", node->name);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
return edev;
|
||||
}
|
||||
#else
|
||||
struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
|
||||
{
|
||||
return ERR_PTR(-ENOSYS);
|
||||
}
|
||||
#endif /* CONFIG_OF */
|
||||
EXPORT_SYMBOL_GPL(extcon_get_edev_by_phandle);
|
||||
|
||||
static int __init extcon_class_init(void)
|
||||
{
|
||||
return create_extcon_class();
|
||||
|
|
|
@ -176,9 +176,7 @@ static int gpio_extcon_resume(struct device *dev)
|
|||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops gpio_extcon_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(NULL, gpio_extcon_resume)
|
||||
};
|
||||
static SIMPLE_DEV_PM_OPS(gpio_extcon_pm_ops, NULL, gpio_extcon_resume);
|
||||
|
||||
static struct platform_driver gpio_extcon_driver = {
|
||||
.probe = gpio_extcon_probe,
|
||||
|
|
|
@ -271,10 +271,7 @@ static int palmas_usb_resume(struct device *dev)
|
|||
};
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops palmas_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(palmas_usb_suspend,
|
||||
palmas_usb_resume)
|
||||
};
|
||||
static SIMPLE_DEV_PM_OPS(palmas_pm_ops, palmas_usb_suspend, palmas_usb_resume);
|
||||
|
||||
static struct of_device_id of_palmas_match_tbl[] = {
|
||||
{ .compatible = "ti,palmas-usb", },
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* OF helpers for External connector (extcon) framework
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments, Inc.
|
||||
* Kishon Vijay Abraham I <kishon@ti.com>
|
||||
*
|
||||
* Copyright (C) 2013 Samsung Electronics
|
||||
* Chanwoo Choi <cw00.choi@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/extcon/of_extcon.h>
|
||||
|
||||
/*
|
||||
* of_extcon_get_extcon_dev - Get the name of extcon device from devicetree
|
||||
* @dev - instance to the given device
|
||||
* @index - index into list of extcon_dev
|
||||
*
|
||||
* return the instance of extcon device
|
||||
*/
|
||||
struct extcon_dev *of_extcon_get_extcon_dev(struct device *dev, int index)
|
||||
{
|
||||
struct device_node *node;
|
||||
struct extcon_dev *edev;
|
||||
struct platform_device *extcon_parent_dev;
|
||||
|
||||
if (!dev->of_node) {
|
||||
dev_dbg(dev, "device does not have a device node entry\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
node = of_parse_phandle(dev->of_node, "extcon", index);
|
||||
if (!node) {
|
||||
dev_dbg(dev, "failed to get phandle in %s node\n",
|
||||
dev->of_node->full_name);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
extcon_parent_dev = of_find_device_by_node(node);
|
||||
if (!extcon_parent_dev) {
|
||||
dev_dbg(dev, "unable to find device by node\n");
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
edev = extcon_get_extcon_dev(dev_name(&extcon_parent_dev->dev));
|
||||
if (!edev) {
|
||||
dev_dbg(dev, "unable to get extcon device : %s\n",
|
||||
dev_name(&extcon_parent_dev->dev));
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
return edev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_extcon_get_extcon_dev);
|
|
@ -99,10 +99,23 @@ static ssize_t fmc_read_eeprom(struct file *file, struct kobject *kobj,
|
|||
return count;
|
||||
}
|
||||
|
||||
static ssize_t fmc_write_eeprom(struct file *file, struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t off, size_t count)
|
||||
{
|
||||
struct device *dev;
|
||||
struct fmc_device *fmc;
|
||||
|
||||
dev = container_of(kobj, struct device, kobj);
|
||||
fmc = container_of(dev, struct fmc_device, dev);
|
||||
return fmc->op->write_ee(fmc, off, buf, count);
|
||||
}
|
||||
|
||||
static struct bin_attribute fmc_eeprom_attr = {
|
||||
.attr = { .name = "eeprom", .mode = S_IRUGO, },
|
||||
.attr = { .name = "eeprom", .mode = S_IRUGO | S_IWUSR, },
|
||||
.size = 8192, /* more or less standard */
|
||||
.read = fmc_read_eeprom,
|
||||
.write = fmc_write_eeprom,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -154,7 +167,7 @@ int fmc_device_register_n(struct fmc_device **devs, int n)
|
|||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
if (fmc->flags == FMC_DEVICE_NO_MEZZANINE) {
|
||||
if (fmc->flags & FMC_DEVICE_NO_MEZZANINE) {
|
||||
dev_info(fmc->hwdev, "absent mezzanine in slot %d\n",
|
||||
fmc->slot_id);
|
||||
continue;
|
||||
|
@ -189,9 +202,6 @@ int fmc_device_register_n(struct fmc_device **devs, int n)
|
|||
for (i = 0; i < n; i++) {
|
||||
fmc = devarray[i];
|
||||
|
||||
if (fmc->flags == FMC_DEVICE_NO_MEZZANINE)
|
||||
continue; /* dev_info already done above */
|
||||
|
||||
fmc->nr_slots = n; /* each slot must know how many are there */
|
||||
fmc->devarray = devarray;
|
||||
|
||||
|
@ -263,8 +273,6 @@ void fmc_device_unregister_n(struct fmc_device **devs, int n)
|
|||
kfree(devs[0]->devarray);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if (devs[i]->flags == FMC_DEVICE_NO_MEZZANINE)
|
||||
continue;
|
||||
sysfs_remove_bin_file(&devs[i]->dev.kobj, &fmc_eeprom_attr);
|
||||
device_del(&devs[i]->dev);
|
||||
fmc_free_id_info(devs[i]);
|
||||
|
|
|
@ -150,23 +150,36 @@ int fmc_reprogram(struct fmc_device *fmc, struct fmc_driver *d, char *gw,
|
|||
}
|
||||
EXPORT_SYMBOL(fmc_reprogram);
|
||||
|
||||
static char *__strip_trailing_space(char *buf, char *str, int len)
|
||||
{
|
||||
int i = len - 1;
|
||||
|
||||
memcpy(buf, str, len);
|
||||
while(i >= 0 && buf[i] == ' ')
|
||||
buf[i--] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
#define __sdb_string(buf, field) ({ \
|
||||
BUILD_BUG_ON(sizeof(buf) < sizeof(field)); \
|
||||
__strip_trailing_space(buf, (void *)(field), sizeof(field)); \
|
||||
})
|
||||
|
||||
static void __fmc_show_sdb_tree(const struct fmc_device *fmc,
|
||||
const struct sdb_array *arr)
|
||||
{
|
||||
unsigned long base = arr->baseaddr;
|
||||
int i, j, n = arr->len, level = arr->level;
|
||||
const struct sdb_array *ap;
|
||||
char buf[64];
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
unsigned long base;
|
||||
union sdb_record *r;
|
||||
struct sdb_product *p;
|
||||
struct sdb_component *c;
|
||||
r = &arr->record[i];
|
||||
c = &r->dev.sdb_component;
|
||||
p = &c->product;
|
||||
base = 0;
|
||||
for (ap = arr; ap; ap = ap->parent)
|
||||
base += ap->baseaddr;
|
||||
|
||||
dev_info(&fmc->dev, "SDB: ");
|
||||
|
||||
for (j = 0; j < level; j++)
|
||||
|
@ -193,8 +206,8 @@ static void __fmc_show_sdb_tree(const struct fmc_device *fmc,
|
|||
p->name,
|
||||
__be64_to_cpu(c->addr_first) + base);
|
||||
if (IS_ERR(arr->subtree[i])) {
|
||||
printk(KERN_CONT "(bridge error %li)\n",
|
||||
PTR_ERR(arr->subtree[i]));
|
||||
dev_info(&fmc->dev, "SDB: (bridge error %li)\n",
|
||||
PTR_ERR(arr->subtree[i]));
|
||||
break;
|
||||
}
|
||||
__fmc_show_sdb_tree(fmc, arr->subtree[i]);
|
||||
|
@ -203,10 +216,20 @@ static void __fmc_show_sdb_tree(const struct fmc_device *fmc,
|
|||
printk(KERN_CONT "integration\n");
|
||||
break;
|
||||
case sdb_type_repo_url:
|
||||
printk(KERN_CONT "repo-url\n");
|
||||
printk(KERN_CONT "Synthesis repository: %s\n",
|
||||
__sdb_string(buf, r->repo_url.repo_url));
|
||||
break;
|
||||
case sdb_type_synthesis:
|
||||
printk(KERN_CONT "synthesis-info\n");
|
||||
printk(KERN_CONT "Bitstream '%s' ",
|
||||
__sdb_string(buf, r->synthesis.syn_name));
|
||||
printk(KERN_CONT "synthesized %08x by %s ",
|
||||
__be32_to_cpu(r->synthesis.date),
|
||||
__sdb_string(buf, r->synthesis.user_name));
|
||||
printk(KERN_CONT "(%s version %x), ",
|
||||
__sdb_string(buf, r->synthesis.tool_name),
|
||||
__be32_to_cpu(r->synthesis.tool_version));
|
||||
printk(KERN_CONT "commit %pm\n",
|
||||
r->synthesis.commit_id);
|
||||
break;
|
||||
case sdb_type_empty:
|
||||
printk(KERN_CONT "empty\n");
|
||||
|
|
|
@ -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_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_fcopy.o
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/hyperv.h>
|
||||
#include <linux/uio.h>
|
||||
|
||||
#include "hyperv_vmbus.h"
|
||||
|
||||
|
@ -554,14 +555,14 @@ EXPORT_SYMBOL_GPL(vmbus_close);
|
|||
*
|
||||
* Mainly used by Hyper-V drivers.
|
||||
*/
|
||||
int vmbus_sendpacket(struct vmbus_channel *channel, const void *buffer,
|
||||
int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer,
|
||||
u32 bufferlen, u64 requestid,
|
||||
enum vmbus_packet_type type, u32 flags)
|
||||
{
|
||||
struct vmpacket_descriptor desc;
|
||||
u32 packetlen = sizeof(struct vmpacket_descriptor) + bufferlen;
|
||||
u32 packetlen_aligned = ALIGN(packetlen, sizeof(u64));
|
||||
struct scatterlist bufferlist[3];
|
||||
struct kvec bufferlist[3];
|
||||
u64 aligned_data = 0;
|
||||
int ret;
|
||||
bool signal = false;
|
||||
|
@ -575,11 +576,12 @@ int vmbus_sendpacket(struct vmbus_channel *channel, const void *buffer,
|
|||
desc.len8 = (u16)(packetlen_aligned >> 3);
|
||||
desc.trans_id = requestid;
|
||||
|
||||
sg_init_table(bufferlist, 3);
|
||||
sg_set_buf(&bufferlist[0], &desc, sizeof(struct vmpacket_descriptor));
|
||||
sg_set_buf(&bufferlist[1], buffer, bufferlen);
|
||||
sg_set_buf(&bufferlist[2], &aligned_data,
|
||||
packetlen_aligned - packetlen);
|
||||
bufferlist[0].iov_base = &desc;
|
||||
bufferlist[0].iov_len = sizeof(struct vmpacket_descriptor);
|
||||
bufferlist[1].iov_base = buffer;
|
||||
bufferlist[1].iov_len = bufferlen;
|
||||
bufferlist[2].iov_base = &aligned_data;
|
||||
bufferlist[2].iov_len = (packetlen_aligned - packetlen);
|
||||
|
||||
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal);
|
||||
|
||||
|
@ -605,7 +607,7 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
|
|||
u32 descsize;
|
||||
u32 packetlen;
|
||||
u32 packetlen_aligned;
|
||||
struct scatterlist bufferlist[3];
|
||||
struct kvec bufferlist[3];
|
||||
u64 aligned_data = 0;
|
||||
bool signal = false;
|
||||
|
||||
|
@ -637,11 +639,12 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
|
|||
desc.range[i].pfn = pagebuffers[i].pfn;
|
||||
}
|
||||
|
||||
sg_init_table(bufferlist, 3);
|
||||
sg_set_buf(&bufferlist[0], &desc, descsize);
|
||||
sg_set_buf(&bufferlist[1], buffer, bufferlen);
|
||||
sg_set_buf(&bufferlist[2], &aligned_data,
|
||||
packetlen_aligned - packetlen);
|
||||
bufferlist[0].iov_base = &desc;
|
||||
bufferlist[0].iov_len = descsize;
|
||||
bufferlist[1].iov_base = buffer;
|
||||
bufferlist[1].iov_len = bufferlen;
|
||||
bufferlist[2].iov_base = &aligned_data;
|
||||
bufferlist[2].iov_len = (packetlen_aligned - packetlen);
|
||||
|
||||
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal);
|
||||
|
||||
|
@ -665,7 +668,7 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
|
|||
u32 descsize;
|
||||
u32 packetlen;
|
||||
u32 packetlen_aligned;
|
||||
struct scatterlist bufferlist[3];
|
||||
struct kvec bufferlist[3];
|
||||
u64 aligned_data = 0;
|
||||
bool signal = false;
|
||||
u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset,
|
||||
|
@ -700,11 +703,12 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
|
|||
memcpy(desc.range.pfn_array, multi_pagebuffer->pfn_array,
|
||||
pfncount * sizeof(u64));
|
||||
|
||||
sg_init_table(bufferlist, 3);
|
||||
sg_set_buf(&bufferlist[0], &desc, descsize);
|
||||
sg_set_buf(&bufferlist[1], buffer, bufferlen);
|
||||
sg_set_buf(&bufferlist[2], &aligned_data,
|
||||
packetlen_aligned - packetlen);
|
||||
bufferlist[0].iov_base = &desc;
|
||||
bufferlist[0].iov_len = descsize;
|
||||
bufferlist[1].iov_base = buffer;
|
||||
bufferlist[1].iov_len = bufferlen;
|
||||
bufferlist[2].iov_base = &aligned_data;
|
||||
bufferlist[2].iov_len = (packetlen_aligned - packetlen);
|
||||
|
||||
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal);
|
||||
|
||||
|
|
|
@ -1171,7 +1171,8 @@ static int dm_thread_func(void *dm_dev)
|
|||
int t;
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
t = wait_for_completion_timeout(&dm_device.config_event, 1*HZ);
|
||||
t = wait_for_completion_interruptible_timeout(
|
||||
&dm_device.config_event, 1*HZ);
|
||||
/*
|
||||
* The host expects us to post information on the memory
|
||||
* pressure every second.
|
||||
|
|
|
@ -0,0 +1,414 @@
|
|||
/*
|
||||
* An implementation of file copy service.
|
||||
*
|
||||
* Copyright (C) 2014, Microsoft, Inc.
|
||||
*
|
||||
* Author : K. Y. Srinivasan <ksrinivasan@novell.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.
|
||||
*
|
||||
*/
|
||||
|
||||
#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"
|
||||
|
||||
#define WIN8_SRV_MAJOR 1
|
||||
#define WIN8_SRV_MINOR 1
|
||||
#define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
|
||||
|
||||
/*
|
||||
* Global state maintained for transaction that is being processed.
|
||||
* For a class of integration services, including the "file copy 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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 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 u8 *recv_buffer;
|
||||
|
||||
static void fcopy_work_func(struct work_struct *dummy)
|
||||
{
|
||||
/*
|
||||
* If the timer fires, the user-mode component has not responded;
|
||||
* process the pending transaction.
|
||||
*/
|
||||
fcopy_respond_to_host(HV_E_FAIL);
|
||||
}
|
||||
|
||||
static int fcopy_handle_handshake(u32 version)
|
||||
{
|
||||
switch (version) {
|
||||
case FCOPY_CURRENT_VERSION:
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* For now we will fail the registration.
|
||||
* If and when we have multiple versions to
|
||||
* deal with, we will be backward compatible.
|
||||
* We will add this code when needed.
|
||||
*/
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fcopy_send_data(void)
|
||||
{
|
||||
struct hv_start_fcopy *smsg_out = &fcopy_transaction.message;
|
||||
int operation = fcopy_transaction.fcopy_msg->operation;
|
||||
struct hv_start_fcopy *smsg_in;
|
||||
|
||||
/*
|
||||
* The strings sent from the host are encoded in
|
||||
* in utf16; convert it to utf8 strings.
|
||||
* The host assures us that the utf16 strings will not exceed
|
||||
* the max lengths specified. We will however, reserve room
|
||||
* for the string terminating character - in the utf16s_utf8s()
|
||||
* function we limit the size of the buffer where the converted
|
||||
* string is placed to W_MAX_PATH -1 to guarantee
|
||||
* that the strings can be properly terminated!
|
||||
*/
|
||||
|
||||
switch (operation) {
|
||||
case START_FILE_COPY:
|
||||
memset(smsg_out, 0, sizeof(struct hv_start_fcopy));
|
||||
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);
|
||||
|
||||
utf16s_to_utf8s((wchar_t *)smsg_in->path_name, W_MAX_PATH,
|
||||
UTF16_LITTLE_ENDIAN,
|
||||
(__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;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
up(&fcopy_transaction.read_sema);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a response back to the host.
|
||||
*/
|
||||
|
||||
static void
|
||||
fcopy_respond_to_host(int error)
|
||||
{
|
||||
struct icmsg_hdr *icmsghdr;
|
||||
u32 buf_len;
|
||||
struct vmbus_channel *channel;
|
||||
u64 req_id;
|
||||
|
||||
/*
|
||||
* Copy the global state for completing the transaction. Note that
|
||||
* only one transaction can be active at a time. This is guaranteed
|
||||
* by the file copy protocol implemented by the host. Furthermore,
|
||||
* the "transaction active" state we maintain ensures that there can
|
||||
* only be one active transaction at a time.
|
||||
*/
|
||||
|
||||
buf_len = fcopy_transaction.recv_len;
|
||||
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)];
|
||||
|
||||
if (channel->onchannel_callback == NULL)
|
||||
/*
|
||||
* We have raced with util driver being unloaded;
|
||||
* silently return.
|
||||
*/
|
||||
return;
|
||||
|
||||
icmsghdr->status = error;
|
||||
icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
|
||||
vmbus_sendpacket(channel, recv_buffer, buf_len, req_id,
|
||||
VM_PKT_DATA_INBAND, 0);
|
||||
}
|
||||
|
||||
void hv_fcopy_onchannelcallback(void *context)
|
||||
{
|
||||
struct vmbus_channel *channel = context;
|
||||
u32 recvlen;
|
||||
u64 requestid;
|
||||
struct hv_fcopy_hdr *fcopy_msg;
|
||||
struct icmsg_hdr *icmsghdr;
|
||||
struct icmsg_negotiate *negop = NULL;
|
||||
int util_fw_version;
|
||||
int fcopy_srv_version;
|
||||
|
||||
if (fcopy_transaction.active) {
|
||||
/*
|
||||
* We will defer processing this callback once
|
||||
* the current transaction is complete.
|
||||
*/
|
||||
fcopy_transaction.fcopy_context = context;
|
||||
return;
|
||||
}
|
||||
|
||||
vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen,
|
||||
&requestid);
|
||||
if (recvlen <= 0)
|
||||
return;
|
||||
|
||||
icmsghdr = (struct icmsg_hdr *)&recv_buffer[
|
||||
sizeof(struct vmbuspipe_hdr)];
|
||||
if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) {
|
||||
util_fw_version = UTIL_FW_VERSION;
|
||||
fcopy_srv_version = WIN8_SRV_VERSION;
|
||||
vmbus_prep_negotiate_resp(icmsghdr, negop, recv_buffer,
|
||||
util_fw_version, fcopy_srv_version);
|
||||
} else {
|
||||
fcopy_msg = (struct hv_fcopy_hdr *)&recv_buffer[
|
||||
sizeof(struct vmbuspipe_hdr) +
|
||||
sizeof(struct icmsg_hdr)];
|
||||
|
||||
/*
|
||||
* Stash away this global state for completing the
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* Send the information to the user-level daemon.
|
||||
*/
|
||||
fcopy_send_data();
|
||||
schedule_delayed_work(&fcopy_work, 5*HZ);
|
||||
return;
|
||||
}
|
||||
icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
|
||||
vmbus_sendpacket(channel, recv_buffer, recvlen, requestid,
|
||||
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)
|
||||
{
|
||||
void *src;
|
||||
size_t copy_size;
|
||||
int operation;
|
||||
|
||||
/*
|
||||
* 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))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&response, buf, sizeof(int)))
|
||||
return -EFAULT;
|
||||
|
||||
if (in_hand_shake) {
|
||||
if (fcopy_handle_handshake(response))
|
||||
return -EINVAL;
|
||||
return sizeof(int);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int fcopy_release(struct inode *inode, struct file *f)
|
||||
{
|
||||
/*
|
||||
* The daemon has exited; reset the state.
|
||||
*/
|
||||
in_hand_shake = true;
|
||||
opened = false;
|
||||
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)
|
||||
{
|
||||
recv_buffer = srv->recv_buffer;
|
||||
|
||||
/*
|
||||
* When this driver loads, the user level daemon that
|
||||
* processes the host requests may not yet be running.
|
||||
* Defer processing channel callbacks until the daemon
|
||||
* has registered.
|
||||
*/
|
||||
fcopy_transaction.active = true;
|
||||
sema_init(&fcopy_transaction.read_sema, 0);
|
||||
|
||||
return fcopy_dev_init();
|
||||
}
|
||||
|
||||
void hv_fcopy_deinit(void)
|
||||
{
|
||||
cancel_delayed_work_sync(&fcopy_work);
|
||||
fcopy_dev_deinit();
|
||||
}
|
|
@ -113,7 +113,7 @@ kvp_register(int reg_value)
|
|||
kvp_msg->kvp_hdr.operation = reg_value;
|
||||
strcpy(version, HV_DRV_VERSION);
|
||||
msg->len = sizeof(struct hv_kvp_msg);
|
||||
cn_netlink_send(msg, 0, GFP_ATOMIC);
|
||||
cn_netlink_send(msg, 0, 0, GFP_ATOMIC);
|
||||
kfree(msg);
|
||||
}
|
||||
}
|
||||
|
@ -435,7 +435,7 @@ kvp_send_key(struct work_struct *dummy)
|
|||
}
|
||||
|
||||
msg->len = sizeof(struct hv_kvp_msg);
|
||||
cn_netlink_send(msg, 0, GFP_ATOMIC);
|
||||
cn_netlink_send(msg, 0, 0, GFP_ATOMIC);
|
||||
kfree(msg);
|
||||
|
||||
return;
|
||||
|
|
|
@ -98,7 +98,7 @@ static void vss_send_op(struct work_struct *dummy)
|
|||
vss_msg->vss_hdr.operation = op;
|
||||
msg->len = sizeof(struct hv_vss_msg);
|
||||
|
||||
cn_netlink_send(msg, 0, GFP_ATOMIC);
|
||||
cn_netlink_send(msg, 0, 0, GFP_ATOMIC);
|
||||
kfree(msg);
|
||||
|
||||
return;
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <linux/reboot.h>
|
||||
#include <linux/hyperv.h>
|
||||
|
||||
#include "hyperv_vmbus.h"
|
||||
|
||||
#define SD_MAJOR 3
|
||||
#define SD_MINOR 0
|
||||
|
@ -82,6 +83,12 @@ static struct hv_util_service util_vss = {
|
|||
.util_deinit = hv_vss_deinit,
|
||||
};
|
||||
|
||||
static struct hv_util_service util_fcopy = {
|
||||
.util_cb = hv_fcopy_onchannelcallback,
|
||||
.util_init = hv_fcopy_init,
|
||||
.util_deinit = hv_fcopy_deinit,
|
||||
};
|
||||
|
||||
static void perform_shutdown(struct work_struct *dummy)
|
||||
{
|
||||
orderly_poweroff(true);
|
||||
|
@ -401,6 +408,10 @@ static const struct hv_vmbus_device_id id_table[] = {
|
|||
{ HV_VSS_GUID,
|
||||
.driver_data = (unsigned long)&util_vss
|
||||
},
|
||||
/* File copy GUID */
|
||||
{ HV_FCOPY_GUID,
|
||||
.driver_data = (unsigned long)&util_fcopy
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
||||
|
|
|
@ -559,8 +559,8 @@ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, void *buffer,
|
|||
void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info);
|
||||
|
||||
int hv_ringbuffer_write(struct hv_ring_buffer_info *ring_info,
|
||||
struct scatterlist *sglist,
|
||||
u32 sgcount, bool *signal);
|
||||
struct kvec *kv_list,
|
||||
u32 kv_count, bool *signal);
|
||||
|
||||
int hv_ringbuffer_peek(struct hv_ring_buffer_info *ring_info, void *buffer,
|
||||
u32 buflen);
|
||||
|
@ -669,5 +669,9 @@ int vmbus_set_event(struct vmbus_channel *channel);
|
|||
|
||||
void vmbus_on_event(unsigned long data);
|
||||
|
||||
int hv_fcopy_init(struct hv_util_service *);
|
||||
void hv_fcopy_deinit(void);
|
||||
void hv_fcopy_onchannelcallback(void *);
|
||||
|
||||
|
||||
#endif /* _HYPERV_VMBUS_H */
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/hyperv.h>
|
||||
#include <linux/uio.h>
|
||||
|
||||
#include "hyperv_vmbus.h"
|
||||
|
||||
|
@ -387,23 +388,20 @@ void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info)
|
|||
*
|
||||
*/
|
||||
int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
|
||||
struct scatterlist *sglist, u32 sgcount, bool *signal)
|
||||
struct kvec *kv_list, u32 kv_count, bool *signal)
|
||||
{
|
||||
int i = 0;
|
||||
u32 bytes_avail_towrite;
|
||||
u32 bytes_avail_toread;
|
||||
u32 totalbytes_towrite = 0;
|
||||
|
||||
struct scatterlist *sg;
|
||||
u32 next_write_location;
|
||||
u32 old_write;
|
||||
u64 prev_indices = 0;
|
||||
unsigned long flags;
|
||||
|
||||
for_each_sg(sglist, sg, sgcount, i)
|
||||
{
|
||||
totalbytes_towrite += sg->length;
|
||||
}
|
||||
for (i = 0; i < kv_count; i++)
|
||||
totalbytes_towrite += kv_list[i].iov_len;
|
||||
|
||||
totalbytes_towrite += sizeof(u64);
|
||||
|
||||
|
@ -427,12 +425,11 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
|
|||
|
||||
old_write = next_write_location;
|
||||
|
||||
for_each_sg(sglist, sg, sgcount, i)
|
||||
{
|
||||
for (i = 0; i < kv_count; i++) {
|
||||
next_write_location = hv_copyto_ringbuffer(outring_info,
|
||||
next_write_location,
|
||||
sg_virt(sg),
|
||||
sg->length);
|
||||
kv_list[i].iov_base,
|
||||
kv_list[i].iov_len);
|
||||
}
|
||||
|
||||
/* Set previous packet start */
|
||||
|
|
|
@ -43,6 +43,12 @@ static struct tasklet_struct msg_dpc;
|
|||
static struct completion probe_event;
|
||||
static int irq;
|
||||
|
||||
struct resource hyperv_mmio = {
|
||||
.name = "hyperv mmio",
|
||||
.flags = IORESOURCE_MEM,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(hyperv_mmio);
|
||||
|
||||
static int vmbus_exists(void)
|
||||
{
|
||||
if (hv_acpi_dev == NULL)
|
||||
|
@ -843,18 +849,21 @@ void vmbus_device_unregister(struct hv_device *device_obj)
|
|||
|
||||
|
||||
/*
|
||||
* VMBUS is an acpi enumerated device. Get the the IRQ information
|
||||
* from DSDT.
|
||||
* VMBUS is an acpi enumerated device. Get the the information we
|
||||
* need from DSDT.
|
||||
*/
|
||||
|
||||
static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *irq)
|
||||
static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx)
|
||||
{
|
||||
switch (res->type) {
|
||||
case ACPI_RESOURCE_TYPE_IRQ:
|
||||
irq = res->data.irq.interrupts[0];
|
||||
break;
|
||||
|
||||
if (res->type == ACPI_RESOURCE_TYPE_IRQ) {
|
||||
struct acpi_resource_irq *irqp;
|
||||
irqp = &res->data.irq;
|
||||
|
||||
*((unsigned int *)irq) = irqp->interrupts[0];
|
||||
case ACPI_RESOURCE_TYPE_ADDRESS64:
|
||||
hyperv_mmio.start = res->data.address64.minimum;
|
||||
hyperv_mmio.end = res->data.address64.maximum;
|
||||
break;
|
||||
}
|
||||
|
||||
return AE_OK;
|
||||
|
@ -863,18 +872,34 @@ static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *irq)
|
|||
static int vmbus_acpi_add(struct acpi_device *device)
|
||||
{
|
||||
acpi_status result;
|
||||
int ret_val = -ENODEV;
|
||||
|
||||
hv_acpi_dev = device;
|
||||
|
||||
result = acpi_walk_resources(device->handle, METHOD_NAME__CRS,
|
||||
vmbus_walk_resources, &irq);
|
||||
vmbus_walk_resources, NULL);
|
||||
|
||||
if (ACPI_FAILURE(result)) {
|
||||
complete(&probe_event);
|
||||
return -ENODEV;
|
||||
if (ACPI_FAILURE(result))
|
||||
goto acpi_walk_err;
|
||||
/*
|
||||
* The parent of the vmbus acpi device (Gen2 firmware) is the VMOD that
|
||||
* has the mmio ranges. Get that.
|
||||
*/
|
||||
if (device->parent) {
|
||||
result = acpi_walk_resources(device->parent->handle,
|
||||
METHOD_NAME__CRS,
|
||||
vmbus_walk_resources, NULL);
|
||||
|
||||
if (ACPI_FAILURE(result))
|
||||
goto acpi_walk_err;
|
||||
if (hyperv_mmio.start && hyperv_mmio.end)
|
||||
request_resource(&iomem_resource, &hyperv_mmio);
|
||||
}
|
||||
ret_val = 0;
|
||||
|
||||
acpi_walk_err:
|
||||
complete(&probe_event);
|
||||
return 0;
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id vmbus_acpi_device_ids[] = {
|
||||
|
|
|
@ -155,6 +155,16 @@ config MCP3422
|
|||
This driver can also be built as a module. If so, the module will be
|
||||
called mcp3422.
|
||||
|
||||
config MEN_Z188_ADC
|
||||
tristate "MEN 16z188 ADC IP Core support"
|
||||
depends on MCB
|
||||
help
|
||||
Say yes here to enable support for the MEN 16z188 ADC IP-Core on a MCB
|
||||
carrier.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called men_z188_adc.
|
||||
|
||||
config NAU7802
|
||||
tristate "Nuvoton NAU7802 ADC driver"
|
||||
depends on I2C
|
||||
|
|
|
@ -17,6 +17,7 @@ obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
|
|||
obj-$(CONFIG_MAX1363) += max1363.o
|
||||
obj-$(CONFIG_MCP320X) += mcp320x.o
|
||||
obj-$(CONFIG_MCP3422) += mcp3422.o
|
||||
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
|
||||
obj-$(CONFIG_NAU7802) += nau7802.o
|
||||
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
|
||||
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* MEN 16z188 Analog to Digial Converter
|
||||
*
|
||||
* Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de)
|
||||
* Author: Johannes Thumshirn <johannes.thumshirn@men.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; version 2 of the License.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mcb.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define Z188_ADC_MAX_CHAN 8
|
||||
#define Z188_ADC_GAIN 0x0700000
|
||||
#define Z188_MODE_VOLTAGE BIT(27)
|
||||
#define Z188_CFG_AUTO 0x1
|
||||
#define Z188_CTRL_REG 0x40
|
||||
|
||||
#define ADC_DATA(x) (((x) >> 2) & 0x7ffffc)
|
||||
#define ADC_OVR(x) ((x) & 0x1)
|
||||
|
||||
struct z188_adc {
|
||||
struct resource *mem;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
#define Z188_ADC_CHANNEL(idx) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (idx), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec z188_adc_iio_channels[] = {
|
||||
Z188_ADC_CHANNEL(0),
|
||||
Z188_ADC_CHANNEL(1),
|
||||
Z188_ADC_CHANNEL(2),
|
||||
Z188_ADC_CHANNEL(3),
|
||||
Z188_ADC_CHANNEL(4),
|
||||
Z188_ADC_CHANNEL(5),
|
||||
Z188_ADC_CHANNEL(6),
|
||||
Z188_ADC_CHANNEL(7),
|
||||
};
|
||||
|
||||
static int z188_iio_read_raw(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long info)
|
||||
{
|
||||
struct z188_adc *adc = iio_priv(iio_dev);
|
||||
int ret;
|
||||
u16 tmp;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
tmp = readw(adc->base + chan->channel * 4);
|
||||
|
||||
if (ADC_OVR(tmp)) {
|
||||
dev_info(&iio_dev->dev,
|
||||
"Oversampling error on ADC channel %d\n",
|
||||
chan->channel);
|
||||
return -EIO;
|
||||
}
|
||||
*val = ADC_DATA(tmp);
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct iio_info z188_adc_info = {
|
||||
.read_raw = &z188_iio_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static void men_z188_config_channels(void __iomem *addr)
|
||||
{
|
||||
int i;
|
||||
u32 cfg;
|
||||
u32 ctl;
|
||||
|
||||
ctl = readl(addr + Z188_CTRL_REG);
|
||||
ctl |= Z188_CFG_AUTO;
|
||||
writel(ctl, addr + Z188_CTRL_REG);
|
||||
|
||||
for (i = 0; i < Z188_ADC_MAX_CHAN; i++) {
|
||||
cfg = readl(addr + i);
|
||||
cfg &= ~Z188_ADC_GAIN;
|
||||
cfg |= Z188_MODE_VOLTAGE;
|
||||
writel(cfg, addr + i);
|
||||
}
|
||||
}
|
||||
|
||||
static int men_z188_probe(struct mcb_device *dev,
|
||||
const struct mcb_device_id *id)
|
||||
{
|
||||
struct z188_adc *adc;
|
||||
struct iio_dev *indio_dev;
|
||||
struct resource *mem;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&dev->dev, sizeof(struct z188_adc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adc = iio_priv(indio_dev);
|
||||
indio_dev->name = "z188-adc";
|
||||
indio_dev->dev.parent = &dev->dev;
|
||||
indio_dev->info = &z188_adc_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = z188_adc_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(z188_adc_iio_channels);
|
||||
|
||||
mem = mcb_request_mem(dev, "z188-adc");
|
||||
if (!mem)
|
||||
return -ENOMEM;
|
||||
|
||||
adc->base = ioremap(mem->start, resource_size(mem));
|
||||
if (adc->base == NULL)
|
||||
goto err;
|
||||
|
||||
men_z188_config_channels(adc->base);
|
||||
|
||||
adc->mem = mem;
|
||||
mcb_set_drvdata(dev, indio_dev);
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
|
||||
err:
|
||||
mcb_release_mem(mem);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static void men_z188_remove(struct mcb_device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = mcb_get_drvdata(dev);
|
||||
struct z188_adc *adc = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iounmap(adc->base);
|
||||
mcb_release_mem(adc->mem);
|
||||
}
|
||||
|
||||
static const struct mcb_device_id men_z188_ids[] = {
|
||||
{ .device = 0xbc },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(mcb, men_z188_ids);
|
||||
|
||||
static struct mcb_driver men_z188_driver = {
|
||||
.driver = {
|
||||
.name = "z188-adc",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = men_z188_probe,
|
||||
.remove = men_z188_remove,
|
||||
.id_table = men_z188_ids,
|
||||
};
|
||||
module_mcb_driver(men_z188_driver);
|
||||
|
||||
MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("IIO ADC driver for MEN 16z188 ADC Core");
|
||||
MODULE_ALIAS("mcb:16z188");
|
|
@ -0,0 +1,31 @@
|
|||
#
|
||||
# MEN Chameleon Bus (MCB) support
|
||||
#
|
||||
|
||||
menuconfig MCB
|
||||
tristate "MCB support"
|
||||
default n
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
|
||||
The MCB (MEN Chameleon Bus) is a Bus specific to MEN Mikroelektronik
|
||||
FPGA based devices. It is used to identify MCB based IP-Cores within
|
||||
an FPGA and provide the necessary framework for instantiating drivers
|
||||
for these devices.
|
||||
|
||||
If build as a module, the module is called mcb.ko
|
||||
|
||||
if MCB
|
||||
config MCB_PCI
|
||||
tristate "PCI based MCB carrier"
|
||||
default n
|
||||
depends on PCI
|
||||
help
|
||||
|
||||
This is a MCB carrier on a PCI device. Both PCI attached on-board
|
||||
FPGAs as well as CompactPCI attached MCB FPGAs are supported with
|
||||
this driver.
|
||||
|
||||
If build as a module, the module is called mcb-pci.ko
|
||||
|
||||
endif # MCB
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
obj-$(CONFIG_MCB) += mcb.o
|
||||
|
||||
mcb-y += mcb-core.o
|
||||
mcb-y += mcb-parse.o
|
||||
|
||||
obj-$(CONFIG_MCB_PCI) += mcb-pci.o
|
|
@ -0,0 +1,414 @@
|
|||
/*
|
||||
* MEN Chameleon Bus.
|
||||
*
|
||||
* Copyright (C) 2013 MEN Mikroelektronik GmbH (www.men.de)
|
||||
* Author: Johannes Thumshirn <johannes.thumshirn@men.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; version 2 of the License.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/mcb.h>
|
||||
|
||||
static DEFINE_IDA(mcb_ida);
|
||||
|
||||
static const struct mcb_device_id *mcb_match_id(const struct mcb_device_id *ids,
|
||||
struct mcb_device *dev)
|
||||
{
|
||||
if (ids) {
|
||||
while (ids->device) {
|
||||
if (ids->device == dev->id)
|
||||
return ids;
|
||||
ids++;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int mcb_match(struct device *dev, struct device_driver *drv)
|
||||
{
|
||||
struct mcb_driver *mdrv = to_mcb_driver(drv);
|
||||
struct mcb_device *mdev = to_mcb_device(dev);
|
||||
const struct mcb_device_id *found_id;
|
||||
|
||||
found_id = mcb_match_id(mdrv->id_table, mdev);
|
||||
if (found_id)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcb_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
{
|
||||
struct mcb_device *mdev = to_mcb_device(dev);
|
||||
int ret;
|
||||
|
||||
ret = add_uevent_var(env, "MODALIAS=mcb:16z%03d", mdev->id);
|
||||
if (ret)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcb_probe(struct device *dev)
|
||||
{
|
||||
struct mcb_driver *mdrv = to_mcb_driver(dev->driver);
|
||||
struct mcb_device *mdev = to_mcb_device(dev);
|
||||
const struct mcb_device_id *found_id;
|
||||
|
||||
found_id = mcb_match_id(mdrv->id_table, mdev);
|
||||
if (!found_id)
|
||||
return -ENODEV;
|
||||
|
||||
return mdrv->probe(mdev, found_id);
|
||||
}
|
||||
|
||||
static int mcb_remove(struct device *dev)
|
||||
{
|
||||
struct mcb_driver *mdrv = to_mcb_driver(dev->driver);
|
||||
struct mcb_device *mdev = to_mcb_device(dev);
|
||||
|
||||
mdrv->remove(mdev);
|
||||
|
||||
put_device(&mdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mcb_shutdown(struct device *dev)
|
||||
{
|
||||
struct mcb_device *mdev = to_mcb_device(dev);
|
||||
struct mcb_driver *mdrv = mdev->driver;
|
||||
|
||||
if (mdrv && mdrv->shutdown)
|
||||
mdrv->shutdown(mdev);
|
||||
}
|
||||
|
||||
static struct bus_type mcb_bus_type = {
|
||||
.name = "mcb",
|
||||
.match = mcb_match,
|
||||
.uevent = mcb_uevent,
|
||||
.probe = mcb_probe,
|
||||
.remove = mcb_remove,
|
||||
.shutdown = mcb_shutdown,
|
||||
};
|
||||
|
||||
/**
|
||||
* __mcb_register_driver() - Register a @mcb_driver at the system
|
||||
* @drv: The @mcb_driver
|
||||
* @owner: The @mcb_driver's module
|
||||
* @mod_name: The name of the @mcb_driver's module
|
||||
*
|
||||
* Register a @mcb_driver at the system. Perform some sanity checks, if
|
||||
* the .probe and .remove methods are provided by the driver.
|
||||
*/
|
||||
int __mcb_register_driver(struct mcb_driver *drv, struct module *owner,
|
||||
const char *mod_name)
|
||||
{
|
||||
if (!drv->probe || !drv->remove)
|
||||
return -EINVAL;
|
||||
|
||||
drv->driver.owner = owner;
|
||||
drv->driver.bus = &mcb_bus_type;
|
||||
drv->driver.mod_name = mod_name;
|
||||
|
||||
return driver_register(&drv->driver);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__mcb_register_driver);
|
||||
|
||||
/**
|
||||
* mcb_unregister_driver() - Unregister a @mcb_driver from the system
|
||||
* @drv: The @mcb_driver
|
||||
*
|
||||
* Unregister a @mcb_driver from the system.
|
||||
*/
|
||||
void mcb_unregister_driver(struct mcb_driver *drv)
|
||||
{
|
||||
driver_unregister(&drv->driver);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mcb_unregister_driver);
|
||||
|
||||
static void mcb_release_dev(struct device *dev)
|
||||
{
|
||||
struct mcb_device *mdev = to_mcb_device(dev);
|
||||
|
||||
mcb_bus_put(mdev->bus);
|
||||
kfree(mdev);
|
||||
}
|
||||
|
||||
/**
|
||||
* mcb_device_register() - Register a mcb_device
|
||||
* @bus: The @mcb_bus of the device
|
||||
* @dev: The @mcb_device
|
||||
*
|
||||
* Register a specific @mcb_device at a @mcb_bus and the system itself.
|
||||
*/
|
||||
int mcb_device_register(struct mcb_bus *bus, struct mcb_device *dev)
|
||||
{
|
||||
int ret;
|
||||
int device_id;
|
||||
|
||||
device_initialize(&dev->dev);
|
||||
dev->dev.bus = &mcb_bus_type;
|
||||
dev->dev.parent = bus->dev.parent;
|
||||
dev->dev.release = mcb_release_dev;
|
||||
|
||||
device_id = dev->id;
|
||||
dev_set_name(&dev->dev, "mcb%d-16z%03d-%d:%d:%d",
|
||||
bus->bus_nr, device_id, dev->inst, dev->group, dev->var);
|
||||
|
||||
ret = device_add(&dev->dev);
|
||||
if (ret < 0) {
|
||||
pr_err("Failed registering device 16z%03d on bus mcb%d (%d)\n",
|
||||
device_id, bus->bus_nr, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mcb_device_register);
|
||||
|
||||
/**
|
||||
* mcb_alloc_bus() - Allocate a new @mcb_bus
|
||||
*
|
||||
* Allocate a new @mcb_bus.
|
||||
*/
|
||||
struct mcb_bus *mcb_alloc_bus(void)
|
||||
{
|
||||
struct mcb_bus *bus;
|
||||
int bus_nr;
|
||||
|
||||
bus = kzalloc(sizeof(struct mcb_bus), GFP_KERNEL);
|
||||
if (!bus)
|
||||
return NULL;
|
||||
|
||||
bus_nr = ida_simple_get(&mcb_ida, 0, 0, GFP_KERNEL);
|
||||
if (bus_nr < 0) {
|
||||
kfree(bus);
|
||||
return ERR_PTR(bus_nr);
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&bus->children);
|
||||
bus->bus_nr = bus_nr;
|
||||
|
||||
return bus;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mcb_alloc_bus);
|
||||
|
||||
static int __mcb_devices_unregister(struct device *dev, void *data)
|
||||
{
|
||||
device_unregister(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mcb_devices_unregister(struct mcb_bus *bus)
|
||||
{
|
||||
bus_for_each_dev(&mcb_bus_type, NULL, NULL, __mcb_devices_unregister);
|
||||
}
|
||||
/**
|
||||
* mcb_release_bus() - Free a @mcb_bus
|
||||
* @bus: The @mcb_bus to release
|
||||
*
|
||||
* Release an allocated @mcb_bus from the system.
|
||||
*/
|
||||
void mcb_release_bus(struct mcb_bus *bus)
|
||||
{
|
||||
mcb_devices_unregister(bus);
|
||||
|
||||
ida_simple_remove(&mcb_ida, bus->bus_nr);
|
||||
|
||||
kfree(bus);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mcb_release_bus);
|
||||
|
||||
/**
|
||||
* mcb_bus_put() - Increment refcnt
|
||||
* @bus: The @mcb_bus
|
||||
*
|
||||
* Get a @mcb_bus' ref
|
||||
*/
|
||||
struct mcb_bus *mcb_bus_get(struct mcb_bus *bus)
|
||||
{
|
||||
if (bus)
|
||||
get_device(&bus->dev);
|
||||
|
||||
return bus;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mcb_bus_get);
|
||||
|
||||
/**
|
||||
* mcb_bus_put() - Decrement refcnt
|
||||
* @bus: The @mcb_bus
|
||||
*
|
||||
* Release a @mcb_bus' ref
|
||||
*/
|
||||
void mcb_bus_put(struct mcb_bus *bus)
|
||||
{
|
||||
if (bus)
|
||||
put_device(&bus->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mcb_bus_put);
|
||||
|
||||
/**
|
||||
* mcb_alloc_dev() - Allocate a device
|
||||
* @bus: The @mcb_bus the device is part of
|
||||
*
|
||||
* Allocate a @mcb_device and add bus.
|
||||
*/
|
||||
struct mcb_device *mcb_alloc_dev(struct mcb_bus *bus)
|
||||
{
|
||||
struct mcb_device *dev;
|
||||
|
||||
dev = kzalloc(sizeof(struct mcb_device), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&dev->bus_list);
|
||||
dev->bus = bus;
|
||||
|
||||
return dev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mcb_alloc_dev);
|
||||
|
||||
/**
|
||||
* mcb_free_dev() - Free @mcb_device
|
||||
* @dev: The device to free
|
||||
*
|
||||
* Free a @mcb_device
|
||||
*/
|
||||
void mcb_free_dev(struct mcb_device *dev)
|
||||
{
|
||||
kfree(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mcb_free_dev);
|
||||
|
||||
static int __mcb_bus_add_devices(struct device *dev, void *data)
|
||||
{
|
||||
struct mcb_device *mdev = to_mcb_device(dev);
|
||||
int retval;
|
||||
|
||||
if (mdev->is_added)
|
||||
return 0;
|
||||
|
||||
retval = device_attach(dev);
|
||||
if (retval < 0)
|
||||
dev_err(dev, "Error adding device (%d)\n", retval);
|
||||
|
||||
mdev->is_added = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __mcb_bus_add_child(struct device *dev, void *data)
|
||||
{
|
||||
struct mcb_device *mdev = to_mcb_device(dev);
|
||||
struct mcb_bus *child;
|
||||
|
||||
BUG_ON(!mdev->is_added);
|
||||
child = mdev->subordinate;
|
||||
|
||||
if (child)
|
||||
mcb_bus_add_devices(child);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mcb_bus_add_devices() - Add devices in the bus' internal device list
|
||||
* @bus: The @mcb_bus we add the devices
|
||||
*
|
||||
* Add devices in the bus' internal device list to the system.
|
||||
*/
|
||||
void mcb_bus_add_devices(const struct mcb_bus *bus)
|
||||
{
|
||||
bus_for_each_dev(&mcb_bus_type, NULL, NULL, __mcb_bus_add_devices);
|
||||
bus_for_each_dev(&mcb_bus_type, NULL, NULL, __mcb_bus_add_child);
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mcb_bus_add_devices);
|
||||
|
||||
/**
|
||||
* mcb_request_mem() - Request memory
|
||||
* @dev: The @mcb_device the memory is for
|
||||
* @name: The name for the memory reference.
|
||||
*
|
||||
* Request memory for a @mcb_device. If @name is NULL the driver name will
|
||||
* be used.
|
||||
*/
|
||||
struct resource *mcb_request_mem(struct mcb_device *dev, const char *name)
|
||||
{
|
||||
struct resource *mem;
|
||||
u32 size;
|
||||
|
||||
if (!name)
|
||||
name = dev->dev.driver->name;
|
||||
|
||||
size = resource_size(&dev->mem);
|
||||
|
||||
mem = request_mem_region(dev->mem.start, size, name);
|
||||
if (!mem)
|
||||
return ERR_PTR(-EBUSY);
|
||||
|
||||
return mem;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mcb_request_mem);
|
||||
|
||||
/**
|
||||
* mcb_release_mem() - Release memory requested by device
|
||||
* @dev: The @mcb_device that requested the memory
|
||||
*
|
||||
* Release memory that was prior requested via @mcb_request_mem().
|
||||
*/
|
||||
void mcb_release_mem(struct resource *mem)
|
||||
{
|
||||
u32 size;
|
||||
|
||||
size = resource_size(mem);
|
||||
release_mem_region(mem->start, size);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mcb_release_mem);
|
||||
|
||||
/**
|
||||
* mcb_get_irq() - Get device's IRQ number
|
||||
* @dev: The @mcb_device the IRQ is for
|
||||
*
|
||||
* Get the IRQ number of a given @mcb_device.
|
||||
*/
|
||||
int mcb_get_irq(struct mcb_device *dev)
|
||||
{
|
||||
struct resource *irq = &dev->irq;
|
||||
|
||||
return irq->start;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mcb_get_irq);
|
||||
|
||||
static int mcb_init(void)
|
||||
{
|
||||
return bus_register(&mcb_bus_type);
|
||||
}
|
||||
|
||||
static void mcb_exit(void)
|
||||
{
|
||||
bus_unregister(&mcb_bus_type);
|
||||
}
|
||||
|
||||
/* mcb must be initialized after PCI but before the chameleon drivers.
|
||||
* That means we must use some initcall between subsys_initcall and
|
||||
* device_initcall.
|
||||
*/
|
||||
fs_initcall(mcb_init);
|
||||
module_exit(mcb_exit);
|
||||
|
||||
MODULE_DESCRIPTION("MEN Chameleon Bus Driver");
|
||||
MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,118 @@
|
|||
#ifndef __MCB_INTERNAL
|
||||
#define __MCB_INTERNAL
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define PCI_VENDOR_ID_MEN 0x1a88
|
||||
#define PCI_DEVICE_ID_MEN_CHAMELEON 0x4d45
|
||||
#define CHAMELEON_FILENAME_LEN 12
|
||||
#define CHAMELEONV2_MAGIC 0xabce
|
||||
|
||||
enum chameleon_descriptor_type {
|
||||
CHAMELEON_DTYPE_GENERAL = 0x0,
|
||||
CHAMELEON_DTYPE_BRIDGE = 0x1,
|
||||
CHAMELEON_DTYPE_CPU = 0x2,
|
||||
CHAMELEON_DTYPE_BAR = 0x3,
|
||||
CHAMELEON_DTYPE_END = 0xf,
|
||||
};
|
||||
|
||||
enum chameleon_bus_type {
|
||||
CHAMELEON_BUS_WISHBONE,
|
||||
CHAMELEON_BUS_AVALON,
|
||||
CHAMELEON_BUS_LPC,
|
||||
CHAMELEON_BUS_ISA,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct chameleon_fpga_header
|
||||
*
|
||||
* @revision: Revison of Chameleon table in FPGA
|
||||
* @model: Chameleon table model ASCII char
|
||||
* @minor: Revision minor
|
||||
* @bus_type: Bus type (usually %CHAMELEON_BUS_WISHBONE)
|
||||
* @magic: Chameleon header magic number (0xabce for version 2)
|
||||
* @reserved: Reserved
|
||||
* @filename: Filename of FPGA bitstream
|
||||
*/
|
||||
struct chameleon_fpga_header {
|
||||
u8 revision;
|
||||
char model;
|
||||
u8 minor;
|
||||
u8 bus_type;
|
||||
u16 magic;
|
||||
u16 reserved;
|
||||
/* This one has no '\0' at the end!!! */
|
||||
char filename[CHAMELEON_FILENAME_LEN];
|
||||
} __packed;
|
||||
#define HEADER_MAGIC_OFFSET 0x4
|
||||
|
||||
/**
|
||||
* struct chameleon_gdd - Chameleon General Device Descriptor
|
||||
*
|
||||
* @irq: the position in the FPGA's IRQ controller vector
|
||||
* @rev: the revision of the variant's implementation
|
||||
* @var: the variant of the IP core
|
||||
* @dev: the device the IP core is
|
||||
* @dtype: device descriptor type
|
||||
* @bar: BAR offset that must be added to module offset
|
||||
* @inst: the instance number of the device, 0 is first instance
|
||||
* @group: the group the device belongs to (0 = no group)
|
||||
* @reserved: reserved
|
||||
* @offset: beginning of the address window of desired module
|
||||
* @size: size of the module's address window
|
||||
*/
|
||||
struct chameleon_gdd {
|
||||
__le32 reg1;
|
||||
__le32 reg2;
|
||||
__le32 offset;
|
||||
__le32 size;
|
||||
|
||||
} __packed;
|
||||
|
||||
/* GDD Register 1 fields */
|
||||
#define GDD_IRQ(x) ((x) & 0x1f)
|
||||
#define GDD_REV(x) (((x) >> 5) & 0x3f)
|
||||
#define GDD_VAR(x) (((x) >> 11) & 0x3f)
|
||||
#define GDD_DEV(x) (((x) >> 18) & 0x3ff)
|
||||
#define GDD_DTY(x) (((x) >> 28) & 0xf)
|
||||
|
||||
/* GDD Register 2 fields */
|
||||
#define GDD_BAR(x) ((x) & 0x7)
|
||||
#define GDD_INS(x) (((x) >> 3) & 0x3f)
|
||||
#define GDD_GRP(x) (((x) >> 9) & 0x3f)
|
||||
|
||||
/**
|
||||
* struct chameleon_bdd - Chameleon Bridge Device Descriptor
|
||||
*
|
||||
* @irq: the position in the FPGA's IRQ controller vector
|
||||
* @rev: the revision of the variant's implementation
|
||||
* @var: the variant of the IP core
|
||||
* @dev: the device the IP core is
|
||||
* @dtype: device descriptor type
|
||||
* @bar: BAR offset that must be added to module offset
|
||||
* @inst: the instance number of the device, 0 is first instance
|
||||
* @dbar: destination bar from the bus _behind_ the bridge
|
||||
* @chamoff: offset within the BAR of the source bus
|
||||
* @offset:
|
||||
* @size:
|
||||
*/
|
||||
struct chameleon_bdd {
|
||||
unsigned int irq:6;
|
||||
unsigned int rev:6;
|
||||
unsigned int var:6;
|
||||
unsigned int dev:10;
|
||||
unsigned int dtype:4;
|
||||
unsigned int bar:3;
|
||||
unsigned int inst:6;
|
||||
unsigned int dbar:3;
|
||||
unsigned int group:6;
|
||||
unsigned int reserved:14;
|
||||
u32 chamoff;
|
||||
u32 offset;
|
||||
u32 size;
|
||||
} __packed;
|
||||
|
||||
int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase,
|
||||
void __iomem *base);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,159 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mcb.h>
|
||||
|
||||
#include "mcb-internal.h"
|
||||
|
||||
struct mcb_parse_priv {
|
||||
phys_addr_t mapbase;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
#define for_each_chameleon_cell(dtype, p) \
|
||||
for ((dtype) = get_next_dtype((p)); \
|
||||
(dtype) != CHAMELEON_DTYPE_END; \
|
||||
(dtype) = get_next_dtype((p)))
|
||||
|
||||
static inline uint32_t get_next_dtype(void __iomem *p)
|
||||
{
|
||||
uint32_t dtype;
|
||||
|
||||
dtype = readl(p);
|
||||
return dtype >> 28;
|
||||
}
|
||||
|
||||
static int chameleon_parse_bdd(struct mcb_bus *bus,
|
||||
phys_addr_t mapbase,
|
||||
void __iomem *base)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int chameleon_parse_gdd(struct mcb_bus *bus,
|
||||
phys_addr_t mapbase,
|
||||
void __iomem *base)
|
||||
{
|
||||
struct chameleon_gdd __iomem *gdd =
|
||||
(struct chameleon_gdd __iomem *) base;
|
||||
struct mcb_device *mdev;
|
||||
u32 offset;
|
||||
u32 size;
|
||||
int ret;
|
||||
__le32 reg1;
|
||||
__le32 reg2;
|
||||
|
||||
mdev = mcb_alloc_dev(bus);
|
||||
if (!mdev)
|
||||
return -ENOMEM;
|
||||
|
||||
reg1 = readl(&gdd->reg1);
|
||||
reg2 = readl(&gdd->reg2);
|
||||
offset = readl(&gdd->offset);
|
||||
size = readl(&gdd->size);
|
||||
|
||||
mdev->id = GDD_DEV(reg1);
|
||||
mdev->rev = GDD_REV(reg1);
|
||||
mdev->var = GDD_VAR(reg1);
|
||||
mdev->bar = GDD_BAR(reg1);
|
||||
mdev->group = GDD_GRP(reg2);
|
||||
mdev->inst = GDD_INS(reg2);
|
||||
|
||||
pr_debug("Found a 16z%03d\n", mdev->id);
|
||||
|
||||
mdev->irq.start = GDD_IRQ(reg1);
|
||||
mdev->irq.end = GDD_IRQ(reg1);
|
||||
mdev->irq.flags = IORESOURCE_IRQ;
|
||||
|
||||
mdev->mem.start = mapbase + offset;
|
||||
mdev->mem.end = mdev->mem.start + size - 1;
|
||||
mdev->mem.flags = IORESOURCE_MEM;
|
||||
|
||||
mdev->is_added = false;
|
||||
|
||||
ret = mcb_device_register(bus, mdev);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
mcb_free_dev(mdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase,
|
||||
void __iomem *base)
|
||||
{
|
||||
char __iomem *p = base;
|
||||
struct chameleon_fpga_header *header;
|
||||
uint32_t dtype;
|
||||
int num_cells = 0;
|
||||
int ret = 0;
|
||||
u32 hsize;
|
||||
|
||||
hsize = sizeof(struct chameleon_fpga_header);
|
||||
|
||||
header = kzalloc(hsize, GFP_KERNEL);
|
||||
if (!header)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Extract header information */
|
||||
memcpy_fromio(header, p, hsize);
|
||||
/* We only support chameleon v2 at the moment */
|
||||
header->magic = le16_to_cpu(header->magic);
|
||||
if (header->magic != CHAMELEONV2_MAGIC) {
|
||||
pr_err("Unsupported chameleon version 0x%x\n",
|
||||
header->magic);
|
||||
kfree(header);
|
||||
return -ENODEV;
|
||||
}
|
||||
p += hsize;
|
||||
|
||||
pr_debug("header->revision = %d\n", header->revision);
|
||||
pr_debug("header->model = 0x%x ('%c')\n", header->model,
|
||||
header->model);
|
||||
pr_debug("header->minor = %d\n", header->minor);
|
||||
pr_debug("header->bus_type = 0x%x\n", header->bus_type);
|
||||
|
||||
|
||||
pr_debug("header->magic = 0x%x\n", header->magic);
|
||||
pr_debug("header->filename = \"%.*s\"\n", CHAMELEON_FILENAME_LEN,
|
||||
header->filename);
|
||||
|
||||
for_each_chameleon_cell(dtype, p) {
|
||||
switch (dtype) {
|
||||
case CHAMELEON_DTYPE_GENERAL:
|
||||
ret = chameleon_parse_gdd(bus, mapbase, p);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
p += sizeof(struct chameleon_gdd);
|
||||
break;
|
||||
case CHAMELEON_DTYPE_BRIDGE:
|
||||
chameleon_parse_bdd(bus, mapbase, p);
|
||||
p += sizeof(struct chameleon_bdd);
|
||||
break;
|
||||
case CHAMELEON_DTYPE_END:
|
||||
break;
|
||||
default:
|
||||
pr_err("Invalid chameleon descriptor type 0x%x\n",
|
||||
dtype);
|
||||
return -EINVAL;
|
||||
}
|
||||
num_cells++;
|
||||
}
|
||||
|
||||
if (num_cells == 0)
|
||||
num_cells = -EINVAL;
|
||||
|
||||
kfree(header);
|
||||
return num_cells;
|
||||
|
||||
out:
|
||||
kfree(header);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(chameleon_parse_cells);
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* MEN Chameleon Bus.
|
||||
*
|
||||
* Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de)
|
||||
* Author: Johannes Thumshirn <johannes.thumshirn@men.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; version 2 of the License.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/mcb.h>
|
||||
|
||||
#include "mcb-internal.h"
|
||||
|
||||
struct priv {
|
||||
struct mcb_bus *bus;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
struct priv *priv;
|
||||
phys_addr_t mapbase;
|
||||
int ret;
|
||||
int num_cells;
|
||||
unsigned long flags;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(struct priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = pci_enable_device(pdev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to enable PCI device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
mapbase = pci_resource_start(pdev, 0);
|
||||
if (!mapbase) {
|
||||
dev_err(&pdev->dev, "No PCI resource\n");
|
||||
goto err_start;
|
||||
}
|
||||
|
||||
ret = pci_request_region(pdev, 0, KBUILD_MODNAME);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to request PCI BARs\n");
|
||||
goto err_start;
|
||||
}
|
||||
|
||||
priv->base = pci_iomap(pdev, 0, 0);
|
||||
if (!priv->base) {
|
||||
dev_err(&pdev->dev, "Cannot ioremap\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_ioremap;
|
||||
}
|
||||
|
||||
flags = pci_resource_flags(pdev, 0);
|
||||
if (flags & IORESOURCE_IO) {
|
||||
ret = -ENOTSUPP;
|
||||
dev_err(&pdev->dev,
|
||||
"IO mapped PCI devices are not supported\n");
|
||||
goto err_ioremap;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, priv);
|
||||
|
||||
priv->bus = mcb_alloc_bus();
|
||||
|
||||
ret = chameleon_parse_cells(priv->bus, mapbase, priv->base);
|
||||
if (ret < 0)
|
||||
goto err_drvdata;
|
||||
num_cells = ret;
|
||||
|
||||
dev_dbg(&pdev->dev, "Found %d cells\n", num_cells);
|
||||
|
||||
mcb_bus_add_devices(priv->bus);
|
||||
|
||||
err_drvdata:
|
||||
pci_iounmap(pdev, priv->base);
|
||||
err_ioremap:
|
||||
pci_release_region(pdev, 0);
|
||||
err_start:
|
||||
pci_disable_device(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mcb_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct priv *priv = pci_get_drvdata(pdev);
|
||||
|
||||
mcb_release_bus(priv->bus);
|
||||
}
|
||||
|
||||
static const struct pci_device_id mcb_pci_tbl[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_MEN, PCI_DEVICE_ID_MEN_CHAMELEON) },
|
||||
{ 0 },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, mcb_pci_tbl);
|
||||
|
||||
static struct pci_driver mcb_pci_driver = {
|
||||
.name = "mcb-pci",
|
||||
.id_table = mcb_pci_tbl,
|
||||
.probe = mcb_pci_probe,
|
||||
.remove = mcb_pci_remove,
|
||||
};
|
||||
|
||||
module_pci_driver(mcb_pci_driver);
|
||||
|
||||
MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("MCB over PCI support");
|
|
@ -66,7 +66,7 @@ static int dm_ulog_sendto_server(struct dm_ulog_request *tfr)
|
|||
msg->seq = tfr->seq;
|
||||
msg->len = sizeof(struct dm_ulog_request) + tfr->data_size;
|
||||
|
||||
r = cn_netlink_send(msg, 0, gfp_any());
|
||||
r = cn_netlink_send(msg, 0, 0, gfp_any());
|
||||
|
||||
return r;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,17 @@ menuconfig MEMORY
|
|||
|
||||
if MEMORY
|
||||
|
||||
config TI_AEMIF
|
||||
tristate "Texas Instruments AEMIF driver"
|
||||
depends on (ARCH_DAVINCI || ARCH_KEYSTONE) && OF
|
||||
help
|
||||
This driver is for the AEMIF module available in Texas Instruments
|
||||
SoCs. AEMIF stands for Asynchronous External Memory Interface and
|
||||
is intended to provide a glue-less interface to a variety of
|
||||
asynchronuous memory devices like ASRAM, NOR and NAND memory. A total
|
||||
of 256M bytes of any of these memories can be accessed at a given
|
||||
time via four chip selects with 64M byte access per chip select.
|
||||
|
||||
config TI_EMIF
|
||||
tristate "Texas Instruments EMIF driver"
|
||||
depends on ARCH_OMAP2PLUS
|
||||
|
@ -50,4 +61,8 @@ config TEGRA30_MC
|
|||
analysis, especially for IOMMU/SMMU(System Memory Management
|
||||
Unit) module.
|
||||
|
||||
config FSL_IFC
|
||||
bool
|
||||
depends on FSL_SOC
|
||||
|
||||
endif
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
ifeq ($(CONFIG_DDR),y)
|
||||
obj-$(CONFIG_OF) += of_memory.o
|
||||
endif
|
||||
obj-$(CONFIG_TI_AEMIF) += ti-aemif.o
|
||||
obj-$(CONFIG_TI_EMIF) += emif.o
|
||||
obj-$(CONFIG_FSL_IFC) += fsl_ifc.o
|
||||
obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o
|
||||
obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o
|
||||
obj-$(CONFIG_TEGRA30_MC) += tegra30-mc.o
|
||||
|
|
|
@ -29,8 +29,8 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/fsl_ifc.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/fsl_ifc.h>
|
||||
|
||||
struct fsl_ifc_ctrl *fsl_ifc_ctrl_dev;
|
||||
EXPORT_SYMBOL(fsl_ifc_ctrl_dev);
|
||||
|
@ -298,7 +298,11 @@ static struct platform_driver fsl_ifc_ctrl_driver = {
|
|||
.remove = fsl_ifc_ctrl_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(fsl_ifc_ctrl_driver);
|
||||
static int __init fsl_ifc_init(void)
|
||||
{
|
||||
return platform_driver_register(&fsl_ifc_ctrl_driver);
|
||||
}
|
||||
subsys_initcall(fsl_ifc_init);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Freescale Semiconductor");
|
|
@ -0,0 +1,427 @@
|
|||
/*
|
||||
* TI AEMIF driver
|
||||
*
|
||||
* Copyright (C) 2010 - 2013 Texas Instruments Incorporated. http://www.ti.com/
|
||||
*
|
||||
* Authors:
|
||||
* Murali Karicheri <m-karicheri2@ti.com>
|
||||
* Ivan Khoronzhuk <ivan.khoronzhuk@ti.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.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define TA_SHIFT 2
|
||||
#define RHOLD_SHIFT 4
|
||||
#define RSTROBE_SHIFT 7
|
||||
#define RSETUP_SHIFT 13
|
||||
#define WHOLD_SHIFT 17
|
||||
#define WSTROBE_SHIFT 20
|
||||
#define WSETUP_SHIFT 26
|
||||
#define EW_SHIFT 30
|
||||
#define SS_SHIFT 31
|
||||
|
||||
#define TA(x) ((x) << TA_SHIFT)
|
||||
#define RHOLD(x) ((x) << RHOLD_SHIFT)
|
||||
#define RSTROBE(x) ((x) << RSTROBE_SHIFT)
|
||||
#define RSETUP(x) ((x) << RSETUP_SHIFT)
|
||||
#define WHOLD(x) ((x) << WHOLD_SHIFT)
|
||||
#define WSTROBE(x) ((x) << WSTROBE_SHIFT)
|
||||
#define WSETUP(x) ((x) << WSETUP_SHIFT)
|
||||
#define EW(x) ((x) << EW_SHIFT)
|
||||
#define SS(x) ((x) << SS_SHIFT)
|
||||
|
||||
#define ASIZE_MAX 0x1
|
||||
#define TA_MAX 0x3
|
||||
#define RHOLD_MAX 0x7
|
||||
#define RSTROBE_MAX 0x3f
|
||||
#define RSETUP_MAX 0xf
|
||||
#define WHOLD_MAX 0x7
|
||||
#define WSTROBE_MAX 0x3f
|
||||
#define WSETUP_MAX 0xf
|
||||
#define EW_MAX 0x1
|
||||
#define SS_MAX 0x1
|
||||
#define NUM_CS 4
|
||||
|
||||
#define TA_VAL(x) (((x) & TA(TA_MAX)) >> TA_SHIFT)
|
||||
#define RHOLD_VAL(x) (((x) & RHOLD(RHOLD_MAX)) >> RHOLD_SHIFT)
|
||||
#define RSTROBE_VAL(x) (((x) & RSTROBE(RSTROBE_MAX)) >> RSTROBE_SHIFT)
|
||||
#define RSETUP_VAL(x) (((x) & RSETUP(RSETUP_MAX)) >> RSETUP_SHIFT)
|
||||
#define WHOLD_VAL(x) (((x) & WHOLD(WHOLD_MAX)) >> WHOLD_SHIFT)
|
||||
#define WSTROBE_VAL(x) (((x) & WSTROBE(WSTROBE_MAX)) >> WSTROBE_SHIFT)
|
||||
#define WSETUP_VAL(x) (((x) & WSETUP(WSETUP_MAX)) >> WSETUP_SHIFT)
|
||||
#define EW_VAL(x) (((x) & EW(EW_MAX)) >> EW_SHIFT)
|
||||
#define SS_VAL(x) (((x) & SS(SS_MAX)) >> SS_SHIFT)
|
||||
|
||||
#define NRCSR_OFFSET 0x00
|
||||
#define AWCCR_OFFSET 0x04
|
||||
#define A1CR_OFFSET 0x10
|
||||
|
||||
#define ACR_ASIZE_MASK 0x3
|
||||
#define ACR_EW_MASK BIT(30)
|
||||
#define ACR_SS_MASK BIT(31)
|
||||
#define ASIZE_16BIT 1
|
||||
|
||||
#define CONFIG_MASK (TA(TA_MAX) | \
|
||||
RHOLD(RHOLD_MAX) | \
|
||||
RSTROBE(RSTROBE_MAX) | \
|
||||
RSETUP(RSETUP_MAX) | \
|
||||
WHOLD(WHOLD_MAX) | \
|
||||
WSTROBE(WSTROBE_MAX) | \
|
||||
WSETUP(WSETUP_MAX) | \
|
||||
EW(EW_MAX) | SS(SS_MAX) | \
|
||||
ASIZE_MAX)
|
||||
|
||||
/**
|
||||
* struct aemif_cs_data: structure to hold cs parameters
|
||||
* @cs: chip-select number
|
||||
* @wstrobe: write strobe width, ns
|
||||
* @rstrobe: read strobe width, ns
|
||||
* @wsetup: write setup width, ns
|
||||
* @whold: write hold width, ns
|
||||
* @rsetup: read setup width, ns
|
||||
* @rhold: read hold width, ns
|
||||
* @ta: minimum turn around time, ns
|
||||
* @enable_ss: enable/disable select strobe mode
|
||||
* @enable_ew: enable/disable extended wait mode
|
||||
* @asize: width of the asynchronous device's data bus
|
||||
*/
|
||||
struct aemif_cs_data {
|
||||
u8 cs;
|
||||
u16 wstrobe;
|
||||
u16 rstrobe;
|
||||
u8 wsetup;
|
||||
u8 whold;
|
||||
u8 rsetup;
|
||||
u8 rhold;
|
||||
u8 ta;
|
||||
u8 enable_ss;
|
||||
u8 enable_ew;
|
||||
u8 asize;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct aemif_device: structure to hold device data
|
||||
* @base: base address of AEMIF registers
|
||||
* @clk: source clock
|
||||
* @clk_rate: clock's rate in kHz
|
||||
* @num_cs: number of assigned chip-selects
|
||||
* @cs_offset: start number of cs nodes
|
||||
* @cs_data: array of chip-select settings
|
||||
*/
|
||||
struct aemif_device {
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
unsigned long clk_rate;
|
||||
u8 num_cs;
|
||||
int cs_offset;
|
||||
struct aemif_cs_data cs_data[NUM_CS];
|
||||
};
|
||||
|
||||
/**
|
||||
* aemif_calc_rate - calculate timing data.
|
||||
* @pdev: platform device to calculate for
|
||||
* @wanted: The cycle time needed in nanoseconds.
|
||||
* @clk: The input clock rate in kHz.
|
||||
* @max: The maximum divider value that can be programmed.
|
||||
*
|
||||
* On success, returns the calculated timing value minus 1 for easy
|
||||
* programming into AEMIF timing registers, else negative errno.
|
||||
*/
|
||||
static int aemif_calc_rate(struct platform_device *pdev, int wanted,
|
||||
unsigned long clk, int max)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = DIV_ROUND_UP((wanted * clk), NSEC_PER_MSEC) - 1;
|
||||
|
||||
dev_dbg(&pdev->dev, "%s: result %d from %ld, %d\n", __func__, result,
|
||||
clk, wanted);
|
||||
|
||||
/* It is generally OK to have a more relaxed timing than requested... */
|
||||
if (result < 0)
|
||||
result = 0;
|
||||
|
||||
/* ... But configuring tighter timings is not an option. */
|
||||
else if (result > max)
|
||||
result = -EINVAL;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* aemif_config_abus - configure async bus parameters
|
||||
* @pdev: platform device to configure for
|
||||
* @csnum: aemif chip select number
|
||||
*
|
||||
* This function programs the given timing values (in real clock) into the
|
||||
* AEMIF registers taking the AEMIF clock into account.
|
||||
*
|
||||
* This function does not use any locking while programming the AEMIF
|
||||
* because it is expected that there is only one user of a given
|
||||
* chip-select.
|
||||
*
|
||||
* Returns 0 on success, else negative errno.
|
||||
*/
|
||||
static int aemif_config_abus(struct platform_device *pdev, int csnum)
|
||||
{
|
||||
struct aemif_device *aemif = platform_get_drvdata(pdev);
|
||||
struct aemif_cs_data *data = &aemif->cs_data[csnum];
|
||||
int ta, rhold, rstrobe, rsetup, whold, wstrobe, wsetup;
|
||||
unsigned long clk_rate = aemif->clk_rate;
|
||||
unsigned offset;
|
||||
u32 set, val;
|
||||
|
||||
offset = A1CR_OFFSET + (data->cs - aemif->cs_offset) * 4;
|
||||
|
||||
ta = aemif_calc_rate(pdev, data->ta, clk_rate, TA_MAX);
|
||||
rhold = aemif_calc_rate(pdev, data->rhold, clk_rate, RHOLD_MAX);
|
||||
rstrobe = aemif_calc_rate(pdev, data->rstrobe, clk_rate, RSTROBE_MAX);
|
||||
rsetup = aemif_calc_rate(pdev, data->rsetup, clk_rate, RSETUP_MAX);
|
||||
whold = aemif_calc_rate(pdev, data->whold, clk_rate, WHOLD_MAX);
|
||||
wstrobe = aemif_calc_rate(pdev, data->wstrobe, clk_rate, WSTROBE_MAX);
|
||||
wsetup = aemif_calc_rate(pdev, data->wsetup, clk_rate, WSETUP_MAX);
|
||||
|
||||
if (ta < 0 || rhold < 0 || rstrobe < 0 || rsetup < 0 ||
|
||||
whold < 0 || wstrobe < 0 || wsetup < 0) {
|
||||
dev_err(&pdev->dev, "%s: cannot get suitable timings\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
set = TA(ta) | RHOLD(rhold) | RSTROBE(rstrobe) | RSETUP(rsetup) |
|
||||
WHOLD(whold) | WSTROBE(wstrobe) | WSETUP(wsetup);
|
||||
|
||||
set |= (data->asize & ACR_ASIZE_MASK);
|
||||
if (data->enable_ew)
|
||||
set |= ACR_EW_MASK;
|
||||
if (data->enable_ss)
|
||||
set |= ACR_SS_MASK;
|
||||
|
||||
val = readl(aemif->base + offset);
|
||||
val &= ~CONFIG_MASK;
|
||||
val |= set;
|
||||
writel(val, aemif->base + offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int aemif_cycles_to_nsec(int val, unsigned long clk_rate)
|
||||
{
|
||||
return ((val + 1) * NSEC_PER_MSEC) / clk_rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* aemif_get_hw_params - function to read hw register values
|
||||
* @pdev: platform device to read for
|
||||
* @csnum: aemif chip select number
|
||||
*
|
||||
* This function reads the defaults from the registers and update
|
||||
* the timing values. Required for get/set commands and also for
|
||||
* the case when driver needs to use defaults in hardware.
|
||||
*/
|
||||
static void aemif_get_hw_params(struct platform_device *pdev, int csnum)
|
||||
{
|
||||
struct aemif_device *aemif = platform_get_drvdata(pdev);
|
||||
struct aemif_cs_data *data = &aemif->cs_data[csnum];
|
||||
unsigned long clk_rate = aemif->clk_rate;
|
||||
u32 val, offset;
|
||||
|
||||
offset = A1CR_OFFSET + (data->cs - aemif->cs_offset) * 4;
|
||||
val = readl(aemif->base + offset);
|
||||
|
||||
data->ta = aemif_cycles_to_nsec(TA_VAL(val), clk_rate);
|
||||
data->rhold = aemif_cycles_to_nsec(RHOLD_VAL(val), clk_rate);
|
||||
data->rstrobe = aemif_cycles_to_nsec(RSTROBE_VAL(val), clk_rate);
|
||||
data->rsetup = aemif_cycles_to_nsec(RSETUP_VAL(val), clk_rate);
|
||||
data->whold = aemif_cycles_to_nsec(WHOLD_VAL(val), clk_rate);
|
||||
data->wstrobe = aemif_cycles_to_nsec(WSTROBE_VAL(val), clk_rate);
|
||||
data->wsetup = aemif_cycles_to_nsec(WSETUP_VAL(val), clk_rate);
|
||||
data->enable_ew = EW_VAL(val);
|
||||
data->enable_ss = SS_VAL(val);
|
||||
data->asize = val & ASIZE_MAX;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_aemif_parse_abus_config - parse CS configuration from DT
|
||||
* @pdev: platform device to parse for
|
||||
* @np: device node ptr
|
||||
*
|
||||
* This function update the emif async bus configuration based on the values
|
||||
* configured in a cs device binding node.
|
||||
*/
|
||||
static int of_aemif_parse_abus_config(struct platform_device *pdev,
|
||||
struct device_node *np)
|
||||
{
|
||||
struct aemif_device *aemif = platform_get_drvdata(pdev);
|
||||
struct aemif_cs_data *data;
|
||||
u32 cs;
|
||||
u32 val;
|
||||
|
||||
if (of_property_read_u32(np, "ti,cs-chipselect", &cs)) {
|
||||
dev_dbg(&pdev->dev, "cs property is required");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (cs - aemif->cs_offset >= NUM_CS || cs < aemif->cs_offset) {
|
||||
dev_dbg(&pdev->dev, "cs number is incorrect %d", cs);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (aemif->num_cs >= NUM_CS) {
|
||||
dev_dbg(&pdev->dev, "cs count is more than %d", NUM_CS);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data = &aemif->cs_data[aemif->num_cs];
|
||||
data->cs = cs;
|
||||
|
||||
/* read the current value in the hw register */
|
||||
aemif_get_hw_params(pdev, aemif->num_cs++);
|
||||
|
||||
/* override the values from device node */
|
||||
if (!of_property_read_u32(np, "ti,cs-min-turnaround-ns", &val))
|
||||
data->ta = val;
|
||||
|
||||
if (!of_property_read_u32(np, "ti,cs-read-hold-ns", &val))
|
||||
data->rhold = val;
|
||||
|
||||
if (!of_property_read_u32(np, "ti,cs-read-strobe-ns", &val))
|
||||
data->rstrobe = val;
|
||||
|
||||
if (!of_property_read_u32(np, "ti,cs-read-setup-ns", &val))
|
||||
data->rsetup = val;
|
||||
|
||||
if (!of_property_read_u32(np, "ti,cs-write-hold-ns", &val))
|
||||
data->whold = val;
|
||||
|
||||
if (!of_property_read_u32(np, "ti,cs-write-strobe-ns", &val))
|
||||
data->wstrobe = val;
|
||||
|
||||
if (!of_property_read_u32(np, "ti,cs-write-setup-ns", &val))
|
||||
data->wsetup = val;
|
||||
|
||||
if (!of_property_read_u32(np, "ti,cs-bus-width", &val))
|
||||
if (val == 16)
|
||||
data->asize = 1;
|
||||
data->enable_ew = of_property_read_bool(np, "ti,cs-extended-wait-mode");
|
||||
data->enable_ss = of_property_read_bool(np, "ti,cs-select-strobe-mode");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id aemif_of_match[] = {
|
||||
{ .compatible = "ti,davinci-aemif", },
|
||||
{ .compatible = "ti,da850-aemif", },
|
||||
{},
|
||||
};
|
||||
|
||||
static int aemif_probe(struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
int ret = -ENODEV;
|
||||
struct resource *res;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *child_np;
|
||||
struct aemif_device *aemif;
|
||||
|
||||
if (np == NULL)
|
||||
return 0;
|
||||
|
||||
aemif = devm_kzalloc(dev, sizeof(*aemif), GFP_KERNEL);
|
||||
if (!aemif)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, aemif);
|
||||
|
||||
aemif->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(aemif->clk)) {
|
||||
dev_err(dev, "cannot get clock 'aemif'\n");
|
||||
return PTR_ERR(aemif->clk);
|
||||
}
|
||||
|
||||
clk_prepare_enable(aemif->clk);
|
||||
aemif->clk_rate = clk_get_rate(aemif->clk) / MSEC_PER_SEC;
|
||||
|
||||
if (of_device_is_compatible(np, "ti,da850-aemif"))
|
||||
aemif->cs_offset = 2;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
aemif->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(aemif->base)) {
|
||||
ret = PTR_ERR(aemif->base);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* For every controller device node, there is a cs device node that
|
||||
* describe the bus configuration parameters. This functions iterate
|
||||
* over these nodes and update the cs data array.
|
||||
*/
|
||||
for_each_available_child_of_node(np, child_np) {
|
||||
ret = of_aemif_parse_abus_config(pdev, child_np);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (i = 0; i < aemif->num_cs; i++) {
|
||||
ret = aemif_config_abus(pdev, i);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Error configuring chip select %d\n",
|
||||
aemif->cs_data[i].cs);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a child devices explicitly from here to
|
||||
* guarantee that the child will be probed after the AEMIF timing
|
||||
* parameters are set.
|
||||
*/
|
||||
for_each_available_child_of_node(np, child_np) {
|
||||
ret = of_platform_populate(child_np, NULL, NULL, dev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
clk_disable_unprepare(aemif->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int aemif_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct aemif_device *aemif = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable_unprepare(aemif->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver aemif_driver = {
|
||||
.probe = aemif_probe,
|
||||
.remove = aemif_remove,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(aemif_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(aemif_driver);
|
||||
|
||||
MODULE_AUTHOR("Murali Karicheri <m-karicheri2@ti.com>");
|
||||
MODULE_AUTHOR("Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>");
|
||||
MODULE_DESCRIPTION("Texas Instruments AEMIF driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" KBUILD_MODNAME);
|
|
@ -235,7 +235,7 @@ config SGI_XP
|
|||
|
||||
config CS5535_MFGPT
|
||||
tristate "CS5535/CS5536 Geode Multi-Function General Purpose Timer (MFGPT) support"
|
||||
depends on PCI && X86 && MFD_CS5535
|
||||
depends on MFD_CS5535
|
||||
default n
|
||||
help
|
||||
This driver provides access to MFGPT functionality for other
|
||||
|
|
|
@ -72,7 +72,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
|
|
|
@ -47,7 +47,6 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include "bmp085.h"
|
||||
|
|
|
@ -101,7 +101,6 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/io.h>
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/list.h>
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/jiffies.h>
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kobject.h>
|
||||
|
@ -96,7 +95,7 @@ static int sunxi_sid_remove(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
static const struct of_device_id sunxi_sid_of_match[] = {
|
||||
{ .compatible = "allwinner,sun4i-sid", .data = (void *)16},
|
||||
{ .compatible = "allwinner,sun4i-a10-sid", .data = (void *)16},
|
||||
{ .compatible = "allwinner,sun7i-a20-sid", .data = (void *)512},
|
||||
{/* sentinel */},
|
||||
};
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mutex.h>
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
*
|
||||
* See Documentation/fault-injection/provoke-crashes.txt for instructions
|
||||
*/
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
|
@ -45,6 +46,7 @@
|
|||
#include <linux/debugfs.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/mman.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#ifdef CONFIG_IDE
|
||||
#include <linux/ide.h>
|
||||
|
@ -101,6 +103,7 @@ enum ctype {
|
|||
CT_EXEC_USERSPACE,
|
||||
CT_ACCESS_USERSPACE,
|
||||
CT_WRITE_RO,
|
||||
CT_WRITE_KERN,
|
||||
};
|
||||
|
||||
static char* cp_name[] = {
|
||||
|
@ -137,6 +140,7 @@ static char* cp_type[] = {
|
|||
"EXEC_USERSPACE",
|
||||
"ACCESS_USERSPACE",
|
||||
"WRITE_RO",
|
||||
"WRITE_KERN",
|
||||
};
|
||||
|
||||
static struct jprobe lkdtm;
|
||||
|
@ -316,6 +320,13 @@ static void do_nothing(void)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Must immediately follow do_nothing for size calculuations to work out. */
|
||||
static void do_overwritten(void)
|
||||
{
|
||||
pr_info("do_overwritten wasn't overwritten!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
static noinline void corrupt_stack(void)
|
||||
{
|
||||
/* Use default char array length that triggers stack protection. */
|
||||
|
@ -328,7 +339,12 @@ static void execute_location(void *dst)
|
|||
{
|
||||
void (*func)(void) = dst;
|
||||
|
||||
pr_info("attempting ok execution at %p\n", do_nothing);
|
||||
do_nothing();
|
||||
|
||||
memcpy(dst, do_nothing, EXEC_SIZE);
|
||||
flush_icache_range((unsigned long)dst, (unsigned long)dst + EXEC_SIZE);
|
||||
pr_info("attempting bad execution at %p\n", func);
|
||||
func();
|
||||
}
|
||||
|
||||
|
@ -337,8 +353,13 @@ static void execute_user_location(void *dst)
|
|||
/* Intentionally crossing kernel/user memory boundary. */
|
||||
void (*func)(void) = dst;
|
||||
|
||||
pr_info("attempting ok execution at %p\n", do_nothing);
|
||||
do_nothing();
|
||||
|
||||
if (copy_to_user((void __user *)dst, do_nothing, EXEC_SIZE))
|
||||
return;
|
||||
flush_icache_range((unsigned long)dst, (unsigned long)dst + EXEC_SIZE);
|
||||
pr_info("attempting bad execution at %p\n", func);
|
||||
func();
|
||||
}
|
||||
|
||||
|
@ -463,8 +484,12 @@ static void lkdtm_do_action(enum ctype which)
|
|||
}
|
||||
|
||||
ptr = (unsigned long *)user_addr;
|
||||
|
||||
pr_info("attempting bad read at %p\n", ptr);
|
||||
tmp = *ptr;
|
||||
tmp += 0xc0dec0de;
|
||||
|
||||
pr_info("attempting bad write at %p\n", ptr);
|
||||
*ptr = tmp;
|
||||
|
||||
vm_munmap(user_addr, PAGE_SIZE);
|
||||
|
@ -475,10 +500,28 @@ static void lkdtm_do_action(enum ctype which)
|
|||
unsigned long *ptr;
|
||||
|
||||
ptr = (unsigned long *)&rodata;
|
||||
|
||||
pr_info("attempting bad write at %p\n", ptr);
|
||||
*ptr ^= 0xabcd1234;
|
||||
|
||||
break;
|
||||
}
|
||||
case CT_WRITE_KERN: {
|
||||
size_t size;
|
||||
unsigned char *ptr;
|
||||
|
||||
size = (unsigned long)do_overwritten -
|
||||
(unsigned long)do_nothing;
|
||||
ptr = (unsigned char *)do_overwritten;
|
||||
|
||||
pr_info("attempting bad %zu byte write at %p\n", size, ptr);
|
||||
memcpy(ptr, (unsigned char *)do_nothing, size);
|
||||
flush_icache_range((unsigned long)ptr,
|
||||
(unsigned long)(ptr + size));
|
||||
|
||||
do_overwritten();
|
||||
break;
|
||||
}
|
||||
case CT_NONE:
|
||||
default:
|
||||
break;
|
||||
|
@ -493,8 +536,8 @@ static void lkdtm_handler(void)
|
|||
|
||||
spin_lock_irqsave(&count_lock, flags);
|
||||
count--;
|
||||
printk(KERN_INFO "lkdtm: Crash point %s of type %s hit, trigger in %d rounds\n",
|
||||
cp_name_to_str(cpoint), cp_type_to_str(cptype), count);
|
||||
pr_info("Crash point %s of type %s hit, trigger in %d rounds\n",
|
||||
cp_name_to_str(cpoint), cp_type_to_str(cptype), count);
|
||||
|
||||
if (count == 0) {
|
||||
do_it = true;
|
||||
|
@ -551,18 +594,18 @@ static int lkdtm_register_cpoint(enum cname which)
|
|||
lkdtm.kp.symbol_name = "generic_ide_ioctl";
|
||||
lkdtm.entry = (kprobe_opcode_t*) jp_generic_ide_ioctl;
|
||||
#else
|
||||
printk(KERN_INFO "lkdtm: Crash point not available\n");
|
||||
pr_info("Crash point not available\n");
|
||||
return -EINVAL;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
printk(KERN_INFO "lkdtm: Invalid Crash Point\n");
|
||||
pr_info("Invalid Crash Point\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cpoint = which;
|
||||
if ((ret = register_jprobe(&lkdtm)) < 0) {
|
||||
printk(KERN_INFO "lkdtm: Couldn't register jprobe\n");
|
||||
pr_info("Couldn't register jprobe\n");
|
||||
cpoint = CN_INVALID;
|
||||
}
|
||||
|
||||
|
@ -709,8 +752,7 @@ static ssize_t direct_entry(struct file *f, const char __user *user_buf,
|
|||
if (type == CT_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
printk(KERN_INFO "lkdtm: Performing direct entry %s\n",
|
||||
cp_type_to_str(type));
|
||||
pr_info("Performing direct entry %s\n", cp_type_to_str(type));
|
||||
lkdtm_do_action(type);
|
||||
*off += count;
|
||||
|
||||
|
@ -772,7 +814,7 @@ static int __init lkdtm_module_init(void)
|
|||
/* Register debugfs interface */
|
||||
lkdtm_debugfs_root = debugfs_create_dir("provoke-crash", NULL);
|
||||
if (!lkdtm_debugfs_root) {
|
||||
printk(KERN_ERR "lkdtm: creating root dir failed\n");
|
||||
pr_err("creating root dir failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
@ -787,28 +829,26 @@ static int __init lkdtm_module_init(void)
|
|||
de = debugfs_create_file(cur->name, 0644, lkdtm_debugfs_root,
|
||||
NULL, &cur->fops);
|
||||
if (de == NULL) {
|
||||
printk(KERN_ERR "lkdtm: could not create %s\n",
|
||||
cur->name);
|
||||
pr_err("could not create %s\n", cur->name);
|
||||
goto out_err;
|
||||
}
|
||||
}
|
||||
|
||||
if (lkdtm_parse_commandline() == -EINVAL) {
|
||||
printk(KERN_INFO "lkdtm: Invalid command\n");
|
||||
pr_info("Invalid command\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if (cpoint != CN_INVALID && cptype != CT_NONE) {
|
||||
ret = lkdtm_register_cpoint(cpoint);
|
||||
if (ret < 0) {
|
||||
printk(KERN_INFO "lkdtm: Invalid crash point %d\n",
|
||||
cpoint);
|
||||
pr_info("Invalid crash point %d\n", cpoint);
|
||||
goto out_err;
|
||||
}
|
||||
printk(KERN_INFO "lkdtm: Crash point %s of type %s registered\n",
|
||||
cpoint_name, cpoint_type);
|
||||
pr_info("Crash point %s of type %s registered\n",
|
||||
cpoint_name, cpoint_type);
|
||||
} else {
|
||||
printk(KERN_INFO "lkdtm: No crash points registered, enable through debugfs\n");
|
||||
pr_info("No crash points registered, enable through debugfs\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -823,7 +863,7 @@ static void __exit lkdtm_module_exit(void)
|
|||
debugfs_remove_recursive(lkdtm_debugfs_root);
|
||||
|
||||
unregister_jprobe(&lkdtm);
|
||||
printk(KERN_INFO "lkdtm: Crash point unregistered\n");
|
||||
pr_info("Crash point unregistered\n");
|
||||
}
|
||||
|
||||
module_init(lkdtm_module_init);
|
||||
|
|
|
@ -34,3 +34,12 @@ config INTEL_MEI_ME
|
|||
82Q33 Express
|
||||
82X38/X48 Express
|
||||
|
||||
config INTEL_MEI_TXE
|
||||
tristate "Intel Trusted Execution Environment with ME Interface"
|
||||
select INTEL_MEI
|
||||
depends on X86 && PCI && WATCHDOG_CORE
|
||||
help
|
||||
MEI Support for Trusted Execution Environment device on Intel SoCs
|
||||
|
||||
Supported SoCs:
|
||||
Intel Bay Trail
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#
|
||||
# Makefile - Intel Management Engine Interface (Intel MEI) Linux driver
|
||||
# Copyright (c) 2010-2011, Intel Corporation.
|
||||
# Copyright (c) 2010-2014, Intel Corporation.
|
||||
#
|
||||
obj-$(CONFIG_INTEL_MEI) += mei.o
|
||||
mei-objs := init.o
|
||||
|
@ -17,3 +17,7 @@ mei-$(CONFIG_DEBUG_FS) += debugfs.o
|
|||
obj-$(CONFIG_INTEL_MEI_ME) += mei-me.o
|
||||
mei-me-objs := pci-me.o
|
||||
mei-me-objs += hw-me.o
|
||||
|
||||
obj-$(CONFIG_INTEL_MEI_TXE) += mei-txe.o
|
||||
mei-txe-objs := pci-txe.o
|
||||
mei-txe-objs += hw-txe.o
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include <linux/fcntl.h>
|
||||
#include <linux/aio.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/list.h>
|
||||
|
@ -35,7 +34,6 @@
|
|||
|
||||
#include "mei_dev.h"
|
||||
#include "hbm.h"
|
||||
#include "hw-me.h"
|
||||
#include "client.h"
|
||||
|
||||
const uuid_le mei_amthif_guid = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d,
|
||||
|
@ -79,10 +77,9 @@ int mei_amthif_host_init(struct mei_device *dev)
|
|||
|
||||
i = mei_me_cl_by_uuid(dev, &mei_amthif_guid);
|
||||
if (i < 0) {
|
||||
ret = i;
|
||||
dev_info(&dev->pdev->dev,
|
||||
"amthif: failed to find the client %d\n", ret);
|
||||
return ret;
|
||||
"amthif: failed to find the client %d\n", i);
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
cl->me_client_id = dev->me_clients[i].client_id;
|
||||
|
@ -116,14 +113,11 @@ int mei_amthif_host_init(struct mei_device *dev)
|
|||
|
||||
cl->state = MEI_FILE_CONNECTING;
|
||||
|
||||
if (mei_hbm_cl_connect_req(dev, cl)) {
|
||||
dev_dbg(&dev->pdev->dev, "amthif: Failed to connect to ME client\n");
|
||||
cl->state = MEI_FILE_DISCONNECTED;
|
||||
cl->host_client_id = 0;
|
||||
} else {
|
||||
cl->timer_count = MEI_CONNECT_TIMEOUT;
|
||||
}
|
||||
return 0;
|
||||
ret = mei_cl_connect(cl, NULL);
|
||||
|
||||
dev->iamthif_state = MEI_IAMTHIF_IDLE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -137,14 +131,12 @@ int mei_amthif_host_init(struct mei_device *dev)
|
|||
struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev,
|
||||
struct file *file)
|
||||
{
|
||||
struct mei_cl_cb *pos = NULL;
|
||||
struct mei_cl_cb *next = NULL;
|
||||
struct mei_cl_cb *cb;
|
||||
|
||||
list_for_each_entry_safe(pos, next,
|
||||
&dev->amthif_rd_complete_list.list, list) {
|
||||
if (pos->cl && pos->cl == &dev->iamthif_cl &&
|
||||
pos->file_object == file)
|
||||
return pos;
|
||||
list_for_each_entry(cb, &dev->amthif_rd_complete_list.list, list) {
|
||||
if (cb->cl && cb->cl == &dev->iamthif_cl &&
|
||||
cb->file_object == file)
|
||||
return cb;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
@ -180,14 +172,13 @@ int mei_amthif_read(struct mei_device *dev, struct file *file,
|
|||
/* Only possible if we are in timeout */
|
||||
if (!cl || cl != &dev->iamthif_cl) {
|
||||
dev_dbg(&dev->pdev->dev, "bad file ext.\n");
|
||||
return -ETIMEDOUT;
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
i = mei_me_cl_by_id(dev, dev->iamthif_cl.me_client_id);
|
||||
|
||||
if (i < 0) {
|
||||
dev_dbg(&dev->pdev->dev, "amthif client not found.\n");
|
||||
return -ENODEV;
|
||||
return -ENOTTY;
|
||||
}
|
||||
dev_dbg(&dev->pdev->dev, "checking amthif data\n");
|
||||
cb = mei_amthif_find_read_list_entry(dev, file);
|
||||
|
@ -228,7 +219,7 @@ int mei_amthif_read(struct mei_device *dev, struct file *file,
|
|||
dev_dbg(&dev->pdev->dev, "amthif Time out\n");
|
||||
/* 15 sec for the message has expired */
|
||||
list_del(&cb->list);
|
||||
rets = -ETIMEDOUT;
|
||||
rets = -ETIME;
|
||||
goto free;
|
||||
}
|
||||
}
|
||||
|
@ -253,9 +244,10 @@ int mei_amthif_read(struct mei_device *dev, struct file *file,
|
|||
* the buf_idx may point beyond */
|
||||
length = min_t(size_t, length, (cb->buf_idx - *offset));
|
||||
|
||||
if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length))
|
||||
if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length)) {
|
||||
dev_dbg(&dev->pdev->dev, "failed to copy data to userland\n");
|
||||
rets = -EFAULT;
|
||||
else {
|
||||
} else {
|
||||
rets = length;
|
||||
if ((*offset + length) < cb->buf_idx) {
|
||||
*offset += length;
|
||||
|
@ -302,9 +294,8 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret && dev->hbuf_is_ready) {
|
||||
if (ret && mei_hbuf_acquire(dev)) {
|
||||
ret = 0;
|
||||
dev->hbuf_is_ready = false;
|
||||
if (cb->request_buffer.size > mei_hbuf_max_len(dev)) {
|
||||
mei_hdr.length = mei_hbuf_max_len(dev);
|
||||
mei_hdr.msg_complete = 0;
|
||||
|
@ -336,10 +327,6 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb)
|
|||
list_add_tail(&cb->list, &dev->write_list.list);
|
||||
}
|
||||
} else {
|
||||
if (!dev->hbuf_is_ready)
|
||||
dev_dbg(&dev->pdev->dev, "host buffer is not empty");
|
||||
|
||||
dev_dbg(&dev->pdev->dev, "No flow control credentials, so add iamthif cb to write list.\n");
|
||||
list_add_tail(&cb->list, &dev->write_list.list);
|
||||
}
|
||||
return 0;
|
||||
|
@ -365,7 +352,7 @@ int mei_amthif_write(struct mei_device *dev, struct mei_cl_cb *cb)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
cb->fop_type = MEI_FOP_IOCTL;
|
||||
cb->fop_type = MEI_FOP_WRITE;
|
||||
|
||||
if (!list_empty(&dev->amthif_cmd_list.list) ||
|
||||
dev->iamthif_state != MEI_IAMTHIF_IDLE) {
|
||||
|
@ -447,23 +434,23 @@ unsigned int mei_amthif_poll(struct mei_device *dev,
|
|||
|
||||
|
||||
/**
|
||||
* mei_amthif_irq_write_completed - processes completed iamthif operation.
|
||||
* mei_amthif_irq_write - write iamthif command in irq thread context.
|
||||
*
|
||||
* @dev: the device structure.
|
||||
* @slots: free slots.
|
||||
* @cb_pos: callback block.
|
||||
* @cl: private data of the file object.
|
||||
* @cmpl_list: complete list.
|
||||
*
|
||||
* returns 0, OK; otherwise, error.
|
||||
*/
|
||||
int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
s32 *slots, struct mei_cl_cb *cmpl_list)
|
||||
int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
struct mei_cl_cb *cmpl_list)
|
||||
{
|
||||
struct mei_device *dev = cl->dev;
|
||||
struct mei_msg_hdr mei_hdr;
|
||||
size_t len = dev->iamthif_msg_buf_size - dev->iamthif_msg_buf_index;
|
||||
u32 msg_slots = mei_data2slots(len);
|
||||
int slots;
|
||||
int rets;
|
||||
|
||||
rets = mei_cl_flow_ctrl_creds(cl);
|
||||
|
@ -480,13 +467,15 @@ int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
|
|||
mei_hdr.reserved = 0;
|
||||
mei_hdr.internal = 0;
|
||||
|
||||
if (*slots >= msg_slots) {
|
||||
slots = mei_hbuf_empty_slots(dev);
|
||||
|
||||
if (slots >= msg_slots) {
|
||||
mei_hdr.length = len;
|
||||
mei_hdr.msg_complete = 1;
|
||||
/* Split the message only if we can write the whole host buffer */
|
||||
} else if (*slots == dev->hbuf_depth) {
|
||||
msg_slots = *slots;
|
||||
len = (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
|
||||
} else if (slots == dev->hbuf_depth) {
|
||||
msg_slots = slots;
|
||||
len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
|
||||
mei_hdr.length = len;
|
||||
mei_hdr.msg_complete = 0;
|
||||
} else {
|
||||
|
@ -496,7 +485,6 @@ int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
|
|||
|
||||
dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr));
|
||||
|
||||
*slots -= msg_slots;
|
||||
rets = mei_write_message(dev, &mei_hdr,
|
||||
dev->iamthif_msg_buf + dev->iamthif_msg_buf_index);
|
||||
if (rets) {
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#include <linux/mei_cl_bus.h>
|
||||
|
||||
#include "mei_dev.h"
|
||||
#include "hw-me.h"
|
||||
#include "client.h"
|
||||
|
||||
#define to_mei_cl_driver(d) container_of(d, struct mei_cl_driver, driver)
|
||||
|
@ -145,9 +144,9 @@ static struct device_type mei_cl_device_type = {
|
|||
static struct mei_cl *mei_bus_find_mei_cl_by_uuid(struct mei_device *dev,
|
||||
uuid_le uuid)
|
||||
{
|
||||
struct mei_cl *cl, *next;
|
||||
struct mei_cl *cl;
|
||||
|
||||
list_for_each_entry_safe(cl, next, &dev->device_list, device_link) {
|
||||
list_for_each_entry(cl, &dev->device_list, device_link) {
|
||||
if (!uuid_le_cmp(uuid, cl->device_uuid))
|
||||
return cl;
|
||||
}
|
||||
|
@ -524,6 +523,22 @@ void mei_cl_bus_rx_event(struct mei_cl *cl)
|
|||
schedule_work(&device->event_work);
|
||||
}
|
||||
|
||||
void mei_cl_bus_remove_devices(struct mei_device *dev)
|
||||
{
|
||||
struct mei_cl *cl, *next;
|
||||
|
||||
mutex_lock(&dev->device_lock);
|
||||
list_for_each_entry_safe(cl, next, &dev->device_list, device_link) {
|
||||
if (cl->device)
|
||||
mei_cl_remove_device(cl->device);
|
||||
|
||||
list_del(&cl->device_link);
|
||||
mei_cl_unlink(cl);
|
||||
kfree(cl);
|
||||
}
|
||||
mutex_unlock(&dev->device_lock);
|
||||
}
|
||||
|
||||
int __init mei_cl_bus_init(void)
|
||||
{
|
||||
return bus_register(&mei_cl_bus_type);
|
||||
|
|
|
@ -29,20 +29,21 @@
|
|||
* mei_me_cl_by_uuid - locate index of me client
|
||||
*
|
||||
* @dev: mei device
|
||||
*
|
||||
* Locking: called under "dev->device_lock" lock
|
||||
*
|
||||
* returns me client index or -ENOENT if not found
|
||||
*/
|
||||
int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *uuid)
|
||||
{
|
||||
int i, res = -ENOENT;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dev->me_clients_num; ++i)
|
||||
if (uuid_le_cmp(*uuid,
|
||||
dev->me_clients[i].props.protocol_name) == 0) {
|
||||
res = i;
|
||||
break;
|
||||
}
|
||||
dev->me_clients[i].props.protocol_name) == 0)
|
||||
return i;
|
||||
|
||||
return res;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
|
||||
|
@ -60,34 +61,76 @@ int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *uuid)
|
|||
int mei_me_cl_by_id(struct mei_device *dev, u8 client_id)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dev->me_clients_num; i++)
|
||||
if (dev->me_clients[i].client_id == client_id)
|
||||
break;
|
||||
if (WARN_ON(dev->me_clients[i].client_id != client_id))
|
||||
return -ENOENT;
|
||||
return i;
|
||||
|
||||
if (i == dev->me_clients_num)
|
||||
return -ENOENT;
|
||||
|
||||
return i;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mei_cl_cmp_id - tells if the clients are the same
|
||||
*
|
||||
* @cl1: host client 1
|
||||
* @cl2: host client 2
|
||||
*
|
||||
* returns true - if the clients has same host and me ids
|
||||
* false - otherwise
|
||||
*/
|
||||
static inline bool mei_cl_cmp_id(const struct mei_cl *cl1,
|
||||
const struct mei_cl *cl2)
|
||||
{
|
||||
return cl1 && cl2 &&
|
||||
(cl1->host_client_id == cl2->host_client_id) &&
|
||||
(cl1->me_client_id == cl2->me_client_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_io_list_flush - removes cbs belonging to cl.
|
||||
*
|
||||
* @list: an instance of our list structure
|
||||
* @cl: host client, can be NULL for flushing the whole list
|
||||
* @free: whether to free the cbs
|
||||
*/
|
||||
static void __mei_io_list_flush(struct mei_cl_cb *list,
|
||||
struct mei_cl *cl, bool free)
|
||||
{
|
||||
struct mei_cl_cb *cb;
|
||||
struct mei_cl_cb *next;
|
||||
|
||||
/* enable removing everything if no cl is specified */
|
||||
list_for_each_entry_safe(cb, next, &list->list, list) {
|
||||
if (!cl || (cb->cl && mei_cl_cmp_id(cl, cb->cl))) {
|
||||
list_del(&cb->list);
|
||||
if (free)
|
||||
mei_io_cb_free(cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_io_list_flush - removes list entry belonging to cl.
|
||||
*
|
||||
* @list: An instance of our list structure
|
||||
* @cl: host client
|
||||
*/
|
||||
void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl)
|
||||
static inline void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl)
|
||||
{
|
||||
struct mei_cl_cb *cb;
|
||||
struct mei_cl_cb *next;
|
||||
__mei_io_list_flush(list, cl, false);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(cb, next, &list->list, list) {
|
||||
if (cb->cl && mei_cl_cmp_id(cl, cb->cl))
|
||||
list_del(&cb->list);
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_io_list_free - removes cb belonging to cl and free them
|
||||
*
|
||||
* @list: An instance of our list structure
|
||||
* @cl: host client
|
||||
*/
|
||||
static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl)
|
||||
{
|
||||
__mei_io_list_flush(list, cl, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -196,8 +239,8 @@ int mei_cl_flush_queues(struct mei_cl *cl)
|
|||
|
||||
cl_dbg(dev, cl, "remove list entry belonging to cl\n");
|
||||
mei_io_list_flush(&cl->dev->read_list, cl);
|
||||
mei_io_list_flush(&cl->dev->write_list, cl);
|
||||
mei_io_list_flush(&cl->dev->write_waiting_list, cl);
|
||||
mei_io_list_free(&cl->dev->write_list, cl);
|
||||
mei_io_list_free(&cl->dev->write_waiting_list, cl);
|
||||
mei_io_list_flush(&cl->dev->ctrl_wr_list, cl);
|
||||
mei_io_list_flush(&cl->dev->ctrl_rd_list, cl);
|
||||
mei_io_list_flush(&cl->dev->amthif_cmd_list, cl);
|
||||
|
@ -254,10 +297,9 @@ struct mei_cl *mei_cl_allocate(struct mei_device *dev)
|
|||
struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl)
|
||||
{
|
||||
struct mei_device *dev = cl->dev;
|
||||
struct mei_cl_cb *cb = NULL;
|
||||
struct mei_cl_cb *next = NULL;
|
||||
struct mei_cl_cb *cb;
|
||||
|
||||
list_for_each_entry_safe(cb, next, &dev->read_list.list, list)
|
||||
list_for_each_entry(cb, &dev->read_list.list, list)
|
||||
if (mei_cl_cmp_id(cl, cb->cl))
|
||||
return cb;
|
||||
return NULL;
|
||||
|
@ -375,6 +417,23 @@ void mei_host_client_init(struct work_struct *work)
|
|||
mutex_unlock(&dev->device_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_hbuf_acquire: try to acquire host buffer
|
||||
*
|
||||
* @dev: the device structure
|
||||
* returns true if host buffer was acquired
|
||||
*/
|
||||
bool mei_hbuf_acquire(struct mei_device *dev)
|
||||
{
|
||||
if (!dev->hbuf_is_ready) {
|
||||
dev_dbg(&dev->pdev->dev, "hbuf is not ready\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
dev->hbuf_is_ready = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_cl_disconnect - disconnect host client from the me one
|
||||
|
@ -406,8 +465,7 @@ int mei_cl_disconnect(struct mei_cl *cl)
|
|||
return -ENOMEM;
|
||||
|
||||
cb->fop_type = MEI_FOP_CLOSE;
|
||||
if (dev->hbuf_is_ready) {
|
||||
dev->hbuf_is_ready = false;
|
||||
if (mei_hbuf_acquire(dev)) {
|
||||
if (mei_hbm_cl_disconnect_req(dev, cl)) {
|
||||
rets = -ENODEV;
|
||||
cl_err(dev, cl, "failed to disconnect.\n");
|
||||
|
@ -461,17 +519,17 @@ free:
|
|||
bool mei_cl_is_other_connecting(struct mei_cl *cl)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_cl *pos;
|
||||
struct mei_cl *next;
|
||||
struct mei_cl *ocl; /* the other client */
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
return false;
|
||||
|
||||
dev = cl->dev;
|
||||
|
||||
list_for_each_entry_safe(pos, next, &dev->file_list, link) {
|
||||
if ((pos->state == MEI_FILE_CONNECTING) &&
|
||||
(pos != cl) && cl->me_client_id == pos->me_client_id)
|
||||
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)
|
||||
return true;
|
||||
|
||||
}
|
||||
|
@ -505,11 +563,10 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
|
|||
goto out;
|
||||
}
|
||||
|
||||
cb->fop_type = MEI_FOP_IOCTL;
|
||||
|
||||
if (dev->hbuf_is_ready && !mei_cl_is_other_connecting(cl)) {
|
||||
dev->hbuf_is_ready = false;
|
||||
cb->fop_type = MEI_FOP_CONNECT;
|
||||
|
||||
/* run hbuf acquire last so we don't have to undo */
|
||||
if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) {
|
||||
if (mei_hbm_cl_connect_req(dev, cl)) {
|
||||
rets = -ENODEV;
|
||||
goto out;
|
||||
|
@ -521,18 +578,19 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
|
|||
}
|
||||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
rets = wait_event_timeout(dev->wait_recvd_msg,
|
||||
(cl->state == MEI_FILE_CONNECTED ||
|
||||
cl->state == MEI_FILE_DISCONNECTED),
|
||||
mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
|
||||
wait_event_timeout(dev->wait_recvd_msg,
|
||||
(cl->state == MEI_FILE_CONNECTED ||
|
||||
cl->state == MEI_FILE_DISCONNECTED),
|
||||
mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
if (cl->state != MEI_FILE_CONNECTED) {
|
||||
rets = -EFAULT;
|
||||
/* 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);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rets = cl->status;
|
||||
|
@ -554,7 +612,8 @@ out:
|
|||
int mei_cl_flow_ctrl_creds(struct mei_cl *cl)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
int i;
|
||||
struct mei_me_client *me_cl;
|
||||
int id;
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
return -EINVAL;
|
||||
|
@ -567,19 +626,19 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl)
|
|||
if (cl->mei_flow_ctrl_creds > 0)
|
||||
return 1;
|
||||
|
||||
for (i = 0; i < dev->me_clients_num; i++) {
|
||||
struct mei_me_client *me_cl = &dev->me_clients[i];
|
||||
if (me_cl->client_id == cl->me_client_id) {
|
||||
if (me_cl->mei_flow_ctrl_creds) {
|
||||
if (WARN_ON(me_cl->props.single_recv_buf == 0))
|
||||
return -EINVAL;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
id = mei_me_cl_by_id(dev, cl->me_client_id);
|
||||
if (id < 0) {
|
||||
cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
|
||||
return id;
|
||||
}
|
||||
return -ENOENT;
|
||||
|
||||
me_cl = &dev->me_clients[id];
|
||||
if (me_cl->mei_flow_ctrl_creds) {
|
||||
if (WARN_ON(me_cl->props.single_recv_buf == 0))
|
||||
return -EINVAL;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -595,32 +654,31 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl)
|
|||
int mei_cl_flow_ctrl_reduce(struct mei_cl *cl)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
int i;
|
||||
struct mei_me_client *me_cl;
|
||||
int id;
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
return -EINVAL;
|
||||
|
||||
dev = cl->dev;
|
||||
|
||||
if (!dev->me_clients_num)
|
||||
return -ENOENT;
|
||||
|
||||
for (i = 0; i < dev->me_clients_num; i++) {
|
||||
struct mei_me_client *me_cl = &dev->me_clients[i];
|
||||
if (me_cl->client_id == cl->me_client_id) {
|
||||
if (me_cl->props.single_recv_buf != 0) {
|
||||
if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0))
|
||||
return -EINVAL;
|
||||
dev->me_clients[i].mei_flow_ctrl_creds--;
|
||||
} else {
|
||||
if (WARN_ON(cl->mei_flow_ctrl_creds <= 0))
|
||||
return -EINVAL;
|
||||
cl->mei_flow_ctrl_creds--;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
id = mei_me_cl_by_id(dev, cl->me_client_id);
|
||||
if (id < 0) {
|
||||
cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
|
||||
return id;
|
||||
}
|
||||
return -ENOENT;
|
||||
|
||||
me_cl = &dev->me_clients[id];
|
||||
if (me_cl->props.single_recv_buf != 0) {
|
||||
if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0))
|
||||
return -EINVAL;
|
||||
me_cl->mei_flow_ctrl_creds--;
|
||||
} else {
|
||||
if (WARN_ON(cl->mei_flow_ctrl_creds <= 0))
|
||||
return -EINVAL;
|
||||
cl->mei_flow_ctrl_creds--;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -652,7 +710,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
|
|||
i = mei_me_cl_by_id(dev, cl->me_client_id);
|
||||
if (i < 0) {
|
||||
cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
|
||||
return -ENODEV;
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
cb = mei_io_cb_init(cl, NULL);
|
||||
|
@ -666,8 +724,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
|
|||
goto err;
|
||||
|
||||
cb->fop_type = MEI_FOP_READ;
|
||||
if (dev->hbuf_is_ready) {
|
||||
dev->hbuf_is_ready = false;
|
||||
if (mei_hbuf_acquire(dev)) {
|
||||
if (mei_hbm_cl_flow_control_req(dev, cl)) {
|
||||
cl_err(dev, cl, "flow control send failed\n");
|
||||
rets = -ENODEV;
|
||||
|
@ -687,27 +744,26 @@ err:
|
|||
}
|
||||
|
||||
/**
|
||||
* mei_cl_irq_write_complete - write a message to device
|
||||
* mei_cl_irq_write - write a message to device
|
||||
* from the interrupt thread context
|
||||
*
|
||||
* @cl: client
|
||||
* @cb: callback block.
|
||||
* @slots: free slots.
|
||||
* @cmpl_list: complete list.
|
||||
*
|
||||
* returns 0, OK; otherwise error.
|
||||
*/
|
||||
int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
s32 *slots, struct mei_cl_cb *cmpl_list)
|
||||
int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
struct mei_cl_cb *cmpl_list)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_msg_data *buf;
|
||||
struct mei_msg_hdr mei_hdr;
|
||||
size_t len;
|
||||
u32 msg_slots;
|
||||
int slots;
|
||||
int rets;
|
||||
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
return -ENODEV;
|
||||
|
||||
|
@ -724,6 +780,7 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
|
|||
return 0;
|
||||
}
|
||||
|
||||
slots = mei_hbuf_empty_slots(dev);
|
||||
len = buf->size - cb->buf_idx;
|
||||
msg_slots = mei_data2slots(len);
|
||||
|
||||
|
@ -732,13 +789,13 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
|
|||
mei_hdr.reserved = 0;
|
||||
mei_hdr.internal = cb->internal;
|
||||
|
||||
if (*slots >= msg_slots) {
|
||||
if (slots >= msg_slots) {
|
||||
mei_hdr.length = len;
|
||||
mei_hdr.msg_complete = 1;
|
||||
/* Split the message only if we can write the whole host buffer */
|
||||
} else if (*slots == dev->hbuf_depth) {
|
||||
msg_slots = *slots;
|
||||
len = (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
|
||||
} else if (slots == dev->hbuf_depth) {
|
||||
msg_slots = slots;
|
||||
len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
|
||||
mei_hdr.length = len;
|
||||
mei_hdr.msg_complete = 0;
|
||||
} else {
|
||||
|
@ -749,7 +806,6 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
|
|||
cl_dbg(dev, cl, "buf: size = %d idx = %lu\n",
|
||||
cb->request_buffer.size, cb->buf_idx);
|
||||
|
||||
*slots -= msg_slots;
|
||||
rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx);
|
||||
if (rets) {
|
||||
cl->status = rets;
|
||||
|
@ -802,21 +858,29 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
|
|||
|
||||
|
||||
cb->fop_type = MEI_FOP_WRITE;
|
||||
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.reserved = 0;
|
||||
mei_hdr.msg_complete = 0;
|
||||
mei_hdr.internal = cb->internal;
|
||||
|
||||
rets = mei_cl_flow_ctrl_creds(cl);
|
||||
if (rets < 0)
|
||||
goto err;
|
||||
|
||||
/* Host buffer is not ready, we queue the request */
|
||||
if (rets == 0 || !dev->hbuf_is_ready) {
|
||||
cb->buf_idx = 0;
|
||||
/* unseting complete will enqueue the cb for write */
|
||||
mei_hdr.msg_complete = 0;
|
||||
if (rets == 0) {
|
||||
cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
|
||||
rets = buf->size;
|
||||
goto out;
|
||||
}
|
||||
if (!mei_hbuf_acquire(dev)) {
|
||||
cl_dbg(dev, cl, "Cannot acquire the host buffer: not sending.\n");
|
||||
rets = buf->size;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev->hbuf_is_ready = false;
|
||||
|
||||
/* Check for a maximum length */
|
||||
if (buf->size > mei_hbuf_max_len(dev)) {
|
||||
|
@ -827,12 +891,6 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
|
|||
mei_hdr.msg_complete = 1;
|
||||
}
|
||||
|
||||
mei_hdr.host_addr = cl->host_client_id;
|
||||
mei_hdr.me_addr = cl->me_client_id;
|
||||
mei_hdr.reserved = 0;
|
||||
mei_hdr.internal = cb->internal;
|
||||
|
||||
|
||||
rets = mei_write_message(dev, &mei_hdr, buf->data);
|
||||
if (rets)
|
||||
goto err;
|
||||
|
@ -840,13 +898,12 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
|
|||
cl->writing_state = MEI_WRITING;
|
||||
cb->buf_idx = mei_hdr.length;
|
||||
|
||||
rets = buf->size;
|
||||
out:
|
||||
if (mei_hdr.msg_complete) {
|
||||
if (mei_cl_flow_ctrl_reduce(cl)) {
|
||||
rets = -ENODEV;
|
||||
rets = mei_cl_flow_ctrl_reduce(cl);
|
||||
if (rets < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
list_add_tail(&cb->list, &dev->write_waiting_list.list);
|
||||
} else {
|
||||
list_add_tail(&cb->list, &dev->write_list.list);
|
||||
|
@ -856,15 +913,18 @@ out:
|
|||
if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) {
|
||||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
if (wait_event_interruptible(cl->tx_wait,
|
||||
cl->writing_state == MEI_WRITE_COMPLETE)) {
|
||||
if (signal_pending(current))
|
||||
rets = -EINTR;
|
||||
else
|
||||
rets = -ERESTARTSYS;
|
||||
}
|
||||
rets = wait_event_interruptible(cl->tx_wait,
|
||||
cl->writing_state == MEI_WRITE_COMPLETE);
|
||||
mutex_lock(&dev->device_lock);
|
||||
/* wait_event_interruptible returns -ERESTARTSYS */
|
||||
if (rets) {
|
||||
if (signal_pending(current))
|
||||
rets = -EINTR;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
rets = buf->size;
|
||||
err:
|
||||
return rets;
|
||||
}
|
||||
|
@ -905,9 +965,9 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
|
|||
|
||||
void mei_cl_all_disconnect(struct mei_device *dev)
|
||||
{
|
||||
struct mei_cl *cl, *next;
|
||||
struct mei_cl *cl;
|
||||
|
||||
list_for_each_entry_safe(cl, next, &dev->file_list, link) {
|
||||
list_for_each_entry(cl, &dev->file_list, link) {
|
||||
cl->state = MEI_FILE_DISCONNECTED;
|
||||
cl->mei_flow_ctrl_creds = 0;
|
||||
cl->timer_count = 0;
|
||||
|
@ -922,8 +982,8 @@ void mei_cl_all_disconnect(struct mei_device *dev)
|
|||
*/
|
||||
void mei_cl_all_wakeup(struct mei_device *dev)
|
||||
{
|
||||
struct mei_cl *cl, *next;
|
||||
list_for_each_entry_safe(cl, next, &dev->file_list, link) {
|
||||
struct mei_cl *cl;
|
||||
list_for_each_entry(cl, &dev->file_list, link) {
|
||||
if (waitqueue_active(&cl->rx_wait)) {
|
||||
cl_dbg(dev, cl, "Waking up reading client!\n");
|
||||
wake_up_interruptible(&cl->rx_wait);
|
||||
|
@ -942,20 +1002,8 @@ void mei_cl_all_wakeup(struct mei_device *dev)
|
|||
*/
|
||||
void mei_cl_all_write_clear(struct mei_device *dev)
|
||||
{
|
||||
struct mei_cl_cb *cb, *next;
|
||||
struct list_head *list;
|
||||
|
||||
list = &dev->write_list.list;
|
||||
list_for_each_entry_safe(cb, next, list, list) {
|
||||
list_del(&cb->list);
|
||||
mei_io_cb_free(cb);
|
||||
}
|
||||
|
||||
list = &dev->write_waiting_list.list;
|
||||
list_for_each_entry_safe(cb, next, list, list) {
|
||||
list_del(&cb->list);
|
||||
mei_io_cb_free(cb);
|
||||
}
|
||||
mei_io_list_free(&dev->write_list, NULL);
|
||||
mei_io_list_free(&dev->write_waiting_list, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -45,8 +45,6 @@ static inline void mei_io_list_init(struct mei_cl_cb *list)
|
|||
{
|
||||
INIT_LIST_HEAD(&list->list);
|
||||
}
|
||||
void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl);
|
||||
|
||||
/*
|
||||
* MEI Host Client Functions
|
||||
*/
|
||||
|
@ -61,22 +59,6 @@ int mei_cl_unlink(struct mei_cl *cl);
|
|||
int mei_cl_flush_queues(struct mei_cl *cl);
|
||||
struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl);
|
||||
|
||||
/**
|
||||
* mei_cl_cmp_id - tells if file private data have same id
|
||||
*
|
||||
* @fe1: private data of 1. file object
|
||||
* @fe2: private data of 2. file object
|
||||
*
|
||||
* returns true - if ids are the same and not NULL
|
||||
*/
|
||||
static inline bool mei_cl_cmp_id(const struct mei_cl *cl1,
|
||||
const struct mei_cl *cl2)
|
||||
{
|
||||
return cl1 && cl2 &&
|
||||
(cl1->host_client_id == cl2->host_client_id) &&
|
||||
(cl1->me_client_id == cl2->me_client_id);
|
||||
}
|
||||
|
||||
|
||||
int mei_cl_flow_ctrl_creds(struct mei_cl *cl);
|
||||
|
||||
|
@ -86,15 +68,15 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl);
|
|||
*/
|
||||
static inline bool mei_cl_is_connected(struct mei_cl *cl)
|
||||
{
|
||||
return (cl->dev &&
|
||||
return cl->dev &&
|
||||
cl->dev->dev_state == MEI_DEV_ENABLED &&
|
||||
cl->state == MEI_FILE_CONNECTED);
|
||||
cl->state == MEI_FILE_CONNECTED;
|
||||
}
|
||||
static inline bool mei_cl_is_transitioning(struct mei_cl *cl)
|
||||
{
|
||||
return (MEI_FILE_INITIALIZING == cl->state ||
|
||||
return MEI_FILE_INITIALIZING == cl->state ||
|
||||
MEI_FILE_DISCONNECTED == cl->state ||
|
||||
MEI_FILE_DISCONNECTING == cl->state);
|
||||
MEI_FILE_DISCONNECTING == cl->state;
|
||||
}
|
||||
|
||||
bool mei_cl_is_other_connecting(struct mei_cl *cl);
|
||||
|
@ -102,8 +84,8 @@ int mei_cl_disconnect(struct mei_cl *cl);
|
|||
int mei_cl_connect(struct mei_cl *cl, struct file *file);
|
||||
int mei_cl_read_start(struct mei_cl *cl, size_t length);
|
||||
int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking);
|
||||
int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
s32 *slots, struct mei_cl_cb *cmpl_list);
|
||||
int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
struct mei_cl_cb *cmpl_list);
|
||||
|
||||
void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb);
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче