/*
 * Copyright (c) 2022 Intel Corporation
 * SPDX-License-Identifier: Apache-2.0
 *
 * Derived from FreeBSD original driver made by Jim Harris
 * with contributions from Alexander Motin and Wojciech Macek
 */

#ifndef ZEPHYR_DRIVERS_DISK_NVME_NVME_H_
#define ZEPHYR_DRIVERS_DISK_NVME_NVME_H_

#include "nvme_helpers.h"
#include "nvme_cmd.h"
#include "nvme_namespace.h"

struct nvme_registers {
	uint32_t	cap_lo; /* controller capabilities */
	uint32_t	cap_hi;
	uint32_t	vs;	/* version */
	uint32_t	intms;	/* interrupt mask set */
	uint32_t	intmc;	/* interrupt mask clear */
	uint32_t	cc;	/* controller configuration */
	uint32_t	reserved1;
	uint32_t	csts;	/* controller status */
	uint32_t	nssr;	/* NVM Subsystem Reset */
	uint32_t	aqa;	/* admin queue attributes */
	uint64_t	asq;	/* admin submission queue base addr */
	uint64_t	acq;	/* admin completion queue base addr */
	uint32_t	cmbloc;	/* Controller Memory Buffer Location */
	uint32_t	cmbsz;	/* Controller Memory Buffer Size */
	uint32_t	bpinfo;	/* Boot Partition Information */
	uint32_t	bprsel;	/* Boot Partition Read Select */
	uint64_t	bpmbl;	/* Boot Partition Memory Buffer Location */
	uint64_t	cmbmsc;	/* Controller Memory Buffer Memory Space Control */
	uint32_t	cmbsts;	/* Controller Memory Buffer Status */
	uint8_t		reserved3[3492]; /* 5Ch - DFFh */
	uint32_t	pmrcap;	/* Persistent Memory Capabilities */
	uint32_t	pmrctl;	/* Persistent Memory Region Control */
	uint32_t	pmrsts;	/* Persistent Memory Region Status */
	uint32_t	pmrebs;	/* Persistent Memory Region Elasticity Buffer Size */
	uint32_t	pmrswtp; /* Persistent Memory Region Sustained Write Throughput */
	uint32_t	pmrmsc_lo; /* Persistent Memory Region Controller Memory Space Control */
	uint32_t	pmrmsc_hi;
	uint8_t		reserved4[484]; /* E1Ch - FFFh */
	struct {
		uint32_t	sq_tdbl; /* submission queue tail doorbell */
		uint32_t	cq_hdbl; /* completion queue head doorbell */
	} doorbell[1];
};

struct nvme_power_state {
	/** Maximum Power */
	uint16_t	mp;
	uint8_t		ps_rsvd1;

	/** Max Power Scale, Non-Operational State */
	uint8_t		mps_nops;

	/** Entry Latency */
	uint32_t	enlat;

	/** Exit Latency */
	uint32_t	exlat;

	/** Relative Read Throughput */
	uint8_t		rrt;

	/** Relative Read Latency */
	uint8_t		rrl;

	/** Relative Write Throughput */
	uint8_t		rwt;

	/** Relative Write Latency */
	uint8_t		rwl;

	/** Idle Power */
	uint16_t	idlp;

	/** Idle Power Scale */
	uint8_t		ips;
	uint8_t		ps_rsvd8;

	/** Active Power */
	uint16_t	actp;

	/** Active Power Workload, Active Power Scale */
	uint8_t		apw_aps;

	uint8_t		ps_rsvd10[9];
} __packed;

#define NVME_SERIAL_NUMBER_LENGTH	20
#define NVME_MODEL_NUMBER_LENGTH	40
#define NVME_FIRMWARE_REVISION_LENGTH	8

struct nvme_controller_data {
	/* bytes 0-255: controller capabilities and features */

	/** pci vendor id */
	uint16_t		vid;

	/** pci subsystem vendor id */
	uint16_t		ssvid;

	/** serial number */
	uint8_t			sn[NVME_SERIAL_NUMBER_LENGTH];

	/** model number */
	uint8_t			mn[NVME_MODEL_NUMBER_LENGTH];

	/** firmware revision */
	uint8_t			fr[NVME_FIRMWARE_REVISION_LENGTH];

	/** recommended arbitration burst */
	uint8_t			rab;

	/** ieee oui identifier */
	uint8_t			ieee[3];

	/** multi-interface capabilities */
	uint8_t			mic;

	/** maximum data transfer size */
	uint8_t			mdts;

	/** Controller ID */
	uint16_t		ctrlr_id;

	/** Version */
	uint32_t		ver;

	/** RTD3 Resume Latency */
	uint32_t		rtd3r;

	/** RTD3 Enter Latency */
	uint32_t		rtd3e;

	/** Optional Asynchronous Events Supported */
	uint32_t		oaes;	/* bitfield really */

	/** Controller Attributes */
	uint32_t		ctratt;	/* bitfield really */

	/** Read Recovery Levels Supported */
	uint16_t		rrls;

	uint8_t			reserved1[9];

	/** Controller Type */
	uint8_t			cntrltype;

	/** FRU Globally Unique Identifier */
	uint8_t			fguid[16];

	/** Command Retry Delay Time 1 */
	uint16_t		crdt1;

	/** Command Retry Delay Time 2 */
	uint16_t		crdt2;

	/** Command Retry Delay Time 3 */
	uint16_t		crdt3;

	uint8_t			reserved2[122];

	/* bytes 256-511: admin command set attributes */

	/** optional admin command support */
	uint16_t		oacs;

	/** abort command limit */
	uint8_t			acl;

	/** asynchronous event request limit */
	uint8_t			aerl;

	/** firmware updates */
	uint8_t			frmw;

	/** log page attributes */
	uint8_t			lpa;

	/** error log page entries */
	uint8_t			elpe;

	/** number of power states supported */
	uint8_t			npss;

	/** admin vendor specific command configuration */
	uint8_t			avscc;

	/** Autonomous Power State Transition Attributes */
	uint8_t			apsta;

	/** Warning Composite Temperature Threshold */
	uint16_t		wctemp;

	/** Critical Composite Temperature Threshold */
	uint16_t		cctemp;

	/** Maximum Time for Firmware Activation */
	uint16_t		mtfa;

	/** Host Memory Buffer Preferred Size */
	uint32_t		hmpre;

	/** Host Memory Buffer Minimum Size */
	uint32_t		hmmin;

	/** Name space capabilities  */
	struct {
		/* if nsmgmt, report tnvmcap and unvmcap */
		uint8_t    tnvmcap[16];
		uint8_t    unvmcap[16];
	} __packed untncap;

	/** Replay Protected Memory Block Support */
	uint32_t		rpmbs; /* Really a bitfield */

	/** Extended Device Self-test Time */
	uint16_t		edstt;

	/** Device Self-test Options */
	uint8_t			dsto; /* Really a bitfield */

	/** Firmware Update Granularity */
	uint8_t			fwug;

	/** Keep Alive Support */
	uint16_t		kas;

	/** Host Controlled Thermal Management Attributes */
	uint16_t		hctma; /* Really a bitfield */

	/** Minimum Thermal Management Temperature */
	uint16_t		mntmt;

	/** Maximum Thermal Management Temperature */
	uint16_t		mxtmt;

	/** Sanitize Capabilities */
	uint32_t		sanicap; /* Really a bitfield */

	/** Host Memory Buffer Minimum Descriptor Entry Size */
	uint32_t		hmminds;

	/** Host Memory Maximum Descriptors Entries */
	uint16_t		hmmaxd;

	/** NVM Set Identifier Maximum */
	uint16_t		nsetidmax;

	/** Endurance Group Identifier Maximum */
	uint16_t		endgidmax;

	/** ANA Transition Time */
	uint8_t			anatt;

	/** Asymmetric Namespace Access Capabilities */
	uint8_t			anacap;

	/** ANA Group Identifier Maximum */
	uint32_t		anagrpmax;

	/** Number of ANA Group Identifiers */
	uint32_t		nanagrpid;

	/** Persistent Event Log Size */
	uint32_t		pels;

	uint8_t			reserved3[156];
	/* bytes 512-703: nvm command set attributes */

	/** submission queue entry size */
	uint8_t			sqes;

	/** completion queue entry size */
	uint8_t			cqes;

	/** Maximum Outstanding Commands */
	uint16_t		maxcmd;

	/** number of namespaces */
	uint32_t		nn;

	/** optional nvm command support */
	uint16_t		oncs;

	/** fused operation support */
	uint16_t		fuses;

	/** format nvm attributes */
	uint8_t			fna;

	/** volatile write cache */
	uint8_t			vwc;

	/** Atomic Write Unit Normal */
	uint16_t		awun;

	/** Atomic Write Unit Power Fail */
	uint16_t		awupf;

	/** NVM Vendor Specific Command Configuration */
	uint8_t			nvscc;

	/** Namespace Write Protection Capabilities */
	uint8_t			nwpc;

	/** Atomic Compare & Write Unit */
	uint16_t		acwu;
	uint16_t		reserved6;

	/** SGL Support */
	uint32_t		sgls;

	/** Maximum Number of Allowed Namespaces */
	uint32_t		mnan;

	/* bytes 540-767: Reserved */
	uint8_t			reserved7[224];

	/** NVM Subsystem NVMe Qualified Name */
	uint8_t			subnqn[256];

	/* bytes 1024-1791: Reserved */
	uint8_t			reserved8[768];

	/* bytes 1792-2047: NVMe over Fabrics specification */
	uint8_t			reserved9[256];

	/* bytes 2048-3071: power state descriptors */
	struct nvme_power_state power_state[32];

	/* bytes 3072-4095: vendor specific */
	uint8_t			vs[1024];
} __packed __aligned(4);

static inline
void nvme_controller_data_swapbytes(struct nvme_controller_data *s)
{
#if _BYTE_ORDER != _LITTLE_ENDIAN
	s->vid = sys_le16_to_cpu(s->vid);
	s->ssvid = sys_le16_to_cpu(s->ssvid);
	s->ctrlr_id = sys_le16_to_cpu(s->ctrlr_id);
	s->ver = sys_le32_to_cpu(s->ver);
	s->rtd3r = sys_le32_to_cpu(s->rtd3r);
	s->rtd3e = sys_le32_to_cpu(s->rtd3e);
	s->oaes = sys_le32_to_cpu(s->oaes);
	s->ctratt = sys_le32_to_cpu(s->ctratt);
	s->rrls = sys_le16_to_cpu(s->rrls);
	s->crdt1 = sys_le16_to_cpu(s->crdt1);
	s->crdt2 = sys_le16_to_cpu(s->crdt2);
	s->crdt3 = sys_le16_to_cpu(s->crdt3);
	s->oacs = sys_le16_to_cpu(s->oacs);
	s->wctemp = sys_le16_to_cpu(s->wctemp);
	s->cctemp = sys_le16_to_cpu(s->cctemp);
	s->mtfa = sys_le16_to_cpu(s->mtfa);
	s->hmpre = sys_le32_to_cpu(s->hmpre);
	s->hmmin = sys_le32_to_cpu(s->hmmin);
	s->rpmbs = sys_le32_to_cpu(s->rpmbs);
	s->edstt = sys_le16_to_cpu(s->edstt);
	s->kas = sys_le16_to_cpu(s->kas);
	s->hctma = sys_le16_to_cpu(s->hctma);
	s->mntmt = sys_le16_to_cpu(s->mntmt);
	s->mxtmt = sys_le16_to_cpu(s->mxtmt);
	s->sanicap = sys_le32_to_cpu(s->sanicap);
	s->hmminds = sys_le32_to_cpu(s->hmminds);
	s->hmmaxd = sys_le16_to_cpu(s->hmmaxd);
	s->nsetidmax = sys_le16_to_cpu(s->nsetidmax);
	s->endgidmax = sys_le16_to_cpu(s->endgidmax);
	s->anagrpmax = sys_le32_to_cpu(s->anagrpmax);
	s->nanagrpid = sys_le32_to_cpu(s->nanagrpid);
	s->pels = sys_le32_to_cpu(s->pels);
	s->maxcmd = sys_le16_to_cpu(s->maxcmd);
	s->nn = sys_le32_to_cpu(s->nn);
	s->oncs = sys_le16_to_cpu(s->oncs);
	s->fuses = sys_le16_to_cpu(s->fuses);
	s->awun = sys_le16_to_cpu(s->awun);
	s->awupf = sys_le16_to_cpu(s->awupf);
	s->acwu = sys_le16_to_cpu(s->acwu);
	s->sgls = sys_le32_to_cpu(s->sgls);
	s->mnan = sys_le32_to_cpu(s->mnan);
#else
	ARG_UNUSED(s);
#endif
}

#include <zephyr/device.h>
#include <zephyr/drivers/pcie/pcie.h>
#include <zephyr/drivers/pcie/msi.h>

#define NVME_PCIE_BAR_IDX 0

#define NVME_REQUEST_AMOUNT (CONFIG_NVME_ADMIN_ENTRIES +	\
			     CONFIG_NVME_IO_ENTRIES)

/* admin queue + io queue(s) */
#define NVME_PCIE_MSIX_VECTORS 1 + CONFIG_NVME_IO_QUEUES

#define NVME_QUEUE_ALLOCATE(name, n_entries)				\
	static struct nvme_command cmd_##name[n_entries] __aligned(0x1000); \
	static struct nvme_completion cpl_##name[n_entries] __aligned(0x1000); \
									\
	static struct nvme_cmd_qpair name = {				\
		.num_entries = n_entries,				\
		.cmd = cmd_##name,					\
		.cpl = cpl_##name,					\
	}

#define NVME_ADMINQ_ALLOCATE(n, n_entries)		\
	NVME_QUEUE_ALLOCATE(admin_##n, n_entries)
#define NVME_IOQ_ALLOCATE(n, n_entries)			\
	NVME_QUEUE_ALLOCATE(io_##n, n_entries)

struct nvme_controller_config {
	struct pcie_dev *pcie;
};

struct nvme_controller {
	DEVICE_MMIO_RAM;

	const struct device *dev;

	struct k_mutex lock;

	uint32_t id;

	msi_vector_t vectors[NVME_PCIE_MSIX_VECTORS];

	struct nvme_controller_data cdata;

	uint32_t num_io_queues;
	struct nvme_cmd_qpair *adminq;
	struct nvme_cmd_qpair *ioq;

	uint32_t ready_timeout_in_ms;

	/** LO and HI capacity mask */
	uint32_t cap_lo;
	uint32_t cap_hi;

	/** Page size and log2(page_size) - 12 that we're currently using */
	uint32_t page_size;
	uint32_t mps;

	/** doorbell stride */
	uint32_t dstrd;

	/** maximum i/o size in bytes */
	uint32_t max_xfer_size;

	struct nvme_namespace ns[CONFIG_NVME_MAX_NAMESPACES];
};

static inline
bool nvme_controller_has_dataset_mgmt(struct nvme_controller *ctrlr)
{
	/* Assumes cd was byte swapped by nvme_controller_data_swapbytes() */
	return ((ctrlr->cdata.oncs >> NVME_CTRLR_DATA_ONCS_DSM_SHIFT) &
		NVME_CTRLR_DATA_ONCS_DSM_MASK);
}

static inline void nvme_lock(const struct device *dev)
{
	struct nvme_controller *nvme_ctrlr = dev->data;

	k_mutex_lock(&nvme_ctrlr->lock, K_FOREVER);
}

static inline void nvme_unlock(const struct device *dev)
{
	struct nvme_controller *nvme_ctrlr = dev->data;

	k_mutex_unlock(&nvme_ctrlr->lock);
}

#endif /* ZEPHYR_DRIVERS_DISK_NVME_NVME_H_ */
