Commit a3ee514c authored by Federico Vaga's avatar Federico Vaga

Merge branch 'release/v0.9.0'

parents cfdd4b9b d9c1e59d
What: /sys/class/fmc/
Date: July 2018
KernelVersion: 4.18
Contact: Federico Vaga <federico.vaga@cern.ch>
Description:
The fmc/ class sub-directory belongs to the FMC subsystem and
provides general FMC information, per-FMC carrier and per-FMC
mezzanine module.
What: /sys/class/fmc-carrier-X/
Date: July 2018
KernelVersion: 4.18
Contact: Federico Vaga <federico.vaga@cern.ch>
Description:
The /sys/class/fmc-carrier-0, /sys/class/fmc-carrier-1, etc
directories describe FMC carriers (FMC carrier 0, 1, etc). They
contain general FMC carrier information and per FMC slot
information (each FMC carrier has one or more FMC slots)
What: /sys/class/fmc-slot-X.Y/
Date: July 2018
KernelVersion: 4.18
Contact: Federico Vaga <federico.vaga@cern.ch>
Description:
The /sys/class/fmc-slot-0.1, /sys/class/fmc-slot-0.2, etc
directories describe FMC slots (FMC slot 1 on carrier 0,
2 on carrier 0, etc). They contain general FMC slot
information.
What: /sys/class/fmc/fmc-slot-X.Y/fmc_ga
Date: July 2018
KernelVersion: 4.18
Contact: Federico Vaga <federico.vaga@cern.ch>
Description:
The FMC geographical address assigned to the slot as described
in the standard.
What: /sys/class/fmc/fmc-slot-X.Y/present
Date: July 2018
KernelVersion: 4.18
Contact: Federico Vaga <federico.vaga@cern.ch>
Description:
Tells if an FMC mezzanine module is plugged, or not, in the
slot. It returns an integer number representing the presence
status: 1 if an FMC mezzanine module is present, 0 when the
slot is empty.
What: /sys/class/fmc/fmc-slot-X.Y/fru_valid
Date: July 2018
KernelVersion: 4.18
Contact: Federico Vaga <federico.vaga@cern.ch>
Description:
Tells if the FMC mezzanine module EEPROM contains a valid FRU.
It returns an integer number representing the FRU status:
1 if an FMC mezzanine module EEPROM has a valid FRU, 0 when the
FRU is invalid or the FMC slot is empty (present = 0).
What: /sys/class/fmc/fmc-slot-X.Y/eeprom_type
Date: July 2018
KernelVersion: 4.18
Contact: Federico Vaga <federico.vaga@cern.ch>
Description:
Tells the EEPROM device driver to be used by the FMC framework
to access the FMC mezzanine module EEPROM on the current slot.
By standard, and by default, this is "24c02". But since the
standard is unclear on this point, we must support other
EEPROM types as well. The user can write here the I2C device
name of the EEPROM in use.
What: /sys/class/fmc/fmc-slot-X.Y/fru_eeprom
Date: July 2018
KernelVersio: 4.18
Contact: Federico Vaga <federico.vaga@cern.ch>
Description:
A symbolic link to the EEPROM device driver sysfs instance
that drives the access the FMC module EEPROM that contains
the FRU information. This link exists only if the FMC module
is present and it is possible to load an I2C driver for the
EEPROM.
.. _fmc_carrier:
=======================================
Enable FMC Framework For Carrier Boards
=======================================
This document describes how to enable the FMC features on an FMC carrier
board. In order to support this description, you should have a look at the
following figure; it shows a possible FMC carrier that hosts 7 FMC slots,
where 6 of them are occupied by FMC mezzanines. This will be used as an
example in the following discussion.
.. kernel-figure:: fmc-carrier.svg
:alt: FMC Carrier Example
Before being able to register your device as an FMC carrier you should:
- provide an instance of
:c:type:`struct fmc_carrier_operations <fmc_carrier_operations>` with all
the required operations implemented;
- provide an instance of :c:type:`struct i2c_adapter <i2c_adapter>` for each
I2C bus used for FMC purposes;
- provide a description for each available FMC slot using
:c:type:`struct fmc_slot_info <fmc_slot_info>`;
.. note::
The I2C bus does not belong to the FMC domain: FMC is an user of it.
For this reason the declaration and the registration of all the I2C buses
is left to the carrier driver which has full knowledge. Then, the carrier
driver informs the FMC framework about which I2C bus must be used to access
an FMC slot. This approach makes the use of the I2C bus more flexible:
- the carrier is free to connect devices on the bus
- the carrier can delegate the FMC framework to handle I2C devices on an
FMC slot.
Once you have satisfied these requirements, you can register your device as an
FMC carrier by using :c:func:`fmc_carrier_register()`.::
err = fmc_carrier_register(&mycarrier->dev,
&mycarrier_fmc_ops,
ARRAY_SIZE(mycarrier_slot_info),
mycarrier_slot_info);
if (err) {
/* handle errors*/
}
Then, you can use :c:func:`fmc_carrier_unregister()` to unregister the carrier
from the FMC framework.::
err = fmc_carrier_unregister(&mycarrier->dev);
if (err) {
/* handle errors*/
}
Once a device has been registered as an FMC carrier, automatically the FMC
framework registers a device instance for each slot. For each of them, an
I2C EEPROM instance (type 'at24c02') will be created.
If the carrier driver needs the associated :c:type:`struct fmc_carrier`
instance, it can obtained by using :c:func:`fmc_carrier_get()`.
This will increment the device reference counter, so when you are done with
it, remember to release it with :c:func:`fmc_carrier_put()`. But in principle
this should not be necessary because the carrier already has access to all
resources.
This diff is collapsed.
.. _fmc_device:
==================================
Use The FMC Framework From Devices
==================================
Devices requiring access to FMC information need to get the slot
structure :c:type:`struct fmc_slot` in which they are interested by
calling :c:func:`fmc_slot_get()`. This will increment the device reference
counter, so when you are done with it, remember to release it with
:c:func:`fmc_slot_put()`.
Having a valid pointer to :c:type:`struct fmc_slot` enables the device
to use the following functions :c:func:`fmc_slot_present()` and
:c:func:`fmc_slot_fru_valid()`.
As describe in :ref:`fmc_intro_i2c_dev`, FMC modules may have other I2C
devices connected on the same I2C bus used to access the EEPROM with the
FRU data. The device driver that drives[1] the FMC module may want to
create instances for those I2C devices and then use them. To achieve this
the driver must first get the slot with :c:func:`fmc_slot_get()` then
the standard I2C API can be used to create new devices; the important thing
to remember here is that the I2C address for the new device needs to be
adjusted with the FMC slot geographical address by using
:func:`fmc_slot_i2c_address()`. For example::
struct i2c_board_info info_l = { /* your values*/ };
struct i2c_client *i2cdev;
struct fmc_slot *slot;
slot = fmc_slot_get(parent_dev);
if (!slot) {
/* handle error */
}
info.addr = fmc_slot_i2c_address(info.addr, slot->ga);
i2cdev = i2c_new_device(slot->adapter, &info);
if (!i2cdev) {
/* handle error */
}
:Author: Federico Vaga <federico.vaga@cern.ch>
.. _fmc_intro:
=============================
A Summary Of The FMC Standard
=============================
.. warning::
Part of this document assumes that you have access to the `VITA-57.1`_
standard.
Introduction
============
This framework aims to provide standard support for FMC mezzanine module and
FMC carriers as described in `VITA-57.1`_.
The FMC standard describes a generic way to connect IO mezzanines to a carrier
with an FPGA on it. How the FPGA gets programmed is not within the scope of
this standard because despite the fact that FMC has been thought with an FPGA
on one side, this is not an hard requirement.
From a software prospective, and in particular from the prospective of a
generic FMC abstraction in kernel, what is interesting is the access to the
I2C EEPROM on FMC modules (Rule 5.69). This EEPROM contains identification
data (Observation 5.20), which can useful to decide what FPGA code can be
loaded or not in order to avoid electrical problems.
The identification data format is described by the "Platform Management FRU
Information Storage Definition V1.0" (Rule 5.74)
This framework does not implement any cleaver algorithm or data structure
to handle the FMC modules or carriers. What it offers is a standardized
access to the FMC resources. In order to not re-invent yet another wheel,
it reuses internal kernel drivers: I2C adapters, I2C EEPROMs, IPMI FRU.
Ingredients
===========
I2C Bus
-------
Any FMC carrier must provide at least one I2C master. This is necessary to
access the FMC module's EEPROM (Rule 5.69) and, optionally, other I2C devices
(Rule 5.81).
There is not an explicit limit to the number of FMC slots; the limit imposed
is to have no more than four FMC slots per I2C bus. This means, that 8 slots
require at least two distinct I2C buses (Rule 5.82).
The I2C buses layout of the FMC carrier is unknown to this framework. For
this reason this implementation does not handle it directly. Indeed, other
I2C devices on the carrier card can be connected and only the carrier knows
what to do with them or other specialized drivers.
The FMC carrier driver developer must instantiate all necessary I2C buses
(adapters) and inform the FMC framework about which ones to be used for FMC
purposes.
The standard is not very rigid on this side, so we have to be flexible on
the kernel side. In other words, we have to support the worst case scenario
where each FMC slot has a dedicated I2C bus.
.. _fmc_intro_i2c_dev:
I2C EEPROM And Other I2C Devices
--------------------------------
The FMC standard says that an I2C EEPROM is mandatory on FMC modules
(Rule 5.69). The standard reserves for this EEPROM the address ``b10100xx``
(Rule 5.81), where the last two bits (``GA[0..1]``) are used for geographical
addressing within the same I2C bus (Rule 5.82). Other devices can be
connected to that bus but they must not use the address prefix reserved
for the I2C EEPROM (Rule 5.84).
After a discussion with VITA standards board members about `VITA-57.1`_
Rule 5.69, it was acknowledged that the current standard is not completely
clear about what kind of EEPROM is mandatory. Despite the lack of clarity in
`VITA-57.1`_, there is some implicit specification via Rule 5.74. This rule
says that the EEPROM content must be compliant with the "Platform Management
FRU Information Storage Definition V1.0", and here is stated that the EEPROM
to use must be 24C02 compatible.
Because of the lack of clarity about the EEPROM type, we cannot assume that
all the FMC modules around are using a 24C02-compatible EEPROM. For this
reason this framework will automatically instantiate a 24C02 EEPROM for each
slot, but it will also offer a mechanism to replace this instance with another
one. All standard FMC modules that follow this rule will work out of the box,
and the AT24 driver will be used to driver the EEPROM; those which do not
respect the standard will need user intervention.
IPMI FRU
--------
The EEPROM content must be formatted as "Platform Management FRU
Information Storage Definition V1.0" (Rule 5.74) and it must contain at least
the BOARD_INFO area (rule 5.75)
This framework does not provide any direct support to this. If an in-kernel
user (e.g. a driver) needs this information, it can access the EEPROM data and
parse it using the ipmi-fru API.
This framework will only validate the EEPROM content. This means that it will
check if the EEPROM contains, at least, a valid FRU header table.
IPMI Basic Commands
-------------------
It is not mandatory, and for this reason it is not implemented yet.
Presence
---------
The standard dedicate a signal to detect if an IO mezzanine is present or not
(Chapter 5.7: Rule 5.85, Rule 5.86, Rule 5.87).
This means that, on software side, we should expect this information to be
available through the carrier interface.
Observations
------------
- As you can see, it is evident that the role of an FMC framework is to glue
together different components in order to provides a standardized interface
to FMC carriers and mezzanines.
- The standard says *Geographical Addresses* when it speaks about the signals
``GA[0..1]``. This does not really work as a carrier geographical addressing
schema because the standard does not explicitly say that the number of FMC
slots on a carrier is limited to 4. Therefore, this schema works in groups
of 4 FMC slots and not for all the available FMC slots (if more than 4).
So the geographical addressing must take into account a software schema to
distinguish the different I2C buses (the I2C bus number).
.. _`VITA-57.1`: https://www.vita.com/fmc
.. _fmc_framework_user:
==================
FMC User Interface
==================
The user can interact with an FMC carrier and its mezzanines through sysfs.
All attributes are described in the ABI file ``sysfs-class-fmc``
.. _fmc:
.. toctree::
:maxdepth: 2
fmc-introduction
fmc-carrier
fmc-device
This diff is collapsed.
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the fpga framework and fpga manager drivers.
#
ifdef CONFIG_SUPER_REPO
ifdef CONFIG_SUPER_REPO_VERSION
SUBMODULE_VERSIONS += MODULE_INFO(version_$(CONFIG_SUPER_REPO),\"$(CONFIG_SUPER_REPO_VERSION)\");
endif
endif
# The library includes <sdb.h>, so point -I directtly there
# include our header before to avoid conflicts with the kernel
LINUXINCLUDE := -I$(src)/../../include -I$(src)/../../include/linux $(LINUXINCLUDE)
ccflags-y += -I$(src)/../../include
obj-$(CONFIG_FMC) = fmc.o
fmc-y := fmc-core.o
fmc-y += fmc-eeprom.o
fmc-y += fru-parse.o
\ No newline at end of file
-include Makefile.specific
# include parent_common.mk for buildsystem's defines
#use absolute path for REPO_PARENT
REPO_PARENT ?= $(shell /bin/pwd)/../..
-include $(REPO_PARENT)/parent_common.mk
LINUX ?= /lib/modules/$(shell uname -r)/build
export CONFIG_FMC=m
all: modules
modules help install modules_install:
make -C $(LINUX) M=$(shell pwd)
.PHONY: all modules clean help install modules_install
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* FMC Framework
*
* Copyright (C) 2018 CERN
* Author: Federico Vaga <federico.vaga@vaga.pv.it>
*/
#include <linux/version.h>
#if KERNEL_VERSION(3, 11, 0) <= LINUX_VERSION_CODE
#include <linux/ipmi-fru.h>
#else
#include "ipmi-fru.h"
#endif
#if KERNEL_VERSION(3, 11, 0) > LINUX_VERSION_CODE
#define __ATTR_RW(_name) __ATTR(_name, (S_IWUSR | S_IRUGO), \
_name##_show, _name##_store)
#define DEVICE_ATTR_RW(_name) \
struct device_attribute dev_attr_##_name = __ATTR_RW(_name)
#define DEVICE_ATTR_RO(_name) \
struct device_attribute dev_attr_##_name = __ATTR_RO(_name)
#endif
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2019 CERN
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/memory.h>
#include <linux/fmc.h>
#include "fmc-internal.h"
#include "fmc-compat.h"
#define FRU_EEPROM_NAME "fru_eeprom"
/**
* Default EEPROM type according to the standard.
*/
#define FMC_EEPROM_TYPE_DEFAULT "24c02"
/**
* Setup function for the AT24C02 EEPROM. What we need to do here is to
* quickly validate the EEPROM content. The EEPROM should contain a valid
* FRU.
*/
static void fmc_slot_eeprom_setup(struct memory_accessor *macc, void *context)
{
struct fmc_slot *slot = context;
slot->macc = macc;
}
/**
* Default configuration for AT24C02 EEPROM type
*/
static const struct at24_platform_data at24_24c02 = {
.byte_len = 256,
.page_size = 8,
.flags = 0,
.setup = fmc_slot_eeprom_setup,
};
/**
* Initialize I2C EEPROM info with standad values
*/
static void fmc_slot_eeprom_init(struct fmc_slot *slot,
struct i2c_board_info *info,
const char *name)
{
strncpy(info->type, name, I2C_NAME_SIZE);
info->addr = FMC_EEPROM_ADDR_SPACE;
info->platform_data = &slot->at24_data;
}
static void fmc_slot_eeprom_init_default(struct fmc_slot *slot,
struct i2c_board_info *info)
{
memset(info, 0, sizeof(*info));
fmc_slot_eeprom_init(slot, info, FMC_EEPROM_TYPE_DEFAULT);
memcpy(&slot->at24_data, &at24_24c02, sizeof(at24_24c02));
slot->at24_data.context = slot;
}
/**
* Read from EEPROM
*/
ssize_t fmc_slot_eeprom_read(struct fmc_slot *slot,
void *buf, off_t offset, size_t count)
{
/*
* TODO if we export this function, do we have to lock it when we
* use it? Think about it
*/
if (!slot->macc || !slot->macc->read)
return -ENODEV;
return slot->macc->read(slot->macc, buf, offset, count);
}
EXPORT_SYMBOL(fmc_slot_eeprom_read);
/**
* Add EEPROM which will be associated to the given FMC slot
* @slot: FMC slot instance
* @info: I2C EEPROM information
*
* This creates also a symlink to the EEPROM device in the FMC slot sysfs
* directory. If this fails it will be reported on dmesg but this does not
* prevent the system from running; for this reason the function will
* succeed anyway.
*
* This should be used only by the FMC framework itself. But, this is also
* exported to other users in order to be able to support corner cases
* where the EEPROM type is not supported by the AT24 driver (in which case
* the sysfs attribute eeprom_type can be easily used).
*
* Return: 0 on success, otherwise a negative error number
*/
static int __fmc_slot_eeprom_add(struct fmc_slot *slot,
struct i2c_board_info *info)
{
struct i2c_board_info info_l = *info;
int err;
if (!fmc_slot_present(slot))
return -ENODEV;
if (!slot->adapter) {
dev_err(&slot->dev, "missing I2C adapter\n");
return -ENODEV;
}
info_l.addr = fmc_slot_i2c_address(info_l.addr, slot->ga);
slot->eeprom = i2c_new_device(slot->adapter, &info_l);
if (!slot->eeprom)
return -ENODEV;
err = sysfs_create_link(&slot->dev.kobj, &slot->eeprom->dev.kobj,
FRU_EEPROM_NAME);
if (err)
dev_err(&slot->dev, "Failed to create eeprom symlink to %s\n",
dev_name(&slot->eeprom->dev));
return 0;
}
/**
* Add EEPROM to a given FMC slot
* @slot: FMC slot instance
*
* Return: 0 on success, otherwise a negative error number
*/
int fmc_slot_eeprom_add(struct fmc_slot *slot)
{
struct i2c_board_info i2c_info;
fmc_slot_eeprom_init_default(slot, &i2c_info);
return __fmc_slot_eeprom_add(slot, &i2c_info);
}
/**
* Remove EEPROM associated to the given FMC slot
* @slot: FMC slot instance
*
* This should be used only by the FMC framework itself. But, this is also
* exported to other users in order to be able to support corner cases
* where the EEPROM type is not supported by the AT24 driver (in which case
* the sysfs attribute eeprom_type can be easily used).
*/
void fmc_slot_eeprom_del(struct fmc_slot *slot)
{
if (!slot || !slot->eeprom)
return;
sysfs_remove_link(&slot->dev.kobj, FRU_EEPROM_NAME);
i2c_unregister_device(slot->eeprom);
slot->eeprom = NULL;
slot->macc = NULL;
}
/**
* Replace current EEPROM instance with a given one
* @slot: FMC slot instance
* @info: I2C EEPROM information
*
* Return: 0 on success, otherwise a negative error number
*/
static int fmc_slot_eeprom_replace(struct fmc_slot *slot,
struct i2c_board_info *info)
{
fmc_slot_eeprom_del(slot);
return __fmc_slot_eeprom_add(slot, info);
}
int fmc_slot_eeprom_type_set(struct fmc_slot *slot, const char *type)
{
#define FMC_EEPROM_SIZE_OFF 3
struct i2c_board_info i2c_info;
unsigned int len;
int ret;
if (strncmp(type, "24c", 3)) {
if (strncmp(type, "at24c", 5)) {
dev_err(&slot->dev,
"Invalid EEPROM type. Expected 'at24cXX' or '24cXX' (got %s)\n",
type);
return -EINVAL;
}
type += 2; /* skip 'at' */
}
ret = kstrtouint(type + FMC_EEPROM_SIZE_OFF, 10, &len);
if (ret < 0) {
dev_err(&slot->dev,
"Failed to get EEPROM size %d ('%s' %d)\n",
ret, type + FMC_EEPROM_SIZE_OFF, len);
return -EINVAL;
}
memset(&i2c_info, 0, sizeof(struct i2c_board_info));
memset(&slot->at24_data, 0, sizeof(struct at24_platform_data));
len = (len * 1024) / 8;
/*
* For sizes between 1K and 16K the EEPROM uses part of the device
* address as internal memory address
*/
if (len > 4096) /* 32K 4KiB */
slot->at24_data.flags = AT24_FLAG_ADDR16;
else if (len > 131072) /* 1024K 128KiB */
return -EINVAL;
fmc_slot_eeprom_init(slot, &i2c_info, type);
slot->at24_data.byte_len = len;
slot->at24_data.page_size = 1; /* 1Byte page to play safe */
slot->at24_data.setup = fmc_slot_eeprom_setup;
slot->at24_data.context = slot;
dev_dbg(&slot->dev, "%s 0x%x %d %d 0x%x\n",
i2c_info.type, i2c_info.addr,
slot->at24_data.byte_len, slot->at24_data.page_size,
slot->at24_data.flags);
return fmc_slot_eeprom_replace(slot, &i2c_info);
}
EXPORT_SYMBOL(fmc_slot_eeprom_type_set);
/**
* Check if an FMC mezzanine in the FMC slot has a valid FRU
* @slot: FMC slot to verify
*
* Return: 1 if the slot is present, otherwise 0
*/
int fmc_slot_fru_valid(struct fmc_slot *slot)
{
struct fru_common_header fru_h;
ssize_t ret;
ret = fmc_slot_eeprom_read(slot, (void *)&fru_h, 0x0,
sizeof(struct fru_common_header));
if (ret != sizeof(struct fru_common_header)) {
dev_err(&slot->dev,
"Failed while reading mezzanine's EEPROM (ret: %zd)\n",
ret);
return 0;
}
if (!fru_header_cksum_ok(&fru_h)) {
dev_warn(&slot->dev, "Invalid FRU: checksum failure\n");
return 0;
}
return 1;
}
EXPORT_SYMBOL(fmc_slot_fru_valid);
static ssize_t fru_valid_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct fmc_slot *slot = to_fmc_slot(dev);
return sprintf(buf, "%d\n", fmc_slot_fru_valid(slot));
}
static DEVICE_ATTR_RO(fru_valid);
const char *fmc_slot_eeprom_type_get(struct fmc_slot *slot)
{
return slot && slot->eeprom ? slot->eeprom->name : "none";
}
EXPORT_SYMBOL(fmc_slot_eeprom_type_get);
static ssize_t eeprom_type_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return sprintf(buf, "%s\n",
fmc_slot_eeprom_type_get(to_fmc_slot(dev)));
}
static ssize_t eeprom_type_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct fmc_slot *slot = to_fmc_slot(dev);
int ret;
if (!fmc_slot_present(slot))
return -ENODEV;
ret = fmc_slot_eeprom_type_set(slot, buf);
return ret ? ret : count;
}
static DEVICE_ATTR_RW(eeprom_type);
static struct attribute *fmc_slot_eeprom_attrs[] = {
&dev_attr_fru_valid.attr,
&dev_attr_eeprom_type.attr,
NULL,
};
const struct attribute_group fmc_slot_eeprom_group = {
.attrs = fmc_slot_eeprom_attrs,
};
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2018 CERN
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#include <linux/sysfs.h>
#include <linux/fmc.h>
extern const struct attribute_group fmc_slot_eeprom_group;
extern void fmc_slot_eeprom_del(struct fmc_slot *slot);
extern int fmc_slot_eeprom_add(struct fmc_slot *slot);
/*
* 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/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0)
/*
* On kernel 3.11 and greater this is included
*/
#include <linux/slab.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);
}
/* 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;
tl = __fru_get_board_tl(header, nr);
if (!tl)
return NULL;
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);
}
EXPORT_SYMBOL(fru_get_board_manufacturer);
char *fru_get_product_name(struct fru_common_header *header)
{
return __fru_alloc_get_tl(header, 1);
}
EXPORT_SYMBOL(fru_get_product_name);
char *fru_get_serial_number(struct fru_common_header *header)
{
return __fru_alloc_get_tl(header, 2);
}
EXPORT_SYMBOL(fru_get_serial_number);
char *fru_get_part_number(struct fru_common_header *header)
{
return __fru_alloc_get_tl(header, 3);
}
EXPORT_SYMBOL(fru_get_part_number);
#endif
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2019 CERN
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/idr.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/version.h>
#if KERNEL_VERSION(3, 10, 0) <= LINUX_VERSION_CODE
#include <linux/platform_data/at24.h>
#else
#include <linux/i2c/at24.h>
#endif
#ifndef _LINUX_FMC_H
#define _LINUX_FMC_H
struct fmc_carrier;
struct fmc_slot;
#define FMC_SLOT_MAX_I2C 4
/**
* According to the standard, the EEPROM form a mezzanine must be
* at address 0x50 to 0x53
*/
#define FMC_EEPROM_ADDR_SPACE (0x50)
/**
* Convert a Geographical Address suffix into an I2C address
* @ga: geographical address
*
* Return: an I2C address suffix
*
* Geographical address must be translated into a proper address
*
* Observation 5.22
* GA[0] -> Address[1]
* GA[1] -> Address[0]
*/
static inline uint8_t fmc_ga_to_i2c_addr(uint8_t ga)
{
return ((((ga >> 1) & 0x1) | ((ga & 0x1) << 1)) & 0x3);
}
/**
* Fix given address with the FMC geographical address
* @addr: an address
* @ga: geographical address to apply
*
* Return: a fixed address
*/
static inline uint8_t fmc_slot_i2c_address(uint8_t addr, uint8_t ga)
{
return (addr & ~0x3) | fmc_ga_to_i2c_addr(ga);
}
/**
* struct fmc_slot - FMC slot instance
* @dev: FMC slot device
* @adapter: I2C bus adapter used to communicate with the slot
* @ga: Geographical Address according to FMC standard
* @lun: slot logical unit number
* @eeprom: I2C adapter used to talk with the mezzanine eeprom
* @macc: operations to access the mezzanine EEPROM
*
* The FMC standard does not say that all the FMC slots are on the same
* I2C bus. So, we must foresee that this is not the case and each FMC
* slot has a dedicated I2C bus (but using different I2C address for
* each slot in theory).
*/
struct fmc_slot {
struct device dev;
struct i2c_adapter *adapter;
uint8_t ga;
uint8_t lun;
struct i2c_client *eeprom;
struct memory_accessor *macc;
struct at24_platform_data at24_data;
};
/**
* struct fmc_flot_info - information for adding new slots
* @i2c_bus_nr: I2C bus number
* @ga: Geographical Address according to FMC standard
* @lun: slot logical unit number
*/
struct fmc_slot_info {
int i2c_bus_nr;
uint8_t ga;
uint8_t lun;
};
/**
* Get an FMC slot from its Linux device
* @dev_ptr: pointer to device structure
*
* Return: correspondent fmc_carrier structure
*/
static inline struct fmc_slot *to_fmc_slot(struct device *dev_ptr)
{
return container_of(dev_ptr, struct fmc_slot, dev);
}
struct fmc_slot *fmc_slot_get(struct device *parent,
unsigned int lun);
void fmc_slot_put(struct fmc_slot *slot);
int fmc_slot_present(struct fmc_slot *slot);
int fmc_slot_fru_valid(struct fmc_slot *slot);
ssize_t fmc_slot_eeprom_read(struct fmc_slot *slot,
void *buf, off_t offset, size_t count);
/**
* sturct fmc_carrier_operations - FMC operations for carriers
* @owner: the module that will execute the operations
* @is_present: check if an FMC slot is present or not in the slot
* (present: 1, not present or error: 0). The carrier
* must check PRSNT_M2C_L (VITA 57.1 Rule 5.76)
*/
struct fmc_carrier_operations {
struct module *owner;
int (*is_present)(struct fmc_carrier *carrier,
struct fmc_slot *slot);
};
/**
* struct fmc_carrier - FMC carrier instance
* @dev: FMC carrier device instance
* @ops: list of operations for FMC
* @slot_nr: number of maximum slots available
* @slot: list of slot's pointers.
* @priv: private data
*/
struct fmc_carrier {
struct device dev;
const struct fmc_carrier_operations *ops;
unsigned int slot_nr;
struct fmc_slot **slot;
void *priv;
};
/**
* Get an FMC carrier from its Linux device
* @dev_ptr: pointer to device structure
*
* Return: correspondent fmc_carrier structure
*/
static inline struct fmc_carrier *to_fmc_carrier(struct device *dev_ptr)
{
return container_of(dev_ptr, struct fmc_carrier, dev);
}
struct fmc_carrier *fmc_carrier_get(struct device *parent);
void fmc_carrier_put(struct fmc_carrier *carrier);
int fmc_carrier_register(struct device *parent,
const struct fmc_carrier_operations *ops,
unsigned int nr_slot,
struct fmc_slot_info *slot_info,
void *priv);
int fmc_carrier_unregister(struct device *parent);
int fmc_slot_eeprom_type_set(struct fmc_slot *slot, const char *type);
const char *fmc_slot_eeprom_type_get(struct fmc_slot *slot);
#endif
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#ifndef __LINUX_IPMI_FRU_H__
#define __LINUX_IPMI_FRU_H__
#ifdef __KERNEL__
# include <linux/types.h>
# include <linux/string.h>
#else
# include <stdint.h>
# include <string.h>
#endif
#define FRU_SIZE_MAX 256
/*
* These structures match the unaligned crap we have in FRU1011.pdf
* (http://download.intel.com/design/servers/ipmi/FRU1011.pdf)
*/
/* chapter 8, page 5 */
struct fru_common_header {
uint8_t format; /* 0x01 */
uint8_t internal_use_off; /* multiple of 8 bytes */
uint8_t chassis_info_off; /* multiple of 8 bytes */
uint8_t board_area_off; /* multiple of 8 bytes */
uint8_t product_area_off; /* multiple of 8 bytes */
uint8_t multirecord_off; /* multiple of 8 bytes */
uint8_t pad; /* must be 0 */
uint8_t checksum; /* sum modulo 256 must be 0 */
};
/* chapter 9, page 5 -- internal_use: not used by us */
/* chapter 10, page 6 -- chassis info: not used by us */
/* chapter 13, page 9 -- used by board_info_area below */
struct fru_type_length {
uint8_t type_length;
uint8_t data[0];
};
/* chapter 11, page 7 */
struct fru_board_info_area {
uint8_t format; /* 0x01 */
uint8_t area_len; /* multiple of 8 bytes */
uint8_t language; /* I hope it's 0 */
uint8_t mfg_date[3]; /* LSB, minutes since 1996-01-01 */
struct fru_type_length tl[0]; /* type-length stuff follows */
/*
* the TL there are in order:
* Board Manufacturer
* Board Product Name
* Board Serial Number
* Board Part Number
* FRU File ID (may be null)
* more manufacturer-specific stuff
* 0xc1 as a terminator
* 0x00 pad to a multiple of 8 bytes - 1
* checksum (sum of all stuff module 256 must be zero)
*/
};
enum fru_type {
FRU_TYPE_BINARY = 0x00,
FRU_TYPE_BCDPLUS = 0x40,
FRU_TYPE_ASCII6 = 0x80,
FRU_TYPE_ASCII = 0xc0, /* not ascii: depends on language */
};
/*
* some helpers
*/
static inline struct fru_board_info_area *fru_get_board_area(
const struct fru_common_header *header)
{
/* we know for sure that the header is 8 bytes in size */
return (struct fru_board_info_area *)(header + header->board_area_off);
}
static inline int fru_type(struct fru_type_length *tl)
{
return tl->type_length & 0xc0;
}
static inline int fru_length(struct fru_type_length *tl)
{
return (tl->type_length & 0x3f) + 1; /* len of whole record */
}
/* assume ascii-latin1 encoding */
static inline int fru_strlen(struct fru_type_length *tl)
{
return fru_length(tl) - 1;
}
static inline char *fru_strcpy(char *dest, struct fru_type_length *tl)
{
int len = fru_strlen(tl);
memcpy(dest, tl->data, len);
dest[len] = '\0';
return dest;
}
static inline struct fru_type_length *fru_next_tl(struct fru_type_length *tl)
{
return tl + fru_length(tl);
}
static inline int fru_is_eof(struct fru_type_length *tl)
{
return tl->type_length == 0xc1;
}
/*
* External functions defined in fru-parse.c.
*/
extern int fru_header_cksum_ok(struct fru_common_header *header);
extern int fru_bia_cksum_ok(struct fru_board_info_area *bia);
/* All these 4 return allocated strings by calling fru_alloc() */
extern char *fru_get_board_manufacturer(struct fru_common_header *header);
extern char *fru_get_product_name(struct fru_common_header *header);
extern char *fru_get_serial_number(struct fru_common_header *header);
extern char *fru_get_part_number(struct fru_common_header *header);
/* This must be defined by the caller of the above functions */
extern void *fru_alloc(size_t size);
#endif /* __LINUX_IMPI_FRU_H__ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment