mirror of
https://github.com/torvalds/linux.git
synced 2024-11-28 15:11:31 +00:00
Char/Misc merge for 3.11-rc1
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. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.19 (GNU/Linux) iEYEABECAAYFAlHRqsQACgkQMUfUDdst+ykNlACgwnDHLav/u2NrAxoqxmw7Bcd8 qY0An3h0ZGI5PpDe6U0IyBDQIipHuOjG =vaRG -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
fe3c22bd5c
@ -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
|
||||
|
19
Documentation/devicetree/bindings/ata/atmel-at91_cf.txt
Normal file
19
Documentation/devicetree/bindings/ata/atmel-at91_cf.txt
Normal file
@ -0,0 +1,19 @@
|
||||
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 */
|
||||
>;
|
||||
};
|
15
Documentation/devicetree/bindings/extcon/extcon-twl.txt
Normal file
15
Documentation/devicetree/bindings/extcon/extcon-twl.txt
Normal file
@ -0,0 +1,15 @@
|
||||
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;
|
||||
};
|
@ -0,0 +1,156 @@
|
||||
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>;
|
||||
};
|
||||
};
|
||||
};
|
38
Documentation/fmc/00-INDEX
Normal file
38
Documentation/fmc/00-INDEX
Normal file
@ -0,0 +1,38 @@
|
||||
|
||||
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
|
47
Documentation/fmc/API.txt
Normal file
47
Documentation/fmc/API.txt
Normal file
@ -0,0 +1,47 @@
|
||||
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.
|
88
Documentation/fmc/FMC-and-SDB.txt
Normal file
88
Documentation/fmc/FMC-and-SDB.txt
Normal file
@ -0,0 +1,88 @@
|
||||
|
||||
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);
|
311
Documentation/fmc/carrier.txt
Normal file
311
Documentation/fmc/carrier.txt
Normal file
@ -0,0 +1,311 @@
|
||||
FMC Device
|
||||
**********
|
||||
|
||||
Within the Linux bus framework, the FMC device is created and
|
||||
registered by the carrier driver. For example, the PCI driver for the
|
||||
SPEC card fills a data structure for each SPEC that it drives, and
|
||||
registers an associated FMC device for each card. The SVEC driver can
|
||||
do exactly the same for the VME carrier (actually, it should do it
|
||||
twice, because the SVEC carries two FMC mezzanines). Similarly, an
|
||||
Etherbone driver will be able to register its own FMC devices, offering
|
||||
communication primitives through frame exchange.
|
||||
|
||||
The contents of the EEPROM within the FMC are used for identification
|
||||
purposes, i.e. for matching the device with its own driver. For this
|
||||
reason the device structure includes a complete copy of the EEPROM
|
||||
(actually, the carrier driver may choose whether or not to return it -
|
||||
for example we most likely won't have the whole EEPROM available for
|
||||
Etherbone devices.
|
||||
|
||||
The following listing shows the current structure defining a device.
|
||||
Please note that all the machinery is in place but some details may
|
||||
still change in the future. For this reason, there is a version field
|
||||
at the beginning of the structure. As usual, the minor number will
|
||||
change for compatible changes (like a new flag) and the major number
|
||||
will increase when an incompatible change happens (for example, a
|
||||
change in layout of some fmc data structures). Device writers should
|
||||
just set it to the value FMC_VERSION, and be ready to get back -EINVAL
|
||||
at registration time.
|
||||
|
||||
struct fmc_device {
|
||||
unsigned long version;
|
||||
unsigned long flags;
|
||||
struct module *owner; /* char device must pin it */
|
||||
struct fmc_fru_id id; /* for EEPROM-based match */
|
||||
struct fmc_operations *op; /* carrier-provided */
|
||||
int irq; /* according to host bus. 0 == none */
|
||||
int eeprom_len; /* Usually 8kB, may be less */
|
||||
int eeprom_addr; /* 0x50, 0x52 etc */
|
||||
uint8_t *eeprom; /* Full contents or leading part */
|
||||
char *carrier_name; /* "SPEC" or similar, for special use */
|
||||
void *carrier_data; /* "struct spec *" or equivalent */
|
||||
__iomem void *fpga_base; /* May be NULL (Etherbone) */
|
||||
__iomem void *slot_base; /* Set by the driver */
|
||||
struct fmc_device **devarray; /* Allocated by the bus */
|
||||
int slot_id; /* Index in the slot array */
|
||||
int nr_slots; /* Number of slots in this carrier */
|
||||
unsigned long memlen; /* Used for the char device */
|
||||
struct device dev; /* For Linux use */
|
||||
struct device *hwdev; /* The underlying hardware device */
|
||||
unsigned long sdbfs_entry;
|
||||
struct sdb_array *sdb;
|
||||
uint32_t device_id; /* Filled by the device */
|
||||
char *mezzanine_name; /* Defaults to ``fmc'' */
|
||||
void *mezzanine_data;
|
||||
};
|
||||
|
||||
The meaning of most fields is summarized in the code comment above.
|
||||
|
||||
The following fields must be filled by the carrier driver before
|
||||
registration:
|
||||
|
||||
* version: must be set to FMC_VERSION.
|
||||
|
||||
* owner: set to MODULE_OWNER.
|
||||
|
||||
* op: the operations to act on the device.
|
||||
|
||||
* irq: number for the mezzanine; may be zero.
|
||||
|
||||
* eeprom_len: length of the following array.
|
||||
|
||||
* eeprom_addr: 0x50 for first mezzanine and so on.
|
||||
|
||||
* eeprom: the full content of the I2C EEPROM.
|
||||
|
||||
* carrier_name.
|
||||
|
||||
* carrier_data: a unique pointer for the carrier.
|
||||
|
||||
* fpga_base: the I/O memory address (may be NULL).
|
||||
|
||||
* slot_id: the index of this slot (starting from zero).
|
||||
|
||||
* memlen: if fpga_base is valid, the length of I/O memory.
|
||||
|
||||
* hwdev: to be used in some dev_err() calls.
|
||||
|
||||
* device_id: a slot-specific unique integer number.
|
||||
|
||||
|
||||
Please note that the carrier should read its own EEPROM memory before
|
||||
registering the device, as well as fill all other fields listed above.
|
||||
|
||||
The following fields should not be assigned, because they are filled
|
||||
later by either the bus or the device driver:
|
||||
|
||||
* flags.
|
||||
|
||||
* fru_id: filled by the bus, parsing the eeprom.
|
||||
|
||||
* slot_base: filled and used by the driver, if useful to it.
|
||||
|
||||
* devarray: an array og all mezzanines driven by a singe FPGA.
|
||||
|
||||
* nr_slots: set by the core at registration time.
|
||||
|
||||
* dev: used by Linux.
|
||||
|
||||
* sdb: FPGA contents, scanned according to driver's directions.
|
||||
|
||||
* sdbfs_entry: SDB entry point in EEPROM: autodetected.
|
||||
|
||||
* mezzanine_data: available for the driver.
|
||||
|
||||
* mezzanine_name: filled by fmc-bus during identification.
|
||||
|
||||
|
||||
Note: mezzanine_data may be redundant, because Linux offers the drvdata
|
||||
approach, so the field may be removed in later versions of this bus
|
||||
implementation.
|
||||
|
||||
As I write this, she SPEC carrier is already completely functional in
|
||||
the fmc-bus environment, and is a good reference to look at.
|
||||
|
||||
|
||||
The API Offered by Carriers
|
||||
===========================
|
||||
|
||||
The carrier provides a number of methods by means of the
|
||||
`fmc_operations' structure, which currently is defined like this
|
||||
(again, it is a moving target, please refer to the header rather than
|
||||
this document):
|
||||
|
||||
struct fmc_operations {
|
||||
uint32_t (*readl)(struct fmc_device *fmc, int offset);
|
||||
void (*writel)(struct fmc_device *fmc, uint32_t value, int offset);
|
||||
int (*reprogram)(struct fmc_device *f, struct fmc_driver *d, char *gw);
|
||||
int (*validate)(struct fmc_device *fmc, struct fmc_driver *drv);
|
||||
int (*irq_request)(struct fmc_device *fmc, irq_handler_t h,
|
||||
char *name, int flags);
|
||||
void (*irq_ack)(struct fmc_device *fmc);
|
||||
int (*irq_free)(struct fmc_device *fmc);
|
||||
int (*gpio_config)(struct fmc_device *fmc, struct fmc_gpio *gpio,
|
||||
int ngpio);
|
||||
int (*read_ee)(struct fmc_device *fmc, int pos, void *d, int l);
|
||||
int (*write_ee)(struct fmc_device *fmc, int pos, const void *d, int l);
|
||||
};
|
||||
|
||||
The individual methods perform the following tasks:
|
||||
|
||||
`readl'
|
||||
`writel'
|
||||
These functions access FPGA registers by whatever means the
|
||||
carrier offers. They are not expected to fail, and most of the time
|
||||
they will just make a memory access to the host bus. If the
|
||||
carrier provides a fpga_base pointer, the driver may use direct
|
||||
access through that pointer. For this reason the header offers the
|
||||
inline functions fmc_readl and fmc_writel that access fpga_base if
|
||||
the respective method is NULL. A driver that wants to be portable
|
||||
and efficient should use fmc_readl and fmc_writel. For Etherbone,
|
||||
or other non-local carriers, error-management is still to be
|
||||
defined.
|
||||
|
||||
`validate'
|
||||
Module parameters are used to manage different applications for
|
||||
two or more boards of the same kind. Validation is based on the
|
||||
busid module parameter, if provided, and returns the matching
|
||||
index in the associated array. See *note Module Parameters:: in in
|
||||
doubt. If no match is found, `-ENOENT' is returned; if the user
|
||||
didn't pass `busid=', all devices will pass validation. The value
|
||||
returned by the validate method can be used as index into other
|
||||
parameters (for example, some drivers use the `lm32=' parameter in
|
||||
this way). Such "generic parameters" are documented in *note
|
||||
Module Parameters::, below. The validate method is used by
|
||||
`fmc-trivial.ko', described in *note fmc-trivial::.
|
||||
|
||||
`reprogram'
|
||||
The carrier enumerates FMC devices by loading a standard (or
|
||||
golden) FPGA binary that allows EEPROM access. Each driver, then,
|
||||
will need to reprogram the FPGA by calling this function. If the
|
||||
name argument is NULL, the carrier should reprogram the golden
|
||||
binary. If the gateware name has been overridden through module
|
||||
parameters (in a carrier-specific way) the file loaded will match
|
||||
the parameters. Per-device gateware names can be specified using
|
||||
the `gateware=' parameter, see *note Module Parameters::. Note:
|
||||
Clients should call rhe new helper, fmc_reprogram, which both
|
||||
calls this method and parse the SDB tree of the FPGA.
|
||||
|
||||
`irq_request'
|
||||
`irq_ack'
|
||||
`irq_free'
|
||||
Interrupt management is carrier-specific, so it is abstracted as
|
||||
operations. The interrupt number is listed in the device
|
||||
structure, and for the mezzanine driver the number is only
|
||||
informative. The handler will receive the fmc pointer as dev_id;
|
||||
the flags argument is passed to the Linux request_irq function,
|
||||
but fmc-specific flags may be added in the future. You'll most
|
||||
likely want to pass the `IRQF_SHARED' flag.
|
||||
|
||||
`gpio_config'
|
||||
The method allows to configure a GPIO pin in the carrier, and read
|
||||
its current value if it is configured as input. See *note The GPIO
|
||||
Abstraction:: for details.
|
||||
|
||||
`read_ee'
|
||||
`write_ee'
|
||||
Read or write the EEPROM. The functions are expected to be only
|
||||
called before reprogramming and the carrier should refuse them
|
||||
with `ENODEV' after reprogramming. The offset is expected to be
|
||||
within 8kB (the current size), but addresses up to 1MB are
|
||||
reserved to fit bigger I2C devices in the future. Carriers may
|
||||
offer access to other internal flash memories using these same
|
||||
methods: for example the SPEC driver may define that its carrier
|
||||
I2C memory is seen at offset 1M and the internal SPI flash is seen
|
||||
at offset 16M. This multiplexing of several flash memories in the
|
||||
same address space is is carrier-specific and should only be used
|
||||
by a driver that has verified the `carrier_name' field.
|
||||
|
||||
|
||||
|
||||
The GPIO Abstraction
|
||||
====================
|
||||
|
||||
Support for GPIO pins in the fmc-bus environment is not very
|
||||
straightforward and deserves special discussion.
|
||||
|
||||
While the general idea of a carrier-independent driver seems to fly,
|
||||
configuration of specific signals within the carrier needs at least
|
||||
some knowledge of the carrier itself. For this reason, the specific
|
||||
driver can request to configure carrier-specific GPIO pins, numbered
|
||||
from 0 to at most 4095. Configuration is performed by passing a
|
||||
pointer to an array of struct fmc_gpio items, as well as the length of
|
||||
the array. This is the data structure:
|
||||
|
||||
struct fmc_gpio {
|
||||
char *carrier_name;
|
||||
int gpio;
|
||||
int _gpio; /* internal use by the carrier */
|
||||
int mode; /* GPIOF_DIR_OUT etc, from <linux/gpio.h> */
|
||||
int irqmode; /* IRQF_TRIGGER_LOW and so on */
|
||||
};
|
||||
|
||||
By specifying a carrier_name for each pin, the driver may access
|
||||
different pins in different carriers. The gpio_config method is
|
||||
expected to return the number of pins successfully configured, ignoring
|
||||
requests for other carriers. However, if no pin is configured (because
|
||||
no structure at all refers to the current carrier_name), the operation
|
||||
returns an error so the caller will know that it is running under a
|
||||
yet-unsupported carrier.
|
||||
|
||||
So, for example, a driver that has been developed and tested on both
|
||||
the SPEC and the SVEC may request configuration of two different GPIO
|
||||
pins, and expect one such configuration to succeed - if none succeeds
|
||||
it most likely means that the current carrier is a still-unknown one.
|
||||
|
||||
If, however, your GPIO pin has a specific known role, you can pass a
|
||||
special number in the gpio field, using one of the following macros:
|
||||
|
||||
#define FMC_GPIO_RAW(x) (x) /* 4096 of them */
|
||||
#define FMC_GPIO_IRQ(x) ((x) + 0x1000) /* 256 of them */
|
||||
#define FMC_GPIO_LED(x) ((x) + 0x1100) /* 256 of them */
|
||||
#define FMC_GPIO_KEY(x) ((x) + 0x1200) /* 256 of them */
|
||||
#define FMC_GPIO_TP(x) ((x) + 0x1300) /* 256 of them */
|
||||
#define FMC_GPIO_USER(x) ((x) + 0x1400) /* 256 of them */
|
||||
|
||||
Use of virtual GPIO numbers (anything but FMC_GPIO_RAW) is allowed
|
||||
provided the carrier_name field in the data structure is left
|
||||
unspecified (NULL). Each carrier is responsible for providing a mapping
|
||||
between virtual and physical GPIO numbers. The carrier may then use the
|
||||
_gpio field to cache the result of this mapping.
|
||||
|
||||
All carriers must map their I/O lines to the sets above starting from
|
||||
zero. The SPEC, for example, maps interrupt pins 0 and 1, and test
|
||||
points 0 through 3 (even if the test points on the PCB are called
|
||||
5,6,7,8).
|
||||
|
||||
If, for example, a driver requires a free LED and a test point (for a
|
||||
scope probe to be plugged at some point during development) it may ask
|
||||
for FMC_GPIO_LED(0) and FMC_GPIO_TP(0). Each carrier will provide
|
||||
suitable GPIO pins. Clearly, the person running the drivers will know
|
||||
the order used by the specific carrier driver in assigning leds and
|
||||
testpoints, so to make a carrier-dependent use of the diagnostic tools.
|
||||
|
||||
In theory, some form of autodetection should be possible: a driver like
|
||||
the wr-nic (which uses IRQ(1) on the SPEC card) should configure
|
||||
IRQ(0), make a test with software-generated interrupts and configure
|
||||
IRQ(1) if the test fails. This probing step should be used because even
|
||||
if the wr-nic gateware is known to use IRQ1 on the SPEC, the driver
|
||||
should be carrier-independent and thus use IRQ(0) as a first bet -
|
||||
actually, the knowledge that IRQ0 may fail is carrier-dependent
|
||||
information, but using it doesn't make the driver unsuitable for other
|
||||
carriers.
|
||||
|
||||
The return value of gpio_config is defined as follows:
|
||||
|
||||
* If no pin in the array can be used by the carrier, `-ENODEV'.
|
||||
|
||||
* If at least one virtual GPIO number cannot be mapped, `-ENOENT'.
|
||||
|
||||
* On success, 0 or positive. The value returned is the number of
|
||||
high input bits (if no input is configured, the value for success
|
||||
is 0).
|
||||
|
||||
While I admit the procedure is not completely straightforward, it
|
||||
allows configuration, input and output with a single carrier operation.
|
||||
Given the typical use case of FMC devices, GPIO operations are not
|
||||
expected to ever by in hot paths, and GPIO access so fare has only been
|
||||
used to configure the interrupt pin, mode and polarity. Especially
|
||||
reading inputs is not expected to be common. If your device has GPIO
|
||||
capabilities in the hot path, you should consider using the kernel's
|
||||
GPIO mechanisms.
|
64
Documentation/fmc/fmc-chardev.txt
Normal file
64
Documentation/fmc/fmc-chardev.txt
Normal file
@ -0,0 +1,64 @@
|
||||
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
|
36
Documentation/fmc/fmc-fakedev.txt
Normal file
36
Documentation/fmc/fmc-fakedev.txt
Normal file
@ -0,0 +1,36 @@
|
||||
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
|
17
Documentation/fmc/fmc-trivial.txt
Normal file
17
Documentation/fmc/fmc-trivial.txt
Normal file
@ -0,0 +1,17 @@
|
||||
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.
|
125
Documentation/fmc/fmc-write-eeprom.txt
Normal file
125
Documentation/fmc/fmc-write-eeprom.txt
Normal file
@ -0,0 +1,125 @@
|
||||
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
|
168
Documentation/fmc/identifiers.txt
Normal file
168
Documentation/fmc/identifiers.txt
Normal file
@ -0,0 +1,168 @@
|
||||
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.
|
123
Documentation/fmc/mezzanine.txt
Normal file
123
Documentation/fmc/mezzanine.txt
Normal file
@ -0,0 +1,123 @@
|
||||
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.
|
56
Documentation/fmc/parameters.txt
Normal file
56
Documentation/fmc/parameters.txt
Normal file
@ -0,0 +1,56 @@
|
||||
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.
|
||||
|
10
MAINTAINERS
10
MAINTAINERS
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
246
drivers/extcon/extcon-palmas.c
Normal file
246
drivers/extcon/extcon-palmas.c
Normal file
@ -0,0 +1,246 @@
|
||||
/*
|
||||
* 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);
|
51
drivers/fmc/Kconfig
Normal file
51
drivers/fmc/Kconfig
Normal file
@ -0,0 +1,51 @@
|
||||
#
|
||||
# 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
|
13
drivers/fmc/Makefile
Normal file
13
drivers/fmc/Makefile
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
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
|
202
drivers/fmc/fmc-chardev.c
Normal file
202
drivers/fmc/fmc-chardev.c
Normal file
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* 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");
|
296
drivers/fmc/fmc-core.c
Normal file
296
drivers/fmc/fmc-core.c
Normal file
@ -0,0 +1,296 @@
|
||||
/*
|
||||
* 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");
|
100
drivers/fmc/fmc-dump.c
Normal file
100
drivers/fmc/fmc-dump.c
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
355
drivers/fmc/fmc-fakedev.c
Normal file
355
drivers/fmc/fmc-fakedev.c
Normal file
@ -0,0 +1,355 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/fmc.h>
|
||||
|
||||
#define FF_EEPROM_SIZE 8192 /* The standard eeprom size */
|
||||
#define FF_MAX_MEZZANINES 4 /* Fakes a multi-mezzanine carrier */
|
||||
|
||||
/* The user can pass up to 4 names of eeprom images to load */
|
||||
static char *ff_eeprom[FF_MAX_MEZZANINES];
|
||||
static int ff_nr_eeprom;
|
||||
module_param_array_named(eeprom, ff_eeprom, charp, &ff_nr_eeprom, 0444);
|
||||
|
||||
/* The user can ask for a multi-mezzanine carrier, with the default eeprom */
|
||||
static int ff_nr_dev = 1;
|
||||
module_param_named(ndev, ff_nr_dev, int, 0444);
|
||||
|
||||
|
||||
/* Lazily, don't support the "standard" module parameters */
|
||||
|
||||
/*
|
||||
* Eeprom built from these commands:
|
||||
|
||||
../fru-generator -v fake-vendor -n fake-design-for-testing \
|
||||
-s 01234 -p none > IPMI-FRU
|
||||
|
||||
gensdbfs . ../fake-eeprom.bin
|
||||
*/
|
||||
static char ff_eeimg[FF_MAX_MEZZANINES][FF_EEPROM_SIZE] = {
|
||||
{
|
||||
0x01, 0x00, 0x00, 0x01, 0x00, 0x0c, 0x00, 0xf2, 0x01, 0x0b, 0x00, 0xb2,
|
||||
0x86, 0x87, 0xcb, 0x66, 0x61, 0x6b, 0x65, 0x2d, 0x76, 0x65, 0x6e, 0x64,
|
||||
0x6f, 0x72, 0xd7, 0x66, 0x61, 0x6b, 0x65, 0x2d, 0x64, 0x65, 0x73, 0x69,
|
||||
0x67, 0x6e, 0x2d, 0x66, 0x6f, 0x72, 0x2d, 0x74, 0x65, 0x73, 0x74, 0x69,
|
||||
0x6e, 0x67, 0xc5, 0x30, 0x31, 0x32, 0x33, 0x34, 0xc4, 0x6e, 0x6f, 0x6e,
|
||||
0x65, 0xda, 0x32, 0x30, 0x31, 0x32, 0x2d, 0x31, 0x31, 0x2d, 0x31, 0x39,
|
||||
0x20, 0x32, 0x32, 0x3a, 0x34, 0x32, 0x3a, 0x33, 0x30, 0x2e, 0x30, 0x37,
|
||||
0x34, 0x30, 0x35, 0x35, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87,
|
||||
0x02, 0x02, 0x0d, 0xf7, 0xf8, 0x02, 0xb0, 0x04, 0x74, 0x04, 0xec, 0x04,
|
||||
0x00, 0x00, 0x00, 0x00, 0xe8, 0x03, 0x02, 0x02, 0x0d, 0x5c, 0x93, 0x01,
|
||||
0x4a, 0x01, 0x39, 0x01, 0x5a, 0x01, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x0b,
|
||||
0x02, 0x02, 0x0d, 0x63, 0x8c, 0x00, 0xfa, 0x00, 0xed, 0x00, 0x06, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0xa0, 0x0f, 0x01, 0x02, 0x0d, 0xfb, 0xf5, 0x05,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x02, 0x0d, 0xfc, 0xf4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x0d, 0xfd, 0xf3, 0x03,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xfa, 0x82, 0x0b, 0xea, 0x8f, 0xa2, 0x12, 0x00, 0x00, 0x1e, 0x44, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x53, 0x44, 0x42, 0x2d, 0x00, 0x03, 0x01, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01, 0xc4, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x61, 0x74, 0x61,
|
||||
0x2e, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x2e, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc4, 0x46, 0x69, 0x6c, 0x65,
|
||||
0x44, 0x61, 0x74, 0x61, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf,
|
||||
0x46, 0x69, 0x6c, 0x65, 0x44, 0x61, 0x74, 0x61, 0x49, 0x50, 0x4d, 0x49,
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x49, 0x50, 0x4d, 0x49,
|
||||
0x2d, 0x46, 0x52, 0x55, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x01, 0x66, 0x61, 0x6b, 0x65, 0x0a,
|
||||
},
|
||||
};
|
||||
|
||||
struct ff_dev {
|
||||
struct fmc_device *fmc[FF_MAX_MEZZANINES];
|
||||
struct device dev;
|
||||
};
|
||||
|
||||
static struct ff_dev *ff_current_dev; /* We have 1 carrier, 1 slot */
|
||||
|
||||
static int ff_reprogram(struct fmc_device *fmc, struct fmc_driver *drv,
|
||||
char *gw)
|
||||
{
|
||||
const struct firmware *fw;
|
||||
int ret;
|
||||
|
||||
if (!gw) {
|
||||
/* program golden: success */
|
||||
fmc->flags &= ~FMC_DEVICE_HAS_CUSTOM;
|
||||
fmc->flags |= FMC_DEVICE_HAS_GOLDEN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_info(&fmc->dev, "reprogramming with %s\n", gw);
|
||||
ret = request_firmware(&fw, gw, &fmc->dev);
|
||||
if (ret < 0) {
|
||||
dev_warn(&fmc->dev, "request firmware \"%s\": error %i\n",
|
||||
gw, ret);
|
||||
goto out;
|
||||
}
|
||||
fmc->flags &= ~FMC_DEVICE_HAS_GOLDEN;
|
||||
fmc->flags |= FMC_DEVICE_HAS_CUSTOM;
|
||||
|
||||
out:
|
||||
release_firmware(fw);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ff_irq_request(struct fmc_device *fmc, irq_handler_t handler,
|
||||
char *name, int flags)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* FIXME: should also have some fake FMC GPIO mapping */
|
||||
|
||||
|
||||
/*
|
||||
* This work function is called when we changed the eeprom. It removes the
|
||||
* current fmc device and registers a new one, with different identifiers.
|
||||
*/
|
||||
static struct ff_dev *ff_dev_create(void); /* defined later */
|
||||
|
||||
static void ff_work_fn(struct work_struct *work)
|
||||
{
|
||||
struct ff_dev *ff = ff_current_dev;
|
||||
int ret;
|
||||
|
||||
fmc_device_unregister_n(ff->fmc, ff_nr_dev);
|
||||
device_unregister(&ff->dev);
|
||||
ff_current_dev = NULL;
|
||||
|
||||
ff = ff_dev_create();
|
||||
if (IS_ERR(ff)) {
|
||||
pr_warning("%s: can't re-create FMC devices\n", __func__);
|
||||
return;
|
||||
}
|
||||
ret = fmc_device_register_n(ff->fmc, ff_nr_dev);
|
||||
if (ret < 0) {
|
||||
dev_warn(&ff->dev, "can't re-register FMC devices\n");
|
||||
device_unregister(&ff->dev);
|
||||
return;
|
||||
}
|
||||
|
||||
ff_current_dev = ff;
|
||||
}
|
||||
|
||||
static DECLARE_DELAYED_WORK(ff_work, ff_work_fn);
|
||||
|
||||
|
||||
/* low-level i2c */
|
||||
static int ff_eeprom_read(struct fmc_device *fmc, uint32_t offset,
|
||||
void *buf, size_t size)
|
||||
{
|
||||
if (offset > FF_EEPROM_SIZE)
|
||||
return -EINVAL;
|
||||
if (offset + size > FF_EEPROM_SIZE)
|
||||
size = FF_EEPROM_SIZE - offset;
|
||||
memcpy(buf, fmc->eeprom + offset, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
static int ff_eeprom_write(struct fmc_device *fmc, uint32_t offset,
|
||||
const void *buf, size_t size)
|
||||
{
|
||||
if (offset > FF_EEPROM_SIZE)
|
||||
return -EINVAL;
|
||||
if (offset + size > FF_EEPROM_SIZE)
|
||||
size = FF_EEPROM_SIZE - offset;
|
||||
dev_info(&fmc->dev, "write_eeprom: offset %i, size %zi\n",
|
||||
(int)offset, size);
|
||||
memcpy(fmc->eeprom + offset, buf, size);
|
||||
schedule_delayed_work(&ff_work, HZ * 2); /* remove, replug, in 2s */
|
||||
return size;
|
||||
}
|
||||
|
||||
/* i2c operations for fmc */
|
||||
static int ff_read_ee(struct fmc_device *fmc, int pos, void *data, int len)
|
||||
{
|
||||
if (!(fmc->flags & FMC_DEVICE_HAS_GOLDEN))
|
||||
return -EOPNOTSUPP;
|
||||
return ff_eeprom_read(fmc, pos, data, len);
|
||||
}
|
||||
|
||||
static int ff_write_ee(struct fmc_device *fmc, int pos,
|
||||
const void *data, int len)
|
||||
{
|
||||
if (!(fmc->flags & FMC_DEVICE_HAS_GOLDEN))
|
||||
return -EOPNOTSUPP;
|
||||
return ff_eeprom_write(fmc, pos, data, len);
|
||||
}
|
||||
|
||||
/* readl and writel do not do anything. Don't waste RAM with "base" */
|
||||
static uint32_t ff_readl(struct fmc_device *fmc, int offset)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ff_writel(struct fmc_device *fmc, uint32_t value, int offset)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* validate is useful so fmc-write-eeprom will not reprogram every 2 seconds */
|
||||
static int ff_validate(struct fmc_device *fmc, struct fmc_driver *drv)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!drv->busid_n)
|
||||
return 0; /* everyhing is valid */
|
||||
for (i = 0; i < drv->busid_n; i++)
|
||||
if (drv->busid_val[i] == fmc->device_id)
|
||||
return i;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct fmc_operations ff_fmc_operations = {
|
||||
.read32 = ff_readl,
|
||||
.write32 = ff_writel,
|
||||
.reprogram = ff_reprogram,
|
||||
.irq_request = ff_irq_request,
|
||||
.read_ee = ff_read_ee,
|
||||
.write_ee = ff_write_ee,
|
||||
.validate = ff_validate,
|
||||
};
|
||||
|
||||
/* This device is kmalloced: release it */
|
||||
static void ff_dev_release(struct device *dev)
|
||||
{
|
||||
struct ff_dev *ff = container_of(dev, struct ff_dev, dev);
|
||||
kfree(ff);
|
||||
}
|
||||
|
||||
static struct fmc_device ff_template_fmc = {
|
||||
.version = FMC_VERSION,
|
||||
.owner = THIS_MODULE,
|
||||
.carrier_name = "fake-fmc-carrier",
|
||||
.device_id = 0xf001, /* fool */
|
||||
.eeprom_len = sizeof(ff_eeimg[0]),
|
||||
.memlen = 0x1000, /* 4k, to show something */
|
||||
.op = &ff_fmc_operations,
|
||||
.hwdev = NULL, /* filled at creation time */
|
||||
.flags = FMC_DEVICE_HAS_GOLDEN,
|
||||
};
|
||||
|
||||
static struct ff_dev *ff_dev_create(void)
|
||||
{
|
||||
struct ff_dev *ff;
|
||||
struct fmc_device *fmc;
|
||||
int i, ret;
|
||||
|
||||
ff = kzalloc(sizeof(*ff), GFP_KERNEL);
|
||||
if (!ff)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
dev_set_name(&ff->dev, "fake-fmc-carrier");
|
||||
ff->dev.release = ff_dev_release;
|
||||
|
||||
ret = device_register(&ff->dev);
|
||||
if (ret < 0) {
|
||||
put_device(&ff->dev);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/* Create fmc structures that refer to this new "hw" device */
|
||||
for (i = 0; i < ff_nr_dev; i++) {
|
||||
fmc = kmemdup(&ff_template_fmc, sizeof(ff_template_fmc),
|
||||
GFP_KERNEL);
|
||||
fmc->hwdev = &ff->dev;
|
||||
fmc->carrier_data = ff;
|
||||
fmc->nr_slots = ff_nr_dev;
|
||||
/* the following fields are different for each slot */
|
||||
fmc->eeprom = ff_eeimg[i];
|
||||
fmc->eeprom_addr = 0x50 + 2 * i;
|
||||
fmc->slot_id = i;
|
||||
ff->fmc[i] = fmc;
|
||||
/* increment the identifier, each must be different */
|
||||
ff_template_fmc.device_id++;
|
||||
}
|
||||
return ff;
|
||||
}
|
||||
|
||||
/* init and exit */
|
||||
static int ff_init(void)
|
||||
{
|
||||
struct ff_dev *ff;
|
||||
const struct firmware *fw;
|
||||
int i, len, ret = 0;
|
||||
|
||||
/* Replicate the default eeprom for the max number of mezzanines */
|
||||
for (i = 1; i < FF_MAX_MEZZANINES; i++)
|
||||
memcpy(ff_eeimg[i], ff_eeimg[0], sizeof(ff_eeimg[0]));
|
||||
|
||||
if (ff_nr_eeprom > ff_nr_dev)
|
||||
ff_nr_dev = ff_nr_eeprom;
|
||||
|
||||
ff = ff_dev_create();
|
||||
if (IS_ERR(ff))
|
||||
return PTR_ERR(ff);
|
||||
|
||||
/* If the user passed "eeprom=" as a parameter, fetch them */
|
||||
for (i = 0; i < ff_nr_eeprom; i++) {
|
||||
if (!strlen(ff_eeprom[i]))
|
||||
continue;
|
||||
ret = request_firmware(&fw, ff_eeprom[i], &ff->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&ff->dev, "Mezzanine %i: can't load \"%s\" "
|
||||
"(error %i)\n", i, ff_eeprom[i], -ret);
|
||||
} else {
|
||||
len = min_t(size_t, fw->size, (size_t)FF_EEPROM_SIZE);
|
||||
memcpy(ff_eeimg[i], fw->data, len);
|
||||
release_firmware(fw);
|
||||
dev_info(&ff->dev, "Mezzanine %i: eeprom \"%s\"\n", i,
|
||||
ff_eeprom[i]);
|
||||
}
|
||||
}
|
||||
|
||||
ret = fmc_device_register_n(ff->fmc, ff_nr_dev);
|
||||
if (ret) {
|
||||
device_unregister(&ff->dev);
|
||||
return ret;
|
||||
}
|
||||
ff_current_dev = ff;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ff_exit(void)
|
||||
{
|
||||
if (ff_current_dev) {
|
||||
fmc_device_unregister_n(ff_current_dev->fmc, ff_nr_dev);
|
||||
device_unregister(&ff_current_dev->dev);
|
||||
}
|
||||
cancel_delayed_work_sync(&ff_work);
|
||||
}
|
||||
|
||||
module_init(ff_init);
|
||||
module_exit(ff_exit);
|
||||
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
114
drivers/fmc/fmc-match.c
Normal file
114
drivers/fmc/fmc-match.c
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
266
drivers/fmc/fmc-sdb.c
Normal file
266
drivers/fmc/fmc-sdb.c
Normal file
@ -0,0 +1,266 @@
|
||||
/*
|
||||
* 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);
|
107
drivers/fmc/fmc-trivial.c
Normal file
107
drivers/fmc/fmc-trivial.c
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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");
|
176
drivers/fmc/fmc-write-eeprom.c
Normal file
176
drivers/fmc/fmc-write-eeprom.c
Normal file
@ -0,0 +1,176 @@
|
||||
/*
|
||||
* 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");
|
82
drivers/fmc/fru-parse.c
Normal file
82
drivers/fmc/fru-parse.c
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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));
|
||||
|
||||
spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
|
||||
list_del(&channel->listentry);
|
||||
spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
|
||||
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 @@ cleanup:
|
||||
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
|
||||
|
340
drivers/memory/mvebu-devbus.c
Normal file
340
drivers/memory/mvebu-devbus.c
Normal file
@ -0,0 +1,340 @@
|
||||
/*
|
||||
* 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 @@ out_return:
|
||||
|
||||
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 @@ err_clients:
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -39,63 +39,6 @@ struct ep93xx_pwm {
|
||||
u32 duty_percent;
|
||||
};
|
||||
|
||||
static inline void ep93xx_pwm_writel(struct ep93xx_pwm *pwm,
|
||||
unsigned int val, unsigned int off)
|
||||
{
|
||||
__raw_writel(val, pwm->mmio_base + off);
|
||||
}
|
||||
|
||||
static inline unsigned int ep93xx_pwm_readl(struct ep93xx_pwm *pwm,
|
||||
unsigned int off)
|
||||
{
|
||||
return __raw_readl(pwm->mmio_base + off);
|
||||
}
|
||||
|
||||
static inline void ep93xx_pwm_write_tc(struct ep93xx_pwm *pwm, u16 value)
|
||||
{
|
||||
ep93xx_pwm_writel(pwm, value, EP93XX_PWMx_TERM_COUNT);
|
||||
}
|
||||
|
||||
static inline u16 ep93xx_pwm_read_tc(struct ep93xx_pwm *pwm)
|
||||
{
|
||||
return ep93xx_pwm_readl(pwm, EP93XX_PWMx_TERM_COUNT);
|
||||
}
|
||||
|
||||
static inline void ep93xx_pwm_write_dc(struct ep93xx_pwm *pwm, u16 value)
|
||||
{
|
||||
ep93xx_pwm_writel(pwm, value, EP93XX_PWMx_DUTY_CYCLE);
|
||||
}
|
||||
|
||||
static inline void ep93xx_pwm_enable(struct ep93xx_pwm *pwm)
|
||||
{
|
||||
ep93xx_pwm_writel(pwm, 0x1, EP93XX_PWMx_ENABLE);
|
||||
}
|
||||
|
||||
static inline void ep93xx_pwm_disable(struct ep93xx_pwm *pwm)
|
||||
{
|
||||
ep93xx_pwm_writel(pwm, 0x0, EP93XX_PWMx_ENABLE);
|
||||
}
|
||||
|
||||
static inline int ep93xx_pwm_is_enabled(struct ep93xx_pwm *pwm)
|
||||
{
|
||||
return ep93xx_pwm_readl(pwm, EP93XX_PWMx_ENABLE) & 0x1;
|
||||
}
|
||||
|
||||
static inline void ep93xx_pwm_invert(struct ep93xx_pwm *pwm)
|
||||
{
|
||||
ep93xx_pwm_writel(pwm, 0x1, EP93XX_PWMx_INVERT);
|
||||
}
|
||||
|
||||
static inline void ep93xx_pwm_normal(struct ep93xx_pwm *pwm)
|
||||
{
|
||||
ep93xx_pwm_writel(pwm, 0x0, EP93XX_PWMx_INVERT);
|
||||
}
|
||||
|
||||
static inline int ep93xx_pwm_is_inverted(struct ep93xx_pwm *pwm)
|
||||
{
|
||||
return ep93xx_pwm_readl(pwm, EP93XX_PWMx_INVERT) & 0x1;
|
||||
}
|
||||
|
||||
/*
|
||||
* /sys/devices/platform/ep93xx-pwm.N
|
||||
* /min_freq read-only minimum pwm output frequency
|
||||
@ -131,9 +74,9 @@ static ssize_t ep93xx_pwm_get_freq(struct device *dev,
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
|
||||
|
||||
if (ep93xx_pwm_is_enabled(pwm)) {
|
||||
if (readl(pwm->mmio_base + EP93XX_PWMx_ENABLE) & 0x1) {
|
||||
unsigned long rate = clk_get_rate(pwm->clk);
|
||||
u16 term = ep93xx_pwm_read_tc(pwm);
|
||||
u16 term = readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT);
|
||||
|
||||
return sprintf(buf, "%ld\n", rate / (term + 1));
|
||||
} else {
|
||||
@ -149,12 +92,12 @@ static ssize_t ep93xx_pwm_set_freq(struct device *dev,
|
||||
long val;
|
||||
int err;
|
||||
|
||||
err = strict_strtol(buf, 10, &val);
|
||||
err = kstrtol(buf, 10, &val);
|
||||
if (err)
|
||||
return -EINVAL;
|
||||
|
||||
if (val == 0) {
|
||||
ep93xx_pwm_disable(pwm);
|
||||
writel(0x0, pwm->mmio_base + EP93XX_PWMx_ENABLE);
|
||||
} else if (val <= (clk_get_rate(pwm->clk) / 2)) {
|
||||
u32 term, duty;
|
||||
|
||||
@ -164,20 +107,20 @@ static ssize_t ep93xx_pwm_set_freq(struct device *dev,
|
||||
if (val < 1)
|
||||
val = 1;
|
||||
|
||||
term = ep93xx_pwm_read_tc(pwm);
|
||||
term = readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT);
|
||||
duty = ((val + 1) * pwm->duty_percent / 100) - 1;
|
||||
|
||||
/* If pwm is running, order is important */
|
||||
if (val > term) {
|
||||
ep93xx_pwm_write_tc(pwm, val);
|
||||
ep93xx_pwm_write_dc(pwm, duty);
|
||||
writel(val, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT);
|
||||
writel(duty, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE);
|
||||
} else {
|
||||
ep93xx_pwm_write_dc(pwm, duty);
|
||||
ep93xx_pwm_write_tc(pwm, val);
|
||||
writel(duty, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE);
|
||||
writel(val, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT);
|
||||
}
|
||||
|
||||
if (!ep93xx_pwm_is_enabled(pwm))
|
||||
ep93xx_pwm_enable(pwm);
|
||||
if (!readl(pwm->mmio_base + EP93XX_PWMx_ENABLE) & 0x1)
|
||||
writel(0x1, pwm->mmio_base + EP93XX_PWMx_ENABLE);
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -202,13 +145,15 @@ static ssize_t ep93xx_pwm_set_duty_percent(struct device *dev,
|
||||
long val;
|
||||
int err;
|
||||
|
||||
err = strict_strtol(buf, 10, &val);
|
||||
err = kstrtol(buf, 10, &val);
|
||||
if (err)
|
||||
return -EINVAL;
|
||||
|
||||
if (val > 0 && val < 100) {
|
||||
u32 term = ep93xx_pwm_read_tc(pwm);
|
||||
ep93xx_pwm_write_dc(pwm, ((term + 1) * val / 100) - 1);
|
||||
u32 term = readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT);
|
||||
u32 duty = ((term + 1) * val / 100) - 1;
|
||||
|
||||
writel(duty, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE);
|
||||
pwm->duty_percent = val;
|
||||
return count;
|
||||
}
|
||||
@ -221,8 +166,9 @@ static ssize_t ep93xx_pwm_get_invert(struct device *dev,
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
|
||||
int inverted = readl(pwm->mmio_base + EP93XX_PWMx_INVERT) & 0x1;
|
||||
|
||||
return sprintf(buf, "%d\n", ep93xx_pwm_is_inverted(pwm));
|
||||
return sprintf(buf, "%d\n", inverted);
|
||||
}
|
||||
|
||||
static ssize_t ep93xx_pwm_set_invert(struct device *dev,
|
||||
@ -233,14 +179,14 @@ static ssize_t ep93xx_pwm_set_invert(struct device *dev,
|
||||
long val;
|
||||
int err;
|
||||
|
||||
err = strict_strtol(buf, 10, &val);
|
||||
err = kstrtol(buf, 10, &val);
|
||||
if (err)
|
||||
return -EINVAL;
|
||||
|
||||
if (val == 0)
|
||||
ep93xx_pwm_normal(pwm);
|
||||
writel(0x0, pwm->mmio_base + EP93XX_PWMx_INVERT);
|
||||
else if (val == 1)
|
||||
ep93xx_pwm_invert(pwm);
|
||||
writel(0x1, pwm->mmio_base + EP93XX_PWMx_INVERT);
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
@ -269,89 +215,55 @@ static const struct attribute_group ep93xx_pwm_sysfs_files = {
|
||||
.attrs = ep93xx_pwm_attrs,
|
||||
};
|
||||
|
||||
static int __init ep93xx_pwm_probe(struct platform_device *pdev)
|
||||
static int ep93xx_pwm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ep93xx_pwm *pwm;
|
||||
struct resource *res;
|
||||
int err;
|
||||
int ret;
|
||||
|
||||
err = ep93xx_pwm_acquire_gpio(pdev);
|
||||
if (err)
|
||||
return err;
|
||||
pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
|
||||
if (!pwm)
|
||||
return -ENOMEM;
|
||||
|
||||
pwm = kzalloc(sizeof(struct ep93xx_pwm), GFP_KERNEL);
|
||||
if (!pwm) {
|
||||
err = -ENOMEM;
|
||||
goto fail_no_mem;
|
||||
}
|
||||
pwm->clk = devm_clk_get(&pdev->dev, "pwm_clk");
|
||||
if (IS_ERR(pwm->clk))
|
||||
return PTR_ERR(pwm->clk);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (res == NULL) {
|
||||
err = -ENXIO;
|
||||
goto fail_no_mem_resource;
|
||||
}
|
||||
pwm->mmio_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(pwm->mmio_base))
|
||||
return PTR_ERR(pwm->mmio_base);
|
||||
|
||||
res = request_mem_region(res->start, resource_size(res), pdev->name);
|
||||
if (res == NULL) {
|
||||
err = -EBUSY;
|
||||
goto fail_no_mem_resource;
|
||||
}
|
||||
ret = ep93xx_pwm_acquire_gpio(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pwm->mmio_base = ioremap(res->start, resource_size(res));
|
||||
if (pwm->mmio_base == NULL) {
|
||||
err = -ENXIO;
|
||||
goto fail_no_ioremap;
|
||||
}
|
||||
|
||||
err = sysfs_create_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files);
|
||||
if (err)
|
||||
goto fail_no_sysfs;
|
||||
|
||||
pwm->clk = clk_get(&pdev->dev, "pwm_clk");
|
||||
if (IS_ERR(pwm->clk)) {
|
||||
err = PTR_ERR(pwm->clk);
|
||||
goto fail_no_clk;
|
||||
ret = sysfs_create_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files);
|
||||
if (ret) {
|
||||
ep93xx_pwm_release_gpio(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pwm->duty_percent = 50;
|
||||
|
||||
platform_set_drvdata(pdev, pwm);
|
||||
|
||||
/* disable pwm at startup. Avoids zero value. */
|
||||
ep93xx_pwm_disable(pwm);
|
||||
ep93xx_pwm_write_tc(pwm, EP93XX_PWM_MAX_COUNT);
|
||||
ep93xx_pwm_write_dc(pwm, EP93XX_PWM_MAX_COUNT / 2);
|
||||
writel(0x0, pwm->mmio_base + EP93XX_PWMx_ENABLE);
|
||||
writel(EP93XX_PWM_MAX_COUNT, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT);
|
||||
writel(EP93XX_PWM_MAX_COUNT/2, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE);
|
||||
|
||||
clk_enable(pwm->clk);
|
||||
|
||||
platform_set_drvdata(pdev, pwm);
|
||||
return 0;
|
||||
|
||||
fail_no_clk:
|
||||
sysfs_remove_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files);
|
||||
fail_no_sysfs:
|
||||
iounmap(pwm->mmio_base);
|
||||
fail_no_ioremap:
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
fail_no_mem_resource:
|
||||
kfree(pwm);
|
||||
fail_no_mem:
|
||||
ep93xx_pwm_release_gpio(pdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __exit ep93xx_pwm_remove(struct platform_device *pdev)
|
||||
static int ep93xx_pwm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
|
||||
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
ep93xx_pwm_disable(pwm);
|
||||
writel(0x0, pwm->mmio_base + EP93XX_PWMx_ENABLE);
|
||||
clk_disable(pwm->clk);
|
||||
clk_put(pwm->clk);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files);
|
||||
iounmap(pwm->mmio_base);
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
kfree(pwm);
|
||||
ep93xx_pwm_release_gpio(pdev);
|
||||
|
||||
return 0;
|
||||
@ -362,10 +274,10 @@ static struct platform_driver ep93xx_pwm_driver = {
|
||||
.name = "ep93xx-pwm",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.remove = __exit_p(ep93xx_pwm_remove),
|
||||
.probe = ep93xx_pwm_probe,
|
||||
.remove = ep93xx_pwm_remove,
|
||||
};
|
||||
|
||||
module_platform_driver_probe(ep93xx_pwm_driver, ep93xx_pwm_probe);
|
||||
module_platform_driver(ep93xx_pwm_driver);
|
||||
|
||||
MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>, "
|
||||
"H Hartley Sweeten <hsweeten@visionengravers.com>");
|
||||
|
@ -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);
|
||||
|
||||
|
@ -485,7 +485,6 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_cl_cb *cb;
|
||||
long timeout = mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT);
|
||||
int rets;
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
@ -518,7 +517,7 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
|
||||
rets = wait_event_timeout(dev->wait_recvd_msg,
|
||||
(cl->state == MEI_FILE_CONNECTED ||
|
||||
cl->state == MEI_FILE_DISCONNECTED),
|
||||
timeout * HZ);
|
||||
mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
if (cl->state != MEI_FILE_CONNECTED) {
|
||||
@ -681,6 +680,68 @@ err:
|
||||
return rets;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_cl_irq_write_complete - write a message to device
|
||||
* from the interrupt thread context
|
||||
*
|
||||
* @cl: client
|
||||
* @cb: callback block.
|
||||
* @slots: free slots.
|
||||
* @cmpl_list: complete list.
|
||||
*
|
||||
* returns 0, OK; otherwise error.
|
||||
*/
|
||||
int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
s32 *slots, struct mei_cl_cb *cmpl_list)
|
||||
{
|
||||
struct mei_device *dev = cl->dev;
|
||||
struct mei_msg_hdr mei_hdr;
|
||||
size_t len = cb->request_buffer.size - cb->buf_idx;
|
||||
u32 msg_slots = mei_data2slots(len);
|
||||
|
||||
mei_hdr.host_addr = cl->host_client_id;
|
||||
mei_hdr.me_addr = cl->me_client_id;
|
||||
mei_hdr.reserved = 0;
|
||||
|
||||
if (*slots >= msg_slots) {
|
||||
mei_hdr.length = len;
|
||||
mei_hdr.msg_complete = 1;
|
||||
/* Split the message only if we can write the whole host buffer */
|
||||
} else if (*slots == dev->hbuf_depth) {
|
||||
msg_slots = *slots;
|
||||
len = (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
|
||||
mei_hdr.length = len;
|
||||
mei_hdr.msg_complete = 0;
|
||||
} else {
|
||||
/* wait for next time the host buffer is empty */
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_dbg(&dev->pdev->dev, "buf: size = %d idx = %lu\n",
|
||||
cb->request_buffer.size, cb->buf_idx);
|
||||
dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr));
|
||||
|
||||
*slots -= msg_slots;
|
||||
if (mei_write_message(dev, &mei_hdr,
|
||||
cb->request_buffer.data + cb->buf_idx)) {
|
||||
cl->status = -ENODEV;
|
||||
list_move_tail(&cb->list, &cmpl_list->list);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
cl->status = 0;
|
||||
cl->writing_state = MEI_WRITING;
|
||||
cb->buf_idx += mei_hdr.length;
|
||||
|
||||
if (mei_hdr.msg_complete) {
|
||||
if (mei_cl_flow_ctrl_reduce(cl))
|
||||
return -ENODEV;
|
||||
list_move_tail(&cb->list, &dev->write_waiting_list.list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_cl_write - submit a write cb to mei device
|
||||
assumes device_lock is locked
|
||||
@ -723,7 +784,6 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
|
||||
cb->buf_idx = 0;
|
||||
/* unseting complete will enqueue the cb for write */
|
||||
mei_hdr.msg_complete = 0;
|
||||
cl->writing_state = MEI_WRITING;
|
||||
rets = buf->size;
|
||||
goto out;
|
||||
}
|
||||
@ -785,6 +845,32 @@ err:
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mei_cl_complete - processes completed operation for a client
|
||||
*
|
||||
* @cl: private data of the file object.
|
||||
* @cb: callback block.
|
||||
*/
|
||||
void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
|
||||
{
|
||||
if (cb->fop_type == MEI_FOP_WRITE) {
|
||||
mei_io_cb_free(cb);
|
||||
cb = NULL;
|
||||
cl->writing_state = MEI_WRITE_COMPLETE;
|
||||
if (waitqueue_active(&cl->tx_wait))
|
||||
wake_up_interruptible(&cl->tx_wait);
|
||||
|
||||
} else if (cb->fop_type == MEI_FOP_READ &&
|
||||
MEI_READING == cl->reading_state) {
|
||||
cl->reading_state = MEI_READ_COMPLETE;
|
||||
if (waitqueue_active(&cl->rx_wait))
|
||||
wake_up_interruptible(&cl->rx_wait);
|
||||
else
|
||||
mei_cl_bus_rx_event(cl);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mei_cl_all_disconnect - disconnect forcefully all connected clients
|
||||
|
@ -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);
|
||||
|
||||
|
@ -535,6 +535,20 @@ static void mei_hbm_fw_disconnect_req(struct mei_device *dev,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mei_hbm_version_is_supported - checks whether the driver can
|
||||
* support the hbm version of the device
|
||||
*
|
||||
* @dev: the device structure
|
||||
* returns true if driver can support hbm version of the device
|
||||
*/
|
||||
bool mei_hbm_version_is_supported(struct mei_device *dev)
|
||||
{
|
||||
return (dev->version.major_version < HBM_MAJOR_VERSION) ||
|
||||
(dev->version.major_version == HBM_MAJOR_VERSION &&
|
||||
dev->version.minor_version <= HBM_MINOR_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_hbm_dispatch - bottom half read routine after ISR to
|
||||
* handle the read bus message cmd processing.
|
||||
@ -562,9 +576,24 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
|
||||
switch (mei_msg->hbm_cmd) {
|
||||
case HOST_START_RES_CMD:
|
||||
version_res = (struct hbm_host_version_response *)mei_msg;
|
||||
if (!version_res->host_version_supported) {
|
||||
dev->version = version_res->me_max_version;
|
||||
dev_dbg(&dev->pdev->dev, "version mismatch.\n");
|
||||
|
||||
dev_dbg(&dev->pdev->dev, "HBM VERSION: DRIVER=%02d:%02d DEVICE=%02d:%02d\n",
|
||||
HBM_MAJOR_VERSION, HBM_MINOR_VERSION,
|
||||
version_res->me_max_version.major_version,
|
||||
version_res->me_max_version.minor_version);
|
||||
|
||||
if (version_res->host_version_supported) {
|
||||
dev->version.major_version = HBM_MAJOR_VERSION;
|
||||
dev->version.minor_version = HBM_MINOR_VERSION;
|
||||
} else {
|
||||
dev->version.major_version =
|
||||
version_res->me_max_version.major_version;
|
||||
dev->version.minor_version =
|
||||
version_res->me_max_version.minor_version;
|
||||
}
|
||||
|
||||
if (!mei_hbm_version_is_supported(dev)) {
|
||||
dev_warn(&dev->pdev->dev, "hbm version mismatch: stopping the driver.\n");
|
||||
|
||||
dev->hbm_state = MEI_HBM_STOP;
|
||||
mei_hbm_stop_req_prepare(dev, &dev->wr_msg.hdr,
|
||||
@ -575,8 +604,6 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
|
||||
return;
|
||||
}
|
||||
|
||||
dev->version.major_version = HBM_MAJOR_VERSION;
|
||||
dev->version.minor_version = HBM_MINOR_VERSION;
|
||||
if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
|
||||
dev->hbm_state == MEI_HBM_START) {
|
||||
dev->init_clients_timer = 0;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -106,8 +106,7 @@ int mei_start(struct mei_device *dev)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (dev->version.major_version != HBM_MAJOR_VERSION ||
|
||||
dev->version.minor_version != HBM_MINOR_VERSION) {
|
||||
if (!mei_hbm_version_is_supported(dev)) {
|
||||
dev_dbg(&dev->pdev->dev, "MEI start failed.\n");
|
||||
goto err;
|
||||
}
|
||||
@ -133,13 +132,19 @@ EXPORT_SYMBOL_GPL(mei_start);
|
||||
void mei_reset(struct mei_device *dev, int interrupts_enabled)
|
||||
{
|
||||
bool unexpected;
|
||||
int ret;
|
||||
|
||||
unexpected = (dev->dev_state != MEI_DEV_INITIALIZING &&
|
||||
dev->dev_state != MEI_DEV_DISABLED &&
|
||||
dev->dev_state != MEI_DEV_POWER_DOWN &&
|
||||
dev->dev_state != MEI_DEV_POWER_UP);
|
||||
|
||||
mei_hw_reset(dev, interrupts_enabled);
|
||||
ret = mei_hw_reset(dev, interrupts_enabled);
|
||||
if (ret) {
|
||||
dev_err(&dev->pdev->dev, "hw reset failed disabling the device\n");
|
||||
interrupts_enabled = false;
|
||||
dev->dev_state = MEI_DEV_DISABLED;
|
||||
}
|
||||
|
||||
dev->hbm_state = MEI_HBM_IDLE;
|
||||
|
||||
@ -176,7 +181,12 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
|
||||
return;
|
||||
}
|
||||
|
||||
mei_hw_start(dev);
|
||||
ret = mei_hw_start(dev);
|
||||
if (ret) {
|
||||
dev_err(&dev->pdev->dev, "hw_start failed disabling the device\n");
|
||||
dev->dev_state = MEI_DEV_DISABLED;
|
||||
return;
|
||||
}
|
||||
|
||||
dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n");
|
||||
/* link is established * start sending messages. */
|
||||
|
@ -30,32 +30,6 @@
|
||||
#include "client.h"
|
||||
|
||||
|
||||
/**
|
||||
* mei_cl_complete_handler - processes completed operation for a client
|
||||
*
|
||||
* @cl: private data of the file object.
|
||||
* @cb: callback block.
|
||||
*/
|
||||
static void mei_cl_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb)
|
||||
{
|
||||
if (cb->fop_type == MEI_FOP_WRITE) {
|
||||
mei_io_cb_free(cb);
|
||||
cb = NULL;
|
||||
cl->writing_state = MEI_WRITE_COMPLETE;
|
||||
if (waitqueue_active(&cl->tx_wait))
|
||||
wake_up_interruptible(&cl->tx_wait);
|
||||
|
||||
} else if (cb->fop_type == MEI_FOP_READ &&
|
||||
MEI_READING == cl->reading_state) {
|
||||
cl->reading_state = MEI_READ_COMPLETE;
|
||||
if (waitqueue_active(&cl->rx_wait))
|
||||
wake_up_interruptible(&cl->rx_wait);
|
||||
else
|
||||
mei_cl_bus_rx_event(cl);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_irq_compl_handler - dispatch complete handelers
|
||||
* for the completed callbacks
|
||||
@ -78,7 +52,7 @@ void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *compl_list)
|
||||
if (cl == &dev->iamthif_cl)
|
||||
mei_amthif_complete(dev, cb);
|
||||
else
|
||||
mei_cl_complete_handler(cl, cb);
|
||||
mei_cl_complete(cl, cb);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_irq_compl_handler);
|
||||
@ -189,21 +163,21 @@ static int mei_cl_irq_read_msg(struct mei_device *dev,
|
||||
}
|
||||
|
||||
/**
|
||||
* _mei_irq_thread_close - processes close related operation.
|
||||
* mei_cl_irq_close - processes close related operation from
|
||||
* interrupt thread context - send disconnect request
|
||||
*
|
||||
* @dev: the device structure.
|
||||
* @cl: client
|
||||
* @cb: callback block.
|
||||
* @slots: free slots.
|
||||
* @cb_pos: callback block.
|
||||
* @cl: private data of the file object.
|
||||
* @cmpl_list: complete list.
|
||||
*
|
||||
* returns 0, OK; otherwise, error.
|
||||
*/
|
||||
static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots,
|
||||
struct mei_cl_cb *cb_pos,
|
||||
struct mei_cl *cl,
|
||||
struct mei_cl_cb *cmpl_list)
|
||||
static int mei_cl_irq_close(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
s32 *slots, struct mei_cl_cb *cmpl_list)
|
||||
{
|
||||
struct mei_device *dev = cl->dev;
|
||||
|
||||
u32 msg_slots =
|
||||
mei_data2slots(sizeof(struct hbm_client_connect_request));
|
||||
|
||||
@ -214,15 +188,15 @@ static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots,
|
||||
|
||||
if (mei_hbm_cl_disconnect_req(dev, cl)) {
|
||||
cl->status = 0;
|
||||
cb_pos->buf_idx = 0;
|
||||
list_move_tail(&cb_pos->list, &cmpl_list->list);
|
||||
cb->buf_idx = 0;
|
||||
list_move_tail(&cb->list, &cmpl_list->list);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
cl->state = MEI_FILE_DISCONNECTING;
|
||||
cl->status = 0;
|
||||
cb_pos->buf_idx = 0;
|
||||
list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list);
|
||||
cb->buf_idx = 0;
|
||||
list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
|
||||
cl->timer_count = MEI_CONNECT_TIMEOUT;
|
||||
|
||||
return 0;
|
||||
@ -230,26 +204,26 @@ static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots,
|
||||
|
||||
|
||||
/**
|
||||
* _mei_irq_thread_read - processes read related operation.
|
||||
* mei_cl_irq_close - processes client read related operation from the
|
||||
* interrupt thread context - request for flow control credits
|
||||
*
|
||||
* @dev: the device structure.
|
||||
* @cl: client
|
||||
* @cb: callback block.
|
||||
* @slots: free slots.
|
||||
* @cb_pos: callback block.
|
||||
* @cl: private data of the file object.
|
||||
* @cmpl_list: complete list.
|
||||
*
|
||||
* returns 0, OK; otherwise, error.
|
||||
*/
|
||||
static int _mei_irq_thread_read(struct mei_device *dev, s32 *slots,
|
||||
struct mei_cl_cb *cb_pos,
|
||||
struct mei_cl *cl,
|
||||
struct mei_cl_cb *cmpl_list)
|
||||
static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
s32 *slots, struct mei_cl_cb *cmpl_list)
|
||||
{
|
||||
struct mei_device *dev = cl->dev;
|
||||
|
||||
u32 msg_slots = mei_data2slots(sizeof(struct hbm_flow_control));
|
||||
|
||||
if (*slots < msg_slots) {
|
||||
/* return the cancel routine */
|
||||
list_del(&cb_pos->list);
|
||||
list_del(&cb->list);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
@ -257,38 +231,38 @@ static int _mei_irq_thread_read(struct mei_device *dev, s32 *slots,
|
||||
|
||||
if (mei_hbm_cl_flow_control_req(dev, cl)) {
|
||||
cl->status = -ENODEV;
|
||||
cb_pos->buf_idx = 0;
|
||||
list_move_tail(&cb_pos->list, &cmpl_list->list);
|
||||
cb->buf_idx = 0;
|
||||
list_move_tail(&cb->list, &cmpl_list->list);
|
||||
return -ENODEV;
|
||||
}
|
||||
list_move_tail(&cb_pos->list, &dev->read_list.list);
|
||||
list_move_tail(&cb->list, &dev->read_list.list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* _mei_irq_thread_ioctl - processes ioctl related operation.
|
||||
* mei_cl_irq_ioctl - processes client ioctl related operation from the
|
||||
* interrupt thread context - send connection request
|
||||
*
|
||||
* @dev: the device structure.
|
||||
* @cl: client
|
||||
* @cb: callback block.
|
||||
* @slots: free slots.
|
||||
* @cb_pos: callback block.
|
||||
* @cl: private data of the file object.
|
||||
* @cmpl_list: complete list.
|
||||
*
|
||||
* returns 0, OK; otherwise, error.
|
||||
*/
|
||||
static int _mei_irq_thread_ioctl(struct mei_device *dev, s32 *slots,
|
||||
struct mei_cl_cb *cb_pos,
|
||||
struct mei_cl *cl,
|
||||
struct mei_cl_cb *cmpl_list)
|
||||
static int mei_cl_irq_ioctl(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
s32 *slots, struct mei_cl_cb *cmpl_list)
|
||||
{
|
||||
struct mei_device *dev = cl->dev;
|
||||
|
||||
u32 msg_slots =
|
||||
mei_data2slots(sizeof(struct hbm_client_connect_request));
|
||||
|
||||
if (*slots < msg_slots) {
|
||||
/* return the cancel routine */
|
||||
list_del(&cb_pos->list);
|
||||
list_del(&cb->list);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
@ -298,76 +272,17 @@ static int _mei_irq_thread_ioctl(struct mei_device *dev, s32 *slots,
|
||||
|
||||
if (mei_hbm_cl_connect_req(dev, cl)) {
|
||||
cl->status = -ENODEV;
|
||||
cb_pos->buf_idx = 0;
|
||||
list_del(&cb_pos->list);
|
||||
return -ENODEV;
|
||||
} else {
|
||||
list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list);
|
||||
cl->timer_count = MEI_CONNECT_TIMEOUT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_irq_thread_write_complete - write messages to device.
|
||||
*
|
||||
* @dev: the device structure.
|
||||
* @slots: free slots.
|
||||
* @cb: callback block.
|
||||
* @cmpl_list: complete list.
|
||||
*
|
||||
* returns 0, OK; otherwise, error.
|
||||
*/
|
||||
static int mei_irq_thread_write_complete(struct mei_device *dev, s32 *slots,
|
||||
struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list)
|
||||
{
|
||||
struct mei_msg_hdr mei_hdr;
|
||||
struct mei_cl *cl = cb->cl;
|
||||
size_t len = cb->request_buffer.size - cb->buf_idx;
|
||||
u32 msg_slots = mei_data2slots(len);
|
||||
|
||||
mei_hdr.host_addr = cl->host_client_id;
|
||||
mei_hdr.me_addr = cl->me_client_id;
|
||||
mei_hdr.reserved = 0;
|
||||
|
||||
if (*slots >= msg_slots) {
|
||||
mei_hdr.length = len;
|
||||
mei_hdr.msg_complete = 1;
|
||||
/* Split the message only if we can write the whole host buffer */
|
||||
} else if (*slots == dev->hbuf_depth) {
|
||||
msg_slots = *slots;
|
||||
len = (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
|
||||
mei_hdr.length = len;
|
||||
mei_hdr.msg_complete = 0;
|
||||
} else {
|
||||
/* wait for next time the host buffer is empty */
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_dbg(&dev->pdev->dev, "buf: size = %d idx = %lu\n",
|
||||
cb->request_buffer.size, cb->buf_idx);
|
||||
dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr));
|
||||
|
||||
*slots -= msg_slots;
|
||||
if (mei_write_message(dev, &mei_hdr,
|
||||
cb->request_buffer.data + cb->buf_idx)) {
|
||||
cl->status = -ENODEV;
|
||||
list_move_tail(&cb->list, &cmpl_list->list);
|
||||
cb->buf_idx = 0;
|
||||
list_del(&cb->list);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
cl->status = 0;
|
||||
cb->buf_idx += mei_hdr.length;
|
||||
if (mei_hdr.msg_complete) {
|
||||
if (mei_cl_flow_ctrl_reduce(cl))
|
||||
return -ENODEV;
|
||||
list_move_tail(&cb->list, &dev->write_waiting_list.list);
|
||||
}
|
||||
|
||||
list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
|
||||
cl->timer_count = MEI_CONNECT_TIMEOUT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mei_irq_read_handler - bottom half read routine after ISR to
|
||||
* handle the read processing.
|
||||
@ -481,7 +396,7 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)
|
||||
{
|
||||
|
||||
struct mei_cl *cl;
|
||||
struct mei_cl_cb *pos = NULL, *next = NULL;
|
||||
struct mei_cl_cb *cb, *next;
|
||||
struct mei_cl_cb *list;
|
||||
s32 slots;
|
||||
int ret;
|
||||
@ -498,19 +413,19 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)
|
||||
dev_dbg(&dev->pdev->dev, "complete all waiting for write cb.\n");
|
||||
|
||||
list = &dev->write_waiting_list;
|
||||
list_for_each_entry_safe(pos, next, &list->list, list) {
|
||||
cl = pos->cl;
|
||||
list_for_each_entry_safe(cb, next, &list->list, list) {
|
||||
cl = cb->cl;
|
||||
if (cl == NULL)
|
||||
continue;
|
||||
|
||||
cl->status = 0;
|
||||
list_del(&pos->list);
|
||||
list_del(&cb->list);
|
||||
if (MEI_WRITING == cl->writing_state &&
|
||||
pos->fop_type == MEI_FOP_WRITE &&
|
||||
cb->fop_type == MEI_FOP_WRITE &&
|
||||
cl != &dev->iamthif_cl) {
|
||||
dev_dbg(&dev->pdev->dev, "MEI WRITE COMPLETE\n");
|
||||
cl->writing_state = MEI_WRITE_COMPLETE;
|
||||
list_add_tail(&pos->list, &cmpl_list->list);
|
||||
list_add_tail(&cb->list, &cmpl_list->list);
|
||||
}
|
||||
if (cl == &dev->iamthif_cl) {
|
||||
dev_dbg(&dev->pdev->dev, "check iamthif flow control.\n");
|
||||
@ -552,25 +467,23 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)
|
||||
|
||||
/* complete control write list CB */
|
||||
dev_dbg(&dev->pdev->dev, "complete control write list cb.\n");
|
||||
list_for_each_entry_safe(pos, next, &dev->ctrl_wr_list.list, list) {
|
||||
cl = pos->cl;
|
||||
list_for_each_entry_safe(cb, next, &dev->ctrl_wr_list.list, list) {
|
||||
cl = cb->cl;
|
||||
if (!cl) {
|
||||
list_del(&pos->list);
|
||||
list_del(&cb->list);
|
||||
return -ENODEV;
|
||||
}
|
||||
switch (pos->fop_type) {
|
||||
switch (cb->fop_type) {
|
||||
case MEI_FOP_CLOSE:
|
||||
/* send disconnect message */
|
||||
ret = _mei_irq_thread_close(dev, &slots, pos,
|
||||
cl, cmpl_list);
|
||||
ret = mei_cl_irq_close(cl, cb, &slots, cmpl_list);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
break;
|
||||
case MEI_FOP_READ:
|
||||
/* send flow control message */
|
||||
ret = _mei_irq_thread_read(dev, &slots, pos,
|
||||
cl, cmpl_list);
|
||||
ret = mei_cl_irq_read(cl, cb, &slots, cmpl_list);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -579,8 +492,7 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)
|
||||
/* connect message */
|
||||
if (mei_cl_is_other_connecting(cl))
|
||||
continue;
|
||||
ret = _mei_irq_thread_ioctl(dev, &slots, pos,
|
||||
cl, cmpl_list);
|
||||
ret = mei_cl_irq_ioctl(cl, cb, &slots, cmpl_list);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -593,8 +505,8 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)
|
||||
}
|
||||
/* complete write list CB */
|
||||
dev_dbg(&dev->pdev->dev, "complete write list cb.\n");
|
||||
list_for_each_entry_safe(pos, next, &dev->write_list.list, list) {
|
||||
cl = pos->cl;
|
||||
list_for_each_entry_safe(cb, next, &dev->write_list.list, list) {
|
||||
cl = cb->cl;
|
||||
if (cl == NULL)
|
||||
continue;
|
||||
if (mei_cl_flow_ctrl_creds(cl) <= 0) {
|
||||
@ -605,14 +517,13 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)
|
||||
}
|
||||
|
||||
if (cl == &dev->iamthif_cl)
|
||||
ret = mei_amthif_irq_write_complete(dev, &slots,
|
||||
pos, cmpl_list);
|
||||
ret = mei_amthif_irq_write_complete(cl, cb,
|
||||
&slots, cmpl_list);
|
||||
else
|
||||
ret = mei_irq_thread_write_complete(dev, &slots, pos,
|
||||
cmpl_list);
|
||||
ret = mei_cl_irq_write_complete(cl, cb,
|
||||
&slots, cmpl_list);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -194,7 +194,6 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
|
||||
struct mei_cl_cb *cb_pos = NULL;
|
||||
struct mei_cl_cb *cb = NULL;
|
||||
struct mei_device *dev;
|
||||
int i;
|
||||
int rets;
|
||||
int err;
|
||||
|
||||
@ -210,38 +209,26 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((cl->sm_state & MEI_WD_STATE_INDEPENDENCE_MSG_SENT) == 0) {
|
||||
/* Do not allow to read watchdog client */
|
||||
i = mei_me_cl_by_uuid(dev, &mei_wd_guid);
|
||||
if (i >= 0) {
|
||||
struct mei_me_client *me_client = &dev->me_clients[i];
|
||||
if (cl->me_client_id == me_client->client_id) {
|
||||
rets = -EBADF;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cl->sm_state &= ~MEI_WD_STATE_INDEPENDENCE_MSG_SENT;
|
||||
}
|
||||
|
||||
if (cl == &dev->iamthif_cl) {
|
||||
rets = mei_amthif_read(dev, file, ubuf, length, offset);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (cl->read_cb && cl->read_cb->buf_idx > *offset) {
|
||||
if (cl->read_cb) {
|
||||
cb = cl->read_cb;
|
||||
goto copy_buffer;
|
||||
} else if (cl->read_cb && cl->read_cb->buf_idx > 0 &&
|
||||
cl->read_cb->buf_idx <= *offset) {
|
||||
cb = cl->read_cb;
|
||||
rets = 0;
|
||||
goto free;
|
||||
} else if ((!cl->read_cb || !cl->read_cb->buf_idx) && *offset > 0) {
|
||||
/*Offset needs to be cleaned for contiguous reads*/
|
||||
/* read what left */
|
||||
if (cb->buf_idx > *offset)
|
||||
goto copy_buffer;
|
||||
/* offset is beyond buf_idx we have no more data return 0 */
|
||||
if (cb->buf_idx > 0 && cb->buf_idx <= *offset) {
|
||||
rets = 0;
|
||||
goto free;
|
||||
}
|
||||
/* Offset needs to be cleaned for contiguous reads*/
|
||||
if (cb->buf_idx == 0 && *offset > 0)
|
||||
*offset = 0;
|
||||
} else if (*offset > 0) {
|
||||
*offset = 0;
|
||||
rets = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = mei_cl_read_start(cl, length);
|
||||
@ -420,16 +407,6 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
|
||||
if (rets)
|
||||
goto out;
|
||||
|
||||
cl->sm_state = 0;
|
||||
if (length == 4 &&
|
||||
((memcmp(mei_wd_state_independence_msg[0],
|
||||
write_cb->request_buffer.data, 4) == 0) ||
|
||||
(memcmp(mei_wd_state_independence_msg[1],
|
||||
write_cb->request_buffer.data, 4) == 0) ||
|
||||
(memcmp(mei_wd_state_independence_msg[2],
|
||||
write_cb->request_buffer.data, 4) == 0)))
|
||||
cl->sm_state |= MEI_WD_STATE_INDEPENDENCE_MSG_SENT;
|
||||
|
||||
if (cl == &dev->iamthif_cl) {
|
||||
rets = mei_amthif_write(dev, write_cb);
|
||||
|
||||
|
@ -55,11 +55,6 @@ extern const uuid_le mei_amthif_guid;
|
||||
*/
|
||||
extern const uuid_le mei_wd_guid;
|
||||
|
||||
/*
|
||||
* Watchdog independence state message
|
||||
*/
|
||||
extern const u8 mei_wd_state_independence_msg[3][4];
|
||||
|
||||
/*
|
||||
* Number of Maximum MEI Clients
|
||||
*/
|
||||
@ -201,7 +196,6 @@ struct mei_cl {
|
||||
u8 timer_count;
|
||||
enum mei_file_transaction_states reading_state;
|
||||
enum mei_file_transaction_states writing_state;
|
||||
int sm_state;
|
||||
struct mei_cl_cb *read_cb;
|
||||
|
||||
/* MEI CL bus data */
|
||||
@ -239,7 +233,7 @@ struct mei_hw_ops {
|
||||
bool (*host_is_ready) (struct mei_device *dev);
|
||||
|
||||
bool (*hw_is_ready) (struct mei_device *dev);
|
||||
void (*hw_reset) (struct mei_device *dev, bool enable);
|
||||
int (*hw_reset) (struct mei_device *dev, bool enable);
|
||||
int (*hw_start) (struct mei_device *dev);
|
||||
void (*hw_config) (struct mei_device *dev);
|
||||
|
||||
@ -502,8 +496,8 @@ struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev,
|
||||
|
||||
void mei_amthif_run_next_cmd(struct mei_device *dev);
|
||||
|
||||
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);
|
||||
|
||||
void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb);
|
||||
int mei_amthif_irq_read_msg(struct mei_device *dev,
|
||||
@ -522,15 +516,6 @@ void mei_nfc_host_exit(void);
|
||||
*/
|
||||
extern const uuid_le mei_nfc_guid;
|
||||
|
||||
int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots,
|
||||
struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list);
|
||||
|
||||
void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb);
|
||||
int mei_amthif_irq_read_message(struct mei_cl_cb *complete_list,
|
||||
struct mei_device *dev, struct mei_msg_hdr *mei_hdr);
|
||||
int mei_amthif_irq_read(struct mei_device *dev, s32 *slots);
|
||||
|
||||
|
||||
int mei_wd_send(struct mei_device *dev);
|
||||
int mei_wd_stop(struct mei_device *dev);
|
||||
int mei_wd_host_init(struct mei_device *dev);
|
||||
@ -554,14 +539,14 @@ static inline void mei_hw_config(struct mei_device *dev)
|
||||
{
|
||||
dev->ops->hw_config(dev);
|
||||
}
|
||||
static inline void mei_hw_reset(struct mei_device *dev, bool enable)
|
||||
static inline int mei_hw_reset(struct mei_device *dev, bool enable)
|
||||
{
|
||||
dev->ops->hw_reset(dev, enable);
|
||||
return dev->ops->hw_reset(dev, enable);
|
||||
}
|
||||
|
||||
static inline void mei_hw_start(struct mei_device *dev)
|
||||
static inline int mei_hw_start(struct mei_device *dev)
|
||||
{
|
||||
dev->ops->hw_start(dev);
|
||||
return dev->ops->hw_start(dev);
|
||||
}
|
||||
|
||||
static inline void mei_clear_interrupts(struct mei_device *dev)
|
||||
|
@ -43,9 +43,6 @@
|
||||
#include "hw-me.h"
|
||||
#include "client.h"
|
||||
|
||||
/* AMT device is a singleton on the platform */
|
||||
static struct pci_dev *mei_pdev;
|
||||
|
||||
/* mei_pci_tbl - PCI Device ID Table */
|
||||
static DEFINE_PCI_DEVICE_TABLE(mei_me_pci_tbl) = {
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82946GZ)},
|
||||
@ -88,8 +85,6 @@ static DEFINE_PCI_DEVICE_TABLE(mei_me_pci_tbl) = {
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, mei_me_pci_tbl);
|
||||
|
||||
static DEFINE_MUTEX(mei_mutex);
|
||||
|
||||
/**
|
||||
* mei_quirk_probe - probe for devices that doesn't valid ME interface
|
||||
*
|
||||
@ -126,17 +121,12 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
struct mei_me_hw *hw;
|
||||
int err;
|
||||
|
||||
mutex_lock(&mei_mutex);
|
||||
|
||||
if (!mei_me_quirk_probe(pdev, ent)) {
|
||||
err = -ENODEV;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (mei_pdev) {
|
||||
err = -EEXIST;
|
||||
goto end;
|
||||
}
|
||||
/* enable pci dev */
|
||||
err = pci_enable_device(pdev);
|
||||
if (err) {
|
||||
@ -195,13 +185,10 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
if (err)
|
||||
goto release_irq;
|
||||
|
||||
mei_pdev = pdev;
|
||||
pci_set_drvdata(pdev, dev);
|
||||
|
||||
schedule_delayed_work(&dev->timer_work, HZ);
|
||||
|
||||
mutex_unlock(&mei_mutex);
|
||||
|
||||
pr_debug("initialization successful.\n");
|
||||
|
||||
return 0;
|
||||
@ -220,7 +207,6 @@ release_regions:
|
||||
disable_device:
|
||||
pci_disable_device(pdev);
|
||||
end:
|
||||
mutex_unlock(&mei_mutex);
|
||||
dev_err(&pdev->dev, "initialization failed.\n");
|
||||
return err;
|
||||
}
|
||||
@ -238,9 +224,6 @@ static void mei_me_remove(struct pci_dev *pdev)
|
||||
struct mei_device *dev;
|
||||
struct mei_me_hw *hw;
|
||||
|
||||
if (mei_pdev != pdev)
|
||||
return;
|
||||
|
||||
dev = pci_get_drvdata(pdev);
|
||||
if (!dev)
|
||||
return;
|
||||
@ -251,8 +234,6 @@ static void mei_me_remove(struct pci_dev *pdev)
|
||||
dev_err(&pdev->dev, "stop\n");
|
||||
mei_stop(dev);
|
||||
|
||||
mei_pdev = NULL;
|
||||
|
||||
/* disable interrupts */
|
||||
mei_disable_interrupts(dev);
|
||||
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -633,17 +633,13 @@ static ssize_t show_pch_mac(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t store_pch_mac(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
u8 mac[6];
|
||||
u8 mac[ETH_ALEN];
|
||||
ssize_t rom_size;
|
||||
struct pch_phub_reg *chip = dev_get_drvdata(dev);
|
||||
|
||||
if (count != 18)
|
||||
if (!mac_pton(buf, mac))
|
||||
return -EINVAL;
|
||||
|
||||
sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
(u32 *)&mac[0], (u32 *)&mac[1], (u32 *)&mac[2], (u32 *)&mac[3],
|
||||
(u32 *)&mac[4], (u32 *)&mac[5]);
|
||||
|
||||
chip->pch_phub_extrom_base_address = pci_map_rom(chip->pdev, &rom_size);
|
||||
if (!chip->pch_phub_extrom_base_address)
|
||||
return -ENOMEM;
|
||||
@ -669,8 +665,6 @@ static struct bin_attribute pch_bin_attr = {
|
||||
static int pch_phub_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
int retval;
|
||||
|
||||
int ret;
|
||||
struct pch_phub_reg *chip;
|
||||
|
||||
@ -713,13 +707,13 @@ static int pch_phub_probe(struct pci_dev *pdev,
|
||||
if (id->driver_data == 1) { /* EG20T PCH */
|
||||
const char *board_name;
|
||||
|
||||
retval = sysfs_create_file(&pdev->dev.kobj,
|
||||
&dev_attr_pch_mac.attr);
|
||||
if (retval)
|
||||
ret = sysfs_create_file(&pdev->dev.kobj,
|
||||
&dev_attr_pch_mac.attr);
|
||||
if (ret)
|
||||
goto err_sysfs_create;
|
||||
|
||||
retval = sysfs_create_bin_file(&pdev->dev.kobj, &pch_bin_attr);
|
||||
if (retval)
|
||||
ret = sysfs_create_bin_file(&pdev->dev.kobj, &pch_bin_attr);
|
||||
if (ret)
|
||||
goto exit_bin_attr;
|
||||
|
||||
pch_phub_read_modify_write_reg(chip,
|
||||
@ -743,8 +737,8 @@ static int pch_phub_probe(struct pci_dev *pdev,
|
||||
chip->pch_opt_rom_start_address = PCH_PHUB_ROM_START_ADDR_EG20T;
|
||||
chip->pch_mac_start_address = PCH_PHUB_MAC_START_ADDR_EG20T;
|
||||
} else if (id->driver_data == 2) { /* ML7213 IOH */
|
||||
retval = sysfs_create_bin_file(&pdev->dev.kobj, &pch_bin_attr);
|
||||
if (retval)
|
||||
ret = sysfs_create_bin_file(&pdev->dev.kobj, &pch_bin_attr);
|
||||
if (ret)
|
||||
goto err_sysfs_create;
|
||||
/* set the prefech value
|
||||
* Device2(USB OHCI #1/ USB EHCI #1/ USB Device):a
|
||||
@ -766,12 +760,12 @@ static int pch_phub_probe(struct pci_dev *pdev,
|
||||
PCH_PHUB_ROM_START_ADDR_ML7223;
|
||||
chip->pch_mac_start_address = PCH_PHUB_MAC_START_ADDR_ML7223;
|
||||
} else if (id->driver_data == 4) { /* ML7223 IOH Bus-n*/
|
||||
retval = sysfs_create_file(&pdev->dev.kobj,
|
||||
&dev_attr_pch_mac.attr);
|
||||
if (retval)
|
||||
ret = sysfs_create_file(&pdev->dev.kobj,
|
||||
&dev_attr_pch_mac.attr);
|
||||
if (ret)
|
||||
goto err_sysfs_create;
|
||||
retval = sysfs_create_bin_file(&pdev->dev.kobj, &pch_bin_attr);
|
||||
if (retval)
|
||||
ret = sysfs_create_bin_file(&pdev->dev.kobj, &pch_bin_attr);
|
||||
if (ret)
|
||||
goto exit_bin_attr;
|
||||
/* set the prefech value
|
||||
* Device2(USB OHCI #0,1,2,3/ USB EHCI #0):a
|
||||
@ -783,13 +777,13 @@ static int pch_phub_probe(struct pci_dev *pdev,
|
||||
PCH_PHUB_ROM_START_ADDR_ML7223;
|
||||
chip->pch_mac_start_address = PCH_PHUB_MAC_START_ADDR_ML7223;
|
||||
} else if (id->driver_data == 5) { /* ML7831 */
|
||||
retval = sysfs_create_file(&pdev->dev.kobj,
|
||||
&dev_attr_pch_mac.attr);
|
||||
if (retval)
|
||||
ret = sysfs_create_file(&pdev->dev.kobj,
|
||||
&dev_attr_pch_mac.attr);
|
||||
if (ret)
|
||||
goto err_sysfs_create;
|
||||
|
||||
retval = sysfs_create_bin_file(&pdev->dev.kobj, &pch_bin_attr);
|
||||
if (retval)
|
||||
ret = sysfs_create_bin_file(&pdev->dev.kobj, &pch_bin_attr);
|
||||
if (ret)
|
||||
goto exit_bin_attr;
|
||||
|
||||
/* set the prefech value */
|
||||
|
@ -160,15 +160,11 @@ static int options_show(struct seq_file *s, void *p)
|
||||
static ssize_t options_write(struct file *file, const char __user *userbuf,
|
||||
size_t count, loff_t *data)
|
||||
{
|
||||
char buf[20];
|
||||
int ret;
|
||||
|
||||
if (count >= sizeof(buf))
|
||||
return -EINVAL;
|
||||
if (copy_from_user(buf, userbuf, count))
|
||||
return -EFAULT;
|
||||
buf[count] = '\0';
|
||||
if (strict_strtoul(buf, 0, &gru_options))
|
||||
return -EINVAL;
|
||||
ret = kstrtoul_from_user(userbuf, count, 0, &gru_options);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
@ -316,8 +316,12 @@ static ssize_t pcie_gadget_store_no_of_msi(
|
||||
struct spear_pcie_gadget_config *config,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
if (strict_strtoul(buf, 0, &config->requested_msi))
|
||||
return -EINVAL;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 0, &config->requested_msi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (config->requested_msi > 32)
|
||||
config->requested_msi = 32;
|
||||
|
||||
@ -330,9 +334,11 @@ static ssize_t pcie_gadget_store_inta(
|
||||
{
|
||||
struct pcie_app_reg __iomem *app_reg = config->va_app_base;
|
||||
ulong en;
|
||||
int ret;
|
||||
|
||||
if (strict_strtoul(buf, 0, &en))
|
||||
return -EINVAL;
|
||||
ret = kstrtoul(buf, 0, &en);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (en)
|
||||
writel(readl(&app_reg->app_ctrl_0) | (1 << SYS_INT_ID),
|
||||
@ -351,9 +357,11 @@ static ssize_t pcie_gadget_store_send_msi(
|
||||
struct pcie_app_reg __iomem *app_reg = config->va_app_base;
|
||||
ulong vector;
|
||||
u32 ven_msi;
|
||||
int ret;
|
||||
|
||||
if (strict_strtoul(buf, 0, &vector))
|
||||
return -EINVAL;
|
||||
ret = kstrtoul(buf, 0, &vector);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!config->configured_msi)
|
||||
return -EINVAL;
|
||||
@ -395,9 +403,11 @@ static ssize_t pcie_gadget_store_vendor_id(
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
ulong id;
|
||||
int ret;
|
||||
|
||||
if (strict_strtoul(buf, 0, &id))
|
||||
return -EINVAL;
|
||||
ret = kstrtoul(buf, 0, &id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spear_dbi_write_reg(config, PCI_VENDOR_ID, 2, id);
|
||||
|
||||
@ -420,9 +430,11 @@ static ssize_t pcie_gadget_store_device_id(
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
ulong id;
|
||||
int ret;
|
||||
|
||||
if (strict_strtoul(buf, 0, &id))
|
||||
return -EINVAL;
|
||||
ret = kstrtoul(buf, 0, &id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spear_dbi_write_reg(config, PCI_DEVICE_ID, 2, id);
|
||||
|
||||
@ -443,9 +455,12 @@ static ssize_t pcie_gadget_store_bar0_size(
|
||||
ulong size;
|
||||
u32 pos, pos1;
|
||||
u32 no_of_bit = 0;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 0, &size);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (strict_strtoul(buf, 0, &size))
|
||||
return -EINVAL;
|
||||
/* min bar size is 256 */
|
||||
if (size <= 0x100)
|
||||
size = 0x100;
|
||||
@ -490,9 +505,11 @@ static ssize_t pcie_gadget_store_bar0_address(
|
||||
{
|
||||
struct pcie_app_reg __iomem *app_reg = config->va_app_base;
|
||||
ulong address;
|
||||
int ret;
|
||||
|
||||
if (strict_strtoul(buf, 0, &address))
|
||||
return -EINVAL;
|
||||
ret = kstrtoul(buf, 0, &address);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
address &= ~(config->bar0_size - 1);
|
||||
if (config->va_bar0_address)
|
||||
@ -518,9 +535,11 @@ static ssize_t pcie_gadget_store_bar0_rw_offset(
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
ulong offset;
|
||||
int ret;
|
||||
|
||||
if (strict_strtoul(buf, 0, &offset))
|
||||
return -EINVAL;
|
||||
ret = kstrtoul(buf, 0, &offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (offset % 4)
|
||||
return -EINVAL;
|
||||
@ -549,9 +568,11 @@ static ssize_t pcie_gadget_store_bar0_data(
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
ulong data;
|
||||
int ret;
|
||||
|
||||
if (strict_strtoul(buf, 0, &data))
|
||||
return -EINVAL;
|
||||
ret = kstrtoul(buf, 0, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!config->va_bar0_address)
|
||||
return -ENOMEM;
|
||||
@ -776,7 +797,7 @@ static int spear_pcie_gadget_probe(struct platform_device *pdev)
|
||||
goto err_iounmap_app;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&pdev->dev, target);
|
||||
platform_set_drvdata(pdev, target);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
@ -814,9 +835,11 @@ static int spear_pcie_gadget_probe(struct platform_device *pdev)
|
||||
clk = clk_get_sys("pcie1", NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("%s:couldn't get clk for pcie1\n", __func__);
|
||||
status = PTR_ERR(clk);
|
||||
goto err_irq;
|
||||
}
|
||||
if (clk_enable(clk)) {
|
||||
status = clk_enable(clk);
|
||||
if (status) {
|
||||
pr_err("%s:couldn't enable clk for pcie1\n", __func__);
|
||||
goto err_irq;
|
||||
}
|
||||
@ -828,9 +851,11 @@ static int spear_pcie_gadget_probe(struct platform_device *pdev)
|
||||
clk = clk_get_sys("pcie2", NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("%s:couldn't get clk for pcie2\n", __func__);
|
||||
status = PTR_ERR(clk);
|
||||
goto err_irq;
|
||||
}
|
||||
if (clk_enable(clk)) {
|
||||
status = clk_enable(clk);
|
||||
if (status) {
|
||||
pr_err("%s:couldn't enable clk for pcie2\n", __func__);
|
||||
goto err_irq;
|
||||
}
|
||||
@ -863,7 +888,7 @@ static int spear_pcie_gadget_remove(struct platform_device *pdev)
|
||||
res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
target = dev_get_drvdata(&pdev->dev);
|
||||
target = platform_get_drvdata(pdev);
|
||||
config = &target->config;
|
||||
|
||||
free_irq(irq, NULL);
|
||||
|
@ -45,15 +45,12 @@ static int sram_probe(struct platform_device *pdev)
|
||||
int ret;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
virt_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(virt_base))
|
||||
return PTR_ERR(virt_base);
|
||||
|
||||
size = resource_size(res);
|
||||
|
||||
virt_base = devm_request_and_ioremap(&pdev->dev, res);
|
||||
if (!virt_base)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
sram = devm_kzalloc(&pdev->dev, sizeof(*sram), GFP_KERNEL);
|
||||
if (!sram)
|
||||
return -ENOMEM;
|
||||
|
@ -680,7 +680,7 @@ void st_kim_ref(struct st_data_s **core_data, int id)
|
||||
*core_data = NULL;
|
||||
return;
|
||||
}
|
||||
kim_gdata = dev_get_drvdata(&pdev->dev);
|
||||
kim_gdata = platform_get_drvdata(pdev);
|
||||
*core_data = kim_gdata->core_data;
|
||||
}
|
||||
|
||||
@ -735,7 +735,7 @@ static int kim_probe(struct platform_device *pdev)
|
||||
pr_err("no mem to allocate");
|
||||
return -ENOMEM;
|
||||
}
|
||||
dev_set_drvdata(&pdev->dev, kim_gdata);
|
||||
platform_set_drvdata(pdev, kim_gdata);
|
||||
|
||||
err = st_core_init(&kim_gdata->core_data);
|
||||
if (err != 0) {
|
||||
@ -810,7 +810,7 @@ static int kim_remove(struct platform_device *pdev)
|
||||
struct ti_st_plat_data *pdata = pdev->dev.platform_data;
|
||||
struct kim_data_s *kim_gdata;
|
||||
|
||||
kim_gdata = dev_get_drvdata(&pdev->dev);
|
||||
kim_gdata = platform_get_drvdata(pdev);
|
||||
|
||||
/* Free the Bluetooth/FM/GPIO
|
||||
* nShutdown gpio from the system
|
||||
|
@ -33,9 +33,11 @@ static ssize_t dac7512_store_val(struct device *dev,
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
unsigned char tmp[2];
|
||||
unsigned long val;
|
||||
int ret;
|
||||
|
||||
if (strict_strtoul(buf, 10, &val) < 0)
|
||||
return -EINVAL;
|
||||
ret = kstrtoul(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tmp[0] = val >> 8;
|
||||
tmp[1] = val & 0xff;
|
||||
|
@ -204,7 +204,7 @@ static ssize_t tsl2550_store_power_state(struct device *dev,
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
int ret;
|
||||
|
||||
if (val < 0 || val > 1)
|
||||
if (val > 1)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
@ -236,7 +236,7 @@ static ssize_t tsl2550_store_operating_mode(struct device *dev,
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
int ret;
|
||||
|
||||
if (val < 0 || val > 1)
|
||||
if (val > 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (data->power_state == 0)
|
||||
|
@ -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>
|
||||
|
@ -243,7 +243,7 @@ config PCMCIA_DEBUG
|
||||
|
||||
config PCMCIA_PROBE
|
||||
bool
|
||||
default y if ISA && !ARCH_SA1100 && !ARCH_CLPS711X && !PARISC
|
||||
default y if ISA && !ARCH_SA1100 && !PARISC
|
||||
|
||||
config M32R_PCC
|
||||
bool "M32R PCMCIA I/F"
|
||||
@ -288,7 +288,7 @@ config BFIN_CFPCMCIA
|
||||
|
||||
config AT91_CF
|
||||
tristate "AT91 CompactFlash Controller"
|
||||
depends on PCMCIA && ARCH_AT91RM9200
|
||||
depends on PCMCIA && ARCH_AT91
|
||||
help
|
||||
Say Y here to support the CompactFlash controller on AT91 chips.
|
||||
Or choose M to compile the driver as a module named "at91_cf".
|
||||
|
@ -18,13 +18,14 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/platform_data/atmel.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <pcmcia/ss.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/sizes.h>
|
||||
|
||||
#include <mach/at91rm9200_mc.h>
|
||||
#include <mach/at91_ramc.h>
|
||||
|
||||
@ -41,8 +42,6 @@
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
static const char driver_name[] = "at91_cf";
|
||||
|
||||
struct at91_cf_socket {
|
||||
struct pcmcia_socket socket;
|
||||
|
||||
@ -76,7 +75,7 @@ static irqreturn_t at91_cf_irq(int irq, void *_cf)
|
||||
/* kick pccard as needed */
|
||||
if (present != cf->present) {
|
||||
cf->present = present;
|
||||
pr_debug("%s: card %s\n", driver_name,
|
||||
dev_dbg(&cf->pdev->dev, "card %s\n",
|
||||
present ? "present" : "gone");
|
||||
pcmcia_parse_events(&cf->socket, SS_DETECT);
|
||||
}
|
||||
@ -100,9 +99,9 @@ static int at91_cf_get_status(struct pcmcia_socket *s, u_int *sp)
|
||||
int vcc = gpio_is_valid(cf->board->vcc_pin);
|
||||
|
||||
*sp = SS_DETECT | SS_3VCARD;
|
||||
if (!rdy || gpio_get_value(rdy))
|
||||
if (!rdy || gpio_get_value(cf->board->irq_pin))
|
||||
*sp |= SS_READY;
|
||||
if (!vcc || gpio_get_value(vcc))
|
||||
if (!vcc || gpio_get_value(cf->board->vcc_pin))
|
||||
*sp |= SS_POWERON;
|
||||
} else
|
||||
*sp = 0;
|
||||
@ -120,22 +119,22 @@ at91_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s)
|
||||
/* switch Vcc if needed and possible */
|
||||
if (gpio_is_valid(cf->board->vcc_pin)) {
|
||||
switch (s->Vcc) {
|
||||
case 0:
|
||||
gpio_set_value(cf->board->vcc_pin, 0);
|
||||
break;
|
||||
case 33:
|
||||
gpio_set_value(cf->board->vcc_pin, 1);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
case 0:
|
||||
gpio_set_value(cf->board->vcc_pin, 0);
|
||||
break;
|
||||
case 33:
|
||||
gpio_set_value(cf->board->vcc_pin, 1);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* toggle reset if needed */
|
||||
gpio_set_value(cf->board->rst_pin, s->flags & SS_RESET);
|
||||
|
||||
pr_debug("%s: Vcc %d, io_irq %d, flags %04x csc %04x\n",
|
||||
driver_name, s->Vcc, s->io_irq, s->flags, s->csc_mask);
|
||||
dev_dbg(&cf->pdev->dev, "Vcc %d, io_irq %d, flags %04x csc %04x\n",
|
||||
s->Vcc, s->io_irq, s->flags, s->csc_mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -171,10 +170,10 @@ static int at91_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
|
||||
*/
|
||||
if (!(io->flags & (MAP_16BIT | MAP_AUTOSZ))) {
|
||||
csr |= AT91_SMC_DBW_8;
|
||||
pr_debug("%s: 8bit i/o bus\n", driver_name);
|
||||
dev_dbg(&cf->pdev->dev, "8bit i/o bus\n");
|
||||
} else {
|
||||
csr |= AT91_SMC_DBW_16;
|
||||
pr_debug("%s: 16bit i/o bus\n", driver_name);
|
||||
dev_dbg(&cf->pdev->dev, "16bit i/o bus\n");
|
||||
}
|
||||
at91_ramc_write(0, AT91_SMC_CSR(cf->board->chipselect), csr);
|
||||
|
||||
@ -215,6 +214,37 @@ static struct pccard_operations at91_cf_ops = {
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static const struct of_device_id at91_cf_dt_ids[] = {
|
||||
{ .compatible = "atmel,at91rm9200-cf" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, at91_cf_dt_ids);
|
||||
|
||||
static int at91_cf_dt_init(struct platform_device *pdev)
|
||||
{
|
||||
struct at91_cf_data *board;
|
||||
|
||||
board = devm_kzalloc(&pdev->dev, sizeof(*board), GFP_KERNEL);
|
||||
if (!board)
|
||||
return -ENOMEM;
|
||||
|
||||
board->irq_pin = of_get_gpio(pdev->dev.of_node, 0);
|
||||
board->det_pin = of_get_gpio(pdev->dev.of_node, 1);
|
||||
board->vcc_pin = of_get_gpio(pdev->dev.of_node, 2);
|
||||
board->rst_pin = of_get_gpio(pdev->dev.of_node, 3);
|
||||
|
||||
pdev->dev.platform_data = board;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int at91_cf_dt_init(struct platform_device *pdev)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __init at91_cf_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct at91_cf_socket *cf;
|
||||
@ -222,14 +252,22 @@ static int __init at91_cf_probe(struct platform_device *pdev)
|
||||
struct resource *io;
|
||||
int status;
|
||||
|
||||
if (!board || !gpio_is_valid(board->det_pin) || !gpio_is_valid(board->rst_pin))
|
||||
if (!board) {
|
||||
status = at91_cf_dt_init(pdev);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
board = pdev->dev.platform_data;
|
||||
}
|
||||
|
||||
if (!gpio_is_valid(board->det_pin) || !gpio_is_valid(board->rst_pin))
|
||||
return -ENODEV;
|
||||
|
||||
io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!io)
|
||||
return -ENODEV;
|
||||
|
||||
cf = kzalloc(sizeof *cf, GFP_KERNEL);
|
||||
cf = devm_kzalloc(&pdev->dev, sizeof(*cf), GFP_KERNEL);
|
||||
if (!cf)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -239,22 +277,25 @@ static int __init at91_cf_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, cf);
|
||||
|
||||
/* must be a GPIO; ergo must trigger on both edges */
|
||||
status = gpio_request(board->det_pin, "cf_det");
|
||||
status = devm_gpio_request(&pdev->dev, board->det_pin, "cf_det");
|
||||
if (status < 0)
|
||||
goto fail0;
|
||||
status = request_irq(gpio_to_irq(board->det_pin), at91_cf_irq, 0, driver_name, cf);
|
||||
return status;
|
||||
|
||||
status = devm_request_irq(&pdev->dev, gpio_to_irq(board->det_pin),
|
||||
at91_cf_irq, 0, "at91_cf detect", cf);
|
||||
if (status < 0)
|
||||
goto fail00;
|
||||
return status;
|
||||
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
|
||||
status = gpio_request(board->rst_pin, "cf_rst");
|
||||
status = devm_gpio_request(&pdev->dev, board->rst_pin, "cf_rst");
|
||||
if (status < 0)
|
||||
goto fail0a;
|
||||
|
||||
if (gpio_is_valid(board->vcc_pin)) {
|
||||
status = gpio_request(board->vcc_pin, "cf_vcc");
|
||||
status = devm_gpio_request(&pdev->dev, board->vcc_pin, "cf_vcc");
|
||||
if (status < 0)
|
||||
goto fail0b;
|
||||
goto fail0a;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -264,32 +305,33 @@ static int __init at91_cf_probe(struct platform_device *pdev)
|
||||
* (Note: DK board doesn't wire the IRQ pin...)
|
||||
*/
|
||||
if (gpio_is_valid(board->irq_pin)) {
|
||||
status = gpio_request(board->irq_pin, "cf_irq");
|
||||
status = devm_gpio_request(&pdev->dev, board->irq_pin, "cf_irq");
|
||||
if (status < 0)
|
||||
goto fail0c;
|
||||
status = request_irq(gpio_to_irq(board->irq_pin), at91_cf_irq,
|
||||
IRQF_SHARED, driver_name, cf);
|
||||
goto fail0a;
|
||||
|
||||
status = devm_request_irq(&pdev->dev, gpio_to_irq(board->irq_pin),
|
||||
at91_cf_irq, IRQF_SHARED, "at91_cf", cf);
|
||||
if (status < 0)
|
||||
goto fail0d;
|
||||
goto fail0a;
|
||||
cf->socket.pci_irq = gpio_to_irq(board->irq_pin);
|
||||
} else
|
||||
cf->socket.pci_irq = nr_irqs + 1;
|
||||
|
||||
/* pcmcia layer only remaps "real" memory not iospace */
|
||||
cf->socket.io_offset = (unsigned long)
|
||||
ioremap(cf->phys_baseaddr + CF_IO_PHYS, SZ_2K);
|
||||
cf->socket.io_offset = (unsigned long) devm_ioremap(&pdev->dev,
|
||||
cf->phys_baseaddr + CF_IO_PHYS, SZ_2K);
|
||||
if (!cf->socket.io_offset) {
|
||||
status = -ENXIO;
|
||||
goto fail1;
|
||||
goto fail0a;
|
||||
}
|
||||
|
||||
/* reserve chip-select regions */
|
||||
if (!request_mem_region(io->start, resource_size(io), driver_name)) {
|
||||
if (!devm_request_mem_region(&pdev->dev, io->start, resource_size(io), "at91_cf")) {
|
||||
status = -ENXIO;
|
||||
goto fail1;
|
||||
goto fail0a;
|
||||
}
|
||||
|
||||
pr_info("%s: irqs det #%d, io #%d\n", driver_name,
|
||||
dev_info(&pdev->dev, "irqs det #%d, io #%d\n",
|
||||
gpio_to_irq(board->det_pin), gpio_to_irq(board->irq_pin));
|
||||
|
||||
cf->socket.owner = THIS_MODULE;
|
||||
@ -303,55 +345,22 @@ static int __init at91_cf_probe(struct platform_device *pdev)
|
||||
|
||||
status = pcmcia_register_socket(&cf->socket);
|
||||
if (status < 0)
|
||||
goto fail2;
|
||||
goto fail0a;
|
||||
|
||||
return 0;
|
||||
|
||||
fail2:
|
||||
release_mem_region(io->start, resource_size(io));
|
||||
fail1:
|
||||
if (cf->socket.io_offset)
|
||||
iounmap((void __iomem *) cf->socket.io_offset);
|
||||
if (gpio_is_valid(board->irq_pin)) {
|
||||
free_irq(gpio_to_irq(board->irq_pin), cf);
|
||||
fail0d:
|
||||
gpio_free(board->irq_pin);
|
||||
}
|
||||
fail0c:
|
||||
if (gpio_is_valid(board->vcc_pin))
|
||||
gpio_free(board->vcc_pin);
|
||||
fail0b:
|
||||
gpio_free(board->rst_pin);
|
||||
fail0a:
|
||||
device_init_wakeup(&pdev->dev, 0);
|
||||
free_irq(gpio_to_irq(board->det_pin), cf);
|
||||
fail00:
|
||||
gpio_free(board->det_pin);
|
||||
fail0:
|
||||
kfree(cf);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int __exit at91_cf_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct at91_cf_socket *cf = platform_get_drvdata(pdev);
|
||||
struct at91_cf_data *board = cf->board;
|
||||
struct resource *io = cf->socket.io[0].res;
|
||||
|
||||
pcmcia_unregister_socket(&cf->socket);
|
||||
release_mem_region(io->start, resource_size(io));
|
||||
iounmap((void __iomem *) cf->socket.io_offset);
|
||||
if (gpio_is_valid(board->irq_pin)) {
|
||||
free_irq(gpio_to_irq(board->irq_pin), cf);
|
||||
gpio_free(board->irq_pin);
|
||||
}
|
||||
if (gpio_is_valid(board->vcc_pin))
|
||||
gpio_free(board->vcc_pin);
|
||||
gpio_free(board->rst_pin);
|
||||
device_init_wakeup(&pdev->dev, 0);
|
||||
free_irq(gpio_to_irq(board->det_pin), cf);
|
||||
gpio_free(board->det_pin);
|
||||
kfree(cf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -391,8 +400,9 @@ static int at91_cf_resume(struct platform_device *pdev)
|
||||
|
||||
static struct platform_driver at91_cf_driver = {
|
||||
.driver = {
|
||||
.name = (char *) driver_name,
|
||||
.name = "at91_cf",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(at91_cf_dt_ids),
|
||||
},
|
||||
.remove = __exit_p(at91_cf_remove),
|
||||
.suspend = at91_cf_suspend,
|
||||
@ -401,17 +411,7 @@ static struct platform_driver at91_cf_driver = {
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
static int __init at91_cf_init(void)
|
||||
{
|
||||
return platform_driver_probe(&at91_cf_driver, at91_cf_probe);
|
||||
}
|
||||
module_init(at91_cf_init);
|
||||
|
||||
static void __exit at91_cf_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&at91_cf_driver);
|
||||
}
|
||||
module_exit(at91_cf_exit);
|
||||
module_platform_driver_probe(at91_cf_driver, at91_cf_probe);
|
||||
|
||||
MODULE_DESCRIPTION("AT91 Compact Flash Driver");
|
||||
MODULE_AUTHOR("David Brownell");
|
||||
|
@ -644,6 +644,7 @@ static int pd6729_pci_probe(struct pci_dev *dev,
|
||||
if (!pci_resource_start(dev, 0)) {
|
||||
dev_warn(&dev->dev, "refusing to load the driver as the "
|
||||
"io_base is NULL.\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_out_disable;
|
||||
}
|
||||
|
||||
@ -673,6 +674,7 @@ static int pd6729_pci_probe(struct pci_dev *dev,
|
||||
mask = pd6729_isa_scan();
|
||||
if (irq_mode == 0 && mask == 0) {
|
||||
dev_warn(&dev->dev, "no ISA interrupt is available.\n");
|
||||
ret = -ENODEV;
|
||||
goto err_out_free_res;
|
||||
}
|
||||
|
||||
|
@ -160,17 +160,5 @@ static struct pci_driver pci_driver = {
|
||||
.remove = remove,
|
||||
};
|
||||
|
||||
static int __init aectc_init(void)
|
||||
{
|
||||
return pci_register_driver(&pci_driver);
|
||||
}
|
||||
|
||||
static void __exit aectc_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&pci_driver);
|
||||
}
|
||||
|
||||
module_pci_driver(pci_driver);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(aectc_init);
|
||||
module_exit(aectc_exit);
|
||||
|
@ -135,19 +135,7 @@ static struct pci_driver hilscher_pci_driver = {
|
||||
.remove = hilscher_pci_remove,
|
||||
};
|
||||
|
||||
static int __init hilscher_init_module(void)
|
||||
{
|
||||
return pci_register_driver(&hilscher_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit hilscher_exit_module(void)
|
||||
{
|
||||
pci_unregister_driver(&hilscher_pci_driver);
|
||||
}
|
||||
|
||||
module_init(hilscher_init_module);
|
||||
module_exit(hilscher_exit_module);
|
||||
|
||||
module_pci_driver(hilscher_pci_driver);
|
||||
MODULE_DEVICE_TABLE(pci, hilscher_pci_ids);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Hans J. Koch, Benedikt Spranger");
|
||||
|
@ -336,8 +336,6 @@ static const struct of_device_id uio_of_genirq_match[] = {
|
||||
{ /* empty for now */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, uio_of_genirq_match);
|
||||
#else
|
||||
# define uio_of_genirq_match NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver uio_dmem_genirq = {
|
||||
@ -347,7 +345,7 @@ static struct platform_driver uio_dmem_genirq = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &uio_dmem_genirq_dev_pm_ops,
|
||||
.of_match_table = uio_of_genirq_match,
|
||||
.of_match_table = of_match_ptr(uio_of_genirq_match),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -174,19 +174,7 @@ static struct pci_driver netx_pci_driver = {
|
||||
.remove = netx_pci_remove,
|
||||
};
|
||||
|
||||
static int __init netx_init_module(void)
|
||||
{
|
||||
return pci_register_driver(&netx_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit netx_exit_module(void)
|
||||
{
|
||||
pci_unregister_driver(&netx_pci_driver);
|
||||
}
|
||||
|
||||
module_init(netx_init_module);
|
||||
module_exit(netx_exit_module);
|
||||
|
||||
module_pci_driver(netx_pci_driver);
|
||||
MODULE_DEVICE_TABLE(pci, netx_pci_ids);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Hans J. Koch, Manuel Traut");
|
||||
|
@ -113,27 +113,14 @@ static void remove(struct pci_dev *pdev)
|
||||
kfree(gdev);
|
||||
}
|
||||
|
||||
static struct pci_driver driver = {
|
||||
static struct pci_driver uio_pci_driver = {
|
||||
.name = "uio_pci_generic",
|
||||
.id_table = NULL, /* only dynamic id's */
|
||||
.probe = probe,
|
||||
.remove = remove,
|
||||
};
|
||||
|
||||
static int __init init(void)
|
||||
{
|
||||
pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
|
||||
return pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
static void __exit cleanup(void)
|
||||
{
|
||||
pci_unregister_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(init);
|
||||
module_exit(cleanup);
|
||||
|
||||
module_pci_driver(uio_pci_driver);
|
||||
MODULE_VERSION(DRIVER_VERSION);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
|
@ -37,6 +37,11 @@ struct uio_pdrv_genirq_platdata {
|
||||
struct platform_device *pdev;
|
||||
};
|
||||
|
||||
/* Bits in uio_pdrv_genirq_platdata.flags */
|
||||
enum {
|
||||
UIO_IRQ_DISABLED = 0,
|
||||
};
|
||||
|
||||
static int uio_pdrv_genirq_open(struct uio_info *info, struct inode *inode)
|
||||
{
|
||||
struct uio_pdrv_genirq_platdata *priv = info->priv;
|
||||
@ -63,8 +68,10 @@ static irqreturn_t uio_pdrv_genirq_handler(int irq, struct uio_info *dev_info)
|
||||
* remember the state so we can allow user space to enable it later.
|
||||
*/
|
||||
|
||||
if (!test_and_set_bit(0, &priv->flags))
|
||||
spin_lock(&priv->lock);
|
||||
if (!__test_and_set_bit(UIO_IRQ_DISABLED, &priv->flags))
|
||||
disable_irq_nosync(irq);
|
||||
spin_unlock(&priv->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@ -78,16 +85,17 @@ static int uio_pdrv_genirq_irqcontrol(struct uio_info *dev_info, s32 irq_on)
|
||||
* in the interrupt controller, but keep track of the
|
||||
* state to prevent per-irq depth damage.
|
||||
*
|
||||
* Serialize this operation to support multiple tasks.
|
||||
* Serialize this operation to support multiple tasks and concurrency
|
||||
* with irq handler on SMP systems.
|
||||
*/
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
if (irq_on) {
|
||||
if (test_and_clear_bit(0, &priv->flags))
|
||||
if (__test_and_clear_bit(UIO_IRQ_DISABLED, &priv->flags))
|
||||
enable_irq(dev_info->irq);
|
||||
} else {
|
||||
if (!test_and_set_bit(0, &priv->flags))
|
||||
disable_irq(dev_info->irq);
|
||||
if (!__test_and_set_bit(UIO_IRQ_DISABLED, &priv->flags))
|
||||
disable_irq_nosync(dev_info->irq);
|
||||
}
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
@ -103,24 +111,16 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
|
||||
int i;
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
int irq;
|
||||
|
||||
/* alloc uioinfo for one device */
|
||||
uioinfo = kzalloc(sizeof(*uioinfo), GFP_KERNEL);
|
||||
if (!uioinfo) {
|
||||
ret = -ENOMEM;
|
||||
dev_err(&pdev->dev, "unable to kmalloc\n");
|
||||
goto bad2;
|
||||
return ret;
|
||||
}
|
||||
uioinfo->name = pdev->dev.of_node->name;
|
||||
uioinfo->version = "devicetree";
|
||||
|
||||
/* Multiple IRQs are not supported */
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq == -ENXIO)
|
||||
uioinfo->irq = UIO_IRQ_NONE;
|
||||
else
|
||||
uioinfo->irq = irq;
|
||||
}
|
||||
|
||||
if (!uioinfo || !uioinfo->name || !uioinfo->version) {
|
||||
@ -148,12 +148,15 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
|
||||
|
||||
if (!uioinfo->irq) {
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to get IRQ\n");
|
||||
goto bad0;
|
||||
}
|
||||
uioinfo->irq = ret;
|
||||
if (ret == -ENXIO && pdev->dev.of_node)
|
||||
uioinfo->irq = UIO_IRQ_NONE;
|
||||
else if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to get IRQ\n");
|
||||
goto bad1;
|
||||
}
|
||||
}
|
||||
|
||||
uiomem = &uioinfo->mem[0];
|
||||
|
||||
for (i = 0; i < pdev->num_resources; ++i) {
|
||||
@ -206,19 +209,19 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
|
||||
ret = uio_register_device(&pdev->dev, priv->uioinfo);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to register uio device\n");
|
||||
goto bad1;
|
||||
goto bad2;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
return 0;
|
||||
bad2:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
bad1:
|
||||
kfree(priv);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
bad0:
|
||||
/* kfree uioinfo for OF */
|
||||
if (pdev->dev.of_node)
|
||||
kfree(uioinfo);
|
||||
bad2:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -263,12 +266,13 @@ static const struct dev_pm_ops uio_pdrv_genirq_dev_pm_ops = {
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id uio_of_genirq_match[] = {
|
||||
{ /* empty for now */ },
|
||||
static struct of_device_id uio_of_genirq_match[] = {
|
||||
{ /* This is filled with module_parm */ },
|
||||
{ /* Sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, uio_of_genirq_match);
|
||||
#else
|
||||
# define uio_of_genirq_match NULL
|
||||
module_param_string(of_id, uio_of_genirq_match[0].compatible, 128, 0);
|
||||
MODULE_PARM_DESC(of_id, "Openfirmware id of the device to be handled by uio");
|
||||
#endif
|
||||
|
||||
static struct platform_driver uio_pdrv_genirq = {
|
||||
@ -278,7 +282,7 @@ static struct platform_driver uio_pdrv_genirq = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &uio_pdrv_genirq_dev_pm_ops,
|
||||
.of_match_table = uio_of_genirq_match,
|
||||
.of_match_table = of_match_ptr(uio_of_genirq_match),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -136,9 +136,9 @@ static int pruss_probe(struct platform_device *dev)
|
||||
gdev->pruss_clk = clk_get(&dev->dev, "pruss");
|
||||
if (IS_ERR(gdev->pruss_clk)) {
|
||||
dev_err(&dev->dev, "Failed to get clock\n");
|
||||
ret = PTR_ERR(gdev->pruss_clk);
|
||||
kfree(gdev->info);
|
||||
kfree(gdev);
|
||||
ret = PTR_ERR(gdev->pruss_clk);
|
||||
return ret;
|
||||
} else {
|
||||
clk_enable(gdev->pruss_clk);
|
||||
|
@ -226,19 +226,7 @@ static struct pci_driver sercos3_pci_driver = {
|
||||
.remove = sercos3_pci_remove,
|
||||
};
|
||||
|
||||
static int __init sercos3_init_module(void)
|
||||
{
|
||||
return pci_register_driver(&sercos3_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit sercos3_exit_module(void)
|
||||
{
|
||||
pci_unregister_driver(&sercos3_pci_driver);
|
||||
}
|
||||
|
||||
module_init(sercos3_init_module);
|
||||
module_exit(sercos3_exit_module);
|
||||
|
||||
module_pci_driver(sercos3_pci_driver);
|
||||
MODULE_DESCRIPTION("UIO driver for the Automata Sercos III PCI card");
|
||||
MODULE_AUTHOR("John Ogness <john.ogness@linutronix.de>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user