/* SPDX-License-Identifier: BSD-3-Clause
 * Copyright(c) 2010-2018 Intel Corporation
 */

#include "opae_hw_api.h"
#include "opae_debug.h"
#include "ifpga_api.h"

/* OPAE Bridge Functions */

/**
 * opae_bridge_alloc - alloc opae_bridge data structure
 * @name: bridge name.
 * @ops: ops of this bridge.
 * @data: private data of this bridge.
 *
 * Return opae_bridge on success, otherwise NULL.
 */
struct opae_bridge *
opae_bridge_alloc(const char *name, struct opae_bridge_ops *ops, void *data)
{
	struct opae_bridge *br = opae_zmalloc(sizeof(*br));

	if (!br)
		return NULL;

	br->name = name;
	br->ops = ops;
	br->data = data;

	opae_log("%s %p\n", __func__, br);

	return br;
}

/**
 * opae_bridge_reset -  reset opae_bridge
 * @br: bridge to be reset.
 *
 * Return: 0 on success, otherwise error code.
 */
int opae_bridge_reset(struct opae_bridge *br)
{
	if (!br)
		return -EINVAL;

	if (br->ops && br->ops->reset)
		return br->ops->reset(br);

	opae_log("%s no ops\n", __func__);

	return -ENOENT;
}

/* Accelerator Functions */

/**
 * opae_accelerator_alloc - alloc opae_accelerator data structure
 * @name: accelerator name.
 * @ops: ops of this accelerator.
 * @data: private data of this accelerator.
 *
 * Return: opae_accelerator on success, otherwise NULL.
 */
struct opae_accelerator *
opae_accelerator_alloc(const char *name, struct opae_accelerator_ops *ops,
		       void *data)
{
	struct opae_accelerator *acc = opae_zmalloc(sizeof(*acc));

	if (!acc)
		return NULL;

	acc->name = name;
	acc->ops = ops;
	acc->data = data;

	opae_log("%s %p\n", __func__, acc);

	return acc;
}

/**
 * opae_acc_reg_read - read accelerator's register from its reg region.
 * @acc: accelerator to read.
 * @region_idx: reg region index.
 * @offset: reg offset.
 * @byte: read operation width, e.g 4 byte = 32bit read.
 * @data: data to store the value read from the register.
 *
 * Return: 0 on success, otherwise error code.
 */
int opae_acc_reg_read(struct opae_accelerator *acc, unsigned int region_idx,
		      u64 offset, unsigned int byte, void *data)
{
	if (!acc || !data)
		return -EINVAL;

	if (acc->ops && acc->ops->read)
		return acc->ops->read(acc, region_idx, offset, byte, data);

	return -ENOENT;
}

/**
 * opae_acc_reg_write - write to accelerator's register from its reg region.
 * @acc: accelerator to write.
 * @region_idx: reg region index.
 * @offset: reg offset.
 * @byte: write operation width, e.g 4 byte = 32bit write.
 * @data: data stored the value to write to the register.
 *
 * Return: 0 on success, otherwise error code.
 */
int opae_acc_reg_write(struct opae_accelerator *acc, unsigned int region_idx,
		       u64 offset, unsigned int byte, void *data)
{
	if (!acc || !data)
		return -EINVAL;

	if (acc->ops && acc->ops->write)
		return acc->ops->write(acc, region_idx, offset, byte, data);

	return -ENOENT;
}

/**
 * opae_acc_get_info - get information of an accelerator.
 * @acc: targeted accelerator
 * @info: accelerator info data structure to be filled.
 *
 * Return: 0 on success, otherwise error code.
 */
int opae_acc_get_info(struct opae_accelerator *acc, struct opae_acc_info *info)
{
	if (!acc || !info)
		return -EINVAL;

	if (acc->ops && acc->ops->get_info)
		return acc->ops->get_info(acc, info);

	return -ENOENT;
}

/**
 * opae_acc_get_region_info - get information of an accelerator register region.
 * @acc: targeted accelerator
 * @info: accelerator region info data structure to be filled.
 *
 * Return: 0 on success, otherwise error code.
 */
int opae_acc_get_region_info(struct opae_accelerator *acc,
			     struct opae_acc_region_info *info)
{
	if (!acc || !info)
		return -EINVAL;

	if (acc->ops && acc->ops->get_region_info)
		return acc->ops->get_region_info(acc, info);

	return -ENOENT;
}

/**
 * opae_acc_set_irq -  set an accelerator's irq.
 * @acc: targeted accelerator
 * @start: start vector number
 * @count: count of vectors to be set from the start vector
 * @evtfds: event fds to be notified when corresponding irqs happens
 *
 * Return: 0 on success, otherwise error code.
 */
int opae_acc_set_irq(struct opae_accelerator *acc,
		     u32 start, u32 count, s32 evtfds[])
{
	if (!acc || !acc->data)
		return -EINVAL;

	if (start + count <= start)
		return -EINVAL;

	if (acc->ops && acc->ops->set_irq)
		return acc->ops->set_irq(acc, start, count, evtfds);

	return -ENOENT;
}

/**
 * opae_acc_get_uuid -  get accelerator's UUID.
 * @acc: targeted accelerator
 * @uuid: a pointer to UUID
 *
 * Return: 0 on success, otherwise error code.
 */
int opae_acc_get_uuid(struct opae_accelerator *acc,
		      struct uuid *uuid)
{
	if (!acc || !uuid)
		return -EINVAL;

	if (acc->ops && acc->ops->get_uuid)
		return acc->ops->get_uuid(acc, uuid);

	return -ENOENT;
}

/* Manager Functions */

/**
 * opae_manager_alloc - alloc opae_manager data structure
 * @name: manager name.
 * @ops: ops of this manager.
 * @network_ops: ops of network management.
 * @data: private data of this manager.
 *
 * Return: opae_manager on success, otherwise NULL.
 */
struct opae_manager *
opae_manager_alloc(const char *name, struct opae_manager_ops *ops,
		struct opae_manager_networking_ops *network_ops, void *data)
{
	struct opae_manager *mgr = opae_zmalloc(sizeof(*mgr));

	if (!mgr)
		return NULL;

	mgr->name = name;
	mgr->ops = ops;
	mgr->network_ops = network_ops;
	mgr->data = data;

	opae_log("%s %p\n", __func__, mgr);

	return mgr;
}

/**
 * opae_manager_flash - flash a reconfiguration image via opae_manager
 * @mgr: opae_manager for flash.
 * @id: id of target region (accelerator).
 * @buf: image data buffer.
 * @size: buffer size.
 * @status: status to store flash result.
 *
 * Return: 0 on success, otherwise error code.
 */
int opae_manager_flash(struct opae_manager *mgr, int id, void *buf, u32 size,
		       u64 *status)
{
	if (!mgr)
		return -EINVAL;

	if (mgr && mgr->ops && mgr->ops->flash)
		return mgr->ops->flash(mgr, id, buf, size, status);

	return -ENOENT;
}

/* Adapter Functions */

/**
 * opae_adapter_data_alloc - alloc opae_adapter_data data structure
 * @type: opae_adapter_type.
 *
 * Return: opae_adapter_data on success, otherwise NULL.
 */
void *opae_adapter_data_alloc(enum opae_adapter_type type)
{
	struct opae_adapter_data *data;
	int size;

	switch (type) {
	case OPAE_FPGA_PCI:
		size = sizeof(struct opae_adapter_data_pci);
		break;
	case OPAE_FPGA_NET:
		size = sizeof(struct opae_adapter_data_net);
		break;
	default:
		size = sizeof(struct opae_adapter_data);
		break;
	}

	data = opae_zmalloc(size);
	if (!data)
		return NULL;

	data->type = type;

	return data;
}

static struct opae_adapter_ops *match_ops(struct opae_adapter *adapter)
{
	struct opae_adapter_data *data;

	if (!adapter || !adapter->data)
		return NULL;

	data = adapter->data;

	if (data->type == OPAE_FPGA_PCI)
		return &ifpga_adapter_ops;

	return NULL;
}

/**
 * opae_adapter_init - init opae_adapter data structure
 * @adapter: pointer of opae_adapter data structure
 * @name: adapter name.
 * @data: private data of this adapter.
 *
 * Return: 0 on success.
 */
int opae_adapter_init(struct opae_adapter *adapter,
		const char *name, void *data)
{
	if (!adapter)
		return -ENOMEM;

	TAILQ_INIT(&adapter->acc_list);
	adapter->data = data;
	adapter->name = name;
	adapter->ops = match_ops(adapter);

	return 0;
}

/**
 * opae_adapter_enumerate - enumerate this adapter
 * @adapter: adapter to enumerate.
 *
 * Return: 0 on success, otherwise error code.
 */
int opae_adapter_enumerate(struct opae_adapter *adapter)
{
	int ret = -ENOENT;

	if (!adapter)
		return -EINVAL;

	if (adapter->ops && adapter->ops->enumerate)
		ret = adapter->ops->enumerate(adapter);

	if (!ret)
		opae_adapter_dump(adapter, 0);

	return ret;
}

/**
 * opae_adapter_destroy - destroy this adapter
 * @adapter: adapter to destroy.
 *
 * destroy things allocated during adapter enumeration.
 */
void opae_adapter_destroy(struct opae_adapter *adapter)
{
	if (adapter && adapter->ops && adapter->ops->destroy)
		adapter->ops->destroy(adapter);
}

/**
 * opae_adapter_get_acc - find and return accelerator with matched id
 * @adapter: adapter to find the accelerator.
 * @acc_id: id (index) of the accelerator.
 *
 * destroy things allocated during adapter enumeration.
 */
struct opae_accelerator *
opae_adapter_get_acc(struct opae_adapter *adapter, int acc_id)
{
	struct opae_accelerator *acc = NULL;

	if (!adapter)
		return NULL;

	opae_adapter_for_each_acc(adapter, acc)
		if (acc->index == acc_id)
			return acc;

	return NULL;
}

/**
 * opae_manager_read_mac_rom - read the content of the MAC ROM
 * @mgr: opae_manager for MAC ROM
 * @port: the port number of retimer
 * @addr: buffer of the MAC address
 *
 * Return: return the bytes of read successfully
 */
int opae_manager_read_mac_rom(struct opae_manager *mgr, int port,
		struct opae_ether_addr *addr)
{
	if (!mgr || !mgr->network_ops)
		return -EINVAL;

	if (mgr->network_ops->read_mac_rom)
		return mgr->network_ops->read_mac_rom(mgr,
				port * sizeof(struct opae_ether_addr),
				addr, sizeof(struct opae_ether_addr));

	return -ENOENT;
}

/**
 * opae_manager_write_mac_rom - write data into MAC ROM
 * @mgr: opae_manager for MAC ROM
 * @port: the port number of the retimer
 * @addr: data of the MAC address
 *
 * Return: return written bytes
 */
int opae_manager_write_mac_rom(struct opae_manager *mgr, int port,
		struct opae_ether_addr *addr)
{
	if (!mgr || !mgr->network_ops)
		return -EINVAL;

	if (mgr->network_ops && mgr->network_ops->write_mac_rom)
		return mgr->network_ops->write_mac_rom(mgr,
				port * sizeof(struct opae_ether_addr),
				addr, sizeof(struct opae_ether_addr));

	return -ENOENT;
}

/**
 * opae_manager_get_eth_group_nums - get eth group numbers
 * @mgr: opae_manager for eth group
 *
 * Return: the numbers of eth group
 */
int opae_manager_get_eth_group_nums(struct opae_manager *mgr)
{
	if (!mgr || !mgr->network_ops)
		return -EINVAL;

	if (mgr->network_ops->get_retimer_info)
		return mgr->network_ops->get_eth_group_nums(mgr);

	return -ENOENT;
}

/**
 * opae_manager_get_eth_group_info - get eth group info
 * @mgr: opae_manager for eth group
 * @group_id: id for eth group
 * @info: info return to caller
 *
 * Return: 0 on success, otherwise error code
 */
int opae_manager_get_eth_group_info(struct opae_manager *mgr,
	       u8 group_id, struct opae_eth_group_info *info)
{
	if (!mgr || !mgr->network_ops)
		return -EINVAL;

	if (mgr->network_ops->get_retimer_info)
		return mgr->network_ops->get_eth_group_info(mgr,
			group_id, info);

	return -ENOENT;
}

/**
 * opae_manager_get_eth_group_region_info
 * @mgr: opae_manager for flash.
 * @info: the memory region info for eth group
 *
 * Return: 0 on success, otherwise error code.
 */
int opae_manager_get_eth_group_region_info(struct opae_manager *mgr,
		u8 group_id, struct opae_eth_group_region_info *info)
{
	if (!mgr)
		return -EINVAL;

	if (group_id >= MAX_ETH_GROUP_DEVICES)
		return -EINVAL;

	info->group_id = group_id;

	if (mgr && mgr->ops && mgr->ops->get_eth_group_region_info)
		return mgr->ops->get_eth_group_region_info(mgr, info);

	return -ENOENT;
}

/**
 * opae_manager_eth_group_read_reg - read ETH group register
 * @mgr: opae_manager for ETH Group
 * @group_id: ETH group id
 * @type: eth type
 * @index: port index in eth group device
 * @addr: register address of ETH Group
 * @data: read buffer
 *
 * Return: 0 on success, otherwise error code
 */
int opae_manager_eth_group_read_reg(struct opae_manager *mgr, u8 group_id,
		u8 type, u8 index, u16 addr, u32 *data)
{
	if (!mgr || !mgr->network_ops)
		return -EINVAL;

	if (mgr->network_ops->eth_group_reg_read)
		return mgr->network_ops->eth_group_reg_read(mgr, group_id,
				type, index, addr, data);

	return -ENOENT;
}

/**
 * opae_manager_eth_group_write_reg - write ETH group register
 * @mgr: opae_manager for ETH Group
 * @group_id: ETH group id
 * @type: eth type
 * @index: port index in eth group device
 * @addr: register address of ETH Group
 * @data: data will write to register
 *
 * Return: 0 on success, otherwise error code
 */
int opae_manager_eth_group_write_reg(struct opae_manager *mgr, u8 group_id,
		u8 type, u8 index, u16 addr, u32 data)
{
	if (!mgr || !mgr->network_ops)
		return -EINVAL;

	if (mgr->network_ops->eth_group_reg_write)
		return mgr->network_ops->eth_group_reg_write(mgr, group_id,
				type, index, addr, data);

	return -ENOENT;
}

/**
 * opae_manager_get_retimer_info - get retimer info like PKVL chip
 * @mgr: opae_manager for retimer
 * @info: info return to caller
 *
 * Return: 0 on success, otherwise error code
 */
int opae_manager_get_retimer_info(struct opae_manager *mgr,
	       struct opae_retimer_info *info)
{
	if (!mgr || !mgr->network_ops)
		return -EINVAL;

	if (mgr->network_ops->get_retimer_info)
		return mgr->network_ops->get_retimer_info(mgr, info);

	return -ENOENT;
}

/**
 * opae_manager_get_retimer_status - get retimer status
 * @mgr: opae_manager of retimer
 * @status: status of retimer
 *
 * Return: 0 on success, otherwise error code
 */
int opae_manager_get_retimer_status(struct opae_manager *mgr,
		struct opae_retimer_status *status)
{
	if (!mgr || !mgr->network_ops)
		return -EINVAL;

	if (mgr->network_ops->get_retimer_status)
		return mgr->network_ops->get_retimer_status(mgr,
				status);

	return -ENOENT;
}
