blob: 4d762c48d06625e3c3d70fc9593fdb8d81533cbf [file] [log] [blame] [edit]
/* SPDX-License-Identifier: GPL-2.0 */
/*
* EdgeTPU multi-chip package management.
*
* Copyright (C) 2020 Google, Inc.
*/
#ifndef __EDGETPU_MCP_H__
#define __EDGETPU_MCP_H__
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include "edgetpu-config.h"
#include "edgetpu-internal.h"
struct edgetpu_mcp {
/* constant fields after initialization */
u8 id; /* the MCP ID matches etdev->mcp_id */
u8 pkgtype; /* package type, definition is chip-dependent */
u8 total_num; /* total number of etdevs expected by this MCP */
u64 serial_num; /* serial number of the package */
/* fields need to be locked before accessing */
struct mutex lock;
u8 cur_num; /* current number of etdevs registered */
/*
* Array of etdevs in this MCP, its length is @total_num.
*
* @etdevs[i] equals NULL means the etdev with mcp_die_index = i is not
* added yet.
*
* @etdevs[i] equals -ENODEV if that device is added but marked as
* failed by edgetpu_mcp_probe_fail().
*
* One should check with !IS_ERR_OR_NULL(etdevs[i]) before accessing.
*/
struct edgetpu_dev **etdevs;
/* MCP-wide fatal errors pending runtime notification */
uint errors_pending_mask;
spinlock_t errors_pending_lock;
struct work_struct errors_pending_work; /* for notify via kworker */
};
#ifdef EDGETPU_HAS_MCP
/*
* Registers @etdev to the MCP manager.
*
* @etdev->mcp_pkg_type, @etdev->mcp_id, and @etdev->mcp_die_index must be set
* before this call.
*
* Returns 0 on success, or -errno on error.
*/
int edgetpu_mcp_add_etdev(struct edgetpu_dev *etdev);
/*
* Init @mcp if all the dies in the mcp have been probed.
*
* After trying to probe each etdev on PCI, the mcp list is fully populated
* with the corresponding etdevs. At this time the mcp list consists of
* successfully probed *etdev and -ENODEV for etdevs that failed probe.
* The mcp can then be initialized.
*
* Returns 0 on success, or -errno on error.
*/
int edgetpu_mcp_init_if_last(struct edgetpu_dev *etdev);
/*
* Reverts edgetpu_mcp_add_etdev().
*
* @etdev->mcp_id and @etdev->mcp_die_index must be the same when called
* edgetpu_mcp_add_etdev().
*/
void edgetpu_mcp_remove_etdev(struct edgetpu_dev *etdev);
/*
* Marks etdev that failed to probe, set MCP entry to -ENODEV.
*/
void edgetpu_mcp_probe_fail(struct edgetpu_dev *etdev);
/*
* Invokes @callback with each (currently) registered MCP.
*
* If @stop_on_err is true, this function stops when @callback returned non-zero
* value. And that value is also returned by this function.
* If @stop_on_err is false, @callback will be called exactly the number of
* registered MCPs times, and this function will always return 0.
*
* @data can be any value, it will be directly passed to @callback.
*
* @callback is expected to return 0 on success, -errno otherwise.
*
* Don't call this or other edgetpu_mcp_* functions recursively to prevent dead
* locking.
*
* It's @callback's responsibility to hold edgetpu_mcp's lock when access
* non-constant fields of edgetpu_mcp.
*/
int edgetpu_mcp_each(bool stop_on_err, void *data,
int (*callback)(struct edgetpu_mcp *, void *));
/*
* Invoke @callback on each device for the specified @mcp.
*
* @data can be any value, it will be directly passed to @callback.
*/
void edgetpu_mcp_foreach_etdev(struct edgetpu_mcp *mcp,
void (*callback)(struct edgetpu_dev *, void *),
void *data);
/*
* Returns the MCP object @etdev belongs to.
*
* Returns NULL when such object is not found.
*
* Note: The returned pointer will be released when the last etdev is removed.
* Don't use the returned pointer after edgetpu_mcp_remove_etdev() is called.
*/
struct edgetpu_mcp *edgetpu_mcp_of_etdev(struct edgetpu_dev *etdev);
/* Returns the next available MCP ID. */
int edgetpu_mcp_next_id(void);
/* To allocate / release structures for MCP management */
void edgetpu_mcp_init(void);
void edgetpu_mcp_exit(void);
#else /* !EDGETPU_HAS_MCP */
static inline struct edgetpu_mcp *
edgetpu_mcp_of_etdev(struct edgetpu_dev *etdev)
{
return NULL;
}
static inline void edgetpu_mcp_init(void)
{
}
static inline void edgetpu_mcp_exit(void)
{
}
#endif /* EDGETPU_HAS_MCP */
#endif /* __EDGETPU_MCP_H__ */