Commit fe3c22bd authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'char-misc-3.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc

Pull char/misc updates from Greg KH:
 "Here's the big char/misc driver tree merge for 3.11-rc1

  A variety of different driver patches here.  All of these have been in
  linux-next for a while, and the networking patches were acked-by David
  Miller, as it made sense for those patches to come through this tree"

* tag 'char-misc-3.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (102 commits)
  Revert "char: misc: assign file->private_data in all cases"
  drivers: uio_pdrv_genirq: Use of_match_ptr() macro
  mei: check whether hw start has succeeded
  mei: check if the hardware reset succeeded
  mei: mei_cl_connect: don't multiply the timeout twice
  mei: do not override a client writing state when buffering
  mei: move mei_cl_irq_write_complete to client.c
  UIO: Fix concurrency issue
  drivers: uio_dmem_genirq: Use of_match_ptr() macro
  char: misc: assign file->private_data in all cases
  drivers: hv: allocate synic structures before hv_synic_init()
  drivers: hv: check interrupt mask before read_index
  vme: vme_tsi148.c: fix error return code in tsi148_probe()
  FMC: fix error handling in probe() function
  fmc: avoid readl/writel namespace conflict
  FMC: NULL dereference on allocation failure
  UIO: fix uio_pdrv_genirq with device tree but no interrupt
  UIO: allow binding uio_pdrv_genirq.c to devices using command line option
  FMC: add a char-device mezzanine driver
  FMC: add a driver to write mezzanine EEPROM
  ...
parents ce49b628 38067269
......@@ -187,6 +187,8 @@ firmware_class/
- request_firmware() hotplug interface info.
flexible-arrays.txt
- how to make use of flexible sized arrays in linux
fmc/
- information about the FMC bus abstraction
frv/
- Fujitsu FR-V Linux documentation.
futex-requeue-pi.txt
......
Atmel AT91RM9200 CompactFlash
Required properties:
- compatible : "atmel,at91rm9200-cf".
- reg : should specify localbus address and size used.
- gpios : specifies the gpio pins to control the CF device. Detect
and reset gpio's are mandatory while irq and vcc gpio's are
optional and may be set to 0 if not present.
Example:
compact-flash@50000000 {
compatible = "atmel,at91rm9200-cf";
reg = <0x50000000 0x30000000>;
gpios = <&pioC 13 0 /* irq */
&pioC 15 0 /* detect */
0 /* vcc */
&pioC 5 0 /* reset */
>;
};
EXTCON FOR TWL CHIPS
PALMAS USB COMPARATOR
Required Properties:
- compatible : Should be "ti,palmas-usb" or "ti,twl6035-usb"
- vbus-supply : phandle to the regulator device tree node.
Optional Properties:
- ti,wakeup : To enable the wakeup comparator in probe
palmas-usb {
compatible = "ti,twl6035-usb", "ti,palmas-usb";
vbus-supply = <&smps10_reg>;
ti,wakeup;
};
Device tree bindings for MVEBU Device Bus controllers
The Device Bus controller available in some Marvell's SoC allows to control
different types of standard memory and I/O devices such as NOR, NAND, and FPGA.
The actual devices are instantiated from the child nodes of a Device Bus node.
Required properties:
- compatible: Currently only Armada 370/XP SoC are supported,
with this compatible string:
marvell,mvebu-devbus
- reg: A resource specifier for the register space.
This is the base address of a chip select within
the controller's register space.
(see the example below)
- #address-cells: Must be set to 1
- #size-cells: Must be set to 1
- ranges: Must be set up to reflect the memory layout with four
integer values for each chip-select line in use:
0 <physical address of mapping> <size>
Mandatory timing properties for child nodes:
Read parameters:
- devbus,turn-off-ps: Defines the time during which the controller does not
drive the AD bus after the completion of a device read.
This prevents contentions on the Device Bus after a read
cycle from a slow device.
- devbus,bus-width: Defines the bus width (e.g. <16>)
- devbus,badr-skew-ps: Defines the time delay from from A[2:0] toggle,
to read data sample. This parameter is useful for
synchronous pipelined devices, where the address
precedes the read data by one or two cycles.
- devbus,acc-first-ps: Defines the time delay from the negation of
ALE[0] to the cycle that the first read data is sampled
by the controller.
- devbus,acc-next-ps: Defines the time delay between the cycle that
samples data N and the cycle that samples data N+1
(in burst accesses).
- devbus,rd-setup-ps: Defines the time delay between DEV_CSn assertion to
DEV_OEn assertion. If set to 0 (default),
DEV_OEn and DEV_CSn are asserted at the same cycle.
This parameter has no affect on <acc-first-ps> parameter
(no affect on first data sample). Set <rd-setup-ps>
to a value smaller than <acc-first-ps>.
- devbus,rd-hold-ps: Defines the time between the last data sample to the
de-assertion of DEV_CSn. If set to 0 (default),
DEV_OEn and DEV_CSn are de-asserted at the same cycle
(the cycle of the last data sample).
This parameter has no affect on DEV_OEn de-assertion.
DEV_OEn is always de-asserted the next cycle after
last data sampled. Also this parameter has no
affect on <turn-off-ps> parameter.
Set <rd-hold-ps> to a value smaller than <turn-off-ps>.
Write parameters:
- devbus,ale-wr-ps: Defines the time delay from the ALE[0] negation cycle
to the DEV_WEn assertion.
- devbus,wr-low-ps: Defines the time during which DEV_WEn is active.
A[2:0] and Data are kept valid as long as DEV_WEn
is active. This parameter defines the setup time of
address and data to DEV_WEn rise.
- devbus,wr-high-ps: Defines the time during which DEV_WEn is kept
inactive (high) between data beats of a burst write.
DEV_A[2:0] and Data are kept valid (do not toggle) for
<wr-high-ps> - <tick> ps.
This parameter defines the hold time of address and
data after DEV_WEn rise.
- devbus,sync-enable: Synchronous device enable.
1: True
0: False
An example for an Armada XP GP board, with a 16 MiB NOR device as child
is showed below. Note that the Device Bus driver is in charge of allocating
the mbus address decoding window for each of its child devices.
The window is created using the chip select specified in the child
device node together with the base address and size specified in the ranges
property. For instance, in the example below the allocated decoding window
will start at base address 0xf0000000, with a size 0x1000000 (16 MiB)
for chip select 0 (a.k.a DEV_BOOTCS).
This address window handling is done in this mvebu-devbus only as a temporary
solution. It will be removed when the support for mbus device tree binding is
added.
The reg property implicitly specifies the chip select as this:
0x10400: DEV_BOOTCS
0x10408: DEV_CS0
0x10410: DEV_CS1
0x10418: DEV_CS2
0x10420: DEV_CS3
Example:
devbus-bootcs@d0010400 {
status = "okay";
ranges = <0 0xf0000000 0x1000000>; /* @addr 0xf0000000, size 0x1000000 */
#address-cells = <1>;
#size-cells = <1>;
/* Device Bus parameters are required */
/* Read parameters */
devbus,bus-width = <8>;
devbus,turn-off-ps = <60000>;
devbus,badr-skew-ps = <0>;
devbus,acc-first-ps = <124000>;
devbus,acc-next-ps = <248000>;
devbus,rd-setup-ps = <0>;
devbus,rd-hold-ps = <0>;
/* Write parameters */
devbus,sync-enable = <0>;
devbus,wr-high-ps = <60000>;
devbus,wr-low-ps = <60000>;
devbus,ale-wr-ps = <60000>;
flash@0 {
compatible = "cfi-flash";
/* 16 MiB */
reg = <0 0x1000000>;
bank-width = <2>;
#address-cells = <1>;
#size-cells = <1>;
/*
* We split the 16 MiB in two partitions,
* just as an example.
*/
partition@0 {
label = "First";
reg = <0 0x800000>;
};
partition@800000 {
label = "Second";
reg = <0x800000 0x800000>;
};
};
};
Documentation in this directory comes from sections of the manual we
wrote for the externally-developed fmc-bus package. The complete
manual as of today (2013-02) is available in PDF format at
http://www.ohwr.org/projects/fmc-bus/files
00-INDEX
- this file.
FMC-and-SDB.txt
- What are FMC and SDB, basic concepts for this framework
API.txt
- The functions that are exported by the bus driver
parameters.txt
- The module parameters
carrier.txt
- writing a carrier (a device)
mezzanine.txt
- writing code for your mezzanine (a driver)
identifiers.txt
- how identification and matching works
fmc-fakedev.txt
- about drivers/fmc/fmc-fakedev.ko
fmc-trivial.txt
- about drivers/fmc/fmc-trivial.ko
fmc-write-eeprom.txt
- about drivers/fmc/fmc-write-eeprom.ko
fmc-chardev.txt
- about drivers/fmc/fmc-chardev.ko
Functions Exported by fmc.ko
****************************
The FMC core exports the usual 4 functions that are needed for a bus to
work, and a few more:
int fmc_driver_register(struct fmc_driver *drv);
void fmc_driver_unregister(struct fmc_driver *drv);
int fmc_device_register(struct fmc_device *fmc);
void fmc_device_unregister(struct fmc_device *fmc);
int fmc_device_register_n(struct fmc_device **fmc, int n);
void fmc_device_unregister_n(struct fmc_device **fmc, int n);
uint32_t fmc_readl(struct fmc_device *fmc, int offset);
void fmc_writel(struct fmc_device *fmc, uint32_t val, int off);
void *fmc_get_drvdata(struct fmc_device *fmc);
void fmc_set_drvdata(struct fmc_device *fmc, void *data);
int fmc_reprogram(struct fmc_device *f, struct fmc_driver *d, char *gw,
int sdb_entry);
The data structure that describe a device is detailed in *note FMC
Device::, the one that describes a driver is detailed in *note FMC
Driver::. Please note that structures of type fmc_device must be
allocated by the caller, but must not be released after unregistering.
The fmc-bus itself takes care of releasing the structure when their use
count reaches zero - actually, the device model does that in lieu of us.
The functions to register and unregister n devices are meant to be used
by carriers that host more than one mezzanine. The devices must all be
registered at the same time because if the FPGA is reprogrammed, all
devices in the array are affected. Usually, the driver matching the
first device will reprogram the FPGA, so other devices must know they
are already driven by a reprogrammed FPGA.
If a carrier hosts slots that are driven by different FPGA devices, it
should register as a group only mezzanines that are driven by the same
FPGA, for the reason outlined above.
Finally, the fmc_reprogram function calls the reprogram method (see
*note The API Offered by Carriers:: and also scans the memory area for
an SDB tree. You can pass -1 as sdb_entry to disable such scan.
Otherwise, the function fails if no tree is found at the specified
entry point. The function is meant to factorize common code, and by
the time you read this it is already used by the spec-sw and fine-delay
modules.
FMC (FPGA Mezzanine Card) is the standard we use for our I/O devices,
in the context of White Rabbit and related hardware.
In our I/O environments we need to write drivers for each mezzanine
card, and such drivers must work regardless of the carrier being used.
To achieve this, we abstract the FMC interface.
We have a carrier for PCI-E called SPEC and one for VME called SVEC,
but more are planned. Also, we support stand-alone devices (usually
plugged on a SPEC card), controlled through Etherbone, developed by GSI.
Code and documentation for the FMC bus was born as part of the spec-sw
project, but now it lives in its own project. Other projects, i.e.
software support for the various carriers, should include this as a
submodule.
The most up to date version of code and documentation is always
available from the repository you can clone from:
git://ohwr.org/fmc-projects/fmc-bus.git (read-only)
git@ohwr.org:fmc-projects/fmc-bus.git (read-write for developers)
Selected versions of the documentation, as well as complete tar
archives for selected revisions are placed to the Files section of the
project: `http://www.ohwr.org/projects/fmc-bus/files'
What is FMC
***********
FMC, as said, stands for "FPGA Mezzanine Card". It is a standard
developed by the VME consortium called VITA (VMEbus International Trade
Association and ratified by ANSI, the American National Standard
Institute. The official documentation is called "ANSI-VITA 57.1".
The FMC card is an almost square PCB, around 70x75 millimeters, that is
called mezzanine in this document. It usually lives plugged into
another PCB for power supply and control; such bigger circuit board is
called carrier from now on, and a single carrier may host more than one
mezzanine.
In the typical application the mezzanine is mostly analog while the
carrier is mostly digital, and hosts an FPGA that must be configured to
match the specific mezzanine and the desired application. Thus, you may
need to load different FPGA images to drive different instances of the
same mezzanine.
FMC, as such, is not a bus in the usual meaning of the term, because
most carriers have only one connector, and carriers with several
connectors have completely separate electrical connections to them.
This package, however, implements a bus as a software abstraction.
What is SDB
***********
SDB (Self Describing Bus) is a set of data structures that we use for
enumerating the internal structure of an FPGA image. We also use it as
a filesystem inside the FMC EEPROM.
SDB is not mandatory for use of this FMC kernel bus, but if you have SDB
this package can make good use of it. SDB itself is developed in the
fpga-config-space OHWR project. The link to the repository is
`git://ohwr.org/hdl-core-lib/fpga-config-space.git' and what is used in
this project lives in the sdbfs subdirectory in there.
SDB support for FMC is described in *note FMC Identification:: and
*note SDB Support::
SDB Support
***********
The fmc.ko bus driver exports a few functions to help drivers taking
advantage of the SDB information that may be present in your own FPGA
memory image.
The module exports the following functions, in the special header
<linux/fmc-sdb.h>. The linux/ prefix in the name is there because we
plan to submit it upstream in the future, and don't want to force
changes on our drivers if that happens.
int fmc_scan_sdb_tree(struct fmc_device *fmc, unsigned long address);
void fmc_show_sdb_tree(struct fmc_device *fmc);
signed long fmc_find_sdb_device(struct sdb_array *tree, uint64_t vendor,
uint32_t device, unsigned long *sz);
int fmc_free_sdb_tree(struct fmc_device *fmc);
This diff is collapsed.
fmc-chardev
===========
This is a simple generic driver, that allows user access by means of a
character device (actually, one for each mezzanine it takes hold of).
The char device is created as a misc device. Its name in /dev (as
created by udev) is the same name as the underlying FMC device. Thus,
the name can be a silly fmc-0000 look-alike if the device has no
identifiers nor bus_id, a more specific fmc-0400 if the device has a
bus-specific address but no associated name, or something like
fdelay-0400 if the FMC core can rely on both a mezzanine name and a bus
address.
Currently the driver only supports read and write: you can lseek to the
desired address and read or write a register.
The driver assumes all registers are 32-bit in size, and only accepts a
single read or write per system call. However, as a result of Unix read
and write semantics, users can simply fread or fwrite bigger areas in
order to dump or store bigger memory areas.
There is currently no support for mmap, user-space interrupt management
and DMA buffers. They may be added in later versions, if the need
arises.
The example below shows raw access to a SPEC card programmed with its
golden FPGA file, that features an SDB structure at offset 256 - i.e.
64 words. The mezzanine's EEPROM in this case is not programmed, so the
default name is fmc-<bus><devfn>, and there are two cards in the system:
spusa.root# insmod fmc-chardev.ko
[ 1073.339332] spec 0000:02:00.0: Driver has no ID: matches all
[ 1073.345051] spec 0000:02:00.0: Created misc device "fmc-0200"
[ 1073.350821] spec 0000:04:00.0: Driver has no ID: matches all
[ 1073.356525] spec 0000:04:00.0: Created misc device "fmc-0400"
spusa.root# ls -l /dev/fmc*
crw------- 1 root root 10, 58 Nov 20 19:23 /dev/fmc-0200
crw------- 1 root root 10, 57 Nov 20 19:23 /dev/fmc-0400
spusa.root# dd bs=4 skip=64 count=1 if=/dev/fmc-0200 2> /dev/null | od -t x1z
0000000 2d 42 44 53 >-BDS<
0000004
The simple program tools/fmc-mem in this package can access an FMC char
device and read or write a word or a whole area. Actually, the program
is not specific to FMC at all, it just uses lseek, read and write.
Its first argument is the device name, the second the offset, the third
(if any) the value to write and the optional last argument that must
begin with "+" is the number of bytes to read or write. In case of
repeated reading data is written to stdout; repeated writes read from
stdin and the value argument is ignored.
The following examples show reading the SDB magic number and the first
SDB record from a SPEC device programmed with its golden image:
spusa.root# ./fmc-mem /dev/fmc-0200 100
5344422d
spusa.root# ./fmc-mem /dev/fmc-0200 100 +40 | od -Ax -t x1z
000000 2d 42 44 53 00 01 02 00 00 00 00 00 00 00 00 00 >-BDS............<
000010 00 00 00 00 ff 01 00 00 00 00 00 00 51 06 00 00 >............Q...<
000020 c9 42 a5 e6 02 00 00 00 11 05 12 20 2d 34 42 57 >.B......... -4BW<
000030 73 6f 72 43 72 61 62 73 49 53 47 2d 00 20 20 20 >sorCrabsISG-. <
000040
fmc-fakedev
===========
This package includes a software-only device, called fmc-fakedev, which
is able to register up to 4 mezzanines (by default it registers one).
Unlike the SPEC driver, which creates an FMC device for each PCI cards
it manages, this module creates a single instance of its set of
mezzanines.
It is meant as the simplest possible example of how a driver should be
written, and it includes a fake EEPROM image (built using the tools
described in *note FMC Identification::),, which by default is
replicated for each fake mezzanine.
You can also use this device to verify the match algorithms, by asking
it to test your own EEPROM image. You can provide the image by means of
the eeprom= module parameter: the new EEPROM image is loaded, as usual,
by means of the firmware loader. This example shows the defaults and a
custom EEPROM image:
spusa.root# insmod fmc-fakedev.ko
[ 99.971247] fake-fmc-carrier: mezzanine 0
[ 99.975393] Manufacturer: fake-vendor
[ 99.979624] Product name: fake-design-for-testing
spusa.root# rmmod fmc-fakedev
spusa.root# insmod fmc-fakedev.ko eeprom=fdelay-eeprom.bin
[ 121.447464] fake-fmc-carrier: Mezzanine 0: eeprom "fdelay-eeprom.bin"
[ 121.462725] fake-fmc-carrier: mezzanine 0
[ 121.466858] Manufacturer: CERN
[ 121.470477] Product name: FmcDelay1ns4cha
spusa.root# rmmod fmc-fakedev
After loading the device, you can use the write_ee method do modify its
own internal fake EEPROM: whenever the image is overwritten starting at
offset 0, the module will unregister and register again the FMC device.
This is shown in fmc-write-eeprom.txt
fmc-trivial
===========
The simple module fmc-trivial is just a simple client that registers an
interrupt handler. I used it to verify the basic mechanism of the FMC
bus and how interrupts worked.
The module implements the generic FMC parameters, so it can program a
different gateware file in each card. The whole list of parameters it
accepts are:
`busid='
`gateware='
Generic parameters. See mezzanine.txt
This driver is worth reading, in my opinion.
fmc-write-eeprom
================
This module is designed to load a binary file from /lib/firmware and to
write it to the internal EEPROM of the mezzanine card. This driver uses
the `busid' generic parameter.
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:
* It accepts a `file=' argument (within /lib/firmware) and if no
such argument is received, it doesn't write anything to EEPROM
(i.e. there is no default file name).
* If the file name ends with `.bin' it is written verbatim starting
at offset 0.
* If the file name ends with `.tlv' it is interpreted as
type-length-value (i.e., it allows writev(2)-like operation).
* If the file name doesn't match any of the patterns above, it is
ignored and no write is performed.
* Only cards listed with `busid=' are written to. If no busid is
specified, no programming is done (and the probe function of the
driver will fail).
Each TLV tuple is formatted in this way: the header is 5 bytes,
followed by data. The first byte is `w' for write, the next two bytes
represent the address, in little-endian byte order, and the next two
represent the data length, in little-endian order. The length does not
include the header (it is the actual number of bytes to be written).
This is a real example: that writes 5 bytes at position 0x110:
spusa.root# od -t x1 -Ax /lib/firmware/try.tlv
000000 77 10 01 05 00 30 31 32 33 34
00000a
spusa.root# insmod /tmp/fmc-write-eeprom.ko busid=0x0200 file=try.tlv
[19983.391498] spec 0000:03:00.0: write 5 bytes at 0x0110
[19983.414615] spec 0000:03:00.0: write_eeprom: success
Please note that you'll most likely want to use SDBFS to build your
EEPROM image, at least if your mezzanines are being used in the White
Rabbit environment. For this reason the TLV format is not expected to
be used much and is not expected to be developed further.
If you want to try reflashing fake EEPROM devices, you can use the
fmc-fakedev.ko module (see *note fmc-fakedev::). Whenever you change
the image starting at offset 0, it will deregister and register again
after two seconds. Please note, however, that if fmc-write-eeprom is
still loaded, the system will associate it to the new device, which
will be reprogrammed and thus will be unloaded after two seconds. The
following example removes the module after it reflashed fakedev the
first time.
spusa.root# insmod fmc-fakedev.ko
[ 72.984733] fake-fmc: Manufacturer: fake-vendor
[ 72.989434] fake-fmc: Product name: fake-design-for-testing
spusa.root# insmod fmc-write-eeprom.ko busid=0 file=fdelay-eeprom.bin; \
rmmod fmc-write-eeprom
[ 130.874098] fake-fmc: Matching a generic driver (no ID)
[ 130.887845] fake-fmc: programming 6155 bytes
[ 130.894567] fake-fmc: write_eeprom: success
[ 132.895794] fake-fmc: Manufacturer: CERN
[ 132.899872] fake-fmc: Product name: FmcDelay1ns4cha
Writing to 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 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# 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
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
FMC Identification
******************
The FMC standard requires every compliant mezzanine to carry
identification information in an I2C EEPROM. The information must be
laid out according to the "IPMI Platform Management FRU Information",
where IPMI is a lie I'd better not expand, and FRU means "Field
Replaceable Unit".
The FRU information is an intricate unreadable binary blob that must
live at offset 0 of the EEPROM, and typically extends for a few hundred
bytes. The standard allows the application to use all the remaining
storage area of the EEPROM as it wants.
This chapter explains how to create your own EEPROM image and how to
write it in your mezzanine, as well as how devices and drivers are
paired at run time. EEPROM programming uses tools that are part of this
package and SDB (part of the fpga-config-space package).
The first sections are only interesting for manufacturers who need to
write the EEPROM. If you are just a software developer writing an FMC
device or driver, you may jump straight to *note SDB Support::.
Building the FRU Structure
==========================
If you want to know the internals of the FRU structure and despair, you
can retrieve the document from
`http://download.intel.com/design/servers/ipmi/FRU1011.pdf' . The
standard is awful and difficult without reason, so we only support the
minimum mandatory subset - we create a simple structure and parse it
back at run time, but we are not able to either generate or parse more
arcane features like non-english languages and 6-bit text. If you need
more items of the FRU standard for your boards, please submit patches.
This package includes the Python script that Matthieu Cattin wrote to
generate the FRU binary blob, based on an helper libipmi by Manohar
Vanga and Matthieu himself. I changed the test script to receive
parameters from the command line or from the environment (the command
line takes precedence)
To make a long story short, in order to build a standard-compliant
binary file to be burned in your EEPROM, you need the following items:
Environment Opt Official Name Default
---------------------------------------------------------------------
FRU_VENDOR -v "Board Manufacturer" fmc-example
FRU_NAME -n "Board Product Name" mezzanine
FRU_SERIAL -s `Board Serial Number" 0001
FRU_PART -p "Board Part Number" sample-part
FRU_OUTPUT -o not applicable /dev/stdout
The "Official Name" above is what you find in the FRU official
documentation, chapter 11, page 7 ("Board Info Area Format"). The
output option is used to save the generated binary to a specific file
name instead of stdout.
You can pass the items to the FRU generator either in the environment
or on the command line. This package has currently no support for
specifying power consumption or such stuff, but I plan to add it as
soon as I find some time for that.
FIXME: consumption etc for FRU are here or in PTS?
The following example creates a binary image for a specific board:
./tools/fru-generator -v CERN -n FmcAdc100m14b4cha \
-s HCCFFIA___-CR000003 -p EDA-02063-V5-0 > eeprom.bin
The following example shows a script that builds several binary EEPROM
images for a series of boards, changing the serial number for each of
them. The script uses a mix of environment variables and command line
options, and uses the same string patterns shown above.
#!/bin/sh
export FRU_VENDOR="CERN"
export FRU_NAME="FmcAdc100m14b4cha"
export FRU_PART="EDA-02063-V5-0"
serial="HCCFFIA___-CR"
for number in $(seq 1 50); do
# build number-string "ns"
ns="$(printf %06d $number)"
./fru-generator -s "${serial}${ns}" > eeprom-${ns}.bin
done
Using SDB-FS in the EEPROM
==========================
If you want to use SDB as a filesystem in the EEPROM device within the
mezzanine, you should create one such filesystem using gensdbfs, from
the fpga-config-space package on OHWR.
By using an SBD filesystem you can cluster several files in a single
EEPROM, so both the host system and a soft-core running in the FPGA (if
any) can access extra production-time information.
We chose to use SDB as a storage filesystem because the format is very
simple, and both the host system and the soft-core will likely already
include support code for such format. The SDB library offered by the
fpga-config-space is less than 1kB under LM32, so it proves quite up to
the task.
The SDB entry point (which acts as a directory listing) cannot live at
offset zero in the flash device, because the FRU information must live
there. To avoid wasting precious storage space while still allowing
for more-than-minimal FRU structures, the fmc.ko will look for the SDB
record at address 256, 512 and 1024.
In order to generate the complete EEPROM image you'll need a
configuration file for gensdbfs: you tell the program where to place
the sdb entry point, and you must force the FRU data file to be placed
at the beginning of the storage device. If needed, you can also place
other files at a special offset (we sometimes do it for backward
compatibility with drivers we wrote before implementing SDB for flash
memory).
The directory tools/sdbfs of this package includes a well-commented
example that you may want to use as a starting point (the comments are
in the file called -SDB-CONFIG-). Reading documentation for gensdbfs
is a suggested first step anyways.
This package (generic FMC bus support) only accesses two files in the
EEPROM: the FRU information, at offset zero, with a suggested filename
of IPMI-FRU and the short name for the mezzanine, in a file called
name. The IPMI-FRU name is not mandatory, but a strongly suggested
choice; the name filename is mandatory, because this is the preferred
short name used by the FMC core. For example, a name of "fdelay" may
supplement a Product Name like "FmcDelay1ns4cha" - exactly as
demonstrated in `tools/sdbfs'.
Note: SDB access to flash memory is not yet supported, so the short
name currently in use is just the "Product Name" FRU string.
The example in tools/sdbfs includes an extra file, that is needed by
the fine-delay driver, and must live at a known address of 0x1800. By
running gensdbfs on that directory you can output your binary EEPROM
image (here below spusa$ is the shell prompt):
spusa$ ../fru-generator -v CERN -n FmcDelay1ns4cha -s proto-0 \
-p EDA-02267-V3 > IPMI-FRU
spusa$ ls -l
total 16
-rw-rw-r-- 1 rubini staff 975 Nov 19 18:08 --SDB-CONFIG--
-rw-rw-r-- 1 rubini staff 216 Nov 19 18:13 IPMI-FRU
-rw-rw-r-- 1 rubini staff 11 Nov 19 18:04 fd-calib
-rw-rw-r-- 1 rubini staff 7 Nov 19 18:04 name
spusa$ sudo gensdbfs . /lib/firmware/fdelay-eeprom.bin
spusa$ sdb-read -l -e 0x100 /lib/firmware/fdelay-eeprom.bin
/home/rubini/wip/sdbfs/userspace/sdb-read: listing format is to be defined
46696c6544617461:2e202020 00000100-000018ff .
46696c6544617461:6e616d65 00000200-00000206 name
46696c6544617461:66642d63 00001800-000018ff fd-calib
46696c6544617461:49504d49 00000000-000000d7 IPMI-FRU
spusa$ ../fru-dump /lib/firmware/fdelay-eeprom.bin
/lib/firmware/fdelay-eeprom.bin: manufacturer: CERN
/lib/firmware/fdelay-eeprom.bin: product-name: FmcDelay1ns4cha
/lib/firmware/fdelay-eeprom.bin: serial-number: proto-0
/lib/firmware/fdelay-eeprom.bin: part-number: EDA-02267-V3
As expected, the output file is both a proper sdbfs object and an IPMI
FRU information blob. The fd-calib file lives at offset 0x1800 and is
over-allocated to 256 bytes, according to the configuration file for
gensdbfs.
FMC Driver
**********
An FMC driver is concerned with the specific mezzanine and associated
gateware. As such, it is expected to be independent of the carrier
being used: it will perform I/O accesses only by means of
carrier-provided functions.
The matching between device and driver is based on the content of the
EEPROM (as mandated by the FMC standard) or by the actual cores
configured in the FPGA; the latter technique is used when the FPGA is
already programmed when the device is registered to the bus core.
In some special cases it is possible for a driver to directly access
FPGA registers, by means of the `fpga_base' field of the device
structure. This may be needed for high-bandwidth peripherals like fast
ADC cards. If the device module registered a remote device (for example
by means of Etherbone), the `fpga_base' pointer will be NULL.
Therefore, drivers must be ready to deal with NULL base pointers, and
fail gracefully. Most driver, however, are not expected to access the
pointer directly but run fmc_readl and fmc_writel instead, which will
work in any case.
In even more special cases, the driver may access carrier-specific
functionality: the `carrier_name' string allows the driver to check
which is the current carrier and make use of the `carrier_data'
pointer. We chose to use carrier names rather than numeric identifiers
for greater flexibility, but also to avoid a central registry within
the `fmc.h' file - we hope other users will exploit our framework with
their own carriers. An example use of carrier names is in GPIO setup
(see *note The GPIO Abstraction::), although the name match is not
expected to be performed by the driver. If you depend on specific
carriers, please check the carrier name and fail gracefully if your
driver finds it is running in a yet-unknown-to-it environment.
ID Table
========
Like most other Linux drivers, and FMC driver must list all the devices
which it is able to drive. This is usually done by means of a device
table, but in FMC we can match hardware based either on the contents of
their EEPROM or on the actual FPGA cores that can be enumerated.
Therefore, we have two tables of identifiers.
Matching of FRU information depends on two names, the manufacturer (or
vendor) and the device (see *note FMC Identification::); for
flexibility during production (i.e. before writing to the EEPROM) the
bus supports a catch-all driver that specifies NULL strings. For this
reason, the table is specified as pointer-and-length, not a a
null-terminated array - the entry with NULL names can be a valid entry.
Matching on FPGA cores depends on two numeric fields: the 64-bit vendor
number and the 32-bit device number. Support for matching based on
class is not yet implemented. Each device is expected to be uniquely
identified by an array of cores (it matches if all of the cores are
instantiated), and for consistency the list is passed as
pointer-and-length. Several similar devices can be driven by the same
driver, and thus the driver specifies and array of such arrays.
The complete set of involved data structures is thus the following:
struct fmc_fru_id { char *manufacturer; char *product_name; };
struct fmc_sdb_one_id { uint64_t vendor; uint32_t device; };
struct fmc_sdb_id { struct fmc_sdb_one_id *cores; int cores_nr; };
struct fmc_device_id {
struct fmc_fru_id *fru_id; int fru_id_nr;
struct fmc_sdb_id *sdb_id; int sdb_id_nr;
};
A better reference, with full explanation, is the <linux/fmc.h> header.
Module Parameters
=================
Most of the FMC drivers need the same set of kernel parameters. This
package includes support to implement common parameters by means of
fields in the `fmc_driver' structure and simple macro definitions.
The parameters are carrier-specific, in that they rely on the busid
concept, that varies among carriers. For the SPEC, the identifier is a
PCI bus and devfn number, 16 bits wide in total; drivers for other
carriers will most likely offer something similar but not identical,
and some code duplication is unavoidable.
This is the list of parameters that are common to several modules to
see how they are actually used, please look at spec-trivial.c.
`busid='
This is an array of integers, listing carrier-specific
identification numbers. For PIC, for example, `0x0400' represents
bus 4, slot 0. If any such ID is specified, the driver will only
accept to drive cards that appear in the list (even if the FMC ID
matches). This is accomplished by the validate carrier method.
`gateware='
The argument is an array of strings. If no busid= is specified,
the first string of gateware= is used for all cards; otherwise the
identifiers and gateware names are paired one by one, in the order
specified.
`show_sdb='
For modules supporting it, this parameter asks to show the SDB
internal structure by means of kernel messages. It is disabled by
default because those lines tend to hide more important messages,
if you look at the system console while loading the drivers.
Note: the parameter is being obsoleted, because fmc.ko itself now
supports dump_sdb= that applies to every client driver.
For example, if you are using the trivial driver to load two different
gateware files to two different cards, you can use the following
parameters to load different binaries to the cards, after looking up
the PCI identifiers. This has been tested with a SPEC carrier.
insmod fmc-trivial.ko \
busid=0x0200,0x0400 \
gateware=fmc/fine-delay.bin,fmc/simple-dio.bin
Please note that not all sub-modules support all of those parameters.
You can use modinfo to check what is supported by each module.
Module Parameters in fmc.ko
***************************
The core driver receives two module parameters, meant to help debugging
client modules. Both parameters can be modified by writing to
/sys/module/fmc/parameters/, because they are used when client drivers
are devices are registered, not when fmc.ko is loaded.
`dump_eeprom='
If not zero, the parameter asks the bus controller to dump the
EEPROM of any device that is registered, using printk.
`dump_sdb='
If not zero, the parameter prints the SDB tree of every FPGA it is
loaded by fmc_reprogram(). If greater than one, it asks to dump
the binary content of SDB records. This currently only dumps the
top-level SDB array, though.
EEPROM dumping avoids repeating lines, since most of the contents is
usually empty and all bits are one or zero. This is an example of the
output:
[ 6625.850480] spec 0000:02:00.0: FPGA programming successful
[ 6626.139949] spec 0000:02:00.0: Manufacturer: CERN
[ 6626.144666] spec 0000:02:00.0: Product name: FmcDelay1ns4cha
[ 6626.150370] FMC: mezzanine 0: 0000:02:00.0 on SPEC
[ 6626.155179] FMC: dumping eeprom 0x2000 (8192) bytes
[ 6626.160087] 0000: 01 00 00 01 00 0b 00 f3 01 0a 00 a5 85 87 c4 43
[ 6626.167069] 0010: 45 52 4e cf 46 6d 63 44 65 6c 61 79 31 6e 73 34
[ 6626.174019] 0020: 63 68 61 c7 70 72 6f 74 6f 2d 30 cc 45 44 41 2d
[ 6626.180975] 0030: 30 32 32 36 37 2d 56 33 da 32 30 31 32 2d 31 31
[...]
[ 6626.371366] 0200: 66 64 65 6c 61 79 0a 00 00 00 00 00 00 00 00 00
[ 6626.378359] 0210: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 6626.385361] [...]
[ 6626.387308] 1800: 70 6c 61 63 65 68 6f 6c 64 65 72 ff ff ff ff ff
[ 6626.394259] 1810: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
[ 6626.401250] [...]
The dump of SDB looks like the following; the example shows the simple
golden gateware for the SPEC card, removing the leading timestamps to
fit the page:
spec 0000:02:00.0: SDB: 00000651:e6a542c9 WB4-Crossbar-GSI
spec 0000:02:00.0: SDB: 0000ce42:ff07fc47 WR-Periph-Syscon (00000000-000000ff)
FMC: mezzanine 0: 0000:02:00.0 on SPEC
FMC: poor dump of sdb first level:
0000: 53 44 42 2d 00 02 01 00 00 00 00 00 00 00 00 00
0010: 00 00 00 00 00 00 01 ff 00 00 00 00 00 00 06 51
0020: e6 a5 42 c9 00 00 00 02 20 12 05 11 57 42 34 2d
0030: 43 72 6f 73 73 62 61 72 2d 47 53 49 20 20 20 00
0040: 00 00 01 01 00 00 00 07 00 00 00 00 00 00 00 00
0050: 00 00 00 00 00 00 00 ff 00 00 00 00 00 00 ce 42
0060: ff 07 fc 47 00 00 00 01 20 12 03 05 57 52 2d 50
0070: 65 72 69 70 68 2d 53 79 73 63 6f 6e 20 20 20 01
......@@ -25,8 +25,8 @@ When a w1 master driver registers with the w1 subsystem, the following occurs:
- sysfs entries for that w1 master are created
- the w1 bus is periodically searched for new slave devices
When a device is found on the bus, w1 core checks if driver for its family is
loaded. If so, the family driver is attached to the slave.
When a device is found on the bus, w1 core tries to load the driver for its family
and check if it is loaded. If so, the family driver is attached to the slave.
If there is no driver for the family, default one is assigned, which allows to perform
almost any kind of operations. Each logical operation is a transaction
in nature, which can contain several (two or one) low-level operations.
......
......@@ -3309,6 +3309,15 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/floppy.git
S: Odd fixes
F: drivers/block/floppy.c
FMC SUBSYSTEM
M: Alessandro Rubini <rubini@gnudd.com>
W: http://www.ohwr.org/projects/fmc-bus
S: Supported
F: drivers/fmc/
F: include/linux/fmc*.h
F: include/linux/ipmi-fru.h
K: fmc_d.*register
FPU EMULATOR
M: Bill Metzenthen <billm@melbpc.org.au>
W: http://floatingpoint.sourceforge.net/emulator/index.html
......@@ -7667,6 +7676,7 @@ STABLE BRANCH
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
L: stable@vger.kernel.org
S: Supported
F: Documentation/stable_kernel_rules.txt
STAGING SUBSYSTEM
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
......
......@@ -166,4 +166,6 @@ source "drivers/ipack/Kconfig"
source "drivers/reset/Kconfig"
source "drivers/fmc/Kconfig"
endmenu
......@@ -152,3 +152,4 @@ obj-$(CONFIG_IIO) += iio/
obj-$(CONFIG_VME_BUS) += vme/
obj-$(CONFIG_IPACK_BUS) += ipack/
obj-$(CONFIG_NTB) += ntb/
obj-$(CONFIG_FMC) += fmc/
......@@ -725,7 +725,7 @@ static int hpet_is_known(struct hpet_data *hdp)
return 0;
}
static ctl_table hpet_table[] = {
static struct ctl_table hpet_table[] = {
{
.procname = "max-user-freq",
.data = &hpet_max_freq,
......@@ -736,7 +736,7 @@ static ctl_table hpet_table[] = {
{}
};
static ctl_table hpet_root[] = {
static struct ctl_table hpet_root[] = {
{
.procname = "hpet",
.maxlen = 0,
......@@ -746,7 +746,7 @@ static ctl_table hpet_root[] = {
{}
};
static ctl_table dev_root[] = {
static struct ctl_table dev_root[] = {
{
.procname = "dev",
.maxlen = 0,
......
......@@ -659,7 +659,7 @@ static struct ipmi_smi_watcher smi_watcher = {
#ifdef CONFIG_PROC_FS
#include <linux/sysctl.h>
static ctl_table ipmi_table[] = {
static struct ctl_table ipmi_table[] = {
{ .procname = "poweroff_powercycle",
.data = &poweroff_powercycle,
.maxlen = sizeof(poweroff_powercycle),
......@@ -668,14 +668,14 @@ static ctl_table ipmi_table[] = {
{ }
};
static ctl_table ipmi_dir_table[] = {
static struct ctl_table ipmi_dir_table[] = {
{ .procname = "ipmi",
.mode = 0555,
.child = ipmi_table },
{ }
};
static ctl_table ipmi_root_table[] = {
static struct ctl_table ipmi_root_table[] = {
{ .procname = "dev",
.mode = 0555,
.child = ipmi_dir_table },
......
......@@ -745,7 +745,7 @@ static loff_t memory_lseek(struct file *file, loff_t offset, int orig)
offset += file->f_pos;
case SEEK_SET:
/* to avoid userland mistaking f_pos=-9 as -EBADF=-9 */
if ((unsigned long long)offset >= ~0xFFFULL) {
if (IS_ERR_VALUE((unsigned long long)offset)) {
ret = -EOVERFLOW;
break;
}
......
......@@ -267,7 +267,7 @@ mspec_mmap(struct file *file, struct vm_area_struct *vma,
if ((vma->vm_flags & VM_WRITE) == 0)
return -EPERM;
pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
pages = vma_pages(vma);
vdata_size = sizeof(struct vma_data) + pages * sizeof(long);
if (vdata_size <= PAGE_SIZE)
vdata = kzalloc(vdata_size, GFP_KERNEL);
......
......@@ -1381,10 +1381,10 @@ static char sysctl_bootid[16];
* as an ASCII string in the standard UUID format. If accesses via the
* sysctl system call, it is returned as 16 bytes of binary data.
*/
static int proc_do_uuid(ctl_table *table, int write,
static int proc_do_uuid(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
ctl_table fake_table;
struct ctl_table fake_table;
unsigned char buf[64], tmp_uuid[16], *uuid;
uuid = table->data;
......@@ -1409,8 +1409,8 @@ static int proc_do_uuid(ctl_table *table, int write,
}
static int sysctl_poolsize = INPUT_POOL_WORDS * 32;
extern ctl_table random_table[];
ctl_table random_table[] = {
extern struct ctl_table random_table[];
struct ctl_table random_table[] = {
{
.procname = "poolsize",
.data = &sysctl_poolsize,
......
......@@ -280,7 +280,7 @@ static irqreturn_t rtc_interrupt(int irq, void *dev_id)
/*
* sysctl-tuning infrastructure.
*/
static ctl_table rtc_table[] = {
static struct ctl_table rtc_table[] = {
{
.procname = "max-user-freq",
.data = &rtc_max_user_freq,
......@@ -291,7 +291,7 @@ static ctl_table rtc_table[] = {
{ }
};
static ctl_table rtc_root[] = {
static struct ctl_table rtc_root[] = {
{
.procname = "rtc",
.mode = 0555,
......@@ -300,7 +300,7 @@ static ctl_table rtc_root[] = {
{ }
};
static ctl_table dev_root[] = {
static struct ctl_table dev_root[] = {
{
.procname = "dev",
.mode = 0555,
......
......@@ -37,7 +37,7 @@
#include <linux/cdev.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <linux/io.h>
struct hwicap_drvdata {
u32 write_buffer_in_use; /* Always in [0,3] */
......@@ -85,7 +85,13 @@ struct hwicap_driver_config {
void (*reset)(struct hwicap_drvdata *drvdata);
};
/* Number of times to poll the done regsiter */
/* Number of times to poll the done register. This has to be large
* enough to allow an entire configuration to complete. If an entire
* page (4kb) is configured at once, that could take up to 4k cycles
* with a byte-wide icap interface. In most cases, this driver is
* used with a much smaller fifo, but this should be sufficient in the
* worst case.
*/
#define XHI_MAX_RETRIES 5000
/************ Constant Definitions *************/
......
......@@ -53,4 +53,11 @@ config EXTCON_ARIZONA
with Wolfson Arizona devices. These are audio CODECs with
advanced audio accessory detection support.
config EXTCON_PALMAS
tristate "Palmas USB EXTCON support"
depends on MFD_PALMAS
help
Say Y here to enable support for USB peripheral and USB host
detection by palmas usb.
endif # MULTISTATE_SWITCH
......@@ -8,3 +8,4 @@ obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o
obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o
obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o
obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o
obj-$(CONFIG_EXTCON_PALMAS) += extcon-palmas.o
......@@ -185,26 +185,6 @@ static ssize_t cable_state_show(struct device *dev,
cable->cable_index));
}
static ssize_t cable_state_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct extcon_cable *cable = container_of(attr, struct extcon_cable,
attr_state);
int ret, state;
ret = sscanf(buf, "%d", &state);
if (ret == 0)
ret = -EINVAL;
else
ret = extcon_set_cable_state_(cable->edev, cable->cable_index,
state);
if (ret < 0)
return ret;
return count;
}
/**
* extcon_update_state() - Update the cable attach states of the extcon device
* only for the masked bits.
......@@ -501,6 +481,7 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj,
return -ENODEV;
}
}
EXPORT_SYMBOL_GPL(extcon_register_interest);
/**
* extcon_unregister_interest() - Unregister the notifier registered by
......@@ -515,6 +496,7 @@ int extcon_unregister_interest(struct extcon_specific_cable_nb *obj)
return raw_notifier_chain_unregister(&obj->edev->nh, &obj->internal_nb);
}
EXPORT_SYMBOL_GPL(extcon_unregister_interest);
/**
* extcon_register_notifier() - Register a notifiee to get notified by
......@@ -665,9 +647,8 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
sysfs_attr_init(&cable->attr_state.attr);
cable->attr_state.attr.name = "state";
cable->attr_state.attr.mode = 0644;
cable->attr_state.attr.mode = 0444;
cable->attr_state.show = cable_state_show;
cable->attr_state.store = cable_state_store;
}
}
......
/*
* Palmas USB transceiver driver
*
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Author: Graeme Gregory <gg@slimlogic.co.uk>
* Author: Kishon Vijay Abraham I <kishon@ti.com>
*
* Based on twl6030_usb.c
*
* Author: Hema HK <hemahk@ti.com>
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/mfd/palmas.h>
#include <linux/of.h>
#include <linux/of_platform.h>
static const char *palmas_extcon_cable[] = {
[0] = "USB",
[1] = "USB-HOST",
NULL,
};
static const int mutually_exclusive[] = {0x3, 0x0};
static void palmas_usb_wakeup(struct palmas *palmas, int enable)
{
if (enable)
palmas_write(palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_WAKEUP,
PALMAS_USB_WAKEUP_ID_WK_UP_COMP);
else
palmas_write(palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_WAKEUP, 0);
}
static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
{
struct palmas_usb *palmas_usb = _palmas_usb;
unsigned int vbus_line_state;
palmas_read(palmas_usb->palmas, PALMAS_INTERRUPT_BASE,
PALMAS_INT3_LINE_STATE, &vbus_line_state);
if (vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS) {
if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) {
palmas_usb->linkstat = PALMAS_USB_STATE_VBUS;
extcon_set_cable_state(&palmas_usb->edev, "USB", true);
} else {
dev_dbg(palmas_usb->dev,
"Spurious connect event detected\n");
}
} else if (!(vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS)) {
if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) {
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
extcon_set_cable_state(&palmas_usb->edev, "USB", false);
} else {
dev_dbg(palmas_usb->dev,
"Spurious disconnect event detected\n");
}
}
return IRQ_HANDLED;
}
static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
{
unsigned int set;
struct palmas_usb *palmas_usb = _palmas_usb;
palmas_read(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
PALMAS_USB_ID_INT_LATCH_SET, &set);
if (set & PALMAS_USB_ID_INT_SRC_ID_GND) {
palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
PALMAS_USB_ID_INT_EN_HI_SET,
PALMAS_USB_ID_INT_EN_HI_SET_ID_FLOAT);
palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
PALMAS_USB_ID_INT_EN_HI_CLR,
PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND);
palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
PALMAS_USB_ID_INT_LATCH_CLR,
PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND);
palmas_usb->linkstat = PALMAS_USB_STATE_ID;
extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", true);
} else if (set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) {
palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
PALMAS_USB_ID_INT_EN_HI_SET,
PALMAS_USB_ID_INT_EN_HI_SET_ID_GND);
palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
PALMAS_USB_ID_INT_EN_HI_CLR,
PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT);
palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
PALMAS_USB_ID_INT_LATCH_CLR,
PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT);
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", false);
}
return IRQ_HANDLED;
}
static void palmas_enable_irq(struct palmas_usb *palmas_usb)
{
palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
PALMAS_USB_VBUS_CTRL_SET,
PALMAS_USB_VBUS_CTRL_SET_VBUS_ACT_COMP);
palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
PALMAS_USB_ID_CTRL_SET, PALMAS_USB_ID_CTRL_SET_ID_ACT_COMP);
palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
PALMAS_USB_ID_INT_EN_HI_SET,
PALMAS_USB_ID_INT_EN_HI_SET_ID_GND);
palmas_vbus_irq_handler(palmas_usb->vbus_irq, palmas_usb);
/* cold plug for host mode needs this delay */
msleep(30);
palmas_id_irq_handler(palmas_usb->id_irq, palmas_usb);
}
static int palmas_usb_probe(struct platform_device *pdev)
{
struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
struct palmas_usb_platform_data *pdata = pdev->dev.platform_data;
struct device_node *node = pdev->dev.of_node;
struct palmas_usb *palmas_usb;
int status;
if (node && !pdata) {
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
pdata->wakeup = of_property_read_bool(node, "ti,wakeup");
} else if (!pdata) {
return -EINVAL;
}
palmas_usb = devm_kzalloc(&pdev->dev, sizeof(*palmas_usb), GFP_KERNEL);
if (!palmas_usb)
return -ENOMEM;
palmas->usb = palmas_usb;
palmas_usb->palmas = palmas;
palmas_usb->dev = &pdev->dev;
palmas_usb->id_otg_irq = regmap_irq_get_virq(palmas->irq_data,
PALMAS_ID_OTG_IRQ);
palmas_usb->id_irq = regmap_irq_get_virq(palmas->irq_data,
PALMAS_ID_IRQ);
palmas_usb->vbus_otg_irq = regmap_irq_get_virq(palmas->irq_data,
PALMAS_VBUS_OTG_IRQ);
palmas_usb->vbus_irq = regmap_irq_get_virq(palmas->irq_data,
PALMAS_VBUS_IRQ);
palmas_usb_wakeup(palmas, pdata->wakeup);
platform_set_drvdata(pdev, palmas_usb);
palmas_usb->edev.name = "palmas-usb";
palmas_usb->edev.supported_cable = palmas_extcon_cable;
palmas_usb->edev.mutually_exclusive = mutually_exclusive;
status = extcon_dev_register(&palmas_usb->edev, palmas_usb->dev);
if (status) {
dev_err(&pdev->dev, "failed to register extcon device\n");
return status;
}
status = devm_request_threaded_irq(palmas_usb->dev, palmas_usb->id_irq,
NULL, palmas_id_irq_handler,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
"palmas_usb_id", palmas_usb);
if (status < 0) {
dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
palmas_usb->id_irq, status);
goto fail_extcon;
}
status = devm_request_threaded_irq(palmas_usb->dev,
palmas_usb->vbus_irq, NULL, palmas_vbus_irq_handler,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
"palmas_usb_vbus", palmas_usb);
if (status < 0) {
dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
palmas_usb->vbus_irq, status);
goto fail_extcon;
}
palmas_enable_irq(palmas_usb);
return 0;
fail_extcon:
extcon_dev_unregister(&palmas_usb->edev);
return status;
}
static int palmas_usb_remove(struct platform_device *pdev)
{
struct palmas_usb *palmas_usb = platform_get_drvdata(pdev);
extcon_dev_unregister(&palmas_usb->edev);
return 0;
}
static struct of_device_id of_palmas_match_tbl[] = {
{ .compatible = "ti,palmas-usb", },
{ .compatible = "ti,twl6035-usb", },
{ /* end */ }
};
static struct platform_driver palmas_usb_driver = {
.probe = palmas_usb_probe,
.remove = palmas_usb_remove,
.driver = {
.name = "palmas-usb",
.of_match_table = of_palmas_match_tbl,
.owner = THIS_MODULE,
},
};
module_platform_driver(palmas_usb_driver);
MODULE_ALIAS("platform:palmas-usb");
MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
MODULE_DESCRIPTION("Palmas USB transceiver driver");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);
#
# FMC (ANSI-VITA 57.1) bus support
#
menuconfig FMC
tristate "FMC support"
help
FMC (FPGA Mezzanine Carrier) is a mechanical and electrical
standard for mezzanine cards that plug into a carrier board.
This kernel subsystem supports the matching between carrier
and mezzanine based on identifiers stored in the internal I2C
EEPROM, as well as having carrier-independent drivers.
The framework was born outside of the kernel and at this time
the off-tree code base is more complete. Code and documentation
is at git://ohwr.org/fmc-projects/fmc-bus.git .
if FMC
config FMC_FAKEDEV
tristate "FMC fake device (software testing)"
help
This is a fake carrier, bringing a default EEPROM content
that can be rewritten at run time and usef for matching
mezzanines.
config FMC_TRIVIAL
tristate "FMC trivial mezzanine driver (software testing)"
help
This is a fake mezzanine driver, to show how FMC works and test it.
The driver also handles interrupts (we used it with a real carrier
before the mezzanines were produced)
config FMC_WRITE_EEPROM
tristate "FMC mezzanine driver to write I2C EEPROM"
help
This driver matches every mezzanine device and can write the
internal EEPROM of the PCB, using the firmware loader to get
its binary and the function carrier->reprogram to actually do it.
It is useful when the mezzanines are produced.
config FMC_CHARDEV
tristate "FMC mezzanine driver that registers a char device"
help
This driver matches every mezzanine device and allows user
space to read and write registers using a char device. It
can be used to write user-space drivers, or just get
aquainted with a mezzanine before writing its specific driver.
endif # FMC
obj-$(CONFIG_FMC) += fmc.o
fmc-y = fmc-core.o
fmc-y += fmc-match.o
fmc-y += fmc-sdb.o
fmc-y += fru-parse.o
fmc-y += fmc-dump.o
obj-$(CONFIG_FMC_FAKEDEV) += fmc-fakedev.o
obj-$(CONFIG_FMC_TRIVIAL) += fmc-trivial.o
obj-$(CONFIG_FMC_WRITE_EEPROM) += fmc-write-eeprom.o
obj-$(CONFIG_FMC_CHARDEV) += fmc-chardev.o
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/spinlock.h>
#include <linux/fmc.h>
#include <linux/uaccess.h>
static LIST_HEAD(fc_devices);
static DEFINE_SPINLOCK(fc_lock);
struct fc_instance {
struct list_head list;
struct fmc_device *fmc;
struct miscdevice misc;
};
/* at open time, we must identify our device */
static int fc_open(struct inode *ino, struct file *f)
{
struct fmc_device *fmc;
struct fc_instance *fc;
int minor = iminor(ino);
list_for_each_entry(fc, &fc_devices, list)
if (fc->misc.minor == minor)
break;
if (fc->misc.minor != minor)
return -ENODEV;
fmc = fc->fmc;
if (try_module_get(fmc->owner) == 0)
return -ENODEV;
f->private_data = fmc;
return 0;
}
static int fc_release(struct inode *ino, struct file *f)
{
struct fmc_device *fmc = f->private_data;
module_put(fmc->owner);
return 0;
}
/* read and write are simple after the default llseek has been used */
static ssize_t fc_read(struct file *f, char __user *buf, size_t count,
loff_t *offp)
{
struct fmc_device *fmc = f->private_data;
unsigned long addr;
uint32_t val;
if (count < sizeof(val))
return -EINVAL;
count = sizeof(val);
addr = *offp;
if (addr > fmc->memlen)
return -ESPIPE; /* Illegal seek */
val = fmc_readl(fmc, addr);
if (copy_to_user(buf, &val, count))
return -EFAULT;
*offp += count;
return count;
}
static ssize_t fc_write(struct file *f, const char __user *buf, size_t count,
loff_t *offp)
{
struct fmc_device *fmc = f->private_data;
unsigned long addr;
uint32_t val;
if (count < sizeof(val))
return -EINVAL;
count = sizeof(val);
addr = *offp;
if (addr > fmc->memlen)
return -ESPIPE; /* Illegal seek */
if (copy_from_user(&val, buf, count))
return -EFAULT;
fmc_writel(fmc, val, addr);
*offp += count;
return count;
}
static const struct file_operations fc_fops = {
.owner = THIS_MODULE,
.open = fc_open,
.release = fc_release,
.llseek = generic_file_llseek,
.read = fc_read,
.write = fc_write,
};
/* Device part .. */
static int fc_probe(struct fmc_device *fmc);
static int fc_remove(struct fmc_device *fmc);
static struct fmc_driver fc_drv = {
.version = FMC_VERSION,
.driver.name = KBUILD_MODNAME,
.probe = fc_probe,
.remove = fc_remove,
/* no table: we want to match everything */
};
/* We accept the generic busid parameter */
FMC_PARAM_BUSID(fc_drv);
/* probe and remove must allocate and release a misc device */
static int fc_probe(struct fmc_device *fmc)
{
int ret;
int index = 0;
struct fc_instance *fc;
if (fmc->op->validate)
index = fmc->op->validate(fmc, &fc_drv);
if (index < 0)
return -EINVAL; /* not our device: invalid */
/* Create a char device: we want to create it anew */
fc = kzalloc(sizeof(*fc), GFP_KERNEL);
if (!fc)
return -ENOMEM;
fc->fmc = fmc;
fc->misc.minor = MISC_DYNAMIC_MINOR;
fc->misc.fops = &fc_fops;
fc->misc.name = kstrdup(dev_name(&fmc->dev), GFP_KERNEL);
spin_lock(&fc_lock);
ret = misc_register(&fc->misc);
if (ret < 0)
goto err_unlock;
list_add(&fc->list, &fc_devices);
spin_unlock(&fc_lock);
dev_info(&fc->fmc->dev, "Created misc device \"%s\"\n",
fc->misc.name);
return 0;
err_unlock:
spin_unlock(&fc_lock);
kfree(fc->misc.name);
kfree(fc);
return ret;
}
static int fc_remove(struct fmc_device *fmc)
{
struct fc_instance *fc;
list_for_each_entry(fc, &fc_devices, list)
if (fc->fmc == fmc)
break;
if (fc->fmc != fmc) {
dev_err(&fmc->dev, "remove called but not found\n");
return -ENODEV;
}
spin_lock(&fc_lock);
list_del(&fc->list);
misc_deregister(&fc->misc);
kfree(fc->misc.name);
kfree(fc);
spin_unlock(&fc_lock);
return 0;
}
static int fc_init(void)
{
int ret;
ret = fmc_driver_register(&fc_drv);
return ret;
}
static void fc_exit(void)
{
fmc_driver_unregister(&fc_drv);
}
module_init(fc_init);
module_exit(fc_exit);
MODULE_LICENSE("GPL");
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/fmc.h>
static int fmc_check_version(unsigned long version, const char *name)
{
if (__FMC_MAJOR(version) != FMC_MAJOR) {
pr_err("%s: \"%s\" has wrong major (has %li, expected %i)\n",
__func__, name, __FMC_MAJOR(version), FMC_MAJOR);
return -EINVAL;
}
if (__FMC_MINOR(version) != FMC_MINOR)
pr_info("%s: \"%s\" has wrong minor (has %li, expected %i)\n",
__func__, name, __FMC_MINOR(version), FMC_MINOR);
return 0;
}
static int fmc_uevent(struct device *dev, struct kobj_uevent_env *env)
{
/* struct fmc_device *fdev = to_fmc_device(dev); */
/* FIXME: The MODALIAS */
add_uevent_var(env, "MODALIAS=%s", "fmc");
return 0;
}
static int fmc_probe(struct device *dev)
{
struct fmc_driver *fdrv = to_fmc_driver(dev->driver);
struct fmc_device *fdev = to_fmc_device(dev);
return fdrv->probe(fdev);
}
static int fmc_remove(struct device *dev)
{
struct fmc_driver *fdrv = to_fmc_driver(dev->driver);
struct fmc_device *fdev = to_fmc_device(dev);
return fdrv->remove(fdev);
}
static void fmc_shutdown(struct device *dev)
{
/* not implemented but mandatory */
}
static struct bus_type fmc_bus_type = {
.name = "fmc",
.match = fmc_match,
.uevent = fmc_uevent,
.probe = fmc_probe,
.remove = fmc_remove,
.shutdown = fmc_shutdown,
};
static void fmc_release(struct device *dev)
{
struct fmc_device *fmc = container_of(dev, struct fmc_device, dev);
kfree(fmc);
}
/*
* The eeprom is exported in sysfs, through a binary attribute
*/
static ssize_t fmc_read_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;
int eelen;
dev = container_of(kobj, struct device, kobj);
fmc = container_of(dev, struct fmc_device, dev);
eelen = fmc->eeprom_len;
if (off > eelen)
return -ESPIPE;
if (off == eelen)
return 0; /* EOF */
if (off + count > eelen)
count = eelen - off;
memcpy(buf, fmc->eeprom + off, count);
return count;
}
static struct bin_attribute fmc_eeprom_attr = {
.attr = { .name = "eeprom", .mode = S_IRUGO, },
.size = 8192, /* more or less standard */
.read = fmc_read_eeprom,
};
/*
* Functions for client modules follow
*/
int fmc_driver_register(struct fmc_driver *drv)
{
if (fmc_check_version(drv->version, drv->driver.name))
return -EINVAL;
drv->driver.bus = &fmc_bus_type;
return driver_register(&drv->driver);
}
EXPORT_SYMBOL(fmc_driver_register);
void fmc_driver_unregister(struct fmc_driver *drv)
{
driver_unregister(&drv->driver);
}
EXPORT_SYMBOL(fmc_driver_unregister);
/*
* When a device set is registered, all eeproms must be read
* and all FRUs must be parsed
*/
int fmc_device_register_n(struct fmc_device **devs, int n)
{
struct fmc_device *fmc, **devarray;
uint32_t device_id;
int i, ret = 0;
if (n < 1)
return 0;
/* Check the version of the first data structure (function prints) */
if (fmc_check_version(devs[0]->version, devs[0]->carrier_name))
return -EINVAL;
devarray = kmemdup(devs, n * sizeof(*devs), GFP_KERNEL);
if (!devarray)
return -ENOMEM;
/* Make all other checks before continuing, for all devices */
for (i = 0; i < n; i++) {
fmc = devarray[i];
if (!fmc->hwdev) {
pr_err("%s: device nr. %i has no hwdev pointer\n",
__func__, i);
ret = -EINVAL;
break;
}
if (fmc->flags == FMC_DEVICE_NO_MEZZANINE) {
dev_info(fmc->hwdev, "absent mezzanine in slot %d\n",
fmc->slot_id);
continue;
}
if (!fmc->eeprom) {
dev_err(fmc->hwdev, "no eeprom provided for slot %i\n",
fmc->slot_id);
ret = -EINVAL;
}
if (!fmc->eeprom_addr) {
dev_err(fmc->hwdev, "no eeprom_addr for slot %i\n",
fmc->slot_id);
ret = -EINVAL;
}
if (!fmc->carrier_name || !fmc->carrier_data ||
!fmc->device_id) {
dev_err(fmc->hwdev,
"deivce nr %i: carrier name, "
"data or dev_id not set\n", i);
ret = -EINVAL;
}
if (ret)
break;
}
if (ret) {
kfree(devarray);
return ret;
}
/* Validation is ok. Now init and register the devices */
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;
device_initialize(&fmc->dev);
fmc->dev.release = fmc_release;
fmc->dev.parent = fmc->hwdev;
/* Fill the identification stuff (may fail) */
fmc_fill_id_info(fmc);
fmc->dev.bus = &fmc_bus_type;
/* Name from mezzanine info or carrier info. Or 0,1,2.. */
device_id = fmc->device_id;
if (!fmc->mezzanine_name)
dev_set_name(&fmc->dev, "fmc-%04x", device_id);
else
dev_set_name(&fmc->dev, "%s-%04x", fmc->mezzanine_name,
device_id);
ret = device_add(&fmc->dev);
if (ret < 0) {
dev_err(fmc->hwdev, "Slot %i: Failed in registering "
"\"%s\"\n", fmc->slot_id, fmc->dev.kobj.name);
goto out;
}
ret = sysfs_create_bin_file(&fmc->dev.kobj, &fmc_eeprom_attr);
if (ret < 0) {
dev_err(&fmc->dev, "Failed in registering eeprom\n");
goto out1;
}
/* This device went well, give information to the user */
fmc_dump_eeprom(fmc);
fmc_dump_sdb(fmc);
}
return 0;
out1:
device_del(&fmc->dev);
out:
fmc_free_id_info(fmc);
put_device(&fmc->dev);
kfree(devarray);
for (i--; i >= 0; i--) {
sysfs_remove_bin_file(&devs[i]->dev.kobj, &fmc_eeprom_attr);
device_del(&devs[i]->dev);
fmc_free_id_info(devs[i]);
put_device(&devs[i]->dev);
}
return ret;
}
EXPORT_SYMBOL(fmc_device_register_n);
int fmc_device_register(struct fmc_device *fmc)
{
return fmc_device_register_n(&fmc, 1);
}
EXPORT_SYMBOL(fmc_device_register);
void fmc_device_unregister_n(struct fmc_device **devs, int n)
{
int i;
if (n < 1)
return;
/* Free devarray first, not used by the later loop */
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]);
put_device(&devs[i]->dev);
}
}
EXPORT_SYMBOL(fmc_device_unregister_n);
void fmc_device_unregister(struct fmc_device *fmc)
{
fmc_device_unregister_n(&fmc, 1);
}
EXPORT_SYMBOL(fmc_device_unregister);
/* Init and exit are trivial */
static int fmc_init(void)
{
return bus_register(&fmc_bus_type);
}
static void fmc_exit(void)
{
bus_unregister(&fmc_bus_type);
}
module_init(fmc_init);
module_exit(fmc_exit);
MODULE_LICENSE("GPL");
/*
* Copyright (C) 2013 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
#include <linux/fmc.h>
#include <linux/fmc-sdb.h>
static int fmc_must_dump_eeprom;
module_param_named(dump_eeprom, fmc_must_dump_eeprom, int, 0644);
static int fmc_must_dump_sdb;
module_param_named(dump_sdb, fmc_must_dump_sdb, int, 0644);
#define LINELEN 16
/* Dumping 8k takes oh so much: avoid duplicate lines */
static const uint8_t *dump_line(int addr, const uint8_t *line,
const uint8_t *prev)
{
int i;
if (!prev || memcmp(line, prev, LINELEN)) {
pr_info("%04x: ", addr);
for (i = 0; i < LINELEN; ) {
printk(KERN_CONT "%02x", line[i]);
i++;
printk(i & 3 ? " " : i & (LINELEN - 1) ? " " : "\n");
}
return line;
}
/* repeated line */
if (line == prev + LINELEN)
pr_info("[...]\n");
return prev;
}
void fmc_dump_eeprom(const struct fmc_device *fmc)
{
const uint8_t *line, *prev;
int i;
if (!fmc_must_dump_eeprom)
return;
pr_info("FMC: %s (%s), slot %i, device %s\n", dev_name(fmc->hwdev),
fmc->carrier_name, fmc->slot_id, dev_name(&fmc->dev));
pr_info("FMC: dumping eeprom 0x%x (%i) bytes\n", fmc->eeprom_len,
fmc->eeprom_len);
line = fmc->eeprom;
prev = NULL;
for (i = 0; i < fmc->eeprom_len; i += LINELEN, line += LINELEN)
prev = dump_line(i, line, prev);
}
void fmc_dump_sdb(const struct fmc_device *fmc)
{
const uint8_t *line, *prev;
int i, len;
if (!fmc->sdb)
return;
if (!fmc_must_dump_sdb)
return;
/* If the argument is not-zero, do simple dump (== show) */
if (fmc_must_dump_sdb > 0)
fmc_show_sdb_tree(fmc);
if (fmc_must_dump_sdb == 1)
return;
/* If bigger than 1, dump it seriously, to help debugging */
/*
* Here we should really use libsdbfs (which is designed to
* work in kernel space as well) , but it doesn't support
* directories yet, and it requires better intergration (it
* should be used instead of fmc-specific code).
*
* So, lazily, just dump the top-level array
*/
pr_info("FMC: %s (%s), slot %i, device %s\n", dev_name(fmc->hwdev),
fmc->carrier_name, fmc->slot_id, dev_name(&fmc->dev));
pr_info("FMC: poor dump of sdb first level:\n");
len = fmc->sdb->len * sizeof(union sdb_record);
line = (void *)fmc->sdb->record;
prev = NULL;
for (i = 0; i < len; i += LINELEN, line += LINELEN)
prev = dump_line(i, line, prev);
return;
}
This diff is collapsed.
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fmc.h>
#include <linux/ipmi-fru.h>
/* The fru parser is both user and kernel capable: it needs alloc */
void *fru_alloc(size_t size)
{
return kzalloc(size, GFP_KERNEL);
}
/* The actual match function */
int fmc_match(struct device *dev, struct device_driver *drv)
{
struct fmc_driver *fdrv = to_fmc_driver(drv);
struct fmc_device *fdev = to_fmc_device(dev);
struct fmc_fru_id *fid;
int i, matched = 0;
/* This currently only matches the EEPROM (FRU id) */
fid = fdrv->id_table.fru_id;
if (!fid) {
dev_warn(&fdev->dev, "Driver has no ID: matches all\n");
matched = 1;
} else {
if (!fdev->id.manufacturer || !fdev->id.product_name)
return 0; /* the device has no FRU information */
for (i = 0; i < fdrv->id_table.fru_id_nr; i++, fid++) {
if (fid->manufacturer &&
strcmp(fid->manufacturer, fdev->id.manufacturer))
continue;
if (fid->product_name &&
strcmp(fid->product_name, fdev->id.product_name))
continue;
matched = 1;
break;
}
}
/* FIXME: match SDB contents */
return matched;
}
/* This function creates ID info for a newly registered device */
int fmc_fill_id_info(struct fmc_device *fmc)
{
struct fru_common_header *h;
struct fru_board_info_area *bia;
int ret, allocated = 0;
/* If we know the eeprom length, try to read it off the device */
if (fmc->eeprom_len && !fmc->eeprom) {
fmc->eeprom = kzalloc(fmc->eeprom_len, GFP_KERNEL);
if (!fmc->eeprom)
return -ENOMEM;
allocated = 1;
ret = fmc->op->read_ee(fmc, 0, fmc->eeprom, fmc->eeprom_len);
if (ret < 0)
goto out;
}
/* If no eeprom, continue with other matches */
if (!fmc->eeprom)
return 0;
dev_info(fmc->hwdev, "mezzanine %i\n", fmc->slot_id); /* header */
/* So we have the eeprom: parse the FRU part (if any) */
h = (void *)fmc->eeprom;
if (h->format != 1) {
pr_info(" EEPROM has no FRU information\n");
goto out;
}
if (!fru_header_cksum_ok(h)) {
pr_info(" FRU: wrong header checksum\n");
goto out;
}
bia = fru_get_board_area(h);
if (!fru_bia_cksum_ok(bia)) {
pr_info(" FRU: wrong board area checksum\n");
goto out;
}
fmc->id.manufacturer = fru_get_board_manufacturer(h);
fmc->id.product_name = fru_get_product_name(h);
pr_info(" Manufacturer: %s\n", fmc->id.manufacturer);
pr_info(" Product name: %s\n", fmc->id.product_name);
/* Create the short name (FIXME: look in sdb as well) */
fmc->mezzanine_name = kstrdup(fmc->id.product_name, GFP_KERNEL);
out:
if (allocated) {
kfree(fmc->eeprom);
fmc->eeprom = NULL;
}
return 0; /* no error: let other identification work */
}
/* Some ID data is allocated using fru_alloc() above, so release it */
void fmc_free_id_info(struct fmc_device *fmc)
{
kfree(fmc->mezzanine_name);
kfree(fmc->id.manufacturer);
kfree(fmc->id.product_name);
}
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/fmc.h>
#include <linux/sdb.h>
#include <linux/err.h>
#include <linux/fmc-sdb.h>
#include <asm/byteorder.h>
static uint32_t __sdb_rd(struct fmc_device *fmc, unsigned long address,
int convert)
{
uint32_t res = fmc_readl(fmc, address);
if (convert)
return __be32_to_cpu(res);
return res;
}
static struct sdb_array *__fmc_scan_sdb_tree(struct fmc_device *fmc,
unsigned long sdb_addr,
unsigned long reg_base, int level)
{
uint32_t onew;
int i, j, n, convert = 0;
struct sdb_array *arr, *sub;
onew = fmc_readl(fmc, sdb_addr);
if (onew == SDB_MAGIC) {
/* Uh! If we are little-endian, we must convert */
if (SDB_MAGIC != __be32_to_cpu(SDB_MAGIC))
convert = 1;
} else if (onew == __be32_to_cpu(SDB_MAGIC)) {
/* ok, don't convert */
} else {
return ERR_PTR(-ENOENT);
}
/* So, the magic was there: get the count from offset 4*/
onew = __sdb_rd(fmc, sdb_addr + 4, convert);
n = __be16_to_cpu(*(uint16_t *)&onew);
arr = kzalloc(sizeof(*arr), GFP_KERNEL);
if (!arr)
return ERR_PTR(-ENOMEM);
arr->record = kzalloc(sizeof(arr->record[0]) * n, GFP_KERNEL);
arr->subtree = kzalloc(sizeof(arr->subtree[0]) * n, GFP_KERNEL);
if (!arr->record || !arr->subtree) {
kfree(arr->record);
kfree(arr->subtree);
kfree(arr);
return ERR_PTR(-ENOMEM);
}
arr->len = n;
arr->level = level;
arr->fmc = fmc;
for (i = 0; i < n; i++) {
union sdb_record *r;
for (j = 0; j < sizeof(arr->record[0]); j += 4) {
*(uint32_t *)((void *)(arr->record + i) + j) =
__sdb_rd(fmc, sdb_addr + (i * 64) + j, convert);
}
r = &arr->record[i];
arr->subtree[i] = ERR_PTR(-ENODEV);
if (r->empty.record_type == sdb_type_bridge) {
struct sdb_component *c = &r->bridge.sdb_component;
uint64_t subaddr = __be64_to_cpu(r->bridge.sdb_child);
uint64_t newbase = __be64_to_cpu(c->addr_first);
subaddr += reg_base;
newbase += reg_base;
sub = __fmc_scan_sdb_tree(fmc, subaddr, newbase,
level + 1);
arr->subtree[i] = sub; /* may be error */
if (IS_ERR(sub))
continue;
sub->parent = arr;
sub->baseaddr = newbase;
}
}
return arr;
}
int fmc_scan_sdb_tree(struct fmc_device *fmc, unsigned long address)
{
struct sdb_array *ret;
if (fmc->sdb)
return -EBUSY;
ret = __fmc_scan_sdb_tree(fmc, address, 0 /* regs */, 0);
if (IS_ERR(ret))
return PTR_ERR(ret);
fmc->sdb = ret;
return 0;
}
EXPORT_SYMBOL(fmc_scan_sdb_tree);
static void __fmc_sdb_free(struct sdb_array *arr)
{
int i, n;
if (!arr)
return;
n = arr->len;
for (i = 0; i < n; i++) {
if (IS_ERR(arr->subtree[i]))
continue;
__fmc_sdb_free(arr->subtree[i]);
}
kfree(arr->record);
kfree(arr->subtree);
kfree(arr);
}
int fmc_free_sdb_tree(struct fmc_device *fmc)
{
__fmc_sdb_free(fmc->sdb);
fmc->sdb = NULL;
return 0;
}
EXPORT_SYMBOL(fmc_free_sdb_tree);
/* This helper calls reprogram and inizialized sdb as well */
int fmc_reprogram(struct fmc_device *fmc, struct fmc_driver *d, char *gw,
int sdb_entry)
{
int ret;
ret = fmc->op->reprogram(fmc, d, gw);
if (ret < 0)
return ret;
if (sdb_entry < 0)
return ret;
/* We are required to find SDB at a given offset */
ret = fmc_scan_sdb_tree(fmc, sdb_entry);
if (ret < 0) {
dev_err(&fmc->dev, "Can't find SDB at address 0x%x\n",
sdb_entry);
return -ENODEV;
}
fmc_dump_sdb(fmc);
return 0;
}
EXPORT_SYMBOL(fmc_reprogram);
static void __fmc_show_sdb_tree(const struct fmc_device *fmc,
const struct sdb_array *arr)
{
int i, j, n = arr->len, level = arr->level;
const struct sdb_array *ap;
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++)
printk(KERN_CONT " ");
switch (r->empty.record_type) {
case sdb_type_interconnect:
printk(KERN_CONT "%08llx:%08x %.19s\n",
__be64_to_cpu(p->vendor_id),
__be32_to_cpu(p->device_id),
p->name);
break;
case sdb_type_device:
printk(KERN_CONT "%08llx:%08x %.19s (%08llx-%08llx)\n",
__be64_to_cpu(p->vendor_id),
__be32_to_cpu(p->device_id),
p->name,
__be64_to_cpu(c->addr_first) + base,
__be64_to_cpu(c->addr_last) + base);
break;
case sdb_type_bridge:
printk(KERN_CONT "%08llx:%08x %.19s (bridge: %08llx)\n",
__be64_to_cpu(p->vendor_id),
__be32_to_cpu(p->device_id),
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]));
break;
}
__fmc_show_sdb_tree(fmc, arr->subtree[i]);
break;
case sdb_type_integration:
printk(KERN_CONT "integration\n");
break;
case sdb_type_repo_url:
printk(KERN_CONT "repo-url\n");
break;
case sdb_type_synthesis:
printk(KERN_CONT "synthesis-info\n");
break;
case sdb_type_empty:
printk(KERN_CONT "empty\n");
break;
default:
printk(KERN_CONT "UNKNOWN TYPE 0x%02x\n",
r->empty.record_type);
break;
}
}
}
void fmc_show_sdb_tree(const struct fmc_device *fmc)
{
if (!fmc->sdb)
return;
__fmc_show_sdb_tree(fmc, fmc->sdb);
}
EXPORT_SYMBOL(fmc_show_sdb_tree);
signed long fmc_find_sdb_device(struct sdb_array *tree,
uint64_t vid, uint32_t did, unsigned long *sz)
{
signed long res = -ENODEV;
union sdb_record *r;
struct sdb_product *p;
struct sdb_component *c;
int i, n = tree->len;
uint64_t last, first;
/* FIXME: what if the first interconnect is not at zero? */
for (i = 0; i < n; i++) {
r = &tree->record[i];
c = &r->dev.sdb_component;
p = &c->product;
if (!IS_ERR(tree->subtree[i]))
res = fmc_find_sdb_device(tree->subtree[i],
vid, did, sz);
if (res >= 0)
return res + tree->baseaddr;
if (r->empty.record_type != sdb_type_device)
continue;
if (__be64_to_cpu(p->vendor_id) != vid)
continue;
if (__be32_to_cpu(p->device_id) != did)
continue;
/* found */
last = __be64_to_cpu(c->addr_last);
first = __be64_to_cpu(c->addr_first);
if (sz)
*sz = (typeof(*sz))(last + 1 - first);
return first + tree->baseaddr;
}
return res;
}
EXPORT_SYMBOL(fmc_find_sdb_device);
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* The software is provided "as is"; the copyright holders disclaim
* all warranties and liabilities, to the extent permitted by
* applicable law.
*/
/* A trivial fmc driver that can load a gateware file and reports interrupts */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/fmc.h>
static struct fmc_driver t_drv; /* initialized later */
static irqreturn_t t_handler(int irq, void *dev_id)
{
struct fmc_device *fmc = dev_id;
fmc->op->irq_ack(fmc);
dev_info(&fmc->dev, "received irq %i\n", irq);
return IRQ_HANDLED;
}
static struct fmc_gpio t_gpio[] = {
{
.gpio = FMC_GPIO_IRQ(0),
.mode = GPIOF_DIR_IN,
.irqmode = IRQF_TRIGGER_RISING,
}, {
.gpio = FMC_GPIO_IRQ(1),
.mode = GPIOF_DIR_IN,
.irqmode = IRQF_TRIGGER_RISING,
}
};
static int t_probe(struct fmc_device *fmc)
{
int ret;
int index = 0;
if (fmc->op->validate)
index = fmc->op->validate(fmc, &t_drv);
if (index < 0)
return -EINVAL; /* not our device: invalid */
ret = fmc->op->irq_request(fmc, t_handler, "fmc-trivial", IRQF_SHARED);
if (ret < 0)
return ret;
/* ignore error code of call below, we really don't care */
fmc->op->gpio_config(fmc, t_gpio, ARRAY_SIZE(t_gpio));
/* Reprogram, if asked to. ESRCH == no filename specified */
ret = -ESRCH;
if (fmc->op->reprogram)
ret = fmc->op->reprogram(fmc, &t_drv, "");
if (ret == -ESRCH)
ret = 0;
if (ret < 0)
fmc->op->irq_free(fmc);
/* FIXME: reprogram LM32 too */
return ret;
}
static int t_remove(struct fmc_device *fmc)
{
fmc->op->irq_free(fmc);
return 0;
}
static struct fmc_driver t_drv = {
.version = FMC_VERSION,
.driver.name = KBUILD_MODNAME,
.probe = t_probe,
.remove = t_remove,
/* no table, as the current match just matches everything */
};
/* We accept the generic parameters */
FMC_PARAM_BUSID(t_drv);
FMC_PARAM_GATEWARE(t_drv);
static int t_init(void)
{
int ret;
ret = fmc_driver_register(&t_drv);
return ret;
}
static void t_exit(void)
{
fmc_driver_unregister(&t_drv);
}
module_init(t_init);
module_exit(t_exit);
MODULE_LICENSE("Dual BSD/GPL");
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <linux/module.h>
#include <linux/string.h>
#include <linux/firmware.h>
#include <linux/init.h>
#include <linux/fmc.h>
#include <asm/unaligned.h>
/*
* This module uses the firmware loader to program the whole or part
* of the FMC eeprom. The meat is in the _run functions. However, no
* default file name is provided, to avoid accidental mishaps. Also,
* you must pass the busid argument
*/
static struct fmc_driver fwe_drv;
FMC_PARAM_BUSID(fwe_drv);
/* The "file=" is like the generic "gateware=" used elsewhere */
static char *fwe_file[FMC_MAX_CARDS];
static int fwe_file_n;
module_param_array_named(file, fwe_file, charp, &fwe_file_n, 444);
static int fwe_run_tlv(struct fmc_device *fmc, const struct firmware *fw,
int write)
{
const uint8_t *p = fw->data;
int len = fw->size;
uint16_t thislen, thisaddr;
int err;
/* format is: 'w' addr16 len16 data... */
while (len > 5) {
thisaddr = get_unaligned_le16(p+1);
thislen = get_unaligned_le16(p+3);
if (p[0] != 'w' || thislen + 5 > len) {
dev_err(&fmc->dev, "invalid tlv at offset %ti\n",
p - fw->data);
return -EINVAL;
}
err = 0;
if (write) {
dev_info(&fmc->dev, "write %i bytes at 0x%04x\n",
thislen, thisaddr);
err = fmc->op->write_ee(fmc, thisaddr, p + 5, thislen);
}
if (err < 0) {
dev_err(&fmc->dev, "write failure @0x%04x\n",
thisaddr);
return err;
}
p += 5 + thislen;
len -= 5 + thislen;
}
if (write)
dev_info(&fmc->dev, "write_eeprom: success\n");
return 0;
}
static int fwe_run_bin(struct fmc_device *fmc, const struct firmware *fw)
{
int ret;
dev_info(&fmc->dev, "programming %zi bytes\n", fw->size);
ret = fmc->op->write_ee(fmc, 0, (void *)fw->data, fw->size);
if (ret < 0) {
dev_info(&fmc->dev, "write_eeprom: error %i\n", ret);
return ret;
}
dev_info(&fmc->dev, "write_eeprom: success\n");
return 0;
}
static int fwe_run(struct fmc_device *fmc, const struct firmware *fw, char *s)
{
char *last4 = s + strlen(s) - 4;
int err;
if (!strcmp(last4, ".bin"))
return fwe_run_bin(fmc, fw);
if (!strcmp(last4, ".tlv")) {
err = fwe_run_tlv(fmc, fw, 0);
if (!err)
err = fwe_run_tlv(fmc, fw, 1);
return err;
}
dev_err(&fmc->dev, "invalid file name \"%s\"\n", s);
return -EINVAL;
}
/*
* Programming is done at probe time. Morever, only those listed with
* busid= are programmed.
* card is probed for, only one is programmed. Unfortunately, it's
* difficult to know in advance when probing the first card if others
* are there.
*/
int fwe_probe(struct fmc_device *fmc)
{
int err, index = 0;
const struct firmware *fw;
struct device *dev = &fmc->dev;
char *s;
if (!fwe_drv.busid_n) {
dev_err(dev, "%s: no busid passed, refusing all cards\n",
KBUILD_MODNAME);
return -ENODEV;
}
if (fmc->op->validate)
index = fmc->op->validate(fmc, &fwe_drv);
if (index < 0) {
pr_err("%s: refusing device \"%s\"\n", KBUILD_MODNAME,
dev_name(dev));
return -ENODEV;
}
if (index >= fwe_file_n) {
pr_err("%s: no filename for device index %i\n",
KBUILD_MODNAME, index);
return -ENODEV;
}
s = fwe_file[index];
if (!s) {
pr_err("%s: no filename for \"%s\" not programming\n",
KBUILD_MODNAME, dev_name(dev));
return -ENOENT;
}
err = request_firmware(&fw, s, dev);
if (err < 0) {
dev_err(&fmc->dev, "request firmware \"%s\": error %i\n",
s, err);
return err;
}
fwe_run(fmc, fw, s);
release_firmware(fw);
return 0;
}
int fwe_remove(struct fmc_device *fmc)
{
return 0;
}
static struct fmc_driver fwe_drv = {
.version = FMC_VERSION,
.driver.name = KBUILD_MODNAME,
.probe = fwe_probe,
.remove = fwe_remove,
/* no table, as the current match just matches everything */
};
static int fwe_init(void)
{
int ret;
ret = fmc_driver_register(&fwe_drv);
return ret;
}
static void fwe_exit(void)
{
fmc_driver_unregister(&fwe_drv);
}
module_init(fwe_init);
module_exit(fwe_exit);
MODULE_LICENSE("GPL");
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <linux/ipmi-fru.h>
/* Some internal helpers */
static struct fru_type_length *
__fru_get_board_tl(struct fru_common_header *header, int nr)
{
struct fru_board_info_area *bia;
struct fru_type_length *tl;
bia = fru_get_board_area(header);
tl = bia->tl;
while (nr > 0 && !fru_is_eof(tl)) {
tl = fru_next_tl(tl);
nr--;
}
if (fru_is_eof(tl))
return NULL;
return tl;
}
static char *__fru_alloc_get_tl(struct fru_common_header *header, int nr)
{
struct fru_type_length *tl;
char *res;
int len;
tl = __fru_get_board_tl(header, nr);
if (!tl)
return NULL;
len = fru_strlen(tl);
res = fru_alloc(fru_strlen(tl) + 1);
if (!res)
return NULL;
return fru_strcpy(res, tl);
}
/* Public checksum verifiers */
int fru_header_cksum_ok(struct fru_common_header *header)
{
uint8_t *ptr = (void *)header;
int i, sum;
for (i = sum = 0; i < sizeof(*header); i++)
sum += ptr[i];
return (sum & 0xff) == 0;
}
int fru_bia_cksum_ok(struct fru_board_info_area *bia)
{
uint8_t *ptr = (void *)bia;
int i, sum;
for (i = sum = 0; i < 8 * bia->area_len; i++)
sum += ptr[i];
return (sum & 0xff) == 0;
}
/* Get various stuff, trivial */
char *fru_get_board_manufacturer(struct fru_common_header *header)
{
return __fru_alloc_get_tl(header, 0);
}
char *fru_get_product_name(struct fru_common_header *header)
{
return __fru_alloc_get_tl(header, 1);
}
char *fru_get_serial_number(struct fru_common_header *header)
{
return __fru_alloc_get_tl(header, 2);
}
char *fru_get_part_number(struct fru_common_header *header)
{
return __fru_alloc_get_tl(header, 3);
}
......@@ -116,6 +116,15 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
unsigned long flags;
int ret, t, err = 0;
spin_lock_irqsave(&newchannel->sc_lock, flags);
if (newchannel->state == CHANNEL_OPEN_STATE) {
newchannel->state = CHANNEL_OPENING_STATE;
} else {
spin_unlock_irqrestore(&newchannel->sc_lock, flags);
return -EINVAL;
}
spin_unlock_irqrestore(&newchannel->sc_lock, flags);
newchannel->onchannel_callback = onchannelcallback;
newchannel->channel_callback_context = context;
......@@ -216,6 +225,9 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
list_del(&open_info->msglistentry);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
if (err == 0)
newchannel->state = CHANNEL_OPENED_STATE;
kfree(open_info);
return err;
......@@ -500,15 +512,14 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle)
}
EXPORT_SYMBOL_GPL(vmbus_teardown_gpadl);
/*
* vmbus_close - Close the specified channel
*/
void vmbus_close(struct vmbus_channel *channel)
static void vmbus_close_internal(struct vmbus_channel *channel)
{
struct vmbus_channel_close_channel *msg;
int ret;
unsigned long flags;
channel->state = CHANNEL_OPEN_STATE;
channel->sc_creation_callback = NULL;
/* Stop callback and cancel the timer asap */
spin_lock_irqsave(&channel->inbound_lock, flags);
channel->onchannel_callback = NULL;
......@@ -538,6 +549,37 @@ void vmbus_close(struct vmbus_channel *channel)
}
/*
* vmbus_close - Close the specified channel
*/
void vmbus_close(struct vmbus_channel *channel)
{
struct list_head *cur, *tmp;
struct vmbus_channel *cur_channel;
if (channel->primary_channel != NULL) {
/*
* We will only close sub-channels when
* the primary is closed.
*/
return;
}
/*
* Close all the sub-channels first and then close the
* primary channel.
*/
list_for_each_safe(cur, tmp, &channel->sc_list) {
cur_channel = list_entry(cur, struct vmbus_channel, sc_list);
if (cur_channel->state != CHANNEL_OPENED_STATE)
continue;
vmbus_close_internal(cur_channel);
}
/*
* Now close the primary.
*/
vmbus_close_internal(channel);
}
EXPORT_SYMBOL_GPL(vmbus_close);
/**
......
......@@ -115,6 +115,9 @@ static struct vmbus_channel *alloc_channel(void)
return NULL;
spin_lock_init(&channel->inbound_lock);
spin_lock_init(&channel->sc_lock);
INIT_LIST_HEAD(&channel->sc_list);
channel->controlwq = create_workqueue("hv_vmbus_ctl");
if (!channel->controlwq) {
......@@ -166,6 +169,7 @@ static void vmbus_process_rescind_offer(struct work_struct *work)
struct vmbus_channel,
work);
unsigned long flags;
struct vmbus_channel *primary_channel;
struct vmbus_channel_relid_released msg;
vmbus_device_unregister(channel->device_obj);
......@@ -174,9 +178,16 @@ static void vmbus_process_rescind_offer(struct work_struct *work)
msg.header.msgtype = CHANNELMSG_RELID_RELEASED;
vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released));
if (channel->primary_channel == NULL) {
spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
list_del(&channel->listentry);
spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
} else {
primary_channel = channel->primary_channel;
spin_lock_irqsave(&primary_channel->sc_lock, flags);
list_del(&channel->listentry);
spin_unlock_irqrestore(&primary_channel->sc_lock, flags);
}
free_channel(channel);
}
......@@ -228,6 +239,24 @@ static void vmbus_process_offer(struct work_struct *work)
spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
if (!fnew) {
/*
* Check to see if this is a sub-channel.
*/
if (newchannel->offermsg.offer.sub_channel_index != 0) {
/*
* Process the sub-channel.
*/
newchannel->primary_channel = channel;
spin_lock_irqsave(&channel->sc_lock, flags);
list_add_tail(&newchannel->sc_list, &channel->sc_list);
spin_unlock_irqrestore(&channel->sc_lock, flags);
newchannel->state = CHANNEL_OPEN_STATE;
if (channel->sc_creation_callback != NULL)
channel->sc_creation_callback(newchannel);
return;
}
free_channel(newchannel);
return;
}
......@@ -685,4 +714,86 @@ int vmbus_request_offers(void)
return ret;
}
/* eof */
/*
* Retrieve the (sub) channel on which to send an outgoing request.
* When a primary channel has multiple sub-channels, we choose a
* channel whose VCPU binding is closest to the VCPU on which
* this call is being made.
*/
struct vmbus_channel *vmbus_get_outgoing_channel(struct vmbus_channel *primary)
{
struct list_head *cur, *tmp;
int cur_cpu = hv_context.vp_index[smp_processor_id()];
struct vmbus_channel *cur_channel;
struct vmbus_channel *outgoing_channel = primary;
int cpu_distance, new_cpu_distance;
if (list_empty(&primary->sc_list))
return outgoing_channel;
list_for_each_safe(cur, tmp, &primary->sc_list) {
cur_channel = list_entry(cur, struct vmbus_channel, sc_list);
if (cur_channel->state != CHANNEL_OPENED_STATE)
continue;
if (cur_channel->target_vp == cur_cpu)
return cur_channel;
cpu_distance = ((outgoing_channel->target_vp > cur_cpu) ?
(outgoing_channel->target_vp - cur_cpu) :
(cur_cpu - outgoing_channel->target_vp));
new_cpu_distance = ((cur_channel->target_vp > cur_cpu) ?
(cur_channel->target_vp - cur_cpu) :
(cur_cpu - cur_channel->target_vp));
if (cpu_distance < new_cpu_distance)
continue;
outgoing_channel = cur_channel;
}
return outgoing_channel;
}
EXPORT_SYMBOL_GPL(vmbus_get_outgoing_channel);
static void invoke_sc_cb(struct vmbus_channel *primary_channel)
{
struct list_head *cur, *tmp;
struct vmbus_channel *cur_channel;
if (primary_channel->sc_creation_callback == NULL)
return;
list_for_each_safe(cur, tmp, &primary_channel->sc_list) {
cur_channel = list_entry(cur, struct vmbus_channel, sc_list);
primary_channel->sc_creation_callback(cur_channel);
}
}
void vmbus_set_sc_create_callback(struct vmbus_channel *primary_channel,
void (*sc_cr_cb)(struct vmbus_channel *new_sc))
{
primary_channel->sc_creation_callback = sc_cr_cb;
}
EXPORT_SYMBOL_GPL(vmbus_set_sc_create_callback);
bool vmbus_are_subchannels_present(struct vmbus_channel *primary)
{
bool ret;
ret = !list_empty(&primary->sc_list);
if (ret) {
/*
* Invoke the callback on sub-channel creation.
* This will present a uniform interface to the
* clients.
*/
invoke_sc_cb(primary);
}
return ret;
}
EXPORT_SYMBOL_GPL(vmbus_are_subchannels_present);
......@@ -246,12 +246,26 @@ struct vmbus_channel *relid2channel(u32 relid)
struct vmbus_channel *channel;
struct vmbus_channel *found_channel = NULL;
unsigned long flags;
struct list_head *cur, *tmp;
struct vmbus_channel *cur_sc;
spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
if (channel->offermsg.child_relid == relid) {
found_channel = channel;
break;
} else if (!list_empty(&channel->sc_list)) {
/*
* Deal with sub-channels.
*/
list_for_each_safe(cur, tmp, &channel->sc_list) {
cur_sc = list_entry(cur, struct vmbus_channel,
sc_list);
if (cur_sc->offermsg.child_relid == relid) {
found_channel = cur_sc;
break;
}
}
}
}
spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
......
......@@ -265,6 +265,59 @@ u16 hv_signal_event(void *con_id)
return status;
}
int hv_synic_alloc(void)
{
size_t size = sizeof(struct tasklet_struct);
int cpu;
for_each_online_cpu(cpu) {
hv_context.event_dpc[cpu] = kmalloc(size, GFP_ATOMIC);
if (hv_context.event_dpc[cpu] == NULL) {
pr_err("Unable to allocate event dpc\n");
goto err;
}
tasklet_init(hv_context.event_dpc[cpu], vmbus_on_event, cpu);
hv_context.synic_message_page[cpu] =
(void *)get_zeroed_page(GFP_ATOMIC);
if (hv_context.synic_message_page[cpu] == NULL) {
pr_err("Unable to allocate SYNIC message page\n");
goto err;
}
hv_context.synic_event_page[cpu] =
(void *)get_zeroed_page(GFP_ATOMIC);
if (hv_context.synic_event_page[cpu] == NULL) {
pr_err("Unable to allocate SYNIC event page\n");
goto err;
}
}
return 0;
err:
return -ENOMEM;
}
void hv_synic_free_cpu(int cpu)
{
kfree(hv_context.event_dpc[cpu]);
if (hv_context.synic_message_page[cpu])
free_page((unsigned long)hv_context.synic_event_page[cpu]);
if (hv_context.synic_message_page[cpu])
free_page((unsigned long)hv_context.synic_message_page[cpu]);
}
void hv_synic_free(void)
{
int cpu;
for_each_online_cpu(cpu)
hv_synic_free_cpu(cpu);
}
/*
* hv_synic_init - Initialize the Synthethic Interrupt Controller.
*
......@@ -289,30 +342,6 @@ void hv_synic_init(void *arg)
/* Check the version */
rdmsrl(HV_X64_MSR_SVERSION, version);
hv_context.event_dpc[cpu] = kmalloc(sizeof(struct tasklet_struct),
GFP_ATOMIC);
if (hv_context.event_dpc[cpu] == NULL) {
pr_err("Unable to allocate event dpc\n");
goto cleanup;
}
tasklet_init(hv_context.event_dpc[cpu], vmbus_on_event, cpu);
hv_context.synic_message_page[cpu] =
(void *)get_zeroed_page(GFP_ATOMIC);
if (hv_context.synic_message_page[cpu] == NULL) {
pr_err("Unable to allocate SYNIC message page\n");
goto cleanup;
}
hv_context.synic_event_page[cpu] =
(void *)get_zeroed_page(GFP_ATOMIC);
if (hv_context.synic_event_page[cpu] == NULL) {
pr_err("Unable to allocate SYNIC event page\n");
goto cleanup;
}
/* Setup the Synic's message page */
rdmsrl(HV_X64_MSR_SIMP, simp.as_uint64);
simp.simp_enabled = 1;
......@@ -355,14 +384,6 @@ void hv_synic_init(void *arg)
rdmsrl(HV_X64_MSR_VP_INDEX, vp_index);
hv_context.vp_index[cpu] = (u32)vp_index;
return;
cleanup:
if (hv_context.synic_event_page[cpu])
free_page((unsigned long)hv_context.synic_event_page[cpu]);
if (hv_context.synic_message_page[cpu])
free_page((unsigned long)hv_context.synic_message_page[cpu]);
return;
}
/*
......
......@@ -527,6 +527,10 @@ extern int hv_post_message(union hv_connection_id connection_id,
extern u16 hv_signal_event(void *con_id);
extern int hv_synic_alloc(void);
extern void hv_synic_free(void);
extern void hv_synic_init(void *irqarg);
extern void hv_synic_cleanup(void *arg);
......
......@@ -32,7 +32,7 @@
void hv_begin_read(struct hv_ring_buffer_info *rbi)
{
rbi->ring_buffer->interrupt_mask = 1;
smp_mb();
mb();
}
u32 hv_end_read(struct hv_ring_buffer_info *rbi)
......@@ -41,7 +41,7 @@ u32 hv_end_read(struct hv_ring_buffer_info *rbi)
u32 write;
rbi->ring_buffer->interrupt_mask = 0;
smp_mb();
mb();
/*
* Now check to see if the ring buffer is still empty.
......@@ -71,10 +71,12 @@ u32 hv_end_read(struct hv_ring_buffer_info *rbi)
static bool hv_need_to_signal(u32 old_write, struct hv_ring_buffer_info *rbi)
{
smp_mb();
mb();
if (rbi->ring_buffer->interrupt_mask)
return false;
/* check interrupt_mask before read_index */
rmb();
/*
* This is the only case we need to signal when the
* ring transitions from being empty to non-empty.
......@@ -442,7 +444,7 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
sizeof(u64));
/* Issue a full memory barrier before updating the write index */
smp_mb();
mb();
/* Now, update the write location */
hv_set_next_write_location(outring_info, next_write_location);
......@@ -549,7 +551,7 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer,
/* Make sure all reads are done before we update the read index since */
/* the writer may start writing to the read area once the read index */
/*is updated */
smp_mb();
mb();
/* Update the read index */
hv_set_next_read_location(inring_info, next_read_location);
......
......@@ -434,7 +434,7 @@ static void vmbus_on_msg_dpc(unsigned long data)
* will not deliver any more messages since there is
* no empty slot
*/
smp_mb();
mb();
if (msg->header.message_flags.msg_pending) {
/*
......@@ -563,6 +563,9 @@ static int vmbus_bus_init(int irq)
*/
hv_register_vmbus_handler(irq, vmbus_isr);
ret = hv_synic_alloc();
if (ret)
goto err_alloc;
/*
* Initialize the per-cpu interrupt state and
* connect to the host.
......@@ -570,13 +573,14 @@ static int vmbus_bus_init(int irq)
on_each_cpu(hv_synic_init, NULL, 1);
ret = vmbus_connect();
if (ret)
goto err_irq;
goto err_alloc;
vmbus_request_offers();
return 0;
err_irq:
err_alloc:
hv_synic_free();
free_irq(irq, hv_acpi_dev);
err_unregister:
......
......@@ -20,6 +20,16 @@ config TI_EMIF
parameters and other settings during frequency, voltage and
temperature changes
config MVEBU_DEVBUS
bool "Marvell EBU Device Bus Controller"
default y
depends on PLAT_ORION && OF
help
This driver is for the Device Bus controller available in some
Marvell EBU SoCs such as Discovery (mv78xx0), Orion (88f5xxx) and
Armada 370 and Armada XP. This controller allows to handle flash
devices such as NOR, NAND, SRAM, and FPGA.
config TEGRA20_MC
bool "Tegra20 Memory Controller(MC) driver"
default y
......
......@@ -6,5 +6,6 @@ ifeq ($(CONFIG_DDR),y)
obj-$(CONFIG_OF) += of_memory.o
endif
obj-$(CONFIG_TI_EMIF) += emif.o
obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o
obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o
obj-$(CONFIG_TEGRA30_MC) += tegra30-mc.o
/*
* Marvell EBU SoC Device Bus Controller
* (memory controller for NOR/NAND/SRAM/FPGA devices)
*
* Copyright (C) 2013 Marvell
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/mbus.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
/* Register definitions */
#define DEV_WIDTH_BIT 30
#define BADR_SKEW_BIT 28
#define RD_HOLD_BIT 23
#define ACC_NEXT_BIT 17
#define RD_SETUP_BIT 12
#define ACC_FIRST_BIT 6
#define SYNC_ENABLE_BIT 24
#define WR_HIGH_BIT 16
#define WR_LOW_BIT 8
#define READ_PARAM_OFFSET 0x0
#define WRITE_PARAM_OFFSET 0x4
static const char * const devbus_wins[] = {
"devbus-boot",
"devbus-cs0",
"devbus-cs1",
"devbus-cs2",
"devbus-cs3",
};
struct devbus_read_params {
u32 bus_width;
u32 badr_skew;
u32 turn_off;
u32 acc_first;
u32 acc_next;
u32 rd_setup;
u32 rd_hold;
};
struct devbus_write_params {
u32 sync_enable;
u32 wr_high;
u32 wr_low;
u32 ale_wr;
};
struct devbus {
struct device *dev;
void __iomem *base;
unsigned long tick_ps;
};
static int get_timing_param_ps(struct devbus *devbus,
struct device_node *node,
const char *name,
u32 *ticks)
{
u32 time_ps;
int err;
err = of_property_read_u32(node, name, &time_ps);
if (err < 0) {
dev_err(devbus->dev, "%s has no '%s' property\n",
name, node->full_name);
return err;
}
*ticks = (time_ps + devbus->tick_ps - 1) / devbus->tick_ps;
dev_dbg(devbus->dev, "%s: %u ps -> 0x%x\n",
name, time_ps, *ticks);
return 0;
}
static int devbus_set_timing_params(struct devbus *devbus,
struct device_node *node)
{
struct devbus_read_params r;
struct devbus_write_params w;
u32 value;
int err;
dev_dbg(devbus->dev, "Setting timing parameter, tick is %lu ps\n",
devbus->tick_ps);
/* Get read timings */
err = of_property_read_u32(node, "devbus,bus-width", &r.bus_width);
if (err < 0) {
dev_err(devbus->dev,
"%s has no 'devbus,bus-width' property\n",
node->full_name);
return err;
}
/* Convert bit width to byte width */
r.bus_width /= 8;
err = get_timing_param_ps(devbus, node, "devbus,badr-skew-ps",
&r.badr_skew);
if (err < 0)
return err;
err = get_timing_param_ps(devbus, node, "devbus,turn-off-ps",
&r.turn_off);
if (err < 0)
return err;
err = get_timing_param_ps(devbus, node, "devbus,acc-first-ps",
&r.acc_first);
if (err < 0)
return err;
err = get_timing_param_ps(devbus, node, "devbus,acc-next-ps",
&r.acc_next);
if (err < 0)
return err;
err = get_timing_param_ps(devbus, node, "devbus,rd-setup-ps",
&r.rd_setup);
if (err < 0)
return err;
err = get_timing_param_ps(devbus, node, "devbus,rd-hold-ps",
&r.rd_hold);
if (err < 0)
return err;
/* Get write timings */
err = of_property_read_u32(node, "devbus,sync-enable",
&w.sync_enable);
if (err < 0) {
dev_err(devbus->dev,
"%s has no 'devbus,sync-enable' property\n",
node->full_name);
return err;
}
err = get_timing_param_ps(devbus, node, "devbus,ale-wr-ps",
&w.ale_wr);
if (err < 0)
return err;
err = get_timing_param_ps(devbus, node, "devbus,wr-low-ps",
&w.wr_low);
if (err < 0)
return err;
err = get_timing_param_ps(devbus, node, "devbus,wr-high-ps",
&w.wr_high);
if (err < 0)
return err;
/* Set read timings */
value = r.bus_width << DEV_WIDTH_BIT |
r.badr_skew << BADR_SKEW_BIT |
r.rd_hold << RD_HOLD_BIT |
r.acc_next << ACC_NEXT_BIT |
r.rd_setup << RD_SETUP_BIT |
r.acc_first << ACC_FIRST_BIT |
r.turn_off;
dev_dbg(devbus->dev, "read parameters register 0x%p = 0x%x\n",
devbus->base + READ_PARAM_OFFSET,
value);
writel(value, devbus->base + READ_PARAM_OFFSET);
/* Set write timings */
value = w.sync_enable << SYNC_ENABLE_BIT |
w.wr_low << WR_LOW_BIT |
w.wr_high << WR_HIGH_BIT |
w.ale_wr;
dev_dbg(devbus->dev, "write parameters register: 0x%p = 0x%x\n",
devbus->base + WRITE_PARAM_OFFSET,
value);
writel(value, devbus->base + WRITE_PARAM_OFFSET);
return 0;
}
static int mvebu_devbus_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = pdev->dev.of_node;
struct device_node *parent;
struct devbus *devbus;
struct resource *res;
struct clk *clk;
unsigned long rate;
const __be32 *ranges;
int err, cs;
int addr_cells, p_addr_cells, size_cells;
int ranges_len, tuple_len;
u32 base, size;
devbus = devm_kzalloc(&pdev->dev, sizeof(struct devbus), GFP_KERNEL);
if (!devbus)
return -ENOMEM;
devbus->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
devbus->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(devbus->base))
return PTR_ERR(devbus->base);
clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(clk))
return PTR_ERR(clk);
clk_prepare_enable(clk);
/*
* Obtain clock period in picoseconds,
* we need this in order to convert timing
* parameters from cycles to picoseconds.
*/
rate = clk_get_rate(clk) / 1000;
devbus->tick_ps = 1000000000 / rate;
/* Read the device tree node and set the new timing parameters */
err = devbus_set_timing_params(devbus, node);
if (err < 0)
return err;
/*
* Allocate an address window for this device.
* If the device probing fails, then we won't be able to
* remove the allocated address decoding window.
*
* FIXME: This is only a temporary hack! We need to do this here
* because we still don't have device tree bindings for mbus.
* Once that support is added, we will declare these address windows
* statically in the device tree, and remove the window configuration
* from here.
*/
/*
* Get the CS to choose the window string.
* This is a bit hacky, but it will be removed once the
* address windows are declared in the device tree.
*/
cs = (((unsigned long)devbus->base) % 0x400) / 8;
/*
* Parse 'ranges' property to obtain a (base,size) window tuple.
* This will be removed once the address windows
* are declared in the device tree.
*/
parent = of_get_parent(node);
if (!parent)
return -EINVAL;
p_addr_cells = of_n_addr_cells(parent);
of_node_put(parent);
addr_cells = of_n_addr_cells(node);
size_cells = of_n_size_cells(node);
tuple_len = (p_addr_cells + addr_cells + size_cells) * sizeof(__be32);
ranges = of_get_property(node, "ranges", &ranges_len);
if (ranges == NULL || ranges_len != tuple_len)
return -EINVAL;
base = of_translate_address(node, ranges + addr_cells);
if (base == OF_BAD_ADDR)
return -EINVAL;
size = of_read_number(ranges + addr_cells + p_addr_cells, size_cells);
/*
* Create an mbus address windows.
* FIXME: Remove this, together with the above code, once the
* address windows are declared in the device tree.
*/
err = mvebu_mbus_add_window(devbus_wins[cs], base, size);
if (err < 0)
return err;
/*
* We need to create a child device explicitly from here to
* guarantee that the child will be probed after the timing
* parameters for the bus are written.
*/
err = of_platform_populate(node, NULL, NULL, dev);
if (err < 0) {
mvebu_mbus_del_window(base, size);
return err;
}
return 0;
}
static const struct of_device_id mvebu_devbus_of_match[] = {
{ .compatible = "marvell,mvebu-devbus" },
{},
};
MODULE_DEVICE_TABLE(of, mvebu_devbus_of_match);
static struct platform_driver mvebu_devbus_driver = {
.probe = mvebu_devbus_probe,
.driver = {
.name = "mvebu-devbus",
.owner = THIS_MODULE,
.of_match_table = mvebu_devbus_of_match,
},
};
static int __init mvebu_devbus_init(void)
{
return platform_driver_register(&mvebu_devbus_driver);
}
module_init(mvebu_devbus_init);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Ezequiel Garcia <ezequiel.garcia@free-electrons.com>");
MODULE_DESCRIPTION("Marvell EBU SoC Device Bus controller");
......@@ -193,8 +193,11 @@ static irqreturn_t tegra20_mc_isr(int irq, void *data)
mask &= stat;
if (!mask)
return IRQ_NONE;
while ((bit = ffs(mask)) != 0)
while ((bit = ffs(mask)) != 0) {
tegra20_mc_decode(mc, bit - 1);
mask &= ~BIT(bit - 1);
}
mc_writel(mc, stat, MC_INTSTATUS);
return IRQ_HANDLED;
}
......
......@@ -218,7 +218,7 @@ static void tegra30_mc_decode(struct tegra30_mc *mc, int n)
return;
}
err = readl(mc + MC_ERR_STATUS);
err = mc_readl(mc, MC_ERR_STATUS);
type = (err & MC_ERR_TYPE_MASK) >> MC_ERR_TYPE_SHIFT;
perm = (err & MC_ERR_INVALID_SMMU_PAGE_MASK) >>
......@@ -235,7 +235,7 @@ static void tegra30_mc_decode(struct tegra30_mc *mc, int n)
if (cid < ARRAY_SIZE(tegra30_mc_client))
client = tegra30_mc_client[cid];
addr = readl(mc + MC_ERR_ADR);
addr = mc_readl(mc, MC_ERR_ADR);
dev_err_ratelimited(mc->dev, "%s (0x%08x): 0x%08x %s (%s %s %s %s)\n",
mc_int_err[idx], err, addr, client,
......@@ -313,8 +313,11 @@ static irqreturn_t tegra30_mc_isr(int irq, void *data)
mask &= stat;
if (!mask)
return IRQ_NONE;
while ((bit = ffs(mask)) != 0)
while ((bit = ffs(mask)) != 0) {
tegra30_mc_decode(mc, bit - 1);
mask &= ~BIT(bit - 1);
}
mc_writel(mc, stat, MC_INTSTATUS);
return IRQ_HANDLED;
}
......
......@@ -480,6 +480,7 @@ config BMP085_SPI
config PCH_PHUB
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) PHUB"
select GENERIC_NET_UTILS
depends on PCI
help
This driver is for PCH(Platform controller Hub) PHUB(Packet Hub) of
......
......@@ -470,7 +470,7 @@ static ssize_t sysfs_set_reg(struct device *dev,
!test_bit(DPOT_RDAC_MASK & reg, data->otp_en_mask))
return -EPERM;
err = strict_strtoul(buf, 10, &value);
err = kstrtoul(buf, 10, &value);
if (err)
return err;
......
......@@ -126,8 +126,9 @@ static ssize_t als_sensing_range_store(struct device *dev,
int ret_val;
unsigned long val;
if (strict_strtoul(buf, 10, &val))
return -EINVAL;
ret_val = kstrtoul(buf, 10, &val);
if (ret_val)
return ret_val;
if (val < 4096)
val = 1;
......
......@@ -696,9 +696,11 @@ static ssize_t apds990x_lux_calib_store(struct device *dev,
{
struct apds990x_chip *chip = dev_get_drvdata(dev);
unsigned long value;
int ret;
if (strict_strtoul(buf, 0, &value))
return -EINVAL;
ret = kstrtoul(buf, 0, &value);
if (ret)
return ret;
chip->lux_calib = value;
......@@ -759,8 +761,9 @@ static ssize_t apds990x_rate_store(struct device *dev,
unsigned long value;
int ret;
if (strict_strtoul(buf, 0, &value))
return -EINVAL;
ret = kstrtoul(buf, 0, &value);
if (ret)
return ret;
mutex_lock(&chip->mutex);
ret = apds990x_set_arate(chip, value);
......@@ -813,9 +816,11 @@ static ssize_t apds990x_prox_enable_store(struct device *dev,
{
struct apds990x_chip *chip = dev_get_drvdata(dev);
unsigned long value;
int ret;
if (strict_strtoul(buf, 0, &value))
return -EINVAL;
ret = kstrtoul(buf, 0, &value);
if (ret)
return ret;
mutex_lock(&chip->mutex);
......@@ -892,11 +897,12 @@ static ssize_t apds990x_lux_thresh_below_show(struct device *dev,
static ssize_t apds990x_set_lux_thresh(struct apds990x_chip *chip, u32 *target,
const char *buf)
{
int ret = 0;
unsigned long thresh;
int ret;
if (strict_strtoul(buf, 0, &thresh))
return -EINVAL;
ret = kstrtoul(buf, 0, &thresh);
if (ret)
return ret;
if (thresh > APDS_RANGE)
return -EINVAL;
......@@ -957,9 +963,11 @@ static ssize_t apds990x_prox_threshold_store(struct device *dev,
{
struct apds990x_chip *chip = dev_get_drvdata(dev);
unsigned long value;
int ret;
if (strict_strtoul(buf, 0, &value))
return -EINVAL;
ret = kstrtoul(buf, 0, &value);
if (ret)
return ret;
if ((value > APDS_RANGE) || (value == 0) ||
(value < APDS_PROX_HYSTERESIS))
......@@ -990,9 +998,12 @@ static ssize_t apds990x_power_state_store(struct device *dev,
{
struct apds990x_chip *chip = dev_get_drvdata(dev);
unsigned long value;
int ret;
ret = kstrtoul(buf, 0, &value);
if (ret)
return ret;
if (strict_strtoul(buf, 0, &value))
return -EINVAL;
if (value) {
pm_runtime_get_sync(dev);
mutex_lock(&chip->mutex);
......
......@@ -291,7 +291,7 @@ static int __init charlcd_probe(struct platform_device *pdev)
lcd->virtbase = ioremap(lcd->phybase, lcd->physize);
if (!lcd->virtbase) {
ret = -ENOMEM;
goto out_no_remap;
goto out_no_memregion;
}
lcd->irq = platform_get_irq(pdev, 0);
......@@ -320,8 +320,6 @@ static int __init charlcd_probe(struct platform_device *pdev)
out_no_irq:
iounmap(lcd->virtbase);
out_no_remap:
platform_set_drvdata(pdev, NULL);
out_no_memregion:
release_mem_region(lcd->phybase, SZ_4K);
out_no_resource:
......@@ -337,7 +335,6 @@ static int __exit charlcd_remove(struct platform_device *pdev)
free_irq(lcd->irq, lcd);
iounmap(lcd->virtbase);
release_mem_region(lcd->phybase, lcd->physize);
platform_set_drvdata(pdev, NULL);
kfree(lcd);
}
......
......@@ -651,8 +651,9 @@ static ssize_t bh1770_power_state_store(struct device *dev,
unsigned long value;
ssize_t ret;
if (strict_strtoul(buf, 0, &value))
return -EINVAL;
ret = kstrtoul(buf, 0, &value);
if (ret)
return ret;
mutex_lock(&chip->mutex);
if (value) {
......@@ -726,9 +727,11 @@ static ssize_t bh1770_prox_enable_store(struct device *dev,
{
struct bh1770_chip *chip = dev_get_drvdata(dev);
unsigned long value;
int ret;
if (strict_strtoul(buf, 0, &value))
return -EINVAL;
ret = kstrtoul(buf, 0, &value);
if (ret)
return ret;
mutex_lock(&chip->mutex);
/* Assume no proximity. Sensor will tell real state soon */
......@@ -824,9 +827,11 @@ static ssize_t bh1770_set_prox_rate_above(struct device *dev,
{
struct bh1770_chip *chip = dev_get_drvdata(dev);
unsigned long value;
int ret;
if (strict_strtoul(buf, 0, &value))
return -EINVAL;
ret = kstrtoul(buf, 0, &value);
if (ret)
return ret;
mutex_lock(&chip->mutex);
chip->prox_rate_threshold = bh1770_prox_rate_validate(value);
......@@ -840,9 +845,11 @@ static ssize_t bh1770_set_prox_rate_below(struct device *dev,
{
struct bh1770_chip *chip = dev_get_drvdata(dev);
unsigned long value;
int ret;
if (strict_strtoul(buf, 0, &value))
return -EINVAL;
ret = kstrtoul(buf, 0, &value);
if (ret)
return ret;
mutex_lock(&chip->mutex);
chip->prox_rate = bh1770_prox_rate_validate(value);
......@@ -865,8 +872,10 @@ static ssize_t bh1770_set_prox_thres(struct device *dev,
unsigned long value;
int ret;
if (strict_strtoul(buf, 0, &value))
return -EINVAL;
ret = kstrtoul(buf, 0, &value);
if (ret)
return ret;
if (value > BH1770_PROX_RANGE)
return -EINVAL;
......@@ -893,9 +902,11 @@ static ssize_t bh1770_prox_persistence_store(struct device *dev,
{
struct bh1770_chip *chip = dev_get_drvdata(dev);
unsigned long value;
int ret;
if (strict_strtoul(buf, 0, &value))
return -EINVAL;
ret = kstrtoul(buf, 0, &value);
if (ret)
return ret;
if (value > BH1770_PROX_MAX_PERSISTENCE)
return -EINVAL;
......@@ -918,9 +929,11 @@ static ssize_t bh1770_prox_abs_thres_store(struct device *dev,
{
struct bh1770_chip *chip = dev_get_drvdata(dev);
unsigned long value;
int ret;
if (strict_strtoul(buf, 0, &value))
return -EINVAL;
ret = kstrtoul(buf, 0, &value);
if (ret)
return ret;
if (value > BH1770_PROX_RANGE)
return -EINVAL;
......@@ -963,9 +976,11 @@ static ssize_t bh1770_lux_calib_store(struct device *dev,
unsigned long value;
u32 old_calib;
u32 new_corr;
int ret;
if (strict_strtoul(buf, 0, &value))
return -EINVAL;
ret = kstrtoul(buf, 0, &value);
if (ret)
return ret;
mutex_lock(&chip->mutex);
old_calib = chip->lux_calib;
......@@ -1012,8 +1027,9 @@ static ssize_t bh1770_set_lux_rate(struct device *dev,
unsigned long rate_hz;
int ret, i;
if (strict_strtoul(buf, 0, &rate_hz))
return -EINVAL;
ret = kstrtoul(buf, 0, &rate_hz);
if (ret)
return ret;
for (i = 0; i < ARRAY_SIZE(lux_rates_hz) - 1; i++)
if (rate_hz >= lux_rates_hz[i])
......@@ -1047,11 +1063,12 @@ static ssize_t bh1770_get_lux_thresh_below(struct device *dev,
static ssize_t bh1770_set_lux_thresh(struct bh1770_chip *chip, u16 *target,
const char *buf)
{
int ret = 0;
unsigned long thresh;
int ret;
if (strict_strtoul(buf, 0, &thresh))
return -EINVAL;
ret = kstrtoul(buf, 0, &thresh);
if (ret)
return ret;
if (thresh > BH1770_LUX_RANGE)
return -EINVAL;
......
......@@ -107,7 +107,7 @@ static ssize_t bh1780_store_power_state(struct device *dev,
unsigned long val;
int error;
error = strict_strtoul(buf, 0, &val);
error = kstrtoul(buf, 0, &val);
if (error)
return error;
......
......@@ -830,8 +830,9 @@ static ssize_t penable_store(struct device *dev, struct device_attribute *attr,
unsigned long val;
int ret;
if (strict_strtoul(buf, 0, &val))
return -EINVAL;
ret = kstrtoul(buf, 0, &val);
if (ret)
return ret;
if (val) {
ret = fpga_enable_power_supplies(priv);
......@@ -859,8 +860,9 @@ static ssize_t program_store(struct device *dev, struct device_attribute *attr,
unsigned long val;
int ret;
if (strict_strtoul(buf, 0, &val))
return -EINVAL;
ret = kstrtoul(buf, 0, &val);
if (ret)
return ret;
/* We can't have an image writer and be programming simultaneously */
if (mutex_lock_interruptible(&priv->lock))
......@@ -919,7 +921,7 @@ static bool dma_filter(struct dma_chan *chan, void *data)
static int fpga_of_remove(struct platform_device *op)
{
struct fpga_dev *priv = dev_get_drvdata(&op->dev);
struct fpga_dev *priv = platform_get_drvdata(op);
struct device *this_device = priv->miscdev.this_device;
sysfs_remove_group(&this_device->kobj, &fpga_attr_group);
......@@ -969,7 +971,7 @@ static int fpga_of_probe(struct platform_device *op)
kref_init(&priv->ref);
dev_set_drvdata(&op->dev, priv);
platform_set_drvdata(op, priv);
priv->dev = &op->dev;
mutex_init(&priv->lock);
init_completion(&priv->completion);
......
......@@ -1002,10 +1002,10 @@ static ssize_t data_en_set(struct device *dev, struct device_attribute *attr,
unsigned long enable;
int ret;
ret = strict_strtoul(buf, 0, &enable);
ret = kstrtoul(buf, 0, &enable);
if (ret) {
dev_err(priv->dev, "unable to parse enable input\n");
return -EINVAL;
return ret;
}
/* protect against concurrent enable/disable */
......@@ -1296,7 +1296,7 @@ static int data_of_probe(struct platform_device *op)
goto out_return;
}
dev_set_drvdata(&op->dev, priv);
platform_set_drvdata(op, priv);
priv->dev = &op->dev;
kref_init(&priv->ref);
mutex_init(&priv->mutex);
......@@ -1400,7 +1400,7 @@ static int data_of_probe(struct platform_device *op)
static int data_of_remove(struct platform_device *op)
{
struct fpga_device *priv = dev_get_drvdata(&op->dev);
struct fpga_device *priv = platform_get_drvdata(op);
struct device *this_device = priv->miscdev.this_device;
/* remove all sysfs files, now the device cannot be re-enabled */
......
......@@ -492,10 +492,9 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (client->dev.platform_data) {
chip = *(struct at24_platform_data *)client->dev.platform_data;
} else {
if (!id->driver_data) {
err = -ENODEV;
goto err_out;
}
if (!id->driver_data)
return -ENODEV;
magic = id->driver_data;
chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
magic >>= AT24_SIZE_BYTELEN;
......@@ -519,8 +518,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
"byte_len looks suspicious (no power of 2)!\n");
if (!chip.page_size) {
dev_err(&client->dev, "page_size must not be 0!\n");
err = -EINVAL;
goto err_out;
return -EINVAL;
}
if (!is_power_of_2(chip.page_size))
dev_warn(&client->dev,
......@@ -528,10 +526,9 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
/* Use I2C operations unless we're stuck with SMBus extensions. */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
if (chip.flags & AT24_FLAG_ADDR16) {
err = -EPFNOSUPPORT;
goto err_out;
}
if (chip.flags & AT24_FLAG_ADDR16)
return -EPFNOSUPPORT;
if (i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
use_smbus = I2C_SMBUS_I2C_BLOCK_DATA;
......@@ -542,8 +539,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
use_smbus = I2C_SMBUS_BYTE_DATA;
} else {
err = -EPFNOSUPPORT;
goto err_out;
return -EPFNOSUPPORT;
}
}
......@@ -553,12 +549,10 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
num_addresses = DIV_ROUND_UP(chip.byte_len,
(chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
at24 = kzalloc(sizeof(struct at24_data) +
at24 = devm_kzalloc(&client->dev, sizeof(struct at24_data) +
num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);
if (!at24) {
err = -ENOMEM;
goto err_out;
}
if (!at24)
return -ENOMEM;
mutex_init(&at24->lock);
at24->use_smbus = use_smbus;
......@@ -596,11 +590,10 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
at24->write_max = write_max;
/* buffer (data + address at the beginning) */
at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL);
if (!at24->writebuf) {
err = -ENOMEM;
goto err_struct;
}
at24->writebuf = devm_kzalloc(&client->dev,
write_max + 2, GFP_KERNEL);
if (!at24->writebuf)
return -ENOMEM;
} else {
dev_warn(&client->dev,
"cannot write due to controller restrictions.");
......@@ -648,11 +641,6 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (at24->client[i])
i2c_unregister_device(at24->client[i]);
kfree(at24->writebuf);
err_struct:
kfree(at24);
err_out:
dev_dbg(&client->dev, "probe error %d\n", err);
return err;
}
......@@ -667,8 +655,6 @@ static int at24_remove(struct i2c_client *client)
for (i = 1; i < at24->num_addresses; i++)
i2c_unregister_device(at24->client[i]);
kfree(at24->writebuf);
kfree(at24);
return 0;
}
......
......@@ -371,11 +371,10 @@ static int at25_probe(struct spi_device *spi)
if (np) {
err = at25_np_to_chip(&spi->dev, np, &chip);
if (err)
goto fail;
return err;
} else {
dev_err(&spi->dev, "Error: no chip description\n");
err = -ENODEV;
goto fail;
return -ENODEV;
}
} else
chip = *(struct spi_eeprom *)spi->dev.platform_data;
......@@ -389,8 +388,7 @@ static int at25_probe(struct spi_device *spi)
addrlen = 3;
else {
dev_dbg(&spi->dev, "unsupported address type\n");
err = -EINVAL;
goto fail;
return -EINVAL;
}
/* Ping the chip ... the status register is pretty portable,
......@@ -400,14 +398,12 @@ static int at25_probe(struct spi_device *spi)
sr = spi_w8r8(spi, AT25_RDSR);
if (sr < 0 || sr & AT25_SR_nRDY) {
dev_dbg(&spi->dev, "rdsr --> %d (%02x)\n", sr, sr);
err = -ENXIO;
goto fail;
return -ENXIO;
}
if (!(at25 = kzalloc(sizeof *at25, GFP_KERNEL))) {
err = -ENOMEM;
goto fail;
}
at25 = devm_kzalloc(&spi->dev, sizeof(struct at25_data), GFP_KERNEL);
if (!at25)
return -ENOMEM;
mutex_init(&at25->lock);
at25->chip = chip;
......@@ -439,7 +435,7 @@ static int at25_probe(struct spi_device *spi)
err = sysfs_create_bin_file(&spi->dev.kobj, &at25->bin);
if (err)
goto fail;
return err;
if (chip.setup)
chip.setup(&at25->mem, chip.context);
......@@ -453,10 +449,6 @@ static int at25_probe(struct spi_device *spi)
(chip.flags & EE_READONLY) ? " (readonly)" : "",
at25->chip.page_size);
return 0;
fail:
dev_dbg(&spi->dev, "probe err %d\n", err);
kfree(at25);
return err;
}
static int at25_remove(struct spi_device *spi)
......@@ -465,7 +457,6 @@ static int at25_remove(struct spi_device *spi)
at25 = spi_get_drvdata(spi);
sysfs_remove_bin_file(&spi->dev.kobj, &at25->bin);
kfree(at25);
return 0;
}
......
This diff is collapsed.
......@@ -46,8 +46,9 @@ static int compass_store(struct device *dev, const char *buf, size_t count,
int ret;
unsigned long val;
if (strict_strtoul(buf, 10, &val))
return -EINVAL;
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
if (val >= strlen(map))
return -EINVAL;
mutex_lock(&compass_mutex);
......
......@@ -208,7 +208,11 @@ static ssize_t isl29003_store_range(struct device *dev,
unsigned long val;
int ret;
if ((strict_strtoul(buf, 10, &val) < 0) || (val > 3))
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
if (val > 3)
return -EINVAL;
ret = isl29003_set_range(client, val);
......@@ -239,7 +243,11 @@ static ssize_t isl29003_store_resolution(struct device *dev,
unsigned long val;
int ret;
if ((strict_strtoul(buf, 10, &val) < 0) || (val > 3))
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
if (val > 3)
return -EINVAL;
ret = isl29003_set_resolution(client, val);
......@@ -267,7 +275,11 @@ static ssize_t isl29003_store_mode(struct device *dev,
unsigned long val;
int ret;
if ((strict_strtoul(buf, 10, &val) < 0) || (val > 2))
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
if (val > 2)
return -EINVAL;
ret = isl29003_set_mode(client, val);
......@@ -298,7 +310,11 @@ static ssize_t isl29003_store_power_state(struct device *dev,
unsigned long val;
int ret;
if ((strict_strtoul(buf, 10, &val) < 0) || (val > 1))
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
if (val > 1)
return -EINVAL;
ret = isl29003_set_power_state(client, val);
......
......@@ -90,8 +90,10 @@ static ssize_t als_sensing_range_store(struct device *dev,
int ret_val;
unsigned long val;
if (strict_strtoul(buf, 10, &val))
return -EINVAL;
ret_val = kstrtoul(buf, 10, &val);
if (ret_val)
return ret_val;
if (val < 1 || val > 64000)
return -EINVAL;
......
......@@ -831,9 +831,11 @@ static ssize_t lis3lv02d_rate_set(struct device *dev,
{
struct lis3lv02d *lis3 = dev_get_drvdata(dev);
unsigned long rate;
int ret;
if (strict_strtoul(buf, 0, &rate))
return -EINVAL;
ret = kstrtoul(buf, 0, &rate);
if (ret)
return ret;
lis3lv02d_sysfs_poweron(lis3);
if (lis3lv02d_set_odr(lis3, rate))
......
......@@ -443,11 +443,11 @@ unsigned int mei_amthif_poll(struct mei_device *dev,
*
* returns 0, OK; otherwise, error.
*/
int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots,
struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list)
int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
s32 *slots, struct mei_cl_cb *cmpl_list)
{
struct mei_device *dev = cl->dev;
struct mei_msg_hdr mei_hdr;
struct mei_cl *cl = cb->cl;
size_t len = dev->iamthif_msg_buf_size - dev->iamthif_msg_buf_index;
u32 msg_slots = mei_data2slots(len);
......
This diff is collapsed.
......@@ -89,6 +89,10 @@ 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);
void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb);
void mei_host_client_init(struct work_struct *work);
......
This diff is collapsed.
......@@ -54,7 +54,7 @@ int mei_hbm_start_wait(struct mei_device *dev);
int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl);
int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl);
int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl);
bool mei_hbm_version_is_supported(struct mei_device *dev);
#endif /* _MEI_HBM_H_ */
......@@ -171,7 +171,7 @@ static void mei_me_hw_reset_release(struct mei_device *dev)
* @dev: the device structure
* @intr_enable: if interrupt should be enabled after reset.
*/
static void mei_me_hw_reset(struct mei_device *dev, bool intr_enable)
static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable)
{
struct mei_me_hw *hw = to_me_hw(dev);
u32 hcsr = mei_hcsr_read(hw);
......@@ -191,6 +191,7 @@ static void mei_me_hw_reset(struct mei_device *dev, bool intr_enable)
mei_me_hw_reset_release(dev);
dev_dbg(&dev->pdev->dev, "current HCSR = 0x%08x.\n", mei_hcsr_read(hw));
return 0;
}
/**
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -31,12 +31,6 @@
static const u8 mei_start_wd_params[] = { 0x02, 0x12, 0x13, 0x10 };
static const u8 mei_stop_wd_params[] = { 0x02, 0x02, 0x14, 0x10 };
const u8 mei_wd_state_independence_msg[3][4] = {
{0x05, 0x02, 0x51, 0x10},
{0x05, 0x02, 0x52, 0x10},
{0x07, 0x02, 0x01, 0x10}
};
/*
* AMT Watchdog Device
*/
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -40,6 +40,7 @@
#include <linux/slab.h>
#include <linux/console.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/netpoll.h>
#include <linux/inet.h>
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment