Skip to content
Snippets Groups Projects
fmc.h 4.71 KiB
Newer Older
/* 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(5, 1, 0) > LINUX_VERSION_CODE
#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

Federico Vaga's avatar
Federico Vaga committed
/*
 * According to the standard, the EEPROM form a mezzanine must be
 * at address 0x50 to 0x53
 */
Federico Vaga's avatar
Federico Vaga committed
#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);
}

/**
Federico Vaga's avatar
Federico Vaga committed
 * 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
Federico Vaga's avatar
Federico Vaga committed
 * @at24_data: data for the EEPROM driver
 *
 * 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;

#if KERNEL_VERSION(4, 6, 0) <= LINUX_VERSION_CODE
#define AT24_NUM_PROPERTIES 4
	struct property_entry at24_data[AT24_NUM_PROPERTIES];
	struct nvmem_device *nvmem;
	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);

/**
 * struct 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